mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 10:08:12 +08:00
Deprecate all usages of the Color struct representing gamma space values, fixing round-trip precision bugs (#4149)
* Deprecate all usages of the Color struct representing gamma space values, fixing round-trip precision bugs * Code review fixes
This commit is contained in:
@@ -6,7 +6,7 @@ use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::utility_types::DocumentToolData;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::vector::style::{FillChoice, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
|
||||
/// Color selector widgets seen in [`LayoutTarget::ToolOptions`] bar.
|
||||
pub struct ToolColorOptions {
|
||||
@@ -47,13 +47,12 @@ impl ToolColorOptions {
|
||||
self.enabled == Some(true)
|
||||
}
|
||||
|
||||
/// The active solid color in linear sRGB, suitable for storing in a working color or downstream rendering input.
|
||||
/// `fill_choice` is stored in gamma space (per [`FillChoice`]'s contract), so this method converts to linear before returning.
|
||||
/// The active solid color, suitable for storing in a working color or downstream rendering input.
|
||||
pub fn active_color(&self) -> Option<Color> {
|
||||
if !self.is_active() {
|
||||
return None;
|
||||
}
|
||||
Some(self.fill_choice.as_ref()?.as_solid()?.to_linear_srgb())
|
||||
self.fill_choice.as_ref()?.as_solid()
|
||||
}
|
||||
|
||||
pub fn apply_fill(&self, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
|
||||
@@ -86,7 +85,8 @@ impl ToolColorOptions {
|
||||
// In the mixed state (`fill_choice` is `None`) the dash overlay covers the swatch, so the underlying widget value just drives the picker's initial position.
|
||||
// `FillChoice::None` gives it a neutral starting point.
|
||||
let mixed_color = self.fill_choice.is_none();
|
||||
let widget_value = self.fill_choice.clone().unwrap_or(FillChoice::None);
|
||||
// Convert the internal linear-light `FillChoice` to the JS-boundary `FillChoiceUI` (with `SRGBA8` colors) for the widget value.
|
||||
let widget_value = FillChoiceUI::from(self.fill_choice.as_ref().unwrap_or(&FillChoice::None));
|
||||
let mixed_enabled = self.enabled.is_none();
|
||||
// In the mixed-enabled state the underlying `checked` value is hidden behind the indeterminate dash.
|
||||
// The frontend's click handler sends `true` when the user resolves the mixed state by clicking.
|
||||
@@ -191,10 +191,9 @@ impl DrawingToolState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `FillChoice::Solid` from a linear-space color, applying gamma conversion to display sRGB.
|
||||
/// Common helper used throughout the color-syncing code where working colors (linear) flow into swatches that store gamma-encoded colors.
|
||||
pub fn solid_gamma(color: Color) -> FillChoice {
|
||||
FillChoice::Solid(color.to_gamma_srgb())
|
||||
/// Builds a `FillChoice::Solid` from a color.
|
||||
pub fn solid(color: Color) -> FillChoice {
|
||||
FillChoice::Solid(color)
|
||||
}
|
||||
|
||||
/// The fill working color (the source for the fill swatch when nothing is selected).
|
||||
@@ -219,8 +218,8 @@ pub fn sync_color_options(
|
||||
document: &DocumentMessageHandler,
|
||||
selection_changed: bool,
|
||||
) -> bool {
|
||||
let fill_fallback = solid_gamma(fill_working_color(global, drawing.colors_swapped));
|
||||
let stroke_fallback = solid_gamma(stroke_working_color(global, drawing.colors_swapped));
|
||||
let fill_fallback = solid(fill_working_color(global, drawing.colors_swapped));
|
||||
let stroke_fallback = solid(stroke_working_color(global, drawing.colors_swapped));
|
||||
|
||||
let mut changed = false;
|
||||
|
||||
@@ -364,7 +363,7 @@ fn sync_stroke_options(drawing: &mut DrawingToolState, document: &DocumentMessag
|
||||
|
||||
/// Same as [`sync_color_options`] but for tools that only have a fill option (e.g., text). The fill follows the given working color when nothing is selected.
|
||||
pub fn sync_fill_only(fill: &mut ToolColorOptions, natural_fill_enabled: bool, fill_color: Color, document: &DocumentMessageHandler, selection_changed: bool) -> bool {
|
||||
let fill_fallback = solid_gamma(fill_color);
|
||||
let fill_fallback = solid(fill_color);
|
||||
|
||||
let new_fill = if let Some(state) = graph_modification_utils::selected_fill_state(document) {
|
||||
let active = state.enabled == Some(true);
|
||||
@@ -417,11 +416,7 @@ pub fn apply_fill_only_color_pick(fill: &mut ToolColorOptions, fill_choice: Fill
|
||||
}
|
||||
graph_modification_utils::set_fill_for_selected_layers(fill_choice, document, responses);
|
||||
} else if let FillChoice::Solid(color) = fill_choice {
|
||||
// Swatch is gamma; working colors are linear.
|
||||
responses.add(ToolMessage::SelectWorkingColor {
|
||||
color: color.to_linear_srgb(),
|
||||
primary: slot_is_primary,
|
||||
});
|
||||
responses.add(ToolMessage::SelectWorkingColor { color, primary: slot_is_primary });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,9 +431,8 @@ pub fn apply_stroke_color_pick(drawing: &mut DrawingToolState, color: Option<Col
|
||||
}
|
||||
graph_modification_utils::set_stroke_color_for_selected_layers(color, drawing.effective_line_weight(), document, responses);
|
||||
} else if let Some(color) = color {
|
||||
// Swatch is gamma; working colors are linear.
|
||||
responses.add(ToolMessage::SelectWorkingColor {
|
||||
color: color.to_linear_srgb(),
|
||||
color,
|
||||
primary: !drawing.colors_swapped,
|
||||
});
|
||||
}
|
||||
@@ -459,14 +453,14 @@ pub fn apply_fill_only_enabled(fill: &mut ToolColorOptions, enabled: bool, worki
|
||||
// Mixed re-tick has no per-layer color to restore; fall back to the working color and keep tracking it.
|
||||
let fill_choice = fill.fill_choice.clone().unwrap_or_else(|| {
|
||||
fill.tracks_working_color = true;
|
||||
solid_gamma(working_color)
|
||||
solid(working_color)
|
||||
});
|
||||
fill.fill_choice = Some(fill_choice.clone());
|
||||
graph_modification_utils::set_fill_for_selected_layers(fill_choice, document, responses);
|
||||
} else {
|
||||
// Unticking from mixed: capture the working color as the saved value so the swatch keeps following the link.
|
||||
if fill.fill_choice.is_none() {
|
||||
fill.fill_choice = Some(solid_gamma(working_color));
|
||||
fill.fill_choice = Some(solid(working_color));
|
||||
fill.tracks_working_color = true;
|
||||
}
|
||||
graph_modification_utils::remove_fill_for_selected_layers(document, responses);
|
||||
@@ -482,13 +476,13 @@ pub fn apply_stroke_enabled(drawing: &mut DrawingToolState, enabled: bool, globa
|
||||
if enabled {
|
||||
let stroke_choice = drawing.stroke.fill_choice.clone().unwrap_or_else(|| {
|
||||
drawing.stroke.tracks_working_color = true;
|
||||
solid_gamma(stroke_working_color(global, drawing.colors_swapped))
|
||||
solid(stroke_working_color(global, drawing.colors_swapped))
|
||||
});
|
||||
drawing.stroke.fill_choice = Some(stroke_choice.clone());
|
||||
graph_modification_utils::set_stroke_color_for_selected_layers(stroke_choice.as_solid(), drawing.effective_line_weight(), document, responses);
|
||||
} else {
|
||||
if drawing.stroke.fill_choice.is_none() {
|
||||
drawing.stroke.fill_choice = Some(solid_gamma(stroke_working_color(global, drawing.colors_swapped)));
|
||||
drawing.stroke.fill_choice = Some(solid(stroke_working_color(global, drawing.colors_swapped)));
|
||||
drawing.stroke.tracks_working_color = true;
|
||||
}
|
||||
graph_modification_utils::remove_stroke_for_selected_layers(document, responses);
|
||||
@@ -513,14 +507,14 @@ pub fn apply_working_colors(drawing: &mut DrawingToolState, global: &DocumentToo
|
||||
/// Refreshes a single swatch from the given working color, subject to the rules in [`apply_working_colors`].
|
||||
pub fn refresh_slot_working_color(slot: &mut ToolColorOptions, working_color: Color, document: &DocumentMessageHandler) {
|
||||
if slot.fill_choice.is_some() && (!has_selection(document) || slot.tracks_working_color) {
|
||||
slot.fill_choice = Some(solid_gamma(working_color));
|
||||
slot.fill_choice = Some(solid(working_color));
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the tool's swatches to the working colors. Called on tool deactivation and shape-mode changes.
|
||||
pub fn reset_colors_on_deactivation(drawing: &mut DrawingToolState, global: &DocumentToolData) {
|
||||
drawing.fill.fill_choice = Some(solid_gamma(fill_working_color(global, drawing.colors_swapped)));
|
||||
drawing.stroke.fill_choice = Some(solid_gamma(stroke_working_color(global, drawing.colors_swapped)));
|
||||
drawing.fill.fill_choice = Some(solid(fill_working_color(global, drawing.colors_swapped)));
|
||||
drawing.stroke.fill_choice = Some(solid(stroke_working_color(global, drawing.colors_swapped)));
|
||||
drawing.fill.tracks_working_color = true;
|
||||
drawing.stroke.tracks_working_color = true;
|
||||
}
|
||||
|
||||
@@ -329,10 +329,10 @@ pub fn get_fill_color(layer: LayerNodeIdentifier, network_interface: &NodeNetwor
|
||||
let fill_index = 1;
|
||||
|
||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER))?;
|
||||
let TaggedValue::Fill(Fill::Solid(color)) = inputs.get(fill_index)?.as_value()? else {
|
||||
let &TaggedValue::Fill(Fill::Solid(color)) = inputs.get(fill_index)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
Some(color.to_linear_srgb())
|
||||
Some(color)
|
||||
}
|
||||
|
||||
/// Get the current blend mode of a layer from the closest upstream "Blend Mode" node.
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::messages::prelude::*;
|
||||
use crate::messages::tool::transform_layer::transform_layer_message_handler::TransformLayerMessageContext;
|
||||
use crate::messages::tool::utility_types::{HintData, ToolType};
|
||||
use crate::node_graph_executor::NodeGraphExecutor;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::raster::color::Color;
|
||||
|
||||
const ARTBOARD_OVERLAY_PROVIDER: OverlayProvider = |context| DocumentMessage::DrawArtboardOverlays { context }.into();
|
||||
@@ -280,7 +281,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
|
||||
let r = (random_number >> 16) as u8;
|
||||
let g = (random_number >> 8) as u8;
|
||||
let b = random_number as u8;
|
||||
let random_color = Color::from_rgba8_srgb(r, g, b, 255);
|
||||
let random_color = Color::from(SRGBA8::new(r, g, b, 255));
|
||||
|
||||
if primary {
|
||||
document_data.primary_color = random_color;
|
||||
|
||||
@@ -4,13 +4,13 @@ use crate::messages::portfolio::document::graph_operation::transform_utils::get_
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_proto_node_type};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, solid_gamma};
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, solid};
|
||||
use graph_craft::document::NodeId;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::brush::brush_stroke::{BrushInputSample, BrushStroke, BrushStyle};
|
||||
use graphene_std::raster::BlendMode;
|
||||
use graphene_std::vector::style::FillChoice;
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI};
|
||||
|
||||
const BRUSH_MAX_SIZE: f64 = 5000.;
|
||||
|
||||
@@ -104,13 +104,12 @@ impl ToolMetadata for BrushTool {
|
||||
impl LayoutHolder for BrushTool {
|
||||
fn layout(&self) -> Layout {
|
||||
let mut widgets = vec![
|
||||
ColorInput::new(self.options.color.fill_choice.clone().unwrap_or(FillChoice::None))
|
||||
ColorInput::new(FillChoiceUI::from(self.options.color.fill_choice.as_ref().unwrap_or(&FillChoice::None)))
|
||||
.mixed(self.options.color.fill_choice.is_none())
|
||||
.narrow(true)
|
||||
.on_update(|color: &ColorInput| {
|
||||
BrushToolMessage::UpdateOptions {
|
||||
// The picker emits gamma-space colors; working colors are stored in linear sRGB.
|
||||
options: BrushToolMessageOptionsUpdate::Color(color.value.as_solid().map(|c| c.to_linear_srgb())),
|
||||
options: BrushToolMessageOptionsUpdate::Color(color.value.as_solid().map(Color::from)),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
@@ -245,7 +244,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Brus
|
||||
}
|
||||
}
|
||||
BrushToolMessageOptionsUpdate::WorkingColorsChanged => {
|
||||
self.options.color.fill_choice = Some(solid_gamma(context.global_tool_data.primary_color));
|
||||
self.options.color.fill_choice = Some(solid(context.global_tool_data.primary_color));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::tool_prelude::*;
|
||||
use crate::messages::frontend::utility_types::EyedropperPreviewImage;
|
||||
use crate::messages::tool::utility_types::DocumentToolData;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::vector::style::RenderMode;
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
@@ -233,8 +234,8 @@ fn update_cursor_preview_common(
|
||||
responses.add(FrontendMessage::UpdateEyedropperSamplingState {
|
||||
image,
|
||||
mouse_position: Some(input.mouse.position.into()),
|
||||
primary_color: "#".to_string() + global_tool_data.primary_color.to_rgb_hex_srgb().as_str(),
|
||||
secondary_color: "#".to_string() + global_tool_data.secondary_color.to_rgb_hex_srgb().as_str(),
|
||||
primary_color: SRGBA8::from(global_tool_data.primary_color).to_css_hex(),
|
||||
secondary_color: SRGBA8::from(global_tool_data.secondary_color).to_css_hex(),
|
||||
set_color_choice,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use super::tool_prelude::*;
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::tool::common_functionality::color_selector::solid_gamma;
|
||||
use crate::messages::tool::common_functionality::color_selector::solid;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::raster::color::Color;
|
||||
use graphene_std::vector::style::Fill;
|
||||
use graphene_std::vector::style::{Fill, FillChoiceUI};
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct FillTool {
|
||||
@@ -43,11 +44,11 @@ impl ToolMetadata for FillTool {
|
||||
impl LayoutHolder for FillTool {
|
||||
fn layout(&self) -> Layout {
|
||||
let widgets = vec![
|
||||
ColorInput::new(solid_gamma(self.primary_color))
|
||||
ColorInput::new(FillChoiceUI::from(&solid(self.primary_color)))
|
||||
.narrow(true)
|
||||
.on_update(|color: &ColorInput| {
|
||||
FillToolMessage::SetColor {
|
||||
color: color.value.as_solid().map(|c| c.to_linear_srgb()),
|
||||
color: color.value.as_solid().map(Color::from),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
@@ -141,7 +142,7 @@ impl Fsm for FillToolFsmState {
|
||||
|
||||
// Get the layer the user is hovering over
|
||||
if let Some(layer) = document.click(input, viewport) {
|
||||
let color_hex = format!("#{}", preview_color.to_rgba_hex_srgb());
|
||||
let color_hex = SRGBA8::from(preview_color).to_css_hex();
|
||||
overlay_context.fill_path_pattern(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), &color_hex);
|
||||
}
|
||||
|
||||
@@ -161,8 +162,8 @@ impl Fsm for FillToolFsmState {
|
||||
return self;
|
||||
}
|
||||
let fill = match color_event {
|
||||
FillToolMessage::FillPrimaryColor => Fill::Solid(global_tool_data.primary_color.to_gamma_srgb()),
|
||||
FillToolMessage::FillSecondaryColor => Fill::Solid(global_tool_data.secondary_color.to_gamma_srgb()),
|
||||
FillToolMessage::FillPrimaryColor => Fill::Solid(global_tool_data.primary_color),
|
||||
FillToolMessage::FillSecondaryColor => Fill::Solid(global_tool_data.secondary_color),
|
||||
_ => return self,
|
||||
};
|
||||
|
||||
@@ -201,6 +202,7 @@ impl Fsm for FillToolFsmState {
|
||||
#[cfg(test)]
|
||||
mod test_fill {
|
||||
pub use crate::test_utils::test_prelude::*;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::vector::fill;
|
||||
use graphene_std::vector::style::Fill;
|
||||
|
||||
@@ -240,7 +242,7 @@ mod test_fill {
|
||||
editor.click_tool(ToolType::Fill, MouseKeys::LEFT, DVec2::new(2., 2.), ModifierKeys::empty()).await;
|
||||
let fills = get_fills(&mut editor).await;
|
||||
assert_eq!(fills.len(), 1);
|
||||
assert_eq!(fills[0].as_solid().unwrap().to_rgba8_srgb(), Color::GREEN.to_rgba8_srgb());
|
||||
assert_eq!(SRGBA8::from(fills[0].as_solid().unwrap()), SRGBA8::from(Color::GREEN));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -252,6 +254,6 @@ mod test_fill {
|
||||
editor.click_tool(ToolType::Fill, MouseKeys::LEFT, DVec2::new(2., 2.), ModifierKeys::SHIFT).await;
|
||||
let fills = get_fills(&mut editor).await;
|
||||
assert_eq!(fills.len(), 1);
|
||||
assert_eq!(fills[0].as_solid().unwrap().to_rgba8_srgb(), Color::YELLOW.to_rgba8_srgb());
|
||||
assert_eq!(SRGBA8::from(fills[0].as_solid().unwrap()), SRGBA8::from(Color::YELLOW));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ impl LayoutHolder for FreehandTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
FreehandToolMessage::UpdateOptions {
|
||||
options: FreehandOptionsUpdate::FillColor(color.value.clone()),
|
||||
options: FreehandOptionsUpdate::FillColor(FillChoice::from(&color.value)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
@@ -127,7 +127,7 @@ impl LayoutHolder for FreehandTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
FreehandToolMessage::UpdateOptions {
|
||||
options: FreehandOptionsUpdate::StrokeColor(color.value.as_solid()),
|
||||
options: FreehandOptionsUpdate::StrokeColor(color.value.as_solid().map(Color::from)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
|
||||
@@ -11,8 +11,9 @@ use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer, get_gradient_stops};
|
||||
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::raster::color::Color;
|
||||
use graphene_std::vector::style::{Fill, FillChoice, Gradient, GradientSpreadMethod, GradientStop, GradientStops, GradientType};
|
||||
use graphene_std::vector::style::{Fill, FillChoice, FillChoiceUI, Gradient, GradientSpreadMethod, GradientStop, GradientStops, GradientStopsUI, GradientType};
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct GradientTool {
|
||||
@@ -49,7 +50,7 @@ pub enum GradientToolMessage {
|
||||
CommitTransactionForColorStop,
|
||||
CloseStopColorPicker,
|
||||
UpdateStopColor { color: Color },
|
||||
UpdateStops { stops: GradientStops },
|
||||
UpdateStops { stops: GradientStopsUI },
|
||||
UpdateOptions { options: GradientOptionsUpdate },
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Grad
|
||||
}
|
||||
}
|
||||
ToolMessage::Gradient(GradientToolMessage::UpdateStops { stops }) => {
|
||||
apply_stops_update(&mut self.data, context, responses, stops);
|
||||
apply_stops_update(&mut self.data, context, responses, GradientStops::from(&stops));
|
||||
}
|
||||
ToolMessage::Gradient(GradientToolMessage::CloseStopColorPicker) => {
|
||||
if self.data.color_picker_transaction_open {
|
||||
@@ -243,7 +244,7 @@ impl LayoutHolder for GradientTool {
|
||||
},
|
||||
]))
|
||||
});
|
||||
let stops_widget = ColorInput::new(stops_value)
|
||||
let stops_widget = ColorInput::new(FillChoiceUI::from(&stops_value))
|
||||
.allow_none(false)
|
||||
.narrow(true)
|
||||
.tooltip_label("Gradient Stops")
|
||||
@@ -856,7 +857,7 @@ impl Fsm for GradientToolFsmState {
|
||||
let (start, end) = (transform.transform_point2(*start), transform.transform_point2(*end));
|
||||
|
||||
fn color_to_hex(color: graphene_std::Color) -> String {
|
||||
format!("#{}", color.to_rgb_hex_srgb_from_gamma())
|
||||
SRGBA8::from(color).to_css_hex()
|
||||
}
|
||||
|
||||
let start_hex = stops.color.first().map(|&c| color_to_hex(c)).unwrap_or(String::from(COLOR_OVERLAY_BLUE));
|
||||
@@ -1024,10 +1025,10 @@ impl Fsm for GradientToolFsmState {
|
||||
let transform = gradient_space_transform(layer, document);
|
||||
let gradient = &selected_gradient.gradient;
|
||||
if stop_index < gradient.stops.position.len() {
|
||||
let color = gradient.stops.color[stop_index].to_gamma_srgb();
|
||||
let color = gradient.stops.color[stop_index];
|
||||
let position = gradient.stops.position[stop_index];
|
||||
let position = transform.transform_point2(gradient.start.lerp(gradient.end, position)).into();
|
||||
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition { color, position });
|
||||
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition { color: color.into(), position });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1081,9 +1082,9 @@ impl Fsm for GradientToolFsmState {
|
||||
.transform
|
||||
.transform_point2(selected_gradient.gradient.start.lerp(selected_gradient.gradient.end, stop_pos));
|
||||
let position = viewport_pos.into();
|
||||
let color = selected_gradient.gradient.stops.color[stop_index].to_gamma_srgb();
|
||||
let color = selected_gradient.gradient.stops.color[stop_index];
|
||||
tool_data.color_picker_editing_color_stop = Some(stop_index);
|
||||
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition { color, position });
|
||||
responses.add(FrontendMessage::UpdateGradientStopColorPickerPosition { color: color.into(), position });
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -1910,6 +1911,7 @@ mod test_gradient {
|
||||
pub use crate::test_utils::test_prelude::*;
|
||||
use glam::DAffine2;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::vector::style::{Fill, Gradient};
|
||||
use graphene_std::vector::{GradientStop, GradientStops, fill};
|
||||
|
||||
@@ -2022,8 +2024,8 @@ mod test_gradient {
|
||||
let (gradient, transform) = get_gradient(&mut editor).await;
|
||||
|
||||
// Gradient goes from primary color to secondary color
|
||||
let stops = gradient.stops.iter().map(|stop| (stop.position, stop.color.to_rgba8_srgb())).collect::<Vec<_>>();
|
||||
assert_eq!(stops, vec![(0., Color::GREEN.to_rgba8_srgb()), (1., Color::BLUE.to_rgba8_srgb())]);
|
||||
let stops = gradient.stops.iter().map(|stop| (stop.position, SRGBA8::from(stop.color))).collect::<Vec<_>>();
|
||||
assert_eq!(stops, vec![(0., SRGBA8::from(Color::GREEN)), (1., SRGBA8::from(Color::BLUE))]);
|
||||
assert!(transform.transform_point2(gradient.start).abs_diff_eq(DVec2::new(2., 3.), 1e-10));
|
||||
assert!(transform.transform_point2(gradient.end).abs_diff_eq(DVec2::new(24., 4.), 1e-10));
|
||||
}
|
||||
@@ -2215,7 +2217,7 @@ mod test_gradient {
|
||||
let positions: Vec<f64> = stops.iter().map(|stop| stop.position).collect();
|
||||
assert_stops_at_positions(&positions, &[0., 0.25, 1.], 0.1);
|
||||
|
||||
let middle_color = stops.color[1].to_rgba8_srgb();
|
||||
let middle_color = SRGBA8::from(stops.color[1]);
|
||||
|
||||
// Simulate dragging the middle stop to position 0.8
|
||||
let click_position = DVec2::new(25., 0.);
|
||||
@@ -2256,9 +2258,9 @@ mod test_gradient {
|
||||
assert_stops_at_positions(&updated_positions, &[0., 0.8, 1.], 0.1);
|
||||
|
||||
// Colors should maintain their associations with the stop points
|
||||
assert_eq!(updated_stops.color[0].to_rgba8_srgb(), Color::GREEN.to_rgba8_srgb());
|
||||
assert_eq!(updated_stops.color[1].to_rgba8_srgb(), middle_color);
|
||||
assert_eq!(updated_stops.color[2].to_rgba8_srgb(), Color::BLUE.to_rgba8_srgb());
|
||||
assert_eq!(SRGBA8::from(updated_stops.color[0]), SRGBA8::from(Color::GREEN));
|
||||
assert_eq!(SRGBA8::from(updated_stops.color[1]), middle_color);
|
||||
assert_eq!(SRGBA8::from(updated_stops.color[2]), SRGBA8::from(Color::BLUE));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -2495,8 +2497,8 @@ mod test_gradient {
|
||||
|
||||
assert_eq!(updated.stops.len(), 3, "Stop count should be preserved");
|
||||
assert_stops_at_positions(&updated.stops.position, &[0., 0.5, 1.], 1e-10);
|
||||
assert_eq!(updated.stops.color[0].to_rgba8_srgb(), Color::RED.to_rgba8_srgb(), "First stop color should be preserved");
|
||||
assert_eq!(updated.stops.color[1].to_rgba8_srgb(), Color::GREEN.to_rgba8_srgb(), "Middle stop color should be preserved");
|
||||
assert_eq!(updated.stops.color[2].to_rgba8_srgb(), Color::BLUE.to_rgba8_srgb(), "Last stop color should be preserved");
|
||||
assert_eq!(SRGBA8::from(updated.stops.color[0]), SRGBA8::from(Color::RED), "First stop color should be preserved");
|
||||
assert_eq!(SRGBA8::from(updated.stops.color[1]), SRGBA8::from(Color::GREEN), "Middle stop color should be preserved");
|
||||
assert_eq!(SRGBA8::from(updated.stops.color[2]), SRGBA8::from(Color::BLUE), "Last stop color should be preserved");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2850,13 +2850,10 @@ impl Fsm for PathToolFsmState {
|
||||
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, parent, responses);
|
||||
|
||||
// Defaults chosen because the pasted geometry has no inherent associated style
|
||||
let stroke_color = Color::BLACK;
|
||||
let fill_color = Color::WHITE;
|
||||
|
||||
let stroke = graphene_std::vector::style::Stroke::new(Some(stroke_color.to_gamma_srgb()), DEFAULT_STROKE_WIDTH);
|
||||
let stroke = graphene_std::vector::style::Stroke::new(Some(Color::BLACK), DEFAULT_STROKE_WIDTH);
|
||||
responses.add(GraphOperationMessage::StrokeSet { layer, stroke });
|
||||
|
||||
let fill = graphene_std::vector::style::Fill::solid(fill_color.to_gamma_srgb());
|
||||
let fill = graphene_std::vector::style::Fill::solid(Color::WHITE);
|
||||
responses.add(GraphOperationMessage::FillSet { layer, fill });
|
||||
|
||||
new_layers.push(layer);
|
||||
|
||||
@@ -154,7 +154,7 @@ impl LayoutHolder for PenTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
PenToolMessage::UpdateOptions {
|
||||
options: PenOptionsUpdate::FillColor(color.value.clone()),
|
||||
options: PenOptionsUpdate::FillColor(FillChoice::from(&color.value)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
@@ -184,7 +184,7 @@ impl LayoutHolder for PenTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
PenToolMessage::UpdateOptions {
|
||||
options: PenOptionsUpdate::StrokeColor(color.value.as_solid()),
|
||||
options: PenOptionsUpdate::StrokeColor(color.value.as_solid().map(Color::from)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
|
||||
@@ -248,7 +248,7 @@ impl LayoutHolder for SelectTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
SelectToolMessage::SelectOptions {
|
||||
options: SelectOptionsUpdate::FillColor(color.value.clone()),
|
||||
options: SelectOptionsUpdate::FillColor(FillChoice::from(&color.value)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
@@ -278,7 +278,7 @@ impl LayoutHolder for SelectTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
SelectToolMessage::SelectOptions {
|
||||
options: SelectOptionsUpdate::StrokeColor(color.value.as_solid()),
|
||||
options: SelectOptionsUpdate::StrokeColor(color.value.as_solid().map(Color::from)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
|
||||
@@ -467,7 +467,7 @@ impl LayoutHolder for ShapeTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
ShapeToolMessage::UpdateOptions {
|
||||
options: ShapeOptionsUpdate::FillColor(color.value.clone()),
|
||||
options: ShapeOptionsUpdate::FillColor(FillChoice::from(&color.value)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
@@ -498,7 +498,7 @@ impl LayoutHolder for ShapeTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
ShapeToolMessage::UpdateOptions {
|
||||
options: ShapeOptionsUpdate::StrokeColor(color.value.as_solid()),
|
||||
options: ShapeOptionsUpdate::StrokeColor(color.value.as_solid().map(Color::from)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
|
||||
@@ -105,7 +105,7 @@ impl LayoutHolder for SplineTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
SplineToolMessage::UpdateOptions {
|
||||
options: SplineOptionsUpdate::FillColor(color.value.clone()),
|
||||
options: SplineOptionsUpdate::FillColor(FillChoice::from(&color.value)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
@@ -135,7 +135,7 @@ impl LayoutHolder for SplineTool {
|
||||
},
|
||||
|color: &ColorInput| {
|
||||
SplineToolMessage::UpdateOptions {
|
||||
options: SplineOptionsUpdate::StrokeColor(color.value.as_solid()),
|
||||
options: SplineOptionsUpdate::StrokeColor(color.value.as_solid().map(Color::from)),
|
||||
}
|
||||
.into()
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Inpu
|
||||
use crate::messages::portfolio::utility_types::{CachedData, FontCatalog, FontCatalogStyle};
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::color_selector::{
|
||||
ToolColorOptions, apply_fill_only_color_pick, apply_fill_only_enabled, refresh_slot_working_color, selection_changed_since_last_sync, solid_gamma, sync_fill_only,
|
||||
ToolColorOptions, apply_fill_only_color_pick, apply_fill_only_enabled, refresh_slot_working_color, selection_changed_since_last_sync, solid, sync_fill_only,
|
||||
};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
@@ -20,9 +20,10 @@ use crate::messages::tool::utility_types::ToolRefreshOptions;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
use graphene_std::choice_type::ChoiceTypeStatic;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::renderer::Quad;
|
||||
use graphene_std::text::{Font, FontCache, TextAlign, TypesettingConfig, lines_clipping};
|
||||
use graphene_std::vector::style::{Fill, FillChoice};
|
||||
use graphene_std::vector::style::{Fill, FillChoice, FillChoiceUI};
|
||||
use graphene_std::{Color, NodeInputDecleration};
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
@@ -269,12 +270,12 @@ impl TextTool {
|
||||
|
||||
fn layout(&self, font_catalog: &FontCatalog, document: &DocumentMessageHandler) -> Layout {
|
||||
let mut widgets = vec![
|
||||
ColorInput::new(self.options.fill.fill_choice.clone().unwrap_or(graphene_std::vector::style::FillChoice::None))
|
||||
ColorInput::new(FillChoiceUI::from(self.options.fill.fill_choice.as_ref().unwrap_or(&FillChoice::None)))
|
||||
.mixed(self.options.fill.fill_choice.is_none())
|
||||
.narrow(true)
|
||||
.on_update(|color: &ColorInput| {
|
||||
TextToolMessage::UpdateOptions {
|
||||
options: TextOptionsUpdate::FillColor(color.value.clone()),
|
||||
options: TextOptionsUpdate::FillColor(FillChoice::from(&color.value)),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
@@ -295,7 +296,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Text
|
||||
// reset the displayed fill color so the next activation starts fresh from the current working color.
|
||||
// Guarded on `Ready` so Esc-mid-editing (which also fires Abort) doesn't wipe the user's customized fill option.
|
||||
if matches!(&message, ToolMessage::Text(TextToolMessage::Abort)) && self.fsm_state == TextToolFsmState::Ready {
|
||||
self.options.fill.fill_choice = Some(solid_gamma(context.global_tool_data.primary_color));
|
||||
self.options.fill.fill_choice = Some(solid(context.global_tool_data.primary_color));
|
||||
}
|
||||
|
||||
let options = match message {
|
||||
@@ -319,7 +320,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Text
|
||||
if can_edit_selected(context.document).is_some() {
|
||||
sync_fill_only(&mut self.options.fill, true, context.global_tool_data.primary_color, context.document, selection_changed);
|
||||
} else if selection_changed {
|
||||
self.options.fill.fill_choice = Some(solid_gamma(context.global_tool_data.primary_color));
|
||||
self.options.fill.fill_choice = Some(solid(context.global_tool_data.primary_color));
|
||||
self.options.fill.tracks_working_color = true;
|
||||
}
|
||||
// Text tool has no fill checkbox; keep enabled so new text never starts with `None`
|
||||
@@ -497,7 +498,7 @@ impl TextToolData {
|
||||
text: editing_text.text.clone(),
|
||||
line_height_ratio: editing_text.typesetting.line_height_ratio,
|
||||
font_size: editing_text.typesetting.font_size,
|
||||
color: editing_text.color.map_or("#000000".to_string(), |color| format!("#{}", color.to_rgba_hex_srgb())),
|
||||
color: editing_text.color.map_or("#000000".to_string(), |color| SRGBA8::from(color).to_css_hex()),
|
||||
font_data: font_cache.get(&editing_text.font).map(|(data, _)| data.clone()).unwrap_or_default().into(),
|
||||
transform: editing_text.transform.to_cols_array(),
|
||||
max_width: editing_text.typesetting.max_width,
|
||||
@@ -574,7 +575,7 @@ impl TextToolData {
|
||||
});
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer: self.layer,
|
||||
fill: if let Some(color) = editing_text.color { Fill::Solid(color.to_gamma_srgb()) } else { Fill::None },
|
||||
fill: if let Some(color) = editing_text.color { Fill::Solid(color) } else { Fill::None },
|
||||
});
|
||||
let transform = editing_text.transform;
|
||||
self.editing_text = Some(editing_text);
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::messages::preferences::PreferencesMessageHandler;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeType;
|
||||
use crate::node_graph_executor::NodeGraphExecutor;
|
||||
use graphene_std::color::SRGBA8;
|
||||
use graphene_std::raster::color::Color;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Debug};
|
||||
@@ -128,9 +129,7 @@ pub struct DocumentToolData {
|
||||
impl DocumentToolData {
|
||||
pub fn update_working_colors(&self, responses: &mut VecDeque<Message>) {
|
||||
let layout = Layout(vec![
|
||||
LayoutGroup::row(vec![
|
||||
WorkingColorsInput::new(self.primary_color.to_gamma_srgb(), self.secondary_color.to_gamma_srgb()).widget_instance(),
|
||||
]),
|
||||
LayoutGroup::row(vec![WorkingColorsInput::new(SRGBA8::from(self.primary_color), SRGBA8::from(self.secondary_color)).widget_instance()]),
|
||||
LayoutGroup::row(vec![
|
||||
IconButton::new("SwapVertical", 16)
|
||||
.tooltip_label("Swap Working Colors")
|
||||
|
||||
Reference in New Issue
Block a user