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