Shift+; again zooms the Timeline back in

Zoom to Fit Comp remembers the span it left and goes back to it when pressed
again from the whole comp.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
pravdomilandClaude Opus 5.5 committed 2026-10-12 17:49:55 +02:00
1 parent 498dbc0c71
commit 0eda174706
4 files changed
+43 -1

No files matched your search

+1 -1
View File
@@ -592,7 +592,7 @@ pub fn invoke(app: &mut EffectcraftApp, ctx: &egui::Context, id: &str, params: V
crate::panels::timeline::zoom(app, ctx, k);
return Ok(Value::Null);
}
"timeline.zoomFit" => app.ui.timeline.pps = None,
"timeline.zoomFit" => crate::panels::timeline::toggle_fit_zoom(app, ctx),
"timeline.zoomFrameToggle" => crate::panels::timeline::toggle_frame_zoom(app, ctx),
"timeline.column" => {
let col = params.get("column").and_then(Value::as_str).ok_or("timeline.column: need `column`")?;
+15
View File
@@ -545,6 +545,21 @@ pub fn toggle_frame_zoom(app: &mut EffectcraftApp, ctx: &egui::Context) {
set_zoom(&mut app.ui.timeline, &comp, MAX_PPS, fit, cti, w as f64 / 2.0);
}
/// `Shift+;`: show the whole comp; from there, go back to the span it was pressed in.
pub fn toggle_fit_zoom(app: &mut EffectcraftApp, ctx: &egui::Context) {
let tl = &mut app.ui.timeline;
if let Some(pps) = tl.pps {
tl.before_fit = Some((tl.start, pps));
tl.pps = None;
tl.start = 0.0;
return;
}
let Some((start, pps)) = tl.before_fit.take() else { return };
let Some((_, w)) = ctx.data(|d| d.get_temp::<(f32, f32)>(egui::Id::new("timeline-graph-area"))) else { return };
let Some(comp) = app.session.active_comp_arc() else { return };
set_zoom(&mut app.ui.timeline, &comp, pps, fit_pps(w, &comp), start, 0.0);
}
/// Zoom the time ruler around the CTI.
pub fn zoom(app: &mut EffectcraftApp, ctx: &egui::Context, k: f64) {
let Some((_, w)) = ctx.data(|d| d.get_temp::<(f32, f32)>(egui::Id::new("timeline-graph-area"))) else { return };
+4
View File
@@ -322,6 +322,9 @@ pub struct TimelineState {
/// Visible span start (seconds) and pixels per second; None = fit comp.
pub start: f64,
pub pps: Option<f64>,
/// The span (`start`, `pps`) Zoom to Fit Comp left, which it goes back to when pressed again.
#[serde(skip)]
pub before_fit: Option<(f64, f64)>,
pub scroll_y: f32,
/// Left column area width.
pub columns_w: f32,
@@ -403,6 +406,7 @@ impl Default for TimelineState {
TimelineState {
start: 0.0,
pps: None,
before_fit: None,
scroll_y: 0.0,
columns_w: 560.0,
outline_scroll: 0.0,
+23
View File
@@ -163,6 +163,29 @@ fn semicolon_toggles_frame_level_and_the_whole_comp() {
assert_eq!(h.state().ui.timeline.pps, None);
}
/// `Shift+;` zooms out to the whole comp, and again back in to the span it left.
#[test]
fn shift_semicolon_toggles_the_whole_comp_and_the_span_before() {
let (mut h, _) = harness();
h.state_mut().session.execute("time.set", json!({"time": 120.0})).unwrap();
h.run_steps(3);
let ctx = h.ctx.clone();
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "timeline.zoomFrameToggle", json!({})).unwrap();
h.run_steps(2);
let before = (h.state().ui.timeline.start, h.state().ui.timeline.pps);
assert!(before.1.is_some());
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "timeline.zoomFit", json!({})).unwrap();
assert_eq!(h.state().ui.timeline.pps, None);
h.run_steps(2);
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "timeline.zoomFit", json!({})).unwrap();
assert_eq!((h.state().ui.timeline.start, h.state().ui.timeline.pps), before);
// With nothing to go back to, the whole comp stays.
h.state_mut().ui.timeline.pps = None;
h.state_mut().ui.timeline.before_fit = None;
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "timeline.zoomFit", json!({})).unwrap();
assert_eq!(h.state().ui.timeline.pps, None);
}
fn space(h: &mut Harness<'_, EffectcraftApp>, pressed: bool) {
h.event(Event::Key { key: egui::Key::Space, physical_key: None, pressed, repeat: false, modifiers: Modifiers::NONE });
h.step();