Merge pull request #5 from bicarlsen/dialog

Added `dialog` module features.
This commit is contained in:
Jonas Kruckenberg 2022-11-13 21:46:37 +01:00 committed by GitHub
commit 198595b3ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 397 additions and 3 deletions

View file

@ -20,9 +20,10 @@ wasm-bindgen-test = "0.3.33"
tauri-sys = { path = ".", features = ["all"] }
[features]
all = ["app", "clipboard", "event", "mocks", "tauri"]
all = ["app", "clipboard", "dialog", "event", "mocks", "tauri"]
app = ["dep:semver"]
clipboard = []
dialog = []
event = []
mocks = []
tauri = ["dep:url"]
tauri = ["dep:url"]

View file

@ -43,6 +43,7 @@ All modules are gated by accordingly named Cargo features. It is recommended you
- **all**: Enables all modules.
- **app**: Enables the `app` module.
- **clipboard**: Enables the `clipboard` module.
- **dialog**: Enables the `dialog` module.
- **event**: Enables the `event` module.
- **mocks**: Enables the `mocks` module.
- **tauri**: Enables the `tauri` module.
@ -54,7 +55,7 @@ These API bindings are not completely on-par with `@tauri-apps/api` yet, but her
- [x] `app`
- [ ] `cli`
- [x] `clipboard`
- [ ] `dialog`
- [x] `dialog`
- [x] `event`
- [ ] `fs`
- [ ] `global_shortcut`

118
dist/dialog.js vendored Normal file
View file

@ -0,0 +1,118 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once3 = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once3) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/helpers/dialog.ts
async function ask(message, options) {
return await invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "askDialog",
message: message.toString(),
title: options?.title?.toString(),
type: options?.type
}
});
}
async function confirm(message, options) {
return await invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "confirmDialog",
message: message.toString(),
title: options?.title?.toString(),
type: options?.type
}
});
}
async function open(options) {
if(!options) {
options = {multiple: false};
}
return await invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "openDialog",
options
}
});
}
async function open_multiple(options) {
if(!options) {
options = {multiple: true};
}
return await invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "openDialog",
options
}
});
}
async function message(message, options) {
await invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "messageDialog",
message: message.toString(),
title: options?.title?.toString(),
type: options?.type
}
});
}
async function save(options) {
return await invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "saveDialog",
options
}
});
}
export {
ask,
confirm,
open,
open_multiple,
message,
save
}

272
src/dialog.rs Normal file
View file

@ -0,0 +1,272 @@
//! User interaction with the file system using dialog boxes.
//!
//! # Example
//!
//! ```rust,no_run
//! use tauri_api::dialog::open;
//!
//! let path = open(None).await;
//! ```
use serde::Serialize;
use std::path::PathBuf;
/// Extension filter for the file dialog.
///
/// # Example
///
/// ```rust,no_run
/// let filter = DialogFilter {
/// extension: vec![".jpg", ".jpeg", ".png", ".bmp"],
/// name: "images",
/// };
/// ```
#[derive(Serialize)]
pub struct DialogFilter {
/// Extensions to filter, without a `.` prefix.
pub extensions: Vec<String>,
/// Filter name
pub name: String,
}
/// Types of a [`message`] dialog.
#[derive(Serialize)]
pub enum MessageDialogType {
Error,
Info,
Warning,
}
/// Options for the [`message`] dialog.
#[derive(Serialize)]
pub struct MessageDialogOptions {
/// The title of the dialog. Defaults to the app name.
pub title: Option<String>,
/// The type of the dialog. Defaults to MessageDialogType::Info.
pub kind: MessageDialogType,
}
impl MessageDialogOptions {
/// Creates a new `MessageDialogOptions` with sensible default values.
pub fn new() -> Self {
Self {
title: None,
kind: MessageDialogType::Info,
}
}
}
/// Options for an [`open`] dialog.
#[derive(Serialize)]
pub struct OpenDialogOptions {
/// Initial directory or file path.
#[serde(rename(serialize = "defaultPath"))]
pub default_path: Option<PathBuf>,
/// Whether the dialog is a directory selection or not.
pub directory: bool,
/// The filters of the dialog.
pub filters: Vec<DialogFilter>,
/// Whether the dialgo allows multiple selection or not.
pub multiple: bool,
/// If `directory` is `true`, indicatees that it will be read recursivley later.
/// Defines whether subdirectories will be allowed on the scope or not.
pub recursive: bool,
/// The title of the dialog window.
pub title: Option<String>,
}
impl OpenDialogOptions {
/// Creates a new `OpenDialogOptions` with sensible default values.
pub fn new() -> Self {
Self {
default_path: None,
directory: false,
filters: Vec::new(),
multiple: false,
recursive: false,
title: None,
}
}
}
/// Options for the save dialog.
#[derive(Serialize)]
pub struct SaveDialogOptions {
/// Initial directory of the file path.
/// If it's not a directory path, the dialog interface will change to that folder.
/// If it's not an existing directory, the file name will be set to the dialog's
/// file name input and the dialog will be set to the parent folder.
#[serde(rename(serialize = "defaultPath"))]
pub default_path: Option<PathBuf>,
/// The filters of the dialog.
pub filters: Vec<DialogFilter>,
/// The title of the dialog window.
pub title: Option<String>,
}
impl SaveDialogOptions {
/// Creates a new `SaveDialogOptions` with sensible default values.
pub fn new() -> Self {
Self {
default_path: None,
filters: Vec::new(),
title: None,
}
}
}
/// Show a question dialog with `Yes` and `No` buttons.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_api::dialog::{ask, MessageDialogOptions};
///
/// let yes = ask("Are you sure?", None).await;
/// ```
/// @param message Message to display.
/// @param options Dialog options.
/// @returns Whether the user selected `Yes` or `No`.
#[inline(always)]
pub async fn ask(message: &str, options: Option<MessageDialogOptions>) -> Option<bool> {
inner::ask(message, serde_wasm_bindgen::to_value(&options).unwrap())
.await
.as_bool()
}
/// Shows a question dialog with `Ok` and `Cancel` buttons.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_api::dialog::{confirm, MessageDialogOptions};
///
/// let confirmed = confirm("Are you sure?", None).await;
/// ```
/// @returns Whether the user selelced `Ok` or `Cancel`.
pub async fn confirm(message: &str, options: Option<MessageDialogOptions>) -> Option<bool> {
inner::confirm(message, serde_wasm_bindgen::to_value(&options).unwrap())
.await
.as_bool()
}
/// Shows a message dialog with an `Ok` button.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_api::dialog::{message, MessageDialogOptions};
///
/// message("Tauri is awesome", None).await;
/// ```
/// @param message Message to display.
/// @param options Dialog options.
/// @returns Promise resolved when user closes the dialog.
pub async fn message(message: &str, options: Option<MessageDialogOptions>) {
inner::message(message, serde_wasm_bindgen::to_value(&options).unwrap()).await
}
/// Opens a file/directory selection dialog for a single file.
/// `multiple` field of [`options`](OpenDialogOptions) must be `false`, if provided.
///
/// The selected paths are added to the filesystem and asset protocol allowlist scopes.
/// When security is mroe important than the ease of use of this API,
/// prefer writing a dedicated command instead.
///
/// Note that the allowlist scope change is not persisited,
/// so the values are cleared when the applicaiton is restarted.
/// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
///
/// # Example
///
/// ```rust,no_run
/// use tauri_api::dialog::{open, OpenDialogOptions};
///
/// let file = open(None).await;
///
/// let mut opts = OpenDialogOptions::new();
/// opts.directory = true;
/// let dir = open(Some(opts)).await;
/// ```
/// @param options Dialog options.
/// @returns List of file paths, or `None` if user cancelled the dialog.
pub async fn open(options: Option<OpenDialogOptions>) -> Option<PathBuf> {
let file = inner::open(serde_wasm_bindgen::to_value(&options).unwrap()).await;
serde_wasm_bindgen::from_value(file).unwrap()
}
/// Opens a file/directory selection dialog for multiple files.
/// `multiple` field of [`options`](OpenDialogOptions) must be `true`, if provided.
///
/// The selected paths are added to the filesystem and asset protocol allowlist scopes.
/// When security is mroe important than the ease of use of this API,
/// prefer writing a dedicated command instead.
///
/// Note that the allowlist scope change is not persisited,
/// so the values are cleared when the applicaiton is restarted.
/// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
///
/// # Example
///
/// ```rust,no_run
/// use tauri_api::dialog::{open, OpenDialogOptions};
///
/// let files = open_multiple(None).await;
///
/// let mut opts = OpenDialogOptions::new();
/// opts.multiple = true;
/// opts.directory = true;
/// let dirs = open(Some(opts)).await;
/// ```
/// @param options Dialog options.
/// @returns List of file paths, or `None` if user cancelled the dialog.
pub async fn open_multiple(options: Option<OpenDialogOptions>) -> Option<Vec<PathBuf>> {
let files = inner::open_multiple(serde_wasm_bindgen::to_value(&options).unwrap()).await;
serde_wasm_bindgen::from_value(files).unwrap()
}
/// Opens a file/directory save dialog.
///
/// The selected paths are added to the filesystem and asset protocol allowlist scopes.
/// When security is mroe important than the ease of use of this API,
/// prefer writing a dedicated command instead.
///
/// Note that the allowlist scope change is not persisited,
/// so the values are cleared when the applicaiton is restarted.
/// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
///
/// # Example
///
/// ```rust,no_run
/// use tauri_api::dialog::{save, SaveDialogOptions};
///
/// let file = save(None).await;
/// ```
/// @param options Dialog options.
/// @returns File path, or `None` if user cancelled the dialog.
pub async fn save(options: Option<SaveDialogOptions>) -> Option<PathBuf> {
let path = inner::save(serde_wasm_bindgen::to_value(&options).unwrap()).await;
serde_wasm_bindgen::from_value(path).unwrap()
}
mod inner {
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
#[wasm_bindgen(module = "/dist/dialog.js")]
extern "C" {
pub async fn ask(message: &str, options: JsValue) -> JsValue;
pub async fn confirm(message: &str, options: JsValue) -> JsValue;
pub async fn open(options: JsValue) -> JsValue;
pub async fn open_multiple(options: JsValue) -> JsValue;
pub async fn message(message: &str, option: JsValue);
pub async fn save(options: JsValue) -> JsValue;
}
}

View file

@ -4,6 +4,8 @@ use wasm_bindgen::JsValue;
pub mod app;
#[cfg(feature = "clipboard")]
pub mod clipboard;
#[cfg(feature = "dialog")]
pub mod dialog;
#[cfg(feature = "event")]
pub mod event;
#[cfg(feature = "mocks")]