fix(windows): attach subclass for context menu if window doesn't have a menu bar (#105)

This commit is contained in:
Amr Bashir 2023-08-29 20:06:58 +03:00 committed by GitHub
parent 93a93a699e
commit 4701bb836e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 19 deletions

View file

@ -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.

View file

@ -207,12 +207,12 @@ impl ContextMenu for Submenu {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) { 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")] #[cfg(target_os = "windows")]
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) { 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")] #[cfg(target_os = "linux")]

View file

@ -324,7 +324,7 @@ impl ContextMenu for Menu {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<Position>) { fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<Position>) {
self.inner self.inner
.borrow() .borrow_mut()
.show_context_menu_for_hwnd(hwnd, position) .show_context_menu_for_hwnd(hwnd, position)
} }

View file

@ -34,11 +34,11 @@ use windows_sys::Win32::{
AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu, AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu,
DestroyAcceleratorTable, DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos, DestroyAcceleratorTable, DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos,
GetMenu, GetMenuItemInfoW, InsertMenuW, PostQuitMessage, RemoveMenu, SendMessageW, GetMenu, GetMenuItemInfoW, InsertMenuW, PostQuitMessage, RemoveMenu, SendMessageW,
SetMenu, SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, SetForegroundWindow, SetMenu, SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL,
MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED, HMENU, MENUITEMINFOW, MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION,
MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, MF_UNCHECKED, MIIM_BITMAP, MF_CHECKED, MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING,
MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, TPM_LEFTALIGN, WM_CLOSE, MF_UNCHECKED, MIIM_BITMAP, MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE,
WM_COMMAND, TPM_LEFTALIGN, WM_CLOSE, WM_COMMAND,
}, },
}, },
}; };
@ -100,6 +100,7 @@ pub(crate) struct Menu {
hmenu: HMENU, hmenu: HMENU,
hpopupmenu: HMENU, hpopupmenu: HMENU,
hwnds: Vec<HWND>, hwnds: Vec<HWND>,
context_hwnds: Vec<HWND>,
haccel_store: Rc<RefCell<AccelWrapper>>, haccel_store: Rc<RefCell<AccelWrapper>>,
children: Vec<Rc<RefCell<MenuChild>>>, children: Vec<Rc<RefCell<MenuChild>>>,
} }
@ -136,6 +137,14 @@ impl Drop for Menu {
} }
unsafe { 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.hmenu);
DestroyMenu(self.hpopupmenu); DestroyMenu(self.hpopupmenu);
} }
@ -153,6 +162,7 @@ impl Menu {
haccel_store: Rc::new(RefCell::new((0, HashMap::new()))), haccel_store: Rc::new(RefCell::new((0, HashMap::new()))),
children: Vec::new(), children: Vec::new(),
hwnds: Vec::new(), hwnds: Vec::new(),
context_hwnds: Vec::new(),
} }
} }
@ -320,12 +330,14 @@ impl Menu {
self.hwnds.push(hwnd); self.hwnds.push(hwnd);
unsafe { unsafe {
SetMenu(hwnd, self.hmenu); SetMenu(hwnd, self.hmenu);
SetWindowSubclass( if !self.context_hwnds.iter().any(|h| *h == hwnd) {
hwnd, SetWindowSubclass(
Some(menu_subclass_proc), hwnd,
MENU_SUBCLASS_ID, Some(menu_subclass_proc),
Box::into_raw(Box::new(self)) as _, MENU_SUBCLASS_ID,
); Box::into_raw(Box::new(self)) as _,
);
}
DrawMenuBar(hwnd); DrawMenuBar(hwnd);
}; };
@ -340,7 +352,9 @@ impl Menu {
.ok_or(crate::Error::NotInitialized)?; .ok_or(crate::Error::NotInitialized)?;
self.hwnds.remove(index); self.hwnds.remove(index);
unsafe { 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); SetMenu(hwnd, 0);
DrawMenuBar(hwnd); DrawMenuBar(hwnd);
} }
@ -399,8 +413,21 @@ impl Menu {
.unwrap_or(false) .unwrap_or(false)
} }
pub fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<Position>) { pub fn show_context_menu_for_hwnd(&mut self, hwnd: isize, position: Option<Position>) {
show_context_menu(hwnd, self.hpopupmenu, position) 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, hmenu: HMENU,
hpopupmenu: HMENU, hpopupmenu: HMENU,
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>, pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
context_hwnds: Option<Vec<HWND>>,
} }
impl Drop for MenuChild { impl Drop for MenuChild {
fn drop(&mut self) { fn drop(&mut self) {
if self.item_type == MenuItemType::Submenu { 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 { unsafe {
DestroyMenu(self.hmenu); DestroyMenu(self.hmenu);
DestroyMenu(self.hpopupmenu); DestroyMenu(self.hpopupmenu);
@ -475,6 +510,7 @@ impl MenuChild {
children: None, children: None,
hmenu: 0, hmenu: 0,
hpopupmenu: 0, hpopupmenu: 0,
context_hwnds: None,
} }
} }
@ -495,6 +531,7 @@ impl MenuChild {
icon: None, icon: None,
checked: false, checked: false,
accelerator: None, accelerator: None,
context_hwnds: Some(Vec::new()),
} }
} }
@ -515,6 +552,7 @@ impl MenuChild {
children: None, children: None,
hmenu: 0, hmenu: 0,
hpopupmenu: 0, hpopupmenu: 0,
context_hwnds: None,
} }
} }
@ -541,6 +579,7 @@ impl MenuChild {
children: None, children: None,
hmenu: 0, hmenu: 0,
hpopupmenu: 0, hpopupmenu: 0,
context_hwnds: None,
} }
} }
@ -567,6 +606,7 @@ impl MenuChild {
children: None, children: None,
hmenu: 0, hmenu: 0,
hpopupmenu: 0, hpopupmenu: 0,
context_hwnds: None,
} }
} }
@ -593,6 +633,7 @@ impl MenuChild {
children: None, children: None,
hmenu: 0, hmenu: 0,
hpopupmenu: 0, hpopupmenu: 0,
context_hwnds: None,
} }
} }
} }
@ -889,8 +930,26 @@ impl MenuChild {
.collect() .collect()
} }
pub fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<Position>) { pub fn show_context_menu_for_hwnd(&mut self, hwnd: isize, position: Option<Position>) {
show_context_menu(hwnd, self.hpopupmenu, position) 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) { pub fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
@ -952,6 +1011,7 @@ fn show_context_menu(hwnd: HWND, hmenu: HMENU, position: Option<Position>) {
GetCursorPos(&mut pt); GetCursorPos(&mut pt);
pt pt
}; };
SetForegroundWindow(hwnd);
TrackPopupMenu(hmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, std::ptr::null()); TrackPopupMenu(hmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, std::ptr::null());
} }
} }