From: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com> Date: Fri, 24 Apr 2020 16:34:11 +0000 (+0200) Subject: Major multi monitor improvements and rewrite of tray window positioning X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~222^2^2~160^2~11 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=f44df7f61a80c3bc86164ef1e2c3e9732cdc724e;p=nextcloud-desktop.git Major multi monitor improvements and rewrite of tray window positioning Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com> --- diff --git a/src/3rdparty/qtmacgoodies b/src/3rdparty/qtmacgoodies new file mode 160000 index 000000000..b59d091b3 --- /dev/null +++ b/src/3rdparty/qtmacgoodies @@ -0,0 +1 @@ +Subproject commit b59d091b3e6b98e7219cf636f7d21fb267242c27 diff --git a/src/common/utility.h b/src/common/utility.h index e8a243546..1aa17928c 100644 --- a/src/common/utility.h +++ b/src/common/utility.h @@ -229,6 +229,7 @@ namespace Utility { OCSYNC_EXPORT bool registryDeleteKeyTree(HKEY hRootKey, const QString &subKey); OCSYNC_EXPORT bool registryDeleteKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName); OCSYNC_EXPORT bool registryWalkSubKeys(HKEY hRootKey, const QString &subKey, const std::function &callback); + OCSYNC_EXPORT QRect getTaskbarDimensions(); #endif } /** @} */ // \addtogroup diff --git a/src/common/utility_win.cpp b/src/common/utility_win.cpp index f8157a68a..9df84e0c5 100644 --- a/src/common/utility_win.cpp +++ b/src/common/utility_win.cpp @@ -100,6 +100,20 @@ static inline bool hasDarkSystray_private() } } +QRect Utility::getTaskbarDimensions() +{ + APPBARDATA barData; + barData.cbSize = sizeof(APPBARDATA); + + BOOL fResult = (BOOL)SHAppBarMessage(ABM_GETTASKBARPOS, &barData); + if (!fResult) { + return QRect(); + } + + RECT barRect = barData.rc; + return QRect(barRect.left, barRect.top, (barRect.right - barRect.left), (barRect.bottom - barRect.top)); +} + QVariant Utility::registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName) { QVariant value; @@ -142,6 +156,15 @@ QVariant Utility::registryGetKeyValue(HKEY hRootKey, const QString &subKey, cons } break; } + case REG_BINARY: { + QByteArray buffer; + buffer.resize(sizeInBytes); + result = RegQueryValueEx(hKey, reinterpret_cast(valueName.utf16()), 0, &type, reinterpret_cast(buffer.data()), &sizeInBytes); + if (result == ERROR_SUCCESS) { + value = buffer.at(12); + } + break; + } default: Q_UNREACHABLE(); } diff --git a/src/gui/systray.cpp b/src/gui/systray.cpp index 66ac956f2..98d55c1f9 100644 --- a/src/gui/systray.cpp +++ b/src/gui/systray.cpp @@ -16,6 +16,7 @@ #include "systray.h" #include "theme.h" #include "config.h" +#include "common/utility.h" #include "tray/UserModel.h" #include @@ -60,6 +61,7 @@ Systray::Systray() connect(AccountManager::instance(), &AccountManager::accountAdded, this, &Systray::showWindow); + } void Systray::create() @@ -69,6 +71,7 @@ void Systray::create() } _trayEngine->load(QStringLiteral("qrc:/qml/src/gui/tray/Window.qml")); hideWindow(); + emit activated(QSystemTrayIcon::ActivationReason::Unknown); } void Systray::slotNewUserSelected() @@ -85,17 +88,6 @@ bool Systray::isOpen() return _isOpen; } -Q_INVOKABLE int Systray::screenIndex() -{ - auto qPos = QCursor::pos(); - for (int i = 0; i < QGuiApplication::screens().count(); i++) { - if (QGuiApplication::screens().at(i)->geometry().contains(qPos)) { - return i; - } - } - return 0; -} - Q_INVOKABLE void Systray::setOpened() { _isOpen = true; @@ -133,123 +125,87 @@ void Systray::setToolTip(const QString &tip) QSystemTrayIcon::setToolTip(tr("%1: %2").arg(Theme::instance()->appNameGUI(), tip)); } -int Systray::calcTrayWindowX() +bool Systray::syncIsPaused() { -#ifdef Q_OS_OSX - // macOS handles DPI awareness differently - // and menu bar is always at the top, icons starting from the right - - QPoint topLeft = this->geometry().topLeft(); - QPoint topRight = this->geometry().topRight(); - int trayIconTopCenterX = (topRight - ((topRight - topLeft) * 0.5)).x(); - return trayIconTopCenterX - (400 * 0.5); -#else - QScreen* trayScreen = nullptr; - if (QGuiApplication::screens().count() > 1) { - trayScreen = QGuiApplication::screens().at(screenIndex()); - } else { - trayScreen = QGuiApplication::primaryScreen(); - } + return _syncIsPaused; +} - int screenWidth = trayScreen->geometry().width(); - int screenHeight = trayScreen->geometry().height(); - int availableWidth = trayScreen->availableGeometry().width(); - int availableHeight = trayScreen->availableGeometry().height(); - - QPoint topRightDpiAware = QPoint(); - QPoint topLeftDpiAware = QPoint(); - if (this->geometry().left() == 0 || this->geometry().top() == 0) { - // tray geometry is invalid - QT bug on some linux desktop environments - // Use mouse position instead. Cringy, but should work for now - topRightDpiAware = QCursor::pos() / trayScreen->devicePixelRatio(); - topLeftDpiAware = QCursor::pos() / trayScreen->devicePixelRatio(); +void Systray::pauseResumeSync() +{ + if (_syncIsPaused) { + _syncIsPaused = false; + emit resumeSync(); } else { - topRightDpiAware = this->geometry().topRight() / trayScreen->devicePixelRatio(); - topLeftDpiAware = this->geometry().topLeft() / trayScreen->devicePixelRatio(); + _syncIsPaused = true; + emit pauseSync(); } +} - // get x coordinate from top center point of tray icon - int trayIconTopCenterX = (topRightDpiAware - ((topRightDpiAware - topLeftDpiAware) * 0.5)).x(); +/********************************************************************************************/ +/* Helper functions for cross-platform tray icon position and taskbar orientation detection */ +/********************************************************************************************/ - if (availableHeight < screenHeight) { - // taskbar is on top or bottom - if (trayIconTopCenterX + (400 * 0.5) > availableWidth) { - return availableWidth - 400 - 12; - } else { - return trayIconTopCenterX - (400 * 0.5); - } - } else { - if (trayScreen->availableGeometry().x() > trayScreen->geometry().x()) { - // on the left - return (screenWidth - availableWidth) + 6; - } else { - // on the right - return screenWidth - 400 - (screenWidth - availableWidth) - 6; +/// Return the current screen index based on cursor position +int Systray::screenIndex() +{ + auto qPos = QCursor::pos(); + for (int i = 0; i < QGuiApplication::screens().count(); i++) { + if (QGuiApplication::screens().at(i)->geometry().contains(qPos)) { + return i; } } -#endif + return 0; } -int Systray::calcTrayWindowY() -{ -#ifdef Q_OS_OSX - // macOS menu bar is always 22 (effective) pixels - // don't use availableGeometry() here, because this also excludes the dock - return 22+6; -#else - QScreen* trayScreen = nullptr; - if (QGuiApplication::screens().count() > 1) { - trayScreen = QGuiApplication::screens().at(screenIndex()); - } else { - trayScreen = QGuiApplication::primaryScreen(); - } - - int screenHeight = trayScreen->geometry().height(); - int availableHeight = trayScreen->availableGeometry().height(); - QPoint topRightDpiAware = QPoint(); - QPoint topLeftDpiAware = QPoint(); - if (this->geometry().left() == 0 || this->geometry().top() == 0) { - // tray geometry is invalid - QT bug on some linux desktop environments - // Use mouse position instead. Cringy, but should work for now - topRightDpiAware = QCursor::pos() / trayScreen->devicePixelRatio(); - topLeftDpiAware = QCursor::pos() / trayScreen->devicePixelRatio(); - } else { - topRightDpiAware = this->geometry().topRight() / trayScreen->devicePixelRatio(); - topLeftDpiAware = this->geometry().topLeft() / trayScreen->devicePixelRatio(); - } - // get y coordinate from top center point of tray icon - int trayIconTopCenterY = (topRightDpiAware - ((topRightDpiAware - topLeftDpiAware) * 0.5)).y(); - - if (availableHeight < screenHeight) { - // taskbar is on top or bottom - if (trayScreen->availableGeometry().y() > trayScreen->geometry().y()) { - // on top - return (screenHeight - availableHeight) + 6; - } else { - // on bottom - return screenHeight - 510 - (screenHeight - availableHeight) - 6; - } - } else { - // on the left or right - return (trayIconTopCenterY - 510 + 12); +int Systray::taskbarOrientation() +{ +// macOS: Always on top +#if defined(Q_OS_MACOS) + return TaskBarPosition::Top; +// Windows: Check registry for actual taskbar orientation +#elif defined(Q_OS_WIN) + auto taskbarPosition = Utility::registryGetKeyValue(HKEY_CURRENT_USER, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects3", + "Settings"); + switch (taskbarPosition.toInt()) { + // Mapping windows binary value (0 = left, 1 = top, 2 = right, 3 = bottom) to qml logic (0 = bottom, 1 = left...) + case 0: + return TaskBarPosition::Left; + case 1: + return TaskBarPosition::Top; + case 2: + return TaskBarPosition::Right; + case 3: + return TaskBarPosition::Bottom; + default: + return TaskBarPosition::Bottom; } +// Else (generally linux DEs): fallback to cursor position nearest edge logic +#else + return 0; #endif } -bool Systray::syncIsPaused() +QRect Systray::taskbarRect() { - return _syncIsPaused; +#if defined(Q_OS_WIN) + return Utility::getTaskbarDimensions(); +#else + return QRect(0, 0, 0, 32); +#endif } -void Systray::pauseResumeSync() +QPoint Systray::calcTrayIconCenter() { - if (_syncIsPaused) { - _syncIsPaused = false; - emit resumeSync(); - } else { - _syncIsPaused = true; - emit pauseSync(); - } +// QSystemTrayIcon::geometry() is broken for ages on most Linux DEs (invalid geometry returned) +// thus we can use this only for Windows and macOS +#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) + auto trayIconCenter = geometry().center(); + return trayIconCenter; +#else +// On Linux, fall back to mouse position (assuming tray icon is activated by mouse click) + return QCursor::pos(); +#endif } } // namespace OCC diff --git a/src/gui/systray.h b/src/gui/systray.h index 0152a75ff..ad92c7a54 100644 --- a/src/gui/systray.h +++ b/src/gui/systray.h @@ -33,6 +33,13 @@ namespace Ui { class Systray; } +enum TaskBarPosition { + Bottom = 0, + Left, + Top, + Right +}; + /** * @brief The Systray class * @ingroup gui @@ -51,12 +58,13 @@ public: bool isOpen(); Q_INVOKABLE void pauseResumeSync(); - Q_INVOKABLE int calcTrayWindowX(); - Q_INVOKABLE int calcTrayWindowY(); Q_INVOKABLE bool syncIsPaused(); Q_INVOKABLE void setOpened(); Q_INVOKABLE void setClosed(); Q_INVOKABLE int screenIndex(); + Q_INVOKABLE QPoint calcTrayIconCenter(); + Q_INVOKABLE int taskbarOrientation(); + Q_INVOKABLE QRect taskbarRect(); signals: void currentUserChanged(); diff --git a/src/gui/tray/Window.qml b/src/gui/tray/Window.qml index ce1cda2c4..1ae7973db 100644 --- a/src/gui/tray/Window.qml +++ b/src/gui/tray/Window.qml @@ -10,6 +10,125 @@ import QtGraphicalEffects 1.0 import Style 1.0 Window { + + function setTrayWindowPosition() + { + var trayIconCenter = systrayBackend.calcTrayIconCenter(); + console.debug("Calculated tray icon center:",trayIconCenter); + var currentScreen = systrayBackend.screenIndex(); + console.debug("Tray menu about to show on screen",currentScreen,"."); + trayWindow.screen = Qt.application.screens[currentScreen]; + trayWindow.show(); + trayWindow.raise(); + trayWindow.requestActivate(); + var trayWindowX; + var trayWindowY; + var taskbarHeight; + var taskbarWidth; + var tbOrientation; + if (Qt.platform.os === "linux") { + var distBottom = Screen.height - (trayIconCenter.y - Screen.virtualY); + var distRight = Screen.width - (trayIconCenter.x - Screen.virtualX); + var distLeft = trayIconCenter.x - Screen.virtualX; + var distTop = trayIconCenter.y - Screen.virtualY; + if (distBottom < distRight && distBottom < distTop && distBottom < distLeft) { + tbOrientation = 0; + } else if (distLeft < distTop && distLeft < distRight && distLeft < distBottom) { + tbOrientation = 1; + } else if (distTop < distRight && distTop < distBottom && distTop < distLeft) { + tbOrientation = 2; + } else { + tbOrientation = 3; + } + } else { + tbOrientation = systrayBackend.taskbarOrientation(); + } + if (Qt.platform.os === "osx") { + taskbarHeight = 22; + taskbarWidth = Screen.width; + } else if (Qt.platform.os === "linux") { + taskbarHeight = (tbOrientation === 0 || tbOrientation === 2) ? 32 : Screen.height; + taskbarWidth = (tbOrientation === 0 || tbOrientation === 2) ? Screen.width : 32; + } else { + taskbarHeight = systrayBackend.taskbarRect().height; + taskbarWidth = systrayBackend.taskbarRect().width; + } + + switch(tbOrientation) { + // Platform separation here: Windows and macOS draw coordinates have to be given in screen-coordinates + // KDE and most xorg based DEs expect them as virtual coordinates + case 0: + console.debug("Taskbar is on the bottom."); + trayWindowX = trayIconCenter.x - trayWindow.width / 2; + trayWindowY = (Qt.platform.os !== "linux") ? (Screen.height - taskbarHeight - trayWindow.height - 4) + : (Screen.height + Screen.virtualY - taskbarHeight - trayWindow.height - 4); + break; + case 1: + console.debug("Taskbar is on the left."); + trayWindowX = (Qt.platform.os !== "linux") ? (taskbarWidth + 4) + : (Screen.virtualX + taskbarWidth + 4); + trayWindowY = trayIconCenter.y; + break; + case 2: + console.debug("Taskbar is on the top."); + trayWindowX = trayIconCenter.x - trayWindow.width / 2; + trayWindowY = Screen.virtualY + taskbarHeight + 4; + break; + case 3: + console.debug("Taskbar is on the right."); + trayWindowX = (Qt.platform.os !== "linux") ? (Screen.width - taskbarWidth - trayWindow.width - 4) + : (Screen.width + Screen.virtualX - taskbarWidth - trayWindow.width - 4); + trayWindowY = trayIconCenter.y; + break; + } + + console.debug("Screen.height:",Screen.height); + console.debug("Screen.desktopAvailableHeight:",Screen.desktopAvailableHeight); + console.debug("Screen.virtualY:",Screen.virtualY); + console.debug("Screen.width:",Screen.width); + console.debug("Screen.desktopAvailableWidth:",Screen.desktopAvailableWidth); + console.debug("Screen.virtualX:",Screen.virtualX); + console.debug("Taskbar height:",taskbarHeight); + console.debug("Taskbar width:",taskbarWidth); + + if (Screen.width <= trayWindowX + trayWindow.width) { + console.debug("Out-of-screen condition on the right detected. Adjusting window position."); + if (Qt.platform.os !== "linux") { + trayWindowX = Screen.width - trayWindow.width - 4; + } else { + trayWindowX = Screen.width + Screen.virtualX - trayWindow.width - 4 - (tbOrientation === 3 ? taskbarWidth : 0); + } + } + if (trayWindowX <= Screen.x && Qt.platform.os !== "linux") { + console.debug("Out-of-screen condition on the left detected. Adjusting window position."); + trayWindowX = Screen.x + 4; + } + if (trayWindowX <= Screen.virtualX && Qt.platform.os === "linux") { + console.debug("Out-of-screen condition on the left detected. Adjusting window position."); + trayWindowX = Screen.virtualX + 4 + (tbOrientation === 1 ? taskbarWidth : 0) + } + if (trayWindowY <= Screen.y && Qt.platform.os !== "linux") { + console.debug("Out-of-screen condition on the top detected. Adjusting window position."); + trayWindowY = Screen.y + 4; + } + if (trayWindowY <= Screen.virtualY && Qt.platform.os === "linux") { + console.debug("Out-of-screen condition on the top detected. Adjusting window position."); + trayWindowY = Screen.virtualY + 4 + (tbOrientation === 2 ? taskbarHeight : 0); + } + if (Screen.height <= trayWindowY - Screen.virtualY + trayWindow.height) { + console.debug("Out-of-screen condition on the bottom detected. Adjusting window position."); + if (Qt.platform.os !== "linux") { + trayWindowY = Screen.height - trayWindow.height - 4; + } else { + trayWindowY = Screen.height + Screen.virtualY - trayWindow.height - 4; + } + + } + console.debug("Tray window position: x =",trayWindowX," y =",trayWindowY); + trayWindow.setX(trayWindowX); + trayWindow.setY(trayWindowY); + } + id: trayWindow width: Style.trayWindowWidth @@ -62,11 +181,7 @@ Window { target: systrayBackend onShowWindow: { accountMenu.close(); - trayWindow.show(); - trayWindow.raise(); - trayWindow.requestActivate(); - trayWindow.setX( Qt.application.screens[systrayBackend.screenIndex()].virtualX + systrayBackend.calcTrayWindowX()); - trayWindow.setY( Qt.application.screens[systrayBackend.screenIndex()].virtualY + systrayBackend.calcTrayWindowY()); + setTrayWindowPosition(); systrayBackend.setOpened(); userModelBackend.fetchCurrentActivityModel(); }