瀏覽代碼

feat(core): add `ContextMenu::hpopupmenu` on Windows (#11354)

* feat(core): add `ContextMenu::hpopupmenu` on Windows

closes #11339

* Update crates/tauri/src/menu/mod.rs

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
Amr Bashir 9 月之前
父節點
當前提交
bcf279278d

+ 5 - 0
.changes/hpopupmenu.md

@@ -0,0 +1,5 @@
+---
+"tauri": "patch:feat"
+---
+
+On Windows, Add `ContextMenu::hpopupmenu` method to get the [`HMENU`](https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#HMENU) used for popups and tray icon menu.

+ 1 - 1
crates/tauri-cli/config.schema.json

@@ -1104,7 +1104,7 @@
       ]
     },
     "Capability": {
-      "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n   \"identifier\": \"main-user-files-write\",\n   \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n   \"windows\": [\n     \"main\"\n   ],\n  \"permissions\": [\n   \"core:default\",\n   \"dialog:open\",\n   {\n     \"identifier\": \"fs:allow-write-text-file\",\n     \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n   },\n  \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
+      "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n   \"identifier\": \"main-user-files-write\",\n   \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n   \"windows\": [\n     \"main\"\n   ],\n  \"permissions\": [\n   \"core:default\",\n   \"dialog:open\",\n   {\n     \"identifier\": \"fs:allow-write-text-file\",\n     \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n   },\n  ],\n  \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
       "type": "object",
       "required": [
         "identifier",

+ 1 - 1
crates/tauri-schema-generator/schemas/capability.schema.json

@@ -1,7 +1,7 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
   "title": "Capability",
-  "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n   \"identifier\": \"main-user-files-write\",\n   \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n   \"windows\": [\n     \"main\"\n   ],\n  \"permissions\": [\n   \"core:default\",\n   \"dialog:open\",\n   {\n     \"identifier\": \"fs:allow-write-text-file\",\n     \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n   },\n  \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
+  "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n   \"identifier\": \"main-user-files-write\",\n   \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n   \"windows\": [\n     \"main\"\n   ],\n  \"permissions\": [\n   \"core:default\",\n   \"dialog:open\",\n   {\n     \"identifier\": \"fs:allow-write-text-file\",\n     \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n   },\n  ],\n  \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
   "type": "object",
   "required": [
     "identifier",

+ 1 - 1
crates/tauri-schema-generator/schemas/config.schema.json

@@ -1104,7 +1104,7 @@
       ]
     },
     "Capability": {
-      "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n   \"identifier\": \"main-user-files-write\",\n   \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n   \"windows\": [\n     \"main\"\n   ],\n  \"permissions\": [\n   \"core:default\",\n   \"dialog:open\",\n   {\n     \"identifier\": \"fs:allow-write-text-file\",\n     \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n   },\n  \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
+      "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n   \"identifier\": \"main-user-files-write\",\n   \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n   \"windows\": [\n     \"main\"\n   ],\n  \"permissions\": [\n   \"core:default\",\n   \"dialog:open\",\n   {\n     \"identifier\": \"fs:allow-write-text-file\",\n     \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n   },\n  ],\n  \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
       "type": "object",
       "required": [
         "identifier",

+ 5 - 0
crates/tauri/src/menu/menu.rs

@@ -21,6 +21,11 @@ pub const WINDOW_SUBMENU_ID: &str = "__tauri_window_menu__";
 pub const HELP_SUBMENU_ID: &str = "__tauri_help_menu__";
 
 impl<R: Runtime> super::ContextMenu for Menu<R> {
+  #[cfg(target_os = "windows")]
+  fn hpopupmenu(&self) -> crate::Result<isize> {
+    run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().hpopupmenu())
+  }
+
   fn popup<T: Runtime>(&self, window: Window<T>) -> crate::Result<()> {
     self.popup_inner(window, None::<Position>)
   }

+ 9 - 0
crates/tauri/src/menu/mod.rs

@@ -732,6 +732,15 @@ pub trait IsMenuItem<R: Runtime>: sealed::IsMenuItemBase {
 ///
 /// This trait is ONLY meant to be implemented internally by the crate.
 pub trait ContextMenu: sealed::ContextMenuBase + Send + Sync {
+  /// Get the popup [`HMENU`] for this menu.
+  ///
+  /// The returned [`HMENU`] is valid as long as the [`ContextMenu`] is.
+  ///
+  /// [`HMENU`]: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#HMENU
+  #[cfg(windows)]
+  #[cfg_attr(docsrs, doc(cfg(windows)))]
+  fn hpopupmenu(&self) -> crate::Result<isize>;
+
   /// Popup this menu as a context menu on the specified window at the cursor position.
   fn popup<R: crate::Runtime>(&self, window: crate::Window<R>) -> crate::Result<()>;
 

+ 5 - 0
crates/tauri/src/menu/submenu.rs

@@ -13,6 +13,11 @@ use crate::{AppHandle, Manager, Position, Runtime, Window};
 use muda::{ContextMenu, MenuId};
 
 impl<R: Runtime> super::ContextMenu for Submenu<R> {
+  #[cfg(target_os = "windows")]
+  fn hpopupmenu(&self) -> crate::Result<isize> {
+    run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().hpopupmenu())
+  }
+
   fn popup<T: Runtime>(&self, window: Window<T>) -> crate::Result<()> {
     self.popup_inner(window, None::<Position>)
   }