瀏覽代碼

feat: add handler method on menu item builders

closes #9060
amrbashir 1 年之前
父節點
當前提交
c8065744d0

+ 5 - 0
.changes/menu-item-builder-generics.md

@@ -0,0 +1,5 @@
+---
+"tauri": "patch:breaking"
+---
+
+Added `<R: Runtime>` generic on `MenuItemBuilder`, `CheckMenuItemBuilder` and `IconMenuItemBuilder` and removed ite from `build` method.

+ 6 - 0
.changes/menu-item-builders-handler.md

@@ -0,0 +1,6 @@
+---
+"tauri": "patch:feat"
+---
+
+Add `handler` method on `MenuItemBuilder`, `CheckMenuItemBuilder` and `IconMenuItemBuilder`.
+

+ 7 - 1
core/tauri/src/app.rs

@@ -1860,7 +1860,13 @@ fn on_event_loop_event<R: Runtime>(
           {
             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());
             }

+ 17 - 1
core/tauri/src/manager/menu.rs

@@ -24,7 +24,11 @@ pub struct MenuManager<R: Runtime> {
   /// Menu event listeners to all windows.
   pub global_event_listeners: Mutex<Vec<crate::app::GlobalMenuEventListener<AppHandle<R>>>>,
   /// Menu event listeners to specific windows.
-  pub event_listeners: Mutex<HashMap<String, crate::app::GlobalMenuEventListener<Window<R>>>>,
+  pub window_event_listeners:
+    Mutex<HashMap<String, crate::app::GlobalMenuEventListener<Window<R>>>>,
+  /// Menu event listeners to specific windows.
+  pub item_event_listeners:
+    Mutex<HashMap<MenuId, crate::app::GlobalMenuEventListener<AppHandle<R>>>>,
 }
 
 impl<R: Runtime> MenuManager<R> {
@@ -95,4 +99,16 @@ impl<R: Runtime> MenuManager<R> {
       .unwrap()
       .push(Box::new(handler));
   }
+
+  pub fn on_menu_item_event<F: Fn(&AppHandle<R>, MenuEvent) + Send + Sync + 'static>(
+    &self,
+    id: MenuId,
+    handler: F,
+  ) {
+    self
+      .item_event_listeners
+      .lock()
+      .unwrap()
+      .insert(id, Box::new(handler));
+  }
 }

+ 2 - 1
core/tauri/src/manager/mod.rs

@@ -267,7 +267,8 @@ impl<R: Runtime> AppManager<R> {
         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(),

+ 37 - 8
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<R: Runtime> {
   id: Option<MenuId>,
   text: String,
   enabled: bool,
   checked: bool,
   accelerator: Option<String>,
+  handler: Option<Box<dyn Fn(&CheckMenuItem<R>, MenuEvent) + Send + Sync + 'static>>,
 }
 
-impl CheckMenuItemBuilder {
+impl<R: Runtime> CheckMenuItemBuilder<R> {
   /// 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<F: Fn(&CheckMenuItem<R>, MenuEvent) + Send + Sync + 'static>(
+    mut self,
+    handler: F,
+  ) -> Self {
+    self.handler.replace(Box::new(handler));
+    self
+  }
+
   /// Build the menu item
-  pub fn build<R: Runtime, M: Manager<R>>(self, manager: &M) -> crate::Result<CheckMenuItem<R>> {
-    if let Some(id) = self.id {
+  pub fn build<M: Manager<R>>(self, manager: &M) -> crate::Result<CheckMenuItem<R>> {
+    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)
   }
 }

+ 36 - 10
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<MenuId>,
   text: String,
   enabled: bool,
   icon: Option<Image<'a>>,
   native_icon: Option<NativeIcon>,
   accelerator: Option<String>,
+  handler: Option<Box<dyn Fn(&IconMenuItem<R>, 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<F: Fn(&IconMenuItem<R>, MenuEvent) + Send + Sync + 'static>(
+    mut self,
+    handler: F,
+  ) -> Self {
+    self.handler.replace(Box::new(handler));
+    self
+  }
+
   /// Build the menu item
-  pub fn build<R: Runtime, M: Manager<R>>(self, manager: &M) -> crate::Result<IconMenuItem<R>> {
-    if self.icon.is_some() {
+  pub fn build<M: Manager<R>>(self, manager: &M) -> crate::Result<IconMenuItem<R>> {
+    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)
   }
 }

+ 37 - 8
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<R: Runtime> {
   id: Option<MenuId>,
   text: String,
   enabled: bool,
   accelerator: Option<String>,
+  handler: Option<Box<dyn Fn(&MenuItem<R>, MenuEvent) + Send + Sync + 'static>>,
 }
 
-impl MenuItemBuilder {
+impl<R: Runtime> MenuItemBuilder<R> {
   /// 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<F: Fn(&MenuItem<R>, MenuEvent) + Send + Sync + 'static>(
+    mut self,
+    handler: F,
+  ) -> Self {
+    self.handler.replace(Box::new(handler));
+    self
+  }
+
   /// Build the menu item
-  pub fn build<R: Runtime, M: Manager<R>>(self, manager: &M) -> crate::Result<MenuItem<R>> {
-    if let Some(id) = self.id {
-      MenuItem::with_id(manager, id, self.text, self.enabled, self.accelerator)
+  pub fn build<M: Manager<R>>(self, manager: &M) -> crate::Result<MenuItem<R>> {
+    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)
   }
 }

+ 1 - 1
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));