Add support for gradients with midpoints and add draggable diamonds to the color picker dialog (#3813)

* Refactor GradientStops to use struct-of-arrays and include midpoint

* Implement interaction and rendering

* Make color picker saturation-value color picking snap to original position and show both axis lines

Make color picker saturation-value color picking snap to original position and show both axis lines

* Add graphite:midpoint attribute to SVG exports

* Add graphite:midpoint parsing to SVG importer
This commit is contained in:
Keavon Chambers
2026-02-23 19:21:51 -08:00
committed by GitHub
parent a1c1039ea1
commit 691d965bcf
21 changed files with 842 additions and 322 deletions
@@ -2,7 +2,7 @@ use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use graphene_std::raster::color::Color;
use graphene_std::vector::style::{FillChoice, GradientStops};
use graphene_std::vector::style::{FillChoice, GradientStop, GradientStops};
use serde_json::Value;
use std::collections::HashMap;
@@ -193,18 +193,17 @@ impl LayoutMessageHandler {
}
// Gradient
let gradient = update_value.get("stops").and_then(|x| x.as_array());
if let Some(stops) = gradient {
let gradient_stops = stops
.iter()
.filter_map(|stop| {
stop.as_object().and_then(|stop| {
let position = stop.get("position").and_then(|x| x.as_f64());
let color = stop.get("color").and_then(|x| x.as_object()).and_then(decode_color);
if let (Some(position), Some(color)) = (position, color) { Some((position, color)) } else { None }
})
})
.collect::<Vec<_>>();
let positions = update_value.get("position").and_then(|x| x.as_array());
let midpoints = update_value.get("midpoint").and_then(|x| x.as_array());
let colors = update_value.get("color").and_then(|x| x.as_array());
if let (Some(positions), Some(midpoints), Some(colors)) = (positions, midpoints, colors) {
let gradient_stops = positions.iter().zip(midpoints.iter()).zip(colors.iter()).filter_map(|((pos, mid), col)| {
let position = pos.as_f64()?;
let midpoint = mid.as_f64()?;
let color = col.as_object().and_then(decode_color)?;
Some(GradientStop { position, midpoint, color })
});
color_button.value = FillChoice::Gradient(GradientStops::new(gradient_stops));
return (color_button.on_update.callback)(color_button);