From 8b08718b9afee8439e951a654903e62ac0fb4f7c Mon Sep 17 00:00:00 2001 From: Gert Pellin Date: Thu, 15 Oct 2026 01:49:41 +0200 Subject: [PATCH] Preferences: Interface size applies when the slider is let go (#767) Dragging the Interface size slider set the interface scale on every frame, which grew or shrank the slider under the pointer and ran it to one of its ends. Keep the value pending in the dialog while the pointer holds the slider and apply it on release; keyboard changes still apply at once. --- crates/ui-egui/src/dialogs.rs | 18 +++++++++++++++--- crates/ui-egui/src/lib.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 18d112c8..453c4184 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -2109,9 +2109,21 @@ fn body(app: &mut SlideApp, ui: &mut Ui, d: &mut Dialog) -> bool { if ui.checkbox(&mut dark, "Dark appearance").changed() { let _ = app.run("view.dark", json!({"on": dark})); } - let mut scale = app.ui.ui_scale; - if ui.add(egui::Slider::new(&mut scale, 0.75..=1.75).text("Interface size")).changed() { - app.ui.ui_scale = scale; + // While the pointer holds the slider its value only waits in the dialog: applying it + // would rescale the slider under the pointer and run it to an end (#767). It applies + // on release, and at once from the keyboard. + let pending = d.fields.get("uiScale").and_then(|v| v.parse::().ok()); + let mut scale = pending.unwrap_or(app.ui.ui_scale); + let r = ui.add(egui::Slider::new(&mut scale, 0.75..=1.75).text("Interface size")); + if r.is_pointer_button_down_on() { + if r.changed() || pending.is_some() { + d.fields.insert("uiScale".into(), scale.to_string()); + } + } else { + if r.changed() || pending.is_some() { + app.ui.ui_scale = scale; + } + d.fields.remove("uiScale"); } ui.label(egui::RichText::new("Editing").font(theme::bold(13.0))); ui.checkbox(&mut app.session.prefs.smart_guides, "Show smart guides"); diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index f020f5f5..f46b51c9 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -3253,4 +3253,38 @@ mod tests { flick(&mut harness, egui::vec2(-24.0, 0.0)); assert_eq!(harness.state().show.as_ref().map(|s| s.state.slide), Some(0)); } + + /// Preferences › Interface size (#767): dragging the slider leaves the interface size alone + /// until the pointer lets go, so the slider doesn't grow under the pointer mid-drag. + #[test] + fn interface_size_applies_when_the_slider_is_let_go() { + let mut harness = egui_kittest::Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_ui_state( + |ui, app: &mut SlideApp| { + app.logic(ui.ctx()); + app.ui(ui); + }, + SlideApp::new(Session::with_new(), Services::default()), + ); + harness.run_steps(2); + harness.state_mut().run("app.preferences", json!({})).unwrap(); + harness.run_steps(4); + let before = harness.state().ui.ui_scale; + let rect = + harness.query_all_by_role_and_label(egui::accesskit::Role::Slider, "Interface size").next().expect("the Interface size slider").rect(); + let (from, to) = (rect.left_center() + egui::vec2(4.0, 0.0), rect.right_center() - egui::vec2(4.0, 0.0)); + harness.hover_at(from); + harness.run_steps(1); + harness.drag_at(from); + harness.run_steps(1); + for k in 1..=8 { + harness.hover_at(from.lerp(to, k as f32 / 8.0)); + harness.run_steps(1); + } + assert_eq!(harness.state().ui.ui_scale, before, "nothing rescales while the slider is held"); + harness.drop_at(to); + harness.run_steps(2); + let after = harness.state().ui.ui_scale; + assert!(after > before + 0.3, "letting go applies the size dragged to: {before} -> {after}"); + assert!(harness.state().dialog.is_some(), "Preferences stays open"); + } }