diff --git a/entry/src/main/ets/common/ContextUtils.ets b/entry/src/main/ets/common/ContextUtils.ets new file mode 100644 index 00000000..6c466d65 --- /dev/null +++ b/entry/src/main/ets/common/ContextUtils.ets @@ -0,0 +1,31 @@ +/* + * 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. + */ +// [Start Common_ContextUtils] +export class ContextUtils { + public static context: Context | undefined; + + static setContext(context: Context): void { + ContextUtils.context = context; + } + + static getContext(uiContext?: UIContext): Context | undefined { + if (uiContext) { + return uiContext.getHostContext(); + } + + return ContextUtils.context; + } +} +// [End Common_ContextUtils] \ No newline at end of file diff --git a/entry/src/main/ets/common/UIContext.ets b/entry/src/main/ets/common/UIContext.ets new file mode 100644 index 00000000..397907d6 --- /dev/null +++ b/entry/src/main/ets/common/UIContext.ets @@ -0,0 +1,57 @@ +/* + * 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. + */ +// [Start Common_PixelUtils] +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { display } from '@kit.ArkUI'; + +const DOMAIN = 0x0000; + +export class PixelUtils { + public static uiContext: UIContext | undefined; + + static setUIContext(uiContext: UIContext): void { + PixelUtils.uiContext = uiContext; + } + + static vp2px(vpValue: number, uiContext?: UIContext): number | undefined { + let _uiContext = uiContext ?? PixelUtils.uiContext; + if (!_uiContext || !_uiContext.isAvailable()) { + let displayClass = display.getDefaultDisplaySync(); + let density = displayClass.densityPixels; + return vpValue * density; + } + return _uiContext.vp2px(vpValue) + } + + static fp2px(fpValue: number, uiContext?: UIContext): number | undefined { + let _uiContext = uiContext ?? PixelUtils.uiContext; + if (!_uiContext || !_uiContext.isAvailable()) { + hilog.error(DOMAIN, 'testTag', `Can't get UIContext`); + return undefined; + } + return _uiContext.fp2px(fpValue) + } + + lpx2px(lpxValue: number, uiContext?: UIContext): number | undefined { + let _uiContext = uiContext ?? PixelUtils.uiContext; + if (!_uiContext || !_uiContext.isAvailable()) { + hilog.error(DOMAIN, 'testTag', `Can't get UIContext`); + return undefined; + } + return _uiContext.lpx2px(lpxValue) + } +} + +// [End Common_PixelUtils] \ No newline at end of file diff --git a/entry/src/main/ets/common/Utils.ets b/entry/src/main/ets/common/Utils.ets new file mode 100644 index 00000000..3e1f1729 --- /dev/null +++ b/entry/src/main/ets/common/Utils.ets @@ -0,0 +1,60 @@ +/* + * 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. + */ +// [Start Common_Utils] +// common/Utils.ets +import { hilog } from '@kit.PerformanceAnalysisKit'; + +const DOMAIN = 0x0000; + +export class PixelUtil { + static uiContext: UIContext | undefined; + + static setUIContext(uiContext: UIContext): void { + PixelUtil.uiContext = uiContext; + } + + static removeUIContext(): void { + PixelUtil.uiContext = undefined; + } + + static vp2px(vpValue: number, uiContext?: UIContext): number | undefined { + let _uiContext = uiContext ?? PixelUtil.uiContext; + if (!_uiContext || !_uiContext.isAvailable()) { + hilog.error(DOMAIN, 'testTag', `Can't get UIContext`); + return undefined; + } + return _uiContext.vp2px(vpValue) + } + + static fp2px(fpValue: number, uiContext?: UIContext): number | undefined { + let _uiContext = uiContext ?? PixelUtil.uiContext; + if (!_uiContext || !_uiContext.isAvailable()) { + hilog.error(DOMAIN, 'testTag', `Can't get UIContext`); + return undefined; + } + return _uiContext.fp2px(fpValue) + } + + lpx2px(lpxValue: number, uiContext?: UIContext): number | undefined { + let _uiContext = uiContext ?? PixelUtil.uiContext; + if (!_uiContext || !_uiContext.isAvailable()) { + hilog.error(DOMAIN, 'testTag', `Can't get UIContext`); + return undefined; + } + return _uiContext.lpx2px(lpxValue) + } +} + +// [End Common_Utils] \ No newline at end of file diff --git a/entry/src/main/ets/common/WindowUtils.ets b/entry/src/main/ets/common/WindowUtils.ets new file mode 100644 index 00000000..296a3962 --- /dev/null +++ b/entry/src/main/ets/common/WindowUtils.ets @@ -0,0 +1,62 @@ +/* + * 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. + */ +// [Start Common_WindowUtils] +// common/WindowUtils.ets +import { display, window } from '@kit.ArkUI'; +import { hilog } from '@kit.PerformanceAnalysisKit'; + +const DOMAIN = 0x0000; + +export class WindowUIContextUtils { + public static activeUIContext: UIContext | undefined; + + static registerWindowCallback(windowClass: window.Window): void { + try { + windowClass.on('windowEvent', (event: window.WindowEventType) => { + if (event === window.WindowEventType.WINDOW_ACTIVE) { + try { + let uiContext = windowClass.getUIContext(); + WindowUIContextUtils.activeUIContext = uiContext; + } catch (exception) { + hilog.error(DOMAIN, 'testTag', `Can't get UIContext, ${exception}`); + } + } + }); + } catch (exception) { + console.error(`Failed to unregister callback. Cause: ${exception}`); + } + } + + static unregisterWindowCallback(windowClass: window.Window): void { + windowClass.off('windowEvent'); + } + + static setActiveUIContext(uiContext: UIContext): void { + WindowUIContextUtils.activeUIContext = uiContext; + } + + static vp2px(vpValue: number, uiContext?: UIContext): number { + let _uiContext = uiContext ?? WindowUIContextUtils.activeUIContext; + if (!_uiContext || !_uiContext.isAvailable()) { + let displayClass = display.getDefaultDisplaySync(); + let density = displayClass.densityPixels; + return vpValue * density; + } + + return _uiContext.vp2px(vpValue); + } +} + +// [End Common_WindowUtils] \ No newline at end of file diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index fc16f68b..ce9a9516 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -1,7 +1,8 @@ import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; -import { window } from '@kit.ArkUI'; -import { AppStorageV2 } from '@kit.ArkUI'; +import { window,display,AppStorageV2} from '@kit.ArkUI'; +import { WindowUIContextUtils } from '../common/WindowUtils'; +import { PixelUtils } from '../common/UIContext'; const DOMAIN = 0x0000; @@ -22,13 +23,45 @@ export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); - + let window = windowStage.getMainWindowSync(); + // 注册主窗的回调。 + WindowUIContextUtils.registerWindowCallback(window); windowStage.loadContent('pages/Index', (err) => { if (err.code) { - hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); + hilog.error(DOMAIN, 'Tag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); return; } AppStorage.setOrCreate('windowStage', windowStage); + let uiContext = window.getUIContext(); + PixelUtils.setUIContext(uiContext); + // 主窗获焦可能早于loadContent完成,需要在成功后设置保证有效。 + WindowUIContextUtils.setActiveUIContext(uiContext) + if (!uiContext) { + hilog.error(DOMAIN, 'testTag', `Can't get UIContext`); + return; + } + let screenWidth:number=display.getDefaultDisplaySync().width; + let screenHeight:number=display.getDefaultDisplaySync().height; + //定义新的宽度和高度(单位:虚拟像素) + const newWidth: number = screenWidth-uiContext.px2vp(200); + const newHeight: number = screenHeight-uiContext.px2vp(400); + + //基于物理分辨率在长和高上分别缩小200vp + windowStage.getMainWindow((err, mainWindow) => { + if (err.code) { + console.error(`Failed to obtain the main window. Code: ${err.code}, message: ${err.message}`); + return; + } + mainWindow.moveWindowTo(uiContext.px2vp(100),uiContext.px2vp(100)) + //调用resize方法修改窗口大小 + mainWindow.resize(newWidth, newHeight, (err) => { + if (err.code) { + console.error(`Failed to resize the window. Code: ${err.code}, message: ${err.message}`); + return; + } + console.info(`Succeeded in resizing the window to ${newWidth}x${newHeight}.`); + }); + }); hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); }); } diff --git a/entry/src/main/ets/pages/CustomStyle/Expandable.ets b/entry/src/main/ets/pages/CustomStyle/Expandable.ets new file mode 100644 index 00000000..87a6efa3 --- /dev/null +++ b/entry/src/main/ets/pages/CustomStyle/Expandable.ets @@ -0,0 +1,27 @@ + + +//一个复合式折叠组件 +@ComponentV2 +export struct Expandable { + // 通过属性传入的标题和内容 + @Param title: string|undefined=undefined; + // 控制内容区域显示与隐藏的状态 + @Consumer('isSubExpanded') isSubExpanded: boolean=true; + build(){ + // 标题行 + Row({ space: 5 }) { + // 切换按钮,显示向上或向下箭头 + Button(this.isSubExpanded ? '▲' : '▼') + .type(ButtonType.Normal) + .fontSize(14) + .onClick(() => { + // 点击按钮时,切换 isSubExpanded 状态 + this.isSubExpanded = !this.isSubExpanded; + }) + Text(this.title) + .fontSize(18) + .fontWeight(FontWeight.Bold) + .alignSelf(ItemAlign.Center) + }.width('100%') + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/EventSubWindow/ExecuteCommand.ets b/entry/src/main/ets/pages/EventSubWindow/ExecuteCommand.ets index beb7a623..7dd43fb5 100644 --- a/entry/src/main/ets/pages/EventSubWindow/ExecuteCommand.ets +++ b/entry/src/main/ets/pages/EventSubWindow/ExecuteCommand.ets @@ -6,7 +6,7 @@ export function ExecuteCommand(event:TitleButton){ if(event?.eEvent=='Execute_LoadModel'){ OCCTLoadModel(undefined,undefined) }else if(event?.eEvent=='Execute_CreateSubWindow'){ - CreateAndShowSubWindow(event.ePage); + CreateAndShowSubWindow(event.eName,event.ePage); }else if(event?.eEvent=='Execute_ExitSubWindow'){ CloseSubWindow(); } diff --git a/entry/src/main/ets/pages/EventSubWindow/SWBase.ets b/entry/src/main/ets/pages/EventSubWindow/SWBase.ets index d1e724bc..5f2bedfa 100644 --- a/entry/src/main/ets/pages/EventSubWindow/SWBase.ets +++ b/entry/src/main/ets/pages/EventSubWindow/SWBase.ets @@ -1,9 +1,9 @@ import { BusinessError } from '@kit.BasicServicesKit'; -import { window } from '@kit.ArkUI'; +import { window,UIContext} from '@kit.ArkUI'; let subWindow: window.Window | undefined = undefined; -export async function CreateAndShowSubWindow(pages:string) { +export async function CreateAndShowSubWindow(name:string,pages:string) { try { const windowStage = AppStorage.get('windowStage') as window.WindowStage; if(windowStage==null){ @@ -11,34 +11,35 @@ export async function CreateAndShowSubWindow(pages:string) { return; } let options: window.SubWindowOptions = { - title: '子窗口', + title: name, decorEnabled: true, isModal: false, maximizeSupported: false, zLevel:-1, outlineEnabled:true, }; + await windowStage.createSubWindowWithOptions('subWindow', options).then((data) => { subWindow = data; subWindow.setResizeByDragEnabled(true, (err: BusinessError) => { console.log("设置拖拽缩放", `报错信息:${err.code}, ${err.message}`) }) //子窗口创建成功后,设置子窗口的位置、大小及相关属性等。 - subWindow.moveWindowTo(150, 300) + subWindow.moveWindowTo(150, 15) //子窗口重置大小 - subWindow.resize(500, 900); + subWindow.resize(800, 1200); subWindow.setUIContent(pages, (err: BusinessError) => { if (err.code) { console.error("加载页面失败:", err); return; } - // 显示窗口 - subWindow?.showWindow((err) => { - if (err.code) { - console.error("显示窗口失败:", err); - } - }); }) + // 显示窗口 + subWindow.showWindow((err) => { + if (err.code) { + console.error("显示窗口失败:", err); + } + }); }) } catch (error) { console.error('Failed to create or show sub window:', (error as BusinessError).message); diff --git a/entry/src/main/ets/pages/EventSubWindow/SWExtrude.ets b/entry/src/main/ets/pages/EventSubWindow/SWExtrude.ets index b9478a89..f4549556 100644 --- a/entry/src/main/ets/pages/EventSubWindow/SWExtrude.ets +++ b/entry/src/main/ets/pages/EventSubWindow/SWExtrude.ets @@ -1,18 +1,29 @@ +import { Expandable } from '../CustomStyle/Expandable'; @Entry @ComponentV2 struct SWExtrude { - + // 控制内容区域显示与隐藏的状态 + @Provider('isSubExpanded') isExpanded:boolean=true; build() { - Column() { - Row({ space: 20 }) { - Blank().width(8); - } - .justifyContent(FlexAlign.SpaceBetween) - .height(56) - .width('100%') - .padding({ left: 10 }) - .backgroundColor('#f2f2f2'); - }.width('100%').height('100%'); + Column({ space: 5 }) { + Column(){ + Expandable({title:"拉伸"}); + Row(){ + // 内容区域,使用条件渲染来控制显示与隐藏 + if (this.isExpanded) { + Text('*选择曲线(0)').width(px2vp(35)).height(px2vp(35)) + Blank().width('30%') + Button('绘制曲线').width(px2vp(35)).height(px2vp(35)) + Button('选择曲线').width(px2vp(35)).height(px2vp(35)) + } + }.justifyContent(FlexAlign.Start) + } + }.width('100%') // 容器宽度 + .padding(15) // 内边距 + .backgroundColor('#ffffff') // 背景色 + .borderRadius(8) // 边框圆角 + .shadow({ radius: 4, color: '#1f000000', offsetX: 2, offsetY: 2 }) // 添加阴影 + .margin({ top: 5, left: 5, bottom: 5, right: 5 }) } } \ No newline at end of file