diff --git a/Cargo.toml b/Cargo.toml index 6e23fec..51ea782 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "menu-rs" version = "0.0.0" +description = "Cross-platform menu creation library." edition = "2021" [dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..be450cc --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# menu-rs + +Cross-platform menu creation library. + +## Example + +Create the root menu and add submenus and men items. +```rs +let mut menu = Menu::new(); + +let file_menu = menu.add_submenu("File", true); +let open_item = file_menu.add_text_item("Open", true); +let save_item = file_menu.add_text_item("Save", true); + +let edit_menu = menu.add_submenu("Edit", true); +let copy_item = file_menu.add_text_item("Copy", true); +let cut_item = file_menu.add_text_item("Cut", true); + +#[cfg(target_os = "windows")] +menu.init_for_hwnd(window.hwnd() as isize); +#[cfg(target_os = "linux")] +menu.init_for_gtk_window(>k_window); +``` +Then listen for the events +```rs +if let Ok(event) = menu_event_receiver().try_recv() { + match event.id { + _ if event.id == save_item.id() => { + println!("Save menu item activated"); + }, + _ => {} + } +} +``` \ No newline at end of file diff --git a/examples/tao.rs b/examples/tao.rs index b27dfe2..9713434 100644 --- a/examples/tao.rs +++ b/examples/tao.rs @@ -48,9 +48,9 @@ fn main() { if let Ok(event) = menu_channel.try_recv() { match event.id { _ if event.id == save_item.id() => { - println!("Save menu item triggered"); + println!("Save menu item activated!"); counter += 1; - save_item.set_label(format!("Save triggered {counter} times")); + save_item.set_label(format!("Save activated {counter} times")); if !open_item_disabled { println!("Open item disabled!"); diff --git a/examples/winit.rs b/examples/winit.rs index 33d049c..0ccbb5d 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -41,9 +41,9 @@ fn main() { if let Ok(event) = menu_channel.try_recv() { match event.id { _ if event.id == save_item.id() => { - println!("Save menu item triggered"); + println!("Save menu item activated!"); counter += 1; - save_item.set_label(format!("Save triggered {counter} times")); + save_item.set_label(format!("Save activated {counter} times")); if !open_item_disabled { println!("Open item disabled!"); diff --git a/src/lib.rs b/src/lib.rs index 0a7e02b..cef2b64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,63 @@ +//! menu-rs is a cross-platform menu creation library. +//! +//! # Creating root menus +//! +//! Before you can add submenus and menu items, you first need a root or a base menu. +//! ```no_run +//! let mut menu = Menu::new(); +//! ``` +//! +//! # Adding submens to the root menu +//! +//! Once you have a root menu you can start adding [`Submenu`]s by using [`Menu::add_submenu`]. +//! ```no_run +//! let mut menu = Menu::new(); +//! let file_menu = menu.add_submenu("File", true); +//! let edit_menu = menu.add_submenu("Edit", true); +//! ``` +//! +//! # Aadding menu items and submenus within another submenu +//! +//! Once you have a [`Submenu`] you can star creating more [`Submenu`]s or [`TextMenuItem`]s. +//! ```no_run +//! let mut menu = Menu::new(); +//! +//! let file_menu = menu.add_submenu("File", true); +//! let open_item = file_menu.add_text_item("Open", true); +//! let save_item = file_menu.add_text_item("Save", true); +//! +//! let edit_menu = menu.add_submenu("Edit", true); +//! let copy_item = file_menu.add_text_item("Copy", true); +//! let cut_item = file_menu.add_text_item("Cut", true); +//! ``` +//! +//! # Add your root menu to a Window (Windows and Linux Only) +//! +//! You can use [`Menu`] to display a top menu in a Window on Windows and Linux. +//! ```no_run +//! let mut menu = Menu::new(); +//! // --snip-- +//! #[cfg(target_os = "windows")] +//! menu.init_for_hwnd(window.hwnd() as isize); +//! #[cfg(target_os = "linux")] +//! menu.init_for_gtk_window(>k_window); +//! ``` +//! +//! # Processing menu events +//! +//! You can use [`menu_event_receiver`] to get a reference to the [`MenuEventReceiver`] +//! which you can use to listen to events when a menu item is activated +//! ```no_run +//! if let Ok(event) = menu_event_receiver().try_recv() { +//! match event.id { +//! _ if event.id == save_item.id() => { +//! println!("Save menu item activated"); +//! }, +//! _ => {} +//! } +//! } +//! ``` + use crossbeam_channel::{unbounded, Receiver, Sender}; use once_cell::sync::Lazy; @@ -6,27 +66,46 @@ 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 { +/// A type alias to the receiver of the menu events channel. +pub type MenuEventReceiver = Receiver; + +/// 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 } /// Describes a menu event emitted when a menu item is activated pub struct MenuEvent { + /// Id of the menu item which triggered this event pub id: u64, } +/// This is the root menu type to which you can add +/// more submenus and later be add to the top of a window (on Windows and Linux) +/// or used as the menubar menu (on macOS) or displayed as a popup menu. +/// +/// # Example +/// +/// ``` +/// let mut menu = Menu::new(); +/// let file_menu = menu.add_submenu("File", true); +/// let edit_menu = menu.add_submenu("Edit", true); +/// ``` pub struct Menu(platform_impl::Menu); impl Menu { + /// Creates a new root menu. pub fn new() -> Self { Self(platform_impl::Menu::new()) } + /// Creates a new [`Submenu`] whithin this menu. pub fn add_submenu(&mut self, label: impl AsRef, enabled: bool) -> Submenu { Submenu(self.0.add_submenu(label, enabled)) } + /// Adds this menu to a [`gtk::Window`]. #[cfg(target_os = "linux")] pub fn init_for_gtk_window(&self, w: &W) where @@ -35,6 +114,7 @@ impl Menu { self.0.init_for_gtk_window(w) } + /// Adds this menu to a win32 window. #[cfg(target_os = "windows")] pub fn init_for_hwnd(&self, hwnd: isize) { self.0.init_for_hwnd(hwnd) @@ -42,53 +122,67 @@ impl Menu { } #[derive(Clone)] +/// This is a submenu within another [`Submenu`] or [`Menu`]. pub struct Submenu(platform_impl::Submenu); impl Submenu { + /// Gets the submenus's current label. pub fn label(&self) -> String { self.0.label() } + /// Sets a new label for the submenu. pub fn set_label(&mut self, label: impl AsRef) { self.0.set_label(label) } + /// Gets the submenu's current state, whether enabled or not. pub fn enabled(&self) -> bool { self.0.enabled() } + /// Enables or disables the submenu pub fn set_enabled(&mut self, enabled: bool) { self.0.set_enabled(enabled) } + + /// Creates a new [`Submenu`] whithin this submenu. pub fn add_submenu(&mut self, label: impl AsRef, enabled: bool) -> Submenu { Submenu(self.0.add_submenu(label, enabled)) } + /// Creates a new [`TextMenuItem`] whithin this submenu. pub fn add_text_item(&mut self, label: impl AsRef, enabled: bool) -> TextMenuItem { TextMenuItem(self.0.add_text_item(label, enabled)) } } +/// This is a Text menu item within a [`Submenu`]. #[derive(Clone)] pub struct TextMenuItem(platform_impl::TextMenuItem); impl TextMenuItem { + /// Gets the menu item's current label. pub fn label(&self) -> String { self.0.label() } + /// Sets a new label for the menu item. pub fn set_label(&mut self, label: impl AsRef) { self.0.set_label(label) } + /// Gets the menu item's current state, whether enabled or not. pub fn enabled(&self) -> bool { self.0.enabled() } + /// Enables or disables the menu item. pub fn set_enabled(&mut self, enabled: bool) { self.0.set_enabled(enabled) } + /// Gets the unique id for this menu item. pub fn id(&self) -> u64 { self.0.id() }