diff --git a/cue/Cargo.toml b/cue/Cargo.toml index 1cf210d..1a5a946 100644 --- a/cue/Cargo.toml +++ b/cue/Cargo.toml @@ -13,6 +13,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" toml = { version = "0.8", features = ["preserve_order"] } dirs-next = "2" +rfd = "0.15" [target.'cfg(windows)'.build-dependencies] winres = "0.1" diff --git a/cue/src/app.rs b/cue/src/app.rs index cce961a..a12a7fc 100644 --- a/cue/src/app.rs +++ b/cue/src/app.rs @@ -131,6 +131,8 @@ pub enum Message { BrowseLoadAsReference(i64), BrowseDeleteMeasurement(i64), BrowseBack, + ExportSession(i64), + ImportSession, /* LSV analysis */ LsvToggleManual, /* Misc */ @@ -1176,6 +1178,53 @@ impl App { self.browse_measurements.clear(); } } + Message::ExportSession(sid) => { + match self.storage.export_session(sid) { + Ok(toml_str) => { + let name = self.browse_sessions.iter() + .find(|(s, _)| s.id == sid) + .map(|(s, _)| s.name.clone()) + .unwrap_or_else(|| format!("session_{}", sid)); + let filename = format!("{}.toml", name.replace(' ', "_")); + let dialog = rfd::FileDialog::new() + .set_file_name(&filename) + .add_filter("TOML", &["toml"]); + if let Some(path) = dialog.save_file() { + match std::fs::write(&path, &toml_str) { + Ok(_) => self.status = format!("Exported to {}", path.display()), + Err(e) => self.status = format!("Write failed: {}", e), + } + } + } + Err(e) => self.status = format!("Export failed: {}", e), + } + } + Message::ImportSession => { + let dialog = rfd::FileDialog::new() + .add_filter("TOML", &["toml"]); + if let Some(path) = dialog.pick_file() { + match std::fs::read_to_string(&path) { + Ok(toml_str) => { + match self.storage.import_session(&toml_str) { + Ok(_) => { + self.browse_sessions = self.storage.list_sessions() + .unwrap_or_default() + .into_iter() + .map(|s| { + let cnt = self.storage.measurement_count(s.id).unwrap_or(0); + (s, cnt) + }) + .collect(); + self.sessions = self.storage.list_sessions().unwrap_or_default(); + self.status = format!("Imported from {}", path.display()); + } + Err(e) => self.status = format!("Import failed: {}", e), + } + } + Err(e) => self.status = format!("Read failed: {}", e), + } + } + } Message::Reconnect => { self.conn_gen += 1; self.cmd_tx = None; @@ -1910,7 +1959,14 @@ impl App { fn view_browse_sessions(&self) -> Element<'_, Message> { let mut items = column![ - text("Sessions").size(16), + row![ + text("Sessions").size(16), + iced::widget::horizontal_space(), + button(text("Import").size(11)) + .style(style_apply()) + .padding([4, 8]) + .on_press(Message::ImportSession), + ].align_y(iced::Alignment::Center), iced::widget::horizontal_rule(1), ].spacing(4); @@ -1986,6 +2042,14 @@ impl App { ); } + header = header.push(iced::widget::horizontal_space()); + header = header.push( + button(text("Export").size(11)) + .style(style_apply()) + .padding([4, 12]) + .on_press(Message::ExportSession(sid)), + ); + let mut mlist = column![].spacing(2); if self.browse_measurements.is_empty() {