Add fill and stroke color choices to the Pen tool options bar (#1188)

* Add basic layout - WIP

* Add color input min-width -> 80px

* First pass implementation - WIP

* Allow fill to be None

* Fix null Fill color

* refactor fill and stroke options into struct

* toolbar progress - WIP

* Switch is_working_color bool to PenColorType enum

* Add todo

* Add WorkingColorChanged event

* remove unused import

* Add WorkingColor[Primary/Secondary] icons

* Allow new strokes to have no color

* Set to base color when X is pressed (as per req)

* Improve icons for new UI layout design

* Add radio buttons

* Fix menu bar Edit12px -> Edit

* Add tooltips to radio buttons

* Make the color selector only on custom

* Fix edit icon correctly this time (whoops)

* Fix working colors icons

* Changes to improve the UX

* Remove lines obviated by Default::default()

* Make Eyedropper tool use working_color_changed event

* Fix tests

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Chase
2023-05-03 02:08:35 +08:00
committed by Keavon Chambers
co-authored by Keavon Chambers
parent cef1cf7587
commit 1aaf2a521b
35 changed files with 275 additions and 71 deletions
@@ -8,4 +8,5 @@ pub enum BroadcastEvent {
DocumentIsDirty,
ToolAbort,
SelectionChanged,
WorkingColorChanged,
}
@@ -1193,7 +1193,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
.widget_holder(),
WidgetHolder::unrelated_separator(),
CheckboxInput::new(!dimensions_is_auto || transform_not_connected)
.icon("Edit")
.icon("Edit12px")
.tooltip({
let message = "Set a custom resolution instead of using the input's dimensions (rounded to the nearest 64)";
let manual_message = "Set a custom resolution instead of using the input's dimensions (rounded to the nearest 64).\n\
@@ -159,7 +159,7 @@ impl OverlayRenderer {
let operation = Operation::AddShape {
path: layer_path.clone(),
subpath,
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, PATH_OUTLINE_WEIGHT)), Fill::None),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), PATH_OUTLINE_WEIGHT)), Fill::None),
insert_index: -1,
transform: DAffine2::IDENTITY.to_cols_array(),
};
@@ -174,7 +174,7 @@ impl OverlayRenderer {
let operation = Operation::AddRect {
path: layer_path.clone(),
transform: DAffine2::IDENTITY.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 2.0)), Fill::solid(Color::WHITE)),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 2.0)), Fill::solid(Color::WHITE)),
insert_index: -1,
};
responses.add(DocumentMessage::Overlays(operation.into()));
@@ -187,7 +187,7 @@ impl OverlayRenderer {
let operation = Operation::AddEllipse {
path: layer_path.clone(),
transform: DAffine2::IDENTITY.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 2.0)), Fill::solid(Color::WHITE)),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 2.0)), Fill::solid(Color::WHITE)),
insert_index: -1,
};
responses.add(DocumentMessage::Overlays(operation.into()));
@@ -212,7 +212,7 @@ impl OverlayRenderer {
let operation = Operation::AddLine {
path: layer_path.clone(),
transform: DAffine2::IDENTITY.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), Fill::None),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 1.0)), Fill::None),
insert_index: -1,
};
responses.add_front(DocumentMessage::Overlays(operation.into()));
@@ -327,8 +327,8 @@ impl OverlayRenderer {
/// Sets the overlay style for this point.
fn style_overlays(state: &SelectedShapeState, layer_path: &[LayerId], manipulator_group: &GraphiteManipulatorGroup, overlays: &ManipulatorGroupOverlays, responses: &mut VecDeque<Message>) {
// TODO Move the style definitions out of the Subpath, should be looked up from a stylesheet or similar
let selected_style = style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, POINT_STROKE_WEIGHT + 1.0)), Fill::solid(COLOR_ACCENT));
let deselected_style = style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, POINT_STROKE_WEIGHT)), Fill::solid(Color::WHITE));
let selected_style = style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), POINT_STROKE_WEIGHT + 1.0)), Fill::solid(COLOR_ACCENT));
let deselected_style = style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), POINT_STROKE_WEIGHT)), Fill::solid(Color::WHITE));
let selected_shape_state = state.get(layer_path);
// Update if the manipulator points are shown as selected
// Here the index is important, even though overlays[..] has five elements we only care about the first three
@@ -46,7 +46,7 @@ impl PathOutline {
let operation = Operation::AddShape {
path: overlay_path.clone(),
subpath: Default::default(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, PATH_OUTLINE_WEIGHT)), Fill::None),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), PATH_OUTLINE_WEIGHT)), Fill::None),
insert_index: -1,
transform: DAffine2::IDENTITY.to_cols_array(),
};
@@ -112,7 +112,7 @@ impl Pivot {
path: layer_paths[0].clone(),
transform: DAffine2::IDENTITY.to_cols_array(),
style: style::PathStyle::new(
Some(style::Stroke::new(COLOR_ACCENT, PIVOT_OUTER_OUTLINE_THICKNESS)),
Some(style::Stroke::new(Some(COLOR_ACCENT), PIVOT_OUTER_OUTLINE_THICKNESS)),
style::Fill::Solid(graphene_core::raster::color::Color::WHITE),
),
insert_index: -1,
@@ -34,7 +34,7 @@ impl SnapOverlays {
Operation::AddLine {
path: layer_path.clone(),
transform,
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), style::Fill::None),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 1.0)), style::Fill::None),
insert_index: -1,
}
} else {
@@ -156,7 +156,7 @@ pub fn add_bounding_box(responses: &mut VecDeque<Message>) -> Vec<LayerId> {
let operation = Operation::AddRect {
path: path.clone(),
transform: DAffine2::ZERO.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), Fill::None),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 1.0)), Fill::None),
insert_index: -1,
};
responses.add(DocumentMessage::Overlays(operation.into()));
@@ -175,7 +175,7 @@ fn add_transform_handles(responses: &mut VecDeque<Message>) -> [Vec<LayerId>; 8]
let operation = Operation::AddRect {
path: current_path.clone(),
transform: DAffine2::ZERO.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 2.0)), Fill::solid(Color::WHITE)),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 2.0)), Fill::solid(Color::WHITE)),
insert_index: -1,
};
responses.add(DocumentMessage::Overlays(operation.into()));
@@ -80,7 +80,7 @@ impl ToolTransition for ArtboardTool {
EventToMessageMap {
document_dirty: Some(ArtboardToolMessage::DocumentIsDirty.into()),
tool_abort: Some(ArtboardToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -164,9 +164,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for BrushTo
impl ToolTransition for BrushTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(BrushToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -73,9 +73,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Ellipse
impl ToolTransition for EllipseTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(EllipseToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -61,9 +61,9 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Eyedrop
impl ToolTransition for EyedropperTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(EyedropperToolMessage::Abort.into()),
selection_changed: None,
working_color_changed: Some(EyedropperToolMessage::PointerMove.into()),
..Default::default()
}
}
}
@@ -59,9 +59,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FillToo
impl ToolTransition for FillTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(FillToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -73,9 +73,8 @@ impl ToolMetadata for FrameTool {
impl ToolTransition for FrameTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(FrameToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -116,9 +116,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Freehan
impl ToolTransition for FreehandTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(FreehandToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -224,6 +223,6 @@ fn add_polyline(data: &FreehandToolData, tool_data: &DocumentToolData, responses
responses.add(GraphOperationMessage::StrokeSet {
layer: layer_path,
stroke: Stroke::new(tool_data.primary_color, data.weight),
stroke: Stroke::new(Some(tool_data.primary_color), data.weight),
});
}
@@ -160,7 +160,7 @@ impl GradientOverlay {
let operation = Operation::AddEllipse {
path: path.clone(),
transform: DAffine2::from_scale_angle_translation(size, 0., translation - size / 2.).to_cols_array(),
style: PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), fill),
style: PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 1.0)), fill),
insert_index: -1,
};
responses.add(DocumentMessage::Overlays(operation.into()));
@@ -179,7 +179,7 @@ impl GradientOverlay {
let operation = Operation::AddLine {
path: path.clone(),
transform,
style: PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), Fill::None),
style: PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 1.0)), Fill::None),
insert_index: -1,
};
responses.add(DocumentMessage::Overlays(operation.into()));
@@ -371,6 +371,7 @@ impl ToolTransition for GradientTool {
document_dirty: Some(GradientToolMessage::DocumentIsDirty.into()),
tool_abort: Some(GradientToolMessage::Abort.into()),
selection_changed: Some(GradientToolMessage::DocumentIsDirty.into()),
..Default::default()
}
}
}
@@ -73,9 +73,8 @@ impl ToolMetadata for ImaginateTool {
impl ToolTransition for ImaginateTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(ImaginateToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -105,9 +105,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for LineToo
impl ToolTransition for LineTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(LineToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -165,7 +164,7 @@ impl Fsm for LineToolFsmState {
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
responses.add(GraphOperationMessage::StrokeSet {
layer: layer_path,
stroke: Stroke::new(global_tool_data.primary_color, tool_options.line_weight),
stroke: Stroke::new(Some(global_tool_data.primary_color), tool_options.line_weight),
});
tool_data.weight = tool_options.line_weight;
@@ -76,9 +76,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Navigat
impl ToolTransition for NavigateTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(NavigateToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -99,6 +99,7 @@ impl ToolTransition for PathTool {
document_dirty: Some(PathToolMessage::DocumentIsDirty.into()),
tool_abort: Some(PathToolMessage::Abort.into()),
selection_changed: Some(PathToolMessage::SelectionChanged.into()),
..Default::default()
}
}
}
@@ -2,6 +2,8 @@ use crate::consts::LINE_ROTATE_SNAP_ANGLE;
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
use crate::messages::layout::utility_types::misc::LayoutTarget;
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, IconButton, RadioEntryData, RadioInput, Separator, SeparatorDirection, SeparatorType, TextLabel, WidgetHolder};
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
use crate::messages::portfolio::document::node_graph::VectorDataModification;
use crate::messages::prelude::*;
@@ -14,7 +16,7 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use document_legacy::LayerId;
use graphene_core::uuid::ManipulatorGroupId;
use graphene_core::vector::style::Stroke;
use graphene_core::vector::style::{Fill, Stroke};
use graphene_core::vector::{ManipulatorPointId, SelectedType};
use graphene_core::Color;
@@ -28,13 +30,53 @@ pub struct PenTool {
options: PenOptions,
}
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
pub enum PenColorType {
Primary,
Secondary,
Custom,
}
pub struct PenColorOptions {
color: Option<Color>,
primary_working_color: Option<Color>,
secondary_working_color: Option<Color>,
color_type: PenColorType,
}
impl PenColorOptions {
pub fn active_color(&self) -> Option<Color> {
match self.color_type {
PenColorType::Custom => self.color,
PenColorType::Primary => self.primary_working_color,
PenColorType::Secondary => self.secondary_working_color,
}
}
}
pub struct PenOptions {
line_weight: f64,
fill: PenColorOptions,
stroke: PenColorOptions,
}
impl Default for PenOptions {
fn default() -> Self {
Self { line_weight: 5. }
Self {
line_weight: 5.,
fill: PenColorOptions {
color: Some(Color::BLACK),
primary_working_color: Some(Color::BLACK),
secondary_working_color: Some(Color::WHITE),
color_type: PenColorType::Primary,
},
stroke: PenColorOptions {
color: None,
primary_working_color: Some(Color::BLACK),
secondary_working_color: Some(Color::WHITE),
color_type: PenColorType::Custom,
},
}
}
}
@@ -49,6 +91,8 @@ pub enum PenToolMessage {
Abort,
#[remain::unsorted]
SelectionChanged,
#[remain::unsorted]
WorkingColorChanged,
// Tool-specific messages
Confirm,
@@ -74,7 +118,13 @@ enum PenToolFsmState {
#[remain::sorted]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
pub enum PenOptionsUpdate {
FillColor(Option<Color>),
FillColorType(PenColorType),
LineWeight(f64),
PrimaryColor(Option<Color>),
SecondaryColor(Option<Color>),
StrokeColor(Option<Color>),
StrokeColorType(PenColorType),
}
impl ToolMetadata for PenTool {
@@ -89,15 +139,102 @@ impl ToolMetadata for PenTool {
}
}
// TODO: Generalize create_fill_widget and create_stroke_widget into one function.
fn create_fill_widget(fill: &PenColorOptions) -> Vec<WidgetHolder> {
let label = TextLabel::new("Fill").widget_holder();
let reset = IconButton::new("CloseX", 12)
.disabled(fill.color.is_none() && fill.color_type == PenColorType::Custom)
.on_update(|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(None)).into())
.tooltip("Clear color")
.widget_holder();
let entries = vec![
("WorkingColorsPrimary", "Primary Working Color", PenColorType::Primary),
("WorkingColorsSecondary", "Secondary Working Color", PenColorType::Secondary),
("Edit", "Custom Color", PenColorType::Custom),
]
.into_iter()
.map(|(icon, tooltip, color_type)| {
RadioEntryData::new("")
.tooltip(tooltip)
.icon(icon)
.on_update(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColorType(color_type.clone())).into())
})
.collect();
let radio = RadioInput::new(entries).selected_index(fill.color_type.clone() as u32).widget_holder();
let color_input = ColorInput::new(fill.active_color())
.on_update(|fill_color| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(fill_color.value)).into())
.widget_holder();
vec![
label,
WidgetHolder::related_separator(),
reset,
WidgetHolder::related_separator(),
radio,
WidgetHolder::related_separator(),
color_input,
]
}
fn create_stroke_widget(stroke: &PenColorOptions) -> Vec<WidgetHolder> {
let label = TextLabel::new("Stroke").widget_holder();
let reset = IconButton::new("CloseX", 12)
.disabled(stroke.color.is_none() && stroke.color_type == PenColorType::Custom)
.on_update(|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(None)).into())
.tooltip("Clear color")
.widget_holder();
let entries = vec![
("WorkingColorsPrimary", "Primary Working Color", PenColorType::Primary),
("WorkingColorsSecondary", "Secondary Working Color", PenColorType::Secondary),
("Edit", "Custom Color", PenColorType::Custom),
]
.into_iter()
.map(|(icon, tooltip, color_type)| {
RadioEntryData::new("")
.tooltip(tooltip)
.icon(icon)
.on_update(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColorType(color_type.clone())).into())
})
.collect();
let radio = RadioInput::new(entries).selected_index(stroke.color_type.clone() as u32).widget_holder();
let color_input = ColorInput::new(stroke.active_color())
.on_update(|stroke_color| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(stroke_color.value)).into())
.widget_holder();
vec![
label,
WidgetHolder::related_separator(),
reset,
WidgetHolder::related_separator(),
radio,
WidgetHolder::related_separator(),
color_input,
]
}
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
NumberInput::new(Some(line_weight))
.unit(" px")
.label("Weight")
.min(0.)
.on_update(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder()
}
impl PropertyHolder for PenTool {
fn properties(&self) -> Layout {
let weight = NumberInput::new(Some(self.options.line_weight))
.unit(" px")
.label("Weight")
.min(0.)
.on_update(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder();
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
let mut widgets = create_fill_widget(&self.options.fill);
widgets.push(Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder());
widgets.append(&mut create_stroke_widget(&self.options.stroke));
widgets.push(WidgetHolder::unrelated_separator());
widgets.push(create_weight_widget(self.options.line_weight));
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
}
}
@@ -106,7 +243,31 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool
if let ToolMessage::Pen(PenToolMessage::UpdateOptions(action)) = message {
match action {
PenOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
PenOptionsUpdate::FillColor(color) => {
self.options.fill.color = color;
self.options.fill.color_type = PenColorType::Custom;
}
PenOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
PenOptionsUpdate::StrokeColor(color) => {
self.options.stroke.color = color;
self.options.stroke.color_type = PenColorType::Custom;
}
PenOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
PenOptionsUpdate::PrimaryColor(color) => {
self.options.stroke.primary_working_color = color;
self.options.fill.primary_working_color = color;
}
PenOptionsUpdate::SecondaryColor(color) => {
self.options.stroke.secondary_working_color = color;
self.options.fill.secondary_working_color = color;
}
}
responses.add(LayoutMessage::SendLayout {
layout: self.properties(),
layout_target: LayoutTarget::ToolOptions,
});
return;
}
@@ -139,6 +300,7 @@ impl ToolTransition for PenTool {
document_dirty: Some(PenToolMessage::DocumentIsDirty.into()),
tool_abort: Some(PenToolMessage::Abort.into()),
selection_changed: Some(PenToolMessage::SelectionChanged.into()),
working_color_changed: Some(PenToolMessage::WorkingColorChanged.into()),
}
}
}
@@ -177,7 +339,16 @@ impl PenToolData {
},
});
}
fn create_new_path(&mut self, document: &DocumentMessageHandler, line_weight: f64, color: Color, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
fn create_new_path(
&mut self,
document: &DocumentMessageHandler,
line_weight: f64,
stroke_color: Option<Color>,
fill_color: Option<Color>,
input: &InputPreprocessorMessageHandler,
responses: &mut VecDeque<Message>,
) {
// Deselect layers because we are now creating a new layer
responses.add(DocumentMessage::DeselectAllLayers);
@@ -192,18 +363,24 @@ impl PenToolData {
// Create the initial shape with a `bez_path` (only contains a moveto initially)
let subpath = bezier_rs::Subpath::new(vec![bezier_rs::ManipulatorGroup::new(start_position, Some(start_position), Some(start_position))], false);
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
responses.add(GraphOperationMessage::FillSet {
layer: layer_path.clone(),
fill: if fill_color.is_some() { Fill::Solid(fill_color.unwrap()) } else { Fill::None },
});
responses.add(GraphOperationMessage::StrokeSet {
layer: layer_path.clone(),
stroke: Stroke::new(color, line_weight),
stroke: Stroke::new(stroke_color, line_weight),
});
self.path = Some(layer_path);
self.from_start = false;
self.subpath_index = 0;
}
// TODO: tooltip / user documentation?
/// If you place the anchor on top of the previous anchor then you break the mirror
///
/// TODO: tooltip / user documentation?
fn check_break(&mut self, document: &DocumentMessageHandler, transform: DAffine2, shape_overlay: &mut OverlayRenderer, responses: &mut VecDeque<Message>) -> Option<()> {
// Get subpath
let layer_path = self.path.as_ref()?;
@@ -523,6 +700,11 @@ impl Fsm for PenToolFsmState {
}
self
}
(_, PenToolMessage::WorkingColorChanged) => {
responses.add(PenToolMessage::UpdateOptions(PenOptionsUpdate::PrimaryColor(Some(global_tool_data.primary_color))));
responses.add(PenToolMessage::UpdateOptions(PenOptionsUpdate::SecondaryColor(Some(global_tool_data.secondary_color))));
self
}
(PenToolFsmState::Ready, PenToolMessage::DragStart) => {
responses.add(DocumentMessage::StartTransaction);
@@ -537,7 +719,14 @@ impl Fsm for PenToolFsmState {
if let Some((layer, subpath_index, from_start)) = should_extend(document, input.mouse.position, crate::consts::SNAP_POINT_TOLERANCE) {
tool_data.extend_subpath(layer, subpath_index, from_start, document, responses);
} else {
tool_data.create_new_path(document, tool_options.line_weight, global_tool_data.primary_color, input, responses);
tool_data.create_new_path(
document,
tool_options.line_weight,
tool_options.stroke.active_color(),
tool_options.fill.active_color(),
input,
responses,
);
}
// Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle
@@ -72,9 +72,8 @@ impl ToolMetadata for RectangleTool {
impl ToolTransition for RectangleTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(RectangleToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -287,6 +287,7 @@ impl ToolTransition for SelectTool {
document_dirty: Some(SelectToolMessage::DocumentIsDirty.into()),
tool_abort: Some(SelectToolMessage::Abort.into()),
selection_changed: Some(SelectToolMessage::SelectionChanged.into()),
..Default::default()
}
}
}
@@ -110,9 +110,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ShapeTo
impl ToolTransition for ShapeTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(ShapeToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -122,9 +122,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SplineT
impl ToolTransition for SplineTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap {
document_dirty: None,
tool_abort: Some(SplineToolMessage::Abort.into()),
selection_changed: None,
..Default::default()
}
}
}
@@ -270,6 +269,6 @@ fn add_spline(tool_data: &SplineToolData, global_tool_data: &DocumentToolData, s
responses.add(GraphOperationMessage::StrokeSet {
layer: layer_path.clone(),
stroke: Stroke::new(global_tool_data.primary_color, tool_data.weight),
stroke: Stroke::new(Some(global_tool_data.primary_color), tool_data.weight),
});
}
@@ -172,6 +172,7 @@ impl ToolTransition for TextTool {
document_dirty: Some(TextToolMessage::DocumentIsDirty.into()),
tool_abort: Some(TextToolMessage::Abort.into()),
selection_changed: Some(TextToolMessage::DocumentIsDirty.into()),
..Default::default()
}
}
}
@@ -370,7 +371,7 @@ fn resize_overlays(overlays: &mut Vec<Vec<LayerId>>, responses: &mut VecDeque<Me
let operation = Operation::AddRect {
path,
transform: DAffine2::ZERO.to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), Fill::None),
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), 1.0)), Fill::None),
insert_index: -1,
};
responses.add(DocumentMessage::Overlays(operation.into()));
+8 -3
View File
@@ -1,6 +1,8 @@
use super::common_functionality::overlay_renderer::OverlayRenderer;
use super::common_functionality::shape_editor::ShapeState;
use super::tool_messages::*;
use crate::messages::broadcast::broadcast_event::BroadcastEvent;
use crate::messages::broadcast::BroadcastMessage;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, LayoutKeysGroup, MouseMotion};
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
@@ -154,7 +156,7 @@ impl DocumentToolData {
})),
WidgetHolder::new(Widget::IconButton(IconButton {
size: 16,
icon: "ResetColors".into(),
icon: "WorkingColors".into(),
tooltip: "Reset".into(),
tooltip_shortcut: action_keys!(ToolMessageDiscriminant::ResetColors),
on_update: WidgetCallback::new(|_| ToolMessage::ResetColors.into()),
@@ -169,15 +171,16 @@ impl DocumentToolData {
layout_target: LayoutTarget::WorkingColors,
});
responses.add(EyedropperToolMessage::PointerMove);
responses.add(BroadcastMessage::TriggerEvent(BroadcastEvent::WorkingColorChanged));
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct EventToMessageMap {
pub document_dirty: Option<ToolMessage>,
pub selection_changed: Option<ToolMessage>,
pub tool_abort: Option<ToolMessage>,
pub working_color_changed: Option<ToolMessage>,
}
pub trait ToolTransition {
@@ -197,6 +200,7 @@ pub trait ToolTransition {
subscribe_message(event_to_tool_map.document_dirty, BroadcastEvent::DocumentIsDirty);
subscribe_message(event_to_tool_map.tool_abort, BroadcastEvent::ToolAbort);
subscribe_message(event_to_tool_map.selection_changed, BroadcastEvent::SelectionChanged);
subscribe_message(event_to_tool_map.working_color_changed, BroadcastEvent::WorkingColorChanged);
}
fn deactivate(&self, responses: &mut VecDeque<Message>) {
@@ -213,6 +217,7 @@ pub trait ToolTransition {
unsubscribe_message(event_to_tool_map.document_dirty, BroadcastEvent::DocumentIsDirty);
unsubscribe_message(event_to_tool_map.tool_abort, BroadcastEvent::ToolAbort);
unsubscribe_message(event_to_tool_map.selection_changed, BroadcastEvent::SelectionChanged);
unsubscribe_message(event_to_tool_map.working_color_changed, BroadcastEvent::WorkingColorChanged);
}
}