Merge pull request #4494 from Silvertree2010/fix/4490-ui-text-field-rounding-behaviour

Round stepper button steps like arrow-key steps
This commit is contained in:
BFlat
2026-10-29 15:42:12 +07:00
committed by GitHub
2 changed files with 27 additions and 8 deletions
+13 -1
View File
@@ -293,6 +293,18 @@ mod spinner_modifier_tests {
assert_eq!(spinner_step(0.0, 1.0, 0.0, false, egui::Modifiers::SHIFT, &[], &pt), 0.0);
}
#[test]
fn plain_and_command_steps_go_to_the_next_multiple_as_arrow_keys_do() {
// As ↑/↓ in the field (#2881): a plain step goes to the next whole number, Ctrl/Cmd to the
// next tenth, rounded to the places fields keep; Shift adds or takes ten.
assert_eq!(spinner_step(1.37, 1.0, 0.0, true, egui::Modifiers::NONE, &[], &pt), 2.0);
assert_eq!(spinner_step(1.37, 1.0, 0.0, false, egui::Modifiers::NONE, &[], &pt), 1.0);
assert_eq!(spinner_step(1.37, 1.0, 0.0, true, egui::Modifiers::COMMAND, &[], &pt), 1.4);
assert_eq!(spinner_step(1.37, 1.0, 0.0, false, egui::Modifiers::COMMAND, &[], &pt), 1.3);
assert_eq!(spinner_step(1.37, 1.0, 0.0, true, egui::Modifiers::SHIFT, &[], &pt), 11.37);
assert_eq!(spinner_step(0.37, 1.0, 0.0, false, egui::Modifiers::NONE, &[], &pt), 0.0);
}
/// A plain click on the spinner stops at each stop; Shift and Ctrl/Cmd steps do not.
#[test]
fn a_plain_spinner_step_stops_at_the_stops() {
@@ -303,7 +315,7 @@ mod spinner_modifier_tests {
assert_eq!(spinner_step(0.1, 1.0, 0.0, false, plain, &stops, &pt), 0.0);
assert_eq!(spinner_step(14.0, 1.0, 0.0, true, plain, &stops, &pt), 15.0);
assert_eq!(spinner_step(14.0, 1.0, 0.0, false, egui::Modifiers::SHIFT, &stops, &pt), 4.0);
assert!((spinner_step(0.52, 1.0, 0.0, false, egui::Modifiers::COMMAND, &stops, &pt) - 0.42).abs() < 1e-9);
assert_eq!(spinner_step(0.52, 1.0, 0.0, true, egui::Modifiers::COMMAND, &stops, &pt), 0.6);
}
/// A stop the spinner's text shows as the value counts as reached: 2.004 pt and the 2 pt stop
+14 -7
View File
@@ -645,7 +645,9 @@ fn spin_generic(
/// Spinner buttons follow the same modifier sizes as ↑/↓ in a focused number field:
/// Shift ×10, Ctrl/Cmd ÷10, otherwise the caller's normal step, which also stops at each of
/// `stops` ([`step_through`]). A stop that `show` writes with the same text as the value counts as
/// reached, as in the field.
/// reached, as in the field. As with ↑/↓, a plain or Ctrl/Cmd step goes to the next multiple of its
/// size ([`step_to_multiple`]), a Shift step adds or takes ten steps, and the result is rounded to
/// [`STEP_DECIMALS`] places (#4490); a stop keeps its places.
pub(super) fn spinner_step(
value: f64,
step: f64,
@@ -655,12 +657,17 @@ pub(super) fn spinner_step(
stops: &[f64],
show: &dyn Fn(f64) -> String,
) -> f64 {
let (mods, multiplier) = STEPS.iter().find(|(m, _)| modifiers.matches_logically(*m)).map_or((egui::Modifiers::NONE, 1.0), |&(m, f)| (m, f));
if mods == egui::Modifiers::NONE && !stops.is_empty() {
return step_through(value, step, if up { 1.0 } else { -1.0 }, stops, shown_alike(show)).max(min);
}
let delta = step * multiplier;
(value + if up { delta } else { -delta }).max(min)
let (mods, multiplier) = STEPS.iter().find(|(m, _)| modifiers.matches_logically(*m)).copied().unwrap_or((egui::Modifiers::NONE, 1.0));
let size = step * multiplier;
let n = if up { 1.0 } else { -1.0 };
let v = if mods == egui::Modifiers::SHIFT {
value + size * n
} else if mods == egui::Modifiers::NONE && !stops.is_empty() {
step_through(value, size, n, stops, shown_alike(show))
} else {
step_to_multiple(value, size, n)
};
(if stops.contains(&v) { v } else { round_to(v, STEP_DECIMALS) }).max(min)
}
/// A number field that may be empty (Stroke dash/gap): `value` (points) shows in `unit` without