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}