mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Add Graphic::None and store paint choices as plain color, gradient, and no-paint values
This commit is contained in:
committed by
Dennis Kobert
parent
d13f926da3
commit
a373fad0ca
@@ -334,6 +334,7 @@ impl TableItemLayout for Graphic<'_> {
|
||||
}
|
||||
fn identifier(&self) -> String {
|
||||
match self {
|
||||
Self::None => "None".to_string(),
|
||||
Self::Graphic(list) => list.identifier(),
|
||||
Self::Vector(list) => list.identifier(),
|
||||
Self::RasterCPU(list) => list.identifier(),
|
||||
@@ -350,6 +351,7 @@ impl TableItemLayout for Graphic<'_> {
|
||||
}
|
||||
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||
match self {
|
||||
Self::None => label("None"),
|
||||
Self::Graphic(list) => list.layout_with_breadcrumb(data),
|
||||
Self::Vector(list) => list.layout_with_breadcrumb(data),
|
||||
Self::RasterCPU(list) => list.layout_with_breadcrumb(data),
|
||||
|
||||
@@ -138,7 +138,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
Some(NodeInput::type_default(descriptor!(List<Graphic>), true)),
|
||||
Some(NodeInput::value(TaggedValue::DVec2(location), false)),
|
||||
Some(NodeInput::value(TaggedValue::DVec2(dimensions), false)),
|
||||
Some(NodeInput::value(TaggedValue::Color(Some(background)), false)),
|
||||
Some(NodeInput::value(TaggedValue::Color(background), false)),
|
||||
Some(NodeInput::value(TaggedValue::Bool(clip), false)),
|
||||
]);
|
||||
self.network_interface.insert_node(new_id, artboard_node_template, &[]);
|
||||
@@ -300,7 +300,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
pub fn insert_color_value(&mut self, color: Color, layer: LayerNodeIdentifier) {
|
||||
let color_value = resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER)
|
||||
.expect("Color Value node does not exist")
|
||||
.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::Color(Some(color)), false))]);
|
||||
.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::Color(color), false))]);
|
||||
|
||||
let color_value_id = NodeId::new();
|
||||
self.network_interface.insert_node(color_value_id, color_value, &[]);
|
||||
@@ -460,8 +460,12 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
let input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::FillInput::INDEX);
|
||||
let backup_input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::BackupColorInput::INDEX);
|
||||
|
||||
self.set_input_with_refresh(backup_input_connector, NodeInput::value(TaggedValue::Color(color), false), true);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Color(color), false), false);
|
||||
// The backup remembers the last solid color, so the red-slash "none" choice leaves it untouched
|
||||
if let Some(color) = color {
|
||||
self.set_input_with_refresh(backup_input_connector, NodeInput::value(TaggedValue::Color(color), false), true);
|
||||
}
|
||||
let fill_value = color.map_or_else(TaggedValue::no_paint, TaggedValue::Color);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(fill_value, false), false);
|
||||
}
|
||||
|
||||
pub fn fill_gradient_set(&mut self, gradient: Gradient, gradient_type: GradientType, spread_method: GradientSpreadMethod, transform: DAffine2) {
|
||||
@@ -716,7 +720,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
};
|
||||
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Color(color), false), true);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(color.map_or_else(TaggedValue::no_paint, TaggedValue::Color), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::WeightInput::INDEX);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.weight), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::AlignInput::INDEX);
|
||||
|
||||
@@ -354,7 +354,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
||||
NodeInput::type_default(descriptor!(List<Graphic>), true),
|
||||
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
|
||||
NodeInput::value(TaggedValue::DVec2(DVec2::new(1920., 1080.)), false),
|
||||
NodeInput::value(TaggedValue::Color(Some(Color::WHITE)), false),
|
||||
NodeInput::value(TaggedValue::Color(Color::WHITE), false),
|
||||
NodeInput::value(TaggedValue::Bool(true), false),
|
||||
],
|
||||
..Default::default()
|
||||
|
||||
@@ -32,9 +32,7 @@ use graphene_std::text_nodes::StringCapitalization;
|
||||
use graphene_std::transform::{Footprint, ReferencePoint, ScaleType, Transform};
|
||||
use graphene_std::vector::misc::BooleanOperation;
|
||||
use graphene_std::vector::misc::{ArcType, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType};
|
||||
use graphene_std::vector::style::{
|
||||
FillChoice, FillChoiceUI, Gradient, GradientSpreadMethod, GradientType, GradientUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation,
|
||||
};
|
||||
use graphene_std::vector::style::{FillChoiceUI, Gradient, GradientSpreadMethod, GradientType, GradientUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation};
|
||||
use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification};
|
||||
|
||||
pub(crate) fn string_properties(text: &str) -> Vec<LayoutGroup> {
|
||||
@@ -1175,30 +1173,37 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
|
||||
|
||||
// Add the color input
|
||||
match &**tagged_value {
|
||||
TaggedValue::Color(color) => widgets.push(
|
||||
color_button
|
||||
.value(FillChoiceUI::from(&match color {
|
||||
Some(color) => FillChoice::Solid(*color),
|
||||
None => FillChoice::None,
|
||||
}))
|
||||
.on_update(update_value(|input: &ColorInput| TaggedValue::Color(input.value.as_solid().map(Color::from)), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_instance(),
|
||||
),
|
||||
TaggedValue::Gradient(stops) => widgets.push(
|
||||
color_button
|
||||
.value(FillChoiceUI::from(&FillChoice::Gradient(stops.clone())))
|
||||
.on_update(update_value(
|
||||
|input: &ColorInput| TaggedValue::Gradient(input.value.as_gradient().map(Gradient::from).unwrap_or_default()),
|
||||
node_id,
|
||||
index,
|
||||
))
|
||||
.on_commit(commit_value)
|
||||
.widget_instance(),
|
||||
),
|
||||
x => warn!("Color {x:?}"),
|
||||
}
|
||||
let widget_value = match &**tagged_value {
|
||||
TaggedValue::Color(color) => FillChoiceUI::Solid(SRGBA8::from(*color)),
|
||||
TaggedValue::Gradient(stops) => FillChoiceUI::Gradient(GradientUI::from(stops)),
|
||||
value if value.is_no_paint() => FillChoiceUI::None,
|
||||
x => {
|
||||
warn!("Color {x:?}");
|
||||
return LayoutGroup::row(widgets);
|
||||
}
|
||||
};
|
||||
|
||||
// A paint input (`allow_none`) stores the pick as a plain color, gradient, or no-paint type default,
|
||||
// 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)),
|
||||
}
|
||||
} else if matches!(&**tagged_value, TaggedValue::Gradient(_)) {
|
||||
|input| TaggedValue::Gradient(input.value.as_gradient().map(Gradient::from).unwrap_or_default())
|
||||
} else {
|
||||
|input| TaggedValue::Color(input.value.as_solid().map(Color::from).unwrap_or(Color::TRANSPARENT))
|
||||
};
|
||||
|
||||
widgets.push(
|
||||
color_button
|
||||
.value(widget_value)
|
||||
.on_update(update_value(on_update, node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_instance(),
|
||||
);
|
||||
|
||||
LayoutGroup::row(widgets)
|
||||
}
|
||||
@@ -2461,9 +2466,6 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
Other,
|
||||
}
|
||||
|
||||
let connector = InputConnector::node(node_id, FillInput::INDEX);
|
||||
let input_type = context.network_interface.input_type(&connector, context.selection_network_path);
|
||||
|
||||
// Pass blank_assist=false because the assist slot is filled below ("Reverse Stops" button when in gradient mode)
|
||||
let mut widgets_first_row = start_widgets(ParameterWidgetsInfo::new(node_id, FillInput::INDEX, false, context));
|
||||
|
||||
@@ -2475,42 +2477,33 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
// bounding-box default transform needs the layer, and it falls back to a unit box when there isn't one.
|
||||
let layer = root_layer_for_chain_node(node_id, context);
|
||||
|
||||
let fill = match input_type.compiled_nested_type() {
|
||||
Some(ty) if ty == &concrete!(List<Color>) => {
|
||||
if let Ok(document_node) = get_document_node(node_id, context) {
|
||||
let color = match document_node.inputs[FillInput::INDEX].as_value() {
|
||||
Some(&TaggedValue::Color(c)) => c,
|
||||
_ => None,
|
||||
};
|
||||
ResolvedFill::Solid(color)
|
||||
} else {
|
||||
ResolvedFill::Other
|
||||
}
|
||||
}
|
||||
Some(ty) if ty == &concrete!(List<Gradient>) => {
|
||||
// Read this node's own inputs rather than the layer's nearest Fill, which may be a different node when Fills are chained
|
||||
if let Ok(document_node) = get_document_node(node_id, context)
|
||||
&& let Some(gradient) = graph_modification_utils::read_fill_node_gradient(document_node, || {
|
||||
let fill = match get_document_node(node_id, context) {
|
||||
Ok(document_node) => match document_node.inputs[FillInput::INDEX].as_value() {
|
||||
Some(TaggedValue::Color(color)) => ResolvedFill::Solid(Some(*color)),
|
||||
Some(value) if value.is_no_paint() => ResolvedFill::Solid(None),
|
||||
Some(TaggedValue::Gradient(_)) => {
|
||||
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))
|
||||
}) {
|
||||
ResolvedFill::Gradient {
|
||||
gradient: gradient.stops,
|
||||
gradient_type: gradient.gradient_type,
|
||||
spread_method: gradient.spread_method,
|
||||
transform: gradient.transform,
|
||||
transform_is_value: gradient.transform_is_value,
|
||||
Some(gradient) => ResolvedFill::Gradient {
|
||||
gradient: gradient.stops,
|
||||
gradient_type: gradient.gradient_type,
|
||||
spread_method: gradient.spread_method,
|
||||
transform: gradient.transform,
|
||||
transform_is_value: gradient.transform_is_value,
|
||||
},
|
||||
None => ResolvedFill::Other,
|
||||
}
|
||||
} else {
|
||||
ResolvedFill::Other
|
||||
}
|
||||
}
|
||||
_ => ResolvedFill::Other,
|
||||
_ => ResolvedFill::Other,
|
||||
},
|
||||
Err(_) => ResolvedFill::Other,
|
||||
};
|
||||
|
||||
let (backup_color, backup_gradient) = match get_document_node(node_id, context) {
|
||||
Ok(document_node) => {
|
||||
let backup_color = match document_node.inputs[BackupColorInput::INDEX].as_value() {
|
||||
Some(&TaggedValue::Color(color)) => color,
|
||||
Some(&TaggedValue::Color(color)) => Some(color),
|
||||
_ => None,
|
||||
};
|
||||
let backup_stops = match document_node.inputs[BackupGradientInput::INDEX].as_value() {
|
||||
@@ -2549,21 +2542,26 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
ResolvedFill::Other => FillChoiceUI::None,
|
||||
};
|
||||
|
||||
let solid_set_messages = move |color: Option<Color>| Message::Batched {
|
||||
messages: Box::new([
|
||||
let solid_set_messages = move |color: Option<Color>| {
|
||||
let mut messages = vec![
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: FillInput::INDEX,
|
||||
value: Box::new(TaggedValue::Color(color)),
|
||||
value: Box::new(color.map_or_else(TaggedValue::no_paint, TaggedValue::Color)),
|
||||
}
|
||||
.into(),
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: BackupColorInput::INDEX,
|
||||
value: Box::new(TaggedValue::Color(color)),
|
||||
}
|
||||
.into(),
|
||||
]),
|
||||
];
|
||||
if let Some(color) = color {
|
||||
messages.push(
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node_id,
|
||||
input_index: BackupColorInput::INDEX,
|
||||
value: Box::new(TaggedValue::Color(color)),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
Message::Batched { messages: messages.into() }
|
||||
};
|
||||
|
||||
let gradient_set_messages = move |gradient: Gradient| Message::Batched {
|
||||
@@ -2611,7 +2609,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
||||
let entries = vec![
|
||||
RadioEntryData::new("solid")
|
||||
.label("Solid")
|
||||
.on_update(update_value(move |_| TaggedValue::Color(backup_color), node_id, FillInput::INDEX))
|
||||
.on_update(update_value(move |_| backup_color.map_or_else(TaggedValue::no_paint, TaggedValue::Color), node_id, FillInput::INDEX))
|
||||
.on_commit(commit_value),
|
||||
RadioEntryData::new("gradient")
|
||||
.label("Gradient")
|
||||
|
||||
@@ -708,3 +708,60 @@ async fn demo_artwork_edit_autosaves_and_round_trips() {
|
||||
// Autosaving the undone state still round-trips cleanly (no drift panic).
|
||||
editor.active_document_mut().commit_storage_snapshot(&byte_store, true);
|
||||
}
|
||||
|
||||
/// The document's single Fill node, as `(network_path, node_id)`.
|
||||
fn find_fill_node(document: &DocumentMessageHandler) -> (Vec<graph_craft::document::NodeId>, graph_craft::document::NodeId) {
|
||||
node_paths(&document.network_interface)
|
||||
.into_iter()
|
||||
.find(|(network_path, node_id)| {
|
||||
let Some(network) = document.network_interface.nested_network(network_path) else { return false };
|
||||
network.nodes[node_id].implementation == graph_craft::document::DocumentNodeImplementation::ProtoNode(graphene_std::vector_nodes::fill::IDENTIFIER)
|
||||
})
|
||||
.expect("the document should contain a Fill node")
|
||||
}
|
||||
|
||||
/// The stored paint value of the document's single Fill node.
|
||||
fn fill_paint_value(document: &DocumentMessageHandler) -> graph_craft::document::value::TaggedValue {
|
||||
use graphene_std::NodeInputDecleration as _;
|
||||
|
||||
let (network_path, node_id) = find_fill_node(document);
|
||||
let network = document.network_interface.nested_network(&network_path).expect("the found network path should resolve");
|
||||
let input = network.nodes[&node_id]
|
||||
.inputs
|
||||
.get(graphene_std::vector::fill::FillInput::INDEX)
|
||||
.expect("Fill should have a paint input");
|
||||
input.as_value().expect("the paint input should hold a value").clone()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn none_fill_survives_document_reopen() {
|
||||
use graphene_std::NodeInputDecleration as _;
|
||||
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Rectangle, 0., 0., 100., 100., ModifierKeys::empty()).await;
|
||||
|
||||
// Pick the red-slash "none" paint, stored the same way as the Fill widget's None choice
|
||||
let (_, fill_node_id) = find_fill_node(editor.active_document());
|
||||
editor
|
||||
.handle_message(NodeGraphMessage::SetInputValue {
|
||||
node_id: fill_node_id,
|
||||
input_index: graphene_std::vector::fill::FillInput::INDEX,
|
||||
value: Box::new(graph_craft::document::value::TaggedValue::no_paint()),
|
||||
})
|
||||
.await;
|
||||
assert!(fill_paint_value(editor.active_document()).is_no_paint(), "the None pick should store as no_paint");
|
||||
|
||||
// Reopen through the editor's real open path, which runs the document migrations
|
||||
let serialized = editor.active_document().serialize_document();
|
||||
editor
|
||||
.handle_message(PortfolioMessage::OpenDocumentFile {
|
||||
document_name: None,
|
||||
document_path: None,
|
||||
document_serialized_content: serialized,
|
||||
})
|
||||
.await;
|
||||
|
||||
let reopened_paint = fill_paint_value(editor.active_document());
|
||||
assert!(reopened_paint.is_no_paint(), "a none fill should survive reopening, but the stored paint became {reopened_paint:?}");
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ use graph_craft::application_io::resource::{DataSource, Resource, ResourceHash,
|
||||
use graph_craft::descriptor;
|
||||
use graph_craft::document::DocumentNode;
|
||||
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
|
||||
use graphene_std::Color;
|
||||
use graphene_std::NodeInputDecleration;
|
||||
use graphene_std::ProtoNodeIdentifier;
|
||||
use graphene_std::text::{TextAlign, TypesettingConfig};
|
||||
use graphene_std::transform::ScaleType;
|
||||
@@ -1630,8 +1632,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
Some(TaggedValue::LegacyFill(old_fill)) => {
|
||||
let exposed = old_inputs[1].is_exposed();
|
||||
let fill_value = match old_fill {
|
||||
graphic_types::migrations::legacy::LegacyFill::None => TaggedValue::Color(None),
|
||||
graphic_types::migrations::legacy::LegacyFill::Solid(color) => TaggedValue::Color(Some(*color)),
|
||||
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()),
|
||||
};
|
||||
document
|
||||
@@ -1730,6 +1732,57 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 9), old_inputs[4].clone(), network_path);
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
// A legacy "no color" on a plain color connector (`TaggedValue::no_paint()` restored by the deserializer) becomes a color,
|
||||
// since only paint connectors keep the no-paint choice
|
||||
{
|
||||
let migrate_color_input = |input: &NodeInput, fallback: Color| -> Option<NodeInput> {
|
||||
let NodeInput::Value { tagged_value, exposed } = input else { return None };
|
||||
if !tagged_value.is_no_paint() {
|
||||
return None;
|
||||
}
|
||||
Some(NodeInput::value(TaggedValue::Color(fallback), *exposed))
|
||||
};
|
||||
|
||||
let conversions: &[(ProtoNodeIdentifier, usize, Color)] = &[
|
||||
(graphene_std::vector::fill::IDENTIFIER, graphene_std::vector::fill::BackupColorInput::INDEX, Color::BLACK),
|
||||
(
|
||||
graphene_std::artboard::create_artboard::IDENTIFIER,
|
||||
graphene_std::artboard::create_artboard::BackgroundInput::INDEX,
|
||||
Color::WHITE,
|
||||
),
|
||||
(
|
||||
graphene_std::math_nodes::color_value::IDENTIFIER,
|
||||
graphene_std::math_nodes::color_value::ColorInput::INDEX,
|
||||
Color::TRANSPARENT,
|
||||
),
|
||||
(
|
||||
graphene_std::raster_nodes::adjustments::black_and_white::IDENTIFIER,
|
||||
graphene_std::raster_nodes::adjustments::black_and_white::TintInput::INDEX,
|
||||
Color::BLACK,
|
||||
),
|
||||
(
|
||||
graphene_std::raster_nodes::blending_nodes::color_overlay::IDENTIFIER,
|
||||
graphene_std::raster_nodes::blending_nodes::color_overlay::ColorInput::INDEX,
|
||||
Color::BLACK,
|
||||
),
|
||||
(
|
||||
graphene_std::raster_nodes::std_nodes::empty_image::IDENTIFIER,
|
||||
graphene_std::raster_nodes::std_nodes::empty_image::ColorInput::INDEX,
|
||||
Color::WHITE,
|
||||
),
|
||||
];
|
||||
for &(ref identifier, index, fallback) in conversions {
|
||||
if reference != DefinitionIdentifier::ProtoNode(identifier.clone()) {
|
||||
continue;
|
||||
}
|
||||
let Some(input) = node.inputs.get(index) else { continue };
|
||||
if let Some(migrated) = migrate_color_input(input, fallback) {
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, index), migrated, network_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade Text node to include line height and character spacing, which were previously hardcoded to 1, from https://github.com/GraphiteEditor/Graphite/pull/2016
|
||||
if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_std::text::TextNode")) && inputs_count == 8 {
|
||||
let mut template: NodeTemplate = legacy_text_node_template()?;
|
||||
|
||||
@@ -362,10 +362,10 @@ pub fn gradient_orientation_rightward(transform: glam::DAffine2) -> bool {
|
||||
/// Get the current fill of a layer from the closest "Fill" node.
|
||||
pub fn get_fill_color(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<Color> {
|
||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER))?;
|
||||
let &TaggedValue::Color(color) = inputs.get(graphene_std::vector::fill::FillInput::INDEX)?.as_value()? else {
|
||||
let TaggedValue::Color(color) = inputs.get(graphene_std::vector::fill::FillInput::INDEX)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
color
|
||||
Some(*color)
|
||||
}
|
||||
|
||||
/// Get the current blend mode of a layer from the closest upstream "Blend Mode" node.
|
||||
@@ -666,7 +666,11 @@ pub fn read_fill_node_gradient(fill_node: &DocumentNode, bounding_box: impl FnOn
|
||||
pub fn get_stroke_color(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<Option<Color>> {
|
||||
let color_index = graphene_std::vector::stroke::PaintInput::INDEX;
|
||||
let tagged = NodeGraphLayer::new(layer, network_interface).find_input(&DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER), color_index)?;
|
||||
if let TaggedValue::Color(color) = tagged { Some(*color) } else { None }
|
||||
match tagged {
|
||||
TaggedValue::Color(color) => Some(Some(*color)),
|
||||
value if value.is_no_paint() => Some(None),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Aggregated fill state across all selected non-artboard layers.
|
||||
@@ -699,8 +703,9 @@ pub fn selected_fill_state(document: &DocumentMessageHandler) -> Option<Selected
|
||||
let fill_node = document.network_interface.document_network().nodes.get(&fill_node_id)?;
|
||||
|
||||
match fill_node.inputs.get(graphene_std::vector::fill::FillInput::INDEX)?.as_value()? {
|
||||
&TaggedValue::Color(color) => Some(color.map_or(FillChoice::None, FillChoice::Solid)),
|
||||
&TaggedValue::Color(color) => Some(FillChoice::Solid(color)),
|
||||
TaggedValue::Gradient(stops) => Some(FillChoice::Gradient(stops.clone())),
|
||||
value if value.is_no_paint() => Some(FillChoice::None),
|
||||
_ => None,
|
||||
}
|
||||
})()
|
||||
@@ -825,7 +830,7 @@ pub fn set_stroke_color_for_selected_layers(color: Option<Color>, weight: f64, d
|
||||
for layer in layers {
|
||||
if let Some(node_id) = get_stroke_id(layer, &document.network_interface) {
|
||||
let input_index = graphene_std::vector::stroke::PaintInput::INDEX;
|
||||
let value = Box::new(TaggedValue::Color(color));
|
||||
let value = Box::new(color.map_or_else(TaggedValue::no_paint, TaggedValue::Color));
|
||||
responses.add(NodeGraphMessage::SetInputValue { node_id, input_index, value });
|
||||
} else {
|
||||
let stroke = graphene_std::vector::style::Stroke::new(weight);
|
||||
|
||||
Reference in New Issue
Block a user