1010#include < vector>
1111#include " ../../image.h"
1212#include " ../../menu.h"
13+ #include " ../../menu_event.h"
1314
1415namespace nativeapi {
1516
@@ -21,6 +22,63 @@ static std::atomic<MenuId> g_next_menu_id{1};
2122static std::unordered_map<MenuItemId, MenuItem*> g_menu_item_registry;
2223static std::unordered_map<MenuId, Menu*> g_menu_registry;
2324
25+ // GTK signal handlers → Event emission
26+ static void OnGtkMenuItemActivate (GtkMenuItem* /* item*/ , gpointer user_data) {
27+ MenuItemId item_id = static_cast <MenuItemId>(GPOINTER_TO_SIZE (user_data));
28+ auto it = g_menu_item_registry.find (item_id);
29+ if (it == g_menu_item_registry.end ()) {
30+ return ;
31+ }
32+ MenuItem* menu_item = it->second ;
33+ std::string text = " " ;
34+ if (auto label = menu_item->GetLabel (); label.has_value ()) {
35+ text = *label;
36+ }
37+ menu_item->EmitSync (MenuItemClickedEvent (item_id, text));
38+ }
39+
40+ static void OnGtkMenuShow (GtkWidget* /* menu*/ , gpointer user_data) {
41+ MenuId menu_id = static_cast <MenuId>(GPOINTER_TO_SIZE (user_data));
42+ auto it = g_menu_registry.find (menu_id);
43+ if (it == g_menu_registry.end ()) {
44+ return ;
45+ }
46+ Menu* menu_obj = it->second ;
47+ menu_obj->EmitSync (MenuOpenedEvent (menu_id));
48+ }
49+
50+ static void OnGtkMenuHide (GtkWidget* /* menu*/ , gpointer user_data) {
51+ MenuId menu_id = static_cast <MenuId>(GPOINTER_TO_SIZE (user_data));
52+ auto it = g_menu_registry.find (menu_id);
53+ if (it == g_menu_registry.end ()) {
54+ return ;
55+ }
56+ Menu* menu_obj = it->second ;
57+ menu_obj->EmitSync (MenuClosedEvent (menu_id));
58+ }
59+
60+ static void OnGtkSubmenuShow (GtkWidget* /* submenu*/ , gpointer user_data) {
61+ MenuItemId item_id = static_cast <MenuItemId>(GPOINTER_TO_SIZE (user_data));
62+ auto it = g_menu_item_registry.find (item_id);
63+ if (it == g_menu_item_registry.end ()) {
64+ return ;
65+ }
66+ MenuItem* menu_item = it->second ;
67+ // Emit submenu opened on the item
68+ menu_item->EmitSync (MenuItemSubmenuOpenedEvent (item_id));
69+ }
70+
71+ static void OnGtkSubmenuHide (GtkWidget* /* submenu*/ , gpointer user_data) {
72+ MenuItemId item_id = static_cast <MenuItemId>(GPOINTER_TO_SIZE (user_data));
73+ auto it = g_menu_item_registry.find (item_id);
74+ if (it == g_menu_item_registry.end ()) {
75+ return ;
76+ }
77+ MenuItem* menu_item = it->second ;
78+ // Emit submenu closed on the item
79+ menu_item->EmitSync (MenuItemSubmenuClosedEvent (item_id));
80+ }
81+
2482// Private implementation class for MenuItem
2583class MenuItem ::Impl {
2684 public:
@@ -79,6 +137,13 @@ MenuItem::MenuItem(const std::string& text, MenuItemType type)
79137
80138 // Register the MenuItem for event emission
81139 g_menu_item_registry[id] = this ;
140+
141+ // Connect activation signal for click events (except separators)
142+ if (gtk_item && type != MenuItemType::Separator) {
143+ g_signal_connect (G_OBJECT (gtk_item), " activate" ,
144+ G_CALLBACK (OnGtkMenuItemActivate),
145+ GSIZE_TO_POINTER (static_cast <gsize>(id)));
146+ }
82147}
83148
84149MenuItem::MenuItem (void * menu_item)
@@ -94,6 +159,12 @@ MenuItem::MenuItem(void* menu_item)
94159 }
95160 }
96161 g_menu_item_registry[id] = this ;
162+
163+ if (pimpl_->gtk_menu_item_ ) {
164+ g_signal_connect (G_OBJECT (pimpl_->gtk_menu_item_ ), " activate" ,
165+ G_CALLBACK (OnGtkMenuItemActivate),
166+ GSIZE_TO_POINTER (static_cast <gsize>(id)));
167+ }
97168}
98169
99170MenuItem::~MenuItem () {
@@ -209,6 +280,17 @@ void MenuItem::SetSubmenu(std::shared_ptr<Menu> submenu) {
209280 if (pimpl_->gtk_menu_item_ && submenu) {
210281 gtk_menu_item_set_submenu (GTK_MENU_ITEM (pimpl_->gtk_menu_item_ ),
211282 (GtkWidget*)submenu->GetNativeObject ());
283+
284+ // Emit submenu open/close events on the parent item when submenu shows/hides
285+ GtkWidget* submenu_widget = (GtkWidget*)submenu->GetNativeObject ();
286+ if (submenu_widget) {
287+ g_signal_connect (G_OBJECT (submenu_widget), " show" ,
288+ G_CALLBACK (OnGtkSubmenuShow),
289+ GSIZE_TO_POINTER (static_cast <gsize>(id)));
290+ g_signal_connect (G_OBJECT (submenu_widget), " hide" ,
291+ G_CALLBACK (OnGtkSubmenuHide),
292+ GSIZE_TO_POINTER (static_cast <gsize>(id)));
293+ }
212294 }
213295}
214296
@@ -252,11 +334,30 @@ Menu::Menu()
252334 : id(g_next_menu_id++),
253335 pimpl_ (std::unique_ptr<Impl>(new Impl(gtk_menu_new()))) {
254336 g_menu_registry[id] = this ;
337+
338+ // Connect menu show/hide to emit open/close events
339+ if (pimpl_->gtk_menu_ ) {
340+ g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " show" ,
341+ G_CALLBACK (OnGtkMenuShow),
342+ GSIZE_TO_POINTER (static_cast <gsize>(id)));
343+ g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " hide" ,
344+ G_CALLBACK (OnGtkMenuHide),
345+ GSIZE_TO_POINTER (static_cast <gsize>(id)));
346+ }
255347}
256348
257349Menu::Menu (void * menu)
258350 : id(g_next_menu_id++), pimpl_(new Impl((GtkWidget*)menu)) {
259351 g_menu_registry[id] = this ;
352+
353+ if (pimpl_->gtk_menu_ ) {
354+ g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " show" ,
355+ G_CALLBACK (OnGtkMenuShow),
356+ GSIZE_TO_POINTER (static_cast <gsize>(id)));
357+ g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " hide" ,
358+ G_CALLBACK (OnGtkMenuHide),
359+ GSIZE_TO_POINTER (static_cast <gsize>(id)));
360+ }
260361}
261362
262363Menu::~Menu () {
@@ -392,7 +493,23 @@ std::shared_ptr<MenuItem> Menu::FindItemByText(const std::string& text,
392493bool Menu::Open (double x, double y) {
393494 if (pimpl_->gtk_menu_ ) {
394495 pimpl_->visible_ = true ;
395- gtk_menu_popup_at_pointer (GTK_MENU (pimpl_->gtk_menu_ ), nullptr );
496+ gtk_widget_show_all (pimpl_->gtk_menu_ );
497+
498+ // Try to position at explicit coordinates if available
499+ GdkWindow* root_window = gdk_get_default_root_window ();
500+ if (root_window) {
501+ GdkRectangle rect;
502+ rect.x = static_cast <int >(x);
503+ rect.y = static_cast <int >(y);
504+ rect.width = 1 ;
505+ rect.height = 1 ;
506+ gtk_menu_popup_at_rect (GTK_MENU (pimpl_->gtk_menu_ ), root_window, &rect,
507+ GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST,
508+ nullptr );
509+ } else {
510+ // Fallback to pointer if root window not available
511+ gtk_menu_popup_at_pointer (GTK_MENU (pimpl_->gtk_menu_ ), nullptr );
512+ }
396513 return true ;
397514 }
398515 return false ;
0 commit comments