Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohoswindowproxydatafactory.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <render/qohoswindowproxydatafactory.h>
5
6#include <QtCore/qscopeguard.h>
7#include <cstdint>
8#include <qarkui/qxcomponentregistry.h>
9#include <qohosutils.h>
10#include <render/qohoswindowproxy.h>
11#include <render/qxcomponent.h>
12
13QT_BEGIN_NAMESPACE
14
15namespace {
16
18{
19 static std::uint64_t uniqueIdSuffixCounter = 0;
20 auto result = QStringLiteral("QtWindow_%1_%2")
21 .arg(internalWindowId.toString())
22 .arg(QString::number(uniqueIdSuffixCounter));
23 ++uniqueIdSuffixCounter;
24 return result.toStdString();
25}
26
33
35{
38 bool isModal;
39};
40
50
59
61 QtOhos::JsState &jsState, const std::string &qAbilityInstanceId)
62{
63 auto qAbilityPeer = jsState.tryGetQAbilityPeerByInstanceId(qAbilityInstanceId);
64 if (!qAbilityPeer) {
65 qOhosReportFatalErrorAndAbort(
66 "Failed to find QAbilityPeer for qAbilityInstanceId: %s", qAbilityInstanceId.c_str());
67 }
68 return qAbilityPeer;
69}
70
72{
74 auto nativeNodeXComponentOpt = registry.tryTakeNodeByXComponentId(xComponentId);
75 if (!nativeNodeXComponentOpt.hasValue()) {
76 qOhosReportFatalErrorAndAbort(
77 "Failed to fetch native node xcomponent with id: %s",
78 xComponentId.stringId().c_str());
79 }
80 return nativeNodeXComponentOpt.value();
81}
82
84{
85 return jsState.eval<QNapi::Object>("LocalStorage.makeNewLocalStorage()");
86}
87
89 QNapi::Object windowStageOrWindowObject,
90 const std::string &windowName, const SubWindowOptions &subWindowOptions)
91{
92 auto subWindowOptionsObject =
93 QNapi::makeObject(
94 windowStageOrWindowObject.Env(),
95 {
96 {"title", subWindowOptions.windowTitle},
97 {"decorEnabled", subWindowOptions.decorEnabled},
98 {"isModal", subWindowOptions.isModal},
99 });
100
101 return windowStageOrWindowObject.call<QNapi::Promise>(
102 "createSubWindowWithOptions",
103 {windowName, subWindowOptionsObject});
104}
105
106std::function<void(const QtOhos::CallbackInfo &)>
108{
109 auto sharedContext = QtOhos::moveToSharedPtr(std::move(subWindowOnAppearCbCtx));
110 return [sharedContext](const QtOhos::CallbackInfo &cbInfo) mutable {
111 if (!sharedContext)
112 return;
113
114 auto xComponent = takeNodeXComponentFromRegistryOrFail(sharedContext->xComponentId);
115
116 sharedContext->resultConsumer(
117 cbInfo.jsState(),
118 QOhosWindowProxyData {
119 .qAbilityPeer = sharedContext->qAbilityPeer,
120 .jsWindow = std::move(sharedContext->windowObject),
121 .windowProxyType = sharedContext->windowProxyType,
122 .nodeXComponent = std::make_shared<QXComponentNode>(xComponent),
123 .jsKeepAliveData = QtOhos::moveToSharedPtr(std::move(sharedContext->localStorageObj)),
124 });
125
126 sharedContext.reset();
127 };
128}
129
130QNapi::Promise loadWindowContents(QNapi::Object window, QNapi::Object localStorage, const std::string &contentPagePath)
131{
132 return window.call<QNapi::Promise>("loadContent", {contentPagePath, localStorage});
133}
134
136 QtOhos::JsState &jsState, LocalStorageForWindowCreateInfo &&createInfo)
137{
138 auto *env = jsState.env();
139
140 auto localStorage = makeLocalStorage(jsState);
141 auto subWindowNativeNodeCreateInfo = QNapi::makeObject(
142 env,
143 {
144 {"xComponentId", createInfo.xComponentId.toNapiValue(env)},
145 {
146 "onDisAppear",
147 [xComponentId = createInfo.xComponentId.stringId()]() {
148 qOhosPrintfDebug(
149 "WindowNativeNodeCreateInfo.onDisAppear() called from JS for xComponentId='%s'",
150 xComponentId.c_str());
151 }
152 },
153 {
154 "onAppear",
155 makeSubWindowOnAppearCallbackHandler(SubWindowOnAppearContext {
156 .xComponentId = createInfo.xComponentId,
157 .localStorageObj = Napi::Persistent(localStorage),
158 .windowObject = Napi::Persistent(createInfo.windowObject),
159 .resultConsumer = std::move(createInfo.resultConsumer),
160 .qAbilityPeer = std::move(createInfo.qAbilityPeer),
161 .windowProxyType = createInfo.windowProxyType,
162 }),
163 }
164 });
165
166 localStorage.call("setOrCreate", {"createInfo", subWindowNativeNodeCreateInfo});
167 return localStorage;
168}
169
171 QtOhos::JsState &, const QNapi::Object &windowObject,
173{
174 struct LoadWindowContentsArgs
175 {
176 QNapi::Reference<QNapi::Object> window;
177 QNapi::Reference<QNapi::Object> localStorage;
178 std::string contentPagePath;
179 };
180
181 return !context.disableWindowFocusableBeforeLoadContentHack
182 ? loadWindowContents(windowObject, context.localStorage, context.contentPagePath)
183 : windowObject.call<QNapi::Promise>("setWindowFocusable", {false})
184 .withContext(LoadWindowContentsArgs {
185 .window = Napi::Persistent(windowObject),
186 .localStorage = Napi::Persistent(context.localStorage),
187 .contentPagePath = context.contentPagePath,
188 })
189 .onThenWithContext([](LoadWindowContentsArgs &windowLocalStoragePair) {
190 return loadWindowContents(
191 windowLocalStoragePair.window.Value(),
192 windowLocalStoragePair.localStorage.Value(),
193 windowLocalStoragePair.contentPagePath);
194 });
195}
196
197}
198
200 QtOhos::JsState &jsState,
201 const QOhosWindowProxyMainWindowCreateInfo &createInfo,
202 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
203{
204 auto abilityStartupOptions =
205 QNapi::makeObject(
206 jsState.env(),
207 {
208 {"windowLeft", createInfo.frameGeometry.x()},
209 {"windowTop", createInfo.frameGeometry.y()},
210 {"windowWidth", createInfo.frameGeometry.width()},
211 {"windowHeight", createInfo.frameGeometry.height()},
212 });
213
214 if (createInfo.fullscreen) {
215 auto fullscreenWindowMode = jsState.eval<QNapi::Number>(
216 "@ohos.app.ability.AbilityConstant.WindowMode.WINDOW_MODE_FULLSCREEN");
217 abilityStartupOptions.set("windowMode", fullscreenWindowMode);
218 }
219
220 if (createInfo.displayId.hasValue())
221 abilityStartupOptions.set("displayId", createInfo.displayId.value().value());
222
223 jsState.startNewQAbilityInstance(
224 jsState.defaultQAbilityPeer(), createInfo.qWindowRef,
225 abilityStartupOptions,
226 [qWindowRef = createInfo.qWindowRef,
227 windowId = createInfo.windowId,
228 resultConsumer = std::move(resultConsumer)](QtOhos::JsState &jsState, std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer) {
229 auto createInfo = QOhosWindowProxyExistingMainWindowCreateInfo {
230 .qWindowRef = qWindowRef,
231 .qAbilityInstanceId = qAbilityPeer->instanceId(),
232 .windowId = windowId,
233 };
234
235 makeWindowProxyDataForExistingMainWindowInJsThread(
236 jsState, createInfo, std::move(resultConsumer));
237 });
238}
239
241 QtOhos::JsState &jsState,
243 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
244{
246 getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId));
247 if (!optQUiAbilityPeer) {
248 qOhosReportFatalErrorAndAbort(
249 "%s Attempting to make window proxy for main window for ability without windowStage. This is most likely a programming error. Aborting...",
250 Q_FUNC_INFO);
251 }
252 optQUiAbilityPeer->setQWindow(jsState.env(), createInfo.qWindowRef);
253
254 auto window = optQUiAbilityPeer->windowStage().call<QNapi::Object>("getMainWindowSync");
255 auto nativeNodeXComponentId = QXComponentId::createForNativeNodeMainWindow(optQUiAbilityPeer->instanceId());
256 auto nodeXComponent = takeNodeXComponentFromRegistryOrFail(nativeNodeXComponentId);
257
258 resultConsumer(
259 jsState,
260 QOhosWindowProxyData {
261 .qAbilityPeer = optQUiAbilityPeer,
262 .jsWindow = Napi::Persistent(window),
263 .windowProxyType = WindowProxyType::MainWindow,
264 .nodeXComponent = std::make_shared<QXComponentNode>(nodeXComponent),
265 .jsKeepAliveData = nullptr,
266 });
267}
268
270 QtOhos::JsState &jsState,
271 const QOhosWindowProxySubWindowCreateInfo &createInfo,
272 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
273{
274 auto qAbilityPeer = getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId);
275 auto optQUiAbilityPeer = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(qAbilityPeer);
276 if (!optQUiAbilityPeer) {
277 qOhosReportFatalErrorAndAbort(
278 "%s Attempting to make window proxy for sub window for ability without windowStage. This is most likely a programming error. Aborting...",
279 Q_FUNC_INFO);
280 }
281 makeWindowProxyDataForSubWindowInJsThread(
282 jsState, optQUiAbilityPeer->windowStage(), createInfo, std::move(resultConsumer));
283}
284
286 QtOhos::JsState &jsState,
287 QNapi::Object windowStageOrWindowObject,
288 const QOhosWindowProxySubWindowCreateInfo &createInfo,
289 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
290{
291 auto xComponentId = QXComponentId::createForNativeNodeSubWindow(createInfo.windowId);
292 struct Context
293 {
294 bool disableWindowFocusableBeforeLoadContentHack;
295 QXComponentId xComponentId;
296 std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer;
297 };
298
299 auto qAbilityPeer = getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId);
300
301 createSubWindowWithOptions(
302 windowStageOrWindowObject,
303 makeOhosUniqueSystemWindowName(createInfo.windowId),
305 .windowTitle = createInfo.windowTitle,
306 .decorEnabled = createInfo.decorEnabled,
307 .isModal = createInfo.modal,
308 })
309 .withContext(Context {
310 .disableWindowFocusableBeforeLoadContentHack = createInfo.disableWindowFocusableBeforeLoadContentHack,
311 .xComponentId = xComponentId,
312 .qAbilityPeer = qAbilityPeer,
313 })
314 .onThenWithContext([resultConsumer = std::move(resultConsumer)](const QtOhos::CallbackInfo &cbInfo, Context &context) mutable {
315 auto windowObject = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
316
317 auto localStorage = makeLocalStorageForWindow(
318 cbInfo.jsState(),
319 LocalStorageForWindowCreateInfo {
320 .xComponentId = context.xComponentId,
321 .windowObject = windowObject,
322 .resultConsumer = std::move(resultConsumer),
323 .qAbilityPeer = context.qAbilityPeer,
324 .windowProxyType = WindowProxyType::SubWindow,
325 });
326
327 return onWindowCreatedLoadWindowContents(
328 cbInfo.jsState(), windowObject,
329 OnWindowCreatedLoadWindowContentsContext {
330 .disableWindowFocusableBeforeLoadContentHack = context.disableWindowFocusableBeforeLoadContentHack,
331 .contentPagePath = "pages/SubWindowNativeNode",
332 .localStorage = localStorage,
333 });
334 })
335 .onCatch([windowId = createInfo.windowId](const QtOhos::CallbackInfo &cbInfo) {
336 QtOhos::logJsCallbackError(cbInfo, "createSubWindowWithOptions() failed");
337 qOhosReportFatalErrorAndAbort(
338 "Failed to create subwindow for windowId='%s'",
339 windowId.toStdString().c_str());
340 });
341}
342
344 QtOhos::JsState &jsState, const QOhosWindowProxyFloatWindowCreateInfo &createInfo,
345 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
346{
347 auto xComponentId = QXComponentId::createForNativeNodeFloatWindow(createInfo.internalWindowId);
348 auto qAbilityPeer = jsState.defaultQAbilityPeer();
349
350 auto configurationObject = QNapi::makeObject(
351 jsState.env(),
352 {
353 // NOTE - The parameter name is misleading, what it refers to actually is (system) window id
354 {"name", makeOhosUniqueSystemWindowName(createInfo.internalWindowId)},
355 {"windowType", jsState.eval<QNapi::Number>("@ohos.window.WindowType.TYPE_FLOAT")},
356 {"ctx", qAbilityPeer->qAbility().get<QNapi::Object>("context")},
357 });
358
359 if (createInfo.displayId.hasValue())
360 configurationObject.set("displayId", createInfo.displayId.value().value());
361
362 struct Context
363 {
364 QXComponentId xComponentId;
365 std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer;
366 };
367
368 jsState
369 .eval<QNapi::Promise>("@ohos.window.createWindow(*)", {configurationObject})
370 .withContext(Context {
371 .xComponentId = xComponentId,
372 .qAbilityPeer = qAbilityPeer,
373 })
374 .onThenWithContext([resultConsumer = std::move(resultConsumer)](const QtOhos::CallbackInfo &cbInfo, Context &context) mutable {
375 auto windowObject = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
376
377 auto localStorage = makeLocalStorageForWindow(
378 cbInfo.jsState(),
379 LocalStorageForWindowCreateInfo {
380 .xComponentId = context.xComponentId,
381 .windowObject = windowObject,
382 .resultConsumer = std::move(resultConsumer),
383 .qAbilityPeer = context.qAbilityPeer,
384 .windowProxyType = WindowProxyType::FloatWindow,
385 });
386
387 return onWindowCreatedLoadWindowContents(
388 cbInfo.jsState(), windowObject,
389 OnWindowCreatedLoadWindowContentsContext {
390 .disableWindowFocusableBeforeLoadContentHack = false,
391 .contentPagePath = "pages/FloatWindowNativeNode",
392 .localStorage = localStorage,
393 });
394 })
395 .onCatch([internalWindowId = createInfo.internalWindowId](const QtOhos::CallbackInfo &cbInfo) {
396 QtOhos::logJsCallbackError(cbInfo, "Failed to create TYPE_FLOAT window");
397 qOhosReportFatalErrorAndAbort(
398 "Failed to create TYPE_FLOAT window for windowId='%s'",
399 internalWindowId.toStdString().c_str());
400 });
401}
402
403
404QT_END_NAMESPACE
static QXComponentRegistry & instance()
QOhosOptional< QXComponentNode > tryTakeNodeByXComponentId(const QXComponentId &id)
static QXComponentId createForNativeNodeMainWindow(const std::string &qAbilityInstanceId)
std::string stringId() const
JsState & jsState() const
virtual std::shared_ptr< QAbilityPeer > tryGetQAbilityPeerByInstanceId(const std::string &instanceId)=0
virtual std::shared_ptr< QAbilityPeer > defaultQAbilityPeer()=0
static std::shared_ptr< QUiAbilityPeer > tryCastFromQAbilityPeerOrNull(std::shared_ptr< QAbilityPeer > qAbilityPeer)
std::shared_ptr< QtOhos::QAbilityPeer > getQAbilityPeerByInstanceIdOrFail(QtOhos::JsState &jsState, const std::string &qAbilityInstanceId)
QNapi::Promise createSubWindowWithOptions(QNapi::Object windowStageOrWindowObject, const std::string &windowName, const SubWindowOptions &subWindowOptions)
QXComponentNode takeNodeXComponentFromRegistryOrFail(const QXComponentId &xComponentId)
std::function< void(const QtOhos::CallbackInfo &)> makeSubWindowOnAppearCallbackHandler(SubWindowOnAppearContext subWindowOnAppearCbCtx)
QNapi::Object makeLocalStorage(QtOhos::JsState &jsState)
QNapi::Promise onWindowCreatedLoadWindowContents(QtOhos::JsState &, const QNapi::Object &windowObject, OnWindowCreatedLoadWindowContentsContext context)
QNapi::Promise loadWindowContents(QNapi::Object window, QNapi::Object localStorage, const std::string &contentPagePath)
std::string makeOhosUniqueSystemWindowName(QtOhos::InternalWindowId internalWindowId)
QNapi::Object makeLocalStorageForWindow(QtOhos::JsState &jsState, LocalStorageForWindowCreateInfo &&createInfo)
void makeWindowProxyDataForFloatWindowInJsThread(QtOhos::JsState &jsState, const QOhosWindowProxyFloatWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
void makeWindowProxyDataForSubWindowInJsThread(QtOhos::JsState &jsState, QNapi::Object windowStageOrWindowObject, const QOhosWindowProxySubWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
void makeWindowProxyDataForSubWindowInJsThread(QtOhos::JsState &jsState, const QOhosWindowProxySubWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
void makeWindowProxyDataForExistingMainWindowInJsThread(QtOhos::JsState &jsState, const QOhosWindowProxyExistingMainWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
void makeWindowProxyDataForMainWindowInJsThread(QtOhos::JsState &jsState, const QOhosWindowProxyMainWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
QXComponent< QXComponentType::Node > QXComponentNode
Definition qxcomponent.h:44
QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer
QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer
std::shared_ptr< QtOhos::QAbilityPeer > qAbilityPeer