diff --git a/.changes/menu-item-builder-generics.md b/.changes/menu-item-builder-generics.md new file mode 100644 index 000000000..2530340e6 --- /dev/null +++ b/.changes/menu-item-builder-generics.md @@ -0,0 +1,5 @@ +--- +"tauri": "patch:breaking" +--- + +Added `` generic on `MenuItemBuilder`, `CheckMenuItemBuilder` and `IconMenuItemBuilder` and removed ite from `build` method. diff --git a/.changes/menu-item-builders-handler.md b/.changes/menu-item-builders-handler.md new file mode 100644 index 000000000..ef49fb08f --- /dev/null +++ b/.changes/menu-item-builders-handler.md @@ -0,0 +1,6 @@ +--- +"tauri": "patch:feat" +--- + +Add `handler` method on `MenuItemBuilder`, `CheckMenuItemBuilder` and `IconMenuItemBuilder`. + diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 8765c3a0e..8276b2460 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -1860,7 +1860,13 @@ fn on_event_loop_event( { listener(app_handle, e.clone()); } - for (label, listener) in &*app_handle.manager.menu.event_listeners.lock().unwrap() { + for (label, listener) in &*app_handle + .manager + .menu + .window_event_listeners + .lock() + .unwrap() + { if let Some(w) = app_handle.manager().get_window(label) { listener(&w, e.clone()); } diff --git a/core/tauri/src/manager/menu.rs b/core/tauri/src/manager/menu.rs index 3536bb48c..b0ed0c4fb 100644 --- a/core/tauri/src/manager/menu.rs +++ b/core/tauri/src/manager/menu.rs @@ -24,7 +24,11 @@ pub struct MenuManager { /// Menu event listeners to all windows. pub global_event_listeners: Mutex>>>, /// Menu event listeners to specific windows. - pub event_listeners: Mutex>>>, + pub window_event_listeners: + Mutex>>>, + /// Menu event listeners to specific windows. + pub item_event_listeners: + Mutex>>>, } impl MenuManager { @@ -95,4 +99,16 @@ impl MenuManager { .unwrap() .push(Box::new(handler)); } + + pub fn on_menu_item_event, MenuEvent) + Send + Sync + 'static>( + &self, + id: MenuId, + handler: F, + ) { + self + .item_event_listeners + .lock() + .unwrap() + .insert(id, Box::new(handler)); + } } diff --git a/core/tauri/src/manager/mod.rs b/core/tauri/src/manager/mod.rs index 283656826..e4389a7b3 100644 --- a/core/tauri/src/manager/mod.rs +++ b/core/tauri/src/manager/mod.rs @@ -267,7 +267,8 @@ impl AppManager { menus: Default::default(), menu: Default::default(), global_event_listeners: Default::default(), - event_listeners: Mutex::new(window_menu_event_listeners), + window_event_listeners: Mutex::new(window_menu_event_listeners), + item_event_listeners: Default::default(), }, plugins: Mutex::new(plugins), listeners: Listeners::default(), diff --git a/core/tauri/src/menu/builders/check.rs b/core/tauri/src/menu/builders/check.rs index 4120a1a1a..87e1765db 100644 --- a/core/tauri/src/menu/builders/check.rs +++ b/core/tauri/src/menu/builders/check.rs @@ -2,18 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{menu::CheckMenuItem, menu::MenuId, Manager, Runtime}; +use crate::{ + menu::{CheckMenuItem, MenuEvent, MenuId}, + Manager, Runtime, +}; /// A builder type for [`CheckMenuItem`] -pub struct CheckMenuItemBuilder { +pub struct CheckMenuItemBuilder { id: Option, text: String, enabled: bool, checked: bool, accelerator: Option, + handler: Option, MenuEvent) + Send + Sync + 'static>>, } -impl CheckMenuItemBuilder { +impl CheckMenuItemBuilder { /// Create a new menu item builder. /// /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic @@ -25,6 +29,7 @@ impl CheckMenuItemBuilder { enabled: true, checked: true, accelerator: None, + handler: None, } } @@ -39,6 +44,7 @@ impl CheckMenuItemBuilder { enabled: true, checked: true, accelerator: None, + handler: None, } } @@ -66,9 +72,18 @@ impl CheckMenuItemBuilder { self } + /// Set a handler to be called when this item is activated. + pub fn handler, MenuEvent) + Send + Sync + 'static>( + mut self, + handler: F, + ) -> Self { + self.handler.replace(Box::new(handler)); + self + } + /// Build the menu item - pub fn build>(self, manager: &M) -> crate::Result> { - if let Some(id) = self.id { + pub fn build>(self, manager: &M) -> crate::Result> { + let i = if let Some(id) = self.id { CheckMenuItem::with_id( manager, id, @@ -76,7 +91,7 @@ impl CheckMenuItemBuilder { self.enabled, self.checked, self.accelerator, - ) + )? } else { CheckMenuItem::new( manager, @@ -84,7 +99,21 @@ impl CheckMenuItemBuilder { self.enabled, self.checked, self.accelerator, - ) - } + )? + }; + + if let Some(handler) = self.handler { + let i = i.clone(); + manager + .manager() + .menu + .on_menu_item_event(i.id().clone(), move |_app, e| { + if e.id == i.id() { + handler(&i, e) + } + }); + }; + + Ok(i) } } diff --git a/core/tauri/src/menu/builders/icon.rs b/core/tauri/src/menu/builders/icon.rs index fea7e00cd..47a5bbf89 100644 --- a/core/tauri/src/menu/builders/icon.rs +++ b/core/tauri/src/menu/builders/icon.rs @@ -4,21 +4,22 @@ use crate::{ image::Image, - menu::{IconMenuItem, MenuId, NativeIcon}, + menu::{IconMenuItem, MenuEvent, MenuId, NativeIcon}, Manager, Runtime, }; /// A builder type for [`IconMenuItem`] -pub struct IconMenuItemBuilder<'a> { +pub struct IconMenuItemBuilder<'a, R: Runtime> { id: Option, text: String, enabled: bool, icon: Option>, native_icon: Option, accelerator: Option, + handler: Option, MenuEvent) + Send + Sync + 'static>>, } -impl<'a> IconMenuItemBuilder<'a> { +impl<'a, R: Runtime> IconMenuItemBuilder<'a, R> { /// Create a new menu item builder. /// /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic @@ -31,6 +32,7 @@ impl<'a> IconMenuItemBuilder<'a> { icon: None, native_icon: None, accelerator: None, + handler: None, } } @@ -46,6 +48,7 @@ impl<'a> IconMenuItemBuilder<'a> { icon: None, native_icon: None, accelerator: None, + handler: None, } } @@ -87,9 +90,18 @@ impl<'a> IconMenuItemBuilder<'a> { self } + /// Set a handler to be called when this item is activated. + pub fn handler, MenuEvent) + Send + Sync + 'static>( + mut self, + handler: F, + ) -> Self { + self.handler.replace(Box::new(handler)); + self + } + /// Build the menu item - pub fn build>(self, manager: &M) -> crate::Result> { - if self.icon.is_some() { + pub fn build>(self, manager: &M) -> crate::Result> { + let i = if self.icon.is_some() { if let Some(id) = self.id { IconMenuItem::with_id( manager, @@ -98,7 +110,7 @@ impl<'a> IconMenuItemBuilder<'a> { self.enabled, self.icon, self.accelerator, - ) + )? } else { IconMenuItem::new( manager, @@ -106,7 +118,7 @@ impl<'a> IconMenuItemBuilder<'a> { self.enabled, self.icon, self.accelerator, - ) + )? } } else if let Some(id) = self.id { IconMenuItem::with_id_and_native_icon( @@ -116,7 +128,7 @@ impl<'a> IconMenuItemBuilder<'a> { self.enabled, self.native_icon, self.accelerator, - ) + )? } else { IconMenuItem::with_native_icon( manager, @@ -124,7 +136,21 @@ impl<'a> IconMenuItemBuilder<'a> { self.enabled, self.native_icon, self.accelerator, - ) - } + )? + }; + + if let Some(handler) = self.handler { + let i = i.clone(); + manager + .manager() + .menu + .on_menu_item_event(i.id().clone(), move |_app, e| { + if e.id == i.id() { + handler(&i, e) + } + }); + }; + + Ok(i) } } diff --git a/core/tauri/src/menu/builders/normal.rs b/core/tauri/src/menu/builders/normal.rs index 7297e5a2d..aecdf299d 100644 --- a/core/tauri/src/menu/builders/normal.rs +++ b/core/tauri/src/menu/builders/normal.rs @@ -2,17 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{menu::MenuId, menu::MenuItem, Manager, Runtime}; +use crate::{ + menu::{MenuEvent, MenuId, MenuItem}, + Manager, Runtime, +}; /// A builder type for [`MenuItem`] -pub struct MenuItemBuilder { +pub struct MenuItemBuilder { id: Option, text: String, enabled: bool, accelerator: Option, + handler: Option, MenuEvent) + Send + Sync + 'static>>, } -impl MenuItemBuilder { +impl MenuItemBuilder { /// Create a new menu item builder. /// /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic @@ -23,6 +27,7 @@ impl MenuItemBuilder { text: text.as_ref().to_string(), enabled: true, accelerator: None, + handler: None, } } @@ -36,6 +41,7 @@ impl MenuItemBuilder { text: text.as_ref().to_string(), enabled: true, accelerator: None, + handler: None, } } @@ -57,12 +63,35 @@ impl MenuItemBuilder { self } + /// Set a handler to be called when this item is activated. + pub fn handler, MenuEvent) + Send + Sync + 'static>( + mut self, + handler: F, + ) -> Self { + self.handler.replace(Box::new(handler)); + self + } + /// Build the menu item - pub fn build>(self, manager: &M) -> crate::Result> { - if let Some(id) = self.id { - MenuItem::with_id(manager, id, self.text, self.enabled, self.accelerator) + pub fn build>(self, manager: &M) -> crate::Result> { + let i = if let Some(id) = self.id { + MenuItem::with_id(manager, id, self.text, self.enabled, self.accelerator)? } else { - MenuItem::new(manager, self.text, self.enabled, self.accelerator) - } + MenuItem::new(manager, self.text, self.enabled, self.accelerator)? + }; + + if let Some(handler) = self.handler { + let i = i.clone(); + manager + .manager() + .menu + .on_menu_item_event(i.id().clone(), move |_app, e| { + if e.id == i.id() { + handler(&i, e) + } + }); + }; + + Ok(i) } } diff --git a/core/tauri/src/window/mod.rs b/core/tauri/src/window/mod.rs index 9df2fc711..4e18b7475 100644 --- a/core/tauri/src/window/mod.rs +++ b/core/tauri/src/window/mod.rs @@ -1105,7 +1105,7 @@ tauri::Builder::default() self .manager .menu - .event_listeners + .window_event_listeners .lock() .unwrap() .insert(self.label().to_string(), Box::new(f));