From af6493b07b5ca9fd01da8324f589eaf3fc0dd000 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 26 May 2020 21:46:28 +0200 Subject: [PATCH] Hide window and dock icon on macOS when tray enabled. Transforms application into a UIElement agent app when minimize to tray is enabled and the window is hidden. This hides the dock icon and removes the application from the Cmd+Tab listing. The changes work well together with macOS's inbuilt hide feature. Also fixes the buggy tray icon context menu trigger behaviour. macOS triggers the tray context menu also on normal left click, which causes the window to toggle at the same time as the menu. To fix this, window toggling has been disabled altogether on macOS and users will be shown only the context menu from now on. Fixes #1334 --- src/gui/MainWindow.cpp | 53 +++++++++++++++----------- src/gui/MainWindow.h | 1 - src/gui/osutils/macutils/AppKit.h | 1 + src/gui/osutils/macutils/AppKitImpl.h | 1 + src/gui/osutils/macutils/AppKitImpl.mm | 15 ++++++++ src/gui/osutils/macutils/MacUtils.cpp | 11 ++++++ src/gui/osutils/macutils/MacUtils.h | 1 + 7 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 69ef33a80..eed3a903a 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1368,6 +1368,16 @@ void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason) void MainWindow::processTrayIconTrigger() { +#ifdef Q_OS_MACOS + // Do not toggle the window on macOS and just show the context menu instead. + // Right click detection doesn't seem to be working anyway + // and anything else will only trigger the context menu AND + // toggle the window at the same time, which is confusing at best. + // Showing only a context menu for tray icons seems to be best + // practice on macOS anyway, so this is probably fine. + return; +#endif + if (m_trayIconTriggerReason == QSystemTrayIcon::DoubleClick) { // Always toggle window on double click toggleWindow(); @@ -1379,7 +1389,7 @@ void MainWindow::processTrayIconTrigger() // clicking the tray icon removes focus from main window. if (isHidden() || (Clock::currentMilliSecondsSinceEpoch() - m_lastFocusOutTime) <= 500) { #else - // If on Linux or macOS, check if the window has focus. + // If on Linux, check if the window has focus. if (hasFocus() || isHidden() || windowHandle()->isActive()) { #endif toggleWindow(); @@ -1394,40 +1404,39 @@ void MainWindow::show() #ifndef Q_OS_WIN m_lastShowTime = Clock::currentMilliSecondsSinceEpoch(); #endif - QMainWindow::show(); -} - -bool MainWindow::shouldHide() -{ -#ifndef Q_OS_WIN - qint64 current_time = Clock::currentMilliSecondsSinceEpoch(); - - if (current_time - m_lastShowTime < 50) { - return false; - } +#ifdef Q_OS_MACOS + // Unset minimize state to avoid weird fly-in effects + setWindowState(windowState() & ~Qt::WindowMinimized); + macUtils()->toggleForegroundApp(true); #endif - return true; + QMainWindow::show(); } void MainWindow::hide() { - if (shouldHide()) { - QMainWindow::hide(); +#ifndef Q_OS_WIN + qint64 current_time = Clock::currentMilliSecondsSinceEpoch(); + if (current_time - m_lastShowTime < 50) { + return; } +#endif + QMainWindow::hide(); +#ifdef Q_OS_MACOS + macUtils()->toggleForegroundApp(false); +#endif } void MainWindow::hideWindow() { saveWindowInformation(); - if (QGuiApplication::platformName() != "xcb") { - // In X11 the window should NOT be minimized and hidden (i.e. not - // shown) at the same time (which would happen if both minimize on - // startup and minimize to tray are set) since otherwise it causes - // problems on restore as seen on issue #1595. Hiding it is enough. - setWindowState(windowState() | Qt::WindowMinimized); - } + // Only hide if tray icon is active, otherwise window will be gone forever if (isTrayIconEnabled()) { + // On X11, the window should NOT be minimized and hidden at the same time. See issue #1595. + // On macOS, we are skipping minimization as well to avoid playing the magic lamp animation. + if (QGuiApplication::platformName() != "xcb" && QGuiApplication::platformName() != "cocoa") { + setWindowState(windowState() | Qt::WindowMinimized); + } hide(); } else { showMinimized(); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 9872145a7..b901ef54c 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -141,7 +141,6 @@ private: static const QString BaseWindowTitle; - bool shouldHide(); void saveWindowInformation(); bool saveLastDatabases(); void updateTrayIcon(); diff --git a/src/gui/osutils/macutils/AppKit.h b/src/gui/osutils/macutils/AppKit.h index 2bff8f5b3..a6f7b3a12 100644 --- a/src/gui/osutils/macutils/AppKit.h +++ b/src/gui/osutils/macutils/AppKit.h @@ -39,6 +39,7 @@ public: bool isDarkMode(); bool enableAccessibility(); bool enableScreenRecording(); + void toggleForegroundApp(bool foreground); signals: void lockDatabases(); diff --git a/src/gui/osutils/macutils/AppKitImpl.h b/src/gui/osutils/macutils/AppKitImpl.h index 326879766..5dadc31dd 100644 --- a/src/gui/osutils/macutils/AppKitImpl.h +++ b/src/gui/osutils/macutils/AppKitImpl.h @@ -38,5 +38,6 @@ - (void) userSwitchHandler:(NSNotification*) notification; - (bool) enableAccessibility; - (bool) enableScreenRecording; +- (void) toggleForegroundApp:(bool) foreground; @end diff --git a/src/gui/osutils/macutils/AppKitImpl.mm b/src/gui/osutils/macutils/AppKitImpl.mm index 4a93f963a..077dd71a6 100644 --- a/src/gui/osutils/macutils/AppKitImpl.mm +++ b/src/gui/osutils/macutils/AppKitImpl.mm @@ -154,6 +154,16 @@ return YES; } +- (void) toggleForegroundApp:(bool) foreground +{ + ProcessSerialNumber psn = {0, kCurrentProcess}; + if (foreground) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + } else { + TransformProcessType(&psn, kProcessTransformToUIElementApplication); + } +} + @end // @@ -215,3 +225,8 @@ bool AppKit::enableScreenRecording() { return [static_cast(self) enableScreenRecording]; } + +void AppKit::toggleForegroundApp(bool foreground) +{ + [static_cast(self) toggleForegroundApp:foreground]; +} diff --git a/src/gui/osutils/macutils/MacUtils.cpp b/src/gui/osutils/macutils/MacUtils.cpp index 9203d73f2..d32a15612 100644 --- a/src/gui/osutils/macutils/MacUtils.cpp +++ b/src/gui/osutils/macutils/MacUtils.cpp @@ -123,3 +123,14 @@ bool MacUtils::isCapslockEnabled() { return (CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState) & kCGEventFlagMaskAlphaShift) != 0; } + +/** + * Toggle application state between foreground app and UIElement app. + * Foreground apps have dock icons, UIElement apps do not. + * + * @param foreground whether app is foreground app + */ +void MacUtils::toggleForegroundApp(bool foreground) +{ + m_appkit->toggleForegroundApp(foreground); +} diff --git a/src/gui/osutils/macutils/MacUtils.h b/src/gui/osutils/macutils/MacUtils.h index ccabf6788..281c5438c 100644 --- a/src/gui/osutils/macutils/MacUtils.h +++ b/src/gui/osutils/macutils/MacUtils.h @@ -46,6 +46,7 @@ public: bool isHidden(); bool enableAccessibility(); bool enableScreenRecording(); + void toggleForegroundApp(bool foreground); signals: void lockDatabases();