Shape Builder Tool Options: a highlight color with an alpha part is refused

OK in Shape Builder Tool Options sent highlightColor to tool.setOption,
which keeps only #rrggbb, so #ff000080 was applied as #ff0000 with no
error. OK now gives the same error as the Live Paint tool options for a
hex color with an alpha part (#rrggbbaa or #rgba), the dialog stays open
and the tool's options do not change. The tool.options params text,
docs/mcp.md and the progress doc say so.
This commit is contained in:
dotCooCoo
2026-11-04 20:51:03 +07:00
committed by bflatastic
parent db6a0d786e
commit 88aeee8f8e
4 changed files with 28 additions and 4 deletions
@@ -13,7 +13,7 @@ use serde_json::{Map, Value, json};
use vectorcraft_color::Color;
use vectorcraft_doc::LAYER_COLORS;
use super::tile_edge_color::resolve;
use super::tile_edge_color::{checked, resolve};
use super::{DialogSpec, form, run_and_close};
use crate::state::Dialog;
use crate::{VectorcraftApp, widgets};
@@ -118,8 +118,11 @@ fn body(_app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
false
}
/// OK: set the Shape Builder's options.
/// OK: set the Shape Builder's options, or an error for a `highlightColor` that is not a color.
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
if d.fields.contains_key("highlightColor") {
checked(&d.str("highlightColor"))?;
}
let values: Map<String, Value> = KEYS.iter().filter_map(|k| Some((k.to_string(), d.fields.get(*k)?.clone()))).collect();
run_and_close(app, "tool.setOption", json!({ "tool": "shapeBuilder", "values": values }))
}
@@ -251,6 +254,25 @@ mod tests {
assert_eq!(bounds, vec![[100.0, 100.0, 300.0, 100.0], [100.0, 100.0, 300.0, 200.0]]);
}
/// A highlight color with an alpha part is refused and leaves the tool's options as they were;
/// `#rrggbb` still sets them.
#[test]
fn a_highlight_color_with_an_alpha_part_is_refused() {
let mut app = app();
app.run("tool.options", json!({"tool": "shapeBuilder"})).unwrap();
let d = app.ui.dialog.as_mut().unwrap();
d.fields.insert("highlightColor".into(), json!("#ff000080"));
d.fields.insert("highlightFill".into(), json!(false));
let err = super::super::confirm(&mut app).unwrap_err();
assert_eq!(err, "`#ff000080` is not a colour (#rrggbb or a preset name)");
assert!(app.ui.dialog.is_some(), "the dialog stays open");
let o = app.session.tool_options_of("shapeBuilder");
assert_eq!((o["highlightColor"].as_str(), o["highlightFill"].as_bool()), (Some("#ff4f4f"), Some(true)), "nothing changes");
app.ui.dialog.as_mut().unwrap().fields.insert("highlightColor".into(), json!("#00ff00"));
super::super::confirm(&mut app).unwrap();
assert_eq!(app.session.tool_options_of("shapeBuilder")["highlightColor"], json!("#00ff00"));
}
#[test]
fn a_straight_line_drag_merges_only_the_regions_the_line_crosses() {
// A wide rectangle and a tall one over the middle of its top edge: three regions, the wide
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -5919,7 +5919,8 @@ pointer show the current fill and the swatches before and after it), `selection`
`straightLine`: a drag touches what the line from its first point to the pointer crosses), `highlightFill` (on by
default; when it is off, merge mode shades no regions), `gapDetection` (off) with `gapLength` (pt, 0 to 72; 3), `clickStrokeSplitsPath` (In Merge Mode,
Clicking Stroke Splits the Path; off), `highlightStroke` (Highlight Stroke When Editable; on) and `highlightColor`
(`#rrggbb` or a layer color's name; `#ff4f4f`, the layer color Red). With `clickStrokeSplitsPath` on, in merge mode,
(`#rrggbb` or a layer color's name; `#ff4f4f`, the layer color Red). The dialog's OK gives an error for a
`highlightColor` with an alpha part (`#rrggbbaa` or `#rgba`) and changes nothing. With `clickStrokeSplitsPath` on, in merge mode,
the pointer shows scissors within 4 screen pixels of a stroke, `highlightStroke` draws the run of stroke under it in
`highlightColor`, and a click splits that run off its path: the run goes from the anchor or crossing point before the
pointer to the one after it, the path keeps the rest of it, and a copy of the path with the run, in the path's
+1
View File
@@ -814,6 +814,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
With two open paths selected, a Pencil stroke from an end of one that ends within Edit selected paths' distance of an end of the other connects them into one path with the first path's appearance, in one undo step, as [Connect two paths with the Pencil tool](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/connect-two-paths-with-the-pencil-tool.html) describes (`path.freehand` `connect`).
Double-clicking the Shape Builder tool opens Shape Builder Tool Options ([Shape Builder tool options](https://web.archive.org/web/20260105172244/https://helpx.adobe.com/illustrator/using/building-new-shapes-using-shape.html)): with Consider Open Filled Paths as Closed off, a filled open path cuts regions as an unfilled one does; Pick Color From Color Swatches fills merged shapes with the current fill, the Left and Right arrow keys step it through the document's color swatches, and Cursor Swatch Preview shows the current fill and the swatches before and after it above the pointer; Selection Straight Line merges what the line from a drag's first point to its last crosses ([Setting Illustrator's Shape Builder Options](https://web.archive.org/web/20260615204453/https://creativepro.com/setting-illustrators-shape-builder-options/)); with Highlight Fill off, merge mode shades no regions (`shapeBuilder.merge` `openFilledAsClosed` and `pickColorFrom`).
With In Merge Mode, Clicking Stroke Splits the Path on in the same dialog, a click on a stroke in merge mode splits its path into the run of stroke under the pointer and the rest of the path, as the [Shape Builder tool options](https://web.archive.org/web/20260105172244/https://helpx.adobe.com/illustrator/using/building-new-shapes-using-shape.html) page describes; the run goes from the anchor or crossing point before the pointer to the one after it, and the pointer shows scissors over it. Highlight Stroke When Editable draws that run in the color chosen in its Color list (Red by default), and with Pick Color From Color Swatches the run takes the current fill as its stroke (`shapeBuilder.merge` `splitStroke`).
As VectorCraft's own rule, OK in Shape Builder Tool Options gives an error for a highlight Color with an alpha part (`#rrggbbaa` or `#rgba`) and changes nothing. Regression test: `dialogs::shape_builder_options::tests::a_highlight_color_with_an_alpha_part_is_refused`.
With Pick Color From Artwork, a merged shape takes the appearance, opacity and blending mode of the object where the drag starts, else of the object where it ends, else of the front-most selected object, as the [Shape Builder combine page](https://web.archive.org/web/20251006211643/https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/combine-shapes-with-shape-builder-tool.html) describes. A click in an area that no selected object covers, such as a hole other shapes enclose, takes the front-most selected object's look (`shapeBuilder.merge` `points`: the first point is where the drag starts, and the last point is where it ends).
A Shift-drag with the Shape Builder tool draws a rectangular marquee for merging many paths at once, as [Combine shapes with the Shape Builder tool](https://web.archive.org/web/20251006211643/https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/combine-shapes-with-shape-builder-tool.html) describes: the regions it reaches are shaded while it is drawn and merge into one shape on release, or with Alt they are deleted. The marquee reaches each region whose outline crosses it or has an anchor inside it, and the region that holds the whole marquee; this rule is VectorCraft's own. For Pick Color From Artwork, the marquee's starting corner is where the drag starts and the opposite corner is where it ends (`shapeBuilder.merge` `rect`).
Shape Builder pieces on dense hatches (lines at exact 0.25 or 0.5 pt steps across another line) come back one per stretch between crossings, with no speck where the sweep cut a line's two passes a rounding error apart: the planar map contracts the stub between such cuts into one vertex, bounded by each cluster's extent so a run of tiny level segments keeps its length (`crates/pathops/src/planar.rs` tests `a_dense_hatch_at_exact_binary_steps_cuts_each_line_once_per_crossing`, `a_run_of_tiny_level_segments_is_not_contracted_as_stubs`, `a_line_crossed_where_its_two_passes_part_is_cut_once`).