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>
|
@ -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()));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 271 B After Width: | Height: | Size: 271 B |
3
frontend/assets/icon-16px-solid/edit.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="M15.2,2.3l-1.6-1.6C13.1,0.3,12.5,0,11.8,0c-0.7,0-1.4,0.3-1.9,0.8L2.2,8.5c-0.6,0.6-0.9,1.3-1.1,2L0,16l5.5-1.1c0.8-0.2,1.5-0.5,2-1.1l7.7-7.7C16.3,5.1,16.3,3.4,15.2,2.3z M6.6,12.9c-0.4,0.4-0.8,0.6-1.4,0.7l-0.1,0l-2.7-2.7l0-0.1c0.1-0.5,0.4-1,0.7-1.4l5.3-5.3l3.4,3.4L6.6,12.9z M14.3,5.2l-1.4,1.4L9.4,3.1l1.4-1.4c0.3-0.3,0.6-0.4,0.9-0.4s0.7,0.1,0.9,0.4l1.6,1.6C14.8,3.8,14.8,4.7,14.3,5.2z" />
|
||||
</svg>
|
After Width: | Height: | Size: 465 B |
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="M13,5c0-2.8-2.2-5-5-5S3,2.2,3,5c0,1.1,0.4,2.2,1,3c-0.6,0.8-1,1.9-1,3c0,2.8,2.2,5,5,5s5-2.2,5-5c0-1.1-0.4-2.2-1-3C12.6,7.2,13,6.1,13,5z M8,1.4c2,0,3.6,1.6,3.6,3.6S10,8.6,8,8.6S4.4,7,4.4,5S6,1.4,8,1.4z M2,5L0,7V3L2,5z M16,3v4l-2-2L16,3z" />
|
||||
</svg>
|
After Width: | Height: | Size: 317 B |
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="M13,5c0-2.8-2.2-5-5-5S3,2.2,3,5c0,1.1,0.4,2.2,1,3c-0.6,0.8-1,1.9-1,3c0,2.8,2.2,5,5,5s5-2.2,5-5c0-1.1-0.4-2.2-1-3C12.6,7.2,13,6.1,13,5z M8,1.4c2,0,3.6,1.6,3.6,3.6S10,8.6,8,8.6S4.4,7,4.4,5S6,1.4,8,1.4z M2,11l-2,2V9L2,11zM16,9v4l-2-2L16,9z" />
|
||||
</svg>
|
After Width: | Height: | Size: 319 B |
|
@ -56,6 +56,7 @@
|
|||
border: 1px solid var(--color-5-dullgray);
|
||||
border-radius: 2px;
|
||||
padding: 1px;
|
||||
min-width: 80px;
|
||||
|
||||
> button {
|
||||
position: relative;
|
||||
|
|
|
@ -12,7 +12,7 @@ import Add from "@graphite-frontend/assets/icon-12px-solid/add.svg";
|
|||
import Checkmark from "@graphite-frontend/assets/icon-12px-solid/checkmark.svg";
|
||||
import CloseX from "@graphite-frontend/assets/icon-12px-solid/close-x.svg";
|
||||
import DropdownArrow from "@graphite-frontend/assets/icon-12px-solid/dropdown-arrow.svg";
|
||||
import Edit from "@graphite-frontend/assets/icon-12px-solid/edit.svg";
|
||||
import Edit12px from "@graphite-frontend/assets/icon-12px-solid/edit-12px.svg";
|
||||
import Empty12px from "@graphite-frontend/assets/icon-12px-solid/empty-12px.svg";
|
||||
import FullscreenEnter from "@graphite-frontend/assets/icon-12px-solid/fullscreen-enter.svg";
|
||||
import FullscreenExit from "@graphite-frontend/assets/icon-12px-solid/fullscreen-exit.svg";
|
||||
|
@ -33,7 +33,6 @@ import KeyboardTab from "@graphite-frontend/assets/icon-12px-solid/keyboard-tab.
|
|||
import Link from "@graphite-frontend/assets/icon-12px-solid/link.svg";
|
||||
import Overlays from "@graphite-frontend/assets/icon-12px-solid/overlays.svg";
|
||||
import Remove from "@graphite-frontend/assets/icon-12px-solid/remove.svg";
|
||||
import ResetColors from "@graphite-frontend/assets/icon-12px-solid/reset-colors.svg";
|
||||
import Snapping from "@graphite-frontend/assets/icon-12px-solid/snapping.svg";
|
||||
import Swap from "@graphite-frontend/assets/icon-12px-solid/swap.svg";
|
||||
import VerticalEllipsis from "@graphite-frontend/assets/icon-12px-solid/vertical-ellipsis.svg";
|
||||
|
@ -42,13 +41,14 @@ import WindowButtonWinClose from "@graphite-frontend/assets/icon-12px-solid/wind
|
|||
import WindowButtonWinMaximize from "@graphite-frontend/assets/icon-12px-solid/window-button-win-maximize.svg";
|
||||
import WindowButtonWinMinimize from "@graphite-frontend/assets/icon-12px-solid/window-button-win-minimize.svg";
|
||||
import WindowButtonWinRestoreDown from "@graphite-frontend/assets/icon-12px-solid/window-button-win-restore-down.svg";
|
||||
import WorkingColors from "@graphite-frontend/assets/icon-12px-solid/working-colors.svg";
|
||||
|
||||
const SOLID_12PX = {
|
||||
Add: { svg: Add, size: 12 },
|
||||
Checkmark: { svg: Checkmark, size: 12 },
|
||||
CloseX: { svg: CloseX, size: 12 },
|
||||
DropdownArrow: { svg: DropdownArrow, size: 12 },
|
||||
Edit: { svg: Edit, size: 12 },
|
||||
Edit12px: { svg: Edit12px, size: 12 },
|
||||
Empty12px: { svg: Empty12px, size: 12 },
|
||||
FullscreenEnter: { svg: FullscreenEnter, size: 12 },
|
||||
FullscreenExit: { svg: FullscreenExit, size: 12 },
|
||||
|
@ -69,7 +69,6 @@ const SOLID_12PX = {
|
|||
Link: { svg: Link, size: 12 },
|
||||
Overlays: { svg: Overlays, size: 12 },
|
||||
Remove: { svg: Remove, size: 12 },
|
||||
ResetColors: { svg: ResetColors, size: 12 },
|
||||
Snapping: { svg: Snapping, size: 12 },
|
||||
Swap: { svg: Swap, size: 12 },
|
||||
VerticalEllipsis: { svg: VerticalEllipsis, size: 12 },
|
||||
|
@ -78,6 +77,7 @@ const SOLID_12PX = {
|
|||
WindowButtonWinMaximize: { svg: WindowButtonWinMaximize, size: 12 },
|
||||
WindowButtonWinMinimize: { svg: WindowButtonWinMinimize, size: 12 },
|
||||
WindowButtonWinRestoreDown: { svg: WindowButtonWinRestoreDown, size: 12 },
|
||||
WorkingColors: { svg: WorkingColors, size: 12 },
|
||||
} as const;
|
||||
|
||||
// 16px Solid
|
||||
|
@ -95,6 +95,7 @@ import BooleanUnion from "@graphite-frontend/assets/icon-16px-solid/boolean-unio
|
|||
import CheckboxChecked from "@graphite-frontend/assets/icon-16px-solid/checkbox-checked.svg";
|
||||
import CheckboxUnchecked from "@graphite-frontend/assets/icon-16px-solid/checkbox-unchecked.svg";
|
||||
import Copy from "@graphite-frontend/assets/icon-16px-solid/copy.svg";
|
||||
import Edit from "@graphite-frontend/assets/icon-16px-solid/edit.svg";
|
||||
import Eyedropper from "@graphite-frontend/assets/icon-16px-solid/eyedropper.svg";
|
||||
import EyeHidden from "@graphite-frontend/assets/icon-16px-solid/eye-hidden.svg";
|
||||
import EyeVisible from "@graphite-frontend/assets/icon-16px-solid/eye-visible.svg";
|
||||
|
@ -133,6 +134,8 @@ import ViewModePixels from "@graphite-frontend/assets/icon-16px-solid/view-mode-
|
|||
import ViewportDesignMode from "@graphite-frontend/assets/icon-16px-solid/viewport-design-mode.svg";
|
||||
import ViewportGuideMode from "@graphite-frontend/assets/icon-16px-solid/viewport-guide-mode.svg";
|
||||
import ViewportSelectMode from "@graphite-frontend/assets/icon-16px-solid/viewport-select-mode.svg";
|
||||
import WorkingColorsPrimary from "@graphite-frontend/assets/icon-16px-solid/working-colors-primary.svg";
|
||||
import WorkingColorsSecondary from "@graphite-frontend/assets/icon-16px-solid/working-colors-secondary.svg";
|
||||
import ZoomIn from "@graphite-frontend/assets/icon-16px-solid/zoom-in.svg";
|
||||
import ZoomOut from "@graphite-frontend/assets/icon-16px-solid/zoom-out.svg";
|
||||
import ZoomReset from "@graphite-frontend/assets/icon-16px-solid/zoom-reset.svg";
|
||||
|
@ -152,6 +155,7 @@ const SOLID_16PX = {
|
|||
CheckboxChecked: { svg: CheckboxChecked, size: 16 },
|
||||
CheckboxUnchecked: { svg: CheckboxUnchecked, size: 16 },
|
||||
Copy: { svg: Copy, size: 16 },
|
||||
Edit: { svg: Edit, size: 16 },
|
||||
Eyedropper: { svg: Eyedropper, size: 16 },
|
||||
EyeHidden: { svg: EyeHidden, size: 16 },
|
||||
EyeVisible: { svg: EyeVisible, size: 16 },
|
||||
|
@ -190,6 +194,8 @@ const SOLID_16PX = {
|
|||
ViewportDesignMode: { svg: ViewportDesignMode, size: 16 },
|
||||
ViewportGuideMode: { svg: ViewportGuideMode, size: 16 },
|
||||
ViewportSelectMode: { svg: ViewportSelectMode, size: 16 },
|
||||
WorkingColorsPrimary: { svg: WorkingColorsPrimary, size: 16 },
|
||||
WorkingColorsSecondary: { svg: WorkingColorsSecondary, size: 16 },
|
||||
ZoomIn: { svg: ZoomIn, size: 16 },
|
||||
ZoomOut: { svg: ZoomOut, size: 16 },
|
||||
ZoomReset: { svg: ZoomReset, size: 16 },
|
||||
|
|
|
@ -281,9 +281,9 @@ impl core::hash::Hash for Stroke {
|
|||
}
|
||||
|
||||
impl Stroke {
|
||||
pub const fn new(color: Color, weight: f64) -> Self {
|
||||
pub const fn new(color: Option<Color>, weight: f64) -> Self {
|
||||
Self {
|
||||
color: Some(color),
|
||||
color,
|
||||
weight,
|
||||
dash_lengths: Vec::new(),
|
||||
dash_offset: 0.,
|
||||
|
@ -439,7 +439,7 @@ impl PathStyle {
|
|||
/// ```
|
||||
/// # use graphene_core::vector::style::{Fill, Stroke, PathStyle};
|
||||
/// # use graphene_core::raster::color::Color;
|
||||
/// let stroke = Stroke::new(Color::GREEN, 42.);
|
||||
/// let stroke = Stroke::new(Some(Color::GREEN), 42.);
|
||||
/// let style = PathStyle::new(Some(stroke.clone()), Fill::None);
|
||||
///
|
||||
/// assert_eq!(style.stroke(), Some(stroke));
|
||||
|
@ -477,7 +477,7 @@ impl PathStyle {
|
|||
///
|
||||
/// assert_eq!(style.stroke(), None);
|
||||
///
|
||||
/// let stroke = Stroke::new(Color::GREEN, 42.);
|
||||
/// let stroke = Stroke::new(Some(Color::GREEN), 42.);
|
||||
/// style.set_stroke(stroke.clone());
|
||||
///
|
||||
/// assert_eq!(style.stroke(), Some(stroke));
|
||||
|
@ -510,7 +510,7 @@ impl PathStyle {
|
|||
/// ```
|
||||
/// # use graphene_core::vector::style::{Fill, Stroke, PathStyle};
|
||||
/// # use graphene_core::raster::color::Color;
|
||||
/// let mut style = PathStyle::new(Some(Stroke::new(Color::GREEN, 42.)), Fill::None);
|
||||
/// let mut style = PathStyle::new(Some(Stroke::new(Some(Color::GREEN), 42.)), Fill::None);
|
||||
///
|
||||
/// assert!(style.stroke().is_some());
|
||||
///
|
||||
|
@ -528,7 +528,7 @@ impl PathStyle {
|
|||
(_, fill) => fill.render(svg_defs, multiplied_transform, bounds, transformed_bounds),
|
||||
};
|
||||
let stroke_attribute = match (view_mode, &self.stroke) {
|
||||
(ViewMode::Outline, _) => Stroke::new(LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT).render(),
|
||||
(ViewMode::Outline, _) => Stroke::new(Some(LAYER_OUTLINE_STROKE_COLOR), LAYER_OUTLINE_STROKE_WEIGHT).render(),
|
||||
(_, Some(stroke)) => stroke.render(),
|
||||
(_, None) => String::new(),
|
||||
};
|
||||
|
|
|
@ -23,7 +23,7 @@ impl VectorData {
|
|||
Self {
|
||||
subpaths: Vec::new(),
|
||||
transform: DAffine2::IDENTITY,
|
||||
style: PathStyle::new(Some(Stroke::new(Color::BLACK, 0.)), super::style::Fill::None),
|
||||
style: PathStyle::new(Some(Stroke::new(Some(Color::BLACK), 0.)), super::style::Fill::None),
|
||||
mirror_angle: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
|