123b3eb3cSopenharmony_ci/* 223b3eb3cSopenharmony_ci * Copyright (c) 2023-2023 Huawei Device Co., Ltd. 323b3eb3cSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 423b3eb3cSopenharmony_ci * you may not use this file except in compliance with the License. 523b3eb3cSopenharmony_ci * You may obtain a copy of the License at 623b3eb3cSopenharmony_ci * 723b3eb3cSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 823b3eb3cSopenharmony_ci * 923b3eb3cSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 1023b3eb3cSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 1123b3eb3cSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1223b3eb3cSopenharmony_ci * See the License for the specific language governing permissions and 1323b3eb3cSopenharmony_ci * limitations under the License. 1423b3eb3cSopenharmony_ci */ 1523b3eb3cSopenharmony_ci 1623b3eb3cSopenharmony_ciimport { KeyCode } from '@ohos.multimodalInput.keyCode' 1723b3eb3cSopenharmony_ciimport window from '@ohos.window' 1823b3eb3cSopenharmony_ciimport common from '@ohos.app.ability.common' 1923b3eb3cSopenharmony_ciimport { BusinessError } from '@kit.BasicServicesKit' 2023b3eb3cSopenharmony_ciimport { hilog } from '@kit.PerformanceAnalysisKit' 2123b3eb3cSopenharmony_ci 2223b3eb3cSopenharmony_ciexport interface ComposeTitleBarMenuItem { 2323b3eb3cSopenharmony_ci value: ResourceStr; 2423b3eb3cSopenharmony_ci isEnabled?: boolean; 2523b3eb3cSopenharmony_ci action?: () => void; 2623b3eb3cSopenharmony_ci label?: ResourceStr; 2723b3eb3cSopenharmony_ci} 2823b3eb3cSopenharmony_ci 2923b3eb3cSopenharmony_ciconst PUBLIC_MORE = $r('sys.media.ohos_ic_public_more') 3023b3eb3cSopenharmony_ciconst PUBLIC_BACK: Resource = $r('sys.media.ohos_ic_back') 3123b3eb3cSopenharmony_ciconst TEXT_EDITABLE_DIALOG = '18.3fp' 3223b3eb3cSopenharmony_ciconst IMAGE_SIZE = '64vp' 3323b3eb3cSopenharmony_ciconst MAX_DIALOG = '256vp' 3423b3eb3cSopenharmony_ciconst MIN_DIALOG = '216vp' 3523b3eb3cSopenharmony_ci 3623b3eb3cSopenharmony_ci@Component 3723b3eb3cSopenharmony_cistruct ComposeTitleBar { 3823b3eb3cSopenharmony_ci item: ComposeTitleBarMenuItem | undefined = undefined 3923b3eb3cSopenharmony_ci title: ResourceStr = '' 4023b3eb3cSopenharmony_ci subtitle: ResourceStr = '' 4123b3eb3cSopenharmony_ci menuItems?: Array<ComposeTitleBarMenuItem> = []; 4223b3eb3cSopenharmony_ci 4323b3eb3cSopenharmony_ci @State titleMaxWidth: number = 0 4423b3eb3cSopenharmony_ci @State fontSize: number = 1; 4523b3eb3cSopenharmony_ci 4623b3eb3cSopenharmony_ci private static readonly totalHeight = 56 4723b3eb3cSopenharmony_ci private static readonly leftPadding = 12 4823b3eb3cSopenharmony_ci private static readonly rightPadding = 12 4923b3eb3cSopenharmony_ci private static readonly portraitImageSize = 40 5023b3eb3cSopenharmony_ci private static readonly portraitImageLeftPadding = 4 5123b3eb3cSopenharmony_ci private static readonly portraitImageRightPadding = 16 5223b3eb3cSopenharmony_ci private static instanceCount = 0 5323b3eb3cSopenharmony_ci 5423b3eb3cSopenharmony_ci build() { 5523b3eb3cSopenharmony_ci Flex({ 5623b3eb3cSopenharmony_ci justifyContent: FlexAlign.SpaceBetween, 5723b3eb3cSopenharmony_ci alignItems: ItemAlign.Stretch 5823b3eb3cSopenharmony_ci }) { 5923b3eb3cSopenharmony_ci Row() { 6023b3eb3cSopenharmony_ci ImageMenuItem({ item: { 6123b3eb3cSopenharmony_ci value: PUBLIC_BACK, 6223b3eb3cSopenharmony_ci isEnabled: true, 6323b3eb3cSopenharmony_ci action: () => this.getUIContext()?.getRouter()?.back() 6423b3eb3cSopenharmony_ci }, index: -1 }) 6523b3eb3cSopenharmony_ci 6623b3eb3cSopenharmony_ci if (this.item !== undefined) { 6723b3eb3cSopenharmony_ci Image(this.item.value) 6823b3eb3cSopenharmony_ci .width(ComposeTitleBar.portraitImageSize) 6923b3eb3cSopenharmony_ci .height(ComposeTitleBar.portraitImageSize) 7023b3eb3cSopenharmony_ci .margin({ 7123b3eb3cSopenharmony_ci left: $r('sys.float.ohos_id_text_paragraph_margin_xs'), 7223b3eb3cSopenharmony_ci right: $r('sys.float.ohos_id_text_paragraph_margin_m') 7323b3eb3cSopenharmony_ci }) 7423b3eb3cSopenharmony_ci .focusable(false) 7523b3eb3cSopenharmony_ci .borderRadius(ImageMenuItem.buttonBorderRadius) 7623b3eb3cSopenharmony_ci } 7723b3eb3cSopenharmony_ci 7823b3eb3cSopenharmony_ci Column() { 7923b3eb3cSopenharmony_ci if (this.title !== undefined) { 8023b3eb3cSopenharmony_ci Row() { 8123b3eb3cSopenharmony_ci Text(this.title) 8223b3eb3cSopenharmony_ci .fontWeight(FontWeight.Medium) 8323b3eb3cSopenharmony_ci .fontSize($r('sys.float.ohos_id_text_size_headline8')) 8423b3eb3cSopenharmony_ci .fontColor($r('sys.color.ohos_id_color_titlebar_text')) 8523b3eb3cSopenharmony_ci .maxLines(this.subtitle !== undefined ? 1 : 2) 8623b3eb3cSopenharmony_ci .textOverflow({ overflow: TextOverflow.Ellipsis }) 8723b3eb3cSopenharmony_ci .constraintSize({ maxWidth: this.titleMaxWidth }) 8823b3eb3cSopenharmony_ci } 8923b3eb3cSopenharmony_ci .justifyContent(FlexAlign.Start) 9023b3eb3cSopenharmony_ci } 9123b3eb3cSopenharmony_ci if (this.subtitle !== undefined) { 9223b3eb3cSopenharmony_ci Row() { 9323b3eb3cSopenharmony_ci Text(this.subtitle) 9423b3eb3cSopenharmony_ci .fontSize($r('sys.float.ohos_id_text_size_over_line')) 9523b3eb3cSopenharmony_ci .fontColor($r('sys.color.ohos_id_color_titlebar_subtitle_text')) 9623b3eb3cSopenharmony_ci .maxLines(1) 9723b3eb3cSopenharmony_ci .textOverflow({ overflow: TextOverflow.Ellipsis }) 9823b3eb3cSopenharmony_ci .constraintSize({ maxWidth: this.titleMaxWidth }) 9923b3eb3cSopenharmony_ci } 10023b3eb3cSopenharmony_ci .justifyContent(FlexAlign.Start) 10123b3eb3cSopenharmony_ci } 10223b3eb3cSopenharmony_ci } 10323b3eb3cSopenharmony_ci .justifyContent(FlexAlign.Start) 10423b3eb3cSopenharmony_ci .alignItems(HorizontalAlign.Start) 10523b3eb3cSopenharmony_ci .constraintSize({ maxWidth: this.titleMaxWidth }) 10623b3eb3cSopenharmony_ci } 10723b3eb3cSopenharmony_ci .margin({ left: $r('sys.float.ohos_id_default_padding_start') }) 10823b3eb3cSopenharmony_ci 10923b3eb3cSopenharmony_ci if (this.menuItems !== undefined && this.menuItems.length > 0) { 11023b3eb3cSopenharmony_ci CollapsibleMenuSection({ menuItems: this.menuItems, index: 1 + ComposeTitleBar.instanceCount++ }) 11123b3eb3cSopenharmony_ci } 11223b3eb3cSopenharmony_ci } 11323b3eb3cSopenharmony_ci .width('100%') 11423b3eb3cSopenharmony_ci .height(ComposeTitleBar.totalHeight) 11523b3eb3cSopenharmony_ci .backgroundColor($r('sys.color.ohos_id_color_background')) 11623b3eb3cSopenharmony_ci .onAreaChange((_oldValue: Area, newValue: Area) => { 11723b3eb3cSopenharmony_ci let newWidth = Number(newValue.width) 11823b3eb3cSopenharmony_ci if (this.menuItems !== undefined) { 11923b3eb3cSopenharmony_ci let menusLength = this.menuItems.length 12023b3eb3cSopenharmony_ci if (menusLength >= CollapsibleMenuSection.maxCountOfVisibleItems) { 12123b3eb3cSopenharmony_ci newWidth = newWidth - ImageMenuItem.imageHotZoneWidth * CollapsibleMenuSection.maxCountOfVisibleItems 12223b3eb3cSopenharmony_ci } else if (menusLength > 0) { 12323b3eb3cSopenharmony_ci newWidth = newWidth - ImageMenuItem.imageHotZoneWidth * menusLength 12423b3eb3cSopenharmony_ci } 12523b3eb3cSopenharmony_ci } 12623b3eb3cSopenharmony_ci this.titleMaxWidth = newWidth 12723b3eb3cSopenharmony_ci this.titleMaxWidth -= ComposeTitleBar.leftPadding 12823b3eb3cSopenharmony_ci this.titleMaxWidth -= ImageMenuItem.imageHotZoneWidth 12923b3eb3cSopenharmony_ci if (this.item !== undefined) { 13023b3eb3cSopenharmony_ci this.titleMaxWidth -= ComposeTitleBar.portraitImageLeftPadding + 13123b3eb3cSopenharmony_ci ComposeTitleBar.portraitImageSize + 13223b3eb3cSopenharmony_ci ComposeTitleBar.portraitImageRightPadding 13323b3eb3cSopenharmony_ci } 13423b3eb3cSopenharmony_ci this.titleMaxWidth -= ComposeTitleBar.rightPadding 13523b3eb3cSopenharmony_ci }) 13623b3eb3cSopenharmony_ci } 13723b3eb3cSopenharmony_ci} 13823b3eb3cSopenharmony_ci 13923b3eb3cSopenharmony_ci@Component 14023b3eb3cSopenharmony_cistruct CollapsibleMenuSection { 14123b3eb3cSopenharmony_ci menuItems?: Array<ComposeTitleBarMenuItem> = []; 14223b3eb3cSopenharmony_ci item: ComposeTitleBarMenuItem = { 14323b3eb3cSopenharmony_ci value: PUBLIC_MORE, 14423b3eb3cSopenharmony_ci label: $r('sys.string.ohos_toolbar_more'), 14523b3eb3cSopenharmony_ci } as ComposeTitleBarMenuItem; 14623b3eb3cSopenharmony_ci index: number = 0 14723b3eb3cSopenharmony_ci longPressTime: number = 500; 14823b3eb3cSopenharmony_ci minFontSize: number = 1.75; 14923b3eb3cSopenharmony_ci isFollowingSystemFontScale: boolean = false; 15023b3eb3cSopenharmony_ci maxFontScale: number = 1; 15123b3eb3cSopenharmony_ci systemFontScale?: number = 1; 15223b3eb3cSopenharmony_ci 15323b3eb3cSopenharmony_ci static readonly maxCountOfVisibleItems = 3 15423b3eb3cSopenharmony_ci private static readonly focusPadding = 4 15523b3eb3cSopenharmony_ci private static readonly marginsNum = 2 15623b3eb3cSopenharmony_ci private firstFocusableIndex = -1 15723b3eb3cSopenharmony_ci 15823b3eb3cSopenharmony_ci @State isPopupShown: boolean = false 15923b3eb3cSopenharmony_ci 16023b3eb3cSopenharmony_ci @State isMoreIconOnFocus: boolean = false 16123b3eb3cSopenharmony_ci @State isMoreIconOnHover: boolean = false 16223b3eb3cSopenharmony_ci @State isMoreIconOnClick: boolean = false 16323b3eb3cSopenharmony_ci @State fontSize: number = 1 16423b3eb3cSopenharmony_ci 16523b3eb3cSopenharmony_ci dialogController: CustomDialogController | null = new CustomDialogController({ 16623b3eb3cSopenharmony_ci builder: ComposeTitleBarDialog({ 16723b3eb3cSopenharmony_ci cancel: () => { 16823b3eb3cSopenharmony_ci }, 16923b3eb3cSopenharmony_ci confirm: () => { 17023b3eb3cSopenharmony_ci }, 17123b3eb3cSopenharmony_ci itemComposeTitleDialog: this.item, 17223b3eb3cSopenharmony_ci composeTitleBarDialog: this.item.label ? this.item.label : '', 17323b3eb3cSopenharmony_ci fontSize: this.fontSize, 17423b3eb3cSopenharmony_ci }), 17523b3eb3cSopenharmony_ci maskColor: Color.Transparent, 17623b3eb3cSopenharmony_ci isModal: true, 17723b3eb3cSopenharmony_ci customStyle: true, 17823b3eb3cSopenharmony_ci }) 17923b3eb3cSopenharmony_ci 18023b3eb3cSopenharmony_ci getMoreIconFgColor() { 18123b3eb3cSopenharmony_ci return this.isMoreIconOnClick ? 18223b3eb3cSopenharmony_ci $r('sys.color.ohos_id_color_titlebar_icon_pressed') : 18323b3eb3cSopenharmony_ci $r('sys.color.ohos_id_color_titlebar_icon') 18423b3eb3cSopenharmony_ci } 18523b3eb3cSopenharmony_ci 18623b3eb3cSopenharmony_ci getMoreIconBgColor() { 18723b3eb3cSopenharmony_ci if (this.isMoreIconOnClick) { 18823b3eb3cSopenharmony_ci return $r('sys.color.ohos_id_color_click_effect') 18923b3eb3cSopenharmony_ci } else if (this.isMoreIconOnHover) { 19023b3eb3cSopenharmony_ci return $r('sys.color.ohos_id_color_hover') 19123b3eb3cSopenharmony_ci } else { 19223b3eb3cSopenharmony_ci return Color.Transparent 19323b3eb3cSopenharmony_ci } 19423b3eb3cSopenharmony_ci } 19523b3eb3cSopenharmony_ci 19623b3eb3cSopenharmony_ci aboutToAppear() { 19723b3eb3cSopenharmony_ci try { 19823b3eb3cSopenharmony_ci let uiContent: UIContext = this.getUIContext(); 19923b3eb3cSopenharmony_ci this.isFollowingSystemFontScale = uiContent.isFollowingSystemFontScale(); 20023b3eb3cSopenharmony_ci this.maxFontScale = uiContent.getMaxFontScale(); 20123b3eb3cSopenharmony_ci } catch (err) { 20223b3eb3cSopenharmony_ci let code: number = (err as BusinessError).code; 20323b3eb3cSopenharmony_ci let message: string = (err as BusinessError).message; 20423b3eb3cSopenharmony_ci hilog.error(0x3900, 'ComposeTitleBar', `Failed to init fontsizescale info, cause, code: ${code}, message: ${message}`); 20523b3eb3cSopenharmony_ci } 20623b3eb3cSopenharmony_ci if (this.menuItems) { 20723b3eb3cSopenharmony_ci this.menuItems.forEach((item, index) => { 20823b3eb3cSopenharmony_ci if (item.isEnabled && this.firstFocusableIndex == -1 && 20923b3eb3cSopenharmony_ci index > CollapsibleMenuSection.maxCountOfVisibleItems - 2) { 21023b3eb3cSopenharmony_ci this.firstFocusableIndex = this.index * 1000 + index + 1 21123b3eb3cSopenharmony_ci } 21223b3eb3cSopenharmony_ci }) 21323b3eb3cSopenharmony_ci } 21423b3eb3cSopenharmony_ci } 21523b3eb3cSopenharmony_ci 21623b3eb3cSopenharmony_ci decideFontScale(): number { 21723b3eb3cSopenharmony_ci try { 21823b3eb3cSopenharmony_ci let uiContent: UIContext = this.getUIContext(); 21923b3eb3cSopenharmony_ci this.systemFontScale = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1; 22023b3eb3cSopenharmony_ci if (!this.isFollowingSystemFontScale) { 22123b3eb3cSopenharmony_ci return 1; 22223b3eb3cSopenharmony_ci } 22323b3eb3cSopenharmony_ci return Math.min(this.systemFontScale, this.maxFontScale); 22423b3eb3cSopenharmony_ci } catch (exception) { 22523b3eb3cSopenharmony_ci let code: number = (exception as BusinessError).code; 22623b3eb3cSopenharmony_ci let message: string = (exception as BusinessError).message; 22723b3eb3cSopenharmony_ci hilog.error(0x3900, 'ComposeTitleBar', `Faild to decideFontScale,cause, code: ${code}, message: ${message}`); 22823b3eb3cSopenharmony_ci return 1; 22923b3eb3cSopenharmony_ci } 23023b3eb3cSopenharmony_ci } 23123b3eb3cSopenharmony_ci 23223b3eb3cSopenharmony_ci 23323b3eb3cSopenharmony_ci build() { 23423b3eb3cSopenharmony_ci Column() { 23523b3eb3cSopenharmony_ci Row() { 23623b3eb3cSopenharmony_ci if (this.menuItems) { 23723b3eb3cSopenharmony_ci if (this.menuItems.length <= CollapsibleMenuSection.maxCountOfVisibleItems) { 23823b3eb3cSopenharmony_ci ForEach(this.menuItems, (item: ComposeTitleBarMenuItem, index:number) => { 23923b3eb3cSopenharmony_ci ImageMenuItem({ item: item, index: this.index * 1000 + index + 1 }) 24023b3eb3cSopenharmony_ci }) 24123b3eb3cSopenharmony_ci } else { 24223b3eb3cSopenharmony_ci ForEach(this.menuItems.slice(0, CollapsibleMenuSection.maxCountOfVisibleItems - 1), 24323b3eb3cSopenharmony_ci (item:ComposeTitleBarMenuItem, index:number) => { 24423b3eb3cSopenharmony_ci ImageMenuItem({ item: item, index: this.index * 1000 + index + 1 }) 24523b3eb3cSopenharmony_ci }) 24623b3eb3cSopenharmony_ci 24723b3eb3cSopenharmony_ci Row() { 24823b3eb3cSopenharmony_ci Image(PUBLIC_MORE) 24923b3eb3cSopenharmony_ci .width(ImageMenuItem.imageSize) 25023b3eb3cSopenharmony_ci .draggable(false) 25123b3eb3cSopenharmony_ci .height(ImageMenuItem.imageSize) 25223b3eb3cSopenharmony_ci .fillColor($r('sys.color.icon_primary')) 25323b3eb3cSopenharmony_ci .focusable(true) 25423b3eb3cSopenharmony_ci } 25523b3eb3cSopenharmony_ci .width(ImageMenuItem.imageHotZoneWidth) 25623b3eb3cSopenharmony_ci .height(ImageMenuItem.imageHotZoneWidth) 25723b3eb3cSopenharmony_ci .borderRadius(ImageMenuItem.buttonBorderRadius) 25823b3eb3cSopenharmony_ci .foregroundColor(this.getMoreIconFgColor()) 25923b3eb3cSopenharmony_ci .backgroundColor(this.getMoreIconBgColor()) 26023b3eb3cSopenharmony_ci .justifyContent(FlexAlign.Center) 26123b3eb3cSopenharmony_ci .stateStyles({ 26223b3eb3cSopenharmony_ci focused: { 26323b3eb3cSopenharmony_ci .border({ 26423b3eb3cSopenharmony_ci radius: $r('sys.float.ohos_id_corner_radius_clicked'), 26523b3eb3cSopenharmony_ci width: ImageMenuItem.focusBorderWidth, 26623b3eb3cSopenharmony_ci color: $r('sys.color.ohos_id_color_focused_outline'), 26723b3eb3cSopenharmony_ci style: BorderStyle.Solid 26823b3eb3cSopenharmony_ci }) 26923b3eb3cSopenharmony_ci }, 27023b3eb3cSopenharmony_ci normal: { 27123b3eb3cSopenharmony_ci .border({ 27223b3eb3cSopenharmony_ci radius: $r('sys.float.ohos_id_corner_radius_clicked'), 27323b3eb3cSopenharmony_ci width: 0 27423b3eb3cSopenharmony_ci }) 27523b3eb3cSopenharmony_ci } 27623b3eb3cSopenharmony_ci }) 27723b3eb3cSopenharmony_ci .onFocus(() => this.isMoreIconOnFocus = true) 27823b3eb3cSopenharmony_ci .onBlur(() => this.isMoreIconOnFocus = false) 27923b3eb3cSopenharmony_ci .onHover((isOn) => this.isMoreIconOnHover = isOn) 28023b3eb3cSopenharmony_ci .onKeyEvent((event) => { 28123b3eb3cSopenharmony_ci if (event.keyCode !== KeyCode.KEYCODE_ENTER && 28223b3eb3cSopenharmony_ci event.keyCode !== KeyCode.KEYCODE_SPACE) { 28323b3eb3cSopenharmony_ci return 28423b3eb3cSopenharmony_ci } 28523b3eb3cSopenharmony_ci if (event.type === KeyType.Down) { 28623b3eb3cSopenharmony_ci this.isMoreIconOnClick = true 28723b3eb3cSopenharmony_ci } 28823b3eb3cSopenharmony_ci if (event.type === KeyType.Up) { 28923b3eb3cSopenharmony_ci this.isMoreIconOnClick = false 29023b3eb3cSopenharmony_ci } 29123b3eb3cSopenharmony_ci }) 29223b3eb3cSopenharmony_ci .onTouch((event) => { 29323b3eb3cSopenharmony_ci if (event.type === TouchType.Down) { 29423b3eb3cSopenharmony_ci this.isMoreIconOnClick = true 29523b3eb3cSopenharmony_ci } 29623b3eb3cSopenharmony_ci if (event.type === TouchType.Up || event.type === TouchType.Cancel) { 29723b3eb3cSopenharmony_ci this.isMoreIconOnClick = false 29823b3eb3cSopenharmony_ci if (this.fontSize >= this.minFontSize) { 29923b3eb3cSopenharmony_ci this.dialogController?.close() 30023b3eb3cSopenharmony_ci } 30123b3eb3cSopenharmony_ci } 30223b3eb3cSopenharmony_ci }) 30323b3eb3cSopenharmony_ci .onClick(() => this.isPopupShown = true) 30423b3eb3cSopenharmony_ci .gesture( 30523b3eb3cSopenharmony_ci LongPressGesture({ repeat: false, duration: this.longPressTime }) 30623b3eb3cSopenharmony_ci .onAction((event: GestureEvent) => { 30723b3eb3cSopenharmony_ci this.fontSize = this.decideFontScale(); 30823b3eb3cSopenharmony_ci if (event) { 30923b3eb3cSopenharmony_ci if (this.fontSize >= this.minFontSize) { 31023b3eb3cSopenharmony_ci this.dialogController?.open() 31123b3eb3cSopenharmony_ci } 31223b3eb3cSopenharmony_ci } 31323b3eb3cSopenharmony_ci })) 31423b3eb3cSopenharmony_ci .bindPopup(this.isPopupShown, { 31523b3eb3cSopenharmony_ci builder: this.popupBuilder, 31623b3eb3cSopenharmony_ci placement: Placement.Bottom, 31723b3eb3cSopenharmony_ci popupColor: Color.White, 31823b3eb3cSopenharmony_ci enableArrow: false, 31923b3eb3cSopenharmony_ci onStateChange: (e) => { 32023b3eb3cSopenharmony_ci this.isPopupShown = e.isVisible 32123b3eb3cSopenharmony_ci if (!e.isVisible) { 32223b3eb3cSopenharmony_ci this.isMoreIconOnClick = false 32323b3eb3cSopenharmony_ci } 32423b3eb3cSopenharmony_ci } 32523b3eb3cSopenharmony_ci }) 32623b3eb3cSopenharmony_ci } 32723b3eb3cSopenharmony_ci } 32823b3eb3cSopenharmony_ci } 32923b3eb3cSopenharmony_ci } 33023b3eb3cSopenharmony_ci .height('100%') 33123b3eb3cSopenharmony_ci .margin({ right: $r('sys.float.ohos_id_default_padding_end') }) 33223b3eb3cSopenharmony_ci .justifyContent(FlexAlign.Center) 33323b3eb3cSopenharmony_ci } 33423b3eb3cSopenharmony_ci 33523b3eb3cSopenharmony_ci @Builder 33623b3eb3cSopenharmony_ci popupBuilder() { 33723b3eb3cSopenharmony_ci Column() { 33823b3eb3cSopenharmony_ci if (this.menuItems) { 33923b3eb3cSopenharmony_ci ForEach(this.menuItems.slice(CollapsibleMenuSection.maxCountOfVisibleItems - 1, 34023b3eb3cSopenharmony_ci this.menuItems.length), 34123b3eb3cSopenharmony_ci (item:ComposeTitleBarMenuItem, index:number) => { 34223b3eb3cSopenharmony_ci ImageMenuItem({ item: item, index: this.index * 1000 + 34323b3eb3cSopenharmony_ci CollapsibleMenuSection.maxCountOfVisibleItems + index, isPopup: true }) 34423b3eb3cSopenharmony_ci }) 34523b3eb3cSopenharmony_ci } 34623b3eb3cSopenharmony_ci } 34723b3eb3cSopenharmony_ci .width(ImageMenuItem.imageHotZoneWidth + 34823b3eb3cSopenharmony_ci CollapsibleMenuSection.focusPadding * CollapsibleMenuSection.marginsNum) 34923b3eb3cSopenharmony_ci .margin({ top: CollapsibleMenuSection.focusPadding, bottom: CollapsibleMenuSection.focusPadding }) 35023b3eb3cSopenharmony_ci .onAppear(() => { 35123b3eb3cSopenharmony_ci focusControl.requestFocus(ImageMenuItem.focusablePrefix + 35223b3eb3cSopenharmony_ci this.firstFocusableIndex) 35323b3eb3cSopenharmony_ci }) 35423b3eb3cSopenharmony_ci } 35523b3eb3cSopenharmony_ci} 35623b3eb3cSopenharmony_ci 35723b3eb3cSopenharmony_ci@Component 35823b3eb3cSopenharmony_cistruct ImageMenuItem { 35923b3eb3cSopenharmony_ci item: ComposeTitleBarMenuItem = {} as ComposeTitleBarMenuItem; 36023b3eb3cSopenharmony_ci index: number = 0 36123b3eb3cSopenharmony_ci longPressTime: number = 500; 36223b3eb3cSopenharmony_ci minFontSize: number = 1.75; 36323b3eb3cSopenharmony_ci isFollowingSystemFontScale: boolean = false; 36423b3eb3cSopenharmony_ci maxFontScale: number = 1; 36523b3eb3cSopenharmony_ci systemFontScale?: number = 1; 36623b3eb3cSopenharmony_ci isPopup: boolean = false; 36723b3eb3cSopenharmony_ci 36823b3eb3cSopenharmony_ci static readonly imageSize = 24 36923b3eb3cSopenharmony_ci static readonly imageHotZoneWidth = 48 37023b3eb3cSopenharmony_ci static readonly buttonBorderRadius = 8 37123b3eb3cSopenharmony_ci static readonly focusBorderWidth = 2 37223b3eb3cSopenharmony_ci static readonly disabledImageOpacity = 0.4 37323b3eb3cSopenharmony_ci static readonly focusablePrefix = "Id-ComposeTitleBar-ImageMenuItem-" 37423b3eb3cSopenharmony_ci 37523b3eb3cSopenharmony_ci @State isOnFocus: boolean = false 37623b3eb3cSopenharmony_ci @State isOnHover: boolean = false 37723b3eb3cSopenharmony_ci @State isOnClick: boolean = false 37823b3eb3cSopenharmony_ci @Prop fontSize: number = 1 37923b3eb3cSopenharmony_ci 38023b3eb3cSopenharmony_ci dialogController: CustomDialogController | null = new CustomDialogController({ 38123b3eb3cSopenharmony_ci builder: ComposeTitleBarDialog({ 38223b3eb3cSopenharmony_ci cancel: () => { 38323b3eb3cSopenharmony_ci }, 38423b3eb3cSopenharmony_ci confirm: () => { 38523b3eb3cSopenharmony_ci }, 38623b3eb3cSopenharmony_ci itemComposeTitleDialog: this.item, 38723b3eb3cSopenharmony_ci composeTitleBarDialog: this.item.label ? this.item.label : this.textDialog(), 38823b3eb3cSopenharmony_ci fontSize: this.fontSize, 38923b3eb3cSopenharmony_ci }), 39023b3eb3cSopenharmony_ci maskColor: Color.Transparent, 39123b3eb3cSopenharmony_ci isModal: true, 39223b3eb3cSopenharmony_ci customStyle: true, 39323b3eb3cSopenharmony_ci }) 39423b3eb3cSopenharmony_ci 39523b3eb3cSopenharmony_ci private textDialog(): ResourceStr { 39623b3eb3cSopenharmony_ci if (this.item.value === PUBLIC_MORE) { 39723b3eb3cSopenharmony_ci return $r('sys.string.ohos_toolbar_more'); 39823b3eb3cSopenharmony_ci } else if (this.item.value === PUBLIC_BACK) { 39923b3eb3cSopenharmony_ci return $r('sys.string.icon_back'); 40023b3eb3cSopenharmony_ci } else { 40123b3eb3cSopenharmony_ci return this.item.label ? this.item.label : ''; 40223b3eb3cSopenharmony_ci } 40323b3eb3cSopenharmony_ci } 40423b3eb3cSopenharmony_ci 40523b3eb3cSopenharmony_ci getFgColor() { 40623b3eb3cSopenharmony_ci return this.isOnClick 40723b3eb3cSopenharmony_ci ? $r('sys.color.ohos_id_color_titlebar_icon_pressed') 40823b3eb3cSopenharmony_ci : $r('sys.color.ohos_id_color_titlebar_icon') 40923b3eb3cSopenharmony_ci } 41023b3eb3cSopenharmony_ci 41123b3eb3cSopenharmony_ci getBgColor() { 41223b3eb3cSopenharmony_ci if (this.isOnClick) { 41323b3eb3cSopenharmony_ci return $r('sys.color.ohos_id_color_click_effect') 41423b3eb3cSopenharmony_ci } else if (this.isOnHover) { 41523b3eb3cSopenharmony_ci return $r('sys.color.ohos_id_color_hover') 41623b3eb3cSopenharmony_ci } else { 41723b3eb3cSopenharmony_ci return Color.Transparent 41823b3eb3cSopenharmony_ci } 41923b3eb3cSopenharmony_ci } 42023b3eb3cSopenharmony_ci 42123b3eb3cSopenharmony_ci aboutToAppear() { 42223b3eb3cSopenharmony_ci try { 42323b3eb3cSopenharmony_ci let uiContent: UIContext = this.getUIContext(); 42423b3eb3cSopenharmony_ci this.isFollowingSystemFontScale = uiContent.isFollowingSystemFontScale(); 42523b3eb3cSopenharmony_ci this.maxFontScale = uiContent.getMaxFontScale(); 42623b3eb3cSopenharmony_ci } catch (err) { 42723b3eb3cSopenharmony_ci let code: number = (err as BusinessError).code; 42823b3eb3cSopenharmony_ci let message: string = (err as BusinessError).message; 42923b3eb3cSopenharmony_ci hilog.error(0x3900, 'ComposeTitleBar', `Failed to init fontsizescale info, cause, code: ${code}, message: ${message}`); 43023b3eb3cSopenharmony_ci } 43123b3eb3cSopenharmony_ci } 43223b3eb3cSopenharmony_ci 43323b3eb3cSopenharmony_ci 43423b3eb3cSopenharmony_ci decideFontScale(): number { 43523b3eb3cSopenharmony_ci try { 43623b3eb3cSopenharmony_ci let uiContent: UIContext = this.getUIContext(); 43723b3eb3cSopenharmony_ci this.systemFontScale = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1; 43823b3eb3cSopenharmony_ci if (!this.isFollowingSystemFontScale) { 43923b3eb3cSopenharmony_ci return 1; 44023b3eb3cSopenharmony_ci } 44123b3eb3cSopenharmony_ci return Math.min(this.systemFontScale, this.maxFontScale); 44223b3eb3cSopenharmony_ci } catch (exception) { 44323b3eb3cSopenharmony_ci let code: number = (exception as BusinessError).code; 44423b3eb3cSopenharmony_ci let message: string = (exception as BusinessError).message; 44523b3eb3cSopenharmony_ci hilog.error(0x3900, 'ComposeTitleBar', `Faild to decideFontScale,cause, code: ${code}, message: ${message}`); 44623b3eb3cSopenharmony_ci return 1; 44723b3eb3cSopenharmony_ci } 44823b3eb3cSopenharmony_ci } 44923b3eb3cSopenharmony_ci 45023b3eb3cSopenharmony_ci 45123b3eb3cSopenharmony_ci build() { 45223b3eb3cSopenharmony_ci Row() { 45323b3eb3cSopenharmony_ci Image(this.item?.value) 45423b3eb3cSopenharmony_ci .matchTextDirection(this.item?.value === PUBLIC_BACK ? true : false) 45523b3eb3cSopenharmony_ci .width(ImageMenuItem.imageSize) 45623b3eb3cSopenharmony_ci .draggable(false) 45723b3eb3cSopenharmony_ci .height(ImageMenuItem.imageSize) 45823b3eb3cSopenharmony_ci .focusable(this.item?.isEnabled) 45923b3eb3cSopenharmony_ci .key(ImageMenuItem.focusablePrefix + this.index) 46023b3eb3cSopenharmony_ci .fillColor($r('sys.color.ohos_id_color_text_primary')) 46123b3eb3cSopenharmony_ci } 46223b3eb3cSopenharmony_ci .width(ImageMenuItem.imageHotZoneWidth) 46323b3eb3cSopenharmony_ci .height(ImageMenuItem.imageHotZoneWidth) 46423b3eb3cSopenharmony_ci .borderRadius(ImageMenuItem.buttonBorderRadius) 46523b3eb3cSopenharmony_ci .foregroundColor(this.getFgColor()) 46623b3eb3cSopenharmony_ci .backgroundColor(this.getBgColor()) 46723b3eb3cSopenharmony_ci .justifyContent(FlexAlign.Center) 46823b3eb3cSopenharmony_ci .opacity(this.item?.isEnabled ? 1 : ImageMenuItem.disabledImageOpacity) 46923b3eb3cSopenharmony_ci .stateStyles({ 47023b3eb3cSopenharmony_ci focused: { 47123b3eb3cSopenharmony_ci .border({ 47223b3eb3cSopenharmony_ci radius: $r('sys.float.ohos_id_corner_radius_clicked'), 47323b3eb3cSopenharmony_ci width: ImageMenuItem.focusBorderWidth, 47423b3eb3cSopenharmony_ci color: $r('sys.color.ohos_id_color_focused_outline'), 47523b3eb3cSopenharmony_ci style: BorderStyle.Solid 47623b3eb3cSopenharmony_ci }) 47723b3eb3cSopenharmony_ci }, 47823b3eb3cSopenharmony_ci normal: { 47923b3eb3cSopenharmony_ci .border({ 48023b3eb3cSopenharmony_ci radius: $r('sys.float.ohos_id_corner_radius_clicked'), 48123b3eb3cSopenharmony_ci width: 0 48223b3eb3cSopenharmony_ci }) 48323b3eb3cSopenharmony_ci } 48423b3eb3cSopenharmony_ci }) 48523b3eb3cSopenharmony_ci .onFocus(() => { 48623b3eb3cSopenharmony_ci if (!this.item?.isEnabled) { 48723b3eb3cSopenharmony_ci return 48823b3eb3cSopenharmony_ci } 48923b3eb3cSopenharmony_ci this.isOnFocus = true 49023b3eb3cSopenharmony_ci }) 49123b3eb3cSopenharmony_ci .onBlur(() => this.isOnFocus = false) 49223b3eb3cSopenharmony_ci .onHover((isOn) => { 49323b3eb3cSopenharmony_ci if (!this.item?.isEnabled) { 49423b3eb3cSopenharmony_ci return 49523b3eb3cSopenharmony_ci } 49623b3eb3cSopenharmony_ci this.isOnHover = isOn 49723b3eb3cSopenharmony_ci }) 49823b3eb3cSopenharmony_ci .onKeyEvent((event) => { 49923b3eb3cSopenharmony_ci if (!this.item?.isEnabled) { 50023b3eb3cSopenharmony_ci return 50123b3eb3cSopenharmony_ci } 50223b3eb3cSopenharmony_ci if (event.keyCode !== KeyCode.KEYCODE_ENTER && 50323b3eb3cSopenharmony_ci event.keyCode !== KeyCode.KEYCODE_SPACE) { 50423b3eb3cSopenharmony_ci return 50523b3eb3cSopenharmony_ci } 50623b3eb3cSopenharmony_ci if (event.type === KeyType.Down) { 50723b3eb3cSopenharmony_ci this.isOnClick = true 50823b3eb3cSopenharmony_ci } 50923b3eb3cSopenharmony_ci if (event.type === KeyType.Up) { 51023b3eb3cSopenharmony_ci this.isOnClick = false 51123b3eb3cSopenharmony_ci } 51223b3eb3cSopenharmony_ci }) 51323b3eb3cSopenharmony_ci .onTouch((event) => { 51423b3eb3cSopenharmony_ci if (!this.item?.isEnabled) { 51523b3eb3cSopenharmony_ci return 51623b3eb3cSopenharmony_ci } 51723b3eb3cSopenharmony_ci if (event.type === TouchType.Down) { 51823b3eb3cSopenharmony_ci this.isOnClick = true 51923b3eb3cSopenharmony_ci } 52023b3eb3cSopenharmony_ci if (event.type === TouchType.Up || event.type === TouchType.Cancel) { 52123b3eb3cSopenharmony_ci this.isOnClick = false 52223b3eb3cSopenharmony_ci if (this.fontSize >= this.minFontSize && this.isPopup === false) { 52323b3eb3cSopenharmony_ci this.dialogController?.close() 52423b3eb3cSopenharmony_ci } 52523b3eb3cSopenharmony_ci } 52623b3eb3cSopenharmony_ci }) 52723b3eb3cSopenharmony_ci .onClick(() => { 52823b3eb3cSopenharmony_ci if (this.item) { 52923b3eb3cSopenharmony_ci return this.item.isEnabled && this.item.action?.() 53023b3eb3cSopenharmony_ci } 53123b3eb3cSopenharmony_ci }) 53223b3eb3cSopenharmony_ci .gesture( 53323b3eb3cSopenharmony_ci LongPressGesture({ repeat: false, duration: this.longPressTime }) 53423b3eb3cSopenharmony_ci .onAction((event: GestureEvent) => { 53523b3eb3cSopenharmony_ci this.fontSize = this.decideFontScale(); 53623b3eb3cSopenharmony_ci if (event) { 53723b3eb3cSopenharmony_ci if (this.fontSize >= this.minFontSize && this.isPopup === false) { 53823b3eb3cSopenharmony_ci this.dialogController?.open() 53923b3eb3cSopenharmony_ci } 54023b3eb3cSopenharmony_ci } 54123b3eb3cSopenharmony_ci })) 54223b3eb3cSopenharmony_ci } 54323b3eb3cSopenharmony_ci} 54423b3eb3cSopenharmony_ci 54523b3eb3cSopenharmony_ci 54623b3eb3cSopenharmony_ci/** 54723b3eb3cSopenharmony_ci * ComposeTitleBarDialog 54823b3eb3cSopenharmony_ci */ 54923b3eb3cSopenharmony_ci@CustomDialog 55023b3eb3cSopenharmony_cistruct ComposeTitleBarDialog { 55123b3eb3cSopenharmony_ci itemComposeTitleDialog: ComposeTitleBarMenuItem = {} as ComposeTitleBarMenuItem; 55223b3eb3cSopenharmony_ci callbackId: number | undefined = undefined; 55323b3eb3cSopenharmony_ci composeTitleBarDialog?: ResourceStr = ''; 55423b3eb3cSopenharmony_ci mainWindowStage: window.Window | undefined = undefined; 55523b3eb3cSopenharmony_ci controller?: CustomDialogController; 55623b3eb3cSopenharmony_ci minFontSize: number = 1.75; 55723b3eb3cSopenharmony_ci maxFontSize: number = 3.2; 55823b3eb3cSopenharmony_ci screenWidth: number = 640; 55923b3eb3cSopenharmony_ci verticalScreenLines: number = 6; 56023b3eb3cSopenharmony_ci horizontalsScreenLines: number = 1; 56123b3eb3cSopenharmony_ci @StorageLink('mainWindow') mainWindow: Promise<window.Window> | undefined = undefined; 56223b3eb3cSopenharmony_ci @State fontSize: number = 1; 56323b3eb3cSopenharmony_ci @State maxLines: number = 1; 56423b3eb3cSopenharmony_ci @StorageProp('windowStandardHeight') windowStandardHeight: number = 0; 56523b3eb3cSopenharmony_ci cancel: () => void = () => { 56623b3eb3cSopenharmony_ci } 56723b3eb3cSopenharmony_ci confirm: () => void = () => { 56823b3eb3cSopenharmony_ci } 56923b3eb3cSopenharmony_ci 57023b3eb3cSopenharmony_ci build() { 57123b3eb3cSopenharmony_ci if (this.composeTitleBarDialog) { 57223b3eb3cSopenharmony_ci Column() { 57323b3eb3cSopenharmony_ci Image(this.itemComposeTitleDialog.value) 57423b3eb3cSopenharmony_ci .width(IMAGE_SIZE) 57523b3eb3cSopenharmony_ci .height(IMAGE_SIZE) 57623b3eb3cSopenharmony_ci .margin({ 57723b3eb3cSopenharmony_ci top: $r('sys.float.padding_level24'), 57823b3eb3cSopenharmony_ci bottom: $r('sys.float.padding_level8'), 57923b3eb3cSopenharmony_ci }) 58023b3eb3cSopenharmony_ci .fillColor($r('sys.color.icon_primary')) 58123b3eb3cSopenharmony_ci Column() { 58223b3eb3cSopenharmony_ci Text(this.composeTitleBarDialog) 58323b3eb3cSopenharmony_ci .fontSize(TEXT_EDITABLE_DIALOG) 58423b3eb3cSopenharmony_ci .textOverflow({ overflow: TextOverflow.Ellipsis }) 58523b3eb3cSopenharmony_ci .maxLines(this.maxLines) 58623b3eb3cSopenharmony_ci .width('100%') 58723b3eb3cSopenharmony_ci .textAlign(TextAlign.Center) 58823b3eb3cSopenharmony_ci .fontColor($r('sys.color.font_primary')) 58923b3eb3cSopenharmony_ci } 59023b3eb3cSopenharmony_ci .width('100%') 59123b3eb3cSopenharmony_ci .padding({ 59223b3eb3cSopenharmony_ci left: $r('sys.float.padding_level4'), 59323b3eb3cSopenharmony_ci right: $r('sys.float.padding_level4'), 59423b3eb3cSopenharmony_ci bottom: $r('sys.float.padding_level12'), 59523b3eb3cSopenharmony_ci }) 59623b3eb3cSopenharmony_ci } 59723b3eb3cSopenharmony_ci .width(this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG) 59823b3eb3cSopenharmony_ci .constraintSize({ minHeight: this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG }) 59923b3eb3cSopenharmony_ci .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK) 60023b3eb3cSopenharmony_ci .shadow(ShadowStyle.OUTER_DEFAULT_LG) 60123b3eb3cSopenharmony_ci .borderRadius($r('sys.float.corner_radius_level10')) 60223b3eb3cSopenharmony_ci } else { 60323b3eb3cSopenharmony_ci Column() { 60423b3eb3cSopenharmony_ci Image(this.itemComposeTitleDialog.value) 60523b3eb3cSopenharmony_ci .width(IMAGE_SIZE) 60623b3eb3cSopenharmony_ci .height(IMAGE_SIZE) 60723b3eb3cSopenharmony_ci .fillColor($r('sys.color.icon_primary')) 60823b3eb3cSopenharmony_ci } 60923b3eb3cSopenharmony_ci .width(this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG) 61023b3eb3cSopenharmony_ci .constraintSize({ minHeight: this.fontSize === this.maxFontSize ? MAX_DIALOG : MIN_DIALOG }) 61123b3eb3cSopenharmony_ci .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK) 61223b3eb3cSopenharmony_ci .shadow(ShadowStyle.OUTER_DEFAULT_LG) 61323b3eb3cSopenharmony_ci .borderRadius($r('sys.float.corner_radius_level10')) 61423b3eb3cSopenharmony_ci .justifyContent(FlexAlign.Center) 61523b3eb3cSopenharmony_ci } 61623b3eb3cSopenharmony_ci } 61723b3eb3cSopenharmony_ci 61823b3eb3cSopenharmony_ci async aboutToAppear(): Promise<void> { 61923b3eb3cSopenharmony_ci let context = this.getUIContext().getHostContext() as common.UIAbilityContext; 62023b3eb3cSopenharmony_ci this.mainWindowStage = context.windowStage.getMainWindowSync(); 62123b3eb3cSopenharmony_ci let properties: window.WindowProperties = this.mainWindowStage.getWindowProperties(); 62223b3eb3cSopenharmony_ci let rect = properties.windowRect; 62323b3eb3cSopenharmony_ci if (px2vp(rect.height) > this.screenWidth) { 62423b3eb3cSopenharmony_ci this.maxLines = this.verticalScreenLines; 62523b3eb3cSopenharmony_ci } else { 62623b3eb3cSopenharmony_ci this.maxLines = this.horizontalsScreenLines; 62723b3eb3cSopenharmony_ci } 62823b3eb3cSopenharmony_ci } 62923b3eb3cSopenharmony_ci} 63023b3eb3cSopenharmony_ci 63123b3eb3cSopenharmony_ciexport { ComposeTitleBar }