Repository: GraphiteEditor/Graphite (language: rust)

Issue:
Reduce path editing clutter with options to hide non-selected anchors' handles
There should be three modes, explained below. But in summary:
- The first mode is the current behavior where all handles are shown.
- The second mode is where all selected anchors' segments' handles are shown.
- The third mode is where only the frontier handles are shown.

## Pen tool

In the Pen tool, only these circled handles should appear:
 
- Only the last-drawn anchor and its handles are shown, plus the handle on the other end of its connected segment. The Pen tool only has modes 1 and 2, but not 3.
  
  ![Image](https://github.com/user-attachments/assets/6e535103-c101-4ba7-8b45-6485dc4931ca)

## Path tool

In the Path tool, which allows multi-selection of anchors, these are the cases where it should appear:
  
- When one anchor is selected, its handles should be shown, as well as the handles of its connected segment(s). When one anchor is selected, this same situation applies to both modes 2 and 3, because mode 3 only becomes active with multi-selection (otherwise mode 2 is used).
  
  ![Image](https://github.com/user-attachments/assets/9f9f4c69-020a-4e25-a4d6-46324c8d795e)

- **Mode 2:** When two or more anchors are selected, all handles (circled in red) on either end of the adjacent segments (purple) are shown.
  
  ![Image](https://github.com/user-attachments/assets/42a1e5ba-e84d-4af8-849d-70c2fe13051d)

- **Mode 3:** When two or more anchors are selected, only the handles on the **frontier** are shown. The green shows the adjacent segments to the selected anchors, and the ends of the green are the frontiers. Selected in red are the only handles to be shown.
  
  ![Image](https://github.com/user-attachments/assets/f1873653-390c-489b-9b15-2e4abe7fc801)

Remember: only the handles in the diagrams above which are circled in red should appear, all other handles should be hidden.

The three modes (two for Pen tool) should be in the respective tools' control bars as a radio button. just use "1", "2", and "3" text for now until I come up with names or icons. The control bar is the area on the left above the drawing viewport where the tool controls are located.


Discussion / hints:
I am working on this, opened a draft PR #2264

Fix patch:
diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs
index 89dfe0313a..a31e119be1 100644
--- a/editor/src/messages/tool/tool_messages/path_tool.rs
+++ b/editor/src/messages/tool/tool_messages/path_tool.rs
@@ -779,6 +779,10 @@ impl Fsm for PathToolFsmState {
 					}
 					PathOverlayMode::FrontierHandles => {
 						let selected_segments = selected_segments(document, shape_editor);
+						let selected_points = shape_editor.selected_points();
+						let selected_anchors = selected_points
+							.filter_map(|point_id| if let ManipulatorPointId::Anchor(p) = point_id { Some(*p) } else { None })
+							.collect::<Vec<_>>();
 
 						// Match the behavior of `PathOverlayMode::SelectedPointHandles` when only one point is selected
 						if shape_editor.selected_points().count() == 1 {
@@ -802,6 +806,9 @@ impl Fsm for PathToolFsmState {
 								for (point, attached_segments) in selected_segments_by_point {
 									if attached_segments.len() == 1 {
 										segment_endpoints.entry(attached_segments[0]).or_default().push(point);
+									} else if !selected_anchors.contains(&point) {
+										segment_endpoints.entry(attached_segments[0]).or_default().push(point);
+										segment_endpoints.entry(attached_segments[1]).or_default().push(point);
 									}
 								}
 							}
@@ -1110,6 +1117,13 @@ impl Fsm for PathToolFsmState {
 				PathToolFsmState::Ready
 			}
 			(PathToolFsmState::Dragging { .. }, PathToolMessage::Escape | PathToolMessage::RightClick) => {
+				if tool_data.handle_drag_toggle && tool_data.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD {
+					shape_editor.deselect_all_points();
+					shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_handle_drag);
+
+					tool_data.saved_points_before_handle_drag.clear();
+					tool_data.handle_drag_toggle = false;
+				}
 				responses.add(DocumentMessage::AbortTransaction);
 				tool_data.snap_manager.cleanup(responses);
 				PathToolFsmState::Ready
diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs
index ef3684476d..6d772236b1 100644
--- a/editor/src/messages/tool/tool_messages/pen_tool.rs
+++ b/editor/src/messages/tool/tool_messages/pen_tool.rs
@@ -827,7 +827,14 @@ impl Fsm for PenToolFsmState {
 				self
 			}
 			(PenToolFsmState::Ready, PenToolMessage::Overlays(mut overlay_context)) => {
-				path_overlays(document, DrawHandles::All, shape_editor, &mut overlay_context);
+				match tool_options.pen_overlay_mode {
+					PenOverlayMode::AllHandles => {
+						path_overlays(document, DrawHandles::All, shape_editor, &mut overlay_context);
+					}
+					PenOverlayMode::FrontierHandles => {
+						path_overlays(document, DrawHandles::None, shape_editor, &mut overlay_context);
+					}
+				}
 				tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
 				self
 			}
@@ -896,7 +903,14 @@ impl Fsm for PenToolFsmState {
 					}
 				} else {
 					// Draw the whole path and its manipulators when the user is clicking-and-dragging out from the most recently placed anchor to set its outgoing handle, during which it would otherwise not have its overlays drawn
-					path_overlays(document, DrawHandles::All, shape_editor, &mut overlay_context);
+					match tool_options.pen_overlay_mode {
+						PenOverlayMode::AllHandles => {
+							path_overlays(document, DrawHandles::All, shape_editor, &mut overlay_context);
+						}
+						PenOverlayMode::FrontierHandles => {
+							path_overlays(document, DrawHandles::None, shape_editor, &mut overlay_context);
+						}
+					}
 				}
 
 				if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) && valid(next_anchor, next_handle_start) {


Test patch: