From e82fd3d5ca7bd962e5df9d847eddd23355fba946 Mon Sep 17 00:00:00 2001 From: Don Omondi <282319807+mockydevs@users.noreply.github.com> Date: Wed, 4 Nov 2026 21:36:54 -0500 Subject: [PATCH] Numeric fields: a decimal comma works in arithmetic next to a number with a point (#5684) A numeric field read a comma as a decimal point only when the text had no point at all. A field shows its value with a point (100.5 pt), so typing -20,5 after it gave 100.5 pt-20,5, which kept its comma and was refused. Each number now reads its own separator: commas become points unless the text reads as thousands (1,000, as before). A number written with both separators (1.234,5, 1,000.5) ends up with two points and is still refused rather than guessed at. --- crates/ui-egui/src/panels/tests_constrain.rs | 15 +++++++++++++++ crates/ui-egui/src/widgets.rs | 15 ++++++++++++--- crates/ui-egui/src/widgets/numeric.rs | 8 +++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/panels/tests_constrain.rs b/crates/ui-egui/src/panels/tests_constrain.rs index 8fa4c6e02..a90f2b55d 100644 --- a/crates/ui-egui/src/panels/tests_constrain.rs +++ b/crates/ui-egui/src/panels/tests_constrain.rs @@ -256,6 +256,21 @@ fn a_thousands_comma_in_a_percentage_is_refused_in_a_size_field() { } } +/// Arithmetic with a decimal comma works in W (#5684), also after a value shown with a point; a +/// number with both separators is refused and leaves the size as it was. +#[test] +fn arithmetic_with_a_decimal_comma_works_in_a_size_field() { + let surfaces: [(&str, Draw); 3] = + [("Transform panel", transform::show), ("Properties", properties::transform_section), ("Control bar", crate::chrome::control_bar)]; + for (name, draw) in surfaces { + for (text, want) in [("100,5-20,5", 80.0), ("90-0,5*20", 80.0), ("10,5*2", 21.0), ("100.5 pt-20,5", 80.0), ("1.234,5", 100.0)] { + let mut app = rect_app(); + set_field(&mut app, draw, "100 pt", text, End::Enter(Modifiers::NONE)); + assert_eq!(size(&app), (want, 50.0), "{name}, {text}"); + } + } +} + /// A length field of the New Document dialog keeps the focus on Ctrl/Cmd+Enter, and the dialog /// creates no document. #[test] diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index ce798eef6..b5bc1991b 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -441,9 +441,9 @@ mod step_to_multiple_tests { mod decimal_point_tests { use super::*; - /// A decimal comma reads as a point, in a length, with arithmetic and in a percentage; text - /// with a point keeps its commas and is refused as before, and so does a thousands comma - /// (#3837). + /// A decimal comma reads as a point, in a length, with arithmetic and in a percentage, next to + /// numbers written with a point too (#5684); a number with both separators is refused, and so + /// is a thousands comma (#3837). #[test] fn a_decimal_comma_reads_as_a_point() { let mm = |s: &str| Unit::Millimeters.parse(&decimal_point(s)).map(|pt| Unit::Millimeters.from_pt(pt)); @@ -460,6 +460,15 @@ mod decimal_point_tests { let got = mm(text).unwrap_or(f64::NAN); assert!((got - want).abs() < 1e-9, "{text} is a decimal: {got}"); } + // Each number reads its own separator: a comma typed after a value shown with a point + // (#5684). A number with both separators is refused. + for (text, want) in [("100,5-20", 80.5), ("100-0,5", 99.5), ("10,5*2", 21.0), ("100.5 mm-0,5", 100.0), ("0,5+100.25", 100.75)] { + let got = mm(text).unwrap_or(f64::NAN); + assert!((got - want).abs() < 1e-9, "{text}: {got}"); + } + for both in ["1.234,5", "1,234.5", "1.5,5-2"] { + assert_eq!(mm(both), None, "{both}: a point and a comma in one number"); + } assert_eq!(share_of("12,5%", 200.0), Some(25.0)); assert_eq!(share_of("1,000%", 200.0), None, "1,000% reads as thousands: refused, not 1%"); } diff --git a/crates/ui-egui/src/widgets/numeric.rs b/crates/ui-egui/src/widgets/numeric.rs index dfa68ac12..ed1f257ee 100644 --- a/crates/ui-egui/src/widgets/numeric.rs +++ b/crates/ui-egui/src/widgets/numeric.rs @@ -20,10 +20,12 @@ pub(crate) fn typed_unit(ctx: &egui::Context, unit: Unit) -> Unit { } /// `text` typed in a numeric field, with a decimal comma read as a point: `0,1 mm` is `0.1 mm` -/// (#3837). Text with a point keeps its commas, so `1,000.5` is still refused rather than read -/// with two decimal separators, and so does a number that reads as thousands ([`thousands`]). +/// (#3837), and each number of a sum reads its own separator, so `100.5 mm-0,5` (a comma typed +/// after the value the field shows) is 100 mm (#5684). A number written with both separators has +/// two points once read, so `1,000.5` and `1.234,5` are refused rather than guessed at; so is a +/// number that reads as thousands ([`thousands`]). pub(super) fn decimal_point(text: &str) -> std::borrow::Cow<'_, str> { - if text.contains(',') && !text.contains('.') && !thousands(text) { text.replace(',', ".").into() } else { text.into() } + if text.contains(',') && !thousands(text) { text.replace(',', ".").into() } else { text.into() } } /// Is `text` a number written with a thousands comma, as `1,000` or `12,500 mm`: one to three