Skip to content

Commit 23c129f

Browse files
Add tray icon position fallback and tolerant checks
Improve mouse positioning for tray interactions when tray icon geometry is unavailable by deriving a fallback target from screen/panel edges, with platform-specific defaults. Also make cursor position/restore validation tolerant to small movement offsets instead of exact pixel matches. Unit tests were updated to avoid hard-failing on CI cursor positioning and only require restore success when positioning succeeded.
1 parent db03817 commit 23c129f

2 files changed

Lines changed: 58 additions & 7 deletions

File tree

src/QtTrayMenu.cpp

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <QCursor>
1111
#include <QDebug>
1212
#include <QMouseEvent>
13+
#include <QScreen>
1314
#include <QStyle>
1415

1516
// local includes
@@ -19,6 +20,46 @@
1920
#include "WindowsAppearance.h"
2021
#endif
2122

23+
namespace {
24+
constexpr int DEFAULT_PANEL_THICKNESS = 24;
25+
constexpr int CURSOR_POSITION_TOLERANCE = 2;
26+
27+
bool positionsAreClose(const QPoint &first, const QPoint &second) {
28+
return (first - second).manhattanLength() <= CURSOR_POSITION_TOLERANCE;
29+
}
30+
31+
bool fallbackTrayIconPosition(QPoint *position) {
32+
const QScreen *screen = QGuiApplication::primaryScreen();
33+
if (screen == nullptr) {
34+
return false;
35+
}
36+
37+
const QRect screenGeometry = screen->geometry();
38+
const QRect availableGeometry = screen->availableGeometry();
39+
const int topInset = availableGeometry.top() - screenGeometry.top();
40+
const int bottomInset = screenGeometry.bottom() - availableGeometry.bottom();
41+
const int rightInset = screenGeometry.right() - availableGeometry.right();
42+
const int leftInset = availableGeometry.left() - screenGeometry.left();
43+
44+
if (topInset > 0) {
45+
*position = QPoint(screenGeometry.right() - (topInset / 2), screenGeometry.top() + (topInset / 2));
46+
} else if (bottomInset > 0) {
47+
*position = QPoint(screenGeometry.right() - (bottomInset / 2), screenGeometry.bottom() - (bottomInset / 2));
48+
} else if (rightInset > 0) {
49+
*position = QPoint(screenGeometry.right() - (rightInset / 2), screenGeometry.bottom() - (rightInset / 2));
50+
} else if (leftInset > 0) {
51+
*position = QPoint(screenGeometry.left() + (leftInset / 2), screenGeometry.bottom() - (leftInset / 2));
52+
} else {
53+
#if defined(_WIN32)
54+
*position = QPoint(screenGeometry.right() - (DEFAULT_PANEL_THICKNESS / 2), screenGeometry.bottom() - (DEFAULT_PANEL_THICKNESS / 2));
55+
#else
56+
*position = QPoint(screenGeometry.right() - (DEFAULT_PANEL_THICKNESS / 2), screenGeometry.top() + (DEFAULT_PANEL_THICKNESS / 2));
57+
#endif
58+
}
59+
return true;
60+
}
61+
} // namespace
62+
2263
QtTrayMenu::QtTrayMenu(QObject *parent, const bool debug):
2364
QtTrayMenu(-1, nullptr, parent, debug) {
2465
};
@@ -349,18 +390,23 @@ bool QtTrayMenu::positionMouseOverIcon() {
349390
}
350391

351392
const QRect iconGeometry = trayIcon->geometry();
352-
if (!iconGeometry.isValid()) {
353-
qWarning("QtTrayMenu: tray icon geometry is unavailable");
393+
QPoint targetPosition;
394+
if (iconGeometry.isValid()) {
395+
targetPosition = iconGeometry.center();
396+
} else if (!fallbackTrayIconPosition(&targetPosition)) {
397+
qWarning("QtTrayMenu: tray icon geometry and screen-edge fallback are unavailable");
354398
return false;
399+
} else {
400+
qWarning("QtTrayMenu: tray icon geometry is unavailable; using the system panel edge");
355401
}
356402

357403
if (!mousePositionSaved) {
358404
savedMousePosition = QCursor::pos();
359405
mousePositionSaved = true;
360406
}
361-
const QPoint targetPosition = iconGeometry.center();
362407
QCursor::setPos(targetPosition);
363-
const bool positioned = QCursor::pos() == targetPosition;
408+
const QPoint currentPosition = QCursor::pos();
409+
const bool positioned = iconGeometry.isValid() ? iconGeometry.contains(currentPosition) : positionsAreClose(currentPosition, targetPosition);
364410
if (!positioned) {
365411
qWarning("QtTrayMenu: could not position the mouse over the tray icon");
366412
}
@@ -374,7 +420,7 @@ bool QtTrayMenu::restoreMousePosition() {
374420

375421
QCursor::setPos(savedMousePosition);
376422
mousePositionSaved = false;
377-
const bool restored = QCursor::pos() == savedMousePosition;
423+
const bool restored = positionsAreClose(QCursor::pos(), savedMousePosition);
378424
if (!restored) {
379425
qWarning("QtTrayMenu: could not restore the saved mouse position");
380426
}

tests/unit/test_tray.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,11 @@ class TrayTest: public BaseTest {
127127
// Capture a screenshot while the tray menu is open, then dismiss and exit.
128128
void captureMenuStateAndExit(const char *screenshotName) const {
129129
const bool positionMouse = lizardbyte::common::is_github_actions();
130+
int positionMouseResult = -1;
130131
if (positionMouse) {
131132
WaitForTrayReady();
132-
ASSERT_EQ(tray_position_mouse_over_icon(), 0);
133+
positionMouseResult = tray_position_mouse_over_icon();
134+
EXPECT_EQ(positionMouseResult, 0);
133135
}
134136

135137
std::atomic_bool exitRequested {false};
@@ -148,7 +150,10 @@ class TrayTest: public BaseTest {
148150
}
149151
capture_thread.join();
150152
if (positionMouse) {
151-
EXPECT_EQ(tray_restore_mouse_position(), 0);
153+
const int restoreMouseResult = tray_restore_mouse_position();
154+
if (positionMouseResult == 0) {
155+
EXPECT_EQ(restoreMouseResult, 0);
156+
}
152157
}
153158
}
154159

0 commit comments

Comments
 (0)