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 {
|
pub fn new(widget: Widget) -> Self {
|
||||||
Self { widget_id: generate_uuid(), widget }
|
Self { widget_id: generate_uuid(), widget }
|
||||||
}
|
}
|
||||||
|
pub fn section_separator() -> Self {
|
||||||
|
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder()
|
||||||
|
}
|
||||||
pub fn unrelated_separator() -> Self {
|
pub fn unrelated_separator() -> Self {
|
||||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Unrelated).widget_holder()
|
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Unrelated).widget_holder()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,9 @@ pub struct ColorInput {
|
||||||
// #[serde(rename = "allowTransparency")]
|
// #[serde(rename = "allowTransparency")]
|
||||||
// #[derivative(Default(value = "false"))]
|
// #[derivative(Default(value = "false"))]
|
||||||
// pub allow_transparency: bool,
|
// pub allow_transparency: bool,
|
||||||
|
#[serde(rename = "allowNone")]
|
||||||
// TODO: Implement
|
#[derivative(Default(value = "true"))]
|
||||||
// #[serde(rename = "allowNone")]
|
pub allow_none: bool,
|
||||||
// #[derivative(Default(value = "false"))]
|
|
||||||
// pub allow_none: bool,
|
|
||||||
|
|
||||||
// pub disabled: bool,
|
// pub disabled: bool,
|
||||||
pub tooltip: String,
|
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> {
|
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 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);
|
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 graph_modification_utils;
|
||||||
pub mod overlay_renderer;
|
pub mod overlay_renderer;
|
||||||
pub mod path_outline;
|
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
|
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
|
|
||||||
|
// Update the working colors for the active tool
|
||||||
|
responses.add(BroadcastEvent::WorkingColorChanged);
|
||||||
|
|
||||||
// Send tool options to the frontend
|
// Send tool options to the frontend
|
||||||
responses.add(ToolMessage::RefreshToolOptions);
|
responses.add(ToolMessage::RefreshToolOptions);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
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::misc::LayoutTarget;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
use crate::messages::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
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::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 crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
|
||||||
use document_legacy::LayerId;
|
use document_legacy::LayerId;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork};
|
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork};
|
||||||
use graphene_core::raster::ImageFrame;
|
use graphene_core::raster::ImageFrame;
|
||||||
|
use graphene_core::Color;
|
||||||
|
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -28,6 +30,7 @@ pub struct BrushOptions {
|
||||||
diameter: f64,
|
diameter: f64,
|
||||||
hardness: f64,
|
hardness: f64,
|
||||||
flow: f64,
|
flow: f64,
|
||||||
|
color: ToolColorOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for BrushOptions {
|
impl Default for BrushOptions {
|
||||||
|
|
@ -36,6 +39,7 @@ impl Default for BrushOptions {
|
||||||
diameter: 40.,
|
diameter: 40.,
|
||||||
hardness: 50.,
|
hardness: 50.,
|
||||||
flow: 100.,
|
flow: 100.,
|
||||||
|
color: ToolColorOptions::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -47,6 +51,8 @@ pub enum BrushToolMessage {
|
||||||
// Standard messages
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
WorkingColorChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
DragStart,
|
DragStart,
|
||||||
|
|
@ -59,9 +65,12 @@ pub enum BrushToolMessage {
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||||
pub enum BrushToolMessageOptionsUpdate {
|
pub enum BrushToolMessageOptionsUpdate {
|
||||||
ChangeDiameter(f64),
|
ChangeDiameter(f64),
|
||||||
|
Color(Option<Color>),
|
||||||
|
ColorType(ToolColorType),
|
||||||
Diameter(f64),
|
Diameter(f64),
|
||||||
Flow(f64),
|
Flow(f64),
|
||||||
Hardness(f64),
|
Hardness(f64),
|
||||||
|
WorkingColors(Option<Color>, Option<Color>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
|
|
@ -85,32 +94,42 @@ impl ToolMetadata for BrushTool {
|
||||||
|
|
||||||
impl PropertyHolder for BrushTool {
|
impl PropertyHolder for BrushTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
let diameter = NumberInput::new(Some(self.options.diameter))
|
let mut widgets = vec![
|
||||||
.label("Diameter")
|
NumberInput::new(Some(self.options.diameter))
|
||||||
.min(1.)
|
.label("Diameter")
|
||||||
.unit(" px")
|
.min(1.)
|
||||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Diameter(number_input.value.unwrap())).into())
|
.unit(" px")
|
||||||
.widget_holder();
|
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Diameter(number_input.value.unwrap())).into())
|
||||||
let hardness = NumberInput::new(Some(self.options.hardness))
|
.widget_holder(),
|
||||||
.label("Hardness")
|
WidgetHolder::related_separator(),
|
||||||
.min(0.)
|
NumberInput::new(Some(self.options.hardness))
|
||||||
.max(100.)
|
.label("Hardness")
|
||||||
.unit("%")
|
.min(0.)
|
||||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Hardness(number_input.value.unwrap())).into())
|
.max(100.)
|
||||||
.widget_holder();
|
.unit("%")
|
||||||
let flow = NumberInput::new(Some(self.options.flow))
|
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Hardness(number_input.value.unwrap())).into())
|
||||||
.label("Flow")
|
.widget_holder(),
|
||||||
.min(1.)
|
WidgetHolder::related_separator(),
|
||||||
.max(100.)
|
NumberInput::new(Some(self.options.flow))
|
||||||
.unit("%")
|
.label("Flow")
|
||||||
.on_update(|number_input: &NumberInput| BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::Flow(number_input.value.unwrap())).into())
|
.min(1.)
|
||||||
.widget_holder();
|
.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.append(&mut self.options.color.create_widgets(
|
||||||
widgets: vec![diameter, separator.clone(), hardness, separator, flow],
|
"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::Diameter(diameter) => self.options.diameter = diameter,
|
||||||
BrushToolMessageOptionsUpdate::Hardness(hardness) => self.options.hardness = hardness,
|
BrushToolMessageOptionsUpdate::Hardness(hardness) => self.options.hardness = hardness,
|
||||||
BrushToolMessageOptionsUpdate::Flow(flow) => self.options.flow = flow,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,6 +198,7 @@ impl ToolTransition for BrushTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
tool_abort: Some(BrushToolMessage::Abort.into()),
|
tool_abort: Some(BrushToolMessage::Abort.into()),
|
||||||
|
working_color_changed: Some(BrushToolMessage::WorkingColorChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -247,7 +282,7 @@ impl Fsm for BrushToolFsmState {
|
||||||
tool_data.points.push(vec![pos]);
|
tool_data.points.push(vec![pos]);
|
||||||
|
|
||||||
if new_layer {
|
if new_layer {
|
||||||
add_brush_render(tool_options, tool_data, global_tool_data, responses);
|
add_brush_render(tool_options, tool_data, responses);
|
||||||
} else {
|
} else {
|
||||||
//tool_data.update_image(node_graph, responses);
|
//tool_data.update_image(node_graph, responses);
|
||||||
tool_data.update_points(responses);
|
tool_data.update_points(responses);
|
||||||
|
|
@ -291,6 +326,13 @@ impl Fsm for BrushToolFsmState {
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
(_, WorkingColorChanged) => {
|
||||||
|
responses.add(BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::WorkingColors(
|
||||||
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
|
self
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
} else {
|
} 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 layer_path = data.path.clone().unwrap();
|
||||||
|
|
||||||
let brush_node = DocumentNode {
|
let brush_node = DocumentNode {
|
||||||
|
|
@ -329,7 +371,7 @@ fn add_brush_render(tool_options: &BrushOptions, data: &BrushToolData, tool_data
|
||||||
// Flow
|
// Flow
|
||||||
NodeInput::value(TaggedValue::F64(tool_options.flow), false),
|
NodeInput::value(TaggedValue::F64(tool_options.flow), false),
|
||||||
// Color
|
// 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()),
|
implementation: DocumentNodeImplementation::Unresolved("graphene_std::brush::BrushNode".into()),
|
||||||
metadata: graph_craft::document::DocumentNodeMetadata { position: (8, 4).into() },
|
metadata: graph_craft::document::DocumentNodeMetadata { position: (8, 4).into() },
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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_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::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::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
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::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
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 glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -16,15 +20,45 @@ use serde::{Deserialize, Serialize};
|
||||||
pub struct EllipseTool {
|
pub struct EllipseTool {
|
||||||
fsm_state: EllipseToolFsmState,
|
fsm_state: EllipseToolFsmState,
|
||||||
data: EllipseToolData,
|
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]
|
#[remain::sorted]
|
||||||
#[impl_message(Message, ToolMessage, Ellipse)]
|
#[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 {
|
pub enum EllipseToolMessage {
|
||||||
// Standard messages
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
WorkingColorChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
DragStart,
|
DragStart,
|
||||||
|
|
@ -33,6 +67,7 @@ pub enum EllipseToolMessage {
|
||||||
center: Key,
|
center: Key,
|
||||||
lock_ratio: Key,
|
lock_ratio: Key,
|
||||||
},
|
},
|
||||||
|
UpdateOptions(EllipseOptionsUpdate),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolMetadata for EllipseTool {
|
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 {
|
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>) {
|
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 {
|
fn actions(&self) -> ActionList {
|
||||||
|
|
@ -74,6 +171,7 @@ impl ToolTransition for EllipseTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
tool_abort: Some(EllipseToolMessage::Abort.into()),
|
tool_abort: Some(EllipseToolMessage::Abort.into()),
|
||||||
|
working_color_changed: Some(EllipseToolMessage::WorkingColorChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +191,7 @@ struct EllipseToolData {
|
||||||
|
|
||||||
impl Fsm for EllipseToolFsmState {
|
impl Fsm for EllipseToolFsmState {
|
||||||
type ToolData = EllipseToolData;
|
type ToolData = EllipseToolData;
|
||||||
type ToolOptions = ();
|
type ToolOptions = EllipseToolOptions;
|
||||||
|
|
||||||
fn transition(
|
fn transition(
|
||||||
self,
|
self,
|
||||||
|
|
@ -106,7 +204,7 @@ impl Fsm for EllipseToolFsmState {
|
||||||
render_data,
|
render_data,
|
||||||
..
|
..
|
||||||
}: &mut ToolActionHandlerData,
|
}: &mut ToolActionHandlerData,
|
||||||
_tool_options: &Self::ToolOptions,
|
tool_options: &Self::ToolOptions,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
use EllipseToolFsmState::*;
|
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::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||||
graph_modification_utils::set_manipulator_mirror_angle(&manipulator_groups, &layer_path, true, 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 {
|
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,
|
layer: layer_path,
|
||||||
fill: Fill::solid(global_tool_data.primary_color),
|
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
||||||
});
|
});
|
||||||
|
|
||||||
Drawing
|
Drawing
|
||||||
|
|
@ -157,6 +260,13 @@ impl Fsm for EllipseToolFsmState {
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
(_, WorkingColorChanged) => {
|
||||||
|
responses.add(EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::WorkingColors(
|
||||||
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
|
self
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
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::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 crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
|
||||||
use document_legacy::LayerId;
|
use document_legacy::LayerId;
|
||||||
use document_legacy::Operation;
|
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 glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -23,11 +27,17 @@ pub struct FreehandTool {
|
||||||
|
|
||||||
pub struct FreehandOptions {
|
pub struct FreehandOptions {
|
||||||
line_weight: f64,
|
line_weight: f64,
|
||||||
|
fill: ToolColorOptions,
|
||||||
|
stroke: ToolColorOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FreehandOptions {
|
impl Default for FreehandOptions {
|
||||||
fn default() -> Self {
|
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
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
WorkingColorChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
DragStart,
|
DragStart,
|
||||||
DragStop,
|
DragStop,
|
||||||
PointerMove,
|
PointerMove,
|
||||||
UpdateOptions(FreehandToolMessageOptionsUpdate),
|
UpdateOptions(FreehandOptionsUpdate),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||||
pub enum FreehandToolMessageOptionsUpdate {
|
pub enum FreehandOptionsUpdate {
|
||||||
|
FillColor(Option<Color>),
|
||||||
|
FillColorType(ToolColorType),
|
||||||
LineWeight(f64),
|
LineWeight(f64),
|
||||||
|
StrokeColor(Option<Color>),
|
||||||
|
StrokeColorType(ToolColorType),
|
||||||
|
WorkingColors(Option<Color>, Option<Color>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[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 {
|
impl PropertyHolder for FreehandTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
let mut widgets = self.options.fill.create_widgets(
|
||||||
.unit(" px")
|
"Fill",
|
||||||
.label("Weight")
|
true,
|
||||||
.min(1.)
|
WidgetCallback::new(|_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(None)).into()),
|
||||||
.on_update(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandToolMessageOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
|color_type: ToolColorType| WidgetCallback::new(move |_| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||||
.widget_holder();
|
WidgetCallback::new(|color: &ColorInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::FillColor(color.value)).into()),
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
);
|
||||||
|
|
||||||
|
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>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||||
if let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message {
|
if let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,6 +179,7 @@ impl ToolTransition for FreehandTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
tool_abort: Some(FreehandToolMessage::Abort.into()),
|
tool_abort: Some(FreehandToolMessage::Abort.into()),
|
||||||
|
working_color_changed: Some(FreehandToolMessage::WorkingColorChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -161,7 +224,7 @@ impl Fsm for FreehandToolFsmState {
|
||||||
|
|
||||||
tool_data.weight = tool_options.line_weight;
|
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
|
Drawing
|
||||||
}
|
}
|
||||||
|
|
@ -172,14 +235,14 @@ impl Fsm for FreehandToolFsmState {
|
||||||
tool_data.points.push(pos);
|
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
|
||||||
}
|
}
|
||||||
(Drawing, DragStop) | (Drawing, Abort) => {
|
(Drawing, DragStop) | (Drawing, Abort) => {
|
||||||
if tool_data.points.len() >= 2 {
|
if tool_data.points.len() >= 2 {
|
||||||
responses.add(remove_preview(tool_data));
|
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);
|
responses.add(DocumentMessage::CommitTransaction);
|
||||||
} else {
|
} else {
|
||||||
responses.add(DocumentMessage::AbortTransaction);
|
responses.add(DocumentMessage::AbortTransaction);
|
||||||
|
|
@ -190,6 +253,13 @@ impl Fsm for FreehandToolFsmState {
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
(_, FreehandToolMessage::WorkingColorChanged) => {
|
||||||
|
responses.add(FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::WorkingColors(
|
||||||
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
|
self
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -215,14 +285,19 @@ fn remove_preview(data: &FreehandToolData) -> Message {
|
||||||
Operation::DeleteLayer { path: data.path.clone().unwrap() }.into()
|
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 subpath = bezier_rs::Subpath::from_anchors(data.points.iter().copied(), false);
|
||||||
|
|
||||||
let layer_path = data.path.clone().unwrap();
|
let layer_path = data.path.clone().unwrap();
|
||||||
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
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 {
|
responses.add(GraphOperationMessage::StrokeSet {
|
||||||
layer: layer_path,
|
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)
|
.selected_index((self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial) as u32)
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
|
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![gradient_type] }]))
|
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::frontend::utility_types::MouseCursorIcon;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
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::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
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 document_legacy::LayerId;
|
||||||
use graphene_core::vector::style::Stroke;
|
use graphene_core::vector::style::Stroke;
|
||||||
|
use graphene_core::Color;
|
||||||
|
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -25,11 +29,15 @@ pub struct LineTool {
|
||||||
|
|
||||||
pub struct LineOptions {
|
pub struct LineOptions {
|
||||||
line_weight: f64,
|
line_weight: f64,
|
||||||
|
stroke: ToolColorOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LineOptions {
|
impl Default for LineOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { line_weight: 5. }
|
Self {
|
||||||
|
line_weight: 5.,
|
||||||
|
stroke: ToolColorOptions::new_primary(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,6 +48,8 @@ pub enum LineToolMessage {
|
||||||
// Standard messages
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
WorkingColorChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
DragStart,
|
DragStart,
|
||||||
|
|
@ -56,6 +66,9 @@ pub enum LineToolMessage {
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||||
pub enum LineOptionsUpdate {
|
pub enum LineOptionsUpdate {
|
||||||
LineWeight(f64),
|
LineWeight(f64),
|
||||||
|
StrokeColor(Option<Color>),
|
||||||
|
StrokeColorType(ToolColorType),
|
||||||
|
WorkingColors(Option<Color>, Option<Color>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolMetadata for LineTool {
|
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 {
|
impl PropertyHolder for LineTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
let mut widgets = self.options.stroke.create_widgets(
|
||||||
.unit(" px")
|
"Stroke",
|
||||||
.label("Weight")
|
true,
|
||||||
.min(0.)
|
WidgetCallback::new(|_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(None)).into()),
|
||||||
.on_update(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
|color_type: ToolColorType| WidgetCallback::new(move |_| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColorType(color_type.clone())).into()),
|
||||||
.widget_holder();
|
WidgetCallback::new(|color: &ColorInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::StrokeColor(color.value)).into()),
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
);
|
||||||
|
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 {
|
if let ToolMessage::Line(LineToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
LineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,6 +147,7 @@ impl ToolTransition for LineTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
tool_abort: Some(LineToolMessage::Abort.into()),
|
tool_abort: Some(LineToolMessage::Abort.into()),
|
||||||
|
working_color_changed: Some(LineToolMessage::WorkingColorChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -164,7 +206,7 @@ impl Fsm for LineToolFsmState {
|
||||||
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||||
responses.add(GraphOperationMessage::StrokeSet {
|
responses.add(GraphOperationMessage::StrokeSet {
|
||||||
layer: layer_path,
|
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;
|
tool_data.weight = tool_options.line_weight;
|
||||||
|
|
@ -192,6 +234,13 @@ impl Fsm for LineToolFsmState {
|
||||||
tool_data.path = None;
|
tool_data.path = None;
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
(_, WorkingColorChanged) => {
|
||||||
|
responses.add(LineToolMessage::UpdateOptions(LineOptionsUpdate::WorkingColors(
|
||||||
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
|
self
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use crate::consts::LINE_ROTATE_SNAP_ANGLE;
|
use crate::consts::LINE_ROTATE_SNAP_ANGLE;
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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_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::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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::portfolio::document::node_graph::VectorDataModification;
|
use crate::messages::portfolio::document::node_graph::VectorDataModification;
|
||||||
use crate::messages::prelude::*;
|
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::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
|
||||||
|
|
||||||
|
|
@ -30,52 +31,18 @@ pub struct PenTool {
|
||||||
options: PenOptions,
|
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 {
|
pub struct PenOptions {
|
||||||
line_weight: f64,
|
line_weight: f64,
|
||||||
fill: PenColorOptions,
|
fill: ToolColorOptions,
|
||||||
stroke: PenColorOptions,
|
stroke: ToolColorOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PenOptions {
|
impl Default for PenOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
line_weight: 5.,
|
line_weight: 5.,
|
||||||
fill: PenColorOptions {
|
fill: ToolColorOptions::new_secondary(),
|
||||||
color: Some(Color::BLACK),
|
stroke: ToolColorOptions::new_primary(),
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,12 +86,11 @@ enum PenToolFsmState {
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||||
pub enum PenOptionsUpdate {
|
pub enum PenOptionsUpdate {
|
||||||
FillColor(Option<Color>),
|
FillColor(Option<Color>),
|
||||||
FillColorType(PenColorType),
|
FillColorType(ToolColorType),
|
||||||
LineWeight(f64),
|
LineWeight(f64),
|
||||||
PrimaryColor(Option<Color>),
|
|
||||||
SecondaryColor(Option<Color>),
|
|
||||||
StrokeColor(Option<Color>),
|
StrokeColor(Option<Color>),
|
||||||
StrokeColorType(PenColorType),
|
StrokeColorType(ToolColorType),
|
||||||
|
WorkingColors(Option<Color>, Option<Color>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolMetadata for PenTool {
|
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 {
|
fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||||
NumberInput::new(Some(line_weight))
|
NumberInput::new(Some(line_weight))
|
||||||
.unit(" px")
|
.unit(" px")
|
||||||
|
|
@ -229,11 +116,26 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||||
|
|
||||||
impl PropertyHolder for PenTool {
|
impl PropertyHolder for PenTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
let mut widgets = create_fill_widget(&self.options.fill);
|
let mut widgets = self.options.fill.create_widgets(
|
||||||
widgets.push(Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder());
|
"Fill",
|
||||||
widgets.append(&mut create_stroke_widget(&self.options.stroke));
|
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(WidgetHolder::unrelated_separator());
|
||||||
widgets.push(create_weight_widget(self.options.line_weight));
|
widgets.push(create_weight_widget(self.options.line_weight));
|
||||||
|
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -244,22 +146,20 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool
|
||||||
match action {
|
match action {
|
||||||
PenOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
PenOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||||
PenOptionsUpdate::FillColor(color) => {
|
PenOptionsUpdate::FillColor(color) => {
|
||||||
self.options.fill.color = color;
|
self.options.fill.custom_color = color;
|
||||||
self.options.fill.color_type = PenColorType::Custom;
|
self.options.fill.color_type = ToolColorType::Custom;
|
||||||
}
|
}
|
||||||
PenOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
PenOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
||||||
PenOptionsUpdate::StrokeColor(color) => {
|
PenOptionsUpdate::StrokeColor(color) => {
|
||||||
self.options.stroke.color = color;
|
self.options.stroke.custom_color = color;
|
||||||
self.options.stroke.color_type = PenColorType::Custom;
|
self.options.stroke.color_type = ToolColorType::Custom;
|
||||||
}
|
}
|
||||||
PenOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
|
PenOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type,
|
||||||
PenOptionsUpdate::PrimaryColor(color) => {
|
PenOptionsUpdate::WorkingColors(primary, secondary) => {
|
||||||
self.options.stroke.primary_working_color = color;
|
self.options.stroke.primary_working_color = primary;
|
||||||
self.options.fill.primary_working_color = color;
|
self.options.stroke.secondary_working_color = secondary;
|
||||||
}
|
self.options.fill.primary_working_color = primary;
|
||||||
PenOptionsUpdate::SecondaryColor(color) => {
|
self.options.fill.secondary_working_color = secondary;
|
||||||
self.options.stroke.secondary_working_color = color;
|
|
||||||
self.options.fill.secondary_working_color = color;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -701,8 +601,10 @@ impl Fsm for PenToolFsmState {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(_, PenToolMessage::WorkingColorChanged) => {
|
(_, PenToolMessage::WorkingColorChanged) => {
|
||||||
responses.add(PenToolMessage::UpdateOptions(PenOptionsUpdate::PrimaryColor(Some(global_tool_data.primary_color))));
|
responses.add(PenToolMessage::UpdateOptions(PenOptionsUpdate::WorkingColors(
|
||||||
responses.add(PenToolMessage::UpdateOptions(PenOptionsUpdate::SecondaryColor(Some(global_tool_data.secondary_color))));
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(PenToolFsmState::Ready, PenToolMessage::DragStart) => {
|
(PenToolFsmState::Ready, PenToolMessage::DragStart) => {
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,63 @@
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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_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::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::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
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::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graphene_core::vector::style::Fill;
|
use graphene_core::vector::style::{Fill, Stroke};
|
||||||
|
use graphene_core::Color;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RectangleTool {
|
pub struct RectangleTool {
|
||||||
fsm_state: RectangleToolFsmState,
|
fsm_state: RectangleToolFsmState,
|
||||||
tool_data: RectangleToolData,
|
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]
|
#[remain::sorted]
|
||||||
#[impl_message(Message, ToolMessage, Rectangle)]
|
#[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 {
|
pub enum RectangleToolMessage {
|
||||||
// Standard messages
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
WorkingColorChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
DragStart,
|
DragStart,
|
||||||
|
|
@ -32,13 +66,76 @@ pub enum RectangleToolMessage {
|
||||||
center: Key,
|
center: Key,
|
||||||
lock_ratio: 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 {
|
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>) {
|
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 {
|
fn actions(&self) -> ActionList {
|
||||||
|
|
@ -73,6 +170,7 @@ impl ToolTransition for RectangleTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
tool_abort: Some(RectangleToolMessage::Abort.into()),
|
tool_abort: Some(RectangleToolMessage::Abort.into()),
|
||||||
|
working_color_changed: Some(RectangleToolMessage::WorkingColorChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +190,7 @@ struct RectangleToolData {
|
||||||
|
|
||||||
impl Fsm for RectangleToolFsmState {
|
impl Fsm for RectangleToolFsmState {
|
||||||
type ToolData = RectangleToolData;
|
type ToolData = RectangleToolData;
|
||||||
type ToolOptions = ();
|
type ToolOptions = RectangleToolOptions;
|
||||||
|
|
||||||
fn transition(
|
fn transition(
|
||||||
self,
|
self,
|
||||||
|
|
@ -105,7 +203,7 @@ impl Fsm for RectangleToolFsmState {
|
||||||
render_data,
|
render_data,
|
||||||
..
|
..
|
||||||
}: &mut ToolActionHandlerData,
|
}: &mut ToolActionHandlerData,
|
||||||
_tool_options: &Self::ToolOptions,
|
tool_options: &Self::ToolOptions,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
use RectangleToolFsmState::*;
|
use RectangleToolFsmState::*;
|
||||||
|
|
@ -124,9 +222,16 @@ impl Fsm for RectangleToolFsmState {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
shape_data.path = Some(layer_path.clone());
|
shape_data.path = Some(layer_path.clone());
|
||||||
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||||
|
|
||||||
|
let fill_color = tool_options.fill.active_color();
|
||||||
responses.add(GraphOperationMessage::FillSet {
|
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,
|
layer: layer_path,
|
||||||
fill: Fill::solid(global_tool_data.primary_color),
|
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
||||||
});
|
});
|
||||||
|
|
||||||
Drawing
|
Drawing
|
||||||
|
|
@ -151,6 +256,13 @@ impl Fsm for RectangleToolFsmState {
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
(_, WorkingColorChanged) => {
|
||||||
|
responses.add(RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::WorkingColors(
|
||||||
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
|
self
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
} else {
|
} 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::assist_widgets::{PivotAssist, PivotPosition};
|
||||||
use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton};
|
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::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::misc::{AlignAggregate, AlignAxis, FlipAxis};
|
||||||
use crate::messages::portfolio::document::utility_types::transformation::Selected;
|
use crate::messages::portfolio::document::utility_types::transformation::Selected;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
@ -137,7 +136,7 @@ impl PropertyHolder for SelectTool {
|
||||||
PivotAssist::new(self.tool_data.pivot.to_pivot_position())
|
PivotAssist::new(self.tool_data.pivot.to_pivot_position())
|
||||||
.on_update(|pivot_assist: &PivotAssist| SelectToolMessage::SetPivot { position: pivot_assist.position }.into())
|
.on_update(|pivot_assist: &PivotAssist| SelectToolMessage::SetPivot { position: pivot_assist.position }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
WidgetHolder::section_separator(),
|
||||||
IconButton::new("AlignLeft", 24)
|
IconButton::new("AlignLeft", 24)
|
||||||
.tooltip("Align Left")
|
.tooltip("Align Left")
|
||||||
.on_update(|_| {
|
.on_update(|_| {
|
||||||
|
|
@ -201,7 +200,7 @@ impl PropertyHolder for SelectTool {
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
WidgetHolder::related_separator(),
|
WidgetHolder::related_separator(),
|
||||||
PopoverButton::new("Align", "Coming soon").widget_holder(),
|
PopoverButton::new("Align", "Coming soon").widget_holder(),
|
||||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
WidgetHolder::section_separator(),
|
||||||
IconButton::new("FlipHorizontal", 24)
|
IconButton::new("FlipHorizontal", 24)
|
||||||
.tooltip("Flip Horizontal")
|
.tooltip("Flip Horizontal")
|
||||||
.on_update(|_| SelectToolMessage::FlipHorizontal.into())
|
.on_update(|_| SelectToolMessage::FlipHorizontal.into())
|
||||||
|
|
@ -216,7 +215,7 @@ impl PropertyHolder for SelectTool {
|
||||||
text: "Coming soon".into(),
|
text: "Coming soon".into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})),
|
})),
|
||||||
Separator::new(SeparatorDirection::Horizontal, SeparatorType::Section).widget_holder(),
|
WidgetHolder::section_separator(),
|
||||||
IconButton::new("BooleanUnion", 24)
|
IconButton::new("BooleanUnion", 24)
|
||||||
.tooltip("Boolean Union (coming soon)")
|
.tooltip("Boolean Union (coming soon)")
|
||||||
.on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(1091) }.into())
|
.on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(1091) }.into())
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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_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::widgets::input_widgets::NumberInput;
|
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::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::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
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::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
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 glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -21,22 +24,32 @@ pub struct ShapeTool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShapeOptions {
|
pub struct ShapeOptions {
|
||||||
|
line_weight: f64,
|
||||||
|
fill: ToolColorOptions,
|
||||||
|
stroke: ToolColorOptions,
|
||||||
vertices: u32,
|
vertices: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ShapeOptions {
|
impl Default for ShapeOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { vertices: 5 }
|
Self {
|
||||||
|
vertices: 5,
|
||||||
|
line_weight: 5.,
|
||||||
|
fill: ToolColorOptions::new_secondary(),
|
||||||
|
stroke: ToolColorOptions::new_primary(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
#[impl_message(Message, ToolMessage, Shape)]
|
#[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 {
|
pub enum ShapeToolMessage {
|
||||||
// Standard messages
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
WorkingColorChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
DragStart,
|
DragStart,
|
||||||
|
|
@ -49,9 +62,15 @@ pub enum ShapeToolMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||||
pub enum ShapeOptionsUpdate {
|
pub enum ShapeOptionsUpdate {
|
||||||
|
FillColor(Option<Color>),
|
||||||
|
FillColorType(ToolColorType),
|
||||||
|
LineWeight(f64),
|
||||||
|
StrokeColor(Option<Color>),
|
||||||
|
StrokeColorType(ToolColorType),
|
||||||
Vertices(u32),
|
Vertices(u32),
|
||||||
|
WorkingColors(Option<Color>, Option<Color>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolMetadata for ShapeTool {
|
impl ToolMetadata for ShapeTool {
|
||||||
|
|
@ -66,26 +85,84 @@ impl ToolMetadata for ShapeTool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PropertyHolder for ShapeTool {
|
fn create_sides_widget(vertices: u32) -> WidgetHolder {
|
||||||
fn properties(&self) -> Layout {
|
NumberInput::new(Some(vertices as f64))
|
||||||
let sides = NumberInput::new(Some(self.options.vertices as f64))
|
.label("Sides")
|
||||||
.label("Sides")
|
.int()
|
||||||
.int()
|
.min(3.)
|
||||||
.min(3.)
|
.max(1000.)
|
||||||
.max(1000.)
|
.mode(crate::messages::layout::utility_types::widget_prelude::NumberInputMode::Increment)
|
||||||
.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())
|
||||||
.on_update(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(number_input.value.unwrap() as u32)).into())
|
.widget_holder()
|
||||||
.widget_holder();
|
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![sides] }]))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
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>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||||
if let ToolMessage::Shape(ShapeToolMessage::UpdateOptions(action)) = message {
|
if let ToolMessage::Shape(ShapeToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
ShapeOptionsUpdate::Vertices(vertices) => self.options.vertices = vertices,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,6 +189,7 @@ impl ToolTransition for ShapeTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
tool_abort: Some(ShapeToolMessage::Abort.into()),
|
tool_abort: Some(ShapeToolMessage::Abort.into()),
|
||||||
|
working_color_changed: Some(ShapeToolMessage::WorkingColorChanged.into()),
|
||||||
..Default::default()
|
..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.);
|
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);
|
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||||
|
|
||||||
|
let fill_color = tool_options.fill.active_color();
|
||||||
responses.add(GraphOperationMessage::FillSet {
|
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,
|
layer: layer_path,
|
||||||
fill: Fill::solid(global_tool_data.primary_color),
|
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
||||||
});
|
});
|
||||||
|
|
||||||
Drawing
|
Drawing
|
||||||
|
|
@ -189,6 +274,13 @@ impl Fsm for ShapeToolFsmState {
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
(_, WorkingColorChanged) => {
|
||||||
|
responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::WorkingColors(
|
||||||
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
|
self
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,20 @@
|
||||||
use crate::consts::DRAG_THRESHOLD;
|
use crate::consts::DRAG_THRESHOLD;
|
||||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
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_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::layout::utility_types::widgets::input_widgets::NumberInput;
|
||||||
use crate::messages::prelude::*;
|
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::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
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 crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
|
||||||
use document_legacy::{LayerId, Operation};
|
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 glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -24,11 +28,17 @@ pub struct SplineTool {
|
||||||
|
|
||||||
pub struct SplineOptions {
|
pub struct SplineOptions {
|
||||||
line_weight: f64,
|
line_weight: f64,
|
||||||
|
fill: ToolColorOptions,
|
||||||
|
stroke: ToolColorOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SplineOptions {
|
impl Default for SplineOptions {
|
||||||
fn default() -> Self {
|
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
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
WorkingColorChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
Confirm,
|
Confirm,
|
||||||
|
|
@ -59,7 +71,12 @@ enum SplineToolFsmState {
|
||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||||
pub enum SplineOptionsUpdate {
|
pub enum SplineOptionsUpdate {
|
||||||
|
FillColor(Option<Color>),
|
||||||
|
FillColorType(ToolColorType),
|
||||||
LineWeight(f64),
|
LineWeight(f64),
|
||||||
|
StrokeColor(Option<Color>),
|
||||||
|
StrokeColorType(ToolColorType),
|
||||||
|
WorkingColors(Option<Color>, Option<Color>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolMetadata for SplineTool {
|
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 {
|
impl PropertyHolder for SplineTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
let weight = NumberInput::new(Some(self.options.line_weight))
|
let mut widgets = self.options.fill.create_widgets(
|
||||||
.unit(" px")
|
"Fill",
|
||||||
.label("Weight")
|
true,
|
||||||
.min(0.)
|
WidgetCallback::new(|_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(None)).into()),
|
||||||
.on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
|
|color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||||
.widget_holder();
|
WidgetCallback::new(|color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(color.value)).into()),
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![weight] }]))
|
);
|
||||||
|
|
||||||
|
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 {
|
if let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message {
|
||||||
match action {
|
match action {
|
||||||
SplineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,6 +185,7 @@ impl ToolTransition for SplineTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
tool_abort: Some(SplineToolMessage::Abort.into()),
|
tool_abort: Some(SplineToolMessage::Abort.into()),
|
||||||
|
working_color_changed: Some(SplineToolMessage::WorkingColorChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +241,7 @@ impl Fsm for SplineToolFsmState {
|
||||||
|
|
||||||
tool_data.weight = tool_options.line_weight;
|
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
|
Drawing
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +257,7 @@ impl Fsm for SplineToolFsmState {
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(remove_preview(tool_data));
|
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
|
||||||
}
|
}
|
||||||
|
|
@ -204,14 +267,14 @@ impl Fsm for SplineToolFsmState {
|
||||||
tool_data.next_point = pos;
|
tool_data.next_point = pos;
|
||||||
|
|
||||||
responses.add(remove_preview(tool_data));
|
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
|
||||||
}
|
}
|
||||||
(Drawing, Confirm) | (Drawing, Abort) => {
|
(Drawing, Confirm) | (Drawing, Abort) => {
|
||||||
if tool_data.points.len() >= 2 {
|
if tool_data.points.len() >= 2 {
|
||||||
responses.add(remove_preview(tool_data));
|
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);
|
responses.add(DocumentMessage::CommitTransaction);
|
||||||
} else {
|
} else {
|
||||||
responses.add(DocumentMessage::AbortTransaction);
|
responses.add(DocumentMessage::AbortTransaction);
|
||||||
|
|
@ -223,6 +286,13 @@ impl Fsm for SplineToolFsmState {
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
(_, WorkingColorChanged) => {
|
||||||
|
responses.add(SplineToolMessage::UpdateOptions(SplineOptionsUpdate::WorkingColors(
|
||||||
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
|
self
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -254,7 +324,7 @@ fn remove_preview(tool_data: &SplineToolData) -> Message {
|
||||||
.into()
|
.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();
|
let mut points = tool_data.points.clone();
|
||||||
if show_preview {
|
if show_preview {
|
||||||
points.push(tool_data.next_point)
|
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::new_vector_layer(vec![subpath], layer_path.clone(), responses);
|
||||||
graph_modification_utils::set_manipulator_mirror_angle(&manipulator_groups, &layer_path, true, 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 {
|
responses.add(GraphOperationMessage::StrokeSet {
|
||||||
layer: layer_path.clone(),
|
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::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::layout_widget::{Layout, LayoutGroup, PropertyHolder, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
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::portfolio::document::node_graph::new_text_network;
|
||||||
use crate::messages::prelude::*;
|
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::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
|
||||||
|
|
@ -34,6 +35,7 @@ pub struct TextOptions {
|
||||||
font_size: u32,
|
font_size: u32,
|
||||||
font_name: String,
|
font_name: String,
|
||||||
font_style: String,
|
font_style: String,
|
||||||
|
fill: ToolColorOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextOptions {
|
impl Default for TextOptions {
|
||||||
|
|
@ -42,20 +44,22 @@ impl Default for TextOptions {
|
||||||
font_size: 24,
|
font_size: 24,
|
||||||
font_name: "Merriweather".into(),
|
font_name: "Merriweather".into(),
|
||||||
font_style: "Normal (400)".into(),
|
font_style: "Normal (400)".into(),
|
||||||
|
fill: ToolColorOptions::new_primary(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
#[impl_message(Message, ToolMessage, Text)]
|
#[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 {
|
pub enum TextToolMessage {
|
||||||
// Standard messages
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
DocumentIsDirty,
|
DocumentIsDirty,
|
||||||
|
#[remain::unsorted]
|
||||||
|
WorkingColorChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
CommitText,
|
CommitText,
|
||||||
|
|
@ -71,10 +75,13 @@ pub enum TextToolMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
|
||||||
pub enum TextOptionsUpdate {
|
pub enum TextOptionsUpdate {
|
||||||
|
FillColor(Option<Color>),
|
||||||
|
FillColorType(ToolColorType),
|
||||||
Font { family: String, style: String },
|
Font { family: String, style: String },
|
||||||
FontSize(u32),
|
FontSize(u32),
|
||||||
|
WorkingColors(Option<Color>, Option<Color>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolMetadata for TextTool {
|
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 {
|
impl PropertyHolder for TextTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
let font = FontInput {
|
let mut widgets = create_text_widgets(self);
|
||||||
is_style_picker: false,
|
|
||||||
font_family: self.options.font_name.clone(),
|
widgets.push(WidgetHolder::section_separator());
|
||||||
font_style: self.options.font_style.clone(),
|
|
||||||
on_update: WidgetCallback::new(|font_input: &FontInput| {
|
widgets.append(&mut self.options.fill.create_widgets(
|
||||||
TextToolMessage::UpdateOptions(TextOptionsUpdate::Font {
|
"Fill",
|
||||||
family: font_input.font_family.clone(),
|
true,
|
||||||
style: font_input.font_style.clone(),
|
WidgetCallback::new(|_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(None)).into()),
|
||||||
})
|
|color_type: ToolColorType| WidgetCallback::new(move |_| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColorType(color_type.clone())).into()),
|
||||||
.into()
|
WidgetCallback::new(|color: &ColorInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FillColor(color.value)).into()),
|
||||||
}),
|
));
|
||||||
..Default::default()
|
|
||||||
}
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
|
||||||
.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],
|
|
||||||
}]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +164,22 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for TextToo
|
||||||
self.register_properties(responses, LayoutTarget::ToolOptions);
|
self.register_properties(responses, LayoutTarget::ToolOptions);
|
||||||
}
|
}
|
||||||
TextOptionsUpdate::FontSize(font_size) => self.options.font_size = font_size,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,6 +208,7 @@ impl ToolTransition for TextTool {
|
||||||
document_dirty: Some(TextToolMessage::DocumentIsDirty.into()),
|
document_dirty: Some(TextToolMessage::DocumentIsDirty.into()),
|
||||||
tool_abort: Some(TextToolMessage::Abort.into()),
|
tool_abort: Some(TextToolMessage::Abort.into()),
|
||||||
selection_changed: Some(TextToolMessage::DocumentIsDirty.into()),
|
selection_changed: Some(TextToolMessage::DocumentIsDirty.into()),
|
||||||
|
working_color_changed: Some(TextToolMessage::WorkingColorChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +225,7 @@ pub struct EditingText {
|
||||||
text: String,
|
text: String,
|
||||||
font: Font,
|
font: Font,
|
||||||
font_size: f64,
|
font_size: f64,
|
||||||
color: Color,
|
color: Option<Color>,
|
||||||
transform: DAffine2,
|
transform: DAffine2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,7 +248,7 @@ impl TextToolData {
|
||||||
text: editing_text.text.clone(),
|
text: editing_text.text.clone(),
|
||||||
line_width: None,
|
line_width: None,
|
||||||
font_size: editing_text.font_size,
|
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(),
|
url: render_data.font_cache.get_preview_url(&editing_text.font).cloned().unwrap_or_default(),
|
||||||
transform: editing_text.transform.to_cols_array(),
|
transform: editing_text.transform.to_cols_array(),
|
||||||
});
|
});
|
||||||
|
|
@ -237,7 +274,7 @@ impl TextToolData {
|
||||||
text: text.clone(),
|
text: text.clone(),
|
||||||
font: font.clone(),
|
font: font.clone(),
|
||||||
font_size,
|
font_size,
|
||||||
color,
|
color: Some(color),
|
||||||
transform,
|
transform,
|
||||||
});
|
});
|
||||||
self.new_text = text.clone();
|
self.new_text = text.clone();
|
||||||
|
|
@ -291,7 +328,7 @@ impl TextToolData {
|
||||||
});
|
});
|
||||||
responses.add(GraphOperationMessage::FillSet {
|
responses.add(GraphOperationMessage::FillSet {
|
||||||
layer: self.layer_path.clone(),
|
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 {
|
responses.add(GraphOperationMessage::TransformSet {
|
||||||
layer: self.layer_path.clone(),
|
layer: self.layer_path.clone(),
|
||||||
|
|
@ -470,7 +507,7 @@ impl Fsm for TextToolFsmState {
|
||||||
transform: DAffine2::from_translation(input.mouse.position),
|
transform: DAffine2::from_translation(input.mouse.position),
|
||||||
font_size: tool_options.font_size as f64,
|
font_size: tool_options.font_size as f64,
|
||||||
font: Font::new(tool_options.font_name.clone(), tool_options.font_style.clone()),
|
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.new_text = String::new();
|
||||||
tool_data.layer_path = document.get_path_for_new_layer();
|
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);
|
tool_data.update_bounds_overlay(document, render_data, responses);
|
||||||
TextToolFsmState::Editing
|
TextToolFsmState::Editing
|
||||||
}
|
}
|
||||||
|
(_, TextToolMessage::WorkingColorChanged) => {
|
||||||
|
responses.add(TextToolMessage::UpdateOptions(TextOptionsUpdate::WorkingColors(
|
||||||
|
Some(global_tool_data.primary_color),
|
||||||
|
Some(global_tool_data.secondary_color),
|
||||||
|
)));
|
||||||
|
self
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
export let value: Color;
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
// export let allowTransparency = false;
|
// export let allowTransparency = false;
|
||||||
// export let allowNone = false;
|
|
||||||
// export let disabled = false;
|
// export let disabled = false;
|
||||||
|
export let allowNone = false;
|
||||||
export let tooltip: string | undefined = undefined;
|
export let tooltip: string | undefined = undefined;
|
||||||
export let sharpRightCorners = false;
|
export let sharpRightCorners = false;
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
value = detail;
|
value = detail;
|
||||||
dispatch("value", detail);
|
dispatch("value", detail);
|
||||||
}}
|
}}
|
||||||
allowNone={true}
|
{allowNone}
|
||||||
/>
|
/>
|
||||||
</LayoutRow>
|
</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 CheckboxChecked from "@graphite-frontend/assets/icon-16px-solid/checkbox-checked.svg";
|
||||||
import CheckboxUnchecked from "@graphite-frontend/assets/icon-16px-solid/checkbox-unchecked.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 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 Edit from "@graphite-frontend/assets/icon-16px-solid/edit.svg";
|
||||||
import Eyedropper from "@graphite-frontend/assets/icon-16px-solid/eyedropper.svg";
|
import Eyedropper from "@graphite-frontend/assets/icon-16px-solid/eyedropper.svg";
|
||||||
import EyeHidden from "@graphite-frontend/assets/icon-16px-solid/eye-hidden.svg";
|
import EyeHidden from "@graphite-frontend/assets/icon-16px-solid/eye-hidden.svg";
|
||||||
|
|
@ -155,6 +156,7 @@ const SOLID_16PX = {
|
||||||
CheckboxChecked: { svg: CheckboxChecked, size: 16 },
|
CheckboxChecked: { svg: CheckboxChecked, size: 16 },
|
||||||
CheckboxUnchecked: { svg: CheckboxUnchecked, size: 16 },
|
CheckboxUnchecked: { svg: CheckboxUnchecked, size: 16 },
|
||||||
Copy: { svg: Copy, size: 16 },
|
Copy: { svg: Copy, size: 16 },
|
||||||
|
CustomColor: { svg: CustomColor, size: 16 },
|
||||||
Edit: { svg: Edit, size: 16 },
|
Edit: { svg: Edit, size: 16 },
|
||||||
Eyedropper: { svg: Eyedropper, size: 16 },
|
Eyedropper: { svg: Eyedropper, size: 16 },
|
||||||
EyeHidden: { svg: EyeHidden, size: 16 },
|
EyeHidden: { svg: EyeHidden, size: 16 },
|
||||||
|
|
|
||||||
|
|
@ -832,9 +832,10 @@ export class ColorInput extends WidgetProps {
|
||||||
)
|
)
|
||||||
value!: Color;
|
value!: Color;
|
||||||
|
|
||||||
|
allowNone!: boolean;
|
||||||
|
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
// allowTransparency!: boolean;
|
// allowTransparency!: boolean;
|
||||||
// allowNone!: boolean;
|
|
||||||
// disabled!: boolean;
|
// disabled!: boolean;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue