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
qohoswindowproxy.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/qohoswindowproxy.h>
5
6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qset.h>
8#include <QtCore/private/qnapi_p.h>
9#include <QtCore/qscopeguard.h>
10#include <QtGui/private/qohosimageconversions_p.h>
11#include <QtGui/qguiapplication.h>
12#include <QtGui/qimage.h>
13#include <algorithm>
14#include <functional>
15#include <memory>
16#include <multimedia/image_framework/image/pixelmap_native.h>
17#include <qarkui/input.h>
18#include <qarkui/qarkuiutils.h>
19#include <qarkui/qxcomponentregistry.h>
20#include <qarkui/window.h>
21#include <qarkui/window_manager.h>
22#include <qohosdeviceinfo_p.h>
23#include <qohosdisplayinfo.h>
24#include <qohosjsutils.h>
25#include <qohospixelmapconversions.h>
26#include <qohosplugincore.h>
27#include <qohosqpaenums.h>
28#include <qohossettings.h>
29#include <qohosutils.h>
30#include <render/qohosbatchingrequestshandler.h>
31#include <render/qohosjswindowregistry.h>
32#include <render/qohoswindowproxydatafactory.h>
33#include <render/qxcomponent.h>
34#include <type_traits>
35
37
38namespace
39{
40
42
43QRect ohosWindowRectToQRect(const QNapi::Object &ohosWindowRect)
44{
45 return QRect {
46 ohosWindowRect.get<QNapi::Number>("left"),
47 ohosWindowRect.get<QNapi::Number>("top"),
48 ohosWindowRect.get<QNapi::Number>("width"),
49 ohosWindowRect.get<QNapi::Number>("height"),
50 };
51}
52
53QOhosWindowProxy::AvoidArea mapAvoidAreaFromJs(const QNapi::Object &avoidAreaObject)
54{
55 return {
56 .visible = avoidAreaObject.get<QNapi::Boolean>("visible"),
57 .leftRect = ohosWindowRectToQRect(avoidAreaObject.get<QNapi::Object>("leftRect")),
58 .topRect = ohosWindowRectToQRect(avoidAreaObject.get<QNapi::Object>("topRect")),
59 .rightRect = ohosWindowRectToQRect(avoidAreaObject.get<QNapi::Object>("rightRect")),
60 .bottomRect = ohosWindowRectToQRect(avoidAreaObject.get<QNapi::Object>("bottomRect")),
61 };
62}
63
65{
66 switch (shape) {
67 case Qt::ArrowCursor:
69 case Qt::UpArrowCursor:
71 case Qt::CrossCursor:
73 case Qt::WaitCursor:
75 case Qt::IBeamCursor:
77 case Qt::SizeVerCursor:
79 case Qt::SizeHorCursor:
81 case Qt::SizeBDiagCursor:
83 case Qt::SizeFDiagCursor:
85 case Qt::SizeAllCursor:
87 case Qt::BlankCursor:
88 // TODO: there is no dedicated Ohos 'Qt::BlankCursor'. Return default.
90 case Qt::SplitVCursor:
92 case Qt::SplitHCursor:
94 case Qt::PointingHandCursor:
96 case Qt::ForbiddenCursor:
98 case Qt::WhatsThisCursor:
100 case Qt::BusyCursor:
102 case Qt::OpenHandCursor:
104 case Qt::ClosedHandCursor:
106 case Qt::DragCopyCursor:
108 case Qt::DragMoveCursor:
110 case Qt::DragLinkCursor:
111 // TODO: there is no dedicated Ohos 'Qt::DragLinkCursor'. Return default.
113 case Qt::BitmapCursor:
114 // TODO: there is no dedicated Ohos 'Qt::BitmapCursor'. Return default.
116 case Qt::CustomCursor:
117 // TODO: there is no dedicated Ohos 'Qt::CustomCursor'. Return default.
119 }
120
122}
123
124std::optional<double> getOptionalNumberPropAsOptionalDouble(const QNapi::Object &object, const std::string &propertyName)
125{
126 auto propOrEmpty = QNapi::getOptionalPropOrEmpty<QNapi::Number>(object, propertyName);
127 return !propOrEmpty.IsEmpty()
128 ? std::optional<double>(propOrEmpty)
129 : std::nullopt;
130}
131
133{
134 auto windowPropsObj = jsWindow.eval<QNapi::Object>("getWindowProperties()");
135 auto displayIdOrEmpty = QNapi::getOptionalPropOrEmpty<QNapi::Number>(windowPropsObj, "displayId");
136 return QArkUi::WindowProperties {
137 .windowRect = ohosWindowRectToQRect(windowPropsObj.get<QNapi::Object>("windowRect")),
138 .drawableRect = ohosWindowRectToQRect(windowPropsObj.get<QNapi::Object>("drawableRect")),
139 .id = QArkUi::JsWindowId(windowPropsObj.get<QNapi::Number>("id")),
140 .displayId = !displayIdOrEmpty.IsEmpty()
141 ? std::optional(QOhosDisplayInfo::JsDisplayId{displayIdOrEmpty.DoubleValue()})
142 : std::nullopt,
143 };
144}
145
146QNapi::Object toNapiObject(napi_env env, const QOhosWindowProxy::MoveConfiguration &moveConfiguration)
147{
148 auto moveConfigurationObject = QNapi::makeObject(env);
149 if (moveConfiguration.displayId.has_value()) {
150 moveConfigurationObject.set(
151 "displayId", moveConfiguration.displayId.value().value());
152 }
153 return moveConfigurationObject;
154}
155
156template<typename ...Args>
158 std::function<void(Args...)> QOhosWindowProxy::WindowCallbacks::*memberPtr,
159 std::shared_ptr<QOhosWindowProxy::WindowCallbacks> qtWindowCallbacks)
160{
161 auto weakQtWindowCallbacks = QtOhos::makeWeakPtr(qtWindowCallbacks);
162
163 return [memberPtr, weakQtWindowCallbacks](Args ...args) {
164 QtOhos::invokeInQtThread([memberPtr, weakQtWindowCallbacks, args...]() {
165 auto qtWindowCallbacks = weakQtWindowCallbacks.lock();
166 if (qtWindowCallbacks)
167 (*qtWindowCallbacks.*memberPtr)(args...);
168 });
169 };
170}
171
172template<typename T>
174 std::function<void(T)> QOhosWindowProxy::WindowCallbacks::*memberPtr,
175 std::shared_ptr<QOhosWindowProxy::WindowCallbacks> qtWindowCallbacks)
176{
177 auto weakQtWindowCallbacks = QtOhos::makeWeakPtr(qtWindowCallbacks);
178
179 return QtOhos::makeCompressingAsyncConsumer<T>(
180 [memberPtr, weakQtWindowCallbacks](T value) {
181 auto qtWindowCallbacks = weakQtWindowCallbacks.lock();
182 if (qtWindowCallbacks)
183 (*qtWindowCallbacks.*memberPtr)(value);
184 },
185 QtOhos::invokeInQtThread);
186}
187
188bool isPointInNonClientArea(const QPoint &point, const QArkUi::WindowProperties &windowProperties)
189{
190 constexpr bool containsPolicyExcludeEdgeValue = true;
191
192 auto drawableRectInScreenSpace =
193 windowProperties.drawableRect.translated(windowProperties.windowRect.topLeft());
194 return !drawableRectInScreenSpace.contains(point, containsPolicyExcludeEdgeValue);
195}
196
198{
199 switch (action) {
200 case ::MOUSE_ACTION_MOVE:
201 return QEvent::NonClientAreaMouseMove;
202 case ::MOUSE_ACTION_BUTTON_DOWN:
203 return QEvent::NonClientAreaMouseButtonPress;
204 case ::MOUSE_ACTION_BUTTON_UP:
205 return QEvent::NonClientAreaMouseButtonRelease;
206 case ::MOUSE_ACTION_CANCEL:
207 case ::MOUSE_ACTION_AXIS_BEGIN:
208 case ::MOUSE_ACTION_AXIS_UPDATE:
209 case ::MOUSE_ACTION_AXIS_END:
210 break;
211 }
212 return std::nullopt;
213}
214
215std::optional<Qt::MouseButton> tryMapMouseEventButtonToQt(::Input_MouseEventButton button)
216{
217 switch (button) {
218 case ::MOUSE_BUTTON_LEFT:
219 return Qt::LeftButton;
220 case ::MOUSE_BUTTON_MIDDLE:
221 return Qt::MiddleButton;
222 case ::MOUSE_BUTTON_RIGHT:
223 return Qt::RightButton;
224 case ::MOUSE_BUTTON_FORWARD:
225 return Qt::ForwardButton;
226 case ::MOUSE_BUTTON_BACK:
227 return Qt::BackButton;
228 case ::MOUSE_BUTTON_NONE:
229 break;
230 }
231 return std::nullopt;
232}
233
235{
236 switch (action) {
237 case ::TOUCH_ACTION_MOVE:
238 return QEvent::NonClientAreaMouseMove;
239 case ::TOUCH_ACTION_DOWN:
240 return QEvent::NonClientAreaMouseButtonPress;
241 case ::TOUCH_ACTION_UP:
242 return QEvent::NonClientAreaMouseButtonRelease;
243 case ::TOUCH_ACTION_CANCEL:
244 break;
245 }
246 return std::nullopt;
247}
248
249template<typename EnumsContainer>
250std::string mapEnumsToLogString(const EnumsContainer &enums)
251{
252 std::string output;
253 for (auto enumValue : enums) {
254 if (!output.empty())
255 output += ",";
256 output += std::to_string(static_cast<std::underlying_type_t<decltype(enumValue)>>(enumValue));
257 }
258 return output;
259}
260
262 QNapi::Object jsWindow, WindowProxyType windowType, std::shared_ptr<QtOhos::QAbilityPeer> abilityPeer)
263{
264 const bool boundToAbility = windowType != WindowProxyType::FloatWindow;
265 return QtOhos::JsWindowsTracker::isWindowClosing(jsWindow) || (boundToAbility && abilityPeer->isTerminating());
266}
267
268}
269
270const QOhosWindowProxy::EventHandlerDescriptor QOhosWindowProxy::eventHandlerDescriptors[] = {
271 {
272 .eventName = "avoidAreaChange",
273 .eventHandler = &QOhosWindowProxy::JsScopeData::handleAvoidAreaChangeCallback,
274 .eventHandlerFlags = {},
275 },
276 {
277 .eventName = "touchOutside",
278 .eventHandler = &QOhosWindowProxy::JsScopeData::handleWindowTouchOutsideCallback,
279 .eventHandlerFlags = {},
280 },
281 {
282 .eventName = "windowEvent",
283 .eventHandler = &QOhosWindowProxy::JsScopeData::handleWindowEventCallback,
284 .eventHandlerFlags = EventHandlerFlagBits::allowCallWhenAbilityIsTerminating,
285 },
286 {
287 .eventName = "windowRectChange",
288 .eventHandler = &QOhosWindowProxy::JsScopeData::handleWindowRectChangeCallback,
289 .eventHandlerFlags = {},
290 },
291 {
292 .eventName = "rectChangeInGlobalDisplay",
293 .eventHandler = &QOhosWindowProxy::JsScopeData::handleWindowRectChangeInGlobalDisplayCallback,
294 .eventHandlerFlags = {},
295 },
296 {
297 .eventName = "windowStatusChange",
298 .eventHandler = &QOhosWindowProxy::JsScopeData::handleWindowStatusCallback,
299 .eventHandlerFlags = {},
300 },
301 {
302 .eventName = "windowVisibilityChange",
303 .eventHandler = &QOhosWindowProxy::JsScopeData::handleWindowVisibilityCallback,
304 .eventHandlerFlags = {},
305 },
306 {
307 .eventName = "displayIdChange",
308 .eventHandler = &QOhosWindowProxy::JsScopeData::handleWindowDisplayIdChangeCallback,
309 .eventHandlerFlags = EventHandlerFlagBits::allowEventHandlerRegistrationFailure,
310 },
311};
312
313QOhosWindowProxy::QOhosWindowProxy(
314 QOhosWindowProxyData windowProxyData)
315 : m_jsScopeData(
316 QtOhos::makeProxyWithJsThreadDeleter(
317 std::make_shared<JsScopeData>(
318 windowProxyData.windowProxyType,
319 std::move(windowProxyData.jsWindow),
320 std::move(windowProxyData.jsKeepAliveData),
321 std::move(windowProxyData.qAbilityPeer),
322 windowProxyData.owningQWindowRef)))
323 , m_windowProxyType(windowProxyData.windowProxyType)
324 , m_nodeXComponent(windowProxyData.nodeXComponent)
325 , m_qAbilityInstanceId(m_jsScopeData->jsWindowRef->owningQAbilityInstanceId())
326{
327 std::vector<std::shared_ptr<void>> eventListenersHandles;
328 for (const auto &eventHandlerDescriptor : eventHandlerDescriptors) {
329 eventListenersHandles.push_back(
330 m_jsScopeData->registerEventListener(
331 eventHandlerDescriptor.eventName, eventHandlerDescriptor.eventHandler,
332 eventHandlerDescriptor.eventHandlerFlags));
333 }
334 m_jsScopeData->m_eventListenersHandle = QtOhos::moveToSharedPtr(std::move(eventListenersHandles));
335}
336
337QOhosWindowProxy::~QOhosWindowProxy()
338{
339 // NOTE
340 // we need to unregister from JS Window events now, while the Window is
341 // still in consistent state. While destroying the m_jsScopeData we
342 // call Window::destroyWindow(), which makes the JS object unusable
343 // (trying to unregister something on it will throw).
344 m_subWindowCloseRegistrationHandle.reset();
345 m_jsScopeData.reset();
346}
347
348void QOhosWindowProxy::removeStartingWindow()
349{
351 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
352 if (m_jsScopeData->isWindowClosing()) {
353 taskPromise();
354 return;
355 }
356 auto optQUiAbilityPeer
357 = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(m_jsScopeData->qAbilityPeer);
358 if (!optQUiAbilityPeer) {
359 taskPromise();
360 return;
361 }
362 auto promise = optQUiAbilityPeer->windowStage().evalToPromiseOrRejectOnThrow(
363 "removeStartingWindow()");
364 promise.onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
365 },
366 Q_FUNC_INFO);
367}
368
370 const QPoint &point, std::optional<QOhosDisplayInfo::JsDisplayId> optMoveToTargetDisplay)
371{
372 auto displayIdValue = optMoveToTargetDisplay.value_or(QOhosDisplayInfo::JsDisplayId(-1)).value();
373 qOhosPrintfDebug("%s: %d,%d,%f", Q_FUNC_INFO, point.x(), point.y(), displayIdValue);
374
376 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
377 if (m_jsScopeData->isWindowClosing()) {
378 taskPromise();
379 return;
380 }
381
382 const auto primaryJsDisplayId = QOhosDisplayInfo::JsDisplayId(0);
383
384 auto targetDisplayId = optMoveToTargetDisplay.has_value()
385 ? optMoveToTargetDisplay.value()
386 : getWindowProperties().displayId.value_or(primaryJsDisplayId);
387
388 auto optDisplayInfo = qTransform(
389 QOhosDisplayInfo::tryGetDisplayById(jsState, targetDisplayId),
390 [&](auto displayObject) {
391 return QOhosDisplayInfo::makeFromOhosDisplayObject(jsState, displayObject);
392 });
393
394 auto optIsDisplayMainOrExtended = qTransform(
395 optDisplayInfo,
396 [](const QOhosDisplayInfo &displayInfo) {
397 return displayInfo.isDisplayMainOrExtended();
398 });
399
400 bool isDisplayMainOrExtended;
401 if (optIsDisplayMainOrExtended.has_value()) {
402 isDisplayMainOrExtended = optIsDisplayMainOrExtended.value();
403 } else {
404 qOhosPrintfWarning(
405 "%s: no display source mode detected. Assumming main/extended screen", Q_FUNC_INFO);
406 isDisplayMainOrExtended = true;
407 }
408
409 QNapi::Promise promise;
410 if (!isDisplayMainOrExtended) {
411 auto optDisplayOffset = qAndThen(
412 optDisplayInfo,
413 [](const QOhosDisplayInfo &displayInfo) {
414 return displayInfo.topLeftOffsetPixels;
415 });
416
417 const QPoint defaultDisplayOffset(0, 0);
418 auto targetCoordinates = point - optDisplayOffset.value_or(defaultDisplayOffset);
419
420 if (!optMoveToTargetDisplay.has_value())
421 qOhosPrintfWarning("%s: trying to move window to not valid target display", Q_FUNC_INFO);
422
423 auto moveConfigurationObject = toNapiObject(
424 jsState.env(),
425 MoveConfiguration {
426 .displayId = optMoveToTargetDisplay,
427 });
428
429 promise = m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
430 "moveWindowToGlobal(*)", {targetCoordinates.x(), targetCoordinates.y(), moveConfigurationObject});
431 } else {
432 promise = m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
433 "moveWindowToGlobalDisplay(*)", {point.x(), point.y()});
434 }
435
436 promise.onFinally(std::move(taskPromise));
437 });
438}
439
440void QOhosWindowProxy::setSize(const QSize &size)
441{
442 qOhosPrintfDebug("%s: %d,%d", Q_FUNC_INFO, size.width(), size.height());
443
445 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
446 if (m_jsScopeData->isWindowClosing()) {
447 taskPromise();
448 return;
449 }
450
451 const auto currentSize = getWindowPropertiesFromJsWindow(
452 m_jsScopeData->jsWindowRef->jsObject()).windowRect.size();
453 if (currentSize == size) {
454 taskPromise();
455 return;
456 }
457
458 auto promise = m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
459 "resizeAsync(*)", {size.width(), size.height()});
460 promise.onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
461 },
462 Q_FUNC_INFO);
463}
464
465void QOhosWindowProxy::setWindowBackgroundColor(const QColor &color)
466{
467 qCDebug(QtForOhos) << Q_FUNC_INFO << color;
468
470 if (m_jsScopeData->isWindowClosing())
471 return;
472 m_jsScopeData->jsWindowRef->eval("setWindowBackgroundColor(*)", {color.name(QColor::HexArgb).toStdString()});
473 },
474 Q_FUNC_INFO);
475}
476
477void QOhosWindowProxy::setCustomCursor(const QImage &customCursorImage, const QPoint &hotSpot)
478{
479 const auto maxOhosCustomCursorSize = QSize(256, 256);
480 const auto customCursorSize = customCursorImage.size();
481 if (customCursorSize.width() > maxOhosCustomCursorSize.width()
482 || customCursorSize.height() > maxOhosCustomCursorSize.height()) {
483 qOhosPrintfError(
484 "%s: can't set %dx%d custom cursor, OHOS max custom cursor size is %dx%d",
485 Q_FUNC_INFO, customCursorSize.width(), customCursorSize.height(),
486 maxOhosCustomCursorSize.width(), maxOhosCustomCursorSize.height());
487 return;
488 }
489
490 auto convertedImage = customCursorImage.convertToFormat(QImage::Format_RGBA8888);
491 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
492 if (m_jsScopeData->isWindowClosing()) {
493 taskPromise();
494 return;
495 }
496 auto windowId = m_jsScopeData->jsWindowRef->jsObject().get<QNapi::Number>("getWindowProperties().id");
497
498 auto jsCursor = QNapi::makeObject(
499 jsState.env(),
500 {
501 {"pixelMap", makeOhosNapiPixelMapFromQImage(jsState, convertedImage)},
502 {"focusX", hotSpot.x()},
503 {"focusY", hotSpot.y()},
504 });
505
506 auto jsCursorConfig = QNapi::makeObject(
507 jsState.env(),
508 {
509 {"followSystem", false},
510 });
511
512 jsState.evalToPromiseOrRejectOnThrow(
513 "@ohos.multimodalInput.pointer.setCustomCursor(*)", {windowId, jsCursor, jsCursorConfig})
514 .onCatch(QtOhos::makeErrorLoggingJsCallback("setCustomCursor()"))
515 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
516 },
517 Q_FUNC_INFO);
518}
519
520void QOhosWindowProxy::setPointerStyleSync(const QCursor &cursor)
521{
523 if (m_jsScopeData->isWindowClosing())
524 return;
525 auto windowId = m_jsScopeData->jsWindowRef->jsObject().get<QNapi::Number>("getWindowProperties().id");
526
527 jsState.eval(
528 "@ohos.multimodalInput.pointer.setPointerStyleSync(*)",
529 {windowId, jsState.mapOhosEnumToJs(convertToOhosCursor(cursor.shape()))});
530 },
531 Q_FUNC_INFO);
532}
533
535{
536 return QtOhos::evalInJsThread(
537 [&](QtOhos::JsState &) {
538 if (m_jsScopeData->isWindowClosing())
539 return QArkUi::WindowProperties {};
540 return getWindowPropertiesFromJsWindow(m_jsScopeData->jsWindowRef->jsObject());
541 },
542 Q_FUNC_INFO);
543}
544
545void QOhosWindowProxy::setWindowCallbackReceiver(std::unique_ptr<WindowCallbacks> callbackReceiver)
546{
547 auto sharedWindowCallbackReceiver = std::shared_ptr<WindowCallbacks>(std::move(callbackReceiver));
548
550 m_jsScopeData->windowCallbackReceiver = QtOhos::moveToSharedPtr(WindowCallbacks {
551 .onWindowEvent = makeQtThreadWindowCallbackDelegate(&WindowCallbacks::onWindowEvent, sharedWindowCallbackReceiver),
552 .onWindowStatusChange = makeQtThreadWindowCallbackDelegate(&WindowCallbacks::onWindowStatusChange, sharedWindowCallbackReceiver),
553 .onWindowVisibilityChange = makeQtThreadWindowCallbackDelegate(&WindowCallbacks::onWindowVisibilityChange, sharedWindowCallbackReceiver),
554 .onTouchOutside = makeQtThreadWindowCallbackDelegate(&WindowCallbacks::onTouchOutside, sharedWindowCallbackReceiver),
555 .onAvoidAreaChange = makeQtThreadWindowCallbackDelegate(&WindowCallbacks::onAvoidAreaChange, sharedWindowCallbackReceiver),
556 .onWindowRectChange = makeCompressingQtThreadWindowCallbackDelegate(&WindowCallbacks::onWindowRectChange, sharedWindowCallbackReceiver),
557 .onWindowRectChangeInGlobalDisplay = makeCompressingQtThreadWindowCallbackDelegate(&WindowCallbacks::onWindowRectChangeInGlobalDisplay, sharedWindowCallbackReceiver),
558 .onWindowDisplayIdChange = makeQtThreadWindowCallbackDelegate(&WindowCallbacks::onWindowDisplayIdChange, sharedWindowCallbackReceiver),
559 });
560 },
561 Q_FUNC_INFO);
562
563 m_qtWindowCallbacksReceiverHandle = sharedWindowCallbackReceiver;
564}
565
567 QObject *contextObject, QOhosConsumer<std::vector<NonClientAreaMouseEvent>> mouseEventBatchConsumer)
568{
569 auto qtConsumer = QtOhos::moveToSharedPtr(std::move(mouseEventBatchConsumer));
570 auto weakQtConsumer = QtOhos::makeWeakPtr(qtConsumer);
571
572 auto jsConsumer = makeQtOhosSimpleBatchingQtRequestsHandler<NonClientAreaMouseEvent>(
573 QtOhos::QObjectThreadSafeRef(contextObject),
574 [weakQtConsumer](std::vector<NonClientAreaMouseEvent> &&batch) {
575 auto qtConsumer = weakQtConsumer.lock();
576 if (qtConsumer)
577 (*qtConsumer)(std::move(batch));
578 });
579
581 m_jsScopeData->nonClientAreaMouseEventConsumer = std::move(jsConsumer);
582 },
583 Q_FUNC_INFO);
584
585 m_qtNonClientAreaMouseWindowCallbackReceiverHandle = qtConsumer;
586}
587
589 QObject *contextObject, QOhosConsumer<std::vector<NonClientAreaTouchEvent>> touchEventBatchConsumer)
590{
591 auto qtConsumer = QtOhos::moveToSharedPtr(std::move(touchEventBatchConsumer));
592 auto weakQtConsumer = QtOhos::makeWeakPtr(qtConsumer);
593
594 auto jsConsumer = makeQtOhosSimpleBatchingQtRequestsHandler<NonClientAreaTouchEvent>(
595 QtOhos::QObjectThreadSafeRef(contextObject),
596 [weakQtConsumer](std::vector<NonClientAreaTouchEvent> &&batch) {
597 auto qtConsumer = weakQtConsumer.lock();
598 if (qtConsumer)
599 (*qtConsumer)(std::move(batch));
600 });
601
603 m_jsScopeData->nonClientAreaTouchEventConsumer = std::move(jsConsumer);
604 },
605 Q_FUNC_INFO);
606
607 m_qtNonClientAreaTouchWindowCallbackReceiverHandle = qtConsumer;
608}
609
610bool QOhosWindowProxy::qtIsMainWindow() const
611{
612 return m_windowProxyType == WindowProxyType::MainWindow;
613}
614
615void QOhosWindowProxy::raiseToAppTop()
616{
617 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
618 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
619 if (m_jsScopeData->isWindowClosing()) {
620 taskPromise();
621 return;
622 }
623 auto promise = m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("raiseToAppTop()");
624 promise.onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
625 },
626 Q_FUNC_INFO);
627}
628
629void QOhosWindowProxy::showWindow(const ShowWindowOptions &options)
630{
631 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
632 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
633 if (m_jsScopeData->isWindowClosing()) {
634 taskPromise();
635 return;
636 }
637
638 std::vector<std::pair<std::string, QNapi::ValueWrapper>> jsOptionsProps;
639
640 if (options.focusOnShow.has_value())
641 jsOptionsProps.emplace_back("focusOnShow", options.focusOnShow.value());
642
643 std::vector<QNapi::ValueWrapper> showWindowArgs;
644 constexpr bool brokenHandlingOfEmptyOptionsParamInOhos = true;
645 if (!(jsOptionsProps.empty() && brokenHandlingOfEmptyOptionsParamInOhos))
646 showWindowArgs.push_back(QNapi::makeObject(jsState.env(), jsOptionsProps));
647
648 auto promise = m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("showWindow(*)", showWindowArgs);
649 promise.onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
650 },
651 Q_FUNC_INFO);
652}
653
654void QOhosWindowProxy::recover()
655{
656 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
657 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
658 if (m_jsScopeData->isWindowClosing()) {
659 taskPromise();
660 return;
661 }
662
663 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("recover()")
664 .onCatch(QtOhos::makeErrorLoggingJsCallback("recover()"))
665 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
666 },
667 Q_FUNC_INFO);
668}
669
670void QOhosWindowProxy::restore()
671{
672 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
673 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
674 if (m_jsScopeData->isWindowClosing()) {
675 taskPromise();
676 return;
677 }
678 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("restore()").onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
679 },
680 Q_FUNC_INFO);
681}
682
683void QOhosWindowProxy::minimize()
684{
685 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
686 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
687 if (m_jsScopeData->isWindowClosing()) {
688 taskPromise();
689 return;
690 }
691 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("minimize()").onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
692 },
693 Q_FUNC_INFO);
694}
695
696void QOhosWindowProxy::maximize(MaximizePresentation maximizePresentation)
697{
698 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
699
700 if (!qtIsMainWindow()) {
701 qCWarning(QtForOhos(), "%s: Maximize is currently supported only on main windows", Q_FUNC_INFO);
702 return;
703 }
704
706 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
707 if (m_jsScopeData->isWindowClosing()) {
708 taskPromise();
709 return;
710 }
711
712 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
713 "maximize(*)", {jsState.mapOhosEnumToJs(maximizePresentation)})
714 .onCatch(QtOhos::makeErrorLoggingJsCallback("maximize()"))
715 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
716 },
717 Q_FUNC_INFO);
718}
719
720void QOhosWindowProxy::setWindowLayoutFullScreen(bool isLayoutFullScreen)
721{
722 qCDebug(QtForOhos, "%s: %s", Q_FUNC_INFO, isLayoutFullScreen ? "true" : "false");
724 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
725 if (m_jsScopeData->isWindowClosing()) {
726 taskPromise();
727 return;
728 }
729 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
730 "setWindowLayoutFullScreen(*)", {isLayoutFullScreen})
731 .onCatch(QtOhos::makeErrorLoggingJsCallback("setWindowLayoutFullScreen()"))
732 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
733 },
734 Q_FUNC_INFO);
735}
736
737void QOhosWindowProxy::setWindowSystemBarEnable(const QStringList &names)
738{
739 qCDebug(QtForOhos) << Q_FUNC_INFO << names;
741 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
742 if (m_jsScopeData->isWindowClosing()) {
743 taskPromise();
744 return;
745 }
746 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
747 "setWindowSystemBarEnable(*)",
748 {QNapi::makeArray(jsState.env(), names, std::mem_fn(&QString::toStdString))})
749 .onCatch(QtOhos::makeErrorLoggingJsCallback("setWindowSystemBarEnable()"))
750 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
751 },
752 Q_FUNC_INFO);
753}
754
755void QOhosWindowProxy::showAbility()
756{
757 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
758
759 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
760 if (m_jsScopeData->isWindowClosing()) {
761 taskPromise();
762 return;
763 }
764
765 m_jsScopeData->qAbilityPeer->qAbility().evalToPromiseOrRejectOnThrow("context.showAbility()")
766 .onCatch(QtOhos::makeErrorLoggingJsCallback("showAbility()"))
767 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
768 },
769 Q_FUNC_INFO);
770}
771
772bool QOhosWindowProxy::tryHideAbility()
773{
774 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
775
776 return QtOhos::evalInJsThreadWithPromise<bool>([&](QtOhos::JsState &, QOhosTaskPromise<bool> evalPromise) {
777 if (m_jsScopeData->isWindowClosing()) {
778 evalPromise(false);
779 return;
780 }
781
782 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
783 m_jsScopeData->qAbilityPeer->qAbility().evalToPromiseOrRejectOnThrow("context.hideAbility()")
784 .onThen(
785 [thenPromise = std::move(thenCatchPromises.first)](const QtOhos::CallbackInfo &) {
786 thenPromise(true);
787 })
788 .onCatch(
789 [catchPromise = std::move(thenCatchPromises.second)](const QtOhos::CallbackInfo &cbInfo) {
790 QtOhos::logJsCallbackError(cbInfo, "got error from hideAbility()");
791 catchPromise(false);
792 });
793 },
794 Q_FUNC_INFO);
795}
796
797bool QOhosWindowProxy::getImmersiveModeEnabledState()
798{
799 return QtOhos::evalInJsThread(
800 [&](QtOhos::JsState &) {
801 if (m_jsScopeData->isWindowClosing())
802 return false;
803 return m_jsScopeData->jsWindowRef->eval<QNapi::Boolean>("getImmersiveModeEnabledState()").Value();
804 },
805 Q_FUNC_INFO);
806}
807
808void QOhosWindowProxy::setWindowPrivacyMode(bool privacyMode)
809{
810 qCDebug(QtForOhos, "%s: %s", Q_FUNC_INFO, privacyMode ? "true" : "false");
811
812 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
813 if (m_jsScopeData->isWindowClosing()) {
814 taskPromise();
815 return;
816 }
817
818 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setWindowPrivacyMode(*)", {privacyMode})
819 .onCatch(QtOhos::makeErrorLoggingJsCallback("setPrivacyMode()"))
820 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
821 },
822 Q_FUNC_INFO);
823}
824
825void QOhosWindowProxy::setWindowFocusable(bool focusable)
826{
827 qCDebug(QtForOhos, "%s: %s", Q_FUNC_INFO, focusable ? "true" : "false");
828 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
829 if (m_jsScopeData->isWindowClosing()) {
830 taskPromise();
831 return;
832 }
833
834 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setWindowFocusable(*)", {focusable})
835 .onCatch(QtOhos::makeErrorLoggingJsCallback("setWindowFocusable()"))
836 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
837 },
838 Q_FUNC_INFO);
839}
840
841void QOhosWindowProxy::setWindowTouchable(bool touchable)
842{
843 qCDebug(QtForOhos, "%s: %s", Q_FUNC_INFO, touchable ? "true" : "false");
844 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
845 if (m_jsScopeData->isWindowClosing()) {
846 taskPromise();
847 return;
848 }
849
850 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setWindowTouchable(*)", {touchable})
851 .onCatch(QtOhos::makeErrorLoggingJsCallback("setWindowTouchable()"))
852 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
853 },
854 Q_FUNC_INFO);
855}
856
857void QOhosWindowProxy::setWindowLimits(const QSize &minSize, const QSize &maxSize)
858{
859 qCDebug(
860 QtForOhos, "%s: (%d x %d)-(%d x %d)", Q_FUNC_INFO, minSize.width(), minSize.height(),
861 maxSize.width(), maxSize.height());
863 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
864 if (m_jsScopeData->isWindowClosing()) {
865 taskPromise();
866 return;
867 }
868 auto windowLimits = QNapi::makeObject(
869 jsState.env(),
870 {
871 {"minWidth", minSize.width()},
872 {"minHeight", minSize.height()},
873 {"maxWidth", maxSize.width()},
874 {"maxHeight", maxSize.height()},
875 });
876
877 std::vector<QNapi::ValueWrapper> setWindowLimitsArgs = {windowLimits};
879 constexpr bool isForcible = true;
880 setWindowLimitsArgs.push_back(isForcible);
881 }
882 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
883 "setWindowLimits(*)", setWindowLimitsArgs)
884 .onCatch(QtOhos::makeErrorLoggingJsCallback("setWindowLimits()"))
885 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
886 },
887 Q_FUNC_INFO);
888}
889
890QOhosWindowProxy::WindowLimits QOhosWindowProxy::getWindowLimits() const
891{
892 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
893 return QtOhos::evalInJsThread(
894 [&](QtOhos::JsState &) {
895 if (m_jsScopeData->isWindowClosing())
896 return WindowLimits {};
897 auto windowLimitsObject = m_jsScopeData->jsWindowRef->eval<QNapi::Object>("getWindowLimits()");
898 return WindowLimits {
899 .minWidth = getOptionalNumberPropAsOptionalDouble(windowLimitsObject, "minWidth"),
900 .minHeight = getOptionalNumberPropAsOptionalDouble(windowLimitsObject, "minHeight"),
901 .maxWidth = getOptionalNumberPropAsOptionalDouble(windowLimitsObject, "maxWidth"),
902 .maxHeight = getOptionalNumberPropAsOptionalDouble(windowLimitsObject, "maxHeight"),
903 };
904 },
905 Q_FUNC_INFO);
906}
907
908QOhosWindowProxy::AvoidArea QOhosWindowProxy::getWindowAvoidArea(AvoidAreaType avoidAreaType) const
909{
910 qCDebug(QtForOhos, "%s: %d", Q_FUNC_INFO, avoidAreaType);
911 return QtOhos::evalInJsThread(
912 [&](QtOhos::JsState &jsState) {
913 if (m_jsScopeData->isWindowClosing())
914 return AvoidArea {};
915 auto avoidAreaObject = m_jsScopeData->jsWindowRef->eval<QNapi::Object>(
916 "getWindowAvoidArea(*)", {jsState.mapOhosEnumToJs(avoidAreaType)});
917 return mapAvoidAreaFromJs(avoidAreaObject);
918 },
919 Q_FUNC_INFO);
920}
921
922void QOhosWindowProxy::setWindowMask(
923 const WindowMask &windowMask, const std::optional<QSize> &ohosMaskSizeOverride)
924{
926 return;
927
928 auto ohosMaskSize = ohosMaskSizeOverride.has_value()
929 ? ohosMaskSizeOverride.value()
930 : getWindowProperties().windowRect.size();
931
932 if (ohosMaskSize.isEmpty()) {
933 const auto *maskSrcSizeMsg = ohosMaskSizeOverride.has_value()
934 ? "overridden mask source"
935 : "window";
936 if (ohosMaskSizeOverride.has_value()) {
937 qOhosPrintfError(
938 "%s failed - %s size is 0x0", maskSrcSizeMsg,
939 "QOhosWindowProxy::setWindowMask");
940 }
941 return;
942 }
943
944 QtOhos::invokeInJsThreadAndWaitForContinue([&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
945 if (m_jsScopeData->isWindowClosing()) {
946 taskPromise();
947 return;
948 }
949 auto *env = jsState.env();
950
951 QNapi::Array maskRowsArray = QNapi::Array::New(env, ohosMaskSize.height());
952 const int defaultValue = windowMask.windowMaskRegion.isEmpty() ? 1 : 0;
953
954 for (int rowIndex = 0; rowIndex < ohosMaskSize.height(); ++rowIndex) {
955 auto arr = QNapi::Array::New(env, ohosMaskSize.width());
956 arr.fill(defaultValue);
957 maskRowsArray[rowIndex] = arr;
958 }
959
960 auto valueToSet = QNapi::Number::New(env, 1);
961 for (const auto &rect: windowMask.windowMaskRegion) {
962 auto top = qBound(0, rect.top(), ohosMaskSize.height() - 1);
963 auto bottom = qBound(0, rect.bottom(), ohosMaskSize.height() - 1);
964 auto left = qBound(0, rect.left(), ohosMaskSize.width() - 1);
965 auto right = qBound(0, rect.right(), ohosMaskSize.width() - 1);
966
967 for (auto rowIndex = top; rowIndex <= bottom; ++rowIndex) {
968 auto row = maskRowsArray.Get(rowIndex).As<QNapi::Array>();
969 for (auto columnIndex = left; columnIndex <= right; ++columnIndex)
970 row[columnIndex] = valueToSet;
971 }
972 }
973
974 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setWindowMask(*)", {maskRowsArray})
975 .onCatch(QtOhos::makeErrorLoggingJsCallback("setWindowMask()"))
976 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
977 },
978 Q_FUNC_INFO);
979}
980
981void QOhosWindowProxy::setSubWindowModalDisabled()
982{
984 return;
985
987 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
988 if (m_jsScopeData->isWindowClosing()) {
989 taskPromise();
990 return;
991 }
992 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setSubWindowModal(*)", {false})
993 .onCatch(QtOhos::makeErrorLoggingJsCallback("setSubWindowModal()"))
994 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
995 },
996 Q_FUNC_INFO);
997}
998
999void QOhosWindowProxy::setSubWindowModalEnabled(ModalityType modalityType)
1000{
1001 if (qtIsMainWindow())
1002 return;
1003
1005 qOhosPrintfWarning(
1006 "%s: APPLICATION_MODALITY option can be used only on devices in the freeform window state - skipping",
1007 Q_FUNC_INFO);
1008 return;
1009 }
1010
1012 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
1013 if (m_jsScopeData->isWindowClosing()) {
1014 taskPromise();
1015 return;
1016 }
1017 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
1018 "setSubWindowModal(*)", {true, jsState.mapOhosEnumToJs(modalityType)})
1019 .onCatch(QtOhos::makeErrorLoggingJsCallback("setSubWindowModal()"))
1020 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1021 },
1022 Q_FUNC_INFO);
1023}
1024
1025void QOhosWindowProxy::setTitle(const QString &title)
1026{
1028 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
1029 if (m_jsScopeData->isWindowClosing()) {
1030 taskPromise();
1031 return;
1032 }
1033
1034 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setWindowTitle(*)", {title.toStdString()})
1035 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1036 },
1037 Q_FUNC_INFO);
1038}
1039
1040void QOhosWindowProxy::setWindowTitleButtonVisible(bool maximizeVisible, bool minimizeVisible, bool closeVisible)
1041{
1042 qOhosPrintfDebug(
1043 "%s: isMaximizeVisible:%s, isMinimizeVisible:%s, isCloseVisible:%s",
1044 Q_FUNC_INFO, maximizeVisible ? "true" : "false", minimizeVisible ? "true": "false",
1045 closeVisible ? "true" : "false");
1046
1047 if (!qtIsMainWindow())
1048 return;
1049
1051 [&](QtOhos::JsState &jsState) {
1052 if (m_jsScopeData->isWindowClosing())
1053 return;
1054
1055 constexpr auto capabilityNotSupportedErrorCode = 801;
1057 jsState, capabilityNotSupportedErrorCode, "setWindowTitleButtonVisible()",
1058 [&]() {
1059 m_jsScopeData->jsWindowRef->eval(
1060 "setWindowTitleButtonVisible(*)",
1061 {maximizeVisible, minimizeVisible, closeVisible});
1062 });
1063 },
1064 Q_FUNC_INFO);
1065}
1066
1067void QOhosWindowProxy::setWindowTopmost(bool topmost)
1068{
1069 if (!qtIsMainWindow())
1070 return;
1071
1073 qOhosPrintfWarning("%s: can be used only on 2-in-1 devices or tablets in PC mode - skipping", Q_FUNC_INFO);
1074 return;
1075 }
1076
1077 qOhosPrintfDebug("%s: topMost: %s", Q_FUNC_INFO, topmost ? "true" : "false");
1078
1080 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
1081 if (m_jsScopeData->isWindowClosing()) {
1082 taskPromise();
1083 return;
1084 }
1085
1086 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setWindowTopmost(*)", {topmost})
1087 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1088 },
1089 Q_FUNC_INFO);
1090}
1091
1092void QOhosWindowProxy::setWindowDecorVisible(bool visible)
1093{
1095 if (m_jsScopeData->isWindowClosing())
1096 return;
1097
1098 m_jsScopeData->jsWindowRef->eval("setWindowDecorVisible(*)", {visible});
1099 },
1100 Q_FUNC_INFO);
1101}
1102
1103void QOhosWindowProxy::setWindowTitleMoveEnabled(bool enabled)
1104{
1106 qOhosPrintfWarning("%s: can be used only on 2-in-1 devices or tablets in PC mode - skipping", Q_FUNC_INFO);
1107 return;
1108 }
1109
1111 if (m_jsScopeData->isWindowClosing())
1112 return;
1113
1114 m_jsScopeData->jsWindowRef->eval("setWindowTitleMoveEnabled(*)", {enabled});
1115 },
1116 Q_FUNC_INFO);
1117}
1118
1119void QOhosWindowProxy::setWindowShadowRadius(double radius)
1120{
1122 qOhosPrintfWarning("%s: can be used only on 2-in-1 devices or tablets - skipping", Q_FUNC_INFO);
1123 return;
1124 }
1125
1126 if (qtIsMainWindow())
1127 return;
1128
1130 m_jsScopeData->jsWindowRef->eval("setWindowShadowRadius(*)", {radius});
1131 },
1132 Q_FUNC_INFO);
1133}
1134
1135void QOhosWindowProxy::setWindowCornerRadius(double radius)
1136{
1137 if (qtIsMainWindow())
1138 return;
1139
1141 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
1142 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setWindowCornerRadius(*)", {radius})
1143 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1144 },
1145 Q_FUNC_INFO);
1146}
1147
1148bool QOhosWindowProxy::isWindowRectAutoSave() const
1149{
1150 return QtOhos::evalInJsThreadWithPromise<bool>(
1151 [&](QtOhos::JsState &, QOhosTaskPromise<bool> evalPromise) {
1152 if (m_jsScopeData->isWindowClosing()) {
1153 evalPromise(false);
1154 return;
1155 }
1156 auto optQUiAbilityPeer = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(m_jsScopeData->qAbilityPeer);
1157 if (!optQUiAbilityPeer) {
1158 evalPromise(false);
1159 return;
1160 }
1161
1162 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
1163 optQUiAbilityPeer->windowStage()
1164 .evalToPromiseOrRejectOnThrow("isWindowRectAutoSave()")
1165 .onThen(
1166 [thenPromise = std::move(thenCatchPromises.first)](const QtOhos::CallbackInfo &cbInfo) {
1167 bool windowRectAutoSaveEnabled = cbInfo.getFirstArg<QNapi::Boolean>(Q_FUNC_INFO);
1168 thenPromise(windowRectAutoSaveEnabled);
1169 })
1170 .onCatch(
1171 [catchPromise = std::move(thenCatchPromises.second)](const QtOhos::CallbackInfo &cbInfo) {
1172 QtOhos::logJsCallbackError(cbInfo, "isWindowRectAutoSave()");
1173 catchPromise(false);
1174 });
1175 },
1176 Q_FUNC_INFO);
1177}
1178
1179void QOhosWindowProxy::setFollowParentMultiScreenPolicy(bool enabled)
1180{
1181 if (qtIsMainWindow())
1182 return;
1183
1185 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
1186 if (m_jsScopeData->isWindowClosing()) {
1187 taskPromise();
1188 return;
1189 }
1190 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setFollowParentMultiScreenPolicy(*)", {enabled})
1191 .onCatch(QtOhos::makeErrorLoggingJsCallback("setFollowParentMultiScreenPolicy()"))
1192 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1193 },
1194 Q_FUNC_INFO);
1195}
1196
1197void QOhosWindowProxy::setWindowKeepScreenOn(bool keepScreenOn)
1198{
1199 qCDebug(QtForOhos, "%s: %s", Q_FUNC_INFO, QtOhos::mapBoolToTrueFalseStr(keepScreenOn));
1200
1202 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
1203 if (m_jsScopeData->isWindowClosing()) {
1204 taskPromise();
1205 return;
1206 }
1207
1208 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("setWindowKeepScreenOn(*)", {keepScreenOn})
1209 .onCatch(QtOhos::makeErrorLoggingJsCallback("setWindowKeepScreenOn()"))
1210 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1211 },
1212 Q_FUNC_INFO);
1213}
1214
1215void QOhosWindowProxy::setSupportedWindowModes(const std::set<SupportWindowMode> &supportedWindowModes)
1216{
1217 qCDebug(QtForOhos, "%s: %s", Q_FUNC_INFO, mapEnumsToLogString(supportedWindowModes).c_str());
1218
1220 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
1221 if (m_jsScopeData->isWindowClosing()) {
1222 taskPromise();
1223 return;
1224 }
1225 auto qUiAbilityPeer = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(m_jsScopeData->qAbilityPeer);
1226 if (!qUiAbilityPeer) {
1227 taskPromise();
1228 return;
1229 }
1230
1231 auto jsSupportedWindowModes = QNapi::makeArray(
1232 jsState.env(), supportedWindowModes,
1233 [&](auto mode) {
1234 return jsState.mapOhosEnumToJs(mode);
1235 });
1236
1237 qUiAbilityPeer->windowStage().evalToPromiseOrRejectOnThrow("setSupportedWindowModes(*)", {jsSupportedWindowModes})
1238 .onCatch(QtOhos::makeErrorLoggingJsCallback("setSupportedWindowModes()"))
1239 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1240 },
1241 Q_FUNC_INFO);
1242}
1243
1244void QOhosWindowProxy::setWindowRectAutoSave(bool enabled)
1245{
1246 constexpr bool isSaveBySpecifiedFlag = true;
1247
1249 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
1250 if (m_jsScopeData->isWindowClosing()) {
1251 taskPromise();
1252 return;
1253 }
1254 auto optQUiAbilityPeer = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(m_jsScopeData->qAbilityPeer);
1255 if (!optQUiAbilityPeer) {
1256 taskPromise();
1257 return;
1258 }
1259
1260 optQUiAbilityPeer->windowStage()
1261 .evalToPromiseOrRejectOnThrow("setWindowRectAutoSave(*)", {enabled, isSaveBySpecifiedFlag})
1262 .onCatch(QtOhos::makeErrorLoggingJsCallback("setWindowRectAutoSave()"))
1263 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1264 },
1265 Q_FUNC_INFO);
1266}
1267
1268void QOhosWindowProxy::setSubWindowCloseHandler(
1269 std::function<void()> handler, bool handlerReturnValue)
1270{
1271 m_subWindowCloseRegistrationHandle =
1272 registerSubWindowCloseHandler(std::move(handler), handlerReturnValue);
1273}
1274
1275void QOhosWindowProxy::resetSubWindowCloseHandler()
1276{
1277 m_subWindowCloseRegistrationHandle.reset();
1278}
1279
1280std::shared_ptr<void> QOhosWindowProxy::registerSubWindowCloseHandler(
1281 std::function<void()> handler, bool handlerReturnValue)
1282{
1283 auto sharedHandler = QtOhos::moveToSharedPtr(std::move(handler));
1284 auto jsWindowRegistrationHandle = QtOhos::evalInJsThread(
1285 [&](QtOhos::JsState &jsState) {
1286 return QtOhos::makeProxyWithJsThreadDeleter(
1287 m_jsScopeData->registerSubWindowCloseHandler(
1288 jsState,
1289 [weakHandler = QtOhos::makeWeakPtr(sharedHandler), handlerReturnValue]() {
1290 QtOhos::invokeInQtThread(
1291 [weakHandler]() {
1292 auto sharedHandler = weakHandler.lock();
1293 if (sharedHandler)
1294 (*sharedHandler)();
1295 });
1296 return handlerReturnValue;
1297 }));
1298 },
1299 Q_FUNC_INFO);
1300
1301 return QtOhos::moveToSharedPtr(
1302 std::make_tuple(sharedHandler, jsWindowRegistrationHandle));
1303}
1304
1305std::shared_ptr<QOhosWindowProxy>
1307{
1308 return QtOhos::evalInJsThreadWithPromise<std::shared_ptr<QOhosWindowProxy>>(
1309 [&](QtOhos::JsState &jsState, QOhosTaskPromise<std::shared_ptr<QOhosWindowProxy>> evalPromise) {
1310 auto sharedEvalPromise = QtOhos::moveToSharedPtr(std::move(evalPromise).makeChained(Q_FUNC_INFO));
1311 makeWindowProxyDataForExistingMainWindowInJsThread(
1312 jsState,
1313 createInfo,
1314 [sharedEvalPromise](QtOhos::JsState &jsState, QOhosWindowProxyData windowProxyData) {
1315 (*sharedEvalPromise)(QOhosWindowProxy::create(jsState, std::move(windowProxyData)));
1316 });
1317 },
1318 Q_FUNC_INFO);
1319}
1320
1321std::shared_ptr<QOhosWindowProxy>
1322QOhosWindowProxy::createFloatWindow(const FloatWindowCreateInfo &createInfo)
1323{
1324 return QtOhos::evalInJsThreadWithPromise<std::shared_ptr<QOhosWindowProxy>>(
1325 [&](QtOhos::JsState &jsState, QOhosTaskPromise<std::shared_ptr<QOhosWindowProxy>> evalPromise) {
1326 auto sharedEvalPromise = QtOhos::moveToSharedPtr(std::move(evalPromise).makeChained(Q_FUNC_INFO));
1327 makeWindowProxyDataForFloatWindowInJsThread(
1328 jsState, createInfo,
1329 [sharedEvalPromise](QtOhos::JsState &jsState, QOhosWindowProxyData windowProxyData) {
1330 (*sharedEvalPromise)(QOhosWindowProxy::create(jsState, std::move(windowProxyData)));
1331 });
1332 },
1333 Q_FUNC_INFO);
1334}
1335
1336std::shared_ptr<QOhosWindowProxy>
1337QOhosWindowProxy::createMainWindow(const MainWindowCreateInfo &createInfo)
1338{
1339 return QtOhos::evalInJsThreadWithPromise<std::shared_ptr<QOhosWindowProxy>>(
1340 [&](QtOhos::JsState &jsState, QOhosTaskPromise<std::shared_ptr<QOhosWindowProxy>> evalPromise) {
1341 auto sharedEvalPromise = QtOhos::moveToSharedPtr(std::move(evalPromise).makeChained(Q_FUNC_INFO));
1342 makeWindowProxyDataForMainWindowInJsThread(
1343 jsState,
1344 createInfo,
1345 [sharedEvalPromise](QtOhos::JsState &jsState, QOhosWindowProxyData windowProxyData) {
1346 (*sharedEvalPromise)(QOhosWindowProxy::create(jsState, std::move(windowProxyData)));
1347 });
1348 },
1349 Q_FUNC_INFO);
1350}
1351
1352std::shared_ptr<QXComponentNode> QOhosWindowProxy::nodeXComponent() const
1353{
1354 return m_nodeXComponent;
1355}
1356
1357std::string QOhosWindowProxy::qAbilityInstanceId() const
1358{
1359 return m_qAbilityInstanceId;
1360}
1361
1362std::shared_ptr<QOhosWindowProxy>
1363QOhosWindowProxy::createSubWindow(const SubWindowCreateInfo &createInfo)
1364{
1365 return QtOhos::evalInJsThreadWithPromise<std::shared_ptr<QOhosWindowProxy>>(
1366 [&](QtOhos::JsState &jsState, QOhosTaskPromise<std::shared_ptr<QOhosWindowProxy>> evalPromise) {
1367 auto sharedEvalPromise = QtOhos::moveToSharedPtr(std::move(evalPromise).makeChained(Q_FUNC_INFO));
1368 auto proxyDataConsumer =
1369 [sharedEvalPromise](QtOhos::JsState &jsState, QOhosWindowProxyData windowProxyData) {
1370 (*sharedEvalPromise)(QOhosWindowProxy::create(jsState, std::move(windowProxyData)));
1371 };
1372 // HACK - calling createSubWindow from window may throw while the context is termination
1373 // This is only a problem because we currenlty do not properly handle main window closing
1374 // Remove this hacky branch after QTFOROH-1080 issues are resolved.
1375 if (m_jsScopeData->isWindowClosing()) {
1376 makeWindowProxyDataForSubWindowInJsThread(
1377 jsState, createInfo, std::move(proxyDataConsumer));
1378 } else {
1379 makeWindowProxyDataForSubWindowInJsThread(
1380 jsState, m_jsScopeData->jsWindowRef->jsObject(), createInfo,
1381 std::move(proxyDataConsumer));
1382 }
1383 },
1384 Q_FUNC_INFO);
1385}
1386
1387QOhosWindowProxy::JsScopeData::JsScopeData(
1388 WindowProxyType windowProxyType, QNapi::Reference<QNapi::Object> jsWindow,
1389 std::shared_ptr<void> optKeepAliveData,
1390 std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer,
1391 QtOhos::QObjectThreadSafeRef owningQWindowRef)
1392 : windowProxyType(windowProxyType)
1393 , windowCallbackReceiver(nullptr)
1394 , windowDestroyedFromSystem(false)
1395 , optKeepAliveData(optKeepAliveData)
1396 , qAbilityPeer(qAbilityPeer)
1397 , m_windowFrameMouseFilterHandle(
1398 QArkUi::registerMouseEventsConsumer(
1399 getWindowPropertiesFromJsWindow(jsWindow.Value()).id,
1400 [this](const QArkUi::MouseEvent &event) {
1401 onMouseEventFromArkUi(event);
1402 }))
1403 , m_windowFrameTouchFilterHandle(
1404 QArkUi::registerTouchEventsConsumer(
1405 getWindowPropertiesFromJsWindow(jsWindow.Value()).id,
1406 [this](const QArkUi::TouchEvent &event) {
1407 onTouchEventFromArkUi(event);
1408 }))
1409 , jsWindowRef(
1410 std::make_shared<QArkUi::JsWindowRef>(
1411 qAbilityPeer->instanceId(),
1412 getWindowPropertiesFromJsWindow(jsWindow.Value()).id,
1413 jsWindow.Value(),
1414 owningQWindowRef))
1415{
1416}
1417
1418QOhosWindowProxy::JsScopeData::~JsScopeData()
1419{
1420 if (isWindowClosingFromSystem(jsWindowRef->jsObject(), windowProxyType, qAbilityPeer)) {
1421 windowDestroyedFromSystem = true;
1422 return;
1423 }
1424
1425 QtOhos::JsWindowsTracker::tagWindowAsClosing(jsWindowRef->jsObject(), "QOhosWindowProxy::JsScopeData destructor");
1426
1427 if (windowProxyType == WindowProxyType::MainWindow) {
1428 // NOTE - Set the windowDestroyedFromSystem flag here early
1429 // to avoid callbacks being invoked directly as a result of
1430 // calling terminate
1431 windowDestroyedFromSystem = true;
1432 qOhosPrintfWarning(
1433 "Attempting to terminate qAbility with instance id: %s",
1434 jsWindowRef->owningQAbilityInstanceId().c_str());
1435 qAbilityPeer->qAbility().eval("context.terminateSelf()");
1436 } else if (!windowDestroyedFromSystem) {
1437 // FIXME - destroyWindow usually does and returns nothing
1438 // once the actual implementation is provided wait for the proomise that this function should return
1439 jsWindowRef->eval("destroyWindow()");
1440 }
1441}
1442
1443std::shared_ptr<void> QOhosWindowProxy::JsScopeData::registerEventListener(
1444 const std::string &eventName,
1445 void (QOhosWindowProxy::JsScopeData::*handleFunction)(const QtOhos::CallbackInfo &),
1446 QFlags<EventHandlerFlagBits> eventHandlerFlags)
1447{
1448
1449 std::weak_ptr<JsScopeData> weakSelf = shared_from_this();
1450 bool ignoreWhenAbilityIsTerminating = !eventHandlerFlags.testFlag(EventHandlerFlagBits::allowCallWhenAbilityIsTerminating);
1451
1452 return registerQOhosOnOffMethodsBasedEventHandler(
1453 jsWindowRef->jsObject(), eventName,
1454 [weakSelf, handleFunction, eventName, ignoreWhenAbilityIsTerminating](const QtOhos::CallbackInfo &cbInfo) {
1455 auto self = weakSelf.lock();
1456 if (Q_UNLIKELY(!self)) {
1457 qOhosPrintfWarning(
1458 "callback '%s' called for destroyed QOhosWindowProxy::JsScopeData, ignoring",
1459 eventName.c_str());
1460 return;
1461 }
1462
1463 if (self->isWindowClosing() && ignoreWhenAbilityIsTerminating) {
1464 qOhosPrintfError(
1465 "QOhosWindowProxy: Received callback for event '%s' during termination of the related QAbility.",
1466 eventName.c_str());
1467 return;
1468 }
1469
1470 if (Q_UNLIKELY(self->windowDestroyedFromSystem)) {
1471 qOhosPrintfError(
1472 "QOhosWindowProxy: Received callback for event '%s' after WINDOW_DESTROYED",
1473 eventName.c_str());
1474 return;
1475 }
1476
1477 ((*self).*handleFunction)(cbInfo);
1478 },
1479 {
1480 .optEventSourceAliveCheckFunc = {},
1481 .extraOnArg = {},
1482 .extraOffArg = {},
1483 .optOnCallExceptionHandler = [&](const Napi::Error &error) {
1484 constexpr std::uint32_t capabilityNotSupportedErrorCode = 801;
1485 constexpr std::uint32_t windowStateIsAbnormalErrorCode = 1300002;
1486
1487 const QSet<std::uint32_t> ignorableErrorCodes = {
1488 capabilityNotSupportedErrorCode,
1489 windowStateIsAbnormalErrorCode,
1490 };
1491
1492 auto errorCode = QtOhos::tryGetCodeFromJsBusinessError(error);
1493
1494 auto ignorableError =
1495 eventHandlerFlags.testFlag(EventHandlerFlagBits::allowEventHandlerRegistrationFailure)
1496 && errorCode.has_value()
1497 && ignorableErrorCodes.contains(errorCode.value());
1498
1499 if (!ignorableError)
1500 throw;
1501
1502 qOhosPrintfWarning(
1503 "%s: Ignored error %u while registering for window event '%s'",
1504 Q_FUNC_INFO, errorCode.value(), eventName.c_str());
1505 },
1506 });
1507}
1508
1509std::shared_ptr<void> QOhosWindowProxy::JsScopeData::registerSubWindowCloseHandler(
1510 QtOhos::JsState &, std::function<bool()> handler)
1511{
1512 auto weakSelf = QtOhos::makeWeakPtr(shared_from_this());
1513 return registerQOhosOnOffMethodsBasedEventHandler(
1514 jsWindowRef->jsObject(), "subWindowClose",
1515 [weakSelf, handler = std::move(handler)](const QtOhos::CallbackInfo &cbInfo) {
1516 bool deferClose = handler();
1517 auto self = weakSelf.lock();
1518 if (self && !deferClose)
1519 QtOhos::JsWindowsTracker::tagWindowAsClosing(self->jsWindowRef->jsObject(), "subWindowClose => false");
1520 return QNapi::Boolean::New(cbInfo.Env(), deferClose);
1521 });
1522}
1523
1524void QOhosWindowProxy::JsScopeData::handleWindowEventCallback(const QtOhos::CallbackInfo &cbInfo)
1525{
1526 auto eventType = cbInfo.getFirstArg<QNapi::Number>(Q_FUNC_INFO);
1527 // NOTE - All windowEvents should be handled by qt but currently
1528 // some are not exposed as a part of public api.
1529 WindowEvent event;
1530 try {
1531 event.type = cbInfo.jsState().mapOhosEnumFromJs<WindowEventType>(eventType);
1532 } catch (const Napi::Error &err) {
1533 qOhosPrintfError(
1534 "Error converting WindowEventType to known value: %s. Event will be ignored.", err.what());
1535 return;
1536 }
1537
1538 if (isWindowClosing() && event.type != WindowEventType::WINDOW_DESTROYED) {
1539 qOhosPrintfError(
1540 "Received WindowEvent for window when it's closing. WindowEventType: %d",
1541 event.type);
1542 return;
1543 }
1544
1545 onWindowEvent(cbInfo.jsState(), event);
1546}
1547
1548void QOhosWindowProxy::JsScopeData::handleWindowStatusCallback(const QtOhos::CallbackInfo &cbInfo)
1549{
1550 auto windowStatusType = cbInfo.getFirstArg<QNapi::Number>(Q_FUNC_INFO);
1551 if (windowCallbackReceiver != nullptr) {
1552 windowCallbackReceiver->onWindowStatusChange(
1553 WindowStatus {
1554 .type = cbInfo.jsState().mapOhosEnumFromJs<WindowStatusType>(windowStatusType),
1555 });
1556 }
1557}
1558
1559void QOhosWindowProxy::JsScopeData::handleWindowVisibilityCallback(const QtOhos::CallbackInfo &cbInfo)
1560{
1561 auto windowVisibility = cbInfo.getFirstArg<QNapi::Boolean>(Q_FUNC_INFO);
1562 if (windowCallbackReceiver != nullptr)
1563 windowCallbackReceiver->onWindowVisibilityChange(windowVisibility);
1564}
1565
1566void QOhosWindowProxy::JsScopeData::handleWindowTouchOutsideCallback(const QtOhos::CallbackInfo &)
1567{
1568 if (windowCallbackReceiver != nullptr)
1569 windowCallbackReceiver->onTouchOutside();
1570}
1571
1572void QOhosWindowProxy::JsScopeData::handleAvoidAreaChangeCallback(const QtOhos::CallbackInfo &cbInfo)
1573{
1574 if (windowCallbackReceiver != nullptr) {
1575 auto callbackArg = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1576 windowCallbackReceiver->onAvoidAreaChange(
1577 cbInfo.jsState().mapOhosEnumFromJs<AvoidAreaType>(callbackArg.get<QNapi::Number>("type")),
1578 mapAvoidAreaFromJs(callbackArg.get<QNapi::Object>("area")));
1579 }
1580}
1581
1582void QOhosWindowProxy::JsScopeData::handleWindowRectChangeCallback(const QtOhos::CallbackInfo &cbInfo)
1583{
1584 auto rectChangeOptionsObjectArg = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1585 auto rectChangeOptions = RectChangeOptions {
1586 .rect = ohosWindowRectToQRect(rectChangeOptionsObjectArg.get<QNapi::Object>("rect")),
1587 .reason = cbInfo.jsState().mapOhosEnumFromJs<RectChangeReason>(rectChangeOptionsObjectArg.get<QNapi::Number>("reason")),
1588 };
1589
1590 if (windowCallbackReceiver != nullptr)
1591 windowCallbackReceiver->onWindowRectChange(rectChangeOptions);
1592}
1593
1594void QOhosWindowProxy::JsScopeData::handleWindowRectChangeInGlobalDisplayCallback(const QtOhos::CallbackInfo &cbInfo)
1595{
1596 auto rectChangeOptionsObjectArg = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1597 auto rectChangeOptions = RectChangeOptions {
1598 .rect = ohosWindowRectToQRect(rectChangeOptionsObjectArg.get<QNapi::Object>("rect")),
1599 .reason = cbInfo.jsState().mapOhosEnumFromJs<RectChangeReason>(rectChangeOptionsObjectArg.get<QNapi::Number>("reason")),
1600 };
1601
1602 if (windowCallbackReceiver != nullptr)
1603 windowCallbackReceiver->onWindowRectChangeInGlobalDisplay(rectChangeOptions);
1604}
1605
1606void QOhosWindowProxy::JsScopeData::handleWindowDisplayIdChangeCallback(const QtOhos::CallbackInfo &cbInfo)
1607{
1608 auto displayIdNumber = cbInfo.getFirstArg<QNapi::Number>(Q_FUNC_INFO);
1609 auto displayId = QOhosDisplayInfo::JsDisplayId(displayIdNumber);
1610
1611 if (windowCallbackReceiver != nullptr)
1612 windowCallbackReceiver->onWindowDisplayIdChange(displayId);
1613}
1614
1615void QOhosWindowProxy::JsScopeData::onWindowEvent(QtOhos::JsState &, const WindowEvent &windowEvent)
1616{
1617 if (windowEvent.type == WindowEventType::WINDOW_DESTROYED) {
1618 QtOhos::JsWindowsTracker::tagWindowAsClosing(jsWindowRef->jsObject(), "WINDOW_DESTROYED");
1619 windowDestroyedFromSystem = true;
1620 }
1621
1622 if (windowCallbackReceiver != nullptr)
1623 windowCallbackReceiver->onWindowEvent(windowEvent);
1624}
1625
1626bool QOhosWindowProxy::JsScopeData::isWindowClosing() const
1627{
1628 return isWindowClosingFromSystem(jsWindowRef->jsObject(), windowProxyType, qAbilityPeer);
1629}
1630
1631void QOhosWindowProxy::JsScopeData::onMouseEventFromArkUi(const QArkUi::MouseEvent &event)
1632{
1633 if (nonClientAreaMouseEventConsumer == nullptr)
1634 return;
1635
1636 auto optAction = tryMapMouseEventActionToNonClientAreaEventType(event.action);
1637 if (!optAction.has_value())
1638 return;
1639
1640 auto optWindowProperties = QArkUi::tryGetWindowProperties(event.jsWindowId);
1641 if (!optWindowProperties.has_value()) {
1642 qOhosPrintfError(
1643 "%s: Failed to retrieve window properties for js window: %f. Ignoring event.",
1644 Q_FUNC_INFO, event.jsWindowId.value());
1645 return;
1646 }
1647
1648 const auto &windowProperties = optWindowProperties.value();
1649 if (!isPointInNonClientArea(event.displayPosition, windowProperties))
1650 return;
1651
1652 auto windowOrigin = windowProperties.windowRect.topLeft() + windowProperties.drawableRect.topLeft();
1653 NonClientAreaMouseEvent nonClientAreaMouseEvent = {
1654 .timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(event.actionTime),
1655 .action = optAction.value(),
1656 .button = tryMapMouseEventButtonToQt(event.button).value_or(Qt::NoButton),
1657 .displayPosition = event.displayPosition,
1658 .localPosition = event.displayPosition - windowOrigin,
1659 .globalPosition = event.globalPosition,
1660 };
1661
1662 nonClientAreaMouseEventConsumer(nonClientAreaMouseEvent);
1663}
1664
1665void QOhosWindowProxy::JsScopeData::onTouchEventFromArkUi(const QArkUi::TouchEvent &event)
1666{
1667 if (nonClientAreaTouchEventConsumer == nullptr)
1668 return;
1669
1670 auto optAction = tryMapTouchEventActionToNonClientAreaEventType(event.action);
1671 if (!optAction.has_value())
1672 return;
1673
1674 auto optWindowProperties = QArkUi::tryGetWindowProperties(event.jsWindowId);
1675 if (!optWindowProperties.has_value()) {
1676 qOhosPrintfError(
1677 "%s: Failed to retrieve window properties for js window: %f. Ignoring event.",
1678 Q_FUNC_INFO, event.jsWindowId.value());
1679 return;
1680 }
1681
1682 const auto &windowProperties = optWindowProperties.value();
1683 if (!isPointInNonClientArea(event.displayPosition, windowProperties))
1684 return;
1685
1686 auto windowOrigin = windowProperties.windowRect.topLeft() + windowProperties.drawableRect.topLeft();
1687 NonClientAreaTouchEvent nonClientAreaTouchEvent = {
1688 .id = event.fingerId,
1689 .timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(event.actionTime),
1690 .action = optAction.value(),
1691 .displayPosition = event.displayPosition,
1692 .localPosition = event.displayPosition - windowOrigin,
1693 .globalPosition = event.globalPosition,
1694 };
1695
1696 nonClientAreaTouchEventConsumer(nonClientAreaTouchEvent);
1697}
1698
1699QPixmap QOhosWindowProxy::snapshot() const
1700{
1701 return QtOhos::evalInJsThreadWithPromise<QPixmap>(
1702 [&](QtOhos::JsState &, QOhosTaskPromise<QPixmap> evalPromise) {
1703 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
1704 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("snapshot()")
1705 .onThen(
1706 [thenPromise = std::move(thenCatchPromises.first)](const QtOhos::CallbackInfo &cbInfo) {
1707 auto napiPixmap = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1708
1709 ::OH_PixelmapNative *pixelMapNativePtr;
1710 QArkUi::callArkUiOrFailOnErrorResult(
1711 Q_OHOS_NAMED_FUNC(::OH_PixelmapNative_ConvertPixelmapNativeFromNapi),
1712 cbInfo.Env(), napiPixmap, &pixelMapNativePtr);
1713 auto pixelMap = wrapOhosNativePixelMapPtr(pixelMapNativePtr);
1714
1715 thenPromise(
1716 QPixmap::fromImage(createQImageFromNativePixelMap(pixelMap.get())));
1717 })
1718 .onCatch(
1719 [catchPromise = std::move(thenCatchPromises.second)](const QtOhos::CallbackInfo &cbInfo) {
1720 QtOhos::logJsCallbackError(cbInfo, "Got error from snapshot()");
1721 catchPromise(QPixmap());
1722 });
1723 },
1724 Q_FUNC_INFO);
1725}
1726
1727QList<QRect> QOhosWindowProxy::getDisplayCutoutRects() const
1728{
1729 // The display cutout (e.g. camera hole) is not in getWindowAvoidArea(); read
1730 // it from the display. Returns the bounding rects in display coordinates.
1731 return QtOhos::evalInJsThreadWithPromise<QList<QRect>>(
1732 [&](QtOhos::JsState &jsState, QOhosTaskPromise<QList<QRect>> evalPromise) {
1733 if (m_jsScopeData->isWindowClosing()) {
1734 evalPromise(QList<QRect>());
1735 return;
1736 }
1737
1738 auto optWindowDisplay = qAndThen(
1739 getWindowPropertiesFromJsWindow(
1740 m_jsScopeData->jsWindowRef->jsObject()).displayId,
1741 [&](QOhosDisplayInfo::JsDisplayId id) {
1742 return QOhosDisplayInfo::tryGetDisplayById(jsState, id);
1743 });
1744 auto display = optWindowDisplay.has_value()
1745 ? optWindowDisplay.value()
1746 : jsState.eval<QNapi::Object>("@ohos.display.getDefaultDisplaySync()");
1747
1748 auto branches = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
1749 display
1750 .evalToPromiseOrRejectOnThrow("getCutoutInfo()")
1751 .onThen(
1752 [thenPromise = std::move(branches.first)](const QtOhos::CallbackInfo &cbInfo) {
1753 auto cutoutInfo = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
1754 auto cutoutRects = QNapi::getArrayElements<QList<QRect>, QNapi::Object>(
1755 cutoutInfo.get<QNapi::Array>("boundingRects"), [](QNapi::Object r) {
1756 return QRect(r.get<QNapi::Number>("left"),
1757 r.get<QNapi::Number>("top"),
1758 r.get<QNapi::Number>("width"),
1759 r.get<QNapi::Number>("height"));
1760 });
1761 thenPromise(cutoutRects);
1762 })
1763 .onCatch(
1764 [catchPromise = std::move(branches.second)](const QtOhos::CallbackInfo &cbInfo) {
1765 QtOhos::logJsCallbackError(cbInfo, "getCutoutInfo() failed");
1766 catchPromise(QList<QRect>());
1767 });
1768 },
1769 Q_FUNC_INFO);
1770}
1771
1772bool QOhosWindowProxy::startMoving()
1773{
1775 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
1776 if (m_jsScopeData->isWindowClosing()) {
1777 taskPromise();
1778 return;
1779 }
1780
1781 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("startMoving()")
1782 .onCatch(QtOhos::makeErrorLoggingJsCallback("startMoving()"))
1783 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1784 },
1785 Q_FUNC_INFO);
1786
1787 return true;
1788}
1789
1790void QOhosWindowProxy::enableDrag(bool enable)
1791{
1792 qCDebug(QtForOhos, "%s: %s", Q_FUNC_INFO, QtOhos::mapBoolToTrueFalseStr(enable));
1793
1794 if (qtIsMainWindow()) {
1795 qCWarning(QtForOhos(), "%s: enableDrag is not supported on main windows", Q_FUNC_INFO);
1796 return;
1797 }
1798
1800 [&](QtOhos::JsState &, QOhosTaskPromise<> taskPromise) {
1801 if (m_jsScopeData->isWindowClosing()) {
1802 taskPromise();
1803 return;
1804 }
1805 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow("enableDrag(*)", {enable})
1806 .onCatch(QtOhos::makeErrorLoggingJsCallback("enableDrag()"))
1807 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1808 },
1809 Q_FUNC_INFO);
1810}
1811
1812std::optional<bool> QOhosWindowProxy::isFocused() const
1813{
1814 return QtOhos::evalInJsThread(
1815 [&](QtOhos::JsState &) -> std::optional<bool> {
1816 if (m_jsScopeData->isWindowClosing())
1817 return std::nullopt;
1818
1819 return m_jsScopeData->jsWindowRef->eval<QNapi::Boolean>("isFocused()");
1820 },
1821 Q_FUNC_INFO);
1822}
1823
1824std::vector<QArkUi::JsWindowId> QOhosWindowProxy::queryWindowIdsByCoordinate(
1825 QOhosDisplayInfo::JsDisplayId displayId, const QPoint &queryLocation, std::uint32_t queryLimit)
1826{
1827 return QtOhos::evalInJsThreadWithPromise<std::vector<QArkUi::JsWindowId>>(
1828 [&](QtOhos::JsState &jsState, auto evalPromise) {
1829 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
1830 jsState.evalToPromiseOrRejectOnThrow("@ohos.window.getWindowsByCoordinate(*)", {
1831 displayId.value(),
1832 queryLimit,
1833 queryLocation.x(),
1834 queryLocation.y()
1835 })
1836 .onThen([thenPromise = std::move(thenCatchPromises.first)](const QtOhos::CallbackInfo &cbInfo) {
1837 auto windowsArray = cbInfo.getFirstArg<QNapi::Array>(Q_FUNC_INFO);
1838 thenPromise(
1839 QNapi::getArrayElements<std::vector<QArkUi::JsWindowId>, QNapi::Object>(
1840 windowsArray,
1841 [&](QNapi::Object jsWindow) {
1842 return getWindowPropertiesFromJsWindow(jsWindow).id;
1843 }));
1844 })
1845 .onCatch([catchPromise = std::move(thenCatchPromises.second)](const QtOhos::CallbackInfo &cbInfo) {
1846 QtOhos::logJsCallbackError(
1847 cbInfo, "got error from @ohos.window.getWindowsByCoordinate()");
1848 catchPromise({});
1849 });
1850 },
1851 Q_FUNC_INFO);
1852}
1853
1854std::vector<QArkUi::JsWindowId> QOhosWindowProxy::queryQtManagedWindowIdsByPredicate(
1855 const std::function<bool(QOhosJsState &, const QArkUi::JsWindowRef &)> &predicate)
1856{
1857 return QtOhos::evalInJsThread([&](QtOhos::JsState &jsState) {
1858 auto &jsWindowRegistry = jsState.getAttachedObjectWithLazyCreate<QOhosJsWindowRegistry>();
1859 return jsWindowRegistry.queryByPredicate(jsState, predicate);
1860 },
1861 Q_FUNC_INFO);
1862}
1863
1864void QOhosWindowProxy::moveWindowToGlobal(
1865 const QPoint &position, const MoveConfiguration &moveConfiguration)
1866{
1868 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
1869 if (m_jsScopeData->isWindowClosing()) {
1870 taskPromise();
1871 return;
1872 }
1873
1874 auto moveConfigurationObject = toNapiObject(jsState.env(), moveConfiguration);
1875 auto moveConfigurationObjectStr = QNapi::toJsonString(moveConfigurationObject);
1876 qOhosPrintfDebug(
1877 "%s: %d,%d,%s",
1878 Q_FUNC_INFO, position.x(), position.y(),
1879 moveConfigurationObjectStr.c_str());
1880
1881 m_jsScopeData->jsWindowRef->evalToPromiseOrRejectOnThrow(
1882 "moveWindowToGlobal(*)", {position.x(), position.y(), moveConfigurationObject})
1883 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1884 },
1885 Q_FUNC_INFO);
1886}
1887
1888std::optional<QOhosDisplayInfo::JsDisplayId> QOhosWindowProxy::tryGetMainWindowJsDisplayId() const
1889{
1890 return qtIsMainWindow()
1891 ? getWindowProperties().displayId
1892 : QtOhos::evalInJsThread(
1893 [&](QtOhos::JsState &) {
1894 auto qUiAbilityPeer
1895 = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(m_jsScopeData->qAbilityPeer);
1896 return qUiAbilityPeer
1897 ? getWindowPropertiesFromJsWindow(qUiAbilityPeer->window()).displayId
1898 : std::nullopt;
1899 },
1900 Q_FUNC_INFO);
1901}
1902
1903void QOhosWindowProxy::shiftAppWindowFocus(QOhosWindowProxy &targetProxy)
1904{
1906 [&](QtOhos::JsState &jsState, QOhosTaskPromise<> taskPromise) {
1907 if (m_jsScopeData->isWindowClosing() || targetProxy.m_jsScopeData->isWindowClosing()) {
1908 taskPromise();
1909 return;
1910 }
1911
1912 auto srcWindowId = getWindowPropertiesFromJsWindow(m_jsScopeData->jsWindowRef->jsObject()).id;
1913 auto targetWindowId = getWindowPropertiesFromJsWindow(targetProxy.m_jsScopeData->jsWindowRef->jsObject()).id;
1914 jsState.evalToPromiseOrRejectOnThrow(
1915 "@ohos.window.shiftAppWindowFocus(*)", {srcWindowId.value(), targetWindowId.value()})
1916 .onCatch(QtOhos::makeErrorLoggingJsCallback("@ohos.window.shiftAppWindowFocus()"))
1917 .onFinally(std::move(taskPromise).makeChained(Q_FUNC_INFO));
1918 },
1919 Q_FUNC_INFO);
1920}
1921
1922std::shared_ptr<QOhosWindowProxy> QOhosWindowProxy::create(QtOhos::JsState &jsState, QOhosWindowProxyData data)
1923{
1924 auto window = std::shared_ptr<QOhosWindowProxy>(new QOhosWindowProxy(std::move(data)));
1925 auto &jsWindowRegistry = jsState.getAttachedObjectWithLazyCreate<QOhosJsWindowRegistry>();
1926 return QtOhos::makeSharedPtrWithAttachedExtraData(
1927 window,
1928 jsWindowRegistry.registerJsWindow(window->m_jsScopeData->jsWindowRef));
1929}
1930
1931QT_END_NAMESPACE
bool isWindowPcModeEnabled() const
static QOhosSettings & instance()
void setWindowRectAutoSave(bool enabed)
void setSubWindowModalEnabled(ModalityType ModalityType)
QtOhosQpa::enums::ohos::window::MaximizePresentation MaximizePresentation
void setWindowKeepScreenOn(bool keepScreenOn)
void moveWindowToGlobal(const QPoint &position, const MoveConfiguration &moveConfiguration)
static std::shared_ptr< QOhosWindowProxy > createFloatWindow(const FloatWindowCreateInfo &createInfo)
void setWindowMask(const WindowMask &windowMask, const std::optional< QSize > &ohosMaskSizeOverride={})
QArkUi::WindowProperties getWindowProperties() const
void moveWindowToGlobalOrGlobalDisplay(const QPoint &position, std::optional< QOhosDisplayInfo::JsDisplayId > optDisplayId)
void setWindowPrivacyMode(bool privacyMode)
QtOhosQpa::enums::ohos::window::WindowEventType WindowEventType
AvoidArea getWindowAvoidArea(AvoidAreaType type) const
void setWindowLayoutFullScreen(bool isLayoutFullScreen)
std::optional< QOhosDisplayInfo::JsDisplayId > tryGetMainWindowJsDisplayId() const
void enableDrag(bool enable)
void setWindowCornerRadius(double radius)
QOhosWindowProxyExistingMainWindowCreateInfo ExistingMainWindowCreateInfo
void setSupportedWindowModes(const std::set< SupportWindowMode > &supportedWindowModes)
void setWindowSystemBarEnable(const QStringList &names)
std::shared_ptr< QOhosWindowProxy > createSubWindow(const SubWindowCreateInfo &createInfo)
QPixmap snapshot() const
static std::shared_ptr< QOhosWindowProxy > createForExistingMainWindow(const ExistingMainWindowCreateInfo &createInfo)
void setWindowTouchable(bool touchable)
void setTitle(const QString &title)
void setFollowParentMultiScreenPolicy(bool enabled)
void setWindowTopmost(bool topmost)
std::shared_ptr< QXComponentNode > nodeXComponent() const
void setWindowCallbackReceiver(std::unique_ptr< WindowCallbacks > receiver)
void setSubWindowCloseHandler(std::function< void()> handler, bool handlerReturnValue)
std::optional< bool > isFocused() const
QtOhosQpa::enums::ohos::window::ModalityType ModalityType
void setWindowLimits(const QSize &minSize, const QSize &maxSize)
bool isWindowRectAutoSave() const
void setWindowDecorVisible(bool visible)
void showWindow(const ShowWindowOptions &options=ShowWindowOptions())
void shiftAppWindowFocus(QOhosWindowProxy &targetProxy)
QOhosWindowProxySubWindowCreateInfo SubWindowCreateInfo
void setNonClientAreaMouseWindowCallbackReceiver(QObject *contextObject, QOhosConsumer< std::vector< NonClientAreaMouseEvent > > mouseEventBatchConsumer)
QtOhosQpa::enums::ohos::window::AvoidAreaType AvoidAreaType
void setCustomCursor(const QImage &customCursorImage, const QPoint &hotSpot)
void setWindowTitleMoveEnabled(bool enabled)
void setWindowFocusable(bool focusable)
void setWindowBackgroundColor(const QColor &color)
void setSize(const QSize &size)
QOhosWindowProxyMainWindowCreateInfo MainWindowCreateInfo
void setWindowShadowRadius(double radius)
bool qtIsMainWindow() const
QOhosWindowProxyFloatWindowCreateInfo FloatWindowCreateInfo
void setWindowTitleButtonVisible(bool maximizeVisible, bool minimizeVisible, bool closeVisible)
void setPointerStyleSync(const QCursor &cursor)
QList< QRect > getDisplayCutoutRects() const
WindowLimits getWindowLimits() const
std::string qAbilityInstanceId() const
void maximize(MaximizePresentation maximizePresentation)
void setNonClientAreaTouchWindowCallbackReceiver(QObject *contextObject, QOhosConsumer< std::vector< NonClientAreaTouchEvent > > touchEventBatchConsumer)
static std::shared_ptr< QOhosWindowProxy > createMainWindow(const MainWindowCreateInfo &createInfo)
JsState & jsState() const
static std::shared_ptr< QUiAbilityPeer > tryCastFromQAbilityPeerOrNull(std::shared_ptr< QAbilityPeer > qAbilityPeer)
std::optional< WindowProperties > tryGetWindowProperties(JsWindowId jsWindowId)
Definition window.cpp:28
Combined button and popup list for selecting options.
QRect ohosWindowRectToQRect(const QNapi::Object &ohosWindowRect)
bool isPointInNonClientArea(const QPoint &point, const QArkUi::WindowProperties &windowProperties)
std::optional< QEvent::Type > tryMapMouseEventActionToNonClientAreaEventType(::Input_MouseEventAction action)
std::optional< double > getOptionalNumberPropAsOptionalDouble(const QNapi::Object &object, const std::string &propertyName)
QOhosPointerStyle convertToOhosCursor(Qt::CursorShape shape)
QOhosWindowProxy::AvoidArea mapAvoidAreaFromJs(const QNapi::Object &avoidAreaObject)
std::string mapEnumsToLogString(const EnumsContainer &enums)
std::optional< Qt::MouseButton > tryMapMouseEventButtonToQt(::Input_MouseEventButton button)
bool isWindowClosingFromSystem(QNapi::Object jsWindow, WindowProxyType windowType, std::shared_ptr< QtOhos::QAbilityPeer > abilityPeer)
QtOhosQpa::enums::ohos::multimodalInput::pointer::PointerStyle QOhosPointerStyle
QArkUi::WindowProperties getWindowPropertiesFromJsWindow(QNapi::Object jsWindow)
std::function< void(Args...)> makeQtThreadWindowCallbackDelegate(std::function< void(Args...)> QOhosWindowProxy::WindowCallbacks::*memberPtr, std::shared_ptr< QOhosWindowProxy::WindowCallbacks > qtWindowCallbacks)
std::function< void(T)> makeCompressingQtThreadWindowCallbackDelegate(std::function< void(T)> QOhosWindowProxy::WindowCallbacks::*memberPtr, std::shared_ptr< QOhosWindowProxy::WindowCallbacks > qtWindowCallbacks)
QNapi::Object toNapiObject(napi_env env, const QOhosWindowProxy::MoveConfiguration &moveConfiguration)
std::optional< QEvent::Type > tryMapTouchEventActionToNonClientAreaEventType(::Input_TouchEventAction action)
std::string const char * mapBoolToTrueFalseStr(bool value)
bool runIgnoringJsBusinessError(QOhosJsState &, std::uint32_t suppressedErrorCode, const char *callerContextName, const std::function< void()> &action)
void runInJsThreadAndWait(const std::function< void(JsState &)> &task, std::string callerContextName={})
void invokeInJsThreadAndWaitForContinue(std::function< void(JsState &, QOhosTaskPromise<>)> &&task, std::string callerContextName={})
QXComponent< QXComponentType::Node > QXComponentNode
Definition qxcomponent.h:44
std::int32_t fingerId
Definition input.h:54
bool isDisplayMainOrExtended() const