From 276233ea0712d4b4cfd70d9da089bf7822541927 Mon Sep 17 00:00:00 2001 From: zitann2020 Date: Sat, 31 Oct 2026 02:09:49 +0900 Subject: [PATCH 1/5] ui: Fast Previews sits beside the resolution control, where After Effects puts it Co-Authored-By: DeepSeek V4.1 Flash --- crates/ui-egui/src/panels/viewer_tools.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/ui-egui/src/panels/viewer_tools.rs b/crates/ui-egui/src/panels/viewer_tools.rs index 3429f5404..d3b05fe13 100644 --- a/crates/ui-egui/src/panels/viewer_tools.rs +++ b/crates/ui-egui/src/panels/viewer_tools.rs @@ -779,6 +779,16 @@ fn bottom_bar_contents(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect, z *x += 24.0; (resp.clicked(), r) }; + // Fast Previews. + let fp = app.session.state.viewer.fast_previews; + let (clicked, fr) = tog(ui, &mut app.auto, &mut x, Icon::Sparkle, fp != FastPreviews::Off || app.ui.viewer.fast_preview, "fastPreviews", "Fast Previews"); + if clicked { + toggle_popup(ui, "vw-fast-pop"); + } + let items: Vec<(String, bool)> = FastPreviews::ALL.iter().map(|m| (m.label().to_string(), fp == *m)).collect(); + if let Some(i) = popup(app, ui, "vw-fast-pop", fr, &items, "fastPreviewsItem") { + let _ = app.session.execute("view.fastPreviewMode", json!({"mode": FastPreviews::ALL[i].id()})); + } if tog(ui, &mut app.auto, &mut x, Icon::Checker, app.ui.viewer.transparency_grid, "transparency", "Toggle Transparency Grid").0 { app.ui.viewer.transparency_grid = !app.ui.viewer.transparency_grid; } @@ -861,16 +871,6 @@ fn bottom_bar_contents(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect, z if app.session.snapshot.is_some() && held != app.session.state.viewer.show_snapshot { let _ = app.session.execute("view.showSnapshot", json!({"value": held})); } - // Fast Previews. - let fp = vo.fast_previews; - let (clicked, fr) = tog(ui, &mut app.auto, &mut x, Icon::Sparkle, fp != FastPreviews::Off || app.ui.viewer.fast_preview, "fastPreviews", "Fast Previews"); - if clicked { - toggle_popup(ui, "vw-fast-pop"); - } - let items: Vec<(String, bool)> = FastPreviews::ALL.iter().map(|m| (m.label().to_string(), fp == *m)).collect(); - if let Some(i) = popup(app, ui, "vw-fast-pop", fr, &items, "fastPreviewsItem") { - let _ = app.session.execute("view.fastPreviewMode", json!({"mode": FastPreviews::ALL[i].id()})); - } x += 4.0; // 3D renderer + view. if comp.has_3d() { From cd3436dc5499009bc484acafed1c84fa3977877e Mon Sep 17 00:00:00 2001 From: zitann2020 Date: Sat, 31 Oct 2026 02:37:11 +0900 Subject: [PATCH 2/5] ui: the bottom bar's comment lists Fast Previews where it is drawn Co-Authored-By: DeepSeek V4.1 Flash --- crates/ui-egui/src/panels/viewer_tools.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/panels/viewer_tools.rs b/crates/ui-egui/src/panels/viewer_tools.rs index d3b05fe13..514fe2e34 100644 --- a/crates/ui-egui/src/panels/viewer_tools.rs +++ b/crates/ui-egui/src/panels/viewer_tools.rs @@ -669,8 +669,8 @@ pub(crate) fn resolution_label(res: Resolution, scale: f64) -> String { } /// The Composition panel's bottom bar, in After Effects 2026 order: magnification, resolution, -/// transparency grid, mask and shape path visibility, region of interest, grid and guide -/// options, show channel, reset exposure, exposure, take / show snapshot, Fast Previews, the 3D +/// Fast Previews, transparency grid, mask and shape path visibility, region of interest, grid and +/// guide options, show channel, reset exposure, exposure, take / show snapshot, the 3D /// renderer and view, and the current time. pub(crate) fn bottom_bar(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect, zoom: f32, time: Tick, comp: &Comp) { let t = app.tokens; @@ -721,7 +721,8 @@ fn exposure_field(app: &mut EffectcraftApp, ui: &mut egui::Ui, er: Rect, stops: } } -/// Lay out the viewer's control row from `bar.min.x`: magnification, resolution, transparency, mask, ROI, grid/guide, +/// Lay out the viewer's control row from `bar.min.x`: magnification, resolution, Fast Previews, +/// transparency, mask, ROI, grid/guide, /// channel/colour management, exposure, snapshot, fast previews, 3D renderer/view and Extended Viewer, timecode and render-time /// indicators, running commands or toggling viewer UI state. Returns the width the row /// occupies, at least `bar.width()`, for the horizontal scroll area. From 1033e705c942ac6d26e688a0aaf20dbb99fa7df6 Mon Sep 17 00:00:00 2001 From: zitann2020 Date: Sat, 31 Oct 2026 02:50:38 +0900 Subject: [PATCH 3/5] ui: the control row's comment matches the order it draws Co-Authored-By: DeepSeek V4.1 Flash --- crates/ui-egui/src/panels/viewer_tools.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/panels/viewer_tools.rs b/crates/ui-egui/src/panels/viewer_tools.rs index 514fe2e34..5e5a644e8 100644 --- a/crates/ui-egui/src/panels/viewer_tools.rs +++ b/crates/ui-egui/src/panels/viewer_tools.rs @@ -722,10 +722,10 @@ fn exposure_field(app: &mut EffectcraftApp, ui: &mut egui::Ui, er: Rect, stops: } /// Lay out the viewer's control row from `bar.min.x`: magnification, resolution, Fast Previews, -/// transparency, mask, ROI, grid/guide, -/// channel/colour management, exposure, snapshot, fast previews, 3D renderer/view and Extended Viewer, timecode and render-time -/// indicators, running commands or toggling viewer UI state. Returns the width the row -/// occupies, at least `bar.width()`, for the horizontal scroll area. +/// transparency, mask, ROI, grid/guide, channel/colour management, exposure, snapshot, 3D +/// renderer/view and Extended Viewer, timecode and render-time indicators, running commands or +/// toggling viewer UI state. Returns the width the row occupies, at least `bar.width()`, for the +/// horizontal scroll area. fn bottom_bar_contents(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect, zoom: f32, time: Tick, comp: &Comp) -> f32 { let t = app.tokens; let p = ui.painter().clone(); From 2efdea0bc220526eb2bca71b10f66bfffe283f1f Mon Sep 17 00:00:00 2001 From: zitann2020 Date: Sat, 31 Oct 2026 04:27:18 +0900 Subject: [PATCH 4/5] test: the bottom bar's order test follows After Effects' order Co-Authored-By: DeepSeek V4.1 Flash --- crates/ui-egui/tests/it/ui_viewer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ui-egui/tests/it/ui_viewer.rs b/crates/ui-egui/tests/it/ui_viewer.rs index 0ab31237b..f0086d28a 100644 --- a/crates/ui-egui/tests/it/ui_viewer.rs +++ b/crates/ui-egui/tests/it/ui_viewer.rs @@ -281,6 +281,7 @@ fn bottom_bar_in_after_effects_order() { let order = [ "viewer.magnification", "viewer.resolution", + "viewer.fastPreviews", "viewer.transparency", "viewer.masks", "viewer.roi", @@ -290,7 +291,6 @@ fn bottom_bar_in_after_effects_order() { "viewer.exposure", "viewer.snapshot", "viewer.showSnapshot", - "viewer.fastPreviews", "viewer.timecode", ]; let xs: Vec = order.iter().map(|id| rect(&h, id).min.x).collect(); From 4cb3a8c6993e19f58c957098c8e9050feb52ad41 Mon Sep 17 00:00:00 2001 From: zitann2020 Date: Sat, 31 Oct 2026 04:41:41 +0900 Subject: [PATCH 5/5] test: the compact row scrolls back before reaching Fast Previews Co-Authored-By: DeepSeek V4.1 Flash --- crates/ui-egui/tests/it/viewer_geometry.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/ui-egui/tests/it/viewer_geometry.rs b/crates/ui-egui/tests/it/viewer_geometry.rs index d57a8aa9e..8a212cc35 100644 --- a/crates/ui-egui/tests/it/viewer_geometry.rs +++ b/crates/ui-egui/tests/it/viewer_geometry.rs @@ -143,6 +143,16 @@ fn compact_control_row_scrolls_and_preserves_interactions() { h.run_steps(3); assert!(!h.state().session.state.viewer.show_snapshot, "releasing the snapshot control must return to the current frame"); + // Fast Previews sits beside the resolution control, so the row that scrolled to the timecode has + // to scroll back before its menu is reachable. + h.event(Event::MouseWheel { + unit: egui::MouseWheelUnit::Point, + delta: vec2(1500.0, 0.0), + modifiers: Default::default(), + phase: egui::TouchPhase::Move, + source: egui::MouseWheelSource::Wheel, + }); + h.run_steps(30); click_control(&mut h, "viewer.fastPreviews"); click_control(&mut h, "viewer.fastPreviewsItem.1"); assert_eq!(h.state().session.state.viewer.fast_previews.id(), "adaptive", "a scrolled dropdown must retain its menu command");