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