1/*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { BusinessError } from '@ohos.base';
17import hilog from '@ohos.hilog';
18import { KeyCode } from '@ohos.multimodalInput.keyCode';
19import resourceManager from '@ohos.resourceManager';
20import { Theme } from '@ohos.arkui.theme';
21import { LengthMetrics } from '@ohos.arkui.node';
22import common from '@ohos.app.ability.common';
23import window from '@ohos.window';
24import { Context } from '@ohos.arkui.UIContext';
25
26export enum EditableLeftIconType {
27  Back,
28  Cancel,
29}
30
31export declare interface EditableTitleBarMenuItem {
32  value: ResourceStr;
33  isEnabled: boolean;
34  label?: ResourceStr;
35  action?: () => void;
36}
37
38export type EditableTitleBarItem = EditableTitleBarMenuItem;
39
40export declare interface EditableTitleBarOptions {
41  backgroundColor?: ResourceColor;
42  backgroundBlurStyle?: BlurStyle;
43  safeAreaTypes?: Array<SafeAreaType>;
44  safeAreaEdges?: Array<SafeAreaEdge>;
45}
46
47enum ItemType {
48  Image,
49  Icon,
50  LeftIcon,
51}
52
53const PUBLIC_CANCEL = $r('sys.symbol.xmark');
54
55const PUBLIC_OK = $r('sys.symbol.checkmark');
56
57const PUBLIC_BACK = $r('sys.symbol.chevron_backward');
58
59const DEFAULT_TITLE_BAR_HEIGHT = 56;
60const DEFAULT_TITLE_PADDING = 2;
61const MAX_LINE_ONE = 1;
62const MAX_LINES_TWO = 2;
63const MAX_MAIN_TITLE_PERCENT = 0.65;
64const MAX_SUB_TITLE_PERCENT = 0.35;
65const MIN_SUBTITLE_SIZE = '10.0vp';
66const TEXT_EDITABLE_DIALOG = '18.3fp';
67const IMAGE_SIZE = '64vp';
68const MAX_DIALOG = '256vp';
69const MIN_DIALOG = '216vp';
70const SYMBOL_SIZE = '24vp';
71const SYMBOL_TITLE_SIZE = '64vp';
72const TITLE_VP: number = 20;
73const SUBTITLE_VP: number = 14;
74
75// 'sys.float.titlebar_title_tertiary_size' id,value: 20vp
76const TITLE_F: number = getNumberByResource(125831095, TITLE_VP);
77// 'sys.float.titlebar_subheader_size' id,value: 14vp
78const SUBTITLE_F: number = getNumberByResource(125831097, SUBTITLE_VP);
79
80const TITLE_F_VP: string = (TITLE_F > 0 ? TITLE_F : TITLE_VP) + 'vp';
81const SUBTITLE_F_VP: string = (SUBTITLE_F > 0 ? SUBTITLE_F : SUBTITLE_VP) + 'vp';
82
83class EditableTitleBarTheme {
84  public iconColor: ResourceColor = $r('sys.color.titlebar_icon_color');
85  public iconBackgroundColor: ResourceColor = $r('sys.color.titlebar_icon_background_color');
86  public iconBackgroundPressedColor: ResourceColor = $r('sys.color.titlebar_icon_background_pressed_color');
87  public iconBackgroundHoverColor: ResourceColor = $r('sys.color.titlebar_icon_background_hover_color');
88  public iconBackgroundFocusOutlineColor: ResourceColor = $r('sys.color.titlebar_icon_background_focus_outline_color');
89  public titleColor: ResourceColor = $r('sys.color.titlebar_title_tertiary_color');
90  public subTitleColor: ResourceColor = $r('sys.color.titlebar_subheader_color');
91}
92
93class ButtonGestureModifier implements GestureModifier {
94  public static readonly longPressTime: number = 500;
95  public static readonly minFontSize: number = 1.75;
96  public fontSize: number = 1;
97  public controller: CustomDialogController | null = null;
98
99  constructor(controller: CustomDialogController | null) {
100    this.controller = controller;
101  }
102
103  applyGesture(event: UIGestureEvent): void {
104    if (this.fontSize >= ButtonGestureModifier.minFontSize) {
105      event.addGesture(
106        new LongPressGestureHandler({ repeat: false, duration: ButtonGestureModifier.longPressTime })
107          .onAction(() => {
108            if (event) {
109              this.controller?.open();
110            }
111          })
112          .onActionEnd(() => {
113            this.controller?.close();
114          })
115      )
116    } else {
117      event.clearGestures();
118    }
119  }
120}
121
122@Component
123export struct EditableTitleBar {
124  leftIconStyle: EditableLeftIconType = EditableLeftIconType.Back;
125  title: ResourceStr = '';
126  subtitle?: ResourceStr = '';
127  isSaveIconRequired: boolean = true;
128  imageItem?: EditableTitleBarItem;
129  menuItems: Array<EditableTitleBarMenuItem> | undefined = undefined;
130  options: EditableTitleBarOptions = {
131    safeAreaTypes: [SafeAreaType.SYSTEM],
132    safeAreaEdges: [SafeAreaEdge.TOP],
133  };
134  onSave?: () => void;
135  onCancel?: () => void;
136  constraintWidth: number = 0;
137  public static readonly maxCountOfExtraItems = 3;
138  public static readonly maxOtherCountOfExtraItems = 2;
139  public static readonly commonZero = 0;
140  public static readonly noneColor = '#00000000';
141  // 'sys.float.titlebar_default_height' id,value: 56vp
142  public static readonly defaultHeight: number = getNumberByResource(125831115, DEFAULT_TITLE_BAR_HEIGHT);
143  // 'sys.float.padding_level1' id,value: 2vp
144  public static readonly defaultTitlePadding: number = getNumberByResource(125830920, DEFAULT_TITLE_PADDING);
145  public static readonly totalHeight: number =
146    EditableTitleBar.defaultHeight === EditableTitleBar.commonZero ? DEFAULT_TITLE_BAR_HEIGHT :
147    EditableTitleBar.defaultHeight;
148  static readonly titlePadding: number =
149    EditableTitleBar.defaultTitlePadding === EditableTitleBar.commonZero ?
150      DEFAULT_TITLE_PADDING : EditableTitleBar.defaultTitlePadding;
151  private static readonly maxMainTitleHeight =
152    (EditableTitleBar.totalHeight - EditableTitleBar.titlePadding) * MAX_MAIN_TITLE_PERCENT;
153  private static readonly maxSubTitleHeight =
154    (EditableTitleBar.totalHeight - EditableTitleBar.titlePadding) * MAX_SUB_TITLE_PERCENT;
155  private isFollowingSystemFontScale: boolean = false;
156  private maxFontScale: number = 1;
157  private systemFontScale?: number = 1;
158  @Provide editableTitleBarTheme: EditableTitleBarTheme = new EditableTitleBarTheme();
159  @Prop contentMargin?: LocalizedMargin;
160  @State titleBarMargin: LocalizedMargin = {
161    start: LengthMetrics.resource($r('sys.float.margin_left')),
162    end: LengthMetrics.resource($r('sys.float.margin_right')),
163  }
164  @State fontSize: number = 1;
165
166  onWillApplyTheme(theme: Theme): void {
167    this.editableTitleBarTheme.iconColor = theme.colors.iconPrimary;
168    this.editableTitleBarTheme.titleColor = theme.colors.fontPrimary;
169    this.editableTitleBarTheme.subTitleColor = theme.colors.fontSecondary;
170    this.editableTitleBarTheme.iconBackgroundPressedColor = theme.colors.interactivePressed;
171    this.editableTitleBarTheme.iconBackgroundHoverColor = theme.colors.interactiveHover;
172    this.editableTitleBarTheme.iconBackgroundFocusOutlineColor = theme.colors.interactiveFocus;
173  }
174
175  aboutToAppear(): void {
176    try {
177      let uiContent: UIContext = this.getUIContext();
178      this.isFollowingSystemFontScale = uiContent.isFollowingSystemFontScale();
179      this.maxFontScale = uiContent.getMaxFontScale();
180    } catch (exception) {
181      let code: number = (exception as BusinessError).code;
182      let message: string = (exception as BusinessError).message;
183      hilog.error(0x3900, 'Ace', `Faild to init fontsizescale info,cause, code: ${code}, message: ${message}`);
184    }
185  }
186
187  decideFontScale(): number {
188    let uiContent: UIContext = this.getUIContext();
189    this.systemFontScale = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1;
190    if (!this.isFollowingSystemFontScale) {
191      return 1;
192    }
193    return Math.min(this.systemFontScale, this.maxFontScale);
194  }
195
196  build() {
197    Flex({
198      justifyContent: FlexAlign.SpaceBetween,
199      alignItems: ItemAlign.Stretch,
200    }) {
201      Row() {
202        Row() {
203          this.leftIconLayout();
204        }
205        .flexShrink(0)
206
207        if (this.imageItem) {
208          Row() {
209            this.imageItemLayout();
210          }
211          .flexShrink(0)
212        }
213
214        Row() {
215          this.titleLayout();
216        }
217        .width('100%')
218        .flexShrink(1)
219
220        Row() {
221          this.rightMenuItemsLayout();
222        }
223        .flexShrink(0)
224      }
225      .width('100%')
226      .margin(this.contentMargin ?? this.titleBarMargin)
227      .height(EditableTitleBar.totalHeight)
228    }
229    .backgroundColor(this.options.backgroundColor ?? EditableTitleBar.noneColor)
230    .backgroundBlurStyle(
231      this.options.backgroundBlurStyle ?? BlurStyle.NONE)
232    .expandSafeArea(
233      this.options.safeAreaTypes,
234      this.options.safeAreaEdges,
235    )
236  }
237
238  @Builder
239  imageItemLayout(): void {
240    ImageMenuItem({
241      item: this.imageItem,
242      attribute: ItemType.Image,
243    })
244  }
245
246  @Builder
247  leftIconLayout(): void {
248    if (this.leftIconStyle === EditableLeftIconType.Back) {
249      ImageMenuItem({
250        item: {
251          value: PUBLIC_BACK,
252          isEnabled: true,
253          action: () => this.onCancel ? this.onCancel() : this.getUIContext()?.getRouter()?.back()
254        },
255        fontSize: this.fontSize,
256        attribute: ItemType.LeftIcon,
257        useSymbol: true,
258      })
259    } else {
260      ImageMenuItem({
261        item: {
262          value: PUBLIC_CANCEL,
263          isEnabled: true,
264          action: () => this.onCancel && this.onCancel(),
265        },
266        fontSize: this.fontSize,
267        attribute: ItemType.LeftIcon,
268        useSymbol: true,
269      })
270    }
271  }
272
273  @Builder
274  titleLayout(): void {
275    Column() {
276      Row() {
277        Text(this.title)
278          .maxFontSize(TITLE_F_VP)
279          .minFontSize(SUBTITLE_F_VP)
280          .fontColor(this.editableTitleBarTheme.titleColor)
281          .maxLines(this.subtitle ? MAX_LINE_ONE : MAX_LINES_TWO)
282          .fontWeight(FontWeight.Bold)
283          .textAlign(TextAlign.Start)
284          .textOverflow({ overflow: TextOverflow.Ellipsis })
285          .heightAdaptivePolicy(this.subtitle ?
286          TextHeightAdaptivePolicy.MAX_LINES_FIRST : TextHeightAdaptivePolicy.MIN_FONT_SIZE_FIRST)
287          .constraintSize({
288            maxHeight: this.subtitle ? EditableTitleBar.maxMainTitleHeight : EditableTitleBar.totalHeight,
289          })
290      }
291      .justifyContent(FlexAlign.Start)
292
293      if (this.subtitle) {
294        Row() {
295          Text(this.subtitle)
296            .maxFontSize(SUBTITLE_F_VP)
297            .minFontSize(MIN_SUBTITLE_SIZE)
298            .fontColor(this.editableTitleBarTheme.subTitleColor)
299            .maxLines(MAX_LINE_ONE)
300            .fontWeight(FontWeight.Regular)
301            .textAlign(TextAlign.Start)
302            .textOverflow({ overflow: TextOverflow.Ellipsis })
303            .heightAdaptivePolicy(TextHeightAdaptivePolicy.MAX_LINES_FIRST)
304            .constraintSize({
305              maxHeight: this.title ? EditableTitleBar.maxSubTitleHeight : EditableTitleBar.totalHeight,
306            })
307        }
308        .margin({
309          top: $r('sys.float.padding_level1'),
310        })
311        .justifyContent(FlexAlign.Start)
312      }
313    }
314    .height(EditableTitleBar.totalHeight)
315    .justifyContent(FlexAlign.Center)
316    .margin({
317      // 'sys.float.titlebar_icon_background_space_horizontal' id,value: 8vp
318      start: LengthMetrics.resource($r('sys.float.titlebar_icon_background_space_horizontal')),
319    })
320    .alignItems(HorizontalAlign.Start)
321  }
322
323  @Builder
324  rightMenuItemsLayout(): void {
325    EditableTitleBarMenuSection({
326      menuItems: this.menuItems,
327      onSave: this.onSave,
328      isSaveEnabled: this.isSaveIconRequired,
329      fontSize: this.fontSize,
330    })
331  }
332
333  onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Layoutable[], constraint: ConstraintSizeOptions): void {
334    children.forEach((child) => {
335      child.layout({ x: 0, y: 0 });
336    })
337  }
338
339  onMeasureSize(selfLayoutInfo: GeometryInfo, children: Measurable[], constraint: ConstraintSizeOptions): SizeResult {
340    let result: SizeResult = { width: selfLayoutInfo.width, height: selfLayoutInfo.height };
341    this.fontSize = this.decideFontScale();
342    children.forEach((child) => {
343      result.height = child.measure(constraint).height;
344      result.width = Number(constraint.maxWidth);
345    })
346    return result;
347  }
348}
349
350@Component
351struct EditableTitleBarMenuSection {
352  menuItems: Array<EditableTitleBarMenuItem> | undefined = undefined;
353  onSave?: () => void;
354  isSaveEnabled: boolean = true;
355  @Prop fontSize: number = 1;
356
357  build() {
358    Column() {
359      Row() {
360        if (this.menuItems !== undefined && this.menuItems.length > EditableTitleBar.commonZero) {
361          ForEach(this.menuItems.slice(EditableTitleBar.commonZero,
362            this.isSaveEnabled ?
363            EditableTitleBar.maxOtherCountOfExtraItems : EditableTitleBar.maxCountOfExtraItems),
364            (item: EditableTitleBarMenuItem) => {
365              ImageMenuItem({
366                item: item,
367                attribute: ItemType.Icon,
368                fontSize: this.fontSize,
369              })
370            })
371        }
372        if (this.isSaveEnabled) {
373          ImageMenuItem({
374            item: {
375              value: PUBLIC_OK,
376              isEnabled: true,
377              action: () => this.onSave && this.onSave(),
378            },
379            fontSize: this.fontSize,
380            attribute: ItemType.Icon,
381            useSymbol: true,
382          })
383        }
384      }
385    }
386    .justifyContent(FlexAlign.Center)
387  }
388}
389
390@Component
391struct ImageMenuItem {
392  item: EditableTitleBarMenuItem = {
393    value: '',
394    isEnabled: true,
395    label: '',
396  };
397  attribute: ItemType = ItemType.Image;
398  callbackId: number | undefined = undefined;
399  minFontSize: number = 1.75;
400  maxFontSize: number = 3.2;
401  longPressTime: number = 500;
402  useSymbol: boolean = false;
403  @Prop @Watch('onFontSizeUpdated') fontSize: number = 1;
404  @State isOnFocus: boolean = false;
405  @State isOnHover: boolean = false;
406  @State isOnClick: boolean = false;
407  @Consume editableTitleBarTheme: EditableTitleBarTheme;
408  dialogController: CustomDialogController | null = new CustomDialogController({
409    builder: EditableTitleBarDialog({
410      cancel: () => {
411      },
412      confirm: () => {
413      },
414      itemEditableDialog: this.item,
415      textEditableTitleBarDialog: this.item.label ? this.item.label : this.textDialog(),
416      fontSize: this.fontSize,
417      useSymbol: this.useSymbol,
418    }),
419    maskColor: Color.Transparent,
420    isModal: true,
421    customStyle: true,
422  });
423  @State buttonGestureModifier: ButtonGestureModifier = new ButtonGestureModifier(this.dialogController);
424
425  private textDialog(): ResourceStr {
426    if (this.item.value === PUBLIC_OK) {
427      return $r('sys.string.icon_save');
428    } else if (this.item.value === PUBLIC_CANCEL) {
429      return $r('sys.string.icon_cancel');
430    } else if (this.item.value === PUBLIC_BACK) {
431      return $r('sys.string.icon_back');
432    } else {
433      return this.item.label ? this.item.label : '';
434    }
435  }
436
437  onFontSizeUpdated(): void {
438    this.buttonGestureModifier.fontSize = this.fontSize;
439  }
440
441  @Styles
442  buttonStateStyles() {
443    .stateStyles({
444      focused: this.focusedStyle,
445      normal: this.notInFocusedStyle,
446      pressed: this.notInFocusedStyle,
447    })
448  }
449
450  @Styles
451  focusedStyle() {
452    .border({
453      radius: $r('sys.float.titlebar_icon_background_shape'),
454      width: $r('sys.float.titlebar_icon_background_focus_outline_weight'),
455      color: this.editableTitleBarTheme.iconBackgroundFocusOutlineColor,
456      style: BorderStyle.Solid,
457    })
458  }
459
460  @Styles
461  notInFocusedStyle() {
462    .border({
463      radius: $r('sys.float.titlebar_icon_background_shape'),
464      width: EditableTitleBar.commonZero,
465    })
466  }
467
468  private touchEventAction(event: TouchEvent): void {
469    if (!this.item.isEnabled) {
470      return;
471    }
472    if (event.type === TouchType.Down) {
473      this.isOnClick = true;
474    }
475    if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
476      if (this.fontSize >= this.minFontSize) {
477        this.dialogController?.close()
478      }
479      this.isOnClick = false;
480    }
481  }
482
483  private keyEventAction(event: KeyEvent): void {
484    if (!this.item.isEnabled) {
485      return;
486    }
487    if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) {
488      return;
489    }
490    if (event.type === KeyType.Down) {
491      this.isOnClick = true;
492    }
493    if (event.type === KeyType.Up) {
494      this.isOnClick = false;
495    }
496  }
497
498  @Styles
499  buttonEventStyle() {
500    .onFocus(() => {
501      if (!this.item.isEnabled) {
502        return;
503      }
504      this.isOnFocus = true;
505    })
506    .onBlur(() => this.isOnFocus = false)
507    .onHover((isOn) => {
508      if (!this.item.isEnabled) {
509        return;
510      }
511      this.isOnHover = isOn;
512    })
513    .onKeyEvent((event) => {
514      this.keyEventAction(event);
515    })
516    .onTouch((event) => {
517      this.touchEventAction(event);
518    })
519    .onClick(() => {
520      this.item.isEnabled && this.item.action && this.item.action()
521    })
522  }
523
524  @Styles
525  backgroundButtonStyle() {
526    .width($r('sys.float.titlebar_icon_background_width'))
527    .height($r('sys.float.titlebar_icon_background_height'))
528    .focusable(this.item.isEnabled)
529    .enabled(this.item.isEnabled)
530  }
531
532  getBgColor(): ResourceColor {
533    if (this.isOnClick) {
534      return this.editableTitleBarTheme.iconBackgroundPressedColor;
535    } else if (this.isOnHover) {
536      return this.editableTitleBarTheme.iconBackgroundHoverColor;
537    } else {
538      return this.editableTitleBarTheme.iconBackgroundColor;
539    }
540  }
541
542  getFgColor(): ResourceStr {
543    if (this.isOnClick) {
544      return $r('sys.color.titlebar_icon_background_pressed_color');
545    } else if (this.isOnHover) {
546      return $r('sys.color.titlebar_icon_background_hover_color');
547    } else {
548      return EditableTitleBar.noneColor;
549    }
550  }
551
552  private getAccessibilityReadText(): Resource | undefined {
553    if (this.item.value === PUBLIC_OK) {
554      return $r('sys.string.icon_save');
555    } else if (this.item.value === PUBLIC_CANCEL) {
556      return $r('sys.string.icon_cancel');
557    } else if (this.item.value === PUBLIC_BACK) {
558      return $r('sys.string.icon_back');
559    } else if (this.item.label) {
560      return this.item.label as Resource;
561    }
562    return undefined;
563  }
564
565  @Builder
566  IconBuilder(): void {
567    Button({ type: ButtonType.Normal, stateEffect: this.item.isEnabled }) {
568      if (this.useSymbol) {
569        SymbolGlyph(this.item.value as Resource)
570          .fontColor([this.editableTitleBarTheme.iconColor])
571          .width($r('sys.float.titlebar_icon_width'))
572          .height($r('sys.float.titlebar_icon_height'))
573          .focusable(this.item.isEnabled)
574          .enabled(this.item.isEnabled)
575          .draggable(false)
576          .fontSize(SYMBOL_SIZE)
577          .accessibilityText(this.getAccessibilityReadText())
578      } else {
579        Image(this.item.value)
580          .fillColor(this.editableTitleBarTheme.iconColor)
581          .matchTextDirection(this.item.value === PUBLIC_BACK ? true : false)
582          .width($r('sys.float.titlebar_icon_width'))
583          .height($r('sys.float.titlebar_icon_height'))
584          .focusable(this.item.isEnabled)
585          .enabled(this.item.isEnabled)
586          .draggable(false)
587          .accessibilityText(this.getAccessibilityReadText())
588      }
589    }
590    .backgroundButtonStyle()
591    .borderRadius($r('sys.float.titlebar_icon_background_shape'))
592    .margin({
593      start: this.attribute === ItemType.LeftIcon ? LengthMetrics.vp(EditableTitleBar.commonZero) :
594      LengthMetrics.resource($r('sys.float.titlebar_icon_background_space_horizontal')),
595    })
596    .focusOnTouch(true)
597    .foregroundColor(this.getFgColor())
598    .backgroundColor(this.getBgColor())
599    .buttonStateStyles()
600    .buttonEventStyle()
601    .gestureModifier(this.buttonGestureModifier)
602  }
603
604  @Builder
605  ImageBuilder() {
606    Stack({ alignContent: Alignment.Center }) {
607      if (this.useSymbol) {
608        SymbolGlyph(this.item.value as Resource)
609          .width($r('sys.float.titlebar_icon_background_width'))
610          .height($r('sys.float.titlebar_icon_background_height'))
611          .borderRadius($r('sys.float.corner_radius_level10'))
612          .focusable(false)
613          .enabled(this.item.isEnabled)
614      } else {
615        Image(this.item.value)
616          .width($r('sys.float.titlebar_icon_background_width'))
617          .height($r('sys.float.titlebar_icon_background_height'))
618          .borderRadius($r('sys.float.corner_radius_level10'))
619          .focusable(false)
620          .enabled(this.item.isEnabled)
621          .objectFit(ImageFit.Cover)
622      }
623
624      Button({ type: ButtonType.Circle })
625        .backgroundButtonStyle()
626        .foregroundColor(this.getFgColor())
627        .backgroundColor(EditableTitleBar.noneColor)
628        .buttonStateStyles()
629        .buttonEventStyle()
630        .gestureModifier(this.buttonGestureModifier)
631    }
632    .margin({
633      start: LengthMetrics.resource($r('sys.float.titlebar_icon_background_space_horizontal')),
634    })
635  }
636
637  build() {
638    if (this.attribute === ItemType.Icon || this.attribute === ItemType.LeftIcon) {
639      this.IconBuilder();
640    } else {
641      this.ImageBuilder();
642    }
643  }
644}
645
646/**
647 * EditableTitleBarDialog
648 *
649 * @since 2024-05-28
650 */
651@CustomDialog
652struct EditableTitleBarDialog {
653  itemEditableDialog: EditableTitleBarMenuItem = {
654    value: '',
655    isEnabled: true,
656  };
657  callbackId: number | undefined = undefined;
658  textEditableTitleBarDialog?: ResourceStr = '';
659  mainWindowStage: window.Window | undefined = undefined;
660  controller?: CustomDialogController
661  minFontSize: number = 1.75;
662  maxFontSize: number = 3.2;
663  screenWidth: number = 640;
664  verticalScreenLines: number = 6;
665  horizontalsScreenLines: number = 1;
666  useSymbol: boolean = false;
667  cancel: () => void = () => {
668  }
669  confirm: () => void = () => {
670  }
671  @StorageLink('mainWindow') mainWindow: Promise<window.Window> | undefined = undefined;
672  @Prop fontSize: number = 1;
673  @State maxLines: number = 1;
674  @StorageProp('windowStandardHeight') windowStandardHeight: number = 0;
675
676  build() {
677    if (this.textEditableTitleBarDialog) {
678      Column() {
679        if (this.useSymbol) {
680          SymbolGlyph(this.itemEditableDialog.value as Resource)
681            .width(SYMBOL_TITLE_SIZE)
682            .height(SYMBOL_TITLE_SIZE)
683            .margin({
684              top: $r('sys.float.padding_level24'),
685              bottom: $r('sys.float.padding_level8'),
686            })
687            .fontSize(SYMBOL_TITLE_SIZE)
688            .fontColor([$r('sys.color.icon_primary')])
689        } else {
690          Image(this.itemEditableDialog.value)
691            .width(IMAGE_SIZE)
692            .height(IMAGE_SIZE)
693            .margin({
694              top: $r('sys.float.padding_level24'),
695              bottom: $r('sys.float.padding_level8'),
696            })
697            .fillColor($r('sys.color.icon_primary'))
698        }
699        Column() {
700          Text(this.textEditableTitleBarDialog)
701            .fontSize(TEXT_EDITABLE_DIALOG)
702            .textOverflow({ overflow: TextOverflow.Ellipsis })
703            .maxLines(this.maxLines)
704            .width('100%')
705            .textAlign(TextAlign.Center)
706            .fontColor($r('sys.color.font_primary'))
707        }
708        .width('100%')
709        .padding({
710          left: $r('sys.float.padding_level4'),
711          right: $r('sys.float.padding_level4'),
712          bottom: $r('sys.float.padding_level12'),
713        })
714      }
715      .width(this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG)
716      .constraintSize({ minHeight: this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG })
717      .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK)
718      .shadow(ShadowStyle.OUTER_DEFAULT_LG)
719      .borderRadius(($r('sys.float.corner_radius_level10')))
720    } else {
721      Column() {
722        if (this.useSymbol) {
723          SymbolGlyph(this.itemEditableDialog.value as Resource)
724            .width(SYMBOL_TITLE_SIZE)
725            .height(SYMBOL_TITLE_SIZE)
726            .fontSize(SYMBOL_TITLE_SIZE)
727            .fontColor([$r('sys.color.icon_primary')])
728        } else {
729          Image(this.itemEditableDialog.value)
730            .width(IMAGE_SIZE)
731            .height(IMAGE_SIZE)
732            .fillColor($r('sys.color.icon_primary'))
733        }
734      }
735      .width(this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG)
736      .constraintSize({ minHeight: this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG })
737      .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK)
738      .shadow(ShadowStyle.OUTER_DEFAULT_LG)
739      .borderRadius(($r('sys.float.corner_radius_level10')))
740      .justifyContent(FlexAlign.Center)
741    }
742  }
743
744  async aboutToAppear(): Promise<void> {
745    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
746    this.mainWindowStage = context.windowStage.getMainWindowSync();
747    let properties: window.WindowProperties = this.mainWindowStage.getWindowProperties();
748    let rect = properties.windowRect;
749    if (px2vp(rect.height) > this.screenWidth) {
750      this.maxLines = this.verticalScreenLines;
751    } else {
752      this.maxLines = this.horizontalsScreenLines;
753    }
754  }
755}
756
757/**
758 * get resource size
759 *
760 * @param resourceId resource id
761 * @return resource size
762 */
763function getNumberByResource(resourceId: number, defaultNumber: number): number {
764  try {
765    let resourceNumber: number = resourceManager.getSystemResourceManager().getNumber(resourceId);
766    if (resourceNumber === 0) {
767      return defaultNumber;
768    } else {
769      return resourceNumber;
770    }
771  } catch (error) {
772    let code: number = (error as BusinessError).code;
773    let message: string = (error as BusinessError).message;
774    hilog.error(0x3900, 'Ace', `EditableTitleBar getNumberByResource error, code: ${code},message:${message}`);
775    return 0;
776  }
777}