Graphite/editor/src/messages/tool/tool_messages/imaginate_tool.rs

209 lines
6.2 KiB
Rust

use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
use crate::messages::portfolio::document::node_graph::{self, IMAGINATE_NODE};
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::resize::Resize;
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use document_legacy::Operation;
use glam::DAffine2;
use serde::{Deserialize, Serialize};
#[derive(Default)]
pub struct ImaginateTool {
fsm_state: ImaginateToolFsmState,
tool_data: ImaginateToolData,
}
#[remain::sorted]
#[impl_message(Message, ToolMessage, Imaginate)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
pub enum ImaginateToolMessage {
// Standard messages
#[remain::unsorted]
Abort,
// Tool-specific messages
DragStart,
DragStop,
Resize {
center: Key,
lock_ratio: Key,
},
}
impl PropertyHolder for ImaginateTool {}
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ImaginateTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
}
fn actions(&self) -> ActionList {
use ImaginateToolFsmState::*;
match self.fsm_state {
Ready => actions!(ImaginateToolMessageDiscriminant;
DragStart,
),
Drawing => actions!(ImaginateToolMessageDiscriminant;
DragStop,
Abort,
Resize,
),
}
}
}
impl ToolMetadata for ImaginateTool {
fn icon_name(&self) -> String {
"RasterImaginateTool".into()
}
fn tooltip(&self) -> String {
"Imaginate Tool".into()
}
fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
ToolType::Imaginate
}
}
impl ToolTransition for ImaginateTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(ImaginateToolMessage::Abort.into()),
selection_changed: None,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
enum ImaginateToolFsmState {
#[default]
Ready,
Drawing,
}
#[derive(Clone, Debug, Default)]
struct ImaginateToolData {
data: Resize,
}
impl Fsm for ImaginateToolFsmState {
type ToolData = ImaginateToolData;
type ToolOptions = ();
fn transition(
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
ToolActionHandlerData { document, input, render_data, .. }: &mut ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
use ImaginateToolFsmState::*;
use ImaginateToolMessage::*;
let mut shape_data = &mut tool_data.data;
if let ToolMessage::Imaginate(event) = event {
match (self, event) {
(Ready, DragStart) => {
shape_data.start(responses, document, input, render_data);
responses.add(DocumentMessage::StartTransaction);
shape_data.path = Some(document.get_path_for_new_layer());
responses.add(DocumentMessage::DeselectAllLayers);
use graph_craft::document::*;
// Utility function to offset the position of each consecutive node
let mut pos = 8;
let mut next_pos = || {
pos += 8;
graph_craft::document::DocumentNodeMetadata::position((pos, 4))
};
// Get the node type for the Transform and Imaginate nodes
let Some(transform_node_type) = crate::messages::portfolio::document::node_graph::resolve_document_node_type("Transform") else {
warn!("Transform node should be in registry");
return Drawing;
};
let imaginate_node_type = &*IMAGINATE_NODE;
// Give them a unique ID
let [transform_node_id, imaginate_node_id] = [100, 101];
// Create the network based on the Input -> Output passthrough default network
let mut network = node_graph::new_image_network(16, imaginate_node_id);
// Insert the nodes into the default network
network
.nodes
.insert(transform_node_id, transform_node_type.to_document_node_default_inputs([Some(NodeInput::node(0, 0))], next_pos()));
network.nodes.insert(
imaginate_node_id,
imaginate_node_type.to_document_node_default_inputs([Some(graph_craft::document::NodeInput::node(transform_node_id, 0))], next_pos()),
);
// Add a layer with a frame to the document
responses.add(Operation::AddFrame {
path: shape_data.path.clone().unwrap(),
insert_index: -1,
transform: DAffine2::ZERO.to_cols_array(),
network,
});
responses.add(NodeGraphMessage::ShiftNode { node_id: imaginate_node_id });
Drawing
}
(state, Resize { center, lock_ratio }) => {
let message = shape_data.calculate_transform(responses, document, input, center, lock_ratio, true);
responses.try_add(message);
state
}
(Drawing, DragStop) => {
if let Some(layer_path) = &shape_data.path {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path: layer_path.to_vec() });
}
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
shape_data.cleanup(responses);
Ready
}
(Drawing, Abort) => {
responses.add(DocumentMessage::AbortTransaction);
shape_data.cleanup(responses);
Ready
}
_ => self,
}
} else {
self
}
}
fn update_hints(&self, responses: &mut VecDeque<Message>) {
let hint_data = match self {
ImaginateToolFsmState::Ready => HintData(vec![HintGroup(vec![
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Repaint Frame"),
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
])]),
ImaginateToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")])]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Crosshair });
}
}