| use super::common_functionality::shape_editor::ShapeState; |
| use super::common_functionality::shapes::shape_utility::ShapeType::{self, Ellipse, Line, Rectangle}; |
| use super::utility_types::{ToolActionHandlerData, ToolFsmState, tool_message_to_tool_type}; |
| use crate::application::generate_uuid; |
| use crate::messages::layout::utility_types::widget_prelude::*; |
| use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider; |
| use crate::messages::portfolio::utility_types::PersistentData; |
| use crate::messages::prelude::*; |
| use crate::messages::tool::utility_types::ToolType; |
| use crate::node_graph_executor::NodeGraphExecutor; |
| use graphene_std::raster::color::Color; |
|
|
| const ARTBOARD_OVERLAY_PROVIDER: OverlayProvider = |context| DocumentMessage::DrawArtboardOverlays(context).into(); |
|
|
| pub struct ToolMessageData<'a> { |
| pub document_id: DocumentId, |
| pub document: &'a mut DocumentMessageHandler, |
| pub input: &'a InputPreprocessorMessageHandler, |
| pub persistent_data: &'a PersistentData, |
| pub node_graph: &'a NodeGraphExecutor, |
| pub preferences: &'a PreferencesMessageHandler, |
| } |
|
|
| #[derive(Debug, Default)] |
| pub struct ToolMessageHandler { |
| pub tool_state: ToolFsmState, |
| pub transform_layer_handler: TransformLayerMessageHandler, |
| pub shape_editor: ShapeState, |
| pub tool_is_active: bool, |
| } |
|
|
| impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler { |
| fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, data: ToolMessageData) { |
| let ToolMessageData { |
| document_id, |
| document, |
| input, |
| persistent_data, |
| node_graph, |
| preferences, |
| } = data; |
| let font_cache = &persistent_data.font_cache; |
|
|
| match message { |
| |
| ToolMessage::TransformLayer(message) => self |
| .transform_layer_handler |
| .process_message(message, responses, (document, input, &self.tool_state.tool_data, &mut self.shape_editor)), |
|
|
| ToolMessage::ActivateToolSelect => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Select }), |
| ToolMessage::ActivateToolArtboard => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Artboard }), |
| ToolMessage::ActivateToolNavigate => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Navigate }), |
| ToolMessage::ActivateToolEyedropper => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Eyedropper }), |
| ToolMessage::ActivateToolText => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text }), |
| ToolMessage::ActivateToolFill => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Fill }), |
| ToolMessage::ActivateToolGradient => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Gradient }), |
|
|
| ToolMessage::ActivateToolPath => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Path }), |
| ToolMessage::ActivateToolPen => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Pen }), |
| ToolMessage::ActivateToolFreehand => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Freehand }), |
| ToolMessage::ActivateToolSpline => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Spline }), |
| ToolMessage::ActivateToolShape => { |
| if self.tool_state.tool_data.active_shape_type.is_some() { |
| self.tool_state.tool_data.active_shape_type = None; |
| self.tool_state.tool_data.active_tool_type = ToolType::Shape; |
| } |
| responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Shape }); |
| responses.add(ShapeToolMessage::SetShape(ShapeType::Polygon)); |
| responses.add(ShapeToolMessage::HideShapeTypeWidget(false)) |
| } |
| ToolMessage::ActivateToolBrush => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Brush }), |
| ToolMessage::ActivateToolShapeLine | ToolMessage::ActivateToolShapeRectangle | ToolMessage::ActivateToolShapeEllipse => { |
| let shape = match message { |
| ToolMessage::ActivateToolShapeLine => Line, |
| ToolMessage::ActivateToolShapeRectangle => Rectangle, |
| ToolMessage::ActivateToolShapeEllipse => Ellipse, |
| _ => unreachable!(), |
| }; |
|
|
| self.tool_state.tool_data.active_shape_type = Some(shape.tool_type()); |
| responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Shape }); |
| responses.add(ShapeToolMessage::HideShapeTypeWidget(true)); |
| responses.add(ShapeToolMessage::SetShape(shape)); |
| } |
| ToolMessage::ActivateTool { tool_type } => { |
| let tool_data = &mut self.tool_state.tool_data; |
| let old_tool = tool_data.active_tool_type.get_tool(); |
| let tool_type = tool_type.get_tool(); |
|
|
| responses.add(ToolMessage::RefreshToolOptions); |
| tool_data.send_layout(responses, LayoutTarget::ToolShelf); |
|
|
| |
| if self.tool_is_active && tool_type == old_tool { |
| return; |
| } |
|
|
| if tool_type != ToolType::Shape { |
| tool_data.active_shape_type = None; |
| } |
|
|
| self.tool_is_active = true; |
|
|
| |
| let mut send_abort_to_tool = |old_tool: ToolType, new_tool: ToolType, update_hints_and_cursor: bool| { |
| if let Some(tool) = tool_data.tools.get_mut(&new_tool) { |
| let mut data = ToolActionHandlerData { |
| document, |
| document_id, |
| global_tool_data: &self.tool_state.document_tool_data, |
| input, |
| font_cache, |
| shape_editor: &mut self.shape_editor, |
| node_graph, |
| preferences, |
| }; |
|
|
| if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort { |
| tool.process_message(tool_abort_message, responses, &mut data); |
| } |
|
|
| if update_hints_and_cursor { |
| if self.transform_layer_handler.is_transforming() { |
| self.transform_layer_handler.hints(responses); |
| } else { |
| tool.process_message(ToolMessage::UpdateHints, responses, &mut data); |
| } |
| tool.process_message(ToolMessage::UpdateCursor, responses, &mut data); |
| } |
| } |
|
|
| if matches!(old_tool, ToolType::Path | ToolType::Select) { |
| responses.add(TransformLayerMessage::CancelTransformOperation); |
| } |
| }; |
|
|
| send_abort_to_tool(old_tool, tool_type, true); |
| send_abort_to_tool(old_tool, old_tool, false); |
|
|
| |
| tool_data.tools.get(&tool_type).unwrap().deactivate(responses); |
|
|
| |
| tool_data.active_tool_type = tool_type; |
|
|
| |
| tool_data.tools.get(&tool_type).unwrap().activate(responses); |
|
|
| |
| responses.add(BroadcastEvent::SelectionChanged); |
|
|
| |
| responses.add(BroadcastEvent::WorkingColorChanged); |
|
|
| |
| responses.add(ToolMessage::RefreshToolOptions); |
|
|
| |
| tool_data.send_layout(responses, LayoutTarget::ToolShelf); |
| } |
| ToolMessage::DeactivateTools => { |
| let tool_data = &mut self.tool_state.tool_data; |
| tool_data.tools.get(&tool_data.active_tool_type).unwrap().deactivate(responses); |
|
|
| |
| let message = Box::new(TransformLayerMessage::SelectionChanged.into()); |
| let on = BroadcastEvent::SelectionChanged; |
| responses.add(BroadcastMessage::UnsubscribeEvent { message, on }); |
|
|
| responses.add(OverlaysMessage::RemoveProvider(ARTBOARD_OVERLAY_PROVIDER)); |
|
|
| responses.add(FrontendMessage::UpdateInputHints { hint_data: Default::default() }); |
| responses.add(FrontendMessage::UpdateMouseCursor { cursor: Default::default() }); |
|
|
| self.tool_is_active = false; |
| } |
| ToolMessage::InitTools => { |
| |
| responses.add(BroadcastMessage::SubscribeEvent { |
| on: BroadcastEvent::SelectionChanged, |
| send: Box::new(TransformLayerMessage::SelectionChanged.into()), |
| }); |
|
|
| self.tool_is_active = true; |
|
|
| let tool_data = &mut self.tool_state.tool_data; |
| let document_data = &self.tool_state.document_tool_data; |
| let active_tool = &tool_data.active_tool_type; |
|
|
| |
| tool_data.tools.get(active_tool).unwrap().activate(responses); |
|
|
| |
| tool_data.tools.get(active_tool).unwrap().send_layout(responses, LayoutTarget::ToolOptions); |
|
|
| |
| tool_data.send_layout(responses, LayoutTarget::ToolShelf); |
|
|
| |
| document_data.update_working_colors(responses); |
|
|
| let mut data = ToolActionHandlerData { |
| document, |
| document_id, |
| global_tool_data: &self.tool_state.document_tool_data, |
| input, |
| font_cache, |
| shape_editor: &mut self.shape_editor, |
| node_graph, |
| preferences, |
| }; |
|
|
| |
| tool_data.active_tool_mut().process_message(ToolMessage::UpdateHints, responses, &mut data); |
| tool_data.active_tool_mut().process_message(ToolMessage::UpdateCursor, responses, &mut data); |
|
|
| responses.add(OverlaysMessage::AddProvider(ARTBOARD_OVERLAY_PROVIDER)); |
| } |
| ToolMessage::PreUndo => { |
| let tool_data = &mut self.tool_state.tool_data; |
| if tool_data.active_tool_type != ToolType::Pen { |
| responses.add(BroadcastEvent::ToolAbort); |
| } |
| } |
| ToolMessage::Redo => { |
| let tool_data = &mut self.tool_state.tool_data; |
| if tool_data.active_tool_type == ToolType::Pen { |
| responses.add(PenToolMessage::Redo); |
| } |
| } |
| ToolMessage::RefreshToolOptions => { |
| let tool_data = &mut self.tool_state.tool_data; |
| tool_data.tools.get(&tool_data.active_tool_type).unwrap().send_layout(responses, LayoutTarget::ToolOptions); |
| } |
| ToolMessage::ResetColors => { |
| let document_data = &mut self.tool_state.document_tool_data; |
|
|
| document_data.primary_color = Color::BLACK; |
| document_data.secondary_color = Color::WHITE; |
|
|
| document_data.update_working_colors(responses); |
| } |
| ToolMessage::SelectRandomWorkingColor { primary } => { |
| |
| let document_data = &mut self.tool_state.document_tool_data; |
|
|
| let random_number = generate_uuid(); |
| let r = (random_number >> 16) as u8; |
| let g = (random_number >> 8) as u8; |
| let b = random_number as u8; |
| let random_color = Color::from_rgba8_srgb(r, g, b, 255); |
|
|
| if primary { |
| document_data.primary_color = random_color; |
| } else { |
| document_data.secondary_color = random_color; |
| } |
|
|
| document_data.update_working_colors(responses); |
| } |
| ToolMessage::SelectWorkingColor { color, primary } => { |
| let document_data = &mut self.tool_state.document_tool_data; |
|
|
| if primary { |
| document_data.primary_color = color; |
| } else { |
| document_data.secondary_color = color; |
| } |
|
|
| document_data.update_working_colors(responses); |
| } |
| ToolMessage::SwapColors => { |
| let document_data = &mut self.tool_state.document_tool_data; |
|
|
| std::mem::swap(&mut document_data.primary_color, &mut document_data.secondary_color); |
|
|
| document_data.update_working_colors(responses); |
| } |
| ToolMessage::Undo => { |
| let tool_data = &mut self.tool_state.tool_data; |
| if tool_data.active_tool_type == ToolType::Pen { |
| responses.add(PenToolMessage::Undo); |
| } |
| } |
|
|
| |
| tool_message => { |
| let tool_type = match &tool_message { |
| ToolMessage::UpdateCursor | ToolMessage::UpdateHints => self.tool_state.tool_data.active_tool_type, |
| tool_message => tool_message_to_tool_type(tool_message), |
| }; |
| let tool_data = &mut self.tool_state.tool_data; |
|
|
| if let Some(tool) = tool_data.tools.get_mut(&tool_type) { |
| let graph_view_overlay_open = document.graph_view_overlay_open(); |
|
|
| if tool_type == tool_data.active_tool_type { |
| let mut data = ToolActionHandlerData { |
| document, |
| document_id, |
| global_tool_data: &self.tool_state.document_tool_data, |
| input, |
| font_cache, |
| shape_editor: &mut self.shape_editor, |
| node_graph, |
| preferences, |
| }; |
| if matches!(tool_message, ToolMessage::UpdateHints) { |
| if graph_view_overlay_open { |
| |
| responses.add(NodeGraphMessage::UpdateHints); |
| } else if self.transform_layer_handler.is_transforming() { |
| self.transform_layer_handler.hints(responses); |
| } else { |
| tool.process_message(ToolMessage::UpdateHints, responses, &mut data) |
| } |
| } else { |
| tool.process_message(tool_message, responses, &mut data); |
| } |
| } |
| } |
| } |
| } |
| } |
|
|
| fn actions(&self) -> ActionList { |
| let mut list = actions!(ToolMessageDiscriminant; |
| ActivateToolSelect, |
| ActivateToolArtboard, |
| ActivateToolNavigate, |
| ActivateToolEyedropper, |
| ActivateToolFill, |
| ActivateToolGradient, |
|
|
| ActivateToolPath, |
| ActivateToolPen, |
| ActivateToolFreehand, |
| ActivateToolSpline, |
| ActivateToolShapeLine, |
| ActivateToolShapeRectangle, |
| ActivateToolShapeEllipse, |
| ActivateToolShape, |
| ActivateToolText, |
|
|
| ActivateToolBrush, |
|
|
| SelectRandomWorkingColor, |
| ResetColors, |
| SwapColors, |
| Undo, |
| ); |
| list.extend(self.tool_state.tool_data.active_tool().actions()); |
| list.extend(self.transform_layer_handler.actions()); |
|
|
| list |
| } |
| } |
|
|