Graphs: a style that removes a mark's painted row resets the stored opacity

Applying or redefining a graphic style on a series mark whose fill or
stroke had paint, when the style leaves that row without paint, now
stores the row's opacity and blend mode on the graph. Before, the
opacity and blend mode another mark of the series left stayed, so a
Filled Lines band's outline kept a stroke opacity after a style without
a stroke went on a stroked marker. A row with no paint before and after
the edit still keeps what the graph stores.
This commit is contained in:
dotCooCoo
2026-11-08 06:50:03 -08:00
parent 32abbfdb8f
commit 76bb829e3a
2 changed files with 32 additions and 2 deletions
+6 -2
View File
@@ -687,11 +687,15 @@ pub(super) fn capture_series_writes(
let items = d.node(id).map_or(&[][..], |n| &n.appearance.items[..]);
items.iter().rev().find(|it| it.is_fill() == kind).is_some_and(AppearanceItem::visible)
};
let written = |d: &vectorcraft_doc::Document, (fill, stroke): (bool, bool)| -> Vec<(NodeId, bool)> {
let kinds = |(fill, stroke): (bool, bool)| -> Vec<(NodeId, bool)> {
let rows = ids.iter().flat_map(|id| [fill.then_some((*id, true)), stroke.then_some((*id, false))]).flatten();
painted_rows(d, rows.filter(|(id, kind)| !top_only || top_shows(d, *id, *kind)))
rows.filter(|(id, kind)| !top_only || top_shows(d, *id, *kind)).collect()
};
let written = |d: &vectorcraft_doc::Document, rows: (bool, bool)| painted_rows(d, kinds(rows));
let mut opacity_rows = top_opacity_changed(before, d, ids);
// A written row that had paint before the edit counts too when the edit left it without paint: only a row without
// paint both before and after keeps what the graph stores.
opacity_rows.extend(painted_rows(before, kinds(opacities)));
opacity_rows.extend(written(d, opacities));
let mut rows = top_paint_changed(before, d, ids);
rows.extend(written(d, paints));
+26
View File
@@ -1218,6 +1218,32 @@ fn a_filled_line_bands_outline_keeps_a_stroke_opacity_and_blend_mode() {
assert_eq!((st.opacity, st.blend), (0.5, BlendMode::Multiply));
}
/// A graphic style without a stroke applied to a marker whose stroke had paint replaces the stroke opacity and blend mode
/// the series' Filled Lines band left: the band's outline is drawn opaque and Normal again. A marker that had no stroke
/// before the style leaves them as they are.
#[test]
fn a_style_that_removes_a_marks_stroke_replaces_the_kept_stroke_opacity() {
for stroked in [true, false] {
let (mut s, id, band) = band_graph();
let marker = series_parts(&s, id, 0)[1].id;
if stroked {
let row = top_row(&s, marker, false);
s.execute("appearance.setItem", &json!({"ids": [marker.0], "index": row, "color": "#ff0000"})).unwrap();
}
let row = top_row(&s, band, false);
s.execute("appearance.setItem", &json!({"ids": [band.0], "index": row, "opacity": 40, "blend": "multiply"})).unwrap();
assert_eq!(spec(&s, id).series_paints[0].stroke_opacity, Some(0.4));
blue_style(&mut s, "Blue", false);
s.execute("graphicStyle.apply", &json!({"name": "Blue", "ids": [marker.0]})).unwrap();
let kept = spec(&s, id).series_paints[0].clone();
let want = if stroked { (None, None) } else { (Some(0.4), Some(BlendMode::Multiply)) };
assert_eq!((kept.stroke_opacity, kept.stroke_blend), want, "stroked {stroked}");
redrawn_band(&mut s, id);
let st = series_parts(&s, id, 0)[0].appearance.stroke().unwrap().clone();
assert_eq!((Some(st.opacity).filter(|o| *o < 1.0), Some(st.blend).filter(|b| *b != BlendMode::Normal)), want, "stroked {stroked}");
}
}
/// A Filled Lines band's stroke row removed with the Appearance panel or set to None with `paint.setStroke` leaves the
/// band its color: an empty stored stroke is not a color for the band.
#[test]