diff --git a/examples/Overlay/.gitignore b/examples/Overlay/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/examples/Overlay/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/examples/Overlay/AppScope/app.json5 b/examples/Overlay/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4b94fd40eeec7f45b90b7dbceb36ef78c06d12f4 --- /dev/null +++ b/examples/Overlay/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * 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. + */ + +{ + "app": { + "bundleName": "com.example.overlay", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/examples/Overlay/AppScope/resources/base/element/string.json b/examples/Overlay/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..beb6355a38843d6f018a09bb0cfde66502a64546 --- /dev/null +++ b/examples/Overlay/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "Overlay" + } + ] +} diff --git a/examples/Overlay/AppScope/resources/base/media/app_icon.png b/examples/Overlay/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/examples/Overlay/AppScope/resources/base/media/app_icon.png differ diff --git a/examples/Overlay/build-profile.json5 b/examples/Overlay/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..065a40f35f7e44457306cce8f9734c31563a1ac1 --- /dev/null +++ b/examples/Overlay/build-profile.json5 @@ -0,0 +1,55 @@ +/* + * 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.3(15)", + "runtimeOS": "HarmonyOS", + "buildOption": { + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } + } + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/Overlay/code-linter.json5 b/examples/Overlay/code-linter.json5 new file mode 100644 index 0000000000000000000000000000000000000000..28586467ee7a761c737d8654a73aed6fddbc3c71 --- /dev/null +++ b/examples/Overlay/code-linter.json5 @@ -0,0 +1,35 @@ +/* + * 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. + */ + +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/.gitignore b/examples/Overlay/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/examples/Overlay/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/examples/Overlay/entry/build-profile.json5 b/examples/Overlay/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e7569e3056e27af38e9991b7ea73ec10f3ba8a05 --- /dev/null +++ b/examples/Overlay/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/examples/Overlay/entry/hvigorfile.ts b/examples/Overlay/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/examples/Overlay/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * 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 { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/examples/Overlay/entry/obfuscation-rules.txt b/examples/Overlay/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/examples/Overlay/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/examples/Overlay/entry/oh-package.json5 b/examples/Overlay/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff --- /dev/null +++ b/examples/Overlay/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/examples/Overlay/entry/src/main/ets/entryability/EntryAbility.ets b/examples/Overlay/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f2f8b94aa24b0a50e272270e4e18b6df93ac5fd --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * 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 { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/examples/Overlay/entry/src/main/ets/extensionAbility/extensionAbility.ets b/examples/Overlay/entry/src/main/ets/extensionAbility/extensionAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b05918fe9bd37a26319ff676db4d186b0d19154e --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/extensionAbility/extensionAbility.ets @@ -0,0 +1,45 @@ +/* + * 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 { UIExtensionAbility, UIExtensionContentSession, Want } from '@kit.AbilityKit'; + +const TAG: string = '[ExampleEmbeddedAbility]' +export default class UIExtAbility extends UIExtensionAbility { + + onCreate() { + console.info(TAG, `onCreate`); + } + + onForeground() { + console.info(TAG, `onForeground`); + } + + onBackground() { + console.info(TAG, `onBackground`); + } + + onDestroy() { + console.info(TAG, `onDestroy`); + } + + onSessionCreate(want: Want, session: UIExtensionContentSession) { + console.info(TAG, `onSessionCreate, want: ${JSON.stringify(want)}`); + let param: Record = { + 'session': session + }; + let storage: LocalStorage = new LocalStorage(param); + session.loadContent('extensionAbility/pages/Index', storage); + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/extensionAbility/pages/Index.ets b/examples/Overlay/entry/src/main/ets/extensionAbility/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..aa28361efb91182c66dedde3e96008ed51b11b52 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/extensionAbility/pages/Index.ets @@ -0,0 +1,42 @@ +/* + * 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 { NavList, NavListItem } from '../../pages/utils/navigationList' + +@Entry +@Component +struct UECIndex { + pathStack: NavPathStack = new NavPathStack() + paths: NavListItem[] = [ + { name: '组件', path: 'NavIndex' }, + ] + + build() { + Navigation(this.pathStack) { + NavList({ + pages: this.paths, + onPathChange: (item: NavListItem) => { + this.pathStack.pushPath( { name: item.path } ) + } + }) + } + .onAppear(() => { + this.pathStack.pushPath({ name: 'NavIndex' }) + }) + .title('UEC弹框组件测试') + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/Index.ets b/examples/Overlay/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..ce7a3492de0efa31349fb640e26cefc374624f35 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,39 @@ +/* + * 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 { NavList, NavListItem } from './utils/navigationList' + +@Entry +@Component +struct Index { + pathStack: NavPathStack = new NavPathStack() + paths: NavListItem[] = [ + { name: '非UIExtension场景', path: 'NavIndex' }, + ] + + build() { + Navigation(this.pathStack) { + NavList({ + pages: this.paths, + onPathChange: (item: NavListItem) => { + this.pathStack.pushPath( { name: item.path } ) + } + }) + } + .title('弹框组件测试') + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/base/ParamOption.ets b/examples/Overlay/entry/src/main/ets/pages/components/base/ParamOption.ets new file mode 100644 index 0000000000000000000000000000000000000000..65a4caf8d7d4d0c86c987892eae95796fd52885f --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/base/ParamOption.ets @@ -0,0 +1,44 @@ +/* + * 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. + */ + +export class SetParam { + title?: string + func?: () => void +} + +@Component +export struct ParamOption { + title: string = 'test' + func: () => void = () => {} + + @Styles pressedStyle(){ + .backgroundColor(0x238E23) + } + + @Styles normalStyles() { + .backgroundColor(0x0000ff) + } + + build() { + Text(this.title) + .key(this.title) + .fontSize(10) + .backgroundColor(0x0000ff) + .fontColor(0xffffff) + .padding(5) + .onClick(this.func) + .stateStyles({pressed: this.pressedStyle, normal: this.normalStyles}) + } +} diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/Index.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..d15aa691e3ff3e1ce6d48974d7b569010ff9e0b4 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/Index.ets @@ -0,0 +1,56 @@ +/* + * 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 { NavList, NavListItem } from '../../utils/navigationList' + +@Builder +export function DialogIndexBuilder(name: string, param: Object) { + DialogIndex() +} + +@Entry +@Component +export struct DialogIndex { + pathStack: NavPathStack = new NavPathStack() + paths: NavListItem[] = [ + { name: 'AlertDialog', path: 'AlertDialog' }, + { name: 'ActionSheet', path: 'ActionSheet' }, + { name: 'CustomDialog', path: 'CustomDialog' }, + { name: 'PromptAction', path: 'PromptAction' }, + { name: 'DialogController', path: 'DialogController' }, + { name: 'DialogLevelOrder', path: 'DialogLevelOrder' }, + { name: 'DialogFocusable', path: 'DialogFocusable' }, + { name: 'EmbeddedDialog', path: 'EmbeddedDialogPageOne' }, + ] + + build() { + NavDestination() { + NavList({ + pages: this.paths, + onPathChange: (item: NavListItem) => { + this.pathStack.pushPath( { name: item.path } ) + } + }) + } + .title('Dialog') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/actionSheet.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/actionSheet.ets new file mode 100644 index 0000000000000000000000000000000000000000..2089d46774e1c5938298a42207882cb6e1c1c577 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/actionSheet.ets @@ -0,0 +1,129 @@ +/* + * 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. + */ + +@Builder +export function ActionSheetBuilder(name: string, param: Object) { + ActionSheetExample() +} + +@Entry +@Component +struct ActionSheetExample { + pathStack: NavPathStack = new NavPathStack() + + build() { + NavDestination() { + Column({ space: 5 }) { + Text('ActionSheet.show') + .fontColor(Color.Gray) + .width('100%') + .padding(10) + + Button('Click to Show ActionSheet') + .onClick(() => { + ActionSheet.show({ + title: 'ActionSheet title', + subtitle: 'ActionSheet subtitle', + message: 'message', + autoCancel: true, + confirm: { + defaultFocus: true, + value: 'Confirm button', + action: () => { + console.log('Get Alert Dialog handled') + } + }, + cancel: () => { + console.log('actionSheet canceled') + }, + sheets: [ + { + title: 'apples', + action: () => { + console.log('apples') + } + }, + { + title: 'bananas', + action: () => { + console.log('bananas') + } + }, + { + title: 'pears', + action: () => { + console.log('pears') + } + } + ] + }) + }) + + Text('UIContext.showActionSheet') + .fontColor(Color.Gray) + .width('100%') + .padding(10) + + Button('Click to Show ActionSheet') + .onClick(() => { + this.getUIContext().showActionSheet({ + title: 'ActionSheet title', + subtitle: 'ActionSheet subtitle', + message: 'message', + autoCancel: true, + confirm: { + defaultFocus: true, + value: 'Confirm button', + action: () => { + console.log('Get Alert Dialog handled') + } + }, + cancel: () => { + console.log('actionSheet canceled') + }, + sheets: [ + { + title: 'apples', + action: () => { + console.log('apples') + } + }, + { + title: 'bananas', + action: () => { + console.log('bananas') + } + }, + { + title: 'pears', + action: () => { + console.log('pears') + } + } + ] + }) + }) + }.width('100%').margin({ top: 5 }) + } + .title('ActionSheet') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/alertDialog.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/alertDialog.ets new file mode 100644 index 0000000000000000000000000000000000000000..24e64367badc2a889e96bc4de371cceb6b2f0799 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/alertDialog.ets @@ -0,0 +1,124 @@ +/* + * 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. + */ + +@Builder +export function AlertDialogBuilder(name: string, param: Object) { + AlertDialogExample() +} + +@Entry +@Component +struct AlertDialogExample { + pathStack: NavPathStack = new NavPathStack() + + build() { + NavDestination() { + Column({ space: 5 }) { + Text('AlertDialog.show') + .fontColor(Color.Gray) + .width('100%') + .padding(10) + + Button('three button dialog') + .margin(10) + .onClick(() => { + AlertDialog.show({ + title: 'title', + subtitle: 'subtitle', + message: 'text', + buttonDirection: DialogButtonDirection.HORIZONTAL, + buttons: [ + { + value: '按钮', + action: () => { + console.info('Callback when button1 is clicked') + } + }, + { + value: '按钮', + action: () => { + console.info('Callback when button2 is clicked') + } + }, + { + value: '按钮', + enabled: true, + defaultFocus: true, + style: DialogButtonStyle.HIGHLIGHT, + action: () => { + console.info('Callback when button3 is clicked') + } + }, + ], + cancel: () => { + console.info('Closed callbacks') + } + }) + }) + + Text('UIContext.showAlertDialog') + .fontColor(Color.Gray) + .width('100%') + .padding(10) + + Button('three button dialog') + .margin(10) + .onClick(() => { + this.getUIContext().showAlertDialog({ + title: 'title', + subtitle: 'subtitle', + message: 'text', + buttonDirection: DialogButtonDirection.HORIZONTAL, + buttons: [ + { + value: '按钮', + action: () => { + console.info('Callback when button1 is clicked') + } + }, + { + value: '按钮', + action: () => { + console.info('Callback when button2 is clicked') + } + }, + { + value: '按钮', + enabled: true, + defaultFocus: true, + style: DialogButtonStyle.HIGHLIGHT, + action: () => { + console.info('Callback when button3 is clicked') + } + }, + ], + cancel: () => { + console.info('Closed callbacks') + } + }) + }) + } + .width('100%') + } + .title('AlertDialog') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/customDialog.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/customDialog.ets new file mode 100644 index 0000000000000000000000000000000000000000..32fc819bb543282d98278d02d7e714f48c86f30b --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/customDialog.ets @@ -0,0 +1,135 @@ +/* + * 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. + */ + +@Builder +export function CustomDialogBuilder(name: string, param: Object) { + CustomDialogExample() +} + +@CustomDialog +@Component +struct CustomBuilder { + @Prop textValue: string + @Prop inputValue: string + controller?: CustomDialogController + // 若尝试在CustomDialog中传入多个其他的Controller,以实现在CustomDialog中打开另一个或另一些CustomDialog, + // 那么此处需要将指向自己的controller放在所有controller的后面 + cancel: () => void = () => { + } + confirm: () => void = () => { + } + + build() { + Column() { + Text('Change text').fontSize(20).margin({ top: 10, bottom: 10 }) + TextInput({ placeholder: '', text: this.textValue }).height(60).width('90%') + .onChange((value: string) => { + this.textValue = value + }) + Flex({ justifyContent: FlexAlign.SpaceAround }) { + Button('cancel') + .onClick(() => { + if (this.controller != undefined) { + this.controller.close() + this.cancel() + } + }).backgroundColor(0xffffff).fontColor(Color.Black) + Button('confirm') + .onClick(() => { + if (this.controller != undefined) { + this.inputValue = this.textValue + this.controller.close() + this.confirm() + } + }).backgroundColor(0xffffff).fontColor(Color.Red) + }.margin({ bottom: 10 }) + }.borderRadius(10) + } +} + +@Entry +@Component +struct CustomDialogExample { + pathStack: NavPathStack = new NavPathStack() + + @State textValue: string = '' + @State inputValue: string = 'click me' + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomBuilder({ + cancel: ()=> { this.onCancel() }, + confirm: ()=> { this.onAccept() }, + textValue: this.textValue, + inputValue: this.inputValue + }), + cancel: this.exitApp, + autoCancel: true, + onWillDismiss:(dismissDialogAction: DismissDialogAction)=> { + console.info('reason=' + JSON.stringify(dismissDialogAction.reason)) + console.log('dialog onWillDismiss') + if (dismissDialogAction.reason == DismissReason.PRESS_BACK) { + dismissDialogAction.dismiss() + } + if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) { + dismissDialogAction.dismiss() + } + }, + alignment: DialogAlignment.Bottom, + customStyle: false, + cornerRadius: 10, + }) + + // 在自定义组件即将析构销毁时将dialogController置空 + aboutToDisappear() { + this.dialogController = null + } + + onCancel() { + console.info('Callback when the first button is clicked') + } + + onAccept() { + console.info('Callback when the second button is clicked') + } + + exitApp() { + console.info('Click the callback in the blank area') + } + + build() { + NavDestination() { + Column({ space: 5 }) { + Text('dialogController.open') + .fontColor(Color.Gray) + .width('100%') + .padding(10) + + Button(this.inputValue) + .onClick(() => { + if (this.dialogController != null) { + this.dialogController.open() + } + }) + }.width('100%').margin({ top: 5 }) + } + .title('CustomDialog') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogController.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogController.ets new file mode 100644 index 0000000000000000000000000000000000000000..cdb8754f56e0541e3ac054dcbff78ff92dbeeee4 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogController.ets @@ -0,0 +1,382 @@ +/* + * 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 { ComponentContent, PromptAction, promptAction } from '@kit.ArkUI' +import { BusinessError } from '@kit.BasicServicesKit' + +class Params { + public text: string = '' + public dialogController: promptAction.CommonController = new promptAction.DialogController() + constructor(text: string, dialogController: promptAction.CommonController) { + this.text = text + this.dialogController = dialogController + } +} + +@Component +struct MyComponent { + build() { + Column() { + Button('点我关闭弹窗:通过自定义组件自带的DialogController') + .onClick(() => { + let dialogController: promptAction.DialogController = this.getDialogController() + if (dialogController !== undefined) { + dialogController.close() + } + }) + } + } +} + +@Builder +function buildText(params: Params) { + Column({ space: 5 }) { + Text(params.text) + .fontSize(30) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 36 }) + if (params.dialogController !== undefined) { + Button('点我关闭弹窗:通过外部传递的DialogController') + .onClick(() => { + params.dialogController.close() + }) + } + MyComponent() + } + .width(300) + .height(200) + .backgroundColor('#FFF0F0F0') +} + +@Builder +function buildTextNoParams() { + Column({ space: 5 }) { + Text('弹窗:无外部传递的DialogController') + .fontSize(30) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 36 }) + MyComponent() + }.backgroundColor('#FFF0F0F0') +} + +class FuncParams { + public text: string = '' + public dialogController: promptAction.CommonController = new promptAction.DialogController() + public func: Function + constructor(text: string, dialogController: promptAction.CommonController, func: Function) { + this.text = text + this.dialogController = dialogController + this.func = func + } +} + +@Builder +function buildTextWithFunc(func: Function, dialogController: promptAction.DialogController) { + Text(func()) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 36 }) + if (dialogController !== undefined) { + Button('点我关闭弹窗:嵌套传递的DialogController') + .onClick(() => { + dialogController.close() + }) + } +} + +@Builder +function buildTextFull(params: FuncParams) { + Column({ space: 5 }) { + Text(params.text) + .fontSize(30) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 36 }) + if (params.dialogController !== undefined) { + Button('点我关闭弹窗:通过外部传递的DialogController') + .onClick(() => { + params.dialogController.close() + }) + } + MyComponent() + buildTextWithFunc(params.func, params.dialogController) + }.backgroundColor('#FFF0F0F0') +} + +@CustomDialog +@Component +struct CustomDialogExample { + controller?: CustomDialogController + + build() { + Column() { + Text('我是内容') + .fontSize(20) + .margin({ top: 10, bottom: 10 }) + TextInput() + Button('点我关闭弹窗:通过自定义组件自带的DialogController') + .onClick(() => { + let dialogController: PromptActionDialogController = this.getDialogController() + if (dialogController !== undefined) { + dialogController.close() + } + }) + } + .height(400) + .backgroundColor('#FFF0F0F0') + } +} + +@Builder +export function DialogControllerBuilder(name: string, param: Object) { + DialogController() +} + +@Component +export struct DialogController { + private message = '弹窗' + private ctx: UIContext = this.getUIContext() + private promptAction: PromptAction = this.ctx.getPromptAction() + + @State customDialogIds: number[] = [] + @State dialogIdIndex: number = 0 + @Builder customDialogComponent(idIndex: number, controller: promptAction.DialogController) { + Column() { + Text(this.message) + .fontSize(30) + TextInput() + Row({ space: 5 }) { + if (idIndex !== undefined) { + Button('点击关闭弹窗:通过ID') + .onClick(() => { + this.promptAction.closeCustomDialog(this.customDialogIds[idIndex]) + this.customDialogIds[idIndex] = null! + }) + } + if (controller !== undefined) { + Button('点击关闭弹窗:通过外部传递的DialogController') + .onClick(() => { + controller.close() + }) + } + } + } + .height(200) + .padding(5) + .justifyContent(FlexAlign.SpaceBetween) + .backgroundColor('#FFF0F0F0') + } + + @Builder customDialogComponentWithId(dialogId: number, controller: promptAction.DialogController) { + Column() { + Text(this.message) + .fontSize(30) + TextInput() + Row({ space: 5 }) { + if (dialogId !== undefined) { + Button('点击关闭弹窗:通过ID') + .onClick(() => { + this.promptAction.closeCustomDialog(dialogId) + }) + } + if (controller !== undefined) { + Button('点击关闭弹窗:通过外部传递的DialogController') + .onClick(() => { + controller.close() + }) + } + } + } + } + + private baseDialogOptions: promptAction.BaseDialogOptions = { + isModal: false, + autoCancel: false + } + + private dialogOptions: promptAction.DialogOptions = { + isModal: false, + autoCancel: false + } + + pathStack: NavPathStack = new NavPathStack() + + build() { + NavDestination() { + Column({ space: 5 }) { + Button('openCustomDialog+CustomBuilder弹窗') + .onClick(() => { + this.promptAction.openCustomDialog({ + builder: () => { + this.customDialogComponent(this.dialogIdIndex, undefined!) + } + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }).catch((err: BusinessError) => { + console.error('openCustomDialog error: ' + err.code + ' ' + err.message) + }) + }) + Button('openCustomDialog+ParamsNode弹窗') + .onClick(() => { + let contentNode: ComponentContent = + new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message, undefined!)) + this.promptAction.openCustomDialog(contentNode, this.baseDialogOptions) + .catch((err: BusinessError) => { + console.error('openCustomDialog error: ' + err.code + ' ' + err.message) + }) + }) + Button('openCustomDialogWithController+全部参数+NoParamsNode弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNodeNoParams: ComponentContent = + new ComponentContent(this.ctx, wrapBuilder(buildTextNoParams)) + this.promptAction.openCustomDialogWithController( + contentNodeNoParams, dialogController, this.baseDialogOptions + ).catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('openCustomDialogWithController+必填参数+NoParamsNode弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNodeNoParams: ComponentContent = + new ComponentContent(this.ctx, wrapBuilder(buildTextNoParams)) + this.promptAction.openCustomDialogWithController(contentNodeNoParams, dialogController) + .catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('openCustomDialogWithController+全部参数+ParamsNode弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNode: ComponentContent = + new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message, dialogController)) + this.promptAction.openCustomDialogWithController(contentNode, dialogController, this.baseDialogOptions) + .catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('openCustomDialogWithController+必填参数+ParamsNode弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNode: ComponentContent = + new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message, dialogController)) + this.promptAction.openCustomDialogWithController(contentNode, dialogController) + .catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('openCustomDialogWithController+全部参数+FullNode弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNodeFull: ComponentContent = new ComponentContent(this.ctx, wrapBuilder(buildTextFull), + new FuncParams(this.message, dialogController, () => { return 'FUNCTION' }), + { nestingBuilderSupported: true }) + this.promptAction.openCustomDialogWithController(contentNodeFull, dialogController, this.baseDialogOptions) + .catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('openCustomDialogWithController+必填参数+FullNode弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNodeFull: ComponentContent = new ComponentContent(this.ctx, wrapBuilder(buildTextFull), + new FuncParams(this.message, dialogController, () => { return 'FUNCTION' }), + { nestingBuilderSupported: true }) + this.promptAction.openCustomDialogWithController(contentNodeFull, dialogController) + .catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('presentCustomDialog+CustomBuilderWithId全部参数弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog((dialogId: number) => { + this.customDialogComponentWithId(dialogId, dialogController) + }, dialogController, this.dialogOptions).catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('presentCustomDialog+CustomBuilderWithId必填参数+Controller弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog((dialogId: number) => { + this.customDialogComponentWithId(dialogId, dialogController) + }, dialogController).catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('presentCustomDialog+CustomBuilderWithId必填参数弹窗') + .onClick(() => { + this.promptAction.presentCustomDialog((dialogId: number) => { + this.customDialogComponentWithId(dialogId, undefined!) + }).catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('presentCustomDialog+CustomBuilder全部参数弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog(() => { + this.customDialogComponent(this.dialogIdIndex, dialogController) + }, dialogController, this.dialogOptions).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }).catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('presentCustomDialog+CustomBuilder必填参数+Controller弹窗') + .onClick(() => { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog(() => { + this.customDialogComponent(this.dialogIdIndex, dialogController) + }, dialogController).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }).catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('presentCustomDialog+CustomBuilder必填参数弹窗') + .onClick(() => { + this.promptAction.presentCustomDialog(() => { + this.customDialogComponent(this.dialogIdIndex, undefined!) + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }).catch((err: BusinessError) => { + console.error('openCustomDialogWithController error: ' + err.code + ' ' + err.message) + }) + }) + Button('CustomDialogController弹窗') + .onClick(() => { + let customDialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialogExample(), + }) + customDialogController.open() + }) + } + } + .title('DialogController') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogFocusable.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogFocusable.ets new file mode 100644 index 0000000000000000000000000000000000000000..c562230f13ab48ca9f9c96df0a38cf3542e36121 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogFocusable.ets @@ -0,0 +1,576 @@ +/* + * 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 { ComponentContent, LengthMetrics, PromptAction, promptAction } from '@kit.ArkUI' +import { ParamOption, SetParam } from '../base/ParamOption' + +class Params { + public dialogController: promptAction.CommonController = new promptAction.DialogController() + public secondInterface: string = 'openCustomDialog' + public secondShowInSubWindow: boolean = false + public secondFocusable: boolean = false + constructor(dialogController: promptAction.CommonController, secondInterface: string, secondShowInSubWindow: boolean, + secondFocusable: boolean) { + this.dialogController = dialogController + this.secondInterface = secondInterface + this.secondShowInSubWindow = secondShowInSubWindow + this.secondFocusable = secondFocusable + } +} + +@Builder +function buildText(params: Params) { + Column({ space: 5 }) { + Text('弹窗') + .fontSize(30) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 36 }) + SecondDialogComponent({ + secondInterface: params.secondInterface, + secondShowInSubWindow: params.secondShowInSubWindow, + secondFocusable: params.secondFocusable + }) + Button('点我关闭弹窗') + .onClick(() => { + if (params.dialogController !== undefined) { + params.dialogController.close() + } + }) + } + .width(300) + .height(200) + .backgroundColor('#FFF0F0F0') +} + +@CustomDialog +@Component +struct CustomDialogExample { + secondInterface: string = 'openCustomDialog' + secondShowInSubWindow: boolean = false + secondFocusable: boolean = false + controller?: CustomDialogController + + private ctx: UIContext = this.getUIContext() + private promptAction: PromptAction = this.ctx.getPromptAction() + + @State customDialogIds: number[] = [] + @State dialogIdIndex: number = 0 + @Builder customDialogComponent(idIndex: number = 0) { + Column() { + Text('弹窗').fontSize(30) + Button('点击关闭弹窗') + .onClick(() => { + this.promptAction.closeCustomDialog(this.customDialogIds[idIndex]) + this.customDialogIds[idIndex] = null! + }) + } + .height(200) + .padding(5) + .justifyContent(FlexAlign.SpaceBetween) + } + + build() { + Column() { + Text('我是内容') + .fontSize(20) + .margin({ top: 10, bottom: 10 }) + TextInput() + .onChange(() => { + switch (this.secondInterface) { + case 'openCustomDialog': { + this.promptAction.openCustomDialog({ + builder: () => { + this.customDialogComponent(this.dialogIdIndex) + }, + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }) + break + } + case 'openCustomDialogWithController': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNode: ComponentContent = new ComponentContent(this.ctx, wrapBuilder(buildText), + new Params(dialogController, this.secondInterface, this.secondShowInSubWindow, this.secondFocusable)); + this.promptAction.openCustomDialogWithController(contentNode, dialogController, { + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }) + break + } + case 'presentCustomDialog': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog(() => { + this.customDialogComponent(this.dialogIdIndex) + }, dialogController, { + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + }) + } + case 'CustomDialogController': { + let customDialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialogExample({ + secondInterface: this.secondInterface, + secondShowInSubWindow: this.secondShowInSubWindow, + secondFocusable: this.secondFocusable + }), + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }) + customDialogController.open() + } + } + }) + Button('点我关闭弹窗') + .onClick(() => { + let ctrl: PromptActionDialogController = this.getDialogController() + if (ctrl !== undefined) { + ctrl.close() + } + }) + } + .height(400) + } +} + +@Component +struct SecondDialogComponent { + secondInterface: string = 'openCustomDialog' + secondShowInSubWindow: boolean = false + secondFocusable: boolean = false + + private ctx: UIContext = this.getUIContext() + private promptAction: PromptAction = this.ctx.getPromptAction() + + @State customDialogIds: number[] = [] + @State dialogIdIndex: number = 0 + @Builder customDialogComponent(idIndex: number = 0) { + Column() { + Text('弹窗').fontSize(30) + Button('点击关闭弹窗') + .onClick(() => { + this.promptAction.closeCustomDialog(this.customDialogIds[idIndex]) + this.customDialogIds[idIndex] = null! + }) + } + .height(200) + .padding(5) + .justifyContent(FlexAlign.SpaceBetween) + } + + build() { + Column() { + TextInput() + .onChange(() => { + switch (this.secondInterface) { + case 'openCustomDialog': { + this.promptAction.openCustomDialog({ + builder: () => { + this.customDialogComponent(this.dialogIdIndex) + }, + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }) + break + } + case 'openCustomDialogWithController': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNode: ComponentContent = new ComponentContent(this.ctx, wrapBuilder(buildText), + new Params(dialogController, this.secondInterface, this.secondShowInSubWindow, this.secondFocusable)); + this.promptAction.openCustomDialogWithController(contentNode, dialogController, { + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }) + break + } + case 'presentCustomDialog': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog(() => { + this.customDialogComponent(this.dialogIdIndex) + }, dialogController, { + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + }) + break + } + case 'CustomDialogController': { + let customDialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialogExample({ + secondInterface: this.secondInterface, + secondShowInSubWindow: this.secondShowInSubWindow, + secondFocusable: this.secondFocusable + }), + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }) + customDialogController.open() + break + } + } + }) + } + } +} + +@Builder +export function DialogFocusableBuilder(name: string, param: Object) { + DialogFocusable() +} + +@Component +export struct DialogFocusable { + private ctx: UIContext = this.getUIContext() + private promptAction: PromptAction = this.ctx.getPromptAction() + + @State customDialogIds: number[] = [] + @State dialogIdIndex: number = 0 + @Builder customDialogComponent(idIndex: number = 0) { + Column() { + Text('弹窗').fontSize(30) + TextInput() + .onChange(() => { + this.openSecondDialog() + }) + Button('点击关闭弹窗') + .onClick(() => { + this.promptAction.closeCustomDialog(this.customDialogIds[idIndex]) + this.customDialogIds[this.dialogIdIndex] = null! + }) + } + .height(200) + .padding(5) + .justifyContent(FlexAlign.SpaceBetween) + } + + @State firstInterface: string = 'openCustomDialog' + private setFirstInterface: SetParam[] = [{ + title: 'open', + func: () => { + this.firstInterface = 'openCustomDialog' + } + }, { + title: 'openWithController', + func: () => { + this.firstInterface = 'openCustomDialogWithController' + } + }, { + title: 'present', + func: () => { + this.firstInterface = 'presentCustomDialog' + } + }, { + title: 'Controller', + func: () => { + this.firstInterface = 'CustomDialogController' + } + }] + + @State firstShowInSubWindow: boolean = false + private setFirstShowInSubWindow: SetParam[] = [{ + title: 'false', + func: () => { + this.firstShowInSubWindow = false + } + }, { + title: 'true', + func: () => { + this.firstShowInSubWindow = true + } + }] + + @State firstFocusable: boolean = false + private setFirstFocusable: SetParam[] = [{ + title: 'false', + func: () => { + this.firstFocusable = false + } + }, { + title: 'true', + func: () => { + this.firstFocusable = true + } + }] + + @State secondInterface: string = 'openCustomDialog' + private setSecondInterface: SetParam[] = [{ + title: 'open', + func: () => { + this.secondInterface = 'openCustomDialog' + } + }, { + title: 'openWithController', + func: () => { + this.secondInterface = 'openCustomDialogWithController' + } + }, { + title: 'present', + func: () => { + this.secondInterface = 'presentCustomDialog' + } + }, { + title: 'Controller', + func: () => { + this.secondInterface = 'CustomDialogController' + } + }] + + @State secondShowInSubWindow: boolean = false + private setSecondShowInSubWindow: SetParam[] = [{ + title: 'false', + func: () => { + this.secondShowInSubWindow = false + } + }, { + title: 'true', + func: () => { + this.secondShowInSubWindow = true + } + }] + + @State secondFocusable: boolean = false + private setSecondFocusable: SetParam[] = [{ + title: 'false', + func: () => { + this.secondFocusable = false + } + }, { + title: 'true', + func: () => { + this.secondFocusable = true + } + }] + + openFirstDialog(enableAsync: boolean = false) { + switch (this.firstInterface) { + case 'openCustomDialog': { + this.promptAction.openCustomDialog({ + builder: () => { + this.customDialogComponent(this.dialogIdIndex) + }, + showInSubWindow: this.firstShowInSubWindow, + focusable: this.firstFocusable + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + if (!enableAsync) { + return + } + setTimeout(() => { + this.openSecondDialog() + }, 5000) + }) + break + } + case 'openCustomDialogWithController': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNode: ComponentContent = new ComponentContent(this.ctx, wrapBuilder(buildText), + new Params(dialogController, this.secondInterface, this.secondShowInSubWindow, this.secondFocusable)); + this.promptAction.openCustomDialogWithController(contentNode, dialogController, { + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }).then(() => { + if (!enableAsync) { + return + } + setTimeout(() => { + this.openSecondDialog() + }, 5000) + }) + break + } + case 'presentCustomDialog': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog(() => { + this.customDialogComponent(this.dialogIdIndex) + }, dialogController, { + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + if (!enableAsync) { + return + } + setTimeout(() => { + this.openSecondDialog() + }, 5000) + }) + break + } + case 'CustomDialogController': { + let customDialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialogExample({ + secondInterface: this.secondInterface, + secondShowInSubWindow: this.secondShowInSubWindow, + secondFocusable: this.secondFocusable + }), + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + keyboardAvoidDistance: LengthMetrics.vp(0) + }) + customDialogController.open() + if (!enableAsync) { + return + } + setTimeout(() => { + this.openSecondDialog() + }, 5000) + break + } + } + } + + openSecondDialog() { + switch (this.secondInterface) { + case 'openCustomDialog': { + this.promptAction.openCustomDialog({ + builder: () => { + this.customDialogComponent(this.dialogIdIndex) + }, + showInSubWindow: this.firstShowInSubWindow, + focusable: this.firstFocusable + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }) + break + } + case 'openCustomDialogWithController': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNode: ComponentContent = new ComponentContent(this.ctx, wrapBuilder(buildText), + new Params(dialogController, this.secondInterface, this.secondShowInSubWindow, this.secondFocusable)); + this.promptAction.openCustomDialogWithController(contentNode, dialogController, { + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }) + break + } + case 'presentCustomDialog': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog(() => { + this.customDialogComponent(this.dialogIdIndex) + }, dialogController, { + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }) + break + } + case 'CustomDialogController': { + let customDialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialogExample({ + secondInterface: this.secondInterface, + secondShowInSubWindow: this.secondShowInSubWindow, + secondFocusable: this.secondFocusable + }), + showInSubWindow: this.secondShowInSubWindow, + focusable: this.secondFocusable, + }) + customDialogController.open() + break + } + } + } + + pathStack: NavPathStack = new NavPathStack() + + build() { + NavDestination() { + Column({ space: 5 }) { + Text('1st Interface: ' + this.firstInterface) + .fontSize(12) + Row({ space: 2 }) { + ForEach(this.setFirstInterface, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Text('1st ShowInSubWindow: ' + this.firstShowInSubWindow) + .fontSize(12) + Row({ space: 2 }) { + ForEach(this.setFirstShowInSubWindow, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Text('1st Focusable: ' + this.firstFocusable) + .fontSize(12) + Row({ space: 2 }) { + ForEach(this.setFirstFocusable, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + TextInput({ placeholder: '输入任意字符弹出弹窗' }) + .onChange(() => { + this.openFirstDialog() + }) + + Text('2nd Interface: ' + this.secondInterface) + .fontSize(12) + Row({ space: 2 }) { + ForEach(this.setSecondInterface, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Text('2nd ShowInSubWindow: ' + this.secondShowInSubWindow) + .fontSize(12) + Row({ space: 2 }) { + ForEach(this.setSecondShowInSubWindow, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Text('2nd Focusable: ' + this.secondFocusable) + .fontSize(12) + Row({ space: 2 }) { + ForEach(this.setSecondFocusable, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Button('弹出多级弹窗') + .onClick(() => { + this.openFirstDialog() + }) + + Button('弹出异步弹窗') + .onClick(() => { + this.openFirstDialog() + }) + } + } + .title('DialogFocusable') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogLevelOrder.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogLevelOrder.ets new file mode 100644 index 0000000000000000000000000000000000000000..b5894228b6634c835913f69c111729d1cedaed4d --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/dialogLevelOrder.ets @@ -0,0 +1,961 @@ +/* + * 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 { ComponentContent, LevelOrder, PromptAction, promptAction } from '@kit.ArkUI' +import { ParamOption, SetParam } from '../base/ParamOption' + +class Params { + public text: string = '' + public dialogController: promptAction.CommonController = new promptAction.DialogController() + constructor(text: string, dialogController: promptAction.CommonController) { + this.text = text + this.dialogController = dialogController + } +} + +@Builder +function buildText(params: Params) { + Column({ space: 5 }) { + Text(params.text) + .fontSize(30) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 36 }) + Button('点我关闭弹窗') + .onClick(() => { + if (params.dialogController !== undefined) { + params.dialogController.close() + } + }) + } + .width(300) + .height(200) + .backgroundColor('#FFF0F0F0') +} + +@CustomDialog +@Component +struct CustomDialogExample { + title: string = '弹窗' + controller?: CustomDialogController + private ctx: UIContext = this.getUIContext() + private promptAction: PromptAction = this.ctx.getPromptAction() + + textInputCtrl: TextInputController = new TextInputController() + @State inputValue: string = '' + @Builder CustomKeyboardBuilder() { + Column() { + Button('X') + .onClick(() => { + this.textInputCtrl.stopEditing() + }) + Grid() { + ForEach([ 1, 2, 3, 4, 5, 6, 7, 8, 9, '*', 0, '#' ], (item: number | string) => { + GridItem() { + Button(item + '') + .width(110).onClick(() => { + this.inputValue += item + }) + } + }) + } + .maxCount(3) + .columnsGap(10) + .rowsGap(10) + .padding(5) + } + .backgroundColor(Color.Gray) + .height(400) + } + + build() { + Column() { + Text(this.title) + .fontSize(20) + .margin({ top: 10, bottom: 10 }) + TextInput({ controller: this.textInputCtrl, text: this.inputValue }) + .customKeyboard(this.CustomKeyboardBuilder(), { supportAvoidance: true }) + .margin(10) + .border({ width: 1 }) + .height('48vp') + .onChange(() => { + let topOrder: LevelOrder = this.promptAction.getTopOrder(); + if (topOrder === undefined) { + console.error('topOrder: ' + topOrder); + } else { + console.error('topOrder: ' + topOrder.getOrder()); + } + + let bottomOrder: LevelOrder = this.promptAction.getBottomOrder(); + if (bottomOrder === undefined) { + console.error('bottomOrder: ' + bottomOrder); + } else { + console.error('bottomOrder: ' + bottomOrder.getOrder()); + } + }) + Button('点我关闭弹窗') + .onClick(() => { + let ctrl: PromptActionDialogController = this.getDialogController() + if (ctrl !== undefined) { + ctrl.close() + } + }) + } + .height(200) + .backgroundColor('#FFF0F0F0') + } +} + +@Builder +export function DialogLevelOrderBuilder(name: string, param: Object) { + DialogLevelOrder() +} + +@Component +export struct DialogLevelOrder { + private message1 = '弹窗1' + private message2 = '弹窗2' + private ctx: UIContext = this.getUIContext() + private promptAction: PromptAction = this.ctx.getPromptAction() + + textInputCtrl: TextInputController = new TextInputController() + @State inputValue: string = '' + @Builder CustomKeyboardBuilder() { + Column() { + Button('X') + .onClick(() => { + this.textInputCtrl.stopEditing() + }) + Grid() { + ForEach([ 1, 2, 3, 4, 5, 6, 7, 8, 9, '*', 0, '#' ], (item: number | string) => { + GridItem() { + Button(item + '') + .width(110).onClick(() => { + this.inputValue += item + }) + } + }) + } + .maxCount(3) + .columnsGap(10) + .rowsGap(10) + .padding(5) + } + .backgroundColor(Color.Gray) + .height(400) + } + + @State customDialogIds: number[] = [] + @State dialogIdIndex: number = 0 + @Builder customDialogComponent(title: string, idIndex: number = 0) { + Column() { + Text(title).fontSize(30) + TextInput({ controller: this.textInputCtrl, text: this.inputValue }) + .customKeyboard(this.CustomKeyboardBuilder(), { supportAvoidance: true }) + .margin(10) + .border({ width: 1 }) + .height('48vp') + .onChange(() => { + let topOrder: LevelOrder = this.promptAction.getTopOrder(); + if (topOrder === undefined) { + console.error('topOrder: ' + topOrder); + } else { + console.error('topOrder: ' + topOrder.getOrder()); + } + + let bottomOrder: LevelOrder = this.promptAction.getBottomOrder(); + if (bottomOrder === undefined) { + console.error('bottomOrder: ' + bottomOrder); + } else { + console.error('bottomOrder: ' + bottomOrder.getOrder()); + } + }) + Button('点击关闭弹窗') + .onClick(() => { + this.promptAction.closeCustomDialog(this.customDialogIds[idIndex]) + this.customDialogIds[idIndex] = null! + }) + } + .height(200) + .padding(5) + .justifyContent(FlexAlign.SpaceBetween) + .backgroundColor('#FFF0F0F0') + } + + @State openInterface1: string = 'openCustomDialog' + private setOpenInterface1: SetParam[] = [{ + title: 'open', + func: () => { + this.openInterface1 = 'openCustomDialog' + } + }, { + title: 'openCtrl', + func: () => { + this.openInterface1 = 'openCustomDialogWithController' + } + }, { + title: 'present', + func: () => { + this.openInterface1 = 'presentCustomDialog' + } + }, { + title: 'ctrlOpen', + func: () => { + this.openInterface1 = 'CustomDialogController' + } + }, { + title: 'show', + func: () => { + this.openInterface1 = 'showDialog' + } + }, { + title: 'actionSheet', + func: () => { + this.openInterface1 = 'showActionSheet' + } + }, { + title: 'alertDialog', + func: () => { + this.openInterface1 = 'showAlertDialog' + } + }] + + @State levelOrder1: LevelOrder = LevelOrder.clamp(undefined) + private setLevelOrder1: SetParam[] = [{ + title: '-100000', + func: () => { + this.levelOrder1 = LevelOrder.clamp(-100000) + } + }, { + title: '-5', + func: () => { + this.levelOrder1 = LevelOrder.clamp(-5) + } + }, { + title: 'undefined', + func: () => { + this.levelOrder1 = LevelOrder.clamp(undefined) + } + }, { + title: '1.41', + func: () => { + this.levelOrder1 = LevelOrder.clamp(1.41) + } + }, { + title: '100000', + func: () => { + this.levelOrder1 = LevelOrder.clamp(100000) + } + }] + + @State openInterface2: string = 'openCustomDialog' + private setOpenInterface2: SetParam[] = [{ + title: 'open', + func: () => { + this.openInterface2 = 'openCustomDialog' + } + }, { + title: 'openCtrl', + func: () => { + this.openInterface2 = 'openCustomDialogWithController' + } + }, { + title: 'present', + func: () => { + this.openInterface2 = 'presentCustomDialog' + } + }, { + title: 'ctrlOpen', + func: () => { + this.openInterface2 = 'CustomDialogController' + } + }, { + title: 'show', + func: () => { + this.openInterface2 = 'showDialog' + } + }, { + title: 'actionSheet', + func: () => { + this.openInterface2 = 'showActionSheet' + } + }, { + title: 'alertDialog', + func: () => { + this.openInterface2 = 'showAlertDialog' + } + }] + + @State levelOrder2: LevelOrder = LevelOrder.clamp(undefined) + private setLevelOrder2: SetParam[] = [{ + title: '-100000', + func: () => { + this.levelOrder2 = LevelOrder.clamp(-100000) + } + }, { + title: '-5', + func: () => { + this.levelOrder2 = LevelOrder.clamp(-5) + } + }, { + title: 'undefined', + func: () => { + this.levelOrder2 = LevelOrder.clamp(undefined) + } + }, { + title: '1.41', + func: () => { + this.levelOrder2 = LevelOrder.clamp(1.41) + } + }, { + title: '100000', + func: () => { + this.levelOrder2 = LevelOrder.clamp(100000) + } + }] + + openDialog(title: string, openInterface: string, options: promptAction.BaseDialogOptions) { + switch (openInterface) { + case 'openCustomDialog': { + this.promptAction.openCustomDialog({ + builder: () => { + this.customDialogComponent(title, this.dialogIdIndex) + }, + isModal: options.isModal, + levelOrder: options.levelOrder, + focusable: options.focusable, + showInSubWindow: options.showInSubWindow, + }).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }) + break + } + case 'openCustomDialogWithController': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + let contentNode: ComponentContent = new ComponentContent(this.ctx, wrapBuilder(buildText), + new Params(title, dialogController)); + this.promptAction.openCustomDialogWithController(contentNode, dialogController, options) + break + } + case 'presentCustomDialog': { + let dialogController: promptAction.CommonController = new promptAction.DialogController() + this.promptAction.presentCustomDialog(() => { + this.customDialogComponent(title, this.dialogIdIndex) + }, dialogController, options).then((dialogId: number) => { + this.customDialogIds[this.dialogIdIndex] = dialogId + this.dialogIdIndex++ + }) + break + } + case 'CustomDialogController': { + let customDialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialogExample({ title: title }), + isModal: options.isModal, + levelOrder: options.levelOrder, + focusable: options.focusable, + showInSubWindow: options.showInSubWindow, + }) + customDialogController.open() + break + } + case 'showDialog': { + this.promptAction.showDialog({ + title: title, + message: '层级: ' + options.levelOrder?.getOrder(), + isModal: options.isModal, + levelOrder: options.levelOrder, + showInSubWindow: options.showInSubWindow, + backgroundColor: '#FFF0F0F0', + buttons: [{ + text: 'button1', + color: '#000000' + }, { + text: 'button2', + color: '#000000' + }] + }) + break + } + case 'showActionSheet': { + this.ctx.showActionSheet({ + title: title, + message: '层级: ' + options.levelOrder?.getOrder(), + isModal: options.isModal, + levelOrder: options.levelOrder, + showInSubWindow: options.showInSubWindow, + backgroundColor: '#FFF0F0F0', + confirm: { + defaultFocus: true, + value: 'Confirm button', + action: () => { + console.info('ActionSheet action') + } + }, + cancel: () => { + console.info('ActionSheet cancel') + }, + sheets: [{ + title: 'apples', + action: () => { + console.info('apples') + } + }, { + title: 'bananas', + action: () => { + console.info('bananas') + } + }] + }) + break + } + case 'showAlertDialog': { + this.ctx.showAlertDialog({ + title: title, + message: '层级: ' + options.levelOrder?.getOrder(), + isModal: options.isModal, + levelOrder: options.levelOrder, + showInSubWindow: options.showInSubWindow, + backgroundColor: '#FFF0F0F0', + confirm: { + defaultFocus: true, + value: 'Confirm button', + action: () => { + console.info('AlertDialog action') + } + }, + cancel: () => { + console.info('AlertDialog cancel') + } + }) + break + } + } + } + + @State isShowMenu1: boolean = false + @State isShowMenu2: boolean = false + @State isShowMenu3: boolean = false + private iconStr: ResourceStr = $r('app.media.app_icon') + private iconStr2: ResourceStr = $r('app.media.app_icon') + @Builder MenuBuilder() { + Menu() { + MenuItem({ startIcon: $r('app.media.app_icon'), content: '菜单选项' }) + MenuItem({ startIcon: $r('app.media.app_icon'), content: '菜单选项' }) + .enabled(false) + MenuItem({ + startIcon: this.iconStr, + content: '菜单选项', + endIcon: this.iconStr2, + }) + MenuItemGroup({ header: '小标题' }) { + MenuItem({ + startIcon: this.iconStr, + content: '菜单选项', + endIcon: this.iconStr2, + }) + MenuItem({ + startIcon: $r('app.media.app_icon'), + content: '菜单选项', + endIcon: this.iconStr2, + }) + } + MenuItem({ + startIcon: this.iconStr, + content: '菜单选项', + }) + } + .onDisAppear(() => { + this.isShowMenu1 = false + this.isShowMenu2 = false + this.isShowMenu3 = false + }) + } + + @State isShowPopup1: boolean = false + @State isShowPopup2: boolean = false + @State isShowPopup3: boolean = false + private popupOptions: PopupOptions = { + message: 'This is a popup with PopupOptions', + placementOnTop: true, + showInSubWindow: false, + primaryButton: { + value: 'confirm', + action: () => { + console.info('confirm Button click') + this.isShowPopup1 = false + this.isShowPopup2 = false + this.isShowPopup3 = false + } + }, + // 第二个按钮 + secondaryButton: { + value: 'cancel', + action: () => { + console.info('cancel Button click') + this.isShowPopup1 = false + this.isShowPopup2 = false + this.isShowPopup3 = false + } + }, + onStateChange: (e) => { + if (!e.isVisible) { + this.isShowPopup1 = false + this.isShowPopup2 = false + this.isShowPopup3 = false + } + } + } + + @State isShowSheet1: boolean = false + @State isShowSheet2: boolean = false + @State isShowSheet3: boolean = false + private items: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + @Builder SheetBuilder() { + Column() { + // 第一步:自定义滚动容器 + List({ space: '10vp' }) { + ForEach(this.items, (item: number) => { + ListItem() { + Text(String(item)).fontSize(16).fontWeight(FontWeight.Bold) + }.width('90%').height('80vp').backgroundColor('#ff53ecd9').borderRadius(10) + }) + } + .alignListItem(ListItemAlign.Center) + .margin({ top: '10vp' }) + .width('100%') + .height('900px') + // 第二步:设置滚动组件的嵌套滚动属性 + .nestedScroll({ + scrollForward: NestedScrollMode.PARENT_FIRST, + scrollBackward: NestedScrollMode.SELF_FIRST, + }) + + Text('非滚动区域') + .width('100%') + .backgroundColor(Color.Gray) + .layoutWeight(1) + .textAlign(TextAlign.Center) + .align(Alignment.Top) + }.width('100%').height('100%') + } + + @State isShowSheetCover1: boolean = false + @State isShowSheetCover2: boolean = false + @State isShowSheetCover3: boolean = false + @Builder SheetCoverBuilder() { + Column() { + Text('Custom SheetCover').fontSize(16) + Button('点击关闭弹窗') + .onClick(() => { + this.isShowSheetCover1 = false + this.isShowSheetCover2 = false + this.isShowSheetCover3 = false + }) + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } + + toastId: number = 0; + + pathStack: NavPathStack = new NavPathStack() + + build() { + NavDestination() { + Column({ space: 5 }) { + Text('1st Interface: ' + this.openInterface1) + .fontSize(12) + Row({ space: 5 }) { + ForEach(this.setOpenInterface1, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Text('1st LevelOrder: ' + this.levelOrder1.getOrder()) + .fontSize(12) + Row({ space: 5 }) { + ForEach(this.setLevelOrder1, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Text('2nd Interface: ' + this.openInterface2) + .fontSize(12) + Row({ space: 5 }) { + ForEach(this.setOpenInterface2, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Text('2nd LevelOrder: ' + this.levelOrder2.getOrder()) + .fontSize(12) + Row({ space: 5 }) { + ForEach(this.setLevelOrder2, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Row() { + Button('弹出弹窗1') + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1, + isModal: false, + }) + }) + Button('获取层级') + .onClick(() => { + let topOrder: LevelOrder = this.promptAction.getTopOrder(); + if (topOrder === undefined) { + console.error('topOrder: ' + topOrder); + } else { + console.error('topOrder: ' + topOrder.getOrder()); + } + + let bottomOrder: LevelOrder = this.promptAction.getBottomOrder(); + if (bottomOrder === undefined) { + console.error('bottomOrder: ' + bottomOrder); + } else { + console.error('bottomOrder: ' + bottomOrder.getOrder()); + } + }) + Button('弹出弹窗2') + .onClick(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2, + isModal: true, + }) + }) + } + + Row() { + Button('Menu+弹窗1+弹窗2') + .fontSize(10) + .bindMenu(this.isShowMenu1, this.MenuBuilder()) + .onClick(() => { + this.isShowMenu1 = !this.isShowMenu1 + setTimeout(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+Menu+弹窗2') + .fontSize(10) + .bindMenu(this.isShowMenu2, this.MenuBuilder()) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.isShowMenu2 = !this.isShowMenu2 + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+弹窗2+Menu') + .fontSize(10) + .bindMenu(this.isShowMenu3, this.MenuBuilder()) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 3000) + setTimeout(() => { + this.isShowMenu3 = !this.isShowMenu3 + }, 6000) + }) + } + + Row() { + Button('Popup+弹窗1+弹窗2') + .fontSize(10) + .bindPopup(this.isShowPopup1, this.popupOptions) + .onClick(() => { + this.isShowPopup1 = !this.isShowPopup1 + setTimeout(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+Popup+弹窗2') + .fontSize(10) + .bindPopup(this.isShowPopup2, this.popupOptions) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.isShowPopup2 = !this.isShowPopup2 + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+弹窗2+Popup') + .fontSize(10) + .bindPopup(this.isShowPopup3, this.popupOptions) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 3000) + setTimeout(() => { + this.isShowPopup3 = !this.isShowPopup3 + }, 6000) + }) + } + + Row() { + Button('Toast+弹窗1+弹窗2') + .fontSize(10) + .onClick(() => { + this.promptAction.showToast({ + message: 'ok,我是DEFAULT toast', + duration: 10000, + showMode: promptAction.ToastShowMode.DEFAULT, + bottom: 80 + }) + setTimeout(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+Toast+弹窗2') + .fontSize(10) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.promptAction.showToast({ + message: 'ok,我是DEFAULT toast', + duration: 10000, + showMode: promptAction.ToastShowMode.DEFAULT, + bottom: 80 + }) + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+弹窗2+Toast') + .fontSize(10) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 3000) + setTimeout(() => { + this.promptAction.showToast({ + message: 'ok,我是DEFAULT toast', + duration: 10000, + showMode: promptAction.ToastShowMode.DEFAULT, + bottom: 80 + }) + }, 6000) + }) + } + + Row() { + Button('Sheet+弹窗1+弹窗2') + .fontSize(10) + .bindSheet(this.isShowSheet1, this.SheetBuilder(), { + detents: [SheetSize.MEDIUM, SheetSize.LARGE, 600], + preferType: SheetType.BOTTOM, + title: { title: '嵌套滚动场景' }, + onDisappear: () => { + this.isShowSheet1 = false + } + }) + .onClick(() => { + this.isShowSheet1 = !this.isShowSheet1 + setTimeout(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+Sheet+弹窗2') + .fontSize(10) + .bindSheet(this.isShowSheet2, this.SheetBuilder(), { + detents: [SheetSize.MEDIUM, SheetSize.LARGE, 600], + preferType: SheetType.BOTTOM, + title: { title: '嵌套滚动场景' }, + onDisappear: () => { + this.isShowSheet2 = false + } + }) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.isShowSheet2 = !this.isShowSheet2 + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+弹窗2+Sheet' + + '') + .fontSize(10) + .bindSheet(this.isShowSheet3, this.SheetBuilder(), { + detents: [SheetSize.MEDIUM, SheetSize.LARGE, 600], + preferType: SheetType.BOTTOM, + title: { title: '嵌套滚动场景' }, + onDisappear: () => { + this.isShowSheet3 = false + } + }) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 3000) + setTimeout(() => { + this.isShowSheet3 = !this.isShowSheet3 + }, 6000) + }) + } + + Row() { + Button('SheetCover+弹窗1+弹窗2') + .fontSize(8) + .bindContentCover(this.isShowSheetCover1, this.SheetCoverBuilder(), { + modalTransition: ModalTransition.DEFAULT, + backgroundColor: Color.Pink, + onDisappear: () => { + this.isShowSheetCover1 = false + } + }) + .onClick(() => { + this.isShowSheetCover1 = !this.isShowSheetCover1; + setTimeout(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+SheetCover+弹窗2') + .fontSize(8) + .bindContentCover(this.isShowSheetCover2, this.SheetCoverBuilder(), { + modalTransition: ModalTransition.DEFAULT, + backgroundColor: Color.Pink, + onDisappear: () => { + this.isShowSheetCover2 = false + } + }) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.isShowSheetCover2 = !this.isShowSheetCover2; + }, 3000) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 6000) + }) + Button('弹窗1+弹窗2+SheetCover') + .fontSize(8) + .bindContentCover(this.isShowSheetCover3, this.SheetCoverBuilder(), { + modalTransition: ModalTransition.DEFAULT, + backgroundColor: Color.Pink, + onDisappear: () => { + this.isShowSheetCover3 = false + } + }) + .onClick(() => { + this.openDialog(this.message1, this.openInterface1, { + levelOrder: this.levelOrder1 + }) + setTimeout(() => { + this.openDialog(this.message2, this.openInterface2, { + levelOrder: this.levelOrder2 + }) + }, 3000) + setTimeout(() => { + this.isShowSheetCover3 = !this.isShowSheetCover3; + }, 6000) + }) + } + } + } + .title('DialogLevelOrder') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/embeddedDialogPageOne.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/embeddedDialogPageOne.ets new file mode 100644 index 0000000000000000000000000000000000000000..81c9c445927b0978041a1e28a95fd1564c381e39 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/embeddedDialogPageOne.ets @@ -0,0 +1,74 @@ +/* + * 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 { promptAction, LevelMode, ImmersiveMode, router } from '@kit.ArkUI' + +let customDialogId: number = 0 + +@Builder +export function EmbeddedDialogPageOneBuilder(name: string, param: Object) { + EmbeddedDialogPageOne() +} + +@Builder +function customDialogBuilder() { + Column() { + Text('Custom dialog Message').fontSize(20).height(100) + Row() { + Button('Next').onClick(() => { + // 在弹窗内部进行路由跳转。 + router.pushUrl({url: 'pages/components/dialog/embeddedDialogPageTwo'}) + }) + Blank().width(50) + Button('Close').onClick(() => { + promptAction.closeCustomDialog(customDialogId) + }) + } + }.padding(20) +} + +@Component +export struct EmbeddedDialogPageOne { + @State message: string = 'Hello World' + + @Builder + customDialogComponent() { + customDialogBuilder() + } + + build() { + NavDestination() { + Column() { + Text(this.message).id('test_text') + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + const node: FrameNode | null = this.getUIContext().getFrameNodeById('test_text') || null; + promptAction.openCustomDialog({ + builder: () => { + this.customDialogComponent() + }, + levelMode: LevelMode.EMBEDDED, // 启用页面级弹出框 + levelUniqueId: node?.getUniqueId(), // 设置页面级弹出框所在页面的任意节点ID + immersiveMode: ImmersiveMode.EXTEND, // 设置页面级弹出框蒙层的显示模式 + }).then((dialogId: number) => { + customDialogId = dialogId + }) + }) + } + .width('100%') + } + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/embeddedDialogPageTwo.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/embeddedDialogPageTwo.ets new file mode 100644 index 0000000000000000000000000000000000000000..6e8eb457cbcc51f486c3963f57746fdf76886543 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/embeddedDialogPageTwo.ets @@ -0,0 +1,36 @@ +/* + * 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 { router } from '@kit.ArkUI' + +@Entry +@Component +export struct EmbeddedDialogPageTwo { + @State message: string = 'Back' + + build() { + Row() { + Column() { + Button(this.message) + .fontSize(20) + .fontWeight(FontWeight.Bold) + .onClick(() => { + router.back() + }) + } + .width('100%') + }.height('100%') + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/dialog/promptAction.ets b/examples/Overlay/entry/src/main/ets/pages/components/dialog/promptAction.ets new file mode 100644 index 0000000000000000000000000000000000000000..17ee27503f7ab3de9970ab79395288e4caeb102b --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/dialog/promptAction.ets @@ -0,0 +1,71 @@ +/* + * 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 { promptAction } from '@kit.ArkUI' + +@Builder +export function PromptActionBuilder(name: string, param: Object) { + PromptActionExample() +} + +@Entry +@Component +struct PromptActionExample { + pathStack: NavPathStack = new NavPathStack() + + build() { + NavDestination() { + Column({ space: 5 }) { + Text('promptAction.showDialog') + .fontColor(Color.Gray) + .width('100%') + .padding(10) + + Button('showDialog') + .onClick(() => { + promptAction.showDialog({ + title: 'Title Info', + message: 'Message Info', + buttons: [ + { + text: 'button1', + color: '#000000' + }, + { + text: 'button2', + color: '#000000' + } + ], + }) + .then(data => { + console.info('showDialog success, click button: ' + data.index); + }) + .catch((err: Error) => { + console.info('showDialog error: ' + err); + }) + }) + + }.width('100%').margin({ top: 5 }) + } + .title('PromptAction') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/overlay/Index.ets b/examples/Overlay/entry/src/main/ets/pages/components/overlay/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..e13434b7966209ea0db0797853c938631edb2304 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/overlay/Index.ets @@ -0,0 +1,50 @@ +/* + * 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 { NavList, NavListItem } from '../../utils/navigationList' + +@Builder +export function OverlayIndexBuilder(name: string, param: Object) { + OverlayIndex() +} + +@Entry +@Component +export struct OverlayIndex { + pathStack: NavPathStack = new NavPathStack() + paths: NavListItem[] = [ + { name: 'OverlayLevelOrder', path: 'OverlayLevelOrder' }, + { name: 'OverlayManagerHitTest', path: 'OverlayManagerHitTest' }, + ] + + build() { + NavDestination() { + NavList({ + pages: this.paths, + onPathChange: (item: NavListItem) => { + this.pathStack.pushPath( { name: item.path } ) + } + }) + } + .title('Overlay') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/overlay/overlayLevelOrder.ets b/examples/Overlay/entry/src/main/ets/pages/components/overlay/overlayLevelOrder.ets new file mode 100644 index 0000000000000000000000000000000000000000..034d6fbd230f3e3b41b76d0c915c636fb9aae1cc --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/overlay/overlayLevelOrder.ets @@ -0,0 +1,123 @@ +/* + * 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 { ComponentContent, LevelOrder, OverlayManager } from '@kit.ArkUI' +import { ParamOption, SetParam } from '../base/ParamOption' + +class Params { + public text: string = '' + public overlayManager: OverlayManager = new OverlayManager() + public overlayIdx: number = 0 + constructor(text: string, overlayManager: OverlayManager, overlayIdx: number) { + this.text = text + this.overlayManager = overlayManager + this.overlayIdx = overlayIdx + } +} + +let overlayArray: ComponentContent[] = [] +let overlayIndex: number = 0 + +@Builder +function buildText(params: Params) { + Column({ space: 5 }) { + Text(params.text) + .fontSize(30) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 36 }) + Button('点我关闭弹窗') + .onClick(() => { + let componentContent = overlayArray[params.overlayIdx] + params.overlayManager.removeComponentContent(componentContent) + overlayArray[params.overlayIdx] = null! + }) + } + .width(300) + .height(200) + .backgroundColor('#FFF0F0F0') +} + +@Builder +export function OverlayLevelOrderBuilder(name: string, param: Object) { + OverlayLevelOrder() +} + +@Component +export struct OverlayLevelOrder { + private message = '弹窗' + private ctx: UIContext = this.getUIContext() + private overlayManager: OverlayManager = this.ctx.getOverlayManager() + + @State levelOrder: LevelOrder = LevelOrder.clamp(undefined) + private setLevelOrder: SetParam[] = [{ + title: '-100000', + func: () => { + this.levelOrder = LevelOrder.clamp(-100000) + } + }, { + title: '-5', + func: () => { + this.levelOrder = LevelOrder.clamp(-5) + } + }, { + title: 'undefined', + func: () => { + this.levelOrder = LevelOrder.clamp(undefined) + } + }, { + title: '1.41', + func: () => { + this.levelOrder = LevelOrder.clamp(1.41) + } + }, { + title: '100000', + func: () => { + this.levelOrder = LevelOrder.clamp(100000) + } + }] + + pathStack: NavPathStack = new NavPathStack() + + build() { + NavDestination() { + Column({ space: 5 }) { + Text('LevelOrder: ' + this.levelOrder.getOrder()) + .fontSize(12) + Row({ space: 5 }) { + ForEach(this.setLevelOrder, (item: SetParam) => { + ParamOption({ title: item.title, func: item.func }) + }, (item: SetParam) => item.title) + } + + Button('弹出弹窗') + .onClick(() => { + let componentContent = new ComponentContent(this.ctx, wrapBuilder<[Params]>(buildText), + new Params(this.message + ', 层级: ' + this.levelOrder.getOrder(), this.overlayManager, overlayIndex)) + overlayArray[overlayIndex] = componentContent + overlayIndex++ + this.overlayManager.addComponentContentWithOrder(componentContent, this.levelOrder) + }) + } + } + .title('OverlayLevelOrder') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/overlay/overlayManagerHitTest.ets b/examples/Overlay/entry/src/main/ets/pages/components/overlay/overlayManagerHitTest.ets new file mode 100644 index 0000000000000000000000000000000000000000..8925e94d329a4e16bb71fcffd883c7fce9da9b32 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/overlay/overlayManagerHitTest.ets @@ -0,0 +1,81 @@ +/* + * 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 { ComponentContent, OverlayManager } from '@kit.ArkUI' + +class Params { + public context: UIContext + public offset: Position + public message: string + constructor(context: UIContext, offset: Position, message: string) { + this.context = context + this.offset = offset + this.message = message + } +} +@Builder +function builderOverlay(params: Params) { + Column() { + Stack(){ + }.width(50).height(50).backgroundColor(Color.Yellow).position(params.offset).borderRadius(50) + .onClick(() => {}) + }.focusable(false).width('100%').height('100%').hitTestBehavior(HitTestMode.Transparent) +} + +@Builder +export function OverlayManagerHitBuilder(name: string, param: Object) { + OverlayManageraHitTest() +} + +@Component +export struct OverlayManageraHitTest { + @State message: string = 'ComponentContent'; + private uiContext: UIContext = this.getUIContext() + private overlayNode: OverlayManager = this.uiContext.getOverlayManager() + private overlayContent:ComponentContent[] = [] + controller: TextInputController = new TextInputController() + @State btnColor: Color = Color.Red; + private changeColor: boolean = false; + + + aboutToDisappear(): void { + if (this.overlayContent.length) { + let componentContent = this.overlayContent.pop() + this.overlayNode.removeComponentContent(componentContent) + } + } + + build() { + NavDestination() { + Column() { + Button('show overlay').onClick(() => { + let uiContext = this.getUIContext(); + let componentContent = new ComponentContent( + this.uiContext, wrapBuilder<[Params]>(builderOverlay), + new Params(uiContext, { x: 180, y: 100 }, 'component1') + ) + this.overlayNode.addComponentContent(componentContent, 0) + this.overlayContent.push(componentContent) + }) + Button('click change').onClick(() => { + this.btnColor = this.changeColor ? Color.Red : Color.Blue; + this.changeColor = !this.changeColor; + }).backgroundColor(this.btnColor) + } + .width('100%') + .height('100%') + } + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/toast/Index.ets b/examples/Overlay/entry/src/main/ets/pages/components/toast/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..a00af4b7f90a0fa3a17c799b89cdd1fdde3678f4 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/toast/Index.ets @@ -0,0 +1,51 @@ +/* + * 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 { NavList, NavListItem } from '../../utils/navigationList' + +@Builder +export function ToastIndexBuilder(name: string, param: Object) { + ToastIndex() +} + +@Entry +@Component +export struct ToastIndex { + pathStack: NavPathStack = new NavPathStack() + paths: NavListItem[] = [ + { name: 'Toast', path: 'Toast' }, + { name: 'showToastTest', path: 'showToastTest' }, + { name: 'toastPositionTest', path: 'toastPositionTest' }, + ] + + build() { + NavDestination() { + NavList({ + pages: this.paths, + onPathChange: (item: NavListItem) => { + this.pathStack.pushPath( { name: item.path } ) + } + }) + } + .title('Toast') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/toast/showToastTest.ets b/examples/Overlay/entry/src/main/ets/pages/components/toast/showToastTest.ets new file mode 100644 index 0000000000000000000000000000000000000000..abb3c6e34fddc70f96c69d492b82bbafc453fb50 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/toast/showToastTest.ets @@ -0,0 +1,74 @@ +/* + * 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 { promptAction } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; + +@Builder +export function ShowToastBuilder(name: string, param: Object) { + toastExample02() +} +@Entry +@Component +struct toastExample02 { + pathStack: NavPathStack = new NavPathStack() + @State toastId: number = 0; + + build() { + NavDestination() { + Column() { + Button('Open Toast') + .height(100) + .type(ButtonType.Capsule) + .onClick(() => { + promptAction.openToast({ + message: 'Toast Massage', + duration: 10000, + }).then((toastId: number) => { + this.toastId = toastId; + }) + .catch((error: BusinessError) => { + console.error(`openToast error code is ${error.code}, message is ${error.message}`) + }) + }) + Blank().height(50); + Button('Close Toast') + .height(100) + .type(ButtonType.Capsule) + .onClick(() => { + try { + promptAction.closeToast(this.toastId); + } catch (error) { + let message = (error as BusinessError).message; + let code = (error as BusinessError).code; + console.error(`CloseToast error code is ${code}, message is ${message}`); + }; + }) + } + .height('100%') + .width('100%') + .justifyContent(FlexAlign.Center) + } + .title('showToastTest') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/toast/toast.ets b/examples/Overlay/entry/src/main/ets/pages/components/toast/toast.ets new file mode 100644 index 0000000000000000000000000000000000000000..0c75707d622d59dbe376a63e74812ad2285747c9 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/toast/toast.ets @@ -0,0 +1,63 @@ +/* + * 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 { promptAction } from '@kit.ArkUI' +import { BusinessError } from '@kit.BasicServicesKit'; + +@Builder +export function ToastBuilder(name: string, param: Object) { + ToastExample() +} + +@Entry +@Component +struct ToastExample { + pathStack: NavPathStack = new NavPathStack() + + build() { + NavDestination() { + Column({ space: 5 }) { + Text('promptAction.showToast') + .fontColor(Color.Gray) + .width('100%') + .padding(10) + + Button('Show toast') + .fontSize(20) + .onClick(() => { + try { + this.getUIContext().getPromptAction().showToast({ + message: 'Hello World', + duration: 2000, + showMode: promptAction.ToastShowMode.TOP_MOST + }); + } catch (error) { + let message = (error as BusinessError).message + let code = (error as BusinessError).code + console.error(`showToast args error code is ${code}, message is ${message}`); + } + }) + }.width('100%').margin({ top: 5 }) + } + .title('Toast') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/components/toast/toastPositionTest.ets b/examples/Overlay/entry/src/main/ets/pages/components/toast/toastPositionTest.ets new file mode 100644 index 0000000000000000000000000000000000000000..cd63b172206ca18d893a8a95f20812a4af45cea9 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/components/toast/toastPositionTest.ets @@ -0,0 +1,204 @@ +/* + * 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 { promptAction } from '@kit.ArkUI' +import { BusinessError } from '@kit.BasicServicesKit'; + +@Builder +export function ToastPositionBuilder(name: string, param: Object) { + ToastExample01() +} + +@Extend(Button) function myButtonStyle() { + .fontSize(10) + .padding(5) + .margin(2) + .borderRadius(5) +} + +@Extend(TextPicker) function myTextPicker() { + .width(70) + .height(50) + .selectedTextStyle({font: {size: 12}}) + .canLoop(false) + .textStyle({font: {size: 12}}) + .borderWidth(2) +} + +@Entry +@Component +struct ToastExample01 { + pathStack: NavPathStack = new NavPathStack() + + @State alignment: Alignment | undefined = Alignment.TopStart + alignmentKeys: string[] = ['undefined'].concat(...Object.keys(Alignment).slice(9)) + alignments: (Alignment | undefined)[] = [ + undefined, + Alignment.TopStart, + Alignment.Top, + Alignment.TopEnd, + Alignment.Start, + Alignment.Center, + Alignment.End, + Alignment.BottomStart, + Alignment.Bottom, + Alignment.BottomEnd + ] + + @State alignIdx: number = 0 + @State showMode: promptAction.ToastShowMode = promptAction.ToastShowMode.DEFAULT + @State toastBackgroundColor: Color | undefined = undefined + @State toastTextColor: Color | undefined = undefined + + aboutToAppear(): void { + console.log('toast: ', JSON.stringify(this.alignmentKeys)) + } + + build() { + NavDestination() { + Scroll() { + Column() { + Row() { + Text('TextColor: ') + TextPicker({ + range: [ + 'undefined', + 'Green', + 'Yellow', + 'White', + 'Orange' + ], + selected: 0 + }) + .myTextPicker() + .onChange((value: string | string[]) => { + switch (value) { + case 'Green': + this.toastTextColor = Color.Green + break + case 'Yellow': + this.toastTextColor = Color.Yellow + break + case 'White': + this.toastTextColor = Color.White + break + case 'Orange': + this.toastTextColor = Color.Orange + break + case 'undefined': + this.toastTextColor = undefined + break + } + }) + } + Row() { + Text('backgroundColor: ') + TextPicker({ + range: [ + 'undefined', + 'Red', + 'Blue', + 'Pink', + 'Black', + ], + selected: 0 + }) + .myTextPicker() + .onChange((value: string | string[]) => { + switch (value) { + case 'Red': + this.toastBackgroundColor = Color.Red + break + case 'Blue': + this.toastBackgroundColor = Color.Blue + break + case 'Pink': + this.toastBackgroundColor = Color.Pink + break + case 'Black': + this.toastBackgroundColor = Color.Black + break + case 'undefined': + this.toastBackgroundColor = undefined + break + } + }) + } + Row() { + Text('showMode: ') + Button('default') + .myButtonStyle() + .backgroundColor(this.showMode == promptAction.ToastShowMode.DEFAULT ? Color.Brown : undefined) + .onClick(()=>{ + this.showMode = promptAction.ToastShowMode.DEFAULT + }) + Button('top_most') + .myButtonStyle() + .backgroundColor(this.showMode == promptAction.ToastShowMode.TOP_MOST ? Color.Brown : undefined) + .onClick(()=>{ + this.showMode = promptAction.ToastShowMode.TOP_MOST + }) + } + .margin(5) + + Row() { + Text(`对齐方式:${this.alignmentKeys[this.alignIdx]}`) + Button('++1') + .myButtonStyle() + .onClick(()=>{ + this.alignIdx = (this.alignIdx + 1) % this.alignments.length + }) + Button('--1') + .myButtonStyle() + .onClick(()=>{ + this.alignIdx = (this.alignIdx - 1) % this.alignments.length + }) + } + + Button('showToast') + .onClick(()=>{ + try { + promptAction.showToast({ + message: 'Hello World', + duration: 2000, + showMode: this.showMode, + alignment: this.alignments[this.alignIdx], + backgroundColor: this.toastBackgroundColor, + textColor: this.toastTextColor, + backgroundBlurStyle: BlurStyle.NONE + }); + } catch (error) { + let message = (error as BusinessError).message + let code = (error as BusinessError).code + console.error(`showToast args error code is ${code}, message is ${message}`); + }; + }) + } + .width('100%') + .margin({top: 5}) + } + .height('100%') + .border({width: 1, color: Color.Red}) + } + .title('toastPositionTest') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/navigationPage.ets b/examples/Overlay/entry/src/main/ets/pages/navigationPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..cd1e5fcfda253179a0ffcbef70e5dd31a2ba1e76 --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/navigationPage.ets @@ -0,0 +1,53 @@ +/* + * 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 { NavList, NavListItem } from './utils/navigationList' + +@Builder +export function NavIndexBuilder(name: string, param: Object) { + NavIndex() +} + +@Entry +@Component +struct NavIndex { + pathStack: NavPathStack = new NavPathStack() + paths: NavListItem[] = [ + { name: 'Dialog', path: 'DialogIndex' }, + { name: 'Toast', path: 'ToastIndex' }, + { name: 'Overlay', path: 'OverlayIndex' }, + ] + + build() { + NavDestination() { + NavList({ + pages: this.paths, + onPathChange: (item: NavListItem) => { + this.pathStack.pushPath( { name: item.path } ) + } + }) + } + .title('Overlay') + .height('100%') + .width('100%') + .onBackPressed(() => { + this.pathStack.pop() + return true + }) + .onReady((context: NavDestinationContext) => { + this.pathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/ets/pages/utils/navigationList.ets b/examples/Overlay/entry/src/main/ets/pages/utils/navigationList.ets new file mode 100644 index 0000000000000000000000000000000000000000..efc65256af25d0cfc85cd95f0e479ac3836a747b --- /dev/null +++ b/examples/Overlay/entry/src/main/ets/pages/utils/navigationList.ets @@ -0,0 +1,39 @@ +/* + * 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. + */ + +export interface NavListItem { + name: string; + path: string; +} + +@Component +export struct NavList { + pages: NavListItem[] = [] + onPathChange: (item: NavListItem) => void = () => {}; + + build() { + Column() { + ForEach(this.pages, (item: NavListItem) => { + Button(item.name) + .stateEffect(true) + .type(ButtonType.Capsule) + .width('80%') + .height(40) + .margin(20) + .onClick(() => this.onPathChange(item)) + }) + } + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/module.json5 b/examples/Overlay/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..893bd6fe1993e8b069abc9fcc8c3b1b528993b46 --- /dev/null +++ b/examples/Overlay/entry/src/main/module.json5 @@ -0,0 +1,64 @@ +/* + * 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:app_icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "orientation": "auto_rotation", + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "UIExtensionProvider", + "srcEntry": "./ets/extensionAbility/extensionAbility.ets", + "description": "UIExtensionProvider", + "exported": false, + "type": "sys/commonUI", + "icon": "$media:app_icon" + } + ], + "routerMap": "$profile:route_map" + } +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/resources/base/element/color.json b/examples/Overlay/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/examples/Overlay/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/resources/base/element/string.json b/examples/Overlay/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/examples/Overlay/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/resources/base/profile/main_pages.json b/examples/Overlay/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..9e81d680722fa3d49755eaf629bd7e04c1ee1d58 --- /dev/null +++ b/examples/Overlay/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,7 @@ +{ + "src": [ + "pages/Index", + "extensionAbility/pages/Index", + "pages/components/dialog/embeddedDialogPageTwo" + ] +} diff --git a/examples/Overlay/entry/src/main/resources/base/profile/route_map.json b/examples/Overlay/entry/src/main/resources/base/profile/route_map.json new file mode 100644 index 0000000000000000000000000000000000000000..22aa1fc7b9e2495400f45f3fa718e50df7d5bc4c --- /dev/null +++ b/examples/Overlay/entry/src/main/resources/base/profile/route_map.json @@ -0,0 +1,89 @@ +{ + "routerMap": [ + { + "name": "NavIndex", + "pageSourceFile": "src/main/ets/pages/navigationPage.ets", + "buildFunction": "NavIndexBuilder" + }, + { + "name": "DialogIndex", + "pageSourceFile": "src/main/ets/pages/components/dialog/Index.ets", + "buildFunction": "DialogIndexBuilder" + }, + { + "name": "AlertDialog", + "pageSourceFile": "src/main/ets/pages/components/dialog/alertDialog.ets", + "buildFunction": "AlertDialogBuilder" + }, + { + "name": "ActionSheet", + "pageSourceFile": "src/main/ets/pages/components/dialog/actionSheet.ets", + "buildFunction": "ActionSheetBuilder" + }, + { + "name": "CustomDialog", + "pageSourceFile": "src/main/ets/pages/components/dialog/customDialog.ets", + "buildFunction": "CustomDialogBuilder" + }, + { + "name": "PromptAction", + "pageSourceFile": "src/main/ets/pages/components/dialog/promptAction.ets", + "buildFunction": "PromptActionBuilder" + }, + { + "name": "DialogController", + "pageSourceFile": "src/main/ets/pages/components/dialog/dialogController.ets", + "buildFunction": "DialogControllerBuilder" + }, + { + "name": "DialogLevelOrder", + "pageSourceFile": "src/main/ets/pages/components/dialog/dialogLevelOrder.ets", + "buildFunction": "DialogLevelOrderBuilder" + }, + { + "name": "DialogFocusable", + "pageSourceFile": "src/main/ets/pages/components/dialog/dialogFocusable.ets", + "buildFunction": "DialogFocusableBuilder" + }, + { + "name": "EmbeddedDialogPageOne", + "pageSourceFile": "src/main/ets/pages/components/dialog/embeddedDialogPageOne.ets", + "buildFunction": "EmbeddedDialogPageOneBuilder" + }, + { + "name": "OverlayIndex", + "pageSourceFile": "src/main/ets/pages/components/overlay/Index.ets", + "buildFunction": "OverlayIndexBuilder" + }, + { + "name": "OverlayLevelOrder", + "pageSourceFile": "src/main/ets/pages/components/overlay/overlayLevelOrder.ets", + "buildFunction": "OverlayLevelOrderBuilder" + }, + { + "name": "OverlayManagerHitTest", + "pageSourceFile": "src/main/ets/pages/components/overlay/overlayManagerHitTest.ets", + "buildFunction": "OverlayManagerHitBuilder" + }, + { + "name": "ToastIndex", + "pageSourceFile": "src/main/ets/pages/components/toast/Index.ets", + "buildFunction": "ToastIndexBuilder" + }, + { + "name": "Toast", + "pageSourceFile": "src/main/ets/pages/components/toast/toast.ets", + "buildFunction": "ToastBuilder" + }, + { + "name": "showToastTest", + "pageSourceFile": "src/main/ets/pages/components/toast/showToastTest.ets", + "buildFunction": "ShowToastBuilder" + }, + { + "name": "toastPositionTest", + "pageSourceFile": "src/main/ets/pages/components/toast/toastPositionTest.ets", + "buildFunction": "ToastPositionBuilder" + } + ] +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/resources/en_US/element/string.json b/examples/Overlay/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/examples/Overlay/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/main/resources/zh_CN/element/string.json b/examples/Overlay/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..597ecf95e61d7e30367c22fe2f8638008361b044 --- /dev/null +++ b/examples/Overlay/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/mock/mock-config.json5 b/examples/Overlay/entry/src/mock/mock-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b9a78e201535765168a92d3543c690273ecdc019 --- /dev/null +++ b/examples/Overlay/entry/src/mock/mock-config.json5 @@ -0,0 +1,17 @@ +/* + * 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. + */ + +{ +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/ohosTest/ets/test/Ability.test.ets b/examples/Overlay/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f8ce9a2c012f8fe36114cef65216ef0b6254f41 --- /dev/null +++ b/examples/Overlay/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* + * 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 { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/ohosTest/ets/test/List.test.ets b/examples/Overlay/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/examples/Overlay/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * 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 abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/ohosTest/module.json5 b/examples/Overlay/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..6b9889e8ccb62f93825d6b26fa53db8e97a974c0 --- /dev/null +++ b/examples/Overlay/entry/src/ohosTest/module.json5 @@ -0,0 +1,28 @@ +/* + * 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/examples/Overlay/entry/src/test/List.test.ets b/examples/Overlay/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1186b1f53c3a70930921c5dbd1417332bec56c9 --- /dev/null +++ b/examples/Overlay/entry/src/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * 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 localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/examples/Overlay/entry/src/test/LocalUnit.test.ets b/examples/Overlay/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..7fc57c77dbf76d8df08a2b802a55b948e3fcf968 --- /dev/null +++ b/examples/Overlay/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,48 @@ +/* + * 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/examples/Overlay/hvigor/hvigor-config.json5 b/examples/Overlay/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..43beb743cbd25c3507b1cf8a744bf8197b3bf2fb --- /dev/null +++ b/examples/Overlay/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/examples/Overlay/hvigorfile.ts b/examples/Overlay/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/examples/Overlay/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * 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 { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/examples/Overlay/oh-package-lock.json5 b/examples/Overlay/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..aa23300ef312bd399ef50cd21202ffe9481200ab --- /dev/null +++ b/examples/Overlay/oh-package-lock.json5 @@ -0,0 +1,42 @@ +/* + * 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. + */ + +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0", + "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19" + }, + "packages": { + "@ohos/hamock@1.0.0": { + "name": "@ohos/hamock", + "version": "1.0.0", + "integrity": "sha512-K6lDPYc6VkKe6ZBNQa9aoG+ZZMiwqfcR/7yAVFSUGIuOAhPvCJAo9+t1fZnpe0dBRBPxj2bxPPbKh69VuyAtDg==", + "resolved": "https://repo.harmonyos.com/ohpm/@ohos/hamock/-/hamock-1.0.0.har", + "registryType": "ohpm" + }, + "@ohos/hypium@1.0.19": { + "name": "@ohos/hypium", + "version": "1.0.19", + "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==", + "resolved": "https://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.19.har", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/examples/Overlay/oh-package.json5 b/examples/Overlay/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..93f097993a458e967d6d5239ea0580e79b5d6998 --- /dev/null +++ b/examples/Overlay/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * 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. + */ + +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +}