mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 12:18:11 +08:00
Comprehensively polish up the input hints across all tools and states (#1670)
* Add missing keyhints to most tools * Standardize hints further * Improve GRS numerical display values * Additional hints improvements * Improve Path tool hints; add Ctrl+Shift+A point deselection --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
2263b0ab89
commit
9ac10cdcf8
@@ -318,15 +318,6 @@ impl ShapeState {
|
||||
document.metadata.document_to_viewport.transform_vector2(offset)
|
||||
}
|
||||
|
||||
pub fn select_anchor_point_by_id(&mut self, layer: LayerNodeIdentifier, id: ManipulatorGroupId, add_to_selection: bool) {
|
||||
if !add_to_selection {
|
||||
self.deselect_all();
|
||||
}
|
||||
let point = ManipulatorPointId::new(id, SelectedType::Anchor);
|
||||
let Some(selected_state) = self.selected_shape_state.get_mut(&layer) else { return };
|
||||
selected_state.select_point(point);
|
||||
}
|
||||
|
||||
/// Select/deselect the first point within the selection threshold.
|
||||
/// Returns a tuple of the points if found and the offset, or `None` otherwise.
|
||||
pub fn change_point_selection(
|
||||
@@ -359,7 +350,7 @@ impl ShapeState {
|
||||
if new_selected {
|
||||
let retain_existing_selection = add_to_selection || already_selected;
|
||||
if !retain_existing_selection {
|
||||
self.deselect_all();
|
||||
self.deselect_all_points();
|
||||
}
|
||||
|
||||
// Add to the selected points
|
||||
@@ -383,20 +374,43 @@ impl ShapeState {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn select_all_points(&mut self, document_network: &NodeNetwork) {
|
||||
for (layer, state) in self.selected_shape_state.iter_mut() {
|
||||
let Some(subpaths) = get_subpaths(*layer, document_network) else { return };
|
||||
for manipulator in get_manipulator_groups(subpaths) {
|
||||
state.select_point(ManipulatorPointId::new(manipulator.id, SelectedType::Anchor));
|
||||
for selected_type in &[SelectedType::InHandle, SelectedType::OutHandle] {
|
||||
state.deselect_point(ManipulatorPointId::new(manipulator.id, *selected_type));
|
||||
}
|
||||
}
|
||||
pub fn select_anchor_point_by_id(&mut self, layer: LayerNodeIdentifier, id: ManipulatorGroupId, add_to_selection: bool) {
|
||||
if !add_to_selection {
|
||||
self.deselect_all_points();
|
||||
}
|
||||
let point = ManipulatorPointId::new(id, SelectedType::Anchor);
|
||||
let Some(selected_state) = self.selected_shape_state.get_mut(&layer) else { return };
|
||||
selected_state.select_point(point);
|
||||
}
|
||||
|
||||
/// Selects all anchors, and deselects all handles, for the given layer.
|
||||
pub fn select_all_anchors_in_layer(&mut self, document_network: &NodeNetwork, layer: LayerNodeIdentifier) {
|
||||
let Some(state) = self.selected_shape_state.get_mut(&layer) else { return };
|
||||
Self::select_all_anchors_in_layer_with_state(document_network, layer, state);
|
||||
}
|
||||
|
||||
/// Selects all anchors, and deselects all handles, for the selected layers.
|
||||
pub fn select_all_anchors_in_selected_layers(&mut self, document_network: &NodeNetwork) {
|
||||
for (&layer, state) in self.selected_shape_state.iter_mut() {
|
||||
Self::select_all_anchors_in_layer_with_state(document_network, layer, state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deselect_all(&mut self) {
|
||||
self.selected_shape_state.values_mut().for_each(|state| state.selected_points.clear());
|
||||
/// Internal helper function that selects all anchors, and deselects all handles, for a layer given its [`LayerNodeIdentifier`] and [`SelectedLayerState`].
|
||||
fn select_all_anchors_in_layer_with_state(document_network: &NodeNetwork, layer: LayerNodeIdentifier, state: &mut SelectedLayerState) {
|
||||
let Some(subpaths) = get_subpaths(layer, document_network) else { return };
|
||||
for manipulator in get_manipulator_groups(subpaths) {
|
||||
state.select_point(ManipulatorPointId::new(manipulator.id, SelectedType::Anchor));
|
||||
state.deselect_point(ManipulatorPointId::new(manipulator.id, SelectedType::InHandle));
|
||||
state.deselect_point(ManipulatorPointId::new(manipulator.id, SelectedType::OutHandle));
|
||||
}
|
||||
}
|
||||
|
||||
/// Deselects all points (anchors and handles) across every selected layer.
|
||||
pub fn deselect_all_points(&mut self) {
|
||||
for state in self.selected_shape_state.values_mut() {
|
||||
state.selected_points.clear()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the shapes we consider for selection, we will choose draggable manipulators from these shapes.
|
||||
@@ -407,6 +421,7 @@ impl ShapeState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over the currently selected layers to get their [`LayerNodeIdentifier`]s.
|
||||
pub fn selected_layers(&self) -> impl Iterator<Item = &LayerNodeIdentifier> {
|
||||
self.selected_shape_state.keys()
|
||||
}
|
||||
@@ -427,14 +442,6 @@ impl ShapeState {
|
||||
self.iter(document_network).flat_map(|subpaths| get_manipulator_groups(subpaths))
|
||||
}
|
||||
|
||||
pub fn select_all_anchors(&mut self, document_network: &NodeNetwork, layer: LayerNodeIdentifier) {
|
||||
let Some(subpaths) = get_subpaths(layer, document_network) else { return };
|
||||
let Some(state) = self.selected_shape_state.get_mut(&layer) else { return };
|
||||
for manipulator in get_manipulator_groups(subpaths) {
|
||||
state.select_point(ManipulatorPointId::new(manipulator.id, SelectedType::Anchor))
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide the currently selected points by reference.
|
||||
pub fn selected_points(&self) -> impl Iterator<Item = &'_ ManipulatorPointId> {
|
||||
self.selected_shape_state.values().flat_map(|state| &state.selected_points)
|
||||
|
||||
@@ -429,10 +429,14 @@ impl Fsm for ArtboardToolFsmState {
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain to Axis")]),
|
||||
]),
|
||||
ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds => HintData(vec![
|
||||
ArtboardToolFsmState::Drawing => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
]),
|
||||
ArtboardToolFsmState::ResizingBounds => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Preserve Aspect Ratio"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
@@ -400,7 +400,7 @@ impl Fsm for BrushToolFsmState {
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Draw")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::BracketLeft, Key::BracketRight], "Shrink/Grow Brush")]),
|
||||
]),
|
||||
BrushToolFsmState::Drawing => HintData(vec![]),
|
||||
BrushToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
@@ -260,7 +260,10 @@ impl Fsm for EllipseToolFsmState {
|
||||
HintInfo::keys([Key::Shift], "Constrain Circular").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])]),
|
||||
EllipseToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Circular"), HintInfo::keys([Key::Alt], "From Center")])]),
|
||||
EllipseToolFsmState::Drawing => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Circular"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
@@ -305,7 +305,7 @@ impl Fsm for FreehandToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
FreehandToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Draw Polyline")])]),
|
||||
FreehandToolFsmState::Drawing => HintData(vec![]),
|
||||
FreehandToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
@@ -459,7 +459,10 @@ impl Fsm for GradientToolFsmState {
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Gradient"),
|
||||
HintInfo::keys([Key::Shift], "Snap 15°").prepend_plus(),
|
||||
])]),
|
||||
GradientToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Snap 15°")])]),
|
||||
GradientToolFsmState::Drawing => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Snap 15°")]),
|
||||
]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
@@ -234,11 +234,14 @@ impl Fsm for LineToolFsmState {
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
HintInfo::keys([Key::Control], "Lock Angle").prepend_plus(),
|
||||
])]),
|
||||
LineToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo::keys([Key::Shift], "Snap 15°"),
|
||||
HintInfo::keys([Key::Alt], "From Center"),
|
||||
HintInfo::keys([Key::Control], "Lock Angle"),
|
||||
])]),
|
||||
LineToolFsmState::Drawing => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![
|
||||
HintInfo::keys([Key::Shift], "Snap 15°"),
|
||||
HintInfo::keys([Key::Alt], "From Center"),
|
||||
HintInfo::keys([Key::Control], "Lock Angle"),
|
||||
]),
|
||||
]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
@@ -154,16 +154,22 @@ impl Fsm for NavigateToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
NavigateToolFsmState::Ready => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Zoom In"), HintInfo::keys([Key::Shift], "Zoom Out").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Zoom"), HintInfo::keys([Key::Control], "Increments").prepend_plus()]),
|
||||
HintGroup(vec![
|
||||
HintInfo::keys_and_mouse([Key::Space], MouseMotion::LmbDrag, ""),
|
||||
HintInfo::mouse(MouseMotion::MmbDrag, "Pan").prepend_slash(),
|
||||
HintInfo::mouse(MouseMotion::MmbDrag, ""),
|
||||
HintInfo::keys_and_mouse([Key::Space], MouseMotion::LmbDrag, "Pan").prepend_slash(),
|
||||
]),
|
||||
HintGroup(vec![HintInfo::keys_and_mouse([Key::Alt], MouseMotion::LmbDrag, "Tilt")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Zoom"), HintInfo::keys([Key::Control], "Increments").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Zoom In"), HintInfo::keys([Key::Shift], "Zoom Out").prepend_plus()]),
|
||||
]),
|
||||
NavigateToolFsmState::Tilting => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")]),
|
||||
]),
|
||||
NavigateToolFsmState::Zooming => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Control], "Increments")]),
|
||||
]),
|
||||
NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")])]),
|
||||
NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Increments")])]),
|
||||
_ => HintData(Vec::new()),
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ pub enum PathToolMessage {
|
||||
|
||||
// Tool-specific messages
|
||||
BreakPath,
|
||||
DeselectAllPoints,
|
||||
Delete,
|
||||
DeleteAndBreakPath,
|
||||
DragStop {
|
||||
@@ -58,7 +59,7 @@ pub enum PathToolMessage {
|
||||
shift: Key,
|
||||
},
|
||||
RightClick,
|
||||
SelectAllPoints,
|
||||
SelectAllAnchors,
|
||||
SelectedPointUpdated,
|
||||
SelectedPointXChanged {
|
||||
new_x: f64,
|
||||
@@ -163,7 +164,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
|
||||
Delete,
|
||||
NudgeSelectedPoints,
|
||||
Enter,
|
||||
SelectAllPoints,
|
||||
SelectAllAnchors,
|
||||
DeselectAllPoints,
|
||||
BreakPath,
|
||||
DeleteAndBreakPath,
|
||||
),
|
||||
@@ -174,7 +176,6 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
|
||||
DragStop,
|
||||
PointerMove,
|
||||
Delete,
|
||||
SelectAllPoints,
|
||||
BreakPath,
|
||||
DeleteAndBreakPath,
|
||||
),
|
||||
@@ -184,9 +185,10 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
|
||||
PointerMove,
|
||||
Delete,
|
||||
Enter,
|
||||
SelectAllPoints,
|
||||
BreakPath,
|
||||
DeleteAndBreakPath,
|
||||
Escape,
|
||||
RightClick,
|
||||
),
|
||||
InsertPoint => actions!(PathToolMessageDiscriminant;
|
||||
Enter,
|
||||
@@ -324,11 +326,12 @@ impl PathToolData {
|
||||
}
|
||||
self.drag_start_pos = input.mouse.position;
|
||||
self.previous_mouse_position = input.mouse.position;
|
||||
shape_editor.select_all_anchors(&document.network, layer);
|
||||
shape_editor.select_all_anchors_in_layer(&document.network, layer);
|
||||
|
||||
PathToolFsmState::Dragging
|
||||
} else {
|
||||
// Start drawing a box
|
||||
}
|
||||
// Start drawing a box
|
||||
else {
|
||||
self.drag_start_pos = input.mouse.position;
|
||||
self.previous_mouse_position = input.mouse.position;
|
||||
|
||||
@@ -495,7 +498,12 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
(PathToolFsmState::Dragging, PathToolMessage::Escape | PathToolMessage::RightClick) => {
|
||||
responses.add(DocumentMessage::AbortTransaction);
|
||||
shape_editor.deselect_all();
|
||||
shape_editor.deselect_all_points();
|
||||
tool_data.snap_manager.cleanup(responses);
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(PathToolFsmState::DrawingBox, PathToolMessage::Escape | PathToolMessage::RightClick) => {
|
||||
responses.add(DocumentMessage::AbortTransaction);
|
||||
tool_data.snap_manager.cleanup(responses);
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
@@ -525,7 +533,7 @@ impl Fsm for PathToolFsmState {
|
||||
if tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD && !shift_pressed {
|
||||
let clicked_selected = shape_editor.selected_points().any(|&point| nearest_point == Some(point));
|
||||
if clicked_selected {
|
||||
shape_editor.deselect_all();
|
||||
shape_editor.deselect_all_points();
|
||||
shape_editor.change_point_selection(&document.network, &document.metadata, input.mouse.position, SELECTION_THRESHOLD, false);
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
}
|
||||
@@ -570,8 +578,13 @@ impl Fsm for PathToolFsmState {
|
||||
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(_, PathToolMessage::SelectAllPoints) => {
|
||||
shape_editor.select_all_points(&document.network);
|
||||
(_, PathToolMessage::SelectAllAnchors) => {
|
||||
shape_editor.select_all_anchors_in_selected_layers(&document.network);
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(_, PathToolMessage::DeselectAllPoints) => {
|
||||
shape_editor.deselect_all_points();
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
@@ -609,26 +622,45 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let general_hint_data = HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Select Point"), HintInfo::keys([Key::Shift], "Extend Selection").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]),
|
||||
HintGroup(vec![HintInfo::arrow_keys("Nudge Selected"), HintInfo::keys([Key::Shift], "10x").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::KeyG, Key::KeyR, Key::KeyS], "Grab/Rotate/Scale Selected")]),
|
||||
]);
|
||||
|
||||
let hint_data = match self {
|
||||
PathToolFsmState::Ready => general_hint_data,
|
||||
PathToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
||||
HintInfo::keys([Key::Alt], "Split/Align Handles (Toggle)"),
|
||||
HintInfo::keys([Key::Shift], "Share Lengths of Aligned Handles"),
|
||||
])]),
|
||||
PathToolFsmState::DrawingBox => HintData(vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Select Area"),
|
||||
HintInfo::keys([Key::Shift], "Extend Selection").prepend_plus(),
|
||||
])]),
|
||||
PathToolFsmState::Ready => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Select Point"), HintInfo::keys([Key::Shift], "Extend Selection").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Insert Point on Segment")]),
|
||||
// TODO: Only show the following hints if at least one point is selected
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::KeyG, Key::KeyR, Key::KeyS], "Grab/Rotate/Scale Selected")]),
|
||||
HintGroup(vec![HintInfo::arrow_keys("Nudge Selected"), HintInfo::keys([Key::Shift], "10x").prepend_plus()]),
|
||||
HintGroup(vec![
|
||||
HintInfo::keys([Key::Delete], "Delete Selected"),
|
||||
// TODO: Only show the following hints if at least one anchor is selected
|
||||
HintInfo::keys([Key::Accel], "No Dissolve").prepend_plus(),
|
||||
HintInfo::keys([Key::Shift], "Break Anchor").prepend_plus(),
|
||||
]),
|
||||
]),
|
||||
PathToolFsmState::Dragging => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![
|
||||
// TODO: Make hint dynamically say "Make Handle Smooth" or "Make Handle Sharp" based on its current state
|
||||
// TODO: Switch this to the "S" key
|
||||
// TODO: Only show this if a handle (not an anchor) is being dragged, and disable that shortcut so it can't be pressed even with the hint not shown
|
||||
HintInfo::keys([Key::Alt], "Toggle Smooth/Sharp Handles"),
|
||||
// TODO: Switch this to the "Alt" key (since it's equivalent to the "From Center" modifier when drawing a line)
|
||||
// TODO: Show this only when a handle is being dragged
|
||||
HintInfo::keys([Key::Shift], "Equidistant Handles (Smooth Only)"),
|
||||
// TODO: Add "Snap 15°" modifier with the "Shift" key (only when a handle is being dragged)
|
||||
// TODO: Add "Lock Angle" modifier with the "Ctrl" key (only when a handle is being dragged)
|
||||
]),
|
||||
]),
|
||||
PathToolFsmState::DrawingBox => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Select Area"),
|
||||
HintInfo::keys([Key::Shift], "Extend Selection").prepend_plus(),
|
||||
]),
|
||||
]),
|
||||
PathToolFsmState::InsertPoint => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Insert Point")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel Insertion").prepend_slash()]),
|
||||
]),
|
||||
};
|
||||
|
||||
|
||||
@@ -721,11 +721,23 @@ impl Fsm for PenToolFsmState {
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
PenToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Draw Path")])]),
|
||||
PenToolFsmState::DraggingHandle | PenToolFsmState::PlacingAnchor => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Anchor"), HintInfo::mouse(MouseMotion::LmbDrag, "Add Handle")]),
|
||||
PenToolFsmState::PlacingAnchor => HintData(vec![
|
||||
HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::Rmb, ""),
|
||||
HintInfo::keys([Key::Escape], "").prepend_slash(),
|
||||
HintInfo::keys([Key::Enter], "End Path").prepend_slash(),
|
||||
]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Snap 15°"), HintInfo::keys([Key::Control], "Lock Angle")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Alt], "Break Handle")]), // TODO: Show this only when dragging a handle
|
||||
HintGroup(vec![HintInfo::keys([Key::Enter], "End Path")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Sharp Point"), HintInfo::mouse(MouseMotion::LmbDrag, "Add Smooth Point")]),
|
||||
]),
|
||||
PenToolFsmState::DraggingHandle => HintData(vec![
|
||||
HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::Rmb, ""),
|
||||
HintInfo::keys([Key::Escape], "").prepend_slash(),
|
||||
HintInfo::keys([Key::Enter], "End Path").prepend_slash(),
|
||||
]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Snap 15°"), HintInfo::keys([Key::Control], "Lock Angle")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Alt], "Bend Handle")]),
|
||||
]),
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ pub struct PolygonOptions {
|
||||
fill: ToolColorOptions,
|
||||
stroke: ToolColorOptions,
|
||||
vertices: u32,
|
||||
primitive_shape_type: PrimitiveShapeType,
|
||||
polygon_type: PolygonType,
|
||||
}
|
||||
|
||||
impl Default for PolygonOptions {
|
||||
@@ -32,7 +32,7 @@ impl Default for PolygonOptions {
|
||||
line_weight: 5.,
|
||||
fill: ToolColorOptions::new_secondary(),
|
||||
stroke: ToolColorOptions::new_primary(),
|
||||
primitive_shape_type: PrimitiveShapeType::Polygon,
|
||||
polygon_type: PolygonType::Convex,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,8 @@ pub enum PolygonToolMessage {
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum PrimitiveShapeType {
|
||||
Polygon = 0,
|
||||
pub enum PolygonType {
|
||||
Convex = 0,
|
||||
Star = 1,
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ pub enum PolygonOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
LineWeight(f64),
|
||||
PrimitiveShapeType(PrimitiveShapeType),
|
||||
PolygonType(PolygonType),
|
||||
StrokeColor(Option<Color>),
|
||||
StrokeColorType(ToolColorType),
|
||||
Vertices(u32),
|
||||
@@ -93,16 +93,16 @@ fn create_sides_widget(vertices: u32) -> WidgetHolder {
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
fn create_star_option_widget(primitive_shape_type: PrimitiveShapeType) -> WidgetHolder {
|
||||
fn create_star_option_widget(polygon_type: PolygonType) -> WidgetHolder {
|
||||
let entries = vec![
|
||||
RadioEntryData::new("polygon")
|
||||
.label("Polygon")
|
||||
.on_update(move |_| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::PrimitiveShapeType(PrimitiveShapeType::Polygon)).into()),
|
||||
RadioEntryData::new("convex")
|
||||
.label("Convex")
|
||||
.on_update(move |_| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::PolygonType(PolygonType::Convex)).into()),
|
||||
RadioEntryData::new("star")
|
||||
.label("Star")
|
||||
.on_update(move |_| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::PrimitiveShapeType(PrimitiveShapeType::Star)).into()),
|
||||
.on_update(move |_| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::PolygonType(PolygonType::Star)).into()),
|
||||
];
|
||||
RadioInput::new(entries).selected_index(Some(primitive_shape_type as u32)).widget_holder()
|
||||
RadioInput::new(entries).selected_index(Some(polygon_type as u32)).widget_holder()
|
||||
}
|
||||
|
||||
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
@@ -118,7 +118,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
impl LayoutHolder for PolygonTool {
|
||||
fn layout(&self) -> Layout {
|
||||
let mut widgets = vec![
|
||||
create_star_option_widget(self.options.primitive_shape_type),
|
||||
create_star_option_widget(self.options.polygon_type),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
create_sides_widget(self.options.vertices),
|
||||
];
|
||||
@@ -156,7 +156,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Polygon
|
||||
};
|
||||
match action {
|
||||
PolygonOptionsUpdate::Vertices(vertices) => self.options.vertices = vertices,
|
||||
PolygonOptionsUpdate::PrimitiveShapeType(primitive_shape_type) => self.options.primitive_shape_type = primitive_shape_type,
|
||||
PolygonOptionsUpdate::PolygonType(polygon_type) => self.options.polygon_type = polygon_type,
|
||||
PolygonOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
@@ -242,9 +242,9 @@ impl Fsm for PolygonToolFsmState {
|
||||
polygon_data.start(document, input);
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
let subpath = match tool_options.primitive_shape_type {
|
||||
PrimitiveShapeType::Polygon => bezier_rs::Subpath::new_regular_polygon(DVec2::ZERO, tool_options.vertices as u64, 1.),
|
||||
PrimitiveShapeType::Star => bezier_rs::Subpath::new_star_polygon(DVec2::ZERO, tool_options.vertices as u64, 1., 0.5),
|
||||
let subpath = match tool_options.polygon_type {
|
||||
PolygonType::Convex => bezier_rs::Subpath::new_regular_polygon(DVec2::ZERO, tool_options.vertices as u64, 1.),
|
||||
PolygonType::Star => bezier_rs::Subpath::new_star_polygon(DVec2::ZERO, tool_options.vertices as u64, 1., 0.5),
|
||||
};
|
||||
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(), responses);
|
||||
polygon_data.layer = Some(layer);
|
||||
@@ -302,10 +302,13 @@ impl Fsm for PolygonToolFsmState {
|
||||
let hint_data = match self {
|
||||
PolygonToolFsmState::Ready => HintData(vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Polygon"),
|
||||
HintInfo::keys([Key::Shift], "Constrain 1:1 Aspect").prepend_plus(),
|
||||
HintInfo::keys([Key::Shift], "Constrain Regular").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])]),
|
||||
PolygonToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain 1:1 Aspect"), HintInfo::keys([Key::Alt], "From Center")])]),
|
||||
PolygonToolFsmState::Drawing => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Regular"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
@@ -267,7 +267,10 @@ impl Fsm for RectangleToolFsmState {
|
||||
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])]),
|
||||
RectangleToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")])]),
|
||||
RectangleToolFsmState::Drawing => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
@@ -1025,7 +1025,6 @@ impl Fsm for SelectToolFsmState {
|
||||
SelectToolFsmState::Ready { selection } => {
|
||||
let hint_data = HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::KeyG, Key::KeyR, Key::KeyS], "Grab/Rotate/Scale Selected")]),
|
||||
HintGroup({
|
||||
let mut hints = vec![HintInfo::mouse(MouseMotion::Lmb, "Select Object"), HintInfo::keys([Key::Shift], "Extend Selection").prepend_plus()];
|
||||
if *selection == NestedSelectionBehavior::Shallowest {
|
||||
@@ -1037,6 +1036,7 @@ impl Fsm for SelectToolFsmState {
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Select Area"),
|
||||
HintInfo::keys([Key::Shift], "Extend Selection").prepend_plus(),
|
||||
]),
|
||||
HintGroup(vec![HintInfo::keys([Key::KeyG, Key::KeyR, Key::KeyS], "Grab/Rotate/Scale Selected")]),
|
||||
HintGroup(vec![
|
||||
HintInfo::arrow_keys("Nudge Selected"),
|
||||
HintInfo::keys([Key::Shift], "10x").prepend_plus(),
|
||||
@@ -1053,14 +1053,17 @@ impl Fsm for SelectToolFsmState {
|
||||
SelectToolFsmState::Dragging => {
|
||||
let hint_data = HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain to Axis")]),
|
||||
HintGroup(vec![
|
||||
HintInfo::keys_and_mouse([Key::Alt], MouseMotion::LmbDrag, "Move Duplicate"),
|
||||
HintInfo::keys([Key::Alt], "Move Duplicate"),
|
||||
HintInfo::keys([Key::Control, Key::KeyD], "Place Duplicate").add_mac_keys([Key::Command, Key::KeyD]),
|
||||
]),
|
||||
]);
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
}
|
||||
SelectToolFsmState::DrawingBox { .. } => {
|
||||
// TODO: Add hint and implement functionality for holding Shift to extend the selection, thus preventing the prior selection from being cleared
|
||||
// TODO: Also fix the current functionality so canceling the box select doesn't clear the prior selection
|
||||
let hint_data = HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]);
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
}
|
||||
|
||||
@@ -297,6 +297,7 @@ impl Fsm for SplineToolFsmState {
|
||||
let hint_data = match self {
|
||||
SplineToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Draw Spline")])]),
|
||||
SplineToolFsmState::Drawing => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Extend Spline")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Enter], "End Spline")]),
|
||||
]),
|
||||
|
||||
@@ -455,11 +455,14 @@ impl Fsm for TextToolFsmState {
|
||||
|
||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||
let hint_data = match self {
|
||||
TextToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Text"), HintInfo::mouse(MouseMotion::Lmb, "Edit Text")])]),
|
||||
TextToolFsmState::Editing => HintData(vec![HintGroup(vec![
|
||||
HintInfo::keys([Key::Control, Key::Enter], "Commit Edit").add_mac_keys([Key::Command, Key::Enter]),
|
||||
HintInfo::keys([Key::Escape], "Discard Edit"),
|
||||
])]),
|
||||
TextToolFsmState::Ready => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Place Text")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Edit Text")]),
|
||||
]),
|
||||
TextToolFsmState::Editing => HintData(vec![
|
||||
HintGroup(vec![HintInfo::keys([Key::Escape], "Discard Changes")]),
|
||||
HintGroup(vec![HintInfo::keys([Key::Control, Key::Enter], "Commit Changes").add_mac_keys([Key::Command, Key::Enter])]),
|
||||
]),
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
|
||||
Reference in New Issue
Block a user