diff options
author | Liang Qi <liang.qi@qt.io> | 2017-02-17 20:10:34 +0100 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2017-02-17 20:10:34 +0100 |
commit | bc4cd465dd5df82e13f3c7709166ee11289d219f (patch) | |
tree | d6323aaed6383e589fbefb6057648c22bb187c76 /src/plugins | |
parent | 43daefb0962794b2df256cae1098e889b9b36f12 (diff) | |
parent | 07745d7bfbf6c8d83e0243150d8ce934675dea87 (diff) |
Merge remote-tracking branch 'origin/5.9' into dev
Conflicts:
qmake/Makefile.unix
Change-Id: Ia18e391198222eef34ffa2df6f683e052058d032
Diffstat (limited to 'src/plugins')
18 files changed, 308 insertions, 197 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index a7cc19b3bf..9e688f4d1b 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -255,7 +255,8 @@ void QCocoaGLContext::setActiveWindow(QWindow *window) QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); cocoaWindow->setCurrentContext(this); - [(QNSView *) cocoaWindow->view() setQCocoaGLContext:this]; + Q_ASSERT(!cocoaWindow->isForeignWindow()); + [qnsview_cast(cocoaWindow->view()) setQCocoaGLContext:this]; } void QCocoaGLContext::updateSurfaceFormat() diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index e177a24e73..8e47974d12 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -581,8 +581,8 @@ void QCocoaMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, // The calls above block, and also swallow any mouse release event, // so we need to clear any mouse button that triggered the menu popup. - if ([view isKindOfClass:[QNSView class]]) - [(QNSView *)view resetMouseButtons]; + if (!cocoaWindow->isForeignWindow()) + [qnsview_cast(view) resetMouseButtons]; } void QCocoaMenu::dismiss() diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index bb015c2419..f60597fe42 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -217,7 +217,7 @@ public: bool windowShouldClose(); bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const; - void setSynchedWindowStateFromWindow(); + void reportCurrentWindowState(bool unconditionally = false); NSInteger windowLevel(Qt::WindowFlags flags); NSUInteger windowStyleMask(Qt::WindowFlags flags); @@ -284,10 +284,15 @@ protected: QCocoaNSWindow *createNSWindow(bool shouldBeChildNSWindow, bool shouldBePanel); QRect nativeWindowGeometry() const; - void syncWindowState(Qt::WindowState newState); void reinsertChildWindow(QCocoaWindow *child); void removeChildWindow(QCocoaWindow *child); + Qt::WindowState windowState() const; + void applyWindowState(Qt::WindowState newState); + void toggleMaximized(); + void toggleFullScreen(); + bool isTransitioningToFullScreen() const; + // private: public: // for QNSView friend class QCocoaBackingStore; @@ -304,8 +309,7 @@ public: // for QNSView bool m_viewIsToBeEmbedded; // true if the m_view is intended to be embedded in a "foreign" NSView hiearchy Qt::WindowFlags m_windowFlags; - bool m_effectivelyMaximized; - Qt::WindowState m_synchedWindowState; + Qt::WindowState m_lastReportedWindowState; Qt::WindowModality m_windowModality; QPointer<QWindow> m_enterLeaveTargetWindow; bool m_windowUnderMouse; @@ -339,11 +343,6 @@ public: // for QNSView int m_topContentBorderThickness; int m_bottomContentBorderThickness; - // used by showFullScreen in fake mode - QRect m_normalGeometry; - Qt::WindowFlags m_oldWindowFlags; - NSApplicationPresentationOptions m_presentationOptions; - struct BorderRange { BorderRange(quintptr i, int u, int l) : identifier(i), upper(u), lower(l) { } quintptr identifier; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 114efc3e92..918f376446 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -87,6 +87,36 @@ static void qt_closePopups() } } +@interface NSWindow (FullScreenProperty) +@property(readonly) BOOL qt_fullScreen; +@end + +@implementation NSWindow (FullScreenProperty) + ++ (void)load +{ + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserverForName:NSWindowDidEnterFullScreenNotification object:nil queue:nil + usingBlock:^(NSNotification *notification) { + objc_setAssociatedObject(notification.object, @selector(qt_fullScreen), + [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN); + } + ]; + [center addObserverForName:NSWindowDidExitFullScreenNotification object:nil queue:nil + usingBlock:^(NSNotification *notification) { + objc_setAssociatedObject(notification.object, @selector(qt_fullScreen), + nil, OBJC_ASSOCIATION_RETAIN); + } + ]; +} + +- (BOOL)qt_fullScreen +{ + NSNumber *number = objc_getAssociatedObject(self, @selector(qt_fullScreen)); + return [number boolValue]; +} +@end + @implementation QNSWindowHelper @synthesize window = _window; @@ -409,8 +439,7 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_nsWindow(0) , m_viewIsEmbedded(false) , m_viewIsToBeEmbedded(false) - , m_effectivelyMaximized(false) - , m_synchedWindowState(Qt::WindowActive) + , m_lastReportedWindowState(Qt::WindowNoState) , m_windowModality(Qt::NonModal) , m_windowUnderMouse(false) , m_inConstructor(true) @@ -435,7 +464,6 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_drawContentBorderGradient(false) , m_topContentBorderThickness(0) , m_bottomContentBorderThickness(0) - , m_normalGeometry(QRect(0,0,-1,-1)) , m_hasWindowFilePath(false) { qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::QCocoaWindow" << window(); @@ -728,7 +756,7 @@ void QCocoaWindow::setVisible(bool visible) // setWindowState might have been called while the window was hidden and // will not change the NSWindow state in that case. Sync up here: - syncWindowState(window()->windowState()); + applyWindowState(window()->windowState()); if (window()->windowState() != Qt::WindowMinimized) { if ((window()->modality() == Qt::WindowModal @@ -930,7 +958,8 @@ void QCocoaWindow::setWindowZoomButton(Qt::WindowFlags flags) // in line with the platform style guidelines. bool fixedSizeNoZoom = (windowMinimumSize().isValid() && windowMaximumSize().isValid() && windowMinimumSize() == windowMaximumSize()); - bool customizeNoZoom = ((flags & Qt::CustomizeWindowHint) && !(flags & Qt::WindowMaximizeButtonHint)); + bool customizeNoZoom = ((flags & Qt::CustomizeWindowHint) + && !(flags & (Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint))); [[m_nsWindow standardWindowButton:NSWindowZoomButton] setEnabled:!(fixedSizeNoZoom || customizeNoZoom)]; } @@ -965,13 +994,16 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags) setWindowZoomButton(flags); } + if (m_nsWindow) + m_nsWindow.ignoresMouseEvents = flags & Qt::WindowTransparentForInput; + m_windowFlags = flags; } void QCocoaWindow::setWindowState(Qt::WindowState state) { if (window()->isVisible()) - syncWindowState(state); // Window state set for hidden windows take effect when show() is called. + applyWindowState(state); // Window state set for hidden windows take effect when show() is called } void QCocoaWindow::setWindowTitle(const QString &title) @@ -1247,6 +1279,9 @@ void QCocoaWindow::windowDidMove() return; [qnsview_cast(m_view) updateGeometry]; + + // Moving a window might bring it out of maximized state + reportCurrentWindowState(); } void QCocoaWindow::windowDidResize() @@ -1259,6 +1294,9 @@ void QCocoaWindow::windowDidResize() clipChildWindows(); [qnsview_cast(m_view) updateGeometry]; + + if (!m_view.inLiveResize) + reportCurrentWindowState(); } void QCocoaWindow::viewDidChangeFrame() @@ -1280,10 +1318,7 @@ void QCocoaWindow::viewDidChangeGlobalFrame() void QCocoaWindow::windowDidEndLiveResize() { - if (m_synchedWindowState == Qt::WindowMaximized && ![m_nsWindow isZoomed]) { - m_effectivelyMaximized = false; - [qnsview_cast(m_view) notifyWindowStateChanged:Qt::WindowNoState]; - } + reportCurrentWindowState(); } void QCocoaWindow::windowDidBecomeKey() @@ -1320,22 +1355,37 @@ void QCocoaWindow::windowDidResignKey() void QCocoaWindow::windowDidMiniaturize() { - [qnsview_cast(m_view) notifyWindowStateChanged:Qt::WindowMinimized]; + reportCurrentWindowState(); } void QCocoaWindow::windowDidDeminiaturize() { - [qnsview_cast(m_view) notifyWindowStateChanged:Qt::WindowNoState]; + reportCurrentWindowState(); } void QCocoaWindow::windowDidEnterFullScreen() { - [qnsview_cast(m_view) notifyWindowStateChanged:Qt::WindowFullScreen]; + Q_ASSERT_X(m_nsWindow.qt_fullScreen, "QCocoaWindow", + "FullScreen category processes window notifications first"); + + reportCurrentWindowState(); } void QCocoaWindow::windowDidExitFullScreen() { - [qnsview_cast(m_view) notifyWindowStateChanged:Qt::WindowNoState]; + Q_ASSERT_X(!m_nsWindow.qt_fullScreen, "QCocoaWindow", + "FullScreen category processes window notifications first"); + + Qt::WindowState requestedState = window()->windowState(); + + // Deliver update of QWindow state + reportCurrentWindowState(); + + if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) { + // We were only going out of full screen as an intermediate step before + // progressing into the final step, so re-sync the desired state. + applyWindowState(requestedState); + } } void QCocoaWindow::windowDidOrderOffScreen() @@ -1399,12 +1449,6 @@ bool QCocoaWindow::windowShouldClose() // -------------------------------------------------------------------------- -void QCocoaWindow::setSynchedWindowStateFromWindow() -{ - if (QWindow *w = window()) - m_synchedWindowState = w->windowState(); -} - bool QCocoaWindow::windowIsPopupType(Qt::WindowType type) const { if (type == Qt::Widget) @@ -1579,15 +1623,8 @@ void QCocoaWindow::recreateWindowIfNeeded() } // Set properties after the window has been made a child NSWindow - m_nsWindow.styleMask = NSBorderlessWindowMask; - m_nsWindow.hasShadow = NO; - m_nsWindow.level = NSNormalWindowLevel; - NSWindowCollectionBehavior collectionBehavior = - NSWindowCollectionBehaviorManaged | NSWindowCollectionBehaviorIgnoresCycle - | NSWindowCollectionBehaviorFullScreenAuxiliary; - m_nsWindow.animationBehavior = NSWindowAnimationBehaviorNone; - m_nsWindow.collectionBehavior = collectionBehavior; setCocoaGeometry(windowGeometry()); + setWindowFlags(window()->flags()); } else { // Child windows have no NSWindow, link the NSViews instead. if ([m_view superview]) @@ -1604,9 +1641,6 @@ void QCocoaWindow::recreateWindowIfNeeded() [m_view setHidden:!window()->isVisible()]; } - m_nsWindow.ignoresMouseEvents = - (window()->flags() & Qt::WindowTransparentForInput) == Qt::WindowTransparentForInput; - const qreal opacity = qt_window_private(window())->opacity; if (!qFuzzyCompare(opacity, qreal(1.0))) setOpacity(opacity); @@ -1674,62 +1708,49 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBeChildNSWindow, bool sh Qt::WindowType type = window()->type(); Qt::WindowFlags flags = window()->flags(); - NSUInteger styleMask; - if (shouldBeChildNSWindow) { - styleMask = NSBorderlessWindowMask; - } else { - styleMask = windowStyleMask(flags); - } - QCocoaNSWindow *createdWindow = 0; - - // Use NSPanel for popup-type windows. (Popup, Tool, ToolTip, SplashScreen) - // and dialogs - if (shouldBePanel) { - QNSPanel *panel = [[QNSPanel alloc] initWithContentRect:frame screen:cocoaScreen->nativeScreen() - styleMask: styleMask qPlatformWindow:this]; - - if ((type & Qt::Popup) == Qt::Popup) - [panel setHasShadow:YES]; - - // Qt::Tool windows hide on app deactivation, unless Qt::WA_MacAlwaysShowToolWindow is set. - QVariant showWithoutActivating = QPlatformWindow::window()->property("_q_macAlwaysShowToolWindow"); - bool shouldHideOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && - !(showWithoutActivating.isValid() && showWithoutActivating.toBool()); - [panel setHidesOnDeactivate: shouldHideOnDeactivate]; + // Create NSWindow + Class windowClass = shouldBePanel ? [QNSPanel class] : [QNSWindow class]; + NSUInteger styleMask = shouldBeChildNSWindow ? NSBorderlessWindowMask : windowStyleMask(flags); + QCocoaNSWindow *window = [[windowClass alloc] initWithContentRect:frame + screen:cocoaScreen->nativeScreen() styleMask:styleMask qPlatformWindow:this]; - // Make popup windows show on the same desktop as the parent full-screen window. - [panel setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - if ((type & Qt::Popup) == Qt::Popup) - [panel setAnimationBehavior:NSWindowAnimationBehaviorUtilityWindow]; + window.restorable = NO; + window.level = shouldBeChildNSWindow ? NSNormalWindowLevel : windowLevel(flags); - createdWindow = panel; - } else { - createdWindow = [[QNSWindow alloc] initWithContentRect:frame screen:cocoaScreen->nativeScreen() - styleMask: styleMask qPlatformWindow:this]; + if (!isOpaque()) { + window.backgroundColor = [NSColor clearColor]; + window.opaque = NO; } - if ([createdWindow respondsToSelector:@selector(setRestorable:)]) - [createdWindow setRestorable: NO]; + Q_ASSERT(!(shouldBePanel && shouldBeChildNSWindow)); - NSInteger level = windowLevel(flags); - [createdWindow setLevel:level]; + if (shouldBePanel) { + // Qt::Tool windows hide on app deactivation, unless Qt::WA_MacAlwaysShowToolWindow is set + window.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && + !qt_mac_resolveOption(false, QPlatformWindow::window(), "_q_macAlwaysShowToolWindow", ""); - // OpenGL surfaces can be ordered either above(default) or below the NSWindow. - // When ordering below the window must be tranclucent and have a clear background color. - static GLint openglSourfaceOrder = qt_mac_resolveOption(1, "QT_MAC_OPENGL_SURFACE_ORDER"); + // Make popup windows show on the same desktop as the parent full-screen window + window.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary; - bool isTranslucent = window()->format().alphaBufferSize() > 0 - || (surface()->supportsOpenGL() && openglSourfaceOrder == -1); - if (isTranslucent) { - [createdWindow setBackgroundColor:[NSColor clearColor]]; - [createdWindow setOpaque:NO]; + if ((type & Qt::Popup) == Qt::Popup) { + window.hasShadow = YES; + window.animationBehavior = NSWindowAnimationBehaviorUtilityWindow; + } + } else if (shouldBeChildNSWindow) { + window.collectionBehavior = + NSWindowCollectionBehaviorManaged + | NSWindowCollectionBehaviorIgnoresCycle + | NSWindowCollectionBehaviorFullScreenAuxiliary; + window.hasShadow = NO; + window.animationBehavior = NSWindowAnimationBehaviorNone; } - m_windowModality = window()->modality(); + // Persist modality so we can detect changes later on + m_windowModality = QPlatformWindow::window()->modality(); - applyContentBorderThickness(createdWindow); + applyContentBorderThickness(window); - return createdWindow; + return window; } void QCocoaWindow::removeMonitor() @@ -1753,85 +1774,155 @@ QRect QCocoaWindow::nativeWindowGeometry() const return qRect; } -// Syncs the NSWindow minimize/maximize/fullscreen state with the current QWindow state -void QCocoaWindow::syncWindowState(Qt::WindowState newState) +/*! + Applies the given state to the NSWindow, going in/out of minimize/zoomed/fullscreen + + When this is called from QWindow::setWindowState(), the QWindow state has not been + updated yet, so window()->windowState() will reflect the previous state that was + reported to QtGui. +*/ +void QCocoaWindow::applyWindowState(Qt::WindowState newState) { + const Qt::WindowState currentState = windowState(); + if (newState == currentState) + return; + if (!m_nsWindow) return; - // if content view width or height is 0 then the window animations will crash so - // do nothing except set the new state - NSRect contentRect = m_view.frame; - if (contentRect.size.width <= 0 || contentRect.size.height <= 0) { + + const NSSize contentSize = m_view.frame.size; + if (contentSize.width <= 0 || contentSize.height <= 0) { + // If content view width or height is 0 then the window animations will crash so + // do nothing. We report the current state back to reflect the failed operation. qWarning("invalid window content view size, check your window geometry"); - m_synchedWindowState = newState; + reportCurrentWindowState(true); return; } - Qt::WindowState predictedState = newState; - if ((m_synchedWindowState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized)) { - const int styleMask = [m_nsWindow styleMask]; - const bool usePerform = styleMask & NSResizableWindowMask; - [m_nsWindow setStyleMask:styleMask | NSResizableWindowMask]; - if (usePerform) - [m_nsWindow performZoom : m_nsWindow]; // toggles - else - [m_nsWindow zoom : m_nsWindow]; // toggles - [m_nsWindow setStyleMask:styleMask]; + if (m_nsWindow.styleMask & NSUtilityWindowMask) { + // Utility panels cannot be fullscreen + qWarning() << window()->type() << "windows can not be made full screen"; + reportCurrentWindowState(true); + return; } - if ((m_synchedWindowState & Qt::WindowMinimized) != (newState & Qt::WindowMinimized)) { - if (newState & Qt::WindowMinimized) { - if ([m_nsWindow styleMask] & NSMiniaturizableWindowMask) - [m_nsWindow performMiniaturize : m_nsWindow]; - else - [m_nsWindow miniaturize : m_nsWindow]; - } else { - [m_nsWindow deminiaturize : m_nsWindow]; - } + const id sender = m_nsWindow; + + // First we need to exit states that can't transition directly to other states + switch (currentState) { + case Qt::WindowMinimized: + [m_nsWindow deminiaturize:sender]; + Q_ASSERT_X(windowState() != Qt::WindowMinimized, "QCocoaWindow", + "[NSWindow deminiaturize:] is synchronous"); + break; + case Qt::WindowFullScreen: { + toggleFullScreen(); + // Exiting fullscreen is not synchronous, so we need to wait for the + // NSWindowDidExitFullScreenNotification before continuing to apply + // the new state. + return; } - - const bool effMax = m_effectivelyMaximized; - if ((m_synchedWindowState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized) || (m_effectivelyMaximized && newState == Qt::WindowNoState)) { - if ((m_synchedWindowState & Qt::WindowFullScreen) == (newState & Qt::WindowFullScreen)) { - [m_nsWindow zoom : m_nsWindow]; // toggles - m_effectivelyMaximized = !effMax; - } else if (!(newState & Qt::WindowMaximized)) { - // it would be nice to change the target geometry that toggleFullScreen will animate toward - // but there is no known way, so the maximized state is not possible at this time - predictedState = static_cast<Qt::WindowState>(static_cast<int>(newState) | Qt::WindowMaximized); - m_effectivelyMaximized = true; - } + default: + Q_FALLTHROUGH(); } - if ((m_synchedWindowState & Qt::WindowFullScreen) != (newState & Qt::WindowFullScreen)) { - if (window()->flags() & Qt::WindowFullscreenButtonHint) { - if (m_effectivelyMaximized && m_synchedWindowState == Qt::WindowFullScreen) - predictedState = Qt::WindowMaximized; - [m_nsWindow toggleFullScreen : m_nsWindow]; - } else { - if (newState & Qt::WindowFullScreen) { - QScreen *screen = window()->screen(); - if (screen) { - if (m_normalGeometry.width() < 0) { - m_oldWindowFlags = m_windowFlags; - window()->setFlags(window()->flags() | Qt::FramelessWindowHint); - m_normalGeometry = nativeWindowGeometry(); - setGeometry(screen->geometry()); - m_presentationOptions = [NSApp presentationOptions]; - [NSApp setPresentationOptions : m_presentationOptions | NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock]; - } - } - } else { - window()->setFlags(m_oldWindowFlags); - setGeometry(m_normalGeometry); - m_normalGeometry.setRect(0, 0, -1, -1); - [NSApp setPresentationOptions : m_presentationOptions]; - } + // Then we apply the new state if needed + if (newState == windowState()) + return; + + switch (newState) { + case Qt::WindowFullScreen: + toggleFullScreen(); + break; + case Qt::WindowMaximized: + toggleMaximized(); + break; + case Qt::WindowMinimized: + [m_nsWindow miniaturize:sender]; + break; + case Qt::WindowNoState: + switch (windowState()) { + case Qt::WindowMaximized: + toggleMaximized(); + default: + Q_FALLTHROUGH(); } + break; + default: + Q_UNREACHABLE(); } +} + +void QCocoaWindow::toggleMaximized() +{ + // The NSWindow needs to be resizable, otherwise the window will + // not be possible to zoom back to non-zoomed state. + const bool wasResizable = m_nsWindow.styleMask & NSResizableWindowMask; + m_nsWindow.styleMask |= NSResizableWindowMask; + + const id sender = m_nsWindow; + [m_nsWindow zoom:sender]; + + if (!wasResizable) + m_nsWindow.styleMask &= ~NSResizableWindowMask; +} + +void QCocoaWindow::toggleFullScreen() +{ + // The NSWindow needs to be resizable, otherwise we'll end up with + // the normal window geometry, centered in the middle of the screen + // on a black background. + const bool wasResizable = m_nsWindow.styleMask & NSResizableWindowMask; + m_nsWindow.styleMask |= NSResizableWindowMask; + + // It also needs to have the correct collection behavior for the + // toggleFullScreen call to have an effect. + const bool wasFullScreenEnabled = m_nsWindow.collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary; + m_nsWindow.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary; + + const id sender = m_nsWindow; + [m_nsWindow toggleFullScreen:sender]; + + if (!wasResizable) + m_nsWindow.styleMask &= ~NSResizableWindowMask; + if (!wasFullScreenEnabled) + m_nsWindow.collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; +} + +bool QCocoaWindow::isTransitioningToFullScreen() const +{ + NSWindow *window = m_view.window; + return window.styleMask & NSFullScreenWindowMask && !window.qt_fullScreen; +} + +Qt::WindowState QCocoaWindow::windowState() const +{ + // FIXME: Support compound states (Qt::WindowStates) + + NSWindow *window = m_view.window; + if (window.miniaturized) + return Qt::WindowMinimized; + if (window.qt_fullScreen) + return Qt::WindowFullScreen; + if ((window.zoomed && !isTransitioningToFullScreen()) + || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen())) + return Qt::WindowMaximized; + + // Note: We do not report Qt::WindowActive, even if isActive() + // is true, as QtGui does not expect this window state to be set. + + return Qt::WindowNoState; +} + +void QCocoaWindow::reportCurrentWindowState(bool unconditionally) +{ + Qt::WindowState currentState = windowState(); + if (!unconditionally && currentState == m_lastReportedWindowState) + return; - // New state is now the current synched state - m_synchedWindowState = predictedState; + QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>( + window(), currentState, m_lastReportedWindowState); + m_lastReportedWindowState = currentState; } bool QCocoaWindow::setWindowModified(bool modified) diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index 84be7eb797..75a508370f 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -100,8 +100,6 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); - (void)drawRect:(NSRect)dirtyRect; - (void)drawBackingStoreUsingCoreGraphics:(NSRect)dirtyRect; - (void)updateGeometry; -- (void)notifyWindowStateChanged:(Qt::WindowState)newState; -- (void)notifyWindowWillZoom:(BOOL)willZoom; - (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification; - (void)viewDidHide; - (void)viewDidUnhide; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 7ce407f7d0..fbf4f99f2e 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -339,21 +339,6 @@ static bool _q_dontOverrideCtrlLMB = false; } } -- (void)notifyWindowStateChanged:(Qt::WindowState)newState -{ - // If the window was maximized, then fullscreen, then tried to go directly to "normal" state, - // this notification will say that it is "normal", but it will still look maximized, and - // if you called performZoom it would actually take it back to "normal". - // So we should say that it is maximized because it actually is. - if (newState == Qt::WindowNoState && m_platformWindow->m_effectivelyMaximized) - newState = Qt::WindowMaximized; - QWindowSystemInterface::handleWindowStateChanged(m_platformWindow->window(), newState); - // We want to read the window state back from the window, - // but the event we just sent may be asynchronous. - QWindowSystemInterface::flushWindowSystemEvents(); - m_platformWindow->setSynchedWindowStateFromWindow(); -} - - (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification { Q_UNUSED(textInputContextKeyboardSelectionDidChangeNotification) @@ -363,14 +348,6 @@ static bool _q_dontOverrideCtrlLMB = false; } } -- (void)notifyWindowWillZoom:(BOOL)willZoom -{ - Qt::WindowState newState = willZoom ? Qt::WindowMaximized : Qt::WindowNoState; - if (!willZoom) - m_platformWindow->m_effectivelyMaximized = false; - [self notifyWindowStateChanged:newState]; -} - - (void)viewDidHide { m_platformWindow->obscureWindow(); diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.h b/src/plugins/platforms/cocoa/qnswindowdelegate.h index a465b249c5..d2078b5786 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.h +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.h @@ -52,7 +52,6 @@ - (id)initWithQCocoaWindow:(QCocoaWindow *)cocoaWindow; - (BOOL)windowShouldClose:(NSNotification *)notification; -- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame; - (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu; - (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard; diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm index 2d5afa9375..ce74aa9973 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm @@ -41,6 +41,7 @@ #include "qcocoahelpers.h" #include <QDebug> +#include <qpa/qplatformscreen.h> #include <qpa/qwindowsysteminterface.h> @implementation QNSWindowDelegate @@ -62,13 +63,19 @@ return YES; } - -- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame +/*! + Overridden to ensure that the zoomed state always results in a maximized + window, which would otherwise not be the case for borderless windows. +*/ +- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)newFrame { Q_UNUSED(newFrame); - if (m_cocoaWindow && !m_cocoaWindow->isForeignWindow()) - [qnsview_cast(m_cocoaWindow->view()) notifyWindowWillZoom:![window isZoomed]]; - return YES; + + // We explicitly go through the QScreen API here instead of just using + // window.screen.visibleFrame directly, as that ensures we have the same + // behavior for both use-cases/APIs. + Q_ASSERT(window == m_cocoaWindow->nativeWindow()); + return m_cocoaWindow->screen()->availableGeometry().toCGRect(); } - (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.cpp index 8a059ba973..1abc430da6 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.cpp @@ -122,13 +122,13 @@ EGLNativeWindowType QEglFSEmulatorIntegration::createNativeWindow(QPlatformWindo { Q_UNUSED(size); Q_UNUSED(format); - static QAtomicInt uniqueWindowId(1); QEglFSEmulatorScreen *screen = static_cast<QEglFSEmulatorScreen *>(platformWindow->screen()); if (screen && setDisplay) { // Let the emulator know which screen the window surface is attached to setDisplay(screen->id()); } - return EGLNativeWindowType(qintptr(uniqueWindowId.fetchAndAddRelaxed(1))); + static QBasicAtomicInt uniqueWindowId = Q_BASIC_ATOMIC_INITIALIZER(0); + return EGLNativeWindowType(qintptr(1 + uniqueWindowId.fetchAndAddRelaxed(1))); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h index 27632de688..a5c05bf1a3 100644 --- a/src/plugins/platforms/windows/qtwindowsglobal.h +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -56,6 +56,10 @@ # define WM_GESTURE 0x0119 #endif +#ifndef WM_DPICHANGED +# define WM_DPICHANGED 0x02E0 +#endif + QT_BEGIN_NAMESPACE namespace QtWindows @@ -96,6 +100,7 @@ enum WindowsEventType // Simplify event types FocusInEvent = WindowEventFlag + 17, FocusOutEvent = WindowEventFlag + 18, WhatsThisEvent = WindowEventFlag + 19, + DpiChangedEvent = WindowEventFlag + 21, MouseEvent = MouseEventFlag + 1, MouseWheelEvent = MouseEventFlag + 2, CursorEvent = MouseEventFlag + 3, @@ -266,6 +271,8 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI #endif case WM_GESTURE: return QtWindows::GestureEvent; + case WM_DPICHANGED: + return QtWindows::DpiChangedEvent; default: break; } diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index fefc848d01..5745fc6d19 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -1088,6 +1088,15 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; #endif } break; + case QtWindows::DpiChangedEvent: { + platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); + const RECT *prcNewWindow = reinterpret_cast<RECT *>(lParam); + SetWindowPos(hwnd, NULL, prcNewWindow->left, prcNewWindow->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); + platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); + return true; + } #if !defined(QT_NO_SESSIONMANAGER) case QtWindows::QueryEndSessionApplicationEvent: { QWindowsSessionManager *sessionManager = platformSessionManager(); diff --git a/src/plugins/platforms/windows/qwindowsglcontext.h b/src/plugins/platforms/windows/qwindowsglcontext.h index 23d2fd0d09..dfaa428520 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.h +++ b/src/plugins/platforms/windows/qwindowsglcontext.h @@ -48,6 +48,7 @@ #include <vector> QT_BEGIN_NAMESPACE +#ifndef QT_NO_OPENGL class QDebug; @@ -227,7 +228,7 @@ private: GLenum (APIENTRY * m_getGraphicsResetStatus)(); bool m_lost; }; - +#endif QT_END_NAMESPACE #endif // QWINDOWSGLCONTEXT_H diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index bd598630ba..24fb12d27a 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -82,6 +82,7 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) if (GetMonitorInfo(hMonitor, &info) == FALSE) return false; + data->hMonitor = hMonitor; data->geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1)); data->availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1)); data->name = QString::fromWCharArray(info.szDevice); diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 00722e2fa4..9a8997326b 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -69,6 +69,7 @@ struct QWindowsScreenData QString name; Qt::ScreenOrientation orientation = Qt::LandscapeOrientation; qreal refreshRateHz = 60; + HMONITOR hMonitor = nullptr; }; class QWindowsScreen : public QPlatformScreen diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 0b6318ce9f..121fdaeabd 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -43,7 +43,11 @@ #include "qwindowsscreen.h" #include "qwindowsintegration.h" #include "qwindowsnativeinterface.h" -#include "qwindowsglcontext.h" +#if QT_CONFIG(dynamicgl) +# include "qwindowsglcontext.h" +#else +# include "qwindowsopenglcontext.h" +#endif #ifdef QT_NO_CURSOR # include "qwindowscursor.h" #endif @@ -1546,10 +1550,16 @@ void QWindowsWindow::handleGeometryChange() && !(m_data.geometry.width() > previousGeometry.width() || m_data.geometry.height() > previousGeometry.height())) { fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), true); } - if (previousGeometry.topLeft() != m_data.geometry.topLeft()) { - QPlatformScreen *newScreen = screenForGeometry(m_data.geometry); - if (newScreen != screen()) - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); + if (!parent() && previousGeometry.topLeft() != m_data.geometry.topLeft()) { + HMONITOR hMonitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTONULL); + QPlatformScreen *currentScreen = screen(); + const auto screens = QWindowsContext::instance()->screenManager().screens(); + auto newScreenIt = std::find_if(screens.begin(), screens.end(), [&](QWindowsScreen *s) { + return s->data().hMonitor == hMonitor + && s->data().flags & QWindowsScreenData::VirtualDesktop; + }); + if (newScreenIt != screens.end() && *newScreenIt != currentScreen) + QWindowSystemInterface::handleWindowScreenChanged(window(), (*newScreenIt)->screen()); } if (testFlag(SynchronousGeometryChangeEvent)) QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); @@ -1629,7 +1639,8 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, if (message == WM_ERASEBKGND) // Backing store - ignored. return true; // Ignore invalid update bounding rectangles - if (!GetUpdateRect(m_data.hwnd, 0, FALSE)) + RECT updateRect; + if (!GetUpdateRect(m_data.hwnd, &updateRect, FALSE)) return false; PAINTSTRUCT ps; @@ -1653,7 +1664,7 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, // we still need to send isExposed=true, for compatibility. // Our tests depend on it. fireExpose(QRegion(qrectFromRECT(ps.rcPaint)), true); - if (!QWindowsContext::instance()->asyncExpose()) + if (qSizeOfRect(updateRect) == m_data.geometry.size() && !QWindowsContext::instance()->asyncExpose()) QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); EndPaint(hwnd, &ps); @@ -2121,8 +2132,12 @@ void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled) void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const { - const QWindowsGeometryHint hint(window(), m_data.customMargins); - hint.applyToMinMaxInfo(m_data.hwnd, mmi); + // We don't apply the min/max size hint as we change the dpi, because we did not adjust the + // QScreen of the window yet so we don't have the min/max with the right ratio + if (!testFlag(QWindowsWindow::WithinDpiChanged)) { + const QWindowsGeometryHint hint(window(), m_data.customMargins); + hint.applyToMinMaxInfo(m_data.hwnd, mmi); + } if ((testFlag(WithinMaximize) || (window()->windowState() == Qt::WindowMinimized)) && (m_data.flags & Qt::FramelessWindowHint)) { diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index bc4f6216ba..227b79a207 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -205,7 +205,8 @@ public: MaximizeToFullScreen = 0x80000, InputMethodDisabled = 0x100000, Compositing = 0x200000, - HasBorderInFullScreen = 0x400000 + HasBorderInFullScreen = 0x400000, + WithinDpiChanged = 0x800000, }; QWindowsWindow(QWindow *window, const QWindowsWindowData &data); diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 3444b21654..8c5f8cae08 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -605,7 +605,8 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra initializeXRender(); #if defined(XCB_USE_XINPUT2) - initializeXInput2(); + if (!qEnvironmentVariableIsSet("QT_XCB_NO_XI2")) + initializeXInput2(); #endif initializeXShape(); initializeXKB(); diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index acdcc996b7..f70c7138b3 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -226,6 +226,9 @@ void QXcbConnection::xi2SetupDevices() isTablet = true; tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); + } else if (name.contains("uc-logic") && isTablet) { + tabletData.pointerType = QTabletEvent::Pen; + dbgType = QLatin1String("pen"); } else { isTablet = false; } |