From c4e139af194055bd45ff4d5dea32e34a04d27de4 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sun, 11 Oct 2026 07:56:27 +0700 Subject: [PATCH] #885: every checkbox keeps a visible box at rest The theme leaves egui's own checkbox without an outline while inactive, so an unchecked box was invisible and a checked one a bare tick. #1080 moved Preferences to widgets::check; the remaining ui.checkbox uses follow: the Properties panel's Snap to Point, Snap to Grid and Scale Strokes & Effects, Revolve's Keep visible surfaces and Shading, Save options, Export for Screens, the Variables dialog and plug-in forms. widgets::check_name shows a document's name (an artboard's, an asset's) as it is, never translated; the check and radio rows translate their labels in the public functions, so choice_row draws text as given. Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 2 +- .../ui-egui/src/dialogs/export_for_screens.rs | 12 +++-- crates/ui-egui/src/dialogs/form.rs | 4 +- crates/ui-egui/src/dialogs/revolve.rs | 17 ++++--- crates/ui-egui/src/dialogs/save_options.rs | 6 +-- crates/ui-egui/src/dialogs/variables.rs | 16 +++--- crates/ui-egui/src/panels/properties.rs | 50 +++++++++++++++---- crates/ui-egui/src/widgets.rs | 29 ++++++----- 8 files changed, 88 insertions(+), 48 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index f3a267ef..0a13139b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -127,7 +127,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap | Date | What landed | |---|---| -| 2026-10-11 | Effect › Brush Strokes: Accented Edges, Angled Strokes, Crosshatch, Dark Strokes, Ink Outlines, Spatter, Sprayed Strokes, Sumi-e (G2). Command parameters from agents and files checked before anything changes (paint, place, cuts, dashes, crop, asset ids, anchors, paths, distort; #984–#999); command dialogs keep whole numbers whole, so Create Object Mosaic takes the tiles typed (#1000); stepper arrows take Shift (×10) and Ctrl/Cmd (÷10) like the arrow keys (#991). Exports and Rasterize supersample Art Optimized edges, so shapes meeting edge to edge leave no seam (#983). Character and paragraph styles take leading, kerning, mojikumi and the other attributes left out at their defaults (#1001). Drawing, typing and pasting with no layer shown and unlocked is refused instead of adding art to a hidden or locked layer (#1002). MCP `add_text` gives type on or in a path the colour asked for (#1003). MCP `screenshot` of a connected app renders the artboard asked for (#1004). `vectorcraft-cli run` ends quietly when its reader closes early (#1005). Select › All on Active Artboard selects on the artboard made active, not always the first (#1006). Reverse Gradient keeps off-centre midpoints, mirrored (#1007). A polar grid with no radial dividers draws none (#1008). Use Precise Cursors also covers the Pen's join pointer (#1009). SVGs with one absolute and one unitless root side import at their size, unstretched (#1010). DXF US survey inches, yards and miles open at their size and name (#1011). A file placed into a document with no other links follows its first change even while the app sits idle (#1031). PDF import keeps the style a font name says when the file doesn't embed the font (#1023). Shift selects ruler guides and art together, and Delete removes both (#1022). Open filled paths are hit by their fill on every side, the rotate cursor bows around the corner it is at, the flip icons match their flips, the Color Picker previews on the selection, and Properties › Transform reaches Scale Corners and Scale Strokes & Effects (#1036). Offset Path and the other booleans finish on outlines where the sweep used to loop until memory ran out (linesweeper 0.5, #1028). Floating panel groups stack into sets that move together, collapse to their tabs and dock as extra dock columns that scroll (#1020). Ruler guides live on layers: listed in the Layers panel, shown, hidden, locked, duplicated, merged and deleted with their layer, and moved between layers (#1021). A `.ai` whose editing data uses global process colours (`Xk`/`XK`) or pattern fills keeps its sublayers: the colours come in as global swatches at their tint, the patterns as pattern swatches (#1025). A `.ai` in PostScript form opens through its layers, read from the program itself, also when it names the prolog defining its operators without including it, as Rhino and other CAD apps write it (#1027). A fill or stroke chosen while the Type tool has characters selected colours just those characters (#1063). Scaled type shows its scaled font size, leading, baseline shift and horizontal scale in the Character panel, and a size typed there is the size you get (#1034). Character panel menu › Proportional Metrics sets Japanese and Chinese full-width glyphs on the font's proportional widths in horizontal type (#966). Align, Distribute, Move, Arrange, Lock, Hide, Ungroup and other Object commands that would change nothing no longer record an undo step or mark the document changed (#1060). Spot colours defined in Lab open with their Lab values from `.ai` and PDF files, instead of as wrong RGB colours or their CMYK equivalents (#1032). Build scripts leave a generated source they wouldn't change untouched, so the next build doesn't compile most of the workspace again where file times lose their sub-second part (#1051). A fill colour recolours the gradient mesh point last clicked with the Mesh tool or Direct Selection, and a Mesh tool click on a mesh's point selects that mesh (#1052). The All Tools drawer stays two tools wide instead of filling a wide window (#1054). Text Wrap's Make, Release and Options refuse a list of object ids with a malformed or unknown id instead of skipping it (#1055). A freeform gradient line with a malformed point index is refused instead of being shortened to the indices that are valid (#1056). The Corners dialog (`ui.corners`) refuses a malformed object id or a listed anchor that isn't a corner instead of editing the selection or the other corners (#1057). New View refuses a centre that isn't exactly [x, y] and a rotation that isn't a number, instead of trimming the one and saving the other as 0° (#1058). The command sweeps try junk scale, spacing and artboard values too, which they had skipped since before those bugs were fixed (#1059). EPS and PostScript `arc` and `arcn` with huge angles no longer hang the import, and an arc whose angles are whole turns apart is empty, as the PostScript reference defines it (#1062). Align and Distribute Spacing keep the key object still also when it is inside a selected group or compound path (#1074). A `.ai` whose layers can't be read yet still opens with its artboards where they were (in a grid, say), not in a row (#1068). Layer Options (`ui.layerOptions`) refuses a malformed or unknown row id instead of skipping it or opening on the highlighted rows (#1077). Shaper scribbles given a list of object ids refuse a malformed or unknown id instead of skipping it and editing the shapes that remain (#1078). Delete Width Point (`stroke.widthPoint.remove`) refuses a list of indices with a malformed entry instead of deleting the points that remain, and no longer wraps a huge index round on the web (#1079). Preferences' checkboxes have an outline when the pointer isn't over them, in every interface brightness, as the app's other dialogs' checkboxes do (#1080). | +| 2026-10-11 | Effect › Brush Strokes: Accented Edges, Angled Strokes, Crosshatch, Dark Strokes, Ink Outlines, Spatter, Sprayed Strokes, Sumi-e (G2). Command parameters from agents and files checked before anything changes (paint, place, cuts, dashes, crop, asset ids, anchors, paths, distort; #984–#999); command dialogs keep whole numbers whole, so Create Object Mosaic takes the tiles typed (#1000); stepper arrows take Shift (×10) and Ctrl/Cmd (÷10) like the arrow keys (#991). Exports and Rasterize supersample Art Optimized edges, so shapes meeting edge to edge leave no seam (#983). Character and paragraph styles take leading, kerning, mojikumi and the other attributes left out at their defaults (#1001). Drawing, typing and pasting with no layer shown and unlocked is refused instead of adding art to a hidden or locked layer (#1002). MCP `add_text` gives type on or in a path the colour asked for (#1003). MCP `screenshot` of a connected app renders the artboard asked for (#1004). `vectorcraft-cli run` ends quietly when its reader closes early (#1005). Select › All on Active Artboard selects on the artboard made active, not always the first (#1006). Reverse Gradient keeps off-centre midpoints, mirrored (#1007). A polar grid with no radial dividers draws none (#1008). Use Precise Cursors also covers the Pen's join pointer (#1009). SVGs with one absolute and one unitless root side import at their size, unstretched (#1010). DXF US survey inches, yards and miles open at their size and name (#1011). A file placed into a document with no other links follows its first change even while the app sits idle (#1031). PDF import keeps the style a font name says when the file doesn't embed the font (#1023). Shift selects ruler guides and art together, and Delete removes both (#1022). Open filled paths are hit by their fill on every side, the rotate cursor bows around the corner it is at, the flip icons match their flips, the Color Picker previews on the selection, and Properties › Transform reaches Scale Corners and Scale Strokes & Effects (#1036). Offset Path and the other booleans finish on outlines where the sweep used to loop until memory ran out (linesweeper 0.5, #1028). Floating panel groups stack into sets that move together, collapse to their tabs and dock as extra dock columns that scroll (#1020). Ruler guides live on layers: listed in the Layers panel, shown, hidden, locked, duplicated, merged and deleted with their layer, and moved between layers (#1021). A `.ai` whose editing data uses global process colours (`Xk`/`XK`) or pattern fills keeps its sublayers: the colours come in as global swatches at their tint, the patterns as pattern swatches (#1025). A `.ai` in PostScript form opens through its layers, read from the program itself, also when it names the prolog defining its operators without including it, as Rhino and other CAD apps write it (#1027). A fill or stroke chosen while the Type tool has characters selected colours just those characters (#1063). Scaled type shows its scaled font size, leading, baseline shift and horizontal scale in the Character panel, and a size typed there is the size you get (#1034). Character panel menu › Proportional Metrics sets Japanese and Chinese full-width glyphs on the font's proportional widths in horizontal type (#966). Align, Distribute, Move, Arrange, Lock, Hide, Ungroup and other Object commands that would change nothing no longer record an undo step or mark the document changed (#1060). Spot colours defined in Lab open with their Lab values from `.ai` and PDF files, instead of as wrong RGB colours or their CMYK equivalents (#1032). Build scripts leave a generated source they wouldn't change untouched, so the next build doesn't compile most of the workspace again where file times lose their sub-second part (#1051). A fill colour recolours the gradient mesh point last clicked with the Mesh tool or Direct Selection, and a Mesh tool click on a mesh's point selects that mesh (#1052). The All Tools drawer stays two tools wide instead of filling a wide window (#1054). Text Wrap's Make, Release and Options refuse a list of object ids with a malformed or unknown id instead of skipping it (#1055). A freeform gradient line with a malformed point index is refused instead of being shortened to the indices that are valid (#1056). The Corners dialog (`ui.corners`) refuses a malformed object id or a listed anchor that isn't a corner instead of editing the selection or the other corners (#1057). New View refuses a centre that isn't exactly [x, y] and a rotation that isn't a number, instead of trimming the one and saving the other as 0° (#1058). The command sweeps try junk scale, spacing and artboard values too, which they had skipped since before those bugs were fixed (#1059). EPS and PostScript `arc` and `arcn` with huge angles no longer hang the import, and an arc whose angles are whole turns apart is empty, as the PostScript reference defines it (#1062). Align and Distribute Spacing keep the key object still also when it is inside a selected group or compound path (#1074). A `.ai` whose layers can't be read yet still opens with its artboards where they were (in a grid, say), not in a row (#1068). Layer Options (`ui.layerOptions`) refuses a malformed or unknown row id instead of skipping it or opening on the highlighted rows (#1077). Shaper scribbles given a list of object ids refuse a malformed or unknown id instead of skipping it and editing the shapes that remain (#1078). Delete Width Point (`stroke.widthPoint.remove`) refuses a list of indices with a malformed entry instead of deleting the points that remain, and no longer wraps a huge index round on the web (#1079). Preferences' checkboxes have an outline when the pointer isn't over them, in every interface brightness, as the app's other dialogs' checkboxes do (#1080). The last plain checkboxes (Properties panel snapping and Scale Strokes & Effects, Revolve, Save options, Export for Screens, Variables, plug-in forms) keep a visible box at rest in every theme too (#885). | | 2026-10-10 | Initial live Revolve (Effect › 3D and Materials, #846, #605), in its own `three-d` crate. Progress docs re-measured and restructured to the craftrules standard (this page, `docs/target-app-parity.md`, `gaps.md`, `ui-parity.md`, the format, hardware, localization, effects and type checklists). Same day: v0.8.0; Effect › Distort, Pixelate and Texture filters (13); German interface; Variables (data merge); Graph Type value axes and tick marks; restart when the first frame never reaches the screen (#964) | | 2026-10-09 | Radial Blur, Smart Blur, Unsharp Mask (gap 2 begins); 340 commits, the busiest day | | 2026-10-08 | v0.5.0–v0.7.0; community contributions: Pen and shape-tool modifiers, Layers, Artboards, Japanese composition, Hebrew/Arabic type, interface languages, saved selections | diff --git a/crates/ui-egui/src/dialogs/export_for_screens.rs b/crates/ui-egui/src/dialogs/export_for_screens.rs index 89435854..c1aeb3f3 100644 --- a/crates/ui-egui/src/dialogs/export_for_screens.rs +++ b/crates/ui-egui/src/dialogs/export_for_screens.rs @@ -353,8 +353,10 @@ fn artboards(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) { ui.painter().rect_filled(r, 2.0, egui::Color32::WHITE); } } - if let Some(b) = boards.get_mut(i) { - ui.checkbox(b, name); + if let Some(b) = boards.get_mut(i) + && crate::widgets::check_name(ui, name, *b) + { + *b ^= true; } }); } @@ -399,10 +401,10 @@ fn assets(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) { ui.horizontal(|ui| { let (r, _) = ui.allocate_exact_size(egui::vec2(46.0, 46.0), egui::Sense::hover()); crate::panels::asset_export::paint_thumb(ui, st, a, r); - let mut checked = on.contains(&a.id); - if ui.checkbox(&mut checked, a.name.as_str()).changed() { + let checked = on.contains(&a.id); + if crate::widgets::check_name(ui, &a.name, checked) { on.retain(|id| *id != a.id); - if checked { + if !checked { on.push(a.id); } } diff --git a/crates/ui-egui/src/dialogs/form.rs b/crates/ui-egui/src/dialogs/form.rs index 2b3ace0d..0cda6ac0 100644 --- a/crates/ui-egui/src/dialogs/form.rs +++ b/crates/ui-egui/src/dialogs/form.rs @@ -320,8 +320,8 @@ pub(super) fn schema_fields(ui: &mut egui::Ui, d: &mut Dialog, specs: &[(String, crate::widgets::range_field(ui, ("plugin-int", k), x, *min as f64..=*max as f64, "", 0, 100.0).map(|x| json!(x as i64)) } ParamSpec::Bool { .. } => { - let mut b = cur.as_bool().unwrap_or(false); - ui.checkbox(&mut b, "").changed().then(|| json!(b)) + let b = cur.as_bool().unwrap_or(false); + crate::widgets::check(ui, "", b, true).then(|| json!(!b)) } ParamSpec::Choice { options, .. } => { let labels: Vec<&str> = options.iter().map(String::as_str).collect(); diff --git a/crates/ui-egui/src/dialogs/revolve.rs b/crates/ui-egui/src/dialogs/revolve.rs index 2d2b09cc..b4030c89 100644 --- a/crates/ui-egui/src/dialogs/revolve.rs +++ b/crates/ui-egui/src/dialogs/revolve.rs @@ -93,14 +93,15 @@ fn controls(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) { super::form::check(ui, d, "__showAxis", tl!("Show axis")); ui.add_space(6.0); ui.horizontal(|ui| { - let mut visible = d.fields.get("expandVisibleOnly").and_then(Value::as_bool).unwrap_or(true); - let response = ui.allocate_ui_with_layout( + let visible = d.fields.get("expandVisibleOnly").and_then(Value::as_bool).unwrap_or(true); + let tip = tl!("Trim covered geometry on Expand Appearance. Live preview keeps the full surface. Transparent paints and surfaces exceeding visibility limits retain all faces."); + let toggled = ui.allocate_ui_with_layout( vec2((ui.available_width() - 30.0).max(100.0), 24.0), egui::Layout::left_to_right(egui::Align::Center), |ui| { - ui.checkbox(&mut visible, egui::RichText::new(tl!("Keep visible surfaces only when expanding")).size(12.0)) + widgets::check_tip(ui, tl!("Keep visible surfaces only when expanding"), visible, true, tip) }, ).inner; - if response.on_hover_text(tl!("Trim covered geometry on Expand Appearance. Live preview keeps the full surface. Transparent paints and surfaces exceeding visibility limits retain all faces.")).changed() { - d.fields.insert("expandVisibleOnly".into(), json!(visible)); + if toggled { + d.fields.insert("expandVisibleOnly".into(), json!(!visible)); } if reset_dot(ui, "expandVisibleOnly", !visible, "true") { d.fields.insert("expandVisibleOnly".into(), json!(true)); @@ -111,9 +112,9 @@ fn controls(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) { fn lighting_controls(ui: &mut egui::Ui, d: &mut Dialog) { ui.horizontal(|ui| { - let mut shade = d.bool("shade"); - if ui.checkbox(&mut shade, tl!("Shading")).changed() { - d.fields.insert("shade".into(), json!(shade)); + let shade = d.bool("shade"); + if widgets::check(ui, tl!("Shading"), shade, true) { + d.fields.insert("shade".into(), json!(!shade)); } ui.add_space((ui.available_width() - 24.0).max(0.0)); if reset_dot(ui, "shade", !shade, "true") { diff --git a/crates/ui-egui/src/dialogs/save_options.rs b/crates/ui-egui/src/dialogs/save_options.rs index fd95d1fb..2f89839b 100644 --- a/crates/ui-egui/src/dialogs/save_options.rs +++ b/crates/ui-egui/src/dialogs/save_options.rs @@ -145,9 +145,9 @@ fn option_row(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, o: &Forma let value = d.fields.get(o.name).cloned().unwrap_or(Value::Null); if o.ty == "boolean" { ui.label(""); - let mut on = value.as_bool().unwrap_or(false); - if ui.checkbox(&mut on, label.trim_end_matches(':')).on_hover_text(o.description).changed() { - d.fields.insert(o.name.into(), json!(on)); + let on = value.as_bool().unwrap_or(false); + if widgets::check_tip(ui, label.trim_end_matches(':'), on, true, o.description) { + d.fields.insert(o.name.into(), json!(!on)); } ui.end_row(); if o.name == "separateArtboards" { diff --git a/crates/ui-egui/src/dialogs/variables.rs b/crates/ui-egui/src/dialogs/variables.rs index 5e1c0f87..887a2eed 100644 --- a/crates/ui-egui/src/dialogs/variables.rs +++ b/crates/ui-egui/src/dialogs/variables.rs @@ -202,9 +202,11 @@ fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { label(ui, &v.name); let key = value_key(&v.name); match v.kind { - VariableKind::Text => text(ui, d, &key, 220.0), + VariableKind::Text => { + text(ui, d, &key, 220.0); + } VariableKind::Visibility => checkbox(ui, d, &key), - }; + } ui.end_row(); } } @@ -224,13 +226,11 @@ fn text(ui: &mut egui::Ui, d: &mut Dialog, key: &str, width: f32) -> egui::Respo } /// A checkbox for `key`, written back as it is toggled. -fn checkbox(ui: &mut egui::Ui, d: &mut Dialog, key: &str) -> egui::Response { - let mut b = d.bool(key); - let r = ui.checkbox(&mut b, ""); - if r.changed() { - d.fields.insert(key.into(), json!(b)); +fn checkbox(ui: &mut egui::Ui, d: &mut Dialog, key: &str) { + let b = d.bool(key); + if widgets::check(ui, "", b, true) { + d.fields.insert(key.into(), json!(!b)); } - r } /// A dropdown over the document's names `options` (shown as they are, never translated) for diff --git a/crates/ui-egui/src/panels/properties.rs b/crates/ui-egui/src/panels/properties.rs index ccd451ce..769d5b99 100644 --- a/crates/ui-egui/src/panels/properties.rs +++ b/crates/ui-egui/src/panels/properties.rs @@ -356,13 +356,11 @@ fn document_sections(app: &mut VectorcraftApp, ui: &mut Ui) { }); divider(ui); section_header(ui, tl!("Snap Options")); - let mut sp = app.ui.view.snap_to_point; - if ui.checkbox(&mut sp, tl!("Snap to Point")).changed() { - app.ui.view.snap_to_point = sp; + if widgets::check(ui, tl!("Snap to Point"), app.ui.view.snap_to_point, true) { + app.ui.view.snap_to_point ^= true; } - let mut sg = app.ui.view.snap_to_grid; - if ui.checkbox(&mut sg, tl!("Snap to Grid")).changed() { - app.ui.view.snap_to_grid = sg; + if widgets::check(ui, tl!("Snap to Grid"), app.ui.view.snap_to_grid, true) { + app.ui.view.snap_to_grid ^= true; } divider(ui); section_header(ui, tl!("Preferences")); @@ -373,9 +371,9 @@ fn document_sections(app: &mut VectorcraftApp, ui: &mut Ui) { app.session.prefs.keyboard_increment = v.max(0.001); } }); - let mut ss = app.session.prefs.scale_strokes; - if ui.checkbox(&mut ss, tl!("Scale Strokes & Effects")).changed() { - super::transform::set_pref(app, "scaleStrokes", ss); + let ss = app.session.prefs.scale_strokes; + if widgets::check(ui, tl!("Scale Strokes & Effects"), ss, true) { + super::transform::set_pref(app, "scaleStrokes", !ss); } divider(ui); section_header(ui, tl!("Quick Actions")); @@ -584,6 +582,40 @@ mod tests { frame(app, ctx, vec![b(false)]); } + /// The Snap Options and Scale Strokes & Effects checkboxes keep a visible box at rest in every + /// theme, as the Preferences ones do (#1080), and toggle when clicked. + #[test] + fn the_document_checkboxes_show_their_boxes_and_toggle() { + for brightness in crate::theme::Brightness::ALL { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 200, "height": 100})).unwrap(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + crate::theme::apply(&ctx, brightness); + frame(&mut app, &ctx, vec![]); + let raw = egui::RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, vec2(300.0, 700.0))), ..Default::default() }; + let mut out = ctx.run_ui(raw, |ui| show(&mut app, ui)); + out.textures_delta.clear(); + let boxes = out + .shapes + .iter() + .filter(|s| matches!(&s.shape, egui::Shape::Rect(r) if r.stroke.width > 0.0 && (r.rect.width() - 13.0).abs() < 0.5 && (r.rect.height() - 13.0).abs() < 0.5)) + .count(); + assert!(boxes >= 3, "{brightness:?}: {boxes} checkbox outlines"); + } + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 200, "height": 100})).unwrap(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let texts = frame(&mut app, &ctx, vec![]); + let (grid, strokes) = (app.ui.view.snap_to_grid, app.session.prefs.scale_strokes); + click(&mut app, &ctx, &texts, "Snap to Grid"); + assert_eq!(app.ui.view.snap_to_grid, !grid); + let texts = frame(&mut app, &ctx, vec![]); + click(&mut app, &ctx, &texts, "Scale Strokes & Effects"); + assert_eq!(app.session.prefs.scale_strokes, !strokes); + } + /// #921: with the Gradient tool, a Gradient section: the type, and for a freeform gradient /// Draw as Points or Lines radio buttons. #[test] diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index d8883a17..c99302c0 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -982,12 +982,11 @@ pub fn link_label(ui: &mut Ui, text: &str) -> Response { resp } -/// The row of a checkbox or radio button: allocates a 13 pt box plus `label`, draws the label and -/// returns the box, the response and the box's border colour. -fn choice_row(ui: &mut Ui, label: &str, enabled: bool) -> (Rect, Response, Color32) { +/// The row of a checkbox or radio button: allocates a 13 pt box plus `text` (already translated), +/// draws the text and returns the box, the response and the box's border colour. +fn choice_row(ui: &mut Ui, text: &str, enabled: bool) -> (Rect, Response, Color32) { let t = Tokens::get(ui.ctx()); - let galley = - ui.painter().layout_no_wrap(tl!(label).to_string(), egui::FontId::proportional(12.5), if enabled { t.text } else { t.text_disabled }); + let galley = ui.painter().layout_no_wrap(text.to_string(), egui::FontId::proportional(12.5), if enabled { t.text } else { t.text_disabled }); let (rect, resp) = ui.allocate_exact_size(vec2(18.0 + galley.size().x, 20.0f32.max(galley.size().y)), if enabled { Sense::click() } else { Sense::hover() }); let bx = Rect::from_min_size(pos2(rect.left(), rect.center().y - 6.5), Vec2::splat(13.0)); @@ -1009,21 +1008,27 @@ pub fn check(ui: &mut Ui, label: &str, value: bool, enabled: bool) -> bool { /// [`check`] with tooltip `tip` on the box and its label. Returns true when toggled. pub fn check_tip(ui: &mut Ui, label: &str, value: bool, enabled: bool, tip: &str) -> bool { - let (clicked, resp) = check_row(ui, label, Some(value), enabled); + let (clicked, resp) = check_row(ui, tl!(label), Some(value), enabled); resp.on_hover_text(tip); clicked } /// Three-state [`check`]: `None` shows a dash (a neutral or mixed state). Returns true when clicked. pub fn check3(ui: &mut Ui, label: &str, value: Option, enabled: bool) -> bool { - check_row(ui, label, value, enabled).0 + check_row(ui, tl!(label), value, enabled).0 } -/// [`check3`], also returning the row's response (what a tooltip goes on: a widget around it never -/// counts as hovered under the box). -fn check_row(ui: &mut Ui, label: &str, value: Option, enabled: bool) -> (bool, Response) { +/// [`check`] for a name of the document's (an artboard's, an asset's), shown as it is, never +/// translated. Returns true when toggled. +pub fn check_name(ui: &mut Ui, name: &str, value: bool) -> bool { + check_row(ui, name, Some(value), true).0 +} + +/// [`check3`] with its `text` already translated, also returning the row's response (what a +/// tooltip goes on: a widget around it never counts as hovered under the box). +fn check_row(ui: &mut Ui, text: &str, value: Option, enabled: bool) -> (bool, Response) { let t = Tokens::get(ui.ctx()); - let (bx, resp, border) = choice_row(ui, label, enabled); + let (bx, resp, border) = choice_row(ui, text, enabled); let checked = value == Some(true); ui.painter().rect_filled(bx, CornerRadius::same(2), if checked && enabled { t.accent_strong } else { t.input }); ui.painter().rect_stroke(bx, CornerRadius::same(2), Stroke::new(1.0, border), StrokeKind::Inside); @@ -1045,7 +1050,7 @@ fn check_row(ui: &mut Ui, label: &str, value: Option, enabled: bool) -> (b /// Radio button in the style of [`check`], with a disabled state. Returns true when clicked. pub fn radio(ui: &mut Ui, label: &str, selected: bool, enabled: bool) -> bool { let t = Tokens::get(ui.ctx()); - let (bx, resp, border) = choice_row(ui, label, enabled); + let (bx, resp, border) = choice_row(ui, tl!(label), enabled); let c = bx.center(); ui.painter().circle(c, 6.0, if selected && enabled { t.accent_strong } else { t.input }, Stroke::new(1.0, border)); if selected {