Merge #2116: ui: Spacebar hand pans timeline rows vertically (#2083) (@gmrmad1)

This commit is contained in:
aiongg committed 2026-10-23 21:26:10 +00:00
commit 38ca712cc2
3 files changed
+73 -6

No files matched your search

+11 -6
View File
@@ -3330,8 +3330,9 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
super::markers_ui::comp_markers(app, ui, &comp, tm, mstrip, mrows);
super::markers_ui::marker_bin(app, ui, &comp, tm, bin, mstrip);
// Spacebar held: the Hand tool. Dragging the ruler or the time graph scrolls it in time when
// zoomed in (registered last, so it takes the drag from the bars, keys and markers there).
// Spacebar held: the Hand tool. Dragging pans the rows vertically as well as scrolling the
// time graph in time when zoomed in (registered last, so it takes the drag from the bars,
// keys and markers there).
if super::space_hand(&ctx) {
let area = Rect::from_min_max(pos2(graph_x0, ruler.min.y), pos2(rect.max.x, rows_rect.max.y));
let hand = ui.interact(area, egui::Id::new("tl-hand"), Sense::drag());
@@ -3339,10 +3340,14 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
if hand.hovered() || hand.dragged() {
ctx.set_cursor_icon(if hand.dragged() { egui::CursorIcon::Grabbing } else { egui::CursorIcon::Grab });
}
if hand.dragged() && app.ui.timeline.pps.is_some() {
let start = app.ui.timeline.start;
let last = (comp.duration.seconds() - (tm.t(graph_x1) - tm.start)).max(start).max(0.0);
app.ui.timeline.start = (start - hand.drag_delta().x as f64 / pps).clamp(0.0, last);
if hand.dragged() {
let dy = ui.input(|i| i.pointer.delta().y);
app.ui.timeline.scroll_y = (app.ui.timeline.scroll_y - dy).clamp(0.0, max_scroll);
if app.ui.timeline.pps.is_some() {
let start = app.ui.timeline.start;
let last = (comp.duration.seconds() - (tm.t(graph_x1) - tm.start)).max(start).max(0.0);
app.ui.timeline.start = (start - hand.drag_delta().x as f64 / pps).clamp(0.0, last);
}
}
}
+1
View File
@@ -63,6 +63,7 @@ mod ui_text_range_handles;
mod ui_theme_favorites;
mod ui_time_zoom_controls;
mod ui_timeline_effect_label;
mod ui_timeline_pan;
mod ui_timeline_shapes;
mod ui_timeline_switches;
mod ui_timeline_trim;
@@ -0,0 +1,61 @@
//! Spacebar-drag panning in the Timeline: the Hand tool scrolls the layer rows vertically as well
//! as the time graph horizontally (#2083).
use effectcraft_engine::Session;
use effectcraft_ui_egui::EffectcraftApp;
use egui::{Event, Modifiers, Pos2, pos2, vec2};
use egui_kittest::Harness;
use serde_json::json;
/// A 10 s comp with 40 solids, enough rows to scroll.
fn harness() -> (Harness<'static, EffectcraftApp>, u64) {
let mut s = Session::default();
s.execute("comp.new", json!({"name": "Many", "width": 320, "height": 180, "frameRate": 30, "duration": 10})).unwrap();
let mut top = 0;
for i in 0..40 {
top = s.execute("layer.newSolid", json!({"name": format!("Layer {i}"), "color": "#406080"})).unwrap()["layer"].as_u64().unwrap();
}
s.execute("layer.select", json!({"layers": []})).unwrap();
let app = EffectcraftApp::new(s);
let mut h = Harness::builder().with_size(vec2(1200.0, 800.0)).build_eframe(|_| app);
h.run_steps(4);
(h, top)
}
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();
}
fn drag(h: &mut Harness<'_, EffectcraftApp>, from: Pos2, to: Pos2) {
h.event(Event::PointerMoved(from));
h.step();
h.event(Event::PointerButton { pos: from, button: egui::PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE });
h.step();
for k in 1..=10 {
h.event(Event::PointerMoved(from + (to - from) * (k as f32 / 10.0)));
h.step();
}
h.event(Event::PointerButton { pos: to, button: egui::PointerButton::Primary, pressed: false, modifiers: Modifiers::NONE });
h.run_steps(3);
}
#[test]
fn spacebar_drag_pans_the_rows_vertically() {
let (mut h, layer) = harness();
assert_eq!(h.state().ui.timeline.scroll_y, 0.0);
let bar = h.state().auto.find(&format!("timeline.layer.{layer}.bar")).expect("layer bar").rect;
let from = pos2(bar[0] + 40.0, bar[1] + bar[3] / 2.0);
let timing = |h: &Harness<'_, EffectcraftApp>| {
let l = &h.state().session.active_comp().unwrap().layers[0];
(l.in_point.seconds(), l.out_point.seconds(), l.start_time.seconds())
};
let before = timing(&h);
space(&mut h, true);
drag(&mut h, from, from - vec2(0.0, 180.0));
space(&mut h, false);
let y = h.state().ui.timeline.scroll_y;
assert!(y > 100.0, "the rows scrolled vertically: {y}");
assert_eq!(h.state().ui.timeline.start, 0.0, "the time graph did not pan sideways");
assert_eq!(timing(&h), before, "the layer under the hand did not move");
}