Skip to content

Commit 7e0a274

Browse files
0SlowPoke0Keavon
andauthored
Fix Shape tool arc gizmo snap visualization and pointer cursor icon when hovering or dragging (#2957)
* fixed cursor,arc bugs * Update cursor icon * send pointer-move only when required * make it compile * Code review * remove repeated modifier key --------- Co-authored-by: Keavon Chambers <[email protected]>
1 parent 3cc9dd7 commit 7e0a274

File tree

13 files changed

+147
-46
lines changed

13 files changed

+147
-46
lines changed

editor/src/messages/input_mapper/input_mappings.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ pub fn input_mappings() -> Mapping {
178178
entry!(KeyDown(Escape); action_dispatch=ShapeToolMessage::Abort),
179179
entry!(KeyDown(BracketLeft); action_dispatch=ShapeToolMessage::DecreaseSides),
180180
entry!(KeyDown(BracketRight); action_dispatch=ShapeToolMessage::IncreaseSides),
181-
entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=ShapeToolMessage::PointerMove([Alt, Shift, Control, Shift])),
181+
entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=ShapeToolMessage::PointerMove([Alt, Shift, Control])),
182182
entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowLeft], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
183183
entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowRight], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
184184
entry!(KeyDown(ArrowUp); modifiers=[Shift], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: 0., delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),

editor/src/messages/portfolio/document/overlays/utility_types.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -423,11 +423,9 @@ impl OverlayContext {
423423
self.render_context.stroke();
424424
}
425425

426-
pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, dash_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
426+
pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
427427
let end_point1 = pivot + bold_radius * DVec2::from_angle(angle + offset_angle);
428-
let end_point2 = pivot + dash_radius * DVec2::from_angle(offset_angle);
429428
self.line(pivot, end_point1, None, None);
430-
self.dashed_line(pivot, end_point2, None, None, Some(2.), Some(2.), Some(0.5));
431429
self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle);
432430
}
433431

@@ -592,9 +590,9 @@ impl OverlayContext {
592590
self.end_dpi_aware_transform();
593591
}
594592

595-
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, dash_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
593+
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
596594
self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED));
597-
self.draw_arc_gizmo_angle(pivot, bold_radius, dash_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
595+
self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
598596
self.text(&text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
599597
}
600598

editor/src/messages/portfolio/document/overlays/utility_types_vello.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -379,11 +379,9 @@ impl OverlayContext {
379379
self.scene.stroke(&kurbo::Stroke::new(1.0), self.get_transform(), Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
380380
}
381381

382-
pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, dash_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
382+
pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
383383
let end_point1 = pivot + bold_radius * DVec2::from_angle(angle + offset_angle);
384-
let end_point2 = pivot + dash_radius * DVec2::from_angle(offset_angle);
385384
self.line(pivot, end_point1, None, None);
386-
self.dashed_line(pivot, end_point2, None, None, Some(2.), Some(2.), Some(0.5));
387385
self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle);
388386
}
389387

@@ -542,9 +540,9 @@ impl OverlayContext {
542540
}
543541

544542
#[allow(clippy::too_many_arguments)]
545-
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, dash_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
543+
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
546544
self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED));
547-
self.draw_arc_gizmo_angle(pivot, bold_radius, dash_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
545+
self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
548546
self.text(text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
549547
}
550548

editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::messages::frontend::utility_types::MouseCursorIcon;
12
use crate::messages::message::Message;
23
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
34
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@@ -123,6 +124,15 @@ impl ShapeGizmoHandlers {
123124
Self::None => {}
124125
}
125126
}
127+
128+
pub fn gizmo_cursor_icon(&self) -> Option<MouseCursorIcon> {
129+
match self {
130+
Self::Star(h) => h.mouse_cursor_icon(),
131+
Self::Polygon(h) => h.mouse_cursor_icon(),
132+
Self::Arc(h) => h.mouse_cursor_icon(),
133+
Self::None => None,
134+
}
135+
}
126136
}
127137

128138
/// Central manager that coordinates shape gizmo handlers for interactive editing on the canvas.
@@ -256,4 +266,12 @@ impl GizmoManager {
256266
}
257267
}
258268
}
269+
270+
/// Returns the cursor icon to display when hovering or dragging a gizmo.
271+
///
272+
/// If a gizmo is active (hovered or being manipulated), it returns the cursor icon associated with that gizmo;
273+
/// otherwise, returns `None` to indicate the default crosshair cursor should be used.
274+
pub fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
275+
self.active_shape_handler.as_ref().and_then(|h| h.gizmo_cursor_icon())
276+
}
259277
}

editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/sweep_angle_gizmo.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use crate::consts::{ARC_SNAP_THRESHOLD, COLOR_OVERLAY_RED, GIZMO_HIDE_THRESHOLD};
2-
use crate::messages::frontend::utility_types::MouseCursorIcon;
32
use crate::messages::message::Message;
43
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
54
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
65
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
7-
use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage};
6+
use crate::messages::prelude::DocumentMessageHandler;
87
use crate::messages::tool::common_functionality::graph_modification_utils;
98
use crate::messages::tool::common_functionality::shapes::shape_utility::{arc_end_points, calculate_arc_text_transform, extract_arc_parameters, format_rounded};
109
use crate::messages::tool::tool_messages::tool_prelude::*;
@@ -57,7 +56,7 @@ impl SweepAngleGizmo {
5756
self.handle_state == SweepAngleGizmoState::Dragging || self.handle_state == SweepAngleGizmoState::Snapped
5857
}
5958

60-
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2, responses: &mut VecDeque<Message>) {
59+
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2) {
6160
if self.handle_state == SweepAngleGizmoState::Inactive {
6261
let Some((start, end)) = arc_end_points(Some(layer), document) else { return };
6362
let Some((_, start_angle, sweep_angle, _)) = extract_arc_parameters(Some(layer), document) else {
@@ -89,8 +88,6 @@ impl SweepAngleGizmo {
8988
self.snap_angles = Self::calculate_snap_angles();
9089

9190
self.update_state(SweepAngleGizmoState::Hover);
92-
93-
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
9491
}
9592
}
9693
}
@@ -127,6 +124,11 @@ impl SweepAngleGizmo {
127124

128125
// Depending on which endpoint is being dragged, draw guides relative to the static point
129126
let point = if self.endpoint == EndpointType::End { current_end } else { current_start };
127+
128+
// Draw the dashed line from center to drag start position
129+
overlay_context.dashed_line(self.position_before_rotation, viewport.transform_point2(DVec2::ZERO), None, None, Some(5.), Some(5.), Some(0.5));
130+
131+
// Draw the angle, text and the bold line
130132
self.dragging_snapping_overlays(self.position_before_rotation, point, tilt_offset, viewport, overlay_context);
131133
}
132134
SweepAngleGizmoState::Snapped => {
@@ -143,6 +145,9 @@ impl SweepAngleGizmo {
143145
// Draw lines from endpoints to the arc center
144146
overlay_context.line(start, center, Some(COLOR_OVERLAY_RED), Some(2.));
145147
overlay_context.line(end, center, Some(COLOR_OVERLAY_RED), Some(2.));
148+
149+
// Draw the line from drag start to arc center
150+
overlay_context.dashed_line(self.position_before_rotation, center, None, None, Some(5.), Some(5.), Some(0.5));
146151
}
147152
}
148153
}
@@ -155,7 +160,6 @@ impl SweepAngleGizmo {
155160
let final_vector = final_point - center;
156161
let offset_angle = initial_vector.to_angle() + tilt_offset;
157162

158-
let dash_radius = initial_point.distance(center);
159163
let bold_radius = final_point.distance(center);
160164

161165
let angle = initial_vector.angle_to(final_vector).to_degrees();
@@ -170,7 +174,7 @@ impl SweepAngleGizmo {
170174

171175
let transform = calculate_arc_text_transform(angle, offset_angle, center, text_texture_width);
172176

173-
overlay_context.arc_sweep_angle(offset_angle, angle, final_point, bold_radius, dash_radius, center, &text, transform);
177+
overlay_context.arc_sweep_angle(offset_angle, angle, final_point, bold_radius, center, &text, transform);
174178
}
175179

176180
pub fn update_arc(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
@@ -209,12 +213,11 @@ impl SweepAngleGizmo {
209213
let wrapped = new_sweep_angle % 360.;
210214
self.total_angle_delta = -wrapped;
211215

212-
// Remaining drag gets passed to the ending endpoint
213-
let rest_angle = angle_delta + wrapped;
214216
self.endpoint = EndpointType::End;
215217

216218
self.initial_sweep_angle = 360.;
217-
self.initial_start_angle = current_start_angle + rest_angle;
219+
self.initial_start_angle = current_start_angle;
220+
self.update_state(SweepAngleGizmoState::Snapped);
218221

219222
self.apply_arc_update(node_id, self.initial_start_angle, self.initial_sweep_angle - wrapped, input, responses);
220223
}
@@ -288,12 +291,13 @@ impl SweepAngleGizmo {
288291
}
289292
// Clamp sweep angle above 360°, switch to start
290293
() if new_sweep_angle > 360. => {
291-
let delta = angle_delta - (360. - current_sweep_angle);
294+
let delta = angle_delta - (360. - new_sweep_angle);
292295
let sign = -delta.signum();
293296

294-
self.total_angle_delta = angle_delta;
297+
self.total_angle_delta = angle_delta - (360. - new_sweep_angle);
295298
self.initial_sweep_angle = 360.;
296299
self.endpoint = EndpointType::Start;
300+
self.update_state(SweepAngleGizmoState::Snapped);
297301

298302
self.apply_arc_update(node_id, self.initial_start_angle + angle_delta, self.initial_sweep_angle + angle_delta.abs() * sign, input, responses);
299303
}
@@ -339,7 +343,7 @@ impl SweepAngleGizmo {
339343
pub fn calculate_snap_angles() -> Vec<f64> {
340344
let mut snap_points = Vec::new();
341345

342-
for i in 0..8 {
346+
for i in 0..=8 {
343347
let snap_point = i as f64 * FRAC_PI_4;
344348
snap_points.push(snap_point.to_degrees());
345349
}

editor/src/messages/tool/common_functionality/shapes/arc_shape.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ impl ArcGizmoHandler {
2626
}
2727

2828
impl ShapeGizmoHandler for ArcGizmoHandler {
29-
fn handle_state(&mut self, selected_shape_layers: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
30-
self.sweep_angle_gizmo.handle_actions(selected_shape_layers, document, mouse_position, responses);
29+
fn handle_state(&mut self, selected_shape_layers: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, _responses: &mut VecDeque<Message>) {
30+
self.sweep_angle_gizmo.handle_actions(selected_shape_layers, document, mouse_position);
3131
}
3232

3333
fn is_any_gizmo_hovered(&self) -> bool {
@@ -74,6 +74,14 @@ impl ShapeGizmoHandler for ArcGizmoHandler {
7474
arc_outline(selected_shape_layers.or(self.sweep_angle_gizmo.layer), document, overlay_context);
7575
}
7676

77+
fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
78+
if self.sweep_angle_gizmo.hovered() || self.sweep_angle_gizmo.is_dragging_or_snapped() {
79+
return Some(MouseCursorIcon::Default);
80+
}
81+
82+
None
83+
}
84+
7785
fn cleanup(&mut self) {
7886
self.sweep_angle_gizmo.cleanup();
7987
}

editor/src/messages/tool/common_functionality/shapes/ellipse_shape.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl Ellipse {
2828
modifier: ShapeToolModifierKey,
2929
responses: &mut VecDeque<Message>,
3030
) {
31-
let [center, lock_ratio, _, _] = modifier;
31+
let [center, lock_ratio, _] = modifier;
3232

3333
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
3434
let Some(node_id) = graph_modification_utils::get_ellipse_id(layer, &document.network_interface) else {

editor/src/messages/tool/common_functionality/shapes/line_shape.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl Line {
5353
modifier: ShapeToolModifierKey,
5454
responses: &mut VecDeque<Message>,
5555
) {
56-
let [center, _, lock_angle, snap_angle] = modifier;
56+
let [center, snap_angle, lock_angle] = modifier;
5757

5858
shape_tool_data.line_data.drag_current = ipp.mouse.position;
5959

editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
8989
}
9090
}
9191

92+
fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
93+
if self.number_of_points_dial.is_dragging() || self.number_of_points_dial.is_hovering() {
94+
return Some(MouseCursorIcon::EWResize);
95+
}
96+
97+
if self.point_radius_handle.is_dragging_or_snapped() || self.point_radius_handle.hovered() {
98+
return Some(MouseCursorIcon::Default);
99+
}
100+
101+
None
102+
}
103+
92104
fn cleanup(&mut self) {
93105
self.number_of_points_dial.cleanup();
94106
self.point_radius_handle.cleanup();
@@ -112,7 +124,7 @@ impl Polygon {
112124
modifier: ShapeToolModifierKey,
113125
responses: &mut VecDeque<Message>,
114126
) {
115-
let [center, lock_ratio, _, _] = modifier;
127+
let [center, lock_ratio, _] = modifier;
116128

117129
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
118130
// TODO: We need to determine how to allow the polygon node to make irregular shapes

editor/src/messages/tool/common_functionality/shapes/rectangle_shape.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl Rectangle {
2828
modifier: ShapeToolModifierKey,
2929
responses: &mut VecDeque<Message>,
3030
) {
31-
let [center, lock_ratio, _, _] = modifier;
31+
let [center, lock_ratio, _] = modifier;
3232

3333
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
3434
let Some(node_id) = graph_modification_utils::get_rectangle_id(layer, &document.network_interface) else {

0 commit comments

Comments
 (0)