1use rfd::{FileDialog, MessageButtons, MessageDialog, MessageDialogResult, MessageLevel};
9use serde_json::Value;
10
11use crate::MainDispatcher;
12
13fn arg_str<'a>(args: &'a Value, key: &str) -> Option<&'a str> {
14 args.get(key).and_then(|v| v.as_str())
15}
16
17fn with_filters(mut dialog: FileDialog, args: &Value) -> FileDialog {
18 if let Some(filters) = args.get("filters").and_then(|v| v.as_array()) {
19 for f in filters {
20 let name = f.get("name").and_then(|v| v.as_str()).unwrap_or("");
21 let exts: Vec<&str> = f
22 .get("extensions")
23 .and_then(|v| v.as_array())
24 .map(|a| a.iter().filter_map(|v| v.as_str()).collect())
25 .unwrap_or_default();
26 if !exts.is_empty() {
27 dialog = dialog.add_filter(name, &exts);
28 }
29 }
30 }
31 if let Some(dir) = arg_str(args, "defaultPath") {
32 dialog = dialog.set_directory(dir);
33 }
34 dialog
35}
36
37pub(crate) fn open(main: &MainDispatcher, args: &Value) -> Result<Value, String> {
38 let args = args.clone();
39 main.run(move || {
40 let directory = args
41 .get("directory")
42 .and_then(|v| v.as_bool())
43 .unwrap_or(false);
44 let multiple = args
45 .get("multiple")
46 .and_then(|v| v.as_bool())
47 .unwrap_or(false);
48 let dialog = with_filters(FileDialog::new(), &args);
49
50 if directory {
51 if multiple {
52 dialog
53 .pick_folders()
54 .map(|v| {
55 Value::Array(
56 v.into_iter()
57 .map(|p| Value::from(p.to_string_lossy().to_string()))
58 .collect(),
59 )
60 })
61 .unwrap_or(Value::Null)
62 } else {
63 dialog
64 .pick_folder()
65 .map(|p| Value::from(p.to_string_lossy().to_string()))
66 .unwrap_or(Value::Null)
67 }
68 } else if multiple {
69 dialog
70 .pick_files()
71 .map(|v| {
72 Value::Array(
73 v.into_iter()
74 .map(|p| Value::from(p.to_string_lossy().to_string()))
75 .collect(),
76 )
77 })
78 .unwrap_or(Value::Null)
79 } else {
80 dialog
81 .pick_file()
82 .map(|p| Value::from(p.to_string_lossy().to_string()))
83 .unwrap_or(Value::Null)
84 }
85 })
86}
87
88pub(crate) fn save(main: &MainDispatcher, args: &Value) -> Result<Value, String> {
89 let args = args.clone();
90 main.run(move || {
91 with_filters(FileDialog::new(), &args)
92 .save_file()
93 .map(|p| Value::from(p.to_string_lossy().to_string()))
94 .unwrap_or(Value::Null)
95 })
96}
97
98pub(crate) fn ask(main: &MainDispatcher, args: &Value) -> Result<Value, String> {
99 let args = args.clone();
100 main.run(move || {
101 let message = arg_str(&args, "message").unwrap_or("");
102 let title = arg_str(&args, "title").unwrap_or("");
103 let ok_label = arg_str(&args, "okLabel");
104 let cancel_label = arg_str(&args, "cancelLabel");
105 let buttons = match (ok_label, cancel_label) {
106 (Some(ok), Some(cancel)) => {
107 MessageButtons::OkCancelCustom(ok.to_string(), cancel.to_string())
108 }
109 _ => MessageButtons::YesNo,
110 };
111 let result = MessageDialog::new()
112 .set_title(title)
113 .set_description(message)
114 .set_level(MessageLevel::Info)
115 .set_buttons(buttons)
116 .show();
117 let ok = matches!(
118 result,
119 MessageDialogResult::Yes | MessageDialogResult::Ok | MessageDialogResult::Custom(_)
120 );
121 Value::from(ok)
122 })
123}
124
125pub(crate) fn message(main: &MainDispatcher, args: &Value) -> Result<Value, String> {
126 let args = args.clone();
127 main.run(move || {
128 let body = arg_str(&args, "message").unwrap_or("");
129 let title = arg_str(&args, "title").unwrap_or("");
130 let kind = arg_str(&args, "kind").unwrap_or("info");
131 let level = match kind {
132 "error" => MessageLevel::Error,
133 "warning" => MessageLevel::Warning,
134 _ => MessageLevel::Info,
135 };
136 MessageDialog::new()
137 .set_title(title)
138 .set_description(body)
139 .set_level(level)
140 .set_buttons(MessageButtons::Ok)
141 .show();
142 Value::Null
143 })
144}