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:
milan-sedivy
2024-03-12 20:20:25 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent 2263b0ab89
commit 9ac10cdcf8
18 changed files with 218 additions and 136 deletions
@@ -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 });