Move all the position computation on the C++ side
authorKevin Ottens <kevin.ottens@nextcloud.com>
Wed, 20 May 2020 17:36:57 +0000 (19:36 +0200)
committerKevin Ottens (Rebase PR Action) <er-vin@users.noreply.github.com>
Mon, 15 Jun 2020 12:32:25 +0000 (12:32 +0000)
The API is just more convenient there, the rect and point types on the
QML side are just pale shadow of their C++ counterparts.

Also improved a bit the constness of the Systray class.

Signed-off-by: Kevin Ottens <kevin.ottens@nextcloud.com>
src/gui/systray.cpp
src/gui/systray.h
src/gui/tray/Window.qml

index 3370a85f77b01d88fb93798a738f817238df21d9..5db933983fed9d15b82aec289be4de6a1a65d297 100644 (file)
@@ -61,8 +61,6 @@ Systray::Systray()
 
     connect(AccountManager::instance(), &AccountManager::accountAdded,
         this, &Systray::showWindow);
-
-    qmlRegisterUncreatableType<Systray>("com.nextcloud.gui", 1, 0, "Systray", "This type is uncreatable, it is exported for its enums");
 }
 
 void Systray::create()
@@ -146,8 +144,22 @@ void Systray::pauseResumeSync()
 /* Helper functions for cross-platform tray icon position and taskbar orientation detection */
 /********************************************************************************************/
 
+QScreen *Systray::currentScreen() const
+{
+    const auto screens = QGuiApplication::screens();
+    const auto cursorPos = QCursor::pos();
+
+    for (const auto screen : screens) {
+        if (screen->geometry().contains(cursorPos)) {
+            return screen;
+        }
+    }
+
+    return nullptr;
+}
+
 /// Return the current screen index based on cursor position
-int Systray::screenIndex()
+int Systray::screenIndex() const
 {
     auto qPos = QCursor::pos();
     for (int i = 0; i < QGuiApplication::screens().count(); i++) {
@@ -158,7 +170,7 @@ int Systray::screenIndex()
     return 0;
 }
 
-Systray::TaskBarPosition Systray::taskbarOrientation()
+Systray::TaskBarPosition Systray::taskbarOrientation() const
 {
 // macOS: Always on top
 #if defined(Q_OS_MACOS)
@@ -208,7 +220,7 @@ Systray::TaskBarPosition Systray::taskbarOrientation()
 }
 
 // TODO: Get real taskbar dimensions Linux as well
-QRect Systray::taskbarRect()
+QRect Systray::taskbarGeometry() const
 {
 #if defined(Q_OS_WIN)
     QRect tbRect = Utility::getTaskbarDimensions();
@@ -234,15 +246,90 @@ QRect Systray::taskbarRect()
 #endif
 }
 
-QPoint Systray::calcTrayIconCenter()
+QRect Systray::currentScreenRect() const
+{
+    const auto screen = currentScreen();
+    const auto rect = screen->geometry();
+    return rect.translated(screen->virtualGeometry().topLeft());
+}
+
+QPoint Systray::computeWindowReferencePoint(int width, int height) const
+{
+    const auto trayIconCenter = calcTrayIconCenter();
+    const auto taskbarRect = taskbarGeometry();
+    const auto taskbarScreenEdge = taskbarOrientation();
+    const auto screenRect = currentScreenRect();
+
+    switch(taskbarScreenEdge) {
+    case TaskBarPosition::Bottom:
+        return {
+            trayIconCenter.x() - width / 2,
+            screenRect.bottom() - taskbarRect.height() - height - 4
+        };
+    case TaskBarPosition::Left:
+        return {
+            screenRect.left() + taskbarRect.width() + 4,
+            trayIconCenter.y()
+        };
+    case TaskBarPosition::Top:
+        return {
+            trayIconCenter.x() - width / 2,
+            screenRect.top() + taskbarRect.height() + 4
+        };
+    case TaskBarPosition::Right:
+        return {
+            screenRect.right() - taskbarRect.width() - width - 4,
+            trayIconCenter.y()
+        };
+    }
+    Q_UNREACHABLE();
+}
+
+QPoint Systray::computeWindowPosition(int width, int height) const
+{
+    auto referencePoint = computeWindowReferencePoint(width, height);
+
+    const auto taskbarScreenEdge = taskbarOrientation();
+    const auto taskbarRect = taskbarGeometry();
+    const auto screenRect = currentScreenRect();
+
+    if (screenRect.right() <= referencePoint.x() + width) {
+        referencePoint.rx() = screenRect.right() - width - 4;
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MACOS)
+        referencePoint.rx() -= taskbarScreenEdge == TaskBarPosition::Right ? taskbarRect.width() : 0;
+#endif
+    }
+
+    if (referencePoint.x() <= screenRect.left()) {
+        referencePoint.rx() = screenRect.left() + 4;
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MACOS)
+        referencePoint.rx() += taskbarScreenEdge == TaskBarPosition::Left ? taskbarRect.width() : 0;
+#endif
+    }
+
+    if (referencePoint.y() <= screenRect.top()) {
+        referencePoint.ry() = screenRect.top() + 4;
+
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MACOS)
+        referencePoint.ry() += taskbarScreenEdge == TaskBarPosition::Top ? taskbarRect.height() : 0;
+#endif
+    }
+    if (screenRect.bottom() <= referencePoint.y() + height) {
+        referencePoint.ry() = screenRect.bottom() - height - 4;
+    }
+
+    return referencePoint;
+}
+
+QPoint Systray::calcTrayIconCenter() const
 {
-// QSystemTrayIcon::geometry() is broken for ages on most Linux DEs (invalid geometry returned)
-// thus we can use this only for Windows and macOS
+    // 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)
+    // On Linux, fall back to mouse position (assuming tray icon is activated by mouse click)
     return QCursor::pos();
 #endif
 }
index 5dd26ad043acc9d8519ebf95406ee16290ff6acb..bc19237071b8b86a174a389a67da646b2ec81426 100644 (file)
@@ -20,6 +20,7 @@
 #include "accountmanager.h"
 #include "tray/UserModel.h"
 
+class QScreen;
 class QQmlApplicationEngine;
 
 namespace OCC {
@@ -57,10 +58,11 @@ public:
     Q_INVOKABLE bool syncIsPaused();
     Q_INVOKABLE void setOpened();
     Q_INVOKABLE void setClosed();
-    Q_INVOKABLE int screenIndex();
-    Q_INVOKABLE QPoint calcTrayIconCenter();
-    Q_INVOKABLE TaskBarPosition taskbarOrientation();
-    Q_INVOKABLE QRect taskbarRect();
+    Q_INVOKABLE int screenIndex() const;
+    Q_INVOKABLE QPoint calcTrayIconCenter() const;
+    Q_INVOKABLE TaskBarPosition taskbarOrientation() const;
+    Q_INVOKABLE QRect taskbarGeometry() const;
+    Q_INVOKABLE QPoint computeWindowPosition(int width, int height) const;
 
 signals:
     void currentUserChanged();
@@ -80,6 +82,11 @@ public slots:
 private:
     static Systray *_instance;
     Systray();
+
+    QScreen *currentScreen() const;
+    QRect currentScreenRect() const;
+    QPoint computeWindowReferencePoint(int width, int height) const;
+
     bool _isOpen = false;
     bool _syncIsPaused = false;
     QQmlApplicationEngine *_trayEngine;
index f0c82986efb8f9eca8d16ae0fc9a055f67a05e44..505875808509cb153d215a67bdeff19cf62b37c3 100644 (file)
@@ -8,97 +8,8 @@ import QtGraphicalEffects 1.0
 \r
 // Custom qml modules are in /theme (and included by resources.qrc)\r
 import Style 1.0\r
-import com.nextcloud.gui 1.0\r
 \r
 Window {\r
-\r
-    function setTrayWindowPosition()\r
-    {\r
-        var trayIconCenter = systrayBackend.calcTrayIconCenter();\r
-        console.debug("Calculated tray icon center:",trayIconCenter);\r
-        var currentScreen = systrayBackend.screenIndex();\r
-        console.debug("Tray menu about to show on screen",currentScreen,".");\r
-        trayWindow.screen = Qt.application.screens[currentScreen];\r
-        trayWindow.show();\r
-        trayWindow.raise();\r
-        trayWindow.requestActivate();\r
-        var trayWindowX;\r
-        var trayWindowY;\r
-        var taskbarOrientation = systrayBackend.taskbarOrientation();\r
-        var taskbarRect = systrayBackend.taskbarRect();\r
-        var screenRect = Qt.rect(0, 0, Screen.width, Screen.height)\r
-        if (Qt.platform.os === "linux") {\r
-            screenRect.x = Screen.virtualX\r
-            screenRect.y = Screen.virtualY\r
-        }\r
-\r
-        switch(taskbarOrientation) {\r
-            // Platform separation here: Windows and macOS draw coordinates have to be given in screen-coordinates\r
-            // KDE and most xorg based DEs expect them as virtual coordinates\r
-            case Systray.Bottom:\r
-                console.debug("Taskbar is on the bottom.");\r
-                trayWindowX = trayIconCenter.x - trayWindow.width / 2;\r
-                trayWindowY = screenRect.bottom - taskbarRect.height - trayWindow.height - 4;\r
-                break;\r
-            case Systray.Left:\r
-                console.debug("Taskbar is on the left.");\r
-                trayWindowX = screenRect.left + taskbarRect.width + 4;\r
-                trayWindowY = trayIconCenter.y;\r
-                break;\r
-            case Systray.Top:\r
-                console.debug("Taskbar is on the top.");\r
-                trayWindowX = trayIconCenter.x - trayWindow.width / 2;\r
-                trayWindowY = screenRect.top + taskbarRect.height + 4;\r
-                break;\r
-            case Systray.Right:\r
-                console.debug("Taskbar is on the right.");\r
-                trayWindowX = screenRect.right - taskbarRect.width - trayWindow.width - 4;\r
-                trayWindowY = trayIconCenter.y;\r
-                break;\r
-        }\r
-\r
-        console.debug("Screen.height:", Screen.height);\r
-        console.debug("Screen.desktopAvailableHeight:", Screen.desktopAvailableHeight);\r
-        console.debug("Screen.virtualY:", Screen.virtualY);\r
-        console.debug("Screen.width:", Screen.width);\r
-        console.debug("Screen.desktopAvailableWidth:", Screen.desktopAvailableWidth);\r
-        console.debug("Screen.virtualX:", Screen.virtualX);\r
-        console.debug("Taskbar height:", taskbarRect.height);\r
-        console.debug("Taskbar width:", taskbarRect.width);\r
-\r
-        if (screenRect.right <= trayWindowX + trayWindow.width) {\r
-            console.debug("Out-of-screen condition on the right detected. Adjusting window position.");\r
-            trayWindowX = screenRect.right - trayWindow.width - 4;\r
-\r
-            if (Qt.platform.os === "linux") {\r
-                trayWindowX -= taskbarOrientation === Systray.Right ? taskbarRect.width : 0;\r
-            }\r
-        }\r
-        if (trayWindowX <= screenRect.left) {\r
-            console.debug("Out-of-screen condition on the left detected. Adjusting window position.");\r
-            trayWindowX = screenRect.left + 4;\r
-\r
-            if (Qt.platform.os === "linux") {\r
-               trayWindowX += taskbarOrientation === Systray.Left ? taskbarRect.width : 0;\r
-            }\r
-        }\r
-        if (trayWindowY <= screenRect.top) {\r
-            console.debug("Out-of-screen condition on the top detected. Adjusting window position.");\r
-            trayWindowY = screenRect.top + 4;\r
-\r
-            if (Qt.platform.os === "linux") {\r
-                trayWindowY += taskbarOrientation === Systray.Top ? taskbarRect.height : 0;\r
-            }\r
-        }\r
-        if (screenRect.bottom <= trayWindowY + trayWindow.height) {\r
-            console.debug("Out-of-screen condition on the bottom detected. Adjusting window position.");\r
-            trayWindowY = screenRect.bottom - trayWindow.height - 4;\r
-        }\r
-        console.debug("Tray window position: x =",trayWindowX," y =",trayWindowY);\r
-        trayWindow.setX(trayWindowX);\r
-        trayWindow.setY(trayWindowY);\r
-    }\r
-\r
     id:         trayWindow\r
 \r
     width:      Style.trayWindowWidth\r
@@ -151,7 +62,17 @@ Window {
         target: systrayBackend\r
         onShowWindow: {\r
             accountMenu.close();\r
-            setTrayWindowPosition();\r
+\r
+            trayWindow.screen = Qt.application.screens[systrayBackend.screenIndex()];\r
+\r
+            var position = systrayBackend.computeWindowPosition(trayWindow.width, trayWindow.height)\r
+            trayWindow.x = position.x\r
+            trayWindow.y = position.y\r
+\r
+            trayWindow.show();\r
+            trayWindow.raise();\r
+            trayWindow.requestActivate();\r
+\r
             systrayBackend.setOpened();\r
             userModelBackend.fetchCurrentActivityModel();\r
         }\r