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"
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]

View file

@ -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::<UserEvent>::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;
}
}
}
},
_ => (),
}
})

View file

@ -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::<UserEvent>::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();
}
_ => (),
}
})

View file

@ -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<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);
impl Menu {
@ -36,13 +51,8 @@ impl Submenu {
Submenu(self.0.add_submenu(label, enabled))
}
pub fn add_text_item<F: FnMut(&mut TextMenuItem) + 'static>(
&mut self,
label: impl AsRef<str>,
enabled: bool,
f: F,
) -> TextMenuItem {
TextMenuItem(self.0.add_text_item(label, enabled, f))
pub fn add_text_item(&mut self, label: impl AsRef<str>, enabled: bool) -> TextMenuItem {
TextMenuItem(self.0.add_text_item(label, enabled))
}
}

View file

@ -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<Vec<Arc<Mutex<MenuEntry>>>>,
etype: MenuEntryType,
item_handler: Option<Rc<RefCell<dyn FnMut(&mut crate::TextMenuItem) + 'static>>>,
item_id: Option<u64>,
menu_gtk_items: Option<Arc<Mutex<Vec<(gtk::MenuItem, gtk::Menu)>>>>,
item_gtk_items: Option<Arc<Mutex<Vec<gtk::MenuItem>>>>,
@ -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<M: IsA<gtk::MenuShell>>(gtk_menu: &M, entries: &Vec<Arc<Mutex<MenuEntry>>>) {
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(&gtk_item);
@ -94,18 +91,9 @@ fn add_entries_to_menu<M: IsA<gtk::MenuShell>>(gtk_menu: &M, entries: &Vec<Arc<M
.lock()
.push((gtk_item, gtk_menu));
} else {
let handler = Rc::clone(&entry.item_handler.as_mut().unwrap());
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(),
};
let id = entry.item_id.unwrap_or_default();
gtk_item.connect_activate(move |_| {
let mut handler = handler.borrow_mut();
let mut item = crate::TextMenuItem(item.clone());
handler(&mut item);
let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id });
});
entry.item_gtk_items.as_mut().unwrap().lock().push(gtk_item);
}
@ -148,7 +136,6 @@ impl Submenu {
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,
@ -167,12 +154,7 @@ impl Submenu {
}
}
pub fn add_text_item<F: FnMut(&mut crate::TextMenuItem) + 'static>(
&mut self,
label: impl AsRef<str>,
enabled: bool,
f: F,
) -> TextMenuItem {
pub fn add_text_item(&mut self, label: impl AsRef<str>, 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()),