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
This commit is contained in:
Janek Bevendorff 2020-05-26 21:46:28 +02:00
parent b17b9683e1
commit af6493b07b
7 changed files with 60 additions and 23 deletions

View File

@ -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();

View File

@ -141,7 +141,6 @@ private:
static const QString BaseWindowTitle;
bool shouldHide();
void saveWindowInformation();
bool saveLastDatabases();
void updateTrayIcon();

View File

@ -39,6 +39,7 @@ public:
bool isDarkMode();
bool enableAccessibility();
bool enableScreenRecording();
void toggleForegroundApp(bool foreground);
signals:
void lockDatabases();

View File

@ -38,5 +38,6 @@
- (void) userSwitchHandler:(NSNotification*) notification;
- (bool) enableAccessibility;
- (bool) enableScreenRecording;
- (void) toggleForegroundApp:(bool) foreground;
@end

View File

@ -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<id>(self) enableScreenRecording];
}
void AppKit::toggleForegroundApp(bool foreground)
{
[static_cast<id>(self) toggleForegroundApp:foreground];
}

View File

@ -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);
}

View File

@ -46,6 +46,7 @@ public:
bool isHidden();
bool enableAccessibility();
bool enableScreenRecording();
void toggleForegroundApp(bool foreground);
signals:
void lockDatabases();