зеркало из https://github.com/nextcloud/desktop.git
Major multi monitor improvements and rewrite of tray window positioning
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
This commit is contained in:
Родитель
121e23e322
Коммит
f44df7f61a
|
@ -0,0 +1 @@
|
|||
Subproject commit b59d091b3e6b98e7219cf636f7d21fb267242c27
|
|
@ -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<void(HKEY, const QString &)> &callback);
|
||||
OCSYNC_EXPORT QRect getTaskbarDimensions();
|
||||
#endif
|
||||
}
|
||||
/** @} */ // \addtogroup
|
||||
|
|
|
@ -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<LPCWSTR>(valueName.utf16()), 0, &type, reinterpret_cast<LPBYTE>(buffer.data()), &sizeInBytes);
|
||||
if (result == ERROR_SUCCESS) {
|
||||
value = buffer.at(12);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "systray.h"
|
||||
#include "theme.h"
|
||||
#include "config.h"
|
||||
#include "common/utility.h"
|
||||
#include "tray/UserModel.h"
|
||||
|
||||
#include <QCursor>
|
||||
|
@ -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,109 +125,6 @@ void Systray::setToolTip(const QString &tip)
|
|||
QSystemTrayIcon::setToolTip(tr("%1: %2").arg(Theme::instance()->appNameGUI(), tip));
|
||||
}
|
||||
|
||||
int Systray::calcTrayWindowX()
|
||||
{
|
||||
#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();
|
||||
}
|
||||
|
||||
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();
|
||||
} else {
|
||||
topRightDpiAware = this->geometry().topRight() / trayScreen->devicePixelRatio();
|
||||
topLeftDpiAware = this->geometry().topLeft() / trayScreen->devicePixelRatio();
|
||||
}
|
||||
|
||||
// get x coordinate from top center point of tray icon
|
||||
int trayIconTopCenterX = (topRightDpiAware - ((topRightDpiAware - topLeftDpiAware) * 0.5)).x();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Systray::syncIsPaused()
|
||||
{
|
||||
return _syncIsPaused;
|
||||
|
@ -252,4 +141,71 @@ void Systray::pauseResumeSync()
|
|||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
/* Helper functions for cross-platform tray icon position and taskbar orientation detection */
|
||||
/********************************************************************************************/
|
||||
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
QRect Systray::taskbarRect()
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
return Utility::getTaskbarDimensions();
|
||||
#else
|
||||
return QRect(0, 0, 0, 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
QPoint Systray::calcTrayIconCenter()
|
||||
{
|
||||
// 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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче