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 <qohosdeviceinfo_p.h>
10#include <qohosutils.h>
11#include <render/qohoswindowproxy.h>
12#include <render/qxcomponent.h>
13
14QT_BEGIN_NAMESPACE
15
16namespace {
17
19{
20 static std::uint64_t uniqueIdSuffixCounter = 0;
21 auto result = QStringLiteral("QtWindow_%1_%2")
22 .arg(internalWindowId.toString())
23 .arg(QString::number(uniqueIdSuffixCounter));
24 ++uniqueIdSuffixCounter;
25 return result.toStdString();
26}
27
34
42
52
61
63 QtOhos::JsState &jsState, const std::string &qAbilityInstanceId)
64{
65 auto qAbilityPeer = jsState.tryGetQAbilityPeerByInstanceId(qAbilityInstanceId);
66 if (!qAbilityPeer) {
67 qOhosReportFatalErrorAndAbort(
68 "Failed to find QAbilityPeer for qAbilityInstanceId: %s", qAbilityInstanceId.c_str());
69 }
70 return qAbilityPeer;
71}
72
74{
76 auto nativeNodeXComponentOpt = registry.tryTakeNodeByXComponentId(xComponentId);
77 if (!nativeNodeXComponentOpt.has_value()) {
78 qOhosReportFatalErrorAndAbort(
79 "Failed to fetch native node xcomponent with id: %s",
80 xComponentId.stringId().c_str());
81 }
82 return nativeNodeXComponentOpt.value();
83}
84
86{
87 return jsState.eval<QNapi::Object>("LocalStorage.makeNewLocalStorage()");
88}
89
90QNapi::Object makeRectObject(Napi::Env env, const QRect &rect)
91{
92 return QNapi::makeObject(
93 env,
94 {
95 {"left", rect.x()},
96 {"top", rect.top()},
97 {"width", rect.width()},
98 {"height", rect.height()},
99 });
100}
101
103 QNapi::Object windowStageOrWindowObject,
104 const std::string &windowName, const SubWindowOptions &subWindowOptions)
105{
106 auto subWindowOptionsObject =
107 QNapi::makeObject(
108 windowStageOrWindowObject.Env(),
109 {
110 {"title", subWindowOptions.windowTitle},
111 {"decorEnabled", subWindowOptions.decorEnabled},
112 {"isModal", subWindowOptions.isModal},
113 {
114 "windowRect", makeRectObject(windowStageOrWindowObject.Env(), subWindowOptions.windowRect)
115 }
116 });
117
118 return windowStageOrWindowObject.evalToPromiseOrRejectOnThrow(
119 "createSubWindowWithOptions(*)",
120 {windowName, subWindowOptionsObject});
121}
122
123// Optionless sub window creation, used on phones where the SessionManager-only
124// createSubWindowWithOptions() is unavailable. WindowStage.createSubWindow() is @crossplatform.
126 QNapi::Object windowStageOrWindowObject, const std::string &windowName)
127{
128 return windowStageOrWindowObject.evalToPromiseOrRejectOnThrow(
129 "createSubWindow(*)", {windowName});
130}
131
132std::function<void(const QtOhos::CallbackInfo &)>
134{
135 auto sharedContext = QtOhos::moveToSharedPtr(std::move(subWindowOnAppearCbCtx));
136 return [sharedContext](const QtOhos::CallbackInfo &cbInfo) mutable {
137 if (!sharedContext)
138 return;
139
140 auto xComponent = takeNodeXComponentFromRegistryOrFail(sharedContext->xComponentId);
141
142 sharedContext->resultConsumer(
143 cbInfo.jsState(),
144 QOhosWindowProxyData {
145 .qAbilityPeer = sharedContext->qAbilityPeer,
146 .jsWindow = std::move(sharedContext->windowObject),
147 .windowProxyType = sharedContext->windowProxyType,
148 .nodeXComponent = std::make_shared<QXComponentNode>(xComponent),
149 .jsKeepAliveData = QtOhos::moveToSharedPtr(std::move(sharedContext->localStorageObj)),
150 });
151
152 sharedContext.reset();
153 };
154}
155
156QNapi::Promise loadWindowContents(QNapi::Object window, QNapi::Object localStorage, const std::string &contentPagePath)
157{
158 return window.evalToPromiseOrRejectOnThrow("loadContent(*)", {contentPagePath, localStorage});
159}
160
162 QtOhos::JsState &jsState, LocalStorageForWindowCreateInfo &&createInfo)
163{
164 auto *env = jsState.env();
165
166 auto localStorage = makeLocalStorage(jsState);
167 auto subWindowNativeNodeCreateInfo = QNapi::makeObject(
168 env,
169 {
170 {"xComponentId", createInfo.xComponentId.toNapiValue(env)},
171 {
172 "onDisAppear",
173 [xComponentId = createInfo.xComponentId.stringId()]() {
174 qOhosPrintfDebug(
175 "WindowNativeNodeCreateInfo.onDisAppear() called from JS for xComponentId='%s'",
176 xComponentId.c_str());
177 }
178 },
179 {
180 "onAppear",
181 makeSubWindowOnAppearCallbackHandler(SubWindowOnAppearContext {
182 .xComponentId = createInfo.xComponentId,
183 .localStorageObj = Napi::Persistent(localStorage),
184 .windowObject = Napi::Persistent(createInfo.windowObject),
185 .resultConsumer = std::move(createInfo.resultConsumer),
186 .qAbilityPeer = std::move(createInfo.qAbilityPeer),
187 .windowProxyType = createInfo.windowProxyType,
188 }),
189 }
190 });
191
192 localStorage.call("setOrCreate", {"createInfo", subWindowNativeNodeCreateInfo});
193 return localStorage;
194}
195
197 QtOhos::JsState &, const QNapi::Object &windowObject,
199{
200 struct LoadWindowContentsArgs
201 {
202 QNapi::Reference<QNapi::Object> window;
203 QNapi::Reference<QNapi::Object> localStorage;
204 std::string contentPagePath;
205 };
206
207 return !context.disableWindowFocusableBeforeLoadContentHack
208 ? loadWindowContents(windowObject, context.localStorage, context.contentPagePath)
209 : windowObject.evalToPromiseOrRejectOnThrow("setWindowFocusable(*)", {false})
210 .withContext(LoadWindowContentsArgs {
211 .window = Napi::Persistent(windowObject),
212 .localStorage = Napi::Persistent(context.localStorage),
213 .contentPagePath = context.contentPagePath,
214 })
215 .onThenWithContext([](LoadWindowContentsArgs &windowLocalStoragePair) {
216 return loadWindowContents(
217 windowLocalStoragePair.window.Value(),
218 windowLocalStoragePair.localStorage.Value(),
219 windowLocalStoragePair.contentPagePath);
220 });
221}
222
223}
224
226 QtOhos::JsState &jsState,
227 const QOhosWindowProxyMainWindowCreateInfo &createInfo,
228 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
229{
230 auto abilityStartupOptions =
231 QNapi::makeObject(
232 jsState.env(),
233 {
234 {"windowLeft", createInfo.frameGeometry.x()},
235 {"windowTop", createInfo.frameGeometry.y()},
236 {"windowWidth", createInfo.frameGeometry.width()},
237 {"windowHeight", createInfo.frameGeometry.height()},
238 });
239
240 if (createInfo.fullscreen) {
241 auto fullscreenWindowMode = jsState.eval<QNapi::Number>(
242 "@ohos.app.ability.AbilityConstant.WindowMode.WINDOW_MODE_FULLSCREEN");
243 abilityStartupOptions.set("windowMode", fullscreenWindowMode);
244 }
245
246 jsState.startNewQAbilityInstance(
247 jsState.defaultQAbilityPeer(), createInfo.qWindowRef,
248 abilityStartupOptions,
249 [qWindowRef = createInfo.qWindowRef,
250 windowId = createInfo.windowId,
251 resultConsumer = std::move(resultConsumer)](QtOhos::JsState &jsState, std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer) {
252 auto createInfo = QOhosWindowProxyExistingMainWindowCreateInfo {
253 .qWindowRef = qWindowRef,
254 .qAbilityInstanceId = qAbilityPeer->instanceId(),
255 .windowId = windowId,
256 };
257
258 makeWindowProxyDataForExistingMainWindowInJsThread(
259 jsState, createInfo, std::move(resultConsumer));
260 });
261}
262
264 QtOhos::JsState &jsState,
266 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
267{
269 getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId));
270 if (!optQUiAbilityPeer) {
271 qOhosReportFatalErrorAndAbort(
272 "%s Attempting to make window proxy for main window for ability without windowStage. This is most likely a programming error. Aborting...",
273 Q_FUNC_INFO);
274 }
275 optQUiAbilityPeer->setQWindow(jsState.env(), createInfo.qWindowRef);
276
277 auto window = optQUiAbilityPeer->windowStage().call<QNapi::Object>("getMainWindowSync");
278 auto nativeNodeXComponentId = QXComponentId::createForNativeNodeMainWindow(optQUiAbilityPeer->instanceId());
279 auto nodeXComponent = takeNodeXComponentFromRegistryOrFail(nativeNodeXComponentId);
280
281 resultConsumer(
282 jsState,
283 QOhosWindowProxyData {
284 .qAbilityPeer = optQUiAbilityPeer,
285 .jsWindow = Napi::Persistent(window),
286 .windowProxyType = WindowProxyType::MainWindow,
287 .nodeXComponent = std::make_shared<QXComponentNode>(nodeXComponent),
288 .jsKeepAliveData = nullptr,
289 });
290}
291
293 QtOhos::JsState &jsState,
294 const QOhosWindowProxySubWindowCreateInfo &createInfo,
295 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
296{
297 auto qAbilityPeer = getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId);
298 auto optQUiAbilityPeer = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(qAbilityPeer);
299 if (!optQUiAbilityPeer) {
300 qOhosReportFatalErrorAndAbort(
301 "%s Attempting to make window proxy for sub window for ability without windowStage. This is most likely a programming error. Aborting...",
302 Q_FUNC_INFO);
303 }
304 makeWindowProxyDataForSubWindowInJsThread(
305 jsState, optQUiAbilityPeer->windowStage(), createInfo, std::move(resultConsumer));
306}
307
309 QtOhos::JsState &jsState,
310 QNapi::Object windowStageOrWindowObject,
311 const QOhosWindowProxySubWindowCreateInfo &createInfo,
312 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
313{
314 auto xComponentId = QXComponentId::createForNativeNodeSubWindow(createInfo.windowId);
315 struct Context
316 {
317 bool disableWindowFocusableBeforeLoadContentHack;
318 QXComponentId xComponentId;
319 std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer;
320 };
321
322 auto qAbilityPeer = getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId);
323
324 const auto windowName = makeOhosUniqueSystemWindowName(createInfo.windowId);
325
326 // createSubWindowWithOptions() needs SystemCapability.Window.SessionManager, which is absent
327 // on phones (it is present on 2-in-1 and tablet). On phones fall back to the optionless
328 // createSubWindow(); its dropped options (title/decor/modality/rect) are applied separately
329 // after creation anyway.
330 auto subWindowCreationPromise = [&]() -> QNapi::Promise {
331 if (!QOhosDeviceInfo::isPhone()) {
332 return createSubWindowWithOptions(
333 windowStageOrWindowObject,
334 windowName,
335 SubWindowOptions {
336 .windowTitle = createInfo.windowTitle,
337 .decorEnabled = createInfo.decorEnabled,
338 .isModal = createInfo.modal,
339 .windowRect = createInfo.windowRect,
340 });
341 }
342
343 // createSubWindow() exists only on the WindowStage, not on a Window.
344 auto optQUiAbilityPeer = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(qAbilityPeer);
345 if (!optQUiAbilityPeer) {
346 qOhosReportFatalErrorAndAbort(
347 "%s sub window creation requires an ability with a windowStage. Aborting...",
348 Q_FUNC_INFO);
349 }
350 return createSubWindow(optQUiAbilityPeer->windowStage(), windowName);
351 }();
352
353 subWindowCreationPromise
354 .withContext(Context {
355 .disableWindowFocusableBeforeLoadContentHack = createInfo.disableWindowFocusableBeforeLoadContentHack,
356 .xComponentId = xComponentId,
357 .qAbilityPeer = qAbilityPeer,
358 })
359 .onThenWithContext([resultConsumer = std::move(resultConsumer)](const QtOhos::CallbackInfo &cbInfo, Context &context) mutable {
360 auto windowObject = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
361
362 auto localStorage = makeLocalStorageForWindow(
363 cbInfo.jsState(),
364 LocalStorageForWindowCreateInfo {
365 .xComponentId = context.xComponentId,
366 .windowObject = windowObject,
367 .resultConsumer = std::move(resultConsumer),
368 .qAbilityPeer = context.qAbilityPeer,
369 .windowProxyType = WindowProxyType::SubWindow,
370 });
371
372 return onWindowCreatedLoadWindowContents(
373 cbInfo.jsState(), windowObject,
374 OnWindowCreatedLoadWindowContentsContext {
375 .disableWindowFocusableBeforeLoadContentHack = context.disableWindowFocusableBeforeLoadContentHack,
376 .contentPagePath = "pages/SubWindowNativeNode",
377 .localStorage = localStorage,
378 });
379 })
380 .onCatch([windowId = createInfo.windowId](const QtOhos::CallbackInfo &cbInfo) {
381 QtOhos::logJsCallbackError(cbInfo, "sub window creation failed");
382 qOhosReportFatalErrorAndAbort(
383 "Failed to create subwindow for windowId='%s'",
384 windowId.toStdString().c_str());
385 });
386}
387
389 QtOhos::JsState &jsState, const QOhosWindowProxyFloatWindowCreateInfo &createInfo,
390 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
391{
392 auto xComponentId = QXComponentId::createForNativeNodeFloatWindow(createInfo.internalWindowId);
393 auto qAbilityPeer = jsState.defaultQAbilityPeer();
394
395 auto configurationObject = QNapi::makeObject(
396 jsState.env(),
397 {
398 // NOTE - The parameter name is misleading, what it refers to actually is (system) window id
399 {"name", makeOhosUniqueSystemWindowName(createInfo.internalWindowId)},
400 {"windowType", jsState.eval<QNapi::Number>("@ohos.window.WindowType.TYPE_FLOAT")},
401 {"ctx", qAbilityPeer->qAbility().get<QNapi::Object>("context")},
402 });
403
404 struct Context
405 {
406 QXComponentId xComponentId;
407 std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer;
408 };
409
410 jsState
411 .evalToPromiseOrRejectOnThrow("@ohos.window.createWindow(*)", {configurationObject})
412 .withContext(Context {
413 .xComponentId = xComponentId,
414 .qAbilityPeer = qAbilityPeer,
415 })
416 .onThenWithContext([resultConsumer = std::move(resultConsumer)](const QtOhos::CallbackInfo &cbInfo, Context &context) mutable {
417 auto windowObject = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
418
419 auto localStorage = makeLocalStorageForWindow(
420 cbInfo.jsState(),
421 LocalStorageForWindowCreateInfo {
422 .xComponentId = context.xComponentId,
423 .windowObject = windowObject,
424 .resultConsumer = std::move(resultConsumer),
425 .qAbilityPeer = context.qAbilityPeer,
426 .windowProxyType = WindowProxyType::FloatWindow,
427 });
428
429 return onWindowCreatedLoadWindowContents(
430 cbInfo.jsState(), windowObject,
431 OnWindowCreatedLoadWindowContentsContext {
432 .disableWindowFocusableBeforeLoadContentHack = false,
433 .contentPagePath = "pages/FloatWindowNativeNode",
434 .localStorage = localStorage,
435 });
436 })
437 .onCatch([internalWindowId = createInfo.internalWindowId](const QtOhos::CallbackInfo &cbInfo) {
438 QtOhos::logJsCallbackError(cbInfo, "Failed to create TYPE_FLOAT window");
439 qOhosReportFatalErrorAndAbort(
440 "Failed to create TYPE_FLOAT window for windowId='%s'",
441 internalWindowId.toStdString().c_str());
442 });
443}
444
445
446QT_END_NAMESPACE
static QXComponentRegistry & instance()
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::Object makeRectObject(Napi::Env env, const QRect &rect)
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::Promise createSubWindow(QNapi::Object windowStageOrWindowObject, const std::string &windowName)
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