mirror of
https://github.com/storytold/soundcraft.git
synced 2026-11-02 16:30:24 -06:00
Close Session, New, Open, Revert, Quit, the window's close button, dropping a session file on the window and opening one from the Workspace window all replaced or closed the session without asking, so unsaved edits were lost (#136). They now ask first: Save, Don't Save or Cancel. Save on a session that has never been saved goes through Save As, then carries on. Revert asks for confirmation instead, and Revert to Saved is unavailable until the session has a file. A take being recorded is stopped first, so it counts as a change. The web build has nowhere to save, so it offers only Don't Save and Cancel. Calls from the control channel, MCP and the CLI never ask. Selecting, locating, zooming and scrolling no longer mark the session modified, so the question only comes after real edits. Undoing back to the saved state makes the session clean again. The engine compares the document with the one last opened or saved, ignoring the selection, the playhead and the zoom, once per revision. Fixes #136
98 lines
3.3 KiB
Rust
98 lines
3.3 KiB
Rust
use egui_kittest::Harness;
|
|
use egui_kittest::kittest::Queryable;
|
|
use serde_json::json;
|
|
use soundcraft_ui_egui::dialogs::Dialog;
|
|
use soundcraft_ui_egui::{Services, SoundApp, menus};
|
|
|
|
fn harness() -> Harness<'static, SoundApp> {
|
|
let app = SoundApp::new(soundcraft_engine::demo::demo_engine(), None, Services::default());
|
|
Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_ui_state(
|
|
|ui, app: &mut SoundApp| {
|
|
let ctx = ui.ctx().clone();
|
|
app.logic(&ctx);
|
|
app.ui(ui);
|
|
},
|
|
app,
|
|
)
|
|
}
|
|
|
|
fn steps(h: &mut Harness<'_, SoundApp>) {
|
|
for _ in 0..4 {
|
|
h.step();
|
|
}
|
|
}
|
|
|
|
fn click(h: &mut Harness<'_, SoundApp>, label: &str) {
|
|
h.get_by_label(label).click();
|
|
steps(h);
|
|
}
|
|
|
|
fn close_session(h: &mut Harness<'_, SoundApp>) {
|
|
menus::invoke_menu(h.state_mut(), "session.close", "File > Close Session");
|
|
steps(h);
|
|
}
|
|
|
|
fn mute_kick(h: &mut Harness<'_, SoundApp>) {
|
|
h.state_mut().run("mix.mute", json!({"track": "Kick"})).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn closing_a_modified_session_asks_first() {
|
|
let mut h = harness();
|
|
let tracks = h.state().engine.session().tracks.len();
|
|
h.state_mut().run("edit.select", json!({"tracks": ["Kick"], "start": 0, "end": 48_000})).unwrap();
|
|
close_session(&mut h);
|
|
assert!(h.state().dialogs.open.is_none(), "a selection alone is not a change");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
|
|
let mut h = harness();
|
|
mute_kick(&mut h);
|
|
close_session(&mut h);
|
|
assert_eq!(h.state().dialogs.open_name(), Some("save_changes"));
|
|
click(&mut h, "Cancel");
|
|
assert!(h.state().dialogs.open.is_none());
|
|
assert_eq!(h.state().engine.session().tracks.len(), tracks);
|
|
|
|
close_session(&mut h);
|
|
click(&mut h, "Don't Save");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
|
|
let mut h = harness();
|
|
mute_kick(&mut h);
|
|
menus::invoke_menu(h.state_mut(), "app.quit", "");
|
|
steps(&mut h);
|
|
assert!(!h.state().quit_requested);
|
|
click(&mut h, "Don't Save");
|
|
assert!(h.state().quit_requested);
|
|
}
|
|
|
|
#[test]
|
|
fn save_in_the_question_saves_before_closing() {
|
|
let dir = std::env::temp_dir().join(format!("soundcraft-save-changes-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
let path = dir.join("Song.scraft").to_string_lossy().into_owned();
|
|
|
|
let mut h = harness();
|
|
mute_kick(&mut h);
|
|
close_session(&mut h);
|
|
click(&mut h, "Save");
|
|
match &mut h.state_mut().dialogs.open {
|
|
Some(Dialog::PathPrompt { cmd, path: p, .. }) if cmd == "session.save_as" => p.clone_from(&path),
|
|
other => panic!("expected Save As, got {other:?}"),
|
|
}
|
|
steps(&mut h);
|
|
click(&mut h, "OK");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
|
|
h.state_mut().run("session.open", json!({"path": path})).unwrap();
|
|
assert!(h.state().engine.session().track_by_name("Kick").unwrap().mixer.mute);
|
|
h.state_mut().run("mix.mute", json!({"track": "Kick"})).unwrap();
|
|
close_session(&mut h);
|
|
click(&mut h, "Save");
|
|
assert!(h.state().dialogs.open.is_none());
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
h.state_mut().run("session.open", json!({"path": path})).unwrap();
|
|
assert!(!h.state().engine.session().track_by_name("Kick").unwrap().mixer.mute);
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|