Skip to content
8 changes: 4 additions & 4 deletions editor/src/messages/clipboard/clipboard_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,11 @@ impl MessageHandler<ClipboardMessage, ClipboardMessageContext<'_>> for Clipboard
});

// Add default fill and stroke to the layer
let fill = graphene_std::vector::style::Fill::solid(Color::WHITE);
responses.add(GraphOperationMessage::FillSet { layer, fill });
responses.add(GraphOperationMessage::FillColorSet { layer, color: Some(Color::WHITE) });

let stroke = graphene_std::vector::style::Stroke::new(Some(Color::BLACK), DEFAULT_STROKE_WIDTH);
responses.add(GraphOperationMessage::StrokeSet { layer, stroke });
let color = Some(Color::BLACK);
let stroke = graphene_std::vector::style::Stroke::new(DEFAULT_STROKE_WIDTH);
responses.add(GraphOperationMessage::StrokeSet { layer, color, stroke });

// Create new point ids and add those into the existing Vector path
let mut points_map = HashMap::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use graphene_std::list::List;
use graphene_std::memo::IORecord;
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::vector::Vector;
use graphene_std::vector::style::{Fill, FillChoice, FillChoiceUI, GradientSpreadMethod, GradientType};
use graphene_std::vector::style::{FillChoice, FillChoiceUI, GradientSpreadMethod, GradientType};
use graphene_std::{Artboard, Color, Context, Graphic};
use std::any::Any;
use std::sync::Arc;
Expand Down Expand Up @@ -385,61 +385,7 @@ impl TableItemLayout for Vector {
VectorTableTab::Properties => {
table_rows.push(column_headings(&["property", "value"]));

match self.style.fill.clone() {
Fill::None => table_rows.push(vec![
TextLabel::new("Fill").narrow(true).widget_instance(),
ColorInput::new(FillChoiceUI::None)
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
.widget_instance(),
]),
Fill::Solid(color) => table_rows.push(vec![
TextLabel::new("Fill").narrow(true).widget_instance(),
ColorInput::new(FillChoiceUI::from(&FillChoice::Solid(color)))
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
.widget_instance(),
]),
Fill::Gradient(gradient) => {
table_rows.push(vec![
TextLabel::new("Fill").narrow(true).widget_instance(),
ColorInput::new(FillChoiceUI::from(&FillChoice::Gradient(gradient.stops)))
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
.widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Fill Gradient Type").narrow(true).widget_instance(),
TextLabel::new(gradient.gradient_type.to_string()).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Fill Gradient Start").narrow(true).widget_instance(),
TextLabel::new(format_dvec2(gradient.start)).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Fill Gradient End").narrow(true).widget_instance(),
TextLabel::new(format_dvec2(gradient.end)).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Fill Gradient Transform").narrow(true).widget_instance(),
TextLabel::new(format_transform_matrix(gradient.transform)).narrow(true).widget_instance(),
]);
}
}

if let Some(stroke) = self.style.stroke.clone() {
let color = if let Some(color) = stroke.color { FillChoice::Solid(color) } else { FillChoice::None };
table_rows.push(vec![
TextLabel::new("Stroke").narrow(true).widget_instance(),
ColorInput::new(FillChoiceUI::from(&color))
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.narrow(true)
.widget_instance(),
]);
if let Some(stroke) = self.stroke.as_ref() {
table_rows.push(vec![
TextLabel::new("Stroke Weight").narrow(true).widget_instance(),
TextLabel::new(format!("{} px", stroke.weight)).narrow(true).widget_instance(),
Expand Down
32 changes: 11 additions & 21 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ use graphene_std::math::quad::Quad;
use graphene_std::path_bool_nodes::boolean_intersect;
use graphene_std::raster::BlendMode;
use graphene_std::subpath::Subpath;
use graphene_std::vector::PointId;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::misc::dvec2_to_point;
use graphene_std::vector::style::{Fill, Gradient, RenderMode};
use graphene_std::vector::style::RenderMode;
use graphene_std::vector::{PointId, graphic_types};
use kurbo::{Affine, BezPath, Line, PathSeg};
use std::collections::HashSet;
use std::path::PathBuf;
Expand Down Expand Up @@ -125,7 +125,7 @@ pub struct DocumentMessageHandler {
/// network path, the node itself, and its original relative gradient. The deferred migration removes each entry as its bake lands.
/// Transient migration state, but persisted in the saved document so unfinished bakes retry on the next open instead of losing placement.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub(crate) pending_gradient_bbox_bake: Vec<(Vec<NodeId>, NodeId, Gradient)>,
pub(crate) pending_gradient_bbox_bake: Vec<(Vec<NodeId>, NodeId, graphic_types::migrations::legacy::Gradient)>,

// =============================================
// Fields omitted from the saved document format
Expand Down Expand Up @@ -2763,30 +2763,20 @@ impl DocumentMessageHandler {
let mut resulting_layers: Vec<NodeId> = Vec::new();

for layer in selected_layers {
let style = self.network_interface.document_metadata().layer_vector_data.get(&layer).map(|arc| arc.style.clone());
let Some(style) = style else {
let Some(vector_data) = self.network_interface.document_metadata().layer_vector_data.get(&layer) else {
resulting_layers.push(layer.to_node());
continue;
};
let stroke = vector_data.stroke.as_ref();

let fill_graphic_list = self.network_interface.document_metadata().layer_fill_attributes.get(&layer);
let stroke_graphic_list = self.network_interface.document_metadata().layer_stroke_attributes.get(&layer);

// `ATTR_FILL` is the source of truth when set; fall back to the legacy `style.fill` only when no attribute is present
let has_fill = if let Some(list) = fill_graphic_list {
is_paint_present(list)
} else {
!matches!(style.fill, Fill::None)
};
// `style.stroke` is `Some` whenever a `Stroke` node is in the chain, even with weight 0 or a transparent color.
// So `is_some()` would treat invisibly-stroked fill-only layers as having a stroke.
// `ATTR_STROKE` is the source of truth when set; fall back to `style.stroke.color` only when no attribute is present.
let stroke_visible = if let Some(list) = stroke_graphic_list {
list.element(0).is_some_and(|g| !g.is_fully_transparent())
} else {
style.stroke.as_ref().and_then(|s| s.color()).is_some_and(|c| c.a() != 0.)
};
let has_stroke = style.stroke.as_ref().is_some_and(|s| s.has_renderable_stroke()) && stroke_visible;
let has_fill = fill_graphic_list.is_some_and(|list| is_paint_present(list));
// `Vector.stroke` captures stroke geometry, even with weight 0 or transparent paint.
// So stroke visibility must be checked from `ATTR_STROKE`, the paint source of truth.
let stroke_visible = stroke_graphic_list.is_some_and(|list| list.element(0).is_some_and(|g| !g.is_fully_transparent()));
let has_stroke = stroke.as_ref().is_some_and(|s| s.has_renderable_stroke()) && stroke_visible;

// No stroke means there's nothing to solidify. Fill-only layers are already in the desired form, so skip.
if !has_stroke {
Expand Down Expand Up @@ -4017,7 +4007,7 @@ mod document_message_handler_tests {
#[test]
fn pending_gradient_bakes_round_trip_through_serialization() {
let document = DocumentMessageHandler {
pending_gradient_bbox_bake: vec![(vec![NodeId(7)], NodeId(42), Gradient::default())],
pending_gradient_bbox_bake: vec![(vec![NodeId(7)], NodeId(42), graphic_types::migrations::legacy::Gradient::default())],
..Default::default()
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,22 @@ use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Image;
use graphene_std::subpath::Subpath;
use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::style::{Fill, GradientSpreadMethod, GradientType, Stroke};
use graphene_std::vector::style::{GradientSpreadMethod, GradientType, Stroke};
use graphene_std::vector::{GradientStops, PointId, VectorModificationType};

#[impl_message(Message, DocumentMessage, GraphOperation)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum GraphOperationMessage {
FillSet {
FillColorSet {
layer: LayerNodeIdentifier,
fill: Fill,
color: Option<Color>,
},
FillGradientSet {
layer: LayerNodeIdentifier,
gradient: GradientStops,
gradient_type: GradientType,
spread_method: GradientSpreadMethod,
transform: DAffine2,
},
BlendingFillSet {
layer: LayerNodeIdentifier,
Expand All @@ -28,10 +35,9 @@ pub enum GraphOperationMessage {
layer: LayerNodeIdentifier,
stops: GradientStops,
},
GradientLineSet {
GradientTransformSet {
layer: LayerNodeIdentifier,
start: DVec2,
end: DVec2,
transform: DAffine2,
},
GradientTypeSet {
layer: LayerNodeIdentifier,
Expand All @@ -54,6 +60,7 @@ pub enum GraphOperationMessage {
},
StrokeSet {
layer: LayerNodeIdentifier,
color: Option<Color>,
stroke: Stroke,
},
TransformChange {
Expand Down
Loading
Loading