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])
170 const Qt::WindowType type = window()->type();
171 return (type != Qt::Popup && type != Qt::ToolTip) || !window()->isActive();
174void QIOSWindow::setOpacity(qreal level)
176 m_view.alpha = qBound(0.0, level, 1.0);
179void QIOSWindow::setGeometry(
const QRect &rect)
181 m_normalGeometry = rect;
183 if (window()->windowState() != Qt::WindowNoState) {
184 QPlatformWindow::setGeometry(rect);
188 [m_view setNeedsLayout];
190 if (window()->inherits(
"QWidgetWindow")) {
194 QWindowSystemInterface::handleWindowStateChanged(window(), window()->windowState());
199 [m_view layoutIfNeeded];
208void QIOSWindow::applyGeometry(
const QRect &rect)
214 QPlatformWindow::setGeometry(rect);
216 m_view.frame = rect.toCGRect();
220 [m_view setNeedsLayout];
222 if (window()->inherits(
"QWidgetWindow"))
223 [m_view layoutIfNeeded];
226QMargins QIOSWindow::safeAreaMargins()
const
228 UIEdgeInsets safeAreaInsets = m_view.safeAreaInsets;
229 return QMargins(safeAreaInsets.left, safeAreaInsets.top,
230 safeAreaInsets.right, safeAreaInsets.bottom);
233bool QIOSWindow::isExposed()
const
235 return qApp->applicationState() != Qt::ApplicationSuspended
236 && window()->isVisible() && !window()->geometry().isEmpty();
239void QIOSWindow::setWindowState(Qt::WindowStates state)
244 qt_window_private(window())->windowState = state;
246 if (window()->isTopLevel() && window()->isVisible() && window()->isActive())
247 [m_view.qtViewController updateStatusBarProperties];
249 if (state & Qt::WindowMinimized) {
250 applyGeometry(QRect());
251 }
else if (state & (Qt::WindowFullScreen | Qt::WindowMaximized)) {
252 QRect uiWindowBounds = QRectF::fromCGRect(m_view.window.bounds).toRect();
253 if (NSProcessInfo.processInfo.iOSAppOnMac) {
261 applyGeometry(uiWindowBounds);
266 applyGeometry(uiWindowBounds);
268 QRect fullscreenGeometry = screen()->geometry();
269 QRect maximizedGeometry = fullscreenGeometry;
271#if !defined(Q_OS_VISIONOS)
272 if (!(window()->flags() & Qt::ExpandedClientAreaHint)) {
277 UIScreen *uiScreen = m_view.window.windowScene.screen;
278 UIEdgeInsets safeAreaInsets = m_view.window.safeAreaInsets;
279 if (m_view.window.bounds.size.width == uiScreen.bounds.size.width)
280 maximizedGeometry.adjust(safeAreaInsets.left, 0, -safeAreaInsets.right, 0);
281 if (m_view.window.bounds.size.height == uiScreen.bounds.size.height)
282 maximizedGeometry.adjust(0, safeAreaInsets.top, 0, -safeAreaInsets.bottom);
292 fullscreenGeometry = fullscreenGeometry.intersected(uiWindowBounds);
293 maximizedGeometry = maximizedGeometry.intersected(uiWindowBounds);
296 if (state & Qt::WindowFullScreen)
297 applyGeometry(fullscreenGeometry);
299 applyGeometry(maximizedGeometry);
302 applyGeometry(m_normalGeometry);
306void QIOSWindow::setParent(
const QPlatformWindow *parentWindow)
308 UIView *superview =
nullptr;
310 superview =
reinterpret_cast<UIView *>(parentWindow->winId());
311 else if (isQtApplication() && !isForeignWindow())
312 superview = rootViewForScreen(window()->screen()->handle());
315 [superview addSubview:m_view];
316 else if (quiview_cast(m_view.superview))
317 [m_view removeFromSuperview];
320void QIOSWindow::requestActivateWindow()
325 if (blockedByModal())
328 [m_view.window makeKeyWindow];
329 [m_view becomeFirstResponder];
331 if (window()->isTopLevel())
335void QIOSWindow::raiseOrLower(
bool raise)
340 NSArray<UIView *> *subviews = m_view.superview.subviews;
341 if (subviews.count == 1)
344 if (m_view.superview == m_view.qtViewController.view) {
347 for (
int i =
int(subviews.count) - 1; i >= 0; --i) {
348 UIView *view =
static_cast<UIView *>([subviews objectAtIndex:i]);
349 if (view.hidden || view == m_view || !view.qwindow)
351 int level =
static_cast<QIOSWindow *>(view.qwindow->handle())->windowLevel();
352 if (windowLevel() > level || (raise && windowLevel() == level)) {
353 [m_view.superview insertSubview:m_view aboveSubview:view];
357 [m_view.superview insertSubview:m_view atIndex:0];
361 [m_view.superview bringSubviewToFront:m_view];
363 [m_view.superview sendSubviewToBack:m_view];
367int QIOSWindow::windowLevel()
const
369 Qt::WindowType type = window()->type();
373 if (type == Qt::ToolTip)
375 else if (window()->flags() & Qt::WindowStaysOnTopHint)
377 else if (window()->isModal())
379 else if (type == Qt::Popup)
381 else if (type == Qt::SplashScreen)
383 else if (type == Qt::Tool)
389 QWindow *transientParent = window()->transientParent();
390 QIOSWindow *transientParentWindow = transientParent ?
static_cast<QIOSWindow *>(transientParent->handle()) : 0;
391 if (transientParentWindow)
392 level = qMax(transientParentWindow->windowLevel(), level);
397void QIOSWindow::applicationStateChanged(Qt::ApplicationState)
399 if (isForeignWindow())
402 if (window()->isExposed() != isExposed())
403 [quiview_cast(m_view) sendUpdatedExposeEvent];
406qreal QIOSWindow::devicePixelRatio()
const
408#if !defined(Q_OS_VISIONOS)
412 if (!m_view.window.windowScene.screen)
413 return screen()->devicePixelRatio();
417 return m_view.contentScaleFactor;
420void QIOSWindow::clearAccessibleCache()
422 if (isForeignWindow())
425 [quiview_cast(m_view) clearAccessibleCache];
428void QIOSWindow::requestUpdate()
430 static_cast<QIOSScreen *>(screen())->setUpdatesPaused(
false);
433void QIOSWindow::setMask(
const QRegion ®ion)
435 if (!region.isEmpty()) {
436 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
437 for (
const QRect &r : region)
438 CGPathAddRect(maskPath,
nullptr, r.toCGRect());
439 CAShapeLayer *maskLayer = [CAShapeLayer layer];
440 maskLayer.path = maskPath;
441 m_view.layer.mask = maskLayer;
443 m_view.layer.mask = nil;
448CAEAGLLayer *QIOSWindow::eaglLayer()
const
450 Q_ASSERT([m_view.layer isKindOfClass:[CAEAGLLayer
class]]);
451 return static_cast<CAEAGLLayer *>(m_view.layer);
455#ifndef QT_NO_DEBUG_STREAM
458 QDebugStateSaver saver(debug);
460 debug <<
"QIOSWindow(" << (
const void *)window;
462 debug <<
", window=" << window->window();
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
486 return qt_objc_cast<QUIView *>(view);
489bool QIOSWindow::isForeignWindow()
const
491 return ![m_view isKindOfClass:QUIView.
class];
494UIView *QIOSWindow::view()
const
501#include "moc_qioswindow.cpp"
bool isRunningOnVisionOS()
QDebug operator<<(QDebug debug, const QIOSWindow *window)
QUIView * quiview_cast(UIView *view)
Returns the view cast to a QUIview if possible.