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