mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
Reduce tool code duplication and boilerplate (#993)
* Reduce tool code duplication * Add doc comments * Fix Mac key variant, which can't be auto-converted without false positives * Rename "key" to "keys" --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
0531df18d5
commit
0a03aec343
@@ -423,6 +423,10 @@ impl<const LENGTH: usize> BitVector<LENGTH> {
|
||||
(self.0[offset] & bit) != 0
|
||||
}
|
||||
|
||||
pub fn key(&self, key: Key) -> bool {
|
||||
self.get(key as usize)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
let mut result = 0;
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
// Origin is top left
|
||||
pub type ViewportPosition = DVec2;
|
||||
@@ -85,6 +89,13 @@ impl MouseState {
|
||||
scroll_delta: ScrollDelta::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish_transaction(&self, drag_start: DVec2, responses: &mut VecDeque<Message>) {
|
||||
match drag_start.distance(self.position) <= DRAG_THRESHOLD {
|
||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use crate::messages::layout::utility_types::layout_widget::WidgetCallback;
|
||||
use graphite_proc_macros::WidgetBuilder;
|
||||
|
||||
use derivative::*;
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize)]
|
||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder)]
|
||||
#[derivative(Debug, PartialEq)]
|
||||
pub struct PivotAssist {
|
||||
#[widget_builder(constructor)]
|
||||
pub position: PivotPosition,
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::application::generate_uuid;
|
||||
use crate::consts::SELECTION_TOLERANCE;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
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::utility_types::misc::TargetDocument;
|
||||
use crate::messages::prelude::*;
|
||||
@@ -59,23 +59,8 @@ impl ToolMetadata for ArtboardTool {
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ArtboardTool {
|
||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
responses.push_back(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }.into());
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.data, data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
}
|
||||
fn process_message(&mut self, message: ToolMessage, transition_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
self.fsm_state.process_event(message, &mut self.data, transition_data, &(), responses, false);
|
||||
}
|
||||
|
||||
advertise_actions!(ArtboardToolMessageDiscriminant;
|
||||
@@ -100,20 +85,15 @@ impl ToolTransition for ArtboardTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum ArtboardToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
ResizingBounds,
|
||||
Dragging,
|
||||
}
|
||||
|
||||
impl Default for ArtboardToolFsmState {
|
||||
fn default() -> Self {
|
||||
ArtboardToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ArtboardToolData {
|
||||
bounding_box_overlays: Option<BoundingBoxOverlays>,
|
||||
@@ -138,14 +118,9 @@ impl Fsm for ArtboardToolFsmState {
|
||||
) -> Self {
|
||||
if let ToolMessage::Artboard(event) = event {
|
||||
match (self, event) {
|
||||
(ArtboardToolFsmState::Ready | ArtboardToolFsmState::ResizingBounds | ArtboardToolFsmState::Dragging, ArtboardToolMessage::DocumentIsDirty) => {
|
||||
match (
|
||||
tool_data
|
||||
.selected_artboard
|
||||
.map(|path| document.artboard_bounding_box_and_transform(&[path], font_cache))
|
||||
.unwrap_or(None),
|
||||
tool_data.bounding_box_overlays.take(),
|
||||
) {
|
||||
(state, ArtboardToolMessage::DocumentIsDirty) if state != ArtboardToolFsmState::Drawing => {
|
||||
let current_artboard = tool_data.selected_artboard.and_then(|path| document.artboard_bounding_box_and_transform(&[path], font_cache));
|
||||
match (current_artboard, tool_data.bounding_box_overlays.take()) {
|
||||
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
|
||||
(Some((bounds, transform)), paths) => {
|
||||
let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses));
|
||||
@@ -443,51 +418,14 @@ impl Fsm for ArtboardToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
ArtboardToolFsmState::Ready => HintData(vec![
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Artboard"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Move Artboard"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Backspace]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Delete Artboard"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Draw Artboard")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Move Artboard")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Backspace], "Delete Artboard")]),
|
||||
]),
|
||||
ArtboardToolFsmState::Dragging => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain to Axis"),
|
||||
plus: false,
|
||||
}])]),
|
||||
ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: false,
|
||||
},
|
||||
])]),
|
||||
ArtboardToolFsmState::Dragging => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain to Axis")])]),
|
||||
ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds => {
|
||||
HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")])])
|
||||
}
|
||||
};
|
||||
|
||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
@@ -52,23 +51,7 @@ impl PropertyHolder for EllipseTool {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EllipseTool {
|
||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.data, data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.data, data, &(), responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -97,18 +80,13 @@ impl ToolTransition for EllipseTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum EllipseToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
|
||||
impl Default for EllipseToolFsmState {
|
||||
fn default() -> Self {
|
||||
EllipseToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct EllipseToolData {
|
||||
data: Resize,
|
||||
@@ -159,12 +137,9 @@ impl Fsm for EllipseToolFsmState {
|
||||
state
|
||||
}
|
||||
(Drawing, DragStop) => {
|
||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
Ready
|
||||
}
|
||||
(Drawing, Abort) => {
|
||||
@@ -183,44 +158,11 @@ impl Fsm for EllipseToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
EllipseToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Ellipse"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Circular"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: true,
|
||||
},
|
||||
])]),
|
||||
EllipseToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Circular"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Ellipse"),
|
||||
HintInfo::keys([Key::Shift], "Constrain Circular").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])]),
|
||||
EllipseToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Circular"), HintInfo::keys([Key::Alt], "From Center")])]),
|
||||
};
|
||||
|
||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
@@ -45,23 +45,7 @@ impl PropertyHolder for EyedropperTool {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EyedropperTool {
|
||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.data, data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.data, data, &(), responses, true);
|
||||
}
|
||||
|
||||
advertise_actions!(EyedropperToolMessageDiscriminant;
|
||||
@@ -84,19 +68,14 @@ impl ToolTransition for EyedropperTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum EyedropperToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
SamplingPrimary,
|
||||
SamplingSecondary,
|
||||
}
|
||||
|
||||
impl Default for EyedropperToolFsmState {
|
||||
fn default() -> Self {
|
||||
EyedropperToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct EyedropperToolData {}
|
||||
|
||||
@@ -162,28 +141,10 @@ impl Fsm for EyedropperToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
EyedropperToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Sample to Primary"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Rmb),
|
||||
label: String::from("Sample to Secondary"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::Lmb, "Sample to Primary"),
|
||||
HintInfo::mouse(MouseMotion::Rmb, "Sample to Secondary"),
|
||||
])]),
|
||||
EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Escape]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Cancel"),
|
||||
plus: false,
|
||||
}])]),
|
||||
EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Escape], "Cancel")])]),
|
||||
};
|
||||
|
||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||
|
||||
@@ -48,23 +48,7 @@ impl PropertyHolder for FillTool {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FillTool {
|
||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.data, data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.data, data, &(), responses, true);
|
||||
}
|
||||
|
||||
advertise_actions!(FillToolMessageDiscriminant;
|
||||
@@ -83,17 +67,12 @@ impl ToolTransition for FillTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum FillToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
}
|
||||
|
||||
impl Default for FillToolFsmState {
|
||||
fn default() -> Self {
|
||||
FillToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct FillToolData {}
|
||||
|
||||
@@ -144,20 +123,8 @@ impl Fsm for FillToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
FillToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Fill with Primary"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Rmb),
|
||||
label: String::from("Fill with Secondary"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::Lmb, "Fill with Primary"),
|
||||
HintInfo::mouse(MouseMotion::Rmb, "Fill with Secondary"),
|
||||
])]),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
@@ -51,8 +51,9 @@ pub enum FreehandToolMessageOptionsUpdate {
|
||||
LineWeight(f64),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum FreehandToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
@@ -71,32 +72,18 @@ impl ToolMetadata for FreehandTool {
|
||||
|
||||
impl PropertyHolder for FreehandTool {
|
||||
fn properties(&self) -> Layout {
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Weight".into(),
|
||||
value: Some(self.options.line_weight as f64),
|
||||
is_integer: false,
|
||||
min: Some(1.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandToolMessageOptionsUpdate::LineWeight(number_input.value.unwrap())).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}]))
|
||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(1.)
|
||||
.on_update(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandToolMessageOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FreehandTool {
|
||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
FreehandToolMessageOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
@@ -104,13 +91,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FreehandTool
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.data, data, &self.options, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.data, data, &self.options, responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -141,11 +122,6 @@ impl ToolTransition for FreehandTool {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FreehandToolFsmState {
|
||||
fn default() -> Self {
|
||||
FreehandToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct FreehandToolData {
|
||||
points: Vec<DVec2>,
|
||||
@@ -222,13 +198,7 @@ impl Fsm for FreehandToolFsmState {
|
||||
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
FreehandToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Polyline"),
|
||||
plus: false,
|
||||
}])]),
|
||||
FreehandToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Draw Polyline")])]),
|
||||
FreehandToolFsmState::Drawing => HintData(vec![]),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::application::generate_uuid;
|
||||
use crate::consts::{COLOR_ACCENT, DRAG_THRESHOLD, LINE_ROTATE_SNAP_ANGLE, MANIPULATOR_GROUP_MARKER_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE};
|
||||
use crate::consts::{COLOR_ACCENT, LINE_ROTATE_SNAP_ANGLE, MANIPULATOR_GROUP_MARKER_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{RadioEntryData, RadioInput};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
@@ -78,15 +78,6 @@ impl ToolMetadata for GradientTool {
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool {
|
||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
if let ToolMessage::Gradient(GradientToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
GradientOptionsUpdate::Type(gradient_type) => {
|
||||
@@ -100,12 +91,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.data, data, &self.options, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.data, data, &self.options, responses, false);
|
||||
}
|
||||
|
||||
advertise_actions!(GradientToolMessageDiscriminant;
|
||||
@@ -120,47 +106,29 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool
|
||||
|
||||
impl PropertyHolder for GradientTool {
|
||||
fn properties(&self) -> Layout {
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::RadioInput(RadioInput {
|
||||
selected_index: if self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
entries: vec![
|
||||
RadioEntryData {
|
||||
value: "linear".into(),
|
||||
label: "Linear".into(),
|
||||
tooltip: "Linear Gradient".into(),
|
||||
on_update: WidgetCallback::new(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Linear)).into()),
|
||||
..RadioEntryData::default()
|
||||
},
|
||||
RadioEntryData {
|
||||
value: "radial".into(),
|
||||
label: "Radial".into(),
|
||||
tooltip: "Radial Gradient".into(),
|
||||
on_update: WidgetCallback::new(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Radial)).into()),
|
||||
..RadioEntryData::default()
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
}))],
|
||||
}]))
|
||||
let gradient_type = RadioInput::new(vec![
|
||||
RadioEntryData::new("Linear")
|
||||
.value("linear")
|
||||
.tooltip("Linear Gradient")
|
||||
.on_update(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Linear)).into()),
|
||||
RadioEntryData::new("Radial")
|
||||
.value("radial")
|
||||
.tooltip("Radial Gradient")
|
||||
.on_update(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Radial)).into()),
|
||||
])
|
||||
.selected_index((self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial) as u32)
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![gradient_type] }]))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum GradientToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
|
||||
impl Default for GradientToolFsmState {
|
||||
fn default() -> Self {
|
||||
GradientToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the transform from gradient space to layer space (where gradient space is 0..1 in layer space)
|
||||
fn gradient_space_transform(path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, font_cache: &FontCache) -> DAffine2 {
|
||||
let bounds = layer.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap();
|
||||
@@ -638,14 +606,7 @@ impl Fsm for GradientToolFsmState {
|
||||
}
|
||||
|
||||
(GradientToolFsmState::Drawing, GradientToolMessage::PointerUp) => {
|
||||
match tool_data.drag_start.distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||
true => {
|
||||
responses.push_back(DocumentMessage::AbortTransaction.into());
|
||||
responses.push_back(GradientToolMessage::DocumentIsDirty.into());
|
||||
}
|
||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
input.mouse.finish_transaction(tool_data.drag_start, responses);
|
||||
tool_data.snap_manager.cleanup(responses);
|
||||
|
||||
GradientToolFsmState::Ready
|
||||
@@ -669,28 +630,10 @@ impl Fsm for GradientToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
GradientToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Gradient"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Gradient"),
|
||||
HintInfo::keys([Key::Shift], "Snap 15°").prepend_plus(),
|
||||
])]),
|
||||
GradientToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
}])]),
|
||||
GradientToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Snap 15°")])]),
|
||||
};
|
||||
|
||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
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::{NodeImplementation, IMAGINATE_NODE};
|
||||
use crate::messages::portfolio::document::node_graph::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};
|
||||
@@ -40,23 +39,7 @@ impl PropertyHolder for ImaginateTool {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ImaginateTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -97,17 +80,13 @@ impl ToolTransition for ImaginateTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum ImaginateToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
|
||||
impl Default for ImaginateToolFsmState {
|
||||
fn default() -> Self {
|
||||
ImaginateToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ImaginateToolData {
|
||||
data: Resize,
|
||||
@@ -178,11 +157,7 @@ impl Fsm for ImaginateToolFsmState {
|
||||
state
|
||||
}
|
||||
(Drawing, DragStop) => {
|
||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
@@ -207,44 +182,11 @@ impl Fsm for ImaginateToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
ImaginateToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Repaint Frame"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: true,
|
||||
},
|
||||
])]),
|
||||
ImaginateToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: false,
|
||||
},
|
||||
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.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::consts::{DRAG_THRESHOLD, LINE_ROTATE_SNAP_ANGLE};
|
||||
use crate::consts::LINE_ROTATE_SNAP_ANGLE;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
@@ -72,32 +72,18 @@ impl ToolMetadata for LineTool {
|
||||
|
||||
impl PropertyHolder for LineTool {
|
||||
fn properties(&self) -> Layout {
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Weight".into(),
|
||||
value: Some(self.options.line_weight as f64),
|
||||
is_integer: false,
|
||||
min: Some(0.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}]))
|
||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for LineTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
fn process_message(&mut self, message: ToolMessage, transition_data: ToolActionHandlerData<'a>, messages: &mut VecDeque<Message>) {
|
||||
if let ToolMessage::Line(LineToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
LineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
@@ -105,27 +91,13 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for LineTool {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, transition_data, &self.options, messages, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
use LineToolFsmState::*;
|
||||
|
||||
match self.fsm_state {
|
||||
Ready => actions!(LineToolMessageDiscriminant;
|
||||
DragStart,
|
||||
),
|
||||
Drawing => actions!(LineToolMessageDiscriminant;
|
||||
DragStop,
|
||||
Redraw,
|
||||
Abort,
|
||||
),
|
||||
LineToolFsmState::Ready => actions!(LineToolMessageDiscriminant; DragStart),
|
||||
LineToolFsmState::Drawing => actions!(LineToolMessageDiscriminant; DragStop, Redraw, Abort),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,18 +112,13 @@ impl ToolTransition for LineTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
enum LineToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
|
||||
impl Default for LineToolFsmState {
|
||||
fn default() -> Self {
|
||||
LineToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct LineToolData {
|
||||
drag_start: ViewportPosition,
|
||||
@@ -205,20 +172,14 @@ impl Fsm for LineToolFsmState {
|
||||
(Drawing, Redraw { center, snap_angle, lock_angle }) => {
|
||||
tool_data.drag_current = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
|
||||
|
||||
let values: Vec<_> = [lock_angle, snap_angle, center].iter().map(|k| input.keyboard.get(*k as usize)).collect();
|
||||
responses.push_back(generate_transform(tool_data, values[0], values[1], values[2]));
|
||||
let keyboard = &input.keyboard;
|
||||
responses.push_back(generate_transform(tool_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center)));
|
||||
|
||||
Drawing
|
||||
}
|
||||
(Drawing, DragStop) => {
|
||||
tool_data.drag_current = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
|
||||
tool_data.snap_manager.cleanup(responses);
|
||||
|
||||
match tool_data.drag_start.distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
input.mouse.finish_transaction(tool_data.drag_start, responses);
|
||||
tool_data.path = None;
|
||||
|
||||
Ready
|
||||
@@ -239,57 +200,15 @@ impl Fsm for LineToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
LineToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Line"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Lock Angle"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Line"),
|
||||
HintInfo::keys([Key::Shift], "Snap 15°").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
HintInfo::keys([Key::Control], "Lock Angle").prepend_plus(),
|
||||
])]),
|
||||
LineToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Lock Angle"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::keys([Key::Shift], "Snap 15°"),
|
||||
HintInfo::keys([Key::Alt], "From Center"),
|
||||
HintInfo::keys([Key::Control], "Lock Angle"),
|
||||
])]),
|
||||
};
|
||||
|
||||
@@ -301,40 +220,38 @@ impl Fsm for LineToolFsmState {
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_transform(tool_data: &mut LineToolData, lock: bool, snap: bool, center: bool) -> Message {
|
||||
fn generate_transform(tool_data: &mut LineToolData, lock_angle: bool, snap_angle: bool, center: bool) -> Message {
|
||||
let mut start = tool_data.drag_start;
|
||||
let stop = tool_data.drag_current;
|
||||
let line_vector = tool_data.drag_current - start;
|
||||
|
||||
let dir = stop - start;
|
||||
let mut angle = -line_vector.angle_between(DVec2::X);
|
||||
|
||||
let mut angle = -dir.angle_between(DVec2::X);
|
||||
if lock_angle {
|
||||
angle = tool_data.angle;
|
||||
}
|
||||
|
||||
if lock {
|
||||
angle = tool_data.angle
|
||||
};
|
||||
|
||||
if snap {
|
||||
if snap_angle {
|
||||
let snap_resolution = LINE_ROTATE_SNAP_ANGLE.to_radians();
|
||||
angle = (angle / snap_resolution).round() * snap_resolution;
|
||||
}
|
||||
|
||||
tool_data.angle = angle;
|
||||
|
||||
let mut scale = dir.length();
|
||||
let mut line_length = line_vector.length();
|
||||
|
||||
if lock {
|
||||
if lock_angle {
|
||||
let angle_vec = DVec2::new(angle.cos(), angle.sin());
|
||||
scale = dir.dot(angle_vec);
|
||||
line_length = line_vector.dot(angle_vec);
|
||||
}
|
||||
|
||||
if center {
|
||||
start -= scale * DVec2::new(angle.cos(), angle.sin());
|
||||
scale *= 2.;
|
||||
start -= line_length * DVec2::new(angle.cos(), angle.sin());
|
||||
line_length *= 2.;
|
||||
}
|
||||
|
||||
Operation::SetLayerTransformInViewport {
|
||||
path: tool_data.path.clone().unwrap(),
|
||||
transform: glam::DAffine2::from_scale_angle_translation(DVec2::new(scale, 1.), angle, start).to_cols_array(),
|
||||
transform: glam::DAffine2::from_scale_angle_translation(DVec2::new(line_length, 1.), angle, start).to_cols_array(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
@@ -52,23 +52,7 @@ impl PropertyHolder for NavigateTool {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for NavigateTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -99,20 +83,15 @@ impl ToolTransition for NavigateTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum NavigateToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Panning,
|
||||
Tilting,
|
||||
Zooming,
|
||||
}
|
||||
|
||||
impl Default for NavigateToolFsmState {
|
||||
fn default() -> Self {
|
||||
NavigateToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct NavigateToolData {
|
||||
drag_start: DVec2,
|
||||
@@ -192,76 +171,13 @@ impl Fsm for NavigateToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
NavigateToolFsmState::Ready => HintData(vec![
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Zoom In"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Zoom Out"),
|
||||
plus: true,
|
||||
},
|
||||
]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Zoom"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap Increments"),
|
||||
plus: true,
|
||||
},
|
||||
]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::MmbDrag),
|
||||
label: String::from("Pan"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::RmbDrag),
|
||||
label: String::from("Tilt"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: true,
|
||||
},
|
||||
]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Zoom In"), HintInfo::keys([Key::Shift], "Zoom Out").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Zoom"), HintInfo::keys([Key::Control], "Snap Increments").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::MmbDrag, "Pan")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::RmbDrag, "Tilt"), HintInfo::keys([Key::Control], "Snap 15°").prepend_plus()]),
|
||||
]),
|
||||
NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
}])]),
|
||||
NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap Increments"),
|
||||
plus: false,
|
||||
}])]),
|
||||
NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")])]),
|
||||
NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap Increments")])]),
|
||||
_ => HintData(Vec::new()),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
@@ -39,23 +38,7 @@ impl PropertyHolder for NodeGraphFrameTool {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for NodeGraphFrameTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -96,17 +79,13 @@ impl ToolTransition for NodeGraphFrameTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum NodeGraphToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
|
||||
impl Default for NodeGraphToolFsmState {
|
||||
fn default() -> Self {
|
||||
NodeGraphToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct NodeGraphToolData {
|
||||
data: Resize,
|
||||
@@ -160,11 +139,7 @@ impl Fsm for NodeGraphToolFsmState {
|
||||
state
|
||||
}
|
||||
(Drawing, DragStop) => {
|
||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
@@ -188,44 +163,11 @@ impl Fsm for NodeGraphToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
NodeGraphToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Repaint Frame"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: true,
|
||||
},
|
||||
])]),
|
||||
NodeGraphToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Repaint Frame"),
|
||||
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])]),
|
||||
NodeGraphToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")])]),
|
||||
};
|
||||
|
||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::consts::{SELECTION_THRESHOLD, SELECTION_TOLERANCE};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
||||
@@ -62,23 +62,7 @@ impl PropertyHolder for PathTool {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PathTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||
}
|
||||
|
||||
// Different actions depending on state may be wanted:
|
||||
@@ -111,18 +95,13 @@ impl ToolTransition for PathTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum PathToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Dragging,
|
||||
}
|
||||
|
||||
impl Default for PathToolFsmState {
|
||||
fn default() -> Self {
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PathToolData {
|
||||
shape_editor: ShapeEditor,
|
||||
@@ -309,88 +288,23 @@ impl Fsm for PathToolFsmState {
|
||||
let hint_data = match self {
|
||||
PathToolFsmState::Ready => HintData(vec![
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Select Point"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grow/Shrink Selection"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::Lmb, "Select Point"),
|
||||
HintInfo::keys([Key::Shift], "Grow/Shrink Selection").prepend_plus(),
|
||||
]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Drag Selected"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![
|
||||
KeysGroup(vec![Key::ArrowUp]).into(),
|
||||
KeysGroup(vec![Key::ArrowRight]).into(),
|
||||
KeysGroup(vec![Key::ArrowDown]).into(),
|
||||
KeysGroup(vec![Key::ArrowLeft]).into(),
|
||||
],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Nudge Selected (coming soon)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Big Increment Nudge"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo::arrow_keys("Nudge Selected (coming soon)"),
|
||||
HintInfo::keys([Key::Shift], "Big Increment Nudge").prepend_plus(),
|
||||
]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyG]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grab Selected (coming soon)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyR]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Rotate Selected (coming soon)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyS]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Scale Selected (coming soon)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::keys([Key::KeyG], "Grab Selected (coming soon)"),
|
||||
HintInfo::keys([Key::KeyR], "Rotate Selected (coming soon)"),
|
||||
HintInfo::keys([Key::KeyS], "Scale Selected (coming soon)"),
|
||||
]),
|
||||
]),
|
||||
PathToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Split/Align Handles (Toggle)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Share Lengths of Aligned Handles"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::keys([Key::Alt], "Split/Align Handles (Toggle)"),
|
||||
HintInfo::keys([Key::Shift], "Share Lengths of Aligned Handles"),
|
||||
])]),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::consts::LINE_ROTATE_SNAP_ANGLE;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
||||
@@ -59,8 +59,9 @@ pub enum PenToolMessage {
|
||||
UpdateOptions(PenOptionsUpdate),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum PenToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
DraggingHandle,
|
||||
PlacingAnchor,
|
||||
@@ -86,32 +87,18 @@ impl ToolMetadata for PenTool {
|
||||
|
||||
impl PropertyHolder for PenTool {
|
||||
fn properties(&self) -> Layout {
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Weight".into(),
|
||||
value: Some(self.options.line_weight),
|
||||
is_integer: false,
|
||||
min: Some(0.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}]))
|
||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PenTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if let ToolMessage::Pen(PenToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
PenOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
@@ -119,13 +106,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PenTool {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -158,11 +139,6 @@ impl ToolTransition for PenTool {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PenToolFsmState {
|
||||
fn default() -> Self {
|
||||
PenToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct PenToolData {
|
||||
weight: f64,
|
||||
@@ -614,49 +590,13 @@ impl Fsm for PenToolFsmState {
|
||||
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
PenToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Draw Path"),
|
||||
plus: false,
|
||||
}])]),
|
||||
PenToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Draw Path")])]),
|
||||
PenToolFsmState::DraggingHandle | PenToolFsmState::PlacingAnchor => HintData(vec![
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Add Handle"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Add Anchor"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Break Handle"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Enter]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("End Path"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Add Handle")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Anchor")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Break Handle")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Enter], "End Path")]),
|
||||
]),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
@@ -40,23 +39,7 @@ impl PropertyHolder for RectangleTool {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for RectangleTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &(), responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -97,17 +80,13 @@ impl ToolTransition for RectangleTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum RectangleToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
|
||||
impl Default for RectangleToolFsmState {
|
||||
fn default() -> Self {
|
||||
RectangleToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct RectangleToolData {
|
||||
data: Resize,
|
||||
@@ -158,11 +137,7 @@ impl Fsm for RectangleToolFsmState {
|
||||
state
|
||||
}
|
||||
(Drawing, DragStop) => {
|
||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
Ready
|
||||
@@ -184,44 +159,11 @@ impl Fsm for RectangleToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
RectangleToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Rectangle"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: true,
|
||||
},
|
||||
])]),
|
||||
RectangleToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Rectangle"),
|
||||
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])]),
|
||||
RectangleToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")])]),
|
||||
};
|
||||
|
||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::application::generate_uuid;
|
||||
use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::assist_widgets::{PivotAssist, PivotPosition};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton};
|
||||
@@ -85,182 +85,112 @@ impl PropertyHolder for SelectTool {
|
||||
fn properties(&self) -> Layout {
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignLeft".into(),
|
||||
tooltip: "Align Left".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
IconButton::new("AlignLeft", 24)
|
||||
.tooltip("Align Left")
|
||||
.on_update(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::X,
|
||||
aggregate: AlignAggregate::Min,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignHorizontalCenter".into(),
|
||||
tooltip: "Align Horizontal Center".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
})
|
||||
.widget_holder(),
|
||||
IconButton::new("AlignHorizontalCenter", 24)
|
||||
.tooltip("Align Horizontal Center")
|
||||
.on_update(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::X,
|
||||
aggregate: AlignAggregate::Center,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignRight".into(),
|
||||
tooltip: "Align Right".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
})
|
||||
.widget_holder(),
|
||||
IconButton::new("AlignRight", 24)
|
||||
.tooltip("Align Right")
|
||||
.on_update(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::X,
|
||||
aggregate: AlignAggregate::Max,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignTop".into(),
|
||||
tooltip: "Align Top".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
})
|
||||
.widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
IconButton::new("AlignTop", 24)
|
||||
.tooltip("Align Top")
|
||||
.on_update(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::Y,
|
||||
aggregate: AlignAggregate::Min,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignVerticalCenter".into(),
|
||||
tooltip: "Align Vertical Center".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
})
|
||||
.widget_holder(),
|
||||
IconButton::new("AlignVerticalCenter", 24)
|
||||
.tooltip("Align Vertical Center")
|
||||
.on_update(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::Y,
|
||||
aggregate: AlignAggregate::Center,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignBottom".into(),
|
||||
tooltip: "Align Bottom".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
})
|
||||
.widget_holder(),
|
||||
IconButton::new("AlignBottom", 24)
|
||||
.tooltip("Align Bottom")
|
||||
.on_update(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::Y,
|
||||
aggregate: AlignAggregate::Max,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Align".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Section,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "FlipHorizontal".into(),
|
||||
tooltip: "Flip Horizontal".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| SelectToolMessage::FlipHorizontal.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "FlipVertical".into(),
|
||||
tooltip: "Flip Vertical".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| SelectToolMessage::FlipVertical.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
})
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
PopoverButton::new("Align", "Coming soon").widget_holder(),
|
||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||
IconButton::new("FlipHorizontal", 24)
|
||||
.tooltip("Flip Horizontal")
|
||||
.on_update(|_| SelectToolMessage::FlipHorizontal.into())
|
||||
.widget_holder(),
|
||||
IconButton::new("FlipVertical", 24)
|
||||
.tooltip("Flip Vertical")
|
||||
.on_update(|_| SelectToolMessage::FlipVertical.into())
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Flip".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Section,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanUnion".into(),
|
||||
tooltip: "Boolean Union".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::BooleanOperation(BooleanOperation::Union).into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanSubtractFront".into(),
|
||||
tooltip: "Boolean Subtract Front".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::BooleanOperation(BooleanOperation::SubtractFront).into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanSubtractBack".into(),
|
||||
tooltip: "Boolean Subtract Back".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::BooleanOperation(BooleanOperation::SubtractBack).into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanIntersect".into(),
|
||||
tooltip: "Boolean Intersect".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::BooleanOperation(BooleanOperation::Intersection).into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanDifference".into(),
|
||||
tooltip: "Boolean Difference".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::BooleanOperation(BooleanOperation::Difference).into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Boolean".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Section,
|
||||
})),
|
||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||
IconButton::new("BooleanUnion", 24)
|
||||
.tooltip("Boolean Union")
|
||||
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::Union).into())
|
||||
.widget_holder(),
|
||||
IconButton::new("BooleanSubtractFront", 24)
|
||||
.tooltip("Boolean Subtract Front")
|
||||
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::SubtractFront).into())
|
||||
.widget_holder(),
|
||||
IconButton::new("BooleanSubtractBack", 24)
|
||||
.tooltip("Boolean Subtract Back")
|
||||
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::SubtractBack).into())
|
||||
.widget_holder(),
|
||||
IconButton::new("BooleanIntersect", 24)
|
||||
.tooltip("Boolean Intersect")
|
||||
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::Intersection).into())
|
||||
.widget_holder(),
|
||||
IconButton::new("BooleanDifference", 24)
|
||||
.tooltip("Boolean Difference")
|
||||
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::Difference).into())
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
PopoverButton::new("Boolean", "Coming soon").widget_holder(),
|
||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||
// We'd like this widget to hide and show itself whenever the transformation cage is active or inactive (i.e. when no layers are selected)
|
||||
WidgetHolder::new(Widget::PivotAssist(PivotAssist {
|
||||
position: self.tool_data.pivot.to_pivot_position(),
|
||||
on_update: WidgetCallback::new(|pivot_assist: &PivotAssist| SelectToolMessage::SetPivot { position: pivot_assist.position }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
PivotAssist::new(self.tool_data.pivot.to_pivot_position())
|
||||
.on_update(|pivot_assist: &PivotAssist| SelectToolMessage::SetPivot { position: pivot_assist.position }.into())
|
||||
.widget_holder(),
|
||||
],
|
||||
}]))
|
||||
}
|
||||
@@ -268,27 +198,12 @@ impl PropertyHolder for SelectTool {
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for SelectTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
responses.push_back(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }.into());
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &(), responses);
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, false);
|
||||
|
||||
if self.tool_data.pivot.should_refresh_pivot_position() {
|
||||
// Notify the frontend about the updated pivot position (a bit ugly to do it here not in the fsm but that doesn't have SelectTool)
|
||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
}
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
}
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -321,8 +236,9 @@ impl ToolTransition for SelectTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
|
||||
enum SelectToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Dragging,
|
||||
DrawingBox,
|
||||
@@ -331,12 +247,6 @@ enum SelectToolFsmState {
|
||||
DraggingPivot,
|
||||
}
|
||||
|
||||
impl Default for SelectToolFsmState {
|
||||
fn default() -> Self {
|
||||
SelectToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct SelectToolData {
|
||||
drag_start: ViewportPosition,
|
||||
@@ -812,138 +722,34 @@ impl Fsm for SelectToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
SelectToolFsmState::Ready => HintData(vec![
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Drag Selected"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyG]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grab Selected"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyR]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Rotate Selected"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyS]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Scale Selected"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::keys([Key::KeyG], "Grab Selected"),
|
||||
HintInfo::keys([Key::KeyR], "Rotate Selected"),
|
||||
HintInfo::keys([Key::KeyS], "Scale Selected"),
|
||||
]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Select Object"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command]).into()]),
|
||||
mouse: None,
|
||||
label: String::from("Innermost"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grow/Shrink Selection"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::Lmb, "Select Object"),
|
||||
HintInfo::keys([Key::Control], "Innermost").add_mac_keys([Key::Command]).prepend_plus(),
|
||||
HintInfo::keys([Key::Shift], "Grow/Shrink Selection").prepend_plus(),
|
||||
]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Select Area"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grow/Shrink Selection"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Select Area"),
|
||||
HintInfo::keys([Key::Shift], "Grow/Shrink Selection").prepend_plus(),
|
||||
]),
|
||||
HintGroup(vec![HintInfo::arrow_keys("Nudge Selected"), HintInfo::keys([Key::Shift], "Big Increment Nudge").prepend_plus()]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![
|
||||
KeysGroup(vec![Key::ArrowUp]).into(),
|
||||
KeysGroup(vec![Key::ArrowRight]).into(),
|
||||
KeysGroup(vec![Key::ArrowDown]).into(),
|
||||
KeysGroup(vec![Key::ArrowLeft]).into(),
|
||||
],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Nudge Selected"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Big Increment Nudge"),
|
||||
plus: true,
|
||||
},
|
||||
]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Move Duplicate"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control, Key::KeyD]).into()],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::KeyD]).into()]),
|
||||
mouse: None,
|
||||
label: String::from("Duplicate"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::keys([Key::Alt], "Move Duplicate"),
|
||||
HintInfo::keys([Key::Control, Key::KeyD], "Duplicate").add_mac_keys([Key::Command, Key::KeyD]),
|
||||
]),
|
||||
]),
|
||||
SelectToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain to Axis"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap to Points (coming soon)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::keys([Key::Shift], "Constrain to Axis"),
|
||||
HintInfo::keys([Key::Control], "Snap to Points (coming soon)"),
|
||||
])]),
|
||||
SelectToolFsmState::DrawingBox => HintData(vec![]),
|
||||
SelectToolFsmState::ResizingBounds => HintData(vec![]),
|
||||
SelectToolFsmState::RotatingBounds => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
}])]),
|
||||
SelectToolFsmState::RotatingBounds => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")])]),
|
||||
SelectToolFsmState::DraggingPivot => HintData(vec![]),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
@@ -69,32 +68,20 @@ impl ToolMetadata for ShapeTool {
|
||||
|
||||
impl PropertyHolder for ShapeTool {
|
||||
fn properties(&self) -> Layout {
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
label: "Sides".into(),
|
||||
value: Some(self.options.vertices as f64),
|
||||
is_integer: true,
|
||||
min: Some(3.),
|
||||
max: Some(1000.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(number_input.value.unwrap() as u32)).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}]))
|
||||
let sides = NumberInput::new(Some(self.options.vertices as f64))
|
||||
.label("Sides")
|
||||
.int()
|
||||
.min(3.)
|
||||
.max(1000.)
|
||||
.mode(crate::messages::layout::utility_types::widget_prelude::NumberInputMode::Increment)
|
||||
.on_update(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(number_input.value.unwrap() as u32)).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![sides] }]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ShapeTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if let ToolMessage::Shape(ShapeToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
ShapeOptionsUpdate::Vertices(vertices) => self.options.vertices = vertices,
|
||||
@@ -102,13 +89,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ShapeTool {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -137,17 +118,13 @@ impl ToolTransition for ShapeTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum ShapeToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
|
||||
impl Default for ShapeToolFsmState {
|
||||
fn default() -> Self {
|
||||
ShapeToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ShapeToolData {
|
||||
sides: u32,
|
||||
@@ -201,11 +178,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
state
|
||||
}
|
||||
(Drawing, DragStop) => {
|
||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
Ready
|
||||
@@ -227,44 +200,11 @@ impl Fsm for ShapeToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
ShapeToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Draw Shape"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain 1:1 Aspect"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: true,
|
||||
},
|
||||
])]),
|
||||
ShapeToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain 1:1 Aspect"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Shape"),
|
||||
HintInfo::keys([Key::Shift], "Constrain 1:1 Aspect").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])]),
|
||||
ShapeToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain 1:1 Aspect"), HintInfo::keys([Key::Alt], "From Center")])]),
|
||||
};
|
||||
|
||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
@@ -49,8 +49,9 @@ pub enum SplineToolMessage {
|
||||
UpdateOptions(SplineOptionsUpdate),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum SplineToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Drawing,
|
||||
}
|
||||
@@ -75,32 +76,18 @@ impl ToolMetadata for SplineTool {
|
||||
|
||||
impl PropertyHolder for SplineTool {
|
||||
fn properties(&self) -> Layout {
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Weight".into(),
|
||||
value: Some(self.options.line_weight),
|
||||
is_integer: false,
|
||||
min: Some(0.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}]))
|
||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for SplineTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
SplineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
@@ -108,13 +95,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for SplineTool {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -148,11 +129,6 @@ impl ToolTransition for SplineTool {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SplineToolFsmState {
|
||||
fn default() -> Self {
|
||||
SplineToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct SplineToolData {
|
||||
points: Vec<DVec2>,
|
||||
@@ -251,28 +227,10 @@ impl Fsm for SplineToolFsmState {
|
||||
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
SplineToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Draw Spline"),
|
||||
plus: false,
|
||||
}])]),
|
||||
SplineToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Draw Spline")])]),
|
||||
SplineToolFsmState::Drawing => HintData(vec![
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Extend Spline"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Enter]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("End Spline"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Extend Spline")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Enter], "End Spline")]),
|
||||
]),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::application::generate_uuid;
|
||||
use crate::consts::{COLOR_ACCENT, SELECTION_TOLERANCE};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{FontInput, NumberInput};
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::{Separator, SeparatorDirection, SeparatorType};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
@@ -86,68 +85,49 @@ impl ToolMetadata for TextTool {
|
||||
|
||||
impl PropertyHolder for TextTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let font = FontInput {
|
||||
is_style_picker: false,
|
||||
font_family: self.options.font_name.clone(),
|
||||
font_style: self.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.widget_holder();
|
||||
let style = FontInput {
|
||||
is_style_picker: true,
|
||||
font_family: self.options.font_name.clone(),
|
||||
font_style: self.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.widget_holder();
|
||||
let size = NumberInput::new(Some(self.options.font_size as f64))
|
||||
.unit(" px")
|
||||
.label("Size")
|
||||
.int()
|
||||
.min(1.)
|
||||
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap() as u32)).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::FontInput(FontInput {
|
||||
is_style_picker: false,
|
||||
font_family: self.options.font_name.clone(),
|
||||
font_style: self.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
WidgetHolder::new(Widget::FontInput(FontInput {
|
||||
is_style_picker: true,
|
||||
font_family: self.options.font_name.clone(),
|
||||
font_style: self.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Size".into(),
|
||||
value: Some(self.options.font_size as f64),
|
||||
is_integer: true,
|
||||
min: Some(1.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap() as u32)).into()),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
],
|
||||
widgets: vec![font, WidgetHolder::related_separator(), style, WidgetHolder::related_separator(), size],
|
||||
}]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for TextTool {
|
||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if message == ToolMessage::UpdateHints {
|
||||
self.fsm_state.update_hints(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if message == ToolMessage::UpdateCursor {
|
||||
self.fsm_state.update_cursor(responses);
|
||||
return;
|
||||
}
|
||||
|
||||
if let ToolMessage::Text(TextToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
TextOptionsUpdate::Font { family, style } => {
|
||||
@@ -161,13 +141,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for TextTool {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
self.fsm_state.update_hints(responses);
|
||||
self.fsm_state.update_cursor(responses);
|
||||
}
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
@@ -196,18 +170,12 @@ impl ToolTransition for TextTool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum TextToolFsmState {
|
||||
#[default]
|
||||
Ready,
|
||||
Editing,
|
||||
}
|
||||
|
||||
impl Default for TextToolFsmState {
|
||||
fn default() -> Self {
|
||||
TextToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct TextToolData {
|
||||
layer_path: Vec<LayerId>,
|
||||
@@ -421,37 +389,10 @@ impl Fsm for TextToolFsmState {
|
||||
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
TextToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Add Text"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::Lmb),
|
||||
label: String::from("Edit Text"),
|
||||
plus: false,
|
||||
},
|
||||
])]),
|
||||
TextToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Text"), HintInfo::mouse(MouseMotion::Lmb, "Edit Text")])]),
|
||||
TextToolFsmState::Editing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control, Key::Enter]).into()],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::Enter]).into()]),
|
||||
mouse: None,
|
||||
label: String::from("Commit Edit"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Escape]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Discard Edit"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo::keys([Key::Control, Key::Enter], "Commit Edit").add_mac_keys([Key::Command, Key::Enter]),
|
||||
HintInfo::keys([Key::Escape], "Discard Edit"),
|
||||
])]),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use super::tool_messages::*;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::LayoutKeysGroup;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, LayoutKeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
@@ -23,15 +22,78 @@ impl<T> ToolCommon for T where T: for<'a> MessageHandler<ToolMessage, ToolAction
|
||||
|
||||
type Tool = dyn ToolCommon + Send + Sync;
|
||||
|
||||
/// The FSM (finite state machine) is a flowchart between different operating states that a specific tool might be in.
|
||||
/// It is the central "core" logic area of each tool which is in charge of maintaining the state of the tool and responding to events coming from outside (like user input).
|
||||
/// For example, a tool might be `Ready` or `Drawing` depending on if the user is idle or actively drawing with the mouse held down.
|
||||
/// The FSM keeps track of what the tool is doing and allows the tool to take action when events are directed at the FSM.
|
||||
/// Every tool, which implements this trait, must implement the `transition()` function.
|
||||
/// That is where new events are sent, and where the flowchart transition logic occurs to respond to events and end in a new state.
|
||||
pub trait Fsm {
|
||||
/// The implementing tool must set this to a struct designed to store the internal values stored in the tool.
|
||||
/// For example, it might be used to store the starting location of a point when a drag began so the displacement distance can be calculated.
|
||||
type ToolData;
|
||||
/// The implementing tool must set this to a struct (or `()` if none) designed to store the values of the tool options set by the user in the Options Bar
|
||||
/// (located above the viewport, below the document's tab).
|
||||
type ToolOptions;
|
||||
|
||||
/// Implementing this mandatory trait function lets a specific tool react accordingly (and potentially change its state or internal variables) upon receiving an event to do something.
|
||||
/// Based on its current state, and what the event is, the FSM (finite state machine) should direct the tool to an appropriate outcome.
|
||||
/// For example, if the tool's FSM is in a `Ready` state and receives a `DragStart` message as its event, it may decide to send some messages,
|
||||
/// update some internal tool variables, and end by transitioning to a `Drawing` state.
|
||||
#[must_use]
|
||||
fn transition(self, message: ToolMessage, tool_data: &mut Self::ToolData, transition_data: ToolActionHandlerData, options: &Self::ToolOptions, messages: &mut VecDeque<Message>) -> Self;
|
||||
|
||||
/// Implementing this trait function lets a specific tool provide a list of hints (user input actions presently available) to draw in the footer bar.
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>);
|
||||
/// Implementing this trait function lets a specific tool set the current mouse cursor icon.
|
||||
fn update_cursor(&self, responses: &mut VecDeque<Message>);
|
||||
|
||||
/// If this message is a standard tool message, process it and return true. Standard tool messages are those which are common across every tool.
|
||||
fn standard_tool_messages(&self, message: &ToolMessage, messages: &mut VecDeque<Message>) -> bool {
|
||||
// Check for standard hits or cursor events
|
||||
match message {
|
||||
ToolMessage::UpdateHints => {
|
||||
self.update_hints(messages);
|
||||
true
|
||||
}
|
||||
ToolMessage::UpdateCursor => {
|
||||
self.update_cursor(messages);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// When an event makes the tool change or do something, it is processed here to perform a step (transition) on the tool's finite state machine (FSM).
|
||||
/// This function is called by the specific tool's message handler when the dispatcher routes a message to the active tool.
|
||||
fn process_event(
|
||||
&mut self,
|
||||
message: ToolMessage,
|
||||
tool_data: &mut Self::ToolData,
|
||||
transition_data: ToolActionHandlerData,
|
||||
options: &Self::ToolOptions,
|
||||
messages: &mut VecDeque<Message>,
|
||||
update_cursor_on_transition: bool,
|
||||
) where
|
||||
Self: PartialEq + Sized + Copy,
|
||||
{
|
||||
// If this message is one of the standard tool messages, process it and exit early
|
||||
if self.standard_tool_messages(&message, messages) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Transition the tool
|
||||
let new_state = self.transition(message, tool_data, transition_data, options, messages);
|
||||
|
||||
// Update state
|
||||
if *self != new_state {
|
||||
*self = new_state;
|
||||
self.update_hints(messages);
|
||||
if update_cursor_on_transition {
|
||||
self.update_cursor(messages);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -455,6 +517,55 @@ pub struct HintInfo {
|
||||
pub plus: bool,
|
||||
}
|
||||
|
||||
impl HintInfo {
|
||||
pub fn keys(keys: impl IntoIterator<Item = Key>, label: impl Into<String>) -> Self {
|
||||
let keys: Vec<_> = keys.into_iter().collect();
|
||||
Self {
|
||||
key_groups: vec![KeysGroup(keys).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: label.into(),
|
||||
plus: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mouse(mouse_motion: MouseMotion, label: impl Into<String>) -> Self {
|
||||
Self {
|
||||
key_groups: vec![],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(mouse_motion),
|
||||
label: label.into(),
|
||||
plus: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arrow_keys(label: impl Into<String>) -> Self {
|
||||
HintInfo {
|
||||
key_groups: vec![
|
||||
KeysGroup(vec![Key::ArrowUp]).into(),
|
||||
KeysGroup(vec![Key::ArrowRight]).into(),
|
||||
KeysGroup(vec![Key::ArrowDown]).into(),
|
||||
KeysGroup(vec![Key::ArrowLeft]).into(),
|
||||
],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: label.into(),
|
||||
plus: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepend_plus(mut self) -> Self {
|
||||
self.plus = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_mac_keys(mut self, keys: impl IntoIterator<Item = Key>) -> Self {
|
||||
let mac_keys: Vec<_> = keys.into_iter().collect();
|
||||
self.key_groups_mac = Some(vec![KeysGroup(mac_keys).into()]);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tool_crash_on_layer_delete_tests {
|
||||
use crate::application::{set_uuid_seed, Editor};
|
||||
|
||||
Reference in New Issue
Block a user