Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion editor/src/messages/input_mapper/input_mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(Escape); action_dispatch=ShapeToolMessage::Abort),
entry!(KeyDown(BracketLeft); action_dispatch=ShapeToolMessage::DecreaseSides),
entry!(KeyDown(BracketRight); action_dispatch=ShapeToolMessage::IncreaseSides),
entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=ShapeToolMessage::PointerMove([Alt, Shift, Control, Shift])),
entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=ShapeToolMessage::PointerMove([Alt, Shift, Control])),
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 }),
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 }),
entry!(KeyDown(ArrowUp); modifiers=[Shift], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: 0., delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,11 +423,9 @@ impl OverlayContext {
self.render_context.stroke();
}

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

Expand Down Expand Up @@ -592,9 +590,9 @@ impl OverlayContext {
self.end_dpi_aware_transform();
}

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) {
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) {
self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED));
self.draw_arc_gizmo_angle(pivot, bold_radius, dash_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
self.text(&text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,9 @@ impl OverlayContext {
self.scene.stroke(&kurbo::Stroke::new(1.0), self.get_transform(), Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
}

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

Expand Down Expand Up @@ -542,9 +540,9 @@ impl OverlayContext {
}

#[allow(clippy::too_many_arguments)]
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) {
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) {
self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED));
self.draw_arc_gizmo_angle(pivot, bold_radius, dash_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
self.text(text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
Expand Down Expand Up @@ -123,6 +124,15 @@ impl ShapeGizmoHandlers {
Self::None => {}
}
}

pub fn gizmo_cursor_icon(&self) -> Option<MouseCursorIcon> {
match self {
Self::Star(h) => h.mouse_cursor_icon(),
Self::Polygon(h) => h.mouse_cursor_icon(),
Self::Arc(h) => h.mouse_cursor_icon(),
Self::None => None,
}
}
}

/// Central manager that coordinates shape gizmo handlers for interactive editing on the canvas.
Expand Down Expand Up @@ -256,4 +266,12 @@ impl GizmoManager {
}
}
}

/// Returns the cursor icon to display when hovering or dragging a gizmo.
///
/// If a gizmo is active (hovered or being manipulated), it returns the cursor icon associated with that gizmo;
/// otherwise, returns `None` to indicate the default crosshair cursor should be used.
pub fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
self.active_shape_handler.as_ref().and_then(|h| h.gizmo_cursor_icon())
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use crate::consts::{ARC_SNAP_THRESHOLD, COLOR_OVERLAY_RED, GIZMO_HIDE_THRESHOLD};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage};
use crate::messages::prelude::DocumentMessageHandler;
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::shapes::shape_utility::{arc_end_points, calculate_arc_text_transform, extract_arc_parameters, format_rounded};
use crate::messages::tool::tool_messages::tool_prelude::*;
Expand Down Expand Up @@ -57,7 +56,7 @@ impl SweepAngleGizmo {
self.handle_state == SweepAngleGizmoState::Dragging || self.handle_state == SweepAngleGizmoState::Snapped
}

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

self.update_state(SweepAngleGizmoState::Hover);

responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
}
}
}
Expand Down Expand Up @@ -127,6 +124,11 @@ impl SweepAngleGizmo {

// Depending on which endpoint is being dragged, draw guides relative to the static point
let point = if self.endpoint == EndpointType::End { current_end } else { current_start };

// Draw the dashed line from center to drag start position
overlay_context.dashed_line(self.position_before_rotation, viewport.transform_point2(DVec2::ZERO), None, None, Some(5.), Some(5.), Some(0.5));

// Draw the angle, text and the bold line
self.dragging_snapping_overlays(self.position_before_rotation, point, tilt_offset, viewport, overlay_context);
}
SweepAngleGizmoState::Snapped => {
Expand All @@ -143,6 +145,9 @@ impl SweepAngleGizmo {
// Draw lines from endpoints to the arc center
overlay_context.line(start, center, Some(COLOR_OVERLAY_RED), Some(2.));
overlay_context.line(end, center, Some(COLOR_OVERLAY_RED), Some(2.));

// Draw the line from drag start to arc center
overlay_context.dashed_line(self.position_before_rotation, center, None, None, Some(5.), Some(5.), Some(0.5));
}
}
}
Expand All @@ -155,7 +160,6 @@ impl SweepAngleGizmo {
let final_vector = final_point - center;
let offset_angle = initial_vector.to_angle() + tilt_offset;

let dash_radius = initial_point.distance(center);
let bold_radius = final_point.distance(center);

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

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

overlay_context.arc_sweep_angle(offset_angle, angle, final_point, bold_radius, dash_radius, center, &text, transform);
overlay_context.arc_sweep_angle(offset_angle, angle, final_point, bold_radius, center, &text, transform);
}

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

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

self.initial_sweep_angle = 360.;
self.initial_start_angle = current_start_angle + rest_angle;
self.initial_start_angle = current_start_angle;
self.update_state(SweepAngleGizmoState::Snapped);

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

self.total_angle_delta = angle_delta;
self.total_angle_delta = angle_delta - (360. - new_sweep_angle);
self.initial_sweep_angle = 360.;
self.endpoint = EndpointType::Start;
self.update_state(SweepAngleGizmoState::Snapped);

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

for i in 0..8 {
for i in 0..=8 {
let snap_point = i as f64 * FRAC_PI_4;
snap_points.push(snap_point.to_degrees());
}
Expand Down
12 changes: 10 additions & 2 deletions editor/src/messages/tool/common_functionality/shapes/arc_shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ impl ArcGizmoHandler {
}

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

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

fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
if self.sweep_angle_gizmo.hovered() || self.sweep_angle_gizmo.is_dragging_or_snapped() {
return Some(MouseCursorIcon::Default);
}

None
}

fn cleanup(&mut self) {
self.sweep_angle_gizmo.cleanup();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl Ellipse {
modifier: ShapeToolModifierKey,
responses: &mut VecDeque<Message>,
) {
let [center, lock_ratio, _, _] = modifier;
let [center, lock_ratio, _] = modifier;

if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
let Some(node_id) = graph_modification_utils::get_ellipse_id(layer, &document.network_interface) else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl Line {
modifier: ShapeToolModifierKey,
responses: &mut VecDeque<Message>,
) {
let [center, _, lock_angle, snap_angle] = modifier;
let [center, snap_angle, lock_angle] = modifier;

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
}
}

fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
if self.number_of_points_dial.is_dragging() || self.number_of_points_dial.is_hovering() {
return Some(MouseCursorIcon::EWResize);
}

if self.point_radius_handle.is_dragging_or_snapped() || self.point_radius_handle.hovered() {
return Some(MouseCursorIcon::Default);
}

None
}

fn cleanup(&mut self) {
self.number_of_points_dial.cleanup();
self.point_radius_handle.cleanup();
Expand All @@ -112,7 +124,7 @@ impl Polygon {
modifier: ShapeToolModifierKey,
responses: &mut VecDeque<Message>,
) {
let [center, lock_ratio, _, _] = modifier;
let [center, lock_ratio, _] = modifier;

if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
// TODO: We need to determine how to allow the polygon node to make irregular shapes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl Rectangle {
modifier: ShapeToolModifierKey,
responses: &mut VecDeque<Message>,
) {
let [center, lock_ratio, _, _] = modifier;
let [center, lock_ratio, _] = modifier;

if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
let Some(node_id) = graph_modification_utils::get_rectangle_id(layer, &document.network_interface) else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::ShapeToolData;
use crate::consts::{ARC_SWEEP_GIZMO_RADIUS, ARC_SWEEP_GIZMO_TEXT_HEIGHT};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
Expand Down Expand Up @@ -74,7 +75,7 @@ impl ShapeType {
}
}

pub type ShapeToolModifierKey = [Key; 4];
pub type ShapeToolModifierKey = [Key; 3];

/// The `ShapeGizmoHandler` trait defines the interactive behavior and overlay logic for shape-specific tools in the editor.
/// A gizmo is a visual handle or control point used to manipulate a shape's properties (e.g., number of sides, radius, angle).
Expand Down Expand Up @@ -127,6 +128,8 @@ pub trait ShapeGizmoHandler {
///
/// For example, dragging states or hover flags should be cleared to avoid visual glitches when switching tools or shapes.
fn cleanup(&mut self);

fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon>;
}

/// Center, Lock Ratio, Lock Angle, Snap Angle, Increase/Decrease Side
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ impl ShapeGizmoHandler for StarGizmoHandler {
self.number_of_points_dial.cleanup();
self.point_radius_handle.cleanup();
}

fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
if self.number_of_points_dial.is_dragging() || self.number_of_points_dial.is_hovering() {
return Some(MouseCursorIcon::EWResize);
}

if self.point_radius_handle.is_dragging_or_snapped() || self.point_radius_handle.hovered() {
return Some(MouseCursorIcon::Default);
}

None
}
}

#[derive(Default)]
Expand All @@ -114,7 +126,7 @@ impl Star {
modifier: ShapeToolModifierKey,
responses: &mut VecDeque<Message>,
) {
let [center, lock_ratio, _, _] = modifier;
let [center, lock_ratio, _] = modifier;

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