cj-tauri 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -99
- package/bin/cj-tauri.js +287 -287
- package/cjpm.toml +63 -52
- package/cli/cjpm.toml +22 -22
- package/cli/src/doctor.cj +308 -306
- package/cli/src/main.cj +129 -129
- package/cli/src/manifest.cj +632 -558
- package/cli/src/manifest_test.cj +87 -0
- package/cli/src/project.cj +770 -770
- package/cli/src/resolve.cj +348 -348
- package/cli/src/scaffold.cj +369 -369
- package/cli/src/sync.cj +526 -526
- package/cli/src/util.cj +168 -168
- package/cli/templates/app/README.md +51 -48
- package/cli/templates/app/assets/capabilities/default.json +7 -7
- package/cli/templates/app/assets/ui/index.html +120 -118
- package/cli/templates/app/cj-tauri.toml +32 -22
- package/cli/templates/app/cjpm.toml +34 -34
- package/cli/templates/app/src/main.cj +101 -90
- package/cli/templates/app-react/README.md +56 -56
- package/cli/templates/app-react/assets/capabilities/default.json +7 -7
- package/cli/templates/app-react/assets/ui/index.html +25 -25
- package/cli/templates/app-react/assets/ui/package.json +19 -19
- package/cli/templates/app-react/assets/ui/src/App.css +67 -67
- package/cli/templates/app-react/assets/ui/src/App.jsx +124 -124
- package/cli/templates/app-react/assets/ui/src/main.jsx +6 -6
- package/cli/templates/app-react/assets/ui/vite.config.js +24 -24
- package/cli/templates/app-react/cj-tauri.toml +33 -23
- package/cli/templates/app-react/cjpm.toml +34 -34
- package/cli/templates/app-react/src/main.cj +91 -91
- package/cli/templates/app-vue/README.md +56 -56
- package/cli/templates/app-vue/assets/capabilities/default.json +7 -7
- package/cli/templates/app-vue/assets/ui/index.html +25 -25
- package/cli/templates/app-vue/assets/ui/package.json +18 -18
- package/cli/templates/app-vue/assets/ui/src/App.vue +176 -176
- package/cli/templates/app-vue/assets/ui/src/main.js +4 -4
- package/cli/templates/app-vue/assets/ui/vite.config.js +24 -24
- package/cli/templates/app-vue/cj-tauri.toml +33 -23
- package/cli/templates/app-vue/cjpm.toml +34 -34
- package/cli/templates/app-vue/src/main.cj +91 -91
- package/cli/templates/ohos-skeleton/AppScope/app.json5 +13 -13
- package/cli/templates/ohos-skeleton/AppScope/resources/base/element/string.json +8 -8
- package/cli/templates/ohos-skeleton/AppScope/resources/base/media/layered_image.json +6 -6
- package/cli/templates/ohos-skeleton/README.md +84 -79
- package/cli/templates/ohos-skeleton/build-hap.sh +15 -0
- package/cli/templates/ohos-skeleton/build-profile.json5 +46 -46
- package/cli/templates/ohos-skeleton/entry/build-profile.json5 +26 -26
- package/cli/templates/ohos-skeleton/entry/cjpm.toml +51 -43
- package/cli/templates/ohos-skeleton/entry/hvigorfile.ts +6 -6
- package/cli/templates/ohos-skeleton/entry/oh-package.json5 +9 -9
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/ability_stage.cj +17 -17
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/app_host.cj +94 -94
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/dialog_host.cj +217 -0
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/index.cj +527 -167
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/main_ability.cj +139 -139
- package/cli/templates/ohos-skeleton/entry/src/main/module.json5 +57 -49
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/color.json +8 -8
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/string.json +16 -16
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/media/layered_image.json +6 -6
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/profile/main_pages.json +2 -2
- package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/capabilities.json +6 -6
- package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/index.html +92 -92
- package/cli/templates/ohos-skeleton/hvigor/hvigor-config.json5 +5 -5
- package/cli/templates/ohos-skeleton/hvigorfile.ts +6 -6
- package/cli/templates/ohos-skeleton/oh-package.json5 +10 -10
- package/native/build_win.bat +62 -62
- package/native/core/bridge_core.c +1681 -1626
- package/native/core/bridge_core.h +675 -618
- package/native/core/bridge_js.h +108 -108
- package/native/platforms/harmonyos/README.md +115 -6
- package/native/platforms/linux/bridge_linux.c +2787 -2485
- package/native/platforms/windows/bridge_win.c +3740 -3248
- package/native/tests/gtk_threading_probe.c +143 -143
- package/native/tests/test_bridge_core.c +1339 -1228
- package/package.json +53 -53
- package/prebuilt/linux-x64/cj-tauri +0 -0
- package/src/api_system.cj +67 -67
- package/src/app.cj +325 -969
- package/src/app_events.cj +166 -0
- package/src/app_host_ops.cj +287 -0
- package/src/app_run.cj +283 -0
- package/src/app_spec.cj +76 -76
- package/src/arkweb_menu.cj +216 -0
- package/src/arkweb_script.cj +142 -142
- package/src/capability.cj +409 -409
- package/src/clipboard_bridge.cj +178 -0
- package/src/dialog_spec.cj +79 -0
- package/src/dragdrop.cj +26 -26
- package/src/embedded_assets.cj +98 -98
- package/src/host.cj +752 -607
- package/src/host_arkweb.cj +924 -778
- package/src/host_webkit.cj +663 -570
- package/src/host_webview2.cj +663 -570
- package/src/hotkey.cj +66 -66
- package/src/ipc_hub.cj +337 -337
- package/src/ipc_message.cj +286 -286
- package/src/launch.cj +62 -62
- package/src/logging.cj +127 -127
- package/src/menu.cj +197 -197
- package/src/platform/platform_app_context.cj +77 -77
- package/src/platform/platform_assets.cj +33 -33
- package/src/platform/platform_discover.cj +176 -176
- package/src/platform/platform_env.cj +29 -29
- package/src/platform/platform_feature.cj +31 -31
- package/src/platform/platform_page_source.cj +87 -87
- package/src/platform/platform_target_info.cj +32 -32
- package/src/platform_host_provider.cj +87 -87
- package/src/plugin.cj +333 -333
- package/src/plugins/clipboard/plugin_clipboard.cj +152 -152
- package/src/plugins/dialog/plugin_dialog.cj +209 -209
- package/src/plugins/fs/plugin_fs.cj +105 -1
- package/src/plugins/http/http_backend.cj +131 -0
- package/src/plugins/http/http_desktop.cj +195 -0
- package/src/plugins/http/http_ohos.cj +204 -0
- package/src/plugins/http/plugin_http.cj +310 -459
- package/src/plugins/log/plugin_log.cj +105 -105
- package/src/plugins/menu/plugin_menu.cj +158 -158
- package/src/plugins/notification/plugin_notification.cj +154 -154
- package/src/plugins/serial/plugin_serial.cj +458 -454
- package/src/plugins/serial/serial_bridge.cj +194 -194
- package/src/plugins/serial/serial_manager_ohos.cj +92 -0
- package/src/plugins/serial/serial_ohos.cj +529 -0
- package/src/plugins/shell/plugin_shell.cj +258 -258
- package/src/plugins/sqlite/plugin_sqlite.cj +705 -710
- package/src/plugins/sqlite/sqlite_ffi.cj +206 -211
- package/src/plugins/ws/plugin_ws.cj +488 -470
- package/src/process_hidden.cj +174 -174
- package/src/scope.cj +391 -391
- package/src/shell_event.cj +69 -69
- package/src/tests/app_spec_test.cj +126 -127
- package/src/tests/capability_test.cj +243 -243
- package/src/tests/dialog_spec_test.cj +73 -0
- package/src/tests/fake_host.cj +201 -201
- package/src/tests/ipc_hub_test.cj +487 -487
- package/src/tests/logging_test.cj +62 -62
- package/src/tests/platform_contract_test.cj +251 -251
- package/src/tests/plugin_clipboard_test.cj +222 -222
- package/src/tests/plugin_dialog_test.cj +237 -237
- package/src/tests/plugin_fs_test.cj +207 -0
- package/src/tests/plugin_http_test.cj +124 -124
- package/src/tests/plugin_notification_test.cj +205 -205
- package/src/tests/plugin_serial_test.cj +48 -0
- package/src/tests/plugin_test.cj +605 -585
- package/src/tests/plugin_ws_test.cj +40 -40
- package/src/tests/process_hidden_test.cj +129 -129
- package/src/tests/scope_test.cj +350 -350
- package/src/tests/system_host_test.cj +99 -81
- package/src/tests/urlutil_test.cj +53 -53
- package/src/tests/version_test.cj +75 -75
- package/src/tests/window_registry_test.cj +123 -123
- package/src/tray.cj +90 -90
- package/src/urlutil.cj +159 -159
- package/src/version.cj +44 -44
- package/src/window.cj +51 -51
- package/src/window_registry.cj +116 -116
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/plugins/serial/serial_ohos_stub.cj +0 -61
- package/src/plugins/sqlite/sqlite_ohos_stub.cj +0 -91
|
@@ -1,167 +1,527 @@
|
|
|
1
|
-
package ohos_app_cangjie_entry
|
|
2
|
-
|
|
3
|
-
import kit.ArkUI.*
|
|
4
|
-
import kit.ArkWeb.*
|
|
5
|
-
import kit.PerformanceAnalysisKit.Hilog
|
|
6
|
-
import ohos.arkui.component.web.Web
|
|
7
|
-
import ohos.arkui.state_macro_manage.*
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
1
|
+
package ohos_app_cangjie_entry
|
|
2
|
+
|
|
3
|
+
import kit.ArkUI.*
|
|
4
|
+
import kit.ArkWeb.*
|
|
5
|
+
import kit.PerformanceAnalysisKit.Hilog
|
|
6
|
+
import ohos.arkui.component.web.Web
|
|
7
|
+
import ohos.arkui.state_macro_manage.*
|
|
8
|
+
import std.collection.ArrayList
|
|
9
|
+
import cjTauri.*
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* 菜单条外观常量(0xAARRGGBB,Int64 就是 ResourceColor)。
|
|
13
|
+
*
|
|
14
|
+
* 对齐的是桌面应用的菜单栏:一行文字、悬停时浅灰底浮起、**没有**按钮那种描边与实心块。
|
|
15
|
+
* 早先用 `Button` 时观感与 Windows 差得远——那是一排按钮,不是菜单栏(用户实测反馈)。
|
|
16
|
+
*
|
|
17
|
+
* 必须写在**使用点之前**:仓颉的全局 `let` 按出现顺序解析,写在文件尾部时 `@Component`
|
|
18
|
+
* 宏展开出来的 build() 会报 `variable 'MENU_BAR_BG' is used before being defined`。
|
|
19
|
+
*/
|
|
20
|
+
let MENU_BAR_BG: Int64 = 0xFFF9F9F9
|
|
21
|
+
|
|
22
|
+
let MENU_BAR_HOVER: Int64 = 0xFFE8E8E8
|
|
23
|
+
|
|
24
|
+
let MENU_BAR_TEXT: Int64 = 0xFF1A1A1A
|
|
25
|
+
|
|
26
|
+
let MENU_BAR_TEXT_DISABLED: Int64 = 0xFF9AA0A6
|
|
27
|
+
|
|
28
|
+
/*
|
|
29
|
+
* 鸿蒙键盘码:**仓颉 SDK 不导出键码常量**(`KeyEvent.keyCode` 只是裸 `Int32`),
|
|
30
|
+
* 下面三个值是 `uinput -K` 注键 + hilog 探针在真机上量出来的,不是猜的:
|
|
31
|
+
* 字母区 `KEYCODE_A = 2017` 起连续排到 `KEYCODE_Z = 2042`,左右 Ctrl 各 2072 / 2073。
|
|
32
|
+
* 要支持别的组合键(如功能键 F1..F12)照同一办法量一遍再补,别照 Android 的 29 起算抄。
|
|
33
|
+
*/
|
|
34
|
+
let KEY_LETTER_A: Int64 = 2017
|
|
35
|
+
|
|
36
|
+
let KEY_CTRL_LEFT: Int64 = 2072
|
|
37
|
+
|
|
38
|
+
let KEY_CTRL_RIGHT: Int64 = 2073
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* 应用侧的 ArkWeb 宿主组件
|
|
42
|
+
*
|
|
43
|
+
* 这个 `@Component` **必须住在应用模块里**,不能由框架包提供:框架包一旦 import
|
|
44
|
+
* `kit.ArkUI.*` / `kit.ArkWeb.*`,合并进应用动态库后包初始化链会跳到坏地址、启动即 SIGSEGV。
|
|
45
|
+
* 框架侧只留 `arkWebProxyPost` / `arkWebProxyPoll` 两个不碰 ArkUI 的桥接函数。
|
|
46
|
+
*
|
|
47
|
+
* 单窗口版:label 用框架默认的 DEFAULT_WINDOW_LABEL。要做多窗口就照着加一份、
|
|
48
|
+
* 换成别的 label(框架的宿主路由是按 label 查的,不会串台)。
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* proxy 的 `post` 入口:先交给框架处理报文,再把队列里的脚本推回页面
|
|
53
|
+
*
|
|
54
|
+
* 为什么「顺手推」放在这里:`javaScriptProxy` 的回调就在 UI 线程上,而
|
|
55
|
+
* `controller.runJavaScript` 也必须在 UI 线程调。页面 → 原生的**返回值通道不可用**
|
|
56
|
+
* (实测恒为 `null`),所以鸿蒙侧只能「页面发心跳 → 原生在该回调里把待执行脚本推出去」,
|
|
57
|
+
* 这是唯一的回投路径;框架会把它收到的 invoke 结果 / event 都排进这个队列。
|
|
58
|
+
*/
|
|
59
|
+
func onProxyPost(label: String, raw: String, ctrl: WebviewController): String {
|
|
60
|
+
let ack = arkWebProxyPost(label, raw)
|
|
61
|
+
match (ArkWebRuntime.hostFor(label)) {
|
|
62
|
+
case Some(h) =>
|
|
63
|
+
let pending = h.takePendingScripts()
|
|
64
|
+
var i = 0
|
|
65
|
+
while (i < pending.size) {
|
|
66
|
+
ctrl.runJavaScript(pending[i], { _, _ => () })
|
|
67
|
+
i += 1
|
|
68
|
+
}
|
|
69
|
+
case None => ()
|
|
70
|
+
}
|
|
71
|
+
return ack
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 取一条待呈现的原生弹窗(**UI 线程**;没有就返回 None)
|
|
76
|
+
*
|
|
77
|
+
* 弹窗的**呈现**必须在组件上下文里做(`getUIContext()` 只在组件内可调),所以这里只把请求
|
|
78
|
+
* 取出来,真正的画面交给 `build()` 里的声明式 overlay——命令式 `showDialog` 从 JS 桥接
|
|
79
|
+
* 回调栈上启动会被 ArkUI 静默丢弃(取证见 docs/鸿蒙ArkWeb接入踩坑.md §10.11)。
|
|
80
|
+
*/
|
|
81
|
+
func arkWebTakeDialog(label: String): Option<ArkWebDialogRequest> {
|
|
82
|
+
match (ArkWebRuntime.hostFor(label)) {
|
|
83
|
+
case Some(h) =>
|
|
84
|
+
let request = h.takePendingDialog()
|
|
85
|
+
match (request) {
|
|
86
|
+
case Some(r) => Hilog.info(1, "CjTauri", "dialog take label=${label} id=${r.id.toString()} kind=${r.kind}")
|
|
87
|
+
case None => ()
|
|
88
|
+
}
|
|
89
|
+
return request
|
|
90
|
+
case None => return None
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** 回填用户的选择(overlay 按钮回调里调;宿主可能已销毁——如实留日志,不假装成功) */
|
|
95
|
+
func answerArkWebDialog(label: String, id: Int64, confirmed: Bool): Unit {
|
|
96
|
+
Hilog.info(1, "CjTauri", "dialog answer label=${label} id=${id.toString()} confirmed=${confirmed.toString()}")
|
|
97
|
+
match (ArkWebRuntime.hostFor(label)) {
|
|
98
|
+
case Some(h) => h.answerDialog(id, confirmed)
|
|
99
|
+
case None => Hilog.info(1, "CjTauri", "dialog answer without host")
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Component
|
|
104
|
+
class CjTauriWebView {
|
|
105
|
+
/* label 决定:①页面里 __CJ_TAURI_LABEL__ ②报文里的 window 字段 ③宿主取哪份页面来源。
|
|
106
|
+
必须是 `@Prop`:普通成员会被宏展开成 ObservedProperty 形参,外面传字符串进来会报
|
|
107
|
+
「expected 4 arguments」。 */
|
|
108
|
+
@Prop
|
|
109
|
+
var windowLabel: String
|
|
110
|
+
|
|
111
|
+
/* 组件生命周期内必须保持同一个 controller 实例(ArkWeb 的硬约束) */
|
|
112
|
+
private let controller: WebviewController = WebviewController()
|
|
113
|
+
|
|
114
|
+
/* 页面地址由宿主给定;@State 让它参与刷新(宿主登记页面往往晚于组件首次 build)。
|
|
115
|
+
`@State` 不允许与 private/public/static 同用——宏会直接拒绝。 */
|
|
116
|
+
@State
|
|
117
|
+
var pageUrl: String = ""
|
|
118
|
+
|
|
119
|
+
/*
|
|
120
|
+
* 菜单快照:框架存模型(`ArkWebHost.setMenu` 解析线路格式)、**应用侧**画(`bindMenu`)——
|
|
121
|
+
* 这是「框架包不引用 ArkUI」前提下的唯一分工(见框架 src/arkweb_menu.cj 的类注释)。
|
|
122
|
+
*
|
|
123
|
+
* 刷新节拍借用页面的心跳:`setMenuItemState` 的改动没有别的通知通道(框架碰不到 ArkUI),
|
|
124
|
+
* 而页面每 500ms 发一次 pull 心跳正好会进 proxy 回调,顺手重取一次快照即可。
|
|
125
|
+
* 应用不调 `setMenu` 时这份快照为空,菜单条不占位、界面与以前完全一样。
|
|
126
|
+
*/
|
|
127
|
+
@State
|
|
128
|
+
var menuSnapshot: ArrayList<ArkWebMenuItem> = ArrayList<ArkWebMenuItem>()
|
|
129
|
+
|
|
130
|
+
/* 声明式弹窗的状态(@State 不能与 private 同用,宏会拒绝;理由同探针工程 §10.11) */
|
|
131
|
+
@State
|
|
132
|
+
var dialogActive: Bool = false
|
|
133
|
+
|
|
134
|
+
@State
|
|
135
|
+
var dialogTitle: String = ""
|
|
136
|
+
|
|
137
|
+
@State
|
|
138
|
+
var dialogMessage: String = ""
|
|
139
|
+
|
|
140
|
+
@State
|
|
141
|
+
var dialogIsConfirm: Bool = false
|
|
142
|
+
|
|
143
|
+
@State
|
|
144
|
+
var dialogRequestId: Int64 = -1
|
|
145
|
+
|
|
146
|
+
/* kind 只落到「确定键底色」这一处可视区分(info 蓝 / warning 橙 / error 红);
|
|
147
|
+
单键还是双键由框架侧的 `request.isConfirm()` 定,别在这里再判一次。 */
|
|
148
|
+
@State
|
|
149
|
+
var dialogKind: String = ""
|
|
150
|
+
|
|
151
|
+
/* 菜单条当前悬停的那一格(按下标,-1 = 都没有)。只为「悬停浅灰底」这一个视觉状态服务,
|
|
152
|
+
与菜单模型无关——模型刷新时下标可能指向别的项,下一次 onHover 就会纠正。 */
|
|
153
|
+
@State
|
|
154
|
+
var menuHoverIndex: Int64 = -1
|
|
155
|
+
|
|
156
|
+
/* Ctrl 的按下态:仓颉 `KeyEvent` 没有修饰键查询接口,只能自己按 keyCode 记账
|
|
157
|
+
(Down 置位、Up 清位)。@State 只为复用同一套字段声明习惯,取值不进布局。 */
|
|
158
|
+
@State
|
|
159
|
+
var ctrlDown: Bool = false
|
|
160
|
+
|
|
161
|
+
public func build() {
|
|
162
|
+
/*
|
|
163
|
+
* Stack 包住「页面 + 对话框 overlay」:overlay 与 Web 同属一棵组件树、走声明式重绘,
|
|
164
|
+
* 绕开「桥接回调里 showDialog 被丢弃」的坑(docs/鸿蒙ArkWeb接入踩坑.md §10.11)。
|
|
165
|
+
*/
|
|
166
|
+
Stack(alignContent: Alignment.Center) {
|
|
167
|
+
Column {
|
|
168
|
+
/*
|
|
169
|
+
* 菜单条:**有菜单才占位**(没调 setMenu 的应用看不到它)。
|
|
170
|
+
*
|
|
171
|
+
* 分组渲染:顶层每个子菜单(文件 / 视图 / 帮助)各占一格,点开弹该组的扁平项——
|
|
172
|
+
* 仓颉侧 `MenuElement` 没有嵌套,组内仍是一层,但顶层按组分开,
|
|
173
|
+
* 不再是一根「文件 / 打开…」摊平的大列表。分隔线与禁用项不画。
|
|
174
|
+
*
|
|
175
|
+
* 用 `Text` 而不是 `Button`:后者自带描边/实心块/按压缩放,一排排开就是「一排按钮」,
|
|
176
|
+
* 与 Windows 的菜单栏(纯文字 + 悬停浅灰底)差异明显。悬停态由 `menuHoverIndex` 记,
|
|
177
|
+
* `onHover` 只改这个状态、不碰别的,重绘范围就是那一格的底色。
|
|
178
|
+
*/
|
|
179
|
+
if (this.menuSnapshot.size > 0) {
|
|
180
|
+
Row {
|
|
181
|
+
/* lambda 形参**必须标类型**:不标时 @Component 宏展开后给 ForEach 补不出 T,
|
|
182
|
+
报的是 `expected type argument`(指向宏展开出来的 `ForEach<>`),
|
|
183
|
+
而 `ForEach<T>(…)` 这种写法又会被宏读成「组件 + 尾随块」而报
|
|
184
|
+
`expected expression after '<'`——两条都试过,只有标类型这条路能过。 */
|
|
185
|
+
ForEach(this.menuSnapshot, itemGenerator: { item: ArkWebMenuItem, index: Int64 =>
|
|
186
|
+
if (item.isSubmenu() && item.enabled) {
|
|
187
|
+
Text(menuItemText(item))
|
|
188
|
+
.fontSize(14)
|
|
189
|
+
.fontColor(menuBarTextColor(item))
|
|
190
|
+
.height(30)
|
|
191
|
+
.padding(top: 0, right: 12, bottom: 0, left: 12)
|
|
192
|
+
.borderRadius(6)
|
|
193
|
+
.backgroundColor(menuBarCellBg(this.menuHoverIndex == index))
|
|
194
|
+
.onHover({ hover => this.menuHoverIndex = if (hover) { index } else { -1 } })
|
|
195
|
+
.bindMenu(this.groupElements(item))
|
|
196
|
+
} else if (item.enabled && !item.isSeparator()) {
|
|
197
|
+
Text(menuItemText(item))
|
|
198
|
+
.fontSize(14)
|
|
199
|
+
.fontColor(menuBarTextColor(item))
|
|
200
|
+
.height(30)
|
|
201
|
+
.padding(top: 0, right: 12, bottom: 0, left: 12)
|
|
202
|
+
.borderRadius(6)
|
|
203
|
+
.backgroundColor(menuBarCellBg(this.menuHoverIndex == index))
|
|
204
|
+
.onHover({ hover => this.menuHoverIndex = if (hover) { index } else { -1 } })
|
|
205
|
+
.onClick({ _ => this.fireMenu(item.id) })
|
|
206
|
+
}
|
|
207
|
+
}, keyGenerator: { item: ArkWebMenuItem, index: Int64 => keyOf(item, index) })
|
|
208
|
+
}
|
|
209
|
+
.width(100.percent)
|
|
210
|
+
.height(40)
|
|
211
|
+
.padding(top: 0, right: 6, bottom: 0, left: 6)
|
|
212
|
+
.backgroundColor(MENU_BAR_BG)
|
|
213
|
+
.alignItems(VerticalAlign.Center)
|
|
214
|
+
}
|
|
215
|
+
if (this.pageUrl.size > 0) {
|
|
216
|
+
Web(src: this.pageUrl, controller: this.controller)
|
|
217
|
+
.width(100.percent)
|
|
218
|
+
.height(100.percent)
|
|
219
|
+
.fileAccess(true)
|
|
220
|
+
.onKeyEvent({ e => this.handleMenuKey(e) })
|
|
221
|
+
.javaScriptProxy(
|
|
222
|
+
funcList: [
|
|
223
|
+
{
|
|
224
|
+
raw =>
|
|
225
|
+
let ack = onProxyPost(this.windowLabel, raw, this.controller)
|
|
226
|
+
/* 心跳节拍:页面每 500ms 一次 pull,顺手把宿主里的菜单模型
|
|
227
|
+
重取一次(setMenuItemState 的改动没有别的通知通道),
|
|
228
|
+
并取一次待呈现的对话框——呈现点必须同时满足 UI 线程
|
|
229
|
+
与组件上下文,proxy 回调两者都占。 */
|
|
230
|
+
this.reloadMenu()
|
|
231
|
+
this.presentPendingDialog()
|
|
232
|
+
return ack
|
|
233
|
+
},
|
|
234
|
+
/* `poll` 只为兼容页面上可能残留的旧桥脚本:返回值传不回 JS,
|
|
235
|
+
真正的脚本推送已在 `onProxyPost` 里完成。 */
|
|
236
|
+
{ _ => "" }
|
|
237
|
+
],
|
|
238
|
+
name: ARKWEB_PROXY_NAME, methodList: ["post", "poll"],
|
|
239
|
+
controller: this.controller
|
|
240
|
+
)
|
|
241
|
+
.onPageBegin({ _ => this.onPageBegin() })
|
|
242
|
+
.onPageEnd({ _ => this.onPageEnd() })
|
|
243
|
+
}
|
|
244
|
+
}.width(100.percent).height(100.percent)
|
|
245
|
+
|
|
246
|
+
/* 对话框 overlay 交给同包的 `CjDialogHost`(dialog_host.cj)呈现:
|
|
247
|
+
业务要换配色/布局改那个组件,不必动这份骨架。这里只负责驱动——
|
|
248
|
+
`dialogActive` 由 `presentPendingDialog()` 置起,收起由组件自己记。 */
|
|
249
|
+
if (this.dialogActive) {
|
|
250
|
+
CjDialogHost(
|
|
251
|
+
windowLabel: this.windowLabel,
|
|
252
|
+
requestId: this.dialogRequestId,
|
|
253
|
+
title: this.dialogTitle,
|
|
254
|
+
message: this.dialogMessage,
|
|
255
|
+
twoKey: this.dialogIsConfirm,
|
|
256
|
+
kind: this.dialogKind
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
}.width(100.percent).height(100.percent)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** 从宿主取一条待呈现对话框并点亮 overlay(proxy 回调里调:UI 线程 + 组件上下文) */
|
|
263
|
+
private func presentPendingDialog(): Unit {
|
|
264
|
+
/* 上一条还没被回填就不再取新请求。页面连发两条(菜单点击与另一条撞在同一帧)时,
|
|
265
|
+
后一条会顶掉前一条的 `dialogRequestId`,而前一条在框架侧 `awaitingDialogs` 里仍挂着——
|
|
266
|
+
真机日志里就是 `dialog take id=1` 紧跟 `dialog take id=2`,最后只回填了 id=2,
|
|
267
|
+
id=1 一直悬挂到 120s 超时。等当前这条被回填、overlay 收起后再取下一条即可。
|
|
268
|
+
|
|
269
|
+
判据用「请求 id == 已回填 id」而**不是** `dialogActive`:后者一旦置起就不复位,
|
|
270
|
+
拿它当闸门会把收起之后的第二次点菜单也一并挡掉(表现为「只弹得出第一次」)。 */
|
|
271
|
+
if (this.dialogRequestId != dialogAnsweredIdOf(this.windowLabel)) {
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
match (arkWebTakeDialog(this.windowLabel)) {
|
|
275
|
+
case Some(request) =>
|
|
276
|
+
Hilog.info(1, "CjTauri", "dialog present label=${this.windowLabel} id=${request.id.toString()} title=${request.title}")
|
|
277
|
+
this.dialogRequestId = request.id
|
|
278
|
+
this.dialogIsConfirm = request.isConfirm()
|
|
279
|
+
this.dialogTitle = request.title
|
|
280
|
+
this.dialogMessage = request.message
|
|
281
|
+
this.dialogKind = request.kind
|
|
282
|
+
this.dialogActive = true
|
|
283
|
+
case None => ()
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 组件挂载钩子(首次 `build()` 之前调用一次)
|
|
289
|
+
*
|
|
290
|
+
* **必须在这里同步宿主地址**:宿主把页面落盘后才把 `file://` 地址放进自己的字段,
|
|
291
|
+
* 而 `build()` 是声明式的一次性求值——`pageUrl` 仍为空就永远不建 `Web`,
|
|
292
|
+
* `onPageBegin` 也就永远不会来,页面白屏。
|
|
293
|
+
*/
|
|
294
|
+
protected override func aboutToAppear(): Unit {
|
|
295
|
+
match (ArkWebRuntime.hostFor(this.windowLabel)) {
|
|
296
|
+
case Some(h) =>
|
|
297
|
+
let u = h.currentUrl()
|
|
298
|
+
if (u.size > 0) {
|
|
299
|
+
this.pageUrl = u
|
|
300
|
+
}
|
|
301
|
+
Hilog.info(1, "CjTauri", "aboutToAppear label=${this.windowLabel} pageUrl=${u}")
|
|
302
|
+
case None => Hilog.info(1, "CjTauri", "aboutToAppear without host")
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private func onPageBegin(): Unit {
|
|
307
|
+
Hilog.info(1, "CjTauri", "page begin label=${this.windowLabel}")
|
|
308
|
+
match (ArkWebRuntime.hostFor(this.windowLabel)) {
|
|
309
|
+
case Some(h) =>
|
|
310
|
+
h.notePageBegin()
|
|
311
|
+
let url = h.currentUrl()
|
|
312
|
+
if (url.size > 0 && url != this.pageUrl) {
|
|
313
|
+
this.pageUrl = url
|
|
314
|
+
}
|
|
315
|
+
// 桥脚本与插件 shim 都在 initScripts 里:ArkWeb 没有 document-start 注入,
|
|
316
|
+
// `onPageBegin` 是能拿到的最早时机。
|
|
317
|
+
for (js in h.initScriptsSnapshot()) {
|
|
318
|
+
this.runScript(js)
|
|
319
|
+
}
|
|
320
|
+
this.runScript(arkWebLabelScript(this.windowLabel))
|
|
321
|
+
case None => Hilog.info(1, "CjTauri", "page begin without host: label=${this.windowLabel}")
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private func onPageEnd(): Unit {
|
|
326
|
+
Hilog.info(1, "CjTauri", "page end label=${this.windowLabel}")
|
|
327
|
+
match (ArkWebRuntime.hostFor(this.windowLabel)) {
|
|
328
|
+
case Some(h) =>
|
|
329
|
+
h.notePageEnd()
|
|
330
|
+
// 组件首次 build 时宿主可能还没登记(页面为空),这里再同步一次并触发刷新
|
|
331
|
+
let url = h.currentUrl()
|
|
332
|
+
if (url.size > 0 && url != this.pageUrl) {
|
|
333
|
+
this.pageUrl = url
|
|
334
|
+
}
|
|
335
|
+
case None => ()
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** 单条脚本执行(ArkWeb 的 runJavaScript 是异步的,回调里只做失败日志) */
|
|
340
|
+
private func runScript(js: String): Unit {
|
|
341
|
+
if (js.size == 0) {
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
// runJavaScript(script, callback) 两个都是位置参数(命名参数前缀会被编译器拒)
|
|
345
|
+
this.controller.runJavaScript(
|
|
346
|
+
js,
|
|
347
|
+
{
|
|
348
|
+
err, result =>
|
|
349
|
+
match (err) {
|
|
350
|
+
case Some(e) => Hilog.info(1, "CjTauri", "runJavaScript failed: ${e.message}")
|
|
351
|
+
case None => Hilog.info(1, "CjTauri", "runJavaScript ok")
|
|
352
|
+
}
|
|
353
|
+
let _ = result
|
|
354
|
+
}
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/*
|
|
359
|
+
* 从宿主取一份菜单快照。没有宿主时保持空表——UI 照常建树,别等菜单才画 Web
|
|
360
|
+
* (那条链会死成「不建 Web → onPageBegin 不来 → 地址永远空」的白屏,见 AGENTS §4)。
|
|
361
|
+
*/
|
|
362
|
+
private func reloadMenu(): Unit {
|
|
363
|
+
match (ArkWebRuntime.hostFor(this.windowLabel)) {
|
|
364
|
+
case Some(h) => this.menuSnapshot = h.menuModel()
|
|
365
|
+
case None => ()
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/*
|
|
370
|
+
* 一个子菜单组 → `bindMenu` 的扁平项表。
|
|
371
|
+
*
|
|
372
|
+
* 仓颉侧 `MenuElement` 只有 `value` + `action`:**组内没有嵌套、没有禁用位**。分隔线
|
|
373
|
+
* 不画(画出来点了没反应会被误读成「宿主没落地」);勾选项用「✓ 」前缀表达——
|
|
374
|
+
* 点击经 fireMenu 交回宿主,框架回写模型,下一次心跳重取快照时勾选态跟上。
|
|
375
|
+
*/
|
|
376
|
+
private func groupElements(group: ArkWebMenuItem): Array<MenuElement> {
|
|
377
|
+
let out = ArrayList<MenuElement>()
|
|
378
|
+
for (child in group.children) {
|
|
379
|
+
if (!child.enabled || child.isSeparator()) {
|
|
380
|
+
continue
|
|
381
|
+
}
|
|
382
|
+
let target = child.id
|
|
383
|
+
out.add(MenuElement(value: menuItemText(child), action: { => this.fireMenu(target) }))
|
|
384
|
+
}
|
|
385
|
+
return out.toArray()
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* 一项被点了:交回宿主(宿主回投 shell 报文,与桌面同形),顺带刷新勾选态 */
|
|
389
|
+
private func fireMenu(id: String): Unit {
|
|
390
|
+
// 留一行 hilog 再往下走:设备上框架 stderr 不进 hilog(AGENTS §4),
|
|
391
|
+
// 应用侧这行是「原生真的收到点击」的唯一凭证。
|
|
392
|
+
Hilog.info(1, "CjTauri", "menu click label=${this.windowLabel} id=${id}")
|
|
393
|
+
match (ArkWebRuntime.hostFor(this.windowLabel)) {
|
|
394
|
+
case Some(h) =>
|
|
395
|
+
h.noteMenuClick(id)
|
|
396
|
+
this.reloadMenu()
|
|
397
|
+
case None => Hilog.info(1, "CjTauri", "menu click without host: label=${this.windowLabel}")
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/*
|
|
402
|
+
* 菜单快捷键的按键入口(鸿蒙专属)。
|
|
403
|
+
*
|
|
404
|
+
* 桌面两平台由宿主把 `MenuItem.accel` 绑成真热键(Windows `RegisterHotKey` / Linux X11 grab),
|
|
405
|
+
* 鸿蒙宿主没有这一层(`hostCapabilities()` 的快捷键位是 0),所以应用侧按 accel 自己认键、
|
|
406
|
+
* 再走 `fireMenu` 回投 `menu:click` ——页面侧收到的仍是一条普通菜单事件,不需要为鸿蒙另写分支。
|
|
407
|
+
*
|
|
408
|
+
* Ctrl 的按下态自己记:仓颉 `KeyEvent` 只暴露 `keyType` / `keyCode` / `keyText`,
|
|
409
|
+
* 没有修饰键查询接口(`getModifierKeyState` 不在导出里),所以按下/抬起各记一次。
|
|
410
|
+
*
|
|
411
|
+
* 匹配方向刻意选「拿 accel 反算键码」:事件里只有裸 `keyCode`,若反过来把键码翻成字符
|
|
412
|
+
* 就得自己维护整张字母表;现在只需认识 `Ctrl+<字母>` 这一种形状。
|
|
413
|
+
*/
|
|
414
|
+
private func accelKeyCode(accel: String): Int64 {
|
|
415
|
+
/* 只认 `Ctrl+<单个大写字母>`(6 字节)。其它形状(`Shift+F5` / `Alt+…`)暂不认,
|
|
416
|
+
如实不响应,而不是瞎猜键码——量过的只有字母区与 Ctrl。 */
|
|
417
|
+
if (accel.size != 6 || !accel.startsWith("Ctrl+")) {
|
|
418
|
+
return -1
|
|
419
|
+
}
|
|
420
|
+
var offset: Int64 = -1
|
|
421
|
+
for (b in accel[5..6]) {
|
|
422
|
+
let c = Int64(b)
|
|
423
|
+
if (c >= 65 && c <= 90) {
|
|
424
|
+
offset = c - 65
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (offset < 0) {
|
|
428
|
+
return -1
|
|
429
|
+
}
|
|
430
|
+
return KEY_LETTER_A + offset
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* 在菜单快照里按键码反查条目 id(子菜单递归到底);禁用项不响应,与桌面宿主一致。 */
|
|
434
|
+
private func accelIdIn(items: ArrayList<ArkWebMenuItem>, code: Int64): String {
|
|
435
|
+
for (item in items) {
|
|
436
|
+
if (item.enabled && accelKeyCode(item.accel) == code) {
|
|
437
|
+
return item.id
|
|
438
|
+
}
|
|
439
|
+
let nested = this.accelIdIn(item.children, code)
|
|
440
|
+
if (nested.size > 0) {
|
|
441
|
+
return nested
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return ""
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
private func handleMenuKey(e: KeyEvent): Unit {
|
|
448
|
+
let isDown = match (e.keyType) {
|
|
449
|
+
case KeyType.Down => true
|
|
450
|
+
case _ => false
|
|
451
|
+
}
|
|
452
|
+
let code = Int64(e.keyCode)
|
|
453
|
+
/* Ctrl 的按下态只能自己记账:`KeyEvent` 没有修饰键查询接口
|
|
454
|
+
(真机核对过——`keyText` 也只是 `KEYCODE_O` 这样的裸名字)。 */
|
|
455
|
+
if (code == KEY_CTRL_LEFT || code == KEY_CTRL_RIGHT) {
|
|
456
|
+
this.ctrlDown = isDown
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
|
+
/* **在抬起时判定**:焦点在 Web 内容里时,字母的 key-down 被 ArkWeb/CEF 吃掉当文本输入,
|
|
460
|
+
只有 key-up 会冒泡到 ArkUI(真机日志可证:同一串 Ctrl+S 只见到 ctrl 的 down/up 与
|
|
461
|
+
S 的 up)。若按按下判定,所有组合键都会静默失效。 */
|
|
462
|
+
if (isDown || !this.ctrlDown) {
|
|
463
|
+
return
|
|
464
|
+
}
|
|
465
|
+
let id = this.accelIdIn(this.menuSnapshot, code)
|
|
466
|
+
if (id.size > 0) {
|
|
467
|
+
this.fireMenu(id)
|
|
468
|
+
} else {
|
|
469
|
+
/* 留一行:否则「按了没反应」分不清是键没到、还是 accel 没对上。 */
|
|
470
|
+
Hilog.info(1, "CjTauri", "menu accel miss code=${code}")
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/*
|
|
476
|
+
* ForEach 的键:**必须唯一**,所以拼上序号。
|
|
477
|
+
*
|
|
478
|
+
* 顶层子菜单的 `id` 恒为空串(`MenuItem.submenu` 的规定:子菜单自身不回调,动作交给 children),
|
|
479
|
+
* 直接拿 `item.id` 当键会让「文件 / 视图 / 帮助」三个键全撞成 "",ArkUI 按重复键去重——
|
|
480
|
+
* 表现是**菜单条上只剩第一组**(2026-10-24 真机实测:日志里三组都在,屏上只画出「文件」)。
|
|
481
|
+
*/
|
|
482
|
+
func keyOf(item: ArkWebMenuItem, index: Int64): String {
|
|
483
|
+
return "${index.toString()}-${item.kind}-${item.id}"
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* 菜单项的显示文本:勾选项带「✓」前缀(MenuElement 没有勾选位,只能写进标签) */
|
|
487
|
+
func menuItemText(item: ArkWebMenuItem): String {
|
|
488
|
+
if (item.isCheckbox() && item.checked) {
|
|
489
|
+
return "✓ ${item.label}"
|
|
490
|
+
}
|
|
491
|
+
return item.label
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/*
|
|
495
|
+
* 菜单条外观常量在上面(文件头 import 之后)——全局 `let` 必须先定义后使用。
|
|
496
|
+
*/
|
|
497
|
+
|
|
498
|
+
/* 文字色随启用态变(禁用项按桌面惯例置灰,但不画、也不响应点击的是分隔线) */
|
|
499
|
+
func menuBarTextColor(item: ArkWebMenuItem): Int64 {
|
|
500
|
+
if (item.enabled) {
|
|
501
|
+
return MENU_BAR_TEXT
|
|
502
|
+
}
|
|
503
|
+
return MENU_BAR_TEXT_DISABLED
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/* 悬停格的底色:仓颉**没有** `cond ? a : b` 三元运算符(`?` 只用在可选类型上),
|
|
507
|
+
拿它写会报 `expected '?' after '?'` 一串看不懂的语法错——一律用 if 表达式。 */
|
|
508
|
+
func menuBarCellBg(hovered: Bool): Int64 {
|
|
509
|
+
if (hovered) {
|
|
510
|
+
return MENU_BAR_HOVER
|
|
511
|
+
}
|
|
512
|
+
return MENU_BAR_BG
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
@Entry
|
|
516
|
+
@Component
|
|
517
|
+
class EntryView {
|
|
518
|
+
func build() {
|
|
519
|
+
Column {
|
|
520
|
+
// 高度由**外层容器**给:自定义组件的调用点不能挂链式修饰(`.height(...)`),
|
|
521
|
+
// 那样宏展开时补不上 `__gen_parent`,报的是「missing arguments for parameter list」。
|
|
522
|
+
Column() {
|
|
523
|
+
CjTauriWebView(windowLabel: DEFAULT_WINDOW_LABEL)
|
|
524
|
+
}.width(100.percent).height(100.percent)
|
|
525
|
+
}.width(100.percent).height(100.percent)
|
|
526
|
+
}
|
|
527
|
+
}
|