mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 23:38:12 +08:00
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:
co-authored by
Keavon Chambers
parent
1f7a9188ba
commit
1a81e45673
@@ -1,8 +1,8 @@
|
||||
use super::utility_types::OverlayProvider;
|
||||
use super::utility_types::{OverlayProvider, OverlaysVisibilitySettings};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
pub struct OverlaysMessageData<'a> {
|
||||
pub overlays_visible: bool,
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub device_pixel_ratio: f64,
|
||||
}
|
||||
@@ -18,7 +18,7 @@ pub struct OverlaysMessageHandler {
|
||||
|
||||
impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessageHandler {
|
||||
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, data: OverlaysMessageData) {
|
||||
let OverlaysMessageData { overlays_visible, ipp, .. } = data;
|
||||
let OverlaysMessageData { visibility_settings, ipp, .. } = data;
|
||||
|
||||
match message {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -50,24 +50,26 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessag
|
||||
context.clear_rect(0., 0., canvas.width().into(), canvas.height().into());
|
||||
let _ = context.reset_transform();
|
||||
|
||||
if overlays_visible {
|
||||
if visibility_settings.all() {
|
||||
responses.add(DocumentMessage::GridOverlays(OverlayContext {
|
||||
render_context: context.clone(),
|
||||
size: size.as_dvec2(),
|
||||
device_pixel_ratio,
|
||||
visibility_settings: visibility_settings.clone(),
|
||||
}));
|
||||
for provider in &self.overlay_providers {
|
||||
responses.add(provider(OverlayContext {
|
||||
render_context: context.clone(),
|
||||
size: size.as_dvec2(),
|
||||
device_pixel_ratio,
|
||||
visibility_settings: visibility_settings.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
OverlaysMessage::Draw => {
|
||||
warn!("Cannot render overlays on non-Wasm targets.\n{responses:?} {overlays_visible} {ipp:?}",);
|
||||
warn!("Cannot render overlays on non-Wasm targets.\n{responses:?} {visibility_settings:?} {ipp:?}",);
|
||||
}
|
||||
OverlaysMessage::AddProvider(message) => {
|
||||
self.overlay_providers.insert(message);
|
||||
|
||||
@@ -77,7 +77,7 @@ fn overlay_bezier_handles(bezier: Bezier, segment_id: SegmentId, transform: DAff
|
||||
}
|
||||
}
|
||||
|
||||
pub fn overlay_bezier_handle_specific_point(
|
||||
fn overlay_bezier_handle_specific_point(
|
||||
bezier: Bezier,
|
||||
segment_id: SegmentId,
|
||||
(start, end): (PointId, PointId),
|
||||
@@ -112,59 +112,73 @@ pub fn overlay_bezier_handle_specific_point(
|
||||
}
|
||||
|
||||
pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandles, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext) {
|
||||
let display_path = overlay_context.visibility_settings.path();
|
||||
let display_handles = overlay_context.visibility_settings.handles();
|
||||
let display_anchors = overlay_context.visibility_settings.anchors();
|
||||
|
||||
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
|
||||
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
|
||||
let transform = document.metadata().transform_to_viewport(layer);
|
||||
overlay_context.outline_vector(&vector_data, transform);
|
||||
if display_path {
|
||||
overlay_context.outline_vector(&vector_data, transform);
|
||||
}
|
||||
|
||||
let selected = shape_editor.selected_shape_state.get(&layer);
|
||||
let is_selected = |point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_selected(point));
|
||||
|
||||
let opposite_handles_data: Vec<(PointId, SegmentId)> = shape_editor.selected_points().filter_map(|point_id| vector_data.adjacent_segment(point_id)).collect();
|
||||
if display_handles {
|
||||
let opposite_handles_data: Vec<(PointId, SegmentId)> = shape_editor.selected_points().filter_map(|point_id| vector_data.adjacent_segment(point_id)).collect();
|
||||
|
||||
match draw_handles {
|
||||
DrawHandles::All => {
|
||||
vector_data.segment_bezier_iter().for_each(|(segment_id, bezier, _start, _end)| {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
});
|
||||
}
|
||||
DrawHandles::SelectedAnchors(ref selected_segments) => {
|
||||
vector_data
|
||||
.segment_bezier_iter()
|
||||
.filter(|(segment_id, ..)| selected_segments.contains(segment_id))
|
||||
.for_each(|(segment_id, bezier, _start, _end)| {
|
||||
match draw_handles {
|
||||
DrawHandles::All => {
|
||||
vector_data.segment_bezier_iter().for_each(|(segment_id, bezier, _start, _end)| {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
});
|
||||
}
|
||||
DrawHandles::SelectedAnchors(ref selected_segments) => {
|
||||
vector_data
|
||||
.segment_bezier_iter()
|
||||
.filter(|(segment_id, ..)| selected_segments.contains(segment_id))
|
||||
.for_each(|(segment_id, bezier, _start, _end)| {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
});
|
||||
|
||||
for (segment_id, bezier, start, end) in vector_data.segment_bezier_iter() {
|
||||
if let Some((corresponding_anchor, _)) = opposite_handles_data.iter().find(|(_, adj_segment_id)| adj_segment_id == &segment_id) {
|
||||
overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), *corresponding_anchor, transform, is_selected, overlay_context);
|
||||
for (segment_id, bezier, start, end) in vector_data.segment_bezier_iter() {
|
||||
if let Some((corresponding_anchor, _)) = opposite_handles_data.iter().find(|(_, adj_segment_id)| adj_segment_id == &segment_id) {
|
||||
overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), *corresponding_anchor, transform, is_selected, overlay_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
DrawHandles::FrontierHandles(ref segment_endpoints) => {
|
||||
vector_data
|
||||
.segment_bezier_iter()
|
||||
.filter(|(segment_id, ..)| segment_endpoints.contains_key(segment_id))
|
||||
.for_each(|(segment_id, bezier, start, end)| {
|
||||
if segment_endpoints.get(&segment_id).unwrap().len() == 1 {
|
||||
let point_to_render = segment_endpoints.get(&segment_id).unwrap()[0];
|
||||
overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), point_to_render, transform, is_selected, overlay_context);
|
||||
} else {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
}
|
||||
});
|
||||
}
|
||||
DrawHandles::None => {}
|
||||
}
|
||||
DrawHandles::FrontierHandles(ref segment_endpoints) => {
|
||||
vector_data
|
||||
.segment_bezier_iter()
|
||||
.filter(|(segment_id, ..)| segment_endpoints.contains_key(segment_id))
|
||||
.for_each(|(segment_id, bezier, start, end)| {
|
||||
if segment_endpoints.get(&segment_id).unwrap().len() == 1 {
|
||||
let point_to_render = segment_endpoints.get(&segment_id).unwrap()[0];
|
||||
overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), point_to_render, transform, is_selected, overlay_context);
|
||||
} else {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
}
|
||||
});
|
||||
}
|
||||
DrawHandles::None => {}
|
||||
}
|
||||
|
||||
for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
|
||||
overlay_context.manipulator_anchor(transform.transform_point2(position), is_selected(ManipulatorPointId::Anchor(id)), None);
|
||||
if display_anchors {
|
||||
for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
|
||||
overlay_context.manipulator_anchor(transform.transform_point2(position), is_selected(ManipulatorPointId::Anchor(id)), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext, preferences: &PreferencesMessageHandler) {
|
||||
if !overlay_context.visibility_settings.anchors() {
|
||||
return;
|
||||
}
|
||||
|
||||
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
|
||||
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
|
||||
continue;
|
||||
|
||||
@@ -21,6 +21,107 @@ pub fn empty_provider() -> OverlayProvider {
|
||||
|_| Message::NoOp
|
||||
}
|
||||
|
||||
// Types of overlays used by DocumentMessage to enable/disable select group of overlays in the frontend
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub enum OverlaysType {
|
||||
ArtboardName,
|
||||
CompassRose,
|
||||
QuickMeasurement,
|
||||
TransformMeasurement,
|
||||
TransformCage,
|
||||
HoverOutline,
|
||||
SelectionOutline,
|
||||
Pivot,
|
||||
Path,
|
||||
Anchors,
|
||||
Handles,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub struct OverlaysVisibilitySettings {
|
||||
pub all: bool,
|
||||
pub artboard_name: bool,
|
||||
pub compass_rose: bool,
|
||||
pub quick_measurement: bool,
|
||||
pub transform_measurement: bool,
|
||||
pub transform_cage: bool,
|
||||
pub hover_outline: bool,
|
||||
pub selection_outline: bool,
|
||||
pub pivot: bool,
|
||||
pub path: bool,
|
||||
pub anchors: bool,
|
||||
pub handles: bool,
|
||||
}
|
||||
|
||||
impl Default for OverlaysVisibilitySettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
all: true,
|
||||
artboard_name: true,
|
||||
compass_rose: true,
|
||||
quick_measurement: true,
|
||||
transform_measurement: true,
|
||||
transform_cage: true,
|
||||
hover_outline: true,
|
||||
selection_outline: true,
|
||||
pivot: true,
|
||||
path: true,
|
||||
anchors: true,
|
||||
handles: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OverlaysVisibilitySettings {
|
||||
pub fn all(&self) -> bool {
|
||||
self.all
|
||||
}
|
||||
|
||||
pub fn artboard_name(&self) -> bool {
|
||||
self.all && self.artboard_name
|
||||
}
|
||||
|
||||
pub fn compass_rose(&self) -> bool {
|
||||
self.all && self.compass_rose
|
||||
}
|
||||
|
||||
pub fn quick_measurement(&self) -> bool {
|
||||
self.all && self.quick_measurement
|
||||
}
|
||||
|
||||
pub fn transform_measurement(&self) -> bool {
|
||||
self.all && self.transform_measurement
|
||||
}
|
||||
|
||||
pub fn transform_cage(&self) -> bool {
|
||||
self.all && self.transform_cage
|
||||
}
|
||||
|
||||
pub fn hover_outline(&self) -> bool {
|
||||
self.all && self.hover_outline
|
||||
}
|
||||
|
||||
pub fn selection_outline(&self) -> bool {
|
||||
self.all && self.selection_outline
|
||||
}
|
||||
|
||||
pub fn pivot(&self) -> bool {
|
||||
self.all && self.pivot
|
||||
}
|
||||
|
||||
pub fn path(&self) -> bool {
|
||||
self.all && self.path
|
||||
}
|
||||
|
||||
pub fn anchors(&self) -> bool {
|
||||
self.all && self.anchors
|
||||
}
|
||||
|
||||
pub fn handles(&self) -> bool {
|
||||
self.all && self.anchors && self.handles
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub struct OverlayContext {
|
||||
// Serde functionality isn't used but is required by the message system macros
|
||||
@@ -31,6 +132,7 @@ pub struct OverlayContext {
|
||||
// The device pixel ratio is a property provided by the browser window and is the CSS pixel size divided by the physical monitor's pixel size.
|
||||
// It allows better pixel density of visualizations on high-DPI displays where the OS display scaling is not 100%, or where the browser is zoomed.
|
||||
pub device_pixel_ratio: f64,
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
}
|
||||
// Message hashing isn't used but is required by the message system macros
|
||||
impl core::hash::Hash for OverlayContext {
|
||||
|
||||
Reference in New Issue
Block a user