Pārlūkot izejas kodu

feat: implement `Default` for `Menu`, closes #2398 (#4291)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Amr Bashir 3 gadi atpakaļ
vecāks
revīzija
4c4acc3094

+ 6 - 0
.changes/default-menu-on-template.md

@@ -0,0 +1,6 @@
+---
+"cli.rs": patch
+"cli.js": patch
+---
+
+Use the default window menu in the app template.

+ 6 - 0
.changes/default-menu.md

@@ -0,0 +1,6 @@
+---
+"tauri": "patch"
+"tauri-runtime": "patch"
+---
+
+Add `Menu::os_default` which will create a menu filled with default menu items and submenus.

+ 125 - 0
core/tauri-runtime/src/menu.rs

@@ -186,6 +186,131 @@ impl Menu {
     Default::default()
   }
 
+  /// Creates a menu filled with default menu items and submenus.
+  ///
+  /// ## Platform-specific:
+  ///
+  /// - **Windows**:
+  ///   - File
+  ///     - CloseWindow
+  ///     - Quit
+  ///   - Edit
+  ///     - Cut
+  ///     - Copy
+  ///     - Paste
+  ///   - Window
+  ///     - Minimize
+  ///     - CloseWindow
+  ///
+  /// - **Linux**:
+  ///   - File
+  ///     - CloseWindow
+  ///     - Quit
+  ///   - Window
+  ///     - Minimize
+  ///     - CloseWindow
+  ///
+  /// - **macOS**:
+  ///   - App
+  ///     - About
+  ///     - Separator
+  ///     - Services
+  ///     - Separator
+  ///     - Hide
+  ///     - HideOthers
+  ///     - ShowAll
+  ///     - Separator
+  ///     - Quit
+  ///   - File
+  ///     - CloseWindow
+  ///   - Edit
+  ///     - Undo
+  ///     - Redo
+  ///     - Separator
+  ///     - Cut
+  ///     - Copy
+  ///     - Paste
+  ///     - SelectAll
+  ///   - View
+  ///     - EnterFullScreen
+  ///   - Window
+  ///     - Minimize
+  ///     - Zoom
+  ///     - Separator
+  ///     - CloseWindow
+  pub fn os_default(#[allow(unused)] app_name: &str) -> Self {
+    let mut menu = Menu::new();
+    #[cfg(target_os = "macos")]
+    {
+      menu = menu.add_submenu(Submenu::new(
+        app_name,
+        Menu::new()
+          .add_native_item(MenuItem::About(
+            app_name.to_string(),
+            AboutMetadata::default(),
+          ))
+          .add_native_item(MenuItem::Separator)
+          .add_native_item(MenuItem::Services)
+          .add_native_item(MenuItem::Separator)
+          .add_native_item(MenuItem::Hide)
+          .add_native_item(MenuItem::HideOthers)
+          .add_native_item(MenuItem::ShowAll)
+          .add_native_item(MenuItem::Separator)
+          .add_native_item(MenuItem::Quit),
+      ));
+    }
+
+    let mut file_menu = Menu::new();
+    file_menu = file_menu.add_native_item(MenuItem::CloseWindow);
+    #[cfg(not(target_os = "macos"))]
+    {
+      file_menu = file_menu.add_native_item(MenuItem::Quit);
+    }
+    menu = menu.add_submenu(Submenu::new("File", file_menu));
+
+    #[cfg(not(target_os = "linux"))]
+    let mut edit_menu = Menu::new();
+    #[cfg(target_os = "macos")]
+    {
+      edit_menu = edit_menu.add_native_item(MenuItem::Undo);
+      edit_menu = edit_menu.add_native_item(MenuItem::Redo);
+      edit_menu = edit_menu.add_native_item(MenuItem::Separator);
+    }
+    #[cfg(not(target_os = "linux"))]
+    {
+      edit_menu = edit_menu.add_native_item(MenuItem::Cut);
+      edit_menu = edit_menu.add_native_item(MenuItem::Copy);
+      edit_menu = edit_menu.add_native_item(MenuItem::Paste);
+    }
+    #[cfg(target_os = "macos")]
+    {
+      edit_menu = edit_menu.add_native_item(MenuItem::SelectAll);
+    }
+    #[cfg(not(target_os = "linux"))]
+    {
+      menu = menu.add_submenu(Submenu::new("Edit", edit_menu));
+    }
+    #[cfg(target_os = "macos")]
+    {
+      menu = menu.add_submenu(Submenu::new(
+        "View",
+        Menu::new().add_native_item(MenuItem::EnterFullScreen),
+      ));
+    }
+
+    let mut window_menu = Menu::new();
+    window_menu = window_menu.add_native_item(MenuItem::Minimize);
+    #[cfg(target_os = "macos")]
+    {
+      window_menu = window_menu.add_native_item(MenuItem::Zoom);
+      window_menu = window_menu.add_native_item(MenuItem::Separator);
+    }
+    window_menu = window_menu.add_native_item(MenuItem::CloseWindow);
+    menu = menu.add_submenu(Submenu::new("Window", window_menu));
+
+    menu
+  }
+
   /// Creates a new window menu with the given items.
   ///
   /// # Examples

+ 8 - 8
examples/api/src-tauri/Cargo.lock

@@ -359,9 +359,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "3.2.3"
+version = "3.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1df386a2d0f35bdefc0642fd8bcb2cd28243959f028abfd22fbade6f7d30980e"
+checksum = "6d20de3739b4fb45a17837824f40aa1769cc7655d7a83e68739a77fe7b30c87a"
 dependencies = [
  "atty",
  "bitflags",
@@ -3099,7 +3099,7 @@ dependencies = [
 
 [[package]]
 name = "tauri"
-version = "1.0.0-rc.15"
+version = "1.0.0-rc.16"
 dependencies = [
  "anyhow",
  "attohttpc",
@@ -3160,7 +3160,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "1.0.0-rc.13"
+version = "1.0.0-rc.14"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -3174,7 +3174,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "base64",
  "brotli",
@@ -3195,7 +3195,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "heck 0.4.0",
  "proc-macro2",
@@ -3207,7 +3207,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "gtk",
  "http",
@@ -3224,7 +3224,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "cocoa",
  "gtk",

+ 3 - 3
examples/commands/main.rs

@@ -157,7 +157,9 @@ fn borrow_cmd_async(argument: &str) -> &str {
 }
 
 fn main() {
+  let context = tauri::generate_context!("../../examples/commands/tauri.conf.json");
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .manage(MyState {
       value: 0,
       label: "Tauri!".into(),
@@ -187,8 +189,6 @@ fn main() {
       future_simple_command_with_result,
       async_stateful_command_with_result,
     ])
-    .run(tauri::generate_context!(
-      "../../examples/commands/tauri.conf.json"
-    ))
+    .run(context)
     .expect("error while running tauri application");
 }

+ 3 - 3
examples/helloworld/main.rs

@@ -8,9 +8,9 @@
 )]
 
 fn main() {
+  let context = tauri::generate_context!("../../examples/helloworld/tauri.conf.json");
   tauri::Builder::default()
-    .run(tauri::generate_context!(
-      "../../examples/helloworld/tauri.conf.json"
-    ))
+    .menu(tauri::Menu::os_default(&context.package_info().name))
+    .run(context)
     .expect("error while running tauri application");
 }

+ 3 - 3
examples/isolation/main.rs

@@ -21,10 +21,10 @@ fn main() {
 
 #[cfg(feature = "isolation")]
 fn main() {
+  let context = tauri::generate_context!("../../examples/isolation/tauri.conf.json");
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .invoke_handler(tauri::generate_handler![ping])
-    .run(tauri::generate_context!(
-      "../../examples/isolation/tauri.conf.json"
-    ))
+    .run(context)
     .expect("error while running tauri application");
 }

+ 3 - 3
examples/multiwindow/main.rs

@@ -10,7 +10,9 @@
 use tauri::WindowBuilder;
 
 fn main() {
+  let context = tauri::generate_context!("../../examples/multiwindow/tauri.conf.json");
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .on_page_load(|window, _payload| {
       let label = window.label().to_string();
       window.listen("clicked".to_string(), move |_payload| {
@@ -27,8 +29,6 @@ fn main() {
       .build()?;
       Ok(())
     })
-    .run(tauri::generate_context!(
-      "../../examples/multiwindow/tauri.conf.json"
-    ))
+    .run(context)
     .expect("failed to run tauri application");
 }

+ 3 - 3
examples/navigation/main.rs

@@ -8,9 +8,9 @@
 )]
 
 fn main() {
+  let context = tauri::generate_context!("../../examples/navigation/tauri.conf.json");
   tauri::Builder::default()
-    .run(tauri::generate_context!(
-      "../../examples/navigation/tauri.conf.json"
-    ))
+    .menu(tauri::Menu::os_default(&context.package_info().name))
+    .run(context)
     .expect("error while running tauri application");
 }

+ 3 - 3
examples/parent-window/main.rs

@@ -24,7 +24,9 @@ async fn create_child_window(id: String, window: Window) {
 }
 
 fn main() {
+  let context = tauri::generate_context!("../../examples/parent-window/tauri.conf.json");
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .on_page_load(|window, _payload| {
       let label = window.label().to_string();
       window.listen("clicked".to_string(), move |_payload| {
@@ -39,8 +41,6 @@ fn main() {
         .build()?;
       Ok(())
     })
-    .run(tauri::generate_context!(
-      "../../examples/parent-window/tauri.conf.json"
-    ))
+    .run(context)
     .expect("failed to run tauri application");
 }

+ 6 - 6
examples/resources/src-tauri/Cargo.lock

@@ -2415,7 +2415,7 @@ dependencies = [
 
 [[package]]
 name = "tauri"
-version = "1.0.0-rc.15"
+version = "1.0.0-rc.16"
 dependencies = [
  "anyhow",
  "cocoa",
@@ -2461,7 +2461,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "1.0.0-rc.13"
+version = "1.0.0-rc.14"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -2476,7 +2476,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "base64",
  "brotli",
@@ -2497,7 +2497,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "heck 0.4.0",
  "proc-macro2",
@@ -2509,7 +2509,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "gtk",
  "http",
@@ -2526,7 +2526,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "cocoa",
  "gtk",

+ 4 - 1
examples/resources/src-tauri/src/main.rs

@@ -13,7 +13,10 @@ fn main() {
     Manager,
   };
 
+  let context = tauri::generate_context!();
+
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .setup(move |app| {
       let window = app.get_window("main").unwrap();
       let script_path = app
@@ -40,6 +43,6 @@ fn main() {
 
       Ok(())
     })
-    .run(tauri::generate_context!())
+    .run(context)
     .expect("error while running tauri application");
 }

+ 6 - 6
examples/sidecar/src-tauri/Cargo.lock

@@ -2415,7 +2415,7 @@ dependencies = [
 
 [[package]]
 name = "tauri"
-version = "1.0.0-rc.15"
+version = "1.0.0-rc.16"
 dependencies = [
  "anyhow",
  "cocoa",
@@ -2461,7 +2461,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "1.0.0-rc.13"
+version = "1.0.0-rc.14"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -2476,7 +2476,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "base64",
  "brotli",
@@ -2497,7 +2497,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "heck 0.4.0",
  "proc-macro2",
@@ -2509,7 +2509,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "gtk",
  "http",
@@ -2526,7 +2526,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "cocoa",
  "gtk",

+ 3 - 1
examples/sidecar/src-tauri/src/main.rs

@@ -13,7 +13,9 @@ use tauri::{
 };
 
 fn main() {
+  let context = tauri::generate_context!();
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .setup(|app| {
       let window = app.get_window("main").unwrap();
       tauri::async_runtime::spawn(async move {
@@ -39,6 +41,6 @@ fn main() {
 
       Ok(())
     })
-    .run(tauri::generate_context!())
+    .run(context)
     .expect("error while running tauri application");
 }

+ 6 - 6
examples/splashscreen/main.rs

@@ -17,7 +17,9 @@ mod rust {
   fn close_splashscreen() {}
 
   pub fn main() {
+    let context = tauri::generate_context!("../../examples/splashscreen/tauri.conf.json");
     tauri::Builder::default()
+      .menu(tauri::Menu::os_default(&context.package_info().name))
       .setup(|app| {
         let splashscreen_window = app.get_window("splashscreen").unwrap();
         let main_window = app.get_window("main").unwrap();
@@ -34,9 +36,7 @@ mod rust {
         Ok(())
       })
       .invoke_handler(tauri::generate_handler![close_splashscreen])
-      .run(tauri::generate_context!(
-        "../../examples/splashscreen/tauri.conf.json"
-      ))
+      .run(context)
       .expect("failed to run app");
   }
 }
@@ -64,7 +64,9 @@ mod ui {
   }
 
   pub fn main() {
+    let context = tauri::generate_context!("../../examples/splashscreen/tauri.conf.json");
     tauri::Builder::default()
+      .menu(tauri::Menu::os_default(&context.package_info().name))
       .setup(|app| {
         // set the splashscreen and main windows to be globally available with the tauri state API
         app.manage(SplashscreenWindow(Arc::new(Mutex::new(
@@ -76,9 +78,7 @@ mod ui {
         Ok(())
       })
       .invoke_handler(tauri::generate_handler![close_splashscreen])
-      .run(tauri::generate_context!(
-        "../../examples/splashscreen/tauri.conf.json"
-      ))
+      .run(context)
       .expect("error while running tauri application");
   }
 }

+ 3 - 3
examples/state/main.rs

@@ -69,7 +69,9 @@ fn db_read(key: String, db: State<'_, Database>) -> Option<String> {
 }
 
 fn main() {
+  let context = tauri::generate_context!("../../examples/state/tauri.conf.json");
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .manage(Counter(AtomicUsize::new(0)))
     .manage(Database(Default::default()))
     .manage(Connection(Default::default()))
@@ -81,8 +83,6 @@ fn main() {
       disconnect,
       connection_send
     ])
-    .run(tauri::generate_context!(
-      "../../examples/state/tauri.conf.json"
-    ))
+    .run(context)
     .expect("error while running tauri application");
 }

+ 4 - 3
examples/streaming/main.rs

@@ -38,7 +38,10 @@ fn main() {
     assert!(video_file.exists());
   }
 
+  let context = tauri::generate_context!("../../examples/streaming/tauri.conf.json");
+
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .register_uri_scheme_protocol("stream", move |_app, request| {
       // prepare our response
       let mut response = ResponseBuilder::new();
@@ -112,8 +115,6 @@ fn main() {
 
       response.mimetype("video/mp4").status(status_code).body(buf)
     })
-    .run(tauri::generate_context!(
-      "../../examples/streaming/tauri.conf.json"
-    ))
+    .run(context)
     .expect("error while running tauri application");
 }

+ 6 - 6
examples/tauri-dynamic-lib/src-tauri/Cargo.lock

@@ -2385,7 +2385,7 @@ dependencies = [
 
 [[package]]
 name = "tauri"
-version = "1.0.0-rc.15"
+version = "1.0.0-rc.16"
 dependencies = [
  "anyhow",
  "cocoa",
@@ -2428,7 +2428,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "1.0.0-rc.13"
+version = "1.0.0-rc.14"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -2443,7 +2443,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "base64",
  "brotli",
@@ -2463,7 +2463,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "heck 0.4.0",
  "proc-macro2",
@@ -2475,7 +2475,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "gtk",
  "http",
@@ -2492,7 +2492,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "cocoa",
  "gtk",

+ 4 - 4
examples/tauri-dynamic-lib/src-tauri/src/lib.rs

@@ -11,10 +11,10 @@
 )]
 
 #[no_mangle]
-pub extern fn run_tauri() {
+pub extern "C" fn run_tauri() {
+  let context = tauri::generate_context!("./tauri.conf.json");
   tauri::Builder::default()
-    .run(tauri::generate_context!(
-      "./tauri.conf.json"
-    ))
+    .menu(tauri::Menu::os_default(&context.package_info().name))
+    .run(context)
     .expect("error while running tauri application");
 }

+ 6 - 6
examples/updater/src-tauri/Cargo.lock

@@ -2576,7 +2576,7 @@ dependencies = [
 
 [[package]]
 name = "tauri"
-version = "1.0.0-rc.15"
+version = "1.0.0-rc.16"
 dependencies = [
  "anyhow",
  "attohttpc",
@@ -2625,7 +2625,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "1.0.0-rc.13"
+version = "1.0.0-rc.14"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -2640,7 +2640,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "base64",
  "brotli",
@@ -2660,7 +2660,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "1.0.0-rc.9"
+version = "1.0.0-rc.10"
 dependencies = [
  "heck 0.4.0",
  "proc-macro2",
@@ -2672,7 +2672,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "gtk",
  "http",
@@ -2689,7 +2689,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "cocoa",
  "gtk",

+ 3 - 1
examples/updater/src-tauri/src/main.rs

@@ -13,8 +13,10 @@ fn my_custom_command(argument: String) {
 }
 
 fn main() {
+  let context = tauri::generate_context!();
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .invoke_handler(tauri::generate_handler![my_custom_command])
-    .run(tauri::generate_context!())
+    .run(context)
     .expect("error while running tauri application");
 }

+ 3 - 1
tooling/cli/templates/app/src-tauri/src/main.rs

@@ -4,7 +4,9 @@
 )]
 
 fn main() {
+  let context = tauri::generate_context!();
   tauri::Builder::default()
-    .run(tauri::generate_context!())
+    .menu(tauri::Menu::os_default(&context.package_info().name))
+    .run(context)
     .expect("error while running tauri application");
 }

+ 3 - 1
tooling/cli/templates/plugin/backend/examples/vanilla/src-tauri/src/main.rs

@@ -4,8 +4,10 @@
 )]
 
 fn main() {
+  let context = tauri::generate_context!();
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .plugin(tauri_plugin_{{ plugin_name_snake_case }}::init())
-    .run(tauri::generate_context!())
+    .run(context)
     .expect("error while running tauri application");
 }

+ 3 - 1
tooling/cli/templates/plugin/with-api/examples/svelte-app/src-tauri/src/main.rs

@@ -4,8 +4,10 @@
 )]
 
 fn main() {
+  let context = tauri::generate_context!();
   tauri::Builder::default()
+    .menu(tauri::Menu::os_default(&context.package_info().name))
     .plugin(tauri_plugin_{{ plugin_name_snake_case }}::init())
-    .run(tauri::generate_context!())
+    .run(context)
     .expect("failed to run app");
 }