Skip to content

Commit

Permalink
feat: return context menu dismiss result (#236)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason Tsai authored Nov 5, 2024
1 parent 9f29f3e commit cf9dcfa
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changes/add-context-menu-dismiss-result.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"muda": minor
---

Return `bool` in `ContextMenu::show_context_menu_for_hwnd`, `ContextMenu::show_context_menu_for_nsview` and `ContextMenu::show_context_menu_for_gtk_window` to indicate why the context menu was closed.
10 changes: 7 additions & 3 deletions src/items/submenu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ impl ContextMenu for Submenu {
}

#[cfg(target_os = "windows")]
unsafe fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<Position>) {
unsafe fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<Position>) -> bool {
self.inner
.borrow_mut()
.show_context_menu_for_hwnd(hwnd, position)
Expand All @@ -234,7 +234,11 @@ impl ContextMenu for Submenu {
}

#[cfg(target_os = "linux")]
fn show_context_menu_for_gtk_window(&self, w: &gtk::Window, position: Option<Position>) {
fn show_context_menu_for_gtk_window(
&self,
w: &gtk::Window,
position: Option<Position>,
) -> bool {
self.inner
.borrow_mut()
.show_context_menu_for_gtk_window(w, position)
Expand All @@ -250,7 +254,7 @@ impl ContextMenu for Submenu {
&self,
view: *const std::ffi::c_void,
position: Option<Position>,
) {
) -> bool {
self.inner
.borrow_mut()
.show_context_menu_for_nsview(view, position)
Expand Down
22 changes: 19 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,17 @@ pub trait ContextMenu {
///
/// - `position` is relative to the window top-left corner, if `None`, the cursor position is used.
///
/// Returns `true` if menu tracking ended because an item was selected, and `false` if menu tracking was cancelled for any reason.
///
/// # Safety
///
/// The `hwnd` must be a valid window HWND.
#[cfg(target_os = "windows")]
unsafe fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<dpi::Position>);
unsafe fn show_context_menu_for_hwnd(
&self,
hwnd: isize,
position: Option<dpi::Position>,
) -> bool;

/// Attach the menu subclass handler to the given hwnd
/// so you can recieve events from that window using [MenuEvent::receiver]
Expand All @@ -341,8 +347,16 @@ pub trait ContextMenu {
/// Shows this menu as a context menu inside a [`gtk::Window`]
///
/// - `position` is relative to the window top-left corner, if `None`, the cursor position is used.
///
/// Returns `true` if menu tracking ended because an item was selected or clicked outside the menu to dismiss it.
///
/// Returns `false` if menu tracking was cancelled for any reason.
#[cfg(target_os = "linux")]
fn show_context_menu_for_gtk_window(&self, w: &gtk::Window, position: Option<dpi::Position>);
fn show_context_menu_for_gtk_window(
&self,
w: &gtk::Window,
position: Option<dpi::Position>,
) -> bool;

/// Get the underlying gtk menu reserved for context menus.
///
Expand All @@ -354,6 +368,8 @@ pub trait ContextMenu {
///
/// - `position` is relative to the window top-left corner, if `None`, the cursor position is used.
///
/// Returns `true` if menu tracking ended because an item was selected, and `false` if menu tracking was cancelled for any reason.
///
/// # Safety
///
/// The view must be a pointer to a valid `NSView`.
Expand All @@ -362,7 +378,7 @@ pub trait ContextMenu {
&self,
view: *const std::ffi::c_void,
position: Option<dpi::Position>,
);
) -> bool;

/// Get the underlying NSMenu reserved for context menus.
///
Expand Down
10 changes: 7 additions & 3 deletions src/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ impl ContextMenu for Menu {
}

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

#[cfg(target_os = "linux")]
fn show_context_menu_for_gtk_window(&self, window: &gtk::Window, position: Option<Position>) {
fn show_context_menu_for_gtk_window(
&self,
window: &gtk::Window,
position: Option<Position>,
) -> bool {
self.inner
.borrow_mut()
.show_context_menu_for_gtk_window(window, position)
Expand All @@ -408,7 +412,7 @@ impl ContextMenu for Menu {
&self,
view: *const std::ffi::c_void,
position: Option<Position>,
) {
) -> bool {
self.inner
.borrow_mut()
.show_context_menu_for_nsview(view, position)
Expand Down
32 changes: 29 additions & 3 deletions src/platform_impl/gtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ impl Menu {
&mut self,
widget: &impl IsA<gtk::Widget>,
position: Option<Position>,
) {
) -> bool {
show_context_menu(self.gtk_context_menu(), widget, position)
}

Expand Down Expand Up @@ -952,7 +952,7 @@ impl MenuChild {
&mut self,
widget: &impl IsA<gtk::Widget>,
position: Option<Position>,
) {
) -> bool {
show_context_menu(self.gtk_context_menu(), widget, position)
}

Expand Down Expand Up @@ -1364,7 +1364,7 @@ fn show_context_menu(
gtk_menu: gtk::Menu,
widget: &impl IsA<gtk::Widget>,
position: Option<Position>,
) {
) -> bool {
let (pos, window) = if let Some(pos) = position {
let window = widget.window();
(
Expand Down Expand Up @@ -1411,14 +1411,40 @@ fn show_context_menu(
}
}

let (tx, rx) = crossbeam_channel::unbounded();
let tx_clone = tx.clone();
let id = gtk_menu.connect_cancel(move |_| tx_clone.send(false).unwrap_or(()));
let id2 = gtk_menu.connect_selection_done(move |_| tx.send(true).unwrap_or(()));

gtk_menu.popup_at_rect(
&window,
&gdk::Rectangle::new(pos.0, pos.1, 0, 0),
gdk::Gravity::NorthWest,
gdk::Gravity::NorthWest,
Some(&event),
);

loop {
gtk::main_iteration();

match rx.try_recv() {
Ok(result) => {
gtk_menu.disconnect(id);
gtk_menu.disconnect(id2);
return result;
}
Err(err) => {
if err.is_disconnected() {
gtk_menu.disconnect(id);
gtk_menu.disconnect(id2);
return false;
}
}
}
}
}

false
}

impl PredefinedMenuItemType {
Expand Down
12 changes: 8 additions & 4 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl Menu {
&self,
view: *const c_void,
position: Option<Position>,
) {
) -> bool {
// SAFETY: Upheld by caller
show_context_menu(&self.ns_menu.1, view, position)
}
Expand Down Expand Up @@ -660,7 +660,7 @@ impl MenuChild {
&self,
view: *const c_void,
position: Option<Position>,
) {
) -> bool {
show_context_menu(&self.ns_menu.as_ref().unwrap().1, view, position)
}

Expand Down Expand Up @@ -1100,7 +1100,11 @@ fn menuitem_set_native_icon(menuitem: &NSMenuItem, icon: Option<NativeIcon>) {
}
}

unsafe fn show_context_menu(ns_menu: &NSMenu, view: *const c_void, position: Option<Position>) {
unsafe fn show_context_menu(
ns_menu: &NSMenu,
view: *const c_void,
position: Option<Position>,
) -> bool {
// SAFETY: Caller verifies that the view is valid.
let view: &NSView = unsafe { &*view.cast() };

Expand All @@ -1120,7 +1124,7 @@ unsafe fn show_context_menu(ns_menu: &NSMenu, view: *const c_void, position: Opt
(location, None)
};

let _ = unsafe { ns_menu.popUpMenuPositioningItem_atLocation_inView(None, location, in_view) };
unsafe { ns_menu.popUpMenuPositioningItem_atLocation_inView(None, location, in_view) }
}

impl NativeIcon {
Expand Down
20 changes: 17 additions & 3 deletions src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,11 +420,19 @@ impl Menu {
.unwrap_or(false)
}

pub unsafe fn show_context_menu_for_hwnd(&mut self, hwnd: isize, position: Option<Position>) {
pub unsafe fn show_context_menu_for_hwnd(
&mut self,
hwnd: isize,
position: Option<Position>,
) -> bool {
let rc = show_context_menu(hwnd as _, self.hpopupmenu, position);
if let Some(item) = rc.and_then(|rc| self.find_by_id(rc)) {
menu_selected(hwnd as _, &mut item.borrow_mut());
unsafe {
menu_selected(hwnd as _, &mut item.borrow_mut());
}
return true;
}
false
}

pub unsafe fn set_theme_for_hwnd(&self, hwnd: isize, theme: MenuTheme) -> crate::Result<()> {
Expand Down Expand Up @@ -943,13 +951,19 @@ impl MenuChild {
.collect()
}

pub unsafe fn show_context_menu_for_hwnd(&mut self, hwnd: isize, position: Option<Position>) {
pub unsafe fn show_context_menu_for_hwnd(
&mut self,
hwnd: isize,
position: Option<Position>,
) -> bool {
let rc = show_context_menu(hwnd as _, self.hpopupmenu, position);
if let Some(item) = rc.and_then(|rc| self.find_by_id(rc)) {
unsafe {
menu_selected(hwnd as _, &mut item.borrow_mut());
}
return true;
}
false
}

pub unsafe fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
Expand Down

0 comments on commit cf9dcfa

Please sign in to comment.