mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 06:08:11 +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
co-authored by
Keavon Chambers
parent
78b01b46ec
commit
d367b99bef
@@ -423,6 +423,10 @@ impl<const LENGTH: usize> BitVector<LENGTH> {
|
|||||||
(self.0[offset] & bit) != 0
|
(self.0[offset] & bit) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn key(&self, key: Key) -> bool {
|
||||||
|
self.get(key as usize)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
let mut result = 0;
|
let mut result = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
use crate::consts::DRAG_THRESHOLD;
|
||||||
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
// Origin is top left
|
// Origin is top left
|
||||||
pub type ViewportPosition = DVec2;
|
pub type ViewportPosition = DVec2;
|
||||||
@@ -85,6 +89,13 @@ impl MouseState {
|
|||||||
scroll_delta: ScrollDelta::default(),
|
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)]
|
#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
use crate::messages::layout::utility_types::layout_widget::WidgetCallback;
|
use crate::messages::layout::utility_types::layout_widget::WidgetCallback;
|
||||||
|
use graphite_proc_macros::WidgetBuilder;
|
||||||
|
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize)]
|
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
pub struct PivotAssist {
|
pub struct PivotAssist {
|
||||||
|
#[widget_builder(constructor)]
|
||||||
pub position: PivotPosition,
|
pub position: PivotPosition,
|
||||||
|
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::application::generate_uuid;
|
use crate::application::generate_uuid;
|
||||||
use crate::consts::SELECTION_TOLERANCE;
|
use crate::consts::SELECTION_TOLERANCE;
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::TargetDocument;
|
use crate::messages::portfolio::document::utility_types::misc::TargetDocument;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
@@ -59,23 +59,8 @@ impl ToolMetadata for ArtboardTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ArtboardTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ArtboardTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_message(&mut self, message: ToolMessage, transition_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.data, transition_data, &(), responses, false);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
advertise_actions!(ArtboardToolMessageDiscriminant;
|
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 {
|
enum ArtboardToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
ResizingBounds,
|
ResizingBounds,
|
||||||
Dragging,
|
Dragging,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ArtboardToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
ArtboardToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ArtboardToolData {
|
struct ArtboardToolData {
|
||||||
bounding_box_overlays: Option<BoundingBoxOverlays>,
|
bounding_box_overlays: Option<BoundingBoxOverlays>,
|
||||||
@@ -138,14 +118,9 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
if let ToolMessage::Artboard(event) = event {
|
if let ToolMessage::Artboard(event) = event {
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
(ArtboardToolFsmState::Ready | ArtboardToolFsmState::ResizingBounds | ArtboardToolFsmState::Dragging, ArtboardToolMessage::DocumentIsDirty) => {
|
(state, ArtboardToolMessage::DocumentIsDirty) if state != ArtboardToolFsmState::Drawing => {
|
||||||
match (
|
let current_artboard = tool_data.selected_artboard.and_then(|path| document.artboard_bounding_box_and_transform(&[path], font_cache));
|
||||||
tool_data
|
match (current_artboard, tool_data.bounding_box_overlays.take()) {
|
||||||
.selected_artboard
|
|
||||||
.map(|path| document.artboard_bounding_box_and_transform(&[path], font_cache))
|
|
||||||
.unwrap_or(None),
|
|
||||||
tool_data.bounding_box_overlays.take(),
|
|
||||||
) {
|
|
||||||
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
|
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
|
||||||
(Some((bounds, transform)), paths) => {
|
(Some((bounds, transform)), paths) => {
|
||||||
let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses));
|
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>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
ArtboardToolFsmState::Ready => HintData(vec![
|
ArtboardToolFsmState::Ready => HintData(vec![
|
||||||
HintGroup(vec![HintInfo {
|
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Draw Artboard")]),
|
||||||
key_groups: vec![],
|
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Move Artboard")]),
|
||||||
key_groups_mac: None,
|
HintGroup(vec![HintInfo::keys([Key::Backspace], "Delete Artboard")]),
|
||||||
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,
|
|
||||||
}]),
|
|
||||||
]),
|
]),
|
||||||
ArtboardToolFsmState::Dragging => HintData(vec![HintGroup(vec![HintInfo {
|
ArtboardToolFsmState::Dragging => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain to Axis")])]),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds => {
|
||||||
key_groups_mac: None,
|
HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")])])
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
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::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::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
use crate::messages::tool::common_functionality::resize::Resize;
|
||||||
@@ -52,23 +51,7 @@ impl PropertyHolder for EllipseTool {}
|
|||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EllipseTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EllipseTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.data, data, &(), responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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 {
|
enum EllipseToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EllipseToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
EllipseToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct EllipseToolData {
|
struct EllipseToolData {
|
||||||
data: Resize,
|
data: Resize,
|
||||||
@@ -159,12 +137,9 @@ impl Fsm for EllipseToolFsmState {
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
(Drawing, DragStop) => {
|
(Drawing, DragStop) => {
|
||||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
|
||||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
shape_data.cleanup(responses);
|
shape_data.cleanup(responses);
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
(Drawing, Abort) => {
|
(Drawing, Abort) => {
|
||||||
@@ -183,44 +158,11 @@ impl Fsm for EllipseToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
EllipseToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
EllipseToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Ellipse"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Constrain Circular").prepend_plus(),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
|
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());
|
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
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 {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EyedropperTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.data, data, &(), responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
advertise_actions!(EyedropperToolMessageDiscriminant;
|
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 {
|
enum EyedropperToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
SamplingPrimary,
|
SamplingPrimary,
|
||||||
SamplingSecondary,
|
SamplingSecondary,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EyedropperToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
EyedropperToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct EyedropperToolData {}
|
struct EyedropperToolData {}
|
||||||
|
|
||||||
@@ -162,28 +141,10 @@ impl Fsm for EyedropperToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
EyedropperToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
EyedropperToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::Lmb, "Sample to Primary"),
|
||||||
key_groups: vec![],
|
HintInfo::mouse(MouseMotion::Rmb, "Sample to Secondary"),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary => HintData(vec![HintGroup(vec![HintInfo {
|
EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Escape], "Cancel")])]),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Escape]).into()],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: None,
|
|
||||||
label: String::from("Cancel"),
|
|
||||||
plus: false,
|
|
||||||
}])]),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||||
|
|||||||
@@ -48,23 +48,7 @@ impl PropertyHolder for FillTool {}
|
|||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FillTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FillTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.data, data, &(), responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
advertise_actions!(FillToolMessageDiscriminant;
|
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 {
|
enum FillToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FillToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
FillToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct FillToolData {}
|
struct FillToolData {}
|
||||||
|
|
||||||
@@ -144,20 +123,8 @@ impl Fsm for FillToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
FillToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
FillToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::Lmb, "Fill with Primary"),
|
||||||
key_groups: vec![],
|
HintInfo::mouse(MouseMotion::Rmb, "Fill with Secondary"),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
@@ -51,8 +51,9 @@ pub enum FreehandToolMessageOptionsUpdate {
|
|||||||
LineWeight(f64),
|
LineWeight(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
enum FreehandToolFsmState {
|
enum FreehandToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
@@ -71,32 +72,18 @@ impl ToolMetadata for FreehandTool {
|
|||||||
|
|
||||||
impl PropertyHolder for FreehandTool {
|
impl PropertyHolder for FreehandTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
.unit(" px")
|
||||||
unit: " px".into(),
|
.label("Weight")
|
||||||
label: "Weight".into(),
|
.min(1.)
|
||||||
value: Some(self.options.line_weight as f64),
|
.on_update(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandToolMessageOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
is_integer: false,
|
.widget_holder();
|
||||||
min: Some(1.),
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||||
on_update: WidgetCallback::new(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandToolMessageOptionsUpdate::LineWeight(number_input.value.unwrap())).into()),
|
|
||||||
..NumberInput::default()
|
|
||||||
}))],
|
|
||||||
}]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FreehandTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FreehandTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
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 {
|
if let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
FreehandToolMessageOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
FreehandToolMessageOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||||
@@ -104,13 +91,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FreehandTool
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_state = self.fsm_state.transition(message, &mut self.data, data, &self.options, responses);
|
self.fsm_state.process_event(message, &mut self.data, data, &self.options, responses, true);
|
||||||
|
|
||||||
if self.fsm_state != new_state {
|
|
||||||
self.fsm_state = new_state;
|
|
||||||
self.fsm_state.update_hints(responses);
|
|
||||||
self.fsm_state.update_cursor(responses);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct FreehandToolData {
|
struct FreehandToolData {
|
||||||
points: Vec<DVec2>,
|
points: Vec<DVec2>,
|
||||||
@@ -222,13 +198,7 @@ impl Fsm for FreehandToolFsmState {
|
|||||||
|
|
||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
FreehandToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo {
|
FreehandToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Draw Polyline")])]),
|
||||||
key_groups: vec![],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: Some(MouseMotion::LmbDrag),
|
|
||||||
label: String::from("Draw Polyline"),
|
|
||||||
plus: false,
|
|
||||||
}])]),
|
|
||||||
FreehandToolFsmState::Drawing => HintData(vec![]),
|
FreehandToolFsmState::Drawing => HintData(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::application::generate_uuid;
|
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::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::{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::{RadioEntryData, RadioInput};
|
use crate::messages::layout::utility_types::widgets::input_widgets::{RadioEntryData, RadioInput};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||||
@@ -78,15 +78,6 @@ impl ToolMetadata for GradientTool {
|
|||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
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 {
|
if let ToolMessage::Gradient(GradientToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
GradientOptionsUpdate::Type(gradient_type) => {
|
GradientOptionsUpdate::Type(gradient_type) => {
|
||||||
@@ -100,12 +91,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_state = self.fsm_state.transition(message, &mut self.data, data, &self.options, responses);
|
self.fsm_state.process_event(message, &mut self.data, data, &self.options, responses, false);
|
||||||
|
|
||||||
if self.fsm_state != new_state {
|
|
||||||
self.fsm_state = new_state;
|
|
||||||
self.fsm_state.update_hints(responses);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
advertise_actions!(GradientToolMessageDiscriminant;
|
advertise_actions!(GradientToolMessageDiscriminant;
|
||||||
@@ -120,47 +106,29 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool
|
|||||||
|
|
||||||
impl PropertyHolder for GradientTool {
|
impl PropertyHolder for GradientTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
let gradient_type = RadioInput::new(vec![
|
||||||
widgets: vec![WidgetHolder::new(Widget::RadioInput(RadioInput {
|
RadioEntryData::new("Linear")
|
||||||
selected_index: if self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial {
|
.value("linear")
|
||||||
1
|
.tooltip("Linear Gradient")
|
||||||
} else {
|
.on_update(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Linear)).into()),
|
||||||
0
|
RadioEntryData::new("Radial")
|
||||||
},
|
.value("radial")
|
||||||
entries: vec![
|
.tooltip("Radial Gradient")
|
||||||
RadioEntryData {
|
.on_update(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Radial)).into()),
|
||||||
value: "linear".into(),
|
])
|
||||||
label: "Linear".into(),
|
.selected_index((self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial) as u32)
|
||||||
tooltip: "Linear Gradient".into(),
|
.widget_holder();
|
||||||
on_update: WidgetCallback::new(move |_| GradientToolMessage::UpdateOptions(GradientOptionsUpdate::Type(GradientType::Linear)).into()),
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![gradient_type] }]))
|
||||||
..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()
|
|
||||||
}))],
|
|
||||||
}]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
enum GradientToolFsmState {
|
enum GradientToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
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)
|
/// 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 {
|
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();
|
let bounds = layer.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap();
|
||||||
@@ -638,14 +606,7 @@ impl Fsm for GradientToolFsmState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(GradientToolFsmState::Drawing, GradientToolMessage::PointerUp) => {
|
(GradientToolFsmState::Drawing, GradientToolMessage::PointerUp) => {
|
||||||
match tool_data.drag_start.distance(input.mouse.position) <= DRAG_THRESHOLD {
|
input.mouse.finish_transaction(tool_data.drag_start, responses);
|
||||||
true => {
|
|
||||||
responses.push_back(DocumentMessage::AbortTransaction.into());
|
|
||||||
responses.push_back(GradientToolMessage::DocumentIsDirty.into());
|
|
||||||
}
|
|
||||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
tool_data.snap_manager.cleanup(responses);
|
tool_data.snap_manager.cleanup(responses);
|
||||||
|
|
||||||
GradientToolFsmState::Ready
|
GradientToolFsmState::Ready
|
||||||
@@ -669,28 +630,10 @@ impl Fsm for GradientToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
GradientToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
GradientToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Gradient"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Snap 15°").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
GradientToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo {
|
GradientToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Snap 15°")])]),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: None,
|
|
||||||
label: String::from("Snap 15°"),
|
|
||||||
plus: false,
|
|
||||||
}])]),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
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::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::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::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
use crate::messages::tool::common_functionality::resize::Resize;
|
||||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
@@ -40,23 +39,7 @@ impl PropertyHolder for ImaginateTool {}
|
|||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> 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>) {
|
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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 {
|
enum ImaginateToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ImaginateToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
ImaginateToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ImaginateToolData {
|
struct ImaginateToolData {
|
||||||
data: Resize,
|
data: Resize,
|
||||||
@@ -178,11 +157,7 @@ impl Fsm for ImaginateToolFsmState {
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
(Drawing, DragStop) => {
|
(Drawing, DragStop) => {
|
||||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
|
||||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
|
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
|
||||||
shape_data.cleanup(responses);
|
shape_data.cleanup(responses);
|
||||||
|
|
||||||
@@ -207,44 +182,11 @@ impl Fsm for ImaginateToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
ImaginateToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
ImaginateToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Repaint Frame"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
|
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());
|
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::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::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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||||
@@ -72,32 +72,18 @@ impl ToolMetadata for LineTool {
|
|||||||
|
|
||||||
impl PropertyHolder for LineTool {
|
impl PropertyHolder for LineTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
.unit(" px")
|
||||||
unit: " px".into(),
|
.label("Weight")
|
||||||
label: "Weight".into(),
|
.min(0.)
|
||||||
value: Some(self.options.line_weight as f64),
|
.on_update(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
is_integer: false,
|
.widget_holder();
|
||||||
min: Some(0.),
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||||
on_update: WidgetCallback::new(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into()),
|
|
||||||
..NumberInput::default()
|
|
||||||
}))],
|
|
||||||
}]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for LineTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for LineTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_message(&mut self, message: ToolMessage, transition_data: ToolActionHandlerData<'a>, messages: &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::Line(LineToolMessage::UpdateOptions(action)) = message {
|
if let ToolMessage::Line(LineToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
LineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
LineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||||
@@ -105,27 +91,13 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for LineTool {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
self.fsm_state.process_event(message, &mut self.tool_data, transition_data, &self.options, messages, true);
|
||||||
|
|
||||||
if self.fsm_state != new_state {
|
|
||||||
self.fsm_state = new_state;
|
|
||||||
self.fsm_state.update_hints(responses);
|
|
||||||
self.fsm_state.update_cursor(responses);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
fn actions(&self) -> ActionList {
|
||||||
use LineToolFsmState::*;
|
|
||||||
|
|
||||||
match self.fsm_state {
|
match self.fsm_state {
|
||||||
Ready => actions!(LineToolMessageDiscriminant;
|
LineToolFsmState::Ready => actions!(LineToolMessageDiscriminant; DragStart),
|
||||||
DragStart,
|
LineToolFsmState::Drawing => actions!(LineToolMessageDiscriminant; DragStop, Redraw, Abort),
|
||||||
),
|
|
||||||
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 {
|
enum LineToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LineToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
LineToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct LineToolData {
|
struct LineToolData {
|
||||||
drag_start: ViewportPosition,
|
drag_start: ViewportPosition,
|
||||||
@@ -205,20 +172,14 @@ impl Fsm for LineToolFsmState {
|
|||||||
(Drawing, Redraw { center, snap_angle, lock_angle }) => {
|
(Drawing, Redraw { center, snap_angle, lock_angle }) => {
|
||||||
tool_data.drag_current = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
|
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();
|
let keyboard = &input.keyboard;
|
||||||
responses.push_back(generate_transform(tool_data, values[0], values[1], values[2]));
|
responses.push_back(generate_transform(tool_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center)));
|
||||||
|
|
||||||
Drawing
|
Drawing
|
||||||
}
|
}
|
||||||
(Drawing, DragStop) => {
|
(Drawing, DragStop) => {
|
||||||
tool_data.drag_current = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
|
|
||||||
tool_data.snap_manager.cleanup(responses);
|
tool_data.snap_manager.cleanup(responses);
|
||||||
|
input.mouse.finish_transaction(tool_data.drag_start, 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()),
|
|
||||||
}
|
|
||||||
|
|
||||||
tool_data.path = None;
|
tool_data.path = None;
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
@@ -239,57 +200,15 @@ impl Fsm for LineToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
LineToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
LineToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Line"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Snap 15°").prepend_plus(),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||||
mouse: Some(MouseMotion::LmbDrag),
|
HintInfo::keys([Key::Control], "Lock Angle").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
LineToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
LineToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::keys([Key::Shift], "Snap 15°"),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
HintInfo::keys([Key::Alt], "From Center"),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::Control], "Lock Angle"),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -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 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 {
|
if snap_angle {
|
||||||
angle = tool_data.angle
|
|
||||||
};
|
|
||||||
|
|
||||||
if snap {
|
|
||||||
let snap_resolution = LINE_ROTATE_SNAP_ANGLE.to_radians();
|
let snap_resolution = LINE_ROTATE_SNAP_ANGLE.to_radians();
|
||||||
angle = (angle / snap_resolution).round() * snap_resolution;
|
angle = (angle / snap_resolution).round() * snap_resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
tool_data.angle = angle;
|
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());
|
let angle_vec = DVec2::new(angle.cos(), angle.sin());
|
||||||
scale = dir.dot(angle_vec);
|
line_length = line_vector.dot(angle_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if center {
|
if center {
|
||||||
start -= scale * DVec2::new(angle.cos(), angle.sin());
|
start -= line_length * DVec2::new(angle.cos(), angle.sin());
|
||||||
scale *= 2.;
|
line_length *= 2.;
|
||||||
}
|
}
|
||||||
|
|
||||||
Operation::SetLayerTransformInViewport {
|
Operation::SetLayerTransformInViewport {
|
||||||
path: tool_data.path.clone().unwrap(),
|
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()
|
.into()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
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 {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for NavigateTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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 {
|
enum NavigateToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Panning,
|
Panning,
|
||||||
Tilting,
|
Tilting,
|
||||||
Zooming,
|
Zooming,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NavigateToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
NavigateToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct NavigateToolData {
|
struct NavigateToolData {
|
||||||
drag_start: DVec2,
|
drag_start: DVec2,
|
||||||
@@ -192,76 +171,13 @@ impl Fsm for NavigateToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
NavigateToolFsmState::Ready => HintData(vec![
|
NavigateToolFsmState::Ready => HintData(vec![
|
||||||
HintGroup(vec![
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Zoom In"), HintInfo::keys([Key::Shift], "Zoom Out").prepend_plus()]),
|
||||||
HintInfo {
|
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Zoom"), HintInfo::keys([Key::Control], "Snap Increments").prepend_plus()]),
|
||||||
key_groups: vec![],
|
HintGroup(vec![HintInfo::mouse(MouseMotion::MmbDrag, "Pan")]),
|
||||||
key_groups_mac: None,
|
HintGroup(vec![HintInfo::mouse(MouseMotion::RmbDrag, "Tilt"), HintInfo::keys([Key::Control], "Snap 15°").prepend_plus()]),
|
||||||
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,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
]),
|
]),
|
||||||
NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo {
|
NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")])]),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap Increments")])]),
|
||||||
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,
|
|
||||||
}])]),
|
|
||||||
_ => HintData(Vec::new()),
|
_ => HintData(Vec::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::consts::DRAG_THRESHOLD;
|
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
use crate::messages::tool::common_functionality::resize::Resize;
|
||||||
@@ -39,23 +38,7 @@ impl PropertyHolder for NodeGraphFrameTool {}
|
|||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> 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>) {
|
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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 {
|
enum NodeGraphToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NodeGraphToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
NodeGraphToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct NodeGraphToolData {
|
struct NodeGraphToolData {
|
||||||
data: Resize,
|
data: Resize,
|
||||||
@@ -160,11 +139,7 @@ impl Fsm for NodeGraphToolFsmState {
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
(Drawing, DragStop) => {
|
(Drawing, DragStop) => {
|
||||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
|
||||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
|
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
|
||||||
shape_data.cleanup(responses);
|
shape_data.cleanup(responses);
|
||||||
|
|
||||||
@@ -188,44 +163,11 @@ impl Fsm for NodeGraphToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
NodeGraphToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
NodeGraphToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Repaint Frame"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
|
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());
|
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::consts::{SELECTION_THRESHOLD, SELECTION_TOLERANCE};
|
use crate::consts::{SELECTION_THRESHOLD, SELECTION_TOLERANCE};
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
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 {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PathTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Different actions depending on state may be wanted:
|
// 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 {
|
enum PathToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Dragging,
|
Dragging,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PathToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
PathToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct PathToolData {
|
struct PathToolData {
|
||||||
shape_editor: ShapeEditor,
|
shape_editor: ShapeEditor,
|
||||||
@@ -309,88 +288,23 @@ impl Fsm for PathToolFsmState {
|
|||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
PathToolFsmState::Ready => HintData(vec![
|
PathToolFsmState::Ready => HintData(vec![
|
||||||
HintGroup(vec![
|
HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::Lmb, "Select Point"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Grow/Shrink Selection").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
]),
|
]),
|
||||||
HintGroup(vec![HintInfo {
|
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]),
|
||||||
key_groups: vec![],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: Some(MouseMotion::LmbDrag),
|
|
||||||
label: String::from("Drag Selected"),
|
|
||||||
plus: false,
|
|
||||||
}]),
|
|
||||||
HintGroup(vec![
|
HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::arrow_keys("Nudge Selected (coming soon)"),
|
||||||
key_groups: vec![
|
HintInfo::keys([Key::Shift], "Big Increment Nudge").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
]),
|
]),
|
||||||
HintGroup(vec![
|
HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::keys([Key::KeyG], "Grab Selected (coming soon)"),
|
||||||
key_groups: vec![KeysGroup(vec![Key::KeyG]).into()],
|
HintInfo::keys([Key::KeyR], "Rotate Selected (coming soon)"),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::KeyS], "Scale Selected (coming soon)"),
|
||||||
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,
|
|
||||||
},
|
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
PathToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
PathToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::keys([Key::Alt], "Split/Align Handles (Toggle)"),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
HintInfo::keys([Key::Shift], "Share Lengths of Aligned Handles"),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::consts::LINE_ROTATE_SNAP_ANGLE;
|
use crate::consts::LINE_ROTATE_SNAP_ANGLE;
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::{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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
||||||
@@ -59,8 +59,9 @@ pub enum PenToolMessage {
|
|||||||
UpdateOptions(PenOptionsUpdate),
|
UpdateOptions(PenOptionsUpdate),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
enum PenToolFsmState {
|
enum PenToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
DraggingHandle,
|
DraggingHandle,
|
||||||
PlacingAnchor,
|
PlacingAnchor,
|
||||||
@@ -86,32 +87,18 @@ impl ToolMetadata for PenTool {
|
|||||||
|
|
||||||
impl PropertyHolder for PenTool {
|
impl PropertyHolder for PenTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
.unit(" px")
|
||||||
unit: " px".into(),
|
.label("Weight")
|
||||||
label: "Weight".into(),
|
.min(0.)
|
||||||
value: Some(self.options.line_weight),
|
.on_update(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
is_integer: false,
|
.widget_holder();
|
||||||
min: Some(0.),
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||||
on_update: WidgetCallback::new(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into()),
|
|
||||||
..NumberInput::default()
|
|
||||||
}))],
|
|
||||||
}]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PenTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PenTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
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 {
|
if let ToolMessage::Pen(PenToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
PenOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
PenOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||||
@@ -119,13 +106,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PenTool {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||||
|
|
||||||
if self.fsm_state != new_state {
|
|
||||||
self.fsm_state = new_state;
|
|
||||||
self.fsm_state.update_hints(responses);
|
|
||||||
self.fsm_state.update_cursor(responses);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct PenToolData {
|
struct PenToolData {
|
||||||
weight: f64,
|
weight: f64,
|
||||||
@@ -614,49 +590,13 @@ impl Fsm for PenToolFsmState {
|
|||||||
|
|
||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
PenToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo {
|
PenToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Draw Path")])]),
|
||||||
key_groups: vec![],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: Some(MouseMotion::Lmb),
|
|
||||||
label: String::from("Draw Path"),
|
|
||||||
plus: false,
|
|
||||||
}])]),
|
|
||||||
PenToolFsmState::DraggingHandle | PenToolFsmState::PlacingAnchor => HintData(vec![
|
PenToolFsmState::DraggingHandle | PenToolFsmState::PlacingAnchor => HintData(vec![
|
||||||
HintGroup(vec![HintInfo {
|
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Add Handle")]),
|
||||||
key_groups: vec![],
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Anchor")]),
|
||||||
key_groups_mac: None,
|
HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")]),
|
||||||
mouse: Some(MouseMotion::LmbDrag),
|
HintGroup(vec![HintInfo::keys([Key::Shift], "Break Handle")]),
|
||||||
label: String::from("Add Handle"),
|
HintGroup(vec![HintInfo::keys([Key::Enter], "End Path")]),
|
||||||
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,
|
|
||||||
}]),
|
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::consts::DRAG_THRESHOLD;
|
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
use crate::messages::tool::common_functionality::resize::Resize;
|
||||||
@@ -40,23 +39,7 @@ impl PropertyHolder for RectangleTool {}
|
|||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> 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>) {
|
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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 {
|
enum RectangleToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RectangleToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
RectangleToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct RectangleToolData {
|
struct RectangleToolData {
|
||||||
data: Resize,
|
data: Resize,
|
||||||
@@ -158,11 +137,7 @@ impl Fsm for RectangleToolFsmState {
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
(Drawing, DragStop) => {
|
(Drawing, DragStop) => {
|
||||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
|
||||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
shape_data.cleanup(responses);
|
shape_data.cleanup(responses);
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
@@ -184,44 +159,11 @@ impl Fsm for RectangleToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
RectangleToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
RectangleToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Rectangle"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
|
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());
|
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use crate::application::generate_uuid;
|
use crate::application::generate_uuid;
|
||||||
use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE};
|
use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE};
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::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::misc::LayoutTarget;
|
||||||
use crate::messages::layout::utility_types::widgets::assist_widgets::{PivotAssist, PivotPosition};
|
use crate::messages::layout::utility_types::widgets::assist_widgets::{PivotAssist, PivotPosition};
|
||||||
use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton};
|
use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton};
|
||||||
@@ -85,182 +85,112 @@ impl PropertyHolder for SelectTool {
|
|||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
IconButton::new("AlignLeft", 24)
|
||||||
icon: "AlignLeft".into(),
|
.tooltip("Align Left")
|
||||||
tooltip: "Align Left".into(),
|
.on_update(|_| {
|
||||||
size: 24,
|
|
||||||
on_update: WidgetCallback::new(|_| {
|
|
||||||
DocumentMessage::AlignSelectedLayers {
|
DocumentMessage::AlignSelectedLayers {
|
||||||
axis: AlignAxis::X,
|
axis: AlignAxis::X,
|
||||||
aggregate: AlignAggregate::Min,
|
aggregate: AlignAggregate::Min,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..IconButton::default()
|
.widget_holder(),
|
||||||
})),
|
IconButton::new("AlignHorizontalCenter", 24)
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
.tooltip("Align Horizontal Center")
|
||||||
icon: "AlignHorizontalCenter".into(),
|
.on_update(|_| {
|
||||||
tooltip: "Align Horizontal Center".into(),
|
|
||||||
size: 24,
|
|
||||||
on_update: WidgetCallback::new(|_| {
|
|
||||||
DocumentMessage::AlignSelectedLayers {
|
DocumentMessage::AlignSelectedLayers {
|
||||||
axis: AlignAxis::X,
|
axis: AlignAxis::X,
|
||||||
aggregate: AlignAggregate::Center,
|
aggregate: AlignAggregate::Center,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..IconButton::default()
|
.widget_holder(),
|
||||||
})),
|
IconButton::new("AlignRight", 24)
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
.tooltip("Align Right")
|
||||||
icon: "AlignRight".into(),
|
.on_update(|_| {
|
||||||
tooltip: "Align Right".into(),
|
|
||||||
size: 24,
|
|
||||||
on_update: WidgetCallback::new(|_| {
|
|
||||||
DocumentMessage::AlignSelectedLayers {
|
DocumentMessage::AlignSelectedLayers {
|
||||||
axis: AlignAxis::X,
|
axis: AlignAxis::X,
|
||||||
aggregate: AlignAggregate::Max,
|
aggregate: AlignAggregate::Max,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..IconButton::default()
|
.widget_holder(),
|
||||||
})),
|
WidgetHolder::unrelated_separator(),
|
||||||
WidgetHolder::new(Widget::Separator(Separator {
|
IconButton::new("AlignTop", 24)
|
||||||
direction: SeparatorDirection::Horizontal,
|
.tooltip("Align Top")
|
||||||
separator_type: SeparatorType::Unrelated,
|
.on_update(|_| {
|
||||||
})),
|
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
|
||||||
icon: "AlignTop".into(),
|
|
||||||
tooltip: "Align Top".into(),
|
|
||||||
size: 24,
|
|
||||||
on_update: WidgetCallback::new(|_| {
|
|
||||||
DocumentMessage::AlignSelectedLayers {
|
DocumentMessage::AlignSelectedLayers {
|
||||||
axis: AlignAxis::Y,
|
axis: AlignAxis::Y,
|
||||||
aggregate: AlignAggregate::Min,
|
aggregate: AlignAggregate::Min,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..IconButton::default()
|
.widget_holder(),
|
||||||
})),
|
IconButton::new("AlignVerticalCenter", 24)
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
.tooltip("Align Vertical Center")
|
||||||
icon: "AlignVerticalCenter".into(),
|
.on_update(|_| {
|
||||||
tooltip: "Align Vertical Center".into(),
|
|
||||||
size: 24,
|
|
||||||
on_update: WidgetCallback::new(|_| {
|
|
||||||
DocumentMessage::AlignSelectedLayers {
|
DocumentMessage::AlignSelectedLayers {
|
||||||
axis: AlignAxis::Y,
|
axis: AlignAxis::Y,
|
||||||
aggregate: AlignAggregate::Center,
|
aggregate: AlignAggregate::Center,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..IconButton::default()
|
.widget_holder(),
|
||||||
})),
|
IconButton::new("AlignBottom", 24)
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
.tooltip("Align Bottom")
|
||||||
icon: "AlignBottom".into(),
|
.on_update(|_| {
|
||||||
tooltip: "Align Bottom".into(),
|
|
||||||
size: 24,
|
|
||||||
on_update: WidgetCallback::new(|_| {
|
|
||||||
DocumentMessage::AlignSelectedLayers {
|
DocumentMessage::AlignSelectedLayers {
|
||||||
axis: AlignAxis::Y,
|
axis: AlignAxis::Y,
|
||||||
aggregate: AlignAggregate::Max,
|
aggregate: AlignAggregate::Max,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}),
|
})
|
||||||
..IconButton::default()
|
.widget_holder(),
|
||||||
})),
|
WidgetHolder::related_separator(),
|
||||||
WidgetHolder::new(Widget::Separator(Separator {
|
PopoverButton::new("Align", "Coming soon").widget_holder(),
|
||||||
direction: SeparatorDirection::Horizontal,
|
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||||
separator_type: SeparatorType::Related,
|
IconButton::new("FlipHorizontal", 24)
|
||||||
})),
|
.tooltip("Flip Horizontal")
|
||||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
.on_update(|_| SelectToolMessage::FlipHorizontal.into())
|
||||||
header: "Align".into(),
|
.widget_holder(),
|
||||||
text: "Coming soon".into(),
|
IconButton::new("FlipVertical", 24)
|
||||||
..Default::default()
|
.tooltip("Flip Vertical")
|
||||||
})),
|
.on_update(|_| SelectToolMessage::FlipVertical.into())
|
||||||
WidgetHolder::new(Widget::Separator(Separator {
|
.widget_holder(),
|
||||||
direction: SeparatorDirection::Horizontal,
|
WidgetHolder::related_separator(),
|
||||||
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,
|
|
||||||
})),
|
|
||||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||||
header: "Flip".into(),
|
header: "Flip".into(),
|
||||||
text: "Coming soon".into(),
|
text: "Coming soon".into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})),
|
})),
|
||||||
WidgetHolder::new(Widget::Separator(Separator {
|
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||||
direction: SeparatorDirection::Horizontal,
|
IconButton::new("BooleanUnion", 24)
|
||||||
separator_type: SeparatorType::Section,
|
.tooltip("Boolean Union")
|
||||||
})),
|
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::Union).into())
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
.widget_holder(),
|
||||||
icon: "BooleanUnion".into(),
|
IconButton::new("BooleanSubtractFront", 24)
|
||||||
tooltip: "Boolean Union".into(),
|
.tooltip("Boolean Subtract Front")
|
||||||
size: 24,
|
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::SubtractFront).into())
|
||||||
on_update: WidgetCallback::new(|_| DocumentMessage::BooleanOperation(BooleanOperation::Union).into()),
|
.widget_holder(),
|
||||||
..IconButton::default()
|
IconButton::new("BooleanSubtractBack", 24)
|
||||||
})),
|
.tooltip("Boolean Subtract Back")
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::SubtractBack).into())
|
||||||
icon: "BooleanSubtractFront".into(),
|
.widget_holder(),
|
||||||
tooltip: "Boolean Subtract Front".into(),
|
IconButton::new("BooleanIntersect", 24)
|
||||||
size: 24,
|
.tooltip("Boolean Intersect")
|
||||||
on_update: WidgetCallback::new(|_| DocumentMessage::BooleanOperation(BooleanOperation::SubtractFront).into()),
|
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::Intersection).into())
|
||||||
..IconButton::default()
|
.widget_holder(),
|
||||||
})),
|
IconButton::new("BooleanDifference", 24)
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
.tooltip("Boolean Difference")
|
||||||
icon: "BooleanSubtractBack".into(),
|
.on_update(|_| DocumentMessage::BooleanOperation(BooleanOperation::Difference).into())
|
||||||
tooltip: "Boolean Subtract Back".into(),
|
.widget_holder(),
|
||||||
size: 24,
|
WidgetHolder::related_separator(),
|
||||||
on_update: WidgetCallback::new(|_| DocumentMessage::BooleanOperation(BooleanOperation::SubtractBack).into()),
|
PopoverButton::new("Boolean", "Coming soon").widget_holder(),
|
||||||
..IconButton::default()
|
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||||
})),
|
|
||||||
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,
|
|
||||||
})),
|
|
||||||
// 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)
|
// 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 {
|
PivotAssist::new(self.tool_data.pivot.to_pivot_position())
|
||||||
position: self.tool_data.pivot.to_pivot_position(),
|
.on_update(|pivot_assist: &PivotAssist| SelectToolMessage::SetPivot { position: pivot_assist.position }.into())
|
||||||
on_update: WidgetCallback::new(|pivot_assist: &PivotAssist| SelectToolMessage::SetPivot { position: pivot_assist.position }.into()),
|
.widget_holder(),
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
],
|
],
|
||||||
}]))
|
}]))
|
||||||
}
|
}
|
||||||
@@ -268,27 +198,12 @@ impl PropertyHolder for SelectTool {
|
|||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> 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>) {
|
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if message == ToolMessage::UpdateHints {
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, false);
|
||||||
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);
|
|
||||||
|
|
||||||
if self.tool_data.pivot.should_refresh_pivot_position() {
|
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)
|
// 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);
|
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 {
|
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 {
|
enum SelectToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Dragging,
|
Dragging,
|
||||||
DrawingBox,
|
DrawingBox,
|
||||||
@@ -331,12 +247,6 @@ enum SelectToolFsmState {
|
|||||||
DraggingPivot,
|
DraggingPivot,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SelectToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
SelectToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct SelectToolData {
|
struct SelectToolData {
|
||||||
drag_start: ViewportPosition,
|
drag_start: ViewportPosition,
|
||||||
@@ -812,138 +722,34 @@ impl Fsm for SelectToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
SelectToolFsmState::Ready => HintData(vec![
|
SelectToolFsmState::Ready => HintData(vec![
|
||||||
HintGroup(vec![HintInfo {
|
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]),
|
||||||
key_groups: vec![],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: Some(MouseMotion::LmbDrag),
|
|
||||||
label: String::from("Drag Selected"),
|
|
||||||
plus: false,
|
|
||||||
}]),
|
|
||||||
HintGroup(vec![
|
HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::keys([Key::KeyG], "Grab Selected"),
|
||||||
key_groups: vec![KeysGroup(vec![Key::KeyG]).into()],
|
HintInfo::keys([Key::KeyR], "Rotate Selected"),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::KeyS], "Scale Selected"),
|
||||||
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,
|
|
||||||
},
|
|
||||||
]),
|
]),
|
||||||
HintGroup(vec![
|
HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::Lmb, "Select Object"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Control], "Innermost").add_mac_keys([Key::Command]).prepend_plus(),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::Shift], "Grow/Shrink Selection").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
]),
|
]),
|
||||||
HintGroup(vec![
|
HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::LmbDrag, "Select Area"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Grow/Shrink Selection").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
]),
|
]),
|
||||||
|
HintGroup(vec![HintInfo::arrow_keys("Nudge Selected"), HintInfo::keys([Key::Shift], "Big Increment Nudge").prepend_plus()]),
|
||||||
HintGroup(vec![
|
HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::keys([Key::Alt], "Move Duplicate"),
|
||||||
key_groups: vec![
|
HintInfo::keys([Key::Control, Key::KeyD], "Duplicate").add_mac_keys([Key::Command, Key::KeyD]),
|
||||||
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,
|
|
||||||
},
|
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
SelectToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
SelectToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::keys([Key::Shift], "Constrain to Axis"),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
HintInfo::keys([Key::Control], "Snap to Points (coming soon)"),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
SelectToolFsmState::DrawingBox => HintData(vec![]),
|
SelectToolFsmState::DrawingBox => HintData(vec![]),
|
||||||
SelectToolFsmState::ResizingBounds => HintData(vec![]),
|
SelectToolFsmState::ResizingBounds => HintData(vec![]),
|
||||||
SelectToolFsmState::RotatingBounds => HintData(vec![HintGroup(vec![HintInfo {
|
SelectToolFsmState::RotatingBounds => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")])]),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: None,
|
|
||||||
label: String::from("Snap 15°"),
|
|
||||||
plus: false,
|
|
||||||
}])]),
|
|
||||||
SelectToolFsmState::DraggingPivot => HintData(vec![]),
|
SelectToolFsmState::DraggingPivot => HintData(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::consts::DRAG_THRESHOLD;
|
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::{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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
use crate::messages::tool::common_functionality::resize::Resize;
|
||||||
@@ -69,32 +68,20 @@ impl ToolMetadata for ShapeTool {
|
|||||||
|
|
||||||
impl PropertyHolder for ShapeTool {
|
impl PropertyHolder for ShapeTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
let sides = NumberInput::new(Some(self.options.vertices as f64))
|
||||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
.label("Sides")
|
||||||
label: "Sides".into(),
|
.int()
|
||||||
value: Some(self.options.vertices as f64),
|
.min(3.)
|
||||||
is_integer: true,
|
.max(1000.)
|
||||||
min: Some(3.),
|
.mode(crate::messages::layout::utility_types::widget_prelude::NumberInputMode::Increment)
|
||||||
max: Some(1000.),
|
.on_update(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(number_input.value.unwrap() as u32)).into())
|
||||||
on_update: WidgetCallback::new(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(number_input.value.unwrap() as u32)).into()),
|
.widget_holder();
|
||||||
..NumberInput::default()
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![sides] }]))
|
||||||
}))],
|
|
||||||
}]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ShapeTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ShapeTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
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 {
|
if let ToolMessage::Shape(ShapeToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
ShapeOptionsUpdate::Vertices(vertices) => self.options.vertices = vertices,
|
ShapeOptionsUpdate::Vertices(vertices) => self.options.vertices = vertices,
|
||||||
@@ -102,13 +89,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ShapeTool {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||||
|
|
||||||
if self.fsm_state != new_state {
|
|
||||||
self.fsm_state = new_state;
|
|
||||||
self.fsm_state.update_hints(responses);
|
|
||||||
self.fsm_state.update_cursor(responses);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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 {
|
enum ShapeToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ShapeToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
ShapeToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ShapeToolData {
|
struct ShapeToolData {
|
||||||
sides: u32,
|
sides: u32,
|
||||||
@@ -201,11 +178,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
(Drawing, DragStop) => {
|
(Drawing, DragStop) => {
|
||||||
match shape_data.viewport_drag_start(document).distance(input.mouse.position) <= DRAG_THRESHOLD {
|
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
|
||||||
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
|
|
||||||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
|
||||||
}
|
|
||||||
|
|
||||||
shape_data.cleanup(responses);
|
shape_data.cleanup(responses);
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
@@ -227,44 +200,11 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
ShapeToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
ShapeToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Shape"),
|
||||||
key_groups: vec![],
|
HintInfo::keys([Key::Shift], "Constrain 1:1 Aspect").prepend_plus(),
|
||||||
key_groups_mac: None,
|
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
|
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());
|
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::consts::DRAG_THRESHOLD;
|
use crate::consts::DRAG_THRESHOLD;
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::{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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||||
@@ -49,8 +49,9 @@ pub enum SplineToolMessage {
|
|||||||
UpdateOptions(SplineOptionsUpdate),
|
UpdateOptions(SplineOptionsUpdate),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
enum SplineToolFsmState {
|
enum SplineToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
@@ -75,32 +76,18 @@ impl ToolMetadata for SplineTool {
|
|||||||
|
|
||||||
impl PropertyHolder for SplineTool {
|
impl PropertyHolder for SplineTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
.unit(" px")
|
||||||
unit: " px".into(),
|
.label("Weight")
|
||||||
label: "Weight".into(),
|
.min(0.)
|
||||||
value: Some(self.options.line_weight),
|
.on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||||
is_integer: false,
|
.widget_holder();
|
||||||
min: Some(0.),
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||||
on_update: WidgetCallback::new(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into()),
|
|
||||||
..NumberInput::default()
|
|
||||||
}))],
|
|
||||||
}]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for SplineTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for SplineTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
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 {
|
if let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
SplineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
SplineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||||
@@ -108,13 +95,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for SplineTool {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||||
|
|
||||||
if self.fsm_state != new_state {
|
|
||||||
self.fsm_state = new_state;
|
|
||||||
self.fsm_state.update_hints(responses);
|
|
||||||
self.fsm_state.update_cursor(responses);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct SplineToolData {
|
struct SplineToolData {
|
||||||
points: Vec<DVec2>,
|
points: Vec<DVec2>,
|
||||||
@@ -251,28 +227,10 @@ impl Fsm for SplineToolFsmState {
|
|||||||
|
|
||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
SplineToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo {
|
SplineToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Draw Spline")])]),
|
||||||
key_groups: vec![],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: Some(MouseMotion::Lmb),
|
|
||||||
label: String::from("Draw Spline"),
|
|
||||||
plus: false,
|
|
||||||
}])]),
|
|
||||||
SplineToolFsmState::Drawing => HintData(vec![
|
SplineToolFsmState::Drawing => HintData(vec![
|
||||||
HintGroup(vec![HintInfo {
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Extend Spline")]),
|
||||||
key_groups: vec![],
|
HintGroup(vec![HintInfo::keys([Key::Enter], "End Spline")]),
|
||||||
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,
|
|
||||||
}]),
|
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use crate::application::generate_uuid;
|
use crate::application::generate_uuid;
|
||||||
use crate::consts::{COLOR_ACCENT, SELECTION_TOLERANCE};
|
use crate::consts::{COLOR_ACCENT, SELECTION_TOLERANCE};
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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::{Layout, LayoutGroup, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
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::misc::LayoutTarget;
|
||||||
use crate::messages::layout::utility_types::widgets::input_widgets::{FontInput, NumberInput};
|
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::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
@@ -86,68 +85,49 @@ impl ToolMetadata for TextTool {
|
|||||||
|
|
||||||
impl PropertyHolder for TextTool {
|
impl PropertyHolder for TextTool {
|
||||||
fn properties(&self) -> Layout {
|
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 {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
widgets: vec![
|
widgets: vec![font, WidgetHolder::related_separator(), style, WidgetHolder::related_separator(), size],
|
||||||
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()
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
}]))
|
}]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for TextTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for TextTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, tool_data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
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 {
|
if let ToolMessage::Text(TextToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
TextOptionsUpdate::Font { family, style } => {
|
TextOptionsUpdate::Font { family, style } => {
|
||||||
@@ -161,13 +141,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for TextTool {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_state = self.fsm_state.transition(message, &mut self.tool_data, tool_data, &self.options, responses);
|
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||||
|
|
||||||
if self.fsm_state != new_state {
|
|
||||||
self.fsm_state = new_state;
|
|
||||||
self.fsm_state.update_hints(responses);
|
|
||||||
self.fsm_state.update_cursor(responses);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
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 {
|
enum TextToolFsmState {
|
||||||
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Editing,
|
Editing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextToolFsmState {
|
|
||||||
fn default() -> Self {
|
|
||||||
TextToolFsmState::Ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct TextToolData {
|
struct TextToolData {
|
||||||
layer_path: Vec<LayerId>,
|
layer_path: Vec<LayerId>,
|
||||||
@@ -421,37 +389,10 @@ impl Fsm for TextToolFsmState {
|
|||||||
|
|
||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
TextToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
TextToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Text"), HintInfo::mouse(MouseMotion::Lmb, "Edit Text")])]),
|
||||||
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::Editing => HintData(vec![HintGroup(vec![
|
TextToolFsmState::Editing => HintData(vec![HintGroup(vec![
|
||||||
HintInfo {
|
HintInfo::keys([Key::Control, Key::Enter], "Commit Edit").add_mac_keys([Key::Command, Key::Enter]),
|
||||||
key_groups: vec![KeysGroup(vec![Key::Control, Key::Enter]).into()],
|
HintInfo::keys([Key::Escape], "Discard Edit"),
|
||||||
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,
|
|
||||||
},
|
|
||||||
])]),
|
])]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use super::tool_messages::*;
|
use super::tool_messages::*;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::LayoutKeysGroup;
|
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, LayoutKeysGroup, MouseMotion};
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
|
||||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
||||||
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, 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;
|
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 {
|
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;
|
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;
|
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]
|
#[must_use]
|
||||||
fn transition(self, message: ToolMessage, tool_data: &mut Self::ToolData, transition_data: ToolActionHandlerData, options: &Self::ToolOptions, messages: &mut VecDeque<Message>) -> Self;
|
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>);
|
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>);
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -455,6 +517,55 @@ pub struct HintInfo {
|
|||||||
pub plus: bool,
|
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)]
|
#[cfg(test)]
|
||||||
mod tool_crash_on_layer_delete_tests {
|
mod tool_crash_on_layer_delete_tests {
|
||||||
use crate::application::{set_uuid_seed, Editor};
|
use crate::application::{set_uuid_seed, Editor};
|
||||||
|
|||||||
Reference in New Issue
Block a user