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