Skip to content

Commit 7b72873

Browse files
committed
Emit menu opened/closed events on Windows
Added handling for WM_INITMENUPOPUP and WM_UNINITMENUPOPUP messages to emit MenuOpenedEvent and MenuClosedEvent when menus are shown or hidden. Updated menu visibility tracking and improved menu lifecycle management for Windows platform.
1 parent 0cad78b commit 7b72873

File tree

1 file changed

+48
-13
lines changed

1 file changed

+48
-13
lines changed

src/platform/windows/menu_windows.cpp

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,44 @@ class Menu::Impl {
328328
}
329329
}
330330

331-
// Handle window procedure delegate for menu commands
331+
// Handle window procedure delegate for menu commands and menu lifecycle events
332332
std::optional<LRESULT> HandleWindowProc(HWND hwnd,
333333
UINT message,
334334
WPARAM wparam,
335-
LPARAM lparam) {
336-
if (message == WM_COMMAND) {
335+
LPARAM lparam,
336+
Menu* menu) {
337+
// Handle menu lifecycle events
338+
if (message == WM_INITMENUPOPUP) {
339+
// wParam contains the HMENU handle of the popup menu being opened
340+
HMENU popup_menu = reinterpret_cast<HMENU>(wparam);
341+
342+
if (popup_menu == hmenu_) {
343+
// This is our menu being opened
344+
visible_ = true;
345+
346+
// Emit menu opened event
347+
try {
348+
menu->Emit<MenuOpenedEvent>(id_);
349+
} catch (...) {
350+
// Protect against event emission exceptions
351+
}
352+
}
353+
} else if (message == WM_UNINITMENUPOPUP) {
354+
// wParam contains the HMENU handle of the popup menu being closed
355+
HMENU popup_menu = reinterpret_cast<HMENU>(wparam);
356+
357+
if (popup_menu == hmenu_) {
358+
// This is our menu being closed
359+
visible_ = false;
360+
361+
// Emit menu closed event
362+
try {
363+
menu->Emit<MenuClosedEvent>(id_);
364+
} catch (...) {
365+
// Protect against event emission exceptions
366+
}
367+
}
368+
} else if (message == WM_COMMAND) {
337369
// For WM_COMMAND from menus, wparam contains the menu item ID
338370
// When using popup menus (TrackPopupMenu), the full 32-bit ID is
339371
// preserved in wparam, unlike menu bars which only use 16-bit IDs
@@ -375,29 +407,29 @@ class Menu::Impl {
375407
Menu::Menu(void* native_menu)
376408
: pimpl_(std::make_unique<Impl>(IdAllocator::Allocate<Menu>(),
377409
static_cast<HMENU>(native_menu))) {
378-
// Register window procedure handler for menu commands
410+
// Register window procedure handler for menu commands and events
379411
HWND host_window = WindowMessageDispatcher::GetInstance().GetHostWindow();
380412
if (host_window) {
381413
pimpl_->window_proc_handle_id_ =
382414
WindowMessageDispatcher::GetInstance().RegisterHandler(
383415
host_window,
384416
[this](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
385-
return pimpl_->HandleWindowProc(hwnd, message, wparam, lparam);
417+
return pimpl_->HandleWindowProc(hwnd, message, wparam, lparam, this);
386418
});
387419
}
388420
}
389421

390422
Menu::Menu()
391423
: pimpl_(std::make_unique<Impl>(IdAllocator::Allocate<Menu>(),
392424
CreatePopupMenu())) {
393-
// Register window procedure handler for menu commands
425+
// Register window procedure handler for menu commands and events
394426
HWND host_window = WindowMessageDispatcher::GetInstance().GetHostWindow();
395427
if (host_window) {
396428
pimpl_->window_proc_handle_id_ =
397429
WindowMessageDispatcher::GetInstance().RegisterHandler(
398430
host_window,
399431
[this](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
400-
return pimpl_->HandleWindowProc(hwnd, message, wparam, lparam);
432+
return pimpl_->HandleWindowProc(hwnd, message, wparam, lparam, this);
401433
});
402434
}
403435
}
@@ -539,25 +571,24 @@ std::vector<std::shared_ptr<MenuItem>> Menu::GetAllItems() const {
539571
}
540572

541573
bool Menu::Open(double x, double y) {
542-
pimpl_->visible_ = true;
543-
544574
POINT pt = {static_cast<int>(x), static_cast<int>(y)};
545575

546576
// Get the host window for menus
547577
HWND host_window = WindowMessageDispatcher::GetInstance().GetHostWindow();
548578
if (!host_window) {
549-
pimpl_->visible_ = false;
550579
return false;
551580
}
552581

553582
// Set the host window as foreground to ensure menu can be displayed
554583
SetForegroundWindow(host_window);
555584

556585
// Show the context menu using the host window
586+
// Note: TrackPopupMenu is a blocking call
587+
// - WM_INITMENUPOPUP is sent when the menu opens (triggers MenuOpenedEvent)
588+
// - WM_UNINITMENUPOPUP is sent when the menu closes (triggers MenuClosedEvent)
557589
TrackPopupMenu(pimpl_->hmenu_, TPM_BOTTOMALIGN | TPM_LEFTALIGN, pt.x, pt.y, 0,
558590
host_window, nullptr);
559591

560-
pimpl_->visible_ = false;
561592
return true;
562593
}
563594

@@ -569,8 +600,12 @@ bool Menu::Open() {
569600

570601
bool Menu::Close() {
571602
if (pimpl_->visible_) {
572-
// Windows automatically closes popup menus when user clicks elsewhere
573-
pimpl_->visible_ = false;
603+
// Send WM_CANCELMODE to close any open menus
604+
HWND host_window = WindowMessageDispatcher::GetInstance().GetHostWindow();
605+
if (host_window) {
606+
// This will close the menu and trigger WM_UNINITMENUPOPUP
607+
SendMessage(host_window, WM_CANCELMODE, 0, 0);
608+
}
574609
return true;
575610
}
576611
return false;

0 commit comments

Comments
 (0)