Move G/R/S to tools folder (#1076)

* Move G/R/S to tools folder

* Fix bad cache

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2023-03-10 15:06:31 -08:00
committed by GitHub
co-authored by Keavon Chambers
parent 6febdea3c3
commit 0c8df9bf51
11 changed files with 64 additions and 30 deletions
+3
View File
@@ -3,9 +3,12 @@ mod tool_message_handler;
pub mod common_functionality;
pub mod tool_messages;
pub mod transform_layer;
pub mod utility_types;
#[doc(inline)]
pub use tool_message::{ToolMessage, ToolMessageDiscriminant};
#[doc(inline)]
pub use tool_message_handler::ToolMessageHandler;
#[doc(inline)]
pub use transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant};
+4
View File
@@ -10,6 +10,10 @@ use serde::{Deserialize, Serialize};
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum ToolMessage {
// Sub-messages
#[remain::unsorted]
#[child]
TransformLayer(TransformLayerMessage),
#[remain::unsorted]
#[child]
Select(SelectToolMessage),
@@ -12,6 +12,7 @@ use graphene_core::raster::color::Color;
#[derive(Debug, Default)]
pub struct ToolMessageHandler {
tool_state: ToolFsmState,
transform_layer_handler: TransformLayerMessageHandler,
}
impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocessorMessageHandler, &PersistentData)> for ToolMessageHandler {
@@ -27,6 +28,11 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
#[remain::sorted]
match message {
// Messages
#[remain::unsorted]
ToolMessage::TransformLayer(message) => self
.transform_layer_handler
.process_message(message, responses, (document, input, &render_data, &self.tool_state.tool_data)),
#[remain::unsorted]
ToolMessage::ActivateToolSelect => responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Select }.into()),
#[remain::unsorted]
@@ -115,8 +121,18 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
ToolMessage::DeactivateTools => {
let tool_data = &mut self.tool_state.tool_data;
tool_data.tools.get(&tool_data.active_tool_type).unwrap().deactivate(responses);
// Unsubscribe the transform layer to selection change events
let message = Box::new(TransformLayerMessage::SelectionChanged.into());
let on = BroadcastEvent::SelectionChanged;
responses.push_back(BroadcastMessage::UnsubscribeEvent { message, on }.into());
}
ToolMessage::InitTools => {
// Subscribe the transform layer to selection change events
let send = Box::new(TransformLayerMessage::SelectionChanged.into());
let on = BroadcastEvent::SelectionChanged;
responses.push_back(BroadcastMessage::SubscribeEvent { send, on }.into());
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;
@@ -232,6 +248,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
SwapColors,
);
list.extend(self.tool_state.tool_data.active_tool().actions());
list.extend(self.transform_layer_handler.actions());
list
}
@@ -0,0 +1,15 @@
//! Handles Blender inspired layer transformation with the <kbd>G</kbd> <kbd>R</kbd> and <kbd>S</kbd> keys for grabbing, rotating and scaling.
//!
//! Other features include
//! - Typing a number for a precise transformation
//! - <kbd>Shift</kbd> to slow transformation
//! - <kbd>Ctrl</kbd> to snap angles to 15°
//! - Escape or right click to cancel
mod transform_layer_message;
mod transform_layer_message_handler;
#[doc(inline)]
pub use transform_layer_message::{TransformLayerMessage, TransformLayerMessageDiscriminant};
#[doc(inline)]
pub use transform_layer_message_handler::TransformLayerMessageHandler;
@@ -0,0 +1,24 @@
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[remain::sorted]
#[impl_message(Message, ToolMessage, TransformLayer)]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub enum TransformLayerMessage {
// Messages
ApplyTransformOperation,
BeginGrab,
BeginRotate,
BeginScale,
CancelTransformOperation,
ConstrainX,
ConstrainY,
PointerMove { slow_key: Key, snap_key: Key },
SelectionChanged,
TypeBackspace,
TypeDecimalPoint,
TypeDigit { digit: u8 },
TypeNegate,
}
@@ -0,0 +1,214 @@
use crate::consts::SLOWING_DIVISOR;
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
use crate::messages::portfolio::document::utility_types::transformation::{Axis, OriginalTransforms, Selected, TransformOperation, Typing};
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::shape_editor::ShapeEditor;
use crate::messages::tool::utility_types::{ToolData, ToolType};
use document_legacy::layers::style::RenderData;
use glam::DVec2;
#[derive(Debug, Clone, Default)]
pub struct TransformLayerMessageHandler {
transform_operation: TransformOperation,
slow: bool,
snap: bool,
typing: Typing,
mouse_position: ViewportPosition,
start_mouse: ViewportPosition,
original_transforms: OriginalTransforms,
pivot: DVec2,
shape_editor: ShapeEditor,
}
type TransformData<'a> = (&'a DocumentMessageHandler, &'a InputPreprocessorMessageHandler, &'a RenderData<'a>, &'a ToolData);
impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformLayerMessageHandler {
#[remain::check]
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (document, ipp, render_data, tool_data): TransformData) {
use TransformLayerMessage::*;
// TODO: Transform individual points when using the path tool.
let _using_path_tool = tool_data.active_tool_type == ToolType::Path;
// You may also want the shape editor here? If not, then feel free to remove.
let _shape_editor = &self.shape_editor;
let selected_layers = document.layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then_some(layer_path)).collect::<Vec<_>>();
let mut selected = Selected::new(&mut self.original_transforms, &mut self.pivot, &selected_layers, responses, &document.document_legacy);
let mut begin_operation = |operation: TransformOperation, typing: &mut Typing, mouse_position: &mut DVec2, start_mouse: &mut DVec2| {
if operation != TransformOperation::None {
selected.revert_operation();
typing.clear();
} else {
*selected.pivot = selected.mean_average_of_pivots(render_data);
}
*mouse_position = ipp.mouse.position;
*start_mouse = ipp.mouse.position;
};
#[remain::sorted]
match message {
ApplyTransformOperation => {
self.original_transforms.clear();
self.typing.clear();
self.transform_operation = TransformOperation::None;
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
BeginGrab => {
if let TransformOperation::Grabbing(_) = self.transform_operation {
return;
}
// Don't allow grab with no selected layers
if selected_layers.is_empty() {
return;
}
begin_operation(self.transform_operation, &mut self.typing, &mut self.mouse_position, &mut self.start_mouse);
self.transform_operation = TransformOperation::Grabbing(Default::default());
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
self.original_transforms.clear();
}
BeginRotate => {
if let TransformOperation::Rotating(_) = self.transform_operation {
return;
}
// Don't allow rotate with no selected layers
if selected_layers.is_empty() {
return;
}
begin_operation(self.transform_operation, &mut self.typing, &mut self.mouse_position, &mut self.start_mouse);
self.transform_operation = TransformOperation::Rotating(Default::default());
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
self.original_transforms.clear();
}
BeginScale => {
if let TransformOperation::Scaling(_) = self.transform_operation {
return;
}
// Don't allow scale with no selected layers
if selected_layers.is_empty() {
return;
}
begin_operation(self.transform_operation, &mut self.typing, &mut self.mouse_position, &mut self.start_mouse);
self.transform_operation = TransformOperation::Scaling(Default::default());
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
self.original_transforms.clear();
}
CancelTransformOperation => {
selected.revert_operation();
selected.original_transforms.clear();
self.typing.clear();
self.transform_operation = TransformOperation::None;
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
ConstrainX => self.transform_operation.constrain_axis(Axis::X, &mut selected, self.snap),
ConstrainY => self.transform_operation.constrain_axis(Axis::Y, &mut selected, self.snap),
PointerMove { slow_key, snap_key } => {
self.slow = ipp.keyboard.get(slow_key as usize);
let new_snap = ipp.keyboard.get(snap_key as usize);
if new_snap != self.snap {
self.snap = new_snap;
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
}
if self.typing.digits.is_empty() {
let delta_pos = ipp.mouse.position - self.mouse_position;
match self.transform_operation {
TransformOperation::None => unreachable!(),
TransformOperation::Grabbing(translation) => {
let change = if self.slow { delta_pos / SLOWING_DIVISOR } else { delta_pos };
self.transform_operation = TransformOperation::Grabbing(translation.increment_amount(change));
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
}
TransformOperation::Rotating(rotation) => {
let selected_pivot = selected.mean_average_of_pivots(render_data);
let angle = {
let start_offset = self.mouse_position - selected_pivot;
let end_offset = ipp.mouse.position - selected_pivot;
start_offset.angle_between(end_offset)
};
let change = if self.slow { angle / SLOWING_DIVISOR } else { angle };
self.transform_operation = TransformOperation::Rotating(rotation.increment_amount(change));
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
}
TransformOperation::Scaling(scale) => {
let change = {
let previous_frame_dist = (self.mouse_position - *selected.pivot).length();
let current_frame_dist = (ipp.mouse.position - *selected.pivot).length();
let start_transform_dist = (self.start_mouse - *selected.pivot).length();
(current_frame_dist - previous_frame_dist) / start_transform_dist
};
let change = if self.slow { change / SLOWING_DIVISOR } else { change };
self.transform_operation = TransformOperation::Scaling(scale.increment_amount(change));
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
}
};
}
self.mouse_position = ipp.mouse.position;
}
SelectionChanged => {
let layer_paths = document.selected_visible_layers().map(|layer_path| layer_path.to_vec()).collect();
self.shape_editor.set_selected_layers(layer_paths);
}
TypeBackspace => self.transform_operation.handle_typed(self.typing.type_backspace(), &mut selected, self.snap),
TypeDecimalPoint => self.transform_operation.handle_typed(self.typing.type_decimal_point(), &mut selected, self.snap),
TypeDigit { digit } => self.transform_operation.handle_typed(self.typing.type_number(digit), &mut selected, self.snap),
TypeNegate => self.transform_operation.handle_typed(self.typing.type_negate(), &mut selected, self.snap),
}
}
fn actions(&self) -> ActionList {
let mut common = actions!(TransformLayerMessageDiscriminant;
BeginGrab,
BeginScale,
BeginRotate,
);
if self.transform_operation != TransformOperation::None {
let active = actions!(TransformLayerMessageDiscriminant;
PointerMove,
CancelTransformOperation,
ApplyTransformOperation,
TypeDigit,
TypeBackspace,
TypeDecimalPoint,
TypeNegate,
ConstrainX,
ConstrainY,
);
common.extend(active);
}
common
}
}