Implement input hints based on the active tool state (#388)

* Hook up user input hints to display in the frontend status bar

Closes #171

* MVP hint system based on tool FSM

* Fix hints for Fill and Eyedropper tools

* Add icons for keyboard shortcuts

* Fix hints for Pen Tool

* Cleanup
This commit is contained in:
Keavon Chambers
2021-12-24 01:46:03 -08:00
parent 3500160bf7
commit d2b0411295
38 changed files with 1070 additions and 212 deletions
+60 -7
View File
@@ -1,5 +1,6 @@
use crate::input::keyboard::Key;
use crate::input::keyboard::{Key, MouseMotion};
use crate::input::InputPreprocessor;
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
use crate::{document::DocumentMessageHandler, message_prelude::*};
use glam::DAffine2;
@@ -25,13 +26,24 @@ pub enum RectangleMessage {
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Rectangle {
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
self.fsm_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
if action == ToolMessage::UpdateHints {
self.fsm_state.update_hints(responses);
return;
}
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
if self.fsm_state != new_state {
self.fsm_state = new_state;
self.fsm_state.update_hints(responses);
}
}
fn actions(&self) -> ActionList {
use RectangleToolFsmState::*;
match self.fsm_state {
Ready => actions!(RectangleMessageDiscriminant; DragStart),
Dragging => actions!(RectangleMessageDiscriminant; DragStop, Abort, Resize),
Drawing => actions!(RectangleMessageDiscriminant; DragStop, Abort, Resize),
}
}
}
@@ -39,7 +51,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Rectangle {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum RectangleToolFsmState {
Ready,
Dragging,
Drawing,
}
impl Default for RectangleToolFsmState {
@@ -85,7 +97,7 @@ impl Fsm for RectangleToolFsmState {
.into(),
);
Dragging
Drawing
}
(state, Resize { center, lock_ratio }) => {
if let Some(message) = shape_data.calculate_transform(document, center, lock_ratio, input) {
@@ -94,7 +106,7 @@ impl Fsm for RectangleToolFsmState {
state
}
(Dragging, DragStop) => {
(Drawing, DragStop) => {
// TODO: introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100)
match shape_data.drag_start == input.mouse.position {
true => responses.push_back(DocumentMessage::AbortTransaction.into()),
@@ -104,7 +116,7 @@ impl Fsm for RectangleToolFsmState {
shape_data.cleanup();
Ready
}
(Dragging, Abort) => {
(Drawing, Abort) => {
responses.push_back(DocumentMessage::AbortTransaction.into());
shape_data.cleanup();
@@ -116,4 +128,45 @@ impl Fsm for RectangleToolFsmState {
self
}
}
fn update_hints(&self, responses: &mut VecDeque<Message>) {
let hint_data = match self {
RectangleToolFsmState::Ready => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![],
mouse: Some(MouseMotion::LmbDrag),
label: String::from("Draw Rectangle"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyShift])],
mouse: None,
label: String::from("Constrain Square"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyAlt])],
mouse: None,
label: String::from("From Center"),
plus: true,
},
])]),
RectangleToolFsmState::Drawing => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyShift])],
mouse: None,
label: String::from("Constrain Square"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyAlt])],
mouse: None,
label: String::from("From Center"),
plus: false,
},
])]),
};
responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
}
}