Skip to content

Add folders to frontend and folder creation to backend #315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 38 commits into from
Aug 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5b46774
Add folders to frontend and folder creation to backend
Keavon Jul 29, 2021
f235329
Add Group keybind
TrueDoctor Aug 6, 2021
bdb103f
Add logic to handle expanding of folders
TrueDoctor Aug 6, 2021
824e269
Send all paths as (u32, u32)
TrueDoctor Aug 7, 2021
0fa6bf6
Add custom serialization for path
TrueDoctor Aug 8, 2021
90723d8
Merge two layer_panel files
TrueDoctor Aug 8, 2021
72c06a0
Refactor frontend layer merging
TrueDoctor Aug 9, 2021
e471f2f
Fix JS linting
Keavon Aug 10, 2021
01d8d48
Update upstream thumbnail changes
TrueDoctor Aug 9, 2021
67ee317
Add paste into selected folder + fix thumbnail dirtification
TrueDoctor Aug 11, 2021
34e1bb1
Implement CollapseFolder function
TrueDoctor Aug 11, 2021
c16c70d
Skip folders on a different indentation level during reorder
TrueDoctor Aug 11, 2021
14f1eac
Only reorder within the same folder
TrueDoctor Aug 11, 2021
f570650
Merge branch 'master' into add-folders
Keavon Aug 12, 2021
711521c
Add folder node icon for folder layers
Keavon Aug 14, 2021
74f1e44
Add expand/collapse folder button; partly implement new layer tree de…
Keavon Aug 14, 2021
c36a73c
Update terminology in the docs
Keavon Aug 12, 2021
7c7957c
Add number labels to ruler marks
Keavon Aug 12, 2021
cda530c
Replace promise with await in MenuList.vue
Keavon Aug 13, 2021
325d416
Miscellaneous minor code cleanup
Keavon Aug 13, 2021
3763bcc
Disallow snake_case variable names in frontend
Keavon Aug 13, 2021
78e0e20
Add support for saving and opening files (#325)
azeemba Aug 14, 2021
9cb9da9
Refactor ViewportPosition from u32 (UVec2) to f64 (DVec2) (#345)
Keavon Aug 14, 2021
c7adb2f
Improve Frontend -> Backend user input system (#348)
Keavon Aug 14, 2021
735d5c8
Update the readme
Keavon Aug 15, 2021
ae390f5
Make scrollbars interactable (#328)
0HyperCube Aug 15, 2021
ec93adb
Improve scrollbar behavior (#351)
0HyperCube Aug 16, 2021
06e028b
Implement bounding box for selected layers (#349)
TrueDoctor Aug 20, 2021
8a4e408
Fix collapsing of folders
TrueDoctor Aug 20, 2021
e8faf21
Add have pixel offset to selection bounding box
TrueDoctor Aug 20, 2021
2100f83
Don't panic on Ctrl + A
TrueDoctor Aug 20, 2021
ba238da
Merge branch 'master' into add-folders
TrueDoctor Aug 20, 2021
1c3b2e2
Rename to camel case
TrueDoctor Aug 21, 2021
bd679f8
Add todo comment for Keavon
TrueDoctor Aug 21, 2021
92f67fa
Merge branch 'master' into add-folders
TrueDoctor Aug 21, 2021
94e6d9a
Apply @Hypercubes review suggestions
TrueDoctor Aug 21, 2021
41a5ffe
Merge branch 'master' into add-folders
Keavon Aug 29, 2021
006a1bc
Fix many panics, improve behavior of copy/paste and grouping (but gro…
Keavon Aug 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions editor/src/communication/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ mod test {
let mut editor = create_editor_with_three_layers();

let document_before_copy = editor.dispatcher.documents_message_handler.active_document().document.clone();
editor.handle_message(DocumentsMessage::CopySelectedLayers).unwrap();
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::Copy).unwrap();
editor.handle_message(DocumentsMessage::PasteIntoFolder { path: vec![], insert_index: -1 }).unwrap();
let document_after_copy = editor.dispatcher.documents_message_handler.active_document().document.clone();

let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers();
Expand Down Expand Up @@ -159,8 +159,8 @@ mod test {
let shape_id = document_before_copy.root.as_folder().unwrap().layer_ids[1];

editor.handle_message(DocumentMessage::SetSelectedLayers(vec![vec![shape_id]])).unwrap();
editor.handle_message(DocumentsMessage::CopySelectedLayers).unwrap();
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::Copy).unwrap();
editor.handle_message(DocumentsMessage::PasteIntoFolder { path: vec![], insert_index: -1 }).unwrap();

let document_after_copy = editor.dispatcher.documents_message_handler.active_document().document.clone();

Expand Down Expand Up @@ -192,7 +192,7 @@ mod test {
const LINE_INDEX: usize = 0;
const PEN_INDEX: usize = 1;

editor.handle_message(DocumentMessage::AddFolder(vec![])).unwrap();
editor.handle_message(DocumentMessage::CreateFolder(vec![])).unwrap();

let document_before_added_shapes = editor.dispatcher.documents_message_handler.active_document().document.clone();
let folder_id = document_before_added_shapes.root.as_folder().unwrap().layer_ids[FOLDER_INDEX];
Expand Down Expand Up @@ -222,10 +222,10 @@ mod test {

let document_before_copy = editor.dispatcher.documents_message_handler.active_document().document.clone();

editor.handle_message(DocumentsMessage::CopySelectedLayers).unwrap();
editor.handle_message(DocumentsMessage::Copy).unwrap();
editor.handle_message(DocumentMessage::DeleteSelectedLayers).unwrap();
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::PasteIntoFolder { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::PasteIntoFolder { path: vec![], insert_index: -1 }).unwrap();

let document_after_copy = editor.dispatcher.documents_message_handler.active_document().document.clone();

Expand Down Expand Up @@ -283,11 +283,11 @@ mod test {
let ellipse_id = document_before_copy.root.as_folder().unwrap().layer_ids[ELLIPSE_INDEX];

editor.handle_message(DocumentMessage::SetSelectedLayers(vec![vec![rect_id], vec![ellipse_id]])).unwrap();
editor.handle_message(DocumentsMessage::CopySelectedLayers).unwrap();
editor.handle_message(DocumentsMessage::Copy).unwrap();
editor.handle_message(DocumentMessage::DeleteSelectedLayers).unwrap();
editor.draw_rect(0., 800., 12., 200.);
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::PasteIntoFolder { path: vec![], insert_index: -1 }).unwrap();
editor.handle_message(DocumentsMessage::PasteIntoFolder { path: vec![], insert_index: -1 }).unwrap();

let document_after_copy = editor.dispatcher.documents_message_handler.active_document().document.clone();

Expand Down
97 changes: 65 additions & 32 deletions editor/src/document/document_file.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
pub use super::layer_panel::*;
use crate::{
consts::{ASYMPTOTIC_EFFECT, FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING},
frontend::layer_panel::*,
EditorError,
};
use glam::{DAffine2, DVec2};
Expand Down Expand Up @@ -93,16 +92,17 @@ pub enum DocumentMessage {
DeleteLayer(Vec<LayerId>),
DeleteSelectedLayers,
DuplicateSelectedLayers,
CreateFolder(Vec<LayerId>),
SetBlendModeForSelectedLayers(BlendMode),
SetOpacityForSelectedLayers(f64),
AddFolder(Vec<LayerId>),
RenameLayer(Vec<LayerId>, String),
ToggleLayerVisibility(Vec<LayerId>),
FlipSelectedLayers(FlipAxis),
ToggleLayerExpansion(Vec<LayerId>),
FolderChanged(Vec<LayerId>),
StartTransaction,
RollbackTransaction,
GroupSelectedLayers,
AbortTransaction,
CommitTransaction,
ExportDocument,
Expand Down Expand Up @@ -139,7 +139,7 @@ impl DocumentMessageHandler {
let _ = self.document.render_root();
self.layer_data(&path).expanded.then(|| {
let children = self.layer_panel(path.as_slice()).expect("The provided Path was not valid");
FrontendMessage::ExpandFolder { path, children }.into()
FrontendMessage::ExpandFolder { path: path.into(), children }.into()
})
}

Expand All @@ -154,7 +154,7 @@ impl DocumentMessageHandler {
self.layer_data(path).selected = true;
let data = self.layer_panel_entry(path.to_vec()).ok()?;
// TODO: Add deduplication
(!path.is_empty()).then(|| FrontendMessage::UpdateLayer { path: path.to_vec(), data }.into())
(!path.is_empty()).then(|| FrontendMessage::UpdateLayer { path: path.to_vec().into(), data }.into())
}

pub fn selected_layers_bounding_box(&self) -> Option<[DVec2; 2]> {
Expand All @@ -165,9 +165,9 @@ impl DocumentMessageHandler {
// TODO: Consider moving this to some kind of overlay manager in the future
pub fn selected_layers_vector_points(&self) -> Vec<VectorManipulatorShape> {
let shapes = self.selected_layers().filter_map(|path_to_shape| {
let viewport_transform = self.document.generate_transform_relative_to_viewport(path_to_shape.as_slice()).ok()?;
let viewport_transform = self.document.generate_transform_relative_to_viewport(path_to_shape).ok()?;

let shape = match &self.document.layer(path_to_shape.as_slice()).ok()?.data {
let shape = match &self.document.layer(path_to_shape).ok()?.data {
LayerDataType::Shape(shape) => Some(shape),
LayerDataType::Folder(_) => None,
}?;
Expand Down Expand Up @@ -205,8 +205,8 @@ impl DocumentMessageHandler {
self.layer_data.entry(path.to_vec()).or_insert_with(|| LayerData::new(true))
}

pub fn selected_layers(&self) -> impl Iterator<Item = &Vec<LayerId>> {
self.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path))
pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
self.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.as_slice()))
}

/// Returns the paths to all layers in order, optionally including only selected or non-selected layers.
Expand Down Expand Up @@ -326,7 +326,7 @@ impl DocumentMessageHandler {
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>) -> Result<LayerPanelEntry, EditorError> {
let data: LayerData = *layer_data(&mut self.layer_data, &path);
let layer = self.document.layer(&path)?;
let entry = layer_panel_entry(&data, self.document.multiply_transforms(&path).unwrap(), layer, path);
let entry = layer_panel_entry(&data, self.document.multiply_transforms(&path)?, layer, path);
Ok(entry)
}

Expand Down Expand Up @@ -362,7 +362,6 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
match message {
Movement(message) => self.movement_handler.process_action(message, (layer_data(&mut self.layer_data, &[]), &self.document, ipp), responses),
DeleteLayer(path) => responses.push_back(DocumentOperation::DeleteLayer { path }.into()),
AddFolder(path) => responses.push_back(DocumentOperation::AddFolder { path }.into()),
StartTransaction => self.backup(),
RollbackTransaction => {
self.rollback().unwrap_or_else(|e| log::warn!("{}", e));
Expand Down Expand Up @@ -409,6 +408,32 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
.into(),
)
}
CreateFolder(mut path) => {
let id = generate_uuid();
path.push(id);
self.layerdata_mut(&path).expanded = true;
responses.push_back(DocumentOperation::CreateFolder { path }.into())
}
GroupSelectedLayers => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit, but I would have expected this to also select the newly created folder

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sortof done but still wonky because of a different part of the codebase

let common_prefix = self.document.common_prefix(self.selected_layers());
let (_id, common_prefix) = common_prefix.split_last().unwrap_or((&0, &[]));

let mut new_folder_path = common_prefix.to_vec();
new_folder_path.push(generate_uuid());

responses.push_back(DocumentsMessage::Copy.into());
responses.push_back(DocumentMessage::DeleteSelectedLayers.into());
responses.push_back(DocumentOperation::CreateFolder { path: new_folder_path.clone() }.into());
responses.push_back(DocumentMessage::ToggleLayerExpansion(new_folder_path.clone()).into());
responses.push_back(
DocumentsMessage::PasteIntoFolder {
path: new_folder_path.clone(),
insert_index: -1,
}
.into(),
);
responses.push_back(DocumentMessage::SetSelectedLayers(vec![new_folder_path]).into());
}
SetBlendModeForSelectedLayers(blend_mode) => {
self.backup();
for path in self.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) {
Expand All @@ -419,7 +444,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
self.backup();
let opacity = opacity.clamp(0., 1.);

for path in self.selected_layers().cloned() {
for path in self.selected_layers().map(|path| path.to_vec()) {
responses.push_back(DocumentOperation::SetLayerOpacity { path, opacity }.into());
}
}
Expand All @@ -428,7 +453,11 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
}
ToggleLayerExpansion(path) => {
self.layer_data(&path).expanded ^= true;
responses.push_back(FolderChanged(path).into());
match self.layer_data(&path).expanded {
true => responses.push_back(FolderChanged(path.clone()).into()),
false => responses.push_back(FrontendMessage::CollapseFolder { path: path.clone().into() }.into()),
}
responses.extend(self.layer_panel_entry(path.clone()).ok().map(|data| FrontendMessage::UpdateLayer { path: path.into(), data }.into()));
}
SelectionChanged => {
// TODO: Hoist this duplicated code into wider system
Expand All @@ -437,7 +466,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
DeleteSelectedLayers => {
self.backup();
responses.push_front(ToolMessage::SelectedLayersChanged.into());
for path in self.selected_layers().cloned() {
for path in self.selected_layers().map(|path| path.to_vec()) {
responses.push_front(DocumentOperation::DeleteLayer { path }.into());
}
}
Expand Down Expand Up @@ -469,14 +498,12 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
let all_layer_paths = self
.layer_data
.keys()
.filter(|path| !path.is_empty() && !self.document.layer(path).unwrap().overlay)
.filter(|path| !path.is_empty() && !self.document.layer(path).map(|layer| layer.overlay).unwrap_or(false))
.cloned()
.collect::<Vec<_>>();
responses.push_front(SetSelectedLayers(all_layer_paths).into());
}
DeselectAllLayers => {
responses.push_front(SetSelectedLayers(vec![]).into());
}
DeselectAllLayers => responses.push_front(SetSelectedLayers(vec![]).into()),
DocumentHistoryBackward => self.undo().unwrap_or_else(|e| log::warn!("{}", e)),
DocumentHistoryForward => self.redo().unwrap_or_else(|e| log::warn!("{}", e)),
Undo => {
Expand Down Expand Up @@ -505,18 +532,19 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
self.layer_data.remove(&path);
Some(ToolMessage::SelectedLayersChanged.into())
}
DocumentResponse::LayerChanged { path } => (!self.document.layer(&path).unwrap().overlay).then(|| {
FrontendMessage::UpdateLayer {
path: path.clone(),
data: self.layer_panel_entry(path).unwrap(),
}
.into()
DocumentResponse::LayerChanged { path } => self.layer_panel_entry(path.clone()).ok().and_then(|entry| {
let overlay = self.document.layer(&path).unwrap().overlay;
(!overlay).then(|| FrontendMessage::UpdateLayer { path: path.into(), data: entry }.into())
}),
DocumentResponse::CreatedLayer { path } => (!self.document.layer(&path).unwrap().overlay).then(|| SetSelectedLayers(vec![path]).into()),
DocumentResponse::CreatedLayer { path } => {
self.layer_data.insert(path.clone(), LayerData::new(false));
(!self.document.layer(&path).unwrap().overlay).then(|| SetSelectedLayers(vec![path]).into())
}
DocumentResponse::DocumentChanged => Some(RenderDocument.into()),
})
.flatten(),
);
log::debug!("LayerPanel: {:?}", self.layer_data.keys());
}
Err(e) => log::error!("DocumentError: {:?}", e),
Ok(_) => (),
Expand Down Expand Up @@ -551,7 +579,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand

NudgeSelectedLayers(x, y) => {
self.backup();
for path in self.selected_layers().cloned() {
for path in self.selected_layers().map(|path| path.to_vec()) {
let operation = DocumentOperation::TransformLayerInViewport {
path,
transform: DAffine2::from_translation((x, y).into()).to_cols_array(),
Expand All @@ -561,9 +589,9 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
responses.push_back(ToolMessage::SelectedLayersChanged.into());
}
MoveSelectedLayersTo { path, insert_index } => {
responses.push_back(DocumentsMessage::CopySelectedLayers.into());
responses.push_back(DocumentsMessage::Copy.into());
responses.push_back(DocumentMessage::DeleteSelectedLayers.into());
responses.push_back(DocumentsMessage::PasteLayers { path, insert_index }.into());
responses.push_back(DocumentsMessage::PasteIntoFolder { path, insert_index }.into());
}
ReorderSelectedLayers(relative_position) => {
self.backup();
Expand All @@ -574,7 +602,11 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
1 => selected_layers.last(),
_ => unreachable!(),
} {
if let Some(pos) = all_layer_paths.iter().position(|path| path == pivot) {
let all_layer_paths: Vec<_> = all_layer_paths
.iter()
.filter(|layer| layer.starts_with(&pivot[0..pivot.len() - 1]) && pivot.len() == layer.len())
.collect();
if let Some(pos) = all_layer_paths.iter().position(|path| *path == pivot) {
let max = all_layer_paths.len() as i64 - 1;
let insert_pos = (pos as i64 + relative_position as i64).clamp(0, max) as usize;
let insert = all_layer_paths.get(insert_pos);
Expand Down Expand Up @@ -602,13 +634,13 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
FlipAxis::X => DVec2::new(-1., 1.),
FlipAxis::Y => DVec2::new(1., -1.),
};
if let Some([min, max]) = self.document.combined_viewport_bounding_box(self.selected_layers().map(|x| x.as_slice())) {
if let Some([min, max]) = self.document.combined_viewport_bounding_box(self.selected_layers().map(|x| x)) {
let center = (max + min) / 2.;
let bbox_trans = DAffine2::from_translation(-center);
for path in self.selected_layers() {
responses.push_back(
DocumentOperation::TransformLayerInScope {
path: path.clone(),
path: path.to_vec(),
transform: DAffine2::from_scale(scale).to_cols_array(),
scope: bbox_trans.to_cols_array(),
}
Expand All @@ -627,7 +659,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
AlignAxis::Y => DVec2::Y,
};
let lerp = |bbox: &[DVec2; 2]| bbox[0].lerp(bbox[1], 0.5);
if let Some(combined_box) = self.document.combined_viewport_bounding_box(self.selected_layers().map(|x| x.as_slice())) {
if let Some(combined_box) = self.document.combined_viewport_bounding_box(self.selected_layers().map(|x| x)) {
let aggregated = match aggregate {
AlignAggregate::Min => combined_box[0],
AlignAggregate::Max => combined_box[1],
Expand All @@ -643,7 +675,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
let translation = (aggregated - center) * axis;
responses.push_back(
DocumentOperation::TransformLayerInViewport {
path: path.clone(),
path: path.to_vec(),
transform: DAffine2::from_translation(translation).to_cols_array(),
}
.into(),
Expand Down Expand Up @@ -673,6 +705,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
DuplicateSelectedLayers,
NudgeSelectedLayers,
ReorderSelectedLayers,
GroupSelectedLayers,
);
common.extend(select);
}
Expand Down
Loading