Files
effectcraft/crates/ui-egui/tests/ui_comp_presets.rs
Anton IliashenkoandClaude Opus 5.5 14b6b9c242 Composition Settings: save and delete your own presets
As in After Effects, the Preset menu in Composition Settings / New Composition gets a Save
button (asks for the name in a Choose Name window; an existing name of yours is replaced) and a
Delete button for the selected preset of yours. Your presets keep frame size, pixel aspect and
frame rate, are listed above the built-in ones and live in the settings (`compPresets`), so they
survive restarts and Reset All. Commands: `comp.savePreset`, `comp.deletePreset`, `comp.presets`.
A hand-edited settings file loses only its invalid presets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:16:52 +02:00

86 lines
3.9 KiB
Rust

//! Composition Settings: saving, picking and deleting your own presets (egui_kittest).
use effectcraft_engine::Session;
use effectcraft_ui_egui::{Dialog, EffectcraftApp};
use egui::{Event, Key, Modifiers, PointerButton, Pos2, pos2};
use egui_kittest::Harness;
use serde_json::json;
fn harness() -> Harness<'static, EffectcraftApp> {
let mut s = Session::default();
s.execute("comp.new", json!({"name": "Edit", "width": 2560, "height": 1440, "frameRate": 60, "duration": 4})).unwrap();
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).with_step_dt(1.0 / 60.0).build_eframe(|_| EffectcraftApp::new(s));
h.run_steps(3);
h
}
fn center(h: &Harness<'_, EffectcraftApp>, id: &str) -> Pos2 {
let e = h.state().auto.find(id).unwrap_or_else(|| panic!("no {id}"));
pos2(e.rect[0] + e.rect[2] / 2.0, e.rect[1] + e.rect[3] / 2.0)
}
fn click(h: &mut Harness<'_, EffectcraftApp>, id: &str) {
let p = center(h, id);
h.input_mut().events.push(Event::PointerMoved(p));
h.input_mut().events.push(Event::PointerButton { pos: p, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
h.run_steps(1);
h.input_mut().events.push(Event::PointerButton { pos: p, button: PointerButton::Primary, pressed: false, modifiers: Modifiers::NONE });
// The automation registry still holds the previous frame's elements: one more frame.
h.run_steps(3);
}
fn key(h: &mut Harness<'_, EffectcraftApp>, key: Key) {
h.input_mut().events.push(Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE });
h.input_mut().events.push(Event::Key { key, physical_key: None, pressed: false, repeat: false, modifiers: Modifiers::NONE });
h.run_steps(3);
}
fn open_settings(h: &mut Harness<'_, EffectcraftApp>) {
let ctx = h.ctx.clone();
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "comp.settings", json!({})).unwrap();
h.run_steps(3);
assert_eq!(h.state().dialog, Some(Dialog::CompSettings));
}
fn preset_names(h: &Harness<'_, EffectcraftApp>) -> Vec<String> {
h.state().session.prefs.comp_presets.iter().map(|q| q.name.clone()).collect()
}
/// Save the comp's size and rate as a preset (Enter saves without pressing OK), then delete it.
#[test]
fn composition_settings_save_and_delete_your_own_preset() {
let mut h = harness();
open_settings(&mut h);
assert!(h.state().auto.find("dialog.comp.presetDelete").is_none(), "nothing of yours to delete yet");
click(&mut h, "dialog.comp.presetSave");
// Choose Name opens with the keyboard in its field.
h.input_mut().events.push(Event::Text("1440p 60".into()));
h.run_steps(2);
key(&mut h, Key::Enter);
assert_eq!(preset_names(&h), ["1440p 60"]);
let q = &h.state().session.prefs.comp_presets[0];
assert_eq!((q.width, q.height, q.pixel_aspect, q.frame_rate), (2560, 1440, 1.0, 60.0));
assert_eq!(h.state().dialog, Some(Dialog::CompSettings), "Enter in the name field doesn't press OK");
assert!(h.state().auto.find("dialog.comp.presetName").is_none(), "the name row closes");
// The comp's values are now that preset, so it can be deleted.
click(&mut h, "dialog.comp.presetDelete");
assert!(preset_names(&h).is_empty());
assert!(h.state().auto.find("dialog.comp.presetDelete").is_none());
}
/// A built-in preset's name can't be taken; Cancel closes the name row without saving.
#[test]
fn a_preset_name_cannot_be_a_built_in_one() {
let mut h = harness();
open_settings(&mut h);
click(&mut h, "dialog.comp.presetSave");
click(&mut h, "dialog.comp.presetName");
h.input_mut().events.push(Event::Text("HDTV 1080 24".into()));
h.run_steps(2);
click(&mut h, "dialog.comp.presetNameSave");
assert!(preset_names(&h).is_empty(), "the built-in name is refused");
click(&mut h, "dialog.comp.presetNameCancel");
assert!(h.state().auto.find("dialog.comp.presetName").is_none());
assert!(preset_names(&h).is_empty());
}