Skip to content
Open
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
65 changes: 62 additions & 3 deletions editor/src/messages/tool/tool_messages/pen_tool.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::tool_prelude::*;
use crate::consts::{COLOR_OVERLAY_BLUE, DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, SEGMENT_OVERLAY_SIZE};
use crate::consts::{COLOR_OVERLAY_BLUE, DEFAULT_STROKE_WIDTH, DOUBLE_CLICK_MILLISECONDS, DRAG_THRESHOLD, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, SEGMENT_OVERLAY_SIZE};
use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_network_node_type;
use crate::messages::portfolio::document::overlays::utility_functions::path_overlays;
Expand Down Expand Up @@ -412,6 +412,9 @@ struct PenToolData {
/// and Ctrl is pressed near the anchor to make it colinear with its opposite handle.
angle_locked: bool,
path_closed: bool,
last_click_time: Option<u64>,
last_click_pos: Option<DVec2>,
pending_double_click_confirm: bool,

handle_mode: HandleMode,
prior_segment_layer: Option<LayerNodeIdentifier>,
Expand Down Expand Up @@ -448,6 +451,18 @@ impl PenToolData {
self.latest_points.clear();
self.point_index = 0;
self.snap_manager.cleanup(responses);
self.pending_double_click_confirm = false;
self.last_click_time = None;
self.last_click_pos = None;
}

fn update_click_timing(&mut self, time: u64, position: DVec2) -> bool {
let within_time = self.last_click_time.map(|last_time| time.saturating_sub(last_time) <= DOUBLE_CLICK_MILLISECONDS).unwrap_or(false);
let within_distance = self.last_click_pos.map(|last_pos| last_pos.distance(position) <= DRAG_THRESHOLD).unwrap_or(false);
let is_double_click = within_time && within_distance;
self.last_click_time = Some(time);
self.last_click_pos = Some(position);
is_double_click
}

/// Check whether target handle is primary, end, or `self.handle_end`
Expand Down Expand Up @@ -1805,6 +1820,8 @@ impl Fsm for PenToolFsmState {
self
}
(PenToolFsmState::Ready, PenToolMessage::DragStart { append_to_selected }) => {
tool_data.pending_double_click_confirm = false;
let _ = tool_data.update_click_timing(input.time, input.mouse.position);
responses.add(DocumentMessage::StartTransaction);
tool_data.handle_mode = HandleMode::Free;

Expand All @@ -1827,6 +1844,14 @@ impl Fsm for PenToolFsmState {
state
}
(PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected }) => {
let double_click = if tool_data.buffering_merged_vector {
false
} else {
tool_data.update_click_timing(input.time, input.mouse.position)
};
if !tool_data.buffering_merged_vector {
tool_data.pending_double_click_confirm = double_click;
}
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
Expand Down Expand Up @@ -1881,9 +1906,34 @@ impl Fsm for PenToolFsmState {
}
(PenToolFsmState::DraggingHandle(_), PenToolMessage::DragStop) => {
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
tool_data

// Handle double-click to close path by connecting to first point
let is_double_click = tool_data.pending_double_click_confirm;
if is_double_click {
tool_data.pending_double_click_confirm = false;

// Set next_point to first point's position of the CURRENT shape to close the path
if let Some(first_point) = tool_data.latest_points.first() {
tool_data.next_point = first_point.pos;
tool_data.next_handle_start = first_point.pos;

// Also set handle_end to ensure proper closing
if tool_data.handle_end.is_none() {
tool_data.handle_end = Some(first_point.pos);
}
}
}

let next_state = tool_data
.finish_placing_handle(SnapData::new(document, input, viewport), transform, responses)
.unwrap_or(PenToolFsmState::PlacingAnchor)
.unwrap_or(PenToolFsmState::PlacingAnchor);

// If double-click occurred and path closed, ensure we clean up properly
if is_double_click && next_state == PenToolFsmState::Ready {
tool_data.cleanup(responses);
}

next_state
}
(
PenToolFsmState::DraggingHandle(_),
Expand All @@ -1903,6 +1953,15 @@ impl Fsm for PenToolFsmState {
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
};

// If the user drags the mouse beyond the threshold, we should not close the path on release
if tool_data.pending_double_click_confirm {
if let Some(last_pos) = tool_data.last_click_pos {
if last_pos.distance(input.mouse.position) > DRAG_THRESHOLD {
tool_data.pending_double_click_confirm = false;
}
}
}

let snap_data = SnapData::new(document, input, viewport);
if tool_data.modifiers.colinear && !tool_data.toggle_colinear_debounce {
tool_data.handle_mode = match tool_data.handle_mode {
Expand Down
Loading