Skip to content

Commit 10c3065

Browse files
committed
Refactor tray icon example and improve Linux menu events
Refactored the tray icon example to use the Application event loop and removed platform-specific code for macOS. Updated Linux menu event handling to use GTK 'map' and 'unmap' signals instead of 'show' and 'hide', ensuring menu open/close events are emitted only when menus are actually visible. Improved context menu handling for tray icons on Linux.
1 parent d468825 commit 10c3065

File tree

3 files changed

+34
-52
lines changed

3 files changed

+34
-52
lines changed

examples/tray_icon_example/main.cpp

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@
33
#include <memory>
44
#include <thread>
55

6+
#include "../../src/application.h"
7+
#include "../../src/application_event.h"
68
#include "../../src/image.h"
79
#include "../../src/menu.h"
810
#include "../../src/tray_icon.h"
911
#include "../../src/tray_icon_event.h"
1012
#include "../../src/tray_manager.h"
1113

12-
#ifdef __APPLE__
13-
#import <Cocoa/Cocoa.h>
14-
#endif
15-
1614
using namespace nativeapi;
1715
using nativeapi::Menu;
1816
using nativeapi::MenuItem;
@@ -22,11 +20,8 @@ using nativeapi::MenuItemType;
2220
int main() {
2321
std::cout << "Starting TrayIcon Example..." << std::endl;
2422

25-
#ifdef __APPLE__
26-
// Initialize Cocoa application for macOS
27-
[NSApplication sharedApplication];
28-
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
29-
#endif
23+
// Get the Application instance - this handles platform initialization
24+
Application& app = Application::GetInstance();
3025

3126
// Check if tray icons are supported
3227
TrayManager& trayManager = TrayManager::GetInstance();
@@ -53,11 +48,11 @@ int main() {
5348
std::cout << "Tray icon ID: " << event.GetTrayIconId() << std::endl;
5449
});
5550

56-
trayIcon->AddListener<TrayIconRightClickedEvent>([](const TrayIconRightClickedEvent& event) {
51+
trayIcon->AddListener<TrayIconRightClickedEvent>([trayIcon](const TrayIconRightClickedEvent& event) {
5752
std::cout << "*** TRAY ICON RIGHT CLICKED! ***" << std::endl;
5853
std::cout << "This is the right click handler working!" << std::endl;
5954
std::cout << "Tray icon ID: " << event.GetTrayIconId() << std::endl;
60-
// Context menu will be shown automatically
55+
trayIcon->OpenContextMenu();
6156
});
6257

6358
trayIcon->AddListener<TrayIconDoubleClickedEvent>([](const TrayIconDoubleClickedEvent& event) {
@@ -100,10 +95,9 @@ int main() {
10095

10196
// Add exit item
10297
auto exit_item = std::make_shared<MenuItem>("Exit", MenuItemType::Normal);
103-
bool* should_exit = new bool(false);
104-
exit_item->AddListener<MenuItemClickedEvent>([should_exit](const MenuItemClickedEvent& event) {
98+
exit_item->AddListener<MenuItemClickedEvent>([&app](const MenuItemClickedEvent& event) {
10599
std::cout << "Exit clicked from context menu" << std::endl;
106-
*should_exit = true;
100+
app.Quit(0);
107101
});
108102
context_menu->AddItem(exit_item);
109103

@@ -130,36 +124,21 @@ int main() {
130124
std::cout << "- Right click: Opens context menu" << std::endl;
131125
std::cout << "- Double click: Quick double click" << std::endl;
132126
std::cout << "- Context menu: Right-click to see options including Exit" << std::endl;
133-
std::cout << "The application will run for 60 seconds, or until you click Exit." << std::endl;
127+
std::cout << "Use the Exit menu item to quit the application." << std::endl;
134128
std::cout << "========================================" << std::endl;
135129

136-
// Keep the application running for 60 seconds or until exit is clicked
137-
int countdown = 60;
138-
while (countdown > 0 && !*should_exit) {
139-
std::this_thread::sleep_for(std::chrono::seconds(1));
140-
countdown--;
141-
142-
// Check if tray icon is still visible
143-
if (!trayIcon->IsVisible()) {
144-
std::cout << "Tray icon is no longer visible!" << std::endl;
145-
break;
146-
}
147-
148-
// Print countdown every 10 seconds
149-
if (countdown % 10 == 0) {
150-
std::cout << "Application will exit in " << countdown << " seconds..." << std::endl;
130+
// Set up application event listeners
131+
app.AddListener<ApplicationExitingEvent>([&trayIcon](const ApplicationExitingEvent& event) {
132+
std::cout << "Application is exiting with code: " << event.GetExitCode() << std::endl;
133+
// Hide the tray icon before exiting
134+
if (trayIcon) {
135+
trayIcon->SetVisible(false);
151136
}
152-
}
137+
});
153138

154-
if (*should_exit) {
155-
std::cout << "Exit requested from context menu." << std::endl;
156-
}
139+
// Run the application event loop - this will block until app.Quit() is called
140+
int exit_code = app.Run();
157141

158-
// Hide the tray icon before exiting
159-
trayIcon->SetVisible(false);
160142
std::cout << "Exiting TrayIcon Example..." << std::endl;
161-
162-
// Cleanup
163-
delete should_exit;
164-
return 0;
143+
return exit_code;
165144
}

src/platform/linux/menu_linux.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,23 @@ static void OnGtkCheckMenuItemToggled(GtkCheckMenuItem* item, gpointer user_data
4343
}
4444
}
4545

46-
static void OnGtkMenuShow(GtkWidget* /*menu*/, gpointer user_data) {
46+
static void OnGtkMenuMap(GtkWidget* /*menu*/, gpointer user_data) {
4747
Menu* menu_obj = static_cast<Menu*>(user_data);
4848
if (!menu_obj) {
4949
return;
5050
}
5151
menu_obj->Emit(MenuOpenedEvent(menu_obj->GetId()));
5252
}
5353

54-
static void OnGtkMenuHide(GtkWidget* /*menu*/, gpointer user_data) {
54+
static void OnGtkMenuUnmap(GtkWidget* /*menu*/, gpointer user_data) {
5555
Menu* menu_obj = static_cast<Menu*>(user_data);
5656
if (!menu_obj) {
5757
return;
5858
}
5959
menu_obj->Emit(MenuClosedEvent(menu_obj->GetId()));
6060
}
6161

62-
static void OnGtkSubmenuShow(GtkWidget* /*submenu*/, gpointer user_data) {
62+
static void OnGtkSubmenuMap(GtkWidget* /*submenu*/, gpointer user_data) {
6363
MenuItem* menu_item = static_cast<MenuItem*>(user_data);
6464
if (!menu_item) {
6565
return;
@@ -68,7 +68,7 @@ static void OnGtkSubmenuShow(GtkWidget* /*submenu*/, gpointer user_data) {
6868
menu_item->Emit(MenuItemSubmenuOpenedEvent(menu_item->GetId()));
6969
}
7070

71-
static void OnGtkSubmenuHide(GtkWidget* /*submenu*/, gpointer user_data) {
71+
static void OnGtkSubmenuUnmap(GtkWidget* /*submenu*/, gpointer user_data) {
7272
MenuItem* menu_item = static_cast<MenuItem*>(user_data);
7373
if (!menu_item) {
7474
return;
@@ -404,11 +404,11 @@ void MenuItem::SetSubmenu(std::shared_ptr<Menu> submenu) {
404404
(GtkWidget*)submenu->GetNativeObject());
405405

406406
// Emit submenu open/close events on the parent item when submenu
407-
// shows/hides
407+
// maps/unmaps (actual visibility on screen)
408408
GtkWidget* submenu_widget = (GtkWidget*)submenu->GetNativeObject();
409409
if (submenu_widget) {
410-
g_signal_connect(G_OBJECT(submenu_widget), "show", G_CALLBACK(OnGtkSubmenuShow), this);
411-
g_signal_connect(G_OBJECT(submenu_widget), "hide", G_CALLBACK(OnGtkSubmenuHide), this);
410+
g_signal_connect(G_OBJECT(submenu_widget), "map", G_CALLBACK(OnGtkSubmenuMap), this);
411+
g_signal_connect(G_OBJECT(submenu_widget), "unmap", G_CALLBACK(OnGtkSubmenuUnmap), this);
412412
}
413413
}
414414
}
@@ -434,19 +434,19 @@ class Menu::Impl {
434434
Menu::Menu() {
435435
MenuId id = IdAllocator::Allocate<Menu>();
436436
pimpl_ = std::unique_ptr<Impl>(new Impl(id, gtk_menu_new()));
437-
// Connect menu show/hide to emit open/close events
437+
// Connect menu map/unmap to emit open/close events when actually visible
438438
if (pimpl_->gtk_menu_) {
439-
g_signal_connect(G_OBJECT(pimpl_->gtk_menu_), "show", G_CALLBACK(OnGtkMenuShow), this);
440-
g_signal_connect(G_OBJECT(pimpl_->gtk_menu_), "hide", G_CALLBACK(OnGtkMenuHide), this);
439+
g_signal_connect(G_OBJECT(pimpl_->gtk_menu_), "map", G_CALLBACK(OnGtkMenuMap), this);
440+
g_signal_connect(G_OBJECT(pimpl_->gtk_menu_), "unmap", G_CALLBACK(OnGtkMenuUnmap), this);
441441
}
442442
}
443443

444444
Menu::Menu(void* menu) {
445445
MenuId id = IdAllocator::Allocate<Menu>();
446446
pimpl_ = std::unique_ptr<Impl>(new Impl(id, (GtkWidget*)menu));
447447
if (pimpl_->gtk_menu_) {
448-
g_signal_connect(G_OBJECT(pimpl_->gtk_menu_), "show", G_CALLBACK(OnGtkMenuShow), this);
449-
g_signal_connect(G_OBJECT(pimpl_->gtk_menu_), "hide", G_CALLBACK(OnGtkMenuHide), this);
448+
g_signal_connect(G_OBJECT(pimpl_->gtk_menu_), "map", G_CALLBACK(OnGtkMenuMap), this);
449+
g_signal_connect(G_OBJECT(pimpl_->gtk_menu_), "unmap", G_CALLBACK(OnGtkMenuUnmap), this);
450450
}
451451
}
452452

src/platform/linux/tray_icon_linux.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ void TrayIcon::SetContextMenu(std::shared_ptr<Menu> menu) {
183183
if (pimpl_->app_indicator_ && menu && menu->GetNativeObject()) {
184184
GtkMenu* gtk_menu = static_cast<GtkMenu*>(menu->GetNativeObject());
185185
app_indicator_set_menu(pimpl_->app_indicator_, gtk_menu);
186+
// Ensure the menu and its children are realized; actual popup is controlled
187+
// by the indicator. Using map/unmap signals in menu_linux.cpp prevents
188+
// premature opened/closed emissions.
186189
gtk_widget_show_all(GTK_WIDGET(gtk_menu));
187190
}
188191
}

0 commit comments

Comments
 (0)