iOS: Add support for foreign windows

Task-number: QTBUG-116183
Pick-to: 6.6
Change-Id: Ia92ee2d15f2e91a326ad342237fb0a83305c019f
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Tor Arne Vestbø 2023-08-17 18:03:33 +02:00
parent 199570e3b1
commit 17e7d98626
6 changed files with 69 additions and 23 deletions

View File

@ -441,6 +441,20 @@ private:
// -------------------------------------------------------------------------
#ifdef __OBJC__
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, T>::type
qt_objc_cast(id object)
{
if ([object isKindOfClass:[typename std::remove_pointer<T>::type class]])
return static_cast<T>(object);
return nil;
}
#endif
// -------------------------------------------------------------------------
QT_END_NAMESPACE
#endif // QCORE_MAC_P_H

View File

@ -56,16 +56,6 @@ NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions);
Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions);
Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions);
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, T>::type
qt_objc_cast(id object)
{
if ([object isKindOfClass:[typename std::remove_pointer<T>::type class]])
return static_cast<T>(object);
return nil;
}
QT_MANGLE_NAMESPACE(QNSView) *qnsview_cast(NSView *view);
// Misc

View File

@ -31,6 +31,7 @@ public:
bool hasCapability(Capability cap) const override;
QPlatformWindow *createPlatformWindow(QWindow *window) const override;
QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override;
QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
#if QT_CONFIG(opengl)

View File

@ -150,6 +150,8 @@ bool QIOSIntegration::hasCapability(Capability cap) const
return false;
case ApplicationState:
return true;
case ForeignWindows:
return true;
default:
return QPlatformIntegration::hasCapability(cap);
}
@ -160,6 +162,11 @@ QPlatformWindow *QIOSIntegration::createPlatformWindow(QWindow *window) const
return new QIOSWindow(window);
}
QPlatformWindow *QIOSIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const
{
return new QIOSWindow(window, nativeHandle);
}
QPlatformBackingStore *QIOSIntegration::createPlatformBackingStore(QWindow *window) const
{
return new QRhiBackingStore(window);

View File

@ -21,7 +21,7 @@ class QIOSWindow : public QObject, public QPlatformWindow
Q_OBJECT
public:
explicit QIOSWindow(QWindow *window);
explicit QIOSWindow(QWindow *window, WId nativeHandle = 0);
~QIOSWindow();
void setGeometry(const QRect &rect) override;
@ -66,7 +66,7 @@ private:
void applicationStateChanged(Qt::ApplicationState state);
void applyGeometry(const QRect &rect);
QUIView *m_view;
UIView *m_view;
QRect m_normalGeometry;
int m_windowLevel;
@ -82,6 +82,8 @@ private:
QDebug operator<<(QDebug debug, const QIOSWindow *window);
#endif
QT_MANGLE_NAMESPACE(QUIView) *quiview_cast(UIView *view);
QT_END_NAMESPACE
#endif // QIOSWINDOW_H

View File

@ -29,10 +29,14 @@
QT_BEGIN_NAMESPACE
QIOSWindow::QIOSWindow(QWindow *window)
QIOSWindow::QIOSWindow(QWindow *window, WId nativeHandle)
: QPlatformWindow(window)
, m_windowLevel(0)
{
if (nativeHandle) {
m_view = reinterpret_cast<UIView *>(nativeHandle);
[m_view retain];
} else {
#ifdef Q_OS_IOS
if (window->surfaceType() == QSurface::RasterSurface)
window->setSurfaceType(QSurface::MetalSurface);
@ -42,6 +46,7 @@ QIOSWindow::QIOSWindow(QWindow *window)
else
#endif
m_view = [[QUIView alloc] initWithQIOSWindow:this];
}
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QIOSWindow::applicationStateChanged);
@ -80,7 +85,7 @@ QIOSWindow::~QIOSWindow()
clearAccessibleCache();
m_view.platformWindow = 0;
quiview_cast(m_view).platformWindow = 0;
[m_view removeFromSuperview];
[m_view release];
}
@ -120,7 +125,7 @@ void QIOSWindow::setVisible(bool visible)
if (visible && shouldAutoActivateWindow()) {
if (!window()->property("_q_showWithoutActivating").toBool())
requestActivateWindow();
} else if (!visible && [m_view isActiveWindow]) {
} else if (!visible && [quiview_cast(m_view) isActiveWindow]) {
// Our window was active/focus window but now hidden, so relinquish
// focus to the next possible window in the stack.
NSArray<UIView *> *subviews = m_view.viewController.view.subviews;
@ -340,8 +345,11 @@ void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientatio
void QIOSWindow::applicationStateChanged(Qt::ApplicationState)
{
if (isForeignWindow())
return;
if (window()->isExposed() != isExposed())
[m_view sendUpdatedExposeEvent];
[quiview_cast(m_view) sendUpdatedExposeEvent];
}
qreal QIOSWindow::devicePixelRatio() const
@ -351,7 +359,10 @@ qreal QIOSWindow::devicePixelRatio() const
void QIOSWindow::clearAccessibleCache()
{
[m_view clearAccessibleCache];
if (isForeignWindow())
return;
[quiview_cast(m_view) clearAccessibleCache];
}
void QIOSWindow::requestUpdate()
@ -394,6 +405,27 @@ QDebug operator<<(QDebug debug, const QIOSWindow *window)
}
#endif // !QT_NO_DEBUG_STREAM
/*!
Returns the view cast to a QUIview if possible.
If the view is not a QUIview, nil is returned, which is safe to
send messages to, effectively making [quiview_cast(view) message]
a no-op.
For extra verbosity and clearer code, please consider checking
that the platform window is not a foreign window before using
this cast, via QPlatformWindow::isForeignWindow().
Do not use this method solely to check for foreign windows, as
that will make the code harder to read for people not working
primarily on iOS, who do not know the difference between the
UIView and QUIView cases.
*/
QUIView *quiview_cast(UIView *view)
{
return qt_objc_cast<QUIView *>(view);
}
QT_END_NAMESPACE
#include "moc_qioswindow.cpp"