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
qohosfloatingwindow.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 <qohosfloatingwindow.h>
5
6#include <QtCore/private/qohoslogger_p.h>
7#include "QtGui/private/qguiapplication_p.h"
8#include <qohosdeviceinfo_p.h>
9#include <qohosinputcontext.h>
10#include <qohosinputmethodeventhandler.h>
11#include <qohosjsmain.h>
12#include <qohosplatformbackingstore.h>
13#include <qohosplatformintegration.h>
14#include <qohosplatformwindow.h>
15#include <qohosruntimedevicetypeandmode.h>
16#include <qohossettings.h>
17#include <render/qohossurface.h>
18#include <render/qohoswindowproxy.h>
19#include <render/qwindowproxyregistry.h>
20#include <utility>
21
23
24namespace {
25
27 QWindow *window, QOhosWindowProxy::RectChangeOptions rectChangeOptions)
28{
30 && window->geometry() != rectChangeOptions.rect
31 && rectChangeOptions.reason == QOhosWindowProxy::RectChangeReason::UNDEFINED;
32}
33
34}
35
37: QOhosPlatformWindow(window)
38{
39}
40
42
43void QOhosFloatingWindow::setGeometry(const QRect &rect)
44{
45 auto *view = ownedViewOrNull();
46 bool geometryControlledBySystem =
47 !QOhosSettings::isWindowPcModeEnabled()
48 && view != nullptr
49 && view->viewType() == QOhosView::ViewType::MainWindow;
50
51 if (geometryControlledBySystem) {
52 setWindowGeometryFromOhos(windowGeometry());
53 return;
54 }
55
56 auto oldWindowFrameGeometry = windowFrameGeometry();
57
58 QOhosPlatformWindow::setGeometry(rect);
59
60 if (view != nullptr) {
61 auto frameGeometry = windowFrameGeometry();
62 if (oldWindowFrameGeometry.topLeft() != frameGeometry.topLeft())
63 view->setPosition(frameGeometry.topLeft());
64 if (oldWindowFrameGeometry.size() != frameGeometry.size())
65 view->setSize(frameGeometry.size());
66 }
67}
68
70{
71 if (!visible) {
72 // Avoid ´minimize´ animation when the window is being closed or destroyed
73 if (!QOhosPlatformWindow::isWindowBeingClosedOrDestroyed(window()))
74 m_view->hide();
75 } else {
76 m_view->showImmediate();
77 }
78}
79
81{
82 auto windowObjectName = window()->objectName();
83
84 qOhosPrintfDebug(
85 "%s: winId called for window named: \"%s\"",
86 Q_FUNC_INFO,
87 windowObjectName.toStdString().c_str());
88
89 return reinterpret_cast<WId>(m_view->viewWindowId());
90}
91
93{
94 auto *view = ownedViewOrNull();
95 if (view != nullptr) {
96 view->raise();
97 window()->requestUpdate();
98 }
99}
100
102{
103 auto *view = ownedViewOrNull();
104 if (view != nullptr) {
105 view->lower();
106 window()->requestUpdate();
107 }
108}
109
111{
112 auto *view = ownedViewOrNull();
113 return view != nullptr ? view->surfaceOrNull() : nullptr;
114}
115
117{
118 return m_view.get();
119}
120
122{
124 m_view = QOhosView::createForWindow(this, propertiesProvider());
125
126 auto *qWindow = window();
127
128 // HACK
129 // There is no functionality to fetch the initial window status from ohos
130 // We can only listen for the changes - we need to assume sane default for tablets
131 // so assign the default most-likely status of the tablet here.
132 if (m_view->viewType() == QOhosView::ViewType::MainWindow
133 && !QOhosSettings::isWindowPcModeEnabled()
134 && !m_lastWindowStatusType.hasValue()) {
135 m_lastWindowStatusType = QOhosWindowProxy::WindowStatusType::FULL_SCREEN;
136 }
137
138 QObject::connect(
139 m_view.get(), &QOhosView::externalContentInteractionDetected,
140 qWindow, &QOhosPlatformWindow::closeAllActivePopups);
141
142 QObject::connect(
143 m_view.get(), &QOhosView::windowEvent,
144 qWindow,
145 [this](QOhosWindowProxy::WindowEvent evt) { handleWindowEvent(evt); });
146
147 QObject::connect(
148 m_view.get(), &QOhosView::windowStatusChange,
149 qWindow,
150 [this](QOhosWindowProxy::WindowStatus evt) { handleWindowStatusChange(evt); });
151
152 QObject::connect(
153 m_view.get(), &QOhosView::windowVisibilityChange,
154 qWindow,
155 [this](bool visible) { handleWindowVisibilityChange(visible); });
156
157 bool monitorAvoidAreaChange =
158 !(isHandheldDeviceType() && m_view->viewType() == QOhosView::ViewType::SubWindow);
159
160 if (monitorAvoidAreaChange) {
161 QObject::connect(
162 m_view.get(), &QOhosView::avoidAreaChanged,
163 qWindow,
164 [this](QOhosWindowProxy::AvoidAreaType avoidAreaType,
165 const QOhosWindowProxy::AvoidArea &systemAvoidArea) {
166 handleAvoidAreaChanged(avoidAreaType, systemAvoidArea);
167 });
168 }
169
170 QObject::connect(
171 m_view.get(), &QOhosView::windowRectChanged,
172 qWindow,
173 [this](const QOhosWindowProxy::RectChangeOptions &rectChangeOptions) {
174 handleWindowRectChanged(rectChangeOptions);
175 });
176
177 QObject::connect(qWindow, &QWindow::modalityChanged, m_view.get(), &QOhosView::setModality);
178
179 QObject::connect(
180 m_view.get(), &QOhosView::surfaceStatusChanged,
181 qWindow,
182 [this](const QOhosOptional<QSize> &optSurfaceSize) { handleSurfaceStatusChanged(optSurfaceSize); });
183
184 QObject::connect(qWindow, &QWindow::windowTitleChanged, m_view.get(), &QOhosView::setTitle);
185
186 QObject::connect(
187 m_view.get(), &QOhosView::windowDisplayIdChanged,
188 qWindow,
189 [this](QOhosDisplayInfo::JsDisplayId displayId) { handleWindowDisplayIdChanged(displayId); });
190}
191
192void QOhosFloatingWindow::updateWindowGeometryFromView(QOhosView &view)
193{
194 auto viewGeometry = view.viewGeometry();
195 qCDebug(QtForOhos) << "View Geometry: " << viewGeometry.frameGeometry << viewGeometry.geometry;
196
197 if (view.viewType() != QOhosView::ViewType::EmbeddedWindow)
198 setDisplayIdFromOhos(viewGeometry.displayId);
199
200 if (viewGeometry.geometry.size().isEmpty()) {
201 qCCritical(QtForOhos, "%s: Failed as the received geometry is invalid", Q_FUNC_INFO);
202 return;
203 }
204
205 bool shouldSkipMarginCalculation =
206 view.viewType() == QOhosView::ViewType::MainWindow
207 && queryQOhosRuntimeDeviceAndMode() == QOhosRuntimeDeviceTypeAndMode::HandheldDeviceFullScreen;
208
209 QMargins margins =
210 shouldSkipMarginCalculation
211 ? QMargins()
212 : [&]() {
213 QMargins margins;
214 margins.setTop(qAbs(viewGeometry.frameGeometry.top() - viewGeometry.geometry.top()));
215 margins.setLeft(qAbs(viewGeometry.frameGeometry.left() - viewGeometry.geometry.left()));
216 margins.setRight(viewGeometry.frameGeometry.right() - viewGeometry.geometry.right());
217 margins.setBottom(viewGeometry.frameGeometry.bottom() - viewGeometry.geometry.bottom());
218 return margins;
219 }();
220
221 const auto oldOhosWindowFrameMargins = frameMargins();
222 bool shouldResizeWindowDueToInconsistentMargins =
223 shouldSkipMarginCalculation
224 && oldOhosWindowFrameMargins != margins;
225
226 setWindowMarginsFromOhos(margins);
227
228 if (shouldResizeWindowDueToInconsistentMargins) {
229 const auto newGeometry = viewGeometry.frameGeometry - oldOhosWindowFrameMargins;
230 setGeometry(newGeometry);
231 return;
232 }
233
234 if (shouldSkipMarginCalculation) {
235 auto targetGeometry = view.isSubWindowCoveringFullScreen()
236 ? viewGeometry.frameGeometry
237 : viewGeometry.frameGeometry
238 .marginsRemoved(view.avoidAreaMargins(QOhosWindowProxy::AvoidAreaType::TYPE_SYSTEM))
239 .marginsRemoved(view.avoidAreaMargins(QOhosWindowProxy::AvoidAreaType::TYPE_NAVIGATION_INDICATOR));
240 setWindowGeometryFromOhos(targetGeometry);
241 return;
242 }
243
244 // WORKAROUND
245 // This is temporary fix, that only regards state when "free windows mode" is on and window
246 // is maximized. System returns invalid geometry and system bottom bar covers application
247 // contents.
248 // TODO: remove when system returns correct geometry in this state.
249 bool maximized = windowStates().testFlag(Qt::WindowState::WindowMaximized);
251 auto targetGeometry = viewGeometry.geometry.marginsRemoved(
252 view.avoidAreaMargins(QOhosWindowProxy::AvoidAreaType::TYPE_NAVIGATION_INDICATOR));
253 setWindowGeometryFromOhos(targetGeometry);
254 return;
255 }
256
257 if (windowFrameGeometry() != viewGeometry.frameGeometry)
258 setWindowGeometryFromOhos(viewGeometry.geometry);
259}
260
261void QOhosFloatingWindow::updateWindowGeometryFromSurface()
262{
263 if (!m_lastRectChangeOptions.hasValue())
264 return;
265
266 auto drawableRect = m_lastRectChangeOptions.value().rect.marginsRemoved(frameMargins());
267 setWindowGeometryFromOhos(
268 QRect(
269 drawableRect.topLeft(),
270 m_optLastSurfaceSize.valueOr(drawableRect.size())));
271}
272
273void QOhosFloatingWindow::restoreWindowCurrentCursorIfNeeded()
274{
275 auto *view = ownedViewOrNull();
276 if (view != nullptr && m_cursor.hasValue())
277 view->setCursor(m_cursor.value());
278}
279
281 Qt::WindowFlags previousWindowFlags, Qt::WindowFlags currentWindowFlags)
282{
283 auto *view = ownedViewOrNull();
284 if (view == nullptr)
285 return;
286
287 view->handleWindowFlagsChange(previousWindowFlags, currentWindowFlags);
288}
289
291 Qt::WindowStates oldWindowState, Qt::WindowStates currentWindowState)
292{
293 auto *view = ownedViewOrNull();
294 if (view == nullptr)
295 return;
296
297 view->handleWindowStateChange(oldWindowState, currentWindowState);
298}
299
300void QOhosFloatingWindow::internalHijackSystemFocusAsPopup()
301{
302 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::PopupFocusReason);
303}
304
305void QOhosFloatingWindow::focusHijackingPopupHidden()
306{
307 auto systemFocusedWindows = QWindowProxyRegistry::instance().queryWindowsWithSystemWindowAndFocus();
308 QWindowSystemInterface::handleFocusWindowChanged(
309 !systemFocusedWindows.empty() ? systemFocusedWindows.front() : nullptr,
310 Qt::ActiveWindowFocusReason);
311}
312
313void QOhosFloatingWindow::setMask(const QRegion &region)
314{
315 m_windowMask = region;
316
317 auto *view = ownedViewOrNull();
318 if (view == nullptr)
319 return;
320
321 view->setWindowMask(QOhosWindowProxy::WindowMask{region});
322}
323
325{
326 auto *view = ownedViewOrNull();
327 if (view != nullptr)
328 return view->startMoving();
329 return false;
330}
331
333{
334 if (windowFlags().testFlag(Qt::WindowDoesNotAcceptFocus) && window()->type() == Qt::Popup) {
335 internalHijackSystemFocusAsPopup();
336 return;
337 }
338
340}
341
342void QOhosFloatingWindow::handleWindowEvent(QOhosWindowProxy::WindowEvent evt)
343{
344 auto *qWindow = window();
345 bool windowAcceptsFocusAndInput = checkWindowAcceptsFocus() && checkWindowAcceptsInput();
346 Qt::WindowStates windowStatesToSet = windowStates();
347 bool windowActive = true;
348 auto previousWindowEventType = std::exchange(m_lastWindowEventType, makeQOhosOptional(evt.type));
349
350 switch (evt.type) {
351 case QOhosWindowProxy::WindowEventType::WINDOW_ACTIVE:
352 {
353 QWindow *modalWindow = nullptr;
354 if (QGuiApplicationPrivate::instance()->isWindowBlocked(qWindow, &modalWindow)
355 && qWindow != modalWindow) {
356 modalWindow->requestActivate();
357 return;
358 }
359
360 // FIXME - restore cursor set by the User every time window is activated.
361 // Perform it because Ohos overrides it with default "standard arrow" cursor (when
362 // window is deactivated) and does not bring selected cursor back (when window is
363 // activated again). This bahaviour is Ohos platform issue.
364 restoreWindowCurrentCursorIfNeeded();
365 if (windowAcceptsFocusAndInput) {
366 QWindowSystemInterface::handleFocusWindowChanged(qWindow);
368 }
369 break;
370 }
371 case QOhosWindowProxy::WindowEventType::WINDOW_INACTIVE:
372 windowActive = false;
373 if (!windowAcceptsFocusAndInput)
374 break;
375 if (QGuiApplicationPrivate::focus_window == qWindow
376 || (QGuiApplicationPrivate::focus_window != nullptr
377 && QOhosPlatformWindow::platformWindowFlagsForQWindow(
378 QGuiApplicationPrivate::focus_window).testFlag(Qt::WindowDoesNotAcceptFocus))) {
379 QWindowSystemInterface::handleFocusWindowChanged(nullptr, Qt::ActiveWindowFocusReason);
380 }
382 break;
383 case QOhosWindowProxy::WindowEventType::WINDOW_HIDDEN:
384 if (!checkWindowAcceptsFocus() && QGuiApplicationPrivate::focus_window == qWindow)
385 focusHijackingPopupHidden();
386 clearExposed();
387 windowActive = false;
388 break;
389 case QOhosWindowProxy::WindowEventType::WINDOW_SHOWN:
390 // HACK
391 // This is a fix for the windows hidden using `QWidget::hide()`.
392 // When a window is hidden and then bring back from the system Dock
393 // Qt side keeps wrong (hidden) state. This causes different issues due to
394 // improper state. To fix it - change visibility state based on the system
395 // window state.
396 if (previousWindowEventType == QOhosWindowProxy::WindowEventType::WINDOW_HIDDEN && !qWindow->isVisible())
397 qWindow->setVisible(true);
399 updateWindowGeometryFromView(*m_view);
400 break;
401 case QOhosWindowProxy::WindowEventType::WINDOW_DESTROYED:
402 windowActive = false;
404 return;
405 }
406
407 windowStatesToSet.setFlag(Qt::WindowState::WindowActive, windowActive && windowAcceptsFocusAndInput);
408 setWindowStateFromOhos(windowStatesToSet);
409}
410
411void QOhosFloatingWindow::handleWindowStatusChange(QOhosWindowProxy::WindowStatus evt)
412{
413 auto *qWindow = window();
414 qCDebug(
415 QtForOhos,
416 "Window status changed, window: %p(%s) status: %d",
417 qWindow,
418 qPrintable(qWindow->objectName()),
419 evt.type);
420 Qt::WindowStates windowStatesToSet = windowStates();
421 Qt::WindowState flagToSet;
422 m_lastWindowStatusType = evt.type;
423
424 switch (evt.type) {
425 case QOhosWindowProxy::WindowStatusType::FULL_SCREEN:
426 flagToSet = Qt::WindowState::WindowFullScreen;
427 break;
428 case QOhosWindowProxy::WindowStatusType::MAXIMIZE:
429 flagToSet = Qt::WindowState::WindowMaximized;
430 break;
431 case QOhosWindowProxy::WindowStatusType::MINIMIZE:
432 flagToSet = Qt::WindowState::WindowMinimized;
433 break;
434 case QOhosWindowProxy::WindowStatusType::UNDEFINED:
435 case QOhosWindowProxy::WindowStatusType::FLOATING:
436 Q_FALLTHROUGH();
437 case QOhosWindowProxy::WindowStatusType::SPLIT_SCREEN:
438 flagToSet = Qt::WindowState::WindowNoState;
439 break;
440 }
441
442 // NOTE - Qt::WindowFullScreen and Qt::WindowMaximized represent the exact same
443 // OHOS Window state.
444 if (flagToSet == Qt::WindowState::WindowFullScreen
445 || flagToSet == Qt::WindowState::WindowMaximized) {
446 windowStatesToSet = m_view->isFullscreenImmersiveModeEnabled()
447 ? Qt::WindowState::WindowFullScreen
448 : Qt::WindowState::WindowMaximized;
449 } else {
450 static constexpr Qt::WindowState mutuallyExclusiveFlags[] = {
451 Qt::WindowState::WindowFullScreen,
452 Qt::WindowState::WindowMaximized,
453 Qt::WindowState::WindowMinimized,
454 Qt::WindowState::WindowNoState,
455 };
456
457 for (const auto exclusiveState : mutuallyExclusiveFlags)
458 windowStatesToSet.setFlag(exclusiveState, flagToSet == exclusiveState);
459 }
460
461 setWindowStateFromOhos(windowStatesToSet);
462
463 // HACK
464 // There is no functionality in OHOS to fetch the window status that would allow checking
465 // the value while processing the WINDOW_SHOWN window event type. Therefore, the window
466 // geometry view needs to be recalculated with any change.
467 updateWindowGeometryFromView(*m_view);
468}
469
470void QOhosFloatingWindow::handleWindowVisibilityChange(bool visible)
471{
472 auto *qWindow = window();
473 qCDebug(
474 QtForOhos,
475 "Window %p(%s) visibility changed: %s",
476 qWindow,
477 qPrintable(qWindow->objectName()),
478 visible ? "true" : "false");
479 if (visible && m_windowMask.hasValue())
480 m_view->setWindowMask(QOhosWindowProxy::WindowMask{m_windowMask.value()});
481
482 if (visible)
484 else
485 clearExposed();
486}
487
488void QOhosFloatingWindow::handleAvoidAreaChanged(
489 QOhosWindowProxy::AvoidAreaType avoidAreaType,
490 const QOhosWindowProxy::AvoidArea &systemAvoidArea)
491{
492 const auto &cached = m_avoidAreaCache[avoidAreaType];
493
494 bool actuallyChanged =
495 cached.visible != systemAvoidArea.visible
496 || cached.leftRect != systemAvoidArea.leftRect
497 || cached.rightRect != systemAvoidArea.rightRect
498 || cached.bottomRect != systemAvoidArea.bottomRect
499 || cached.topRect != systemAvoidArea.topRect;
500
501 if (actuallyChanged) {
502 m_avoidAreaCache[avoidAreaType] = systemAvoidArea;
503 qCDebug(QtForOhos) << "Avoid area changed:"
504 << static_cast<int>(avoidAreaType)
505 << "visible:" << systemAvoidArea.visible
506 << "top:" << systemAvoidArea.topRect
507 << "left:" << systemAvoidArea.leftRect
508 << "right:" << systemAvoidArea.rightRect
509 << "bottom:" << systemAvoidArea.bottomRect;
510 updateWindowGeometryFromView(*m_view);
511 }
512}
513
514void QOhosFloatingWindow::handleSurfaceStatusChanged(const QOhosOptional<QSize> &optSurfaceSize)
515{
516 m_optLastSurfaceSize = optSurfaceSize;
517 bool hasSurface = m_view->surfaceOrNull() != nullptr;
518 if (m_view->viewType() == QOhosView::ViewType::EmbeddedWindow) {
519 if (hasSurface)
521 else
522 clearExposed();
523 }
524
525 updateWindowGeometryFromSurface();
526}
527
528void QOhosFloatingWindow::handleWindowDisplayIdChanged(QOhosDisplayInfo::JsDisplayId displayId)
529{
530 setDisplayIdFromOhos(makeQOhosOptional(displayId));
531}
532
533void QOhosFloatingWindow::handleWindowRectChanged(
534 const QOhosWindowProxy::RectChangeOptions &rectChangeOptions)
535{
536 bool shouldUpdateWindowGeometryFromView = true;
537 bool needsCloseAllActivePopups = false;
538 m_lastRectChangeOptions = rectChangeOptions;
539
540 qCDebug(QtForOhos)
541 << "windowRectChanged window:" << window()
542 << "rect:" << rectChangeOptions.rect
543 << "reason:" << static_cast<int>(rectChangeOptions.reason);
544
545 switch (rectChangeOptions.reason) {
546 case QOhosWindowProxy::RectChangeReason::UNDEFINED:
547 case QOhosWindowProxy::RectChangeReason::MAXIMIZE:
548 break;
549 case QOhosWindowProxy::RectChangeReason::DRAG_START:
550 shouldUpdateWindowGeometryFromView = false;
551 needsCloseAllActivePopups = true;
552 break;
553 case QOhosWindowProxy::RectChangeReason::RECOVER:
554 case QOhosWindowProxy::RectChangeReason::MOVE:
555 case QOhosWindowProxy::RectChangeReason::DRAG:
556 case QOhosWindowProxy::RectChangeReason::DRAG_END:
557 shouldUpdateWindowGeometryFromView = false;
558 break;
559 }
560
561 if (shouldUpdateWindowGeometryFromView)
562 updateWindowGeometryFromView(*m_view);
563 else
564 updateWindowGeometryFromSurface();
565
566 if (isWindowRotatedByTabletScreenRotation(window(), rectChangeOptions))
567 needsCloseAllActivePopups = true;
568
569 if (needsCloseAllActivePopups)
571}
572
573QT_END_NAMESPACE
QOhosFloatingWindow(QWindow *window)
~QOhosFloatingWindow() override
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
bool startSystemMove() override
Reimplement this method to start a system move operation if the system supports it and return true to...
QOhosView * ownedViewOrNull() const override
void onWindowStateChanged(Qt::WindowStates oldWindowState, Qt::WindowStates currentWindowState) override
WId winId() const override
Reimplement in subclasses to return a handle to the native window.
void initialize() override
Called as part of QWindow::create(), after constructing the window.
void onWindowFlagsChanged(Qt::WindowFlags previousWindowFlags, Qt::WindowFlags currentWindowFlags) override
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
QOhosSurface * ownedSurfaceOrNull() const override
void setMask(const QRegion &region) override
Reimplement to be able to let Qt set the mask of a window.
void lower() override
Reimplement to be able to let Qt lower windows to the bottom of the desktop.
void raise() override
Reimplement to be able to let Qt raise windows to the top of the desktop.
void setDisplayIdFromOhos(QOhosOptional< QOhosDisplayInfo::JsDisplayId > displayId)
void initialize() override
Called as part of QWindow::create(), after constructing the window.
void notifyInputSystemsWindowActiveStatusChanged(bool active)
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
QOhosSurface * surfaceOrNull() const
void setWindowMask(const QOhosWindowProxy::WindowMask &windowMask)
void lower()
ViewGeometry viewGeometry() const
bool startMoving()
bool isSubWindowCoveringFullScreen() const
void raise()
static QWindowProxyRegistry & instance()
bool isWindowPcModeEnabled()
Combined button and popup list for selecting options.
bool isWindowRotatedByTabletScreenRotation(QWindow *window, QOhosWindowProxy::RectChangeOptions rectChangeOptions)
QOhosRuntimeDeviceTypeAndMode queryQOhosRuntimeDeviceAndMode()