mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 18:18:12 +08:00
Improve the Gradient tool by visualizing color stops (#3698)
* Gradient * improvement * removed unnecessary code * corrected error * Partical changes * Improveded * remove from advertised actions * Mousedown * Partial code review * changes as per recommendation * corrected error * corrected error -2 * changes as per recommendation * error corrected * changes as suggested * Bug Fix * Fix hints --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
ea68d62ec4
commit
e606efd008
@@ -964,6 +964,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();
|
||||
@@ -1431,6 +1432,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() {
|
||||
|
||||
@@ -25,6 +25,7 @@ use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
use crate::messages::viewport::Position;
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
||||
use graphene_std::math::math_ext::QuadExt;
|
||||
use graphene_std::vector::algorithms::bezpath_algorithms::bezpath_is_inside_bezpath;
|
||||
@@ -1699,12 +1700,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
|
||||
let is_fill = matches!(value, TaggedValue::Fill(_));
|
||||
let input = NodeInput::value(value, false);
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(node_id, input_index),
|
||||
input,
|
||||
});
|
||||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
if is_fill {
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
}
|
||||
if network_interface.connected_to_output(&node_id, selection_network_path) {
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -583,6 +587,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));
|
||||
@@ -832,7 +859,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);
|
||||
});
|
||||
@@ -844,7 +871,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);
|
||||
});
|
||||
@@ -891,7 +918,7 @@ 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) {
|
||||
@@ -899,7 +926,7 @@ 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_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) {
|
||||
@@ -1035,16 +1062,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
|
||||
};
|
||||
|
||||
@@ -1059,7 +1086,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);
|
||||
@@ -1068,7 +1095,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] {
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user