libsurfer/
file_dialog.rs
1#[cfg(not(target_arch = "wasm32"))]
2use std::path::PathBuf;
3
4#[cfg(not(target_arch = "wasm32"))]
5use camino::Utf8PathBuf;
6use rfd::AsyncFileDialog;
7use serde::Deserialize;
8
9use crate::async_util::perform_async_work;
10use crate::message::Message;
11use crate::wave_source::{LoadOptions, STATE_FILE_EXTENSION};
12use crate::SystemState;
13
14#[derive(Debug, Deserialize)]
15pub enum OpenMode {
16 Open,
17 Switch,
18}
19
20impl SystemState {
21 #[cfg(not(target_arch = "wasm32"))]
22 fn file_dialog<F>(&mut self, title: &'static str, filter: (String, Vec<String>), message: F)
23 where
24 F: FnOnce(PathBuf) -> Message + Send + 'static,
25 {
26 let sender = self.channels.msg_sender.clone();
27
28 perform_async_work(async move {
29 if let Some(file) = create_file_dialog(filter, title).pick_file().await {
30 sender.send(message(file.path().to_path_buf())).unwrap();
31 }
32 });
33 }
34
35 #[cfg(target_arch = "wasm32")]
36 fn file_dialog<F>(&mut self, title: &'static str, filter: (String, Vec<String>), message: F)
37 where
38 F: FnOnce(Vec<u8>) -> Message + Send + 'static,
39 {
40 let sender = self.channels.msg_sender.clone();
41
42 perform_async_work(async move {
43 if let Some(file) = create_file_dialog(filter, title).pick_file().await {
44 sender.send(message(file.read().await)).unwrap();
45 }
46 });
47 }
48
49 pub fn open_file_dialog(&mut self, mode: OpenMode) {
50 let keep_unavailable = self.user.config.behavior.keep_during_reload;
51 let keep_variables = match mode {
52 OpenMode::Open => false,
53 OpenMode::Switch => true,
54 };
55
56 #[cfg(not(target_arch = "wasm32"))]
57 let message = move |file: PathBuf| {
58 Message::LoadFile(
59 Utf8PathBuf::from_path_buf(file).unwrap(),
60 LoadOptions {
61 keep_variables,
62 keep_unavailable,
63 },
64 )
65 };
66
67 #[cfg(target_arch = "wasm32")]
68 let message = move |file: Vec<u8>| {
69 Message::LoadFromData(
70 file,
71 LoadOptions {
72 keep_variables,
73 keep_unavailable,
74 },
75 )
76 };
77
78 self.file_dialog(
79 "Open waveform file",
80 (
81 "Waveform/Transaction-files (*.vcd, *.fst, *.ghw, *.ftr)".to_string(),
82 vec![
83 "vcd".to_string(),
84 "fst".to_string(),
85 "ghw".to_string(),
86 "ftr".to_string(),
87 ],
88 ),
89 message,
90 );
91 }
92
93 pub fn open_command_file_dialog(&mut self) {
94 #[cfg(not(target_arch = "wasm32"))]
95 let message = move |file: PathBuf| {
96 Message::LoadCommandFile(Utf8PathBuf::from_path_buf(file).unwrap())
97 };
98
99 #[cfg(target_arch = "wasm32")]
100 let message = move |file: Vec<u8>| Message::LoadCommandFromData(file);
101
102 self.file_dialog(
103 "Open command file",
104 (
105 "Command-file (*.sucl)".to_string(),
106 vec!["sucl".to_string()],
107 ),
108 message,
109 );
110 }
111
112 #[cfg(feature = "python")]
113 pub fn open_python_file_dialog(&mut self) {
114 self.file_dialog(
115 "Open Python translator file",
116 ("Python files (*.py)".to_string(), vec!["py".to_string()]),
117 |file| Message::LoadPythonTranslator(Utf8PathBuf::from_path_buf(file).unwrap()),
118 );
119 }
120}
121
122pub async fn save_state_dialog() -> Option<rfd::FileHandle> {
123 create_file_dialog(
124 (
125 format!("Surfer state files (*.{})", STATE_FILE_EXTENSION),
126 ([STATE_FILE_EXTENSION.to_string()]).to_vec(),
127 ),
128 "Save state",
129 )
130 .save_file()
131 .await
132}
133
134pub async fn load_state_dialog() -> Option<rfd::FileHandle> {
135 create_file_dialog(
136 (
137 format!("Surfer state files (*.{})", STATE_FILE_EXTENSION),
138 ([STATE_FILE_EXTENSION.to_string()]).to_vec(),
139 ),
140 "Load state",
141 )
142 .pick_file()
143 .await
144}
145
146fn create_file_dialog(filter: (String, Vec<String>), title: &'static str) -> AsyncFileDialog {
147 AsyncFileDialog::new()
148 .set_title(title)
149 .add_filter(filter.0, &filter.1)
150 .add_filter("All files", &["*"])
151}