diff options
Diffstat (limited to 'src/plugins/platforms')
22 files changed, 327 insertions, 275 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index e0d623fc4c..c10ada1ada 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -335,24 +335,8 @@ NSCursor *QCocoaCursor::createCursorFromBitmap(const QBitmap *bitmap, const QBit NSCursor *QCocoaCursor::createCursorFromPixmap(const QPixmap pixmap, const QPoint hotspot) { NSPoint hotSpot = NSMakePoint(hotspot.x(), hotspot.y()); - NSImage *nsimage; - if (pixmap.devicePixelRatio() > 1.0) { - QSize layoutSize = pixmap.size() / pixmap.devicePixelRatio(); - QPixmap scaledPixmap = pixmap.scaled(layoutSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - scaledPixmap.setDevicePixelRatio(1.0); - nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(scaledPixmap)); - CGImageRef cgImage = qt_mac_toCGImage(pixmap.toImage()); - NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; - [nsimage addRepresentation:imageRep]; - [imageRep release]; - CGImageRelease(cgImage); - } else { - nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap)); - } - - NSCursor *nsCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot]; - [nsimage release]; - return nsCursor; + auto *image = [NSImage imageFromQImage:pixmap.toImage()]; + return [[NSCursor alloc] initWithImage:image hotSpot:hotSpot]; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoadrag.mm b/src/plugins/platforms/cocoa/qcocoadrag.mm index 95808b8a11..b4a16ab912 100644 --- a/src/plugins/platforms/cocoa/qcocoadrag.mm +++ b/src/plugins/platforms/cocoa/qcocoadrag.mm @@ -130,9 +130,8 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o) QPoint hotSpot = m_drag->hotSpot(); QPixmap pm = dragPixmap(m_drag, hotSpot); - QSize pmDeviceIndependentSize = pm.size() / pm.devicePixelRatio(); - NSImage *nsimage = qt_mac_create_nsimage(pm); - [nsimage setSize:NSSizeFromCGSize(pmDeviceIndependentSize.toCGSize())]; + NSImage *dragImage = [NSImage imageFromQImage:pm.toImage()]; + Q_ASSERT(dragImage); QMacPasteboard dragBoard(CFStringRef(NSPasteboardNameDrag), QMacInternalPasteboardMime::MIME_DND); m_drag->mimeData()->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy")); @@ -142,12 +141,12 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o) NSWindow *theWindow = [m_lastEvent window]; Q_ASSERT(theWindow); event_location.x -= hotSpot.x(); - CGFloat flippedY = pmDeviceIndependentSize.height() - hotSpot.y(); + CGFloat flippedY = dragImage.size.height - hotSpot.y(); event_location.y -= flippedY; NSSize mouseOffset_unused = NSMakeSize(0.0, 0.0); NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSPasteboardNameDrag]; - [theWindow dragImage:nsimage + [theWindow dragImage:dragImage at:event_location offset:mouseOffset_unused event:m_lastEvent @@ -155,8 +154,6 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o) source:m_lastView slideBack:YES]; - [nsimage release]; - m_drag = nullptr; return m_executed_drop_action; } diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index 6db4bdb9fd..ccb6e20071 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -554,7 +554,7 @@ NSOpenGLContext *QCocoaGLContext::nativeContext() const QFunctionPointer QCocoaGLContext::getProcAddress(const char *procName) { - return (QFunctionPointer)dlsym(RTLD_DEFAULT, procName); + return (QFunctionPointer)dlsym(RTLD_NEXT, procName); } #ifndef QT_NO_DEBUG_STREAM diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index b2698b05fe..1fb250317d 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -482,14 +482,7 @@ QList<QCocoaWindow *> *QCocoaIntegration::popupWindowStack() void QCocoaIntegration::setApplicationIcon(const QIcon &icon) const { - NSImage *image = nil; - if (!icon.isNull()) { - NSSize size = [[[NSApplication sharedApplication] dockTile] size]; - QPixmap pixmap = icon.pixmap(size.width, size.height); - image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap)); - } - [[NSApplication sharedApplication] setApplicationIconImage:image]; - [image release]; + NSApp.applicationIconImage = [NSImage imageFromQIcon:icon]; } void QCocoaIntegration::beep() const diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index ef9b2659d2..a2100a6369 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -340,13 +340,7 @@ NSMenuItem *QCocoaMenuItem::sync() m_native.keyEquivalentModifierMask = NSEventModifierFlagCommand; } - NSImage *img = nil; - if (!m_icon.isNull()) { - img = qt_mac_create_nsimage(m_icon, m_iconSize); - img.size = CGSizeMake(m_iconSize, m_iconSize); - } - m_native.image = img; - [img release]; + m_native.image = [NSImage imageFromQIcon:m_icon withSize:m_iconSize]; m_native.state = m_checked ? NSOnState : NSOffState; return m_native; diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h index 738c40aba6..7999438ca5 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h @@ -49,14 +49,23 @@ #include "QtCore/qstring.h" #include "QtGui/qpa/qplatformsystemtrayicon.h" -QT_BEGIN_NAMESPACE +#include "qcocoamenu.h" + +QT_FORWARD_DECLARE_CLASS(QCocoaSystemTrayIcon); + +@interface QT_MANGLE_NAMESPACE(QStatusItemDelegate) : NSObject <NSUserNotificationCenterDelegate> +- (instancetype)initWithSysTray:(QCocoaSystemTrayIcon *)platformSystemTray; +@property (nonatomic, assign) QCocoaSystemTrayIcon *platformSystemTray; +@end -class QSystemTrayIconSys; +QT_NAMESPACE_ALIAS_OBJC_CLASS(QStatusItemDelegate); + +QT_BEGIN_NAMESPACE class Q_GUI_EXPORT QCocoaSystemTrayIcon : public QPlatformSystemTrayIcon { public: - QCocoaSystemTrayIcon() : m_sys(nullptr) {} + QCocoaSystemTrayIcon() {} void init() override; void cleanup() override; @@ -70,8 +79,12 @@ public: bool isSystemTrayAvailable() const override; bool supportsMessages() const override; + void statusItemClicked(); + private: - QSystemTrayIconSys *m_sys; + NSStatusItem *m_statusItem = nullptr; + QStatusItemDelegate *m_delegate = nullptr; + QCocoaMenu *m_menu = nullptr; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index 559188aa5f..8e7c86a0ef 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -92,67 +92,46 @@ #import <AppKit/AppKit.h> -QT_USE_NAMESPACE - -@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject <NSUserNotificationCenterDelegate> -@property (nonatomic, assign) QCocoaMenu *menu; -@property (nonatomic, assign) QIcon icon; -@property (nonatomic, readonly) NSStatusItem *item; -@property (nonatomic, readonly) QRectF geometry; -- (instancetype)initWithSysTray:(QCocoaSystemTrayIcon *)systray; -- (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton; -- (void)doubleClickSelector:(id)sender; -@end +QT_BEGIN_NAMESPACE -QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSStatusItem); +void QCocoaSystemTrayIcon::init() +{ + m_statusItem = [[NSStatusBar.systemStatusBar statusItemWithLength:NSSquareStatusItemLength] retain]; -@interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView -@property (nonatomic, assign) BOOL down; -@property (nonatomic, assign) QNSStatusItem *parent; -@end + m_delegate = [[QStatusItemDelegate alloc] initWithSysTray:this]; -QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSImageView); + m_statusItem.button.target = m_delegate; + m_statusItem.button.action = @selector(statusItemClicked); + [m_statusItem.button sendActionOn:NSEventMaskLeftMouseUp | NSEventMaskRightMouseUp | NSEventMaskOtherMouseUp]; +} -QT_BEGIN_NAMESPACE -class QSystemTrayIconSys +void QCocoaSystemTrayIcon::cleanup() { -public: - QSystemTrayIconSys(QCocoaSystemTrayIcon *sys) { - item = [[QNSStatusItem alloc] initWithSysTray:sys]; - NSUserNotificationCenter.defaultUserNotificationCenter.delegate = item; - } - ~QSystemTrayIconSys() { - [[[item item] view] setHidden: YES]; - NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter; - if (center.delegate == item) - center.delegate = nil; - [item release]; - } - QNSStatusItem *item; -}; + NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter; + if (center.delegate == m_delegate) + center.delegate = nil; -void QCocoaSystemTrayIcon::init() -{ - if (!m_sys) - m_sys = new QSystemTrayIconSys(this); + [NSStatusBar.systemStatusBar removeStatusItem:m_statusItem]; + [m_statusItem release]; + m_statusItem = nil; + + [m_delegate release]; + m_delegate = nil; + + m_menu = nullptr; } QRect QCocoaSystemTrayIcon::geometry() const { - if (!m_sys) + if (!m_statusItem) return QRect(); - const QRectF geom = [m_sys->item geometry]; - if (!geom.isNull()) - return geom.toRect(); - else - return QRect(); -} + if (NSWindow *window = m_statusItem.button.window) { + if (QCocoaScreen *screen = QCocoaScreen::get(window.screen)) + return screen->mapFromNative(window.frame).toRect(); + } -void QCocoaSystemTrayIcon::cleanup() -{ - delete m_sys; - m_sys = nullptr; + return QRect(); } static bool heightCompareFunction (QSize a, QSize b) { return (a.height() < b.height()); } @@ -165,17 +144,15 @@ static QList<QSize> sortByHeight(const QList<QSize> &sizes) void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) { - if (!m_sys) + if (!m_statusItem) return; - m_sys->item.icon = icon; - - // The reccomended maximum title bar icon height is 18 points + // The recommended maximum title bar icon height is 18 points // (device independent pixels). The menu height on past and // current OS X versions is 22 points. Provide some future-proofing // by deriving the icon height from the menu height. const int padding = 4; - const int menuHeight = [[NSStatusBar systemStatusBar] thickness]; + const int menuHeight = NSStatusBar.systemStatusBar.thickness; const int maxImageHeight = menuHeight - padding; // Select pixmap based on the device pixel height. Ideally we would use @@ -228,30 +205,28 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) p.drawPixmap(r, pixmap); } - NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(fullHeightPixmap)); + auto *nsimage = [NSImage imageFromQImage:fullHeightPixmap.toImage()]; [nsimage setTemplate:icon.isMask()]; - [(NSImageView*)[[m_sys->item item] view] setImage: nsimage]; - [nsimage release]; + m_statusItem.button.image = nsimage; + m_statusItem.button.imageScaling = NSImageScaleProportionallyDown; } void QCocoaSystemTrayIcon::updateMenu(QPlatformMenu *menu) { - if (!m_sys) - return; - - m_sys->item.menu = static_cast<QCocoaMenu *>(menu); - if (menu && [m_sys->item.menu->nsMenu() numberOfItems] > 0) { - [[m_sys->item item] setHighlightMode:YES]; - } else { - [[m_sys->item item] setHighlightMode:NO]; - } + // We don't set the menu property of the NSStatusItem here, + // as that would prevent us from receiving the action for the + // click, and we wouldn't be able to emit the activated signal. + // Instead we show the menu manually when the status item is + // clicked. + m_menu = static_cast<QCocoaMenu *>(menu); } void QCocoaSystemTrayIcon::updateToolTip(const QString &toolTip) { - if (!m_sys) + if (!m_statusItem) return; - [[[m_sys->item item] view] setToolTip:toolTip.toNSString()]; + + m_statusItem.button.toolTip = toolTip.toNSString(); } bool QCocoaSystemTrayIcon::isSystemTrayAvailable() const @@ -267,180 +242,83 @@ bool QCocoaSystemTrayIcon::supportsMessages() const void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &message, const QIcon& icon, MessageIcon, int msecs) { - if (!m_sys) + if (!m_statusItem) return; - NSUserNotification *notification = [[NSUserNotification alloc] init]; - notification.title = [NSString stringWithUTF8String:title.toUtf8().data()]; - notification.informativeText = [NSString stringWithUTF8String:message.toUtf8().data()]; - - if (!icon.isNull()) { - auto *nsimage = qt_mac_create_nsimage(icon); - [nsimage setTemplate:icon.isMask()]; - notification.contentImage = [nsimage autorelease]; - } + auto *notification = [[NSUserNotification alloc] init]; + notification.title = title.toNSString(); + notification.informativeText = message.toNSString(); + notification.contentImage = [NSImage imageFromQIcon:icon]; NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter; - center.delegate = m_sys->item; - [center deliverNotification:notification]; + center.delegate = m_delegate; + + [center deliverNotification:[notification autorelease]]; + if (msecs) { NSTimeInterval timeout = msecs / 1000.0; [center performSelector:@selector(removeDeliveredNotification:) withObject:notification afterDelay:timeout]; } - [notification release]; } -QT_END_NAMESPACE -@implementation NSStatusItem (Qt) -@end - -@implementation QNSImageView -- (instancetype)initWithParent:(QNSStatusItem *)myParent { - self = [super init]; - self.parent = myParent; - self.down = NO; - return self; -} - -- (void)menuTrackingDone:(NSNotification *)__unused notification +void QCocoaSystemTrayIcon::statusItemClicked() { - self.down = NO; + auto *mouseEvent = NSApp.currentEvent; - [self setNeedsDisplay:YES]; -} + auto activationReason = QPlatformSystemTrayIcon::Unknown; -- (void)mousePressed:(NSEvent *)mouseEvent -{ - self.down = YES; - int clickCount = [mouseEvent clickCount]; - [self setNeedsDisplay:YES]; - - if (clickCount == 2) { - [self menuTrackingDone:nil]; - [self.parent doubleClickSelector:self]; + if (mouseEvent.clickCount == 2) { + activationReason = QPlatformSystemTrayIcon::DoubleClick; } else { - [self.parent triggerSelector:self button:cocoaButton2QtButton(mouseEvent)]; + auto mouseButton = cocoaButton2QtButton(mouseEvent); + if (mouseButton == Qt::MidButton) + activationReason = QPlatformSystemTrayIcon::MiddleClick; + else if (mouseButton == Qt::RightButton) + activationReason = QPlatformSystemTrayIcon::Context; + else + activationReason = QPlatformSystemTrayIcon::Trigger; } -} - -- (void)mouseDown:(NSEvent *)mouseEvent -{ - [self mousePressed:mouseEvent]; -} -- (void)mouseUp:(NSEvent *)mouseEvent -{ - Q_UNUSED(mouseEvent); - [self menuTrackingDone:nil]; -} + emit activated(activationReason); -- (void)rightMouseDown:(NSEvent *)mouseEvent -{ - [self mousePressed:mouseEvent]; + if (NSMenu *menu = m_menu ? m_menu->nsMenu() : nil) + [m_statusItem popUpStatusItemMenu:menu]; } -- (void)rightMouseUp:(NSEvent *)mouseEvent -{ - Q_UNUSED(mouseEvent); - [self menuTrackingDone:nil]; -} +QT_END_NAMESPACE -- (void)otherMouseDown:(NSEvent *)mouseEvent -{ - [self mousePressed:mouseEvent]; -} +@implementation QStatusItemDelegate -- (void)otherMouseUp:(NSEvent *)mouseEvent +- (instancetype)initWithSysTray:(QCocoaSystemTrayIcon *)platformSystemTray { - Q_UNUSED(mouseEvent); - [self menuTrackingDone:nil]; -} - -- (void)drawRect:(NSRect)rect { - [[self.parent item] drawStatusBarBackgroundInRect:rect withHighlight:self.down]; - [super drawRect:rect]; -} -@end - -@implementation QNSStatusItem { - QCocoaSystemTrayIcon *systray; - NSStatusItem *item; - QNSImageView *imageCell; -} + if ((self = [super init])) + self.platformSystemTray = platformSystemTray; -@synthesize menu = menu; -@synthesize icon = icon; - -- (instancetype)initWithSysTray:(QCocoaSystemTrayIcon *)sys -{ - self = [super init]; - if (self) { - item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; - menu = nullptr; - systray = sys; - imageCell = [[QNSImageView alloc] initWithParent:self]; - [item setView: imageCell]; - } return self; } -- (void)dealloc { - [[NSStatusBar systemStatusBar] removeStatusItem:item]; - [[NSNotificationCenter defaultCenter] removeObserver:imageCell]; - imageCell.parent = nil; - [imageCell release]; - [item release]; +- (void)dealloc +{ + self.platformSystemTray = nullptr; [super dealloc]; } -- (NSStatusItem *)item { - return item; -} - -- (QRectF)geometry { - if (NSWindow *window = item.view.window) { - if (QCocoaScreen *screen = QCocoaScreen::get(window.screen)) - return screen->mapFromNative(window.frame); - } - return QRectF(); -} - -- (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton { - Q_UNUSED(sender); - if (!systray) - return; - - if (mouseButton == Qt::MidButton) - emit systray->activated(QPlatformSystemTrayIcon::MiddleClick); - else - emit systray->activated(QPlatformSystemTrayIcon::Trigger); - - if (menu) { - NSMenu *m = menu->nsMenu(); - [[NSNotificationCenter defaultCenter] addObserver:imageCell - selector:@selector(menuTrackingDone:) - name:NSMenuDidEndTrackingNotification - object:m]; - [item popUpStatusItemMenu: m]; - } -} - -- (void)doubleClickSelector:(id)sender { - Q_UNUSED(sender); - if (!systray) - return; - emit systray->activated(QPlatformSystemTrayIcon::DoubleClick); +- (void)statusItemClicked +{ + self.platformSystemTray->statusItemClicked(); } -- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification { +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification +{ Q_UNUSED(center); Q_UNUSED(notification); return YES; } -- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification { +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification +{ [center removeDeliveredNotification:notification]; - emit systray->messageClicked(); + emit self.platformSystemTray->messageClicked(); } @end diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 61bf0d4a4e..35b0ec2d40 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -891,14 +891,10 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon) QMacAutoReleasePool pool; - if (icon.isNull()) { - NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; - [iconButton setImage:[workspace iconForFile:m_view.window.representedFilename]]; - } else { - QPixmap pixmap = icon.pixmap(QSize(22, 22)); - NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap)); - [iconButton setImage:[image autorelease]]; - } + if (icon.isNull()) + iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename]; + else + iconButton.image = [NSImage imageFromQIcon:icon]; } void QCocoaWindow::setAlertState(bool enabled) @@ -1237,7 +1233,9 @@ void QCocoaWindow::windowDidOrderOffScreen() void QCocoaWindow::windowDidChangeOcclusionState() { - if (m_view.window.occlusionState & NSWindowOcclusionStateVisible) + bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible; + qCDebug(lcQpaWindow) << "QCocoaWindow::windowDidChangeOcclusionState" << window() << "is now" << (visible ? "visible" : "occluded"); + if (visible) [m_view setNeedsDisplay:YES]; else handleExposeEvent(QRegion()); diff --git a/src/plugins/platforms/cocoa/qnsview_dragging.mm b/src/plugins/platforms/cocoa/qnsview_dragging.mm index 650612e7ff..463e3c5579 100644 --- a/src/plugins/platforms/cocoa/qnsview_dragging.mm +++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm @@ -150,10 +150,8 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin break; } } else { - NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor); - nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize()); + auto *nsimage = [NSImage imageFromQImage:pixmapCursor.toImage()]; nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint]; - [nsimage release]; } // Change the cursor diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp index e3145aa0b0..b985386a4e 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp @@ -185,7 +185,9 @@ void QEglFSDeviceIntegration::platformDestroy() EGLNativeDisplayType QEglFSDeviceIntegration::platformDisplay() const { - return EGL_DEFAULT_DISPLAY; + bool displayOk; + const int defaultDisplay = qEnvironmentVariableIntValue("QT_QPA_EGLFS_DEFAULT_DISPLAY", &displayOk); + return displayOk ? EGLNativeDisplayType(quintptr(defaultDisplay)) : EGL_DEFAULT_DISPLAY; } EGLDisplay QEglFSDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay) diff --git a/src/plugins/platforms/ios/kernel.pro b/src/plugins/platforms/ios/kernel.pro index 01e0105223..c483076856 100644 --- a/src/plugins/platforms/ios/kernel.pro +++ b/src/plugins/platforms/ios/kernel.pro @@ -59,13 +59,15 @@ HEADERS = \ qiosmenu.mm \ qiosfiledialog.mm \ qiosmessagedialog.mm \ - qiostextinputoverlay.mm + qiostextinputoverlay.mm \ + qiosdocumentpickercontroller.mm HEADERS += \ qiosclipboard.h \ qiosmenu.h \ qiosfiledialog.h \ qiosmessagedialog.h \ - qiostextinputoverlay.h + qiostextinputoverlay.h \ + qiosdocumentpickercontroller.h } OTHER_FILES = \ diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h new file mode 100644 index 0000000000..dba6f24fc5 --- /dev/null +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Harald Meyer. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import <UIKit/UIKit.h> + +#include "qiosfiledialog.h" + +@interface QIOSDocumentPickerController : UIDocumentPickerViewController <UIDocumentPickerDelegate, UINavigationControllerDelegate> +- (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; +@end diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm new file mode 100644 index 0000000000..c1b641e839 --- /dev/null +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Harald Meyer. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import <UIKit/UIKit.h> +#import <MobileCoreServices/MobileCoreServices.h> + +#include "qiosdocumentpickercontroller.h" + +@implementation QIOSDocumentPickerController { + QIOSFileDialog *m_fileDialog; +} + +- (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog +{ + NSMutableArray <NSString *> *docTypes = [[[NSMutableArray alloc] init] autorelease]; + UIDocumentPickerMode importMode; + switch (fileDialog->options()->fileMode()) { + case QFileDialogOptions::AnyFile: + case QFileDialogOptions::ExistingFile: + case QFileDialogOptions::ExistingFiles: + [docTypes addObject:(__bridge NSString *)kUTTypeContent]; + [docTypes addObject:(__bridge NSString *)kUTTypeItem]; + [docTypes addObject:(__bridge NSString *)kUTTypeData]; + importMode = UIDocumentPickerModeImport; + break; + case QFileDialogOptions::Directory: + case QFileDialogOptions::DirectoryOnly: + // Directory picking is not supported because it requires + // special handling not possible with the current QFilePicker + // implementation. + + Q_UNREACHABLE(); + } + + if (self = [super initWithDocumentTypes:docTypes inMode:importMode]) { + m_fileDialog = fileDialog; + self.modalPresentationStyle = UIModalPresentationFormSheet; + self.delegate = self; + + if (m_fileDialog->options()->fileMode() == QFileDialogOptions::ExistingFiles) + self.allowsMultipleSelection = YES; + + if (@available(ios 13.0, *)) + self.directoryURL = m_fileDialog->options()->initialDirectory().toNSURL(); + } + return self; +} + +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray <NSURL *>*)urls +{ + Q_UNUSED(controller); + + QList<QUrl> files; + for (NSURL* url in urls) + files.append(QUrl::fromNSURL(url)); + + m_fileDialog->selectedFilesChanged(files); + emit m_fileDialog->accept(); +} + +- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller +{ + Q_UNUSED(controller) + emit m_fileDialog->reject(); +} + +@end diff --git a/src/plugins/platforms/ios/qiosfiledialog.h b/src/plugins/platforms/ios/qiosfiledialog.h index 5cb1b45e20..eab05091ef 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.h +++ b/src/plugins/platforms/ios/qiosfiledialog.h @@ -65,7 +65,7 @@ public: void selectNameFilter(const QString &) override {} QString selectedNameFilter() const override { return QString(); } - void selectedFilesChanged(QList<QUrl> selection); + void selectedFilesChanged(const QList<QUrl> &selection); private: QUrl m_directory; @@ -74,6 +74,7 @@ private: UIViewController *m_viewController; bool showImagePickerDialog(QWindow *parent); + bool showNativeDocumentPickerDialog(QWindow *parent); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosfiledialog.mm b/src/plugins/platforms/ios/qiosfiledialog.mm index e8a3f5b30e..edf04016fd 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.mm +++ b/src/plugins/platforms/ios/qiosfiledialog.mm @@ -48,6 +48,7 @@ #include "qiosfiledialog.h" #include "qiosintegration.h" #include "qiosoptionalplugininterface.h" +#include "qiosdocumentpickercontroller.h" QIOSFileDialog::QIOSFileDialog() : m_viewController(nullptr) @@ -72,8 +73,12 @@ bool QIOSFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality window bool acceptOpen = options()->acceptMode() == QFileDialogOptions::AcceptOpen; QString directory = options()->initialDirectory().toLocalFile(); - if (acceptOpen && directory.startsWith(QLatin1String("assets-library:"))) - return showImagePickerDialog(parent); + if (acceptOpen) { + if (directory.startsWith(QLatin1String("assets-library:"))) + return showImagePickerDialog(parent); + else + return showNativeDocumentPickerDialog(parent); + } return false; } @@ -102,6 +107,25 @@ bool QIOSFileDialog::showImagePickerDialog(QWindow *parent) return true; } +bool QIOSFileDialog::showNativeDocumentPickerDialog(QWindow *parent) +{ +#ifndef Q_OS_TVOS + if (options()->fileMode() == QFileDialogOptions::Directory || + options()->fileMode() == QFileDialogOptions::DirectoryOnly) + return false; + + m_viewController = [[QIOSDocumentPickerController alloc] initWithQIOSFileDialog:this]; + + UIWindow *window = parent ? reinterpret_cast<UIView *>(parent->winId()).window + : qt_apple_sharedApplication().keyWindow; + [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; + + return true; +#else + return false; +#endif +} + void QIOSFileDialog::hide() { // QFileDialog will remember the last directory set, and open subsequent dialogs in the same @@ -123,7 +147,7 @@ QList<QUrl> QIOSFileDialog::selectedFiles() const return m_selection; } -void QIOSFileDialog::selectedFilesChanged(QList<QUrl> selection) +void QIOSFileDialog::selectedFilesChanged(const QList<QUrl> &selection) { m_selection = selection; emit filesSelected(m_selection); diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index c4f2b30965..23f838a7fe 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -3,9 +3,9 @@ QT_FOR_CONFIG += gui-private android:!android-embedded: SUBDIRS += android -!android: SUBDIRS += minimal +!wasm:!android: SUBDIRS += minimal -!android:qtConfig(freetype): SUBDIRS += offscreen +!wasm:!android:qtConfig(freetype): SUBDIRS += offscreen qtConfig(xcb) { SUBDIRS += xcb diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index 4ddd56fd8c..fbf700518e 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -88,7 +88,7 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons EmscriptenWebGLContextAttributes attributes; emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes - attributes.preferLowPowerToHighPerformance = false; + attributes.powerPreference = EM_WEBGL_POWER_PREFERENCE_HIGH_PERFORMANCE; attributes.failIfMajorPerformanceCaveat = false; attributes.antialias = true; attributes.enableExtensionsByDefault = true; diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index 594db65cfd..f95335f891 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -265,6 +265,8 @@ bool QWasmWindow::isPointOnTitle(QPoint point) const bool QWasmWindow::isPointOnResizeRegion(QPoint point) const { + if (window()->flags().testFlag(Qt::Popup)) + return false; return resizeRegion().contains(point); } @@ -402,7 +404,8 @@ void QWasmWindow::requestUpdate() bool QWasmWindow::hasTitleBar() const { - return !(m_windowState & Qt::WindowFullScreen) && (window()->flags().testFlag(Qt::WindowTitleHint) && m_needsCompositor); + return !(m_windowState & Qt::WindowFullScreen) && (window()->flags().testFlag(Qt::WindowTitleHint) && m_needsCompositor) + && !window()->flags().testFlag(Qt::Popup); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp index 79b5ba06e6..ff5c50b702 100644 --- a/src/plugins/platforms/xcb/qxcbatom.cpp +++ b/src/plugins/platforms/xcb/qxcbatom.cpp @@ -122,6 +122,7 @@ static const char *xcb_atomnames = { "_NET_WM_STATE_MODAL\0" "_NET_WM_STATE_STAYS_ON_TOP\0" "_NET_WM_STATE_DEMANDS_ATTENTION\0" + "_NET_WM_STATE_HIDDEN\0" "_NET_WM_USER_TIME\0" "_NET_WM_USER_TIME_WINDOW\0" diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h index 233d2eadb7..80b5887395 100644 --- a/src/plugins/platforms/xcb/qxcbatom.h +++ b/src/plugins/platforms/xcb/qxcbatom.h @@ -123,6 +123,7 @@ public: _NET_WM_STATE_MODAL, _NET_WM_STATE_STAYS_ON_TOP, _NET_WM_STATE_DEMANDS_ATTENTION, + _NET_WM_STATE_HIDDEN, _NET_WM_USER_TIME, _NET_WM_USER_TIME_WINDOW, diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 5be04a984a..40106bbeaf 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -903,6 +903,8 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates() result |= NetWmStateStaysOnTop; if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION))) result |= NetWmStateDemandsAttention; + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_HIDDEN))) + result |= NetWmStateHidden; } else { qCDebug(lcQpaXcb, "getting net wm state (%x), empty\n", m_window); } @@ -1074,6 +1076,9 @@ void QXcbWindow::setNetWmStateOnUnmappedWindow() states |= NetWmStateBelow; } + if (window()->windowStates() & Qt::WindowMinimized) + states |= NetWmStateHidden; + if (window()->windowStates() & Qt::WindowFullScreen) states |= NetWmStateFullScreen; @@ -1107,6 +1112,8 @@ void QXcbWindow::setNetWmStateOnUnmappedWindow() atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE)); if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_BELOW))) atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_BELOW)); + if (states & NetWmStateHidden && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_HIDDEN))) + atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_HIDDEN)); if (states & NetWmStateFullScreen && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN))) atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); if (states & NetWmStateMaximizedHorz && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ))) @@ -2204,10 +2211,16 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized)); } } - if (m_minimized) - newState = Qt::WindowMinimized; const NetWmStates states = netWmStates(); + // _NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate that a window would + // not be visible on the screen if its desktop/viewport were active and its coordinates were + // within the screen bounds. The canonical example is that minimized windows should be in + // the _NET_WM_STATE_HIDDEN state. + if (m_minimized && (!connection()->wmSupport()->isSupportedByWM(NetWmStateHidden) + || states.testFlag(NetWmStateHidden))) + newState = Qt::WindowMinimized; + if (states & NetWmStateFullScreen) newState |= Qt::WindowFullScreen; if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert)) diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index d6f370eebe..0cdc40f82d 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -68,7 +68,8 @@ public: NetWmStateMaximizedVert = 0x10, NetWmStateModal = 0x20, NetWmStateStaysOnTop = 0x40, - NetWmStateDemandsAttention = 0x80 + NetWmStateDemandsAttention = 0x80, + NetWmStateHidden = 0x100 }; Q_DECLARE_FLAGS(NetWmStates, NetWmState) |