Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qioswindow.mm
Go to the documentation of this file.
1// Copyright (C) 2016 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 "qioswindow.h"
5
7#include "qiosglobal.h"
8#include "qiosintegration.h"
9#include "qiosscreen.h"
10#include "qiosviewcontroller.h"
11#include "quiview.h"
12#include "qiosinputcontext.h"
13
14#include <QtGui/private/qwindow_p.h>
15#include <qpa/qplatformintegration.h>
16
17#if QT_CONFIG(opengl)
18#import <QuartzCore/CAEAGLLayer.h>
19#endif
20
21#ifdef Q_OS_IOS
22#import <QuartzCore/CAMetalLayer.h>
23#endif
24
25#include <QtDebug>
26
28
31 , m_windowLevel(0)
32{
33#ifdef Q_OS_IOS
35 window->setSurfaceType(QSurface::MetalSurface);
36
38 m_view = [[QUIMetalView alloc] initWithQIOSWindow:this];
39 else
40#endif
41 m_view = [[QUIView alloc] initWithQIOSWindow:this];
42
43 connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QIOSWindow::applicationStateChanged);
44
46
47 // Resolve default window geometry in case it was not set before creating the
48 // platform window. This picks up eg. minimum-size if set, and defaults to
49 // the "maxmized" geometry (even though we're not in that window state).
50 // FIXME: Detect if we apply a maximized geometry and send a window state
51 // change event in that case.
53 screen()->availableGeometry().width(), screen()->availableGeometry().height());
54
55 setWindowState(window->windowStates());
57
58 Qt::ScreenOrientation initialOrientation = window->contentOrientation();
59 if (initialOrientation != Qt::PrimaryOrientation) {
60 // Start up in portrait, then apply possible content orientation,
61 // as per Apple's documentation.
62 dispatch_async(dispatch_get_main_queue(), ^{
63 handleContentOrientationChange(initialOrientation);
64 });
65 }
66}
67
69{
70 // According to the UIResponder documentation, Cocoa Touch should react to system interruptions
71 // that "might cause the view to be removed from the window" by sending touchesCancelled, but in
72 // practice this doesn't seem to happen when removing the view from its superview. To ensure that
73 // Qt's internal state for touch and mouse handling is kept consistent, we therefore have to force
74 // cancellation of all touch events.
75 [m_view touchesCancelled:[NSSet set] withEvent:0];
76
78
79 m_view.platformWindow = 0;
80 [m_view removeFromSuperview];
81 [m_view release];
82}
83
84
86{
87 return window()->requestedFormat();
88}
89
90
91bool QIOSWindow::blockedByModal()
92{
94 return modalWindow && modalWindow != window();
95}
96
97void QIOSWindow::setVisible(bool visible)
98{
99 m_view.hidden = !visible;
100 [m_view setNeedsDisplay];
101
102 if (!isQtApplication() || !window()->isTopLevel())
103 return;
104
105 // Since iOS doesn't do window management the way a Qt application
106 // expects, we need to raise and activate windows ourselves:
107 if (visible)
108 updateWindowLevel();
109
110 if (blockedByModal()) {
111 if (visible)
112 raise();
113 return;
114 }
115
116 if (visible && shouldAutoActivateWindow()) {
117 if (!window()->property("_q_showWithoutActivating").toBool())
119 } else if (!visible && [m_view isActiveWindow]) {
120 // Our window was active/focus window but now hidden, so relinquish
121 // focus to the next possible window in the stack.
122 NSArray<UIView *> *subviews = m_view.viewController.view.subviews;
123 for (int i = int(subviews.count) - 1; i >= 0; --i) {
124 UIView *view = [subviews objectAtIndex:i];
125 if (view.hidden)
126 continue;
127
128 QWindow *w = view.qwindow;
129 if (!w || !w->isTopLevel())
130 continue;
131
132 QIOSWindow *iosWindow = static_cast<QIOSWindow *>(w->handle());
133 if (!iosWindow->shouldAutoActivateWindow())
134 continue;
135
136 iosWindow->requestActivateWindow();
137 break;
138 }
139 }
140}
141
143{
144 if (![m_view canBecomeFirstResponder])
145 return false;
146
147 // We don't want to do automatic window activation for popup windows
148 // that are unlikely to contain editable controls (to avoid hiding
149 // the keyboard while the popup is showing)
150 const Qt::WindowType type = window()->type();
151 return (type != Qt::Popup && type != Qt::ToolTip) || !window()->isActive();
152}
153
155{
156 m_view.alpha = qBound(0.0, level, 1.0);
157}
158
160{
161 m_normalGeometry = rect;
162
163 if (window()->windowState() != Qt::WindowNoState) {
165
166 // The layout will realize the requested geometry was not applied, and
167 // send geometry-change events that match the actual geometry.
168 [m_view setNeedsLayout];
169
170 if (window()->inherits("QWidgetWindow")) {
171 // QWidget wrongly assumes that setGeometry resets the window
172 // state back to Qt::NoWindowState, so we need to inform it that
173 // that his is not the case by re-issuing the current window state.
175
176 // It also needs to be told immediately that the geometry it requested
177 // did not apply, otherwise it will continue on as if it did, instead
178 // of waiting for a resize event.
179 [m_view layoutIfNeeded];
180 }
181
182 return;
183 }
184
185 applyGeometry(rect);
186}
187
188void QIOSWindow::applyGeometry(const QRect &rect)
189{
190 // Geometry changes are asynchronous, but QWindow::geometry() is
191 // expected to report back the 'requested geometry' until we get
192 // a callback with the updated geometry from the window system.
193 // The baseclass takes care of persisting this for us.
195
196 m_view.frame = rect.toCGRect();
197
198 // iOS will automatically trigger -[layoutSubviews:] for resize,
199 // but not for move, so we force it just in case.
200 [m_view setNeedsLayout];
201
202 if (window()->inherits("QWidgetWindow"))
203 [m_view layoutIfNeeded];
204}
205
207{
208 UIEdgeInsets safeAreaInsets = m_view.qt_safeAreaInsets;
209 return QMargins(safeAreaInsets.left, safeAreaInsets.top,
210 safeAreaInsets.right, safeAreaInsets.bottom);
211}
212
214{
215 return qApp->applicationState() != Qt::ApplicationSuspended
216 && window()->isVisible() && !window()->geometry().isEmpty();
217}
218
219void QIOSWindow::setWindowState(Qt::WindowStates state)
220{
221 // Update the QWindow representation straight away, so that
222 // we can update the statusbar visibility based on the new
223 // state before applying geometry changes.
225
226 if (window()->isTopLevel() && window()->isVisible() && window()->isActive())
227 [m_view.qtViewController updateProperties];
228
230 applyGeometry(QRect());
232 // When an application is in split-view mode, the UIScreen still has the
233 // same geometry, but the UIWindow is resized to the area reserved for the
234 // application. We use this to constrain the geometry used when applying the
235 // fullscreen or maximized window states. Note that we do not do this
236 // in applyGeometry(), as we don't want to artificially limit window
237 // placement "outside" of the screen bounds if that's what the user wants.
238
239 QRect uiWindowBounds = QRectF::fromCGRect(m_view.window.bounds).toRect();
240 QRect fullscreenGeometry = screen()->geometry().intersected(uiWindowBounds);
241 QRect maximizedGeometry = window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint ?
242 fullscreenGeometry : screen()->availableGeometry().intersected(uiWindowBounds);
243
245 applyGeometry(fullscreenGeometry);
246 else
247 applyGeometry(maximizedGeometry);
248 } else {
249 applyGeometry(m_normalGeometry);
250 }
251}
252
253void QIOSWindow::setParent(const QPlatformWindow *parentWindow)
254{
255 UIView *parentView = parentWindow ? reinterpret_cast<UIView *>(parentWindow->winId())
256 : isQtApplication() ? static_cast<QIOSScreen *>(screen())->uiWindow().rootViewController.view : 0;
257
258 [parentView addSubview:m_view];
259}
260
262{
263 // Note that several windows can be active at the same time if they exist in the same
264 // hierarchy (transient children). But only one window can be QGuiApplication::focusWindow().
265 // Despite the name, 'requestActivateWindow' means raise and transfer focus to the window:
266 if (blockedByModal())
267 return;
268
269 Q_ASSERT(m_view.window);
270 [m_view.window makeKeyWindow];
271 [m_view becomeFirstResponder];
272
273 if (window()->isTopLevel())
274 raise();
275}
276
277void QIOSWindow::raiseOrLower(bool raise)
278{
279 // Re-insert m_view at the correct index among its sibling views
280 // (QWindows) according to their current m_windowLevel:
281 if (!isQtApplication())
282 return;
283
284 NSArray<UIView *> *subviews = m_view.superview.subviews;
285 if (subviews.count == 1)
286 return;
287
288 for (int i = int(subviews.count) - 1; i >= 0; --i) {
289 UIView *view = static_cast<UIView *>([subviews objectAtIndex:i]);
290 if (view.hidden || view == m_view || !view.qwindow)
291 continue;
292 int level = static_cast<QIOSWindow *>(view.qwindow->handle())->m_windowLevel;
293 if (m_windowLevel > level || (raise && m_windowLevel == level)) {
294 [m_view.superview insertSubview:m_view aboveSubview:view];
295 return;
296 }
297 }
298 [m_view.superview insertSubview:m_view atIndex:0];
299}
300
301void QIOSWindow::updateWindowLevel()
302{
303 Qt::WindowType type = window()->type();
304
305 if (type == Qt::ToolTip)
306 m_windowLevel = 120;
307 else if (window()->flags() & Qt::WindowStaysOnTopHint)
308 m_windowLevel = 100;
309 else if (window()->isModal())
310 m_windowLevel = 40;
311 else if (type == Qt::Popup)
312 m_windowLevel = 30;
313 else if (type == Qt::SplashScreen)
314 m_windowLevel = 20;
315 else if (type == Qt::Tool)
316 m_windowLevel = 10;
317 else
318 m_windowLevel = 0;
319
320 // A window should be in at least the same m_windowLevel as its parent:
321 QWindow *transientParent = window()->transientParent();
322 QIOSWindow *transientParentWindow = transientParent ? static_cast<QIOSWindow *>(transientParent->handle()) : 0;
323 if (transientParentWindow)
324 m_windowLevel = qMax(transientParentWindow->m_windowLevel, m_windowLevel);
325}
326
328{
329 // Update the QWindow representation straight away, so that
330 // we can update the statusbar orientation based on the new
331 // content orientation.
333
334 [m_view.qtViewController updateProperties];
335}
336
337void QIOSWindow::applicationStateChanged(Qt::ApplicationState)
338{
339 if (window()->isExposed() != isExposed())
340 [m_view sendUpdatedExposeEvent];
341}
342
344{
345 return m_view.contentScaleFactor;
346}
347
349{
350 [m_view clearAccessibleCache];
351}
352
354{
355 static_cast<QIOSScreen *>(screen())->setUpdatesPaused(false);
356}
357
358#if QT_CONFIG(opengl)
359CAEAGLLayer *QIOSWindow::eaglLayer() const
360{
361 Q_ASSERT([m_view.layer isKindOfClass:[CAEAGLLayer class]]);
362 return static_cast<CAEAGLLayer *>(m_view.layer);
363}
364#endif
365
366#ifndef QT_NO_DEBUG_STREAM
368{
369 QDebugStateSaver saver(debug);
370 debug.nospace();
371 debug << "QIOSWindow(" << (const void *)window;
372 if (window)
373 debug << ", window=" << window->window();
374 debug << ')';
375 return debug;
376}
377#endif // !QT_NO_DEBUG_STREAM
378
380
381#include "moc_qioswindow.cpp"
\inmodule QtCore
\inmodule QtCore
static QWindow * modalWindow()
Returns the most recently shown modal window.
void applicationStateChanged(Qt::ApplicationState state)
QIOSWindow(QWindow *window)
Definition qioswindow.mm:29
bool shouldAutoActivateWindow() const
void handleContentOrientationChange(Qt::ScreenOrientation orientation) override
Handle changes to the orientation of the platform window's contents.
void setParent(const QPlatformWindow *window) override
This function is called to enable native child window in QPA.
void clearAccessibleCache()
void setWindowState(Qt::WindowStates state) override
Requests setting the window state of this surface to type.
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
void requestUpdate() override
Requests an QEvent::UpdateRequest event.
void setOpacity(qreal level) override
Reimplement to be able to let Qt set the opacity level of a window.
qreal devicePixelRatio() const override
Reimplement this function in subclass to return the device pixel ratio for the window.
bool isExposed() const override
Returns if this window is exposed in the windowing system.
void raise() override
Reimplement to be able to let Qt raise windows to the top of the desktop.
Definition qioswindow.h:40
QMargins safeAreaMargins() const override
The safe area margins of a window represent the area that is safe to place content within,...
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
Definition qioswindow.mm:97
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
QSurfaceFormat format() const override
Returns the actual surface format of the window.
Definition qioswindow.mm:85
\inmodule QtCore
Definition qmargins.h:23
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
bool inherits(const char *classname) const
Returns true if this object is an instance of a class that inherits className or a QObject subclass t...
Definition qobject.h:313
virtual QRect geometry() const =0
Reimplement in subclass to return the pixel geometry of the screen.
virtual QRect availableGeometry() const
Reimplement in subclass to return the pixel geometry of the available space This normally is the desk...
The QPlatformWindow class provides an abstraction for top-level windows.
QWindow * window() const
Returns the window which belongs to the QPlatformWindow.
QPlatformScreen * screen() const override
Returns the platform screen handle corresponding to this platform window, or null if the window is no...
QPlatformWindow * parent() const
Returns the parent platform window (or \nullptr if orphan).
virtual void setGeometry(const QRect &rect)
This function is called by Qt whenever a window is moved or resized using the QWindow API.
virtual QRect geometry() const
Returns the current geometry of a window.
virtual bool isActive() const
Returns true if the window should appear active from a style perspective.
virtual WId winId() const
Reimplement in subclasses to return a handle to the native window.
static QRect initialGeometry(const QWindow *w, const QRect &initialGeometry, int defaultWidth, int defaultHeight, const QScreen **resultingScreenReturn=nullptr)
Helper function to get initial geometry on windowing systems which do not do smart positioning and al...
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:166
QRect intersected(const QRect &other) const noexcept
Definition qrect.h:414
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
@ RasterSurface
Definition qsurface.h:31
@ MetalSurface
Definition qsurface.h:36
Qt::ScreenOrientation contentOrientation
Definition qwindow_p.h:123
Qt::WindowStates windowState
Definition qwindow_p.h:112
static void handleWindowStateChanged(QWindow *window, Qt::WindowStates newState, int oldState=-1)
\inmodule QtGui
Definition qwindow.h:63
Qt::WindowFlags flags
the window flags of the window
Definition qwindow.h:79
SurfaceType surfaceType() const override
Returns the surface type of the window.
Definition qwindow.cpp:628
Qt::ScreenOrientation contentOrientation
the orientation of the window's contents
Definition qwindow.h:95
qreal opacity
The opacity of the window in the windowing system.
Definition qwindow.h:96
rect
[4]
else opt state
[0]
Combined button and popup list for selecting options.
@ WindowFullScreen
Definition qnamespace.h:254
@ WindowNoState
Definition qnamespace.h:251
@ WindowMinimized
Definition qnamespace.h:252
@ WindowMaximized
Definition qnamespace.h:253
ScreenOrientation
Definition qnamespace.h:270
@ PrimaryOrientation
Definition qnamespace.h:271
ApplicationState
Definition qnamespace.h:261
@ ApplicationSuspended
Definition qnamespace.h:262
WindowType
Definition qnamespace.h:204
@ ToolTip
Definition qnamespace.h:212
@ Popup
Definition qnamespace.h:210
@ SplashScreen
Definition qnamespace.h:213
@ WindowStaysOnTopHint
Definition qnamespace.h:232
@ MaximizeUsingFullscreenGeometryHint
Definition qnamespace.h:236
@ Tool
Definition qnamespace.h:211
#define qApp
#define qGuiApp
bool isQtApplication()
Definition qiosglobal.mm:17
QDebug operator<<(QDebug debug, const QIOSWindow *window)
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLenum GLuint GLint level
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLint GLsizei width
GLenum type
GLbitfield flags
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
double qreal
Definition qtypes.h:92
Q_GUI_EXPORT QWindowPrivate * qt_window_private(QWindow *window)
Definition qwindow.cpp:2864
const char property[13]
Definition qwizard.cpp:101
QFuture< QSet< QChar > > set
[10]
sem release()
edit isVisible()
aWidget window() -> setWindowTitle("New Window Title")
[2]
QQuickView * view
[0]
QT_BEGIN_NAMESPACE bool toBool(const QString &str)
Definition utils.h:14