mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Store paint attributes in the paint markers' owned optional form
This commit is contained in:
@@ -206,13 +206,9 @@ pub fn graphic_list_at<'a>(list: &'a List<Vector>, index: usize, attribute: &str
|
||||
.filter(|graphic_list| is_paint_present(graphic_list))
|
||||
}
|
||||
|
||||
/// The paint attribute's list, in either transitional storage form: the
|
||||
/// canonical `List<Graphic>` value, or the fill marker's owned
|
||||
/// `Option<List<Graphic>>` clone as the render bridge copies it.
|
||||
/// The paint attribute's list. Storage is the paint marker's owned
|
||||
/// `Option<List<Graphic>>` form, which every writer produces.
|
||||
fn paint_at<'a, T>(list: &'a List<T>, index: usize, attribute: &str) -> Option<&'a List<Graphic>> {
|
||||
if let Some(graphic_list) = list.attribute::<List<Graphic>>(attribute, index) {
|
||||
return Some(graphic_list);
|
||||
}
|
||||
list.attribute::<Option<List<Graphic>>>(attribute, index).and_then(|optional| optional.as_ref())
|
||||
}
|
||||
|
||||
@@ -222,14 +218,14 @@ pub fn has_paint_at(list: &List<Vector>, index: usize, attribute: &str) -> bool
|
||||
paint_at(list, index, attribute).is_some_and(is_paint_present)
|
||||
}
|
||||
|
||||
/// Stores a paint attribute in its canonical `List<Graphic>` form, the only representation paint readers accept.
|
||||
/// Stores a paint attribute in the paint marker's owned form, the only representation paint readers accept.
|
||||
pub fn set_paint_attribute(attributes: &mut ItemAttributeValues, key: &str, paint: impl IntoGraphicList) {
|
||||
attributes.insert(key, paint.into_graphic_list());
|
||||
attributes.insert(key, Some(paint.into_graphic_list()));
|
||||
}
|
||||
|
||||
/// Stores a paint attribute at a list index in its canonical `List<Graphic>` form, the only representation paint readers accept.
|
||||
/// Stores a paint attribute at a list index in the paint marker's owned form, the only representation paint readers accept.
|
||||
pub fn set_paint_attribute_at<T>(list: &mut List<T>, index: usize, key: &str, paint: impl IntoGraphicList) {
|
||||
list.set_attribute(key, index, paint.into_graphic_list());
|
||||
list.set_attribute(key, index, Some(paint.into_graphic_list()));
|
||||
}
|
||||
|
||||
/// Bake the provided transform into the per-item transforms of the paint graphics stored under the
|
||||
@@ -256,9 +252,7 @@ pub fn bake_paint_transforms(attributes: &mut ItemAttributeValues, transform: DA
|
||||
}
|
||||
|
||||
for paint_key in [ATTR_FILL, ATTR_STROKE] {
|
||||
if let Some(graphics) = attributes.get_mut::<List<Graphic>>(paint_key) {
|
||||
bake_graphic_paint_transform(graphics, transform);
|
||||
} else if let Some(Some(graphics)) = attributes.get_mut::<Option<List<Graphic>>>(paint_key) {
|
||||
if let Some(Some(graphics)) = attributes.get_mut::<Option<List<Graphic>>>(paint_key) {
|
||||
bake_graphic_paint_transform(graphics, transform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1626,7 +1626,7 @@ impl Render for List<Vector> {
|
||||
// If this item carries a snapshot of upstream graphic content (e.g. it was produced by Boolean Operation,
|
||||
// Flatten Path, Morph, or any other destructive merge), recurse into that snapshot so the editor can
|
||||
// surface the original child layers' click targets.
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<List<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, index);
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Option<List<Graphic>>>(ATTR_EDITOR_MERGED_LAYERS, index).unwrap_or_default();
|
||||
if !upstream_nested_layers.is_empty() {
|
||||
let mut upstream_footprint = footprint;
|
||||
upstream_footprint.transform *= transform;
|
||||
@@ -1884,7 +1884,7 @@ impl Render for List<Raster<CPU>> {
|
||||
// The snapshot was captured before Rasterize shifted its input transforms to align with the rasterization
|
||||
// area, so the children are already in the coordinate space matching `footprint` here — we must NOT
|
||||
// multiply in `transform` (which is the rasterization area, not a layer-stack transform).
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<List<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, 0);
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Option<List<Graphic>>>(ATTR_EDITOR_MERGED_LAYERS, 0).unwrap_or_default();
|
||||
if !upstream_nested_layers.is_empty() {
|
||||
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
@@ -1979,7 +1979,7 @@ impl Render for List<Raster<GPU>> {
|
||||
// The snapshot was captured before Rasterize shifted its input transforms to align with the rasterization
|
||||
// area, so the children are already in the coordinate space matching `footprint` here — we must NOT
|
||||
// multiply in `transform` (which is the rasterization area, not a layer-stack transform).
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<List<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, 0);
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Option<List<Graphic>>>(ATTR_EDITOR_MERGED_LAYERS, 0).unwrap_or_default();
|
||||
if !upstream_nested_layers.is_empty() {
|
||||
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
|
||||
@@ -224,8 +224,8 @@ where
|
||||
if mirrored {
|
||||
transform = reflected_transform.expect("a mirrored lane exists only under a reflection") * transform;
|
||||
}
|
||||
let fill = park_paint(legacy.attribute::<List<Graphic>>(graphic_types::ATTR_FILL, source).cloned())?;
|
||||
let stroke = park_paint(legacy.attribute::<List<Graphic>>(graphic_types::ATTR_STROKE, source).cloned())?;
|
||||
let fill = park_paint(legacy.attribute::<Option<List<Graphic>>>(graphic_types::ATTR_FILL, source).cloned().flatten())?;
|
||||
let stroke = park_paint(legacy.attribute::<Option<List<Graphic>>>(graphic_types::ATTR_STROKE, source).cloned().flatten())?;
|
||||
let layer_path: Vec<NodeId> = legacy.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, source).map(|path| path.clone()).unwrap_or_default();
|
||||
let layer_path = arena.alloc(layer_path).ok_or_else(exhausted)?.0;
|
||||
|
||||
@@ -624,7 +624,7 @@ pub fn flatten_vector<T: IntoGraphicList>(_: impl Ctx, #[implementations(List<Gr
|
||||
}
|
||||
}
|
||||
|
||||
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_list);
|
||||
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, Some(graphic_list));
|
||||
}
|
||||
|
||||
output
|
||||
|
||||
@@ -267,7 +267,7 @@ where
|
||||
List::new_from_item(
|
||||
Item::new_from_element(Raster::new_cpu(image))
|
||||
.with_attribute(ATTR_TRANSFORM, footprint.transform)
|
||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, upstream_graphic_list),
|
||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, Some(upstream_graphic_list)),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1427,13 +1427,13 @@ fn solidify_stroke_core(graphic_list: List<Graphic>) -> List<Vector> {
|
||||
vector.stroke = None;
|
||||
let mut fill_attributes = attributes.clone();
|
||||
// No stroke remains on the fill row
|
||||
fill_attributes.remove::<List<Graphic>>(ATTR_STROKE);
|
||||
fill_attributes.remove::<Option<List<Graphic>>>(ATTR_STROKE);
|
||||
Item::from_parts(vector, fill_attributes)
|
||||
});
|
||||
|
||||
let mut stroke_attributes = attributes;
|
||||
// Drop the original fill and use the stroke paint to fill the outlined stroke
|
||||
stroke_attributes.remove::<List<Graphic>>(ATTR_FILL);
|
||||
stroke_attributes.remove::<Option<List<Graphic>>>(ATTR_FILL);
|
||||
stroke_attributes.rename(ATTR_STROKE, ATTR_FILL);
|
||||
|
||||
let stroke_row = Item::from_parts(solidified_stroke, stroke_attributes);
|
||||
@@ -1461,7 +1461,7 @@ fn solidify_stroke_core(graphic_list: List<Graphic>) -> List<Vector> {
|
||||
}
|
||||
}
|
||||
|
||||
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_list);
|
||||
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, Some(graphic_list));
|
||||
}
|
||||
|
||||
output
|
||||
@@ -1523,12 +1523,21 @@ fn emit_legacy_lane<'e>(
|
||||
};
|
||||
|
||||
let element = output.element(lane).cloned().unwrap_or_default();
|
||||
let fill = output.attribute::<List<Graphic>>(ATTR_FILL, lane).map(|paint| park_paint(arena, paint.clone())).transpose()?;
|
||||
let stroke = output.attribute::<List<Graphic>>(ATTR_STROKE, lane).map(|paint| park_paint(arena, paint.clone())).transpose()?;
|
||||
let fill = output
|
||||
.attribute::<Option<List<Graphic>>>(ATTR_FILL, lane)
|
||||
.and_then(|paint| paint.as_ref())
|
||||
.map(|paint| park_paint(arena, paint.clone()))
|
||||
.transpose()?;
|
||||
let stroke = output
|
||||
.attribute::<Option<List<Graphic>>>(ATTR_STROKE, lane)
|
||||
.and_then(|paint| paint.as_ref())
|
||||
.map(|paint| park_paint(arena, paint.clone()))
|
||||
.transpose()?;
|
||||
let layer_path: Vec<NodeId> = output.attribute::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, lane).cloned().unwrap_or_default();
|
||||
let layer_path = arena.alloc(layer_path).ok_or_else(exhausted)?.0;
|
||||
let merged_layers = output
|
||||
.attribute::<List<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, lane)
|
||||
.attribute::<Option<List<Graphic>>>(ATTR_EDITOR_MERGED_LAYERS, lane)
|
||||
.and_then(|layers| layers.as_ref())
|
||||
.map(|layers| arena.alloc(layers.clone()).ok_or_else(exhausted).map(|(parked, _)| parked))
|
||||
.transpose()?;
|
||||
|
||||
@@ -1813,8 +1822,16 @@ fn flatten_path_core<'e>(
|
||||
bake_paint_transforms(&mut attributes, source_transform);
|
||||
|
||||
let carrier = List::new_from_item(Item::from_parts(Vector::default(), attributes));
|
||||
fill = carrier.attribute::<List<Graphic>>(ATTR_FILL, 0).map(|paint| park_paint(arena, paint.clone())).transpose()?;
|
||||
stroke = carrier.attribute::<List<Graphic>>(ATTR_STROKE, 0).map(|paint| park_paint(arena, paint.clone())).transpose()?;
|
||||
fill = carrier
|
||||
.attribute::<Option<List<Graphic>>>(ATTR_FILL, 0)
|
||||
.and_then(|paint| paint.as_ref())
|
||||
.map(|paint| park_paint(arena, paint.clone()))
|
||||
.transpose()?;
|
||||
stroke = carrier
|
||||
.attribute::<Option<List<Graphic>>>(ATTR_STROKE, 0)
|
||||
.and_then(|paint| paint.as_ref())
|
||||
.map(|paint| park_paint(arena, paint.clone()))
|
||||
.transpose()?;
|
||||
|
||||
// Adopt the last input item's layer so the editor can also bucket clicks under a contributing child layer
|
||||
layer_path = flattened.attribute_cloned_or_default::<Vec<NodeId>>(ATTR_EDITOR_LAYER_PATH, primary);
|
||||
@@ -2995,7 +3012,7 @@ fn morph_core(content: List<Graphic>, progression: f64, reverse: bool, distribut
|
||||
|
||||
let mut attributes = content.clone_item_attributes(endpoint_index);
|
||||
attributes.insert(ATTR_TRANSFORM, lerped_transform);
|
||||
attributes.insert(ATTR_EDITOR_MERGED_LAYERS, graphic_list_content);
|
||||
attributes.insert(ATTR_EDITOR_MERGED_LAYERS, Some(graphic_list_content));
|
||||
|
||||
return List::new_from_item(Item::from_parts(endpoint_element.clone(), attributes));
|
||||
}
|
||||
@@ -3186,13 +3203,13 @@ fn morph_core(content: List<Graphic>, progression: f64, reverse: bool, distribut
|
||||
.with_attribute(ATTR_OPACITY_FILL, lerped_fill)
|
||||
.with_attribute(ATTR_CLIPPING_MASK, lerped_clip)
|
||||
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer_path)
|
||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, graphic_list_content);
|
||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, Some(graphic_list_content));
|
||||
|
||||
if let Some(fill) = fill_paint {
|
||||
item.set_attribute(ATTR_FILL, fill);
|
||||
item.set_attribute(ATTR_FILL, Some(fill));
|
||||
}
|
||||
if let Some(stroke) = stroke_paint {
|
||||
item.set_attribute(ATTR_STROKE, stroke);
|
||||
item.set_attribute(ATTR_STROKE, Some(stroke));
|
||||
}
|
||||
|
||||
List::new_from_item(item)
|
||||
@@ -3885,10 +3902,10 @@ mod test {
|
||||
|
||||
let item_a = Item::new_from_element(rect())
|
||||
.with_attribute(ATTR_TRANSFORM, DAffine2::IDENTITY)
|
||||
.with_attribute(ATTR_FILL, List::new_from_element(Color::RED).into_graphic_list());
|
||||
.with_attribute(ATTR_FILL, Some(List::new_from_element(Color::RED).into_graphic_list()));
|
||||
let item_b = Item::new_from_element(rect())
|
||||
.with_attribute(ATTR_TRANSFORM, DAffine2::from_translation((-100., -100.).into()))
|
||||
.with_attribute(ATTR_FILL, List::new_from_element(Color::BLUE).into_graphic_list());
|
||||
.with_attribute(ATTR_FILL, Some(List::new_from_element(Color::BLUE).into_graphic_list()));
|
||||
|
||||
let mut content = List::new_from_item(item_a);
|
||||
content.push(item_b);
|
||||
|
||||
Reference in New Issue
Block a user