mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Copy and paste layers MVP (#220)
* Initial implementation of copy and paste for layers * Sort layers on copy and add tests * Fix logger init for test * Fix `copy_paste_deleted_layers` test * Readd erroneously removed svg * Make Layer serializable and cleanup * Add test for copy and pasting folders * Cleanup * Rename left_mouseup * Cleanup * Add length check to test * Fix typo * Make mouseup, mousedown more consistent
This commit is contained in:
committed by
Keavon Chambers
parent
1d2bb6fb2b
commit
94c42ff5d7
@@ -133,6 +133,30 @@ impl Document {
|
||||
self.folder(path)?.layer(id).ok_or(DocumentError::LayerNotFound)
|
||||
}
|
||||
|
||||
/// 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, mut path: &[LayerId]) -> Result<Vec<usize>, DocumentError> {
|
||||
let mut root = if self.is_mounted(self.work_mount_path.as_slice(), path) {
|
||||
path = &path[self.work_mount_path.len()..];
|
||||
&self.work
|
||||
} else {
|
||||
&self.root
|
||||
}
|
||||
.as_folder()?;
|
||||
let mut indices = vec![];
|
||||
let (path, layer_id) = split_path(path)?;
|
||||
|
||||
for id in path {
|
||||
let pos = root.layer_ids.iter().position(|x| *x == *id).ok_or(DocumentError::LayerNotFound)?;
|
||||
indices.push(pos);
|
||||
root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?;
|
||||
}
|
||||
|
||||
indices.push(root.layer_ids.iter().position(|x| *x == layer_id).ok_or(DocumentError::LayerNotFound)?);
|
||||
|
||||
Ok(indices)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the layer struct at the specified `path`.
|
||||
/// If you manually edit the layer you have to set the cache_dirty flag yourself.
|
||||
pub fn layer_mut(&mut self, path: &[LayerId]) -> Result<&mut Layer, DocumentError> {
|
||||
@@ -227,6 +251,13 @@ impl Document {
|
||||
let (path, _) = split_path(path.as_slice()).unwrap_or_else(|_| (&[], 0));
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::FolderChanged { path: path.to_vec() }])
|
||||
}
|
||||
Operation::PasteLayer { path, layer } => {
|
||||
let folder = self.folder_mut(path)?;
|
||||
//FIXME: This clone of layer should be avoided somehow
|
||||
folder.add_layer(layer.clone(), -1).ok_or(DocumentError::IndexOutOfBounds)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::FolderChanged { path: path.clone() }])
|
||||
}
|
||||
Operation::DuplicateLayer { path } => {
|
||||
let layer = self.layer(&path)?.clone();
|
||||
let (folder_path, _) = split_path(path.as_slice()).unwrap_or_else(|_| (&[], 0));
|
||||
|
||||
@@ -3,9 +3,10 @@ use kurbo::Shape;
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default, Deserialize, Serialize)]
|
||||
pub struct Ellipse {}
|
||||
|
||||
impl Ellipse {
|
||||
|
||||
@@ -2,9 +2,10 @@ use crate::{DocumentError, LayerId};
|
||||
|
||||
use super::{style, Layer, LayerData, LayerDataTypes};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Folder {
|
||||
next_assignment_id: LayerId,
|
||||
pub layer_ids: Vec<LayerId>,
|
||||
|
||||
@@ -4,9 +4,10 @@ use kurbo::Point;
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Line {}
|
||||
|
||||
impl Line {
|
||||
|
||||
@@ -4,6 +4,7 @@ pub mod ellipse;
|
||||
pub use ellipse::Ellipse;
|
||||
|
||||
pub mod line;
|
||||
use glam::{DMat2, DVec2};
|
||||
use kurbo::BezPath;
|
||||
pub use line::Line;
|
||||
|
||||
@@ -17,16 +18,16 @@ pub mod shape;
|
||||
pub use shape::Shape;
|
||||
|
||||
pub mod folder;
|
||||
pub use folder::Folder;
|
||||
|
||||
use crate::DocumentError;
|
||||
pub use folder::Folder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub trait LayerData {
|
||||
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle);
|
||||
fn to_kurbo_path(&mut self, transform: glam::DAffine2, style: style::PathStyle) -> BezPath;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub enum LayerDataTypes {
|
||||
Folder(Folder),
|
||||
Ellipse(Ellipse),
|
||||
@@ -77,11 +78,19 @@ impl LayerDataTypes {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(remote = "glam::DAffine2")]
|
||||
struct DAffine2Ref {
|
||||
pub matrix2: DMat2,
|
||||
pub translation: DVec2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Layer {
|
||||
pub visible: bool,
|
||||
pub name: Option<String>,
|
||||
pub data: LayerDataTypes,
|
||||
#[serde(with = "DAffine2Ref")]
|
||||
pub transform: glam::DAffine2,
|
||||
pub style: style::PathStyle,
|
||||
pub cache: String,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
use super::{style, LayerData};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct PolyLine {
|
||||
points: Vec<glam::DVec2>,
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ use kurbo::Point;
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Rect {}
|
||||
|
||||
impl Rect {
|
||||
|
||||
@@ -4,9 +4,10 @@ use kurbo::Vec2;
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Shape {
|
||||
equal_sides: bool,
|
||||
sides: u8,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{layers::style, LayerId};
|
||||
use crate::{
|
||||
layers::{style, Layer},
|
||||
LayerId,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -44,6 +47,10 @@ pub enum Operation {
|
||||
DuplicateLayer {
|
||||
path: Vec<LayerId>,
|
||||
},
|
||||
PasteLayer {
|
||||
layer: Layer,
|
||||
path: Vec<LayerId>,
|
||||
},
|
||||
AddFolder {
|
||||
path: Vec<LayerId>,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user