use crate::{frontend::FrontendMessageHandler, message_prelude::*, Callback, EditorError}; pub use crate::document::DocumentMessageHandler; pub use crate::input::{InputMapper, InputPreprocessor}; pub use crate::tool::ToolMessageHandler; use crate::global::GlobalMessageHandler; use std::collections::VecDeque; pub struct Dispatcher { frontend_message_handler: FrontendMessageHandler, input_preprocessor: InputPreprocessor, input_mapper: InputMapper, global_message_handler: GlobalMessageHandler, tool_message_handler: ToolMessageHandler, document_message_handler: DocumentMessageHandler, messages: VecDeque, } impl Dispatcher { pub fn handle_message>(&mut self, message: T) -> Result<(), EditorError> { let message = message.into(); use Message::*; if !(matches!( message, Message::InputPreprocessor(_) | Message::InputMapper(_) | Message::Document(DocumentMessage::RenderDocument) | Message::Frontend(FrontendMessage::UpdateCanvas { .. }) | Message::Frontend(FrontendMessage::SetCanvasZoom { .. }) | Message::Frontend(FrontendMessage::SetCanvasRotation { .. }) | Message::Document(DocumentMessage::DispatchOperation { .. }) ) || MessageDiscriminant::from(&message).local_name().ends_with("MouseMove")) { log::trace!("Message: {}", message.to_discriminant().local_name()); log::trace!("Hints:{}", self.input_mapper.hints(self.collect_actions())); } match message { NoOp => (), Document(message) => self.document_message_handler.process_action(message, &self.input_preprocessor, &mut self.messages), Global(message) => self.global_message_handler.process_action(message, (), &mut self.messages), Tool(message) => self .tool_message_handler .process_action(message, (&self.document_message_handler.active_document(), &self.input_preprocessor), &mut self.messages), Frontend(message) => self.frontend_message_handler.process_action(message, (), &mut self.messages), InputPreprocessor(message) => self.input_preprocessor.process_action(message, (), &mut self.messages), InputMapper(message) => { let actions = self.collect_actions(); self.input_mapper.process_action(message, (&self.input_preprocessor, actions), &mut self.messages) } } if let Some(message) = self.messages.pop_front() { self.handle_message(message)?; } Ok(()) } pub fn collect_actions(&self) -> ActionList { //TODO: reduce the number of heap allocations let mut list = Vec::new(); list.extend(self.frontend_message_handler.actions()); list.extend(self.input_preprocessor.actions()); list.extend(self.input_mapper.actions()); list.extend(self.global_message_handler.actions()); list.extend(self.tool_message_handler.actions()); list.extend(self.document_message_handler.actions()); list } pub fn new(callback: Callback) -> Dispatcher { Dispatcher { frontend_message_handler: FrontendMessageHandler::new(callback), input_preprocessor: InputPreprocessor::default(), global_message_handler: GlobalMessageHandler::new(), input_mapper: InputMapper::default(), document_message_handler: DocumentMessageHandler::default(), tool_message_handler: ToolMessageHandler::default(), messages: VecDeque::new(), } } } #[cfg(test)] mod test { use crate::{ communication::DocumentMessageHandler, message_prelude::{DocumentMessage, Message}, misc::test_utils::EditorTestUtils, Editor, }; use document_core::{color::Color, Operation}; use log::info; fn init_logger() { let _ = env_logger::builder().is_test(true).try_init(); } /// Create an editor instance with three layers /// 1. A red rectangle /// 2. A blue shape /// 3. A green ellipse fn create_editor_with_three_layers() -> Editor { let mut editor = Editor::new(Box::new(|e| { info!("Got frontend message: {:?}", e); })); editor.select_primary_color(Color::RED); editor.draw_rect(100, 200, 300, 400); editor.select_primary_color(Color::BLUE); editor.draw_shape(10, 1200, 1300, 400); editor.select_primary_color(Color::GREEN); editor.draw_ellipse(104, 1200, 1300, 400); editor } #[test] /// - create rect, shape and ellipse /// - copy /// - paste /// - assert that ellipse was copied fn copy_paste_single_layer() { init_logger(); let mut editor = create_editor_with_three_layers(); let document_before_copy = editor.dispatcher.document_message_handler.active_document().document.clone(); editor.handle_message(Message::Document(DocumentMessage::CopySelectedLayers)).unwrap(); editor.handle_message(Message::Document(DocumentMessage::PasteLayers { path: vec![], insert_index: -1 })).unwrap(); let document_after_copy = editor.dispatcher.document_message_handler.active_document().document.clone(); let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers(); let layers_after_copy = document_after_copy.root.as_folder().unwrap().layers(); assert_eq!(layers_before_copy.len(), 3); assert_eq!(layers_after_copy.len(), 4); // Existing layers are unaffected for i in 0..=2 { assert_eq!(layers_before_copy[i], layers_after_copy[i]); } // The ellipse was copied assert_eq!(layers_before_copy[2], layers_after_copy[3]); } #[test] /// - create rect, shape and ellipse /// - select shape /// - copy /// - paste /// - assert that shape was copied fn copy_paste_single_layer_from_middle() { init_logger(); let mut editor = create_editor_with_three_layers(); let document_before_copy = editor.dispatcher.document_message_handler.active_document().document.clone(); let shape_id = document_before_copy.root.as_folder().unwrap().layer_ids[1]; editor.handle_message(Message::Document(DocumentMessage::SelectLayers(vec![vec![shape_id]]))).unwrap(); editor.handle_message(Message::Document(DocumentMessage::CopySelectedLayers)).unwrap(); editor.handle_message(Message::Document(DocumentMessage::PasteLayers { path: vec![], insert_index: -1 })).unwrap(); let document_after_copy = editor.dispatcher.document_message_handler.active_document().document.clone(); let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers(); let layers_after_copy = document_after_copy.root.as_folder().unwrap().layers(); assert_eq!(layers_before_copy.len(), 3); assert_eq!(layers_after_copy.len(), 4); // Existing layers are unaffected for i in 0..=2 { assert_eq!(layers_before_copy[i], layers_after_copy[i]); } // The shape was copied assert_eq!(layers_before_copy[1], layers_after_copy[3]); } #[test] fn copy_paste_folder() { init_logger(); let mut editor = create_editor_with_three_layers(); const FOLDER_INDEX: usize = 3; const ELLIPSE_INDEX: usize = 2; const SHAPE_INDEX: usize = 1; const RECT_INDEX: usize = 0; const LINE_INDEX: usize = 0; const PEN_INDEX: usize = 1; editor.handle_message(Message::Document(DocumentMessage::AddFolder(vec![]))).unwrap(); let document_before_added_shapes = editor.dispatcher.document_message_handler.active_document().document.clone(); let folder_id = document_before_added_shapes.root.as_folder().unwrap().layer_ids[FOLDER_INDEX]; // TODO: This adding of a Line and Pen should be rewritten using the corresponding functions in EditorTestUtils. // This has not been done yet as the line and pen tool are not yet able to add layers to the currently selected folder editor .handle_message(Message::Document(DocumentMessage::DispatchOperation(Operation::AddLine { path: vec![folder_id], insert_index: 0, transform: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], style: Default::default(), }))) .unwrap(); editor .handle_message(Message::Document(DocumentMessage::DispatchOperation(Operation::AddPen { path: vec![folder_id], insert_index: 0, transform: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], style: Default::default(), points: vec![(10.0, 20.0), (30.0, 40.0)], }))) .unwrap(); editor.handle_message(Message::Document(DocumentMessage::SelectLayers(vec![vec![folder_id]]))).unwrap(); let document_before_copy = editor.dispatcher.document_message_handler.active_document().document.clone(); editor.handle_message(Message::Document(DocumentMessage::CopySelectedLayers)).unwrap(); editor.handle_message(Message::Document(DocumentMessage::DeleteSelectedLayers)).unwrap(); editor.handle_message(Message::Document(DocumentMessage::PasteLayers { path: vec![], insert_index: -1 })).unwrap(); editor.handle_message(Message::Document(DocumentMessage::PasteLayers { path: vec![], insert_index: -1 })).unwrap(); let document_after_copy = editor.dispatcher.document_message_handler.active_document().document.clone(); let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers(); let layers_after_copy = document_after_copy.root.as_folder().unwrap().layers(); assert_eq!(layers_before_copy.len(), 4); assert_eq!(layers_after_copy.len(), 5); let rect_before_copy = &layers_before_copy[RECT_INDEX]; let ellipse_before_copy = &layers_before_copy[ELLIPSE_INDEX]; let shape_before_copy = &layers_before_copy[SHAPE_INDEX]; let folder_before_copy = &layers_before_copy[FOLDER_INDEX]; let line_before_copy = folder_before_copy.as_folder().unwrap().layers()[LINE_INDEX].clone(); let pen_before_copy = folder_before_copy.as_folder().unwrap().layers()[PEN_INDEX].clone(); assert_eq!(&layers_after_copy[0], rect_before_copy); assert_eq!(&layers_after_copy[1], shape_before_copy); assert_eq!(&layers_after_copy[2], ellipse_before_copy); assert_eq!(&layers_after_copy[3], folder_before_copy); assert_eq!(&layers_after_copy[4], folder_before_copy); // Check the layers inside the two folders let first_folder_layers_after_copy = layers_after_copy[3].as_folder().unwrap().layers(); let second_folder_layers_after_copy = layers_after_copy[4].as_folder().unwrap().layers(); assert_eq!(first_folder_layers_after_copy.len(), 2); assert_eq!(second_folder_layers_after_copy.len(), 2); assert_eq!(first_folder_layers_after_copy[0], line_before_copy); assert_eq!(first_folder_layers_after_copy[1], pen_before_copy); assert_eq!(second_folder_layers_after_copy[0], line_before_copy); assert_eq!(second_folder_layers_after_copy[1], pen_before_copy); } #[test] /// - create rect, shape and ellipse /// - select ellipse and rect /// - copy /// - delete /// - create another rect /// - paste /// - paste fn copy_paste_deleted_layers() { init_logger(); let mut editor = create_editor_with_three_layers(); const ELLIPSE_INDEX: usize = 2; const SHAPE_INDEX: usize = 1; const RECT_INDEX: usize = 0; let document_before_copy = editor.dispatcher.document_message_handler.active_document().document.clone(); let rect_id = document_before_copy.root.as_folder().unwrap().layer_ids[RECT_INDEX]; let ellipse_id = document_before_copy.root.as_folder().unwrap().layer_ids[ELLIPSE_INDEX]; editor.handle_message(Message::Document(DocumentMessage::SelectLayers(vec![vec![rect_id], vec![ellipse_id]]))).unwrap(); editor.handle_message(Message::Document(DocumentMessage::CopySelectedLayers)).unwrap(); editor.handle_message(Message::Document(DocumentMessage::DeleteSelectedLayers)).unwrap(); editor.draw_rect(0, 800, 12, 200); editor.handle_message(Message::Document(DocumentMessage::PasteLayers { path: vec![], insert_index: -1 })).unwrap(); editor.handle_message(Message::Document(DocumentMessage::PasteLayers { path: vec![], insert_index: -1 })).unwrap(); let document_after_copy = editor.dispatcher.document_message_handler.active_document().document.clone(); let layers_before_copy = document_before_copy.root.as_folder().unwrap().layers(); let layers_after_copy = document_after_copy.root.as_folder().unwrap().layers(); assert_eq!(layers_before_copy.len(), 3); assert_eq!(layers_after_copy.len(), 6); let rect_before_copy = &layers_before_copy[RECT_INDEX]; let ellipse_before_copy = &layers_before_copy[ELLIPSE_INDEX]; assert_eq!(layers_after_copy[0], layers_before_copy[SHAPE_INDEX]); assert_eq!(&layers_after_copy[2], rect_before_copy); assert_eq!(&layers_after_copy[3], ellipse_before_copy); assert_eq!(&layers_after_copy[4], rect_before_copy); assert_eq!(&layers_after_copy[5], ellipse_before_copy); } #[test] /// - create rect, shape and ellipse /// - select ellipse and rect /// - move them down and back up again fn move_seletion() { init_logger(); let mut editor = create_editor_with_three_layers(); let verify_order = |handler: &mut DocumentMessageHandler| (handler.all_layers_sorted(), handler.non_selected_layers_sorted(), handler.selected_layers_sorted()); editor.handle_message(Message::Document(DocumentMessage::SelectLayers(vec![vec![0], vec![2]]))).unwrap(); editor.handle_message(Message::Document(DocumentMessage::ReorderSelectedLayers(1))).unwrap(); let (all, non_selected, selected) = verify_order(&mut editor.dispatcher.document_message_handler); assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::>()); editor.handle_message(Message::Document(DocumentMessage::ReorderSelectedLayers(-1))).unwrap(); let (all, non_selected, selected) = verify_order(&mut editor.dispatcher.document_message_handler); assert_eq!(all, selected.into_iter().chain(non_selected.into_iter()).collect::>()); editor.handle_message(Message::Document(DocumentMessage::ReorderSelectedLayers(i32::MAX))).unwrap(); let (all, non_selected, selected) = verify_order(&mut editor.dispatcher.document_message_handler); assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::>()); } }