New overlay system that reimplements how overlays are drawn (#418)

* New overlay system that reimplements how overlays are drawn

* Fix overlay message declaration

* Fix small mistake

* WIP (broken) changes to plumb the overlay document

* Fix confusion over messaging system architecture

* Removed log

* Overlay system working

* (broken) WIP overlay association

* Finish the overlay system (except test failure)

* Change back IDs in test

* Fixed test, but stilled fails due to revealed real problem with layer reordering selection

* Disable broken test that has a bug in issue #444

Co-authored-by: Dennis <dennis@kobert.dev>
Co-authored-by: otdavies <oliver@psyfer.io>
This commit is contained in:
Keavon Chambers
2021-12-30 09:48:39 -08:00
co-authored by Dennis otdavies
parent 1a70667f63
commit eb96eafe60
22 changed files with 416 additions and 313 deletions
+72 -65
View File
@@ -4,6 +4,7 @@ use std::collections::VecDeque;
use super::document_message_handler::CopyBufferEntry;
pub use super::layer_panel::*;
use super::movement_handler::{MovementMessage, MovementMessageHandler};
use super::overlay_message_handler::OverlayMessageHandler;
use super::transform_layer_handler::{TransformLayerMessage, TransformLayerMessageHandler};
use super::vectorize_layerdata;
@@ -14,16 +15,16 @@ use crate::input::InputPreprocessor;
use crate::message_prelude::*;
use crate::EditorError;
use graphene::layers::{style::ViewMode, BlendMode, LayerDataType};
use graphene::{document::Document as GrapheneDocument, DocumentError, LayerId};
use graphene::{DocumentResponse, Operation as DocumentOperation};
use glam::{DAffine2, DVec2};
use graphene::layers::Folder;
use kurbo::PathSeg;
use log::warn;
use serde::{Deserialize, Serialize};
use graphene::layers::{style::ViewMode, BlendMode, LayerDataType};
use graphene::{document::Document as GrapheneDocument, DocumentError, LayerId};
use graphene::{DocumentResponse, Operation as DocumentOperation};
type DocumentSave = (GrapheneDocument, HashMap<Vec<LayerId>, LayerData>);
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
@@ -75,6 +76,8 @@ pub struct DocumentMessageHandler {
#[serde(skip)]
movement_handler: MovementMessageHandler,
#[serde(skip)]
overlay_message_handler: OverlayMessageHandler,
#[serde(skip)]
transform_layer_handler: TransformLayerMessageHandler,
pub snapping_enabled: bool,
pub view_mode: ViewMode,
@@ -91,6 +94,7 @@ impl Default for DocumentMessageHandler {
layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(),
layer_range_selection_reference: Vec::new(),
movement_handler: MovementMessageHandler::default(),
overlay_message_handler: OverlayMessageHandler::default(),
transform_layer_handler: TransformLayerMessageHandler::default(),
snapping_enabled: true,
view_mode: ViewMode::default(),
@@ -106,6 +110,8 @@ pub enum DocumentMessage {
#[child]
TransformLayers(TransformLayerMessage),
DispatchOperation(Box<DocumentOperation>),
#[child]
Overlay(OverlayMessage),
UpdateLayerData {
path: Vec<LayerId>,
layer_data_entry: LayerData,
@@ -113,6 +119,7 @@ pub enum DocumentMessage {
SetSelectedLayers(Vec<Vec<LayerId>>),
AddSelectedLayers(Vec<Vec<LayerId>>),
SelectAllLayers,
DebugPrintDocument,
SelectLayer(Vec<LayerId>, bool, bool),
SelectionChanged,
DeselectAllLayers,
@@ -145,7 +152,6 @@ pub enum DocumentMessage {
Redo,
DocumentHistoryBackward,
DocumentHistoryForward,
ClearOverlays,
NudgeSelectedLayers(f64, f64),
AlignSelectedLayers(AlignAxis, AlignAggregate),
MoveSelectedLayersTo {
@@ -210,25 +216,31 @@ impl DocumentMessageHandler {
}
fn select_layer(&mut self, path: &[LayerId]) -> Option<Message> {
if self.graphene_document.layer(path).ok()?.overlay {
return None;
}
println!("Select_layer fail: {:?}", self.all_layers_sorted());
self.layer_data_mut(path).selected = true;
let data = self.layer_panel_entry(path.to_vec()).ok()?;
(!path.is_empty()).then(|| FrontendMessage::UpdateLayer { data }.into())
}
pub fn selected_layers_bounding_box(&self) -> Option<[DVec2; 2]> {
let paths = self.selected_layers();
pub fn selected_visible_layers_bounding_box(&self) -> Option<[DVec2; 2]> {
let paths = self.selected_visible_layers();
self.graphene_document.combined_viewport_bounding_box(paths)
}
// TODO: Consider moving this to some kind of overlay manager in the future
pub fn selected_layers_vector_points(&self) -> Vec<VectorManipulatorShape> {
pub fn selected_visible_layers_vector_points(&self) -> Vec<VectorManipulatorShape> {
let shapes = self.selected_layers().filter_map(|path_to_shape| {
let viewport_transform = self.graphene_document.generate_transform_relative_to_viewport(path_to_shape).ok()?;
let layer = self.graphene_document.layer(path_to_shape);
let shape = match &self.graphene_document.layer(path_to_shape).ok()?.data {
// Filter out the non-visible layers from the filter_map
match &layer {
Ok(layer) if layer.visible => {}
_ => return None,
};
let shape = match &layer.ok()?.data {
LayerDataType::Shape(shape) => Some(shape),
LayerDataType::Folder(_) => None,
}?;
@@ -266,6 +278,13 @@ impl DocumentMessageHandler {
self.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.as_slice()))
}
pub fn selected_visible_layers(&self) -> impl Iterator<Item = &[LayerId]> {
self.selected_layers().filter(|path| match self.graphene_document.layer(path) {
Ok(layer) => layer.visible,
Err(_) => false,
})
}
fn serialize_structure(&self, folder: &Folder, structure: &mut Vec<u64>, data: &mut Vec<LayerId>, path: &mut Vec<LayerId>) {
let mut space = 0;
for (id, layer) in folder.layer_ids.iter().zip(folder.layers()) {
@@ -319,6 +338,11 @@ impl DocumentMessageHandler {
structure
}
/// Returns an unsorted list of all layer paths including folders at all levels, except the document's top-level root folder itself
pub fn all_layers(&self) -> Vec<Vec<LayerId>> {
self.layer_data.keys().filter(|path| !path.is_empty()).cloned().collect()
}
/// Returns the paths to all layers in order, optionally including only selected or non-selected layers.
fn layers_sorted(&self, selected: Option<bool>) -> Vec<Vec<LayerId>> {
// Compute the indices for each layer to be able to sort them
@@ -378,12 +402,7 @@ impl DocumentMessageHandler {
pub fn backup(&mut self, responses: &mut VecDeque<Message>) {
self.document_redo_history.clear();
let new_layer_data = self
.layer_data
.iter()
.filter_map(|(key, value)| (!self.graphene_document.layer(key).unwrap().overlay).then(|| (key.clone(), *value)))
.collect();
self.document_undo_history.push((self.graphene_document.clone_without_overlays(), new_layer_data));
self.document_undo_history.push((self.graphene_document.clone(), self.layer_data.clone()));
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
responses.push_back(DocumentsMessage::UpdateOpenDocumentsList.into());
@@ -418,11 +437,7 @@ impl DocumentMessageHandler {
Some((document, layer_data)) => {
let document = std::mem::replace(&mut self.graphene_document, document);
let layer_data = std::mem::replace(&mut self.layer_data, layer_data);
let new_layer_data = layer_data
.iter()
.filter_map(|(key, value)| (!self.graphene_document.layer(key).unwrap().overlay).then(|| (key.clone(), *value)))
.collect();
self.document_undo_history.push((document.clone_without_overlays(), new_layer_data));
self.document_undo_history.push((document.clone(), layer_data.clone()));
Ok(())
}
None => Err(EditorError::NoTransactionInProgress),
@@ -474,10 +489,7 @@ impl DocumentMessageHandler {
.ok()?;
let layer = self.graphene_document.layer(path).ok()?;
match layer.overlay {
true => None,
false => Some(layer_panel_entry(layer_data, transform, layer, path.to_vec())),
}
Some(layer_panel_entry(layer_data, transform, layer, path.to_vec()))
}
}
@@ -502,6 +514,11 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
responses.extend([RenderDocument.into(), DocumentStructureChanged.into()]);
}
CommitTransaction => (),
Overlay(message) => {
self.overlay_message_handler
.process_action(message, (Self::layer_data_mut_no_borrow_self(&mut self.layer_data, &[]), &self.graphene_document, ipp), responses);
// responses.push_back(OverlayMessage::RenderOverlays.into());
}
ExportDocument => {
let bbox = self.graphene_document.visible_layers_bounding_box().unwrap_or([DVec2::ZERO, ipp.viewport_bounds.size()]);
let size = bbox[1] - bbox[0];
@@ -588,6 +605,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
}
ToggleLayerVisibility(path) => {
responses.push_back(DocumentOperation::ToggleLayerVisibility { path }.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
}
ToggleLayerExpansion(path) => {
self.layer_data_mut(&path).expanded ^= true;
@@ -601,21 +619,15 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
}
SelectionChanged => {
// TODO: Hoist this duplicated code into wider system
responses.push_back(ToolMessage::SelectedLayersChanged.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
}
DeleteSelectedLayers => {
self.backup(responses);
responses.push_front(ToolMessage::SelectedLayersChanged.into());
responses.push_front(ToolMessage::DocumentIsDirty.into());
for path in self.selected_layers().map(|path| path.to_vec()) {
responses.push_front(DocumentOperation::DeleteLayer { path }.into());
}
}
ClearOverlays => {
responses.push_back(ToolMessage::SelectedLayersChanged.into());
for path in self.layer_data.keys().filter(|path| self.graphene_document.layer(path).unwrap().overlay).cloned() {
responses.push_front(DocumentOperation::DeleteLayer { path }.into());
}
}
SetViewMode(mode) => {
self.view_mode = mode;
responses.push_front(DocumentMessage::DirtyRenderDocument.into());
@@ -680,15 +692,13 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
}
// TODO: Correctly update layer panel in clear_selection instead of here
responses.push_back(FolderChanged(Vec::new()).into());
responses.push_back(ToolMessage::SelectedLayersChanged.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
}
DebugPrintDocument => {
log::debug!("{:#?}\n{:#?}", self.graphene_document, self.layer_data);
}
SelectAllLayers => {
let all_layer_paths = self
.layer_data
.keys()
.filter(|path| !path.is_empty() && !self.graphene_document.layer(path).map(|layer| layer.overlay).unwrap_or(false))
.cloned()
.collect::<Vec<_>>();
let all_layer_paths = self.all_layers();
responses.push_front(SetSelectedLayers(all_layer_paths).into());
}
DeselectAllLayers => {
@@ -700,14 +710,14 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
Undo => {
responses.push_back(SelectMessage::Abort.into());
responses.push_back(DocumentHistoryBackward.into());
responses.push_back(ToolMessage::SelectedLayersChanged.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
responses.push_back(RenderDocument.into());
responses.push_back(FolderChanged(vec![]).into());
}
Redo => {
responses.push_back(SelectMessage::Abort.into());
responses.push_back(DocumentHistoryForward.into());
responses.push_back(ToolMessage::SelectedLayersChanged.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
responses.push_back(RenderDocument.into());
responses.push_back(FolderChanged(vec![]).into());
}
@@ -720,41 +730,37 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
responses.push_back(FrontendMessage::DisplayFolderTreeStructure { data_buffer }.into())
}
LayerChanged(path) => {
responses.extend(self.layer_panel_entry(path.clone()).ok().and_then(|entry| {
let overlay = self.graphene_document.layer(&path).unwrap().overlay;
(!overlay).then(|| FrontendMessage::UpdateLayer { data: entry }.into())
}));
if let Ok(layer_entry) = self.layer_panel_entry(path) {
responses.push_back(FrontendMessage::UpdateLayer { data: layer_entry }.into());
}
}
DispatchOperation(op) => match self.graphene_document.handle_operation(&op) {
Ok(Some(document_responses)) => {
for response in document_responses {
match response {
DocumentResponse::FolderChanged { path } => responses.push_back(FolderChanged(path).into()),
match &response {
DocumentResponse::FolderChanged { path } => responses.push_back(FolderChanged(path.clone()).into()),
DocumentResponse::DeletedLayer { path } => {
self.layer_data.remove(&path);
responses.push_back(ToolMessage::SelectedLayersChanged.into())
self.layer_data.remove(path);
}
DocumentResponse::LayerChanged { path } => responses.push_back(LayerChanged(path).into()),
DocumentResponse::LayerChanged { path } => responses.push_back(LayerChanged(path.clone()).into()),
DocumentResponse::CreatedLayer { path } => {
self.layer_data.insert(path.clone(), LayerData::new(false));
responses.push_back(LayerChanged(path.clone()).into());
if !self.graphene_document.layer(&path).unwrap().overlay {
self.layer_range_selection_reference = path.clone();
responses.push_back(SetSelectedLayers(vec![path]).into());
}
self.layer_range_selection_reference = path.clone();
responses.push_back(SetSelectedLayers(vec![path.clone()]).into());
}
DocumentResponse::DocumentChanged => responses.push_back(RenderDocument.into()),
};
responses.push_back(ToolMessage::DocumentIsDirty.into());
}
// log::debug!("LayerPanel: {:?}", self.layer_data.keys());
}
Err(e) => log::error!("DocumentError: {:?}", e),
Ok(_) => (),
},
RenderDocument => {
responses.push_back(
FrontendMessage::UpdateCanvas {
document: self.graphene_document.render_root(self.view_mode),
FrontendMessage::UpdateArtwork {
svg: self.graphene_document.render_root(self.view_mode),
}
.into(),
);
@@ -815,7 +821,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
};
responses.push_back(operation.into());
}
responses.push_back(ToolMessage::SelectedLayersChanged.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
}
MoveSelectedLayersTo { path, insert_index } => {
responses.push_back(DocumentsMessage::Copy(Clipboard::System).into());
@@ -870,7 +876,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
FlipAxis::X => DVec2::new(-1., 1.),
FlipAxis::Y => DVec2::new(1., -1.),
};
if let Some([min, max]) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers().map(|x| x)) {
if let Some([min, max]) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers()) {
let center = (max + min) / 2.;
let bbox_trans = DAffine2::from_translation(-center);
for path in self.selected_layers() {
@@ -883,7 +889,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
.into(),
);
}
responses.push_back(ToolMessage::SelectedLayersChanged.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
}
}
AlignSelectedLayers(axis, aggregate) => {
@@ -898,7 +904,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.graphene_document.combined_viewport_bounding_box(self.selected_layers().map(|x| x)) {
if let Some(combined_box) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers()) {
let aggregated = match aggregate {
AlignAggregate::Min => combined_box[0],
AlignAggregate::Max => combined_box[1],
@@ -920,7 +926,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
.into(),
);
}
responses.push_back(ToolMessage::SelectedLayersChanged.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
}
}
RenameLayer(path, name) => responses.push_back(DocumentOperation::RenameLayer { path, name }.into()),
@@ -976,6 +982,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
ExportDocument,
SaveDocument,
SetSnapping,
DebugPrintDocument,
MoveLayerInTree,
);