mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Wrap serialized gradient stops in a new GradientRamp struct and unify FillChoice (#4400)
* Introduce the GradientRamp exchange struct as the serialized TaggedValue::Gradient payload * Unify FillChoice and FillChoiceUI into one enum generic over color format, carrying GradientRamp stops * Rename the TaggedValue::Gradient variant to GradientRamp to match its payload * Move the Color variant into the tagged_value macro list since its stored and wire forms match
This commit is contained in:
@@ -5,7 +5,7 @@ use crate::messages::prelude::*;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::core_types::misc::parse_css_color;
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI, Gradient, GradientStops};
|
||||
use graphene_std::vector::style::{FillChoice, Gradient, GradientRamp, GradientStops};
|
||||
|
||||
/// Bounds for a midpoint position (relative to the interval between two adjacent gradient stops).
|
||||
const MIN_MIDPOINT: f64 = 0.01;
|
||||
@@ -79,11 +79,12 @@ impl MessageHandler<ColorPickerMessage, ()> for ColorPickerMessageHandler {
|
||||
self.active_marker_is_midpoint = false;
|
||||
self.adopt_color(color);
|
||||
}
|
||||
FillChoice::Gradient(stops) => {
|
||||
FillChoice::Gradient(ramp) => {
|
||||
self.active_marker_index = Some(0);
|
||||
self.active_marker_is_midpoint = false;
|
||||
let first_color = stops.color(0).unwrap_or(Color::BLACK);
|
||||
self.gradient = Some(stops);
|
||||
let gradient = Gradient::from(ramp);
|
||||
let first_color = gradient.color(0).unwrap_or(Color::BLACK);
|
||||
self.gradient = Some(gradient);
|
||||
self.adopt_color(first_color);
|
||||
}
|
||||
}
|
||||
@@ -154,7 +155,7 @@ impl MessageHandler<ColorPickerMessage, ()> for ColorPickerMessageHandler {
|
||||
match preset {
|
||||
FillChoice::None => {
|
||||
self.set_new_hsva(0., 0., 0., 1., true);
|
||||
responses.add(FrontendMessage::ColorPickerColorChanged { value: FillChoiceUI::None });
|
||||
responses.add(FrontendMessage::ColorPickerColorChanged { value: FillChoice::<SRGBA8>::None });
|
||||
}
|
||||
FillChoice::Solid(color) => {
|
||||
self.adopt_color(color);
|
||||
@@ -179,7 +180,7 @@ impl MessageHandler<ColorPickerMessage, ()> for ColorPickerMessageHandler {
|
||||
self.set_old_hsva(temp.0, temp.1, temp.2, temp.3, temp.4);
|
||||
|
||||
if self.is_none {
|
||||
responses.add(FrontendMessage::ColorPickerColorChanged { value: FillChoiceUI::None });
|
||||
responses.add(FrontendMessage::ColorPickerColorChanged { value: FillChoice::<SRGBA8>::None });
|
||||
} else {
|
||||
self.emit_color(responses);
|
||||
}
|
||||
@@ -269,15 +270,12 @@ impl ColorPickerMessageHandler {
|
||||
&& (active_index as usize) < gradient.len()
|
||||
{
|
||||
gradient.set_color(active_index as usize, color);
|
||||
let stops = gradient.clone();
|
||||
let fill_choice = FillChoice::Gradient(stops);
|
||||
responses.add(FrontendMessage::ColorPickerColorChanged {
|
||||
value: FillChoiceUI::from(&fill_choice),
|
||||
value: FillChoice::Gradient(GradientRamp::from(&*gradient)),
|
||||
});
|
||||
} else {
|
||||
let fill_choice = FillChoice::Solid(color);
|
||||
responses.add(FrontendMessage::ColorPickerColorChanged {
|
||||
value: FillChoiceUI::from(&fill_choice),
|
||||
value: FillChoice::Solid(SRGBA8::from(color)),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -399,11 +397,10 @@ impl ColorPickerMessageHandler {
|
||||
SpectrumInputUpdate::ActiveMarker { .. } => unreachable!("handled above"),
|
||||
}
|
||||
|
||||
self.gradient = Some(gradient.clone());
|
||||
let fill_choice = FillChoice::Gradient(gradient);
|
||||
responses.add(FrontendMessage::ColorPickerColorChanged {
|
||||
value: FillChoiceUI::from(&fill_choice),
|
||||
value: FillChoice::Gradient(GradientRamp::from(&gradient)),
|
||||
});
|
||||
self.gradient = Some(gradient);
|
||||
self.send_layouts(responses);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::messages::prelude::*;
|
||||
use crate::messages::tool::tool_messages::eyedropper_tool::PrimarySecondary;
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::vector::style::FillChoiceUI;
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
@@ -165,7 +165,7 @@ pub enum FrontendMessage {
|
||||
},
|
||||
/// The Rust color picker handler picked a new color/gradient. The frontend `<ColorPicker />` forwards this as its `colorOrGradient` event.
|
||||
ColorPickerColorChanged {
|
||||
value: FillChoiceUI,
|
||||
value: FillChoice<SRGBA8>,
|
||||
},
|
||||
/// The Rust color picker handler is starting an undo transaction. The frontend `<ColorPicker />` forwards this as its `startHistoryTransaction` event.
|
||||
ColorPickerStartHistoryTransaction,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
|
||||
use crate::messages::prelude::*;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::vector::style::FillChoiceUI;
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -197,11 +197,11 @@ impl LayoutMessageHandler {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (color_button.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let Ok(fill_choice_ui) = serde_json::from_value::<FillChoiceUI>(value) else {
|
||||
warn!("ColorInput update was not able to be parsed as FillChoiceUI: {color_button:?}");
|
||||
let Ok(fill_choice) = serde_json::from_value::<FillChoice<SRGBA8>>(value) else {
|
||||
warn!("ColorInput update was not able to be parsed as FillChoice<SRGBA8>: {color_button:?}");
|
||||
return;
|
||||
};
|
||||
color_button.value = fill_choice_ui;
|
||||
color_button.value = fill_choice;
|
||||
(color_button.on_update.callback)(color_button)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@ use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
|
||||
use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback;
|
||||
use derivative::*;
|
||||
use graphene_std::vector::style::FillChoiceUI;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
use graphite_proc_macros::WidgetBuilder;
|
||||
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
@@ -191,9 +192,9 @@ pub struct ImageButton {
|
||||
pub struct ColorInput {
|
||||
// Content
|
||||
#[widget_builder(constructor)]
|
||||
pub value: FillChoiceUI,
|
||||
pub value: FillChoice<SRGBA8>,
|
||||
/// CSS `linear-gradient(...)` (or solid-color stand-in) for the swatch's `background-image`. Auto-populated from `value` at layout-send time.
|
||||
/// `None` when `value` is `FillChoiceUI::None`, in which case the frontend uses its "none" fallback styling.
|
||||
/// `None` when `value` is `FillChoice::<SRGBA8>::None`, in which case the frontend uses its "none" fallback styling.
|
||||
#[serde(rename = "chosenGradient")]
|
||||
#[widget_builder(skip)]
|
||||
pub chosen_gradient: Option<String>,
|
||||
|
||||
@@ -6,7 +6,7 @@ use derivative::*;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::transform::ReferencePoint;
|
||||
use graphene_std::vector::style::{FillChoiceUI, GradientStops};
|
||||
use graphene_std::vector::style::{FillChoice, GradientStops};
|
||||
use graphite_proc_macros::WidgetBuilder;
|
||||
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
@@ -520,7 +520,7 @@ pub struct ColorPresetsInput {
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ColorPresetsInputUpdate {
|
||||
Preset(FillChoiceUI),
|
||||
Preset(FillChoice<SRGBA8>),
|
||||
EyedropperColorCode(String),
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use graphene_std::transform::{ReferencePoint, ScaleType};
|
||||
use graphene_std::vector::misc::{
|
||||
ArcType, BooleanOperation, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType,
|
||||
};
|
||||
use graphene_std::vector::style::{DashPattern, FillChoice, FillChoiceUI, GradientSpreadMethod, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
use graphene_std::vector::style::{DashPattern, FillChoice, GradientRamp, GradientSpreadMethod, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
use graphene_std::vector::{QRCodeErrorCorrectionLevel, Vector};
|
||||
use graphene_std::{Artboard, Color, Context, Graphic};
|
||||
use std::any::Any;
|
||||
@@ -720,7 +720,7 @@ impl TableItemLayout for Color {
|
||||
}
|
||||
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||
vec![
|
||||
ColorInput::new(FillChoiceUI::from(&FillChoice::Solid(*self)))
|
||||
ColorInput::new(FillChoice::<SRGBA8>::from(&FillChoice::Solid(*self)))
|
||||
.disabled(true)
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.narrow(true)
|
||||
@@ -751,7 +751,7 @@ impl TableItemLayout for Gradient {
|
||||
.narrow(true)
|
||||
.widget_instance(),
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
ColorInput::new(FillChoiceUI::from(&FillChoice::Gradient(self.clone())))
|
||||
ColorInput::new(FillChoice::<SRGBA8>::Gradient(GradientRamp::from(self)))
|
||||
.menu_direction(Some(MenuDirection::Top))
|
||||
.disabled(true)
|
||||
.narrow(true)
|
||||
|
||||
@@ -22,6 +22,7 @@ pub enum GraphOperationMessage {
|
||||
},
|
||||
FillGradientSet {
|
||||
layer: LayerNodeIdentifier,
|
||||
#[serde(skip)]
|
||||
gradient: Gradient,
|
||||
gradient_type: GradientType,
|
||||
spread_method: GradientSpreadMethod,
|
||||
@@ -33,6 +34,7 @@ pub enum GraphOperationMessage {
|
||||
},
|
||||
GradientStopsSet {
|
||||
layer: LayerNodeIdentifier,
|
||||
#[serde(skip)]
|
||||
stops: Gradient,
|
||||
},
|
||||
GradientPositionsSet {
|
||||
|
||||
@@ -17,7 +17,7 @@ use graphene_std::raster_types::Image;
|
||||
use graphene_std::subpath::Subpath;
|
||||
use graphene_std::text::{Font, TypesettingConfig};
|
||||
use graphene_std::vector::style::{GradientSpreadMethod, GradientType, Stroke};
|
||||
use graphene_std::vector::{Gradient, PointId, Vector, VectorModification, VectorModificationType};
|
||||
use graphene_std::vector::{Gradient, GradientRamp, PointId, Vector, VectorModification, VectorModificationType};
|
||||
use graphene_std::{Artboard, Color, Graphic};
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
||||
@@ -409,12 +409,13 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
};
|
||||
let backup_input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::BackupGradientInput);
|
||||
|
||||
self.set_input_with_refresh(backup_input_connector, NodeInput::value(TaggedValue::Gradient(gradient.clone()), false), true);
|
||||
let ramp = GradientRamp::from(gradient);
|
||||
self.set_input_with_refresh(backup_input_connector, NodeInput::value(TaggedValue::GradientRamp(ramp.clone()), false), true);
|
||||
|
||||
// Skip the rerender on all but the last input so the whole update triggers a single graph run
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::FillInput),
|
||||
NodeInput::value(TaggedValue::Gradient(gradient), false),
|
||||
NodeInput::value(TaggedValue::GradientRamp(ramp), false),
|
||||
true,
|
||||
);
|
||||
|
||||
@@ -548,7 +549,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
};
|
||||
|
||||
let input_connector = InputConnector::node(gradient_value_id, graphene_std::math_nodes::gradient_value::GradientInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Gradient(stops), false), false);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::GradientRamp(GradientRamp::from(stops)), false), false);
|
||||
}
|
||||
|
||||
/// Update the last 'Gradient Positions' node in the chain when one exists, so on-canvas stop drags stay live even
|
||||
|
||||
@@ -32,7 +32,9 @@ use graphene_std::vector::misc::BooleanOperation;
|
||||
use graphene_std::vector::misc::{
|
||||
ArcType, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType,
|
||||
};
|
||||
use graphene_std::vector::style::{FillChoiceUI, Gradient, GradientSpreadMethod, GradientStops, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation};
|
||||
use graphene_std::vector::style::{
|
||||
FillChoice, Gradient, GradientRamp, GradientSpreadMethod, GradientStops, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation,
|
||||
};
|
||||
use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification};
|
||||
use graphene_std::{NodeParameter, ParameterRef};
|
||||
|
||||
@@ -1155,9 +1157,9 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
|
||||
// Add the color input
|
||||
let widget_value = match &**tagged_value {
|
||||
TaggedValue::Color(color) => FillChoiceUI::Solid(SRGBA8::from(*color)),
|
||||
TaggedValue::Gradient(stops) => FillChoiceUI::Gradient(GradientStops::from(stops)),
|
||||
value if value.is_no_paint() => FillChoiceUI::None,
|
||||
TaggedValue::Color(color) => FillChoice::<SRGBA8>::Solid(SRGBA8::from(*color)),
|
||||
TaggedValue::GradientRamp(ramp) => FillChoice::<SRGBA8>::Gradient(GradientRamp::from(ramp)),
|
||||
value if value.is_no_paint() => FillChoice::<SRGBA8>::None,
|
||||
x => {
|
||||
warn!("Color {x:?}");
|
||||
return LayoutGroup::row(widgets);
|
||||
@@ -1168,12 +1170,12 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
// while a plain color or gradient input always keeps its own value type
|
||||
let on_update: fn(&ColorInput) -> TaggedValue = if color_button.allow_none {
|
||||
|input| match &input.value {
|
||||
FillChoiceUI::None => TaggedValue::no_paint(),
|
||||
FillChoiceUI::Solid(srgba) => TaggedValue::Color(Color::from(*srgba)),
|
||||
FillChoiceUI::Gradient(gradient_ui) => TaggedValue::Gradient(Gradient::from(gradient_ui)),
|
||||
FillChoice::<SRGBA8>::None => TaggedValue::no_paint(),
|
||||
FillChoice::<SRGBA8>::Solid(srgba) => TaggedValue::Color(Color::from(*srgba)),
|
||||
FillChoice::<SRGBA8>::Gradient(ramp) => TaggedValue::GradientRamp(GradientRamp::from(ramp)),
|
||||
}
|
||||
} else if matches!(&**tagged_value, TaggedValue::Gradient(_)) {
|
||||
|input| TaggedValue::Gradient(input.value.as_gradient().map(Gradient::from).unwrap_or_else(Gradient::black_to_white))
|
||||
} else if matches!(&**tagged_value, TaggedValue::GradientRamp(_)) {
|
||||
|input| TaggedValue::GradientRamp(input.value.as_gradient().map(GradientRamp::from).unwrap_or_else(GradientRamp::black_to_white))
|
||||
} else {
|
||||
|input| TaggedValue::Color(input.value.as_solid().map(Color::from).unwrap_or(Color::TRANSPARENT))
|
||||
};
|
||||
@@ -2422,7 +2424,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
Ok(document_node) => match document_node.input_value(FillInput) {
|
||||
Some(TaggedValue::Color(color)) => ResolvedFill::Solid(Some(*color)),
|
||||
Some(value) if value.is_no_paint() => ResolvedFill::Solid(None),
|
||||
Some(TaggedValue::Gradient(_)) => {
|
||||
Some(TaggedValue::GradientRamp(_)) => {
|
||||
match graph_modification_utils::read_fill_node_gradient(document_node, || {
|
||||
layer.map_or([DVec2::ZERO, DVec2::ONE], |layer| context.network_interface.document_metadata().nonzero_bounding_box(layer))
|
||||
}) {
|
||||
@@ -2448,12 +2450,12 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
_ => None,
|
||||
};
|
||||
let backup_stops = match document_node.input_value(BackupGradientInput) {
|
||||
Some(TaggedValue::Gradient(stops)) => stops.clone(),
|
||||
_ => Gradient::black_to_white(),
|
||||
Some(TaggedValue::GradientRamp(ramp)) => ramp.clone(),
|
||||
_ => GradientRamp::black_to_white(),
|
||||
};
|
||||
(backup_color, backup_stops)
|
||||
}
|
||||
Err(_) => (None, Gradient::black_to_white()),
|
||||
Err(_) => (None, GradientRamp::black_to_white()),
|
||||
};
|
||||
|
||||
match &fill {
|
||||
@@ -2463,7 +2465,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
let reverse_button = IconButton::new("Reverse", 24)
|
||||
.tooltip_label("Reverse Stops")
|
||||
.tooltip_description("Reverse the gradient color stops.")
|
||||
.on_update(update_value(move |_| TaggedValue::Gradient(stops.reversed()), node_id, FillInput))
|
||||
.on_update(update_value(move |_| TaggedValue::GradientRamp(GradientRamp::from(stops.reversed())), node_id, FillInput))
|
||||
.widget_instance();
|
||||
widgets_first_row.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||
widgets_first_row.push(reverse_button);
|
||||
@@ -2471,16 +2473,16 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
_ => add_blank_assist(&mut widgets_first_row),
|
||||
}
|
||||
|
||||
let fill_choice_ui = match &fill {
|
||||
let widget_value = match &fill {
|
||||
ResolvedFill::Solid(color) => {
|
||||
if let Some(color) = color {
|
||||
FillChoiceUI::Solid(SRGBA8::from(*color))
|
||||
FillChoice::<SRGBA8>::Solid(SRGBA8::from(*color))
|
||||
} else {
|
||||
FillChoiceUI::None
|
||||
FillChoice::<SRGBA8>::None
|
||||
}
|
||||
}
|
||||
ResolvedFill::Gradient { gradient: stops, .. } => FillChoiceUI::Gradient(GradientStops::from(stops)),
|
||||
ResolvedFill::Other => FillChoiceUI::None,
|
||||
ResolvedFill::Gradient { gradient: stops, .. } => FillChoice::<SRGBA8>::Gradient(GradientRamp::from(stops)),
|
||||
ResolvedFill::Other => FillChoice::<SRGBA8>::None,
|
||||
};
|
||||
|
||||
let solid_set_messages = move |color: Option<Color>| {
|
||||
@@ -2505,18 +2507,18 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
Message::Batched { messages: messages.into() }
|
||||
};
|
||||
|
||||
let gradient_set_messages = move |gradient: Gradient| Message::Batched {
|
||||
let gradient_set_messages = move |ramp: GradientRamp| Message::Batched {
|
||||
messages: Box::new([
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: FillInput::INDEX,
|
||||
value: TaggedValue::Gradient(gradient.clone()).into(),
|
||||
value: TaggedValue::GradientRamp(ramp.clone()).into(),
|
||||
}
|
||||
.into(),
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: BackupGradientInput::INDEX,
|
||||
value: TaggedValue::Gradient(gradient).into(),
|
||||
value: TaggedValue::GradientRamp(ramp).into(),
|
||||
}
|
||||
.into(),
|
||||
]),
|
||||
@@ -2525,17 +2527,14 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
widgets_first_row.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||
widgets_first_row.push(
|
||||
ColorInput::default()
|
||||
.value(fill_choice_ui)
|
||||
.value(widget_value)
|
||||
.on_update(move |x: &ColorInput| match &x.value {
|
||||
FillChoiceUI::None => solid_set_messages(None),
|
||||
FillChoiceUI::Solid(srgba8) => {
|
||||
FillChoice::<SRGBA8>::None => solid_set_messages(None),
|
||||
FillChoice::<SRGBA8>::Solid(srgba8) => {
|
||||
let color = Some(Color::from(*srgba8));
|
||||
solid_set_messages(color)
|
||||
}
|
||||
FillChoiceUI::Gradient(gradient_stops_ui) => {
|
||||
let gradient = Gradient::from(gradient_stops_ui);
|
||||
gradient_set_messages(gradient)
|
||||
}
|
||||
FillChoice::<SRGBA8>::Gradient(ramp) => gradient_set_messages(GradientRamp::from(ramp)),
|
||||
})
|
||||
.on_commit(commit_value)
|
||||
.widget_instance(),
|
||||
@@ -2554,7 +2553,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
.on_commit(commit_value),
|
||||
RadioEntryData::new("gradient")
|
||||
.label("Gradient")
|
||||
.on_update(update_value(move |_| TaggedValue::Gradient(backup_gradient.clone()), node_id, FillInput))
|
||||
.on_update(update_value(move |_| TaggedValue::GradientRamp(backup_gradient.clone()), node_id, FillInput))
|
||||
.on_commit(commit_value),
|
||||
];
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::messages::prelude::*;
|
||||
use glam::DVec2;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::renderer::Quad;
|
||||
use graphene_std::vector::style::FillChoiceUI;
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
|
||||
fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
@@ -274,7 +274,7 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
||||
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||
]);
|
||||
color_widgets.push(
|
||||
ColorInput::new(FillChoiceUI::Solid(SRGBA8::from_hex_str(&grid.color).unwrap_or(SRGBA8::BLACK)))
|
||||
ColorInput::new(FillChoice::<SRGBA8>::Solid(SRGBA8::from_hex_str(&grid.color).unwrap_or(SRGBA8::BLACK)))
|
||||
.tooltip_label("Grid Display Color")
|
||||
.allow_none(false)
|
||||
.on_update(update_val::<ColorInput, _>(grid, |grid, color| {
|
||||
|
||||
@@ -801,12 +801,13 @@ async fn legacy_four_input_fill_migrates_to_the_split_transform_shape() {
|
||||
"the transform input should hold a matrix, but became {transform:?}"
|
||||
);
|
||||
|
||||
// The Sample Gradient parameter held the tuple-form stops, which parse as the stops value with even positions elided
|
||||
// The Sample Gradient parameter held the tuple-form stops, which parse as the ramp value with even positions elided
|
||||
let sample_gradient_node = &network.nodes[&graph_craft::document::NodeId(2)];
|
||||
let stops = sample_gradient_node.input_value(graphene_std::math_nodes::sample_gradient::GradientInput);
|
||||
let Some(TaggedValue::Gradient(stops)) = stops else {
|
||||
panic!("the legacy stops parameter should become a gradient stops value, but became {stops:?}");
|
||||
let Some(TaggedValue::GradientRamp(ramp)) = stops else {
|
||||
panic!("the legacy stops parameter should become a gradient ramp value, but became {stops:?}");
|
||||
};
|
||||
let stops = graphene_std::vector::Gradient::from(ramp);
|
||||
assert_eq!(stops.len(), 2);
|
||||
assert!(!stops.has_position_attribute(), "even legacy tuple positions should elide rather than materialize");
|
||||
}
|
||||
|
||||
@@ -1626,7 +1626,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
let fill_value = match old_fill {
|
||||
graphic_types::migrations::legacy::LegacyFill::None => TaggedValue::no_paint(),
|
||||
graphic_types::migrations::legacy::LegacyFill::Solid(color) => TaggedValue::Color(*color),
|
||||
graphic_types::migrations::legacy::LegacyFill::Gradient(gradient) => TaggedValue::Gradient(gradient.stops.clone()),
|
||||
graphic_types::migrations::legacy::LegacyFill::Gradient(gradient) => TaggedValue::GradientRamp(gradient.stops.clone()),
|
||||
};
|
||||
document
|
||||
.network_interface
|
||||
@@ -1674,7 +1674,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
if let Some(TaggedValue::LegacyGradient(g)) = old_inputs[3].as_value() {
|
||||
document.network_interface.set_input(
|
||||
&InputConnector::node_at_index(*node_id, 3),
|
||||
NodeInput::value(TaggedValue::Gradient(g.stops.clone()), false),
|
||||
NodeInput::value(TaggedValue::GradientRamp(g.stops.clone()), false),
|
||||
network_path,
|
||||
);
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::utility_types::DocumentToolData;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::vector::style::{FillChoice, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
|
||||
/// Color selector widgets seen in [`LayoutTarget::ToolOptions`] bar.
|
||||
pub struct ToolColorOptions {
|
||||
@@ -85,8 +86,8 @@ impl ToolColorOptions {
|
||||
// In the mixed state (`fill_choice` is `None`) the dash overlay covers the swatch, so the underlying widget value just drives the picker's initial position.
|
||||
// `FillChoice::None` gives it a neutral starting point.
|
||||
let mixed_color = self.fill_choice.is_none();
|
||||
// Convert the internal linear-light `FillChoice` to the JS-boundary `FillChoiceUI` (with `SRGBA8` colors) for the widget value.
|
||||
let widget_value = FillChoiceUI::from(self.fill_choice.as_ref().unwrap_or(&FillChoice::None));
|
||||
// Convert the internal linear-light `FillChoice` to the JS-boundary `FillChoice<SRGBA8>` (with `SRGBA8` colors) for the widget value.
|
||||
let widget_value = FillChoice::<SRGBA8>::from(self.fill_choice.as_ref().unwrap_or(&FillChoice::None));
|
||||
let mixed_enabled = self.enabled.is_none();
|
||||
// In the mixed-enabled state the underlying `checked` value is hidden behind the indeterminate dash.
|
||||
// The frontend's click handler sends `true` when the user resolves the mixed state by clicking.
|
||||
|
||||
@@ -316,14 +316,14 @@ pub fn get_gradient_stops(layer: LayerNodeIdentifier, network_interface: &NodeNe
|
||||
.get(&fill_node_id)
|
||||
.and_then(|node| node.input(graphene_std::vector::fill::FillInput))
|
||||
.and_then(|input| input.as_value())
|
||||
.and_then(|value| if let TaggedValue::Gradient(gradient) = value { Some(gradient.clone()) } else { None });
|
||||
.and_then(|value| if let TaggedValue::GradientRamp(ramp) = value { Some(Gradient::from(ramp)) } else { None });
|
||||
}
|
||||
|
||||
let gradient_value_node = network_interface.document_network().nodes.get(&get_upstream_gradient_value_node_id(layer, network_interface)?)?;
|
||||
let TaggedValue::Gradient(stops) = gradient_value_node.input(graphene_std::math_nodes::gradient_value::GradientInput)?.as_value()? else {
|
||||
let TaggedValue::GradientRamp(ramp) = gradient_value_node.input(graphene_std::math_nodes::gradient_value::GradientInput)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
let mut stops = stops.clone();
|
||||
let mut stops = Gradient::from(ramp);
|
||||
|
||||
// The chain's stop placement comes from the closest-to-layer 'Gradient Positions'/'Gradient Midpoints' nodes,
|
||||
// matching the runtime where each later node overwrites the whole attribute
|
||||
@@ -662,10 +662,10 @@ pub struct FillNodeGradient {
|
||||
pub fn read_fill_node_gradient(fill_node: &DocumentNode, bounding_box: impl FnOnce() -> [DVec2; 2]) -> Option<FillNodeGradient> {
|
||||
use graphene_std::vector::fill;
|
||||
|
||||
let TaggedValue::Gradient(stops) = fill_node.input(fill::FillInput)?.as_value()? else {
|
||||
let TaggedValue::GradientRamp(ramp) = fill_node.input(fill::FillInput)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
let stops = stops.clone();
|
||||
let stops = Gradient::from(ramp);
|
||||
let gradient_type = match fill_node.input(fill::GradientTypeInput).and_then(|input| input.as_value()) {
|
||||
Some(&TaggedValue::GradientType(value)) => value,
|
||||
_ => GradientType::default(),
|
||||
@@ -731,7 +731,7 @@ pub fn selected_fill_state(document: &DocumentMessageHandler) -> Option<Selected
|
||||
|
||||
match fill_node.input(graphene_std::vector::fill::FillInput)?.as_value()? {
|
||||
TaggedValue::Color(color) => Some(FillChoice::Solid(*color)),
|
||||
TaggedValue::Gradient(stops) => Some(FillChoice::Gradient(stops.clone())),
|
||||
TaggedValue::GradientRamp(ramp) => Some(FillChoice::Gradient(ramp.clone())),
|
||||
value if value.is_no_paint() => Some(FillChoice::None),
|
||||
_ => None,
|
||||
}
|
||||
@@ -815,7 +815,7 @@ pub fn set_fill_for_selected_layers(fill_choice: FillChoice, document: &Document
|
||||
match &fill_choice {
|
||||
FillChoice::None => responses.add(GraphOperationMessage::FillColorSet { layer, color: None }),
|
||||
FillChoice::Solid(color) => responses.add(GraphOperationMessage::FillColorSet { layer, color: Some(*color) }),
|
||||
FillChoice::Gradient(stops) => {
|
||||
FillChoice::Gradient(ramp) => {
|
||||
use graphene_std::vector::fill;
|
||||
let fill_parameters = NodeGraphLayer::new(layer, &document.network_interface).find_node_parameters(fill::IDENTIFIER);
|
||||
|
||||
@@ -836,7 +836,7 @@ pub fn set_fill_for_selected_layers(fill_choice: FillChoice, document: &Document
|
||||
|
||||
responses.add(GraphOperationMessage::FillGradientSet {
|
||||
layer,
|
||||
gradient: stops.clone(),
|
||||
gradient: Gradient::from(ramp),
|
||||
gradient_type,
|
||||
spread_method,
|
||||
transform,
|
||||
|
||||
@@ -9,8 +9,9 @@ use graph_craft::document::NodeId;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::brush::brush_stroke::{BrushInputSample, BrushStroke, BrushStyle};
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::raster::BlendMode;
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI};
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
|
||||
const BRUSH_MAX_SIZE: f64 = 5000.;
|
||||
|
||||
@@ -104,7 +105,7 @@ impl ToolMetadata for BrushTool {
|
||||
impl LayoutHolder for BrushTool {
|
||||
fn layout(&self) -> Layout {
|
||||
let mut widgets = vec![
|
||||
ColorInput::new(FillChoiceUI::from(self.options.color.fill_choice.as_ref().unwrap_or(&FillChoice::None)))
|
||||
ColorInput::new(FillChoice::<SRGBA8>::from(self.options.color.fill_choice.as_ref().unwrap_or(&FillChoice::None)))
|
||||
.mixed(self.options.color.fill_choice.is_none())
|
||||
.narrow(true)
|
||||
.on_update(|color: &ColorInput| {
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::messages::tool::common_functionality::color_selector::solid;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::raster::color::Color;
|
||||
use graphene_std::vector::style::FillChoiceUI;
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct FillTool {
|
||||
@@ -44,7 +44,7 @@ impl ToolMetadata for FillTool {
|
||||
impl LayoutHolder for FillTool {
|
||||
fn layout(&self) -> Layout {
|
||||
let widgets = vec![
|
||||
ColorInput::new(FillChoiceUI::from(&solid(self.primary_color)))
|
||||
ColorInput::new(FillChoice::<SRGBA8>::from(&solid(self.primary_color)))
|
||||
.narrow(true)
|
||||
.on_update(|color: &ColorInput| {
|
||||
FillToolMessage::SetColor {
|
||||
|
||||
@@ -16,7 +16,7 @@ use glam::DMat2;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::raster::color::Color;
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI, Gradient, GradientSpreadMethod, GradientStop, GradientStops, GradientType, build_transform_with_y_preservation};
|
||||
use graphene_std::vector::style::{FillChoice, Gradient, GradientRamp, GradientSpreadMethod, GradientStop, GradientStops, GradientType, build_transform_with_y_preservation};
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct GradientTool {
|
||||
@@ -257,33 +257,27 @@ impl LayoutHolder for GradientTool {
|
||||
.widget_instance();
|
||||
|
||||
// Display priority: the selected layer's stops, then any user-customized tool default, then the working colors
|
||||
let stops_value = self
|
||||
.data
|
||||
.current_gradient_stops
|
||||
.clone()
|
||||
.or_else(|| self.data.default_gradient_stops.clone())
|
||||
.map(FillChoice::Gradient)
|
||||
.unwrap_or_else(|| {
|
||||
FillChoice::Gradient(Gradient::new([
|
||||
GradientStop {
|
||||
position: 0.,
|
||||
midpoint: 0.5,
|
||||
color: self.data.primary_color,
|
||||
},
|
||||
GradientStop {
|
||||
position: 1.,
|
||||
midpoint: 0.5,
|
||||
color: self.data.secondary_color,
|
||||
},
|
||||
]))
|
||||
});
|
||||
let stops_widget = ColorInput::new(FillChoiceUI::from(&stops_value))
|
||||
let stops_value = self.data.current_gradient_stops.clone().or_else(|| self.data.default_gradient_stops.clone()).unwrap_or_else(|| {
|
||||
Gradient::new([
|
||||
GradientStop {
|
||||
position: 0.,
|
||||
midpoint: 0.5,
|
||||
color: self.data.primary_color,
|
||||
},
|
||||
GradientStop {
|
||||
position: 1.,
|
||||
midpoint: 0.5,
|
||||
color: self.data.secondary_color,
|
||||
},
|
||||
])
|
||||
});
|
||||
let stops_widget = ColorInput::new(FillChoice::Gradient(GradientRamp::from(&stops_value)))
|
||||
.allow_none(false)
|
||||
.narrow(true)
|
||||
.tooltip_label("Gradient Stops")
|
||||
.tooltip_description("Edit the gradient's color stops.")
|
||||
.on_update(|input: &ColorInput| {
|
||||
let stops = input.value.as_gradient().cloned().unwrap_or_default();
|
||||
let stops = input.value.as_gradient().map(|ramp| ramp.stops.clone()).unwrap_or_default();
|
||||
GradientToolMessage::UpdateStops { stops }.into()
|
||||
})
|
||||
.on_commit(|_| DocumentMessage::AddTransaction.into())
|
||||
@@ -2024,7 +2018,7 @@ mod test_gradient {
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::vector::style::{GradientSpreadMethod, build_transform_with_y_preservation};
|
||||
use graphene_std::vector::{Gradient, GradientStop, fill};
|
||||
use graphene_std::vector::{Gradient, GradientRamp, GradientStop, fill};
|
||||
|
||||
use super::gradient_space_transform;
|
||||
|
||||
@@ -2067,7 +2061,7 @@ mod test_gradient {
|
||||
let fill_node = document.network_interface.document_network().nodes.get(&fill_node_id)?;
|
||||
|
||||
let stops = match fill_node.input(fill::FillInput)?.as_value()? {
|
||||
TaggedValue::Gradient(stops) => stops.clone(),
|
||||
TaggedValue::GradientRamp(ramp) => Gradient::from(ramp),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@@ -2156,7 +2150,7 @@ mod test_gradient {
|
||||
.handle_message(NodeGraphMessage::SetInputValue {
|
||||
node_id: gradient_node_id,
|
||||
input_index: 1,
|
||||
value: TaggedValue::Gradient(Gradient::new([
|
||||
value: TaggedValue::GradientRamp(GradientRamp::from(Gradient::new([
|
||||
GradientStop {
|
||||
position: 0.,
|
||||
midpoint: 0.5,
|
||||
@@ -2167,7 +2161,7 @@ mod test_gradient {
|
||||
midpoint: 0.5,
|
||||
color: Color::BLUE,
|
||||
},
|
||||
]))
|
||||
])))
|
||||
.into(),
|
||||
})
|
||||
.await;
|
||||
@@ -2191,10 +2185,10 @@ mod test_gradient {
|
||||
.and_then(|node| node.input(graphene_std::math_nodes::gradient_value::GradientInput))
|
||||
.and_then(|input| input.as_value())
|
||||
.cloned();
|
||||
let Some(TaggedValue::Gradient(stops)) = stops else {
|
||||
let Some(TaggedValue::GradientRamp(ramp)) = stops else {
|
||||
panic!("expected a gradient default, got {stops:?}")
|
||||
};
|
||||
assert_eq!(stops.positions(), vec![0., 1.], "the parameter default should be the black-to-white starting gradient");
|
||||
assert_eq!(Gradient::from(ramp).positions(), vec![0., 1.], "the parameter default should be the black-to-white starting gradient");
|
||||
}
|
||||
|
||||
async fn create_fill_gradient_chain_layer(editor: &mut EditorTestUtils) -> LayerNodeIdentifier {
|
||||
@@ -2216,7 +2210,7 @@ mod test_gradient {
|
||||
.handle_message(NodeGraphMessage::SetInputValue {
|
||||
node_id: gradient_node_id,
|
||||
input_index: 1,
|
||||
value: TaggedValue::Gradient(Gradient::new([
|
||||
value: TaggedValue::GradientRamp(GradientRamp::from(Gradient::new([
|
||||
GradientStop {
|
||||
position: 0.,
|
||||
midpoint: 0.5,
|
||||
@@ -2227,7 +2221,7 @@ mod test_gradient {
|
||||
midpoint: 0.5,
|
||||
color: Color::BLUE,
|
||||
},
|
||||
]))
|
||||
])))
|
||||
.into(),
|
||||
})
|
||||
.await;
|
||||
@@ -2860,7 +2854,7 @@ mod test_gradient {
|
||||
.handle_message(NodeGraphMessage::SetInputValue {
|
||||
node_id: gradient_value_id,
|
||||
input_index: 1,
|
||||
value: TaggedValue::Gradient(Gradient::new([
|
||||
value: TaggedValue::GradientRamp(GradientRamp::from(Gradient::new([
|
||||
GradientStop {
|
||||
position: 0.,
|
||||
midpoint: 0.5,
|
||||
@@ -2871,7 +2865,7 @@ mod test_gradient {
|
||||
midpoint: 0.5,
|
||||
color: Color::BLUE,
|
||||
},
|
||||
]))
|
||||
])))
|
||||
.into(),
|
||||
})
|
||||
.await;
|
||||
|
||||
@@ -24,7 +24,7 @@ use graphene_std::choice_type::ChoiceTypeStatic;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::renderer::Quad;
|
||||
use graphene_std::text::{Font, TextAlign, TypesettingConfig, lines_clipping};
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI};
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
use graphene_std::{Color, NodeParameter};
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
@@ -261,7 +261,7 @@ impl TextTool {
|
||||
|
||||
fn layout(&self, font_catalog: &FontCatalog, document: &DocumentMessageHandler) -> Layout {
|
||||
let mut widgets = vec![
|
||||
ColorInput::new(FillChoiceUI::from(self.options.fill.fill_choice.as_ref().unwrap_or(&FillChoice::None)))
|
||||
ColorInput::new(FillChoice::<SRGBA8>::from(self.options.fill.fill_choice.as_ref().unwrap_or(&FillChoice::None)))
|
||||
.mixed(self.options.fill.fill_choice.is_none())
|
||||
.narrow(true)
|
||||
.on_update(|color: &ColorInput| {
|
||||
|
||||
Reference in New Issue
Block a user