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