15#include <QtCore/private/qcore_mac_p.h>
17#include <QtGui/private/qwindow_p.h>
18#include <QtGui/private/qhighdpiscaling_p.h>
19#include <qpa/qplatformintegration.h>
22#import <QuartzCore/CAEAGLLayer.h>
26#import <QuartzCore/CAMetalLayer.h>
38QIOSWindow::QIOSWindow(QWindow *window, WId nativeHandle)
39 : QPlatformWindow(window)
42 m_view =
reinterpret_cast<UIView *>(nativeHandle);
46 if (window->surfaceType() == QSurface::RasterSurface)
47 window->setSurfaceType(QSurface::MetalSurface);
49 if (window->surfaceType() == QSurface::MetalSurface)
50 m_view = [[QUIMetalView alloc] initWithQIOSWindow:
this];
53 m_view = [[QUIView alloc] initWithQIOSWindow:
this];
56 connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this, &QIOSWindow::applicationStateChanged);
61 setParent(QPlatformWindow::parent());
63 if (!isForeignWindow()) {
66 m_normalGeometry = initialGeometry(window, QPlatformWindow::geometry(),
67 defaultWindowWidth, defaultWindowHeight);
69 setWindowState(window->windowStates());
70 setOpacity(window->opacity());
71 setMask(QHighDpi::toNativeLocalRegion(window->mask(), window));
74 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
77 Qt::ScreenOrientation initialOrientation = window->contentOrientation();
78 if (initialOrientation != Qt::PrimaryOrientation) {
81 dispatch_async(dispatch_get_main_queue(), ^{
82 handleContentOrientationChange(initialOrientation);
87QIOSWindow::~QIOSWindow()
94 [m_view touchesCancelled:[NSSet set] withEvent:0];
96 clearAccessibleCache();
98 quiview_cast(m_view).platformWindow =
nullptr;
103 if (!(isForeignWindow() && !QPlatformWindow::parent()))
104 [m_view removeFromSuperview];
110QSurfaceFormat QIOSWindow::format()
const
112 return window()->requestedFormat();
116bool QIOSWindow::blockedByModal()
118 QWindow *modalWindow = QGuiApplication::modalWindow();
119 return modalWindow && modalWindow != window();
122void QIOSWindow::setVisible(
bool visible)
124 m_view.hidden = !visible;
125 [m_view setNeedsDisplay];
130 if (blockedByModal()) {
136 if (visible && shouldAutoActivateWindow()) {
137 if (!window()->property(
"_q_showWithoutActivating").toBool())
138 requestActivateWindow();
139 }
else if (!visible && [quiview_cast(m_view) isActiveWindow]) {
142 NSArray<UIView *> *subviews = m_view.viewController.view.subviews;
143 for (
int i =
int(subviews.count) - 1; i >= 0; --i) {
144 UIView *view = [subviews objectAtIndex:i];
148 QWindow *w = view.qwindow;
149 if (!w || !w->isTopLevel())
152 QIOSWindow *iosWindow =
static_cast<QIOSWindow *>(w->handle());
153 if (!iosWindow->shouldAutoActivateWindow())
156 iosWindow->requestActivateWindow();
162bool QIOSWindow::shouldAutoActivateWindow()
const
164 if (![m_view canBecomeFirstResponder])
167 const Qt::WindowType type = window()->type();
172 if (type == Qt::ToolTip)
178 return type != Qt::Popup || !window()->isActive();
181void QIOSWindow::setOpacity(qreal level)
183 m_view.alpha = qBound(0.0, level, 1.0);
186void QIOSWindow::setGeometry(
const QRect &rect)
188 m_normalGeometry = rect;
190 if (window()->windowState() != Qt::WindowNoState) {
191 QPlatformWindow::setGeometry(rect);
195 [m_view setNeedsLayout];
197 if (window()->inherits(
"QWidgetWindow")) {
201 QWindowSystemInterface::handleWindowStateChanged(window(), window()->windowState());
206 [m_view layoutIfNeeded];
215void QIOSWindow::applyGeometry(
const QRect &rect)
221 QPlatformWindow::setGeometry(rect);
223 m_view.frame = rect.toCGRect();
227 [m_view setNeedsLayout];
229 if (window()->inherits(
"QWidgetWindow"))
230 [m_view layoutIfNeeded];
233QMargins QIOSWindow::safeAreaMargins()
const
235 UIEdgeInsets safeAreaInsets = m_view.safeAreaInsets;
236 return QMargins(safeAreaInsets.left, safeAreaInsets.top,
237 safeAreaInsets.right, safeAreaInsets.bottom);
240bool QIOSWindow::isExposed()
const
242 return qApp->applicationState() != Qt::ApplicationSuspended
243 && window()->isVisible() && !window()->geometry().isEmpty();
246void QIOSWindow::setWindowState(Qt::WindowStates state)
251 qt_window_private(window())->windowState = state;
253 if (window()->isTopLevel() && window()->isVisible() && window()->isActive())
254 [m_view.qtViewController updateStatusBarProperties];
256 if (state & Qt::WindowMinimized) {
257 applyGeometry(QRect());
258 }
else if (state & (Qt::WindowFullScreen | Qt::WindowMaximized)) {
259 QRect uiWindowBounds = QRectF::fromCGRect(m_view.window.bounds).toRect();
260 if (NSProcessInfo.processInfo.iOSAppOnMac) {
268 applyGeometry(uiWindowBounds);
273 applyGeometry(uiWindowBounds);
275 QRect fullscreenGeometry = screen()->geometry();
276 QRect maximizedGeometry = fullscreenGeometry;
278#if !defined(Q_OS_VISIONOS)
279 if (!(window()->flags() & Qt::ExpandedClientAreaHint)) {
284 UIScreen *uiScreen = m_view.window.windowScene.screen;
285 UIEdgeInsets safeAreaInsets = m_view.window.safeAreaInsets;
286 if (m_view.window.bounds.size.width == uiScreen.bounds.size.width)
287 maximizedGeometry.adjust(safeAreaInsets.left, 0, -safeAreaInsets.right, 0);
288 if (m_view.window.bounds.size.height == uiScreen.bounds.size.height)
289 maximizedGeometry.adjust(0, safeAreaInsets.top, 0, -safeAreaInsets.bottom);
299 fullscreenGeometry = fullscreenGeometry.intersected(uiWindowBounds);
300 maximizedGeometry = maximizedGeometry.intersected(uiWindowBounds);
303 if (state & Qt::WindowFullScreen)
304 applyGeometry(fullscreenGeometry);
306 applyGeometry(maximizedGeometry);
309 applyGeometry(m_normalGeometry);
313void QIOSWindow::setParent(
const QPlatformWindow *parentWindow)
315 UIView *superview =
nullptr;
317 superview =
reinterpret_cast<UIView *>(parentWindow->winId());
318 else if (isQtApplication() && !isForeignWindow())
319 superview = rootViewForScreen(window()->screen()->handle());
322 [superview addSubview:m_view];
323 else if (quiview_cast(m_view.superview))
324 [m_view removeFromSuperview];
327void QIOSWindow::requestActivateWindow()
332 if (blockedByModal())
335 [m_view.window makeKeyWindow];
336 [m_view becomeFirstResponder];
338 if (window()->isTopLevel())
342void QIOSWindow::raiseOrLower(
bool raise)
347 NSArray<UIView *> *subviews = m_view.superview.subviews;
348 if (subviews.count == 1)
351 if (m_view.superview == m_view.qtViewController.view) {
354 for (
int i =
int(subviews.count) - 1; i >= 0; --i) {
355 UIView *view =
static_cast<UIView *>([subviews objectAtIndex:i]);
356 if (view.hidden || view == m_view || !view.qwindow)
358 int level =
static_cast<QIOSWindow *>(view.qwindow->handle())->windowLevel();
359 if (windowLevel() > level || (raise && windowLevel() == level)) {
360 [m_view.superview insertSubview:m_view aboveSubview:view];
364 [m_view.superview insertSubview:m_view atIndex:0];
368 [m_view.superview bringSubviewToFront:m_view];
370 [m_view.superview sendSubviewToBack:m_view];
374int QIOSWindow::windowLevel()
const
376 Qt::WindowType type = window()->type();
380 if (type == Qt::ToolTip)
382 else if (window()->flags() & Qt::WindowStaysOnTopHint)
384 else if (window()->isModal())
386 else if (type == Qt::Popup)
388 else if (type == Qt::SplashScreen)
390 else if (type == Qt::Tool)
396 QWindow *transientParent = window()->transientParent();
397 QIOSWindow *transientParentWindow = transientParent ?
static_cast<QIOSWindow *>(transientParent->handle()) : 0;
398 if (transientParentWindow)
399 level = qMax(transientParentWindow->windowLevel(), level);
404void QIOSWindow::applicationStateChanged(Qt::ApplicationState)
406 if (isForeignWindow())
409 if (window()->isExposed() != isExposed())
410 [quiview_cast(m_view) sendUpdatedExposeEvent];
413qreal QIOSWindow::devicePixelRatio()
const
415#if !defined(Q_OS_VISIONOS)
419 if (!m_view.window.windowScene.screen)
420 return screen()->devicePixelRatio();
424 return m_view.contentScaleFactor;
427void QIOSWindow::clearAccessibleCache()
429 if (isForeignWindow())
432 [quiview_cast(m_view) clearAccessibleCache];
435void QIOSWindow::requestUpdate()
437 static_cast<QIOSScreen *>(screen())->setUpdatesPaused(
false);
440void QIOSWindow::setMask(
const QRegion ®ion)
442 if (!region.isEmpty()) {
443 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
444 for (
const QRect &r : region)
445 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
446 CAShapeLayer *maskLayer = [CAShapeLayer layer];
447 maskLayer.path = maskPath;
448 m_view.layer.mask = maskLayer;
450 m_view.layer.mask = nil;
455CAEAGLLayer *QIOSWindow::eaglLayer()
const
457 Q_ASSERT([m_view.layer isKindOfClass:[CAEAGLLayer
class]]);
458 return static_cast<CAEAGLLayer *>(m_view.layer);
462#ifndef QT_NO_DEBUG_STREAM
465 QDebugStateSaver saver(debug);
467 debug <<
"QIOSWindow(" << (
const void *)window;
469 debug <<
", window=" << window->window();
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
493 return qt_objc_cast<QUIView *>(view);
496bool QIOSWindow::isForeignWindow()
const
498 return ![m_view isKindOfClass:QUIView.
class];
501UIView *QIOSWindow::view()
const
508#include "moc_qioswindow.cpp"
Combined button and popup list for selecting options.
bool isRunningOnVisionOS()
QDebug operator<<(QDebug debug, const QIOSWindow *window)
QUIView * quiview_cast(UIView *view)
Returns the view cast to a QUIview if possible.