Skip to content

Commit cc560cb

Browse files
0HyperCubeKeavonotdavies
authored
Drag to rearrange layers in the layer panel (#434)
* Add insert line * Implement dragging * Improve CSS and variable naming consistency * Resolved folder crash, added Undo/Redo support * Removed marker TODO leftover * JS cleanup * WIP preserving expanded state (via LayerData) when copy/pasting and moving layers in layer panel * Finish making folders copy/paste preserving expanded state, but not recursively yet * Add cut, copy, and paste to the Edit menu (#440) * Add cut * Hook up edit dropdown * Use copy message Co-authored-by: Keavon Chambers <[email protected]> Co-authored-by: otdavies <[email protected]>
1 parent 62fc882 commit cc560cb

File tree

9 files changed

+264
-36
lines changed

9 files changed

+264
-36
lines changed

editor/src/document/document_file.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::HashMap;
22
use std::collections::VecDeque;
33

4+
use super::document_message_handler::CopyBufferEntry;
45
pub use super::layer_panel::*;
56
use super::movement_handler::{MovementMessage, MovementMessageHandler};
67
use super::transform_layer_handler::{TransformLayerMessage, TransformLayerMessageHandler};
@@ -105,6 +106,10 @@ pub enum DocumentMessage {
105106
#[child]
106107
TransformLayers(TransformLayerMessage),
107108
DispatchOperation(Box<DocumentOperation>),
109+
UpdateLayerData {
110+
path: Vec<LayerId>,
111+
layer_data_entry: LayerData,
112+
},
108113
SetSelectedLayers(Vec<Vec<LayerId>>),
109114
AddSelectedLayers(Vec<Vec<LayerId>>),
110115
SelectAllLayers,
@@ -148,6 +153,11 @@ pub enum DocumentMessage {
148153
insert_index: isize,
149154
},
150155
ReorderSelectedLayers(i32), // relative_position,
156+
MoveLayerInTree {
157+
layer: Vec<LayerId>,
158+
insert_above: bool,
159+
neighbor: Vec<LayerId>,
160+
},
151161
SetSnapping(bool),
152162
}
153163

@@ -653,6 +663,9 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
653663
}
654664
}
655665
}
666+
UpdateLayerData { path, layer_data_entry } => {
667+
self.layer_data.insert(path, layer_data_entry);
668+
}
656669
SetSelectedLayers(paths) => {
657670
self.layer_data.iter_mut().filter(|(_, layer_data)| layer_data.selected).for_each(|(path, layer_data)| {
658671
layer_data.selected = false;
@@ -911,6 +924,42 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
911924
}
912925
}
913926
RenameLayer(path, name) => responses.push_back(DocumentOperation::RenameLayer { path, name }.into()),
927+
MoveLayerInTree {
928+
layer: target_layer,
929+
insert_above,
930+
neighbor,
931+
} => {
932+
let neighbor_id = neighbor.last().expect("Tried to move next to root");
933+
let neighbor_path = &neighbor[..neighbor.len() - 1];
934+
935+
if !neighbor.starts_with(&target_layer) {
936+
let containing_folder = self.graphene_document.folder(neighbor_path).expect("Neighbor does not exist");
937+
let neighbor_index = containing_folder.position_of_layer(*neighbor_id).expect("Neighbor layer does not exist");
938+
939+
let layer = self.graphene_document.layer(&target_layer).expect("Layer moving does not exist.").to_owned();
940+
let destination_path = [neighbor_path.to_vec(), vec![generate_uuid()]].concat();
941+
let insert_index = if insert_above { neighbor_index } else { neighbor_index + 1 } as isize;
942+
943+
responses.push_back(DocumentMessage::StartTransaction.into());
944+
responses.push_back(
945+
DocumentOperation::InsertLayer {
946+
layer,
947+
destination_path: destination_path.clone(),
948+
insert_index,
949+
}
950+
.into(),
951+
);
952+
responses.push_back(
953+
DocumentMessage::UpdateLayerData {
954+
path: destination_path,
955+
layer_data_entry: *self.layer_data(&target_layer),
956+
}
957+
.into(),
958+
);
959+
responses.push_back(DocumentOperation::DeleteLayer { path: target_layer }.into());
960+
responses.push_back(DocumentMessage::CommitTransaction.into());
961+
}
962+
}
914963
SetSnapping(new_status) => {
915964
self.snapping_enabled = new_status;
916965
}
@@ -927,6 +976,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
927976
ExportDocument,
928977
SaveDocument,
929978
SetSnapping,
979+
MoveLayerInTree,
930980
);
931981

932982
if self.layer_data.values().any(|data| data.selected) {

editor/src/document/document_message_handler.rs

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
use super::{DocumentMessageHandler, LayerData};
2+
use crate::consts::DEFAULT_DOCUMENT_NAME;
13
use crate::frontend::frontend_message_handler::FrontendDocumentDetails;
24
use crate::input::InputPreprocessor;
35
use crate::message_prelude::*;
46
use graphene::layers::Layer;
57
use graphene::{LayerId, Operation as DocumentOperation};
8+
69
use log::warn;
710
use serde::{Deserialize, Serialize};
8-
use std::collections::{HashMap, VecDeque};
911

10-
use super::DocumentMessageHandler;
11-
use crate::consts::DEFAULT_DOCUMENT_NAME;
12+
use std::collections::{HashMap, VecDeque};
1213

1314
#[repr(u8)]
1415
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
@@ -17,7 +18,8 @@ pub enum Clipboard {
1718
User,
1819
_ClipboardCount,
1920
}
20-
static CLIPBOARD_COUNT: u8 = Clipboard::_ClipboardCount as u8;
21+
22+
const CLIPBOARD_COUNT: u8 = Clipboard::_ClipboardCount as u8;
2123

2224
#[impl_message(Message, Documents)]
2325
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
@@ -60,7 +62,13 @@ pub struct DocumentsMessageHandler {
6062
documents: HashMap<u64, DocumentMessageHandler>,
6163
document_ids: Vec<u64>,
6264
active_document_id: u64,
63-
copy_buffer: Vec<Vec<Layer>>,
65+
copy_buffer: [Vec<CopyBufferEntry>; CLIPBOARD_COUNT as usize],
66+
}
67+
68+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
69+
pub struct CopyBufferEntry {
70+
layer: Layer,
71+
layer_data: LayerData,
6472
}
6573

6674
impl DocumentsMessageHandler {
@@ -156,10 +164,12 @@ impl Default for DocumentsMessageHandler {
156164
let starting_key = generate_uuid();
157165
documents_map.insert(starting_key, DocumentMessageHandler::default());
158166

167+
const EMPTY_VEC: Vec<CopyBufferEntry> = vec![];
168+
159169
Self {
160170
documents: documents_map,
161171
document_ids: vec![starting_key],
162-
copy_buffer: vec![vec![]; CLIPBOARD_COUNT as usize],
172+
copy_buffer: [EMPTY_VEC; CLIPBOARD_COUNT as usize],
163173
active_document_id: starting_key,
164174
}
165175
}
@@ -345,11 +355,12 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
345355
let paths = self.active_document().selected_layers_sorted();
346356
self.copy_buffer[clipboard as usize].clear();
347357
for path in paths {
348-
match self.active_document().graphene_document.layer(&path).map(|t| t.clone()) {
349-
Ok(layer) => {
350-
self.copy_buffer[clipboard as usize].push(layer);
358+
let document = self.active_document();
359+
match (document.graphene_document.layer(&path).map(|t| t.clone()), document.layer_data(&path).clone()) {
360+
(Ok(layer), layer_data) => {
361+
self.copy_buffer[clipboard as usize].push(CopyBufferEntry { layer, layer_data });
351362
}
352-
Err(e) => warn!("Could not access selected layer {:?}: {:?}", path, e),
363+
(Err(e), _) => warn!("Could not access selected layer {:?}: {:?}", path, e),
353364
}
354365
}
355366
}
@@ -374,24 +385,35 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
374385
);
375386
}
376387
PasteIntoFolder { clipboard, path, insert_index } => {
377-
let paste = |layer: &Layer, responses: &mut VecDeque<_>| {
378-
log::trace!("Pasting into folder {:?} as index: {}", path, insert_index);
388+
let paste = |entry: &CopyBufferEntry, responses: &mut VecDeque<_>| {
389+
log::trace!("Pasting into folder {:?} as index: {}", &path, insert_index);
390+
391+
let destination_path = [path.to_vec(), vec![generate_uuid()]].concat();
392+
379393
responses.push_back(
380-
DocumentOperation::PasteLayer {
381-
layer: layer.clone(),
382-
path: path.clone(),
394+
DocumentOperation::InsertLayer {
395+
layer: entry.layer.clone(),
396+
destination_path: destination_path.clone(),
383397
insert_index,
384398
}
385399
.into(),
386-
)
400+
);
401+
responses.push_back(
402+
DocumentMessage::UpdateLayerData {
403+
path: destination_path,
404+
layer_data_entry: entry.layer_data,
405+
}
406+
.into(),
407+
);
387408
};
409+
388410
if insert_index == -1 {
389-
for layer in self.copy_buffer[clipboard as usize].iter() {
390-
paste(layer, responses)
411+
for entry in self.copy_buffer[clipboard as usize].iter() {
412+
paste(entry, responses)
391413
}
392414
} else {
393-
for layer in self.copy_buffer[clipboard as usize].iter().rev() {
394-
paste(layer, responses)
415+
for entry in self.copy_buffer[clipboard as usize].iter().rev() {
416+
paste(entry, responses)
395417
}
396418
}
397419
}

editor/src/tool/tools/fill.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ impl Fsm for FillToolFsmState {
8282
RightMouseDown => tool_data.secondary_color,
8383
Abort => unreachable!(),
8484
};
85+
responses.push_back(DocumentMessage::StartTransaction.into());
8586
responses.push_back(Operation::SetLayerFill { path: path.to_vec(), color }.into());
87+
responses.push_back(DocumentMessage::CommitTransaction.into());
8688
}
8789

8890
Ready

0 commit comments

Comments
 (0)