diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index abe8c87e..576a2297 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -2142,9 +2142,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 ce196a5a..88674070 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -3322,4 +3322,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"); + } }