mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 01:08:12 +08:00
Remove all references to legacy layers (#1523)
* Remove visible field from LegacyLayer * Remove LegacyLayer wrapper around LegacyLayerType * Remove FolderLegacyLayer and LayerLegacyLayer wrappers around their data * Remove legacy layers
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
use crate::document_metadata::{is_artboard, DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::layers::folder_layer::FolderLegacyLayer;
|
||||
use crate::layers::layer_info::{LegacyLayer, LegacyLayerType};
|
||||
use crate::DocumentError;
|
||||
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeNetwork, NodeOutput};
|
||||
use graphene_core::renderer::ClickTarget;
|
||||
@@ -23,9 +20,6 @@ pub type LayerId = u64;
|
||||
pub struct Document {
|
||||
#[serde(default)]
|
||||
pub document_network: NodeNetwork,
|
||||
/// The root layer, usually a [FolderLegacyLayer](layers::folder_layer::FolderLegacyLayer) that contains all other [LegacyLayers](layers::layer_info::LegacyLayer).
|
||||
#[serde(skip)]
|
||||
pub root: LegacyLayer,
|
||||
/// The state_identifier serves to provide a way to uniquely identify a particular state that the document is in.
|
||||
/// This identifier is not a hash and is not guaranteed to be equal for equivalent documents.
|
||||
#[serde(skip)]
|
||||
@@ -43,11 +37,6 @@ impl PartialEq for Document {
|
||||
impl Default for Document {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
root: LegacyLayer {
|
||||
name: None,
|
||||
visible: true,
|
||||
data: LegacyLayerType::Folder(FolderLegacyLayer::default()),
|
||||
},
|
||||
state_identifier: DefaultHasher::new(),
|
||||
document_network: {
|
||||
use graph_craft::document::{value::TaggedValue, NodeInput};
|
||||
@@ -156,100 +145,4 @@ impl Document {
|
||||
pub fn current_state_identifier(&self) -> u64 {
|
||||
self.state_identifier.finish()
|
||||
}
|
||||
|
||||
/// Returns a reference to the requested folder. Fails if the path does not exist,
|
||||
/// or if the requested layer is not of type folder.
|
||||
pub fn folder(&self, path: impl AsRef<[LayerId]>) -> Result<&FolderLegacyLayer, DocumentError> {
|
||||
let mut root = &self.root;
|
||||
for id in path.as_ref() {
|
||||
root = root.as_folder()?.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.as_ref().into()))?;
|
||||
}
|
||||
root.as_folder()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the requested folder. Fails if the path does not exist,
|
||||
/// or if the requested layer is not of type folder.
|
||||
fn folder_mut(&mut self, path: &[LayerId]) -> Result<&mut FolderLegacyLayer, DocumentError> {
|
||||
let mut root = &mut self.root;
|
||||
for id in path {
|
||||
root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
|
||||
}
|
||||
root.as_folder_mut()
|
||||
}
|
||||
|
||||
/// Returns a reference to the layer or folder at the path.
|
||||
pub fn layer(&self, path: &[LayerId]) -> Result<&LegacyLayer, DocumentError> {
|
||||
if path.is_empty() {
|
||||
return Ok(&self.root);
|
||||
}
|
||||
let (path, id) = split_path(path)?;
|
||||
self.folder(path)?.layer(id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the layer or folder at the path.
|
||||
pub fn layer_mut(&mut self, path: &[LayerId]) -> Result<&mut LegacyLayer, DocumentError> {
|
||||
if path.is_empty() {
|
||||
return Ok(&mut self.root);
|
||||
}
|
||||
let (path, id) = split_path(path)?;
|
||||
self.folder_mut(path)?.layer_mut(id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))
|
||||
}
|
||||
|
||||
pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> &'a [LayerId] {
|
||||
layers.reduce(|a, b| &a[..a.iter().zip(b.iter()).take_while(|&(a, b)| a == b).count()]).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns the shallowest folder given the selection, even if the selection doesn't contain any folders
|
||||
pub fn shallowest_common_folder<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> Result<&'a [LayerId], DocumentError> {
|
||||
let common_prefix_of_path = self.common_layer_path_prefix(layers);
|
||||
|
||||
Ok(match self.layer(common_prefix_of_path)?.data {
|
||||
LegacyLayerType::Folder(_) => common_prefix_of_path,
|
||||
_ => &common_prefix_of_path[..common_prefix_of_path.len() - 1],
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns all layers that are not contained in any other of the given folders
|
||||
/// Takes and Iterator over &[LayerId] or &Vec<LayerId>.
|
||||
pub fn shallowest_unique_layers<'a, T>(layers: impl Iterator<Item = T>) -> Vec<T>
|
||||
where
|
||||
T: AsRef<[LayerId]> + std::cmp::Ord + 'a,
|
||||
{
|
||||
let mut sorted_layers: Vec<_> = layers.collect();
|
||||
sorted_layers.sort();
|
||||
// Sorting here creates groups of similar UUID paths
|
||||
sorted_layers.dedup_by(|a, b| a.as_ref().starts_with(b.as_ref()));
|
||||
sorted_layers
|
||||
}
|
||||
|
||||
/// Given a path to a layer, returns a vector of the indices in the layer tree
|
||||
/// These indices can be used to order a list of layers
|
||||
pub fn indices_for_path(&self, path: &[LayerId]) -> Result<Vec<usize>, DocumentError> {
|
||||
let mut root = self.root.as_folder()?;
|
||||
let mut indices = vec![];
|
||||
let (path, layer_id) = split_path(path)?;
|
||||
|
||||
// TODO: appears to be n^2? should we maintain a lookup table?
|
||||
for id in path {
|
||||
let pos = root.layer_ids.iter().position(|x| *x == *id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
|
||||
indices.push(pos);
|
||||
root = match root.layer(*id) {
|
||||
Some(LegacyLayer {
|
||||
data: LegacyLayerType::Folder(folder),
|
||||
..
|
||||
}) => Some(folder),
|
||||
_ => None,
|
||||
}
|
||||
.ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
|
||||
}
|
||||
|
||||
indices.push(root.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?);
|
||||
|
||||
Ok(indices)
|
||||
}
|
||||
}
|
||||
|
||||
fn split_path(path: &[LayerId]) -> Result<(&[LayerId], LayerId), DocumentError> {
|
||||
let (id, path) = path.split_last().ok_or(DocumentError::InvalidPath)?;
|
||||
Ok((path, *id))
|
||||
}
|
||||
|
||||
@@ -154,24 +154,20 @@ impl DocumentMetadata {
|
||||
|
||||
// selected layer modifications
|
||||
impl DocumentMetadata {
|
||||
#[must_use]
|
||||
pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) -> SelectionChanged {
|
||||
pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) {
|
||||
self.selected_nodes.retain(f);
|
||||
SelectionChanged
|
||||
}
|
||||
#[must_use]
|
||||
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) -> SelectionChanged {
|
||||
|
||||
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) {
|
||||
self.selected_nodes = new;
|
||||
SelectionChanged
|
||||
}
|
||||
#[must_use]
|
||||
pub fn add_selected_nodes(&mut self, iter: impl IntoIterator<Item = NodeId>) -> SelectionChanged {
|
||||
|
||||
pub fn add_selected_nodes(&mut self, iter: impl IntoIterator<Item = NodeId>) {
|
||||
self.selected_nodes.extend(iter);
|
||||
SelectionChanged
|
||||
}
|
||||
#[must_use]
|
||||
pub fn clear_selected_nodes(&mut self) -> SelectionChanged {
|
||||
self.set_selected_nodes(Vec::new())
|
||||
|
||||
pub fn clear_selected_nodes(&mut self) {
|
||||
self.set_selected_nodes(Vec::new());
|
||||
}
|
||||
|
||||
/// Loads the structure of layer nodes from a node graph.
|
||||
@@ -374,8 +370,8 @@ impl LayerNodeIdentifier {
|
||||
#[track_caller]
|
||||
pub fn new(node_id: NodeId, network: &NodeNetwork) -> Self {
|
||||
debug_assert!(
|
||||
is_layer_node(node_id, network),
|
||||
"Layer identifier constructed from non layer node {node_id}: {:#?}",
|
||||
node_id == LayerNodeIdentifier::ROOT.to_node() || network.nodes.get(&node_id).is_some_and(|node| node.is_layer()),
|
||||
"Layer identifier constructed from non-layer node {node_id}: {:#?}",
|
||||
network.nodes.get(&node_id)
|
||||
);
|
||||
Self::new_unchecked(node_id)
|
||||
@@ -633,10 +629,6 @@ pub struct NodeRelations {
|
||||
last_child: Option<LayerNodeIdentifier>,
|
||||
}
|
||||
|
||||
fn is_layer_node(node: NodeId, network: &NodeNetwork) -> bool {
|
||||
node == LayerNodeIdentifier::ROOT.to_node() || network.nodes.get(&node).is_some_and(|node| node.is_layer())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tree() {
|
||||
let mut document_metadata = DocumentMetadata::default();
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
use super::layer_info::LegacyLayer;
|
||||
use crate::document::LayerId;
|
||||
use crate::DocumentError;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A layer that encapsulates other layers, including potentially more folders.
|
||||
/// The contained layers are rendered in the same order they are stored.
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Default)]
|
||||
pub struct FolderLegacyLayer {
|
||||
/// The IDs of the [Layer]s contained within the Folder
|
||||
pub layer_ids: Vec<LayerId>,
|
||||
/// The [Layer]s contained in the folder
|
||||
pub layers: Vec<LegacyLayer>,
|
||||
}
|
||||
|
||||
impl FolderLegacyLayer {
|
||||
pub fn layer(&self, layer_id: LayerId) -> Option<&LegacyLayer> {
|
||||
let index = self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into())).ok()?;
|
||||
Some(&self.layers[index])
|
||||
}
|
||||
|
||||
pub fn layer_mut(&mut self, layer_id: LayerId) -> Option<&mut LegacyLayer> {
|
||||
let index = self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into())).ok()?;
|
||||
Some(&mut self.layers[index])
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
use super::folder_layer::FolderLegacyLayer;
|
||||
use super::layer_layer::LayerLegacyLayer;
|
||||
use crate::DocumentError;
|
||||
|
||||
use core::fmt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ===============
|
||||
// LegacyLayerType
|
||||
// ===============
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
/// Represents different types of layers.
|
||||
pub enum LegacyLayerType {
|
||||
/// A layer that wraps a [FolderLegacyLayer] struct.
|
||||
Folder(FolderLegacyLayer),
|
||||
/// A layer that wraps an [LayerLegacyLayer] struct.
|
||||
Layer(LayerLegacyLayer),
|
||||
}
|
||||
|
||||
impl Default for LegacyLayerType {
|
||||
fn default() -> Self {
|
||||
LegacyLayerType::Layer(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// LayerDataTypeDiscriminant
|
||||
// =========================
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, specta::Type)]
|
||||
pub enum LayerDataTypeDiscriminant {
|
||||
Folder,
|
||||
Layer,
|
||||
}
|
||||
|
||||
impl fmt::Display for LayerDataTypeDiscriminant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
LayerDataTypeDiscriminant::Folder => write!(f, "Folder"),
|
||||
LayerDataTypeDiscriminant::Layer => write!(f, "Layer"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&LegacyLayerType> for LayerDataTypeDiscriminant {
|
||||
fn from(data: &LegacyLayerType) -> Self {
|
||||
use LegacyLayerType::*;
|
||||
|
||||
match data {
|
||||
Folder(_) => LayerDataTypeDiscriminant::Folder,
|
||||
Layer(_) => LayerDataTypeDiscriminant::Layer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========
|
||||
// LegacyLayer
|
||||
// ===========
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct LegacyLayer {
|
||||
/// The user-given name of the layer.
|
||||
pub name: Option<String>,
|
||||
/// Whether the layer is currently visible or hidden.
|
||||
pub visible: bool,
|
||||
/// The type of layer, such as folder or shape.
|
||||
pub data: LegacyLayerType,
|
||||
}
|
||||
|
||||
impl LegacyLayer {
|
||||
/// Iterate over the layers encapsulated by this layer.
|
||||
/// If the [Layer type](Layer::data) is not a folder, the only item in the iterator will be the layer itself.
|
||||
/// If the [Layer type](Layer::data) wraps a [Folder](LegacyLayerType::Folder), the iterator will recursively yield all the layers contained in the folder as well as potential sub-folders.
|
||||
pub fn iter(&self) -> LayerIter<'_> {
|
||||
LayerIter { stack: vec![self] }
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the Folder wrapped by the layer.
|
||||
/// This operation will fail if the [Layer type](Layer::data) is not `LegacyLayerType::Folder`.
|
||||
pub fn as_folder_mut(&mut self) -> Result<&mut FolderLegacyLayer, DocumentError> {
|
||||
match &mut self.data {
|
||||
LegacyLayerType::Folder(f) => Ok(f),
|
||||
_ => Err(DocumentError::NotFolder),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the Folder wrapped by the layer.
|
||||
/// This operation will fail if the [Layer type](Layer::data) is not `LegacyLayerType::Folder`.
|
||||
pub fn as_folder(&self) -> Result<&FolderLegacyLayer, DocumentError> {
|
||||
match &self.data {
|
||||
LegacyLayerType::Folder(f) => Ok(f),
|
||||
_ => Err(DocumentError::NotFolder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========
|
||||
// LayerIter
|
||||
// =========
|
||||
|
||||
/// An iterator over the layers encapsulated by this layer.
|
||||
/// See [Layer::iter] for more information.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LayerIter<'a> {
|
||||
pub stack: Vec<&'a LegacyLayer>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for LayerIter<'a> {
|
||||
type Item = &'a LegacyLayer;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.stack.pop() {
|
||||
Some(layer) => {
|
||||
if let LegacyLayerType::Folder(folder) = &layer.data {
|
||||
let layers = folder.layers.as_slice();
|
||||
self.stack.extend(layers);
|
||||
};
|
||||
Some(layer)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ================
|
||||
// LayerLegacyLayer
|
||||
// ================
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
||||
pub struct LayerLegacyLayer {
|
||||
/// The document node network that this layer contains
|
||||
pub network: graph_craft::document::NodeNetwork,
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//! # Layers
|
||||
//! A document consists of a set of [Layers](layer_info::Layer).
|
||||
//! Layers allow the user to mutate part of the document while leaving the rest unchanged.
|
||||
//! There are currently these different types of layers:
|
||||
//! * [Folder layers](folder_layer::FolderLegacyLayer), which encapsulate sub-layers
|
||||
//! * [Layer layers](layer_layer::LayerLegacyLayer), which contain a node graph layer
|
||||
//!
|
||||
//! Refer to the module-level documentation for detailed information on each layer.
|
||||
//!
|
||||
//! ## Overlapping layers
|
||||
//! Layers are rendered on top of each other.
|
||||
//! When different layers overlap, they are blended together according to the [BlendMode](blend_mode::BlendMode)
|
||||
//! using the CSS [`mix-blend-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode) property and the layer opacity.
|
||||
|
||||
/// Contains the [FolderLegacyLayer](folder_layer::FolderLegacyLayer) type that encapsulates other layers, including more folders.
|
||||
pub mod folder_layer;
|
||||
/// Contains the base [Layer](layer_info::Layer) type, an abstraction over the different types of layers.
|
||||
pub mod layer_info;
|
||||
/// Contains the [LayerLegacyLayer](nodegraph_layer::LayerLegacyLayer) type that contains a node graph.
|
||||
pub mod layer_layer;
|
||||
@@ -1,16 +1,2 @@
|
||||
// `macro_use` puts the log macros (`error!`, `warn!`, `debug!`, `info!` and `trace!`) in scope for the crate
|
||||
// #[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod document;
|
||||
pub mod document_metadata;
|
||||
pub mod layers;
|
||||
|
||||
/// A set of different errors that can occur when using this crate.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum DocumentError {
|
||||
LayerNotFound(Vec<document::LayerId>),
|
||||
InvalidPath,
|
||||
NotFolder,
|
||||
InvalidFile(String),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user