Skip to content
1 change: 1 addition & 0 deletions editor/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ pub const COLOR_OVERLAY_RED: &str = "#ef5454";
pub const COLOR_OVERLAY_GRAY: &str = "#cccccc";
pub const COLOR_OVERLAY_GRAY_25: &str = "#cccccc40";
pub const COLOR_OVERLAY_WHITE: &str = "#ffffff";
pub const COLOR_OVERLAY_BLACK: &str = "#000000";
pub const COLOR_OVERLAY_BLACK_75: &str = "#000000bf";

// DOCUMENT
Expand Down
1 change: 0 additions & 1 deletion editor/src/messages/input_mapper/input_mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping {
entry!(KeyDown(MouseLeft); action_dispatch=GradientToolMessage::PointerDown),
entry!(PointerMove; refresh_keys=[Shift], action_dispatch=GradientToolMessage::PointerMove { constrain_axis: Shift }),
entry!(KeyUp(MouseLeft); action_dispatch=GradientToolMessage::PointerUp),
entry!(DoubleClick(MouseButton::Left); action_dispatch=GradientToolMessage::InsertStop),
entry!(KeyDown(Delete); action_dispatch=GradientToolMessage::DeleteStop),
entry!(KeyDown(Backspace); action_dispatch=GradientToolMessage::DeleteStop),
entry!(KeyDown(MouseRight); action_dispatch=GradientToolMessage::Abort),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(DocumentMessage::DocumentHistoryForward);
responses.add(ToolMessage::Redo);
responses.add(OverlaysMessage::Draw);
responses.add(EventMessage::SelectionChanged);
}
DocumentMessage::RenameDocument { new_name } => {
self.name = new_name.clone();
Expand Down Expand Up @@ -1432,6 +1433,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(DocumentMessage::DocumentHistoryBackward);
responses.add(OverlaysMessage::Draw);
responses.add(ToolMessage::Undo);
responses.add(EventMessage::SelectionChanged);
}
DocumentMessage::UngroupSelectedLayers => {
if !self.selection_network_path.is_empty() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::consts::{
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL,
COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE,
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW,
COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS,
MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
};
use crate::messages::portfolio::document::overlays::utility_functions::{GLOBAL_FONT_CACHE, GLOBAL_TEXT_CONTEXT};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
Expand Down Expand Up @@ -273,6 +273,10 @@ impl OverlayContext {
self.internal().manipulator_anchor(position, selected, color);
}

pub fn gradient_color_stop(&mut self, position: DVec2, selected: bool, color: &str) {
self.internal().gradient_color_stop(position, selected, color);
}

pub fn resize_handle(&mut self, position: DVec2, rotation: f64) {
self.internal().resize_handle(position, rotation);
}
Expand Down Expand Up @@ -582,6 +586,29 @@ impl OverlayContextInternal {
self.square(position, None, Some(color_fill), Some(COLOR_OVERLAY_BLUE));
}

fn gradient_color_stop(&mut self, position: DVec2, selected: bool, color: &str) {
let transform = self.get_transform();
let position = position.round() - DVec2::splat(0.5);

let (radius_offset, stroke_width) = if selected { (1., 3.) } else { (0., 1.) };
let radius = MANIPULATOR_GROUP_MARKER_SIZE / 1.5 + 1. + radius_offset;

let mut draw_circle = |radius: f64, width: Option<f64>, color: &str| {
let circle = kurbo::Circle::new((position.x, position.y), radius);
if let Some(width) = width {
self.scene.stroke(&kurbo::Stroke::new(width), transform, Self::parse_color(color), None, &circle);
} else {
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color), None, &circle);
}
};
// Fill
draw_circle(radius, None, color);
// Stroke (inner)
draw_circle(radius + stroke_width / 2., Some(1.), COLOR_OVERLAY_BLACK);
// Stroke (outer)
draw_circle(radius, Some(stroke_width), COLOR_OVERLAY_WHITE);
}

fn resize_handle(&mut self, position: DVec2, rotation: f64) {
let quad = DAffine2::from_angle_translation(rotation, position) * Quad::from_box([DVec2::splat(-RESIZE_HANDLE_SIZE / 2.), DVec2::splat(RESIZE_HANDLE_SIZE / 2.)]);
self.quad(quad, None, Some(COLOR_OVERLAY_WHITE));
Expand Down Expand Up @@ -831,7 +858,7 @@ impl OverlayContextInternal {
path.move_to(kurbo::Point::new(x, y));
path.line_to(kurbo::Point::new(start1_x, start1_y));

let arc1 = kurbo::Arc::new((x, y), (DOWEL_PIN_RADIUS, DOWEL_PIN_RADIUS), start1, FRAC_PI_2, 0.0);
let arc1 = kurbo::Arc::new((x, y), (DOWEL_PIN_RADIUS, DOWEL_PIN_RADIUS), start1, FRAC_PI_2, 0.);
arc1.to_cubic_beziers(0.1, |p1, p2, p| {
path.curve_to(p1, p2, p);
});
Expand All @@ -843,7 +870,7 @@ impl OverlayContextInternal {
path.move_to(kurbo::Point::new(x, y));
path.line_to(kurbo::Point::new(start2_x, start2_y));

let arc2 = kurbo::Arc::new((x, y), (DOWEL_PIN_RADIUS, DOWEL_PIN_RADIUS), start2, FRAC_PI_2, 0.0);
let arc2 = kurbo::Arc::new((x, y), (DOWEL_PIN_RADIUS, DOWEL_PIN_RADIUS), start2, FRAC_PI_2, 0.);
arc2.to_cubic_beziers(0.1, |p1, p2, p| {
path.curve_to(p1, p2, p);
});
Expand Down Expand Up @@ -890,15 +917,15 @@ impl OverlayContextInternal {
let mut path = BezPath::new();
self.bezier_to_path(bezier, transform, true, &mut path);

self.scene.stroke(&kurbo::Stroke::new(4.0), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
self.scene.stroke(&kurbo::Stroke::new(4.), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
}

fn outline_overlay_bezier(&mut self, bezier: PathSeg, transform: DAffine2) {
let vello_transform = self.get_transform();
let mut path = BezPath::new();
self.bezier_to_path(bezier, transform, true, &mut path);

self.scene.stroke(&kurbo::Stroke::new(4.0), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE_50), None, &path);
self.scene.stroke(&kurbo::Stroke::new(4.), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE_50), None, &path);
}

fn bezier_to_path(&self, bezier: PathSeg, transform: DAffine2, move_to: bool, path: &mut BezPath) {
Expand Down Expand Up @@ -1033,16 +1060,16 @@ impl OverlayContextInternal {

fn text(&mut self, text: &str, font_color: &str, background_color: Option<&str>, transform: DAffine2, padding: f64, pivot: [Pivot; 2]) {
// Use the proper text-to-path system for accurate text rendering
const FONT_SIZE: f64 = 12.0;
const FONT_SIZE: f64 = 12.;

// Create typesetting configuration
let typesetting = TypesettingConfig {
font_size: FONT_SIZE,
line_height_ratio: 1.2,
character_spacing: 0.0,
character_spacing: 0.,
max_width: None,
max_height: None,
tilt: 0.0,
tilt: 0.,
align: TextAlign::Left, // We'll handle alignment manually via pivot
};

Expand All @@ -1057,7 +1084,7 @@ impl OverlayContextInternal {
let text_width = text_size.x;
let text_height = text_size.y;
// Create a rect from the size (assuming text starts at origin)
let text_bounds = kurbo::Rect::new(0.0, 0.0, text_width, text_height);
let text_bounds = kurbo::Rect::new(0., 0., text_width, text_height);

// Convert text to vector paths for rendering
let text_table = text_context.to_path(text, &font, &GLOBAL_FONT_CACHE, typesetting, false);
Expand All @@ -1066,7 +1093,7 @@ impl OverlayContextInternal {
let mut position = DVec2::ZERO;
match pivot[0] {
Pivot::Start => position.x = padding,
Pivot::Middle => position.x = -text_width / 2.0,
Pivot::Middle => position.x = -text_width / 2.,
Pivot::End => position.x = -padding - text_width,
}
match pivot[1] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::utility_functions::overlay_canvas_context;
use crate::consts::{
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL,
COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE,
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SEGMENT_SELECTED_THICKNESS, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW,
COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS,
MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SEGMENT_SELECTED_THICKNESS, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::prelude::Message;
Expand Down Expand Up @@ -468,6 +468,37 @@ impl OverlayContext {
self.square(position, None, Some(color_fill), Some(color_stroke));
}

pub fn gradient_color_stop(&mut self, position: DVec2, selected: bool, color: &str) {
self.start_dpi_aware_transform();

let position = position.round() - DVec2::splat(0.5);

let (radius_offset, stroke_width) = if selected { (1., 3.) } else { (0., 1.) };
let radius = MANIPULATOR_GROUP_MARKER_SIZE / 1.5 + 1. + radius_offset;

let draw_circle = |radius: f64, width: Option<f64>, color: &str| {
self.render_context.begin_path();
self.render_context.arc(position.x, position.y, radius, 0., TAU).expect("Failed to draw the circle");

if let Some(width) = width {
self.render_context.set_line_width(width);
self.render_context.set_stroke_style_str(color);
self.render_context.stroke();
} else {
self.render_context.set_fill_style_str(color);
self.render_context.fill();
}
};
// Fill
draw_circle(radius, None, color);
// Stroke (inner)
draw_circle(radius + stroke_width / 2., Some(1.), COLOR_OVERLAY_BLACK);
// Stroke (outer)
draw_circle(radius, Some(stroke_width), COLOR_OVERLAY_WHITE);

self.end_dpi_aware_transform();
}

pub fn hover_manipulator_handle(&mut self, position: DVec2, selected: bool) {
self.start_dpi_aware_transform();

Expand Down
Loading