mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 16:33:22 -06:00
fix: 線の色追加時にゼロ幅を既定値へ戻す #1253
This commit is contained in:
committed by
bflatastic
parent
540505fcad
commit
2f64b5a86b
@@ -446,6 +446,9 @@ fn apply_paint(s: &mut Session, p: &Value, paint: Paint, fill: bool) -> Result<V
|
||||
if n.appearance.paint_at(index, fill).is_none() {
|
||||
n.appearance.set_paint_at(index, fill, Paint::None);
|
||||
}
|
||||
if !fill {
|
||||
restore_unpainted_stroke(&mut n.appearance, index, &paint);
|
||||
}
|
||||
let placed = place_paint(&paint, p, item_paint_bounds(n, index, fill));
|
||||
n.appearance.set_paint_at(index, fill, placed);
|
||||
Ok(())
|
||||
@@ -470,6 +473,9 @@ fn apply_paint(s: &mut Session, p: &Value, paint: Paint, fill: bool) -> Result<V
|
||||
if fill {
|
||||
s.paint.fill = unplaced(&paint);
|
||||
} else {
|
||||
if !paint.is_none() && s.paint.stroke.is_none() && s.paint.stroke_width == 0.0 {
|
||||
s.paint.stroke_width = 1.0;
|
||||
}
|
||||
s.paint.stroke = unplaced(&paint);
|
||||
}
|
||||
if bool_or(p, "focus", true) {
|
||||
@@ -495,6 +501,17 @@ fn set_run_stroke(style: &mut CharStyle, paint: Paint) {
|
||||
style.stroke = paint;
|
||||
}
|
||||
|
||||
/// Use the existing 1 pt default when adding paint to an unpainted zero-width stroke.
|
||||
fn restore_unpainted_stroke(ap: &mut Appearance, index: Option<usize>, paint: &Paint) {
|
||||
if !paint.is_none()
|
||||
&& let Some(stroke) = ap.stroke_at_mut(index)
|
||||
&& stroke.paint.is_none()
|
||||
&& stroke.width == 0.0
|
||||
{
|
||||
stroke.width = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
fn swap(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let ids = paint_targets(s, p)?;
|
||||
if !ids.is_empty() {
|
||||
@@ -512,11 +529,15 @@ fn swap(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let f = n.appearance.fill_paint();
|
||||
let st = n.appearance.stroke_paint();
|
||||
n.appearance.set_fill(st);
|
||||
restore_unpainted_stroke(&mut n.appearance, None, &f);
|
||||
n.appearance.set_stroke(f);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
if s.paint.stroke.is_none() && !s.paint.fill.is_none() && s.paint.stroke_width == 0.0 {
|
||||
s.paint.stroke_width = 1.0;
|
||||
}
|
||||
std::mem::swap(&mut s.paint.fill, &mut s.paint.stroke);
|
||||
ok()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
//! Painting a previously unpainted zero-width stroke restores the existing 1 pt default.
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use serde_json::json;
|
||||
use vectorcraft_doc::NodeId;
|
||||
use vectorcraft_engine::{Session, ViewInfo};
|
||||
use vectorcraft_tools::{PointerEvent, PointerKind};
|
||||
|
||||
fn fixture() -> (Session, NodeId) {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
||||
let id = rectangle(&mut s, 10.0);
|
||||
(s, id)
|
||||
}
|
||||
|
||||
fn rectangle(s: &mut Session, x: f64) -> NodeId {
|
||||
NodeId(s.execute("shape.rectangle", &json!({"x": x, "y": 10, "width": 50, "height": 40})).unwrap()["id"].as_u64().unwrap())
|
||||
}
|
||||
|
||||
fn width(s: &Session, id: NodeId) -> f64 {
|
||||
s.doc().unwrap().doc.node(id).unwrap().appearance.stroke().unwrap().width
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_a_stroke_after_sampling_strokeless_art_makes_it_visible_in_one_undo_step() {
|
||||
let (mut s, _) = fixture();
|
||||
s.execute("paint.setStroke", &json!({"none": true})).unwrap();
|
||||
s.execute("select.set", &json!({"ids": []})).unwrap();
|
||||
s.select_tool("eyedropper", ViewInfo::default()).unwrap();
|
||||
s.pointer(&PointerEvent::new(PointerKind::Down, 25.0, 25.0), ViewInfo::default()).unwrap();
|
||||
let id = rectangle(&mut s, 100.0);
|
||||
assert_eq!(width(&s, id), 0.0, "sampled None paint is a normal source of a zero-width stroke");
|
||||
let st = s.doc().unwrap();
|
||||
let before = st.doc.clone();
|
||||
let sel = st.selection.clone();
|
||||
let history = st.history.undo.len();
|
||||
let geometry = st.doc.node(id).unwrap().geometric_bounds();
|
||||
s.execute("paint.setStroke", &json!({"color": "#ee2222"})).unwrap();
|
||||
assert_eq!(width(&s, id), 1.0);
|
||||
let st = s.doc().unwrap();
|
||||
assert_eq!(st.doc.node(id).unwrap().geometric_bounds(), geometry);
|
||||
assert_eq!(json!(st.selection), json!(sel));
|
||||
assert_eq!(st.history.undo.len(), history + 1);
|
||||
let after = st.doc.clone();
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(json!(s.doc().unwrap().doc), json!(before));
|
||||
s.execute("edit.redo", &json!({})).unwrap();
|
||||
assert_eq!(json!(s.doc().unwrap().doc), json!(after));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_stroke_paint_to_the_defaults_gives_the_next_shape_a_visible_stroke() {
|
||||
let (mut s, _) = fixture();
|
||||
s.execute("select.set", &json!({"ids": []})).unwrap();
|
||||
s.execute("paint.setStroke", &json!({"none": true})).unwrap();
|
||||
s.execute("stroke.set", &json!({"weight": 0})).unwrap();
|
||||
s.execute("paint.setStroke", &json!({"color": "#ee2222"})).unwrap();
|
||||
assert_eq!(s.new_art_stroke().width, 1.0);
|
||||
let id = rectangle(&mut s, 100.0);
|
||||
assert_eq!(width(&s, id), 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn swapping_fill_into_an_unpainted_zero_width_stroke_makes_it_visible() {
|
||||
let (mut s, id) = fixture();
|
||||
s.execute("paint.setStroke", &json!({"none": true})).unwrap();
|
||||
s.execute("stroke.set", &json!({"weight": 0})).unwrap();
|
||||
let fill = s.doc().unwrap().doc.node(id).unwrap().appearance.fill_paint();
|
||||
s.execute("paint.swap", &json!({})).unwrap();
|
||||
let n = s.doc().unwrap().doc.node(id).unwrap();
|
||||
assert!(n.appearance.fill_paint().is_none());
|
||||
assert_eq!(n.appearance.stroke_paint(), fill);
|
||||
assert_eq!(width(&s, id), 1.0);
|
||||
assert_eq!(s.new_art_stroke().width, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_targeted_stroke_is_restored_without_changing_the_other_stroke() {
|
||||
let (mut s, id) = fixture();
|
||||
s.execute("stroke.set", &json!({"weight": 5})).unwrap();
|
||||
s.execute("appearance.addStroke", &json!({})).unwrap();
|
||||
s.execute("paint.setStroke", &json!({"item": 1, "none": true})).unwrap();
|
||||
s.execute("stroke.set", &json!({"item": 1, "weight": 0})).unwrap();
|
||||
s.execute("paint.setStroke", &json!({"item": 1, "color": "#ee2222"})).unwrap();
|
||||
let ap = &s.doc().unwrap().doc.node(id).unwrap().appearance;
|
||||
assert_eq!(ap.stroke_at(Some(1)).unwrap().width, 1.0);
|
||||
assert_eq!(ap.stroke_at(Some(2)).unwrap().width, 5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn existing_weights_and_explicitly_zero_painted_strokes_keep_their_widths() {
|
||||
for (was_none, weight) in [(true, 5.0), (false, 5.0), (false, 0.0)] {
|
||||
let (mut s, id) = fixture();
|
||||
if was_none {
|
||||
s.execute("paint.setStroke", &json!({"none": true})).unwrap();
|
||||
}
|
||||
s.execute("stroke.set", &json!({"weight": weight})).unwrap();
|
||||
s.execute("paint.setStroke", &json!({"color": "#ee2222"})).unwrap();
|
||||
assert_eq!(width(&s, id), weight);
|
||||
}
|
||||
let (mut s, id) = fixture();
|
||||
s.execute("paint.setStroke", &json!({"none": true})).unwrap();
|
||||
s.execute("stroke.set", &json!({"weight": 0})).unwrap();
|
||||
s.execute("paint.setFill", &json!({"color": "#ffaa00"})).unwrap();
|
||||
s.execute("paint.setStroke", &json!({"none": true})).unwrap();
|
||||
assert_eq!(width(&s, id), 0.0, "fill edits and removing paint do not enable a stroke");
|
||||
}
|
||||
Reference in New Issue
Block a user