Files
effectcraft/crates/ui-egui/tests/it/ui_graph_fit.rs
T
aiongg 0173abde8c CI: dev-profile gate, one test binary per crate, nextest, line tables only (#2235)
* Dev profile: line tables only for workspace crates, no debug info for dependencies

* xtask ci: dev-profile gate, nextest, tests without examples, wasm --keep-going

* One integration-test binary per crate

* xtask ci: run effectcraft-gpu's shared-device tests in one cargo test process

* xtask ci: run every stage and report all failures

* xtask ci: name the stage type (clippy type_complexity)

* xtask ci: run every test pass and report all failures

* cargo fmt
2026-10-23 12:59:51 -04:00

80 lines
4.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Opening the Graph Editor frames the graphs, as in After Effects: Fit Selection to View when
//! keys are selected (a key selected in the Timeline shows its property's graph even when the
//! layer isn't selected), else Fit All Graphs to View. It used to open on an empty 0–100 range
//! or with the curves off-view.
use effectcraft_engine::Session;
use effectcraft_ui_egui::EffectcraftApp;
use egui_kittest::Harness;
use serde_json::json;
/// A 20 s comp whose Box layer has Rotation keys 300° at 8 s and 720° at 12 s; the Timeline is
/// zoomed in on 0–4 s, so neither key is in view. Returns the harness and Rotation's uid.
fn harness(select_layer: bool) -> (Harness<'static, EffectcraftApp>, u64, u64) {
let mut s = Session::default();
s.execute("comp.new", json!({"name": "Fit", "width": 320, "height": 180, "frameRate": 30, "duration": 20})).unwrap();
s.execute("layer.newSolid", json!({"name": "Box", "color": "#e04020", "width": 80, "height": 80})).unwrap();
let id = s.active_comp().unwrap().layers[0].id;
for (t, v) in [(8.0, 300.0), (12.0, 720.0)] {
s.execute("prop.addKey", json!({"layer": id.0, "path": "transform/rotation", "time": t, "value": v})).unwrap();
}
let uid = s.active_comp().unwrap().layer(id).unwrap().props.prop("transform/rotation").unwrap().uid;
s.execute("layer.select", json!({"layers": if select_layer { vec![id.0] } else { vec![] }})).unwrap();
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_eframe(|_| EffectcraftApp::new(s));
h.state_mut().ui.timeline.pps = Some(300.0);
h.state_mut().ui.timeline.start = 0.0;
h.run_steps(3);
(h, id.0, uid)
}
fn open_graph(h: &mut Harness<'_, EffectcraftApp>) {
let ctx = h.ctx.clone();
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "timeline.graphEditor", json!({})).unwrap();
h.run_steps(4);
assert!(h.state().ui.timeline.graph_editor);
}
/// Every key of the property sits inside the plot, and the value range spans `lo..=hi`.
fn assert_framed(h: &Harness<'_, EffectcraftApp>, uid: u64, keys: &[usize], lo: f64, hi: f64) {
let plot = h.state().auto.find("timeline.graph").expect("graph plot").rect;
for i in keys {
let k = h.state().auto.find(&format!("timeline.graph.key.{uid}.0.{i}")).unwrap_or_else(|| panic!("graph key {i} not drawn")).rect;
let (cx, cy) = (k[0] + k[2] / 2.0, k[1] + k[3] / 2.0);
assert!(cx > plot[0] && cx < plot[0] + plot[2] && cy > plot[1] && cy < plot[1] + plot[3], "key {i} at ({cx}, {cy}) outside plot {plot:?}");
}
let (a, b) = h.state().ui.timeline.graph_range.expect("graph range");
assert!(a <= lo && b >= hi, "value range {a}..{b} doesn't contain {lo}..{hi}");
}
#[test]
fn opening_the_graph_editor_fits_the_selected_key() {
let (mut h, layer, uid) = harness(false);
h.state_mut().session.execute("keys.select", json!({"keys": [{"layer": layer, "prop": uid, "time": 12.0}]})).unwrap();
h.run_steps(2);
open_graph(&mut h);
// A lone key also frames its neighbour, so its curve shows.
assert_framed(&h, uid, &[0, 1], 300.0, 720.0);
assert!(!h.state().ui.timeline.graph_auto_zoom, "Fit Selection turns auto-zoom off");
}
#[test]
fn opening_the_graph_editor_with_no_keys_selected_fits_all_graphs() {
let (mut h, _, uid) = harness(true);
open_graph(&mut h);
assert_eq!(h.state().ui.timeline.pps, None, "the whole comp in time");
assert_framed(&h, uid, &[0, 1], 300.0, 720.0);
// Zooming away and toggling the Graph Editor off and on frames it again.
h.state_mut().ui.timeline.pps = Some(300.0);
h.state_mut().ui.timeline.start = 0.0;
open_graph_toggle_twice(&mut h);
assert_framed(&h, uid, &[0, 1], 300.0, 720.0);
}
fn open_graph_toggle_twice(h: &mut Harness<'_, EffectcraftApp>) {
let ctx = h.ctx.clone();
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "timeline.graphEditor", json!({})).unwrap();
h.run_steps(2);
assert!(!h.state().ui.timeline.graph_editor);
open_graph(h);
}