Move the gradient spread method into GradientRamp and the color picker popover (#4402)

* Add a spread method field to GradientRamp, carried at runtime as the gradient item's attribute

* Retire the Fill node's spread method input, folding its value into the gradient ramps on document upgrade

* Add an Ends spread method radio to the color picker popover, replacing the Gradient tool's control bar radio

* Update the demo art
This commit is contained in:
Keavon Chambers
2026-08-04 04:08:21 -07:00
committed by Dennis Kobert
parent 2196d308a7
commit 26d67eb48b
19 changed files with 400 additions and 193 deletions

View File

@@ -167,7 +167,15 @@ macro_rules! tagged_value {
Self::DashPattern(lengths) => Box::new(DashPattern::from(lengths)),
Self::BoxCorners(values) => Box::new(BoxCorners::from(values)),
Self::Color(color) => Box::new(List::<Color>::new_from_element(color)),
Self::GradientRamp(ramp) => Box::new(List::<Gradient>::new_from_element(Gradient::from(ramp))),
Self::GradientRamp(ramp) => {
// The ramp's spread method rides the served list as its attribute, as `Item<Gradient>::from` does on master.
let spread_method = ramp.spread_method;
let mut list = List::<Gradient>::new_from_element(Gradient::from(ramp));
if !spread_method.is_default() {
list.set_attribute(graphic_types::vector_types::ATTR_SPREAD_METHOD, 0, spread_method);
}
Box::new(list)
}
Self::BrushStrokes(strokes) => {
let list: List<BrushStroke> = strokes.into_iter().map(core_types::list::Item::new_from_element).collect();
Box::new(list)
@@ -213,7 +221,15 @@ macro_rules! tagged_value {
Self::DashPattern(lengths) => Arc::new(DashPattern::from(lengths)),
Self::BoxCorners(values) => Arc::new(BoxCorners::from(values)),
Self::Color(color) => Arc::new(List::<Color>::new_from_element(color)),
Self::GradientRamp(ramp) => Arc::new(List::<Gradient>::new_from_element(Gradient::from(ramp))),
Self::GradientRamp(ramp) => {
// The ramp's spread method rides the served list as its attribute, as `Item<Gradient>::from` does on master.
let spread_method = ramp.spread_method;
let mut list = List::<Gradient>::new_from_element(Gradient::from(ramp));
if !spread_method.is_default() {
list.set_attribute(graphic_types::vector_types::ATTR_SPREAD_METHOD, 0, spread_method);
}
Arc::new(list)
}
Self::BrushStrokes(strokes) => {
let list: List<BrushStroke> = strokes.into_iter().map(core_types::list::Item::new_from_element).collect();
Arc::new(list)
@@ -1086,6 +1102,8 @@ mod paint_default_parsing {
#[cfg(test)]
mod gradient_shape_migration {
use graphic_types::vector_types::GradientSpreadMethod;
use super::*;
fn load(payload: serde_json::Value) -> TaggedValue {
@@ -1104,7 +1122,10 @@ mod gradient_shape_migration {
fn modern_ramp_payload_round_trips() {
let mut gradient = Gradient::from(vec![Color::BLACK, Color::WHITE]);
gradient.set_positions(&[0.2, 0.9]);
let value = TaggedValue::GradientRamp(GradientRamp::from(gradient));
let value = TaggedValue::GradientRamp(GradientRamp {
spread_method: GradientSpreadMethod::Reflect,
..GradientRamp::from(gradient)
});
let json = serde_json::to_value(&value).unwrap();
assert!(json.get("GradientRamp").and_then(|payload| payload.get("stops")).is_some(), "the payload should nest its stops: {json}");