ui: Graph Editor fit goes through the timeline zoom; AZERTY zoom keys in the graph

Co-Authored-By: Claude <[email protected]>

(cherry picked from commit 6277a98f3ff6207bd215213af514c5f6fb481532)

(cherry picked from commit 646f4055b180d98ccc6ac6898b64ba97f9e2ad7d)
(cherry picked from commit b21932ed7058cec68da01d337ba46521501ebc49)

(cherry picked from commit 0d6b93962e123b2e0179622f2abd472cfa9015ac)
This commit is contained in:
Elias authored and Elias committed 2026-10-12 19:21:42 +01:00
1 parent ac442f8c61
commit c4fdbd6064
3 files changed
+27 -11

No files matched your search

+6 -3
View File
@@ -676,9 +676,12 @@ fn fit_selection(app: &mut EffectcraftApp, cs: &[Curve], tm: TMap, plot: Rect) {
}
}
let span = (t1 - t0).max(0.2);
let pps = (plot.width() as f64 * 0.8 / span).clamp(5.0, 4000.0);
app.ui.timeline.pps = Some(pps);
app.ui.timeline.start = (t0 - span * 0.1).max(0.0);
let pps = plot.width() as f64 * 0.8 / span;
// Through the timeline's zoom, so the fit never zooms out past the whole comp.
if let Some(comp) = app.session.active_comp_arc() {
let fit = super::timeline::fit_pps(plot.width(), &comp);
super::timeline::set_zoom(&mut app.ui.timeline, &comp, pps, fit, (t0 - span * 0.1).max(0.0), 0.0);
}
let pad = ((v1 - v0) * 0.15).max(1.0);
app.ui.timeline.graph_auto_zoom = false;
app.ui.timeline.graph_range = Some((v0 - pad, v1 + pad));
+2 -2
View File
@@ -517,14 +517,14 @@ const MAX_PPS: f64 = 4000.0;
pub(crate) const ZOOM_STEP: f64 = 1.5;
/// Pixels per second that fit `comp` in a time graph `width` points wide.
fn fit_pps(width: f32, comp: &Comp) -> f64 {
pub(crate) fn fit_pps(width: f32, comp: &Comp) -> f64 {
(width - 10.0).max(1.0) as f64 / comp.duration.seconds().max(0.01)
}
/// Zoom the time ruler to `pps` pixels per second, keeping comp time `at_t` at `at_px` pixels
/// from the ruler's start. Zooming out stops at `fit`, where the whole comp shows (and the
/// ruler keeps fitting it).
fn set_zoom(tl: &mut crate::state::TimelineState, comp: &Comp, pps: f64, fit: f64, at_t: f64, at_px: f64) {
pub(crate) fn set_zoom(tl: &mut crate::state::TimelineState, comp: &Comp, pps: f64, fit: f64, at_t: f64, at_px: f64) {
let npps = pps.min(MAX_PPS.max(fit)).max(fit);
if npps <= fit {
tl.pps = None;
+19 -6
View File
@@ -142,17 +142,30 @@ fn graph_curves_follow_the_time_zoom_and_the_navigator() {
}
#[test]
fn switching_between_layer_bars_and_graph_keeps_the_time_zoom() {
fn leaving_the_graph_keeps_its_time_zoom() {
let mut h = harness(false);
send(&mut h, "ui.key", json!({"key": "="}));
send(&mut h, "ui.key", json!({"key": "="}));
let before = view(&h);
// Opening the Graph Editor fits the graphs (ui_graph_fit); the zoom set there stays.
send(&mut h, "ui.click", json!({"id": "timeline.switch.graph"}));
assert!(h.state().ui.timeline.graph_editor);
assert_eq!(view(&h), before);
send(&mut h, "ui.key", json!({"key": "-"}));
send(&mut h, "ui.key", json!({"key": "="}));
let graph = view(&h);
send(&mut h, "ui.click", json!({"id": "timeline.switch.graph"}));
assert!(!h.state().ui.timeline.graph_editor);
assert_eq!(view(&h), graph);
}
#[test]
fn azerty_physical_zoom_keys_zoom_the_graph() {
let mut h = harness(true);
// The "Elias AE"-style bindings: AZERTY's - (US 6 position) zooms in, ) (US minus) out.
for (command, keys) in [("timeline.zoomIn", "Physical+6"), ("timeline.zoomOut", "Physical+Minus")] {
h.state_mut().session.execute("shortcuts.set", json!({"command": command, "keys": keys})).unwrap();
}
let (_, span0) = view(&h);
send(&mut h, "ui.key", json!({"key": "Physical+6"}));
let (_, span1) = view(&h);
assert!(span1 < span0 * 0.8, "Physical+6 zooms the graph in: {span0} -> {span1}");
send(&mut h, "ui.key", json!({"key": "Physical+Minus"}));
let (_, span2) = view(&h);
assert!(span2 > span1 * 1.2, "Physical+Minus zooms the graph out: {span1} -> {span2}");
}