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 { TextModifier } from '@ohos.arkui.modifier'; 17import { Theme } from '@ohos.arkui.theme'; 18import { ColorMetrics, LengthMetrics, LengthUnit } from '@ohos.arkui.node'; 19import resourceManager from '@ohos.resourceManager'; 20import { BusinessError } from '@ohos.base'; 21import hilog from '@ohos.hilog'; 22import common from '@ohos.app.ability.common'; 23import { HashMap } from '@kit.ArkTS'; 24 25const INDEX_ZERO: number = 0; 26const INDEX_ONE: number = 1; 27const INDEX_TWO: number = 2; 28// 行数及整体高度 29const SINGLE_LINE_NUM: number = 1; 30const DOUBLE_LINE_NUM: number = 2; 31const SINGLE_LINE_HEIGHT: number = 56; 32const DOUBLE_LINE_HEIGHT: number = 72; 33// 资源数值 34const RESOURCE_TYPE_SYMBOL: number = 40000; 35// 左边尺寸常量 36const LEFT_ICON_SIZE: ResourceStr = '16vp'; 37const LEFT_ICON_SIZE_NUMBER: number = 16; 38const LEFT_TEXT_NUMBER: number = 8; 39// 右边尺寸常量 40const OPERATE_ITEM_LENGTH: number = 24; 41const ARROW_ICON_WIDTH: number = 12; 42const SINGLE_ICON_ZONE_SIZE: number = 28; 43const RIGHT_SINGLE_ICON_SIZE: ResourceStr = '24vp'; 44const PADDING_LEVEL_2: number = 4; 45const MAX_RIGHT_WIDTH: Length = '34%'; 46const MIN_FONT_SIZE: number = 1.75; 47const MIN_HOT_AREA_LENGTH: number = 40; 48const MULTI_ICON_REGION_WIDTH: number = 37; 49const ICON_REGION_X: number = -9; 50const ICON_REGION_Y: number = -6; 51const SINGLE_ICON_REGION_X: number = -12; 52const SINGLE_ICON_NUMBER: number = 1; 53const PADDING_LEFT: number = 2; 54 55export enum OperationType { 56 TEXT_ARROW = 0, 57 BUTTON = 1, 58 ICON_GROUP = 2, 59 LOADING = 3, 60} 61 62export declare class OperationOption { 63 public value: ResourceStr; 64 public action?: () => void; 65} 66 67export declare class SelectOptions { 68 public options: Array<SelectOption>; 69 public selected?: number; 70 public value?: string; 71 public onSelect?: (index: number, value?: string) => void; 72} 73 74export declare class SymbolOptions { 75 public fontSize?: Length; 76 public fontColor?: Array<ResourceColor>; 77 public fontWeight?: number | FontWeight | string; 78 public effectStrategy?: SymbolEffectStrategy; 79 public renderingStrategy?: SymbolRenderingStrategy; 80} 81 82class IconOptions { 83 public icon?: Resource; 84 public symbolicIconOption?: SymbolOptions | null; 85} 86 87class ContentIconOption { 88 public content?: ResourceStr; 89 public subContent?: ResourceStr; 90 public iconOptions?: IconOptions; 91 public action?: () => void; 92} 93 94class FontStyle { 95 public maxLines: number = 0; 96 public fontWeight: number = 0; 97 public fontColor?: ResourceColor; 98 public alignment?: Alignment; 99} 100 101class SubHeaderTheme { 102 public fontPrimaryColor: ResourceColor = $r('sys.color.font_primary'); 103 public fontSecondaryColor: ResourceColor = $r('sys.color.font_secondary'); 104 public fontButtonColor: ResourceColor = $r('sys.color.font_emphasize'); 105 public iconArrowColor: ResourceColor = $r('sys.color.icon_tertiary'); 106 public textArrowHoverBgColor: ResourceColor = $r('sys.color.interactive_hover'); 107 public borderFocusColor: ResourceColor = $r('sys.color.interactive_focus'); 108 public leftIconColor: ResourceColor = $r('sys.color.icon_secondary'); 109 public rightIconColor: ResourceColor = $r('sys.color.icon_primary'); 110} 111 112@Extend(Text) 113function secondaryTitleStyles(fontStyle: FontStyle) { 114 .fontSize(`${getResourceValue('sys.float.Subtitle_S')}fp`) 115 .fontColor(fontStyle?.fontColor ?? $r('sys.color.font_secondary')) 116 .fontWeight(fontStyle?.fontWeight) 117 .maxLines(fontStyle?.maxLines) 118 .textOverflow({ overflow: TextOverflow.Ellipsis }) 119 .align(fontStyle?.alignment) 120} 121 122@Extend(Text) 123function primaryTitleStyles(fontStyle: FontStyle) { 124 .fontSize(`${getResourceValue('sys.float.Subtitle_L')}fp`) 125 .fontColor(fontStyle?.fontColor ?? $r('sys.color.font_primary')) 126 .fontWeight(fontStyle?.fontWeight) 127 .maxLines(fontStyle?.maxLines) 128 .textOverflow({ overflow: TextOverflow.Ellipsis }) 129 .align(fontStyle?.alignment) 130} 131 132@Styles 133function pressedStyle() { 134 .backgroundColor($r('sys.color.interactive_pressed')) 135} 136 137@Styles 138function disabledStyle() { 139 .opacity(getResourceValue('sys.float.interactive_disable')) 140} 141 142class SubHeaderModifier implements AttributeModifier<RowAttribute> { 143 public isAgeing: boolean = false 144 145 applyNormalAttribute(instance: RowAttribute): void { 146 if (this.isAgeing) { 147 instance.width('100%') 148 } else { 149 } 150 } 151} 152 153interface ResourceInfo { 154 resourceId: number, 155 defaultValue: number, 156 resourceValue?: number, 157} 158 159const RESOURCE_CACHE_MAP: HashMap<string, ResourceInfo> = new HashMap(); 160// padding_level0: 125830919, 0 161RESOURCE_CACHE_MAP.set('sys.float.padding_level0', { resourceId: 125830919, defaultValue: 0 }); 162// padding_level1: 125830920, 2 163RESOURCE_CACHE_MAP.set('sys.float.padding_level1', { resourceId: 125830920, defaultValue: 2 }); 164// padding_level2: 125830921, 4 165RESOURCE_CACHE_MAP.set('sys.float.padding_level2', { resourceId: 125830921, defaultValue: 4 }); 166// padding_level3: 125830922, 6 167RESOURCE_CACHE_MAP.set('sys.float.padding_level3', { resourceId: 125830922, defaultValue: 6 }); 168// padding_level4: 125830923, 8 169RESOURCE_CACHE_MAP.set('sys.float.padding_level4', { resourceId: 125830923, defaultValue: 8 }); 170// padding_level6: 125830925, 12 171RESOURCE_CACHE_MAP.set('sys.float.padding_level6', { resourceId: 125830925, defaultValue: 12 }); 172// padding_level8: 125830927, 16 173RESOURCE_CACHE_MAP.set('sys.float.padding_level8', { resourceId: 125830927, defaultValue: 16 }); 174// margin_left: 125830936, 16 175RESOURCE_CACHE_MAP.set('sys.float.margin_left', { resourceId: 125830936, defaultValue: 16 }); 176// margin_right: 125830937, 16 177RESOURCE_CACHE_MAP.set('sys.float.margin_right', { resourceId: 125830937, defaultValue: 16 }); 178// outline_extra_larger: 125830951, 2 179RESOURCE_CACHE_MAP.set('sys.float.outline_extra_larger', { resourceId: 125830951, defaultValue: 2 }); 180// corner_radius_level4: 125830909, 8 181RESOURCE_CACHE_MAP.set('sys.float.corner_radius_level4', { resourceId: 125830909, defaultValue: 8 }); 182// Subtitle_S: 125830969, 14 183RESOURCE_CACHE_MAP.set('sys.float.Subtitle_S', { resourceId: 125830969, defaultValue: 14 }); 184// Subtitle_L: 125830967, 18 185RESOURCE_CACHE_MAP.set('sys.float.Subtitle_L', { resourceId: 125830967, defaultValue: 18 }); 186// Body_L: 125830970, 16 187RESOURCE_CACHE_MAP.set('sys.float.Body_L', { resourceId: 125830970, defaultValue: 16 }); 188// interactive_disable: 125831067, 0.4 189RESOURCE_CACHE_MAP.set('sys.float.interactive_disable', { resourceId: 125831067, defaultValue: 0.4 }); 190 191@Component 192export struct SubHeader { 193 @Prop icon: Resource | null = null; 194 iconSymbolOptions?: SymbolOptions | null = null; 195 @Prop primaryTitle: string | null = null; 196 @State primaryTitleModifier: TextModifier = new TextModifier(); 197 @Prop secondaryTitle: string | null = null; 198 @State secondaryTitleModifier: TextModifier = new TextModifier(); 199 @State subHeaderModifier: SubHeaderModifier = new SubHeaderModifier(); 200 select: SelectOptions | null = null; 201 @Prop operationType: OperationType = OperationType.BUTTON; 202 operationItem: Array<OperationOption> | null = null; 203 operationSymbolOptions?: Array<SymbolOptions> | null = null; 204 @State fontSize: number = 1; 205 @State ageing: boolean = true; 206 // 内部变量 207 @State textArrowBgColor: ResourceColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 208 @State buttonBgColor: ResourceColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 209 @State selectedIndex: number | Resource | undefined = -1; 210 @State selectValue: ResourceStr | undefined = ''; 211 @BuilderParam titleBuilder?: () => void; 212 @Prop contentMargin: LocalizedMargin; 213 @Prop contentPadding: LocalizedPadding; 214 subHeaderMargin: LocalizedMargin = { 215 start: LengthMetrics.vp(getResourceValue('sys.float.margin_left')), 216 end: LengthMetrics.vp(getResourceValue('sys.float.margin_right')), 217 }; 218 @Provide subHeaderTheme: SubHeaderTheme = new SubHeaderTheme(); 219 isFollowingSystemFontScale: boolean = false; 220 appMaxFontScale: number = 3.2; 221 222 onWillApplyTheme(theme: Theme) { 223 this.subHeaderTheme.fontPrimaryColor = theme.colors.fontPrimary; 224 this.subHeaderTheme.fontSecondaryColor = theme.colors.fontSecondary; 225 this.subHeaderTheme.fontButtonColor = theme.colors.fontEmphasize; 226 this.subHeaderTheme.iconArrowColor = theme.colors.iconTertiary; 227 this.subHeaderTheme.textArrowHoverBgColor = theme.colors.interactiveHover; 228 this.subHeaderTheme.borderFocusColor = theme.colors.interactiveFocus; 229 this.subHeaderTheme.leftIconColor = theme.colors.iconSecondary; 230 this.subHeaderTheme.rightIconColor = theme.colors.iconPrimary; 231 } 232 233 @Styles 234 private commonContentPadding() { 235 .padding({ 236 end: LengthMetrics.vp(getResourceValue('sys.float.padding_level0')), 237 top: this.fontSize >= MIN_FONT_SIZE ? LengthMetrics.vp(getResourceValue('sys.float.padding_level0')) 238 : LengthMetrics.vp(getResourceValue('sys.float.padding_level4')), 239 bottom: this.fontSize >= MIN_FONT_SIZE ? LengthMetrics.vp(getResourceValue('sys.float.padding_level0')) 240 : LengthMetrics.vp(getResourceValue('sys.float.padding_level4')), 241 }) 242 } 243 244 @Styles 245 private commonListPadding() { 246 .padding({ 247 end: LengthMetrics.vp(getResourceValue('sys.float.padding_level6')), 248 top: this.fontSize >= MIN_FONT_SIZE ? LengthMetrics.vp(getResourceValue('sys.float.padding_level0')) 249 : LengthMetrics.vp(getResourceValue('sys.float.padding_level4')), 250 bottom: this.fontSize >= MIN_FONT_SIZE ? LengthMetrics.vp(getResourceValue('sys.float.padding_level0')) 251 : LengthMetrics.vp(getResourceValue('sys.float.padding_level4')), 252 }) 253 } 254 255 @Styles 256 private rightAreaClickEvent() { 257 .onClick(() => { 258 if ((this.operationType === OperationType.TEXT_ARROW || this.operationType === OperationType.BUTTON) && 259 this.operationItem && this.operationItem.length > 0 && this.operationItem[0].action) { 260 this.operationItem[0].action(); 261 } 262 }) 263 .onTouch((event) => { 264 if (event.type === TouchType.Down) { 265 if (this.operationType === OperationType.TEXT_ARROW) { 266 this.textArrowBgColor = $r('sys.color.interactive_pressed'); 267 } 268 if (this.operationType === OperationType.BUTTON) { 269 this.buttonBgColor = $r('sys.color.interactive_pressed'); 270 } 271 } 272 if (event.type === TouchType.Up || event.type === TouchType.Cancel) { 273 if (this.operationType === OperationType.TEXT_ARROW) { 274 this.textArrowBgColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 275 } 276 if (this.operationType === OperationType.BUTTON) { 277 this.buttonBgColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 278 } 279 } 280 }) 281 } 282 283 updateFontScale(): number { 284 try { 285 let uiContext: UIContext = this.getUIContext(); 286 let systemFontScale = (uiContext.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1; 287 if (!this.isFollowingSystemFontScale) { 288 return 1; 289 } 290 return Math.min(systemFontScale, this.appMaxFontScale); 291 } catch (exception) { 292 let code: number = (exception as BusinessError).code; 293 let message: string = (exception as BusinessError).message; 294 hilog.error(0x3900, 'Ace', `Faild to init fontsizescale info,cause, code: ${code}, message: ${message}`); 295 return 1; 296 } 297 } 298 299 async aboutToAppear(): Promise<void> { 300 let uiContext: UIContext = this.getUIContext(); 301 this.isFollowingSystemFontScale = uiContext.isFollowingSystemFontScale(); 302 this.appMaxFontScale = uiContext.getMaxFontScale(); 303 this.fontSize = this.updateFontScale(); 304 if (this.isSuitableAging()) { 305 this.ageing = true; 306 this.subHeaderModifier.isAgeing = this.ageing; 307 } else { 308 this.ageing = false; 309 this.subHeaderModifier.isAgeing = this.ageing; 310 } 311 if (this.select) { 312 this.selectedIndex = this.select.selected; 313 this.selectValue = this.select.value; 314 } 315 } 316 317 private isSuitableAging(): boolean | null { 318 return (this.fontSize >= MIN_FONT_SIZE) && ((this.operationType === OperationType.TEXT_ARROW) || 319 this.operationType === OperationType.BUTTON) && this.operationItem && 320 (this.operationItem?.length > 0) && this.operationItem[0].value !== ''; 321 } 322 323 private isLeftAreaAccessibilityGroup(): boolean { 324 if (this.titleBuilder || this.secondaryTitle) { 325 return true; 326 } 327 if (this.select) { 328 return false; 329 } 330 return true; 331 } 332 333 build() { 334 if (this.isSuitableAging()) { 335 Column() { 336 Row() { 337 this.leftArea(); 338 } 339 .margin({ 340 top: LengthMetrics.vp(getResourceValue('sys.float.padding_level8')), 341 bottom: LengthMetrics.vp(getResourceValue('sys.float.padding_level1')), 342 }) 343 .padding({ 344 start: this.contentMargin ? this.contentMargin.start : 345 LengthMetrics.vp(getResourceValue('sys.float.margin_left')), 346 end: this.contentMargin ? this.contentMargin.end : 347 LengthMetrics.vp(getResourceValue('sys.float.margin_right')), 348 }) 349 .width('100%') 350 .accessibilityGroup(this.isLeftAreaAccessibilityGroup()) 351 352 if (this.isRightAreaExists()) { 353 this.rightAreaParentAging(); 354 } 355 } 356 .constraintSize({ minHeight: this.getMinHeight() }) 357 .padding(this.getAreaPadding()) 358 .alignItems(HorizontalAlign.Start) 359 } else { 360 Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.End }) { 361 Row() { 362 this.leftArea(); 363 } 364 .margin({ 365 top: this.fontSize >= MIN_FONT_SIZE ? getResourceValue('sys.float.padding_level8') : '', 366 bottom: this.fontSize >= MIN_FONT_SIZE ? getResourceValue('sys.float.padding_level4') : '', 367 }) 368 .width('100%') 369 .flexShrink(1) 370 .accessibilityGroup(this.isLeftAreaAccessibilityGroup()) 371 372 if (this.isRightAreaExists()) { 373 this.rightAreaParent(); 374 } 375 } 376 .constraintSize({ minHeight: this.getMinHeight() }) 377 .margin(this.contentMargin ?? this.subHeaderMargin) 378 .padding(this.getAreaPadding()) 379 } 380 } 381 382 private isRightAreaExists(): boolean { 383 if (this.operationItem && this.operationItem.length > 0) { 384 return true; 385 } 386 if (this.operationType === OperationType.LOADING) { 387 return true; 388 } 389 return false; 390 } 391 392 @Styles 393 private rightAreaParentAgingStyles() { 394 .margin({ 395 bottom: getResourceValue('sys.float.padding_level4'), 396 }) 397 .padding({ 398 // 'sys.float.margin_left' id,value: 16vp 399 start: LengthMetrics.vp((this.contentMargin ? (this.contentMargin.start ? this.contentMargin.start.value : 0) : 400 getResourceValue('sys.float.margin_left')) - PADDING_LEFT), 401 // 'sys.float.margin_right' id,value: 16vp 402 end: this.contentMargin ? this.contentMargin.end : 403 LengthMetrics.vp(getResourceValue('sys.float.margin_right')), 404 }) 405 .accessibilityLevel(this.operationType === OperationType.BUTTON || this.operationType === OperationType.TEXT_ARROW ? 406 'yes' : 'no') 407 } 408 409 private getRightAreaAccessibilityText(): string { 410 if (this.operationType !== OperationType.TEXT_ARROW || !this.operationItem || this.operationItem.length <= 0) { 411 return ''; 412 } 413 if (this.operationItem[0].value.toString().length <= 0) { 414 // 播报:更多、more等, 使用的字段是:sys.string.ohos_toolbar_more 415 return Util.getStringByResource(125833704, ''); 416 } 417 return ''; 418 } 419 420 @Builder 421 rightAreaParentAging(): void { 422 if (this.operationType === OperationType.BUTTON || this.operationType === OperationType.TEXT_ARROW) { 423 Button({ type: ButtonType.Normal, stateEffect: false }) { 424 this.rightArea(); 425 } 426 .focusable(this.operationItem ? true : false) 427 .align(Alignment.Start) 428 .rightAreaClickEvent() 429 .rightAreaParentAgingStyles() 430 .backgroundColor($r('sys.color.ohos_id_color_sub_background_transparent')) 431 .hoverEffect(HoverEffect.None) 432 .accessibilityText(this.getRightAreaAccessibilityText()) 433 } else { 434 Row() { 435 this.rightArea(); 436 } 437 .focusable(this.operationItem && this.operationType !== OperationType.LOADING ? true : false) 438 .justifyContent(FlexAlign.Start) 439 .rightAreaClickEvent() 440 .rightAreaParentAgingStyles() 441 } 442 } 443 444 @Styles 445 private rightAreaParentStyles() { 446 .constraintSize({ 447 maxWidth: this.getRightAreaMaxWidth(), 448 minWidth: this.getRightAreaMinWidth(), 449 minHeight: MIN_HOT_AREA_LENGTH, 450 }) 451 .flexShrink(0) 452 .accessibilityLevel(this.operationType === OperationType.BUTTON || this.operationType === OperationType.TEXT_ARROW ? 453 'yes' : 'no') 454 } 455 456 @Builder 457 rightAreaParent(): void { 458 if (this.operationType === OperationType.BUTTON || this.operationType === OperationType.TEXT_ARROW) { 459 Button({ type: ButtonType.Normal, stateEffect: false }) { 460 this.rightArea(); 461 } 462 .focusable(this.operationItem ? true : false) 463 .margin(INDEX_ZERO) 464 .padding(INDEX_ZERO) 465 .align(Alignment.BottomEnd) 466 .rightAreaClickEvent() 467 .rightAreaParentStyles() 468 .hoverEffect(HoverEffect.None) 469 .backgroundColor($r('sys.color.ohos_id_color_sub_background_transparent')) 470 .accessibilityText(this.getRightAreaAccessibilityText()) 471 } else { 472 Row() { 473 this.rightArea(); 474 } 475 .focusable(this.operationItem && this.operationType !== OperationType.LOADING ? true : false) 476 .justifyContent(FlexAlign.End) 477 .alignItems(VerticalAlign.Bottom) 478 .rightAreaClickEvent() 479 .rightAreaParentStyles() 480 } 481 } 482 483 onMeasureSize(selfLayoutInfo: GeometryInfo, children: Measurable[], constraint: ConstraintSizeOptions): SizeResult { 484 let result: SizeResult = { width: selfLayoutInfo.width, height: selfLayoutInfo.height }; 485 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; 486 this.fontSize = this.updateFontScale(); 487 if (this.isSuitableAging()) { 488 this.ageing = true; 489 this.subHeaderModifier.isAgeing = this.ageing; 490 } else { 491 this.ageing = false; 492 this.subHeaderModifier.isAgeing = this.ageing; 493 } 494 children.forEach((child) => { 495 constraint.minHeight = Math.min(Number(this.getMinHeight()), Number(constraint.maxHeight)); 496 result.height = child.measure(constraint).height; 497 result.width = Number(constraint.maxWidth); 498 }) 499 return result; 500 } 501 502 onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Layoutable[], constraint: ConstraintSizeOptions): void { 503 children.forEach((child) => { 504 child.layout({ x: 0, y: 0 }); 505 }) 506 } 507 508 private getRightAreaMaxWidth(): Length { 509 if (this.operationType === OperationType.ICON_GROUP && (this.operationItem && this.operationItem.length > 0)) { 510 return '100%'; 511 } 512 return MAX_RIGHT_WIDTH; 513 } 514 515 private getRightAreaMinWidth(): Length { 516 if (this.operationItem && this.operationItem.length > 0) { 517 return MIN_HOT_AREA_LENGTH; 518 } 519 return 0; 520 } 521 522 private getMinHeight(): Length { 523 if (this.secondaryTitle && this.icon) { 524 return SINGLE_LINE_HEIGHT; 525 } else if (this.secondaryTitle && this.primaryTitle) { 526 return DOUBLE_LINE_HEIGHT; 527 } 528 return SINGLE_LINE_HEIGHT; 529 } 530 531 private getTextArrowPaddingLeft(): LengthMetrics { 532 if (this.operationItem && this.operationItem.length > 0 && this.operationItem[0].value) { 533 return LengthMetrics.vp(getResourceValue('sys.float.padding_level1')); 534 } 535 return LengthMetrics.vp(getResourceValue('sys.float.padding_level0')); 536 } 537 538 private getTextArrowMarginRight(): LengthMetrics { 539 if (this.operationItem && this.operationItem.length > 0 && this.operationItem[0].value) { 540 return LengthMetrics.vp(PADDING_LEVEL_2 + ARROW_ICON_WIDTH); 541 } 542 return LengthMetrics.vp(ARROW_ICON_WIDTH); 543 } 544 545 private getAreaPadding(): LocalizedPadding { 546 if (this.contentPadding) { 547 return this.contentPadding; 548 } 549 let padding: LocalizedPadding = {}; 550 if (!this.titleBuilder && ((this.secondaryTitle && this.icon) || 551 (!this.primaryTitle && this.secondaryTitle))) { 552 padding = { 553 start: LengthMetrics.vp(getResourceValue('sys.float.padding_level6')), 554 end: LengthMetrics.vp(getResourceValue('sys.float.padding_level6')), 555 } 556 } 557 return padding; 558 } 559 560 @Builder 561 leftArea(): void { 562 if (this.titleBuilder) { 563 this.titleBuilder(); 564 } else if (this.secondaryTitle && this.icon) { 565 this.IconSecondaryTitleStyle({ 566 content: this.secondaryTitle, 567 iconOptions: { 568 icon: this.icon, 569 symbolicIconOption: this.iconSymbolOptions, 570 }, 571 }); 572 } else if (this.secondaryTitle && this.primaryTitle) { 573 this.SubTitleStyle({ content: this.primaryTitle, subContent: this.secondaryTitle }); 574 } else if (this.secondaryTitle) { 575 this.SecondTitleStyle({ content: this.secondaryTitle }); 576 } else if (this.select) { 577 this.SelectStyle(this.select); 578 } else if (this.primaryTitle) { 579 this.PrimaryTitleStyle({ content: this.primaryTitle }); 580 } else { 581 // 其他不支持场景 582 this.dummyFunction(); 583 } 584 } 585 586 @Builder 587 rightArea(): void { 588 if (this.operationType === OperationType.BUTTON && (this.operationItem && this.operationItem.length > 0)) { 589 this.ButtonStyle(this.operationItem[0]); 590 } 591 if (this.operationType === OperationType.TEXT_ARROW && (this.operationItem && this.operationItem.length > 0)) { 592 this.TextArrowStyle(this.operationItem[0]); 593 } 594 if (this.operationType === OperationType.ICON_GROUP && (this.operationItem && this.operationItem.length > 0)) { 595 this.IconGroupStyle(this.operationItem); 596 } 597 if (this.operationType === OperationType.LOADING) { 598 this.LoadingProcessStyle(); 599 } 600 } 601 602 @Builder 603 IconSecondaryTitleStyle($$: ContentIconOption): void { 604 Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) { 605 if (Util.isSymbolResource($$.iconOptions?.icon)) { 606 SymbolGlyph($$.iconOptions?.icon) 607 .fontSize($$.iconOptions?.symbolicIconOption?.fontSize ? 608 Util.symbolFontSize($$.iconOptions?.symbolicIconOption?.fontSize) : LEFT_ICON_SIZE) 609 .fontColor($$.iconOptions?.symbolicIconOption?.fontColor ?? [this.subHeaderTheme.leftIconColor]) 610 .fontWeight($$.iconOptions?.symbolicIconOption?.fontWeight) 611 .renderingStrategy($$.iconOptions?.symbolicIconOption?.renderingStrategy) 612 .effectStrategy($$.iconOptions?.symbolicIconOption?.effectStrategy) 613 .margin({ end: LengthMetrics.vp(getResourceValue('sys.float.padding_level4')) }) 614 .flexShrink(0) 615 } else { 616 Image($$.iconOptions?.icon) 617 .fillColor(this.subHeaderTheme.leftIconColor) 618 .width(LEFT_ICON_SIZE) 619 .height(LEFT_ICON_SIZE) 620 .margin({ end: LengthMetrics.vp(getResourceValue('sys.float.padding_level4')) }) 621 .draggable(false) 622 .flexShrink(0) 623 } 624 Text($$.content) 625 .secondaryTitleStyles({ 626 maxLines: DOUBLE_LINE_NUM, 627 fontWeight: FontWeight.Medium, 628 alignment: Alignment.Start, 629 fontColor: this.subHeaderTheme.fontSecondaryColor, 630 }) 631 .attributeModifier(this.secondaryTitleModifier) 632 .flexShrink(1) 633 } 634 .commonListPadding() 635 } 636 637 @Builder 638 SubTitleStyle($$: ContentIconOption): void { 639 Column() { 640 Text($$.content) 641 .primaryTitleStyles({ 642 fontWeight: FontWeight.Bold, 643 maxLines: DOUBLE_LINE_NUM, 644 alignment: Alignment.Start, 645 fontColor: this.subHeaderTheme.fontPrimaryColor, 646 }) 647 .attributeModifier(this.primaryTitleModifier) 648 Text($$.subContent) 649 .secondaryTitleStyles({ 650 maxLines: DOUBLE_LINE_NUM, 651 fontWeight: FontWeight.Regular, 652 alignment: Alignment.Start, 653 fontColor: this.subHeaderTheme.fontSecondaryColor, 654 }) 655 .margin({ 656 top: getResourceValue('sys.float.padding_level1'), 657 }) 658 .attributeModifier(this.secondaryTitleModifier) 659 } 660 .width('100%') 661 .commonContentPadding() 662 .alignItems(HorizontalAlign.Start) 663 } 664 665 @Builder 666 SecondTitleStyle($$: ContentIconOption): void { 667 Text($$.content) 668 .secondaryTitleStyles({ 669 maxLines: DOUBLE_LINE_NUM, 670 fontWeight: FontWeight.Medium, 671 alignment: Alignment.Start, 672 fontColor: this.subHeaderTheme.fontSecondaryColor, 673 }) 674 .attributeModifier(this.secondaryTitleModifier) 675 .commonListPadding() 676 } 677 678 @Builder 679 SelectStyle(selectParam: SelectOptions): void { 680 Select(selectParam.options) 681 .height('auto') 682 .width('auto') 683 .selected(this.selectedIndex) 684 .value(this.selectValue) 685 .onSelect((index: number, value?: string) => { 686 this.selectedIndex = index; 687 if (value) { 688 this.selectValue = value; 689 } 690 if (selectParam.onSelect) { 691 selectParam.onSelect(index, value); 692 } 693 }) 694 .font({ 695 size: `${getResourceValue('sys.float.Body_L')}fp`, 696 weight: FontWeight.Medium, 697 }) 698 } 699 700 @Builder 701 PrimaryTitleStyle($$: ContentIconOption): void { 702 Text($$.content) 703 .primaryTitleStyles({ 704 fontWeight: FontWeight.Bold, 705 maxLines: DOUBLE_LINE_NUM, 706 alignment: Alignment.Start, 707 fontColor: this.subHeaderTheme.fontPrimaryColor, 708 }) 709 .attributeModifier(this.primaryTitleModifier) 710 .commonContentPadding() 711 } 712 713 @Builder 714 ButtonStyle(button: OperationOption): void { 715 if (button) { 716 Button({ type: ButtonType.Normal, stateEffect: false }) { 717 Text(button.value) 718 .secondaryTitleStyles({ 719 fontWeight: FontWeight.Medium, 720 maxLines: DOUBLE_LINE_NUM, 721 fontColor: this.subHeaderTheme.fontButtonColor, 722 }) 723 .focusable(true) 724 } 725 .focusable(true) 726 .focusBox({ 727 margin: { value: INDEX_ZERO, unit: LengthUnit.VP }, 728 strokeColor: ColorMetrics.resourceColor(this.subHeaderTheme.borderFocusColor), 729 strokeWidth: LengthMetrics.vp(getResourceValue('sys.float.outline_extra_larger')), 730 }) 731 .padding({ 732 start: LengthMetrics.vp(getResourceValue('sys.float.padding_level1')), 733 end: LengthMetrics.vp(getResourceValue('sys.float.padding_level1')), 734 top: LengthMetrics.vp(getResourceValue('sys.float.padding_level2')), 735 bottom: LengthMetrics.vp(getResourceValue('sys.float.padding_level2')), 736 }) 737 .margin({ 738 start: this.ageing ? 739 LengthMetrics.vp(LengthMetrics.vp(getResourceValue('sys.float.padding_level0')).value + 740 this.leftIconMargin().value) : 741 LengthMetrics.vp(LengthMetrics.vp(getResourceValue('sys.float.padding_level4')).value + 742 this.leftIconMargin().value), 743 bottom: LengthMetrics.vp(this.ageing ? getResourceValue('sys.float.padding_level0') : 744 getResourceValue('sys.float.padding_level2')), 745 }) 746 .backgroundColor(this.buttonBgColor) 747 .constraintSize({ minHeight: OPERATE_ITEM_LENGTH }) 748 .align(Alignment.End) 749 .borderRadius(getResourceValue('sys.float.corner_radius_level4')) 750 .onHover((isHover: boolean) => { 751 if (isHover) { 752 this.buttonBgColor = this.subHeaderTheme.textArrowHoverBgColor; 753 } else { 754 this.buttonBgColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 755 } 756 }) 757 .stateStyles({ 758 pressed: pressedStyle, 759 disabled: disabledStyle, 760 }) 761 } 762 } 763 764 private leftIconMargin(): LengthMetrics { 765 if (this.titleBuilder) { 766 return LengthMetrics.vp(0); 767 } 768 if (this.icon && Util.isSymbolResource(this.icon)) { 769 return this.ageing ? LengthMetrics.vp((this.iconSymbolOptions?.fontSize ? 770 Util.numberToSize(this.iconSymbolOptions?.fontSize) : LEFT_ICON_SIZE_NUMBER) + 771 LEFT_TEXT_NUMBER) : LengthMetrics.vp(0); 772 } else { 773 return (this.ageing && this.icon) ? LengthMetrics.vp(LEFT_ICON_SIZE_NUMBER + 774 LEFT_TEXT_NUMBER) : LengthMetrics.vp(0); 775 } 776 } 777 778 @Builder 779 TextStyle(textArrow: OperationOption): void { 780 Row() { 781 if (textArrow) { 782 Text(textArrow.value) 783 .secondaryTitleStyles({ 784 maxLines: DOUBLE_LINE_NUM, 785 fontWeight: FontWeight.Regular, 786 alignment: Alignment.End, 787 fontColor: this.subHeaderTheme.fontSecondaryColor, 788 }) 789 .margin({ 790 end: this.getTextArrowMarginRight(), 791 }) 792 } 793 } 794 .attributeModifier(this.subHeaderModifier) 795 .alignItems(VerticalAlign.Center) 796 .focusable(true) 797 .constraintSize({ minHeight: OPERATE_ITEM_LENGTH }) 798 .padding({ 799 start: this.getTextArrowPaddingLeft(), 800 top: this.ageing ? LengthMetrics.vp(0) : LengthMetrics.vp(getResourceValue('sys.float.padding_level2')), 801 bottom: this.ageing ? LengthMetrics.vp(0) : LengthMetrics.vp(getResourceValue('sys.float.padding_level2')), 802 }) 803 } 804 805 @Builder 806 ArrowStyle(): void { 807 Row() { 808 Image($r('sys.media.ohos_ic_public_arrow_right')) 809 .fillColor(this.subHeaderTheme.iconArrowColor) 810 .width(ARROW_ICON_WIDTH) 811 .height(OPERATE_ITEM_LENGTH) 812 .focusable(true) 813 .draggable(false) 814 .matchTextDirection(true) 815 } 816 .justifyContent(FlexAlign.End) 817 } 818 819 @Builder 820 TextArrowStyle(textArrow: OperationOption): void { 821 if (textArrow && textArrow.value && textArrow.value.toString().length > 0) { 822 Stack() { 823 Button({ type: ButtonType.Normal, stateEffect: false }) { 824 TextArrowLayout() { 825 ForEach([INDEX_ZERO, INDEX_ONE], (index: number) => { 826 if (index === INDEX_ZERO) { 827 this.TextStyle(textArrow); 828 } else { 829 this.ArrowStyle(); 830 } 831 }); 832 } 833 } 834 .padding(INDEX_ZERO) 835 .margin({ start: this.leftIconMargin() }) 836 .backgroundColor(this.textArrowBgColor) 837 .focusBox({ 838 margin: { value: INDEX_ZERO, unit: LengthUnit.VP }, 839 strokeColor: ColorMetrics.resourceColor(this.subHeaderTheme.borderFocusColor), 840 strokeWidth: LengthMetrics.vp(getResourceValue('sys.float.outline_extra_larger')), 841 }) 842 .borderRadius(getResourceValue('sys.float.corner_radius_level4')) 843 .stateStyles({ 844 pressed: pressedStyle, 845 disabled: disabledStyle, 846 }) 847 .onHover((isHover: boolean) => { 848 if (isHover) { 849 this.textArrowBgColor = this.subHeaderTheme.textArrowHoverBgColor; 850 } else { 851 this.textArrowBgColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 852 } 853 }) 854 } 855 .focusable(true) 856 .align(this.ageing ? Alignment.Start : Alignment.End) 857 .margin({ 858 start: LengthMetrics.vp(this.ageing ? getResourceValue('sys.float.padding_level0') : 859 getResourceValue('sys.float.padding_level4')), 860 bottom: LengthMetrics.vp(this.ageing ? getResourceValue('sys.float.padding_level0') : 861 getResourceValue('sys.float.padding_level2')), 862 }) 863 } else { 864 Row() { 865 Button({ type: ButtonType.Normal, stateEffect: false }) { 866 Image($r('sys.media.ohos_ic_public_arrow_right')) 867 .fillColor(this.subHeaderTheme.iconArrowColor) 868 .width(ARROW_ICON_WIDTH) 869 .height(OPERATE_ITEM_LENGTH) 870 .focusable(true) 871 .draggable(false) 872 .matchTextDirection(true) 873 } 874 .width(ARROW_ICON_WIDTH) 875 .height(OPERATE_ITEM_LENGTH) 876 .backgroundColor(this.textArrowBgColor) 877 .focusBox({ 878 margin: { value: INDEX_ZERO, unit: LengthUnit.VP }, 879 strokeColor: ColorMetrics.resourceColor(this.subHeaderTheme.borderFocusColor), 880 strokeWidth: LengthMetrics.vp(getResourceValue('sys.float.outline_extra_larger')), 881 }) 882 .borderRadius(getResourceValue('sys.float.corner_radius_level4')) 883 .stateStyles({ 884 pressed: pressedStyle, 885 disabled: disabledStyle, 886 }) 887 .onHover((isHover: boolean) => { 888 if (isHover) { 889 this.textArrowBgColor = this.subHeaderTheme.textArrowHoverBgColor; 890 } else { 891 this.textArrowBgColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 892 } 893 }) 894 .focusable(true) 895 .margin({ 896 start: LengthMetrics.vp(this.ageing ? getResourceValue('sys.float.padding_level0') : 897 getResourceValue('sys.float.padding_level4')), 898 bottom: LengthMetrics.vp(this.ageing ? getResourceValue('sys.float.padding_level0') : 899 getResourceValue('sys.float.padding_level2')), 900 }) 901 } 902 .focusable(true) 903 .constraintSize({ minWidth: this.getRightAreaMinWidth() }) 904 .justifyContent(FlexAlign.End) 905 } 906 } 907 908 @Builder 909 IconGroupStyle(operationItem: Array<OperationOption>): void { 910 Row() { 911 ForEach(operationItem, (item: OperationOption, index: number) => { 912 if (Util.isResourceType(item.value)) { 913 if (index <= INDEX_TWO) { 914 SingleIconStyle({ 915 item: { 916 iconOptions: { 917 icon: item.value as Resource, 918 symbolicIconOption: this.operationSymbolOptions && this.operationSymbolOptions.length > index ? 919 this.operationSymbolOptions[index] : null, 920 }, 921 action: item.action, 922 }, 923 isSingleIcon: this.operationItem?.length === SINGLE_ICON_NUMBER, 924 }) 925 .margin({ 926 start: LengthMetrics.vp(getResourceValue('sys.float.padding_level4')), 927 bottom: LengthMetrics.vp(getResourceValue('sys.float.padding_level3')), 928 }) 929 } else { 930 // 最大支持3个ICON,此场景不支持 931 } 932 } 933 }) 934 } 935 .justifyContent(FlexAlign.End) 936 .focusable(true) 937 } 938 939 @Builder 940 LoadingProcessStyle(): void { 941 Row() { 942 LoadingProgress() 943 .width(OPERATE_ITEM_LENGTH) 944 .height(OPERATE_ITEM_LENGTH) 945 .color($r('sys.color.icon_secondary')) 946 } 947 .justifyContent(FlexAlign.End) 948 .padding({ 949 top: getResourceValue('sys.float.padding_level2'), 950 bottom: getResourceValue('sys.float.padding_level2'), 951 }) 952 .margin({ 953 start: LengthMetrics.vp(getResourceValue('sys.float.padding_level4')), 954 }) 955 } 956 957 @Builder 958 dummyFunction(): void { 959 Row() { 960 } 961 } 962} 963 964@Component 965struct SingleIconStyle { 966 @State bgColor: Resource = $r('sys.color.ohos_id_color_sub_background_transparent'); 967 @State isFocus: boolean = false; 968 item: ContentIconOption | null = null; 969 @Consume subHeaderTheme: SubHeaderTheme; 970 isSingleIcon: boolean = true; 971 972 build() { 973 if (this.item && this.item.iconOptions) { 974 Button({ type: ButtonType.Normal, stateEffect: false }) { 975 this.IconZone(); 976 } 977 .focusable(true) 978 .width(SINGLE_ICON_ZONE_SIZE) 979 .height(SINGLE_ICON_ZONE_SIZE) 980 .align(Alignment.Center) 981 .backgroundColor(this.bgColor) 982 .borderRadius(getResourceValue('sys.float.corner_radius_level4')) 983 .focusBox({ 984 margin: { value: INDEX_ZERO, unit: LengthUnit.VP }, 985 strokeColor: ColorMetrics.resourceColor(this.subHeaderTheme.borderFocusColor), 986 strokeWidth: LengthMetrics.vp(getResourceValue('sys.float.outline_extra_larger')), 987 }) 988 .stateStyles({ 989 pressed: pressedStyle, 990 disabled: disabledStyle, 991 }) 992 .onTouch((event) => { 993 if (event.type === TouchType.Down || TouchType.Cancel) { 994 this.bgColor = $r('sys.color.interactive_pressed'); 995 } 996 if (event.type === TouchType.Up) { 997 this.bgColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 998 } 999 }) 1000 .onHover((isHover: boolean) => { 1001 if (isHover) { 1002 this.bgColor = $r('sys.color.interactive_hover'); 1003 } else { 1004 this.bgColor = $r('sys.color.ohos_id_color_sub_background_transparent'); 1005 } 1006 }) 1007 .responseRegion(this.iconResponseRegion()) 1008 .onClick((event) => { 1009 if (this.item?.action) { 1010 this.item?.action(); 1011 } 1012 }) 1013 } 1014 } 1015 1016 private iconResponseRegion(): Rectangle { 1017 if (this.isSingleIcon) { 1018 return { 1019 x: SINGLE_ICON_REGION_X, 1020 y: ICON_REGION_Y, 1021 width: MIN_HOT_AREA_LENGTH, 1022 height: MIN_HOT_AREA_LENGTH, 1023 }; 1024 } 1025 return { 1026 x: ICON_REGION_X, 1027 y: ICON_REGION_Y, 1028 width: MULTI_ICON_REGION_WIDTH, 1029 height: MIN_HOT_AREA_LENGTH, 1030 }; 1031 } 1032 1033 private fontSizeValue(item: ContentIconOption): Length { 1034 return item.iconOptions?.symbolicIconOption?.fontSize ? 1035 Util.symbolFontSize(item.iconOptions?.symbolicIconOption?.fontSize) : RIGHT_SINGLE_ICON_SIZE; 1036 } 1037 1038 @Builder 1039 IconZone(): void { 1040 if (this.item && this.item.iconOptions) { 1041 if (Util.isSymbolResource(this.item.iconOptions.icon)) { 1042 SymbolGlyph(this.item.iconOptions?.icon) 1043 .focusable(true) 1044 .fontSize(this.fontSizeValue(this.item)) 1045 .fontColor(this.item.iconOptions?.symbolicIconOption?.fontColor ?? [this.subHeaderTheme.rightIconColor]) 1046 .fontWeight(this.item.iconOptions?.symbolicIconOption?.fontWeight) 1047 .renderingStrategy(this.item.iconOptions?.symbolicIconOption?.renderingStrategy) 1048 .effectStrategy(this.item.iconOptions?.symbolicIconOption?.effectStrategy) 1049 } else { 1050 Image(this.item?.iconOptions?.icon) 1051 .fillColor(this.subHeaderTheme.rightIconColor) 1052 .width(RIGHT_SINGLE_ICON_SIZE) 1053 .height(RIGHT_SINGLE_ICON_SIZE) 1054 .focusable(true) 1055 .draggable(false) 1056 } 1057 } 1058 } 1059} 1060 1061class Util { 1062 /** 1063 * 是否symbol资源 1064 * @param resourceStr 资源 1065 * @returns true:symbol资源;false:非symbol资源 1066 */ 1067 public static isSymbolResource(resourceStr: ResourceStr | undefined): boolean { 1068 if (!Util.isResourceType(resourceStr)) { 1069 return false; 1070 } 1071 let resource = resourceStr as Resource; 1072 return resource.type === RESOURCE_TYPE_SYMBOL; 1073 } 1074 1075 /** 1076 * 是否Resource类型 1077 * @param resource 资源 1078 * @returns true:Resource类型;false:非Resource类型 1079 */ 1080 public static isResourceType(resource: ResourceStr | Resource | undefined): boolean { 1081 if (!resource) { 1082 return false; 1083 } 1084 if (typeof resource === 'string' || typeof resource === 'undefined') { 1085 return false; 1086 } 1087 return true; 1088 } 1089 1090 /** 1091 * get resource size 1092 * 1093 * @param resourceName resource id 1094 * @returns resource size 1095 */ 1096 public static getNumberByResource(resourceId: number, defaultNumber: number): number { 1097 try { 1098 let resourceNumber: number = resourceManager.getSystemResourceManager().getNumber(resourceId); 1099 if (resourceNumber === 0) { 1100 return defaultNumber; 1101 } else { 1102 return resourceNumber; 1103 } 1104 } catch (error) { 1105 let code: number = (error as BusinessError).code; 1106 let message: string = (error as BusinessError).message; 1107 hilog.error(0x3900, 'Ace', `SubHeader getNumberByResource error, code: ${code}, message: ${message}`); 1108 return 0; 1109 } 1110 } 1111 1112 /** 1113 * get resource string 1114 * 1115 * @param resourceId resource id 1116 * @param defaultString default value 1117 * @returns resource string 1118 */ 1119 public static getStringByResource(resourceId: number, defaultString: string): string { 1120 try { 1121 let resourceString: string = getContext().resourceManager.getStringSync(resourceId); 1122 if (resourceString === '') { 1123 return defaultString; 1124 } else { 1125 return resourceString; 1126 } 1127 } catch (error) { 1128 let code: number = (error as BusinessError).code; 1129 let message: string = (error as BusinessError).message; 1130 hilog.error(0x3900, 'Ace', `SubHeader getStringByResource error, code: ${code}, message: ${message}`); 1131 return ''; 1132 } 1133 } 1134 1135 public static numberToSize(fontSize: Length): number { 1136 if (typeof fontSize === 'string') { 1137 const fontSizeNumber: number = parseInt(fontSize); 1138 return fontSizeNumber; 1139 } else if (typeof fontSize === 'number') { 1140 return fontSize; 1141 } else { 1142 return getContext().resourceManager.getNumber(fontSize); 1143 } 1144 } 1145 1146 public static symbolFontSize(fontSize: Length): Length { 1147 return Util.numberToSize(fontSize) + 'vp'; 1148 } 1149} 1150 1151function getResourceValue(resourceName: string): number { 1152 if (RESOURCE_CACHE_MAP.hasKey(resourceName)) { 1153 let resourceValue: number | undefined = RESOURCE_CACHE_MAP.get(resourceName).resourceValue; 1154 if (typeof resourceValue === 'number') { 1155 return resourceValue; 1156 } else { 1157 resourceValue = Util.getNumberByResource(RESOURCE_CACHE_MAP.get(resourceName).resourceId, 1158 RESOURCE_CACHE_MAP.get(resourceName).defaultValue); 1159 RESOURCE_CACHE_MAP.get(resourceName).resourceValue = resourceValue; 1160 return resourceValue; 1161 } 1162 } 1163 return 0; 1164} 1165 1166@Component 1167struct TextArrowLayout { 1168 @Builder 1169 doNothingBuilder(): void { 1170 }; 1171 1172 @BuilderParam textArrowBuilder: () => void = this.doNothingBuilder; 1173 1174 onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Array<Layoutable>, 1175 constraint: ConstraintSizeOptions) { 1176 let currentX: number = 0; 1177 let currentY: number = 0; 1178 for (let index = 0; index < children.length; index++) { 1179 let child = children[index]; 1180 child.layout({ x: currentX, y: currentY }); 1181 } 1182 } 1183 1184 onMeasureSize(selfLayoutInfo: GeometryInfo, children: Array<Measurable>, 1185 constraint: ConstraintSizeOptions): SizeResult { 1186 let textArrowWidth: number = ARROW_ICON_WIDTH; 1187 let textArrowHeight: number = OPERATE_ITEM_LENGTH; 1188 1189 let textChild: Measurable = children[INDEX_ZERO]; 1190 let textConstraint: ConstraintSizeOptions = { 1191 minWidth: Math.max(textArrowWidth, Number(constraint.minWidth)), 1192 maxWidth: constraint.maxWidth, 1193 minHeight: Math.max(textArrowHeight, Number(constraint.minHeight)), 1194 maxHeight: constraint.maxHeight, 1195 }; 1196 let textMeasureResult: MeasureResult = textChild.measure(textConstraint); 1197 textArrowWidth = Math.max(textArrowWidth, textMeasureResult.width); 1198 textArrowHeight = Math.max(textArrowHeight, textMeasureResult.height); 1199 1200 let arrowChild: Measurable = children[INDEX_ONE]; 1201 let arrowConstraint: ConstraintSizeOptions = { 1202 minWidth: textArrowWidth, 1203 maxWidth: textArrowWidth, 1204 minHeight: textArrowHeight, 1205 maxHeight: textArrowHeight, 1206 }; 1207 arrowChild.measure(arrowConstraint); 1208 return { width: textArrowWidth, height: textArrowHeight }; 1209 } 1210 1211 build() { 1212 this.textArrowBuilder(); 1213 } 1214}