From 15e45069cc841b078a3ac133c9cbf9c56667cb61 Mon Sep 17 00:00:00 2001 From: ".CooCoo" Date: Tue, 3 Nov 2026 06:20:09 -0800 Subject: [PATCH] Keep disabled scroll animations in Preferences (#5886) --- crates/ui-egui/src/dialogs.rs | 17 ++++++++++---- crates/ui-egui/src/dialogs/tests.rs | 35 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 crates/ui-egui/src/dialogs/tests.rs diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 048d519222..84c36f41aa 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -20,6 +20,9 @@ pub(super) mod protect; pub(super) mod standards_ui; pub(super) mod word_count_ui; +#[cfg(test)] +mod tests; + pub(crate) use prompts::{PromptButton, save_prompt_key}; use prompts::{full_screen_prompt, link_prompt, password, remote_link_prompt, save_prompt, unlock}; @@ -1371,20 +1374,26 @@ fn dialog_preferences(ui: &mut egui::Ui, app: &mut PdfCraftApp, req: &mut Pendin // (which only applies when the settings overflow it) opens the dialog at full size. // A scrollbar of its own: the floating one lies over the right edge of the theme // cards and other full-width rows (#3557). + preferences_scroll(ui, max, |ui| preferences::preferences_body(ui, app, &t)); + req.close = preferences::preferences_buttons(ui); + false +} + +fn preferences_scroll(ui: &mut egui::Ui, max: f32, add_contents: impl FnOnce(&mut egui::Ui)) { ui.scope(|ui| { ui.spacing_mut().scroll.floating = false; ui.spacing_mut().scroll.bar_inner_margin = 14.0; ui.spacing_mut().scroll.bar_outer_margin = 2.0; - ui.style_mut().scroll_animation = egui::style::ScrollAnimation::duration(0.25); + if ui.style().scroll_animation != egui::style::ScrollAnimation::none() { + ui.style_mut().scroll_animation = egui::style::ScrollAnimation::duration(0.25); + } egui::ScrollArea::vertical() .id_salt("preferences-dialog-scroll") .max_height(max) .min_scrolled_height(max) .animated(true) - .show(ui, |ui| preferences::preferences_body(ui, app, &t)); + .show(ui, add_contents); }); - req.close = preferences::preferences_buttons(ui); - false } /// `Dialog::NumberPages` content. Returns `false` when it has its own buttons and no footer is wanted. diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs new file mode 100644 index 0000000000..03f5c7c5b8 --- /dev/null +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -0,0 +1,35 @@ +use std::cell::Cell; +use std::rc::Rc; + +use egui_kittest::Harness; +use egui_kittest::kittest::Queryable; + +#[test] +fn preferences_scroll_preserves_disabled_animation() { + for animated in [false, true] { + let scroll = Rc::new(Cell::new(false)); + let requested = scroll.clone(); + let mut h = Harness::builder().with_size(egui::vec2(600.0, 400.0)).with_step_dt(1.0 / 60.0).build_ui(move |ui| { + super::preferences_scroll(ui, 200.0, |ui| { + ui.add_space(1000.0); + let target = ui.label("Scroll target"); + if requested.replace(false) { + ui.scroll_to_rect(target.rect, Some(egui::Align::BOTTOM)); + } + ui.add_space(300.0); + }); + }); + h.ctx.all_styles_mut(|style| { + style.scroll_animation = if animated { egui::style::ScrollAnimation::default() } else { egui::style::ScrollAnimation::none() }; + }); + h.run_steps(4); + let before = h.get_by_label("Scroll target").rect().top(); + scroll.set(true); + h.run_steps(4); + let early = h.get_by_label("Scroll target").rect().top(); + h.run_steps(20); + let settled = h.get_by_label("Scroll target").rect().top(); + assert!((before - settled).abs() > 50.0, "the target must scroll: {before} -> {settled}"); + assert_eq!((early - settled).abs() > 1.0, animated, "animated: {animated}, early: {early}, settled: {settled}"); + } +}