196 lines
6.0 KiB
Rust
196 lines
6.0 KiB
Rust
use crate::message_prelude::*;
|
|
use graphene::color::Color;
|
|
|
|
use crate::input::InputPreprocessor;
|
|
use crate::{
|
|
document::DocumentMessageHandler,
|
|
tool::{tool_options::ToolOptions, DocumentToolData, ToolFsmState, ToolType},
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::VecDeque;
|
|
|
|
#[impl_message(Message, Tool)]
|
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
|
pub enum ToolMessage {
|
|
ActivateTool(ToolType),
|
|
SelectPrimaryColor(Color),
|
|
SelectSecondaryColor(Color),
|
|
SelectedLayersChanged,
|
|
SwapColors,
|
|
ResetColors,
|
|
NoOp,
|
|
SetToolOptions(ToolType, ToolOptions),
|
|
#[child]
|
|
Fill(FillMessage),
|
|
#[child]
|
|
Rectangle(RectangleMessage),
|
|
#[child]
|
|
Ellipse(EllipseMessage),
|
|
#[child]
|
|
Select(SelectMessage),
|
|
#[child]
|
|
Line(LineMessage),
|
|
#[child]
|
|
Crop(CropMessage),
|
|
#[child]
|
|
Eyedropper(EyedropperMessage),
|
|
#[child]
|
|
Navigate(NavigateMessage),
|
|
#[child]
|
|
Path(PathMessage),
|
|
#[child]
|
|
Pen(PenMessage),
|
|
#[child]
|
|
Shape(ShapeMessage),
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct ToolMessageHandler {
|
|
tool_state: ToolFsmState,
|
|
}
|
|
impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessor)> for ToolMessageHandler {
|
|
fn process_action(&mut self, message: ToolMessage, data: (&DocumentMessageHandler, &InputPreprocessor), responses: &mut VecDeque<Message>) {
|
|
let (document, input) = data;
|
|
use ToolMessage::*;
|
|
match message {
|
|
SelectPrimaryColor(color) => {
|
|
let document_data = &mut self.tool_state.document_tool_data;
|
|
document_data.primary_color = color;
|
|
|
|
update_working_colors(&self.tool_state.document_tool_data, responses);
|
|
}
|
|
SelectSecondaryColor(color) => {
|
|
let document_data = &mut self.tool_state.document_tool_data;
|
|
document_data.secondary_color = color;
|
|
|
|
update_working_colors(document_data, responses);
|
|
}
|
|
ActivateTool(new_tool) => {
|
|
let tool_data = &mut self.tool_state.tool_data;
|
|
let document_data = &self.tool_state.document_tool_data;
|
|
let old_tool = tool_data.active_tool_type;
|
|
|
|
// Do nothing if switching to the same tool
|
|
if new_tool == old_tool {
|
|
return;
|
|
}
|
|
|
|
// Get the Abort state of a tool's FSM
|
|
let reset_message = |tool| match tool {
|
|
ToolType::Select => Some(SelectMessage::Abort.into()),
|
|
ToolType::Path => Some(PathMessage::Abort.into()),
|
|
ToolType::Pen => Some(PenMessage::Abort.into()),
|
|
ToolType::Line => Some(LineMessage::Abort.into()),
|
|
ToolType::Rectangle => Some(RectangleMessage::Abort.into()),
|
|
ToolType::Ellipse => Some(EllipseMessage::Abort.into()),
|
|
ToolType::Shape => Some(ShapeMessage::Abort.into()),
|
|
_ => None,
|
|
};
|
|
|
|
// Send the Abort state transition to the tool
|
|
let mut send_message_to_tool = |tool_type, message: ToolMessage| {
|
|
if let Some(tool) = tool_data.tools.get_mut(&tool_type) {
|
|
tool.process_action(message, (document, document_data, input), responses);
|
|
}
|
|
};
|
|
|
|
// Send the old and new tools a transition to their FSM Abort states
|
|
if let Some(tool_message) = reset_message(new_tool) {
|
|
send_message_to_tool(new_tool, tool_message);
|
|
}
|
|
if let Some(tool_message) = reset_message(old_tool) {
|
|
send_message_to_tool(old_tool, tool_message);
|
|
}
|
|
|
|
// Special cases for specific tools
|
|
// TODO: Refactor to avoid doing this here
|
|
if new_tool == ToolType::Select || new_tool == ToolType::Path {
|
|
responses.push_back(ToolMessage::SelectedLayersChanged.into());
|
|
}
|
|
|
|
// Store the new active tool
|
|
tool_data.active_tool_type = new_tool;
|
|
|
|
// Notify the frontend about the new active tool to be displayed
|
|
let tool_name = new_tool.to_string();
|
|
let tool_options = self.tool_state.document_tool_data.tool_options.get(&new_tool).map(|tool_options| *tool_options);
|
|
responses.push_back(FrontendMessage::SetActiveTool { tool_name, tool_options }.into());
|
|
}
|
|
SelectedLayersChanged => {
|
|
match self.tool_state.tool_data.active_tool_type {
|
|
ToolType::Select => responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into()),
|
|
ToolType::Path => responses.push_back(PathMessage::RedrawOverlay.into()),
|
|
_ => (),
|
|
};
|
|
}
|
|
SwapColors => {
|
|
let document_data = &mut self.tool_state.document_tool_data;
|
|
|
|
std::mem::swap(&mut document_data.primary_color, &mut document_data.secondary_color);
|
|
|
|
update_working_colors(document_data, responses);
|
|
}
|
|
ResetColors => {
|
|
let document_data = &mut self.tool_state.document_tool_data;
|
|
|
|
document_data.primary_color = Color::BLACK;
|
|
document_data.secondary_color = Color::WHITE;
|
|
|
|
update_working_colors(document_data, responses);
|
|
}
|
|
SetToolOptions(tool_type, tool_options) => {
|
|
let document_data = &mut self.tool_state.document_tool_data;
|
|
|
|
document_data.tool_options.insert(tool_type, tool_options);
|
|
}
|
|
message => {
|
|
let tool_type = message_to_tool_type(&message);
|
|
let document_data = &self.tool_state.document_tool_data;
|
|
let tool_data = &mut self.tool_state.tool_data;
|
|
|
|
if let Some(tool) = tool_data.tools.get_mut(&tool_type) {
|
|
if tool_type == tool_data.active_tool_type {
|
|
tool.process_action(message, (document, document_data, input), responses);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fn actions(&self) -> ActionList {
|
|
let mut list = actions!(ToolMessageDiscriminant; ResetColors, SwapColors, ActivateTool, SetToolOptions);
|
|
list.extend(self.tool_state.tool_data.active_tool().actions());
|
|
|
|
list
|
|
}
|
|
}
|
|
|
|
fn message_to_tool_type(message: &ToolMessage) -> ToolType {
|
|
use ToolMessage::*;
|
|
let tool_type = match message {
|
|
Fill(_) => ToolType::Fill,
|
|
Rectangle(_) => ToolType::Rectangle,
|
|
Ellipse(_) => ToolType::Ellipse,
|
|
Shape(_) => ToolType::Shape,
|
|
Line(_) => ToolType::Line,
|
|
Pen(_) => ToolType::Pen,
|
|
Select(_) => ToolType::Select,
|
|
Crop(_) => ToolType::Crop,
|
|
Eyedropper(_) => ToolType::Eyedropper,
|
|
Navigate(_) => ToolType::Navigate,
|
|
Path(_) => ToolType::Path,
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
tool_type
|
|
}
|
|
|
|
fn update_working_colors(document_data: &DocumentToolData, responses: &mut VecDeque<Message>) {
|
|
responses.push_back(
|
|
FrontendMessage::UpdateWorkingColors {
|
|
primary: document_data.primary_color,
|
|
secondary: document_data.secondary_color,
|
|
}
|
|
.into(),
|
|
);
|
|
}
|