--- /dev/null
+Subproject commit b59d091b3e6b98e7219cf636f7d21fb267242c27
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<void(HKEY, const QString &)> &callback);
+ OCSYNC_EXPORT QRect getTaskbarDimensions();
#endif
}
/** @} */ // \addtogroup
}
}
+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;
}
break;
}
+ case REG_BINARY: {
+ QByteArray buffer;
+ buffer.resize(sizeInBytes);
+ result = RegQueryValueEx(hKey, reinterpret_cast<LPCWSTR>(valueName.utf16()), 0, &type, reinterpret_cast<LPBYTE>(buffer.data()), &sizeInBytes);
+ if (result == ERROR_SUCCESS) {
+ value = buffer.at(12);
+ }
+ break;
+ }
default:
Q_UNREACHABLE();
}
#include "systray.h"
#include "theme.h"
#include "config.h"
+#include "common/utility.h"
#include "tray/UserModel.h"
#include <QCursor>
connect(AccountManager::instance(), &AccountManager::accountAdded,
this, &Systray::showWindow);
+
}
void Systray::create()
}
_trayEngine->load(QStringLiteral("qrc:/qml/src/gui/tray/Window.qml"));
hideWindow();
+ emit activated(QSystemTrayIcon::ActivationReason::Unknown);
}
void Systray::slotNewUserSelected()
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;
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
class Systray;
}
+enum TaskBarPosition {
+ Bottom = 0,
+ Left,
+ Top,
+ Right
+};
+
/**
* @brief The Systray class
* @ingroup gui
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();
import Style 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 taskbarHeight;\r
+ var taskbarWidth;\r
+ var tbOrientation;\r
+ if (Qt.platform.os === "linux") {\r
+ var distBottom = Screen.height - (trayIconCenter.y - Screen.virtualY);\r
+ var distRight = Screen.width - (trayIconCenter.x - Screen.virtualX);\r
+ var distLeft = trayIconCenter.x - Screen.virtualX;\r
+ var distTop = trayIconCenter.y - Screen.virtualY;\r
+ if (distBottom < distRight && distBottom < distTop && distBottom < distLeft) {\r
+ tbOrientation = 0;\r
+ } else if (distLeft < distTop && distLeft < distRight && distLeft < distBottom) {\r
+ tbOrientation = 1;\r
+ } else if (distTop < distRight && distTop < distBottom && distTop < distLeft) {\r
+ tbOrientation = 2;\r
+ } else {\r
+ tbOrientation = 3;\r
+ }\r
+ } else {\r
+ tbOrientation = systrayBackend.taskbarOrientation();\r
+ }\r
+ if (Qt.platform.os === "osx") {\r
+ taskbarHeight = 22;\r
+ taskbarWidth = Screen.width;\r
+ } else if (Qt.platform.os === "linux") {\r
+ taskbarHeight = (tbOrientation === 0 || tbOrientation === 2) ? 32 : Screen.height;\r
+ taskbarWidth = (tbOrientation === 0 || tbOrientation === 2) ? Screen.width : 32;\r
+ } else {\r
+ taskbarHeight = systrayBackend.taskbarRect().height;\r
+ taskbarWidth = systrayBackend.taskbarRect().width;\r
+ }\r
+\r
+ switch(tbOrientation) {\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 0:\r
+ console.debug("Taskbar is on the bottom.");\r
+ trayWindowX = trayIconCenter.x - trayWindow.width / 2;\r
+ trayWindowY = (Qt.platform.os !== "linux") ? (Screen.height - taskbarHeight - trayWindow.height - 4)\r
+ : (Screen.height + Screen.virtualY - taskbarHeight - trayWindow.height - 4);\r
+ break;\r
+ case 1:\r
+ console.debug("Taskbar is on the left.");\r
+ trayWindowX = (Qt.platform.os !== "linux") ? (taskbarWidth + 4)\r
+ : (Screen.virtualX + taskbarWidth + 4);\r
+ trayWindowY = trayIconCenter.y;\r
+ break;\r
+ case 2:\r
+ console.debug("Taskbar is on the top.");\r
+ trayWindowX = trayIconCenter.x - trayWindow.width / 2;\r
+ trayWindowY = Screen.virtualY + taskbarHeight + 4;\r
+ break;\r
+ case 3:\r
+ console.debug("Taskbar is on the right.");\r
+ trayWindowX = (Qt.platform.os !== "linux") ? (Screen.width - taskbarWidth - trayWindow.width - 4)\r
+ : (Screen.width + Screen.virtualX - taskbarWidth - 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:",taskbarHeight);\r
+ console.debug("Taskbar width:",taskbarWidth);\r
+\r
+ if (Screen.width <= trayWindowX + trayWindow.width) {\r
+ console.debug("Out-of-screen condition on the right detected. Adjusting window position.");\r
+ if (Qt.platform.os !== "linux") {\r
+ trayWindowX = Screen.width - trayWindow.width - 4;\r
+ } else {\r
+ trayWindowX = Screen.width + Screen.virtualX - trayWindow.width - 4 - (tbOrientation === 3 ? taskbarWidth : 0);\r
+ }\r
+ }\r
+ if (trayWindowX <= Screen.x && Qt.platform.os !== "linux") {\r
+ console.debug("Out-of-screen condition on the left detected. Adjusting window position.");\r
+ trayWindowX = Screen.x + 4;\r
+ }\r
+ if (trayWindowX <= Screen.virtualX && Qt.platform.os === "linux") {\r
+ console.debug("Out-of-screen condition on the left detected. Adjusting window position.");\r
+ trayWindowX = Screen.virtualX + 4 + (tbOrientation === 1 ? taskbarWidth : 0)\r
+ }\r
+ if (trayWindowY <= Screen.y && Qt.platform.os !== "linux") {\r
+ console.debug("Out-of-screen condition on the top detected. Adjusting window position.");\r
+ trayWindowY = Screen.y + 4;\r
+ }\r
+ if (trayWindowY <= Screen.virtualY && Qt.platform.os === "linux") {\r
+ console.debug("Out-of-screen condition on the top detected. Adjusting window position.");\r
+ trayWindowY = Screen.virtualY + 4 + (tbOrientation === 2 ? taskbarHeight : 0);\r
+ }\r
+ if (Screen.height <= trayWindowY - Screen.virtualY + trayWindow.height) {\r
+ console.debug("Out-of-screen condition on the bottom detected. Adjusting window position.");\r
+ if (Qt.platform.os !== "linux") {\r
+ trayWindowY = Screen.height - trayWindow.height - 4;\r
+ } else {\r
+ trayWindowY = Screen.height + Screen.virtualY - trayWindow.height - 4;\r
+ }\r
+\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
target: systrayBackend\r
onShowWindow: {\r
accountMenu.close();\r
- trayWindow.show();\r
- trayWindow.raise();\r
- trayWindow.requestActivate();\r
- trayWindow.setX( Qt.application.screens[systrayBackend.screenIndex()].virtualX + systrayBackend.calcTrayWindowX());\r
- trayWindow.setY( Qt.application.screens[systrayBackend.screenIndex()].virtualY + systrayBackend.calcTrayWindowY());\r
+ setTrayWindowPosition();\r
systrayBackend.setOpened();\r
userModelBackend.fetchCurrentActivityModel();\r
}\r