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}