diff --git a/editor/src/consts.rs b/editor/src/consts.rs index b17a8621cc..52c77b0571 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -56,6 +56,8 @@ pub const DEFAULT_STROKE_WIDTH: f64 = 2.; pub const SELECTION_TOLERANCE: f64 = 5.; pub const DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD: f64 = 15.; pub const SELECTION_DRAG_ANGLE: f64 = 90.; +pub const BLUE_LAYER_ORIGIN_CROSS_DIAMETER: f64 = 10.; +pub const BLUE_LAYER_ORIGIN_CROSS_THICKNESS: f64 = 1.; // PIVOT pub const PIVOT_CROSSHAIR_THICKNESS: f64 = 1.; diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 9898a95b93..6ed15fe376 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1261,6 +1261,7 @@ impl MessageHandler> for DocumentMes OverlaysType::TransformCage => visibility_settings.transform_cage = visible, OverlaysType::HoverOutline => visibility_settings.hover_outline = visible, OverlaysType::SelectionOutline => visibility_settings.selection_outline = visible, + OverlaysType::BlueLayerOriginCross => visibility_settings.blue_layer_origin_cross = visible, OverlaysType::Pivot => visibility_settings.pivot = visible, OverlaysType::Origin => visibility_settings.origin = visible, OverlaysType::Path => visibility_settings.path = visible, @@ -2420,6 +2421,24 @@ impl DocumentMessageHandler { ] }, }, + LayoutGroup::Row { + widgets: { + let checkbox_id = CheckboxId::new(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.blue_layer_origin_cross) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::BlueLayerOriginCross), + } + .into() + }) + .for_label(checkbox_id) + .widget_instance(), + TextLabel::new("Blue Layer Origin Cross".to_string()).for_checkbox(checkbox_id).widget_instance(), + ] + }, + }, LayoutGroup::Row { widgets: vec![TextLabel::new("Pen & Path Tools").widget_instance()], }, diff --git a/editor/src/messages/portfolio/document/overlays/utility_types_native.rs b/editor/src/messages/portfolio/document/overlays/utility_types_native.rs index 3a0b40ce90..4cb16ad9f8 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types_native.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types_native.rs @@ -25,12 +25,15 @@ use std::sync::{Arc, Mutex, MutexGuard}; use vello::Scene; use vello::peniko; +// TODO Remove duplicated definition of this in `utility_types_web.rs` pub type OverlayProvider = fn(OverlayContext) -> Message; +// TODO Remove duplicated definition of this in `utility_types_web.rs` pub fn empty_provider() -> OverlayProvider { |_| Message::NoOp } +// TODO Remove duplicated definition of this in `utility_types_web.rs` /// Types of overlays used by DocumentMessage to enable/disable the selected set of viewport overlays. #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] pub enum OverlaysType { @@ -41,6 +44,7 @@ pub enum OverlaysType { TransformCage, HoverOutline, SelectionOutline, + BlueLayerOriginCross, Pivot, Origin, Path, @@ -48,6 +52,7 @@ pub enum OverlaysType { Handles, } +// TODO Remove duplicated definition of this in `utility_types_web.rs` #[derive(PartialEq, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] #[serde(default)] pub struct OverlaysVisibilitySettings { @@ -59,6 +64,7 @@ pub struct OverlaysVisibilitySettings { pub transform_cage: bool, pub hover_outline: bool, pub selection_outline: bool, + pub blue_layer_origin_cross: bool, pub pivot: bool, pub origin: bool, pub path: bool, @@ -66,6 +72,7 @@ pub struct OverlaysVisibilitySettings { pub handles: bool, } +// TODO Remove duplicated definition of this in `utility_types_web.rs` impl Default for OverlaysVisibilitySettings { fn default() -> Self { Self { @@ -77,6 +84,7 @@ impl Default for OverlaysVisibilitySettings { transform_cage: true, hover_outline: true, selection_outline: true, + blue_layer_origin_cross: true, pivot: true, origin: true, path: true, @@ -86,6 +94,7 @@ impl Default for OverlaysVisibilitySettings { } } +// TODO Remove duplicated definition of this in `utility_types_web.rs` impl OverlaysVisibilitySettings { pub fn all(&self) -> bool { self.all @@ -119,6 +128,10 @@ impl OverlaysVisibilitySettings { self.all && self.selection_outline } + pub fn blue_layer_origin_cross(&self) -> bool { + self.all && self.blue_layer_origin_cross + } + pub fn pivot(&self) -> bool { self.all && self.pivot } @@ -391,12 +404,14 @@ impl OverlayContext { } } +// TODO Remove duplicated definition of this in `utility_types_web.rs` pub enum Pivot { Start, Middle, End, } +// TODO Remove duplicated definition of this in `utility_types_web.rs` pub enum DrawHandles { All, SelectedAnchors(HashMap>), diff --git a/editor/src/messages/portfolio/document/overlays/utility_types_web.rs b/editor/src/messages/portfolio/document/overlays/utility_types_web.rs index 0800c65cfe..80f10349af 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types_web.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types_web.rs @@ -37,6 +37,7 @@ pub enum OverlaysType { TransformCage, HoverOutline, SelectionOutline, + BlueLayerOriginCross, Pivot, Origin, Path, @@ -55,6 +56,7 @@ pub struct OverlaysVisibilitySettings { pub transform_cage: bool, pub hover_outline: bool, pub selection_outline: bool, + pub blue_layer_origin_cross: bool, pub pivot: bool, pub origin: bool, pub path: bool, @@ -73,6 +75,7 @@ impl Default for OverlaysVisibilitySettings { transform_cage: true, hover_outline: true, selection_outline: true, + blue_layer_origin_cross: true, pivot: true, origin: true, path: true, @@ -115,6 +118,10 @@ impl OverlaysVisibilitySettings { self.all && self.selection_outline } + pub fn blue_layer_origin_cross(&self) -> bool { + self.all && self.blue_layer_origin_cross + } + pub fn pivot(&self) -> bool { self.all && self.pivot } diff --git a/editor/src/messages/tool/common_functionality/blue_layer_origin_cross.rs b/editor/src/messages/tool/common_functionality/blue_layer_origin_cross.rs new file mode 100644 index 0000000000..7f015156b1 --- /dev/null +++ b/editor/src/messages/tool/common_functionality/blue_layer_origin_cross.rs @@ -0,0 +1,47 @@ +//! Draws a blue cross overlay at the origin point of the layers in layer space. +//! +//! This cross is orientated based on the +X vector of the layer. + +use crate::consts::{BLUE_LAYER_ORIGIN_CROSS_DIAMETER, BLUE_LAYER_ORIGIN_CROSS_THICKNESS, COLOR_OVERLAY_BLUE}; +use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; +use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; +use crate::messages::tool::tool_messages::tool_prelude::DocumentMessageHandler; +use glam::DVec2; + +pub fn draw_for_selected_layers(overlay_context: &mut OverlayContext, document: &DocumentMessageHandler) { + // Can be disabled + if !overlay_context.visibility_settings.blue_layer_origin_cross() { + return; + } + // Only show visible and unlocked and selected and layers + for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) { + // Don't show artboards + if document.network_interface.is_artboard(&layer.to_node(), &[]) { + continue; + } + + // Don't crash if we accidentally have the root. + if layer == LayerNodeIdentifier::ROOT_PARENT { + continue; + } + + // Some layers such as groups don't have a local transform. + if !document.metadata().local_transforms.contains_key(&layer.to_node()) { + continue; + } + + // A transformation from the layer's local space to the viewport space (where overlays are drawn) + let transform_to_viewport = document.metadata().transform_to_viewport(layer); + + // The origin of the layer in viewport space which is the centre of the blue cross/ + let origin_viewport = transform_to_viewport.transform_point2(DVec2::ZERO); + // The forward +X direction vector from layer space (used to orientate the blue orogin cross) + let forward = transform_to_viewport.transform_vector2(DVec2::X).normalize_or_zero(); + + // Draw the cross + let offsets = [forward + forward.perp(), forward - forward.perp()].map(|offset| offset * core::f64::consts::FRAC_1_SQRT_2 * BLUE_LAYER_ORIGIN_CROSS_DIAMETER / 2.); + for offset in offsets { + overlay_context.line(origin_viewport - offset, origin_viewport + offset, Some(COLOR_OVERLAY_BLUE), Some(BLUE_LAYER_ORIGIN_CROSS_THICKNESS)); + } + } +} diff --git a/editor/src/messages/tool/common_functionality/mod.rs b/editor/src/messages/tool/common_functionality/mod.rs index ca3e629e19..4da1fe19be 100644 --- a/editor/src/messages/tool/common_functionality/mod.rs +++ b/editor/src/messages/tool/common_functionality/mod.rs @@ -1,4 +1,5 @@ pub mod auto_panning; +pub mod blue_layer_origin_cross; pub mod color_selector; pub mod compass_rose; pub mod gizmos; diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 3a19f4a399..4159ada5e8 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -608,6 +608,8 @@ impl Fsm for SelectToolFsmState { (_, SelectToolMessage::Overlays { context: mut overlay_context }) => { tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context); + crate::messages::tool::common_functionality::blue_layer_origin_cross::draw_for_selected_layers(&mut overlay_context, &document); + let selected_layers_count = document.network_interface.selected_nodes().selected_unlocked_layers(&document.network_interface).count(); tool_data.selected_layers_changed = selected_layers_count != tool_data.selected_layers_count; tool_data.selected_layers_count = selected_layers_count; @@ -729,6 +731,7 @@ impl Fsm for SelectToolFsmState { if let Some(bounds) = bounds { let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default()); + // TODO: It is very bad to bounding box calculations only here as the user can disable overlays which breaks the bounding box based resizing. bounding_box_manager.bounds = bounds; bounding_box_manager.transform = transform; bounding_box_manager.transform_tampered = transform_tampered; diff --git a/frontend/index.html b/frontend/index.html index ccaf55e420..852f040ab2 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -44,6 +44,12 @@ border: 4px solid #eee; border-color: #eee transparent #eee transparent; animation: spinning-loading-indicator 1s linear infinite; + + @media (prefers-reduced-motion) { + animation: none; + content: "Loading…"; + border: none; + } } @keyframes spinning-loading-indicator {