From 4701bb836e9d5f5c020c7807616d68bd049679a9 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 29 Aug 2023 20:06:58 +0300 Subject: [PATCH] fix(windows): attach subclass for context menu if window doesn't have a menu bar (#105) --- .changes/windows-context-menu.md | 5 ++ src/items/submenu.rs | 4 +- src/menu.rs | 2 +- src/platform_impl/windows/mod.rs | 92 ++++++++++++++++++++++++++------ 4 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 .changes/windows-context-menu.md diff --git a/.changes/windows-context-menu.md b/.changes/windows-context-menu.md new file mode 100644 index 0000000..036731a --- /dev/null +++ b/.changes/windows-context-menu.md @@ -0,0 +1,5 @@ +--- +"muda": "patch" +--- + +On Windows, fix menu items inside a context menu not firing events if the context menu was used on a Window that doesn't have a menu bar. diff --git a/src/items/submenu.rs b/src/items/submenu.rs index 9005671..c8b4555 100644 --- a/src/items/submenu.rs +++ b/src/items/submenu.rs @@ -207,12 +207,12 @@ impl ContextMenu for Submenu { #[cfg(target_os = "windows")] fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) { - self.inner.borrow_mut().attach_menu_subclass_for_hwnd(hwnd) + self.inner.borrow().attach_menu_subclass_for_hwnd(hwnd) } #[cfg(target_os = "windows")] fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) { - self.inner.borrow_mut().detach_menu_subclass_from_hwnd(hwnd) + self.inner.borrow().detach_menu_subclass_from_hwnd(hwnd) } #[cfg(target_os = "linux")] diff --git a/src/menu.rs b/src/menu.rs index 9458ab5..1067919 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -324,7 +324,7 @@ impl ContextMenu for Menu { #[cfg(target_os = "windows")] fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option) { self.inner - .borrow() + .borrow_mut() .show_context_menu_for_hwnd(hwnd, position) } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 43ba328..2add8ba 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -34,11 +34,11 @@ use windows_sys::Win32::{ AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu, DestroyAcceleratorTable, DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos, GetMenu, GetMenuItemInfoW, InsertMenuW, PostQuitMessage, RemoveMenu, SendMessageW, - SetMenu, SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, - MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED, - MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, MF_UNCHECKED, MIIM_BITMAP, - MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, TPM_LEFTALIGN, WM_CLOSE, - WM_COMMAND, + SetForegroundWindow, SetMenu, SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, + HMENU, MENUITEMINFOW, MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, + MF_CHECKED, MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, + MF_UNCHECKED, MIIM_BITMAP, MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, + TPM_LEFTALIGN, WM_CLOSE, WM_COMMAND, }, }, }; @@ -100,6 +100,7 @@ pub(crate) struct Menu { hmenu: HMENU, hpopupmenu: HMENU, hwnds: Vec, + context_hwnds: Vec, haccel_store: Rc>, children: Vec>>, } @@ -136,6 +137,14 @@ impl Drop for Menu { } unsafe { + for hwnd in &self.hwnds { + SetMenu(*hwnd, 0); + RemoveWindowSubclass(*hwnd, Some(menu_subclass_proc), MENU_SUBCLASS_ID); + } + for hwnd in &self.context_hwnds { + SetMenu(*hwnd, 0); + RemoveWindowSubclass(*hwnd, Some(menu_subclass_proc), MENU_SUBCLASS_ID); + } DestroyMenu(self.hmenu); DestroyMenu(self.hpopupmenu); } @@ -153,6 +162,7 @@ impl Menu { haccel_store: Rc::new(RefCell::new((0, HashMap::new()))), children: Vec::new(), hwnds: Vec::new(), + context_hwnds: Vec::new(), } } @@ -320,12 +330,14 @@ impl Menu { self.hwnds.push(hwnd); unsafe { SetMenu(hwnd, self.hmenu); - SetWindowSubclass( - hwnd, - Some(menu_subclass_proc), - MENU_SUBCLASS_ID, - Box::into_raw(Box::new(self)) as _, - ); + if !self.context_hwnds.iter().any(|h| *h == hwnd) { + SetWindowSubclass( + hwnd, + Some(menu_subclass_proc), + MENU_SUBCLASS_ID, + Box::into_raw(Box::new(self)) as _, + ); + } DrawMenuBar(hwnd); }; @@ -340,7 +352,9 @@ impl Menu { .ok_or(crate::Error::NotInitialized)?; self.hwnds.remove(index); unsafe { - RemoveWindowSubclass(hwnd, Some(menu_subclass_proc), MENU_SUBCLASS_ID); + if !self.context_hwnds.iter().any(|h| *h == hwnd) { + RemoveWindowSubclass(hwnd, Some(menu_subclass_proc), MENU_SUBCLASS_ID); + } SetMenu(hwnd, 0); DrawMenuBar(hwnd); } @@ -399,8 +413,21 @@ impl Menu { .unwrap_or(false) } - pub fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option) { - show_context_menu(hwnd, self.hpopupmenu, position) + pub fn show_context_menu_for_hwnd(&mut self, hwnd: isize, position: Option) { + let hpopupmenu = self.hpopupmenu; + if !self.context_hwnds.iter().any(|h| *h == hwnd) && !self.hwnds.iter().any(|h| *h == hwnd) + { + self.context_hwnds.push(hwnd); + unsafe { + SetWindowSubclass( + hwnd, + Some(menu_subclass_proc), + MENU_SUBCLASS_ID, + Box::into_raw(Box::new(self)) as _, + ); + } + } + show_context_menu(hwnd, hpopupmenu, position) } } @@ -432,11 +459,19 @@ pub(crate) struct MenuChild { hmenu: HMENU, hpopupmenu: HMENU, pub children: Option>>>, + context_hwnds: Option>, } impl Drop for MenuChild { fn drop(&mut self) { if self.item_type == MenuItemType::Submenu { + for hwnd in self.context_hwnds.as_ref().unwrap() { + unsafe { + SetMenu(*hwnd, 0); + RemoveWindowSubclass(*hwnd, Some(menu_subclass_proc), SUBMENU_SUBCLASS_ID); + } + } + unsafe { DestroyMenu(self.hmenu); DestroyMenu(self.hpopupmenu); @@ -475,6 +510,7 @@ impl MenuChild { children: None, hmenu: 0, hpopupmenu: 0, + context_hwnds: None, } } @@ -495,6 +531,7 @@ impl MenuChild { icon: None, checked: false, accelerator: None, + context_hwnds: Some(Vec::new()), } } @@ -515,6 +552,7 @@ impl MenuChild { children: None, hmenu: 0, hpopupmenu: 0, + context_hwnds: None, } } @@ -541,6 +579,7 @@ impl MenuChild { children: None, hmenu: 0, hpopupmenu: 0, + context_hwnds: None, } } @@ -567,6 +606,7 @@ impl MenuChild { children: None, hmenu: 0, hpopupmenu: 0, + context_hwnds: None, } } @@ -593,6 +633,7 @@ impl MenuChild { children: None, hmenu: 0, hpopupmenu: 0, + context_hwnds: None, } } } @@ -889,8 +930,26 @@ impl MenuChild { .collect() } - pub fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option) { - show_context_menu(hwnd, self.hpopupmenu, position) + pub fn show_context_menu_for_hwnd(&mut self, hwnd: isize, position: Option) { + let hpopupmenu = self.hpopupmenu; + unsafe { + if !self + .context_hwnds + .as_ref() + .unwrap() + .iter() + .any(|h| *h == hwnd) + { + self.context_hwnds.as_mut().unwrap().push(hwnd); + SetWindowSubclass( + hwnd, + Some(menu_subclass_proc), + SUBMENU_SUBCLASS_ID, + Box::into_raw(Box::new(self)) as _, + ); + } + } + show_context_menu(hwnd, hpopupmenu, position) } pub fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) { @@ -952,6 +1011,7 @@ fn show_context_menu(hwnd: HWND, hmenu: HMENU, position: Option) { GetCursorPos(&mut pt); pt }; + SetForegroundWindow(hwnd); TrackPopupMenu(hmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, std::ptr::null()); } }