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")]
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")]

View file

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

View file

@ -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<HWND>,
context_hwnds: Vec<HWND>,
haccel_store: Rc<RefCell<AccelWrapper>>,
children: Vec<Rc<RefCell<MenuChild>>>,
}
@ -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<Position>) {
show_context_menu(hwnd, self.hpopupmenu, position)
pub fn show_context_menu_for_hwnd(&mut self, hwnd: isize, position: Option<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,
hpopupmenu: HMENU,
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
context_hwnds: Option<Vec<HWND>>,
}
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<Position>) {
show_context_menu(hwnd, self.hpopupmenu, position)
pub fn show_context_menu_for_hwnd(&mut self, hwnd: isize, position: Option<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) {
@ -952,6 +1011,7 @@ fn show_context_menu(hwnd: HWND, hmenu: HMENU, position: Option<Position>) {
GetCursorPos(&mut pt);
pt
};
SetForegroundWindow(hwnd);
TrackPopupMenu(hmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, std::ptr::null());
}
}