Browse Source

feat(core): expose message dialog APIs, fix window.confirm, implement HasRawWindowHandle for Window, closes #2535 (#2700)

Lucas Fernandes Nogueira 3 years ago
parent
commit
e98c1af442

+ 5 - 0
.changes/api-dialog-ask-message-confirm.md

@@ -0,0 +1,5 @@
+---
+"api": patch
+---
+
+Expose `ask`, `message` and `confirm` APIs on the dialog module.

+ 5 - 0
.changes/raw-window-handle.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Implement `raw_window_handle::RawWindowHandle` for `tauri::Window` on `Windows` and `macOS`. The `tauri::api::dialog::window_parent` function was removed since now you can use the window directly.

+ 5 - 0
.changes/window-confirm.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Show `Ok/Cancel` buttons instead of `Yes/No` when executing `window.confirm`.

File diff suppressed because it is too large
+ 0 - 0
core/tauri/scripts/bundle.js


+ 1 - 1
core/tauri/scripts/core.js

@@ -347,7 +347,7 @@
       {
         __tauriModule: 'Dialog',
         message: {
-          cmd: 'askDialog',
+          cmd: 'confirmDialog',
           message: message
         }
       },

+ 37 - 67
core/tauri/src/api/dialog.rs

@@ -32,48 +32,6 @@ macro_rules! run_dialog {
   }};
 }
 
-/// Window parent definition.
-#[cfg(any(windows, target_os = "macos"))]
-#[cfg_attr(doc_cfg, doc(cfg(any(windows, target_os = "macos"))))]
-pub struct WindowParent {
-  #[cfg(windows)]
-  hwnd: *mut std::ffi::c_void,
-  #[cfg(target_os = "macos")]
-  ns_window: *mut std::ffi::c_void,
-}
-
-#[cfg(any(windows, target_os = "macos"))]
-unsafe impl raw_window_handle::HasRawWindowHandle for WindowParent {
-  #[cfg(windows)]
-  fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
-    let mut handle = raw_window_handle::windows::WindowsHandle::empty();
-    handle.hwnd = self.hwnd;
-    raw_window_handle::RawWindowHandle::Windows(handle)
-  }
-
-  #[cfg(target_os = "macos")]
-  fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
-    let mut handle = raw_window_handle::macos::MacOSHandle::empty();
-    handle.ns_window = self.ns_window;
-    raw_window_handle::RawWindowHandle::MacOS(handle)
-  }
-}
-
-#[cfg(any(windows, target_os = "macos"))]
-#[cfg_attr(doc_cfg, doc(cfg(any(windows, target_os = "macos"))))]
-#[doc(hidden)]
-pub fn window_parent<R: Runtime>(window: &Window<R>) -> crate::Result<WindowParent> {
-  #[cfg(windows)]
-  let w = WindowParent {
-    hwnd: window.hwnd()?,
-  };
-  #[cfg(target_os = "macos")]
-  let w = WindowParent {
-    ns_window: window.ns_window()?,
-  };
-  Ok(w)
-}
-
 /// The file dialog builder.
 ///
 /// Constructs file picker dialogs that can select single/multiple files or directories.
@@ -141,25 +99,24 @@ pub fn ask<R: Runtime, F: FnOnce(bool) + Send + 'static>(
   message: impl AsRef<str>,
   f: F,
 ) {
-  let title = title.as_ref().to_string();
-  let message = message.as_ref().to_string();
-  #[allow(unused_mut)]
-  let mut builder = rfd::MessageDialog::new()
-    .set_title(&title)
-    .set_description(&message)
-    .set_buttons(rfd::MessageButtons::YesNo)
-    .set_level(rfd::MessageLevel::Info);
-
-  #[cfg(any(windows, target_os = "macos"))]
-  {
-    if let Some(window) = parent_window {
-      if let Ok(parent) = window_parent(window) {
-        builder = builder.set_parent(&parent);
-      }
-    }
-  }
+  run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo, f)
+}
 
-  run_dialog!(builder.show(), f)
+/// Displays a dialog with a message and an optional title with an "ok" and a "cancel" button.
+#[allow(unused_variables)]
+pub fn confirm<R: Runtime, F: FnOnce(bool) + Send + 'static>(
+  parent_window: Option<&Window<R>>,
+  title: impl AsRef<str>,
+  message: impl AsRef<str>,
+  f: F,
+) {
+  run_message_dialog(
+    parent_window,
+    title,
+    message,
+    rfd::MessageButtons::OkCancel,
+    f,
+  )
 }
 
 /// Displays a message dialog.
@@ -168,26 +125,39 @@ pub fn message<R: Runtime>(
   parent_window: Option<&Window<R>>,
   title: impl AsRef<str>,
   message: impl AsRef<str>,
+) {
+  run_message_dialog(
+    parent_window,
+    title,
+    message,
+    rfd::MessageButtons::Ok,
+    |_| {},
+  )
+}
+
+#[allow(unused_variables)]
+fn run_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>(
+  parent_window: Option<&Window<R>>,
+  title: impl AsRef<str>,
+  message: impl AsRef<str>,
+  buttons: rfd::MessageButtons,
+  f: F,
 ) {
   let title = title.as_ref().to_string();
   let message = message.as_ref().to_string();
-  let cb = |_| {};
-
   #[allow(unused_mut)]
   let mut builder = rfd::MessageDialog::new()
     .set_title(&title)
     .set_description(&message)
-    .set_buttons(rfd::MessageButtons::Ok)
+    .set_buttons(buttons)
     .set_level(rfd::MessageLevel::Info);
 
   #[cfg(any(windows, target_os = "macos"))]
   {
     if let Some(window) = parent_window {
-      if let Ok(parent) = window_parent(window) {
-        builder = builder.set_parent(&parent);
-      }
+      builder = builder.set_parent(window);
     }
   }
 
-  run_dialog!(builder.show(), cb)
+  run_dialog!(builder.show(), f)
 }

+ 32 - 17
core/tauri/src/endpoints/dialog.rs

@@ -6,7 +6,7 @@ use super::InvokeResponse;
 #[cfg(any(dialog_open, dialog_save))]
 use crate::api::dialog::FileDialogBuilder;
 use crate::{
-  api::dialog::{ask as ask_dialog, message as message_dialog},
+  api::dialog::{ask as ask_dialog, confirm as confirm_dialog, message as message_dialog},
   runtime::Runtime,
   Window,
 };
@@ -70,6 +70,10 @@ pub enum Cmd {
     title: Option<String>,
     message: String,
   },
+  ConfirmDialog {
+    title: Option<String>,
+    message: String,
+  },
 }
 
 impl Cmd {
@@ -88,25 +92,25 @@ impl Cmd {
 
       Self::MessageDialog { message } => {
         let exe = std::env::current_exe()?;
-        let app_name = exe
-          .file_stem()
-          .expect("failed to get binary filename")
-          .to_string_lossy()
-          .to_string();
-        message_dialog(Some(&window), app_name, message);
+        message_dialog(
+          Some(&window),
+          &window.app_handle.package_info().name,
+          message,
+        );
         Ok(().into())
       }
       Self::AskDialog { title, message } => {
-        let exe = std::env::current_exe()?;
         let answer = ask(
           &window,
-          title.unwrap_or_else(|| {
-            exe
-              .file_stem()
-              .expect("failed to get binary filename")
-              .to_string_lossy()
-              .to_string()
-          }),
+          title.unwrap_or_else(|| window.app_handle.package_info().name.clone()),
+          message,
+        )?;
+        Ok(answer)
+      }
+      Self::ConfirmDialog { title, message } => {
+        let answer = confirm(
+          &window,
+          title.unwrap_or_else(|| window.app_handle.package_info().name.clone()),
           message,
         )?;
         Ok(answer)
@@ -143,7 +147,7 @@ pub fn open<R: Runtime>(
   let mut dialog_builder = FileDialogBuilder::new();
   #[cfg(any(windows, target_os = "macos"))]
   {
-    dialog_builder = dialog_builder.set_parent(&crate::api::dialog::window_parent(window)?);
+    dialog_builder = dialog_builder.set_parent(window);
   }
   if let Some(default_path) = options.default_path {
     if !default_path.exists() {
@@ -179,7 +183,7 @@ pub fn save<R: Runtime>(
   let mut dialog_builder = FileDialogBuilder::new();
   #[cfg(any(windows, target_os = "macos"))]
   {
-    dialog_builder = dialog_builder.set_parent(&crate::api::dialog::window_parent(&window)?);
+    dialog_builder = dialog_builder.set_parent(&window);
   }
   if let Some(default_path) = options.default_path {
     dialog_builder = set_default_path(dialog_builder, default_path);
@@ -203,3 +207,14 @@ pub fn ask<R: Runtime>(
   ask_dialog(Some(window), title, message, move |m| tx.send(m).unwrap());
   Ok(rx.recv().unwrap().into())
 }
+
+/// Shows a dialog with a ok/cancel message.
+pub fn confirm<R: Runtime>(
+  window: &Window<R>,
+  title: String,
+  message: String,
+) -> crate::Result<InvokeResponse> {
+  let (tx, rx) = channel();
+  confirm_dialog(Some(window), title, message, move |m| tx.send(m).unwrap());
+  Ok(rx.recv().unwrap().into())
+}

+ 20 - 0
core/tauri/src/window.rs

@@ -93,6 +93,26 @@ pub struct Window<R: Runtime> {
   pub(crate) app_handle: AppHandle<R>,
 }
 
+#[cfg(any(windows, target_os = "macos"))]
+#[cfg_attr(doc_cfg, doc(cfg(any(windows, target_os = "macos"))))]
+unsafe impl<R: Runtime> raw_window_handle::HasRawWindowHandle for Window<R> {
+  #[cfg(windows)]
+  fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
+    let mut handle = raw_window_handle::windows::WindowsHandle::empty();
+    handle.hwnd = self.hwnd().expect("failed to get window `hwnd`");
+    raw_window_handle::RawWindowHandle::Windows(handle)
+  }
+
+  #[cfg(target_os = "macos")]
+  fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
+    let mut handle = raw_window_handle::macos::MacOSHandle::empty();
+    handle.ns_window = self
+      .ns_window()
+      .expect("failed to get window's `ns_window`");
+    raw_window_handle::RawWindowHandle::MacOS(handle)
+  }
+}
+
 impl<R: Runtime> Clone for Window<R> {
   fn clone(&self) -> Self {
     Self {

+ 58 - 3
tooling/api/src/dialog.ts

@@ -77,7 +77,7 @@ async function open(
     Object.freeze(options)
   }
 
-  return invokeTauriCommand<string | string[]>({
+  return invokeTauriCommand({
     __tauriModule: 'Dialog',
     message: {
       cmd: 'openDialog',
@@ -96,7 +96,7 @@ async function save(options: SaveDialogOptions = {}): Promise<string> {
     Object.freeze(options)
   }
 
-  return invokeTauriCommand<string>({
+  return invokeTauriCommand({
     __tauriModule: 'Dialog',
     message: {
       cmd: 'saveDialog',
@@ -105,6 +105,61 @@ async function save(options: SaveDialogOptions = {}): Promise<string> {
   })
 }
 
+/**
+ * Shows a message dialog with an `Ok` button.
+ *
+ * @param {string} message The message to show.
+ *
+ * @return {Promise<void>} A promise indicating the success or failure of the operation.
+ */
+async function message(message: string): Promise<void> {
+  return invokeTauriCommand({
+    __tauriModule: 'Dialog',
+    message: {
+      cmd: 'messageDialog',
+      message
+    }
+  })
+}
+
+/**
+ * Shows a question dialog with `Yes` and `No` buttons.
+ *
+ * @param {string} message The message to show.
+ * @param {string|undefined} title The dialog's title. Defaults to the application name.
+ *
+ * @return {Promise<void>} A promise resolving to a boolean indicating whether `Yes` was clicked or not.
+ */
+async function ask(message: string, title?: string): Promise<boolean> {
+  return invokeTauriCommand({
+    __tauriModule: 'Dialog',
+    message: {
+      cmd: 'askDialog',
+      title,
+      message
+    }
+  })
+}
+
+/**
+ * Shows a question dialog with `Ok` and `Cancel` buttons.
+ *
+ * @param {string} message The message to show.
+ * @param {string|undefined} title The dialog's title. Defaults to the application name.
+ *
+ * @return {Promise<void>} A promise resolving to a boolean indicating whether `Ok` was clicked or not.
+ */
+async function confirm(message: string, title?: string): Promise<boolean> {
+  return invokeTauriCommand({
+    __tauriModule: 'Dialog',
+    message: {
+      cmd: 'confirmDialog',
+      title,
+      message
+    }
+  })
+}
+
 export type { DialogFilter, OpenDialogOptions, SaveDialogOptions }
 
-export { open, save }
+export { open, save, message, ask, confirm }

Some files were not shown because too many files changed in this diff