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