diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoasystemtrayicon.h | 21 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 276 |
2 files changed, 97 insertions, 200 deletions
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 1d4b84ee5e..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 @@ -230,27 +207,26 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) auto *nsimage = [NSImage imageFromQImage:fullHeightPixmap.toImage()]; [nsimage setTemplate:icon.isMask()]; - [(NSImageView*)[[m_sys->item item] view] setImage: nsimage]; + 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 @@ -266,175 +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()]; + 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; - - [self setNeedsDisplay:YES]; -} + auto *mouseEvent = NSApp.currentEvent; -- (void)mousePressed:(NSEvent *)mouseEvent -{ - self.down = YES; - int clickCount = [mouseEvent clickCount]; - [self setNeedsDisplay:YES]; + auto activationReason = QPlatformSystemTrayIcon::Unknown; - 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]; -} + emit activated(activationReason); -- (void)mouseUp:(NSEvent *)mouseEvent -{ - Q_UNUSED(mouseEvent); - [self menuTrackingDone:nil]; + if (NSMenu *menu = m_menu ? m_menu->nsMenu() : nil) + [m_statusItem popUpStatusItemMenu:menu]; } -- (void)rightMouseDown:(NSEvent *)mouseEvent -{ - [self mousePressed:mouseEvent]; -} +QT_END_NAMESPACE -- (void)rightMouseUp:(NSEvent *)mouseEvent -{ - Q_UNUSED(mouseEvent); - [self menuTrackingDone:nil]; -} +@implementation QStatusItemDelegate -- (void)otherMouseDown:(NSEvent *)mouseEvent +- (instancetype)initWithSysTray:(QCocoaSystemTrayIcon *)platformSystemTray { - [self mousePressed:mouseEvent]; -} - -- (void)otherMouseUp:(NSEvent *)mouseEvent -{ - 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; -} - -@synthesize menu = menu; -@synthesize icon = icon; + if ((self = [super init])) + self.platformSystemTray = platformSystemTray; -- (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 |