Implement backend part of the layer selection (#172)

* Implement backend part of the layer selection
This commit is contained in:
TrueDoctor
2021-06-09 12:20:50 +02:00
committed by Keavon Chambers
parent 13dd51dbf8
commit 0e8cbad003
11 changed files with 163 additions and 125 deletions
+64 -2
View File
@@ -1,7 +1,69 @@
use document_core::document::Document as InteralDocument;
use crate::{frontend::layer_panel::*, EditorError};
use document_core::{document::Document as InteralDocument, layers::Layer, LayerId};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct Document {
pub document: InteralDocument,
pub name: String,
pub layer_data: HashMap<Vec<LayerId>, LayerData>,
}
impl Default for Document {
fn default() -> Self {
Self {
document: InteralDocument::default(),
name: String::from("Unnamed Document"),
layer_data: vec![(vec![], LayerData { selected: false, expanded: true })].into_iter().collect(),
}
}
}
fn layer_data<'a>(layer_data: &'a mut HashMap<Vec<LayerId>, LayerData>, path: &[LayerId]) -> &'a mut LayerData {
if !layer_data.contains_key(path) {
layer_data.insert(path.to_vec(), LayerData::default());
}
layer_data.get_mut(path).unwrap()
}
pub fn layer_panel_entry(layer_data: &mut LayerData, layer: &Layer, path: Vec<LayerId>) -> LayerPanelEntry {
let layer_type: LayerType = (&layer.data).into();
let name = layer.name.clone().unwrap_or_else(|| format!("Unnamed {}", layer_type));
LayerPanelEntry {
name,
visible: layer.visible,
layer_type,
layer_data: *layer_data,
path,
}
}
impl Document {
pub fn layer_data(&mut self, path: &[LayerId]) -> &mut LayerData {
layer_data(&mut self.layer_data, path)
}
/// Returns a list of `LayerPanelEntry`s intended for display purposes. These don't contain
/// any actual data, but rather metadata such as visibility and names of the layers.
pub fn layer_panel(&mut self, path: &[LayerId]) -> Result<Vec<LayerPanelEntry>, EditorError> {
let folder = self.document.document_folder(path)?;
let self_layer_data = &mut self.layer_data;
let entries = folder
.layers()
.iter()
.zip(folder.layer_ids.iter())
.map(|(layer, id)| {
let path = [path, &[*id]].concat();
layer_panel_entry(layer_data(self_layer_data, &path), layer, path)
})
.collect();
Ok(entries)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Copy, Default)]
pub struct LayerData {
pub selected: bool,
pub expanded: bool,
}
@@ -8,7 +8,7 @@ use std::collections::VecDeque;
#[derive(PartialEq, Clone, Debug)]
pub enum DocumentMessage {
DispatchOperation(DocumentOperation),
SelectLayer(Vec<LayerId>),
SelectLayers(Vec<Vec<LayerId>>),
DeleteLayer(Vec<LayerId>),
AddFolder(Vec<LayerId>),
RenameLayer(Vec<LayerId>, String),
@@ -49,6 +49,13 @@ impl DocumentMessageHandler {
document_responses.retain(|response| !matches!(response, DocumentResponse::DocumentChanged));
document_responses.len() != len
}
fn handle_folder_changed(&mut self, path: Vec<LayerId>) -> Option<Message> {
let document = self.active_document_mut();
document.layer_data(&path).expanded.then(|| {
let children = document.layer_panel(path.as_slice()).expect("The provided Path was not valid");
FrontendMessage::ExpandFolder { path, children }.into()
})
}
}
impl Default for DocumentMessageHandler {
@@ -84,6 +91,17 @@ impl MessageHandler<DocumentMessage, ()> for DocumentMessageHandler {
ToggleLayerVisibility(path) => {
responses.push_back(DocumentOperation::ToggleVisibility { path }.into());
}
ToggleLayerExpansion(path) => {
self.active_document_mut().layer_data(&path).expanded ^= true;
responses.extend(self.handle_folder_changed(path));
}
SelectLayers(paths) => {
for path in paths {
self.active_document_mut().layer_data(&path).selected ^= true;
responses.extend(self.handle_folder_changed(path));
// TODO: Add deduplication
}
}
Undo => {
// this is a temporary fix and will be addressed by #123
if let Some(id) = self.active_document().document.root.list_layers().last() {
@@ -93,7 +111,15 @@ impl MessageHandler<DocumentMessage, ()> for DocumentMessageHandler {
DispatchOperation(op) => {
if let Ok(Some(mut document_responses)) = self.active_document_mut().document.handle_operation(op) {
let canvas_dirty = self.filter_document_responses(&mut document_responses);
responses.extend(document_responses.into_iter().map(Into::into));
responses.extend(
document_responses
.into_iter()
.map(|response| match response {
DocumentResponse::FolderChanged { path } => self.handle_folder_changed(path),
DocumentResponse::DocumentChanged => unreachable!(),
})
.flatten(),
);
if canvas_dirty {
responses.push_back(RenderDocument.into())
}
+1 -1
View File
@@ -2,7 +2,7 @@ mod document_file;
mod document_message_handler;
#[doc(inline)]
pub use document_file::Document;
pub use document_file::{Document, LayerData};
#[doc(inline)]
pub use document_message_handler::{DocumentMessage, DocumentMessageDiscriminant, DocumentMessageHandler};
@@ -1,5 +1,5 @@
use crate::frontend::layer_panel::LayerPanelEntry;
use crate::message_prelude::*;
use document_core::{response::LayerPanelEntry, DocumentResponse, LayerId};
use serde::{Deserialize, Serialize};
pub type Callback = Box<dyn Fn(FrontendMessage)>;
@@ -16,22 +16,6 @@ pub enum FrontendMessage {
DisableTextInput,
}
impl From<DocumentResponse> for Message {
fn from(response: DocumentResponse) -> Self {
let frontend: FrontendMessage = response.into();
frontend.into()
}
}
impl From<DocumentResponse> for FrontendMessage {
fn from(response: DocumentResponse) -> Self {
match response {
DocumentResponse::ExpandFolder { path, children } => Self::ExpandFolder { path, children },
DocumentResponse::CollapseFolder { path } => Self::CollapseFolder { path },
_ => unimplemented!("The frontend does not handle {:?}", response),
}
}
}
pub struct FrontendMessageHandler {
callback: crate::Callback,
}
+55
View File
@@ -0,0 +1,55 @@
use crate::document::LayerData;
use document_core::{layers::LayerDataTypes, LayerId};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct LayerPanelEntry {
pub name: String,
pub visible: bool,
pub layer_type: LayerType,
pub layer_data: LayerData,
pub path: Vec<LayerId>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum LayerType {
Folder,
Shape,
Circle,
Rect,
Line,
PolyLine,
Ellipse,
}
impl fmt::Display for LayerType {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let name = match self {
LayerType::Folder => "Folder",
LayerType::Shape => "Shape",
LayerType::Rect => "Rectangle",
LayerType::Line => "Line",
LayerType::Circle => "Circle",
LayerType::PolyLine => "Polyline",
LayerType::Ellipse => "Ellipse",
};
formatter.write_str(name)
}
}
impl From<&LayerDataTypes> for LayerType {
fn from(data: &LayerDataTypes) -> Self {
use LayerDataTypes::*;
match data {
Folder(_) => LayerType::Folder,
Shape(_) => LayerType::Shape,
Circle(_) => LayerType::Circle,
Rect(_) => LayerType::Rect,
Line(_) => LayerType::Line,
PolyLine(_) => LayerType::PolyLine,
Ellipse(_) => LayerType::Ellipse,
}
}
}
+1
View File
@@ -1,3 +1,4 @@
pub mod frontend_message_handler;
pub mod layer_panel;
pub use frontend_message_handler::{Callback, FrontendMessage, FrontendMessageDiscriminant, FrontendMessageHandler};
+1
View File
@@ -65,6 +65,7 @@ pub mod message_prelude {
pub use super::tool::tools::rectangle::{RectangleMessage, RectangleMessageDiscriminant};
pub use super::tool::tools::select::{SelectMessage, SelectMessageDiscriminant};
pub use super::tool::tools::shape::{ShapeMessage, ShapeMessageDiscriminant};
pub use crate::LayerId;
pub use graphite_proc_macros::*;
pub use std::collections::VecDeque;
}