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