mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 03:48:11 +08:00
Refactor many usages of Color to natively store linear not gamma (#2457)
This commit is contained in:
@@ -82,6 +82,7 @@ impl LayoutMessageHandler {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (color_button.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
// Decodes the colors in gamma, not linear
|
||||
let decode_color = |color: &serde_json::map::Map<String, serde_json::value::Value>| -> Option<Color> {
|
||||
let red = color.get("red").and_then(|x| x.as_f64()).map(|x| x as f32);
|
||||
let green = color.get("green").and_then(|x| x.as_f64()).map(|x| x as f32);
|
||||
@@ -120,20 +121,13 @@ impl LayoutMessageHandler {
|
||||
.filter_map(|stop| {
|
||||
stop.as_object().and_then(|stop| {
|
||||
let position = stop.get("position").and_then(|x| x.as_f64());
|
||||
let color = stop.get("color").and_then(|x| x.as_object());
|
||||
|
||||
if let (Some(position), Some(color_object)) = (position, color) {
|
||||
if let Some(color) = decode_color(color_object) {
|
||||
return Some((position, color));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
let color = stop.get("color").and_then(|x| x.as_object()).and_then(decode_color);
|
||||
if let (Some(position), Some(color)) = (position, color) { Some((position, color)) } else { None }
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
color_button.value = FillChoice::Gradient(GradientStops(gradient_stops));
|
||||
color_button.value = FillChoice::Gradient(GradientStops::new(gradient_stops));
|
||||
return (color_button.on_update.callback)(color_button);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ pub struct ImageButton {
|
||||
#[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
|
||||
#[derivative(Debug, PartialEq, Default)]
|
||||
pub struct ColorInput {
|
||||
/// WARNING: The colors are gamma, not linear!
|
||||
#[widget_builder(constructor)]
|
||||
pub value: FillChoice,
|
||||
|
||||
|
||||
+2
-2
@@ -410,7 +410,7 @@ fn apply_usvg_fill(fill: &usvg::Fill, modify_inputs: &mut ModifyInputsContext, t
|
||||
|
||||
let [start, end] = [bounds_transform.inverse().transform_point2(layer[0]), bounds_transform.inverse().transform_point2(layer[1])];
|
||||
let stops = linear.stops().iter().map(|stop| (stop.offset().get() as f64, usvg_color(stop.color(), stop.opacity().get()))).collect();
|
||||
let stops = GradientStops(stops);
|
||||
let stops = GradientStops::new(stops);
|
||||
|
||||
Fill::Gradient(Gradient {
|
||||
start,
|
||||
@@ -437,7 +437,7 @@ fn apply_usvg_fill(fill: &usvg::Fill, modify_inputs: &mut ModifyInputsContext, t
|
||||
|
||||
let [start, end] = [bounds_transform.inverse().transform_point2(layer[0]), bounds_transform.inverse().transform_point2(layer[1])];
|
||||
let stops = radial.stops().iter().map(|stop| (stop.offset().get() as f64, usvg_color(stop.color(), stop.opacity().get()))).collect();
|
||||
let stops = GradientStops(stops);
|
||||
let stops = GradientStops::new(stops);
|
||||
|
||||
Fill::Gradient(Gradient {
|
||||
start,
|
||||
|
||||
@@ -1087,7 +1087,10 @@ pub fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
||||
return LayoutGroup::Row { widgets };
|
||||
};
|
||||
|
||||
// Add a separator
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
|
||||
// Add the color input
|
||||
match &**tagged_value {
|
||||
TaggedValue::Color(color) => widgets.push(
|
||||
color_button
|
||||
|
||||
@@ -9,7 +9,7 @@ use graphene_std::vector::style::FillChoice;
|
||||
|
||||
fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
let grid_color = document.snapping_state.grid.grid_color;
|
||||
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||
return;
|
||||
};
|
||||
@@ -36,11 +36,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
|
||||
} else {
|
||||
DVec2::new(secondary_pos, primary_end)
|
||||
};
|
||||
overlay_context.line(
|
||||
document_to_viewport.transform_point2(start),
|
||||
document_to_viewport.transform_point2(end),
|
||||
Some(&("#".to_string() + &grid_color.rgba_hex())),
|
||||
);
|
||||
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +48,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
|
||||
// TODO: Implement this with a dashed line (`set_line_dash`), with integer spacing which is continuously adjusted to correct the accumulated error.
|
||||
fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
let grid_color = document.snapping_state.grid.grid_color;
|
||||
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||
return;
|
||||
};
|
||||
@@ -80,16 +76,13 @@ fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_conte
|
||||
let x_per_dot = (end.x - start.x) / total_dots;
|
||||
for dot_index in 0..=total_dots as usize {
|
||||
let exact_x = x_per_dot * dot_index as f64;
|
||||
overlay_context.pixel(
|
||||
document_to_viewport.transform_point2(DVec2::new(start.x + exact_x, start.y)).round(),
|
||||
Some(&("#".to_string() + &grid_color.rgba_hex())),
|
||||
)
|
||||
overlay_context.pixel(document_to_viewport.transform_point2(DVec2::new(start.x + exact_x, start.y)).round(), Some(&grid_color))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, y_axis_spacing: f64, angle_a: f64, angle_b: f64) {
|
||||
let grid_color = document.snapping_state.grid.grid_color;
|
||||
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
|
||||
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
||||
@@ -112,11 +105,7 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
|
||||
let x_pos = (((min_x - origin.x) / spacing).ceil() + line_index as f64) * spacing + origin.x;
|
||||
let start = DVec2::new(x_pos, min_y);
|
||||
let end = DVec2::new(x_pos, max_y);
|
||||
overlay_context.line(
|
||||
document_to_viewport.transform_point2(start),
|
||||
document_to_viewport.transform_point2(end),
|
||||
Some(&("#".to_string() + &grid_color.rgba_hex())),
|
||||
);
|
||||
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color));
|
||||
}
|
||||
|
||||
for (tan, multiply) in [(tan_a, -1.), (tan_b, 1.)] {
|
||||
@@ -130,17 +119,13 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
|
||||
let y_pos = (((inverse_project(&min_y) - origin.y) / spacing).ceil() + line_index as f64) * spacing + origin.y;
|
||||
let start = DVec2::new(min_x, project(&DVec2::new(min_x, y_pos)));
|
||||
let end = DVec2::new(max_x, project(&DVec2::new(max_x, y_pos)));
|
||||
overlay_context.line(
|
||||
document_to_viewport.transform_point2(start),
|
||||
document_to_viewport.transform_point2(end),
|
||||
Some(&("#".to_string() + &grid_color.rgba_hex())),
|
||||
);
|
||||
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, y_axis_spacing: f64, angle_a: f64, angle_b: f64) {
|
||||
let grid_color = document.snapping_state.grid.grid_color;
|
||||
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
|
||||
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
||||
@@ -180,7 +165,7 @@ fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context
|
||||
overlay_context.dashed_line(
|
||||
document_to_viewport.transform_point2(start),
|
||||
document_to_viewport.transform_point2(end),
|
||||
Some(&("#".to_string() + &grid_color.rgba_hex())),
|
||||
Some(&grid_color),
|
||||
Some(1.),
|
||||
Some((spacing_x / cos_a) * document_to_viewport.matrix2.x_axis.length() - 1.),
|
||||
None,
|
||||
@@ -228,10 +213,8 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
||||
};
|
||||
let update_color = |grid, update: fn(&mut GridSnapping) -> Option<&mut Color>| {
|
||||
update_val::<ColorInput, _>(grid, move |grid, color| {
|
||||
if let FillChoice::Solid(color) = color.value {
|
||||
if let Some(update_color) = update(grid) {
|
||||
*update_color = color;
|
||||
}
|
||||
if let (Some(color), Some(update_color)) = (color.value.as_solid(), update(grid)) {
|
||||
*update_color = color.to_linear_srgb();
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -278,7 +261,7 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
]);
|
||||
color_widgets.push(
|
||||
ColorInput::new(FillChoice::Solid(grid.grid_color))
|
||||
ColorInput::new(FillChoice::Solid(grid.grid_color.to_gamma_srgb()))
|
||||
.tooltip("Grid display color")
|
||||
.allow_none(false)
|
||||
.on_update(update_color(grid, |grid| Some(&mut grid.grid_color)))
|
||||
|
||||
@@ -320,7 +320,7 @@ impl OverlayContext {
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(crate::consts::COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.rgba_hex();
|
||||
.to_rgba_hex_srgb();
|
||||
fill_color.insert(0, '#');
|
||||
let fill_color = Some(fill_color.as_str());
|
||||
self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None);
|
||||
@@ -357,7 +357,10 @@ impl OverlayContext {
|
||||
|
||||
// Hover ring
|
||||
if show_hover_ring {
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.5).rgba_hex();
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.5)
|
||||
.to_rgba_hex_srgb();
|
||||
fill_color.insert(0, '#');
|
||||
|
||||
self.render_context.set_line_width(HOVER_RING_STROKE_WIDTH);
|
||||
|
||||
@@ -217,10 +217,7 @@ impl Default for GridSnapping {
|
||||
Self {
|
||||
origin: DVec2::ZERO,
|
||||
grid_type: Default::default(),
|
||||
grid_color: COLOR_OVERLAY_GRAY
|
||||
.strip_prefix('#')
|
||||
.and_then(Color::from_rgb_str)
|
||||
.expect("Should create Color from prefixed hex string"),
|
||||
grid_color: Color::from_rgb_str(COLOR_OVERLAY_GRAY.strip_prefix('#').unwrap()).unwrap(),
|
||||
dot_display: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,14 +60,14 @@ impl ToolColorOptions {
|
||||
|
||||
pub fn apply_fill(&self, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
|
||||
if let Some(color) = self.active_color() {
|
||||
let fill = graphene_core::vector::style::Fill::solid(color);
|
||||
let fill = graphene_core::vector::style::Fill::solid(color.to_gamma_srgb());
|
||||
responses.add(GraphOperationMessage::FillSet { layer, fill });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_stroke(&self, weight: f64, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
|
||||
if let Some(color) = self.active_color() {
|
||||
let stroke = graphene_core::vector::style::Stroke::new(Some(color), weight);
|
||||
let stroke = graphene_core::vector::style::Stroke::new(Some(color.to_gamma_srgb()), weight);
|
||||
responses.add(GraphOperationMessage::StrokeSet { layer, stroke });
|
||||
}
|
||||
}
|
||||
@@ -111,9 +111,11 @@ impl ToolColorOptions {
|
||||
widgets.push(radio);
|
||||
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
|
||||
|
||||
let color_button = ColorInput::new(FillChoice::from_optional_color(self.active_color()))
|
||||
.allow_none(color_allow_none)
|
||||
.on_update(color_callback);
|
||||
let fill_choice = match self.active_color() {
|
||||
Some(color) => FillChoice::Solid(color.to_gamma_srgb()),
|
||||
None => FillChoice::None,
|
||||
};
|
||||
let color_button = ColorInput::new(fill_choice).allow_none(color_allow_none).on_update(color_callback);
|
||||
widgets.push(color_button.widget_holder());
|
||||
|
||||
widgets
|
||||
|
||||
@@ -275,7 +275,7 @@ pub fn get_fill_color(layer: LayerNodeIdentifier, network_interface: &NodeNetwor
|
||||
let TaggedValue::Fill(graphene_std::vector::style::Fill::Solid(color)) = inputs.get(fill_index)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
Some(*color)
|
||||
Some(color.to_linear_srgb())
|
||||
}
|
||||
|
||||
/// Get the current blend mode of a layer from the closest Blend Mode node
|
||||
|
||||
@@ -155,7 +155,7 @@ impl LayoutHolder for BrushTool {
|
||||
false,
|
||||
|_| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::ColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
|
||||
|
||||
@@ -91,7 +91,7 @@ impl LayoutHolder for EllipseTool {
|
||||
true,
|
||||
|_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
);
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
@@ -101,7 +101,7 @@ impl LayoutHolder for EllipseTool {
|
||||
true,
|
||||
|_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
@@ -159,8 +159,8 @@ fn disable_cursor_preview(responses: &mut VecDeque<Message>) {
|
||||
fn update_cursor_preview(responses: &mut VecDeque<Message>, input: &InputPreprocessorMessageHandler, global_tool_data: &DocumentToolData, set_color_choice: Option<String>) {
|
||||
responses.add(FrontendMessage::UpdateEyedropperSamplingState {
|
||||
mouse_position: Some(input.mouse.position.into()),
|
||||
primary_color: "#".to_string() + global_tool_data.primary_color.rgb_hex().as_str(),
|
||||
secondary_color: "#".to_string() + global_tool_data.secondary_color.rgb_hex().as_str(),
|
||||
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(),
|
||||
set_color_choice,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ impl Fsm for FillToolFsmState {
|
||||
return self;
|
||||
}
|
||||
let fill = match color_event {
|
||||
FillToolMessage::FillPrimaryColor => Fill::Solid(global_tool_data.primary_color),
|
||||
FillToolMessage::FillSecondaryColor => Fill::Solid(global_tool_data.secondary_color),
|
||||
FillToolMessage::FillPrimaryColor => Fill::Solid(global_tool_data.primary_color.to_gamma_srgb()),
|
||||
FillToolMessage::FillSecondaryColor => Fill::Solid(global_tool_data.secondary_color.to_gamma_srgb()),
|
||||
_ => return self,
|
||||
};
|
||||
|
||||
@@ -167,7 +167,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], Fill::Solid(Color::GREEN));
|
||||
assert_eq!(fills[0].as_solid().unwrap().to_rgba8_srgb(), Color::GREEN.to_rgba8_srgb());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -180,6 +180,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], Fill::Solid(color));
|
||||
assert_eq!(fills[0].as_solid().unwrap().to_rgba8_srgb(), color.to_rgba8_srgb());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ impl LayoutHolder for FreehandTool {
|
||||
true,
|
||||
|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
);
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
@@ -107,7 +107,7 @@ impl LayoutHolder for FreehandTool {
|
||||
true,
|
||||
|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
@@ -184,11 +184,11 @@ impl SelectedGradient {
|
||||
|
||||
// Should not go off end but can swap
|
||||
let clamped = new_pos.clamp(0., 1.);
|
||||
self.gradient.stops.0[s].0 = clamped;
|
||||
let new_pos = self.gradient.stops.0[s];
|
||||
self.gradient.stops.get_mut(s).unwrap().0 = clamped;
|
||||
let new_pos = self.gradient.stops[s];
|
||||
|
||||
self.gradient.stops.0.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
||||
self.dragging = GradientDragTarget::Step(self.gradient.stops.0.iter().position(|x| *x == new_pos).unwrap());
|
||||
self.gradient.stops.sort();
|
||||
self.dragging = GradientDragTarget::Step(self.gradient.stops.iter().position(|x| *x == new_pos).unwrap());
|
||||
}
|
||||
}
|
||||
self.render_gradient(responses);
|
||||
@@ -259,7 +259,7 @@ impl Fsm for GradientToolFsmState {
|
||||
overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start), None);
|
||||
overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End), None);
|
||||
|
||||
for (index, (position, _)) in stops.0.into_iter().enumerate() {
|
||||
for (index, (position, _)) in stops.into_iter().enumerate() {
|
||||
if position.abs() < f64::EPSILON * 1000. || (1. - position).abs() < f64::EPSILON * 1000. {
|
||||
continue;
|
||||
}
|
||||
@@ -276,7 +276,7 @@ impl Fsm for GradientToolFsmState {
|
||||
};
|
||||
|
||||
// Skip if invalid gradient
|
||||
if selected_gradient.gradient.stops.0.len() < 2 {
|
||||
if selected_gradient.gradient.stops.len() < 2 {
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -284,25 +284,31 @@ impl Fsm for GradientToolFsmState {
|
||||
|
||||
// Remove the selected point
|
||||
match selected_gradient.dragging {
|
||||
GradientDragTarget::Start => selected_gradient.gradient.stops.0.remove(0),
|
||||
GradientDragTarget::End => selected_gradient.gradient.stops.0.pop().unwrap(),
|
||||
GradientDragTarget::Step(index) => selected_gradient.gradient.stops.0.remove(index),
|
||||
GradientDragTarget::Start => {
|
||||
selected_gradient.gradient.stops.remove(0);
|
||||
}
|
||||
GradientDragTarget::End => {
|
||||
let _ = selected_gradient.gradient.stops.pop();
|
||||
}
|
||||
GradientDragTarget::Step(index) => {
|
||||
selected_gradient.gradient.stops.remove(index);
|
||||
}
|
||||
};
|
||||
|
||||
// The gradient has only one point and so should become a fill
|
||||
if selected_gradient.gradient.stops.0.len() == 1 {
|
||||
if selected_gradient.gradient.stops.len() == 1 {
|
||||
if let Some(layer) = selected_gradient.layer {
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer,
|
||||
fill: Fill::Solid(selected_gradient.gradient.stops.0[0].1),
|
||||
fill: Fill::Solid(selected_gradient.gradient.stops[0].1),
|
||||
});
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Find the minimum and maximum positions
|
||||
let min_position = selected_gradient.gradient.stops.0.iter().map(|(pos, _)| *pos).reduce(f64::min).expect("No min");
|
||||
let max_position = selected_gradient.gradient.stops.0.iter().map(|(pos, _)| *pos).reduce(f64::max).expect("No max");
|
||||
let min_position = selected_gradient.gradient.stops.iter().map(|(pos, _)| *pos).reduce(f64::min).expect("No min");
|
||||
let max_position = selected_gradient.gradient.stops.iter().map(|(pos, _)| *pos).reduce(f64::max).expect("No max");
|
||||
|
||||
// Recompute the start and end position of the gradient (in viewport transform)
|
||||
let transform = selected_gradient.transform;
|
||||
@@ -312,7 +318,7 @@ impl Fsm for GradientToolFsmState {
|
||||
selected_gradient.gradient.end = transform.inverse().transform_point2(new_end);
|
||||
|
||||
// Remap the positions
|
||||
for (position, _) in selected_gradient.gradient.stops.0.iter_mut() {
|
||||
for (position, _) in selected_gradient.gradient.stops.iter_mut() {
|
||||
*position = (*position - min_position) / (max_position - min_position);
|
||||
}
|
||||
|
||||
@@ -365,7 +371,7 @@ impl Fsm for GradientToolFsmState {
|
||||
let Some(gradient) = get_gradient(layer, &document.network_interface) else { continue };
|
||||
let transform = gradient_space_transform(layer, document);
|
||||
// Check for dragging step
|
||||
for (index, (pos, _)) in gradient.stops.0.iter().enumerate() {
|
||||
for (index, (pos, _)) in gradient.stops.iter().enumerate() {
|
||||
let pos = transform.transform_point2(gradient.start.lerp(gradient.end, *pos));
|
||||
if pos.distance_squared(mouse) < tolerance {
|
||||
dragging = true;
|
||||
|
||||
@@ -86,7 +86,7 @@ impl LayoutHolder for LineTool {
|
||||
true,
|
||||
|_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
);
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
@@ -933,7 +933,7 @@ impl Fsm for PathToolFsmState {
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(crate::consts::COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.rgba_hex();
|
||||
.to_rgba_hex_srgb();
|
||||
fill_color.insert(0, '#');
|
||||
let fill_color = Some(fill_color.as_str());
|
||||
|
||||
@@ -961,7 +961,10 @@ impl Fsm for PathToolFsmState {
|
||||
let origin = tool_data.drag_start_pos;
|
||||
let viewport_diagonal = input.viewport_bounds.size().length();
|
||||
|
||||
let mut faded_blue = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).rgba_hex();
|
||||
let mut faded_blue = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.25)
|
||||
.to_rgba_hex_srgb();
|
||||
faded_blue.insert(0, '#');
|
||||
let other = faded_blue.as_str();
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ impl LayoutHolder for PenTool {
|
||||
true,
|
||||
|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
);
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
@@ -155,7 +155,7 @@ impl LayoutHolder for PenTool {
|
||||
true,
|
||||
|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
|
||||
@@ -135,7 +135,7 @@ impl LayoutHolder for PolygonTool {
|
||||
true,
|
||||
|_| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::FillColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
@@ -145,7 +145,7 @@ impl LayoutHolder for PolygonTool {
|
||||
true,
|
||||
|_| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::StrokeColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::StrokeColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
@@ -79,7 +79,7 @@ impl LayoutHolder for RectangleTool {
|
||||
true,
|
||||
|_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
);
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
@@ -89,7 +89,7 @@ impl LayoutHolder for RectangleTool {
|
||||
true,
|
||||
|_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
@@ -692,7 +692,7 @@ impl Fsm for SelectToolFsmState {
|
||||
let color = if !hover {
|
||||
color
|
||||
} else {
|
||||
let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).rgba_hex();
|
||||
let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
|
||||
&format!("#{}", color_string)
|
||||
};
|
||||
let line_center = tool_data.line_center;
|
||||
@@ -707,7 +707,10 @@ impl Fsm for SelectToolFsmState {
|
||||
let angle = -mouse_position.angle_to(DVec2::X);
|
||||
let snapped_angle = (angle / snap_resolution).round() * snap_resolution;
|
||||
|
||||
let mut other = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).rgba_hex();
|
||||
let mut other = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.25)
|
||||
.to_rgba_hex_srgb();
|
||||
other.insert(0, '#');
|
||||
let other = other.as_str();
|
||||
|
||||
@@ -758,7 +761,7 @@ impl Fsm for SelectToolFsmState {
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(crate::consts::COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.rgba_hex();
|
||||
.to_rgba_hex_srgb();
|
||||
fill_color.insert(0, '#');
|
||||
let fill_color = Some(fill_color.as_str());
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ impl LayoutHolder for SplineTool {
|
||||
true,
|
||||
|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
);
|
||||
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
@@ -113,7 +113,7 @@ impl LayoutHolder for SplineTool {
|
||||
true,
|
||||
|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
@@ -162,7 +162,7 @@ impl LayoutHolder for TextTool {
|
||||
true,
|
||||
|_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(None)).into(),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
|color: &ColorInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(color.value.as_solid())).into(),
|
||||
|color: &ColorInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(),
|
||||
));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
@@ -378,7 +378,11 @@ impl TextToolData {
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer: self.layer,
|
||||
fill: if editing_text.color.is_some() { Fill::Solid(editing_text.color.unwrap()) } else { Fill::None },
|
||||
fill: if editing_text.color.is_some() {
|
||||
Fill::Solid(editing_text.color.unwrap().to_gamma_srgb())
|
||||
} else {
|
||||
Fill::None
|
||||
},
|
||||
});
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer: self.layer,
|
||||
@@ -450,7 +454,7 @@ impl Fsm for TextToolFsmState {
|
||||
let fill_color = graphene_std::Color::from_rgb_str(crate::consts::COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.rgba_hex();
|
||||
.to_rgba_hex_srgb();
|
||||
|
||||
let ToolMessage::Text(event) = event else { return self };
|
||||
match (self, event) {
|
||||
|
||||
@@ -401,7 +401,6 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: Fix handle snap to anchor issue, see <https://github.com/GraphiteEditor/Graphite/issues/2451>
|
||||
let handle_length = point.as_handle().map(|handle| handle.length(&vector_data));
|
||||
|
||||
if handle_length == Some(0.) {
|
||||
|
||||
@@ -116,7 +116,7 @@ impl DocumentToolData {
|
||||
pub fn update_working_colors(&self, responses: &mut VecDeque<Message>) {
|
||||
let layout = WidgetLayout::new(vec![
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![WorkingColorsInput::new(self.primary_color, self.secondary_color).widget_holder()],
|
||||
widgets: vec![WorkingColorsInput::new(self.primary_color.to_gamma_srgb(), self.secondary_color.to_gamma_srgb()).widget_holder()],
|
||||
},
|
||||
LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
|
||||
Reference in New Issue
Block a user