Skip to content

Commit

Permalink
fix(windows): attach subclass for context menu if window doesn't have…
Browse files Browse the repository at this point in the history
… a menu bar (#105)
  • Loading branch information
amrbashir authored Aug 29, 2023
1 parent 93a93a6 commit 4701bb8
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changes/windows-context-menu.md
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 2 additions & 2 deletions src/items/submenu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
2 changes: 1 addition & 1 deletion src/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
92 changes: 76 additions & 16 deletions src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
};
Expand Down Expand Up @@ -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>>>,
}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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(),
}
}

Expand Down Expand Up @@ -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);
};

Expand All @@ -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);
}
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -475,6 +510,7 @@ impl MenuChild {
children: None,
hmenu: 0,
hpopupmenu: 0,
context_hwnds: None,
}
}

Expand All @@ -495,6 +531,7 @@ impl MenuChild {
icon: None,
checked: false,
accelerator: None,
context_hwnds: Some(Vec::new()),
}
}

Expand All @@ -515,6 +552,7 @@ impl MenuChild {
children: None,
hmenu: 0,
hpopupmenu: 0,
context_hwnds: None,
}
}

Expand All @@ -541,6 +579,7 @@ impl MenuChild {
children: None,
hmenu: 0,
hpopupmenu: 0,
context_hwnds: None,
}
}

Expand All @@ -567,6 +606,7 @@ impl MenuChild {
children: None,
hmenu: 0,
hpopupmenu: 0,
context_hwnds: None,
}
}

Expand All @@ -593,6 +633,7 @@ impl MenuChild {
children: None,
hmenu: 0,
hpopupmenu: 0,
context_hwnds: None,
}
}
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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());
}
}
Expand Down

0 comments on commit 4701bb8

Please sign in to comment.