tauri_plugin_dialog/
desktop.rs1use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
12use rfd::{AsyncFileDialog, AsyncMessageDialog};
13use serde::de::DeserializeOwned;
14use tauri::{plugin::PluginApi, AppHandle, Runtime};
15
16use crate::{models::*, FileDialogBuilder, FilePath, MessageDialogBuilder, OK};
17
18pub fn init<R: Runtime, C: DeserializeOwned>(
19 app: &AppHandle<R>,
20 _api: PluginApi<R, C>,
21) -> crate::Result<Dialog<R>> {
22 Ok(Dialog(app.clone()))
23}
24
25#[derive(Debug)]
27pub struct Dialog<R: Runtime>(AppHandle<R>);
28
29impl<R: Runtime> Clone for Dialog<R> {
30 fn clone(&self) -> Self {
31 Self(self.0.clone())
32 }
33}
34
35impl<R: Runtime> Dialog<R> {
36 pub(crate) fn app_handle(&self) -> &AppHandle<R> {
37 &self.0
38 }
39}
40
41impl From<MessageDialogKind> for rfd::MessageLevel {
42 fn from(kind: MessageDialogKind) -> Self {
43 match kind {
44 MessageDialogKind::Info => Self::Info,
45 MessageDialogKind::Warning => Self::Warning,
46 MessageDialogKind::Error => Self::Error,
47 }
48 }
49}
50
51#[derive(Debug)]
52pub(crate) struct WindowHandle {
53 window_handle: RawWindowHandle,
54 display_handle: RawDisplayHandle,
55}
56
57impl WindowHandle {
58 pub(crate) fn new(window_handle: RawWindowHandle, display_handle: RawDisplayHandle) -> Self {
59 Self {
60 window_handle,
61 display_handle,
62 }
63 }
64}
65
66impl HasWindowHandle for WindowHandle {
67 fn window_handle(
68 &self,
69 ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
70 Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(self.window_handle) })
71 }
72}
73
74impl HasDisplayHandle for WindowHandle {
75 fn display_handle(
76 &self,
77 ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
78 Ok(unsafe { raw_window_handle::DisplayHandle::borrow_raw(self.display_handle) })
79 }
80}
81
82impl<R: Runtime> From<FileDialogBuilder<R>> for AsyncFileDialog {
83 fn from(d: FileDialogBuilder<R>) -> Self {
84 let mut builder = AsyncFileDialog::new();
85
86 if let Some(title) = d.title {
87 builder = builder.set_title(title);
88 }
89 if let Some(starting_directory) = d.starting_directory {
90 builder = builder.set_directory(starting_directory);
91 }
92 if let Some(file_name) = d.file_name {
93 builder = builder.set_file_name(file_name);
94 }
95 for filter in d.filters {
96 let v: Vec<&str> = filter.extensions.iter().map(|x| &**x).collect();
97 builder = builder.add_filter(&filter.name, &v);
98 }
99 #[cfg(desktop)]
100 if let Some(parent) = d.parent {
101 builder = builder.set_parent(&parent);
102 }
103
104 builder = builder.set_can_create_directories(d.can_create_directories.unwrap_or(true));
105
106 builder
107 }
108}
109
110impl From<MessageDialogButtons> for rfd::MessageButtons {
111 fn from(value: MessageDialogButtons) -> Self {
112 match value {
113 MessageDialogButtons::Ok => Self::Ok,
114 MessageDialogButtons::OkCancel => Self::OkCancel,
115 MessageDialogButtons::YesNo => Self::YesNo,
116 MessageDialogButtons::OkCustom(ok) => Self::OkCustom(ok),
117 MessageDialogButtons::OkCancelCustom(ok, cancel) => Self::OkCancelCustom(ok, cancel),
118 }
119 }
120}
121
122impl<R: Runtime> From<MessageDialogBuilder<R>> for AsyncMessageDialog {
123 fn from(d: MessageDialogBuilder<R>) -> Self {
124 let mut dialog = AsyncMessageDialog::new()
125 .set_title(&d.title)
126 .set_description(&d.message)
127 .set_level(d.kind.into())
128 .set_buttons(d.buttons.into());
129
130 if let Some(parent) = d.parent {
131 dialog = dialog.set_parent(&parent);
132 }
133
134 dialog
135 }
136}
137
138pub fn pick_file<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
139 dialog: FileDialogBuilder<R>,
140 f: F,
141) {
142 let f = |path: Option<rfd::FileHandle>| f(path.map(|p| p.path().to_path_buf().into()));
143 let handle = dialog.dialog.app_handle().to_owned();
144 let _ = handle.run_on_main_thread(move || {
145 let dialog = AsyncFileDialog::from(dialog).pick_file();
146 std::thread::spawn(move || f(tauri::async_runtime::block_on(dialog)));
147 });
148}
149
150pub fn pick_files<R: Runtime, F: FnOnce(Option<Vec<FilePath>>) + Send + 'static>(
151 dialog: FileDialogBuilder<R>,
152 f: F,
153) {
154 let f = |paths: Option<Vec<rfd::FileHandle>>| {
155 f(paths.map(|list| {
156 list.into_iter()
157 .map(|p| p.path().to_path_buf().into())
158 .collect()
159 }))
160 };
161 let handle = dialog.dialog.app_handle().to_owned();
162 let _ = handle.run_on_main_thread(move || {
163 let dialog = AsyncFileDialog::from(dialog).pick_files();
164 std::thread::spawn(move || f(tauri::async_runtime::block_on(dialog)));
165 });
166}
167
168pub fn pick_folder<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
169 dialog: FileDialogBuilder<R>,
170 f: F,
171) {
172 let f = |path: Option<rfd::FileHandle>| f(path.map(|p| p.path().to_path_buf().into()));
173 let handle = dialog.dialog.app_handle().to_owned();
174 let _ = handle.run_on_main_thread(move || {
175 let dialog = AsyncFileDialog::from(dialog).pick_folder();
176 std::thread::spawn(move || f(tauri::async_runtime::block_on(dialog)));
177 });
178}
179
180pub fn pick_folders<R: Runtime, F: FnOnce(Option<Vec<FilePath>>) + Send + 'static>(
181 dialog: FileDialogBuilder<R>,
182 f: F,
183) {
184 let f = |paths: Option<Vec<rfd::FileHandle>>| {
185 f(paths.map(|list| {
186 list.into_iter()
187 .map(|p| p.path().to_path_buf().into())
188 .collect()
189 }))
190 };
191 let handle = dialog.dialog.app_handle().to_owned();
192 let _ = handle.run_on_main_thread(move || {
193 let dialog = AsyncFileDialog::from(dialog).pick_folders();
194 std::thread::spawn(move || f(tauri::async_runtime::block_on(dialog)));
195 });
196}
197
198pub fn save_file<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
199 dialog: FileDialogBuilder<R>,
200 f: F,
201) {
202 let f = |path: Option<rfd::FileHandle>| f(path.map(|p| p.path().to_path_buf().into()));
203 let handle = dialog.dialog.app_handle().to_owned();
204 let _ = handle.run_on_main_thread(move || {
205 let dialog = AsyncFileDialog::from(dialog).save_file();
206 std::thread::spawn(move || f(tauri::async_runtime::block_on(dialog)));
207 });
208}
209
210pub fn show_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>(
212 dialog: MessageDialogBuilder<R>,
213 f: F,
214) {
215 use rfd::MessageDialogResult;
216
217 let ok_label = match &dialog.buttons {
218 MessageDialogButtons::OkCustom(ok) => Some(ok.clone()),
219 MessageDialogButtons::OkCancelCustom(ok, _) => Some(ok.clone()),
220 _ => None,
221 };
222 let f = move |res| {
223 f(match res {
224 MessageDialogResult::Ok | MessageDialogResult::Yes => true,
225 MessageDialogResult::Custom(s) => ok_label.map_or(s == OK, |ok_label| ok_label == s),
226 _ => false,
227 });
228 };
229
230 let handle = dialog.dialog.app_handle().to_owned();
231 let _ = handle.run_on_main_thread(move || {
232 let dialog = AsyncMessageDialog::from(dialog).show();
233 std::thread::spawn(move || f(tauri::async_runtime::block_on(dialog)));
234 });
235}