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