Skip to content

Commit

Permalink
refactor: allow changing the menu event sender (#35)
Browse files Browse the repository at this point in the history
* refactor: allow changing the menu event sender

* readme

* fix docs warning

* readme
  • Loading branch information
amrbashir committed Jan 3, 2023
1 parent 1e87714 commit f871c68
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changes/event_handler.md
@@ -0,0 +1,5 @@
---
"muda": "patch"
---

Add `MenuEvent::set_event_handler` to set a handler for new menu events.
5 changes: 5 additions & 0 deletions .changes/rm-menu-event-receiver.md
@@ -0,0 +1,5 @@
---
"muda": "minor"
---

**Breaking change** Remove `menu_event_receiver` function, use `MenuEvent::receiver` instead.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -52,10 +52,10 @@ menu.show_context_menu_for_nsview(nsview, x, y);
```
## Processing menu events

You can use [`menu_event_receiver`](https://docs.rs/muda/latest/muda/fn.menu_event_receiver.html) to get a reference to the [`MenuEventReceiver`](https://docs.rs/muda/latest/muda/type.MenuEventReceiver.html)
You can use `MenuEvent::receiver` to get a reference to the `MenuEventReceiver`
which you can use to listen to events when a menu item is activated
```rs
if let Ok(event) = menu_event_receiver().try_recv() {
if let Ok(event) = MenuEvent::receiver().try_recv() {
match event.id {
_ if event.id == save_item.id() => {
println!("Save menu item activated");
Expand Down
4 changes: 2 additions & 2 deletions examples/tao.rs
Expand Up @@ -5,7 +5,7 @@
#![allow(unused)]
use muda::{
accelerator::{Accelerator, Code, Modifiers},
menu_event_receiver, AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuItem,
AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem,
PredefinedMenuItem, Submenu,
};
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -144,7 +144,7 @@ fn main() {
window_m.set_windows_menu_for_nsapp();
}

let menu_channel = menu_event_receiver();
let menu_channel = MenuEvent::receiver();

let mut x = 0_f64;
let mut y = 0_f64;
Expand Down
4 changes: 2 additions & 2 deletions examples/winit.rs
Expand Up @@ -5,7 +5,7 @@
#![allow(unused)]
use muda::{
accelerator::{Accelerator, Code, Modifiers},
menu_event_receiver, AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuItem,
AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem,
PredefinedMenuItem, Submenu,
};
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -136,7 +136,7 @@ fn main() {
window_m.set_windows_menu_for_nsapp();
}

let menu_channel = menu_event_receiver();
let menu_channel = MenuEvent::receiver();

let mut x = 0_f64;
let mut y = 0_f64;
Expand Down
48 changes: 39 additions & 9 deletions src/lib.rs
Expand Up @@ -79,13 +79,13 @@
//! ```
//! # Processing menu events
//!
//! You can use [`menu_event_receiver`] to get a reference to the [`MenuEventReceiver`]
//! You can use [`MenuEvent::receiver`] to get a reference to the [`MenuEventReceiver`]
//! which you can use to listen to events when a menu item is activated
//! ```no_run
//! # use muda::menu_event_receiver;
//! # use muda::MenuEvent;
//! #
//! # let save_item: muda::MenuItem = unsafe { std::mem::zeroed() };
//! if let Ok(event) = menu_event_receiver().try_recv() {
//! if let Ok(event) = MenuEvent::receiver().try_recv() {
//! match event.id {
//! id if id == save_item.id() => {
//! println!("Save menu item activated");
Expand All @@ -103,7 +103,7 @@
//! See [`Menu::init_for_hwnd`] for more details

use crossbeam_channel::{unbounded, Receiver, Sender};
use once_cell::sync::Lazy;
use once_cell::sync::{Lazy, OnceCell};

pub mod accelerator;
mod check_menu_item;
Expand Down Expand Up @@ -192,7 +192,7 @@ pub trait ContextMenu {
fn show_context_menu_for_hwnd(&self, hwnd: isize, x: f64, y: f64);

/// Attach the menu subclass handler to the given hwnd
/// so you can recieve events from that window using [menu_event_receiver]
/// so you can recieve events from that window using [MenuEvent::receiver]
///
/// This can be used along with [`ContextMenu::hpopupmenu`] when implementing a tray icon menu.
#[cfg(target_os = "windows")]
Expand Down Expand Up @@ -231,11 +231,41 @@ pub struct MenuEvent {

/// A reciever that could be used to listen to menu events.
pub type MenuEventReceiver = Receiver<MenuEvent>;
type MenuEventHandler = Box<dyn Fn(MenuEvent) + Send + Sync + 'static>;

static MENU_CHANNEL: Lazy<(Sender<MenuEvent>, MenuEventReceiver)> = Lazy::new(unbounded);
static MENU_EVENT_HANDLER: OnceCell<Option<MenuEventHandler>> = OnceCell::new();

/// Gets a reference to the event channel's [MenuEventReceiver]
/// which can be used to listen for menu events.
pub fn menu_event_receiver<'a>() -> &'a MenuEventReceiver {
&MENU_CHANNEL.1
impl MenuEvent {
/// Gets a reference to the event channel's [`MenuEventReceiver`]
/// which can be used to listen for menu events.
///
/// ## Note
///
/// This will not receive any events if [`MenuEvent::set_event_handler`] has been called with a `Some` value.
pub fn receiver<'a>() -> &'a MenuEventReceiver {
&MENU_CHANNEL.1
}

/// Set a handler to be called for new events. Useful for implementing custom event sender.
///
/// ## Note
///
/// Calling this function with a `Some` value,
/// will not send new events to the channel associated with [`MenuEvent::receiver`]
pub fn set_event_handler<F: Fn(MenuEvent) + Send + Sync + 'static>(f: Option<F>) {
if let Some(f) = f {
let _ = MENU_EVENT_HANDLER.set(Some(Box::new(f)));
} else {
let _ = MENU_EVENT_HANDLER.set(None);
}
}

pub(crate) fn send(event: MenuEvent) {
if let Some(handler) = MENU_EVENT_HANDLER.get_or_init(|| None) {
handler(event);
} else {
let _ = MENU_CHANNEL.0.send(event);
}
}
}
8 changes: 4 additions & 4 deletions src/platform_impl/gtk/mod.rs
Expand Up @@ -12,7 +12,7 @@ use crate::{
icon::Icon,
predefined::PredfinedMenuItemType,
util::{AddOp, Counter},
MenuItemType,
MenuEvent, MenuItemType,
};
use accelerator::{from_gtk_mnemonic, parse_accelerator, register_accelerator, to_gtk_mnemonic};
use gtk::{prelude::*, Orientation};
Expand Down Expand Up @@ -826,7 +826,7 @@ impl MenuItem {

let id = self_.id;
item.connect_activate(move |_| {
let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id });
MenuEvent::send(crate::MenuEvent { id });
});

if add_to_cache {
Expand Down Expand Up @@ -1069,7 +1069,7 @@ impl CheckMenuItem {

is_syncing_checked_state_c.store(false, Ordering::Release);

let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id });
MenuEvent::send(crate::MenuEvent { id });
}
});

Expand Down Expand Up @@ -1188,7 +1188,7 @@ impl IconMenuItem {

let id = self_.id;
item.connect_activate(move |_| {
let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id });
MenuEvent::send(crate::MenuEvent { id });
});

if add_to_cache {
Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/macos/mod.rs
Expand Up @@ -26,7 +26,7 @@ use crate::{
icon::Icon,
predefined::PredfinedMenuItemType,
util::{AddOp, Counter},
MenuItemExt, MenuItemType,
MenuEvent, MenuItemExt, MenuItemType,
};

static COUNTER: Counter = Counter::new();
Expand Down Expand Up @@ -952,7 +952,7 @@ extern "C" fn fire_menu_item_click(this: &Object, _: Sel, _item: id) {
(*item).set_checked(!(*item).is_checked());
}

let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id });
MenuEvent::send(crate::MenuEvent { id });
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/windows/mod.rs
Expand Up @@ -13,7 +13,7 @@ use crate::{
icon::Icon,
predefined::PredfinedMenuItemType,
util::{AddOp, Counter},
MenuItemType,
MenuEvent, MenuItemType,
};
use std::{cell::RefCell, fmt::Debug, rc::Rc};
use util::{decode_wide, encode_wide, Accel};
Expand Down Expand Up @@ -1144,7 +1144,7 @@ website: {} {}
}

if dispatch {
let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id });
MenuEvent::send(crate::MenuEvent { id });
}
}
}
Expand Down

0 comments on commit f871c68

Please sign in to comment.