• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-2025 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 { Theme } from '@ohos.arkui.theme';
17import { LengthMetrics, LengthUnit, ColorMetrics } from '@ohos.arkui.node';
18import { DividerModifier, SymbolGlyphModifier } from '@ohos.arkui.modifier';
19import hilog from '@ohos.hilog';
20import window from '@ohos.window';
21import common from '@ohos.app.ability.common';
22import { BusinessError } from '@ohos.base';
23import promptAction from '@ohos.promptAction';
24
25export enum ToolBarV2ItemState {
26  ENABLE = 1,
27  DISABLE = 2,
28  ACTIVATE = 3,
29}
30
31// “更多”栏图标
32const PUBLIC_MORE: Resource = $r('sys.symbol.dot_grid_2x2');
33const IMAGE_SIZE: VP = '24vp';
34const DEFAULT_TOOLBAR_HEIGHT: number = 56;
35const TOOLBAR_MAX_LENGTH: number = 5;
36const MAX_FONT_SIZE: number = 3.2;
37const DIALOG_IMAGE_SIZE: VP = '64vp';
38const MAX_DIALOG: VP = '256vp';
39const MIN_DIALOG: VP = '216vp';
40const TEXT_TOOLBAR_DIALOG: FP = '18.3fp';
41const SCREEN_WIDTH_BREAK_POINT: number = 640;
42const VERTICAL_SCREEN_TEXT_MAX_LINES: number = 6;
43const HORIZONTAL_SCREEN_TEXT_MAX_LINES: number = 1;
44const FOCUS_BOX_MARGIN: number = -2;
45const FOCUS_BOX_BORDER_WIDTH: number = 2;
46const RESOURCE_TYPE_SYMBOL: number = 40000;
47
48interface MenuController {
49  value: ResourceStr;
50  action: () => void;
51  enabled?: boolean;
52}
53
54class Util {
55  public static isSymbolResource(resourceStr: ResourceStr | undefined | null): boolean {
56    if (!Util.isResourceType(resourceStr)) {
57      return false;
58    }
59    let resource: Resource = resourceStr as Resource;
60    return resource.type === RESOURCE_TYPE_SYMBOL;
61  }
62
63  public static isResourceType(resource: ResourceStr | Resource | undefined | null): boolean {
64    if (!resource) {
65      return false;
66    }
67    if (typeof resource === 'string' || typeof resource === 'undefined') {
68      return false;
69    }
70    return true;
71  }
72}
73
74@ObservedV2
75export class ToolBarV2SymbolGlyph {
76  @Trace
77  public normal: SymbolGlyphModifier;
78  @Trace
79  public activated?: SymbolGlyphModifier;
80
81  constructor(options: ToolBarSymbolGlyphOptions) {
82    this.normal = options.normal;
83    this.activated = options.activated;
84  }
85}
86
87export interface ToolBarSymbolGlyphOptions {
88  normal: SymbolGlyphModifier;
89  activated?: SymbolGlyphModifier;
90}
91
92class ButtonGestureModifier implements GestureModifier {
93  public static readonly longPressTime: number = 500;
94  public static readonly minFontSize: number = 1.75;
95  public gestureCallBack?: (event: UIGestureEvent) => void = undefined;
96
97  applyGesture(event: UIGestureEvent): void {
98    this.gestureCallBack?.(event);
99  }
100}
101
102@ObservedV2
103export class ToolBarV2ItemText {
104  @Trace
105  public text: ResourceStr;
106  @Trace
107  public color?: ColorMetrics = ColorMetrics.resourceColor($r('sys.color.font_primary'));
108  @Trace
109  public activatedColor?: ColorMetrics = ColorMetrics.resourceColor($r('sys.color.font_emphasize'));
110
111  constructor(options: ToolBarV2ItemTextOptions) {
112    this.text = options.text;
113    this.color = options.color;
114    this.activatedColor = options.activatedColor;
115  }
116}
117
118export interface ToolBarV2ItemTextOptions {
119  text: ResourceStr;
120  color?: ColorMetrics;
121  activatedColor?: ColorMetrics;
122}
123
124@ObservedV2
125export class ToolBarV2ItemImage {
126  @Trace
127  public src: ResourceStr;
128  @Trace
129  public color?: ColorMetrics = undefined;
130  @Trace
131  public activatedColor?: ColorMetrics = undefined;
132
133  constructor(options: ToolBarV2ItemImageOptions) {
134    this.src = options.src;
135    this.color = options.color;
136    this.activatedColor = options.activatedColor;
137  }
138}
139
140export declare type ToolBarV2ItemIconType = ToolBarV2ItemImage | ToolBarV2SymbolGlyph;
141
142export interface ToolBarV2ItemImageOptions {
143  src: ResourceStr;
144  color?: ColorMetrics;
145  activatedColor?: ColorMetrics;
146}
147
148export type ToolBarV2ItemAction = (index: number) => void;
149
150@ObservedV2
151export class ToolBarV2Item {
152  @Trace
153  public content: ToolBarV2ItemText = new ToolBarV2ItemText({ text: '' });
154  @Trace
155  public action?: (index: number) => void = undefined;
156  @Trace
157  public icon?: ToolBarV2ItemIconType = undefined;
158  @Trace
159  public state?: ToolBarV2ItemState = 1;
160  @Trace
161  public accessibilityText?: ResourceStr = '';
162  @Trace
163  public accessibilityDescription?: ResourceStr = '';
164  @Trace
165  public accessibilityLevel?: string = 'auto';
166  // item background, not exported
167  @Trace
168  public backgroundColor: ResourceColor = Color.Transparent;
169
170  constructor(options: ToolBarV2ItemOptions) {
171    this.content = options.content;
172    this.action = options.action;
173    this.icon = options.icon;
174    this.state = options.state;
175    this.accessibilityText = options.accessibilityText;
176    this.accessibilityDescription = options.accessibilityDescription;
177    this.accessibilityLevel = options.accessibilityLevel;
178  }
179
180  @Computed
181  get symbol(): ToolBarV2SymbolGlyph | undefined {
182    if (this.icon instanceof ToolBarV2SymbolGlyph) {
183      return this.icon;
184    }
185    return undefined;
186  }
187
188  @Computed
189  get image(): ToolBarV2ItemImage | undefined {
190    if (!(this.icon instanceof ToolBarV2SymbolGlyph)) {
191      return this.icon;
192    }
193    return undefined;
194  }
195}
196
197export interface ToolBarV2ItemOptions {
198  content: ToolBarV2ItemText;
199  action?: (index: number) => void;
200  icon?: ToolBarV2ItemIconType;
201  state?: ToolBarV2ItemState;
202  accessibilityText?: ResourceStr;
203  accessibilityDescription?: ResourceStr;
204  accessibilityLevel?: string;
205}
206
207@ObservedV2
208export class ToolBarV2Modifier implements AttributeModifier<ColumnAttribute> {
209  @Trace
210  public backgroundColorValue?: ResourceColor = $r('sys.color.ohos_id_color_toolbar_bg');
211  @Trace
212  public heightValue?: LengthMetrics = LengthMetrics.vp(DEFAULT_TOOLBAR_HEIGHT);
213  @Trace
214  public stateEffectValue?: boolean = true;
215  @Trace
216  public paddingValue?: LengthMetrics = LengthMetrics.resource($r('sys.float.padding_level12'));
217
218  applyNormalAttribute(instance: ColumnAttribute): void {
219    instance.backgroundColor(this.backgroundColorValue);
220  }
221
222  public backgroundColor(backgroundColor: ColorMetrics): ToolBarV2Modifier {
223    this.backgroundColorValue = backgroundColor.color;
224    return this;
225  }
226
227  public height(height: LengthMetrics): ToolBarV2Modifier {
228    this.heightValue = height;
229    return this;
230  }
231
232  public stateEffect(stateEffect: boolean): ToolBarV2Modifier {
233    this.stateEffectValue = stateEffect;
234    return this;
235  }
236
237  public padding(padding: LengthMetrics): ToolBarV2Modifier {
238    this.paddingValue = padding;
239    return this;
240  }
241}
242
243@ObservedV2
244class ToolBarV2Theme {
245  @Trace
246  public iconPrimaryColor: ResourceColor = $r('sys.color.icon_primary');
247  @Trace
248  public iconActivePrimaryColor: ResourceColor = $r('sys.color.icon_emphasize');
249  @Trace
250  public fontPrimaryColor: ResourceColor = $r('sys.color.font_primary');
251  @Trace
252  public fontActivatedPrimaryColor: ResourceColor = $r('sys.color.font_emphasize');
253}
254
255@ComponentV2
256export struct ToolBarV2 {
257  @Require @Param
258  toolBarList: ToolBarV2Item[];
259  @Param
260  activatedIndex?: number = -1;
261  @Param
262  dividerModifier?: DividerModifier = new DividerModifier();
263  @Param
264  toolBarModifier?: ToolBarV2Modifier =
265    new ToolBarV2Modifier()
266      .padding(LengthMetrics.resource($r('sys.float.padding_level12')))
267      .stateEffect(true)
268      .height(LengthMetrics.vp(DEFAULT_TOOLBAR_HEIGHT))
269      .backgroundColor(ColorMetrics.resourceColor($r('sys.color.ohos_id_color_toolbar_bg')));
270  @Local
271  localActivatedIndex: number = -1;
272  @Local
273  menuContent: MenuController[] = [];
274  @Local
275  fontSize: number = 1;
276  @Local
277  theme: ToolBarV2Theme = new ToolBarV2Theme();
278
279  @Monitor('activatedIndex')
280  onActivateIndexChange(monitor: IMonitor) {
281    this.localActivatedIndex = monitor.value<number>('activatedIndex')?.now ?? -1;
282  }
283
284  @Computed
285  get menus(): MenuController[] {
286    this.menuContent = [];
287    this.toolBarList.forEach((value: ToolBarV2Item, index: number) => {
288      if (index >= TOOLBAR_MAX_LENGTH - 1) {
289        this.menuContent.push({
290          value: this.toolBarList[index].content.text,
291          action: () => {
292            let callback: ToolBarV2ItemAction | undefined = this.toolBarList[index].action;
293            if (callback) {
294              callback(index);
295            }
296          },
297          enabled: this.toolBarList[index].state !== ToolBarV2ItemState.DISABLE,
298        });
299      }
300    })
301    return this.menuContent;
302  }
303
304  private itemCardTextMaxLine: number = 1;
305  private itemDialogId?: number = undefined;
306  private isFollowSystem: boolean = false;
307  private maxFontSizeScale: number = 3.2;
308  private moreItem: ToolBarV2Item = new ToolBarV2Item({
309    content: new ToolBarV2ItemText({
310      text: $r('sys.string.ohos_toolbar_more'),
311    }),
312    icon: new ToolBarV2ItemImage({
313      src: PUBLIC_MORE
314    })
315  })
316  private moreText: Resource = $r('sys.string.ohos_toolbar_more');
317
318  aboutToAppear(): void {
319    this.localActivatedIndex = this.activatedIndex ?? -1;
320    try {
321      this.isFollowSystem = this.getUIContext()?.isFollowingSystemFontScale();
322      this.maxFontSizeScale = this.getUIContext()?.getMaxFontScale();
323    } catch (err) {
324      let code: number = (err as BusinessError)?.code;
325      let message: string = (err as BusinessError)?.message;
326      hilog.error(0x3900, 'Ace', `Faild to toolBarV2 getMaxFontScale, code: ${code}, message: ${message}`);
327    }
328  }
329
330  onWillApplyTheme(theme: Theme): void {
331    this.theme.iconPrimaryColor = theme.colors.iconPrimary;
332    this.theme.iconActivePrimaryColor = theme.colors.iconEmphasize;
333    this.theme.fontPrimaryColor = theme.colors.fontPrimary;
334    this.theme.fontActivatedPrimaryColor = theme.colors.fontEmphasize;
335  }
336
337  @Builder
338  MoreTabBuilder(index: number): void {
339    Button({ type: ButtonType.Normal, stateEffect: false }) {
340      Column() {
341        SymbolGlyph(PUBLIC_MORE)
342          .fontSize(IMAGE_SIZE)
343          .fontColor([this.theme.iconPrimaryColor])
344          .draggable(false)
345          .margin({ bottom: $r('sys.float.padding_level1') })
346        Text(this.moreText)
347          .fontColor(this.theme.fontPrimaryColor)
348          .fontSize($r('sys.float.ohos_id_text_size_caption'))
349          .fontWeight(FontWeight.Medium)
350          .maxLines(1)
351          .textOverflow({ overflow: TextOverflow.Ellipsis })
352          .textAlign(TextAlign.Center)
353          .focusable(true)
354          .focusOnTouch(true)
355      }
356      .width('100%')
357      .height('100%')
358      .justifyContent(FlexAlign.Center)
359      .padding({
360        start: LengthMetrics.resource($r('sys.float.padding_level2')),
361        end: LengthMetrics.resource($r('sys.float.padding_level2')),
362      })
363      .borderRadius($r('sys.float.ohos_id_corner_radius_clicked'))
364    }
365    .accessibilityGroup(true)
366    .focusable(true)
367    .focusOnTouch(true)
368    .focusBox({
369      margin: LengthMetrics.vp(FOCUS_BOX_MARGIN),
370      strokeWidth: LengthMetrics.vp(FOCUS_BOX_BORDER_WIDTH),
371      strokeColor: ColorMetrics.resourceColor($r('sys.color.ohos_id_color_focused_outline'))
372    })
373    .width('100%')
374    .height('100%')
375    .bindMenu(this.menuContent, { placement: Placement.TopRight, offset: { x: -12, y: -10 } })
376    .borderRadius($r('sys.float.ohos_id_corner_radius_clicked'))
377    .backgroundColor(this.toolBarList[index].backgroundColor)
378    .onHover((isHover: boolean) => {
379      if (isHover) {
380        this.toolBarList[index].backgroundColor = $r('sys.color.ohos_id_color_hover');
381      } else {
382        this.toolBarList[index].backgroundColor = Color.Transparent;
383      }
384    })
385    .stateStyles({
386      pressed: {
387        .backgroundColor((!this.toolBarModifier?.stateEffectValue) ?
388        this.toolBarList[index].backgroundColor : $r('sys.color.ohos_id_color_click_effect'))
389      }
390    })
391    .gestureModifier(this.getItemGestureModifier(this.moreItem, index))
392  }
393
394  @Builder
395  TabBuilder(index: number): void {
396    Button({ type: ButtonType.Normal, stateEffect: false }) {
397      Column() {
398        if (this.toolBarList[index]?.symbol) {
399          SymbolGlyph()
400            .fontSize(IMAGE_SIZE)
401            .symbolEffect(new SymbolEffect(), false)
402            .attributeModifier(this.getToolBarSymbolModifier(index))
403            .margin({ bottom: $r('sys.float.padding_level1') })
404        } else if (Util.isSymbolResource(this.toolBarList[index]?.image?.src)) {
405          SymbolGlyph(this.toolBarList[index]?.image?.src as Resource)
406            .fontSize(IMAGE_SIZE)
407            .fontColor([this.getIconColor(index)])
408            .margin({ bottom: $r('sys.float.padding_level1') })
409        } else {
410          Image(this.toolBarList[index]?.image?.src)
411            .width(IMAGE_SIZE)
412            .height(IMAGE_SIZE)
413            .fillColor(this.getIconColor(index))
414            .margin({ bottom: $r('sys.float.padding_level1') })
415            .objectFit(ImageFit.Contain)
416            .draggable(false)
417        }
418        Text(this.toolBarList[index]?.content.text)
419          .fontColor(this.getTextColor(index))
420          .fontSize($r('sys.float.ohos_id_text_size_caption'))
421          .maxFontSize($r('sys.float.ohos_id_text_size_caption'))
422          .minFontSize(9)
423          .fontWeight(FontWeight.Medium)
424          .maxLines(1)
425          .textOverflow({ overflow: TextOverflow.Ellipsis })
426          .textAlign(TextAlign.Center)
427          .focusable(!(this.toolBarList[index]?.state === ToolBarV2ItemState.DISABLE))
428          .focusOnTouch(!(this.toolBarList[index]?.state === ToolBarV2ItemState.DISABLE))
429      }
430      .justifyContent(FlexAlign.Center)
431      .width('100%')
432      .height('100%')
433      .borderRadius($r('sys.float.ohos_id_corner_radius_clicked'))
434      .padding({
435        start: LengthMetrics.resource($r('sys.float.padding_level2')),
436        end: LengthMetrics.resource($r('sys.float.padding_level2')),
437      })
438    }
439    .accessibilityGroup(true)
440    .accessibilityText(this.toolBarList[index]?.accessibilityText as Resource ??
441      this.toolBarList[index]?.content?.text as Resource)
442    .accessibilityDescription(this.toolBarList[index]?.accessibilityDescription as string ?? '')
443    .accessibilityLevel(this.toolBarList[index]?.accessibilityLevel ?? 'auto')
444    .enabled(this.toolBarList[index]?.state !== ToolBarV2ItemState.DISABLE)
445    .width('100%')
446    .height('100%')
447    .borderRadius($r('sys.float.ohos_id_corner_radius_clicked'))
448    .focusable(!(this.toolBarList[index]?.state === ToolBarV2ItemState.DISABLE))
449    .focusOnTouch(!(this.toolBarList[index]?.state === ToolBarV2ItemState.DISABLE))
450    .focusBox({
451      margin: LengthMetrics.vp(FOCUS_BOX_MARGIN),
452      strokeWidth: LengthMetrics.vp(FOCUS_BOX_BORDER_WIDTH),
453      strokeColor: ColorMetrics.resourceColor($r('sys.color.ohos_id_color_focused_outline'))
454    })
455    .backgroundColor(this.toolBarList[index].backgroundColor)
456    .onHover((isHover: boolean) => {
457      if (isHover && this.toolBarList[index]?.state !== ToolBarV2ItemState.DISABLE) {
458        this.toolBarList[index].backgroundColor = $r('sys.color.ohos_id_color_hover');
459      } else {
460        this.toolBarList[index].backgroundColor = Color.Transparent;
461      }
462    })
463    .stateStyles({
464      pressed: {
465        .backgroundColor((this.toolBarList[index]?.state === ToolBarV2ItemState.DISABLE) ||
466          (!this.toolBarModifier?.stateEffectValue) ?
467        this.toolBarList[index].backgroundColor : $r('sys.color.ohos_id_color_click_effect'))
468      }
469    })
470    .onClick(() => {
471      this.clickEventAction(index);
472    })
473    .gestureModifier(this.getItemGestureModifier(this.toolBarList[index], index))
474  }
475
476  @Builder
477  itemCardDialogBuilder(item: ToolBarV2Item, index: number): void {
478    if (item.content && item.content.text) {
479      Column() {
480        if (item.symbol) {
481          SymbolGlyph()
482            .attributeModifier(this.getToolBarSymbolModifier(index))
483            .symbolEffect(new SymbolEffect(), false)
484            .fontColor([$r('sys.color.icon_primary')])
485            .fontSize(DIALOG_IMAGE_SIZE)
486            .margin({
487              top: $r('sys.float.padding_level24'),
488              bottom: $r('sys.float.padding_level8'),
489            })
490        } else if (Util.isSymbolResource(item.image?.src)) {
491          SymbolGlyph(item.image?.src as Resource)
492            .fontColor([$r('sys.color.icon_primary')])
493            .fontSize(DIALOG_IMAGE_SIZE)
494            .margin({
495              top: $r('sys.float.padding_level24'),
496              bottom: $r('sys.float.padding_level8'),
497            })
498        } else {
499          Image(item.image?.src)
500            .width(DIALOG_IMAGE_SIZE)
501            .height(DIALOG_IMAGE_SIZE)
502            .margin({
503              top: $r('sys.float.padding_level24'),
504              bottom: $r('sys.float.padding_level8'),
505            })
506            .fillColor($r('sys.color.icon_primary'))
507        }
508        Column() {
509          Text(item.content.text)
510            .fontSize(TEXT_TOOLBAR_DIALOG)
511            .textOverflow({ overflow: TextOverflow.Ellipsis })
512            .maxLines(this.itemCardTextMaxLine)
513            .width('100%')
514            .textAlign(TextAlign.Center)
515            .fontColor($r('sys.color.font_primary'))
516        }
517        .width('100%')
518        .padding({
519          left: $r('sys.float.padding_level4'),
520          right: $r('sys.float.padding_level4'),
521          bottom: $r('sys.float.padding_level12'),
522        })
523      }
524      .constraintSize({ minHeight: this.fontSize === MAX_FONT_SIZE ? MAX_DIALOG : MIN_DIALOG })
525    } else {
526      Column() {
527        if (item.symbol) {
528          SymbolGlyph()
529            .attributeModifier(this.getToolBarSymbolModifier(index))
530            .symbolEffect(new SymbolEffect(), false)
531            .fontColor([$r('sys.color.icon_primary')])
532            .fontSize(DIALOG_IMAGE_SIZE)
533        } else if (Util.isSymbolResource(item.image?.src)) {
534          SymbolGlyph(item.image?.src as Resource)
535            .fontColor([$r('sys.color.icon_primary')])
536            .fontSize(DIALOG_IMAGE_SIZE)
537        } else {
538          Image(item.image?.src)
539            .width(DIALOG_IMAGE_SIZE)
540            .height(DIALOG_IMAGE_SIZE)
541            .fillColor($r('sys.color.icon_primary'))
542        }
543      }
544      .constraintSize({ minHeight: this.fontSize === MAX_FONT_SIZE ? MAX_DIALOG : MIN_DIALOG })
545      .justifyContent(FlexAlign.Center)
546    }
547  }
548
549  private getFontSizeScale(): number {
550    let context = this.getUIContext();
551    let fontScaleSystem = (context.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1;
552    if (!this.isFollowSystem) {
553      return 1;
554    } else {
555      return Math.min(fontScaleSystem, this.maxFontSizeScale);
556    }
557  }
558
559  private isItemActivating(index: number): boolean {
560    return this.localActivatedIndex === index && (this.toolBarList[index]?.state === ToolBarV2ItemState.ACTIVATE);
561  }
562
563  private getToolBarSymbolModifier(index: number): SymbolGlyphModifier | undefined {
564    if (this.isItemActivating(index)) {
565      return this.toolBarList[index]?.symbol?.activated;
566    }
567    return this.toolBarList[index]?.symbol?.normal;
568  }
569
570  private getIconColor(index: number): ResourceColor {
571    if (this.isItemActivating(index)) {
572      return this.toolBarList[index]?.image?.activatedColor?.color ?? this.theme.iconActivePrimaryColor;
573    }
574    return this.toolBarList[index]?.image?.color?.color ?? this.theme.iconPrimaryColor;
575  }
576
577  private getTextColor(index: number): ResourceColor {
578    if (this.isItemActivating(index)) {
579      return this.toolBarList[index]?.content.activatedColor?.color ?? this.theme.fontActivatedPrimaryColor;
580    }
581    return this.toolBarList[index]?.content.color?.color ?? this.theme.fontPrimaryColor;
582  }
583
584  private toLengthString(value?: LengthMetrics): string {
585    if (value === undefined) {
586      return '';
587    }
588    const length: number = value.value;
589    let lengthString: string = '';
590    switch (value.unit) {
591      case LengthUnit.PX:
592        lengthString = `${length}px`;
593        break;
594      case LengthUnit.FP:
595        lengthString = `${length}fp`;
596        break;
597      case LengthUnit.LPX:
598        lengthString = `${length}lpx`;
599        break;
600      case LengthUnit.PERCENT:
601        lengthString = `${length * 100}%`;
602        break;
603      case LengthUnit.VP:
604        lengthString = `${length}vp`;
605        break;
606      default:
607        lengthString = `${length}vp`;
608        break;
609    }
610    return lengthString;
611  }
612
613  private clickEventAction(index: number): void {
614    let toolbar = this.toolBarList[index];
615    if (toolbar.state === ToolBarV2ItemState.ACTIVATE) {
616      if (this.localActivatedIndex === index) {
617        this.localActivatedIndex = -1;
618      } else {
619        this.localActivatedIndex = index;
620      }
621    }
622    if (toolbar.state !== ToolBarV2ItemState.DISABLE) {
623      toolbar.action && toolbar.action(index);
624    }
625  }
626
627  private getItemGestureModifier(item: ToolBarV2Item, index: number): ButtonGestureModifier | undefined {
628    if (!item?.icon) {
629      return undefined;
630    }
631    let buttonGestureModifier: ButtonGestureModifier = new ButtonGestureModifier();
632    buttonGestureModifier.gestureCallBack = (event: UIGestureEvent) => {
633      if (this.fontSize >= ButtonGestureModifier.minFontSize) {
634        event.addGesture(
635          new LongPressGestureHandler({ repeat: false, duration: ButtonGestureModifier.longPressTime })
636            .onAction(() => {
637              promptAction.openCustomDialog({
638                builder: () => {
639                  this.itemCardDialogBuilder(item, index);
640                },
641                onWillAppear: () => {
642                  try {
643                    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
644                    let mainWindowStage = context.windowStage.getMainWindowSync();
645                    let properties: window.WindowProperties = mainWindowStage.getWindowProperties();
646                    if (px2vp(properties.windowRect.height) > SCREEN_WIDTH_BREAK_POINT) {
647                      this.itemCardTextMaxLine = VERTICAL_SCREEN_TEXT_MAX_LINES;
648                    } else {
649                      this.itemCardTextMaxLine = HORIZONTAL_SCREEN_TEXT_MAX_LINES;
650                    }
651                  } catch (err) {
652                    let code: number = (err as BusinessError)?.code;
653                    let message: string = (err as BusinessError)?.message;
654                    hilog.error(0x3900, 'Ace',
655                      `ToolBarV2 get window height failed, code: ${code}, message: ${message}`);
656                  }
657                },
658                maskColor: Color.Transparent,
659                isModal: true,
660                backgroundBlurStyle: BlurStyle.COMPONENT_ULTRA_THICK,
661                backgroundColor: Color.Transparent,
662                shadow: ShadowStyle.OUTER_DEFAULT_LG,
663                cornerRadius: $r('sys.float.corner_radius_level10'),
664                width: this.fontSize === MAX_FONT_SIZE ? MAX_DIALOG : MIN_DIALOG
665              }).then((dialogId: number) => {
666                this.itemDialogId = dialogId;
667              });
668            })
669            .onActionEnd(() => {
670              if (this.itemDialogId) {
671                promptAction.closeCustomDialog(this.itemDialogId);
672              }
673            })
674            .onActionCancel(() => {
675              if (this.itemDialogId) {
676                promptAction.closeCustomDialog(this.itemDialogId);
677              }
678            })
679        )
680        return;
681      }
682      event.clearGestures();
683    }
684    return buttonGestureModifier;
685  }
686
687  onMeasureSize(selfLayoutInfo: GeometryInfo, children: Measurable[], constraint: ConstraintSizeOptions): SizeResult {
688    this.fontSize = this.getFontSizeScale();
689    let sizeResult: SizeResult = { height: 0, width: 0 };
690    children.forEach((child) => {
691      let childMeasureResult: MeasureResult = child.measure(constraint);
692      sizeResult.width = childMeasureResult.width;
693      sizeResult.height = childMeasureResult.height;
694    });
695    return sizeResult;
696  }
697
698  build() {
699    Column() {
700      Divider()
701        .width('100%').height(1)
702        .attributeModifier(this.dividerModifier)
703      Row() {
704        ForEach(this.toolBarList, (item: ToolBarV2Item, index: number) => {
705          if (this.toolBarList.length <= TOOLBAR_MAX_LENGTH || index < TOOLBAR_MAX_LENGTH - 1) {
706            Row() {
707              this.TabBuilder(index);
708            }
709            .height('100%')
710            .flexShrink(1)
711          }
712        }, (item: ToolBarV2Item, index: number) => {
713          return `${this.getUniqueId}__${index}}`;
714        })
715        if (this.toolBarList.length > TOOLBAR_MAX_LENGTH) {
716          Row() {
717            this.MoreTabBuilder(TOOLBAR_MAX_LENGTH - 1);
718          }
719          .height('100%')
720          .flexShrink(1)
721        }
722      }
723      .justifyContent(FlexAlign.Center)
724      .constraintSize({
725        minHeight: this.toLengthString(this.toolBarModifier?.heightValue),
726        maxHeight: this.toLengthString(this.toolBarModifier?.heightValue),
727      })
728      .width('100%')
729      .height(this.toLengthString(this.toolBarModifier?.heightValue))
730      .padding({
731        start: this.toolBarList.length < TOOLBAR_MAX_LENGTH ?
732          this.toolBarModifier?.paddingValue : LengthMetrics.resource($r('sys.float.padding_level0')),
733        end: this.toolBarList.length < TOOLBAR_MAX_LENGTH ?
734          this.toolBarModifier?.paddingValue : LengthMetrics.resource($r('sys.float.padding_level0')),
735      })
736    }
737    .attributeModifier(this.toolBarModifier)
738  }
739}