Disable the Path tool's "Colinear Handles" checkbox when no interior anchors are selected (#2339)

* disable "Colinear Handles" checkbox when only endpoint or its handle is selected

* small refactor

* ignore endpoints when calculating multiple selected points colinearity

---------

Co-authored-by: indierusty <priyaayadav@gmail.com>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Priyanshu 2025-03-08 11:20:52 +05:30 committed by GitHub
parent 381dcbf17f
commit 0a91dd2141
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 5 deletions

View File

@ -575,7 +575,7 @@ impl ShapeState {
Some(())
}
/// Iterates over the selected manipulator groups, returning whether their handles have mixed, colinear, or free angles.
/// Iterates over the selected manipulator groups exluding endpoints, returning whether their handles have mixed, colinear, or free angles.
/// If there are no points selected this function returns mixed.
pub fn selected_manipulator_angles(&self, network_interface: &NodeNetworkInterface) -> ManipulatorAngle {
// This iterator contains a bool indicating whether or not selected points' manipulator groups have colinear handles.
@ -583,7 +583,13 @@ impl ShapeState {
.selected_shape_state
.iter()
.map(|(&layer, selection_state)| (network_interface.compute_modified_vector(layer), selection_state))
.flat_map(|(data, selection_state)| selection_state.selected_points.iter().map(move |&point| data.as_ref().is_some_and(|data| data.colinear(point))));
.flat_map(|(data, selection_state)| {
selection_state.selected_points.iter().filter_map(move |&point| {
let Some(data) = &data else { return None };
let Some(_) = point.get_handle_pair(&data) else { return None }; // ignores the endpoints.
Some(data.colinear(point))
})
});
let Some(first_is_colinear) = points_colinear_status.next() else { return ManipulatorAngle::Mixed };
if points_colinear_status.any(|point| first_is_colinear != point) {

View File

@ -175,7 +175,7 @@ impl LayoutHolder for PathTool {
// TODO: Remove `unwrap_or_default` once checkboxes are capable of displaying a mixed state
.unwrap_or_default();
let colinear_handle_checkbox = CheckboxInput::new(colinear_handles_state)
.disabled(self.tool_data.selection_status.is_none())
.disabled(!self.tool_data.can_toggle_colinearity)
.on_update(|&CheckboxInput { checked, .. }| {
if checked {
PathToolMessage::ManipulatorMakeHandlesColinear.into()
@ -186,7 +186,7 @@ impl LayoutHolder for PathTool {
.tooltip(colinear_handles_tooltip)
.widget_holder();
let colinear_handles_label = TextLabel::new("Colinear Handles")
.disabled(self.tool_data.selection_status.is_none())
.disabled(!self.tool_data.can_toggle_colinearity)
.tooltip(colinear_handles_tooltip)
.widget_holder();
@ -359,7 +359,10 @@ struct PathToolData {
opposing_handle_lengths: Option<OpposingHandleLengths>,
/// Describes information about the selected point(s), if any, across one or multiple shapes and manipulator point types (anchor or handle).
/// The available information varies depending on whether `None`, `One`, or `Multiple` points are currently selected.
/// NOTE: It must be updated using `update_selection_status` to ensure `can_toggle_colinearity` stays synchronized with the current selection.
selection_status: SelectionStatus,
/// `true` if we can change the current selection to colinear or not.
can_toggle_colinearity: bool,
segment: Option<ClosestSegment>,
snap_cache: SnapCache,
double_click_handled: bool,
@ -415,6 +418,20 @@ impl PathToolData {
}
}
fn update_selection_status(&mut self, shape_editor: &mut ShapeState, document: &DocumentMessageHandler) {
let selection_status = get_selection_status(&document.network_interface, shape_editor);
self.can_toggle_colinearity = match &selection_status {
SelectionStatus::None => false,
SelectionStatus::One(single_selected_point) => {
let vector_data = document.network_interface.compute_modified_vector(single_selected_point.layer).unwrap();
single_selected_point.id.get_handle_pair(&vector_data).is_some()
}
SelectionStatus::Multiple(_) => true,
};
self.selection_status = selection_status;
}
fn start_insertion(&mut self, responses: &mut VecDeque<Message>, segment: ClosestSegment) -> PathToolFsmState {
if self.segment.is_some() {
warn!("Segment was `Some(..)` before `start_insertion`")
@ -1302,7 +1319,7 @@ impl Fsm for PathToolFsmState {
point_select_state: shape_editor.get_dragging_state(&document.network_interface),
colinear,
};
tool_data.selection_status = get_selection_status(&document.network_interface, shape_editor);
tool_data.update_selection_status(shape_editor, document);
self
}
(_, PathToolMessage::ManipulatorMakeHandlesColinear) => {