diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 3dcbd727..89385ad2 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -75,8 +75,10 @@ pub const SCALE_EFFECT: f64 = 0.5; // Keep changes to these colors updated with `Editor.svelte` pub const COLOR_OVERLAY_BLUE: &str = "#00a8ff"; pub const COLOR_OVERLAY_YELLOW: &str = "#ffc848"; -pub const COLOR_OVERLAY_WHITE: &str = "#ffffff"; +pub const COLOR_OVERLAY_GREEN: &str = "#63ce63"; +pub const COLOR_OVERLAY_RED: &str = "#ef5454"; pub const COLOR_OVERLAY_GRAY: &str = "#cccccc"; +pub const COLOR_OVERLAY_WHITE: &str = "#ffffff"; // Document pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document"; diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index c393ef69..cd4cccd3 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -50,6 +50,7 @@ pub enum DocumentMessage { DocumentHistoryBackward, DocumentHistoryForward, DocumentStructureChanged, + DrawArtboardOverlays(OverlayContext), DuplicateSelectedLayers, EnterNestedNetwork { node_id: NodeId, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 75df1dd5..9a817036 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1,11 +1,12 @@ use super::node_graph::utility_types::Transform; +use super::overlays::utility_types::Pivot; use super::utility_types::clipboards::Clipboard; use super::utility_types::error::EditorError; use super::utility_types::misc::{SnappingOptions, SnappingState, GET_SNAP_BOX_FUNCTIONS, GET_SNAP_GEOMETRY_FUNCTIONS}; use super::utility_types::network_interface::{NodeNetworkInterface, TransactionStatus}; use super::utility_types::nodes::{CollapsedLayers, SelectedNodes}; use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH}; -use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL}; +use crate::consts::{ASYMPTOTIC_EFFECT, COLOR_OVERLAY_GRAY, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL}; use crate::messages::input_mapper::utility_types::macros::action_keys; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; @@ -371,6 +372,18 @@ impl MessageHandler> for DocumentMessag let data_buffer: RawBuffer = self.serialize_root(); responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer }); } + DocumentMessage::DrawArtboardOverlays(overlay_context) => { + for layer in self.metadata().all_layers() { + if !self.network_interface.is_artboard(&layer.to_node(), &[]) { + continue; + } + let Some(bounds) = self.metadata().bounding_box_document(layer) else { continue }; + + let name = self.network_interface.frontend_display_name(&layer.to_node(), &[]); + let transform = self.metadata().document_to_viewport * DAffine2::from_translation(bounds[0].min(bounds[1]) - DVec2::Y * 4.); + overlay_context.text_with_transform(&name, COLOR_OVERLAY_GRAY, None, transform, Pivot::BottomLeft); + } + } DocumentMessage::DuplicateSelectedLayers => { let parent = self.new_layer_parent(false); let calculated_insert_index = diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index 34064577..7e9c4faf 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -247,47 +247,52 @@ impl OverlayContext { self.render_context.stroke(); } - pub fn text(&self, text: &str, pos: DVec2, background: &str, padding: f64) { - let pos = pos.round(); + pub fn text(&self, text: &str, font_color: &str, background_color: Option<&str>, pos: DVec2) { let metrics = self.render_context.measure_text(text).expect("Failed to measure the text dimensions"); - self.render_context.set_fill_style(&background.into()); - self.render_context.fill_rect( - pos.x + metrics.actual_bounding_box_left(), - pos.y - metrics.font_bounding_box_ascent() - metrics.font_bounding_box_descent() - padding * 2., - metrics.actual_bounding_box_right() - metrics.actual_bounding_box_left() + padding * 2., - metrics.font_bounding_box_ascent() + metrics.font_bounding_box_descent() + padding * 2., - ); - self.render_context.set_fill_style(&"white".into()); - self.render_context - .fill_text(text, pos.x + padding, pos.y - padding - metrics.font_bounding_box_descent()) - .expect("Failed to draw the text on the canvas"); + let local_position = pos.round(); + + self.draw_text(background_color, local_position, metrics, font_color, text); } - pub fn angle_text(&self, text: &str, pos: DVec2, direction: DVec2, padding: f64, pivot: Pivot) { - self.render_context.translate(pos.x, pos.y).expect("Failed to translate the render context to the specified position"); - - let angle = -direction.angle_to(DVec2::X); - self.render_context.rotate(angle).expect("Failed to rotate the render context to the specified angle"); + pub fn text_with_transform(&self, text: &str, font_color: &str, background_color: Option<&str>, transform: DAffine2, origin: Pivot) { + let [a, b, c, d, e, f] = transform.to_cols_array(); + self.render_context.set_transform(a, b, c, d, e, f).expect("Failed to rotate the render context to the specified angle"); let metrics = self.render_context.measure_text(text).expect("Failed to measure the text dimensions"); - - self.render_context.set_fill_style(&wasm_bindgen::JsValue::from_str(COLOR_OVERLAY_BLUE)); - - let local_position = match pivot { - Pivot::LeftCentreY => DVec2::new(padding, (metrics.actual_bounding_box_ascent() + metrics.actual_bounding_box_descent()) / 2.), - Pivot::TopCentreX => DVec2::new( - -(metrics.actual_bounding_box_right() + metrics.actual_bounding_box_left()) / 2., - padding + metrics.font_bounding_box_ascent(), - ), + let local_position = match origin { + Pivot::CenterLeft => DVec2::new(0., (metrics.actual_bounding_box_ascent() + metrics.actual_bounding_box_descent()) / 2.), + Pivot::TopCenter => DVec2::new(-(metrics.actual_bounding_box_right() + metrics.actual_bounding_box_left()) / 2., metrics.font_bounding_box_ascent()), + Pivot::BottomLeft => DVec2::ZERO, }; - self.render_context - .fill_text(text, local_position.x, local_position.y) - .expect("Failed to draw the text at the calculated position"); + + self.draw_text(background_color, local_position, metrics, font_color, text); + self.render_context.reset_transform().expect("Failed to reset the render context transform"); } + + fn draw_text(&self, background_color: Option<&str>, local_position: DVec2, metrics: web_sys::TextMetrics, font_color: &str, text: &str) { + // Render background + if let Some(background) = background_color { + self.render_context.set_fill_style(&background.into()); + self.render_context.fill_rect( + local_position.x + metrics.actual_bounding_box_left(), + local_position.y - metrics.font_bounding_box_ascent() - metrics.font_bounding_box_descent(), + metrics.actual_bounding_box_right() - metrics.actual_bounding_box_left(), + metrics.font_bounding_box_ascent() + metrics.font_bounding_box_descent(), + ); + } + + // Render text + self.render_context.set_font("12px Source Sans Pro, Arial, sans-serif"); + self.render_context.set_fill_style(&font_color.into()); + self.render_context + .fill_text(text, local_position.x, local_position.y - metrics.font_bounding_box_descent()) + .expect("Failed to draw the text on the canvas"); + } } pub enum Pivot { - LeftCentreY, - TopCentreX, + TopCenter, + CenterLeft, + BottomLeft, } diff --git a/editor/src/messages/tool/common_functionality/measure.rs b/editor/src/messages/tool/common_functionality/measure.rs index 24074c79..256121d0 100644 --- a/editor/src/messages/tool/common_functionality/measure.rs +++ b/editor/src/messages/tool/common_functionality/measure.rs @@ -1,3 +1,4 @@ +use crate::consts::COLOR_OVERLAY_BLUE; use crate::messages::portfolio::document::overlays::utility_types::{self, OverlayContext}; use crate::messages::tool::tool_messages::tool_prelude::*; @@ -6,7 +7,7 @@ use graphene_std::renderer::Rect; pub fn overlay(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext) { let transform_to_document = document_to_viewport.inverse() * transform; if selected_bounds.intersects(hovered_bounds) { - // TODO: I'm not sure what to do here? + // TODO: Figure out what to do here return; } @@ -23,7 +24,8 @@ pub fn overlay(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, overlay_context.line(min_viewport, max_viewport, None); let length = format!("{:.2}", transform_to_document.transform_vector2(DVec2::X * (turn_x - selected_x)).length()); let direction = -(min_viewport - max_viewport).normalize_or_zero(); - overlay_context.angle_text(&length, (min_viewport + max_viewport) / 2., direction, 5., utility_types::Pivot::TopCentreX); + let transform = DAffine2::from_translation((min_viewport + max_viewport) / 2.) * DAffine2::from_angle(-direction.angle_to(DVec2::X)); + overlay_context.text_with_transform(&length, COLOR_OVERLAY_BLUE, None, transform, utility_types::Pivot::TopCenter); } if turn_y != hovered_y { let min_viewport = transform.transform_point2(DVec2::new(turn_x, turn_y.min(hovered_y))); @@ -31,6 +33,7 @@ pub fn overlay(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2, overlay_context.line(min_viewport, max_viewport, None); let length = format!("{:.2}", transform_to_document.transform_vector2(DVec2::Y * (turn_y - hovered_y)).length()); let direction = (min_viewport - max_viewport).normalize_or_zero().perp(); - overlay_context.angle_text(&length, (min_viewport + max_viewport) / 2., direction, 5., utility_types::Pivot::LeftCentreY); + let transform = DAffine2::from_translation((min_viewport + max_viewport) / 2.) * DAffine2::from_angle(-direction.angle_to(DVec2::X)); + overlay_context.text_with_transform(&length, COLOR_OVERLAY_BLUE, None, transform, utility_types::Pivot::CenterLeft); } } diff --git a/editor/src/messages/tool/common_functionality/snapping.rs b/editor/src/messages/tool/common_functionality/snapping.rs index c20974dd..ed0c98b6 100644 --- a/editor/src/messages/tool/common_functionality/snapping.rs +++ b/editor/src/messages/tool/common_functionality/snapping.rs @@ -5,7 +5,7 @@ mod layer_snapper; mod snap_results; pub use {alignment_snapper::*, distribution_snapper::*, grid_snapper::*, layer_snapper::*, snap_results::*}; -use crate::consts::COLOR_OVERLAY_BLUE; +use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_WHITE}; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::misc::{BoundingBoxSnapTarget, GeometrySnapTarget, GridSnapTarget, SnapTarget}; @@ -466,7 +466,8 @@ impl SnapManager { } if !any_align && ind.distribution_equal_distance_x.is_none() && ind.distribution_equal_distance_y.is_none() { - overlay_context.text(&format!("{:?} to {:?}", ind.source, ind.target), viewport - DVec2::new(0., 5.), "rgba(0, 0, 0, 0.8)", 3.); + let text = format!("{:?} to {:?}", ind.source, ind.target); + overlay_context.text(&text, COLOR_OVERLAY_BLUE, Some(COLOR_OVERLAY_WHITE), viewport - DVec2::new(0., 5.)); overlay_context.square(viewport, Some(4.), Some(COLOR_OVERLAY_BLUE), Some(COLOR_OVERLAY_BLUE)); } } diff --git a/editor/src/messages/tool/tool_message_handler.rs b/editor/src/messages/tool/tool_message_handler.rs index c04ad50f..696c6ecb 100644 --- a/editor/src/messages/tool/tool_message_handler.rs +++ b/editor/src/messages/tool/tool_message_handler.rs @@ -2,6 +2,7 @@ use super::common_functionality::shape_editor::ShapeState; use super::utility_types::{tool_message_to_tool_type, ToolActionHandlerData, ToolFsmState}; use crate::application::generate_uuid; use crate::messages::layout::utility_types::widget_prelude::*; +use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider; use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::prelude::*; use crate::messages::tool::utility_types::ToolType; @@ -9,6 +10,8 @@ use crate::node_graph_executor::NodeGraphExecutor; use graphene_core::raster::color::Color; +const ARTBOARD_OVERLAY_PROVIDER: OverlayProvider = |context| DocumentMessage::DrawArtboardOverlays(context).into(); + pub struct ToolMessageData<'a> { pub document_id: DocumentId, pub document: &'a mut DocumentMessageHandler, @@ -128,6 +131,8 @@ impl MessageHandler> for ToolMessageHandler { let message = Box::new(TransformLayerMessage::SelectionChanged.into()); let on = BroadcastEvent::SelectionChanged; responses.add(BroadcastMessage::UnsubscribeEvent { message, on }); + + responses.add(OverlaysMessage::RemoveProvider(ARTBOARD_OVERLAY_PROVIDER)); } ToolMessage::InitTools => { // Subscribe the transform layer to selection change events @@ -165,6 +170,8 @@ impl MessageHandler> for ToolMessageHandler { // Set initial hints and cursor tool_data.active_tool_mut().process_message(ToolMessage::UpdateHints, responses, &mut data); tool_data.active_tool_mut().process_message(ToolMessage::UpdateCursor, responses, &mut data); + + responses.add(OverlaysMessage::AddProvider(ARTBOARD_OVERLAY_PROVIDER)); } ToolMessage::PreUndo => { let tool_data = &mut self.tool_state.tool_data; diff --git a/node-graph/gcore/src/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index 91b7973b..2f37e2bf 100644 --- a/node-graph/gcore/src/graphic_element/renderer.rs +++ b/node-graph/gcore/src/graphic_element/renderer.rs @@ -599,22 +599,6 @@ impl GraphicElementRendered for Artboard { attributes.push("height", self.dimensions.y.abs().to_string()); }); } - if !render_params.hide_artboards && !render_params.for_export { - // Label - render.parent_tag( - "text", - |attributes| { - attributes.push("fill", "white"); - attributes.push("x", (self.location.x.min(self.location.x + self.dimensions.x)).to_string()); - attributes.push("y", (self.location.y.min(self.location.y + self.dimensions.y) - 4).to_string()); - attributes.push("font-size", "14px"); - }, - |render| { - // TODO: Use the artboard's layer name - render.svg.push(self.label.to_string().into()); - }, - ); - } // Contents group (includes the artwork but not the background) render.parent_tag(