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:
Till Arnold
2021-07-05 00:34:47 +02:00
committed by Keavon Chambers
parent 1d2bb6fb2b
commit 94c42ff5d7
17 changed files with 570 additions and 34 deletions

View File

@@ -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));

View File

@@ -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 {

View File

@@ -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>,

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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>,
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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>,
},