Use overlays to draw artboard names (#1981)
* Artboard label overlay * Finish artboard overlays * cargo fmt * Update font styling --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
32da545a49
commit
0d33a1ae3f
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ pub enum DocumentMessage {
|
|||
DocumentHistoryBackward,
|
||||
DocumentHistoryForward,
|
||||
DocumentStructureChanged,
|
||||
DrawArtboardOverlays(OverlayContext),
|
||||
DuplicateSelectedLayers,
|
||||
EnterNestedNetwork {
|
||||
node_id: NodeId,
|
||||
|
|
|
|||
|
|
@ -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<DocumentMessage, DocumentMessageData<'_>> 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 =
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ToolMessage, ToolMessageData<'_>> 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<ToolMessage, ToolMessageData<'_>> 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;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue