@@ -103,6 +103,8 @@ class MenuItem::Impl {
103103 std::shared_ptr<Menu> submenu_;
104104 int window_proc_handle_id_;
105105 std::function<void (MenuItemId)> clicked_callback_;
106+ size_t submenu_opened_listener_id_;
107+ size_t submenu_closed_listener_id_;
106108
107109 Impl (MenuItemId id, HMENU parent_menu, MenuItemType type)
108110 : id_(id),
@@ -112,13 +114,26 @@ class MenuItem::Impl {
112114 has_accelerator_(false ),
113115 state_(MenuItemState::Unchecked),
114116 radio_group_(-1 ),
115- window_proc_handle_id_(-1 ) {}
117+ window_proc_handle_id_(-1 ),
118+ submenu_opened_listener_id_(0 ),
119+ submenu_closed_listener_id_(0 ) {}
116120
117121 ~Impl () {
118122 // Unregister window procedure handler
119123 if (window_proc_handle_id_ != -1 ) {
120124 WindowMessageDispatcher::GetInstance ().UnregisterHandler (window_proc_handle_id_);
121125 }
126+
127+ // Remove submenu listeners before cleaning up submenu reference
128+ if (submenu_ && submenu_opened_listener_id_ != 0 ) {
129+ submenu_->RemoveListener (submenu_opened_listener_id_);
130+ submenu_opened_listener_id_ = 0 ;
131+ }
132+ if (submenu_ && submenu_closed_listener_id_ != 0 ) {
133+ submenu_->RemoveListener (submenu_closed_listener_id_);
134+ submenu_closed_listener_id_ = 0 ;
135+ }
136+
122137 // Windows menu items are automatically cleaned up when the menu is
123138 // destroyed
124139 }
@@ -307,21 +322,58 @@ int MenuItem::GetRadioGroup() const {
307322}
308323
309324void MenuItem::SetSubmenu (std::shared_ptr<Menu> submenu) {
325+ // Remove previous submenu listeners if they exist
326+ if (pimpl_->submenu_ && pimpl_->submenu_opened_listener_id_ != 0 ) {
327+ pimpl_->submenu_ ->RemoveListener (pimpl_->submenu_opened_listener_id_ );
328+ pimpl_->submenu_opened_listener_id_ = 0 ;
329+ }
330+ if (pimpl_->submenu_ && pimpl_->submenu_closed_listener_id_ != 0 ) {
331+ pimpl_->submenu_ ->RemoveListener (pimpl_->submenu_closed_listener_id_ );
332+ pimpl_->submenu_closed_listener_id_ = 0 ;
333+ }
334+
310335 pimpl_->submenu_ = submenu;
336+
337+ // Update platform menu if parent_menu_ is set
311338 if (pimpl_->parent_menu_ && submenu) {
312339 MENUITEMINFOW mii = {};
313340 mii.cbSize = sizeof (MENUITEMINFOW);
314341 mii.fMask = MIIM_SUBMENU;
315342 mii.hSubMenu = static_cast <HMENU>(submenu->GetNativeObject ());
316343 SetMenuItemInfoW (pimpl_->parent_menu_ , pimpl_->id_ , FALSE , &mii);
317344 }
345+
346+ // Add event listeners to forward submenu events (independent of parent_menu_)
347+ if (submenu) {
348+ MenuItemId menu_item_id = pimpl_->id_ ;
349+ MenuItem* self = this ;
350+ pimpl_->submenu_opened_listener_id_ = submenu->AddListener <MenuOpenedEvent>(
351+ [self, menu_item_id](const MenuOpenedEvent& event) {
352+ self->Emit <MenuItemSubmenuOpenedEvent>(menu_item_id);
353+ });
354+
355+ pimpl_->submenu_closed_listener_id_ = submenu->AddListener <MenuClosedEvent>(
356+ [self, menu_item_id](const MenuClosedEvent& event) {
357+ self->Emit <MenuItemSubmenuClosedEvent>(menu_item_id);
358+ });
359+ }
318360}
319361
320362std::shared_ptr<Menu> MenuItem::GetSubmenu () const {
321363 return pimpl_->submenu_ ;
322364}
323365
324366void MenuItem::RemoveSubmenu () {
367+ // Remove listeners before clearing submenu reference
368+ if (pimpl_->submenu_ && pimpl_->submenu_opened_listener_id_ != 0 ) {
369+ pimpl_->submenu_ ->RemoveListener (pimpl_->submenu_opened_listener_id_ );
370+ pimpl_->submenu_opened_listener_id_ = 0 ;
371+ }
372+ if (pimpl_->submenu_ && pimpl_->submenu_closed_listener_id_ != 0 ) {
373+ pimpl_->submenu_ ->RemoveListener (pimpl_->submenu_closed_listener_id_ );
374+ pimpl_->submenu_closed_listener_id_ = 0 ;
375+ }
376+
325377 pimpl_->submenu_ .reset ();
326378 if (pimpl_->parent_menu_ ) {
327379 MENUITEMINFOW mii = {};
@@ -375,10 +427,17 @@ class Menu::Impl {
375427 // This is our menu being opened
376428 // Emit menu opened event via callback
377429 if (opened_callback_) {
378- try {
379- opened_callback_ (id_);
380- } catch (...) {
381- // Protect against event emission exceptions
430+ opened_callback_ (id_);
431+ }
432+ } else {
433+ // Check if this is a submenu of one of our items
434+ for (const auto & item : items_) {
435+ auto submenu = item->GetSubmenu ();
436+ if (submenu && submenu->GetNativeObject () == popup_menu) {
437+ // This is a submenu of one of our items being opened
438+ // The MenuItem will handle emitting MenuItemSubmenuOpenedEvent
439+ // through its event listeners
440+ break ;
382441 }
383442 }
384443 }
@@ -390,10 +449,17 @@ class Menu::Impl {
390449 // This is our menu being closed
391450 // Emit menu closed event via callback
392451 if (closed_callback_) {
393- try {
394- closed_callback_ (id_);
395- } catch (...) {
396- // Protect against event emission exceptions
452+ closed_callback_ (id_);
453+ }
454+ } else {
455+ // Check if this is a submenu of one of our items
456+ for (const auto & item : items_) {
457+ auto submenu = item->GetSubmenu ();
458+ if (submenu && submenu->GetNativeObject () == popup_menu) {
459+ // This is a submenu of one of our items being closed
460+ // The MenuItem will handle emitting MenuItemSubmenuClosedEvent
461+ // through its event listeners
462+ break ;
397463 }
398464 }
399465 }
0 commit comments