From ec105d0f5609796462e570e7bfff9b5b526acf91 Mon Sep 17 00:00:00 2001 From: Trevor Brown Date: Tue, 10 Nov 2026 14:44:12 -0500 Subject: [PATCH] M4: The radius field shows what a one-radius layer draws (#2222) A boolean group, polygon, star or vector draws its first radius. Per-corner values a script or a file left behind made its field read Mixed, and a relative edit started from the largest. The field now shows radii[0] for those kinds, and a relative edit starts from it; rectangles and frames are unchanged. Co-Authored-By: Claude Opus 5.5 Model-Effort: Opus 5.5 drove and wrote --- crates/ui-egui/src/field_tests.rs | 14 ++++++++++++++ crates/ui-egui/src/panels/right/paints.rs | 10 +++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/field_tests.rs b/crates/ui-egui/src/field_tests.rs index b71979f37..5af141b7d 100644 --- a/crates/ui-egui/src/field_tests.rs +++ b/crates/ui-egui/src/field_tests.rs @@ -1421,3 +1421,17 @@ fn a_boolean_group_has_one_corner_radius_field_and_a_mixed_selection_reads_mixed type_into(&mut u, "Corner radius", "8", Key::Enter); assert_eq!((radii(&u, g), radii(&u, r)), ([8.0; 4], [8.0; 4])); } + +/// A boolean group draws its first radius; per-corner values a script or a file left behind don't +/// make its field read Mixed, and a relative edit starts from the radius it draws. +#[test] +fn a_boolean_groups_field_shows_the_radius_it_draws() { + let (mut u, a, b) = setup(); + let g = id(u.app.session.execute("bool.union", &json!({"ids": [a, b]})).unwrap()); + let corners = json!({"topRightRadius": 30, "bottomRightRadius": 30, "bottomLeftRadius": 30}); + u.app.session.execute("node.set", &json!({"ids": [g], "props": corners})).unwrap(); + u.app.run("select.set", json!({"ids": [g]})); + assert_eq!(field(&mut u, "Corner radius").1, "0", "the radius it draws"); + type_into(&mut u, "Corner radius", "+10", Key::Enter); + assert_eq!(u.app.session.doc().unwrap().node(g).unwrap().corners.radii, [10.0; 4], "0 + 10, not 30 + 10"); +} diff --git a/crates/ui-egui/src/panels/right/paints.rs b/crates/ui-egui/src/panels/right/paints.rs index 157c65731..76f9c38c8 100644 --- a/crates/ui-egui/src/panels/right/paints.rs +++ b/crates/ui-egui/src/panels/right/paints.rs @@ -57,7 +57,10 @@ pub(super) fn appearance(app: &mut FigApp, ui: &mut Ui, doc: &Document, sel: &[N // For polygons, stars, boolean groups and closed vectors the same panel holds only the Corner // smoothing row. let smooth_ok = radius_ok && crate::selection_cache::every(doc, sel, |n| n.kind.has_corner_smoothing()); - let r = common(doc, sel, |n| n.corners.uniform().map(|r| (r * 100.0).round() / 100.0)); + // A layer with one radius (polygon, star, boolean group, vector) draws its first one, whatever + // the other three hold; a rectangle or frame shows a value only when its four corners agree. + let shown = |n: &Node| if n.kind.has_independent_corners() { n.corners.uniform() } else { n.corners.radii.first().copied() }; + let r = common(doc, sel, |n| shown(n).map(|r| (r * 100.0).round() / 100.0)); widgets::sublabels(ui, tr("Opacity"), radius_ok.then_some(tr("Corner radius"))); row(ui, 32.0, |ui| { let lead = Lead::Icon(Icon::Opacity, tr("Opacity")); @@ -75,8 +78,9 @@ pub(super) fn appearance(app: &mut FigApp, ui: &mut Ui, doc: &Document, sel: &[N "", col, ); - // A layer with corners of its own starts from its largest. - let radius_of = |n: &Node| n.corners.uniform().unwrap_or_else(|| n.corners.radii.iter().copied().fold(0.0, f64::max)); + // A relative edit starts from the radius shown; a rectangle with differing corners + // starts from its largest. + let radius_of = |n: &Node| shown(n).unwrap_or_else(|| n.corners.radii.iter().copied().fold(0.0, f64::max)); app.field_of(&ids, "cornerRadius", e, radius_of, |v| json!(v.max(0.0))); let open = ui.data(|d| d.get_temp::(Id::unique("indep_corners"))).unwrap_or(false); // Polygons, stars and boolean groups have no corners of their own: the button opens only