use an event channel instead of callbacks

This commit is contained in:
amrbashir 2022-05-06 16:25:45 +02:00
parent b5598554a0
commit fbef2d8a40
No known key found for this signature in database
GPG key ID: BBD7A47A2003FF33
5 changed files with 90 additions and 101 deletions

View file

@ -3,15 +3,20 @@ name = "menu-rs"
version = "0.0.0" version = "0.0.0"
edition = "2021" edition = "2021"
[target.'cfg(target_os = "windows")'.dependencies] [dependencies]
windows-sys = { version = "0.34", features = [ crossbeam-channel = "0.5"
once_cell = "1.10"
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.34"
features = [
"Win32_UI_WindowsAndMessaging", "Win32_UI_WindowsAndMessaging",
"Win32_Foundation", "Win32_Foundation",
"Win32_Graphics_Gdi" "Win32_Graphics_Gdi",
] } ]
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
parking_lot = "0.12.0" parking_lot = "0.12"
gtk = "0.15" gtk = "0.15"
[dev-dependencies] [dev-dependencies]

View file

@ -1,4 +1,4 @@
use menu_rs::Menu; use menu_rs::{menu_event_receiver, Menu};
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use tao::platform::unix::WindowExtUnix; use tao::platform::unix::WindowExtUnix;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@ -9,12 +9,8 @@ use tao::{
window::WindowBuilder, window::WindowBuilder,
}; };
enum UserEvent {
MenuEvent(u64),
}
fn main() { fn main() {
let event_loop = EventLoop::<UserEvent>::with_user_event(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
let window2 = 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 file_menu = menu_bar.add_submenu("File", true);
let mut edit_menu = menu_bar.add_submenu("Edit", 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 save_item = file_menu.add_text_item("Save", true);
let mut counter = 0; let _quit_item = file_menu.add_text_item("Quit", true);
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 _copy_item = edit_menu.add_text_item("Copy", true, |_| {}); let _copy_item = edit_menu.add_text_item("Copy", true);
let _cut_item = edit_menu.add_text_item("Cut", true, |_| {}); let _cut_item = edit_menu.add_text_item("Cut", true);
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
@ -48,10 +38,29 @@ fn main() {
menu_bar.init_for_gtk_window(window2.gtk_window()); menu_bar.init_for_gtk_window(window2.gtk_window());
} }
let menu_channel = menu_event_receiver();
let mut open_item_disabled = false; let mut open_item_disabled = false;
let counter = 0;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait; *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 { match event {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
@ -60,20 +69,6 @@ fn main() {
Event::MainEventsCleared => { Event::MainEventsCleared => {
window.request_redraw(); 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;
}
}
}
},
_ => (), _ => (),
} }
}) })

View file

@ -1,4 +1,4 @@
use menu_rs::Menu; use menu_rs::{menu_event_receiver, Menu};
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use winit::platform::windows::WindowExtWindows; use winit::platform::windows::WindowExtWindows;
use winit::{ use winit::{
@ -7,63 +7,61 @@ use winit::{
window::WindowBuilder, window::WindowBuilder,
}; };
enum UserEvent {
MenuEvent(u64),
}
fn main() { fn main() {
let event_loop = EventLoop::<UserEvent>::with_user_event(); let event_loop = EventLoop::new();
let _window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
let _window2 = WindowBuilder::new().build(&event_loop).unwrap(); let window2 = WindowBuilder::new().build(&event_loop).unwrap();
let mut menu_bar = Menu::new(); let mut menu_bar = Menu::new();
let mut file_menu = menu_bar.add_submenu("File", true); let mut file_menu = menu_bar.add_submenu("File", true);
let mut edit_menu = menu_bar.add_submenu("Edit", 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 save_item = file_menu.add_text_item("Save", true);
let mut counter = 0; let _quit_item = file_menu.add_text_item("Quit", true);
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 _copy_item = edit_menu.add_text_item("Copy", true, |_| {}); let _copy_item = edit_menu.add_text_item("Copy", true);
let _cut_item = edit_menu.add_text_item("Cut", true, |_| {}); let _cut_item = edit_menu.add_text_item("Cut", true);
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
menu_bar.init_for_hwnd(_window.hwnd() as _); menu_bar.init_for_hwnd(window.hwnd() as _);
menu_bar.init_for_hwnd(_window2.hwnd() as _); menu_bar.init_for_hwnd(window2.hwnd() as _);
} }
let menu_channel = menu_event_receiver();
let mut open_item_disabled = false; let mut open_item_disabled = false;
let counter = 0;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait; *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 { match event {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
Event::MainEventsCleared => {
Event::UserEvent(e) => match e { window.request_redraw();
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;
}
}
}
},
_ => (), _ => (),
} }
}) })

View file

@ -1,6 +1,21 @@
use crossbeam_channel::{unbounded, Receiver, Sender};
use once_cell::sync::Lazy;
mod platform_impl; mod platform_impl;
mod util; mod util;
static MENU_CHANNEL: Lazy<(Sender<MenuEvent>, Receiver<MenuEvent>)> = Lazy::new(|| unbounded());
/// Event channel for receiving menu events.
pub fn menu_event_receiver<'a>() -> &'a Receiver<MenuEvent> {
&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); pub struct Menu(platform_impl::Menu);
impl Menu { impl Menu {
@ -36,13 +51,8 @@ impl Submenu {
Submenu(self.0.add_submenu(label, enabled)) Submenu(self.0.add_submenu(label, enabled))
} }
pub fn add_text_item<F: FnMut(&mut TextMenuItem) + 'static>( pub fn add_text_item(&mut self, label: impl AsRef<str>, enabled: bool) -> TextMenuItem {
&mut self, TextMenuItem(self.0.add_text_item(label, enabled))
label: impl AsRef<str>,
enabled: bool,
f: F,
) -> TextMenuItem {
TextMenuItem(self.0.add_text_item(label, enabled, f))
} }
} }

View file

@ -1,5 +1,5 @@
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{cell::RefCell, rc::Rc, sync::Arc}; use std::sync::Arc;
use gtk::{prelude::*, Orientation}; use gtk::{prelude::*, Orientation};
@ -17,7 +17,6 @@ struct MenuEntry {
enabled: bool, enabled: bool,
entries: Option<Vec<Arc<Mutex<MenuEntry>>>>, entries: Option<Vec<Arc<Mutex<MenuEntry>>>>,
etype: MenuEntryType, etype: MenuEntryType,
item_handler: Option<Rc<RefCell<dyn FnMut(&mut crate::TextMenuItem) + 'static>>>,
item_id: Option<u64>, item_id: Option<u64>,
menu_gtk_items: Option<Arc<Mutex<Vec<(gtk::MenuItem, gtk::Menu)>>>>, menu_gtk_items: Option<Arc<Mutex<Vec<(gtk::MenuItem, gtk::Menu)>>>>,
item_gtk_items: Option<Arc<Mutex<Vec<gtk::MenuItem>>>>, item_gtk_items: Option<Arc<Mutex<Vec<gtk::MenuItem>>>>,
@ -46,7 +45,6 @@ impl Menu {
enabled, enabled,
entries: Some(Vec::new()), entries: Some(Vec::new()),
etype: MenuEntryType::Submenu, etype: MenuEntryType::Submenu,
item_handler: None,
item_id: None, item_id: None,
menu_gtk_items: Some(gtk_items.clone()), menu_gtk_items: Some(gtk_items.clone()),
item_gtk_items: None, item_gtk_items: None,
@ -78,7 +76,6 @@ impl Menu {
fn add_entries_to_menu<M: IsA<gtk::MenuShell>>(gtk_menu: &M, entries: &Vec<Arc<Mutex<MenuEntry>>>) { fn add_entries_to_menu<M: IsA<gtk::MenuShell>>(gtk_menu: &M, entries: &Vec<Arc<Mutex<MenuEntry>>>) {
for entry in entries { for entry in entries {
let entry_clone = entry.clone();
let mut entry = entry.lock(); let mut entry = entry.lock();
let gtk_item = gtk::MenuItem::with_label(&entry.label); let gtk_item = gtk::MenuItem::with_label(&entry.label);
gtk_menu.append(&gtk_item); gtk_menu.append(&gtk_item);
@ -94,18 +91,9 @@ fn add_entries_to_menu<M: IsA<gtk::MenuShell>>(gtk_menu: &M, entries: &Vec<Arc<M
.lock() .lock()
.push((gtk_item, gtk_menu)); .push((gtk_item, gtk_menu));
} else { } else {
let handler = Rc::clone(&entry.item_handler.as_mut().unwrap()); let id = entry.item_id.unwrap_or_default();
let item = TextMenuItem {
label: entry.label.clone(),
enabled: entry.enabled,
id: entry.item_id.unwrap(),
entry: entry_clone,
gtk_items: entry.item_gtk_items.as_ref().unwrap().clone(),
};
gtk_item.connect_activate(move |_| { gtk_item.connect_activate(move |_| {
let mut handler = handler.borrow_mut(); let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id });
let mut item = crate::TextMenuItem(item.clone());
handler(&mut item);
}); });
entry.item_gtk_items.as_mut().unwrap().lock().push(gtk_item); entry.item_gtk_items.as_mut().unwrap().lock().push(gtk_item);
} }
@ -148,7 +136,6 @@ impl Submenu {
enabled, enabled,
entries: Some(Vec::new()), entries: Some(Vec::new()),
etype: MenuEntryType::Submenu, etype: MenuEntryType::Submenu,
item_handler: None,
item_id: None, item_id: None,
menu_gtk_items: Some(gtk_items.clone()), menu_gtk_items: Some(gtk_items.clone()),
item_gtk_items: None, item_gtk_items: None,
@ -167,12 +154,7 @@ impl Submenu {
} }
} }
pub fn add_text_item<F: FnMut(&mut crate::TextMenuItem) + 'static>( pub fn add_text_item(&mut self, label: impl AsRef<str>, enabled: bool) -> TextMenuItem {
&mut self,
label: impl AsRef<str>,
enabled: bool,
f: F,
) -> TextMenuItem {
let id = COUNTER.next(); let id = COUNTER.next();
let label = label.as_ref().to_string(); let label = label.as_ref().to_string();
let gtk_items = Arc::new(Mutex::new(Vec::new())); let gtk_items = Arc::new(Mutex::new(Vec::new()));
@ -181,7 +163,6 @@ impl Submenu {
enabled, enabled,
entries: None, entries: None,
etype: MenuEntryType::Text, etype: MenuEntryType::Text,
item_handler: Some(Rc::new(RefCell::new(f))),
item_id: Some(id), item_id: Some(id),
menu_gtk_items: None, menu_gtk_items: None,
item_gtk_items: Some(gtk_items.clone()), item_gtk_items: Some(gtk_items.clone()),