diff options
author | Liang Qi <liang.qi@qt.io> | 2016-06-13 09:01:02 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2016-06-13 12:46:46 +0200 |
commit | 511790fd1af1e2886a0e2e8dd4308099705cd815 (patch) | |
tree | b42aee537a6103cd064f9f41ae2889b09b79fd23 /src/plugins/platforms | |
parent | 1542d8881fc5ccbc5918cd4acbe4091ebbd24508 (diff) | |
parent | cbe332405aa22257d432f1797b325f5e57007c20 (diff) |
Merge remote-tracking branch 'origin/5.7' into dev
Conflicts:
config_help.txt
configure
mkspecs/features/uikit/sdk.prf
src/corelib/global/qhooks.cpp
src/corelib/io/qfilesystemwatcher.cpp
src/corelib/io/qlockfile_unix.cpp
src/corelib/tools/qalgorithms.h
src/gui/kernel/qwindowsysteminterface.h
src/gui/text/qtextdocument_p.cpp
src/network/access/access.pri
src/network/access/qnetworkaccessmanager.cpp
src/network/access/qnetworkreplynsurlconnectionimpl.mm
src/src.pro
src/testlib/qtestcase.cpp
src/widgets/kernel/qwidgetbackingstore_p.h
src/widgets/styles/qwindowscestyle.cpp
src/widgets/styles/qwindowsmobilestyle.cpp
tests/auto/corelib/io/qdiriterator/qdiriterator.pro
tests/auto/corelib/io/qfileinfo/qfileinfo.pro
tests/auto/gui/kernel/qwindow/BLACKLIST
tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp
tools/configure/configureapp.cpp
Change-Id: Ibf7fb9c8cf263a810ade82f821345d0725c57c67
Diffstat (limited to 'src/plugins/platforms')
26 files changed, 1249 insertions, 60 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 004ed04cab..c9c7ff6c2c 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -153,7 +153,7 @@ static bool isMouseEvent(NSEvent *ev) if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO)) { - QNSView *contentView = (QNSView *)pw->contentView(); + QNSView *contentView = pw->m_qtView; [contentView handleFrameStrutMouseEvent: theEvent]; } } @@ -1188,7 +1188,11 @@ NSView *QCocoaWindow::contentView() const void QCocoaWindow::setContentView(NSView *contentView) { // Remove and release the previous content view - [m_contentView removeFromSuperview]; + if (m_nsWindow) + [m_nsWindow setContentView:nil]; + else + [m_contentView removeFromSuperview]; + [m_contentView release]; // Insert and retain the new content view diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index ffc3dec814..7834c2991d 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -632,7 +632,9 @@ static bool _q_dontOverrideCtrlLMB = false; - (BOOL)becomeFirstResponder { - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (!m_window || !m_platformWindow) + return NO; + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); @@ -641,11 +643,13 @@ static bool _q_dontOverrideCtrlLMB = false; - (BOOL)acceptsFirstResponder { + if (!m_window || !m_platformWindow) + return NO; if (m_isMenuView) return NO; if (m_platformWindow->shouldRefuseKeyWindowAndFirstResponder()) return NO; - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; if ((m_window->flags() & Qt::ToolTip) == Qt::ToolTip) return NO; @@ -655,7 +659,9 @@ static bool _q_dontOverrideCtrlLMB = false; - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { Q_UNUSED(theEvent) - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (!m_window || !m_platformWindow) + return NO; + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; return YES; } @@ -2162,7 +2168,11 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin // keep our state, and QGuiApplication state (buttons member) in-sync, // or future mouse events will be processed incorrectly - m_buttons &= ~(m_sendUpAsRightButton ? Qt::RightButton : Qt::LeftButton); + NSUInteger pmb = [NSEvent pressedMouseButtons]; + for (int buttonNumber = 0; buttonNumber < 32; buttonNumber++) { // see cocoaButton2QtButton() for the 32 value + if (!(pmb & (1 << buttonNumber))) + m_buttons &= ~cocoaButton2QtButton(buttonNumber); + } NSPoint windowPoint = [self convertPoint: point fromView: nil]; QPoint qtWindowPoint(windowPoint.x, windowPoint.y); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp index abff88b4bd..5b779d6732 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp @@ -132,7 +132,7 @@ void QEglFSKmsGbmCursor::updateMouseStatus() m_state = visible ? CursorPendingVisible : CursorPendingHidden; #ifndef QT_NO_CURSOR - changeCursor(nullptr, m_screen->topLevelAt(pos())); + changeCursor(Q_NULLPTR, m_screen->topLevelAt(pos())); #endif } diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h index 82bcf48855..2b0643f26e 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.h +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -42,6 +42,7 @@ #include <UIKit/UIKit.h> +#include <QtCore/qlocale.h> #include <QtGui/qevent.h> #include <QtGui/qtransform.h> #include <qpa/qplatforminputcontext.h> @@ -52,6 +53,7 @@ const char kImePlatformDataReturnKeyType[] = "returnKeyType"; QT_BEGIN_NAMESPACE +@class QIOSLocaleListener; @class QIOSKeyboardListener; @class QIOSTextInputResponder; @protocol KeyboardState; @@ -98,6 +100,8 @@ public: void reset() Q_DECL_OVERRIDE; void commit() Q_DECL_OVERRIDE; + QLocale locale() const Q_DECL_OVERRIDE; + void clearCurrentFocusObject(); void setFocusObject(QObject *object) Q_DECL_OVERRIDE; @@ -118,6 +122,7 @@ public: private: UIView* scrollableRootView(); + QIOSLocaleListener *m_localeListener; QIOSKeyboardListener *m_keyboardHideGesture; QIOSTextInputResponder *m_textResponder; KeyboardState m_keyboardState; diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index a1c51b6968..c6cbb9b101 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -62,6 +62,39 @@ static QUIView *focusView() // ------------------------------------------------------------------------- +@interface QIOSLocaleListener : NSObject +@end + +@implementation QIOSLocaleListener + +- (id)init +{ + if (self = [super init]) { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self + selector:@selector(localeDidChange:) + name:NSCurrentLocaleDidChangeNotification object:nil]; + } + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)localeDidChange:(NSNotification *)notification +{ + Q_UNUSED(notification); + QIOSInputContext::instance()->emitLocaleChanged(); +} + +@end + +// ------------------------------------------------------------------------- + @interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate> { @private QIOSInputContext *m_context; @@ -293,6 +326,7 @@ QIOSInputContext *QIOSInputContext::instance() QIOSInputContext::QIOSInputContext() : QPlatformInputContext() + , m_localeListener([QIOSLocaleListener new]) , m_keyboardHideGesture([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) , m_textResponder(0) { @@ -306,6 +340,7 @@ QIOSInputContext::QIOSInputContext() QIOSInputContext::~QIOSInputContext() { + [m_localeListener release]; [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture]; [m_keyboardHideGesture release]; @@ -673,3 +708,8 @@ void QIOSInputContext::commit() [m_textResponder unmarkText]; [m_textResponder notifyInputDelegate:Qt::ImSurroundingText]; } + +QLocale QIOSInputContext::locale() const +{ + return QLocale(QString::fromNSString([[NSLocale currentLocale] objectForKey:NSLocaleIdentifier])); +} diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm index 655e457664..94d82c3eb9 100644 --- a/src/plugins/platforms/ios/qiostextinputoverlay.mm +++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm @@ -45,6 +45,7 @@ #include "qiostextinputoverlay.h" typedef QPair<int, int> SelectionPair; +typedef void (^Block)(void); static const CGFloat kKnobWidth = 10; @@ -68,7 +69,7 @@ static bool hasSelection() return selection.first != selection.second; } -static void executeBlockWithoutAnimation(void (^block)(void)) +static void executeBlockWithoutAnimation(Block block) { [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; @@ -306,6 +307,7 @@ static void executeBlockWithoutAnimation(void (^block)(void)) @property (nonatomic, assign) CGRect cursorRectangle; @property (nonatomic, assign) CGFloat handleScale; @property (nonatomic, assign) BOOL visible; +@property (nonatomic, copy) Block onAnimationDidStop; @end @implementation QIOSHandleLayer @@ -359,7 +361,8 @@ static void executeBlockWithoutAnimation(void (^block)(void)) [NSNumber numberWithFloat:1], nil]; return animation; } else { - CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:key]; + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key]; + [animation setDelegate:self]; animation.fromValue = [self valueForKey:key]; [animation setDuration:0.2]; return animation; @@ -368,6 +371,14 @@ static void executeBlockWithoutAnimation(void (^block)(void)) return [super actionForKey:key]; } +- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag +{ + Q_UNUSED(animation); + Q_UNUSED(flag); + if (self.onAnimationDidStop) + self.onAnimationDidStop(); +} + - (void)setVisible:(BOOL)visible { if (visible == _visible) @@ -679,7 +690,7 @@ static void executeBlockWithoutAnimation(void (^block)(void)) if (enabled) { // Create a layer that clips the handles inside the input field - _clipRectLayer = [[CALayer new] autorelease]; + _clipRectLayer = [CALayer new]; _clipRectLayer.masksToBounds = YES; [self.focusView.layer addSublayer:_clipRectLayer]; @@ -705,7 +716,26 @@ static void executeBlockWithoutAnimation(void (^block)(void)) [self updateSelection]; } else { - [_clipRectLayer removeFromSuperlayer]; + // Fade out the handles by setting visible to NO, and wait for the animations + // to finish before removing the clip rect layer, including the handles. + // Create a local variable to hold the clipRectLayer while the animation is + // ongoing to ensure that any subsequent calls to setEnabled does not interfere. + // Also, declare it as __block to stop it from being automatically retained, which + // would cause a cyclic dependency between clipRectLayer and the block. + __block CALayer *clipRectLayer = _clipRectLayer; + __block int handleCount = 2; + Block block = ^{ + if (--handleCount == 0) { + [clipRectLayer removeFromSuperlayer]; + [clipRectLayer release]; + } + }; + + _cursorLayer.onAnimationDidStop = block; + _anchorLayer.onAnimationDidStop = block; + _cursorLayer.visible = NO; + _anchorLayer.visible = NO; + _clipRectLayer = 0; _cursorLayer = 0; _anchorLayer = 0; diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index 031cd90828..5ec05ec8ce 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -359,6 +359,7 @@ - (void)sendKeyPressRelease:(Qt::Key)key modifiers:(Qt::KeyboardModifiers)modifiers { + QScopedValueRollback<BOOL> rollback(m_inSendEventToFocusObject, true); QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyPress, key, modifiers); QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyRelease, key, modifiers); QWindowSystemInterface::flushWindowSystemEvents(); diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index 643192797a..c8c07bd298 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -310,17 +310,16 @@ - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration { - Q_UNUSED(orientation); - Q_UNUSED(duration); - self.changingOrientation = YES; + + [super willRotateToInterfaceOrientation:orientation duration:duration]; } - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)orientation { - Q_UNUSED(orientation); - self.changingOrientation = NO; + + [super didRotateFromInterfaceOrientation:orientation]; } - (void)willChangeStatusBarFrame:(NSNotification*)notification diff --git a/src/plugins/platforms/minimal/minimal.pro b/src/plugins/platforms/minimal/minimal.pro index 0d31d6605b..bd6f2d8e6f 100644 --- a/src/plugins/platforms/minimal/minimal.pro +++ b/src/plugins/platforms/minimal/minimal.pro @@ -11,6 +11,7 @@ HEADERS = qminimalintegration.h \ OTHER_FILES += minimal.json CONFIG += qpa/genericunixfontdatabase +darwin: DEFINES += QT_NO_FONTCONFIG PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QMinimalIntegrationPlugin diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index b447cf9b81..454d7f43bd 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -2,9 +2,9 @@ TEMPLATE = subdirs android: SUBDIRS += android -SUBDIRS += minimal +!android: SUBDIRS += minimal -!win32|contains(QT_CONFIG, freetype):SUBDIRS += offscreen +!android:if(!win32|contains(QT_CONFIG, freetype)): SUBDIRS += offscreen contains(QT_CONFIG, xcb) { SUBDIRS += xcb diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp index 23a6f35576..6124b004b6 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp @@ -201,26 +201,9 @@ QWindowsEGLStaticContext::QWindowsEGLStaticContext(EGLDisplay display) { } -QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester::Renderers preferredType) +bool QWindowsEGLStaticContext::initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, + EGLDisplay *display, EGLint *major, EGLint *minor) { - const HDC dc = QWindowsContext::instance()->displayContext(); - if (!dc){ - qWarning("%s: No Display", __FUNCTION__); - return 0; - } - - if (!libEGL.init()) { - qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); - return 0; - } - if (!libGLESv2.init()) { - qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); - return 0; - } - - EGLDisplay display = EGL_NO_DISPLAY; - EGLint major = 0; - EGLint minor = 0; #ifdef EGL_ANGLE_platform_angle if (libEGL.eglGetPlatformDisplayEXT && (preferredType & QWindowsOpenGLTester::AngleBackendMask)) { @@ -238,16 +221,52 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester: else if (preferredType & QWindowsOpenGLTester::AngleRendererD3d11Warp) attributes = anglePlatformAttributes[2]; if (attributes) { - display = libEGL.eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc, attributes); - if (!libEGL.eglInitialize(display, &major, &minor)) { - display = EGL_NO_DISPLAY; - major = minor = 0; + *display = libEGL.eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc, attributes); + if (!libEGL.eglInitialize(*display, major, minor)) { + libEGL.eglTerminate(*display); + *display = EGL_NO_DISPLAY; + *major = *minor = 0; + return false; } } } #else // EGL_ANGLE_platform_angle - Q_UNUSED(preferredType) + Q_UNUSED(preferredType); + Q_UNUSED(dc); + Q_UNUSED(display); + Q_UNUSED(major); + Q_UNUSED(minor); #endif + return true; +} + +QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester::Renderers preferredType) +{ + const HDC dc = QWindowsContext::instance()->displayContext(); + if (!dc){ + qWarning("%s: No Display", __FUNCTION__); + return 0; + } + + if (!libEGL.init()) { + qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); + return 0; + } + if (!libGLESv2.init()) { + qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); + return 0; + } + + EGLDisplay display = EGL_NO_DISPLAY; + EGLint major = 0; + EGLint minor = 0; + + if (!initializeAngle(preferredType, dc, &display, &major, &minor) + && (preferredType & QWindowsOpenGLTester::AngleRendererD3d11)) { + preferredType &= ~QWindowsOpenGLTester::AngleRendererD3d11; + initializeAngle(preferredType, dc, &display, &major, &minor); + } + if (display == EGL_NO_DISPLAY) display = libEGL.eglGetDisplay(dc); if (!display) { diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h index c7f7cee3c2..48a19f81e5 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.h +++ b/src/plugins/platforms/windows/qwindowseglcontext.h @@ -131,6 +131,8 @@ public: private: explicit QWindowsEGLStaticContext(EGLDisplay display); + static bool initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, + EGLDisplay *display, EGLint *major, EGLint *minor); const EGLDisplay m_display; }; diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 0f49c66462..744d882bb2 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -1236,8 +1236,7 @@ QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) QWindowsFontEngineDirectWrite *fedw = new QWindowsFontEngineDirectWrite(directWriteFontFace, fontEngine->fontDef.pixelSize, data); - if (fontEngine->fontDef.weight > QFont::Normal) - fedw->fontDef.weight = fontEngine->fontDef.weight; + fedw->fontDef.weight = fontEngine->fontDef.weight; if (fontEngine->fontDef.style > QFont::StyleNormal) fedw->fontDef.style = fontEngine->fontDef.style; fedw->fontDef.family = fam; @@ -1254,8 +1253,7 @@ QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) // reason QFontEngine *fe = new QWindowsFontEngine(fam, lf, data); - if (fontEngine->fontDef.weight > QFont::Normal) - fe->fontDef.weight = fontEngine->fontDef.weight; + fe->fontDef.weight = fontEngine->fontDef.weight; if (fontEngine->fontDef.style > QFont::StyleNormal) fe->fontDef.style = fontEngine->fontDef.style; fe->fontDef.family = fam; diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 6e324ce0ed..5b8cc7893a 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -322,7 +322,9 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons if (customMarginsV.isValid()) requested.customMargins = qvariant_cast<QMargins>(customMarginsV); - QWindowsWindowData obtained = QWindowsWindowData::create(window, requested, window->title()); + QWindowsWindowData obtained = + QWindowsWindowData::create(window, requested, + QWindowsWindow::formatWindowTitle(window->title())); qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << window << "\n Requested: " << requested.geometry << " frame incl.=" diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index a9307f79fb..6c6eb42d63 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -132,7 +132,16 @@ public: explicit ShGetFileInfoFunction(const wchar_t *fn, DWORD a, SHFILEINFO *i, UINT f, bool *r) : m_fileName(fn), m_attributes(a), m_flags(f), m_info(i), m_result(r) {} - void operator()() const { *m_result = SHGetFileInfo(m_fileName, m_attributes, m_info, sizeof(SHFILEINFO), m_flags); } + void operator()() const + { +#ifndef Q_OS_WINCE + const UINT oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); +#endif + *m_result = SHGetFileInfo(m_fileName, m_attributes, m_info, sizeof(SHFILEINFO), m_flags); +#ifndef Q_OS_WINCE + SetErrorMode(oldErrorMode); +#endif + } private: const wchar_t *m_fileName; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index f54c100f25..3a6e6840fd 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1579,11 +1579,12 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, return false; PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + // Observed painting problems with Aero style disabled (QTBUG-7865). + // 5.8: Consider making it dependent on !DwmIsCompositionEnabled(). if (testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered)) - InvalidateRect(hwnd, 0, false); - - BeginPaint(hwnd, &ps); + SelectClipRgn(ps.hdc, NULL); // If the a window is obscured by another window (such as a child window) // we still need to send isExposed=true, for compatibility. @@ -1598,7 +1599,7 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, void QWindowsWindow::setWindowTitle(const QString &title) { - setWindowTitle_sys(QWindowsWindow::formatWindowTitle(title, QStringLiteral(" - "))); + setWindowTitle_sys(QWindowsWindow::formatWindowTitle(title)); } void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags) @@ -2402,4 +2403,9 @@ void QWindowsWindow::setHasBorderInFullScreen(bool border) clearFlag(HasBorderInFullScreen); } +QString QWindowsWindow::formatWindowTitle(const QString &title) +{ + return QPlatformWindow::formatWindowTitle(title, QStringLiteral(" - ")); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 3736778095..924f242e6e 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -316,6 +316,8 @@ public: void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch); static void setHasBorderInFullScreenStatic(QWindow *window, bool border); void setHasBorderInFullScreen(bool border); + static QString formatWindowTitle(const QString &title); + private: inline void show_sys() const; inline QWindowsWindowData setWindowFlags_sys(Qt::WindowFlags wt, unsigned flags = 0) const; diff --git a/src/plugins/platforms/winrt/qwinrtdrag.cpp b/src/plugins/platforms/winrt/qwinrtdrag.cpp new file mode 100644 index 0000000000..2ef50aa4e2 --- /dev/null +++ b/src/plugins/platforms/winrt/qwinrtdrag.cpp @@ -0,0 +1,893 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwinrtdrag.h" + +#include <QtCore/qglobal.h> +#include <QtCore/QMimeData> +#include <QtCore/QStringList> +#include <QtGui/QGuiApplication> +#include <qpa/qwindowsysteminterface.h> + +#include <qfunctions_winrt.h> +#include <private/qeventdispatcher_winrt_p.h> + +#include <Windows.ApplicationModel.datatransfer.h> +#include <windows.ui.xaml.h> +#include <windows.foundation.collections.h> +#include <windows.graphics.imaging.h> +#include <windows.storage.streams.h> +#include <functional> +#include <robuffer.h> + +using namespace ABI::Windows::ApplicationModel::DataTransfer; +using namespace ABI::Windows::ApplicationModel::DataTransfer::DragDrop; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Graphics::Imaging; +using namespace ABI::Windows::Storage; +using namespace ABI::Windows::Storage::Streams; +using namespace ABI::Windows::UI::Xaml; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") + +ComPtr<IBuffer> createIBufferFromData(const char *data, qint32 size) +{ + static ComPtr<IBufferFactory> bufferFactory; + HRESULT hr; + if (!bufferFactory) { + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + IID_PPV_ARGS(&bufferFactory)); + Q_ASSERT_SUCCEEDED(hr); + } + + ComPtr<IBuffer> buffer; + const UINT32 length = size; + hr = bufferFactory->Create(length, &buffer); + Q_ASSERT_SUCCEEDED(hr); + hr = buffer->put_Length(length); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess; + hr = buffer.As(&byteArrayAccess); + Q_ASSERT_SUCCEEDED(hr); + + byte *bytes; + hr = byteArrayAccess->Buffer(&bytes); + Q_ASSERT_SUCCEEDED(hr); + memcpy(bytes, data, length); + return buffer; +} + +class DragThreadTransferData : public QObject +{ + Q_OBJECT +public slots: + void handleDrag(); +public: + explicit DragThreadTransferData(QObject *parent = Q_NULLPTR); + QWindow *window; + QWinRTInternalMimeData *mime; + QPoint point; + Qt::DropActions actions; + bool dropAction; + ComPtr<IDragEventArgs> nativeArgs; + ComPtr<IDragOperationDeferral> deferral; +}; + +inline QString hStringToQString(const HString &hString) +{ + quint32 l; + const wchar_t *raw = hString.GetRawBuffer(&l); + return (QString::fromWCharArray(raw, l)); +} + +inline HString qStringToHString(const QString &qString) +{ + HString h; + h.Set(reinterpret_cast<const wchar_t*>(qString.utf16()), qString.size()); + return h; +} + +namespace NativeFormatStrings { + static ComPtr<IStandardDataFormatsStatics> dataStatics; + static HSTRING text; // text/plain + static HSTRING html; // text/html + static HSTRING storage; // text/uri-list +} + +static inline DataPackageOperation translateFromQDragDropActions(const Qt::DropAction action) +{ + switch (action) { + case Qt::CopyAction: + return DataPackageOperation_Copy; + case Qt::MoveAction: + return DataPackageOperation_Move; + case Qt::LinkAction: + return DataPackageOperation_Link; + case Qt::IgnoreAction: + default: + return DataPackageOperation_None; + } +} + +static inline Qt::DropActions translateToQDragDropActions(const DataPackageOperation op) +{ + Qt::DropActions actions = Qt::IgnoreAction; + // None needs to be interpreted as the sender being able to handle + // anything and let the receiver decide + if (op == DataPackageOperation_None) + actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction; + if (op & DataPackageOperation_Link) + actions |= Qt::LinkAction; + if (op & DataPackageOperation_Copy) + actions |= Qt::CopyAction; + if (op & DataPackageOperation_Move) + actions |= Qt::MoveAction; + return actions; +} + +QWinRTInternalMimeData::QWinRTInternalMimeData() + : QInternalMimeData() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + if (!NativeFormatStrings::dataStatics) { + HRESULT hr; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_StandardDataFormats).Get(), + IID_PPV_ARGS(&NativeFormatStrings::dataStatics)); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_Text(&NativeFormatStrings::text); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_Html(&NativeFormatStrings::html); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_StorageItems(&NativeFormatStrings::storage); + Q_ASSERT_SUCCEEDED(hr); + } +} + +QWinRTInternalMimeData::~QWinRTInternalMimeData() +{ +} + +bool QWinRTInternalMimeData::hasFormat_sys(const QString &mimetype) const +{ + qCDebug(lcQpaMime) << __FUNCTION__ << mimetype; + + if (!dataView) + return false; + + return formats_sys().contains(mimetype); +} + +QStringList QWinRTInternalMimeData::formats_sys() const +{ + qCDebug(lcQpaMime) << __FUNCTION__; + + if (!dataView) + return QStringList(); + + if (!formats.isEmpty()) + return formats; + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([this]() { + boolean contains; + HRESULT hr; + hr = dataView->Contains(NativeFormatStrings::text, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/plain")); + + hr = dataView->Contains(NativeFormatStrings::html, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/html")); + + hr = dataView->Contains(NativeFormatStrings::storage, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/uri-list")); + + // We need to add any additional format as well, for legacy windows + // reasons, but also in case someone adds custom formats. + ComPtr<IVectorView<HSTRING>> availableFormats; + hr = dataView->get_AvailableFormats(&availableFormats); + RETURN_OK_IF_FAILED("Could not query available formats."); + + quint32 size; + hr = availableFormats->get_Size(&size); + RETURN_OK_IF_FAILED("Could not query format vector size."); + for (quint32 i = 0; i < size; ++i) { + HString str; + hr = availableFormats->GetAt(i, str.GetAddressOf()); + if (FAILED(hr)) + continue; + formats.append(hStringToQString(str)); + } + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); + + return formats; +} + +QVariant QWinRTInternalMimeData::retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const +{ + qCDebug(lcQpaMime) << __FUNCTION__ << mimetype << preferredType; + + if (!dataView || !formats.contains(mimetype)) + return QVariant(); + + QVariant result; + HRESULT hr; + if (mimetype == QLatin1String("text/plain")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<HSTRING>> op; + HString res; + hr = dataView->GetTextAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(res)); + return S_OK; + }); + } else if (mimetype == QLatin1String("text/uri-list")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<IVectorView<IStorageItem*>*>> op; + hr = dataView->GetStorageItemsAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<IStorageItem*>> nativeItems; + hr = QWinRTFunctions::await(op, nativeItems.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + QList<QVariant> items; + quint32 count; + hr = nativeItems->get_Size(&count); + for (quint32 i = 0; i < count; ++i) { + ComPtr<IStorageItem> item; + hr = nativeItems->GetAt(i, &item); + Q_ASSERT_SUCCEEDED(hr); + HString path; + hr = item->get_Path(path.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + items.append(QUrl::fromLocalFile(hStringToQString(path))); + } + result.setValue(items); + return S_OK; + }); + } else if (mimetype == QLatin1String("text/html")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<HSTRING>> op; + HString res; + hr = dataView->GetHtmlFormatAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(res)); + return S_OK; + }); + } else { + // Asking for custom data + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result, mimetype]() { + HRESULT hr; + ComPtr<IAsyncOperation<IInspectable*>> op; + ComPtr<IInspectable> res; + HString type; + type.Set(reinterpret_cast<const wchar_t*>(mimetype.utf16()), mimetype.size()); + hr = dataView->GetDataAsync(type.Get(), &op); + RETURN_OK_IF_FAILED("Could not query custom drag data."); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + if (FAILED(hr) || !res) { + qCDebug(lcQpaMime) << "Custom drop data operation returned no results or failed."; + return S_OK; + } + + // Test for properties + ComPtr<IPropertyValue> propertyValue; + hr = res.As(&propertyValue); + if (SUCCEEDED(hr)) { + // We need to check which type of custom data we are receiving + PropertyType type; + propertyValue->get_Type(&type); + switch (type) { + case PropertyType_UInt8: { + quint8 v; + hr = propertyValue->GetUInt8(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int16: { + qint16 v; + hr = propertyValue->GetInt16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt16: { + quint16 v; + hr = propertyValue->GetUInt16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int32: { + qint32 v; + hr = propertyValue->GetInt32(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt32: { + quint32 v; + hr = propertyValue->GetUInt32(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int64: { + qint64 v; + hr = propertyValue->GetInt64(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt64: { + quint64 v; + hr = propertyValue->GetUInt64(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Single: { + float v; + hr = propertyValue->GetSingle(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Double: { + double v; + hr = propertyValue->GetDouble(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Char16: { + wchar_t v; + hr = propertyValue->GetChar16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(QString::fromWCharArray(&v, 1)); + return S_OK; + } + case PropertyType_Boolean: { + boolean v; + hr = propertyValue->GetBoolean(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_String: { + HString stringProperty; + hr = propertyValue->GetString(stringProperty.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(stringProperty)); + return S_OK; + } + default: + qCDebug(lcQpaMime) << "Unknown property type dropped:" << type; + } + return S_OK; + } + + // Custom data can be read via input streams + ComPtr<IRandomAccessStream> randomAccessStream; + hr = res.As(&randomAccessStream); + if (SUCCEEDED(hr)) { + UINT64 size; + hr = randomAccessStream->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IInputStream> stream; + hr = randomAccessStream.As(&stream); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IBufferFactory> bufferFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + IID_PPV_ARGS(&bufferFactory)); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 length = qBound(quint64(0), quint64(size), quint64(UINT_MAX)); + ComPtr<IBuffer> buffer; + hr = bufferFactory->Create(length, &buffer); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> readOp; + hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &readOp); + + ComPtr<IBuffer> effectiveBuffer; + hr = QWinRTFunctions::await(readOp, effectiveBuffer.GetAddressOf()); + + hr = effectiveBuffer->get_Length(&length); + + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess; + hr = effectiveBuffer.As(&byteArrayAccess); + + byte *bytes; + hr = byteArrayAccess->Buffer(&bytes); + QByteArray array((char *)bytes, length); + result.setValue(array); + return S_OK; + } + + HSTRING runtimeClass; + hr = res->GetRuntimeClassName(&runtimeClass); + Q_ASSERT_SUCCEEDED(hr); + HString converted; + converted.Set(runtimeClass); + qCDebug(lcQpaMime) << "Unknown drop data type received (" << hStringToQString(converted) + << "). Ignoring..."; + return S_OK; + }); + } + return result; +} + +void QWinRTInternalMimeData::setDataView(const Microsoft::WRL::ComPtr<IDataPackageView> &d) +{ + dataView = d; + formats.clear(); +} + +static HRESULT qt_drag_enter(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e); + return S_OK; +} + +static HRESULT qt_drag_over(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e); + return S_OK; +} + +static HRESULT qt_drag_leave(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + // Qt internally checks for new drags and auto sends leave events + // Also there is no QPA function for handling leave + Q_UNUSED(sender); + Q_UNUSED(e); + return S_OK; +} + +static HRESULT qt_drop(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e, true); + return S_OK; +} + +#define Q_DECLARE_DRAGHANDLER(name,func) \ +class QtDragEventHandler##name : public IDragEventHandler \ +{ \ +public: \ + virtual HRESULT STDMETHODCALLTYPE Invoke(IInspectable *sender, \ + ABI::Windows::UI::Xaml::IDragEventArgs *e) \ + { \ + return qt_##func(sender, e);\ + } \ + \ + STDMETHODIMP \ + QueryInterface(REFIID riid, void FAR* FAR* ppvObj) \ + { \ + if (riid == IID_IUnknown || riid == IID_IDragEventHandler) { \ + *ppvObj = this; \ + AddRef(); \ + return NOERROR; \ + } \ + *ppvObj = NULL; \ + return ResultFromScode(E_NOINTERFACE); \ + } \ + \ + STDMETHODIMP_(ULONG) \ + AddRef(void) \ + { \ + return ++m_refs; \ + } \ + \ + STDMETHODIMP_(ULONG) \ + Release(void) \ + { \ + if (--m_refs == 0) { \ + delete this; \ + return 0; \ + } \ + return m_refs; \ + } \ +private: \ +ULONG m_refs{0}; \ +}; + +Q_DECLARE_DRAGHANDLER(Enter, drag_enter) +Q_DECLARE_DRAGHANDLER(Over, drag_over) +Q_DECLARE_DRAGHANDLER(Leave, drag_leave) +Q_DECLARE_DRAGHANDLER(Drop, drop) + +#define Q_INST_DRAGHANDLER(name) QtDragEventHandler##name() + +Q_GLOBAL_STATIC(QWinRTDrag, gDrag); + +extern ComPtr<ABI::Windows::UI::Input::IPointerPoint> qt_winrt_lastPointerPoint; // qwinrtscreen.cpp + +QWinRTDrag::QWinRTDrag() + : QPlatformDrag() + , m_dragTarget(0) +{ + qCDebug(lcQpaMime) << __FUNCTION__; + m_enter = new Q_INST_DRAGHANDLER(Enter); + m_over = new Q_INST_DRAGHANDLER(Over); + m_leave = new Q_INST_DRAGHANDLER(Leave); + m_drop = new Q_INST_DRAGHANDLER(Drop); + m_mimeData = new QWinRTInternalMimeData; +} + +QWinRTDrag::~QWinRTDrag() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + delete m_enter; + delete m_over; + delete m_leave; + delete m_drop; + delete m_mimeData; +} + +QWinRTDrag *QWinRTDrag::instance() +{ + return gDrag; +} + +inline HRESULT resetUiElementDrag(ComPtr<IUIElement3> &elem3, EventRegistrationToken startingToken) +{ + return QEventDispatcherWinRT::runOnXamlThread([elem3, startingToken]() { + HRESULT hr; + hr = elem3->put_CanDrag(false); + Q_ASSERT_SUCCEEDED(hr); + hr = elem3->remove_DragStarting(startingToken); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); +} + +Qt::DropAction QWinRTDrag::drag(QDrag *drag) +{ + qCDebug(lcQpaMime) << __FUNCTION__ << drag; + + if (!qt_winrt_lastPointerPoint) { + Q_ASSERT_X(qt_winrt_lastPointerPoint, Q_FUNC_INFO, "No pointerpoint known"); + return Qt::IgnoreAction; + } + + ComPtr<IUIElement3> elem3; + HRESULT hr = m_ui.As(&elem3); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<ABI::Windows::ApplicationModel::DataTransfer::DataPackageOperation>> op; + EventRegistrationToken startingToken; + + hr = QEventDispatcherWinRT::runOnXamlThread([drag, &op, &hr, elem3, &startingToken, this]() { + + hr = elem3->put_CanDrag(true); + Q_ASSERT_SUCCEEDED(hr); + + auto startingCallback = Callback<ITypedEventHandler<UIElement*, DragStartingEventArgs*>> ([drag](IInspectable *, IDragStartingEventArgs *args) { + qCDebug(lcQpaMime) << "Drag starting" << args; + + ComPtr<IDataPackage> dataPackage; + HRESULT hr; + hr = args->get_Data(dataPackage.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + Qt::DropAction action = drag->defaultAction(); + hr = dataPackage->put_RequestedOperation(translateFromQDragDropActions(action)); + Q_ASSERT_SUCCEEDED(hr); + +#ifndef QT_WINRT_LIMITED_DRAGANDDROP + ComPtr<IDragStartingEventArgs2> args2; + hr = args->QueryInterface(IID_PPV_ARGS(&args2)); + Q_ASSERT_SUCCEEDED(hr); + + Qt::DropActions actions = drag->supportedActions(); + DataPackageOperation allowedOperations = DataPackageOperation_None; + if (actions & Qt::CopyAction) + allowedOperations |= DataPackageOperation_Copy; + if (actions & Qt::MoveAction) + allowedOperations |= DataPackageOperation_Move; + if (actions & Qt::LinkAction) + allowedOperations |= DataPackageOperation_Link; + hr = args2->put_AllowedOperations(allowedOperations); + Q_ASSERT_SUCCEEDED(hr); +#endif // QT_WINRT_LIMITED_DRAGANDDROP + QMimeData *mimeData = drag->mimeData(); + if (mimeData->hasText()) { + hr = dataPackage->SetText(qStringToHString(mimeData->text()).Get()); + Q_ASSERT_SUCCEEDED(hr); + } + if (mimeData->hasHtml()) { + hr = dataPackage->SetHtmlFormat(qStringToHString(mimeData->html()).Get()); + Q_ASSERT_SUCCEEDED(hr); + } + // ### TODO: Missing: weblink, image + + if (!drag->pixmap().isNull()) { + const QImage image2 = drag->pixmap().toImage(); + const QImage image = image2.convertToFormat(QImage::Format_ARGB32); + if (!image.isNull()) { + // Create IBuffer containing image + ComPtr<IBuffer> imageBuffer = createIBufferFromData(reinterpret_cast<const char*>(image.bits()), image.byteCount()); + + ComPtr<ISoftwareBitmapFactory> bitmapFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_SoftwareBitmap).Get(), + IID_PPV_ARGS(&bitmapFactory)); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<ISoftwareBitmap> bitmap; + hr = bitmapFactory->Create(BitmapPixelFormat_Rgba8, image.width(), image.height(), &bitmap); + Q_ASSERT_SUCCEEDED(hr); + + hr = bitmap->CopyFromBuffer(imageBuffer.Get()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IDragUI> dragUi; + hr = args->get_DragUI(dragUi.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + hr = dragUi->SetContentFromSoftwareBitmap(bitmap.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + } + + const QStringList formats = mimeData->formats(); + for (auto item : formats) { + QByteArray data = mimeData->data(item); + + ComPtr<IBuffer> buffer = createIBufferFromData(data.constData(), data.size()); + + // We cannot push the buffer to the data package as the result on + // recipient side is different from native events. It still sends a + // buffer, but that potentially cannot be parsed. Hence we need to create + // a IRandomAccessStream which gets forwarded as is to the drop side. + ComPtr<IRandomAccessStream> ras; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), &ras); + Q_ASSERT_SUCCEEDED(hr); + + hr = ras->put_Size(data.size()); + ComPtr<IOutputStream> outputStream; + hr = ras->GetOutputStreamAt(0, &outputStream); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperationWithProgress<UINT32,UINT32>> writeOp; + hr = outputStream->WriteAsync(buffer.Get(), &writeOp); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 result; + hr = QWinRTFunctions::await(writeOp, &result); + Q_ASSERT_SUCCEEDED(hr); + + unsigned char flushResult; + ComPtr<IAsyncOperation<bool>> flushOp; + hr = outputStream->FlushAsync(&flushOp); + Q_ASSERT_SUCCEEDED(hr); + + hr = QWinRTFunctions::await(flushOp, &flushResult); + Q_ASSERT_SUCCEEDED(hr); + + hr = dataPackage->SetData(qStringToHString(item).Get(), ras.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + return S_OK; + }); + + hr = elem3->add_DragStarting(startingCallback.Get(), &startingToken); + Q_ASSERT_SUCCEEDED(hr); + + hr = elem3->StartDragAsync(qt_winrt_lastPointerPoint.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + + return hr; + }); + if (!op || FAILED(hr)) { + qCDebug(lcQpaMime) << "Drag failed:" << hr; + hr = resetUiElementDrag(elem3, startingToken); + Q_ASSERT_SUCCEEDED(hr); + return Qt::IgnoreAction; + } + + DataPackageOperation nativeOperationType; + // Do not yield, as that can cause deadlocks when dropping inside the same app + hr = QWinRTFunctions::await(op, &nativeOperationType, QWinRTFunctions::ProcessThreadEvents); + Q_ASSERT_SUCCEEDED(hr); + + hr = resetUiElementDrag(elem3, startingToken); + Q_ASSERT_SUCCEEDED(hr); + + Qt::DropAction resultAction; + switch (nativeOperationType) { + case DataPackageOperation_Link: + resultAction = Qt::LinkAction; + break; + case DataPackageOperation_Copy: + resultAction = Qt::CopyAction; + break; + case DataPackageOperation_Move: + resultAction = Qt::MoveAction; + break; + case DataPackageOperation_None: + default: + resultAction = Qt::IgnoreAction; + break; + } + + return resultAction; +} + +void QWinRTDrag::setDropTarget(QWindow *target) +{ + qCDebug(lcQpaMime) << __FUNCTION__ << target; + m_dragTarget = target; +} + +QMimeData *QWinRTDrag::platformDropData() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + return m_mimeData; +} + +void QWinRTDrag::setUiElement(ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element) +{ + qCDebug(lcQpaMime) << __FUNCTION__; + m_ui = element; + // We set the element to always accept drops and then evaluate during + // runtime + HRESULT hr = element->put_AllowDrop(TRUE); + EventRegistrationToken tok; + hr = element->add_DragEnter(m_enter, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragEnter handler."); + hr = element->add_DragOver(m_over, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragOver handler."); + hr = element->add_DragLeave(m_leave, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragLeave handler."); + hr = element->add_Drop(m_drop, &tok); + RETURN_VOID_IF_FAILED("Failed to add Drop handler."); +} + +void QWinRTDrag::handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop) +{ + Q_UNUSED(sender); + + if (!m_dragTarget) + return; + + HRESULT hr; + Point relativePoint; + hr = e->GetPosition(m_ui.Get(), &relativePoint); + RETURN_VOID_IF_FAILED("Could not query drag position."); + const QPoint p(relativePoint.X, relativePoint.Y); + + ComPtr<IDragEventArgs2> e2; + hr = e->QueryInterface(IID_PPV_ARGS(&e2)); + RETURN_VOID_IF_FAILED("Could not convert drag event args"); + + DragDropModifiers modifiers; + hr = e2->get_Modifiers(&modifiers); + +#ifndef QT_WINRT_LIMITED_DRAGANDDROP + ComPtr<IDragEventArgs3> e3; + hr = e->QueryInterface(IID_PPV_ARGS(&e3)); + Q_ASSERT_SUCCEEDED(hr); + + DataPackageOperation dataOp; + hr = e3->get_AllowedOperations(&dataOp); + if (FAILED(hr)) + qCDebug(lcQpaMime) << __FUNCTION__ << "Could not query drag operations"; + + const Qt::DropActions actions = translateToQDragDropActions(dataOp); +#else // !QT_WINRT_LIMITED_DRAGANDDROP + const Qt::DropActions actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction;; +#endif // !QT_WINRT_LIMITED_DRAGANDDROP + + ComPtr<IDataPackageView> dataView; + hr = e2->get_DataView(&dataView); + Q_ASSERT_SUCCEEDED(hr); + + m_mimeData->setDataView(dataView); + + // We use deferral as we need to jump to the Qt thread to handle + // the drag event + ComPtr<IDragOperationDeferral> deferral; + hr = e2->GetDeferral(&deferral); + Q_ASSERT_SUCCEEDED(hr); + + DragThreadTransferData *transferData = new DragThreadTransferData; + transferData->moveToThread(qGuiApp->thread()); + transferData->window = m_dragTarget; + transferData->point = p; + transferData->mime = m_mimeData; + transferData->actions = actions; + transferData->dropAction = drop; + transferData->nativeArgs = e; + transferData->deferral = deferral; + QMetaObject::invokeMethod(transferData, "handleDrag", Qt::QueuedConnection); +} + +DragThreadTransferData::DragThreadTransferData(QObject *parent) + : QObject(parent) + , dropAction(false) +{ +} + +void DragThreadTransferData::handleDrag() +{ + bool accepted = false; + Qt::DropAction acceptedAction; + if (dropAction) { + QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(window, mime, point, actions); + accepted = response.isAccepted(); + acceptedAction = response.acceptedAction(); + } else { + QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(window, mime, point, actions); + accepted = response.isAccepted(); + acceptedAction = response.acceptedAction(); + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([accepted, acceptedAction, this]() { + HRESULT hr; + hr = nativeArgs->put_Handled(accepted); + if (acceptedAction != Qt::IgnoreAction) { + ComPtr<IDragEventArgs2> e2; + hr = nativeArgs.As(&e2); + if (SUCCEEDED(hr)) + hr = e2->put_AcceptedOperation(translateFromQDragDropActions(acceptedAction)); + } + deferral->Complete(); + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); + deleteLater(); +} + +QT_END_NAMESPACE + +#include "qwinrtdrag.moc" diff --git a/src/plugins/platforms/winrt/qwinrtdrag.h b/src/plugins/platforms/winrt/qwinrtdrag.h new file mode 100644 index 0000000000..97079d831b --- /dev/null +++ b/src/plugins/platforms/winrt/qwinrtdrag.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qpa/qplatformdrag.h> + +#include <QtCore/QLoggingCategory> +#include <QtCore/QMimeData> +#include <QtGui/private/qdnd_p.h> // QInternalMime + +#include <wrl.h> + +namespace ABI { + namespace Windows { + namespace ApplicationModel { + namespace DataTransfer { + struct IDataPackageView; + } + } + namespace UI { + namespace Xaml { + struct IUIElement; + struct IDragEventArgs; + struct IDragOperationDeferral; + //struct IDataPackageView; + } + } + } +} + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQpaMime) + +class QtDragEventHandlerEnter; +class QtDragEventHandlerOver; +class QtDragEventHandlerLeave; +class QtDragEventHandlerDrop; +class QWinRTInternalMimeData; + +class QWinRTInternalMimeData : public QInternalMimeData { +public: + QWinRTInternalMimeData(); + virtual ~QWinRTInternalMimeData(); + + bool hasFormat_sys(const QString &mimetype) const Q_DECL_OVERRIDE; + QStringList formats_sys() const Q_DECL_OVERRIDE; + QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const Q_DECL_OVERRIDE; + + void setDataView(const Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::DataTransfer::IDataPackageView> &d); +private: + Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::DataTransfer::IDataPackageView> dataView; + mutable QStringList formats; +}; + +class QWinRTDrag : public QPlatformDrag { +public: + QWinRTDrag(); + virtual ~QWinRTDrag(); + static QWinRTDrag *instance(); + + QMimeData *platformDropData(void) Q_DECL_OVERRIDE; + Qt::DropAction drag(QDrag *) Q_DECL_OVERRIDE; + + void setDropTarget(QWindow *target); + + // Native integration and registration + void setUiElement(Microsoft::WRL::ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element); + + void handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop = false); +private: + Microsoft::WRL::ComPtr<ABI::Windows::UI::Xaml::IUIElement> m_ui; + QWindow *m_dragTarget; + QtDragEventHandlerEnter *m_enter; + QtDragEventHandlerOver *m_over; + QtDragEventHandlerLeave *m_leave; + QtDragEventHandlerDrop *m_drop; + QWinRTInternalMimeData *m_mimeData; +}; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtintegration.cpp b/src/plugins/platforms/winrt/qwinrtintegration.cpp index 8ef560ba20..42b7f7e909 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.cpp +++ b/src/plugins/platforms/winrt/qwinrtintegration.cpp @@ -48,6 +48,9 @@ #include "qwinrtfontdatabase.h" #include "qwinrttheme.h" #include "qwinrtclipboard.h" +#ifndef QT_NO_DRAGANDDROP +#include "qwinrtdrag.h" +#endif #include <QtGui/QOffscreenSurface> #include <QtGui/QOpenGLContext> @@ -312,6 +315,17 @@ QPlatformClipboard *QWinRTIntegration::clipboard() const return d->clipboard; } +#ifndef QT_NO_DRAGANDDROP +QPlatformDrag *QWinRTIntegration::drag() const +{ +#if _MSC_VER >= 1900 + return QWinRTDrag::instance(); +#else + return QPlatformIntegration::drag(); +#endif +} +#endif // QT_NO_DRAGANDDROP + Qt::KeyboardModifiers QWinRTIntegration::queryKeyboardModifiers() const { Q_D(const QWinRTIntegration); diff --git a/src/plugins/platforms/winrt/qwinrtintegration.h b/src/plugins/platforms/winrt/qwinrtintegration.h index 9e28beb8fa..7b4d5531fc 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.h +++ b/src/plugins/platforms/winrt/qwinrtintegration.h @@ -97,6 +97,10 @@ public: QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE; QPlatformServices *services() const Q_DECL_OVERRIDE; QPlatformClipboard *clipboard() const Q_DECL_OVERRIDE; +#ifndef QT_NO_DRAGANDDROP + QPlatformDrag *drag() const Q_DECL_OVERRIDE; +#endif + Qt::KeyboardModifiers queryKeyboardModifiers() const Q_DECL_OVERRIDE; QStringList themeNames() const Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index aed33f6b48..ad32e63cad 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -42,6 +42,9 @@ #include "qwinrtbackingstore.h" #include "qwinrtinputcontext.h" #include "qwinrtcursor.h" +#ifndef QT_NO_DRAGANDDROP +#include "qwinrtdrag.h" +#endif #include "qwinrtwindow.h" #include <private/qeventdispatcher_winrt_p.h> @@ -556,6 +559,9 @@ QWinRTScreen::QWinRTScreen() ComPtr<Xaml::IUIElement> uiElement; hr = canvas.As(&uiElement); Q_ASSERT_SUCCEEDED(hr); +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + QWinRTDrag::instance()->setUiElement(uiElement); +#endif hr = window->put_Content(uiElement.Get()); Q_ASSERT_SUCCEEDED(hr); hr = canvas.As(&d->canvas); @@ -764,6 +770,10 @@ void QWinRTScreen::addWindow(QWindow *window) QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); handleExpose(); QWindowSystemInterface::flushWindowSystemEvents(); + +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + QWinRTDrag::instance()->setDropTarget(window); +#endif } void QWinRTScreen::removeWindow(QWindow *window) @@ -778,6 +788,10 @@ void QWinRTScreen::removeWindow(QWindow *window) QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); handleExpose(); QWindowSystemInterface::flushWindowSystemEvents(); +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + if (wasTopWindow) + QWinRTDrag::instance()->setDropTarget(topWindow()); +#endif } void QWinRTScreen::raise(QWindow *window) @@ -973,6 +987,9 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args) return S_OK; } +// Required for qwinrtdrag.cpp +ComPtr<IPointerPoint> qt_winrt_lastPointerPoint; + HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) { Q_D(QWinRTScreen); @@ -980,6 +997,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) if (FAILED(args->get_CurrentPoint(&pointerPoint))) return E_INVALIDARG; + qt_winrt_lastPointerPoint = pointerPoint; // Common traits - point, modifiers, properties Point point; pointerPoint->get_Position(&point); diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 144c581015..28456f66ec 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -14,6 +14,7 @@ SOURCES = \ qwinrtbackingstore.cpp \ qwinrtclipboard.cpp \ qwinrtcursor.cpp \ + qwinrtdrag.cpp \ qwinrteglcontext.cpp \ qwinrteventdispatcher.cpp \ qwinrtfiledialoghelper.cpp \ @@ -32,6 +33,7 @@ HEADERS = \ qwinrtbackingstore.h \ qwinrtclipboard.h \ qwinrtcursor.h \ + qwinrtdrag.h \ qwinrteglcontext.h \ qwinrteventdispatcher.h \ qwinrtfiledialoghelper.h \ @@ -47,6 +49,15 @@ HEADERS = \ OTHER_FILES += winrt.json +WINRT_SDK_VERSION_STRING = $$(UCRTVersion) +WINRT_SDK_VERSION = $$member($$list($$split(WINRT_SDK_VERSION_STRING, .)), 2) +lessThan(WINRT_SDK_VERSION, 14322): DEFINES += QT_WINRT_LIMITED_DRAGANDDROP + +*-msvc2013|contains(DEFINES, QT_NO_DRAGANDDROP) { + SOURCES -= qwinrtdrag.cpp + HEADERS -= qwinrtdrag.h +} + PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QWinRTIntegrationPlugin !equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp index d536121521..f97c01f390 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp @@ -207,8 +207,10 @@ QPlatformOffscreenSurface *QXcbGlxIntegration::createPlatformOffscreenSurface(QO display = static_cast<Display *>(m_connection->xlib_display()); #endif const char *glxvendor = glXGetClientString(display, GLX_VENDOR); - if (glxvendor && !strcmp(glxvendor, "ATI")) - glxPbufferUsable = false; + if (glxvendor) { + if (!strcmp(glxvendor, "ATI") || !strcmp(glxvendor, "Chromium")) + glxPbufferUsable = false; + } } if (glxPbufferUsable) return new QGLXPbuffer(surface); diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 377cb626ee..5d46c53b30 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/QDebug> #include "qxcbconnection.h" @@ -264,6 +265,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) } else { screen = createScreen(virtualDesktop, output, outputInfo.data()); qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; + QHighDpiScaling::updateHighDpiScaling(); } } } else if (screen) { diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 789229c0a7..063778a1de 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -797,9 +797,9 @@ void QXcbWindow::show() propagateSizeHints(); // update WM_TRANSIENT_FOR - const QWindow *tp = window()->transientParent(); - if (isTransient(window()) || tp != 0) { - xcb_window_t transientXcbParent = 0; + xcb_window_t transientXcbParent = 0; + if (isTransient(window())) { + const QWindow *tp = window()->transientParent(); if (tp && tp->handle()) transientXcbParent = static_cast<const QXcbWindow *>(tp->handle())->winId(); // Default to client leader if there is no transient parent, else modal dialogs can @@ -812,6 +812,8 @@ void QXcbWindow::show() 1, &transientXcbParent)); } } + if (!transientXcbParent) + Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR)); // update _MOTIF_WM_HINTS updateMotifWmHintsBeforeMap(); @@ -1173,9 +1175,11 @@ void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags) mwmhints.flags |= MWM_HINTS_DECORATIONS; bool customize = flags & Qt::CustomizeWindowHint; - if (type == Qt::Window && !customize) - flags |= Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint; - + if (type == Qt::Window && !customize) { + const Qt::WindowFlags defaultFlags = Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint; + if (!(flags & defaultFlags)) + flags |= defaultFlags; + } if (!(flags & Qt::FramelessWindowHint) && !(customize && !(flags & Qt::WindowTitleHint))) { mwmhints.decorations |= MWM_DECOR_BORDER; mwmhints.decorations |= MWM_DECOR_RESIZEH; |