diff --git a/editor/src/messages/input_mapper/default_mapping.rs b/editor/src/messages/input_mapper/default_mapping.rs index 30917842..d6cb6bd7 100644 --- a/editor/src/messages/input_mapper/default_mapping.rs +++ b/editor/src/messages/input_mapper/default_mapping.rs @@ -197,7 +197,8 @@ pub fn default_mapping() -> Mapping { entry!(KeyDown(KeyS); action_dispatch=PathToolMessage::GRS { key: KeyS }), entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=PathToolMessage::PointerMove { alt: Alt, shift: Shift }), entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete), - entry!(KeyDown(KeyA); modifiers=[Control], action_dispatch=PathToolMessage::SelectAllPoints), + entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors), + entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::DeselectAllPoints), entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete), entry!(KeyUp(Lmb); action_dispatch=PathToolMessage::DragStop { shift_mirror_distance: Shift }), entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { @@ -230,7 +231,7 @@ pub fn default_mapping() -> Mapping { entry!(KeyDown(ArrowDown); modifiers=[Shift, ArrowRight], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT }), // // PenToolMessage - entry!(PointerMove; refresh_keys=[Control, Shift], action_dispatch=PenToolMessage::PointerMove { snap_angle: Shift, break_handle: Alt, lock_angle: Control}), + entry!(PointerMove; refresh_keys=[Control, Alt, Shift], action_dispatch=PenToolMessage::PointerMove { snap_angle: Shift, break_handle: Alt, lock_angle: Control}), entry!(KeyDown(Lmb); action_dispatch=PenToolMessage::DragStart), entry!(KeyUp(Lmb); action_dispatch=PenToolMessage::DragStop), entry!(KeyDown(Rmb); action_dispatch=PenToolMessage::Confirm), @@ -296,8 +297,8 @@ pub fn default_mapping() -> Mapping { entry!(KeyDown(KeyZ); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::Redo), entry!(KeyDown(KeyY); modifiers=[Accel], action_dispatch=DocumentMessage::Redo), entry!(KeyDown(KeyZ); modifiers=[Accel], action_dispatch=DocumentMessage::Undo), - entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::DeselectAllLayers), entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=DocumentMessage::SelectAllLayers), + entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::DeselectAllLayers), entry!(KeyDown(KeyS); modifiers=[Accel], action_dispatch=DocumentMessage::SaveDocument), entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=DocumentMessage::DuplicateSelectedLayers), entry!(KeyDown(KeyJ); modifiers=[Accel], action_dispatch=DocumentMessage::DuplicateSelectedLayers), diff --git a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs index 48de9a8a..2cb3cd7d 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs @@ -192,6 +192,8 @@ impl MessageHandler, & responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }); responses.add(FrontendMessage::UpdateInputHints { hint_data: HintData(vec![ + // TODO: Fix bug where canceling doesn't work except with the Navigate tool active + HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), HintGroup(vec![HintInfo { key_groups: vec![KeysGroup(vec![Key::Control]).into()], key_groups_mac: None, @@ -200,8 +202,6 @@ impl MessageHandler, & plus: false, slash: false, }]), - HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Confirm")]), - HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, "Cancel")]), ]), }); @@ -268,7 +268,8 @@ impl MessageHandler, & responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing }); responses.add(FrontendMessage::UpdateInputHints { - hint_data: HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, "Cancel")])]), + // TODO: Fix bug where canceling doesn't work except with the Navigate tool active + hint_data: HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]), }); self.mouse_position = ipp.mouse.position; @@ -302,6 +303,8 @@ impl MessageHandler, & responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn }); responses.add(FrontendMessage::UpdateInputHints { hint_data: HintData(vec![ + // TODO: Fix bug where canceling doesn't work except with the Navigate tool active + HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), HintGroup(vec![HintInfo { key_groups: vec![KeysGroup(vec![Key::Control]).into()], key_groups_mac: None, @@ -310,7 +313,6 @@ impl MessageHandler, & plus: false, slash: false, }]), - HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, "Cancel")]), ]), }); diff --git a/editor/src/messages/portfolio/document/utility_types/transformation.rs b/editor/src/messages/portfolio/document/utility_types/transformation.rs index 1241b8bf..fcef6c56 100644 --- a/editor/src/messages/portfolio/document/utility_types/transformation.rs +++ b/editor/src/messages/portfolio/document/utility_types/transformation.rs @@ -274,37 +274,33 @@ impl TransformOperation { use crate::messages::input_mapper::utility_types::input_keyboard::Key; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; - let mut hints = Vec::new(); - - let axis_str = |vector: DVec2, separate: bool| match axis_constraint { - Axis::Both => { - if separate { - format!("X: {}, Y: {}", vector.x, vector.y) - } else { - vector.x.to_string() - } - } - Axis::X => format!("X: {}", vector.x), - Axis::Y => format!("Y: {}", vector.y), - }; - - let value_str = match self { - TransformOperation::None => String::new(), - TransformOperation::Grabbing(translation) => format!("Translate {}", axis_str(translation.to_dvec(), true)), - TransformOperation::Rotating(rotation) => format!("Rotate {}°", rotation.to_f64(snapping) * 360. / std::f64::consts::TAU), - TransformOperation::Scaling(scale) => format!("Scale {}", axis_str(scale.to_dvec(snapping), false)), - }; - hints.push(HintInfo::label(value_str)); - hints.push(HintInfo::keys([Key::Shift], "Precision Mode")); + let mut input_hints = Vec::new(); + input_hints.push(HintInfo::keys([Key::Shift], "Slow Mode")); if matches!(self, TransformOperation::Rotating(_) | TransformOperation::Scaling(_)) { - hints.push(HintInfo::keys([Key::Control], "Snap")); + input_hints.push(HintInfo::keys([Key::Control], "Snap")); } if matches!(self, TransformOperation::Grabbing(_) | TransformOperation::Scaling(_)) { - hints.push(HintInfo::keys([Key::KeyX], "X Axis")); - hints.push(HintInfo::keys([Key::KeyY], "Y Axis")); + input_hints.push(HintInfo::keys([Key::KeyX], "Along X Axis")); + input_hints.push(HintInfo::keys([Key::KeyY], "Along Y Axis")); } - let hint_data = HintData(vec![HintGroup(hints)]); + // TODO: Eventually, move this somewhere else (maybe an overlay in the corner of the viewport, design is TBD) since servicable but not ideal for UI design consistency to have it in the hints bar + let axis_text = |vector: DVec2, separate: bool| match (axis_constraint, separate) { + (Axis::Both, false) => format!("by {:.3}", vector.x), + (Axis::Both, true) => format!("by {:.3}, {:.3}", vector.x, vector.y), + (Axis::X, _) => format!("X by {:.3}", vector.x), + (Axis::Y, _) => format!("Y by {:.3}", vector.y), + }; + let grs_value_text = match self { + TransformOperation::None => String::new(), + // TODO: Fix that the translation is showing numbers in viewport space, not document space + TransformOperation::Grabbing(translation) => format!("Translating {}", axis_text(translation.to_dvec(), true)), + TransformOperation::Rotating(rotation) => format!("Rotating by {:.3}°", rotation.to_f64(snapping) * 360. / std::f64::consts::TAU), + TransformOperation::Scaling(scale) => format!("Scaling {}", axis_text(scale.to_dvec(snapping), false)), + }; + let grs_value = vec![HintInfo::label(grs_value_text)]; + + let hint_data = HintData(vec![HintGroup(input_hints), HintGroup(grs_value)]); responses.add(FrontendMessage::UpdateInputHints { hint_data }); } } diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 059e0168..bbc07bdf 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -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 { 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 { self.selected_shape_state.values().flat_map(|state| &state.selected_points) diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index e687cde2..76f2f39d 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -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 }); diff --git a/editor/src/messages/tool/tool_messages/brush_tool.rs b/editor/src/messages/tool/tool_messages/brush_tool.rs index f9f0a649..ac8990b7 100644 --- a/editor/src/messages/tool/tool_messages/brush_tool.rs +++ b/editor/src/messages/tool/tool_messages/brush_tool.rs @@ -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 }); diff --git a/editor/src/messages/tool/tool_messages/ellipse_tool.rs b/editor/src/messages/tool/tool_messages/ellipse_tool.rs index e3286c8e..c8f83734 100644 --- a/editor/src/messages/tool/tool_messages/ellipse_tool.rs +++ b/editor/src/messages/tool/tool_messages/ellipse_tool.rs @@ -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 }); diff --git a/editor/src/messages/tool/tool_messages/freehand_tool.rs b/editor/src/messages/tool/tool_messages/freehand_tool.rs index 2d81dff6..99bad4ba 100644 --- a/editor/src/messages/tool/tool_messages/freehand_tool.rs +++ b/editor/src/messages/tool/tool_messages/freehand_tool.rs @@ -305,7 +305,7 @@ impl Fsm for FreehandToolFsmState { fn update_hints(&self, responses: &mut VecDeque) { 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 }); diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index 92b95b9e..ed174886 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -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 }); diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 16901241..98c2c764 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -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 }); diff --git a/editor/src/messages/tool/tool_messages/navigate_tool.rs b/editor/src/messages/tool/tool_messages/navigate_tool.rs index f87fb0a3..587316cc 100644 --- a/editor/src/messages/tool/tool_messages/navigate_tool.rs +++ b/editor/src/messages/tool/tool_messages/navigate_tool.rs @@ -154,16 +154,22 @@ impl Fsm for NavigateToolFsmState { fn update_hints(&self, responses: &mut VecDeque) { 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()), }; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index f6a65f7f..538a6897 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -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> for PathToo Delete, NudgeSelectedPoints, Enter, - SelectAllPoints, + SelectAllAnchors, + DeselectAllPoints, BreakPath, DeleteAndBreakPath, ), @@ -174,7 +176,6 @@ impl<'a> MessageHandler> for PathToo DragStop, PointerMove, Delete, - SelectAllPoints, BreakPath, DeleteAndBreakPath, ), @@ -184,9 +185,10 @@ impl<'a> MessageHandler> 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) { - 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()]), ]), }; diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index bdc28a0f..03575264 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -721,11 +721,23 @@ impl Fsm for PenToolFsmState { fn update_hints(&self, responses: &mut VecDeque) { 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")]), ]), }; diff --git a/editor/src/messages/tool/tool_messages/polygon_tool.rs b/editor/src/messages/tool/tool_messages/polygon_tool.rs index 663b04bc..93ee432f 100644 --- a/editor/src/messages/tool/tool_messages/polygon_tool.rs +++ b/editor/src/messages/tool/tool_messages/polygon_tool.rs @@ -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), FillColorType(ToolColorType), LineWeight(f64), - PrimitiveShapeType(PrimitiveShapeType), + PolygonType(PolygonType), StrokeColor(Option), 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> 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 }); diff --git a/editor/src/messages/tool/tool_messages/rectangle_tool.rs b/editor/src/messages/tool/tool_messages/rectangle_tool.rs index ff2715c9..04c02176 100644 --- a/editor/src/messages/tool/tool_messages/rectangle_tool.rs +++ b/editor/src/messages/tool/tool_messages/rectangle_tool.rs @@ -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 }); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 616ddf16..9e5581b7 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -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 }); } diff --git a/editor/src/messages/tool/tool_messages/spline_tool.rs b/editor/src/messages/tool/tool_messages/spline_tool.rs index 22337553..bc5d4df7 100644 --- a/editor/src/messages/tool/tool_messages/spline_tool.rs +++ b/editor/src/messages/tool/tool_messages/spline_tool.rs @@ -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")]), ]), diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index 4f897fab..6bef8d23 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -455,11 +455,14 @@ impl Fsm for TextToolFsmState { fn update_hints(&self, responses: &mut VecDeque) { 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 });