mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 13:47:21 -06:00
Live Paint: Live Paint Bucket Options set what a click paints and the highlight
Double-clicking the Live Paint Bucket did nothing, and the tool always
filled on a click, stroked on Shift-click and highlighted in red at
4 px. It now opens Live Paint Bucket Options with Paint Fills, Paint
Strokes, Highlight, Color and Width.
- Paint Fills (on) and Paint Strokes (off) set what a click paints.
With Paint Strokes alone, a click strokes the edge within 4 px of the
pointer. With both on, a click strokes an edge within 4 px and
otherwise fills the face. With both off, a click paints nothing.
- Shift switches Paint Fills alone to Paint Strokes alone and back, and
switches both to Paint Fills alone. With the defaults, Shift-click
strokes the edge as before.
- Highlight turns the outline of the face or edge under the pointer on
or off; Color (a layer color or a custom color) and Width (1 to 20 px)
set it. With Paint Strokes alone, a selection of ordinary paths no
longer highlights a region, since a click there paints nothing.
- The options are tool options (tool.setOption {tool:
"livePaintBucket"}): they last across tool switches and are saved
with the preferences.
Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
@@ -36,7 +36,7 @@ use cache::RegionCache;
|
||||
#[cfg(test)]
|
||||
use live_paint::LpHover;
|
||||
use live_paint::lp_color;
|
||||
pub use live_paint::{LP_SELECT_WIDTH, LP_SELECT_WIDTH_RANGE, LivePaintBucketTool, LivePaintSelectionTool};
|
||||
pub use live_paint::{LP_BUCKET_WIDTH, LP_BUCKET_WIDTH_RANGE, LP_SELECT_WIDTH, LP_SELECT_WIDTH_RANGE, LivePaintBucketTool, LivePaintSelectionTool};
|
||||
pub use lookup::{
|
||||
EDGE_NAME, FACE_NAME, Face, LIVE_PAINT_NAME, LIVE_PAINT_PREFIX, SOURCES_NAME, builder_members, drawn_outlines, edge_near, edges, face_at, faces,
|
||||
is_live_paint, leaf_shapes, live_paint_at, live_paint_takes, node_outline, outline_snap, path_contains, shape_builder_shapes, shapes_for,
|
||||
|
||||
@@ -86,20 +86,91 @@ fn lp_overlays(h: &LpHover, cache: &RegionCache, color: [u8; 3], width: f32) ->
|
||||
}
|
||||
}
|
||||
|
||||
/// Live Paint Bucket: click fills the face under the cursor with the current fill; Shift-click
|
||||
/// paints the nearest edge with the current stroke. Alt-click samples a fill or stroke into the
|
||||
/// paint defaults; Alt+Shift-click samples only the color under the pointer. Clicking selected
|
||||
/// ordinary paths without Alt makes them a Live Paint group first.
|
||||
/// The Live Paint Bucket's highlight width (screen pixels) by default.
|
||||
pub const LP_BUCKET_WIDTH: f64 = 4.0;
|
||||
|
||||
/// The least and greatest highlight width (screen pixels) the Live Paint Bucket takes.
|
||||
pub const LP_BUCKET_WIDTH_RANGE: (f64, f64) = (1.0, 20.0);
|
||||
|
||||
/// What a Live Paint Bucket click paints.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum LpPaint {
|
||||
Nothing,
|
||||
/// The face under the pointer.
|
||||
Fills,
|
||||
/// The edge nearest the pointer.
|
||||
Strokes,
|
||||
/// The edge nearest the pointer when one is in reach, else the face under the pointer.
|
||||
Both,
|
||||
}
|
||||
|
||||
/// Live Paint Bucket: by default, a click fills the face under the cursor with the current fill,
|
||||
/// and Shift-click paints the nearest edge with the current stroke. Alt-click samples a fill or
|
||||
/// stroke into the paint defaults; Alt+Shift-click samples only the color under the pointer.
|
||||
/// Clicking selected ordinary paths without Alt makes them a Live Paint group first. Its Tool
|
||||
/// Options (double-click the tool) are `paintFills` (on) and `paintStrokes` (off), `highlight`
|
||||
/// (on), the highlight `color` (`#rrggbb`, red by default) and its `width` ([`LP_BUCKET_WIDTH`]
|
||||
/// pixels). With Paint Strokes alone on, a click paints the edge nearest the pointer and
|
||||
/// Shift-click fills the face. With both on, a click paints the edge nearest the pointer when one
|
||||
/// is within 4 pixels, else it fills the face, and Shift-click fills the face. With both off, a
|
||||
/// click paints nothing. With Highlight off, the face or edge under the pointer is not
|
||||
/// highlighted.
|
||||
pub struct LivePaintBucketTool {
|
||||
pub(super) cache: RegionCache,
|
||||
pub(super) hover: LpHover,
|
||||
/// Where the pointer last moved, and whether Shift and Alt were held.
|
||||
at: Option<(Point, bool, bool)>,
|
||||
fills: bool,
|
||||
strokes: bool,
|
||||
highlight: bool,
|
||||
color: [u8; 3],
|
||||
width: f64,
|
||||
}
|
||||
|
||||
impl Default for LivePaintBucketTool {
|
||||
fn default() -> Self {
|
||||
Self { cache: RegionCache::live_paint(), hover: LpHover::None, at: None }
|
||||
Self {
|
||||
cache: RegionCache::live_paint(),
|
||||
hover: LpHover::None,
|
||||
at: None,
|
||||
fills: true,
|
||||
strokes: false,
|
||||
highlight: true,
|
||||
color: HIGHLIGHT_RED,
|
||||
width: LP_BUCKET_WIDTH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LivePaintBucketTool {
|
||||
/// What a click paints, Shift held or not. Shift switches Paint Fills alone to Paint Strokes
|
||||
/// alone and back, and switches both to Paint Fills alone.
|
||||
fn paints(&self, shift: bool) -> LpPaint {
|
||||
match (self.fills, self.strokes, shift) {
|
||||
(false, false, _) => LpPaint::Nothing,
|
||||
(true, true, false) => LpPaint::Both,
|
||||
(true, false, false) | (_, true, true) => LpPaint::Fills,
|
||||
(true, false, true) | (false, true, false) => LpPaint::Strokes,
|
||||
}
|
||||
}
|
||||
|
||||
/// The highlight at `p` for a pointer with Shift and Alt held as given: none with Alt or with
|
||||
/// Highlight off, else the face or edge a click there paints.
|
||||
fn hover_at(&mut self, cx: &ToolContext, p: Point, shift: bool, alt: bool) -> LpHover {
|
||||
if alt || !self.highlight {
|
||||
return LpHover::None;
|
||||
}
|
||||
let takes = |id| lp_visible(cx, id);
|
||||
match self.paints(shift) {
|
||||
LpPaint::Nothing => LpHover::None,
|
||||
LpPaint::Fills => lp_hover(cx, Some(&mut self.cache), p, false, &takes),
|
||||
// A click paints no region of the selection, so none is highlighted.
|
||||
LpPaint::Strokes => lp_hover(cx, None, p, true, &takes),
|
||||
LpPaint::Both => match lp_part_at(cx.doc, p, cx.tol(4.0), true, &takes) {
|
||||
Some((_, Some(e))) => e.path_data().map(|pd| LpHover::Part(pd.to_bezpath())).unwrap_or_default(),
|
||||
_ => lp_hover(cx, Some(&mut self.cache), p, false, &takes),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,8 +184,7 @@ impl Tool for LivePaintBucketTool {
|
||||
match ev.kind {
|
||||
PointerKind::Move => {
|
||||
self.at = Some((p, ev.mods.shift, ev.mods.alt));
|
||||
self.hover =
|
||||
if ev.mods.alt { LpHover::None } else { lp_hover(cx, Some(&mut self.cache), p, ev.mods.shift, &|id| lp_visible(cx, id)) };
|
||||
self.hover = self.hover_at(cx, p, ev.mods.shift, ev.mods.alt);
|
||||
vec![]
|
||||
}
|
||||
PointerKind::Down => {
|
||||
@@ -142,13 +212,14 @@ impl Tool for LivePaintBucketTool {
|
||||
});
|
||||
}
|
||||
let point = json!([p.x, p.y]);
|
||||
if ev.mods.shift {
|
||||
return match lp_part_at(cx.doc, p, cx.tol(4.0), true, &|id| lp_visible(cx, id)) {
|
||||
Some((g, Some(_))) => {
|
||||
vec![Action::Exec("livePaint.strokeEdge".into(), json!({ "group": g.0, "point": point, "tolerance": cx.tol(4.0) }))]
|
||||
}
|
||||
_ => vec![],
|
||||
};
|
||||
let paint = self.paints(ev.mods.shift);
|
||||
if matches!(paint, LpPaint::Strokes | LpPaint::Both)
|
||||
&& let Some((g, Some(_))) = lp_part_at(cx.doc, p, cx.tol(4.0), true, &|id| lp_visible(cx, id))
|
||||
{
|
||||
return vec![Action::Exec("livePaint.strokeEdge".into(), json!({ "group": g.0, "point": point, "tolerance": cx.tol(4.0) }))];
|
||||
}
|
||||
if !matches!(paint, LpPaint::Fills | LpPaint::Both) {
|
||||
return vec![];
|
||||
}
|
||||
if let Some(g) = live_paint_at(cx.doc, p) {
|
||||
return vec![Action::Exec("livePaint.fill".into(), json!({ "group": g.0, "point": point }))];
|
||||
@@ -164,13 +235,39 @@ impl Tool for LivePaintBucketTool {
|
||||
}
|
||||
|
||||
fn overlays(&self, _cx: &ToolContext) -> Vec<Overlay> {
|
||||
lp_overlays(&self.hover, &self.cache, HIGHLIGHT_RED, 4.0)
|
||||
lp_overlays(&self.hover, &self.cache, self.color, self.width as f32)
|
||||
}
|
||||
|
||||
fn cursor(&self, _cx: &ToolContext, _p: Point, m: Mods) -> Cursor {
|
||||
if m.alt { Cursor::Eyedropper } else { Cursor::LivePaintBucket }
|
||||
}
|
||||
|
||||
fn options(&self) -> Value {
|
||||
let [r, g, b] = self.color;
|
||||
json!({
|
||||
"paintFills": self.fills,
|
||||
"paintStrokes": self.strokes,
|
||||
"highlight": self.highlight,
|
||||
"color": format!("#{r:02x}{g:02x}{b:02x}"),
|
||||
"width": self.width,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_option(&mut self, key: &str, v: &Value) {
|
||||
match key {
|
||||
"paintFills" => self.fills = v.as_bool().unwrap_or(self.fills),
|
||||
"paintStrokes" => self.strokes = v.as_bool().unwrap_or(self.strokes),
|
||||
"highlight" => self.highlight = v.as_bool().unwrap_or(self.highlight),
|
||||
"color" => self.color = lp_color(v).unwrap_or(self.color),
|
||||
"width" => {
|
||||
let (min, max) = LP_BUCKET_WIDTH_RANGE;
|
||||
self.width = v.as_f64().filter(|w| w.is_finite()).map_or(self.width, |w| w.clamp(min, max));
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
self.hover = LpHover::None;
|
||||
}
|
||||
|
||||
fn waiting(&self) -> bool {
|
||||
self.cache.waiting()
|
||||
}
|
||||
@@ -184,7 +281,7 @@ impl Tool for LivePaintBucketTool {
|
||||
loop {
|
||||
self.cache.settle(wait);
|
||||
if let Some((p, shift, alt)) = self.at {
|
||||
self.hover = if alt { LpHover::None } else { lp_hover(cx, Some(&mut self.cache), p, shift, &|id| lp_visible(cx, id)) };
|
||||
self.hover = self.hover_at(cx, p, shift, alt);
|
||||
}
|
||||
if !wait || !self.cache.waiting() {
|
||||
break;
|
||||
|
||||
@@ -765,6 +765,106 @@ fn bucket_click_fills_face_and_shift_click_strokes_edge() {
|
||||
assert_eq!(id, "livePaint.strokeEdge");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bucket_options_choose_what_a_click_paints() {
|
||||
let (d, gid, ..) = live_group_doc();
|
||||
let s = Selection::default();
|
||||
let p = paint();
|
||||
let cx = cx(&d, &s, &p);
|
||||
let shift = Mods { shift: true, ..Default::default() };
|
||||
let fill = |x: f64| vec![Action::Exec("livePaint.fill".into(), json!({ "group": gid.0, "point": [x, 25.0] }))];
|
||||
let stroke = |x: f64| vec![Action::Exec("livePaint.strokeEdge".into(), json!({ "group": gid.0, "point": [x, 25.0], "tolerance": 4.0 }))];
|
||||
let click = |t: &mut LivePaintBucketTool, x: f64, m: Mods| t.pointer(&cx, &ev(PointerKind::Down, x, 25.0).with_mods(m));
|
||||
let hovered = |t: &mut LivePaintBucketTool, x: f64, m: Mods| {
|
||||
t.pointer(&cx, &ev(PointerKind::Move, x, 25.0).with_mods(m));
|
||||
match t.overlays(&cx).as_slice() {
|
||||
[] => None,
|
||||
[Overlay::Path { path, .. }] => Some(path.bounding_box()),
|
||||
ov => panic!("{ov:?}"),
|
||||
}
|
||||
};
|
||||
let (face, edge) = (Some(Rect::new(50.0, 0.0, 100.0, 50.0)), Some(Rect::new(50.0, 0.0, 50.0, 50.0)));
|
||||
let mut t = LivePaintBucketTool::default();
|
||||
assert_eq!(t.options(), json!({"paintFills": true, "paintStrokes": false, "highlight": true, "color": "#ff2a2a", "width": 4.0}));
|
||||
// Paint Fills alone: 1 pt from the edge, a click fills the face and Shift-click strokes the edge.
|
||||
assert_eq!((click(&mut t, 51.0, Mods::default()), hovered(&mut t, 51.0, Mods::default())), (fill(51.0), face));
|
||||
assert_eq!((click(&mut t, 51.0, shift), hovered(&mut t, 51.0, shift)), (stroke(51.0), edge));
|
||||
// Paint Strokes alone: a click strokes the edge in reach and paints nothing farther away;
|
||||
// Shift-click fills the face.
|
||||
t.set_option("paintFills", &json!(false));
|
||||
t.set_option("paintStrokes", &json!(true));
|
||||
assert_eq!((click(&mut t, 51.0, Mods::default()), hovered(&mut t, 51.0, Mods::default())), (stroke(51.0), edge));
|
||||
assert_eq!((click(&mut t, 75.0, Mods::default()), hovered(&mut t, 75.0, Mods::default())), (vec![], None));
|
||||
assert_eq!((click(&mut t, 51.0, shift), hovered(&mut t, 51.0, shift)), (fill(51.0), face));
|
||||
// Both: a click strokes the edge in reach, else fills the face; Shift-click fills the face.
|
||||
t.set_option("paintFills", &json!(true));
|
||||
assert_eq!((click(&mut t, 51.0, Mods::default()), hovered(&mut t, 51.0, Mods::default())), (stroke(51.0), edge));
|
||||
assert_eq!((click(&mut t, 75.0, Mods::default()), hovered(&mut t, 75.0, Mods::default())), (fill(75.0), face));
|
||||
assert_eq!((click(&mut t, 51.0, shift), hovered(&mut t, 51.0, shift)), (fill(51.0), face));
|
||||
// Both off: nothing is painted or highlighted.
|
||||
t.set_option("paintFills", &json!(false));
|
||||
t.set_option("paintStrokes", &json!(false));
|
||||
for m in [Mods::default(), shift] {
|
||||
assert_eq!((click(&mut t, 51.0, m), hovered(&mut t, 51.0, m)), (vec![], None));
|
||||
}
|
||||
// A value that is not true or false is ignored.
|
||||
t.set_option("paintFills", &json!("yes"));
|
||||
assert_eq!(t.options()["paintFills"], false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bucket_strokes_alone_paints_and_highlights_no_region_of_the_selection() {
|
||||
let (d, a, b) = two_rects();
|
||||
let mut s = Selection::default();
|
||||
s.set([a, b]);
|
||||
let p = paint();
|
||||
let cx = cx(&d, &s, &p);
|
||||
let mut t = LivePaintBucketTool::default();
|
||||
t.pointer(&cx, &ev(PointerKind::Move, 75.0, 50.0));
|
||||
t.poll(&cx, true);
|
||||
assert!(!t.overlays(&cx).is_empty(), "Paint Fills highlights the region");
|
||||
t.set_option("paintFills", &json!(false));
|
||||
t.set_option("paintStrokes", &json!(true));
|
||||
t.pointer(&cx, &ev(PointerKind::Move, 75.0, 50.0));
|
||||
t.poll(&cx, true);
|
||||
assert!(t.overlays(&cx).is_empty());
|
||||
assert!(t.pointer(&cx, &ev(PointerKind::Down, 75.0, 50.0)).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bucket_options_set_the_highlight() {
|
||||
let (d, ..) = live_group_doc();
|
||||
let s = Selection::default();
|
||||
let p = paint();
|
||||
let cx = cx(&d, &s, &p);
|
||||
let mut t = LivePaintBucketTool::default();
|
||||
let highlight = |t: &mut LivePaintBucketTool| {
|
||||
t.pointer(&cx, &ev(PointerKind::Move, 75.0, 25.0));
|
||||
match t.overlays(&cx).as_slice() {
|
||||
[] => None,
|
||||
[Overlay::Path { color, width, .. }] => Some((*color, *width)),
|
||||
ov => panic!("{ov:?}"),
|
||||
}
|
||||
};
|
||||
assert_eq!(highlight(&mut t), Some((HIGHLIGHT_RED, 4.0)));
|
||||
t.set_option("color", &json!("#00FF00"));
|
||||
t.set_option("width", &json!(9));
|
||||
assert_eq!(highlight(&mut t), Some(([0, 0xff, 0], 9.0)));
|
||||
t.set_option("color", &json!("light blue"));
|
||||
t.set_option("width", &json!("wide"));
|
||||
assert_eq!(highlight(&mut t), Some(([0x4f, 0x80, 0xff], 9.0)));
|
||||
t.set_option("width", &json!(100));
|
||||
assert_eq!(t.options()["width"], json!(LP_BUCKET_WIDTH_RANGE.1));
|
||||
t.set_option("width", &json!(0));
|
||||
assert_eq!(t.options()["width"], json!(LP_BUCKET_WIDTH_RANGE.0));
|
||||
// Highlight off: nothing is highlighted, and a click still paints.
|
||||
t.set_option("highlight", &json!(false));
|
||||
assert_eq!(highlight(&mut t), None);
|
||||
assert_eq!(t.pointer(&cx, &ev(PointerKind::Down, 75.0, 25.0)).len(), 1);
|
||||
t.set_option("highlight", &json!(true));
|
||||
assert!(highlight(&mut t).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bucket_alt_samples_face_edge_and_selected_region_without_painting() {
|
||||
let (mut d, _gid, face, edge) = live_group_doc();
|
||||
|
||||
@@ -45,6 +45,8 @@ const ROWS: &[Row] = &[
|
||||
(&["blobBrush", "eraser"], None, &["angle", "roundness"]),
|
||||
// Live Paint Selection Tool Options (`builder::LivePaintSelectionTool`).
|
||||
(&["livePaintSelection"], None, &["selectFills", "selectStrokes", "color", "width"]),
|
||||
// Live Paint Bucket Options (`builder::LivePaintBucketTool`).
|
||||
(&["livePaintBucket"], None, &["paintFills", "paintStrokes", "highlight", "color", "width"]),
|
||||
(&["polygon"], None, &["sides"]),
|
||||
(&["star"], None, &["points"]),
|
||||
(&["freeTransform"], None, &["constrain"]),
|
||||
|
||||
@@ -56,6 +56,7 @@ pub mod justification;
|
||||
pub mod layer_options;
|
||||
pub mod layers_panel_options;
|
||||
pub mod liquify;
|
||||
pub mod live_paint_bucket;
|
||||
pub mod live_paint_gaps;
|
||||
pub mod live_paint_selection;
|
||||
pub mod missing_fonts;
|
||||
@@ -389,6 +390,7 @@ registry! {
|
||||
BlendOptions: [blend_options::KIND] => blend_options::SPEC,
|
||||
LivePaintGaps: [live_paint_gaps::KIND] => live_paint_gaps::SPEC,
|
||||
LivePaintSelectionOptions: [live_paint_selection::KIND] => live_paint_selection::SPEC,
|
||||
LivePaintBucketOptions: [live_paint_bucket::KIND] => live_paint_bucket::SPEC,
|
||||
BrushOptions: [brush_options::KIND] => brush_options::SPEC,
|
||||
NewBrush: [new_brush::KIND] => new_brush::SPEC,
|
||||
EditSelection: [edit_selection::KIND] => edit_selection::SPEC,
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
//! Live Paint Bucket Options (double-click the Live Paint Bucket tool): what a click with the tool
|
||||
//! paints and how the face or edge under the pointer highlights. The options last across tool
|
||||
//! switches and are saved with the preferences. OK sets them with
|
||||
//! `tool.setOption {tool: "livePaintBucket", values}`.
|
||||
//!
|
||||
//! Fields: `paintFills` and `paintStrokes` (the faces and the edges of Live Paint groups),
|
||||
//! `highlight` (whether the face or edge under the pointer is outlined), `color` (the highlight
|
||||
//! color: `#rrggbb`, or a layer color's name such as "Red") and `width` (the highlight's width in
|
||||
//! screen pixels).
|
||||
|
||||
use serde_json::{Map, Value, json};
|
||||
use vectorcraft_color::Color;
|
||||
use vectorcraft_doc::LAYER_COLORS;
|
||||
use vectorcraft_tools::builder::LP_BUCKET_WIDTH_RANGE;
|
||||
|
||||
use super::tile_edge_color::resolve;
|
||||
use super::{DialogSpec, form, run_and_close};
|
||||
use crate::state::Dialog;
|
||||
use crate::{VectorcraftApp, widgets};
|
||||
|
||||
/// The dialog kind of the Live Paint Bucket Options.
|
||||
pub const KIND: &str = "livePaintBucketOptions";
|
||||
|
||||
/// The tool the dialog serves.
|
||||
const TOOL: &str = "livePaintBucket";
|
||||
|
||||
/// The options the dialog edits.
|
||||
const KEYS: [&str; 5] = ["paintFills", "paintStrokes", "highlight", "color", "width"];
|
||||
|
||||
/// Width of the label column.
|
||||
const LABEL_W: f32 = 60.0;
|
||||
|
||||
pub(super) const SPEC: DialogSpec = DialogSpec { heading, body, confirm, min_width: 300.0, ..DialogSpec::FORM };
|
||||
|
||||
fn heading(_: &Dialog) -> String {
|
||||
let label = vectorcraft_tools::tool_info(TOOL).map_or("", |t| t.label);
|
||||
crate::i18n::fmt(tl!("{tool} Options"), &[("tool", tl!(label))])
|
||||
}
|
||||
|
||||
/// Open the Live Paint Bucket Options with the tool's current values.
|
||||
pub fn open(app: &mut VectorcraftApp) -> Result<Value, String> {
|
||||
let o = app.session.tool_options_of(TOOL);
|
||||
let fields: Map<String, Value> = KEYS.iter().filter_map(|key| Some((key.to_string(), o.get(*key)?.clone()))).collect();
|
||||
app.ui.dialog = Some(Dialog { kind: KIND.into(), fields });
|
||||
Ok(json!({ "dialog": KIND }))
|
||||
}
|
||||
|
||||
fn body(_app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
|
||||
form::check(ui, d, "paintFills", tl!("Paint Fills"));
|
||||
form::check(ui, d, "paintStrokes", tl!("Paint Strokes"));
|
||||
ui.add_space(6.0);
|
||||
form::check(ui, d, "highlight", tl!("Highlight"));
|
||||
let color = resolve(&d.str("color")).unwrap_or(Color::BLACK);
|
||||
let [r, g, b, _] = color.to_rgba8(1.0);
|
||||
let preset = LAYER_COLORS.iter().position(|(_, c)| *c == [r, g, b]);
|
||||
let names: Vec<&str> = LAYER_COLORS.iter().map(|(n, _)| *n).chain(["Custom"]).collect();
|
||||
ui.add_enabled_ui(d.bool("highlight"), |ui| {
|
||||
widgets::label_row(ui, tl!("Color:"), LABEL_W, |ui| {
|
||||
if let Some((_, [r, g, b])) =
|
||||
widgets::dropdown(ui, "lp-bucket-color", names[preset.unwrap_or(LAYER_COLORS.len())], &names, 130.0).and_then(|i| LAYER_COLORS.get(i))
|
||||
{
|
||||
d.fields.insert("color".into(), json!(Color::rgb8(*r, *g, *b).to_hex()));
|
||||
}
|
||||
// The color button sets any other color; the dropdown then shows Custom.
|
||||
let mut rgb = [r, g, b];
|
||||
if ui.color_edit_button_srgb(&mut rgb).changed() {
|
||||
d.fields.insert("color".into(), json!(Color::rgb8(rgb[0], rgb[1], rgb[2]).to_hex()));
|
||||
}
|
||||
});
|
||||
let (min, max) = LP_BUCKET_WIDTH_RANGE;
|
||||
let v = d.f64("width", min);
|
||||
widgets::label_row(ui, tl!("Width:"), LABEL_W, |ui| {
|
||||
if let Some(x) = widgets::range_field(ui, "lp-bucket-width", v, min..=max, "px", 0, 72.0) {
|
||||
d.fields.insert("width".into(), json!(x));
|
||||
}
|
||||
});
|
||||
});
|
||||
false
|
||||
}
|
||||
|
||||
/// OK: set the tool's options.
|
||||
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
|
||||
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": TOOL, "values": values }))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use vectorcraft_engine::Session;
|
||||
use vectorcraft_tools::{Overlay, PointerEvent, PointerKind};
|
||||
|
||||
/// The labels the open dialog paints.
|
||||
fn painted(app: &mut VectorcraftApp) -> String {
|
||||
crate::tests_labels::painted_text(app, |app, ui| super::super::show(app, ui.ctx()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_clicking_the_tool_opens_its_options_and_ok_sets_them() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
|
||||
// Two squares side by side, made a Live Paint group: two faces and the edge between them.
|
||||
for x in [0, 100] {
|
||||
app.run("shape.rectangle", json!({"x": 50 + x, "y": 50, "width": 100, "height": 100})).unwrap();
|
||||
}
|
||||
app.run("select.all", json!({})).unwrap();
|
||||
app.run("livePaint.make", json!({})).unwrap();
|
||||
app.run("select.set", json!({"ids": []})).unwrap();
|
||||
app.select_tool(TOOL);
|
||||
let hover = |app: &mut VectorcraftApp, x: f64| {
|
||||
let view = app.view_info();
|
||||
crate::canvas::dispatch(app, &PointerEvent::new(PointerKind::Move, x, 100.0), view);
|
||||
app.session.overlays(view)
|
||||
};
|
||||
let ov = hover(&mut app, 151.0);
|
||||
assert!(ov.iter().any(|o| matches!(o, Overlay::Path { color: [0xff, 0x2a, 0x2a], width: 4.0, .. })), "{ov:?}");
|
||||
|
||||
crate::toolbar::open_options(&mut app, TOOL).unwrap();
|
||||
let text = painted(&mut app);
|
||||
for label in ["Live Paint Bucket Options", "Paint Fills", "Paint Strokes", "Highlight", "Color:", "Width:"] {
|
||||
assert!(text.contains(label), "{label} in {text}");
|
||||
}
|
||||
let d = app.ui.dialog.as_mut().unwrap();
|
||||
assert_eq!(d.kind, KIND);
|
||||
assert_eq!(
|
||||
(d.bool("paintFills"), d.bool("paintStrokes"), d.bool("highlight"), d.str("color"), d.f64("width", 0.0)),
|
||||
(true, false, true, "#ff2a2a".to_string(), 4.0)
|
||||
);
|
||||
d.fields.insert("paintFills".into(), json!(false));
|
||||
d.fields.insert("paintStrokes".into(), json!(true));
|
||||
d.fields.insert("color".into(), json!("Green"));
|
||||
d.fields.insert("width".into(), json!(8));
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
assert!(app.ui.dialog.is_none());
|
||||
// The tool takes them: by the edge, the edge highlights in the new color and width.
|
||||
let ov = hover(&mut app, 151.0);
|
||||
assert!(ov.iter().any(|o| matches!(o, Overlay::Path { color: [0x4f, 0xff, 0x4f], width: 8.0, .. })), "{ov:?}");
|
||||
// They last while another tool is chosen, and the dialog opens on them again.
|
||||
app.select_tool("selection");
|
||||
let o = app.session.tool_options_of(TOOL);
|
||||
assert_eq!((o["paintFills"].as_bool(), o["paintStrokes"].as_bool(), o["color"].as_str()), (Some(false), Some(true), Some("#4fff4f")));
|
||||
app.run("tool.options", json!({"tool": TOOL})).unwrap();
|
||||
let d = app.ui.dialog.as_mut().unwrap();
|
||||
assert_eq!((d.bool("paintStrokes"), d.str("color")), (true, "#4fff4f".to_string()));
|
||||
// Highlight off: the tool outlines nothing under the pointer.
|
||||
d.fields.insert("highlight".into(), json!(false));
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
app.select_tool(TOOL);
|
||||
assert!(!hover(&mut app, 151.0).iter().any(|o| matches!(o, Overlay::Path { .. })));
|
||||
}
|
||||
}
|
||||
@@ -1230,6 +1230,9 @@ font-technology Variable Променливи
|
||||
Hide Live Paint Gaps Скриване на пролуките на динамичното оцветяване
|
||||
Select Fills Избиране на запълвания
|
||||
Select Strokes Избиране на щрихове
|
||||
Paint Fills Боядисване на запълвания
|
||||
Paint Strokes Боядисване на щрихове
|
||||
Highlight Маркиране
|
||||
Gaussian Blur… Гаусово размазване…
|
||||
General Общи
|
||||
General › Placement › X Общи › Разположение › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -267,6 +267,9 @@
|
||||
Hide Live Paint Gaps Sakrij praznine živog bojenja
|
||||
Select Fills Odaberi ispune
|
||||
Select Strokes Odaberi poteze
|
||||
Paint Fills Boji ispune
|
||||
Paint Strokes Boji poteze
|
||||
Highlight Isticanje
|
||||
Image Trace Praćenje slike
|
||||
Image Trace (Make and Expand) Praćenje slike (napravi i proširi)
|
||||
Release Image Trace Oslobodi praćenje slike
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1859,6 +1859,7 @@ digits Malayalam Malaiàlam
|
||||
High Quality Print Impressió d’alta qualitat
|
||||
High Resolution Alta resolució
|
||||
High resolution Alta resolució
|
||||
Highlight Ressaltat
|
||||
Highlight Alternate Glyphs Ressalta els glifs alternatius
|
||||
Highlight anchors on mouse over Ressalta els punts d’ancoratge en passar el ratolí
|
||||
Highlight Area Ressalta l’àrea
|
||||
@@ -2844,8 +2845,10 @@ digits Malayalam Malaiàlam
|
||||
Pages Pàgines
|
||||
Paint Daubs Taques de pintura
|
||||
Paint Daubs… Taques de pintura…
|
||||
Paint Fills Pinta els farciments
|
||||
Paint Opacity: Opacitat de pintura:
|
||||
Paint Stops At: La pintura s’atura a:
|
||||
Paint Strokes Pinta els traços
|
||||
Paintbrush Pinzell
|
||||
Paintbrush Tool Eina de pinzell
|
||||
Palette Knife Espàtula
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -277,6 +277,9 @@
|
||||
Hide Live Paint Gaps Skrýt mezery živé malby
|
||||
Select Fills Vybírat výplně
|
||||
Select Strokes Vybírat tahy
|
||||
Paint Fills Malovat výplně
|
||||
Paint Strokes Malovat tahy
|
||||
Highlight Zvýraznění
|
||||
Image Trace Vektorizace obrazu
|
||||
Image Trace (Make and Expand) Vektorizace obrazu (vytvořit a rozložit)
|
||||
Release Image Trace Uvolnit vektorizaci obrazu
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5312,6 +5312,9 @@ swatch Bright Violet Kraftig violet
|
||||
Close Gaps With Paths Luk huller med stier
|
||||
Select Fills Markér udfyldninger
|
||||
Select Strokes Markér konturer
|
||||
Paint Fills Mal udfyldninger
|
||||
Paint Strokes Mal konturer
|
||||
Highlight Fremhævning
|
||||
Column Type: Søjletype:
|
||||
Each Design Represents: Hvert design repræsenterer:
|
||||
Event: Hændelse:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1254,6 +1254,9 @@ file-filter Templates Vorlagen
|
||||
Hide Live Paint Gaps Lücken beim interaktiven Malen ausblenden
|
||||
Select Fills Flächen auswählen
|
||||
Select Strokes Konturen auswählen
|
||||
Paint Fills Flächen malen
|
||||
Paint Strokes Konturen malen
|
||||
Highlight Hervorheben
|
||||
Gaussian Blur… Gaußscher Weichzeichner…
|
||||
General Allgemein
|
||||
General › Placement › X Allgemein › Platzierung › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1196,6 +1196,9 @@ font-technology Variable Μεταβλητές
|
||||
Hide Live Paint Gaps Απόκρυψη διακένων δυναμικής βαφής
|
||||
Select Fills Επιλογή γεμισμάτων
|
||||
Select Strokes Επιλογή περιγραμμάτων
|
||||
Paint Fills Βαφή γεμισμάτων
|
||||
Paint Strokes Βαφή περιγραμμάτων
|
||||
Highlight Επισήμανση
|
||||
Gaussian Blur… Θόλωση Γκάους…
|
||||
General Γενικά
|
||||
General › Placement › X Γενικά › Τοποθέτηση › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 164 and column 5.
|
@@ -1267,6 +1267,9 @@ font-technology Variable Variables
|
||||
Hide Live Paint Gaps Ocultar huecos de pintura interactiva
|
||||
Select Fills Seleccionar rellenos
|
||||
Select Strokes Seleccionar trazos
|
||||
Paint Fills Pintar rellenos
|
||||
Paint Strokes Pintar trazos
|
||||
Highlight Resaltado
|
||||
Gaussian Blur… Desenfoque gaussiano…
|
||||
General General
|
||||
General › Placement › X General › Colocación › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -284,6 +284,9 @@
|
||||
Hide Live Paint Gaps Piilota elävän maalauksen aukot
|
||||
Select Fills Valitse täytöt
|
||||
Select Strokes Valitse viivat
|
||||
Paint Fills Maalaa täytöt
|
||||
Paint Strokes Maalaa viivat
|
||||
Highlight Korostus
|
||||
Image Trace Kuvan jäljitys
|
||||
Image Trace (Make and Expand) Kuvan jäljitys (luo ja laajenna)
|
||||
Release Image Trace Vapauta kuvan jäljitys
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1267,6 +1267,9 @@ font-technology Variable Variables
|
||||
Hide Live Paint Gaps Masquer les espaces de peinture dynamique
|
||||
Select Fills Sélectionner les fonds
|
||||
Select Strokes Sélectionner les contours
|
||||
Paint Fills Peindre les fonds
|
||||
Paint Strokes Peindre les contours
|
||||
Highlight Mise en évidence
|
||||
Gaussian Blur… Flou gaussien…
|
||||
General Général
|
||||
General › Placement › X Général › Placement › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1584,6 +1584,9 @@ file-filter Templates Sablonok
|
||||
Hide Live Paint Gaps Élő festés hézagainak elrejtése
|
||||
Select Fills Kitöltések kijelölése
|
||||
Select Strokes Körvonalak kijelölése
|
||||
Paint Fills Kitöltések festése
|
||||
Paint Strokes Körvonalak festése
|
||||
Highlight Kiemelés
|
||||
Gaussian Blur Gauss-elmosás
|
||||
Gaussian Blur… Gauss-elmosás…
|
||||
General Általános
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5312,6 +5312,9 @@ swatch Bright Violet Violet cerah
|
||||
Close Gaps With Paths Tutup celah dengan jalur
|
||||
Select Fills Pilih isian
|
||||
Select Strokes Pilih stroke
|
||||
Paint Fills Cat isian
|
||||
Paint Strokes Cat stroke
|
||||
Highlight Sorotan
|
||||
Column Type: Jenis kolom:
|
||||
Each Design Represents: Setiap desain mewakili:
|
||||
Event: Peristiwa:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2190 and column 5.
|
@@ -1277,6 +1277,9 @@ file-filter Templates Modelli
|
||||
Hide Live Paint Gaps Nascondi spazi vuoti pittura dinamica
|
||||
Select Fills Seleziona riempimenti
|
||||
Select Strokes Seleziona tracce
|
||||
Paint Fills Colora riempimenti
|
||||
Paint Strokes Colora tracce
|
||||
Highlight Evidenziazione
|
||||
Gaussian Blur… Sfocatura gaussiana…
|
||||
General Generale
|
||||
General › Placement › X Generale › Posizionamento › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -292,6 +292,9 @@ digits Malayalam マラヤーラム数字
|
||||
Hide Live Paint Gaps ライブペイントの間隔を隠す
|
||||
Select Fills 塗りを選択
|
||||
Select Strokes 線を選択
|
||||
Paint Fills 塗りをペイント
|
||||
Paint Strokes 線をペイント
|
||||
Highlight ハイライト
|
||||
Image Trace 画像トレース
|
||||
Image Tracing 画像トレースオブジェクト
|
||||
Tracing Presets トレースプリセット
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5339,6 +5339,9 @@ imageTrace Outline კონტური
|
||||
Scale in Perspective პერსპექტივაში მასშტაბირება
|
||||
Select Fills შევსებების მონიშვნა
|
||||
Select Strokes კონტურების მონიშვნა
|
||||
Paint Fills შევსებების შეღებვა
|
||||
Paint Strokes კონტურების შეღებვა
|
||||
Highlight გამოკვეთა
|
||||
Set History Limit… ისტორიის ლიმიტის დაყენება…
|
||||
Shape Builder Delete ფიგურების კონსტრუქტორით წაშლა
|
||||
Sliding სრიალა
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -285,6 +285,9 @@ digits Malayalam 말라얄람어
|
||||
Hide Live Paint Gaps 라이브 페인트 간격 숨기기
|
||||
Select Fills 칠 선택
|
||||
Select Strokes 선 선택
|
||||
Paint Fills 칠 페인트
|
||||
Paint Strokes 선 페인트
|
||||
Highlight 강조 표시
|
||||
Image Trace 이미지 추적
|
||||
Image Tracing 이미지 추적
|
||||
Tracing Presets 추적 프리셋
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5323,6 +5323,9 @@ swatch Bright Violet Felviolet
|
||||
Close Gaps With Paths Openingen sluiten met paden
|
||||
Select Fills Vullingen selecteren
|
||||
Select Strokes Lijnen selecteren
|
||||
Paint Fills Vullingen verven
|
||||
Paint Strokes Lijnen verven
|
||||
Highlight Markering
|
||||
Column Type: Kolomtype:
|
||||
Each Design Represents: Elk ontwerp staat voor:
|
||||
Event: Gebeurtenis:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -242,6 +242,9 @@
|
||||
Hide Live Paint Gaps Ukryj przerwy aktywnego malowania
|
||||
Select Fills Zaznaczaj wypełnienia
|
||||
Select Strokes Zaznaczaj obrysy
|
||||
Paint Fills Maluj wypełnienia
|
||||
Paint Strokes Maluj obrysy
|
||||
Highlight Podświetlenie
|
||||
Image Trace Obrys obrazu
|
||||
Text Wrap Oblewanie tekstem
|
||||
Text Wrap Options… Opcje oblewania tekstem…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1116,6 +1116,9 @@
|
||||
Hide Live Paint Gaps Ocultar Lacunas da Pintura ao Vivo
|
||||
Select Fills Selecionar Preenchimentos
|
||||
Select Strokes Selecionar Traçados
|
||||
Paint Fills Pintar Preenchimentos
|
||||
Paint Strokes Pintar Traçados
|
||||
Highlight Destaque
|
||||
Gaussian Blur… Desfoque Gaussiano…
|
||||
General Geral
|
||||
Generate CSS Gerar CSS
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1275,6 +1275,9 @@ file-filter Templates Шаблоны
|
||||
Hide Live Paint Gaps Скрыть зазоры быстрой заливки
|
||||
Select Fills Выделять заливки
|
||||
Select Strokes Выделять обводки
|
||||
Paint Fills Рисовать заливки
|
||||
Paint Strokes Рисовать обводки
|
||||
Highlight Подсветка
|
||||
Gaussian Blur… Размытие по Гауссу…
|
||||
General Общие
|
||||
General › Placement › X Общие › Размещение › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1232,6 +1232,9 @@ font-technology Variable Variabla
|
||||
Hide Live Paint Gaps Dölj mellanrum i direktmålning
|
||||
Select Fills Markera fyllningar
|
||||
Select Strokes Markera konturer
|
||||
Paint Fills Måla fyllningar
|
||||
Paint Strokes Måla konturer
|
||||
Highlight Framhävning
|
||||
Gaussian Blur… Gaussisk oskärpa…
|
||||
General Allmänt
|
||||
General › Placement › X Allmänt › Placering › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -5364,6 +5364,9 @@ theme Appearance Görünüm
|
||||
Overlap Örtüşme
|
||||
Select Fills Dolguları Seç
|
||||
Select Strokes Konturları Seç
|
||||
Paint Fills Dolguları Boya
|
||||
Paint Strokes Konturları Boya
|
||||
Highlight Vurgulama
|
||||
Snap to Unit Birim Değerine Yasla
|
||||
The plug-in writes the whole document. Eklenti tüm belgeyi yazar.
|
||||
Attach İliştir
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -1271,6 +1271,9 @@ file-filter Templates Шаблони
|
||||
Hide Live Paint Gaps Приховати проміжки швидкої заливки
|
||||
Select Fills Виділяти заливки
|
||||
Select Strokes Виділяти обведення
|
||||
Paint Fills Фарбувати заливки
|
||||
Paint Strokes Фарбувати обведення
|
||||
Highlight Підсвічування
|
||||
Gaussian Blur… Розмиття за Гауссом…
|
||||
General Загальні
|
||||
General › Placement › X Загальні › Розміщення › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 178 and column 5.
|
@@ -1486,6 +1486,9 @@ file-filter Templates Mẫu
|
||||
Hide Live Paint Gaps Ẩn khoảng cách tô trực tiếp
|
||||
Select Fills Chọn vùng tô
|
||||
Select Strokes Chọn nét
|
||||
Paint Fills Tô vùng tô
|
||||
Paint Strokes Tô nét
|
||||
Highlight Làm nổi bật
|
||||
Gaussian Blur Mờ Gaussian
|
||||
Gaussian Blur… Làm mờ Gaussian…
|
||||
General Chung
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 200 and column 5.
|
@@ -1215,6 +1215,9 @@ file-filter Templates 模板
|
||||
Hide Live Paint Gaps 隐藏实时上色间隙
|
||||
Select Fills 选择填色
|
||||
Select Strokes 选择描边
|
||||
Paint Fills 为填色上色
|
||||
Paint Strokes 为描边上色
|
||||
Highlight 突出显示
|
||||
Gaussian Blur… 高斯模糊…
|
||||
General 常规
|
||||
General › Placement › X 一般 › 位置 › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1241,6 +1241,9 @@ file-filter Templates 範本
|
||||
Hide Live Paint Gaps 隱藏即時上色間隙
|
||||
Select Fills 選取填色
|
||||
Select Strokes 選取筆畫
|
||||
Paint Fills 為填色上色
|
||||
Paint Strokes 為筆畫上色
|
||||
Highlight 強調顯示
|
||||
Gaussian Blur… 高斯模糊…
|
||||
General 一般
|
||||
General › Placement › X 一般 › 位置 › X
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
File diff suppressed because one or more lines are too long
@@ -377,6 +377,8 @@ pub fn open_options(app: &mut VectorcraftApp, tool: &str) -> Result<serde_json::
|
||||
_ if crate::dialogs::freehand::TOOLS.contains(&tool) => crate::dialogs::freehand::open(app, tool),
|
||||
// The Live Paint Selection tool: its Tool Options (what a click selects, the highlight).
|
||||
"livePaintSelection" => crate::dialogs::live_paint_selection::open(app),
|
||||
// The Live Paint Bucket: its options (what a click paints, the highlight).
|
||||
"livePaintBucket" => crate::dialogs::live_paint_bucket::open(app),
|
||||
// The Symbolism tools: the brush they share.
|
||||
_ if vectorcraft_tools::settings::SYMBOLISM.contains(&tool) => Ok(crate::dialogs::symbolism_options::open(app, tool)),
|
||||
// The graph tools: Graph Type for the selected graph.
|
||||
|
||||
@@ -1701,6 +1701,15 @@ already walks Image Trace. Short agent entry points:
|
||||
release, and Escape between the press and the release removes the marquee so that the release runs nothing. The
|
||||
tool does not highlight or select the faces and edges of a Live Paint group that is locked, on a locked layer or on
|
||||
a locked artboard, or in isolation mode outside the isolated art.
|
||||
The Live Paint Bucket's options (`tool.setOption {tool: "livePaintBucket", values}`; double-clicking the tool opens
|
||||
them, dialog `livePaintBucketOptions`) are `paintFills` (on), `paintStrokes` (off), `highlight` (on), `color` (the
|
||||
highlight color, `#rrggbb` or a layer color's name; `#ff2a2a`) and `width` (the highlight's width, 1 to 20 screen
|
||||
pixels; 4). With `paintFills` alone on, a click runs `livePaint.fill` and Shift-click runs `livePaint.strokeEdge`.
|
||||
With `paintStrokes` alone on, a click runs `livePaint.strokeEdge` on the edge within 4 screen pixels of the pointer,
|
||||
or nothing when none is in reach, and Shift-click runs `livePaint.fill`. With both on, a click runs
|
||||
`livePaint.strokeEdge` when an edge is within 4 screen pixels of the pointer and `livePaint.fill` otherwise, and
|
||||
Shift-click runs `livePaint.fill`. With both off, a click runs nothing. With `highlight` off, the face or edge under
|
||||
the pointer is not outlined.
|
||||
`livePaint.merge` keeps the bottom Live Paint group's id, name, lock, visibility, transparency, appearance, opacity
|
||||
mask and graphic style link. With a Live Paint group among the objects, `livePaint.make` adds the others to it as
|
||||
`livePaint.merge` does, and so does `livePaint.fill {ids}` when `point` is outside that group's faces.
|
||||
|
||||
@@ -928,6 +928,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- Create Strokes uses exact squared Euclidean distances and Guo–Hall thinning in bounded per-blob crops. Maximal-disc pruning, split-crossing merges and degree-two joins simplify its skeleton graph. The whole-blob width rule and ink fitting remain; 46 code-drawn inputs measure ends and junctions in [`image-trace-quality.md`](image-trace-quality.md#centre-lines). Junction degrees measure 44/45 correct in each input variant; remaining spurs are recorded. This is a measured skeleton improvement; the Image Trace score, missing per-feature selection and width controls, and estimates remain unchanged.
|
||||
Object › Live Paint › Merge adds the other selected paths and Live Paint groups to the bottom selected Live Paint group ([Live Paint groups](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/live-paint-groups.html)), which keeps its id, the paint of its faces and edges, and its name, lock, visibility, transparency, appearance, opacity mask and graphic style link. Object › Live Paint › Make with a Live Paint group selected, and a Live Paint Bucket click on a selected path beside a selected Live Paint group, add the paths to that group the same way. Make, Merge and the Live Paint Bucket take paths, compound paths and groups of them; with type, an image or another object among the selection they report that it has to be converted to paths first and change nothing, as type and raster images can't go into a Live Paint group directly ([Create Live Paint groups](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/create-live-paint-groups.html)); before, such an object went among the group's hidden sources and vanished from the canvas. Evidence: `tests_build::live_paint_refuses_type_and_images_and_changes_nothing`.
|
||||
With the Live Paint Bucket selected, Alt-click on vector art samples its fill, stroke and stroke weight into the paint defaults ([Paint with the Live Paint Bucket tool](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/paint-with-the-live-paint-bucket-tool.html)). On a placed image it samples the image pixel as a solid fill with no stroke. Alt+Shift-click samples only the color under the pointer, including an intermediate gradient color or image pixels, into the active fill or stroke ([Default keyboard shortcuts](https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html)). Neither click paints or changes the selection or selected art, and the next Bucket click paints with the sampled paint. Evidence: `tests_build::live_paint_bucket_alt_sample_paints_next_face_without_changing_selection`.
|
||||
Double-clicking the Live Paint Bucket opens Live Paint Bucket Options ([Paint with the Live Paint Bucket tool](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/paint-with-the-live-paint-bucket-tool.html)): Paint Fills (on) and Paint Strokes (off) set whether a click paints faces, edges or both, Highlight turns the outline of the face or edge under the pointer on or off, and Color and Width set it (red and 4 px by default). With both on, a click paints the edge within 4 px of the pointer, else the face, and Shift switches to Paint Fills only; with one on, Shift switches to the other. The options last across tool switches and are saved with the preferences (`tool.setOption {tool: "livePaintBucket"}`). Evidence: `builder::tests::{bucket_options_choose_what_a_click_paints, bucket_options_set_the_highlight}`, `dialogs::live_paint_bucket::tests::double_clicking_the_tool_opens_its_options_and_ok_sets_them`.
|
||||
Live Paint Gap Options (Object › Live Paint › Gap Options…, `livePaint.gapOptions`): with Gap Detection on, paint stops at the gaps that run from a loose end of a path to the nearest path, up to Small (3 pt), Medium (6 pt), Large (12 pt) or a custom size; Close Gaps With Paths adds an unpainted path across each gap; Preview applies the options live and draws the gaps in the Gap Preview Color. The options are saved, copied and undone with the group, and Merge keeps the bottom group's options when its Gap Detection is on, or else turns Gap Detection on with Small when another merged group has it on. When Gap Detection is off in every merged group, it stays off.
|
||||
View › Show Live Paint Gaps (`view.livePaintGaps`) draws the gaps of the selected Live Paint groups up to their Paint Stops At size, each in its group's Gap Preview Color, until Hide Live Paint Gaps or Hide Edges hides them ([Live Paint gaps](https://web.archive.org/web/20260105111201/https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/find-and-close-gaps-in-live-paint-groups.html)).
|
||||
Double-clicking the Live Paint Selection tool opens its Tool Options ([Live Paint Selection tool options](https://web.archive.org/web/20250904012025/https://helpx.adobe.com/illustrator/using/selecting-objects.html)): with Select Fills off, a click selects an edge instead of a face; with Select Strokes off, Alt-click selects a face instead of an edge; Color and Width set the color and width of the highlight on the face or edge under the pointer (red and 4 px by default). The options last across tool switches and are saved with the preferences (`tool.setOption {tool: "livePaintSelection"}`).
|
||||
|
||||
Reference in New Issue
Block a user