diff --git a/Cargo.toml b/Cargo.toml index c73b8b7..6a6bbee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,15 +3,20 @@ name = "menu-rs" version = "0.0.0" edition = "2021" -[target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { version = "0.34", features = [ +[dependencies] +crossbeam-channel = "0.5" +once_cell = "1.10" + +[target.'cfg(target_os = "windows")'.dependencies.windows-sys] +version = "0.34" +features = [ "Win32_UI_WindowsAndMessaging", "Win32_Foundation", - "Win32_Graphics_Gdi" -] } + "Win32_Graphics_Gdi", +] [target.'cfg(target_os = "linux")'.dependencies] -parking_lot = "0.12.0" +parking_lot = "0.12" gtk = "0.15" [dev-dependencies] diff --git a/examples/tao.rs b/examples/tao.rs index 0d1dabd..8857019 100644 --- a/examples/tao.rs +++ b/examples/tao.rs @@ -1,4 +1,4 @@ -use menu_rs::Menu; +use menu_rs::{menu_event_receiver, Menu}; #[cfg(target_os = "linux")] use tao::platform::unix::WindowExtUnix; #[cfg(target_os = "windows")] @@ -9,12 +9,8 @@ use tao::{ window::WindowBuilder, }; -enum UserEvent { - MenuEvent(u64), -} - fn main() { - let event_loop = EventLoop::::with_user_event(); + let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); let window2 = WindowBuilder::new().build(&event_loop).unwrap(); @@ -23,19 +19,13 @@ fn main() { let mut file_menu = menu_bar.add_submenu("File", true); let mut edit_menu = menu_bar.add_submenu("Edit", true); - let mut open_item = file_menu.add_text_item("Open", true, |_| {}); + let mut open_item = file_menu.add_text_item("Open", true); - let proxy = event_loop.create_proxy(); - let mut counter = 0; - let save_item = file_menu.add_text_item("Save", true, move |i| { - counter += 1; - i.set_label(format!("Save triggered {} times", counter)); - let _ = proxy.send_event(UserEvent::MenuEvent(i.id())); - }); - let _quit_item = file_menu.add_text_item("Quit", true, |_| {}); + let mut save_item = file_menu.add_text_item("Save", true); + let _quit_item = file_menu.add_text_item("Quit", true); - let _copy_item = edit_menu.add_text_item("Copy", true, |_| {}); - let _cut_item = edit_menu.add_text_item("Cut", true, |_| {}); + let _copy_item = edit_menu.add_text_item("Copy", true); + let _cut_item = edit_menu.add_text_item("Cut", true); #[cfg(target_os = "windows")] { @@ -48,10 +38,29 @@ fn main() { menu_bar.init_for_gtk_window(window2.gtk_window()); } + let menu_channel = menu_event_receiver(); let mut open_item_disabled = false; + let counter = 0; + event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; + if let Ok(event) = menu_channel.try_recv() { + match event.id { + _ if event.id == save_item.id() => { + println!("Save menu item triggered"); + + save_item.set_label(format!("Save triggered {counter} times")); + if !open_item_disabled { + println!("Open item disabled!"); + open_item.set_enabled(false); + open_item_disabled = true; + } + } + _ => {} + } + } + match event { Event::WindowEvent { event: WindowEvent::CloseRequested, @@ -60,20 +69,6 @@ fn main() { Event::MainEventsCleared => { window.request_redraw(); } - - Event::UserEvent(e) => match e { - UserEvent::MenuEvent(id) => { - if id == save_item.id() { - println!("Save menu item triggered"); - - if !open_item_disabled { - println!("Open item disabled!"); - open_item.set_enabled(false); - open_item_disabled = true; - } - } - } - }, _ => (), } }) diff --git a/examples/winit.rs b/examples/winit.rs index e134538..8e96eaa 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -1,4 +1,4 @@ -use menu_rs::Menu; +use menu_rs::{menu_event_receiver, Menu}; #[cfg(target_os = "windows")] use winit::platform::windows::WindowExtWindows; use winit::{ @@ -7,63 +7,61 @@ use winit::{ window::WindowBuilder, }; -enum UserEvent { - MenuEvent(u64), -} - fn main() { - let event_loop = EventLoop::::with_user_event(); + let event_loop = EventLoop::new(); - let _window = WindowBuilder::new().build(&event_loop).unwrap(); - let _window2 = WindowBuilder::new().build(&event_loop).unwrap(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + let window2 = WindowBuilder::new().build(&event_loop).unwrap(); let mut menu_bar = Menu::new(); let mut file_menu = menu_bar.add_submenu("File", true); let mut edit_menu = menu_bar.add_submenu("Edit", true); - let mut open_item = file_menu.add_text_item("Open", true, |_| {}); + let mut open_item = file_menu.add_text_item("Open", true); - let proxy = event_loop.create_proxy(); - let mut counter = 0; - let save_item = file_menu.add_text_item("Save", true, move |i| { - counter += 1; - i.set_label(format!("Save triggered {} times", counter)); - let _ = proxy.send_event(UserEvent::MenuEvent(i.id())); - }); - let _quit_item = file_menu.add_text_item("Quit", true, |_| {}); + let mut save_item = file_menu.add_text_item("Save", true); + let _quit_item = file_menu.add_text_item("Quit", true); - let _copy_item = edit_menu.add_text_item("Copy", true, |_| {}); - let _cut_item = edit_menu.add_text_item("Cut", true, |_| {}); + let _copy_item = edit_menu.add_text_item("Copy", true); + let _cut_item = edit_menu.add_text_item("Cut", true); #[cfg(target_os = "windows")] { - menu_bar.init_for_hwnd(_window.hwnd() as _); - menu_bar.init_for_hwnd(_window2.hwnd() as _); + menu_bar.init_for_hwnd(window.hwnd() as _); + menu_bar.init_for_hwnd(window2.hwnd() as _); } + let menu_channel = menu_event_receiver(); let mut open_item_disabled = false; + let counter = 0; + event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; + if let Ok(event) = menu_channel.try_recv() { + match event.id { + _ if event.id == save_item.id() => { + println!("Save menu item triggered"); + + save_item.set_label(format!("Save triggered {counter} times")); + if !open_item_disabled { + println!("Open item disabled!"); + open_item.set_enabled(false); + open_item_disabled = true; + } + } + _ => {} + } + } + match event { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => *control_flow = ControlFlow::Exit, - - Event::UserEvent(e) => match e { - UserEvent::MenuEvent(id) => { - if id == save_item.id() { - println!("Save menu item triggered"); - - if !open_item_disabled { - println!("Open item disabled!"); - open_item.set_enabled(false); - open_item_disabled = true; - } - } - } - }, + Event::MainEventsCleared => { + window.request_redraw(); + } _ => (), } }) diff --git a/src/lib.rs b/src/lib.rs index 140b00c..b720e18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,21 @@ +use crossbeam_channel::{unbounded, Receiver, Sender}; +use once_cell::sync::Lazy; + mod platform_impl; mod util; +static MENU_CHANNEL: Lazy<(Sender, Receiver)> = Lazy::new(|| unbounded()); + +/// Event channel for receiving menu events. +pub fn menu_event_receiver<'a>() -> &'a Receiver { + &MENU_CHANNEL.1 +} + +/// Describes a menu event emitted when a menu item is activated +pub struct MenuEvent { + pub id: u64, +} + pub struct Menu(platform_impl::Menu); impl Menu { @@ -36,13 +51,8 @@ impl Submenu { Submenu(self.0.add_submenu(label, enabled)) } - pub fn add_text_item( - &mut self, - label: impl AsRef, - enabled: bool, - f: F, - ) -> TextMenuItem { - TextMenuItem(self.0.add_text_item(label, enabled, f)) + pub fn add_text_item(&mut self, label: impl AsRef, enabled: bool) -> TextMenuItem { + TextMenuItem(self.0.add_text_item(label, enabled)) } } diff --git a/src/platform_impl/linux.rs b/src/platform_impl/linux.rs index 0bf8b54..0d0ad40 100644 --- a/src/platform_impl/linux.rs +++ b/src/platform_impl/linux.rs @@ -1,5 +1,5 @@ use parking_lot::Mutex; -use std::{cell::RefCell, rc::Rc, sync::Arc}; +use std::sync::Arc; use gtk::{prelude::*, Orientation}; @@ -17,7 +17,6 @@ struct MenuEntry { enabled: bool, entries: Option>>>, etype: MenuEntryType, - item_handler: Option>>, item_id: Option, menu_gtk_items: Option>>>, item_gtk_items: Option>>>, @@ -46,7 +45,6 @@ impl Menu { enabled, entries: Some(Vec::new()), etype: MenuEntryType::Submenu, - item_handler: None, item_id: None, menu_gtk_items: Some(gtk_items.clone()), item_gtk_items: None, @@ -78,7 +76,6 @@ impl Menu { fn add_entries_to_menu>(gtk_menu: &M, entries: &Vec>>) { for entry in entries { - let entry_clone = entry.clone(); let mut entry = entry.lock(); let gtk_item = gtk::MenuItem::with_label(&entry.label); gtk_menu.append(>k_item); @@ -94,18 +91,9 @@ fn add_entries_to_menu>(gtk_menu: &M, entries: &Vec( - &mut self, - label: impl AsRef, - enabled: bool, - f: F, - ) -> TextMenuItem { + pub fn add_text_item(&mut self, label: impl AsRef, enabled: bool) -> TextMenuItem { let id = COUNTER.next(); let label = label.as_ref().to_string(); let gtk_items = Arc::new(Mutex::new(Vec::new())); @@ -181,7 +163,6 @@ impl Submenu { enabled, entries: None, etype: MenuEntryType::Text, - item_handler: Some(Rc::new(RefCell::new(f))), item_id: Some(id), menu_gtk_items: None, item_gtk_items: Some(gtk_items.clone()),