Add the settings popover menu for the Overlays toggle (#2523)

* Added granular overlays control based on features

* Added basic support for pivot, path, anchors and handles overlay settings

* Added more overlay checks on anchors and handles

* Add new settings over measurements, hover and selection overlays

* Fix errors introduced while rebasing

* Disable anchors and handles functionality with their overlays, extended selection outline check

* Add check to enable/disable outlines on selected layers

* Toggle handles checkbox in sync with anchors checkbox

* Refactor overlays checks

* Remove debug statements

* Update select_tool.rs to resolve conflict

* Minor fix to reflect anchor checkbox state on the handles

* Minor fix to make anchors checkbox work

* Rearrange menu items, and code review

* Fix pivot dragging

* Add handles overlay check when drawing with pen tool

* Fix constrained dragging when transform cage is disabled

* Fix deselecting user selection when anchors are disabled

* Minor fix for disabling anchors

* Remove All from OverlaysType

* Remove debug statements

* Fix editor crash when selecting other layers with path tool and anchors disabled

* Minor fix on overlays check for all overlays

* Add proper code formatting

* Nits

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
seam0s-dev
2025-04-30 04:15:46 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent 1f7a9188ba
commit 1a81e45673
15 changed files with 727 additions and 265 deletions
@@ -19,6 +19,8 @@ pub struct Pivot {
pivot: Option<DVec2>,
/// The old pivot position in the GUI, used to reduce refreshes of the document bar
old_pivot_position: ReferencePoint,
/// Used to enable and disable the pivot
active: bool,
}
impl Default for Pivot {
@@ -28,6 +30,7 @@ impl Default for Pivot {
transform_from_normalized: Default::default(),
pivot: Default::default(),
old_pivot_position: ReferencePoint::Center,
active: true,
}
}
}
@@ -44,6 +47,10 @@ impl Pivot {
/// Recomputes the pivot position and transform.
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) {
if !self.active {
return;
}
let selected_nodes = document.network_interface.selected_nodes();
let mut layers = selected_nodes.selected_visible_and_unlocked_layers(&document.network_interface);
let Some(first) = layers.next() else {
@@ -82,6 +89,13 @@ impl Pivot {
}
pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, draw_data: Option<(f64,)>) {
if !overlay_context.visibility_settings.pivot() {
self.active = false;
return;
} else {
self.active = true;
}
self.recalculate_pivot(document);
if let (Some(pivot), Some(data)) = (self.pivot, draw_data) {
overlay_context.pivot(pivot, data.0);
@@ -90,6 +104,10 @@ impl Pivot {
/// Answers if the pivot widget has changed (so we should refresh the tool bar at the top of the canvas).
pub fn should_refresh_pivot_position(&mut self) -> bool {
if !self.active {
return false;
}
let new = self.to_pivot_position();
let should_refresh = new != self.old_pivot_position;
self.old_pivot_position = new;
@@ -102,6 +120,10 @@ impl Pivot {
/// Sets the viewport position of the pivot for all selected layers.
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
if !self.active {
return;
}
for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) {
let transform = Self::get_layer_pivot_transform(layer, document);
// Only update the pivot when computed position is finite.
@@ -115,11 +137,18 @@ impl Pivot {
/// Set the pivot using the normalized transform that is set above.
pub fn set_normalized_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
if !self.active {
return;
}
self.set_viewport_position(self.transform_from_normalized.transform_point2(position), document, responses);
}
/// Answers if the pointer is currently positioned over the pivot.
pub fn is_over(&self, mouse: DVec2) -> bool {
if !self.active {
return false;
}
self.pivot.filter(|&pivot| mouse.distance_squared(pivot) < (PIVOT_DIAMETER / 2.).powi(2)).is_some()
}
}
@@ -42,6 +42,8 @@ pub enum ManipulatorAngle {
#[derive(Clone, Debug, Default)]
pub struct SelectedLayerState {
selected_points: HashSet<ManipulatorPointId>,
ignore_handles: bool,
ignore_anchors: bool,
}
impl SelectedLayerState {
@@ -52,12 +54,32 @@ impl SelectedLayerState {
self.selected_points.contains(&point)
}
pub fn select_point(&mut self, point: ManipulatorPointId) {
if (point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors) {
return;
}
self.selected_points.insert(point);
}
pub fn deselect_point(&mut self, point: ManipulatorPointId) {
if (point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors) {
return;
}
self.selected_points.remove(&point);
}
pub fn set_handles_status(&mut self, ignore: bool) {
self.ignore_handles = ignore;
}
pub fn set_anchors_status(&mut self, ignore: bool) {
self.ignore_anchors = ignore;
}
pub fn clear_points_force(&mut self) {
self.selected_points.clear();
self.ignore_handles = false;
self.ignore_anchors = false;
}
pub fn clear_points(&mut self) {
if self.ignore_handles || self.ignore_anchors {
return;
}
self.selected_points.clear();
}
pub fn selected_points_count(&self) -> usize {
@@ -524,6 +546,52 @@ impl ShapeState {
}
}
pub fn mark_selected_anchors(&mut self) {
for state in self.selected_shape_state.values_mut() {
state.set_anchors_status(false);
}
}
pub fn mark_selected_handles(&mut self) {
for state in self.selected_shape_state.values_mut() {
state.set_handles_status(false);
}
}
pub fn ignore_selected_anchors(&mut self) {
for state in self.selected_shape_state.values_mut() {
state.set_anchors_status(true);
}
}
pub fn ignore_selected_handles(&mut self) {
for state in self.selected_shape_state.values_mut() {
state.set_handles_status(true);
}
}
/// Deselects all the anchors across every selected layer.
pub fn deselect_all_anchors(&mut self) {
for (_, state) in self.selected_shape_state.iter_mut() {
let selected_anchor_points: Vec<ManipulatorPointId> = state.selected_points.iter().filter(|selected_point| selected_point.as_anchor().is_some()).cloned().collect();
for point in selected_anchor_points {
state.deselect_point(point);
}
}
}
/// Deselects all the handles across every selected layer.
pub fn deselect_all_handles(&mut self) {
for (_, state) in self.selected_shape_state.iter_mut() {
let selected_handle_points: Vec<ManipulatorPointId> = state.selected_points.iter().filter(|selected_point| selected_point.as_handle().is_some()).cloned().collect();
for point in selected_handle_points {
state.deselect_point(point);
}
}
}
/// Set the shapes we consider for selection, we will choose draggable manipulators from these shapes.
pub fn set_selected_layers(&mut self, target_layers: Vec<LayerNodeIdentifier>) {
self.selected_shape_state.retain(|layer_path, _| target_layers.contains(layer_path));
@@ -632,7 +700,7 @@ impl ShapeState {
Some(())
}
/// Iterates over the selected manipulator groups exluding endpoints, returning whether their handles have mixed, colinear, or free angles.
/// Iterates over the selected manipulator groups excluding 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.
@@ -1495,7 +1563,7 @@ impl ShapeState {
pub fn select_all_in_shape(&mut self, network_interface: &NodeNetworkInterface, selection_shape: SelectionShape, selection_change: SelectionChange) {
for (&layer, state) in &mut self.selected_shape_state {
if selection_change == SelectionChange::Clear {
state.clear_points()
state.clear_points_force()
}
let vector_data = network_interface.compute_modified_vector(layer);