mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 18:48:11 +08:00
Remove custom (de)serializers from the DashPattern and BoxCorners TaggedValue variants (#4394)
Replace the DashPattern and BoxCorners custom serde impls with storage-shaped Vec<f64> TaggedValue payloads
This commit is contained in:
@@ -674,7 +674,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintOrderInput);
|
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintOrderInput);
|
||||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::PaintOrder(stroke.paint_order), false), false);
|
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::PaintOrder(stroke.paint_order), false), false);
|
||||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashPatternInput);
|
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashPatternInput);
|
||||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::DashPattern(stroke.dash_lengths.into()), false), true);
|
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::DashPattern(stroke.dash_lengths), false), true);
|
||||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashOffsetInput);
|
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashOffsetInput);
|
||||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.dash_offset), false), true);
|
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.dash_offset), false), true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,7 @@ use graphene_std::vector::misc::BooleanOperation;
|
|||||||
use graphene_std::vector::misc::{
|
use graphene_std::vector::misc::{
|
||||||
ArcType, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType,
|
ArcType, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType,
|
||||||
};
|
};
|
||||||
use graphene_std::vector::style::{
|
use graphene_std::vector::style::{FillChoiceUI, Gradient, GradientSpreadMethod, GradientType, GradientUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation};
|
||||||
DashPattern, FillChoiceUI, Gradient, GradientSpreadMethod, GradientType, GradientUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation,
|
|
||||||
};
|
|
||||||
use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification};
|
use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification};
|
||||||
use graphene_std::{NodeParameter, ParameterRef};
|
use graphene_std::{NodeParameter, ParameterRef};
|
||||||
|
|
||||||
@@ -843,12 +841,12 @@ pub fn dash_pattern_widget(parameter_widgets_info: ParameterWidgetsInfo, text_in
|
|||||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
return vec![];
|
return vec![];
|
||||||
};
|
};
|
||||||
if let Some(TaggedValue::DashPattern(pattern)) = &input.as_non_exposed_value() {
|
if let Some(TaggedValue::DashPattern(lengths)) = &input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
|
||||||
text_input
|
text_input
|
||||||
.value(pattern.0.iter_element_values().map(|length| length.to_string()).collect::<Vec<_>>().join(", "))
|
.value(lengths.iter().map(|length| length.to_string()).collect::<Vec<_>>().join(", "))
|
||||||
.on_update(parameter_widgets_info.optionally_update_value(move |input: &TextInput| Some(TaggedValue::DashPattern(DashPattern::from(input.value.as_str())))))
|
.on_update(parameter_widgets_info.optionally_update_value(move |input: &TextInput| Some(TaggedValue::DashPattern(graphene_std::core_types::misc::parse_f64_list(&input.value)))))
|
||||||
.widget_instance(),
|
.widget_instance(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -2177,7 +2175,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
return vec![];
|
return vec![];
|
||||||
};
|
};
|
||||||
let corner_values = match input.as_non_exposed_value() {
|
let corner_values = match input.as_non_exposed_value() {
|
||||||
Some(TaggedValue::BoxCorners(corners)) => corners.to_corner_values(),
|
Some(TaggedValue::BoxCorners(values)) => BoxCorners::from(values.clone()).to_corner_values(),
|
||||||
_ => [0.; 4],
|
_ => [0.; 4],
|
||||||
};
|
};
|
||||||
let uniform_val = corner_values[0];
|
let uniform_val = corner_values[0];
|
||||||
@@ -2196,7 +2194,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
NodeGraphMessage::SetInputValue {
|
NodeGraphMessage::SetInputValue {
|
||||||
node_id,
|
node_id,
|
||||||
input_index: CornerRadiusInput::INDEX,
|
input_index: CornerRadiusInput::INDEX,
|
||||||
value: TaggedValue::BoxCorners(BoxCorners::from(uniform_val)).into(),
|
value: TaggedValue::BoxCorners(vec![uniform_val]).into(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
]),
|
]),
|
||||||
@@ -2215,7 +2213,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
NodeGraphMessage::SetInputValue {
|
NodeGraphMessage::SetInputValue {
|
||||||
node_id,
|
node_id,
|
||||||
input_index: CornerRadiusInput::INDEX,
|
input_index: CornerRadiusInput::INDEX,
|
||||||
value: TaggedValue::BoxCorners(BoxCorners::from(corner_values.to_vec())).into(),
|
value: TaggedValue::BoxCorners(corner_values.to_vec()).into(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
]),
|
]),
|
||||||
@@ -2229,7 +2227,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
TextInput::default()
|
TextInput::default()
|
||||||
.value(corner_values.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", "))
|
.value(corner_values.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", "))
|
||||||
.on_update(optionally_update_value(
|
.on_update(optionally_update_value(
|
||||||
move |x: &TextInput| Some(TaggedValue::BoxCorners(BoxCorners::from(x.value.as_str()))),
|
move |x: &TextInput| Some(TaggedValue::BoxCorners(graphene_std::core_types::misc::parse_f64_list(&x.value))),
|
||||||
node_id,
|
node_id,
|
||||||
CornerRadiusInput,
|
CornerRadiusInput,
|
||||||
))
|
))
|
||||||
@@ -2238,11 +2236,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
NumberInput::default()
|
NumberInput::default()
|
||||||
.value(Some(uniform_val))
|
.value(Some(uniform_val))
|
||||||
.unit(" px")
|
.unit(" px")
|
||||||
.on_update(update_value(
|
.on_update(update_value(move |x: &NumberInput| TaggedValue::BoxCorners(vec![x.value.unwrap()]), node_id, CornerRadiusInput))
|
||||||
move |x: &NumberInput| TaggedValue::BoxCorners(BoxCorners::from(x.value.unwrap())),
|
|
||||||
node_id,
|
|
||||||
CornerRadiusInput,
|
|
||||||
))
|
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_instance()
|
.widget_instance()
|
||||||
};
|
};
|
||||||
@@ -2705,7 +2699,7 @@ pub fn stroke_properties(node_id: NodeId, context: &mut NodePropertiesContext) -
|
|||||||
};
|
};
|
||||||
|
|
||||||
let has_dash_lengths = match &document_node.input_value(DashPatternInput) {
|
let has_dash_lengths = match &document_node.input_value(DashPatternInput) {
|
||||||
Some(TaggedValue::DashPattern(pattern)) => pattern.0.is_empty(),
|
Some(TaggedValue::DashPattern(lengths)) => lengths.is_empty(),
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
let miter_limit_disabled = join_value != &StrokeJoin::Miter;
|
let miter_limit_disabled = join_value != &StrokeJoin::Miter;
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ use graphene_std::text::{TextAlign, TypesettingConfig};
|
|||||||
use graphene_std::transform::ScaleType;
|
use graphene_std::transform::ScaleType;
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::graphic_types;
|
use graphene_std::vector::graphic_types;
|
||||||
use graphene_std::vector::misc::BoxCorners;
|
use graphene_std::vector::style::{PaintOrder, StrokeAlign};
|
||||||
use graphene_std::vector::style::{DashPattern, PaintOrder, StrokeAlign};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
@@ -1387,24 +1386,24 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
|
|||||||
/// Converts a legacy stroke dash input (a `List<f64>`, single `f64`, or comma/space separated `String`) to the `DashPattern` value type.
|
/// Converts a legacy stroke dash input (a `List<f64>`, single `f64`, or comma/space separated `String`) to the `DashPattern` value type.
|
||||||
fn migrate_dash_input(input: &NodeInput) -> Option<NodeInput> {
|
fn migrate_dash_input(input: &NodeInput) -> Option<NodeInput> {
|
||||||
let NodeInput::Value { tagged_value, exposed } = input else { return None };
|
let NodeInput::Value { tagged_value, exposed } = input else { return None };
|
||||||
let pattern = match &*tagged_value.clone().into_inner() {
|
let lengths = match &*tagged_value.clone().into_inner() {
|
||||||
TaggedValue::F64Array(lengths) => DashPattern::from(lengths.clone()),
|
TaggedValue::F64Array(lengths) => lengths.clone(),
|
||||||
TaggedValue::F64(length) => DashPattern::from(*length),
|
TaggedValue::F64(length) => vec![*length],
|
||||||
TaggedValue::String(text) => DashPattern::from(text.as_str()),
|
TaggedValue::String(text) => graphene_std::core_types::misc::parse_f64_list(text),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(NodeInput::value(TaggedValue::DashPattern(pattern), *exposed))
|
Some(NodeInput::value(TaggedValue::DashPattern(lengths), *exposed))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a legacy rectangle corner radius input (a single `f64` or a `List<f64>` of up to four values) to the `BoxCorners` value type.
|
/// Converts a legacy rectangle corner radius input (a single `f64` or a `List<f64>` of up to four values) to the `BoxCorners` value type.
|
||||||
fn migrate_corner_radius_input(input: &NodeInput) -> Option<NodeInput> {
|
fn migrate_corner_radius_input(input: &NodeInput) -> Option<NodeInput> {
|
||||||
let NodeInput::Value { tagged_value, exposed } = input else { return None };
|
let NodeInput::Value { tagged_value, exposed } = input else { return None };
|
||||||
let corners = match &*tagged_value.clone().into_inner() {
|
let values = match &*tagged_value.clone().into_inner() {
|
||||||
TaggedValue::F64Array(values) => BoxCorners::from(values.clone()),
|
TaggedValue::F64Array(values) => values.clone(),
|
||||||
TaggedValue::F64(value) => BoxCorners::from(*value),
|
TaggedValue::F64(value) => vec![*value],
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(NodeInput::value(TaggedValue::BoxCorners(corners), *exposed))
|
Some(NodeInput::value(TaggedValue::BoxCorners(values), *exposed))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> {
|
fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> {
|
||||||
@@ -1811,6 +1810,18 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
|||||||
.set_input(&InputConnector::node(*node_id, graphene_std::vector::stroke::DashPatternInput), migrated, network_path);
|
.set_input(&InputConnector::node(*node_id, graphene_std::vector::stroke::DashPatternInput), migrated, network_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The corner radius became the `BoxCorners` value type; convert any already-shaped rectangle that still stores a legacy corner input
|
||||||
|
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::rectangle::IDENTIFIER)
|
||||||
|
&& let Some(corner_input) = node.input(graphene_std::vector::generator_nodes::rectangle::CornerRadiusInput)
|
||||||
|
&& let Some(migrated) = migrate_corner_radius_input(corner_input)
|
||||||
|
{
|
||||||
|
document.network_interface.set_input(
|
||||||
|
&InputConnector::node(*node_id, graphene_std::vector::generator_nodes::rectangle::CornerRadiusInput),
|
||||||
|
migrated,
|
||||||
|
network_path,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// The rectangle's corner radius became the `BoxCorners` value type and its hidden individual-radii toggle moved after the
|
// The rectangle's corner radius became the `BoxCorners` value type and its hidden individual-radii toggle moved after the
|
||||||
// user-visible inputs. A legacy rectangle stores that toggle (a plain `bool`) at index 3, where the new shape stores the corner
|
// user-visible inputs. A legacy rectangle stores that toggle (a plain `bool`) at index 3, where the new shape stores the corner
|
||||||
// radius, so a `bool` value there identifies the old input order: [width, height, individual, corner_radius, clamped].
|
// radius, so a `bool` value there identifies the old input order: [width, height, individual, corner_radius, clamped].
|
||||||
|
|||||||
@@ -557,7 +557,7 @@ pub fn get_stroke_options(layer: LayerNodeIdentifier, network_interface: &NodeNe
|
|||||||
_ => PaintOrder::default(),
|
_ => PaintOrder::default(),
|
||||||
};
|
};
|
||||||
let dash_lengths = match parameters.value(stroke::DashPatternInput) {
|
let dash_lengths = match parameters.value(stroke::DashPatternInput) {
|
||||||
Some(TaggedValue::DashPattern(value)) => value.0.iter_element_values().copied().collect(),
|
Some(TaggedValue::DashPattern(lengths)) => lengths.clone(),
|
||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
};
|
};
|
||||||
let dash_offset = match parameters.value(stroke::DashOffsetInput) {
|
let dash_offset = match parameters.value(stroke::DashOffsetInput) {
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ pub fn apply_paint_order(drawing: &mut DrawingToolState, order: PaintOrder, docu
|
|||||||
|
|
||||||
pub fn apply_dash_lengths(drawing: &mut DrawingToolState, lengths: Vec<f64>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
pub fn apply_dash_lengths(drawing: &mut DrawingToolState, lengths: Vec<f64>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
drawing.dash_lengths = Some(lengths.clone());
|
drawing.dash_lengths = Some(lengths.clone());
|
||||||
graph_modification_utils::set_parameter_for_selected_layers(document, graphene_std::vector::stroke::DashPatternInput, TaggedValue::DashPattern(lengths.into()), responses);
|
graph_modification_utils::set_parameter_for_selected_layers(document, graphene_std::vector::stroke::DashPatternInput, TaggedValue::DashPattern(lengths), responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_dash_offset(drawing: &mut DrawingToolState, offset: f64, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
pub fn apply_dash_offset(drawing: &mut DrawingToolState, offset: f64, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
|
|||||||
@@ -86,6 +86,10 @@ macro_rules! tagged_value {
|
|||||||
#[serde(deserialize_with = "core_types::misc::migrate_to_f64_array")] // TODO: Eventually remove this migration document upgrade code
|
#[serde(deserialize_with = "core_types::misc::migrate_to_f64_array")] // TODO: Eventually remove this migration document upgrade code
|
||||||
#[serde(alias = "F64Table", alias = "VecF64", alias = "VecF32", alias = "F64Array4")]
|
#[serde(alias = "F64Table", alias = "VecF64", alias = "VecF32", alias = "F64Array4")]
|
||||||
F64Array(Vec<f64>),
|
F64Array(Vec<f64>),
|
||||||
|
/// Stored compactly as a `Vec<f64>` of dash lengths, materializes as an `Item<DashPattern>` at runtime via `to_dynany`/`to_any`.
|
||||||
|
DashPattern(Vec<f64>),
|
||||||
|
/// Stored compactly as a `Vec<f64>` of corner values, materializes as an `Item<BoxCorners>` at runtime via `to_dynany`/`to_any`.
|
||||||
|
BoxCorners(Vec<f64>),
|
||||||
/// A plain, always-present color. Aliases recover legacy on-disk shapes; a legacy `null` payload (the old "no color")
|
/// A plain, always-present color. Aliases recover legacy on-disk shapes; a legacy `null` payload (the old "no color")
|
||||||
/// is routed to [`TaggedValue::no_paint`] by `deserialize_tagged_value_with_legacy_migration`.
|
/// is routed to [`TaggedValue::no_paint`] by `deserialize_tagged_value_with_legacy_migration`.
|
||||||
#[serde(deserialize_with = "core_types::misc::migrate_to_color")] // TODO: Eventually remove this migration document upgrade code
|
#[serde(deserialize_with = "core_types::misc::migrate_to_color")] // TODO: Eventually remove this migration document upgrade code
|
||||||
@@ -139,6 +143,8 @@ macro_rules! tagged_value {
|
|||||||
// =======================
|
// =======================
|
||||||
$( Self::$identifier(x) => { x.cache_hash(state) }),*
|
$( Self::$identifier(x) => { x.cache_hash(state) }),*
|
||||||
Self::F64Array(values) => values.cache_hash(state),
|
Self::F64Array(values) => values.cache_hash(state),
|
||||||
|
Self::DashPattern(lengths) => lengths.cache_hash(state),
|
||||||
|
Self::BoxCorners(values) => values.cache_hash(state),
|
||||||
Self::Color(color) => color.cache_hash(state),
|
Self::Color(color) => color.cache_hash(state),
|
||||||
Self::Gradient(stops) => stops.cache_hash(state),
|
Self::Gradient(stops) => stops.cache_hash(state),
|
||||||
Self::BrushStrokes(strokes) => strokes.cache_hash(state),
|
Self::BrushStrokes(strokes) => strokes.cache_hash(state),
|
||||||
@@ -201,6 +207,8 @@ macro_rules! tagged_value {
|
|||||||
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
||||||
Box::new(list)
|
Box::new(list)
|
||||||
}
|
}
|
||||||
|
Self::DashPattern(lengths) => Box::new(Item::new_from_element(DashPattern::from(lengths))),
|
||||||
|
Self::BoxCorners(values) => Box::new(Item::new_from_element(BoxCorners::from(values))),
|
||||||
Self::Color(color) => Box::new(Item::new_from_element(color)),
|
Self::Color(color) => Box::new(Item::new_from_element(color)),
|
||||||
Self::Gradient(stops) => Box::new(Item::new_from_element(stops)),
|
Self::Gradient(stops) => Box::new(Item::new_from_element(stops)),
|
||||||
Self::BrushStrokes(strokes) => Box::new(core_types::list::Item::new_from_element(BrushTrace::from(strokes))),
|
Self::BrushStrokes(strokes) => Box::new(core_types::list::Item::new_from_element(BrushTrace::from(strokes))),
|
||||||
@@ -263,6 +271,8 @@ macro_rules! tagged_value {
|
|||||||
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
||||||
Arc::new(list)
|
Arc::new(list)
|
||||||
}
|
}
|
||||||
|
Self::DashPattern(lengths) => Arc::new(Item::new_from_element(DashPattern::from(lengths))),
|
||||||
|
Self::BoxCorners(values) => Arc::new(Item::new_from_element(BoxCorners::from(values))),
|
||||||
Self::Color(color) => Arc::new(Item::new_from_element(color)),
|
Self::Color(color) => Arc::new(Item::new_from_element(color)),
|
||||||
Self::Gradient(stops) => Arc::new(Item::new_from_element(stops)),
|
Self::Gradient(stops) => Arc::new(Item::new_from_element(stops)),
|
||||||
Self::BrushStrokes(strokes) => Arc::new(core_types::list::Item::new_from_element(BrushTrace::from(strokes))),
|
Self::BrushStrokes(strokes) => Arc::new(core_types::list::Item::new_from_element(BrushTrace::from(strokes))),
|
||||||
@@ -291,6 +301,8 @@ macro_rules! tagged_value {
|
|||||||
Self::None => concrete!(()),
|
Self::None => concrete!(()),
|
||||||
Self::TypeDefault(td) => td.clone(),
|
Self::TypeDefault(td) => td.clone(),
|
||||||
Self::F64Array(_) => list!(f64),
|
Self::F64Array(_) => list!(f64),
|
||||||
|
Self::DashPattern(_) => item!(DashPattern),
|
||||||
|
Self::BoxCorners(_) => item!(BoxCorners),
|
||||||
Self::Color(_) => item!(Color),
|
Self::Color(_) => item!(Color),
|
||||||
Self::Gradient(_) => item!(Gradient),
|
Self::Gradient(_) => item!(Gradient),
|
||||||
Self::BrushStrokes(_) => item!(BrushTrace),
|
Self::BrushStrokes(_) => item!(BrushTrace),
|
||||||
@@ -322,7 +334,20 @@ macro_rules! tagged_value {
|
|||||||
// ===============
|
// ===============
|
||||||
// MANUAL VARIANTS
|
// MANUAL VARIANTS
|
||||||
// ===============
|
// ===============
|
||||||
|
// The manual variants convert from both their payload and wire forms, with the newtypes flattening to their stored `Vec<f64>` form
|
||||||
x if x == TypeId::of::<()>() => Ok(TaggedValue::None),
|
x if x == TypeId::of::<()>() => Ok(TaggedValue::None),
|
||||||
|
x if x == TypeId::of::<Vec<f64>>() => Ok(TaggedValue::F64Array(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<List<f64>>() => Ok(TaggedValue::F64Array(downcast::<List<f64>>(input).unwrap().iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<DashPattern>() => Ok(TaggedValue::DashPattern(downcast::<DashPattern>(input).unwrap().0.iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<Item<DashPattern>>() => Ok(TaggedValue::DashPattern(downcast::<Item<DashPattern>>(input).unwrap().into_element().0.iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<BoxCorners>() => Ok(TaggedValue::BoxCorners(downcast::<BoxCorners>(input).unwrap().0.iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<Item<BoxCorners>>() => Ok(TaggedValue::BoxCorners(downcast::<Item<BoxCorners>>(input).unwrap().into_element().0.iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<Color>() => Ok(TaggedValue::Color(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<Item<Color>>() => Ok(TaggedValue::Color(downcast::<Item<Color>>(input).unwrap().into_element())),
|
||||||
|
x if x == TypeId::of::<Gradient>() => Ok(TaggedValue::Gradient(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<Item<Gradient>>() => Ok(TaggedValue::Gradient(downcast::<Item<Gradient>>(input).unwrap().into_element())),
|
||||||
|
x if x == TypeId::of::<Vec<BrushStroke>>() => Ok(TaggedValue::BrushStrokes(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<Item<BrushTrace>>() => Ok(TaggedValue::BrushStrokes(downcast::<Item<BrushTrace>>(input).unwrap().into_element().0.iter_element_values().cloned().collect())),
|
||||||
// =======================
|
// =======================
|
||||||
// AUTO-GENERATED VARIANTS
|
// AUTO-GENERATED VARIANTS
|
||||||
// =======================
|
// =======================
|
||||||
@@ -345,7 +370,20 @@ macro_rules! tagged_value {
|
|||||||
// ===============
|
// ===============
|
||||||
// MANUAL VARIANTS
|
// MANUAL VARIANTS
|
||||||
// ===============
|
// ===============
|
||||||
|
// The manual variants convert from both their payload and wire forms, with the newtypes flattening to their stored `Vec<f64>` form
|
||||||
x if x == TypeId::of::<()>() => Ok(TaggedValue::None),
|
x if x == TypeId::of::<()>() => Ok(TaggedValue::None),
|
||||||
|
x if x == TypeId::of::<Vec<f64>>() => Ok(TaggedValue::F64Array(input.downcast_ref::<Vec<f64>>().unwrap().clone())),
|
||||||
|
x if x == TypeId::of::<List<f64>>() => Ok(TaggedValue::F64Array(input.downcast_ref::<List<f64>>().unwrap().iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<DashPattern>() => Ok(TaggedValue::DashPattern(input.downcast_ref::<DashPattern>().unwrap().0.iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<Item<DashPattern>>() => Ok(TaggedValue::DashPattern(input.downcast_ref::<Item<DashPattern>>().unwrap().element().0.iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<BoxCorners>() => Ok(TaggedValue::BoxCorners(input.downcast_ref::<BoxCorners>().unwrap().0.iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<Item<BoxCorners>>() => Ok(TaggedValue::BoxCorners(input.downcast_ref::<Item<BoxCorners>>().unwrap().element().0.iter_element_values().copied().collect())),
|
||||||
|
x if x == TypeId::of::<Color>() => Ok(TaggedValue::Color(*input.downcast_ref::<Color>().unwrap())),
|
||||||
|
x if x == TypeId::of::<Item<Color>>() => Ok(TaggedValue::Color(*input.downcast_ref::<Item<Color>>().unwrap().element())),
|
||||||
|
x if x == TypeId::of::<Gradient>() => Ok(TaggedValue::Gradient(input.downcast_ref::<Gradient>().unwrap().clone())),
|
||||||
|
x if x == TypeId::of::<Item<Gradient>>() => Ok(TaggedValue::Gradient(input.downcast_ref::<Item<Gradient>>().unwrap().element().clone())),
|
||||||
|
x if x == TypeId::of::<Vec<BrushStroke>>() => Ok(TaggedValue::BrushStrokes(input.downcast_ref::<Vec<BrushStroke>>().unwrap().clone())),
|
||||||
|
x if x == TypeId::of::<Item<BrushTrace>>() => Ok(TaggedValue::BrushStrokes(input.downcast_ref::<Item<BrushTrace>>().unwrap().element().0.iter_element_values().cloned().collect())),
|
||||||
// =======================
|
// =======================
|
||||||
// AUTO-GENERATED VARIANTS
|
// AUTO-GENERATED VARIANTS
|
||||||
// =======================
|
// =======================
|
||||||
@@ -372,6 +410,8 @@ macro_rules! tagged_value {
|
|||||||
if name == std::any::type_name::<()>() { return Some(TaggedValue::None) }
|
if name == std::any::type_name::<()>() { return Some(TaggedValue::None) }
|
||||||
if name == std::any::type_name::<Color>() { return Some(TaggedValue::Color(Color::default())) }
|
if name == std::any::type_name::<Color>() { return Some(TaggedValue::Color(Color::default())) }
|
||||||
if name == std::any::type_name::<Gradient>() { return Some(TaggedValue::Gradient(Gradient::default())) }
|
if name == std::any::type_name::<Gradient>() { return Some(TaggedValue::Gradient(Gradient::default())) }
|
||||||
|
if name == std::any::type_name::<DashPattern>() { return Some(TaggedValue::DashPattern(Vec::new())) }
|
||||||
|
if name == std::any::type_name::<BoxCorners>() { return Some(TaggedValue::BoxCorners(Vec::new())) }
|
||||||
$( if name == std::any::type_name::<$ty>() { return Some(TaggedValue::$identifier(Default::default())) } )*
|
$( if name == std::any::type_name::<$ty>() { return Some(TaggedValue::$identifier(Default::default())) } )*
|
||||||
if name == std::any::type_name::<BrushTrace>() { return Some(TaggedValue::BrushStrokes(Vec::new())) }
|
if name == std::any::type_name::<BrushTrace>() { return Some(TaggedValue::BrushStrokes(Vec::new())) }
|
||||||
// Unranked types without a variant route through `TypeDefault`, with `to_dynany`/`to_any` constructing the actual default at execution time
|
// Unranked types without a variant route through `TypeDefault`, with `to_dynany`/`to_any` constructing the actual default at execution time
|
||||||
@@ -423,6 +463,8 @@ macro_rules! tagged_value {
|
|||||||
Self::None => "()".to_string(),
|
Self::None => "()".to_string(),
|
||||||
Self::TypeDefault(td) => format!("TypeDefault({td})"),
|
Self::TypeDefault(td) => format!("TypeDefault({td})"),
|
||||||
Self::F64Array(values) => format!("F64Array({values:?})"),
|
Self::F64Array(values) => format!("F64Array({values:?})"),
|
||||||
|
Self::DashPattern(lengths) => format!("DashPattern({lengths:?})"),
|
||||||
|
Self::BoxCorners(values) => format!("BoxCorners({values:?})"),
|
||||||
Self::Color(color) => format!("Color({color:?})"),
|
Self::Color(color) => format!("Color({color:?})"),
|
||||||
Self::Gradient(stops) => format!("Gradient({stops:?})"),
|
Self::Gradient(stops) => format!("Gradient({stops:?})"),
|
||||||
Self::BrushStrokes(strokes) => format!("BrushStrokes({strokes:?})"),
|
Self::BrushStrokes(strokes) => format!("BrushStrokes({strokes:?})"),
|
||||||
@@ -521,8 +563,6 @@ tagged_value! {
|
|||||||
StrokeJoin(vector::style::StrokeJoin),
|
StrokeJoin(vector::style::StrokeJoin),
|
||||||
StrokeAlign(vector::style::StrokeAlign),
|
StrokeAlign(vector::style::StrokeAlign),
|
||||||
PaintOrder(vector::style::PaintOrder),
|
PaintOrder(vector::style::PaintOrder),
|
||||||
DashPattern(vector::style::DashPattern),
|
|
||||||
BoxCorners(vector::misc::BoxCorners),
|
|
||||||
GradientType(vector::style::GradientType),
|
GradientType(vector::style::GradientType),
|
||||||
GradientSpreadMethod(vector::style::GradientSpreadMethod),
|
GradientSpreadMethod(vector::style::GradientSpreadMethod),
|
||||||
ReferencePoint(vector::ReferencePoint),
|
ReferencePoint(vector::ReferencePoint),
|
||||||
@@ -670,8 +710,8 @@ impl TaggedValue {
|
|||||||
() if ty == TypeId::of::<Graphic>() => to_color(string).map(TaggedValue::Color)?,
|
() if ty == TypeId::of::<Graphic>() => to_color(string).map(TaggedValue::Color)?,
|
||||||
() if ty == TypeId::of::<Gradient>() => to_gradient(string).map(TaggedValue::Gradient)?,
|
() if ty == TypeId::of::<Gradient>() => to_gradient(string).map(TaggedValue::Gradient)?,
|
||||||
() if ty == TypeId::of::<ReferencePoint>() => to_reference_point(string).map(TaggedValue::ReferencePoint)?,
|
() if ty == TypeId::of::<ReferencePoint>() => to_reference_point(string).map(TaggedValue::ReferencePoint)?,
|
||||||
() if ty == TypeId::of::<DashPattern>() => TaggedValue::DashPattern(DashPattern::from(string)),
|
() if ty == TypeId::of::<DashPattern>() => TaggedValue::DashPattern(core_types::misc::parse_f64_list(string)),
|
||||||
() if ty == TypeId::of::<BoxCorners>() => TaggedValue::BoxCorners(BoxCorners::from(string)),
|
() if ty == TypeId::of::<BoxCorners>() => TaggedValue::BoxCorners(core_types::misc::parse_f64_list(string)),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(ty)
|
Some(ty)
|
||||||
|
|||||||
@@ -1059,7 +1059,7 @@ mod test {
|
|||||||
// If this assert fails: These NodeIds seem to be changing when you modify TaggedValue, just update them.
|
// If this assert fails: These NodeIds seem to be changing when you modify TaggedValue, just update them.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ids,
|
ids,
|
||||||
vec![NodeId(12815475172301479638), NodeId(13251389748338817266), NodeId(7166921994790432021), NodeId(15318519137317483318)]
|
vec![NodeId(12331852515109999872), NodeId(5084548161767585362), NodeId(14635346976242256925), NodeId(16015195863711239715)]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ pub fn migrate_to_f64_array<'de, D: serde::Deserializer<'de>>(deserializer: D) -
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a comma or space separated list of numbers, skipping any pieces that fail to parse.
|
||||||
|
pub fn parse_f64_list(text: &str) -> Vec<f64> {
|
||||||
|
text.split([',', ' ']).filter(|piece| !piece.is_empty()).filter_map(|piece| piece.parse::<f64>().ok()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a CSS color string (named color, hex, `rgb(...)`, `hsl(...)`, etc.) into a linear-light [`Color`] using the `color` crate's CSS Color 4 parser.
|
/// Parse a CSS color string (named color, hex, `rgb(...)`, `hsl(...)`, etc.) into a linear-light [`Color`] using the `color` crate's CSS Color 4 parser.
|
||||||
/// Tries the input as-is first (catches CSS named colors like `red`, `rgb(...)`, and well-formed hex like `#abcdef`), then falls back to treating the input as bare hex with length-based expansion to a CSS-parseable form:
|
/// Tries the input as-is first (catches CSS named colors like `red`, `rgb(...)`, and well-formed hex like `#abcdef`), then falls back to treating the input as bare hex with length-based expansion to a CSS-parseable form:
|
||||||
/// - 1 char `f` → `#fff` (CSS 3-char shorthand)
|
/// - 1 char `f` → `#fff` (CSS 3-char shorthand)
|
||||||
|
|||||||
@@ -77,21 +77,6 @@ impl BoxCorners {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `List<f64>` is a runtime-only wire type, so serialize the corners as their bare values to keep documents stable
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl serde::Serialize for BoxCorners {
|
|
||||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.collect_seq(self.0.iter_element_values())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de> serde::Deserialize<'de> for BoxCorners {
|
|
||||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
Ok(Self::from(<Vec<f64> as serde::Deserialize>::deserialize(deserializer)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<f64> for BoxCorners {
|
impl From<f64> for BoxCorners {
|
||||||
fn from(value: f64) -> Self {
|
fn from(value: f64) -> Self {
|
||||||
Self(List::new_from_element(value))
|
Self(List::new_from_element(value))
|
||||||
@@ -106,12 +91,7 @@ impl From<Vec<f64>> for BoxCorners {
|
|||||||
|
|
||||||
impl From<&str> for BoxCorners {
|
impl From<&str> for BoxCorners {
|
||||||
fn from(text: &str) -> Self {
|
fn from(text: &str) -> Self {
|
||||||
Self::from(
|
Self::from(core_types::misc::parse_f64_list(text))
|
||||||
text.split([',', ' '])
|
|
||||||
.filter(|piece| !piece.is_empty())
|
|
||||||
.filter_map(|piece| piece.parse::<f64>().ok())
|
|
||||||
.collect::<Vec<f64>>(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,18 +118,18 @@ pub enum StrokeCap {
|
|||||||
#[default]
|
#[default]
|
||||||
#[icon("StrokeCapButt")]
|
#[icon("StrokeCapButt")]
|
||||||
Butt,
|
Butt,
|
||||||
#[icon("StrokeCapRound")]
|
|
||||||
Round,
|
|
||||||
#[icon("StrokeCapSquare")]
|
#[icon("StrokeCapSquare")]
|
||||||
Square,
|
Square,
|
||||||
|
#[icon("StrokeCapRound")]
|
||||||
|
Round,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StrokeCap {
|
impl StrokeCap {
|
||||||
pub fn svg_name(&self) -> &'static str {
|
pub fn svg_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
StrokeCap::Butt => "butt",
|
StrokeCap::Butt => "butt",
|
||||||
StrokeCap::Round => "round",
|
|
||||||
StrokeCap::Square => "square",
|
StrokeCap::Square => "square",
|
||||||
|
StrokeCap::Round => "round",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,21 +218,6 @@ impl DashPattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `List<f64>` is a runtime-only wire type, so serialize the pattern as its bare lengths to keep documents stable
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl serde::Serialize for DashPattern {
|
|
||||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.collect_seq(self.0.iter_element_values())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de> serde::Deserialize<'de> for DashPattern {
|
|
||||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
Ok(Self::from(<Vec<f64> as serde::Deserialize>::deserialize(deserializer)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<f64> for DashPattern {
|
impl From<f64> for DashPattern {
|
||||||
fn from(length: f64) -> Self {
|
fn from(length: f64) -> Self {
|
||||||
Self(List::new_from_element(length))
|
Self(List::new_from_element(length))
|
||||||
@@ -247,12 +232,7 @@ impl From<Vec<f64>> for DashPattern {
|
|||||||
|
|
||||||
impl From<&str> for DashPattern {
|
impl From<&str> for DashPattern {
|
||||||
fn from(text: &str) -> Self {
|
fn from(text: &str) -> Self {
|
||||||
Self::from(
|
Self::from(core_types::misc::parse_f64_list(text))
|
||||||
text.split([',', ' '])
|
|
||||||
.filter(|piece| !piece.is_empty())
|
|
||||||
.filter_map(|piece| piece.parse::<f64>().ok())
|
|
||||||
.collect::<Vec<f64>>(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user