Merge pull request #3419 from nanox333/fix/3373-viewer-time-click

Viewer: clicking the current-time display opens Go to Time (#3373)
This commit is contained in:
ash authored and GitHub committed 2026-11-11 16:01:30 +05:30
commit 6aca5e473c
4 files changed
+51 -6

No files matched your search

-3
View File
@@ -3651,7 +3651,6 @@ const SWEDISH: &[(&str, &str)] = &[
("Smoothing", "Utjämning"),
("Top", "Överkant"),
("Mode", "Läge"),
("Frame", "Bildruta"),
("Orientation", "Orientering"),
("Create", "Skapa"),
("Overlap", "Överlappning"),
@@ -8866,7 +8865,6 @@ const GEORGIAN_PANELS: &[(&str, &str)] = &[
("Height", "სიმაღლე"),
("Shape", "ფიგურა"),
("Mode", "რეჟიმი"),
("Frame", "კადრი"),
("Orientation", "ორიენტაცია"),
("Position (pixels)", "პოზიცია (პიქსელი)"),
("Position (percent of the composition)", "პოზიცია (კომპოზიციის პროცენტი)"),
@@ -9849,7 +9847,6 @@ const FRENCH: &[(&str, &str)] = &[
("Height", "Hauteur"),
("Shape", "Forme"),
("Mode", "Mode"),
("Frame", "Image"),
("Orientation", "Orientation"),
("Position (pixels)", "Position (pixels)"),
("Position (percent of the composition)", "Position (pourcentage de la composition)"),
+24 -2
View File
@@ -630,9 +630,11 @@ pub fn open_form(app: &mut EffectcraftApp, id: &str, p: &Value) -> bool {
)],
)
}
// The time as the project displays it (timecode, or frames from Start Numbering Frames At,
// or feet + frames), which `time.set` reads back the same way.
"time.set" if !has(p, &["time", "frame", "timecode"]) => {
let frame = comp.map(|c| c.frame_rate.frame_at(t)).unwrap_or(0);
("Go to Time".into(), vec![Field::num("frame", "Frame", frame as f64)])
let shown = comp.map(|c| crate::panels::timecode(s, c, t)).unwrap_or_default();
("Go to Time".into(), vec![Field::text("timecode", "Time", &shown)])
}
"view.addGuide" if !has(p, &["position"]) => {
let (w, _) = comp.map(|c| (c.width, c.height)).unwrap_or((1920, 1080));
@@ -1108,6 +1110,26 @@ mod tests {
assert!(app.dialog.is_none(), "one Enter confirms");
}
/// Go to Time shows the time as the project displays it, so with Frames numbered from 1 the
/// viewer's frame 00013 opens as 00013 and 00020 goes to the frame displayed 00020 (the
/// engine's frame 19). The dialog used to show and take the engine's own frame number (#3373).
#[test]
fn go_to_time_uses_the_projects_frame_numbering() {
let mut app = EffectcraftApp::new(effectcraft_engine::Session::default());
app.session.execute("comp.new", json!({"name": "Main", "width": 64, "height": 36, "frameRate": 24, "duration": 4})).unwrap();
app.session.execute("file.projectSettings", json!({"timeDisplay": "frames", "frameStart": 1})).unwrap();
app.session.execute("time.set", json!({"frame": 12})).unwrap();
assert!(open_form(&mut app, "time.set", &json!({})));
let f = &app.dialog_state.form;
assert_eq!(f.command, "time.set");
assert!(matches!(&f.fields[0].kind, FieldKind::Text(t) if t == "00013"), "{:?}", f.fields[0]);
app.dialog_state.form.fields[0].kind = FieldKind::Text("00020".into());
let (command, params) = (app.dialog_state.form.command.clone(), app.dialog_state.form.params());
app.session.execute(&command, params).unwrap();
let at = app.session.active_comp().unwrap().frame_rate.frame_at(app.session.time());
assert_eq!(at, 19, "displayed 00020 with frames starting at 1");
}
/// File ▸ Export ▸ Adobe Premiere Pro Project… opens a form that says it writes Final Cut Pro
/// XML; its fields register automation ids and OK runs `file.exportTimeline`.
#[test]
@@ -981,6 +981,7 @@ fn bottom_bar_contents(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect, z
}
app.auto.add_response("viewer.timecode", &resp, "Current time");
crate::panels::toggle_time_display(app, &resp);
go_to_time_on_click(app, &resp);
// Right: render time.
let ms = app.frames.last_ms.lock().map(|v| *v).unwrap_or(0.0);
if tr.max.x + 50.0 < bar.max.x {
@@ -989,6 +990,14 @@ fn bottom_bar_contents(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect, z
(tr.right() + 8.0 - bar.left()).max(bar.width())
}
/// A plain click on the time display opens Go to Time, as it does in After Effects; Ctrl/Cmd+click
/// is [`crate::panels::toggle_time_display`] (#3373).
fn go_to_time_on_click(app: &mut EffectcraftApp, resp: &egui::Response) {
if resp.clicked() && !resp.ctx.input(|i| i.modifiers.command) {
crate::panels::forms::open_form(app, "time.set", &json!({}));
}
}
#[cfg(test)]
mod tests {
use super::*;
+18 -1
View File
@@ -438,8 +438,11 @@ fn ctrl_click_on_the_time_display_toggles_timecode_and_frames() {
click_with(&mut h, at, Modifiers::COMMAND);
assert_eq!(style(&h), TimeDisplayStyle::Timecode);
assert_eq!(shown(&h, "timeline.timecode"), "0:00:01:06");
// A plain click changes nothing.
// A plain click leaves the display alone: it opens Go to Time (#3373), closed here.
click_with(&mut h, at, Modifiers::NONE);
assert_eq!(style(&h), TimeDisplayStyle::Timecode);
key(&mut h, Key::Escape);
assert!(h.query_by_label("Go to Time").is_none(), "Go to Time closed");
let at = center(&h, "timeline.timecode");
click_with(&mut h, at, Modifiers::NONE);
assert_eq!(style(&h), TimeDisplayStyle::Timecode);
@@ -746,3 +749,17 @@ fn typing_one_component_with_several_layers_selected_keeps_their_other_component
assert_eq!(pos(&h, a), [40.0, 50.0, -300.0]);
assert_eq!(pos(&h, b), [200.0, 120.0, -300.0], "B keeps its own X and Y");
}
/// #3373: a click on the Composition panel's time display opens Go to Time, as in After Effects;
/// Ctrl/Cmd+click still switches between timecode and frames.
#[test]
fn clicking_the_viewer_time_display_opens_go_to_time() {
let (mut h, _) = harness();
h.state_mut().session.execute("time.set", json!({"frame": 30})).unwrap();
h.run_steps(2);
assert!(h.query_by_label("Go to Time").is_none(), "set up: no dialog yet");
let at = center(&h, "viewer.timecode");
click(&mut h, at, 1);
h.run_steps(2);
assert!(h.query_by_label("Go to Time").is_some(), "the dialog is open");
}