Add color choices to the options bar of tools (#1199)
* Generalize PenColorType -> ToolColorType * impl default for ToolColorOptions * Add stroke color option to the freehand tool * Consolidate working color update messages * Update tool working colours when switching tools * Update working colors on tool activation * Add stroke color option to line tool * Add fill color option to freehand tool * Add tool color options to spline tool * Fix freehand tool * Add color options to text * Add tool color/weight options to rectangle * Add tool color/weight options to ellipse * Add tool color/weight options to shape * Fix spline default fill/stroke * Reorder widgets and code cleanup * Add CustomColor icon * Fix warnings * Change color defaults to secondary fill, primary stroke * Fix spacing between brush options number inputs * Add toolbar color option to brush * Implement allowNone on color input widget * Rearrange widget and remove X from brush --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
ab2de96640
commit
7e1b452757
|
|
@ -443,6 +443,9 @@ impl WidgetHolder {
|
|||
pub fn new(widget: Widget) -> Self {
|
||||
Self { widget_id: generate_uuid(), widget }
|
||||
}
|
||||
pub fn section_separator() -> Self {
|
||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder()
|
||||
}
|
||||
pub fn unrelated_separator() -> Self {
|
||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Unrelated).widget_holder()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,11 +53,9 @@ pub struct ColorInput {
|
|||
// #[serde(rename = "allowTransparency")]
|
||||
// #[derivative(Default(value = "false"))]
|
||||
// pub allow_transparency: bool,
|
||||
|
||||
// TODO: Implement
|
||||
// #[serde(rename = "allowNone")]
|
||||
// #[derivative(Default(value = "false"))]
|
||||
// pub allow_none: bool,
|
||||
#[serde(rename = "allowNone")]
|
||||
#[derivative(Default(value = "true"))]
|
||||
pub allow_none: bool,
|
||||
|
||||
// pub disabled: bool,
|
||||
pub tooltip: String,
|
||||
|
|
|
|||
|
|
@ -588,7 +588,7 @@ pub fn blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _con
|
|||
}
|
||||
|
||||
pub fn brush_node_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let color = color_widget(document_node, node_id, 7, "Color", ColorInput::default(), true);
|
||||
let color = color_widget(document_node, node_id, 7, "Color", ColorInput::default().allow_none(false), true);
|
||||
|
||||
let size = number_widget(document_node, node_id, 4, "Diameter", NumberInput::default().min(1.).max(100.).unit(" px"), true);
|
||||
let hardness = number_widget(document_node, node_id, 5, "Hardness", NumberInput::default().min(0.).max(100.).unit("%"), true);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
use crate::messages::layout::utility_types::layout_widget::WidgetCallback;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, IconButton, RadioEntryData, RadioInput, TextLabel, WidgetHolder};
|
||||
|
||||
use graphene_core::Color;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum ToolColorType {
|
||||
Primary,
|
||||
Secondary,
|
||||
Custom,
|
||||
}
|
||||
|
||||
pub struct ToolColorOptions {
|
||||
pub custom_color: Option<Color>,
|
||||
pub primary_working_color: Option<Color>,
|
||||
pub secondary_working_color: Option<Color>,
|
||||
pub color_type: ToolColorType,
|
||||
}
|
||||
|
||||
impl Default for ToolColorOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
color_type: ToolColorType::Primary,
|
||||
custom_color: Some(Color::BLACK),
|
||||
primary_working_color: Some(Color::BLACK),
|
||||
secondary_working_color: Some(Color::WHITE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolColorOptions {
|
||||
pub fn new_primary() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn new_secondary() -> Self {
|
||||
Self {
|
||||
color_type: ToolColorType::Secondary,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_none() -> Self {
|
||||
Self {
|
||||
color_type: ToolColorType::Custom,
|
||||
custom_color: None,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_color(&self) -> Option<Color> {
|
||||
match self.color_type {
|
||||
ToolColorType::Custom => self.custom_color,
|
||||
ToolColorType::Primary => self.primary_working_color,
|
||||
ToolColorType::Secondary => self.secondary_working_color,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_widgets(
|
||||
&self,
|
||||
label_text: impl Into<String>,
|
||||
color_allow_none: bool,
|
||||
reset_callback: WidgetCallback<IconButton>,
|
||||
radio_callback: fn(ToolColorType) -> WidgetCallback<()>,
|
||||
color_callback: WidgetCallback<ColorInput>,
|
||||
) -> Vec<WidgetHolder> {
|
||||
let mut widgets = vec![TextLabel::new(label_text).widget_holder()];
|
||||
|
||||
if !color_allow_none {
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
} else {
|
||||
let mut reset = IconButton::new("CloseX", 12)
|
||||
.disabled(self.custom_color.is_none() && self.color_type == ToolColorType::Custom)
|
||||
.tooltip("Clear Color");
|
||||
reset.on_update = reset_callback;
|
||||
|
||||
widgets.push(WidgetHolder::related_separator());
|
||||
widgets.push(reset.widget_holder());
|
||||
widgets.push(WidgetHolder::related_separator());
|
||||
};
|
||||
|
||||
let entries = vec![
|
||||
("WorkingColorsPrimary", "Primary Working Color", ToolColorType::Primary),
|
||||
("WorkingColorsSecondary", "Secondary Working Color", ToolColorType::Secondary),
|
||||
("CustomColor", "Custom Color", ToolColorType::Custom),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(icon, tooltip, color_type)| {
|
||||
let mut entry = RadioEntryData::new("").tooltip(tooltip).icon(icon);
|
||||
entry.on_update = radio_callback(color_type);
|
||||
entry
|
||||
})
|
||||
.collect();
|
||||
let radio = RadioInput::new(entries).selected_index(self.color_type.clone() as u32).widget_holder();
|
||||
widgets.push(radio);
|
||||
widgets.push(WidgetHolder::related_separator());
|
||||
|
||||
let mut color_input = ColorInput::new(self.active_color()).allow_none(color_allow_none);
|
||||
color_input.on_update = color_callback;
|
||||
widgets.push(color_input.widget_holder());
|
||||
|
||||
widgets
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod color_selector;
|
||||
pub mod graph_modification_utils;
|
||||
pub mod overlay_renderer;
|
||||
pub mod path_outline;
|
||||
|
|
|
|||
|
|
@ -129,6 +129,9 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
|
|||
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
|
||||
// Update the working colors for the active tool
|
||||
responses.add(BroadcastEvent::WorkingColorChanged);
|
||||
|
||||
// Send tool options to the frontend
|
||||
responses.add(ToolMessage::RefreshToolOptions);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use document_legacy::LayerId;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork};
|
||||
use graphene_core::raster::ImageFrame;
|
||||
use graphene_core::Color;
|
||||
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -28,6 +30,7 @@ pub struct BrushOptions {
|
|||
diameter: f64,
|
||||
hardness: f64,
|
||||
flow: f64,
|
||||
color: ToolColorOptions,
|
||||
}
|
||||
|
||||
impl Default for BrushOptions {
|
||||
|
|
@ -36,6 +39,7 @@ impl Default for BrushOptions {
|
|||
diameter: 40.,
|
||||
hardness: 50.,
|
||||
flow: 100.,
|
||||
color: ToolColorOptions::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +51,8 @@ pub enum BrushToolMessage {
|
|||
// Standard messages
|
||||
#[remain::unsorted]
|
||||
Abort,
|
||||
#[remain::unsorted]
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
DragStart,
|
||||
|
|
@ -59,9 +65,12 @@ pub enum BrushToolMessage {
|
|||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum BrushToolMessageOptionsUpdate {
|
||||
ChangeDiameter(f64),
|
||||
Color(Option<Color>),
|
||||
ColorType(ToolColorType),
|
||||
Diameter(f64),
|
||||
Flow(f64),
|
||||
Hardness(f64),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
|
|
@ -85,32 +94,42 @@ impl ToolMetadata for BrushTool {
|
|||
|
||||
impl PropertyHolder for BrushTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let diameter = NumberInput::new(Some(self.options.diameter))
|
||||
.label("Diameter")
|
||||
.min(1.)
|
||||
.unit(" px")
|
||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Diameter(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
let hardness = NumberInput::new(Some(self.options.hardness))
|
||||
.label("Hardness")
|
||||
.min(0.)
|
||||
.max(100.)
|
||||
.unit("%")
|
||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Hardness(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
let flow = NumberInput::new(Some(self.options.flow))
|
||||
.label("Flow")
|
||||
.min(1.)
|
||||
.max(100.)
|
||||
.unit("%")
|
||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Flow(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
let mut widgets = vec![
|
||||
NumberInput::new(Some(self.options.diameter))
|
||||
.label("Diameter")
|
||||
.min(1.)
|
||||
.unit(" px")
|
||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Diameter(number_input.value.unwrap())).into())
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
NumberInput::new(Some(self.options.hardness))
|
||||
.label("Hardness")
|
||||
.min(0.)
|
||||
.max(100.)
|
||||
.unit("%")
|
||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Hardness(number_input.value.unwrap())).into())
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
NumberInput::new(Some(self.options.flow))
|
||||
.label("Flow")
|
||||
.min(1.)
|
||||
.max(100.)
|
||||
.unit("%")
|
||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Flow(number_input.value.unwrap())).into())
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let separator = Separator::new(SeparatorDirection::Horizontal, SeparatorType::Related).widget_holder();
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![diameter, separator.clone(), hardness, separator, flow],
|
||||
}]))
|
||||
widgets.append(&mut self.options.color.create_widgets(
|
||||
"Color",
|
||||
false,
|
||||
WidgetCallback::new(|_| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::ColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Color(color.value)).into()),
|
||||
));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +152,22 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for BrushTo
|
|||
BrushToolMessageOptionsUpdate::Diameter(diameter) => self.options.diameter = diameter,
|
||||
BrushToolMessageOptionsUpdate::Hardness(hardness) => self.options.hardness = hardness,
|
||||
BrushToolMessageOptionsUpdate::Flow(flow) => self.options.flow = flow,
|
||||
BrushToolMessageOptionsUpdate::Color(color) => {
|
||||
self.options.color.custom_color = color;
|
||||
self.options.color.color_type = ToolColorType::Custom;
|
||||
}
|
||||
BrushToolMessageOptionsUpdate::ColorType(color_type) => self.options.color.color_type = color_type,
|
||||
BrushToolMessageOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.color.primary_working_color = primary;
|
||||
self.options.color.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -164,6 +198,7 @@ impl ToolTransition for BrushTool {
|
|||
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||
EventToMessageMap {
|
||||
tool_abort: Some(BrushToolMessage::Abort.into()),
|
||||
working_color_changed: Some(BrushToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -247,7 +282,7 @@ impl Fsm for BrushToolFsmState {
|
|||
tool_data.points.push(vec![pos]);
|
||||
|
||||
if new_layer {
|
||||
add_brush_render(tool_options, tool_data, global_tool_data, responses);
|
||||
add_brush_render(tool_options, tool_data, responses);
|
||||
} else {
|
||||
//tool_data.update_image(node_graph, responses);
|
||||
tool_data.update_points(responses);
|
||||
|
|
@ -291,6 +326,13 @@ impl Fsm for BrushToolFsmState {
|
|||
|
||||
Ready
|
||||
}
|
||||
(_, WorkingColorChanged) => {
|
||||
responses.add(BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
|
|
@ -312,7 +354,7 @@ impl Fsm for BrushToolFsmState {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_brush_render(tool_options: &BrushOptions, data: &BrushToolData, tool_data: &DocumentToolData, responses: &mut VecDeque<Message>) {
|
||||
fn add_brush_render(tool_options: &BrushOptions, data: &BrushToolData, responses: &mut VecDeque<Message>) {
|
||||
let layer_path = data.path.clone().unwrap();
|
||||
|
||||
let brush_node = DocumentNode {
|
||||
|
|
@ -329,7 +371,7 @@ fn add_brush_render(tool_options: &BrushOptions, data: &BrushToolData, tool_data
|
|||
// Flow
|
||||
NodeInput::value(TaggedValue::F64(tool_options.flow), false),
|
||||
// Color
|
||||
NodeInput::value(TaggedValue::Color(tool_data.primary_color), false),
|
||||
NodeInput::value(TaggedValue::Color(tool_options.color.active_color().unwrap()), false),
|
||||
],
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_std::brush::BrushNode".into()),
|
||||
metadata: graph_craft::document::DocumentNodeMetadata { position: (8, 4).into() },
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
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::PropertyHolder;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, NumberInput, WidgetHolder};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use graphene_core::vector::style::Fill;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::Color;
|
||||
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -16,15 +20,45 @@ use serde::{Deserialize, Serialize};
|
|||
pub struct EllipseTool {
|
||||
fsm_state: EllipseToolFsmState,
|
||||
data: EllipseToolData,
|
||||
options: EllipseToolOptions,
|
||||
}
|
||||
|
||||
pub struct EllipseToolOptions {
|
||||
line_weight: f64,
|
||||
fill: ToolColorOptions,
|
||||
stroke: ToolColorOptions,
|
||||
}
|
||||
|
||||
impl Default for EllipseToolOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
line_weight: 5.,
|
||||
fill: ToolColorOptions::new_secondary(),
|
||||
stroke: ToolColorOptions::new_primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum EllipseOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
LineWeight(f64),
|
||||
StrokeColor(Option<Color>),
|
||||
StrokeColorType(ToolColorType),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message(Message, ToolMessage, Ellipse)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum EllipseToolMessage {
|
||||
// Standard messages
|
||||
#[remain::unsorted]
|
||||
Abort,
|
||||
#[remain::unsorted]
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
DragStart,
|
||||
|
|
@ -33,6 +67,7 @@ pub enum EllipseToolMessage {
|
|||
center: Key,
|
||||
lock_ratio: Key,
|
||||
},
|
||||
UpdateOptions(EllipseOptionsUpdate),
|
||||
}
|
||||
|
||||
impl ToolMetadata for EllipseTool {
|
||||
|
|
@ -47,11 +82,73 @@ impl ToolMetadata for EllipseTool {
|
|||
}
|
||||
}
|
||||
|
||||
impl PropertyHolder for EllipseTool {}
|
||||
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
NumberInput::new(Some(line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
impl PropertyHolder for EllipseTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::FillColor(color.value)).into()),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for EllipseTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &(), responses, true);
|
||||
if let ToolMessage::Ellipse(EllipseToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
EllipseOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
}
|
||||
EllipseOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
||||
EllipseOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
EllipseOptionsUpdate::StrokeColor(color) => {
|
||||
self.options.stroke.custom_color = color;
|
||||
self.options.stroke.color_type = ToolColorType::Custom;
|
||||
}
|
||||
EllipseOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
|
||||
EllipseOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.stroke.primary_working_color = primary;
|
||||
self.options.stroke.secondary_working_color = secondary;
|
||||
self.options.fill.primary_working_color = primary;
|
||||
self.options.fill.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &self.options, responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
|
|
@ -74,6 +171,7 @@ impl ToolTransition for EllipseTool {
|
|||
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||
EventToMessageMap {
|
||||
tool_abort: Some(EllipseToolMessage::Abort.into()),
|
||||
working_color_changed: Some(EllipseToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +191,7 @@ struct EllipseToolData {
|
|||
|
||||
impl Fsm for EllipseToolFsmState {
|
||||
type ToolData = EllipseToolData;
|
||||
type ToolOptions = ();
|
||||
type ToolOptions = EllipseToolOptions;
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
|
|
@ -106,7 +204,7 @@ impl Fsm for EllipseToolFsmState {
|
|||
render_data,
|
||||
..
|
||||
}: &mut ToolActionHandlerData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
tool_options: &Self::ToolOptions,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
use EllipseToolFsmState::*;
|
||||
|
|
@ -130,10 +228,15 @@ impl Fsm for EllipseToolFsmState {
|
|||
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||
graph_modification_utils::set_manipulator_mirror_angle(&manipulator_groups, &layer_path, true, responses);
|
||||
|
||||
// Set the fill color to the primary working color
|
||||
let fill_color = tool_options.fill.active_color();
|
||||
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,
|
||||
fill: Fill::solid(global_tool_data.primary_color),
|
||||
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
||||
});
|
||||
|
||||
Drawing
|
||||
|
|
@ -157,6 +260,13 @@ impl Fsm for EllipseToolFsmState {
|
|||
|
||||
Ready
|
||||
}
|
||||
(_, WorkingColorChanged) => {
|
||||
responses.add(EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use document_legacy::LayerId;
|
||||
use document_legacy::Operation;
|
||||
use graphene_core::vector::style::Stroke;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::Color;
|
||||
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -23,11 +27,17 @@ pub struct FreehandTool {
|
|||
|
||||
pub struct FreehandOptions {
|
||||
line_weight: f64,
|
||||
fill: ToolColorOptions,
|
||||
stroke: ToolColorOptions,
|
||||
}
|
||||
|
||||
impl Default for FreehandOptions {
|
||||
fn default() -> Self {
|
||||
Self { line_weight: 5. }
|
||||
Self {
|
||||
line_weight: 5.,
|
||||
fill: ToolColorOptions::new_none(),
|
||||
stroke: ToolColorOptions::new_primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -38,18 +48,25 @@ pub enum FreehandToolMessage {
|
|||
// Standard messages
|
||||
#[remain::unsorted]
|
||||
Abort,
|
||||
#[remain::unsorted]
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
DragStart,
|
||||
DragStop,
|
||||
PointerMove,
|
||||
UpdateOptions(FreehandToolMessageOptionsUpdate),
|
||||
UpdateOptions(FreehandOptionsUpdate),
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum FreehandToolMessageOptionsUpdate {
|
||||
pub enum FreehandOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
LineWeight(f64),
|
||||
StrokeColor(Option<Color>),
|
||||
StrokeColorType(ToolColorType),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
|
|
@ -71,15 +88,38 @@ impl ToolMetadata for FreehandTool {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
NumberInput::new(Some(line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(1.)
|
||||
.on_update(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
impl PropertyHolder for FreehandTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(1.)
|
||||
.on_update(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandToolMessageOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(color.value)).into()),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,8 +127,30 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Freehan
|
|||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
if let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
FreehandToolMessageOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
FreehandOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
}
|
||||
FreehandOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
||||
FreehandOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
FreehandOptionsUpdate::StrokeColor(color) => {
|
||||
self.options.stroke.custom_color = color;
|
||||
self.options.stroke.color_type = ToolColorType::Custom;
|
||||
}
|
||||
FreehandOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
|
||||
FreehandOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.stroke.primary_working_color = primary;
|
||||
self.options.stroke.secondary_working_color = secondary;
|
||||
self.options.fill.primary_working_color = primary;
|
||||
self.options.fill.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -117,6 +179,7 @@ impl ToolTransition for FreehandTool {
|
|||
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||
EventToMessageMap {
|
||||
tool_abort: Some(FreehandToolMessage::Abort.into()),
|
||||
working_color_changed: Some(FreehandToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +224,7 @@ impl Fsm for FreehandToolFsmState {
|
|||
|
||||
tool_data.weight = tool_options.line_weight;
|
||||
|
||||
add_polyline(tool_data, global_tool_data, responses);
|
||||
add_polyline(tool_data, tool_options.stroke.active_color(), tool_options.fill.active_color(), responses);
|
||||
|
||||
Drawing
|
||||
}
|
||||
|
|
@ -172,14 +235,14 @@ impl Fsm for FreehandToolFsmState {
|
|||
tool_data.points.push(pos);
|
||||
}
|
||||
|
||||
add_polyline(tool_data, global_tool_data, responses);
|
||||
add_polyline(tool_data, tool_options.stroke.active_color(), tool_options.fill.active_color(), responses);
|
||||
|
||||
Drawing
|
||||
}
|
||||
(Drawing, DragStop) | (Drawing, Abort) => {
|
||||
if tool_data.points.len() >= 2 {
|
||||
responses.add(remove_preview(tool_data));
|
||||
add_polyline(tool_data, global_tool_data, responses);
|
||||
add_polyline(tool_data, tool_options.stroke.active_color(), tool_options.fill.active_color(), responses);
|
||||
responses.add(DocumentMessage::CommitTransaction);
|
||||
} else {
|
||||
responses.add(DocumentMessage::AbortTransaction);
|
||||
|
|
@ -190,6 +253,13 @@ impl Fsm for FreehandToolFsmState {
|
|||
|
||||
Ready
|
||||
}
|
||||
(_, FreehandToolMessage::WorkingColorChanged) => {
|
||||
responses.add(FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
|
|
@ -215,14 +285,19 @@ fn remove_preview(data: &FreehandToolData) -> Message {
|
|||
Operation::DeleteLayer { path: data.path.clone().unwrap() }.into()
|
||||
}
|
||||
|
||||
fn add_polyline(data: &FreehandToolData, tool_data: &DocumentToolData, responses: &mut VecDeque<Message>) {
|
||||
fn add_polyline(data: &FreehandToolData, stroke_color: Option<Color>, fill_color: Option<Color>, responses: &mut VecDeque<Message>) {
|
||||
let subpath = bezier_rs::Subpath::from_anchors(data.points.iter().copied(), false);
|
||||
|
||||
let layer_path = data.path.clone().unwrap();
|
||||
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,
|
||||
stroke: Stroke::new(Some(tool_data.primary_color), data.weight),
|
||||
stroke: Stroke::new(stroke_color, data.weight),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ impl PropertyHolder for GradientTool {
|
|||
])
|
||||
.selected_index((self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial) as u32)
|
||||
.widget_holder();
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![gradient_type] }]))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ 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::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
|
|
@ -12,6 +15,7 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
|||
|
||||
use document_legacy::LayerId;
|
||||
use graphene_core::vector::style::Stroke;
|
||||
use graphene_core::Color;
|
||||
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -25,11 +29,15 @@ pub struct LineTool {
|
|||
|
||||
pub struct LineOptions {
|
||||
line_weight: f64,
|
||||
stroke: ToolColorOptions,
|
||||
}
|
||||
|
||||
impl Default for LineOptions {
|
||||
fn default() -> Self {
|
||||
Self { line_weight: 5. }
|
||||
Self {
|
||||
line_weight: 5.,
|
||||
stroke: ToolColorOptions::new_primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +48,8 @@ pub enum LineToolMessage {
|
|||
// Standard messages
|
||||
#[remain::unsorted]
|
||||
Abort,
|
||||
#[remain::unsorted]
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
DragStart,
|
||||
|
|
@ -56,6 +66,9 @@ pub enum LineToolMessage {
|
|||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum LineOptionsUpdate {
|
||||
LineWeight(f64),
|
||||
StrokeColor(Option<Color>),
|
||||
StrokeColorType(ToolColorType),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
impl ToolMetadata for LineTool {
|
||||
|
|
@ -70,15 +83,28 @@ impl ToolMetadata for LineTool {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
NumberInput::new(Some(line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
impl PropertyHolder for LineTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||
let mut widgets = self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
);
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +113,22 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for LineToo
|
|||
if let ToolMessage::Line(LineToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
LineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
LineOptionsUpdate::StrokeColor(color) => {
|
||||
self.options.stroke.custom_color = color;
|
||||
self.options.stroke.color_type = ToolColorType::Custom;
|
||||
}
|
||||
LineOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
|
||||
LineOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.stroke.primary_working_color = primary;
|
||||
self.options.stroke.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -106,6 +147,7 @@ impl ToolTransition for LineTool {
|
|||
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||
EventToMessageMap {
|
||||
tool_abort: Some(LineToolMessage::Abort.into()),
|
||||
working_color_changed: Some(LineToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -164,7 +206,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(Some(global_tool_data.primary_color), tool_options.line_weight),
|
||||
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
||||
});
|
||||
|
||||
tool_data.weight = tool_options.line_weight;
|
||||
|
|
@ -192,6 +234,13 @@ impl Fsm for LineToolFsmState {
|
|||
tool_data.path = None;
|
||||
Ready
|
||||
}
|
||||
(_, WorkingColorChanged) => {
|
||||
responses.add(LineToolMessage::UpdateOptions(LineOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
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::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, 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::widget_prelude::{ColorInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::portfolio::document::node_graph::VectorDataModification;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
||||
|
||||
|
|
@ -30,52 +31,18 @@ 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,
|
||||
fill: ToolColorOptions,
|
||||
stroke: ToolColorOptions,
|
||||
}
|
||||
|
||||
impl Default for PenOptions {
|
||||
fn default() -> Self {
|
||||
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,
|
||||
},
|
||||
fill: ToolColorOptions::new_secondary(),
|
||||
stroke: ToolColorOptions::new_primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -119,12 +86,11 @@ enum PenToolFsmState {
|
|||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum PenOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(PenColorType),
|
||||
FillColorType(ToolColorType),
|
||||
LineWeight(f64),
|
||||
PrimaryColor(Option<Color>),
|
||||
SecondaryColor(Option<Color>),
|
||||
StrokeColor(Option<Color>),
|
||||
StrokeColorType(PenColorType),
|
||||
StrokeColorType(ToolColorType),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
impl ToolMetadata for PenTool {
|
||||
|
|
@ -139,85 +105,6 @@ 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")
|
||||
|
|
@ -229,11 +116,26 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
|||
|
||||
impl PropertyHolder for PenTool {
|
||||
fn properties(&self) -> Layout {
|
||||
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));
|
||||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(color.value)).into()),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
|
|
@ -244,22 +146,20 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool
|
|||
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;
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::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;
|
||||
self.options.stroke.custom_color = color;
|
||||
self.options.stroke.color_type = ToolColorType::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;
|
||||
PenOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.stroke.primary_working_color = primary;
|
||||
self.options.stroke.secondary_working_color = secondary;
|
||||
self.options.fill.primary_working_color = primary;
|
||||
self.options.fill.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -701,8 +601,10 @@ 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))));
|
||||
responses.add(PenToolMessage::UpdateOptions(PenOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
(PenToolFsmState::Ready, PenToolMessage::DragStart) => {
|
||||
|
|
|
|||
|
|
@ -1,29 +1,63 @@
|
|||
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::PropertyHolder;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, NumberInput, WidgetHolder};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use glam::DVec2;
|
||||
use graphene_core::vector::style::Fill;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::Color;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RectangleTool {
|
||||
fsm_state: RectangleToolFsmState,
|
||||
tool_data: RectangleToolData,
|
||||
options: RectangleToolOptions,
|
||||
}
|
||||
|
||||
pub struct RectangleToolOptions {
|
||||
line_weight: f64,
|
||||
fill: ToolColorOptions,
|
||||
stroke: ToolColorOptions,
|
||||
}
|
||||
|
||||
impl Default for RectangleToolOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
line_weight: 5.,
|
||||
fill: ToolColorOptions::new_secondary(),
|
||||
stroke: ToolColorOptions::new_primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum RectangleOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
LineWeight(f64),
|
||||
StrokeColor(Option<Color>),
|
||||
StrokeColorType(ToolColorType),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message(Message, ToolMessage, Rectangle)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum RectangleToolMessage {
|
||||
// Standard messages
|
||||
#[remain::unsorted]
|
||||
Abort,
|
||||
#[remain::unsorted]
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
DragStart,
|
||||
|
|
@ -32,13 +66,76 @@ pub enum RectangleToolMessage {
|
|||
center: Key,
|
||||
lock_ratio: Key,
|
||||
},
|
||||
UpdateOptions(RectangleOptionsUpdate),
|
||||
}
|
||||
|
||||
impl PropertyHolder for RectangleTool {}
|
||||
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
NumberInput::new(Some(line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
impl PropertyHolder for RectangleTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::FillColor(color.value)).into()),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for RectangleTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||
if let ToolMessage::Rectangle(RectangleToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
RectangleOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
}
|
||||
RectangleOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
||||
RectangleOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
RectangleOptionsUpdate::StrokeColor(color) => {
|
||||
self.options.stroke.custom_color = color;
|
||||
self.options.stroke.color_type = ToolColorType::Custom;
|
||||
}
|
||||
RectangleOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
|
||||
RectangleOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.stroke.primary_working_color = primary;
|
||||
self.options.stroke.secondary_working_color = secondary;
|
||||
self.options.fill.primary_working_color = primary;
|
||||
self.options.fill.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
|
|
@ -73,6 +170,7 @@ impl ToolTransition for RectangleTool {
|
|||
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||
EventToMessageMap {
|
||||
tool_abort: Some(RectangleToolMessage::Abort.into()),
|
||||
working_color_changed: Some(RectangleToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -92,7 +190,7 @@ struct RectangleToolData {
|
|||
|
||||
impl Fsm for RectangleToolFsmState {
|
||||
type ToolData = RectangleToolData;
|
||||
type ToolOptions = ();
|
||||
type ToolOptions = RectangleToolOptions;
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
|
|
@ -105,7 +203,7 @@ impl Fsm for RectangleToolFsmState {
|
|||
render_data,
|
||||
..
|
||||
}: &mut ToolActionHandlerData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
tool_options: &Self::ToolOptions,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
use RectangleToolFsmState::*;
|
||||
|
|
@ -124,9 +222,16 @@ impl Fsm for RectangleToolFsmState {
|
|||
responses.add(DocumentMessage::StartTransaction);
|
||||
shape_data.path = Some(layer_path.clone());
|
||||
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||
|
||||
let fill_color = tool_options.fill.active_color();
|
||||
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,
|
||||
fill: Fill::solid(global_tool_data.primary_color),
|
||||
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
||||
});
|
||||
|
||||
Drawing
|
||||
|
|
@ -151,6 +256,13 @@ impl Fsm for RectangleToolFsmState {
|
|||
|
||||
Ready
|
||||
}
|
||||
(_, WorkingColorChanged) => {
|
||||
responses.add(RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use crate::messages::layout::utility_types::misc::LayoutTarget;
|
|||
use crate::messages::layout::utility_types::widgets::assist_widgets::{PivotAssist, PivotPosition};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{DropdownEntryData, DropdownInput};
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::{Separator, SeparatorDirection, SeparatorType};
|
||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis};
|
||||
use crate::messages::portfolio::document::utility_types::transformation::Selected;
|
||||
use crate::messages::prelude::*;
|
||||
|
|
@ -137,7 +136,7 @@ impl PropertyHolder for SelectTool {
|
|||
PivotAssist::new(self.tool_data.pivot.to_pivot_position())
|
||||
.on_update(|pivot_assist: &PivotAssist| SelectToolMessage::SetPivot { position: pivot_assist.position }.into())
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||
WidgetHolder::section_separator(),
|
||||
IconButton::new("AlignLeft", 24)
|
||||
.tooltip("Align Left")
|
||||
.on_update(|_| {
|
||||
|
|
@ -201,7 +200,7 @@ impl PropertyHolder for SelectTool {
|
|||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
PopoverButton::new("Align", "Coming soon").widget_holder(),
|
||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||
WidgetHolder::section_separator(),
|
||||
IconButton::new("FlipHorizontal", 24)
|
||||
.tooltip("Flip Horizontal")
|
||||
.on_update(|_| SelectToolMessage::FlipHorizontal.into())
|
||||
|
|
@ -216,7 +215,7 @@ impl PropertyHolder for SelectTool {
|
|||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
||||
WidgetHolder::section_separator(),
|
||||
IconButton::new("BooleanUnion", 24)
|
||||
.tooltip("Boolean Union (coming soon)")
|
||||
.on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(1091) }.into())
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
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::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, NumberInput, WidgetHolder};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use graphene_core::vector::style::Fill;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::Color;
|
||||
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -21,22 +24,32 @@ pub struct ShapeTool {
|
|||
}
|
||||
|
||||
pub struct ShapeOptions {
|
||||
line_weight: f64,
|
||||
fill: ToolColorOptions,
|
||||
stroke: ToolColorOptions,
|
||||
vertices: u32,
|
||||
}
|
||||
|
||||
impl Default for ShapeOptions {
|
||||
fn default() -> Self {
|
||||
Self { vertices: 5 }
|
||||
Self {
|
||||
vertices: 5,
|
||||
line_weight: 5.,
|
||||
fill: ToolColorOptions::new_secondary(),
|
||||
stroke: ToolColorOptions::new_primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message(Message, ToolMessage, Shape)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum ShapeToolMessage {
|
||||
// Standard messages
|
||||
#[remain::unsorted]
|
||||
Abort,
|
||||
#[remain::unsorted]
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
DragStart,
|
||||
|
|
@ -49,9 +62,15 @@ pub enum ShapeToolMessage {
|
|||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum ShapeOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
LineWeight(f64),
|
||||
StrokeColor(Option<Color>),
|
||||
StrokeColorType(ToolColorType),
|
||||
Vertices(u32),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
impl ToolMetadata for ShapeTool {
|
||||
|
|
@ -66,26 +85,84 @@ impl ToolMetadata for ShapeTool {
|
|||
}
|
||||
}
|
||||
|
||||
impl PropertyHolder for ShapeTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let sides = NumberInput::new(Some(self.options.vertices as f64))
|
||||
.label("Sides")
|
||||
.int()
|
||||
.min(3.)
|
||||
.max(1000.)
|
||||
.mode(crate::messages::layout::utility_types::widget_prelude::NumberInputMode::Increment)
|
||||
.on_update(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(number_input.value.unwrap() as u32)).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![sides] }]))
|
||||
}
|
||||
fn create_sides_widget(vertices: u32) -> WidgetHolder {
|
||||
NumberInput::new(Some(vertices as f64))
|
||||
.label("Sides")
|
||||
.int()
|
||||
.min(3.)
|
||||
.max(1000.)
|
||||
.mode(crate::messages::layout::utility_types::widget_prelude::NumberInputMode::Increment)
|
||||
.on_update(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(number_input.value.unwrap() as u32)).into())
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
NumberInput::new(Some(line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
impl PropertyHolder for ShapeTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let mut widgets = vec![create_sides_widget(self.options.vertices)];
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
widgets.append(&mut self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::FillColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::FillColor(color.value)).into()),
|
||||
));
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::StrokeColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ShapeTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
if let ToolMessage::Shape(ShapeToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
ShapeOptionsUpdate::Vertices(vertices) => self.options.vertices = vertices,
|
||||
ShapeOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
}
|
||||
ShapeOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
||||
ShapeOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
ShapeOptionsUpdate::StrokeColor(color) => {
|
||||
self.options.stroke.custom_color = color;
|
||||
self.options.stroke.color_type = ToolColorType::Custom;
|
||||
}
|
||||
ShapeOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
|
||||
ShapeOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.stroke.primary_working_color = primary;
|
||||
self.options.stroke.secondary_working_color = secondary;
|
||||
self.options.fill.primary_working_color = primary;
|
||||
self.options.fill.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +189,7 @@ impl ToolTransition for ShapeTool {
|
|||
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||
EventToMessageMap {
|
||||
tool_abort: Some(ShapeToolMessage::Abort.into()),
|
||||
working_color_changed: Some(ShapeToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -162,9 +240,16 @@ impl Fsm for ShapeToolFsmState {
|
|||
|
||||
let subpath = bezier_rs::Subpath::new_regular_polygon(DVec2::ZERO, tool_options.vertices as u64, 1.);
|
||||
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||
|
||||
let fill_color = tool_options.fill.active_color();
|
||||
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,
|
||||
fill: Fill::solid(global_tool_data.primary_color),
|
||||
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
||||
});
|
||||
|
||||
Drawing
|
||||
|
|
@ -189,6 +274,13 @@ impl Fsm for ShapeToolFsmState {
|
|||
|
||||
Ready
|
||||
}
|
||||
(_, WorkingColorChanged) => {
|
||||
responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
use crate::consts::DRAG_THRESHOLD;
|
||||
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::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widget_prelude::{ColorInput, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
use crate::messages::tool::utility_types::{DocumentToolData, EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use document_legacy::{LayerId, Operation};
|
||||
use graphene_core::vector::style::Stroke;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::Color;
|
||||
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -24,11 +28,17 @@ pub struct SplineTool {
|
|||
|
||||
pub struct SplineOptions {
|
||||
line_weight: f64,
|
||||
fill: ToolColorOptions,
|
||||
stroke: ToolColorOptions,
|
||||
}
|
||||
|
||||
impl Default for SplineOptions {
|
||||
fn default() -> Self {
|
||||
Self { line_weight: 5. }
|
||||
Self {
|
||||
line_weight: 5.,
|
||||
fill: ToolColorOptions::new_none(),
|
||||
stroke: ToolColorOptions::new_primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +49,8 @@ pub enum SplineToolMessage {
|
|||
// Standard messages
|
||||
#[remain::unsorted]
|
||||
Abort,
|
||||
#[remain::unsorted]
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
Confirm,
|
||||
|
|
@ -59,7 +71,12 @@ enum SplineToolFsmState {
|
|||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum SplineOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
LineWeight(f64),
|
||||
StrokeColor(Option<Color>),
|
||||
StrokeColorType(ToolColorType),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
impl ToolMetadata for SplineTool {
|
||||
|
|
@ -74,15 +91,38 @@ impl ToolMetadata for SplineTool {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
NumberInput::new(Some(line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
impl PropertyHolder for SplineTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
||||
.unit(" px")
|
||||
.label("Weight")
|
||||
.min(0.)
|
||||
.on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
||||
let mut widgets = self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(color.value)).into()),
|
||||
);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
widgets.append(&mut self.options.stroke.create_widgets(
|
||||
"Stroke",
|
||||
true,
|
||||
WidgetCallback::new(|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(color.value)).into()),
|
||||
));
|
||||
widgets.push(WidgetHolder::unrelated_separator());
|
||||
widgets.push(create_weight_widget(self.options.line_weight));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +131,29 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SplineT
|
|||
if let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message {
|
||||
match action {
|
||||
SplineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
SplineOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
}
|
||||
SplineOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
||||
SplineOptionsUpdate::StrokeColor(color) => {
|
||||
self.options.stroke.custom_color = color;
|
||||
self.options.stroke.color_type = ToolColorType::Custom;
|
||||
}
|
||||
SplineOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
|
||||
SplineOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.stroke.primary_working_color = primary;
|
||||
self.options.stroke.secondary_working_color = secondary;
|
||||
self.options.fill.primary_working_color = primary;
|
||||
self.options.fill.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +185,7 @@ impl ToolTransition for SplineTool {
|
|||
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||
EventToMessageMap {
|
||||
tool_abort: Some(SplineToolMessage::Abort.into()),
|
||||
working_color_changed: Some(SplineToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +241,7 @@ impl Fsm for SplineToolFsmState {
|
|||
|
||||
tool_data.weight = tool_options.line_weight;
|
||||
|
||||
add_spline(tool_data, global_tool_data, true, responses);
|
||||
add_spline(tool_data, true, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses);
|
||||
|
||||
Drawing
|
||||
}
|
||||
|
|
@ -194,7 +257,7 @@ impl Fsm for SplineToolFsmState {
|
|||
}
|
||||
|
||||
responses.add(remove_preview(tool_data));
|
||||
add_spline(tool_data, global_tool_data, true, responses);
|
||||
add_spline(tool_data, true, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses);
|
||||
|
||||
Drawing
|
||||
}
|
||||
|
|
@ -204,14 +267,14 @@ impl Fsm for SplineToolFsmState {
|
|||
tool_data.next_point = pos;
|
||||
|
||||
responses.add(remove_preview(tool_data));
|
||||
add_spline(tool_data, global_tool_data, true, responses);
|
||||
add_spline(tool_data, true, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses);
|
||||
|
||||
Drawing
|
||||
}
|
||||
(Drawing, Confirm) | (Drawing, Abort) => {
|
||||
if tool_data.points.len() >= 2 {
|
||||
responses.add(remove_preview(tool_data));
|
||||
add_spline(tool_data, global_tool_data, false, responses);
|
||||
add_spline(tool_data, false, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses);
|
||||
responses.add(DocumentMessage::CommitTransaction);
|
||||
} else {
|
||||
responses.add(DocumentMessage::AbortTransaction);
|
||||
|
|
@ -223,6 +286,13 @@ impl Fsm for SplineToolFsmState {
|
|||
|
||||
Ready
|
||||
}
|
||||
(_, WorkingColorChanged) => {
|
||||
responses.add(SplineToolMessage::UpdateOptions(SplineOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
|
|
@ -254,7 +324,7 @@ fn remove_preview(tool_data: &SplineToolData) -> Message {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn add_spline(tool_data: &SplineToolData, global_tool_data: &DocumentToolData, show_preview: bool, responses: &mut VecDeque<Message>) {
|
||||
fn add_spline(tool_data: &SplineToolData, show_preview: bool, fill_color: Option<Color>, stroke_color: Option<Color>, responses: &mut VecDeque<Message>) {
|
||||
let mut points = tool_data.points.clone();
|
||||
if show_preview {
|
||||
points.push(tool_data.next_point)
|
||||
|
|
@ -267,8 +337,13 @@ fn add_spline(tool_data: &SplineToolData, global_tool_data: &DocumentToolData, s
|
|||
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||
graph_modification_utils::set_manipulator_mirror_angle(&manipulator_groups, &layer_path, true, 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(Some(global_tool_data.primary_color), tool_data.weight),
|
||||
stroke: Stroke::new(stroke_color, tool_data.weight),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ 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, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{FontInput, NumberInput};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{ColorInput, FontInput, NumberInput};
|
||||
use crate::messages::portfolio::document::node_graph::new_text_network;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
|
|
@ -34,6 +35,7 @@ pub struct TextOptions {
|
|||
font_size: u32,
|
||||
font_name: String,
|
||||
font_style: String,
|
||||
fill: ToolColorOptions,
|
||||
}
|
||||
|
||||
impl Default for TextOptions {
|
||||
|
|
@ -42,20 +44,22 @@ impl Default for TextOptions {
|
|||
font_size: 24,
|
||||
font_name: "Merriweather".into(),
|
||||
font_style: "Normal (400)".into(),
|
||||
fill: ToolColorOptions::new_primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message(Message, ToolMessage, Text)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum TextToolMessage {
|
||||
// Standard messages
|
||||
#[remain::unsorted]
|
||||
Abort,
|
||||
|
||||
#[remain::unsorted]
|
||||
DocumentIsDirty,
|
||||
#[remain::unsorted]
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
CommitText,
|
||||
|
|
@ -71,10 +75,13 @@ pub enum TextToolMessage {
|
|||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||
pub enum TextOptionsUpdate {
|
||||
FillColor(Option<Color>),
|
||||
FillColorType(ToolColorType),
|
||||
Font { family: String, style: String },
|
||||
FontSize(u32),
|
||||
WorkingColors(Option<Color>, Option<Color>),
|
||||
}
|
||||
|
||||
impl ToolMetadata for TextTool {
|
||||
|
|
@ -89,46 +96,60 @@ impl ToolMetadata for TextTool {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_text_widgets(tool: &TextTool) -> Vec<WidgetHolder> {
|
||||
let font = FontInput {
|
||||
is_style_picker: false,
|
||||
font_family: tool.options.font_name.clone(),
|
||||
font_style: tool.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.widget_holder();
|
||||
let style = FontInput {
|
||||
is_style_picker: true,
|
||||
font_family: tool.options.font_name.clone(),
|
||||
font_style: tool.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.widget_holder();
|
||||
let size = NumberInput::new(Some(tool.options.font_size as f64))
|
||||
.unit(" px")
|
||||
.label("Size")
|
||||
.int()
|
||||
.min(1.)
|
||||
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap() as u32)).into())
|
||||
.widget_holder();
|
||||
vec![font, WidgetHolder::related_separator(), style, WidgetHolder::related_separator(), size]
|
||||
}
|
||||
|
||||
impl PropertyHolder for TextTool {
|
||||
fn properties(&self) -> Layout {
|
||||
let font = FontInput {
|
||||
is_style_picker: false,
|
||||
font_family: self.options.font_name.clone(),
|
||||
font_style: self.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.widget_holder();
|
||||
let style = FontInput {
|
||||
is_style_picker: true,
|
||||
font_family: self.options.font_name.clone(),
|
||||
font_style: self.options.font_style.clone(),
|
||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
||||
family: font_input.font_family.clone(),
|
||||
style: font_input.font_style.clone(),
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.widget_holder();
|
||||
let size = NumberInput::new(Some(self.options.font_size as f64))
|
||||
.unit(" px")
|
||||
.label("Size")
|
||||
.int()
|
||||
.min(1.)
|
||||
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap() as u32)).into())
|
||||
.widget_holder();
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||
widgets: vec![font, WidgetHolder::related_separator(), style, WidgetHolder::related_separator(), size],
|
||||
}]))
|
||||
let mut widgets = create_text_widgets(self);
|
||||
|
||||
widgets.push(WidgetHolder::section_separator());
|
||||
|
||||
widgets.append(&mut self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
true,
|
||||
WidgetCallback::new(|_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(None)).into()),
|
||||
|color_type: ToolColorType| WidgetCallback::new(move |_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||
WidgetCallback::new(|color: &ColorInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(color.value)).into()),
|
||||
));
|
||||
|
||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +164,22 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for TextToo
|
|||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||
}
|
||||
TextOptionsUpdate::FontSize(font_size) => self.options.font_size = font_size,
|
||||
TextOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
}
|
||||
TextOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
||||
TextOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||
self.options.fill.primary_working_color = primary;
|
||||
self.options.fill.secondary_working_color = secondary;
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: self.properties(),
|
||||
layout_target: LayoutTarget::ToolOptions,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -172,6 +208,7 @@ impl ToolTransition for TextTool {
|
|||
document_dirty: Some(TextToolMessage::DocumentIsDirty.into()),
|
||||
tool_abort: Some(TextToolMessage::Abort.into()),
|
||||
selection_changed: Some(TextToolMessage::DocumentIsDirty.into()),
|
||||
working_color_changed: Some(TextToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -188,7 +225,7 @@ pub struct EditingText {
|
|||
text: String,
|
||||
font: Font,
|
||||
font_size: f64,
|
||||
color: Color,
|
||||
color: Option<Color>,
|
||||
transform: DAffine2,
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +248,7 @@ impl TextToolData {
|
|||
text: editing_text.text.clone(),
|
||||
line_width: None,
|
||||
font_size: editing_text.font_size,
|
||||
color: editing_text.color,
|
||||
color: editing_text.color.unwrap_or(Color::BLACK),
|
||||
url: render_data.font_cache.get_preview_url(&editing_text.font).cloned().unwrap_or_default(),
|
||||
transform: editing_text.transform.to_cols_array(),
|
||||
});
|
||||
|
|
@ -237,7 +274,7 @@ impl TextToolData {
|
|||
text: text.clone(),
|
||||
font: font.clone(),
|
||||
font_size,
|
||||
color,
|
||||
color: Some(color),
|
||||
transform,
|
||||
});
|
||||
self.new_text = text.clone();
|
||||
|
|
@ -291,7 +328,7 @@ impl TextToolData {
|
|||
});
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer: self.layer_path.clone(),
|
||||
fill: Fill::solid(editing_text.color),
|
||||
fill: if editing_text.color.is_some() { Fill::Solid(editing_text.color.unwrap()) } else { Fill::None },
|
||||
});
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer: self.layer_path.clone(),
|
||||
|
|
@ -470,7 +507,7 @@ impl Fsm for TextToolFsmState {
|
|||
transform: DAffine2::from_translation(input.mouse.position),
|
||||
font_size: tool_options.font_size as f64,
|
||||
font: Font::new(tool_options.font_name.clone(), tool_options.font_style.clone()),
|
||||
color: global_tool_data.primary_color,
|
||||
color: tool_options.fill.active_color(),
|
||||
});
|
||||
tool_data.new_text = String::new();
|
||||
tool_data.layer_path = document.get_path_for_new_layer();
|
||||
|
|
@ -521,6 +558,13 @@ impl Fsm for TextToolFsmState {
|
|||
tool_data.update_bounds_overlay(document, render_data, responses);
|
||||
TextToolFsmState::Editing
|
||||
}
|
||||
(_, TextToolMessage::WorkingColorChanged) => {
|
||||
responses.add(TextToolMessage::UpdateOptions(TextOptionsUpdate::WorkingColors(
|
||||
Some(global_tool_data.primary_color),
|
||||
Some(global_tool_data.secondary_color),
|
||||
)));
|
||||
self
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="M8.2,7.2L4.5,4.4l-0.8,1C3,6.2,3.2,7.5,4.1,8.2L5,8.9c-1.1,1.1-3.3,3.2-3.7,3.7c-0.5,0.7-0.4,1.6,0.3,2.1c0.7,0.5,1.6,0.4,2.1-0.3c0.4-0.5,1.9-3.2,2.7-4.5l0.9,0.7c0.9,0.7,2.1,0.5,2.8-0.4l0.8-1L8.2,7.2z" />
|
||||
<path d="M11.7,8.1L16,2.5c-5.6,0.4-7.9-2.8-9.6-0.6l-1,1.3L11.7,8.1z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 352 B |
|
|
@ -15,8 +15,8 @@
|
|||
export let value: Color;
|
||||
// TODO: Implement
|
||||
// export let allowTransparency = false;
|
||||
// export let allowNone = false;
|
||||
// export let disabled = false;
|
||||
export let allowNone = false;
|
||||
export let tooltip: string | undefined = undefined;
|
||||
export let sharpRightCorners = false;
|
||||
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
value = detail;
|
||||
dispatch("value", detail);
|
||||
}}
|
||||
allowNone={true}
|
||||
{allowNone}
|
||||
/>
|
||||
</LayoutRow>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 CustomColor from "@graphite-frontend/assets/icon-16px-solid/custom-color.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";
|
||||
|
|
@ -155,6 +156,7 @@ const SOLID_16PX = {
|
|||
CheckboxChecked: { svg: CheckboxChecked, size: 16 },
|
||||
CheckboxUnchecked: { svg: CheckboxUnchecked, size: 16 },
|
||||
Copy: { svg: Copy, size: 16 },
|
||||
CustomColor: { svg: CustomColor, size: 16 },
|
||||
Edit: { svg: Edit, size: 16 },
|
||||
Eyedropper: { svg: Eyedropper, size: 16 },
|
||||
EyeHidden: { svg: EyeHidden, size: 16 },
|
||||
|
|
|
|||
|
|
@ -832,9 +832,10 @@ export class ColorInput extends WidgetProps {
|
|||
)
|
||||
value!: Color;
|
||||
|
||||
allowNone!: boolean;
|
||||
|
||||
// TODO: Implement
|
||||
// allowTransparency!: boolean;
|
||||
// allowNone!: boolean;
|
||||
// disabled!: boolean;
|
||||
|
||||
@Transform(({ value }: { value: string }) => value || undefined)
|
||||
|
|
|
|||
Loading…
Reference in New Issue