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 <QtGui/private/qwindow_p.h>
9#include <qohosdeviceinfo_p.h>
10#include <qohosinputcontext.h>
11#include <qohosinputmethodeventhandler.h>
12#include <qohosjsmain.h>
13#include <qohosplatformbackingstore.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 && rectChangeOptions.rect.isValid()
31 && window->geometry() != rectChangeOptions.rect
32 && rectChangeOptions.reason == QOhosWindowProxy::RectChangeReason::UNDEFINED;
33}
34
35}
36
38: QOhosPlatformWindow(window)
39{
40}
41
43
44void QOhosFloatingWindow::setGeometry(const QRect &rect)
45{
46 auto *view = ownedViewOrNull();
47 bool geometryControlledBySystem =
48 !QOhosSettings::instance().isWindowPcModeEnabled()
49 && view != nullptr
50 && view->viewType() == QOhosView::ViewType::MainWindow;
51
52 if (geometryControlledBySystem) {
53 setWindowGeometryFromOhos(windowGeometry());
54 return;
55 }
56
57 auto *currentScreen = QOhosPlatformWindow::screen();
58 QOhosPlatformWindow::setGeometry(rect);
59
60 auto *targetScreen = QOhosPlatformWindow::screenForGeometry(rect.marginsAdded(frameMargins()));
61 if (targetScreen != nullptr && targetScreen != currentScreen)
62 QWindowSystemInterface::handleWindowScreenChanged(window(), targetScreen->screen());
63
64 if (view != nullptr) {
65 auto frameGeometry = rect.marginsAdded(frameMargins());
66 if (!qt_window_private(window())->positionAutomatic)
67 view->setPosition(frameGeometry.topLeft());
68 view->setSize(frameGeometry.size());
69 }
70}
71
73{
74 if (!visible)
75 m_view->hide();
76 else
77 m_view->showImmediate();
78 startAsyncWaitForNodeResizeIfNeeded();
79}
80
82{
83 auto windowObjectName = window()->objectName();
84
85 qOhosPrintfDebug(
86 "%s: winId called for window named: \"%s\"",
87 Q_FUNC_INFO,
88 windowObjectName.toStdString().c_str());
89
90 return reinterpret_cast<WId>(m_view->viewWindowId());
91}
92
94{
95 auto *view = ownedViewOrNull();
96 if (view != nullptr) {
97 view->raise();
98 window()->requestUpdate();
99 }
100}
101
103{
104 auto *view = ownedViewOrNull();
105 if (view != nullptr) {
106 view->lower();
107 window()->requestUpdate();
108 }
109}
110
112{
113 auto *view = ownedViewOrNull();
114 return view != nullptr ? view->surfaceOrNull() : nullptr;
115}
116
118{
119 return m_view.get();
120}
121
123{
125 m_view = QOhosView::createForWindow(this, propertiesProvider());
126
127 auto *qWindow = window();
128
129 // HACK
130 // There is no functionality to fetch the initial window status from ohos
131 // We can only listen for the changes - we need to assume sane default for tablets
132 // so assign the default most-likely status of the tablet here.
133 if (m_view->viewType() == QOhosView::ViewType::MainWindow
134 && !QOhosSettings::instance().isWindowPcModeEnabled()
135 && !m_lastWindowStatusType.has_value()) {
136 m_lastWindowStatusType = QOhosWindowProxy::WindowStatusType::FULL_SCREEN;
137 }
138
139 QObject::connect(
140 m_view.get(), &QOhosView::externalContentInteractionDetected,
141 qWindow, &QOhosPlatformWindow::closeAllActivePopups);
142
143 QObject::connect(
144 m_view.get(), &QOhosView::nodeAreaChanged, m_view.get(),
145 [this](QArkUi::QQtEmbeddedWindowNode::NodeAreaInfo event) {
146 handleNodeResizeEvent(event);
147 });
148
149 QObject::connect(
150 m_view.get(), &QOhosView::windowEvent,
151 qWindow,
152 [this](QOhosWindowProxy::WindowEvent evt) { handleWindowEvent(evt); });
153
154 QObject::connect(
155 m_view.get(), &QOhosView::windowStatusChange,
156 qWindow,
157 [this](QOhosWindowProxy::WindowStatus evt) { handleWindowStatusChange(evt); });
158
159 QObject::connect(
160 m_view.get(), &QOhosView::windowVisibilityChange,
161 qWindow,
162 [this](bool visible) { handleWindowVisibilityChange(visible); });
163
164 bool monitorAvoidAreaChange =
165 !(isHandheldDeviceType() && m_view->viewType() == QOhosView::ViewType::SubWindow);
166
167 if (monitorAvoidAreaChange) {
168 QObject::connect(
169 m_view.get(), &QOhosView::avoidAreaChanged,
170 qWindow,
171 [this](QOhosWindowProxy::AvoidAreaType avoidAreaType,
172 const QOhosWindowProxy::AvoidArea &systemAvoidArea) {
173 handleAvoidAreaChanged(avoidAreaType, systemAvoidArea);
174 });
175 }
176
177 QObject::connect(
178 m_view.get(), &QOhosView::windowRectChangedInGlobalDisplay,
179 qWindow,
180 [this](const QOhosWindowProxy::RectChangeOptions &rectChangeOptions) {
181 handleWindowRectChanged(rectChangeOptions);
182 });
183
184 QObject::connect(qWindow, &QWindow::modalityChanged, m_view.get(), &QOhosView::setModality);
185
186 QObject::connect(
187 m_view.get(), &QOhosView::surfaceStatusChanged,
188 qWindow,
189 [this](const std::optional<QSize> &optSurfaceSize) { handleSurfaceStatusChanged(optSurfaceSize); });
190
191 QObject::connect(qWindow, &QWindow::windowTitleChanged, m_view.get(), &QOhosView::setTitle);
192
193 QObject::connect(
194 m_view.get(), &QOhosView::windowDisplayIdChanged,
195 qWindow,
196 [this](QOhosDisplayInfo::JsDisplayId displayId) { handleWindowDisplayIdChanged(displayId); });
197}
198
199void QOhosFloatingWindow::restoreWindowCurrentCursorIfNeeded()
200{
201 auto *view = ownedViewOrNull();
202 if (view != nullptr && m_cursor.has_value())
203 view->setCursor(m_cursor.value());
204}
205
207 Qt::WindowFlags previousWindowFlags, Qt::WindowFlags currentWindowFlags)
208{
209 auto *view = ownedViewOrNull();
210 if (view == nullptr)
211 return;
212
213 view->handleWindowFlagsChange(previousWindowFlags, currentWindowFlags);
214}
215
217 Qt::WindowStates oldWindowState, Qt::WindowStates currentWindowState)
218{
219 auto *view = ownedViewOrNull();
220 if (view == nullptr)
221 return;
222
223 view->handleWindowStateChange(oldWindowState, currentWindowState);
224}
225
226void QOhosFloatingWindow::internalHijackSystemFocusAsPopup()
227{
228 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::PopupFocusReason);
229}
230
231void QOhosFloatingWindow::focusHijackingPopupHidden()
232{
233 auto systemFocusedWindows = QWindowProxyRegistry::instance().queryWindowsWithSystemWindowAndFocus();
234 QWindowSystemInterface::handleFocusWindowChanged(
235 !systemFocusedWindows.empty() ? systemFocusedWindows.front() : nullptr,
236 Qt::ActiveWindowFocusReason);
237}
238
239void QOhosFloatingWindow::setMask(const QRegion &region)
240{
241 m_windowMask = region;
242
243 auto *view = ownedViewOrNull();
244 if (view == nullptr)
245 return;
246
247 view->setWindowMask(QOhosWindowProxy::WindowMask{region});
248}
249
251{
252 auto *view = ownedViewOrNull();
253 if (view != nullptr)
254 return view->startMoving();
255 return false;
256}
257
259{
260 if (windowFlags().testFlag(Qt::WindowDoesNotAcceptFocus) && window()->type() == Qt::Popup) {
261 internalHijackSystemFocusAsPopup();
262 return;
263 }
264
266}
267
268void QOhosFloatingWindow::handleWindowEvent(QOhosWindowProxy::WindowEvent evt)
269{
270 auto *qWindow = window();
271 bool windowAcceptsFocusAndInput = checkWindowAcceptsFocus() && checkWindowAcceptsInput();
272 Qt::WindowStates windowStatesToSet = windowStates();
273 bool windowActive = true;
274 auto previousWindowEventType = std::exchange(m_lastWindowEventType, evt.type);
275
276 switch (evt.type) {
277 case QOhosWindowProxy::WindowEventType::WINDOW_ACTIVE:
278 {
279 QWindow *modalWindow = nullptr;
280 if (QGuiApplicationPrivate::instance()->isWindowBlocked(qWindow, &modalWindow)
281 && qWindow != modalWindow) {
282 modalWindow->requestActivate();
283 return;
284 }
285
286 // FIXME - restore cursor set by the User every time window is activated.
287 // Perform it because Ohos overrides it with default "standard arrow" cursor (when
288 // window is deactivated) and does not bring selected cursor back (when window is
289 // activated again). This bahaviour is Ohos platform issue.
290 restoreWindowCurrentCursorIfNeeded();
291 if (windowAcceptsFocusAndInput) {
292 QWindowSystemInterface::handleFocusWindowChanged(qWindow, Qt::ActiveWindowFocusReason);
294 }
295 break;
296 }
297 case QOhosWindowProxy::WindowEventType::WINDOW_INACTIVE:
298 windowActive = false;
299 if (!windowAcceptsFocusAndInput)
300 break;
301 if (QGuiApplicationPrivate::focus_window == qWindow
302 || (QGuiApplicationPrivate::focus_window != nullptr
303 && QOhosPlatformWindow::platformWindowFlagsForQWindow(
304 QGuiApplicationPrivate::focus_window).testFlag(Qt::WindowDoesNotAcceptFocus))) {
305 QWindowSystemInterface::handleFocusWindowChanged(nullptr, Qt::ActiveWindowFocusReason);
306 }
308 break;
309 case QOhosWindowProxy::WindowEventType::WINDOW_HIDDEN:
310 if (!checkWindowAcceptsFocus() && QGuiApplicationPrivate::focus_window == qWindow)
311 focusHijackingPopupHidden();
312 setExposedFromOhos(false);
313 windowActive = false;
314 break;
315 case QOhosWindowProxy::WindowEventType::WINDOW_SHOWN:
316 // HACK
317 // This is a fix for the windows hidden using `QWidget::hide()`.
318 // When a window is hidden and then bring back from the system Dock
319 // Qt side keeps wrong (hidden) state. This causes different issues due to
320 // improper state. To fix it - change visibility state based on the system
321 // window state.
322 if (previousWindowEventType == QOhosWindowProxy::WindowEventType::WINDOW_HIDDEN && !qWindow->isVisible())
323 qWindow->setVisible(true);
324 setExposedFromOhos(true);
325 startAsyncWaitForNodeResizeIfNeeded();
326 break;
327 case QOhosWindowProxy::WindowEventType::WINDOW_DESTROYED:
328 windowActive = false;
330 return;
331 }
332
333 windowStatesToSet.setFlag(Qt::WindowState::WindowActive, windowActive && windowAcceptsFocusAndInput);
334 setWindowStateFromOhos(windowStatesToSet);
335}
336
337void QOhosFloatingWindow::handleWindowStatusChange(QOhosWindowProxy::WindowStatus evt)
338{
339 auto *qWindow = window();
340 qCDebug(
341 QtForOhos,
342 "Window status changed, window: %p(%s) status: %d",
343 qWindow,
344 qPrintable(qWindow->objectName()),
345 evt.type);
346
347 // Drop duplicate FULL_SCREEN notifications; MainWindow views in non-PC mode
348 // are exempt because initialize() pre-seeds their status to FULL_SCREEN.
349 if (m_lastWindowStatusType == evt.type
350 && m_lastWindowStatusType == QOhosWindowProxy::WindowStatusType::FULL_SCREEN
351 && !(m_view->viewType() == QOhosView::ViewType::MainWindow
352 && !QOhosSettings::instance().isWindowPcModeEnabled())) {
353 qCDebug(QtForOhos, "Window status is the same as the previous one");
354 return;
355 }
356
357 Qt::WindowStates windowStatesToSet = windowStates();
358 Qt::WindowState flagToSet;
359 m_lastWindowStatusType = evt.type;
360
361 switch (evt.type) {
362 case QOhosWindowProxy::WindowStatusType::FULL_SCREEN:
363 flagToSet = Qt::WindowState::WindowFullScreen;
364 break;
365 case QOhosWindowProxy::WindowStatusType::MAXIMIZE:
366 flagToSet = Qt::WindowState::WindowMaximized;
367 break;
368 case QOhosWindowProxy::WindowStatusType::MINIMIZE:
369 flagToSet = Qt::WindowState::WindowMinimized;
370 break;
371 case QOhosWindowProxy::WindowStatusType::UNDEFINED:
372 case QOhosWindowProxy::WindowStatusType::FLOATING:
373 Q_FALLTHROUGH();
374 case QOhosWindowProxy::WindowStatusType::SPLIT_SCREEN:
375 flagToSet = Qt::WindowState::WindowNoState;
376 break;
377 }
378
379 // NOTE - Qt::WindowFullScreen and Qt::WindowMaximized represent the exact same
380 // OHOS Window state.
381 if (flagToSet == Qt::WindowState::WindowFullScreen
382 || flagToSet == Qt::WindowState::WindowMaximized) {
383 windowStatesToSet = m_view->isFullscreenImmersiveModeEnabled()
384 ? Qt::WindowState::WindowFullScreen
385 : Qt::WindowState::WindowMaximized;
386 } else {
387 static constexpr Qt::WindowState mutuallyExclusiveFlags[] = {
388 Qt::WindowState::WindowFullScreen,
389 Qt::WindowState::WindowMaximized,
390 Qt::WindowState::WindowMinimized,
391 Qt::WindowState::WindowNoState,
392 };
393
394 for (const auto exclusiveState : mutuallyExclusiveFlags)
395 windowStatesToSet.setFlag(exclusiveState, flagToSet == exclusiveState);
396 }
397
398 setWindowStateFromOhos(windowStatesToSet);
399
400 // HACK
401 // There is no functionality in OHOS to fetch the window status that would allow checking
402 // the value while processing the WINDOW_SHOWN window event type. Therefore, the window
403 // geometry view needs to be recalculated with any change.
404 startAsyncWaitForNodeResizeIfNeeded();
405}
406
407void QOhosFloatingWindow::handleWindowVisibilityChange(bool visible)
408{
409 auto *qWindow = window();
410 qCDebug(
411 QtForOhos,
412 "Window %p(%s) visibility changed: %s",
413 qWindow,
414 qPrintable(qWindow->objectName()),
415 visible ? "true" : "false");
416 if (visible && m_windowMask.has_value())
417 m_view->setWindowMask(QOhosWindowProxy::WindowMask{m_windowMask.value()});
418
419 setExposedFromOhos(visible);
420}
421
422void QOhosFloatingWindow::handleAvoidAreaChanged(
423 QOhosWindowProxy::AvoidAreaType avoidAreaType,
424 const QOhosWindowProxy::AvoidArea &systemAvoidArea)
425{
426 const auto &cached = m_avoidAreaCache[avoidAreaType];
427
428 bool actuallyChanged =
429 cached.visible != systemAvoidArea.visible
430 || cached.leftRect != systemAvoidArea.leftRect
431 || cached.rightRect != systemAvoidArea.rightRect
432 || cached.bottomRect != systemAvoidArea.bottomRect
433 || cached.topRect != systemAvoidArea.topRect;
434
435 if (actuallyChanged) {
436 m_avoidAreaCache[avoidAreaType] = systemAvoidArea;
437 qCDebug(QtForOhos) << "Avoid area changed:"
438 << static_cast<int>(avoidAreaType)
439 << "visible:" << systemAvoidArea.visible
440 << "top:" << systemAvoidArea.topRect
441 << "left:" << systemAvoidArea.leftRect
442 << "right:" << systemAvoidArea.rightRect
443 << "bottom:" << systemAvoidArea.bottomRect;
444 startAsyncWaitForNodeResizeIfNeeded();
445 }
446}
447
448void QOhosFloatingWindow::handleSurfaceStatusChanged(const std::optional<QSize> &optSurfaceSize)
449{
450 m_optLastSurfaceSize = optSurfaceSize;
451 bool hasSurface = m_view->surfaceOrNull() != nullptr;
452 if (m_view->viewType() == QOhosView::ViewType::EmbeddedWindow) {
453 setExposedFromOhos(hasSurface);
454 }
455
456 if (hasSurface)
457 startAsyncWaitForNodeResizeIfNeeded();
458}
459
460void QOhosFloatingWindow::handleWindowDisplayIdChanged(QOhosDisplayInfo::JsDisplayId displayId)
461{
462 setDisplayIdFromOhos(displayId);
463 startAsyncWaitForNodeResizeIfNeeded();
464}
465
466void QOhosFloatingWindow::handleWindowRectChanged(
467 const QOhosWindowProxy::RectChangeOptions &rectChangeOptions)
468{
469 bool needsCloseAllActivePopups = rectChangeOptions.reason == QOhosWindowProxy::RectChangeReason::DRAG_START;
470
471 qCDebug(QtForOhos)
472 << "windowRectChanged window:" << window()
473 << "rect:" << rectChangeOptions.rect
474 << "reason:" << static_cast<int>(rectChangeOptions.reason);
475
476 startAsyncWaitForNodeResizeIfNeeded();
477
478 if (isWindowRotatedByTabletScreenRotation(window(), rectChangeOptions))
479 needsCloseAllActivePopups = true;
480
481 if (needsCloseAllActivePopups)
483}
484
486{
487 if (event->type() == QEvent::Timer) {
488 auto *timerEvent = static_cast<QTimerEvent *>(event);
489 if (m_view && timerEvent->timerId() == m_geometryChangeTimer.timerId()) {
490 auto syntheticEvent = m_view->nodeAreaInfo();
491 handleNodeResizeEvent(syntheticEvent);
492 }
493 }
494
495 return QOhosPlatformWindow::windowEvent(event);
496}
497
498void QOhosFloatingWindow::startAsyncWaitForNodeResizeIfNeeded()
499{
500 constexpr auto geometryChangeEventTimeoutMs = std::chrono::milliseconds(16);
501
502 if (!m_geometryChangeTimer.isActive()) {
503 m_geometryChangeTimer.start(
504 geometryChangeEventTimeoutMs.count(),
505 Qt::PreciseTimer,
506 window());
507 }
508}
509
510void QOhosFloatingWindow::handleNodeResizeEvent(const QArkUi::QQtEmbeddedWindowNode::NodeAreaInfo &areaChangeEvent)
511{
512 m_geometryChangeTimer.stop();
513
514 if (Q_UNLIKELY(!m_view))
515 return;
516
517 setWindowGeometryFromOhos(
518 m_view->viewType() != QOhosView::ViewType::EmbeddedWindow
519 ? QRect(areaChangeEvent.globalRelativeOffsetPixels, areaChangeEvent.screenGeometryPixels.size())
520 : QRect(areaChangeEvent.parentRelativeOffsetPixels, areaChangeEvent.screenGeometryPixels.size()));
521}
522
523QT_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
bool windowEvent(QEvent *event) override
Reimplement this method to be able to do any platform specific event handling.
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(std::optional< QOhosDisplayInfo::JsDisplayId > displayId)
void setExposedFromOhos(bool exposed)
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.
bool isWindowPcModeEnabled() const
static QOhosSettings & instance()
QOhosSurface * surfaceOrNull() const
void setWindowMask(const QOhosWindowProxy::WindowMask &windowMask)
void lower()
bool startMoving()
void raise()
QtOhos::enums::ohos::window::AvoidAreaType AvoidAreaType
QtOhos::enums::ohos::window::RectChangeReason RectChangeReason
QtOhos::enums::ohos::window::WindowEventType WindowEventType
QtOhos::enums::ohos::window::WindowStatusType WindowStatusType
static QWindowProxyRegistry & instance()
Combined button and popup list for selecting options.
bool isWindowRotatedByTabletScreenRotation(QWindow *window, QOhosWindowProxy::RectChangeOptions rectChangeOptions)