mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Replace Table<Table<Graphic>> with Table<Artboard> where Artboard is a type boundary newtype (#4093)
Replace Table<Table<Graphic>> with Table<Artboard> with Artboard as a type boundary newtype
This commit is contained in:
@@ -1,25 +1,71 @@
|
||||
use crate::graphic::Graphic;
|
||||
use core_types::blending::BlendMode;
|
||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use core_types::graphene_hash::CacheHash;
|
||||
use core_types::render_complexity::RenderComplexity;
|
||||
use core_types::table::{Table, TableRow};
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_BACKGROUND, ATTR_CLIP, ATTR_DIMENSIONS, ATTR_LOCATION, Color};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, IVec2};
|
||||
|
||||
// An artboard table is `Table<Table<Graphic>>`: each row's element is the artboard's content
|
||||
// (a `Table<Graphic>`), with the artboard's metadata stored alongside on the row as attributes
|
||||
// (see `ATTR_LOCATION`, `ATTR_DIMENSIONS`, `ATTR_BACKGROUND`, `ATTR_CLIP`).
|
||||
//
|
||||
// The artboard's user-visible name is the parent layer's display name (resolved live from the
|
||||
// network interface via the row's `ATTR_EDITOR_LAYER_PATH` attribute) — not stored here, so it
|
||||
// can never go stale.
|
||||
//
|
||||
// These metadata attributes are populated at runtime by the `Artboard` proto node from its
|
||||
// inputs and therefore aren't persisted in document files; the proto node's input values are
|
||||
// what get serialized.
|
||||
/// Nominal wrapper around `Table<Graphic>` representing a single artboard's content.
|
||||
///
|
||||
/// Per-artboard metadata (location, dimensions, background, clip) lives as row attributes on the
|
||||
/// enclosing `Table<Artboard>`, not as fields here. This keeps `Artboard` a pure type-system boundary
|
||||
/// that prevents arbitrary `Table<Table<...<Graphic>>>` nesting.
|
||||
#[derive(Clone, Debug, Default, CacheHash, PartialEq, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Artboard(Table<Graphic>);
|
||||
|
||||
impl Artboard {
|
||||
pub fn new(content: Table<Graphic>) -> Self {
|
||||
Self(content)
|
||||
}
|
||||
|
||||
pub fn as_graphic_table(&self) -> &Table<Graphic> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn as_graphic_table_mut(&mut self) -> &mut Table<Graphic> {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
pub fn into_graphic_table(self) -> Table<Graphic> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Table<Graphic>> for Artboard {
|
||||
fn from(content: Table<Graphic>) -> Self {
|
||||
Self(content)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Artboard> for Table<Graphic> {
|
||||
fn from(artboard: Artboard) -> Self {
|
||||
artboard.0
|
||||
}
|
||||
}
|
||||
|
||||
impl BoundingBox for Artboard {
|
||||
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
|
||||
self.0.bounding_box(transform, include_stroke)
|
||||
}
|
||||
|
||||
fn thumbnail_bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
|
||||
self.0.thumbnail_bounding_box(transform, include_stroke)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Artboard {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.0.render_complexity()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Table<Graphic>>, D::Error> {
|
||||
pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Artboard>, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Mirrors the removed `AlphaBlending` struct for legacy document deserialization.
|
||||
@@ -33,9 +79,7 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
|
||||
pub clip: bool,
|
||||
}
|
||||
|
||||
/// Pre-migration shape of the artboard's stored data: the struct that used to live as the element
|
||||
/// of `Table<Artboard>`. Kept as a private type so we can deserialize legacy documents into the new
|
||||
/// `Table<Table<Graphic>>` (element = `content`, other fields → row attributes).
|
||||
/// Legacy artboard struct shape, kept for deserializing old documents into `Table<Artboard>`.
|
||||
#[derive(Clone, Debug, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct LegacyArtboard {
|
||||
@@ -68,14 +112,14 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
|
||||
ArtboardGroup(LegacyArtboardGroup),
|
||||
OldArtboardTable(OldTable<LegacyArtboard>),
|
||||
LegacyArtboardTable(Table<LegacyArtboard>),
|
||||
// Note: this variant must come last so older formats above are tried first; an empty
|
||||
// `Table<Table<Graphic>>` would otherwise match (since `Table<T>` has the same shell across `T`).
|
||||
ArtboardTable(Table<Table<Graphic>>),
|
||||
// NOTE: Must come last so older tagged formats above are tried first.
|
||||
// Also covers the intermediate `Table<Table<Graphic>>` shape since `Artboard` deserializes transparently.
|
||||
ArtboardTable(Table<Artboard>),
|
||||
}
|
||||
|
||||
fn legacy_to_row(legacy: LegacyArtboard) -> TableRow<Table<Graphic>> {
|
||||
// Legacy `label` field is dropped — the artboard's name now comes from its parent layer's display name.
|
||||
TableRow::new_from_element(legacy.content)
|
||||
fn legacy_to_row(legacy: LegacyArtboard) -> TableRow<Artboard> {
|
||||
// Legacy `label` field is dropped (the artboard's name comes from its parent layer's display name)
|
||||
TableRow::new_from_element(Artboard::new(legacy.content))
|
||||
.with_attribute(ATTR_LOCATION, legacy.location.as_dvec2())
|
||||
.with_attribute(ATTR_DIMENSIONS, legacy.dimensions.as_dvec2())
|
||||
.with_attribute(ATTR_BACKGROUND, legacy.background)
|
||||
|
||||
@@ -7,6 +7,7 @@ pub use raster_types;
|
||||
pub use vector_types;
|
||||
|
||||
// Re-export commonly used types at the crate root
|
||||
pub use artboard::Artboard;
|
||||
pub use graphic::{Graphic, IntoGraphicTable, TryFromGraphic, Vector};
|
||||
|
||||
pub mod migrations {
|
||||
|
||||
@@ -17,13 +17,12 @@ use core_types::{
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_hash::CacheHashWrapper;
|
||||
use graphic_types::Graphic;
|
||||
use graphic_types::Vector;
|
||||
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster};
|
||||
use graphic_types::vector_types::gradient::{GradientStops, GradientType};
|
||||
use graphic_types::vector_types::subpath::Subpath;
|
||||
use graphic_types::vector_types::vector::click_target::{ClickTarget, FreePoint};
|
||||
use graphic_types::vector_types::vector::style::{Fill, PaintOrder, RenderMode, Stroke, StrokeAlign};
|
||||
use graphic_types::{Artboard, Graphic, Vector};
|
||||
use kurbo::{Affine, Cap, Join, Shape};
|
||||
use num_traits::Zero;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -514,8 +513,8 @@ impl Render for Graphic {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the artboard metadata for the row at `index` from a `Table<Table<Graphic>>` of artboards.
|
||||
fn read_artboard_attributes(table: &Table<Table<Graphic>>, index: usize) -> (DVec2, DVec2, Color, bool) {
|
||||
/// Reads the artboard metadata for the row at `index` from a `Table<Artboard>`.
|
||||
fn read_artboard_attributes(table: &Table<Artboard>, index: usize) -> (DVec2, DVec2, Color, bool) {
|
||||
let location: DVec2 = table.attribute_cloned_or_default(ATTR_LOCATION, index);
|
||||
let dimensions: DVec2 = table.attribute_cloned_or_default(ATTR_DIMENSIONS, index);
|
||||
let background: Color = table.attribute_cloned_or_default(ATTR_BACKGROUND, index);
|
||||
@@ -523,10 +522,10 @@ fn read_artboard_attributes(table: &Table<Table<Graphic>>, index: usize) -> (DVe
|
||||
(location, dimensions, background, clip)
|
||||
}
|
||||
|
||||
impl Render for Table<Table<Graphic>> {
|
||||
impl Render for Table<Artboard> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
for index in 0..self.len() {
|
||||
let Some(content) = self.element(index) else { continue };
|
||||
let Some(content) = self.element(index).map(Artboard::as_graphic_table) else { continue };
|
||||
let (location, dimensions, background, clip) = read_artboard_attributes(self, index);
|
||||
|
||||
let x = location.x.min(location.x + dimensions.x);
|
||||
@@ -584,7 +583,7 @@ impl Render for Table<Table<Graphic>> {
|
||||
use vello::peniko;
|
||||
|
||||
for index in 0..self.len() {
|
||||
let Some(content) = self.element(index) else { continue };
|
||||
let Some(content) = self.element(index).map(Artboard::as_graphic_table) else { continue };
|
||||
let (location, dimensions, background, clip) = read_artboard_attributes(self, index);
|
||||
|
||||
let [a, b] = [location, location + dimensions];
|
||||
@@ -614,7 +613,7 @@ impl Render for Table<Table<Graphic>> {
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option<NodeId>) {
|
||||
for index in 0..self.len() {
|
||||
let Some(content) = self.element(index) else { continue };
|
||||
let Some(content) = self.element(index).map(Artboard::as_graphic_table) else { continue };
|
||||
let (location, dimensions, _background, clip) = read_artboard_attributes(self, index);
|
||||
|
||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||
|
||||
Reference in New Issue
Block a user