mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 16:28:11 +08:00
Massively reorganize and clean up the whole Rust codebase (#478)
* Massively reorganize and clean up the whole Rust codebase * Additional changes during code review
This commit is contained in:
@@ -1,26 +1,31 @@
|
||||
use crate::message_prelude::*;
|
||||
|
||||
pub use crate::document::PortfolioMessageHandler;
|
||||
pub use crate::input::{InputMapper, InputPreprocessor};
|
||||
pub use crate::tool::ToolMessageHandler;
|
||||
|
||||
use crate::document::PortfolioMessageHandler;
|
||||
use crate::global::GlobalMessageHandler;
|
||||
use crate::input::{InputMapperMessageHandler, InputPreprocessorMessageHandler};
|
||||
use crate::message_prelude::*;
|
||||
use crate::viewport_tools::tool_message_handler::ToolMessageHandler;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Dispatcher {
|
||||
input_preprocessor: InputPreprocessor,
|
||||
input_mapper: InputMapper,
|
||||
global_message_handler: GlobalMessageHandler,
|
||||
tool_message_handler: ToolMessageHandler,
|
||||
portfolio_message_handler: PortfolioMessageHandler,
|
||||
messages: VecDeque<Message>,
|
||||
message_queue: VecDeque<Message>,
|
||||
pub responses: Vec<FrontendMessage>,
|
||||
message_handlers: DispatcherMessageHandlers,
|
||||
}
|
||||
|
||||
// For optimization, these are messages guaranteed to be redundant when repeated
|
||||
// The last occurrence of the message in the message queue is sufficient to ensure correctness
|
||||
// In addition, these messages do not change any state in the backend (aside from caches)
|
||||
#[remain::sorted]
|
||||
#[derive(Debug, Default)]
|
||||
struct DispatcherMessageHandlers {
|
||||
global_message_handler: GlobalMessageHandler,
|
||||
input_mapper_message_handler: InputMapperMessageHandler,
|
||||
input_preprocessor_message_handler: InputPreprocessorMessageHandler,
|
||||
portfolio_message_handler: PortfolioMessageHandler,
|
||||
tool_message_handler: ToolMessageHandler,
|
||||
}
|
||||
|
||||
// For optimization, these are messages guaranteed to be redundant when repeated.
|
||||
// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
|
||||
// In addition, these messages do not change any state in the backend (aside from caches).
|
||||
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)),
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::FolderChanged)),
|
||||
@@ -35,28 +40,54 @@ impl Dispatcher {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
#[remain::check]
|
||||
pub fn handle_message<T: Into<Message>>(&mut self, message: T) {
|
||||
self.messages.push_back(message.into());
|
||||
self.message_queue.push_back(message.into());
|
||||
|
||||
use Message::*;
|
||||
while let Some(message) = self.messages.pop_front() {
|
||||
while let Some(message) = self.message_queue.pop_front() {
|
||||
// Skip processing of this message if it will be processed later
|
||||
if SIDE_EFFECT_FREE_MESSAGES.contains(&message.to_discriminant()) && self.messages.contains(&message) {
|
||||
if SIDE_EFFECT_FREE_MESSAGES.contains(&message.to_discriminant()) && self.message_queue.contains(&message) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Print the message at a verbosity level of `log`
|
||||
self.log_message(&message);
|
||||
|
||||
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
|
||||
#[remain::sorted]
|
||||
match message {
|
||||
NoOp => (),
|
||||
Portfolio(message) => self.portfolio_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.portfolio_message_handler.active_document(), &self.input_preprocessor), &mut self.messages),
|
||||
Frontend(message) => self.responses.push(message),
|
||||
InputPreprocessor(message) => self.input_preprocessor.process_action(message, (), &mut self.messages),
|
||||
Frontend(message) => {
|
||||
// `FrontendMessage`s are saved and will be sent to the frontend after the message queue is done being processed
|
||||
self.responses.push(message);
|
||||
}
|
||||
Global(message) => {
|
||||
self.message_handlers.global_message_handler.process_action(message, (), &mut self.message_queue);
|
||||
}
|
||||
InputMapper(message) => {
|
||||
let actions = self.collect_actions();
|
||||
self.input_mapper.process_action(message, (&self.input_preprocessor, actions), &mut self.messages)
|
||||
self.message_handlers
|
||||
.input_mapper_message_handler
|
||||
.process_action(message, (&self.message_handlers.input_preprocessor_message_handler, actions), &mut self.message_queue);
|
||||
}
|
||||
InputPreprocessor(message) => {
|
||||
self.message_handlers.input_preprocessor_message_handler.process_action(message, (), &mut self.message_queue);
|
||||
}
|
||||
NoOp => {}
|
||||
Portfolio(message) => {
|
||||
self.message_handlers
|
||||
.portfolio_message_handler
|
||||
.process_action(message, &self.message_handlers.input_preprocessor_message_handler, &mut self.message_queue);
|
||||
}
|
||||
Tool(message) => {
|
||||
self.message_handlers.tool_message_handler.process_action(
|
||||
message,
|
||||
(
|
||||
self.message_handlers.portfolio_message_handler.active_document(),
|
||||
&self.message_handlers.input_preprocessor_message_handler,
|
||||
),
|
||||
&mut self.message_queue,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,11 +96,11 @@ impl Dispatcher {
|
||||
pub fn collect_actions(&self) -> ActionList {
|
||||
// TODO: Reduce the number of heap allocations
|
||||
let mut list = Vec::new();
|
||||
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.portfolio_message_handler.actions());
|
||||
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
|
||||
list.extend(self.message_handlers.input_mapper_message_handler.actions());
|
||||
list.extend(self.message_handlers.global_message_handler.actions());
|
||||
list.extend(self.message_handlers.tool_message_handler.actions());
|
||||
list.extend(self.message_handlers.portfolio_message_handler.actions());
|
||||
list
|
||||
}
|
||||
|
||||
@@ -82,21 +113,21 @@ impl Dispatcher {
|
||||
) || MessageDiscriminant::from(message).local_name().ends_with("MouseMove"))
|
||||
{
|
||||
log::trace!("Message: {:?}", message);
|
||||
// log::trace!("Hints: {:?}", self.input_mapper.hints(self.collect_actions()));
|
||||
// log::trace!("Hints: {:?}", self.input_mapper_message_handler.hints(self.collect_actions()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
communication::set_uuid_seed,
|
||||
document::{Clipboard::*, DocumentMessageHandler},
|
||||
message_prelude::*,
|
||||
misc::test_utils::EditorTestUtils,
|
||||
Editor,
|
||||
};
|
||||
use graphene::{color::Color, Operation};
|
||||
use crate::communication::set_uuid_seed;
|
||||
use crate::document::clipboards::Clipboard;
|
||||
use crate::document::DocumentMessageHandler;
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::test_utils::EditorTestUtils;
|
||||
use crate::Editor;
|
||||
use graphene::color::Color;
|
||||
use graphene::Operation;
|
||||
|
||||
fn init_logger() {
|
||||
let _ = env_logger::builder().is_test(true).try_init();
|
||||
@@ -129,14 +160,14 @@ mod test {
|
||||
init_logger();
|
||||
let mut editor = create_editor_with_three_layers();
|
||||
|
||||
let document_before_copy = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
editor.handle_message(PortfolioMessage::Copy(User));
|
||||
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
editor.handle_message(PortfolioMessage::Copy(Clipboard::User));
|
||||
editor.handle_message(PortfolioMessage::PasteIntoFolder {
|
||||
clipboard: User,
|
||||
clipboard: Clipboard::User,
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
});
|
||||
let document_after_copy = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_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();
|
||||
@@ -163,18 +194,18 @@ mod test {
|
||||
init_logger();
|
||||
let mut editor = create_editor_with_three_layers();
|
||||
|
||||
let document_before_copy = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let shape_id = document_before_copy.root.as_folder().unwrap().layer_ids[1];
|
||||
|
||||
editor.handle_message(DocumentMessage::SetSelectedLayers(vec![vec![shape_id]]));
|
||||
editor.handle_message(PortfolioMessage::Copy(User));
|
||||
editor.handle_message(PortfolioMessage::Copy(Clipboard::User));
|
||||
editor.handle_message(PortfolioMessage::PasteIntoFolder {
|
||||
clipboard: User,
|
||||
clipboard: Clipboard::User,
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
});
|
||||
|
||||
let document_after_copy = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_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();
|
||||
@@ -206,7 +237,7 @@ mod test {
|
||||
|
||||
editor.handle_message(DocumentMessage::CreateEmptyFolder(vec![]));
|
||||
|
||||
let document_before_added_shapes = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let document_before_added_shapes = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_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.
|
||||
@@ -228,22 +259,22 @@ mod test {
|
||||
|
||||
editor.handle_message(DocumentMessage::SetSelectedLayers(vec![vec![folder_id]]));
|
||||
|
||||
let document_before_copy = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
|
||||
editor.handle_message(PortfolioMessage::Copy(User));
|
||||
editor.handle_message(PortfolioMessage::Copy(Clipboard::User));
|
||||
editor.handle_message(DocumentMessage::DeleteSelectedLayers);
|
||||
editor.handle_message(PortfolioMessage::PasteIntoFolder {
|
||||
clipboard: User,
|
||||
clipboard: Clipboard::User,
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
});
|
||||
editor.handle_message(PortfolioMessage::PasteIntoFolder {
|
||||
clipboard: User,
|
||||
clipboard: Clipboard::User,
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
});
|
||||
|
||||
let document_after_copy = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_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();
|
||||
@@ -294,26 +325,26 @@ mod test {
|
||||
const SHAPE_INDEX: usize = 1;
|
||||
const RECT_INDEX: usize = 0;
|
||||
|
||||
let document_before_copy = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_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(DocumentMessage::SetSelectedLayers(vec![vec![rect_id], vec![ellipse_id]]));
|
||||
editor.handle_message(PortfolioMessage::Copy(User));
|
||||
editor.handle_message(PortfolioMessage::Copy(Clipboard::User));
|
||||
editor.handle_message(DocumentMessage::DeleteSelectedLayers);
|
||||
editor.draw_rect(0., 800., 12., 200.);
|
||||
editor.handle_message(PortfolioMessage::PasteIntoFolder {
|
||||
clipboard: User,
|
||||
clipboard: Clipboard::User,
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
});
|
||||
editor.handle_message(PortfolioMessage::PasteIntoFolder {
|
||||
clipboard: User,
|
||||
clipboard: Clipboard::User,
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
});
|
||||
|
||||
let document_after_copy = editor.dispatcher.portfolio_message_handler.active_document().graphene_document.clone();
|
||||
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().graphene_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();
|
||||
@@ -343,7 +374,7 @@ mod test {
|
||||
fn map_to_vec(paths: Vec<&[LayerId]>) -> Vec<Vec<LayerId>> {
|
||||
paths.iter().map(|layer| layer.to_vec()).collect::<Vec<_>>()
|
||||
}
|
||||
let sorted_layers = map_to_vec(editor.dispatcher.portfolio_message_handler.active_document().all_layers_sorted());
|
||||
let sorted_layers = map_to_vec(editor.dispatcher.message_handlers.portfolio_message_handler.active_document().all_layers_sorted());
|
||||
println!("Sorted layers: {:?}", sorted_layers);
|
||||
|
||||
let verify_order = |handler: &mut DocumentMessageHandler| {
|
||||
@@ -357,15 +388,15 @@ mod test {
|
||||
editor.handle_message(DocumentMessage::SetSelectedLayers(sorted_layers[..2].to_vec()));
|
||||
|
||||
editor.handle_message(DocumentMessage::ReorderSelectedLayers(1));
|
||||
let (all, non_selected, selected) = verify_order(editor.dispatcher.portfolio_message_handler.active_document_mut());
|
||||
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut());
|
||||
assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::<Vec<_>>());
|
||||
|
||||
editor.handle_message(DocumentMessage::ReorderSelectedLayers(-1));
|
||||
let (all, non_selected, selected) = verify_order(editor.dispatcher.portfolio_message_handler.active_document_mut());
|
||||
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut());
|
||||
assert_eq!(all, selected.into_iter().chain(non_selected.into_iter()).collect::<Vec<_>>());
|
||||
|
||||
editor.handle_message(DocumentMessage::ReorderSelectedLayers(i32::MAX));
|
||||
let (all, non_selected, selected) = verify_order(editor.dispatcher.portfolio_message_handler.active_document_mut());
|
||||
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut());
|
||||
assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphite_proc_macros::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::hash_map::DefaultHasher,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
pub trait AsMessage: TransitiveChild
|
||||
where
|
||||
@@ -16,22 +16,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Message {
|
||||
#[child]
|
||||
Frontend(FrontendMessage),
|
||||
#[child]
|
||||
Global(GlobalMessage),
|
||||
#[child]
|
||||
InputMapper(InputMapperMessage),
|
||||
#[child]
|
||||
InputPreprocessor(InputPreprocessorMessage),
|
||||
NoOp,
|
||||
#[child]
|
||||
Portfolio(PortfolioMessage),
|
||||
#[child]
|
||||
Global(GlobalMessage),
|
||||
#[child]
|
||||
Tool(ToolMessage),
|
||||
#[child]
|
||||
Frontend(FrontendMessage),
|
||||
#[child]
|
||||
InputPreprocessor(InputPreprocessorMessage),
|
||||
#[child]
|
||||
InputMapper(InputMapperMessage),
|
||||
}
|
||||
|
||||
impl Message {
|
||||
@@ -39,12 +40,13 @@ impl Message {
|
||||
///
|
||||
/// # Safety
|
||||
/// This function reads from uninitialized memory!!!
|
||||
/// Only use if you know what you are doing
|
||||
/// Only use if you know what you are doing.
|
||||
unsafe fn as_slice(&self) -> &[u8] {
|
||||
core::slice::from_raw_parts(self as *const Message as *const u8, std::mem::size_of::<Message>())
|
||||
}
|
||||
|
||||
/// Returns a pseudo hash that should uniquely identify the message.
|
||||
/// This is needed because `Hash` is not implemented for f64s
|
||||
/// This is needed because `Hash` is not implemented for `f64`s
|
||||
///
|
||||
/// # Safety
|
||||
/// This function reads from uninitialized memory but the generated value should be fine.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
pub use crate::communication::dispatcher::*;
|
||||
pub use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub type ActionList = Vec<Vec<MessageDiscriminant>>;
|
||||
|
||||
// TODO: Add Send + Sync requirement
|
||||
// Use something like rw locks for synchronization
|
||||
pub trait MessageHandlerData {}
|
||||
|
||||
pub trait MessageHandler<A: ToDiscriminant, T>
|
||||
where
|
||||
A::Discriminant: AsMessage,
|
||||
<A::Discriminant as TransitiveChild>::TopParent: TransitiveChild<Parent = <A::Discriminant as TransitiveChild>::TopParent, TopParent = <A::Discriminant as TransitiveChild>::TopParent> + AsMessage,
|
||||
{
|
||||
/// Return true if the Action is consumed.
|
||||
fn process_action(&mut self, action: A, data: T, responses: &mut VecDeque<Message>);
|
||||
|
||||
fn actions(&self) -> ActionList;
|
||||
}
|
||||
@@ -1,34 +1,17 @@
|
||||
pub mod dispatcher;
|
||||
pub mod message;
|
||||
use crate::message_prelude::*;
|
||||
pub use dispatcher::*;
|
||||
use rand_chacha::{
|
||||
rand_core::{RngCore, SeedableRng},
|
||||
ChaCha20Rng,
|
||||
};
|
||||
pub mod message_handler;
|
||||
|
||||
pub use crate::communication::dispatcher::*;
|
||||
pub use crate::input::InputPreprocessorMessageHandler;
|
||||
|
||||
use rand_chacha::rand_core::{RngCore, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use spin::Mutex;
|
||||
|
||||
pub use crate::input::InputPreprocessor;
|
||||
use std::{cell::Cell, collections::VecDeque};
|
||||
|
||||
pub type ActionList = Vec<Vec<MessageDiscriminant>>;
|
||||
use std::cell::Cell;
|
||||
|
||||
static RNG: Mutex<Option<ChaCha20Rng>> = Mutex::new(None);
|
||||
|
||||
// TODO: Add Send + Sync requirement
|
||||
// Use something like rw locks for synchronization
|
||||
pub trait MessageHandlerData {}
|
||||
|
||||
pub trait MessageHandler<A: ToDiscriminant, T>
|
||||
where
|
||||
A::Discriminant: AsMessage,
|
||||
<A::Discriminant as TransitiveChild>::TopParent: TransitiveChild<Parent = <A::Discriminant as TransitiveChild>::TopParent, TopParent = <A::Discriminant as TransitiveChild>::TopParent> + AsMessage,
|
||||
{
|
||||
/// Return true if the Action is consumed.
|
||||
fn process_action(&mut self, action: A, data: T, responses: &mut VecDeque<Message>);
|
||||
fn actions(&self) -> ActionList;
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub static UUID_SEED: Cell<Option<u64>> = Cell::new(None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user