diff --git a/atomicservice_config.gni b/atomicservice_config.gni index 77087d83bb3456db266cd14f5682ca33b0c7bf21..3ce772a6fbcbaaa6c61a413a9cc7a938770e217e 100644 --- a/atomicservice_config.gni +++ b/atomicservice_config.gni @@ -45,6 +45,10 @@ if (is_ohos_standard_system) { } } +declare_args() { + advanced_ui_component_feature_pc = false +} + use_hilog = is_mingw || is_mac || is_linux || is_ohos || is_ohos_standard_system # Config gen_obj diff --git a/bundle.json b/bundle.json index 11d851ef75947ae7f183b854354a8af0c4960de8..2551aca6bfdc208b256c1fb04fe87bbeeb861182 100644 --- a/bundle.json +++ b/bundle.json @@ -10,7 +10,7 @@ "name": "advanced_ui_component", "subsystem": "arkui", "syscap": [], - "features": [], + "features": ["advanced_ui_component_feature_pc"], "adapted_system_type": [ "standard" ], diff --git a/customappbar/interfaces/BUILD.gn b/customappbar/interfaces/BUILD.gn index 0031e25baf40f81a8fcf6111e6232ed88c8e14ae..8f9dd5d72d981b1fd4b355a2e3eee8872c037dbd 100644 --- a/customappbar/interfaces/BUILD.gn +++ b/customappbar/interfaces/BUILD.gn @@ -13,9 +13,15 @@ import("//build/ohos.gni") import("//build/templates/abc/ohos_abc.gni") +import("//foundation/arkui/advanced_ui_component/atomicservice_config.gni") ohos_abc("custom_app_bar_abc") { - sources = [ "custom_app_bar.js" ] + sources = [] + if (advanced_ui_component_feature_pc) { + sources += [ "custom_app_bar_forpc.js" ] + } else { + sources += [ "custom_app_bar.js" ] + } output_name = "custom_app_bar" install_images = [ "system" ] module_install_dir = "etc/abc/arkui" diff --git a/customappbar/interfaces/custom_app_bar_forpc.js b/customappbar/interfaces/custom_app_bar_forpc.js new file mode 100644 index 0000000000000000000000000000000000000000..2d726b36d77be6bb7bf9a0e266280d0245bcbf21 --- /dev/null +++ b/customappbar/interfaces/custom_app_bar_forpc.js @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2025 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. + */ + +if (!('finalizeConstruction' in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, 'finalizeConstruction', () => { }); +} +const bundleManager = requireNapi('bundle.bundleManager'); +const hilog = requireNapi('hilog'); +const window = requireNapi('window'); +const LengthMetrics = requireNapi('arkui.node').LengthMetrics; + +const BUTTON_WIDTH = 28; +const VIEW_HEIGHT = 28; +const IMAGE_SIZE = 16; +const MENU_RADIUS = 15.5; +const DIVIDER_HEIGHT = 16.5; +const DIVIDER_WIDTH = 0.5; +const MENU_BUTTON_MARGIN = 2; +const VIEW_MARGIN_TOP = 18; +const VIEW_MARGIN_RIGHT = 24; +const MENU_MARGIN_TOP = 10; +const MENU_BACK_BLUR = 5; +const MENU_BORDER_WIDTH = '0.5px'; +const ICON_FILL_COLOR_DEFAULT = '#182431'; +const BORDER_COLOR_DEFAULT = '#33000000'; +const MENU_BACK_COLOR = '#99FFFFFF'; +const ARKUI_APP_BAR_COLOR_CONFIGURATION = 'arkui_app_bar_color_configuration'; +const ARKUI_APP_BAR_MENU_SAFE_AREA = 'arkui_app_bar_menu_safe_area'; +const ARKUI_APP_BAR_CONTENT_SAFE_AREA = 'arkui_app_bar_content_safe_area'; +const ARKUI_APP_BG_COLOR = 'arkui_app_bg_color'; +const maximizeButtonResourceId = 125829923; +const recoverButtonResourceId = 125829925; +const EVENT_NAME_CUSTOM_APP_BAR_MENU_CLICK = 'arkui_custom_app_bar_menu_click'; +const EVENT_NAME_CUSTOM_APP_BAR_DID_BUILD = 'arkui_custom_app_bar_did_build'; +const EVENT_NAME_MIN_CLICK = 'arkui_custom_min_click'; +const EVENT_NAME_CLOSE_CLICK = 'arkui_custom_close_click'; +const EVENT_NAME_CUSTOM_MAX_CLICK = 'arkui_custom_max_click'; + +class ColorGroup { + constructor(light, dark) { + this.light = '#000000'; + this.dark = '#FFFFFF'; + this.light = light; + this.dark = dark; + } +} +const colorMap = new Map([ + [ICON_FILL_COLOR_DEFAULT, new ColorGroup('#182431', '#e5ffffff')], + [BORDER_COLOR_DEFAULT, new ColorGroup('#33182431', '#4Dffffff')], + [MENU_BACK_COLOR, new ColorGroup('#99FFFFFF', '#33000000')], +]); +export class CustomAppBarForPC extends ViewPU { + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === 'function') { + this.paramsGenerator_ = paramsLambda; + } + this.__menuResource = new ObservedPropertyObjectPU({ + bundleName: '', + moduleName: '', + params: [], + id: 125830217, + type: 20000 + }, this, 'menuResource'); + this.__closeResource = new ObservedPropertyObjectPU({ + bundleName: '', + moduleName: '', + params: [], + id: 125831084, + type: 20000 + }, this, 'closeResource'); + this.__menuFillColor = new ObservedPropertySimplePU(this.getResourceColor(ICON_FILL_COLOR_DEFAULT), this, 'menuFillColor'); + this.__menubarBorderColor = new ObservedPropertySimplePU(this.getResourceColor(BORDER_COLOR_DEFAULT), this, 'menubarBorderColor'); + this.__menubarBackColor = new ObservedPropertySimplePU(this.getResourceColor(MENU_BACK_COLOR), this, 'menubarBackColor'); + this.__dividerBackgroundColor = new ObservedPropertySimplePU(this.getResourceColor(BORDER_COLOR_DEFAULT), this, 'dividerBackgroundColor'); + this.__contentBgColor = new ObservedPropertySimplePU('#FFFFFFFF', this, 'contentBgColor'); + this.__contentMarginTop = new ObservedPropertySimplePU('0vp', this, 'contentMarginTop'); + this.__contentMarginLeft = new ObservedPropertySimplePU('0vp', this, 'contentMarginLeft'); + this.__contentMarginRight = new ObservedPropertySimplePU('0vp', this, 'contentMarginRight'); + this.__contentMarginBottom = new ObservedPropertySimplePU('0vp', this, 'contentMarginBottom'); + this.__menuMarginTop = new ObservedPropertySimplePU('10vp', this, 'menuMarginTop'); + this.__isAdaptPC = new ObservedPropertySimplePU(false, this, 'isAdaptPC'); + this.__maximizeResource = new ObservedPropertyObjectPU(this.getIconResource(maximizeButtonResourceId), this, 'maximizeResource'); + this.isDark = true; + this.windowClass = undefined; + this.setInitiallyProvidedValue(params); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(params) { + if (params.menuResource !== undefined) { + this.menuResource = params.menuResource; + } + if (params.closeResource !== undefined) { + this.closeResource = params.closeResource; + } + if (params.menuFillColor !== undefined) { + this.menuFillColor = params.menuFillColor; + } + if (params.menubarBorderColor !== undefined) { + this.menubarBorderColor = params.menubarBorderColor; + } + if (params.menubarBackColor !== undefined) { + this.menubarBackColor = params.menubarBackColor; + } + if (params.dividerBackgroundColor !== undefined) { + this.dividerBackgroundColor = params.dividerBackgroundColor; + } + if (params.contentBgColor !== undefined) { + this.contentBgColor = params.contentBgColor; + } + if (params.contentMarginTop !== undefined) { + this.contentMarginTop = params.contentMarginTop; + } + if (params.contentMarginLeft !== undefined) { + this.contentMarginLeft = params.contentMarginLeft; + } + if (params.contentMarginRight !== undefined) { + this.contentMarginRight = params.contentMarginRight; + } + if (params.contentMarginBottom !== undefined) { + this.contentMarginBottom = params.contentMarginBottom; + } + if (params.menuMarginTop !== undefined) { + this.menuMarginTop = params.menuMarginTop; + } + if (params.isAdaptPC !== undefined) { + this.isAdaptPC = params.isAdaptPC; + } + if (params.maximizeResource !== undefined) { + this.maximizeResource = params.maximizeResource; + } + if (params.isDark !== undefined) { + this.isDark = params.isDark; + } + if (params.windowClass !== undefined) { + this.windowClass = params.windowClass; + } + } + updateStateVars(params) { + } + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__menuResource.purgeDependencyOnElmtId(rmElmtId); + this.__closeResource.purgeDependencyOnElmtId(rmElmtId); + this.__menuFillColor.purgeDependencyOnElmtId(rmElmtId); + this.__menubarBorderColor.purgeDependencyOnElmtId(rmElmtId); + this.__menubarBackColor.purgeDependencyOnElmtId(rmElmtId); + this.__dividerBackgroundColor.purgeDependencyOnElmtId(rmElmtId); + this.__contentBgColor.purgeDependencyOnElmtId(rmElmtId); + this.__contentMarginTop.purgeDependencyOnElmtId(rmElmtId); + this.__contentMarginLeft.purgeDependencyOnElmtId(rmElmtId); + this.__contentMarginRight.purgeDependencyOnElmtId(rmElmtId); + this.__contentMarginBottom.purgeDependencyOnElmtId(rmElmtId); + this.__menuMarginTop.purgeDependencyOnElmtId(rmElmtId); + this.__isAdaptPC.purgeDependencyOnElmtId(rmElmtId); + this.__maximizeResource.purgeDependencyOnElmtId(rmElmtId); + } + aboutToBeDeleted() { + this.__menuResource.aboutToBeDeleted(); + this.__closeResource.aboutToBeDeleted(); + this.__menuFillColor.aboutToBeDeleted(); + this.__menubarBorderColor.aboutToBeDeleted(); + this.__menubarBackColor.aboutToBeDeleted(); + this.__dividerBackgroundColor.aboutToBeDeleted(); + this.__contentBgColor.aboutToBeDeleted(); + this.__contentMarginTop.aboutToBeDeleted(); + this.__contentMarginLeft.aboutToBeDeleted(); + this.__contentMarginRight.aboutToBeDeleted(); + this.__contentMarginBottom.aboutToBeDeleted(); + this.__menuMarginTop.aboutToBeDeleted(); + this.__isAdaptPC.aboutToBeDeleted(); + this.__maximizeResource.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + get menuResource() { + return this.__menuResource.get(); + } + set menuResource(newValue) { + this.__menuResource.set(newValue); + } + get closeResource() { + return this.__closeResource.get(); + } + set closeResource(newValue) { + this.__closeResource.set(newValue); + } + get menuFillColor() { + return this.__menuFillColor.get(); + } + set menuFillColor(newValue) { + this.__menuFillColor.set(newValue); + } + get menubarBorderColor() { + return this.__menubarBorderColor.get(); + } + set menubarBorderColor(newValue) { + this.__menubarBorderColor.set(newValue); + } + get menubarBackColor() { + return this.__menubarBackColor.get(); + } + set menubarBackColor(newValue) { + this.__menubarBackColor.set(newValue); + } + get dividerBackgroundColor() { + return this.__dividerBackgroundColor.get(); + } + set dividerBackgroundColor(newValue) { + this.__dividerBackgroundColor.set(newValue); + } + get contentBgColor() { + return this.__contentBgColor.get(); + } + set contentBgColor(newValue) { + this.__contentBgColor.set(newValue); + } + get contentMarginTop() { + return this.__contentMarginTop.get(); + } + set contentMarginTop(newValue) { + this.__contentMarginTop.set(newValue); + } + get contentMarginLeft() { + return this.__contentMarginLeft.get(); + } + set contentMarginLeft(newValue) { + this.__contentMarginLeft.set(newValue); + } + get contentMarginRight() { + return this.__contentMarginRight.get(); + } + set contentMarginRight(newValue) { + this.__contentMarginRight.set(newValue); + } + get contentMarginBottom() { + return this.__contentMarginBottom.get(); + } + set contentMarginBottom(newValue) { + this.__contentMarginBottom.set(newValue); + } + get menuMarginTop() { + return this.__menuMarginTop.get(); + } + set menuMarginTop(newValue) { + this.__menuMarginTop.set(newValue); + } + get isAdaptPC() { + return this.__isAdaptPC.get(); + } + set isAdaptPC(newValue) { + this.__isAdaptPC.set(newValue); + } + get maximizeResource() { + return this.__maximizeResource.get(); + } + set maximizeResource(newValue) { + this.__maximizeResource.set(newValue); + } + async aboutToAppear() { + let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE; + try { + bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => { + hilog.info(0x0000, 'testTag', 'getBundleInfoForSelf successfully. Data: %{public}s', JSON.stringify(data.hapModulesInfo[0].deviceTypes)); + let devicetype = data.hapModulesInfo[0].deviceTypes; + for (let i = 0; i < devicetype.length; i++) { + if (devicetype[i] === '2in1') { + this.isAdaptPC = true; + break; + } + } + }).catch((err) => { + hilog.error(0x0000, 'testTag', 'getBundleInfoForSelf failed. Cause: %{public}s', err.message); + }); + } + catch (err) { + let message = err.message; + hilog.error(0x0000, 'testTag', 'getBundleInfoForSelf failed: %{public}s', message); + } + let context = getContext(this); + context?.windowStage?.getMainWindow().then(data => { + this.windowClass = data; + this.windowClass?.setWindowDecorVisible(false); + this.windowClass?.setWindowTitleButtonVisible(false, false, false); + this.windowClass?.on('windowStatusChange', (windowStatusType) => { + console.info('windowStatusChange windowStatusType: ' + JSON.stringify(windowStatusType)); + if (windowStatusType === window.WindowStatusType.FULL_SCREEN) { + this.maximizeResource = this.getIconResource(recoverButtonResourceId); + } + else { + this.maximizeResource = this.getIconResource(maximizeButtonResourceId); + } + }); + + }).catch((err) => { + if (err.code) { + console.error(`Failed to obtain the main window. Cause code: ${err.code}, message: ${err.message}`); + } + }); + } + aboutToDisappear() { + this.windowClass?.off('windowStatusChange'); + } + parseBoolean(value) { + if (value === 'true') { + return true; + } + return false; + } + getResourceColor(defaultColor) { + if (colorMap.has(defaultColor)) { + const colorGroup = colorMap.get(defaultColor); + if (colorGroup) { + return this.isDark ? colorGroup.dark : colorGroup.light; + } + } + return defaultColor; + } + /** + * 监听来自arkui侧的回调 + * @param eventName 事件名 + * @param param 参数 + */ + setCustomCallback(eventName, param) { + if (eventName === ARKUI_APP_BAR_COLOR_CONFIGURATION) { + this.onColorConfigurationUpdate(this.parseBoolean(param)); + } + else if (eventName === ARKUI_APP_BAR_MENU_SAFE_AREA) { + let value = Number(param) + MENU_MARGIN_TOP; + this.menuMarginTop = value.toString(); + } + else if (eventName === ARKUI_APP_BAR_CONTENT_SAFE_AREA) { + //top left right bottom + let splitArray = param.split('|'); + if (splitArray.length < 4) { + return; + } + this.contentMarginTop = splitArray[0]; + this.contentMarginLeft = splitArray[1]; + this.contentMarginRight = splitArray[2]; + this.contentMarginBottom = splitArray[3]; + } + else if (eventName === ARKUI_APP_BG_COLOR) { + this.contentBgColor = param; + } + } + /** + * menu按钮点击 + */ + onMenuButtonClick() { + ContainerAppBar.callNative(EVENT_NAME_CUSTOM_APP_BAR_MENU_CLICK); + } + /** + * 点击放大按钮 + */ + onMaximizeButtonClick() { + ContainerModal.callNative(EVENT_NAME_CUSTOM_MAX_CLICK); + } + /** + * 点击最小化按钮 + */ + onMinimizeButtonClick() { + ContainerModal.callNative(EVENT_NAME_MIN_CLICK); + } + /** + * 点击关闭按钮 + */ + onCloseButtonClick() { + ContainerModal.callNative(EVENT_NAME_CLOSE_CLICK); + } + onDidBuild() { + ContainerAppBar.callNative(EVENT_NAME_CUSTOM_APP_BAR_DID_BUILD); + } + dividerLine(parent = null) { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Divider.create(); + Divider.id('AtomicServiceDividerId'); + Divider.vertical(true); + Divider.color(this.dividerBackgroundColor); + Divider.lineCap(LineCapStyle.Round); + Divider.strokeWidth(DIVIDER_WIDTH); + Divider.height(DIVIDER_HEIGHT); + }, Divider); + } + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Column.create(); + Column.height('100%'); + Column.width('100%'); + Column.justifyContent(FlexAlign.End); + Column.backgroundColor(this.contentBgColor); + Column.hitTestBehavior(HitTestMode.Transparent); + }, Column); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Stack.create({ alignContent: Alignment.TopEnd }); + Stack.id('AtomicServiceContainerId'); + Stack.height('100%'); + Stack.width('100%'); + Stack.backgroundColor(Color.Transparent); + Stack.hitTestBehavior(HitTestMode.Transparent); + }, Stack); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.padding({ + top: this.contentMarginTop, + left: this.contentMarginLeft, + right: this.contentMarginRight, + bottom: this.contentMarginBottom + }); + Row.height('100%'); + Row.width('100%'); + Row.id('AtomicServiceStageId'); + Row.backgroundColor(Color.Blue); + }, Row); + Row.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.id('AtomicServiceMenubarRowId'); + Row.justifyContent(FlexAlign.End); + Row.margin({ top: LengthMetrics.vp(VIEW_MARGIN_TOP), end: LengthMetrics.vp(VIEW_MARGIN_RIGHT) }); + Row.height(VIEW_HEIGHT); + Row.hitTestBehavior(HitTestMode.Transparent); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.borderRadius(MENU_RADIUS); + Row.borderColor(this.menubarBorderColor); + Row.backgroundColor(this.menubarBackColor); + Row.backdropBlur(MENU_BACK_BLUR); + Row.borderWidth(MENU_BORDER_WIDTH); + Row.borderColor($r('sys.color.icon_fourth')); + Row.height(VIEW_HEIGHT); + Row.align(Alignment.Top); + Row.draggable(false); + Row.id('AtomicServiceMenubarId'); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithChild(); + Button.id('AtomicServiceMenuId'); + Button.type(ButtonType.Normal); + Button.borderRadius({ topLeft: MENU_RADIUS, bottomLeft: MENU_RADIUS }); + Button.backgroundColor(Color.Transparent); + Button.width(BUTTON_WIDTH + MENU_BUTTON_MARGIN); + Button.height(VIEW_HEIGHT); + Gesture.create(GesturePriority.Low); + TapGesture.create(); + TapGesture.onAction(() => { + this.onMenuButtonClick(); + }); + TapGesture.pop(); + Gesture.pop(); + }, Button); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Image.create(this.menuResource); + Image.width(IMAGE_SIZE); + Image.height(IMAGE_SIZE); + Image.fillColor(this.menuFillColor); + Image.draggable(false); + Image.interpolation(ImageInterpolation.High); + Image.margin({ start: LengthMetrics.vp(MENU_BUTTON_MARGIN) }); + }, Image); + Button.pop(); + this.dividerLine.bind(this)(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + If.create(); + if (this.isAdaptPC) { + this.ifElseBranchUpdateFunction(0, () => { + if (!If.canRetake('AtomicServiceexpendId')) { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithChild(); + Button.id('AtomicServiceexpendId'); + Button.type(ButtonType.Normal); + Button.backgroundColor(Color.Transparent); + Button.width(BUTTON_WIDTH); + Button.height(VIEW_HEIGHT); + Gesture.create(GesturePriority.Low); + TapGesture.create(); + TapGesture.onAction(() => { + this.onMaximizeButtonClick(); + }); + TapGesture.pop(); + Gesture.pop(); + }, Button); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Image.create(this.maximizeResource); + Image.width(IMAGE_SIZE); + Image.height(IMAGE_SIZE); + Image.fillColor(this.menuFillColor); + Image.draggable(false); + Image.interpolation(ImageInterpolation.High); + }, Image); + Button.pop(); + } + this.dividerLine.bind(this)(); + }); + } + else { + this.ifElseBranchUpdateFunction(1, () => { + }); + } + }, If); + If.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithChild(); + Button.id('AtomicServiceMinusId'); + Button.type(ButtonType.Normal); + Button.backgroundColor(Color.Transparent); + Button.width(BUTTON_WIDTH); + Button.height(VIEW_HEIGHT); + Gesture.create(GesturePriority.Low); + TapGesture.create(); + TapGesture.onAction(() => { + this.onMinimizeButtonClick(); + }); + TapGesture.pop(); + Gesture.pop(); + }, Button); + this.observeComponentCreation2((elmtId, isInitialRender) => { + SymbolGlyph.create({ 'id': -1, 'type': 40000, params: ['sys.symbol.minus'], 'bundleName': '__harDefaultBundleName__', 'moduleName': '__harDefaultModuleName__' }); + SymbolGlyph.fontSize(IMAGE_SIZE); + SymbolGlyph.fontColor([this.menuFillColor]); + SymbolGlyph.draggable(false); + }, SymbolGlyph); + Button.pop(); + this.dividerLine.bind(this)(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Button.createWithChild(); + Button.id('AtomicServiceCloseId'); + Button.type(ButtonType.Normal); + Button.backgroundColor(Color.Transparent); + Button.borderRadius({ topRight: MENU_RADIUS, bottomRight: MENU_RADIUS }); + Button.width(BUTTON_WIDTH + MENU_BUTTON_MARGIN); + Button.height(VIEW_HEIGHT); + Gesture.create(GesturePriority.Low); + TapGesture.create(); + TapGesture.onAction(() => { + this.onCloseButtonClick(); + }); + TapGesture.pop(); + Gesture.pop(); + }, Button); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Image.create(this.closeResource); + Image.width(IMAGE_SIZE); + Image.height(IMAGE_SIZE); + Image.fillColor(this.menuFillColor); + Image.margin({ end: LengthMetrics.vp(MENU_BUTTON_MARGIN) }); + Image.draggable(false); + Image.interpolation(ImageInterpolation.High); + }, Image); + Button.pop(); + Row.pop(); + Row.pop(); + Stack.pop(); + Column.pop(); + } + onColorConfigurationUpdate(isDark) { + this.isDark = isDark; + this.menuFillColor = this.getResourceColor(ICON_FILL_COLOR_DEFAULT); + this.menubarBorderColor = this.getResourceColor(BORDER_COLOR_DEFAULT); + this.dividerBackgroundColor = this.getResourceColor(BORDER_COLOR_DEFAULT); + this.menubarBackColor = this.getResourceColor(MENU_BACK_COLOR); + } + getIconResource(resourceId) { + return { + bundleName: '', + moduleName: '', + params: [], + id: resourceId, + type: 20000 + }; + } + rerender() { + this.updateDirtyElements(); + } +} + +loadCustomAppbar(new CustomAppBarForPC(undefined, {})); +ViewStackProcessor.StopGetAccessRecording(); diff --git a/customappbar/source/custom_app_bar_forpc.ets b/customappbar/source/custom_app_bar_forpc.ets new file mode 100644 index 0000000000000000000000000000000000000000..e6b60e87ebd54a317dd10af780b9e68453ec0d99 --- /dev/null +++ b/customappbar/source/custom_app_bar_forpc.ets @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2025 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 { bundleManager, common } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { LengthMetrics, window } from '@kit.ArkUI'; + +const BUTTON_WIDTH: number = 28; +const VIEW_HEIGHT: number = 28; +const IMAGE_SIZE: number = 16; +const MENU_RADIUS: number = 15.5; +const DIVIDER_HEIGHT: number = 16.5; +const DIVIDER_WIDTH: number = 0.5; +const MENU_BUTTON_MARGIN: number = 2; +const VIEW_MARGIN_TOP: number = 18; +const VIEW_MARGIN_RIGHT: number = 24; +const MENU_MARGIN_TOP: number = 10; +const MENU_BACK_BLUR: number = 5; +const MENU_BORDER_WIDTH: string = '0.5px'; + +const ICON_FILL_COLOR_DEFAULT: string = '#182431'; +const BORDER_COLOR_DEFAULT: string = '#33000000'; +const MENU_BACK_COLOR: string = '#99FFFFFF'; + +const ARKUI_APP_BAR_COLOR_CONFIGURATION: string = 'arkui_app_bar_color_configuration'; +const ARKUI_APP_BAR_MENU_SAFE_AREA: string = 'arkui_app_bar_menu_safe_area'; +const ARKUI_APP_BAR_CONTENT_SAFE_AREA: string = 'arkui_app_bar_content_safe_area'; +const ARKUI_APP_BG_COLOR: string = 'arkui_app_bg_color'; +const maximizeButtonResourceId: number = 125829923; +const recoverButtonResourceId: number = 125829925; +const EVENT_NAME_CUSTOM_APP_BAR_MENU_CLICK = 'arkui_custom_app_bar_menu_click'; +const EVENT_NAME_CUSTOM_APP_BAR_DID_BUILD = 'arkui_custom_app_bar_did_build'; +const EVENT_NAME_MIN_CLICK: string = 'arkui_custom_min_click'; +const EVENT_NAME_CLOSE_CLICK: string = 'arkui_custom_close_click'; +const EVENT_NAME_CUSTOM_MAX_CLICK: string = 'arkui_custom_max_click'; + +class ColorGroup { + public light: string = '#000000'; + public dark: string = '#FFFFFF'; + + constructor(light: string, dark: string) { + this.light = light; + this.dark = dark; + } +} + +const colorMap: Map = new Map([ + [ICON_FILL_COLOR_DEFAULT, new ColorGroup('#182431', '#e5ffffff')], + [BORDER_COLOR_DEFAULT, new ColorGroup('#33182431', '#4Dffffff')], + [MENU_BACK_COLOR, new ColorGroup('#99FFFFFF', '#33000000')], +]); + +@Component +export struct CustomAppBarForPC { + @State menuResource: Resource = { + bundleName: '', + moduleName: '', + params: [], + id: 125830217, + type: 20000 + }; + @State closeResource: Resource = { + bundleName: '', + moduleName: '', + params: [], + id: 125831084, + type: 20000 + }; + @State menuFillColor: string = this.getResourceColor(ICON_FILL_COLOR_DEFAULT); + @State menubarBorderColor: string = this.getResourceColor(BORDER_COLOR_DEFAULT); + @State menubarBackColor: string = this.getResourceColor(MENU_BACK_COLOR); + @State dividerBackgroundColor: string = this.getResourceColor(BORDER_COLOR_DEFAULT); + @State contentBgColor: string = '#FFFFFFFF'; + @State contentMarginTop: string = '0vp'; + @State contentMarginLeft: string = '0vp'; + @State contentMarginRight: string = '0vp'; + @State contentMarginBottom: string = '0vp'; + @State menuMarginTop: string = '10vp'; + @State isAdaptPC: boolean = false; + @State maximizeResource: Resource = this.getIconResource(maximizeButtonResourceId); + private isDark: boolean = true; + private windowClass: window.Window | undefined = undefined; + + async aboutToAppear(): Promise { + let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE; + try { + bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => { + hilog.info(0x0000, 'testTag', 'getBundleInfoForSelf successfully. Data: %{public}s', + JSON.stringify(data.hapModulesInfo[0].deviceTypes)); + let devicetype = data.hapModulesInfo[0].deviceTypes; + for (let i = 0; i < devicetype.length; i++) { + if (devicetype[i] === '2in1') { + this.isAdaptPC = true; + break; + } + } + }).catch((err: BusinessError) => { + hilog.error(0x0000, 'testTag', 'getBundleInfoForSelf failed. Cause: %{public}s', err.message); + }); + } catch (err) { + let message = (err as BusinessError).message; + hilog.error(0x0000, 'testTag', 'getBundleInfoForSelf failed: %{public}s', message); + } + + let context = getContext(this) as common.UIAbilityContext; + context?.windowStage?.getMainWindow().then( + data => { + this.windowClass = data; + this.windowClass?.setWindowDecorVisible(false); + this.windowClass?.setWindowTitleButtonVisible(false, false, false); + this.windowClass?.on('windowStatusChange', (windowStatusType) => { + console.info('windowStatusChange windowStatusType: ' + JSON.stringify(windowStatusType)); + if (windowStatusType === window.WindowStatusType.FULL_SCREEN) { + this.maximizeResource = this.getIconResource(recoverButtonResourceId); + } else { + this.maximizeResource = this.getIconResource(maximizeButtonResourceId); + } + }); + } + ).catch((err: BusinessError) => { + if (err.code) { + console.error(`Failed to obtain the main window. Cause code: ${err.code}, message: ${err.message}`); + } + }); + } + + aboutToDisappear(): void { + this.windowClass?.off('windowStatusChange'); + } + + parseBoolean(value: string): boolean { + if (value === 'true') { + return true; + } + return false; + } + + getResourceColor(defaultColor: string): string { + if (colorMap.has(defaultColor)) { + const colorGroup = colorMap.get(defaultColor); + if (colorGroup) { + return this.isDark ? colorGroup.dark : colorGroup.light; + } + } + return defaultColor; + } + + /** + * 监听来自arkui侧的回调 + * @param eventName 事件名 + * @param param 参数 + */ + setCustomCallback(eventName: string, param: string) { + if (eventName === ARKUI_APP_BAR_COLOR_CONFIGURATION) { + this.onColorConfigurationUpdate(this.parseBoolean(param)); + } else if (eventName === ARKUI_APP_BAR_MENU_SAFE_AREA) { + let value = Number(param) + MENU_MARGIN_TOP; + this.menuMarginTop = value.toString(); + } else if (eventName === ARKUI_APP_BAR_CONTENT_SAFE_AREA) { + //top left right bottom + let splitArray: string[] = param.split('|'); + if (splitArray.length < 4) { + return; + } + this.contentMarginTop = splitArray[0]; + this.contentMarginLeft = splitArray[1]; + this.contentMarginRight = splitArray[2]; + this.contentMarginBottom = splitArray[3]; + } else if (eventName === ARKUI_APP_BG_COLOR) { + this.contentBgColor = param; + } + } + + /** + * menu按钮点击 + */ + onMenuButtonClick(): void { + } + + /** + * 点击放大按钮 + */ + onMaximizeButtonClick(): void { + } + + /** + * 点击最小化按钮 + */ + onMinimizeButtonClick(): void { + } + + /** + * 点击关闭按钮 + */ + onCloseButtonClick(): void { + } + + onDidBuild(): void { + } + + @Builder + dividerLine() { + Divider() + .id('AtomicServiceDividerId') + .vertical(true) + .color(this.dividerBackgroundColor) + .lineCap(LineCapStyle.Round) + .strokeWidth(DIVIDER_WIDTH) + .height(DIVIDER_HEIGHT) + } + + build() { + Column() { + Stack({ alignContent: Alignment.TopEnd }) { + Row() { + } + .padding({ + top: this.contentMarginTop, + left: this.contentMarginLeft, + right: this.contentMarginRight, + bottom: this.contentMarginBottom + }) + .height('100%') + .width('100%') + .id('AtomicServiceStageId') + .backgroundColor(Color.Blue) + + Row() { + Row() { + Button() { + Image(this.menuResource) + .width(IMAGE_SIZE) + .height(IMAGE_SIZE) + .fillColor(this.menuFillColor) + .draggable(false) + .interpolation(ImageInterpolation.High) + .margin({ start: LengthMetrics.vp(MENU_BUTTON_MARGIN) }) + } + .id('AtomicServiceMenuId') + .type(ButtonType.Normal) + .borderRadius({ topLeft: MENU_RADIUS, bottomLeft: MENU_RADIUS }) + .backgroundColor(Color.Transparent) + .width(BUTTON_WIDTH + MENU_BUTTON_MARGIN) + .height(VIEW_HEIGHT) + .gesture(TapGesture().onAction(() => { + this.onMenuButtonClick(); + })) + + this.dividerLine() + + if (this.isAdaptPC) { + Button() { + Image(this.maximizeResource) + .width(IMAGE_SIZE) + .height(IMAGE_SIZE) + .fillColor(this.menuFillColor) + .draggable(false) + .interpolation(ImageInterpolation.High) + } + .id('AtomicServiceexpendId') + .type(ButtonType.Normal) + .backgroundColor(Color.Transparent) + .width(BUTTON_WIDTH) + .height(VIEW_HEIGHT) + .gesture(TapGesture().onAction(() => { + this.onMaximizeButtonClick(); + })) + + this.dividerLine() + } + + Button() { + SymbolGlyph($r('sys.symbol.minus')) + .fontSize(IMAGE_SIZE) + .fontColor([this.menuFillColor]) + .draggable(false) + } + .id('AtomicServiceMinusId') + .type(ButtonType.Normal) + .backgroundColor(Color.Transparent) + .width(BUTTON_WIDTH) + .height(VIEW_HEIGHT) + .gesture(TapGesture().onAction(() => { + this.onMinimizeButtonClick(); + })) + + this.dividerLine() + + Button() { + Image(this.closeResource) + .width(IMAGE_SIZE) + .height(IMAGE_SIZE) + .fillColor(this.menuFillColor) + .margin({ end: LengthMetrics.vp(MENU_BUTTON_MARGIN) }) + .draggable(false) + .interpolation(ImageInterpolation.High) + } + .id('AtomicServiceCloseId') + .type(ButtonType.Normal) + .backgroundColor(Color.Transparent) + .borderRadius({ topRight: MENU_RADIUS, bottomRight: MENU_RADIUS }) + .width(BUTTON_WIDTH + MENU_BUTTON_MARGIN) + .height(VIEW_HEIGHT) + .gesture(TapGesture().onAction(() => { + this.onCloseButtonClick(); + })) + } + .borderRadius(MENU_RADIUS) + .borderColor(this.menubarBorderColor) + .backgroundColor(this.menubarBackColor) + .backdropBlur(MENU_BACK_BLUR) + .borderWidth(MENU_BORDER_WIDTH) + .borderColor($r('sys.color.icon_fourth')) + .height(VIEW_HEIGHT) + .align(Alignment.Top) + .draggable(false) + .id('AtomicServiceMenubarId') + } + .id('AtomicServiceMenubarRowId') + .justifyContent(FlexAlign.End) + .margin({ top: LengthMetrics.vp(VIEW_MARGIN_TOP), end: LengthMetrics.vp(VIEW_MARGIN_RIGHT) }) + .height(VIEW_HEIGHT) + .hitTestBehavior(HitTestMode.Transparent) + } + .id('AtomicServiceContainerId') + .height('100%') + .width('100%') + .backgroundColor(Color.Transparent) + .hitTestBehavior(HitTestMode.Transparent) + } + .height('100%') + .width('100%') + .justifyContent(FlexAlign.End) + .backgroundColor(this.contentBgColor) + .hitTestBehavior(HitTestMode.Transparent) + } + + private onColorConfigurationUpdate(isDark: boolean) { + this.isDark = isDark; + this.menuFillColor = this.getResourceColor(ICON_FILL_COLOR_DEFAULT); + this.menubarBorderColor = this.getResourceColor(BORDER_COLOR_DEFAULT); + this.dividerBackgroundColor = this.getResourceColor(BORDER_COLOR_DEFAULT); + this.menubarBackColor = this.getResourceColor(MENU_BACK_COLOR); + } + + private getIconResource(resourceId: number): Resource { + return { + bundleName: '', + moduleName: '', + params: [], + id: resourceId, + type: 20000 + }; + } +}