mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 23:38:06 +08:00
Rearrange layers refactor (#281)
* Keep selection during reordering * Fix paste layer selection * Remove junk from layer matadata * Add function to get non_selected_layers * Cleanup * Add tests
This commit is contained in:
@@ -65,72 +65,6 @@ impl Folder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reorder_layers(&mut self, source_ids: Vec<LayerId>, target_id: LayerId) -> Result<(), DocumentError> {
|
||||
let source_pos = self.position_of_layer(source_ids[0])?;
|
||||
let source_pos_end = source_pos + source_ids.len() - 1;
|
||||
let target_pos = self.position_of_layer(target_id)?;
|
||||
|
||||
let mut last_pos = source_pos;
|
||||
for layer_id in &source_ids[1..] {
|
||||
let layer_pos = self.position_of_layer(*layer_id)?;
|
||||
if (layer_pos as i32 - last_pos as i32).abs() > 1 {
|
||||
// Selection is not contiguous
|
||||
return Err(DocumentError::NonReorderableSelection);
|
||||
}
|
||||
last_pos = layer_pos;
|
||||
}
|
||||
|
||||
if source_pos < target_pos {
|
||||
// Moving layers up the hierarchy
|
||||
|
||||
// Prevent shifting past end
|
||||
if source_pos_end + 1 >= self.layers.len() {
|
||||
return Err(DocumentError::NonReorderableSelection);
|
||||
}
|
||||
|
||||
fn reorder_up<T>(arr: &mut Vec<T>, source_pos: usize, source_pos_end: usize, target_pos: usize)
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
*arr = [
|
||||
&arr[0..source_pos], // Elements before selection
|
||||
&arr[source_pos_end + 1..=target_pos], // Elements between selection end and target
|
||||
&arr[source_pos..=source_pos_end], // Selection itself
|
||||
&arr[target_pos + 1..], // Elements before target
|
||||
]
|
||||
.concat();
|
||||
}
|
||||
|
||||
reorder_up(&mut self.layers, source_pos, source_pos_end, target_pos);
|
||||
reorder_up(&mut self.layer_ids, source_pos, source_pos_end, target_pos);
|
||||
} else {
|
||||
// Moving layers down the hierarchy
|
||||
|
||||
// Prevent shifting past end
|
||||
if source_pos == 0 {
|
||||
return Err(DocumentError::NonReorderableSelection);
|
||||
}
|
||||
|
||||
fn reorder_down<T>(arr: &mut Vec<T>, source_pos: usize, source_pos_end: usize, target_pos: usize)
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
*arr = [
|
||||
&arr[0..target_pos], // Elements before target
|
||||
&arr[source_pos..=source_pos_end], // Selection itself
|
||||
&arr[target_pos..source_pos], // Elements between selection and target
|
||||
&arr[source_pos_end + 1..], // Elements before selection
|
||||
]
|
||||
.concat();
|
||||
}
|
||||
|
||||
reorder_down(&mut self.layers, source_pos, source_pos_end, target_pos);
|
||||
reorder_down(&mut self.layer_ids, source_pos, source_pos_end, target_pos);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a list of layers in the folder
|
||||
pub fn list_layers(&self) -> &[LayerId] {
|
||||
self.layer_ids.as_slice()
|
||||
@@ -213,144 +147,3 @@ impl Default for Folder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
use crate::layers::{style::PathStyle, Ellipse, Layer, LayerDataTypes, Line, PolyLine, Rect, Shape};
|
||||
|
||||
use super::Folder;
|
||||
|
||||
#[test]
|
||||
fn reorder_layers() {
|
||||
let mut folder = Folder::default();
|
||||
|
||||
let identity_transform = DAffine2::IDENTITY.to_cols_array();
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Shape(Shape::new(true, 3)), identity_transform, PathStyle::default()), 0);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Rect(Rect::default()), identity_transform, PathStyle::default()), 1);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Ellipse(Ellipse::default()), identity_transform, PathStyle::default()), 2);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Line(Line::default()), identity_transform, PathStyle::default()), 3);
|
||||
folder.add_layer(
|
||||
Layer::new(LayerDataTypes::PolyLine(PolyLine::new(vec![DVec2::ZERO, DVec2::ONE])), identity_transform, PathStyle::default()),
|
||||
4,
|
||||
);
|
||||
|
||||
assert_eq!(folder.layer_ids[0], 0);
|
||||
assert_eq!(folder.layer_ids[1], 1);
|
||||
assert_eq!(folder.layer_ids[2], 2);
|
||||
assert_eq!(folder.layer_ids[3], 3);
|
||||
assert_eq!(folder.layer_ids[4], 4);
|
||||
|
||||
assert!(matches!(folder.layer(0).unwrap().data, LayerDataTypes::Shape(_)));
|
||||
assert!(matches!(folder.layer(1).unwrap().data, LayerDataTypes::Rect(_)));
|
||||
assert!(matches!(folder.layer(2).unwrap().data, LayerDataTypes::Ellipse(_)));
|
||||
assert!(matches!(folder.layer(3).unwrap().data, LayerDataTypes::Line(_)));
|
||||
assert!(matches!(folder.layer(4).unwrap().data, LayerDataTypes::PolyLine(_)));
|
||||
|
||||
assert_eq!(folder.layer_ids.len(), 5);
|
||||
assert_eq!(folder.layers.len(), 5);
|
||||
|
||||
folder.reorder_layers(vec![0, 1], 2).unwrap();
|
||||
|
||||
assert_eq!(folder.layer_ids[0], 2);
|
||||
// Moved layers
|
||||
assert_eq!(folder.layer_ids[1], 0);
|
||||
assert_eq!(folder.layer_ids[2], 1);
|
||||
|
||||
assert_eq!(folder.layer_ids[3], 3);
|
||||
assert_eq!(folder.layer_ids[4], 4);
|
||||
|
||||
assert!(matches!(folder.layer(2).unwrap().data, LayerDataTypes::Ellipse(_)));
|
||||
// Moved layers
|
||||
assert!(matches!(folder.layer(0).unwrap().data, LayerDataTypes::Shape(_)));
|
||||
assert!(matches!(folder.layer(1).unwrap().data, LayerDataTypes::Rect(_)));
|
||||
|
||||
assert!(matches!(folder.layer(3).unwrap().data, LayerDataTypes::Line(_)));
|
||||
assert!(matches!(folder.layer(4).unwrap().data, LayerDataTypes::PolyLine(_)));
|
||||
|
||||
assert_eq!(folder.layer_ids.len(), 5);
|
||||
assert_eq!(folder.layers.len(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reorder_layer_to_top() {
|
||||
let mut folder = Folder::default();
|
||||
|
||||
let identity_transform = DAffine2::IDENTITY.to_cols_array();
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Shape(Shape::new(true, 3)), identity_transform, PathStyle::default()), 0);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Rect(Rect::default()), identity_transform, PathStyle::default()), 1);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Ellipse(Ellipse::default()), identity_transform, PathStyle::default()), 2);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Line(Line::default()), identity_transform, PathStyle::default()), 3);
|
||||
|
||||
assert_eq!(folder.layer_ids[0], 0);
|
||||
assert_eq!(folder.layer_ids[1], 1);
|
||||
assert_eq!(folder.layer_ids[2], 2);
|
||||
assert_eq!(folder.layer_ids[3], 3);
|
||||
|
||||
assert!(matches!(folder.layer(0).unwrap().data, LayerDataTypes::Shape(_)));
|
||||
assert!(matches!(folder.layer(1).unwrap().data, LayerDataTypes::Rect(_)));
|
||||
assert!(matches!(folder.layer(2).unwrap().data, LayerDataTypes::Ellipse(_)));
|
||||
assert!(matches!(folder.layer(3).unwrap().data, LayerDataTypes::Line(_)));
|
||||
|
||||
assert_eq!(folder.layer_ids.len(), 4);
|
||||
assert_eq!(folder.layers.len(), 4);
|
||||
|
||||
folder.reorder_layers(vec![1], 3).unwrap();
|
||||
|
||||
assert_eq!(folder.layer_ids[0], 0);
|
||||
assert_eq!(folder.layer_ids[1], 2);
|
||||
assert_eq!(folder.layer_ids[2], 3);
|
||||
// Moved layer
|
||||
assert_eq!(folder.layer_ids[3], 1);
|
||||
|
||||
assert!(matches!(folder.layer(0).unwrap().data, LayerDataTypes::Shape(_)));
|
||||
assert!(matches!(folder.layer(2).unwrap().data, LayerDataTypes::Ellipse(_)));
|
||||
assert!(matches!(folder.layer(3).unwrap().data, LayerDataTypes::Line(_)));
|
||||
// Moved layer
|
||||
assert!(matches!(folder.layer(1).unwrap().data, LayerDataTypes::Rect(_)));
|
||||
|
||||
assert_eq!(folder.layer_ids.len(), 4);
|
||||
assert_eq!(folder.layers.len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reorder_non_contiguous_selection() {
|
||||
let mut folder = Folder::default();
|
||||
|
||||
let identity_transform = DAffine2::IDENTITY.to_cols_array();
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Shape(Shape::new(true, 3)), identity_transform, PathStyle::default()), 0);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Rect(Rect::default()), identity_transform, PathStyle::default()), 1);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Ellipse(Ellipse::default()), identity_transform, PathStyle::default()), 2);
|
||||
folder.add_layer(Layer::new(LayerDataTypes::Line(Line::default()), identity_transform, PathStyle::default()), 3);
|
||||
|
||||
assert_eq!(folder.layer_ids[0], 0);
|
||||
assert_eq!(folder.layer_ids[1], 1);
|
||||
assert_eq!(folder.layer_ids[2], 2);
|
||||
assert_eq!(folder.layer_ids[3], 3);
|
||||
|
||||
assert!(matches!(folder.layer(0).unwrap().data, LayerDataTypes::Shape(_)));
|
||||
assert!(matches!(folder.layer(1).unwrap().data, LayerDataTypes::Rect(_)));
|
||||
assert!(matches!(folder.layer(2).unwrap().data, LayerDataTypes::Ellipse(_)));
|
||||
assert!(matches!(folder.layer(3).unwrap().data, LayerDataTypes::Line(_)));
|
||||
|
||||
assert_eq!(folder.layer_ids.len(), 4);
|
||||
assert_eq!(folder.layers.len(), 4);
|
||||
|
||||
folder.reorder_layers(vec![0, 2], 3).expect_err("Non-contiguous selections can't be reordered");
|
||||
|
||||
// Expect identical state
|
||||
assert_eq!(folder.layer_ids[0], 0);
|
||||
assert_eq!(folder.layer_ids[1], 1);
|
||||
assert_eq!(folder.layer_ids[2], 2);
|
||||
assert_eq!(folder.layer_ids[3], 3);
|
||||
|
||||
assert!(matches!(folder.layer(0).unwrap().data, LayerDataTypes::Shape(_)));
|
||||
assert!(matches!(folder.layer(1).unwrap().data, LayerDataTypes::Rect(_)));
|
||||
assert!(matches!(folder.layer(2).unwrap().data, LayerDataTypes::Ellipse(_)));
|
||||
assert!(matches!(folder.layer(3).unwrap().data, LayerDataTypes::Line(_)));
|
||||
|
||||
assert_eq!(folder.layer_ids.len(), 4);
|
||||
assert_eq!(folder.layers.len(), 4);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user