• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}