Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ endif()
add_library(${PROJECT_NAME} STATIC ${TRAY_SOURCES})
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
if(TRAY_IS_TOP_LEVEL AND BUILD_TESTS)
target_compile_definitions(${PROJECT_NAME} PRIVATE TRAY_ENABLE_TEST_HOOKS)
endif()
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
Expand Down
31 changes: 24 additions & 7 deletions src/QtTrayMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@
* @brief Definitions for Qt tray menu implemenation
*/
// standard includes
#include <chrono>
#include <filesystem>
#include <thread>

// qt includes
#include <QApplication>
#include <QCursor>
#include <QDebug>
#include <QMouseEvent>
#include <QScreen>
#include <QStyle>

// conditional includes
#ifdef TRAY_ENABLE_TEST_HOOKS
// standard
#include <chrono>
#include <thread>

// qt
#include <QCursor>
#include <QScreen>
#endif

// local includes
#include "QtTrayMenu.h"

Expand All @@ -23,6 +30,7 @@
#endif

namespace {
#ifdef TRAY_ENABLE_TEST_HOOKS
constexpr int DEFAULT_PANEL_THICKNESS = 24;
constexpr int CURSOR_POSITION_POLL_INTERVAL_MS = 10;
constexpr int CURSOR_POSITION_TIMEOUT_MS = 500;
Expand Down Expand Up @@ -61,20 +69,27 @@ namespace {
if (topInset > 0) {
*position = QPoint(screenGeometry.right() - (topInset / 2), screenGeometry.top() + (topInset / 2));
} else if (bottomInset > 0) {
#if defined(__linux__)
// Plasma's default bottom panel places the system tray three quarters across the screen.
// Its far-right control is Peek at Desktop, rather than a tray icon.
*position = QPoint(screenGeometry.left() + ((screenGeometry.width() * 3) / 4), screenGeometry.bottom() - (bottomInset / 2));
#else
*position = QPoint(screenGeometry.right() - (bottomInset / 2), screenGeometry.bottom() - (bottomInset / 2));
#endif
} else if (rightInset > 0) {
*position = QPoint(screenGeometry.right() - (rightInset / 2), screenGeometry.bottom() - (rightInset / 2));
} else if (leftInset > 0) {
*position = QPoint(screenGeometry.left() + (leftInset / 2), screenGeometry.bottom() - (leftInset / 2));
} else {
#if defined(_WIN32)
#if defined(_WIN32)
*position = QPoint(screenGeometry.right() - (DEFAULT_PANEL_THICKNESS / 2), screenGeometry.bottom() - (DEFAULT_PANEL_THICKNESS / 2));
#else
#else
*position = QPoint(screenGeometry.right() - (DEFAULT_PANEL_THICKNESS / 2), screenGeometry.top() + (DEFAULT_PANEL_THICKNESS / 2));
#endif
#endif
}
return true;
}
#endif
} // namespace

QtTrayMenu::QtTrayMenu(QObject *parent, const bool debug):
Expand Down Expand Up @@ -401,6 +416,7 @@ void QtTrayMenu::clearMessageCallback() const {
notificationCallback = nullptr;
}

#ifdef TRAY_ENABLE_TEST_HOOKS
bool QtTrayMenu::positionMouseOverIcon() {
if (!trayIcon) {
return false;
Expand Down Expand Up @@ -442,3 +458,4 @@ bool QtTrayMenu::restoreMousePosition() {
}
return restored;
}
#endif
10 changes: 9 additions & 1 deletion src/QtTrayMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
// qt includes
#include <QMenu>
#include <QObject>
#include <QPoint>
#include <QString>
#include <QSystemTrayIcon>

// conditional includes
#ifdef TRAY_ENABLE_TEST_HOOKS
#include <QPoint>
#endif

// local includes
#include "tray.h"

Expand Down Expand Up @@ -111,6 +115,7 @@ class QtTrayMenu: public QObject {
*/
void clearMessageCallback() const;

#ifdef TRAY_ENABLE_TEST_HOOKS
/**
* @brief Move the mouse cursor to the center of the tray icon.
* @return true if the tray icon has valid screen geometry and the cursor was moved
Expand All @@ -122,6 +127,7 @@ class QtTrayMenu: public QObject {
* @return true if a saved position existed and the cursor was restored
*/
bool restoreMousePosition();
#endif

/**
* @brief Check if QtTrayMenu supports messages
Expand Down Expand Up @@ -163,8 +169,10 @@ class QtTrayMenu: public QObject {
bool blockingEventLoop = false;
struct tray_menu *getTrayMenuItem(const QAction *action);
mutable std::function<void()> notificationCallback = nullptr;
#ifdef TRAY_ENABLE_TEST_HOOKS
QPoint savedMousePosition;
bool mousePositionSaved = false;
#endif

private slots:
void onExitRequested();
Expand Down
2 changes: 2 additions & 0 deletions src/tray.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ extern "C" {
*/
void tray_show_menu(void);

#ifdef TRAY_ENABLE_TEST_HOOKS
/**
* @brief Position the mouse over the tray icon (for testing purposes).
* @return 0 on success, -1 if the tray icon geometry is unavailable.
Expand All @@ -81,6 +82,7 @@ extern "C" {
* @return 0 on success, -1 if no saved position exists or the cursor could not be restored.
*/
int tray_restore_mouse_position(void);
#endif

/**
* @brief Simulate a notification click, invoking the notification callback (for testing purposes).
Expand Down
2 changes: 2 additions & 0 deletions src/tray_qt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ extern "C" {
tray_qt::state().trayMenu->showMenu();
}

#ifdef TRAY_ENABLE_TEST_HOOKS
int tray_position_mouse_over_icon(void) {
if (tray_qt::state().trayMenu == nullptr) {
return -1;
Expand All @@ -234,6 +235,7 @@ extern "C" {
}
return tray_qt::state().trayMenu->restoreMousePosition() ? 0 : -1;
}
#endif

void tray_simulate_menu_item_click(int index) {
if (tray_qt::state().trayMenu == nullptr) {
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ target_link_libraries(${PROJECT_NAME}
gtest
gtest_main # if we use this we don't need our own main function
)
target_compile_definitions(${PROJECT_NAME} PRIVATE TRAY_ENABLE_TEST_HOOKS)
target_compile_definitions(${PROJECT_NAME} PUBLIC ${TEST_DEFINITIONS})
target_link_options(${PROJECT_NAME} PRIVATE)

Expand Down
13 changes: 9 additions & 4 deletions tests/unit/test_tray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,14 @@ class TrayTest: public BaseTest {
}
}

// Native tray tooltips are displayed asynchronously by the desktop shell.
void WaitForTooltipReady() const {
for (int i = 0; i < 40; ++i) {
tray_loop(0);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}

void WaitForNotificationReady() const {
WaitForTrayReady();
#if defined(_WIN32) || defined(__APPLE__)
Expand Down Expand Up @@ -478,10 +486,7 @@ TEST_F(TrayTest, TestTooltipDisplayOnHover) {
WaitForTrayReady();

ASSERT_EQ(tray_position_mouse_over_icon(), 0);
for (int i = 0; i < 20; ++i) {
tray_loop(0);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
WaitForTooltipReady();
EXPECT_TRUE(captureScreenshot("tray_tooltip_hover"));
EXPECT_EQ(tray_restore_mouse_position(), 0);
}
Expand Down