/* * Copyright (c) 2023-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { KeyCode } from '@ohos.multimodalInput.keyCode'; import window from '@ohos.window'; import common from '@ohos.app.ability.common'; import { BusinessError } from '@kit.BasicServicesKit'; import hilog from '@ohos.hilog'; export interface ComposeTitleBarMenuItem { value: ResourceStr; symbolStyle?: SymbolGlyphModifier; isEnabled?: boolean; action?: () => void; label?: ResourceStr; accessibilityText?: ResourceStr; accessibilityLevel?: string; accessibilityDescription?: ResourceStr; } const PUBLIC_MORE: Resource = $r('sys.symbol.dot_grid_2x2'); const PUBLIC_BACK: Resource = $r('sys.symbol.arrow_left'); const TEXT_EDITABLE_DIALOG = '18.3fp'; const IMAGE_SIZE = '64vp'; const MAX_DIALOG = '256vp'; const MIN_DIALOG = '216vp'; const RESOURCE_TYPE_SYMBOL: number = 40000; class Util { public static isSymbolResource(resourceStr: ResourceStr | undefined | null): boolean { if (!Util.isResourceType(resourceStr)) { return false; } let resource: Resource = resourceStr as Resource; return resource.type === RESOURCE_TYPE_SYMBOL; } public static isResourceType(resource: ResourceStr | Resource | undefined | null): boolean { if (!resource) { return false; } if (typeof resource === 'string' || typeof resource === 'undefined') { return false; } return true; } } class ButtonGestureModifier implements GestureModifier { public static readonly longPressTime: number = 500; public static readonly minFontSize: number = 1.75; public fontSize: number = 1; public controller: CustomDialogController | null = null; constructor(controller: CustomDialogController | null) { this.controller = controller; } applyGesture(event: UIGestureEvent): void { if (this.fontSize >= ButtonGestureModifier.minFontSize) { event.addGesture( new LongPressGestureHandler({ repeat: false, duration: ButtonGestureModifier.longPressTime }) .onAction(() => { if (event) { this.controller?.open(); } }) .onActionEnd(() => { this.controller?.close(); }) ) } else { event.clearGestures(); } } } @Component struct ComposeTitleBar { item: ComposeTitleBarMenuItem | undefined = undefined; title: ResourceStr = ''; subtitle: ResourceStr = ''; menuItems?: Array = []; @State titleMaxWidth: number = 0; @State fontSize: number = 1; private static readonly totalHeight = 56; private static readonly leftPadding = 12; private static readonly rightPadding = 12; private static readonly portraitImageSize = 40; private static readonly portraitImageLeftPadding = 4; private static readonly portraitImageRightPadding = 16; private static instanceCount = 0; @Provide('uniqueId') uniqueId?: number = -1; build() { Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Stretch }) { Row() { ImageMenuItem({ item: { value: PUBLIC_BACK, isEnabled: true, action: () => this.getUIContext()?.getRouter()?.back() }, index: -1, itemIndex: -1 }) Row() { if (this.item !== undefined) { Image(this.item.value) .width(ComposeTitleBar.portraitImageSize) .height(ComposeTitleBar.portraitImageSize) .margin({ left: $r('sys.float.ohos_id_text_paragraph_margin_xs'), right: $r('sys.float.ohos_id_text_paragraph_margin_m') }) .focusable(false) .borderRadius(ImageMenuItem.buttonBorderRadius) } Column() { if (this.title !== undefined) { Row() { Text(this.title) .fontWeight(FontWeight.Medium) .fontSize($r('sys.float.ohos_id_text_size_headline8')) .fontColor($r('sys.color.ohos_id_color_titlebar_text')) .maxLines(this.subtitle !== undefined ? 1 : 2) .textOverflow({ overflow: TextOverflow.Ellipsis }) .constraintSize({ maxWidth: this.titleMaxWidth }) } .justifyContent(FlexAlign.Start) } if (this.subtitle !== undefined) { Row() { Text(this.subtitle) .fontSize($r('sys.float.ohos_id_text_size_over_line')) .fontColor($r('sys.color.ohos_id_color_titlebar_subtitle_text')) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }) .constraintSize({ maxWidth: this.titleMaxWidth }) } .justifyContent(FlexAlign.Start) } } .justifyContent(FlexAlign.Start) .alignItems(HorizontalAlign.Start) .constraintSize({ maxWidth: this.titleMaxWidth }) } .accessibilityGroup(true) .accessibilityDescription($r('sys.string.subheader_accessibility_title')) } .margin({ left: $r('sys.float.ohos_id_default_padding_start') }) if (this.menuItems !== undefined && this.menuItems.length > 0) { CollapsibleMenuSection({ menuItems: this.menuItems, index: 1 + ComposeTitleBar.instanceCount++ }) } } .onAppear(() => { try { this.uniqueId = this.getUIContext().getFrameNodeByUniqueId(this.getUniqueId())?.getFirstChild()?.getUniqueId(); } catch (exception) { let code: number = (exception as BusinessError)?.code; let message: string = (exception as BusinessError)?.message; hilog.error(0x3900, 'ComposeTitleBar', `Faild to getFrameNodeByUniqueId,cause, code: ${code}, message: ${message}`); } }) .width('100%') .height(ComposeTitleBar.totalHeight) .backgroundColor($r('sys.color.ohos_id_color_background')) .onAreaChange((_oldValue: Area, newValue: Area) => { let newWidth = Number(newValue.width); if (this.menuItems !== undefined) { let menusLength = this.menuItems.length; if (menusLength >= CollapsibleMenuSection.maxCountOfVisibleItems) { newWidth = newWidth - ImageMenuItem.imageHotZoneWidth * CollapsibleMenuSection.maxCountOfVisibleItems; } else if (menusLength > 0) { newWidth = newWidth - ImageMenuItem.imageHotZoneWidth * menusLength; } } this.titleMaxWidth = newWidth; this.titleMaxWidth -= ComposeTitleBar.leftPadding; this.titleMaxWidth -= ImageMenuItem.imageHotZoneWidth; if (this.item !== undefined) { this.titleMaxWidth -= ComposeTitleBar.portraitImageLeftPadding + ComposeTitleBar.portraitImageSize + ComposeTitleBar.portraitImageRightPadding; } this.titleMaxWidth -= ComposeTitleBar.rightPadding; }) } } @Component struct CollapsibleMenuSection { menuItems?: Array = []; item: ComposeTitleBarMenuItem = { value: PUBLIC_MORE, label: $r('sys.string.ohos_toolbar_more'), } as ComposeTitleBarMenuItem; index: number = 0; minFontSize: number = 1.75; isFollowingSystemFontScale: boolean = false; maxFontScale: number = 1; systemFontScale?: number = 1; static readonly maxCountOfVisibleItems = 3; private static readonly focusPadding = 4; private static readonly marginsNum = 2; private firstFocusableIndex = -1; @State isPopupShown: boolean = false; @State isMoreIconOnFocus: boolean = false; @State isMoreIconOnHover: boolean = false; @State isMoreIconOnClick: boolean = false; @Prop @Watch('onFontSizeUpdated') fontSize: number = 1; dialogController: CustomDialogController | null = new CustomDialogController({ builder: ComposeTitleBarDialog({ cancel: () => { }, confirm: () => { }, itemComposeTitleDialog: this.item, composeTitleBarDialog: this.item.label ? this.item.label : '', fontSize: this.fontSize, }), maskColor: Color.Transparent, isModal: true, customStyle: true, }) @State buttonGestureModifier: ButtonGestureModifier = new ButtonGestureModifier(this.dialogController); getMoreIconFgColor() { return this.isMoreIconOnClick ? $r('sys.color.ohos_id_color_titlebar_icon_pressed') : $r('sys.color.ohos_id_color_titlebar_icon'); } getMoreIconBgColor() { if (this.isMoreIconOnClick) { return $r('sys.color.ohos_id_color_click_effect'); } else if (this.isMoreIconOnHover) { return $r('sys.color.ohos_id_color_hover'); } else { return Color.Transparent; } } aboutToAppear() { try { let uiContent: UIContext = this.getUIContext(); this.isFollowingSystemFontScale = uiContent.isFollowingSystemFontScale(); this.maxFontScale = uiContent.getMaxFontScale(); } catch (err) { let code: number = (err as BusinessError)?.code; let message: string = (err as BusinessError)?.message; hilog.error(0x3900, 'ComposeTitleBar', `Failed to init fontsizescale info, cause, code: ${code}, message: ${message}`); } if (this.menuItems) { this.menuItems.forEach((item, index) => { if (item.isEnabled && this.firstFocusableIndex === -1 && index > CollapsibleMenuSection.maxCountOfVisibleItems - 2) { this.firstFocusableIndex = this.index * 1000 + index + 1; } }) } this.fontSize = this.decideFontScale() } decideFontScale(): number { try { let uiContent: UIContext = this.getUIContext(); this.systemFontScale = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1; if (!this.isFollowingSystemFontScale) { return 1; } return Math.min(this.systemFontScale, this.maxFontScale); } catch (exception) { let code: number = (exception as BusinessError)?.code; let message: string = (exception as BusinessError)?.message; hilog.error(0x3900, 'ComposeTitleBar', `Faild to decideFontScale,cause, code: ${code}, message: ${message}`); return 1; } } onFontSizeUpdated(): void { this.buttonGestureModifier.fontSize = this.fontSize; } build() { Column() { Row() { if (this.menuItems) { if (this.menuItems.length <= CollapsibleMenuSection.maxCountOfVisibleItems) { ForEach(this.menuItems, (item: ComposeTitleBarMenuItem, index: number) => { ImageMenuItem({ item: item, index: this.index * 1000 + index + 1, itemIndex: index }) }) } else { ForEach(this.menuItems.slice(0, CollapsibleMenuSection.maxCountOfVisibleItems - 1), (item: ComposeTitleBarMenuItem, index: number) => { ImageMenuItem({ item: item, index: this.index * 1000 + index + 1, itemIndex: index }) }) Button({ type: ButtonType.Normal, stateEffect: true }) { SymbolGlyph(PUBLIC_MORE) .fontSize(`${ImageMenuItem.imageSize}vp`) .fontColor([$r('sys.color.icon_primary')]) .draggable(false) .focusable(true) } .accessibilityText($r('sys.string.ohos_toolbar_more')) .width(ImageMenuItem.imageHotZoneWidth) .height(ImageMenuItem.imageHotZoneWidth) .borderRadius(ImageMenuItem.buttonBorderRadius) .foregroundColor(this.getMoreIconFgColor()) .backgroundColor(this.getMoreIconBgColor()) .stateStyles({ focused: { .border({ radius: $r('sys.float.ohos_id_corner_radius_clicked'), width: ImageMenuItem.focusBorderWidth, color: $r('sys.color.ohos_id_color_focused_outline'), style: BorderStyle.Solid }) }, normal: { .border({ radius: $r('sys.float.ohos_id_corner_radius_clicked'), width: 0 }) } }) .onFocus(() => this.isMoreIconOnFocus = true) .onBlur(() => this.isMoreIconOnFocus = false) .onHover((isOn) => this.isMoreIconOnHover = isOn) .onKeyEvent((event) => { if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) { return; } if (event.type === KeyType.Down) { this.isMoreIconOnClick = true; } if (event.type === KeyType.Up) { this.isMoreIconOnClick = false; } }) .onTouch((event) => { if (event.type === TouchType.Down) { this.isMoreIconOnClick = true; } if (event.type === TouchType.Up || event.type === TouchType.Cancel) { this.isMoreIconOnClick = false; if (this.fontSize >= this.minFontSize) { this.dialogController?.close(); } } }) .onClick(() => this.isPopupShown = true) .gestureModifier(this.buttonGestureModifier) .bindPopup(this.isPopupShown, { builder: this.popupBuilder, placement: Placement.Bottom, popupColor: Color.White, enableArrow: false, onStateChange: (e) => { this.isPopupShown = e.isVisible; if (!e.isVisible) { this.isMoreIconOnClick = false; } } }) } } } } .height('100%') .margin({ right: $r('sys.float.ohos_id_default_padding_end') }) .justifyContent(FlexAlign.Center) } onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Layoutable[], constraint: ConstraintSizeOptions): void { children.forEach((child) => { child.layout({ x: 0, y: 0 }); }) this.fontSize = this.decideFontScale(); } @Builder popupBuilder() { Column() { if (this.menuItems) { ForEach(this.menuItems.slice(CollapsibleMenuSection.maxCountOfVisibleItems - 1, this.menuItems.length), (item: ComposeTitleBarMenuItem, index: number) => { ImageMenuItem({ item: item, index: this.index * 1000 + CollapsibleMenuSection.maxCountOfVisibleItems + index, isPopup: false }) }) } } .width(ImageMenuItem.imageHotZoneWidth + CollapsibleMenuSection.focusPadding * CollapsibleMenuSection.marginsNum) .margin({ top: CollapsibleMenuSection.focusPadding, bottom: CollapsibleMenuSection.focusPadding }) .onAppear(() => { focusControl.requestFocus(ImageMenuItem.focusablePrefix + this.firstFocusableIndex) }) } } @Component struct ImageMenuItem { item: ComposeTitleBarMenuItem = {} as ComposeTitleBarMenuItem; index: number = 0; itemIndex?: number = 0; minFontSize: number = 1.75; isFollowingSystemFontScale: boolean = false; maxFontScale: number = 1; systemFontScale?: number = 1; isPopup: boolean = true; static readonly imageSize = 24; static readonly imageHotZoneWidth = 48; static readonly buttonBorderRadius = 8; static readonly focusBorderWidth = 2; static readonly focusablePrefix = 'Id-ComposeTitleBar-ImageMenuItem-'; @State isOnFocus: boolean = false; @State isOnHover: boolean = false; @State isOnClick: boolean = false; @Prop @Watch('onFontSizeUpdated') fontSize: number = 1; @Consume('uniqueId') parentParentUniqueId?: number; dialogController: CustomDialogController | null = new CustomDialogController({ builder: ComposeTitleBarDialog({ cancel: () => { }, confirm: () => { }, itemComposeTitleDialog: this.item, composeTitleBarDialog: this.item.label ? this.item.label : this.textDialog(), fontSize: this.fontSize, }), maskColor: Color.Transparent, isModal: true, customStyle: true, }) @State buttonGestureModifier: ButtonGestureModifier = new ButtonGestureModifier(this.dialogController); private textDialog(): ResourceStr { if (this.item.value === PUBLIC_MORE) { return $r('sys.string.ohos_toolbar_more'); } else if (this.item.value === PUBLIC_BACK) { return $r('sys.string.icon_back'); } else { return this.item.label ? this.item.label : ''; } } private toStringFormat(resource: ResourceStr | undefined): string | undefined { if (typeof resource === 'string') { return resource; } else if (typeof resource === 'undefined') { return ''; } else { let resourceString: string = ''; try { if (resource.id === -1 ) { resourceString = getContext()?.resourceManager?.getStringByNameSync(resource.params?.[0].split('.').pop()); } else { resourceString = getContext()?.resourceManager?.getStringSync(resource); } } catch (err) { let code: number = (err as BusinessError)?.code; let message: string = (err as BusinessError)?.message; hilog.error(0x3900, 'Ace', `Faild to ComposeTitleBar toStringFormat,code: ${code},message:${message}`); } return resourceString; } } private getAccessibilityReadText(): string | undefined { if (this.item.value === PUBLIC_BACK) { try { return getContext()?.resourceManager?.getStringByNameSync('icon_back'); } catch (err) { let code: number = (err as BusinessError)?.code; let message: string = (err as BusinessError)?.message; hilog.error(0x3900, 'Ace', `Faild to ComposeTitleBar getStringByNameSync icon_back,code: ${code},message:${message}`); } } else if (this.item.value === PUBLIC_MORE) { try { return getContext()?.resourceManager?.getStringByNameSync('ohos_toolbar_more'); } catch (err) { let code: number = (err as BusinessError)?.code; let message: string = (err as BusinessError)?.message; hilog.error(0x3900, 'Ace', `Faild to ComposeTitleBar getStringByNameSync ohos_toolbar_more,code: ${code},message:${message}`); } } else if (this.item.accessibilityText) { return this.item.accessibilityText as string; } else if (this.item.label) { return this.item.label as string; } return ' '; } onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Layoutable[], constraint: ConstraintSizeOptions): void { children.forEach((child) => { child.layout({ x: 0, y: 0 }); }) this.fontSize = this.decideFontScale(); } getFgColor() { return this.isOnClick ? $r('sys.color.ohos_id_color_titlebar_icon_pressed') : $r('sys.color.ohos_id_color_titlebar_icon'); } getBgColor() { if (this.isOnClick) { return $r('sys.color.ohos_id_color_click_effect'); } else if (this.isOnHover) { return $r('sys.color.ohos_id_color_hover'); } else { return Color.Transparent; } } aboutToAppear() { try { let uiContent: UIContext = this.getUIContext(); this.isFollowingSystemFontScale = uiContent.isFollowingSystemFontScale(); this.maxFontScale = uiContent.getMaxFontScale(); } catch (err) { let code: number = (err as BusinessError)?.code; let message: string = (err as BusinessError)?.message; hilog.error(0x3900, 'ComposeTitleBar', `Failed to init fontsizescale info, cause, code: ${code}, message: ${message}`); } this.fontSize = this.decideFontScale(); } onFontSizeUpdated(): void { this.buttonGestureModifier.fontSize = this.fontSize; } decideFontScale(): number { try { let uiContent: UIContext = this.getUIContext(); this.systemFontScale = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1; if (!this.isFollowingSystemFontScale) { return 1; } return Math.min(this.systemFontScale, this.maxFontScale); } catch (exception) { let code: number = (exception as BusinessError)?.code; let message: string = (exception as BusinessError)?.message; hilog.error(0x3900, 'ComposeTitleBar', `Faild to decideFontScale,cause, code: ${code}, message: ${message}`); return 1; } } build() { if (this.isPopup) { Button({ type: ButtonType.Normal, stateEffect: this.item.isEnabled }) { if (this.item?.symbolStyle) { SymbolGlyph() .fontColor([$r('sys.color.ohos_id_color_text_primary')]) .attributeModifier(this.item?.symbolStyle) .fontSize(`${ImageMenuItem.imageSize}vp`) .effectStrategy(SymbolEffectStrategy.NONE) .symbolEffect(new SymbolEffect(), false) .draggable(false) .focusable(this.item?.isEnabled) .key(ImageMenuItem.focusablePrefix + this.index) } else { if (Util.isSymbolResource(this.item.value)) { SymbolGlyph(this.item.value as Resource) .fontSize(`${ImageMenuItem.imageSize}vp`) .fontColor([$r('sys.color.ohos_id_color_text_primary')]) .draggable(false) .focusable(this.item?.isEnabled) .key(ImageMenuItem.focusablePrefix + this.index) } else { Image(this.item?.value) .matchTextDirection(this.item?.value === PUBLIC_BACK ? true : false) .width(ImageMenuItem.imageSize) .draggable(false) .height(ImageMenuItem.imageSize) .focusable(this.item?.isEnabled) .key(ImageMenuItem.focusablePrefix + this.index) .fillColor($r('sys.color.ohos_id_color_text_primary')) } } } .id(`ComposeTitleBar_ImageMenuItem_${this.parentParentUniqueId}_${this.itemIndex}`) .accessibilityText(this.getAccessibilityReadText()) .accessibilityLevel(this.item?.accessibilityLevel ?? 'auto') .accessibilityDescription(this.item?.accessibilityDescription as string) .enabled(this.item.isEnabled ? this.item.isEnabled : false) .width(ImageMenuItem.imageHotZoneWidth) .height(ImageMenuItem.imageHotZoneWidth) .borderRadius(ImageMenuItem.buttonBorderRadius) .foregroundColor(this.getFgColor()) .backgroundColor(this.getBgColor()) .stateStyles({ focused: { .border({ radius: $r('sys.float.ohos_id_corner_radius_clicked'), width: ImageMenuItem.focusBorderWidth, color: $r('sys.color.ohos_id_color_focused_outline'), style: BorderStyle.Solid }) }, normal: { .border({ radius: $r('sys.float.ohos_id_corner_radius_clicked'), width: 0 }) } }) .onFocus(() => { if (!this.item?.isEnabled) { return; } this.isOnFocus = true; }) .onBlur(() => this.isOnFocus = false) .onHover((isOn) => { if (!this.item?.isEnabled) { return; } this.isOnHover = isOn; }) .onKeyEvent((event) => { if (!this.item?.isEnabled) { return; } if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) { return; } if (event.type === KeyType.Down) { this.isOnClick = true; } if (event.type === KeyType.Up) { this.isOnClick = false; } }) .onTouch((event) => { if (!this.item?.isEnabled) { return; } if (event.type === TouchType.Down) { this.isOnClick = true; } if (event.type === TouchType.Up || event.type === TouchType.Cancel) { this.isOnClick = false; if (this.fontSize >= this.minFontSize) { this.dialogController?.close(); } } }) .onClick(() => { if (this.item) { return this.item.isEnabled && this.item.action?.(); } }) .gestureModifier(this.buttonGestureModifier) } else { Button({ type: ButtonType.Normal, stateEffect: this.item.isEnabled }) { if (this.item?.symbolStyle) { SymbolGlyph() .fontColor([$r('sys.color.ohos_id_color_text_primary')]) .attributeModifier(this.item?.symbolStyle) .fontSize(`${ImageMenuItem.imageSize}vp`) .effectStrategy(SymbolEffectStrategy.NONE) .symbolEffect(new SymbolEffect(), false) .draggable(false) .focusable(this.item?.isEnabled) .key(ImageMenuItem.focusablePrefix + this.index) } else { if (Util.isSymbolResource(this.item.value)) { SymbolGlyph(this.item.value as Resource) .fontSize(`${ImageMenuItem.imageSize}vp`) .fontColor([$r('sys.color.ohos_id_color_text_primary')]) .draggable(false) .focusable(this.item?.isEnabled) .key(ImageMenuItem.focusablePrefix + this.index) } else { Image(this.item?.value) .matchTextDirection(this.item?.value === PUBLIC_BACK ? true : false) .width(ImageMenuItem.imageSize) .draggable(false) .height(ImageMenuItem.imageSize) .focusable(this.item?.isEnabled) .key(ImageMenuItem.focusablePrefix + this.index) .fillColor($r('sys.color.ohos_id_color_text_primary')) } } } .id(`ComposeTitleBar_ImageMenuItem_${this.parentParentUniqueId}_${this.itemIndex}`) .accessibilityText(this.getAccessibilityReadText()) .accessibilityLevel(this.item?.accessibilityLevel ?? 'auto') .accessibilityDescription(this.item?.accessibilityDescription as string) .enabled(this.item.isEnabled ? this.item.isEnabled : false) .width(ImageMenuItem.imageHotZoneWidth) .height(ImageMenuItem.imageHotZoneWidth) .borderRadius(ImageMenuItem.buttonBorderRadius) .foregroundColor(this.getFgColor()) .backgroundColor(this.getBgColor()) .stateStyles({ focused: { .border({ radius: $r('sys.float.ohos_id_corner_radius_clicked'), width: ImageMenuItem.focusBorderWidth, color: $r('sys.color.ohos_id_color_focused_outline'), style: BorderStyle.Solid }) }, normal: { .border({ radius: $r('sys.float.ohos_id_corner_radius_clicked'), width: 0 }) } }) .onFocus(() => { if (!this.item?.isEnabled) { return; } this.isOnFocus = true; }) .onBlur(() => this.isOnFocus = false) .onHover((isOn) => { if (!this.item?.isEnabled) { return; } this.isOnHover = isOn; }) .onKeyEvent((event) => { if (!this.item?.isEnabled) { return; } if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) { return; } if (event.type === KeyType.Down) { this.isOnClick = true; } if (event.type === KeyType.Up) { this.isOnClick = false; } }) .onTouch((event) => { if (!this.item?.isEnabled) { return; } if (event.type === TouchType.Down) { this.isOnClick = true; } if (event.type === TouchType.Up || event.type === TouchType.Cancel) { this.isOnClick = false; if (this.fontSize >= this.minFontSize) { this.dialogController?.close(); } } }) .onClick(() => { if (this.item) { return this.item.isEnabled && this.item.action?.(); } }) } } } /** * ComposeTitleBarDialog */ @CustomDialog struct ComposeTitleBarDialog { itemComposeTitleDialog: ComposeTitleBarMenuItem = {} as ComposeTitleBarMenuItem; callbackId: number | undefined = undefined; composeTitleBarDialog?: ResourceStr = ''; mainWindowStage: window.Window | undefined = undefined; controller?: CustomDialogController; minFontSize: number = 1.75; maxFontSize: number = 3.2; screenWidth: number = 640; verticalScreenLines: number = 6; horizontalsScreenLines: number = 1; @StorageLink('mainWindow') mainWindow: Promise | undefined = undefined; @State fontSize: number = 1; @State maxLines: number = 1; @StorageProp('windowStandardHeight') windowStandardHeight: number = 0; cancel: () => void = () => { } confirm: () => void = () => { } build() { if (this.composeTitleBarDialog) { Column() { if (this.itemComposeTitleDialog.symbolStyle) { SymbolGlyph() .fontColor([$r('sys.color.icon_primary')]) .attributeModifier(this.itemComposeTitleDialog.symbolStyle) .fontSize(IMAGE_SIZE) .effectStrategy(SymbolEffectStrategy.NONE) .symbolEffect(new SymbolEffect(), false) .margin({ top: $r('sys.float.padding_level24'), bottom: $r('sys.float.padding_level8'), }) } else { if (Util.isSymbolResource(this.itemComposeTitleDialog.value)) { SymbolGlyph(this.itemComposeTitleDialog.value as Resource) .fontSize(IMAGE_SIZE) .fontColor([$r('sys.color.icon_primary')]) .margin({ top: $r('sys.float.padding_level24'), bottom: $r('sys.float.padding_level8'), }) } else { Image(this.itemComposeTitleDialog.value) .width(IMAGE_SIZE) .height(IMAGE_SIZE) .margin({ top: $r('sys.float.padding_level24'), bottom: $r('sys.float.padding_level8'), }) .fillColor($r('sys.color.icon_primary')) } } Column() { Text(this.composeTitleBarDialog) .fontSize(TEXT_EDITABLE_DIALOG) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(this.maxLines) .width('100%') .textAlign(TextAlign.Center) .fontColor($r('sys.color.font_primary')) } .width('100%') .padding({ left: $r('sys.float.padding_level4'), right: $r('sys.float.padding_level4'), bottom: $r('sys.float.padding_level12'), }) } .width(this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG) .constraintSize({ minHeight: this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG }) .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK, undefined, { disableSystemAdaptation: true }) .shadow(ShadowStyle.OUTER_DEFAULT_LG) .borderRadius($r('sys.float.corner_radius_level10')) } else { Column() { if (this.itemComposeTitleDialog.symbolStyle) { SymbolGlyph() .fontColor([$r('sys.color.icon_primary')]) .attributeModifier(this.itemComposeTitleDialog.symbolStyle) .fontSize(IMAGE_SIZE) .effectStrategy(SymbolEffectStrategy.NONE) .symbolEffect(new SymbolEffect(), false) } else { if (Util.isSymbolResource(this.itemComposeTitleDialog.value)) { SymbolGlyph(this.itemComposeTitleDialog.value as Resource) .fontSize(IMAGE_SIZE) .fontColor([$r('sys.color.icon_primary')]) } else { Image(this.itemComposeTitleDialog.value) .width(IMAGE_SIZE) .height(IMAGE_SIZE) .fillColor($r('sys.color.icon_primary')) } } } .width(this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG) .constraintSize({ minHeight: this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG }) .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK, undefined, { disableSystemAdaptation: true }) .shadow(ShadowStyle.OUTER_DEFAULT_LG) .borderRadius($r('sys.float.corner_radius_level10')) .justifyContent(FlexAlign.Center) } } async aboutToAppear(): Promise { try { let context = this.getUIContext().getHostContext() as common.UIAbilityContext; this.mainWindowStage = context.windowStage.getMainWindowSync(); let properties: window.WindowProperties = this.mainWindowStage.getWindowProperties(); let rect = properties.windowRect; if (px2vp(rect.height) > this.screenWidth) { this.maxLines = this.verticalScreenLines; } else { this.maxLines = this.horizontalsScreenLines; } } catch (exception) { let code: number = (exception as BusinessError)?.code; let message: string = (exception as BusinessError)?.message; hilog.error(0x3900, 'ComposeTitleBar', `Faild to getMainWindowSync,cause, code: ${code}, message: ${message}`); } } } export { ComposeTitleBar }