mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 10:18:11 +08:00
Layout system implementation and applied to tool options bar (#499)
* initial layout system with tool options * cargo fmt * cargo fmt again * document bar defined on the backend * cargo fmt * removed RC<RefCell> * cargo fmt * - fix increment behavior - removed hashmap from layout message handler - removed no op message from layoutMessage * cargo fmt * only send documentBar when zoom or rotation is updated * ctrl-0 changes zoom properly * Code review changes Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
121a68ad3c
commit
96d3ef2650
@@ -2,5 +2,4 @@ pub mod snapping;
|
||||
pub mod tool;
|
||||
pub mod tool_message;
|
||||
pub mod tool_message_handler;
|
||||
pub mod tool_options;
|
||||
pub mod tools;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::tool_options::{SelectAppendMode, ShapeType, ToolOptions};
|
||||
use super::tools::*;
|
||||
use crate::communication::message_handler::MessageHandler;
|
||||
use crate::document::DocumentMessageHandler;
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphene::color::Color;
|
||||
@@ -15,6 +15,7 @@ pub type ToolActionHandlerData<'a> = (&'a DocumentMessageHandler, &'a DocumentTo
|
||||
|
||||
pub trait Fsm {
|
||||
type ToolData;
|
||||
type ToolOptions;
|
||||
|
||||
#[must_use]
|
||||
fn transition(
|
||||
@@ -23,6 +24,7 @@ pub trait Fsm {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
messages: &mut VecDeque<Message>,
|
||||
) -> Self;
|
||||
@@ -35,14 +37,16 @@ pub trait Fsm {
|
||||
pub struct DocumentToolData {
|
||||
pub primary_color: Color,
|
||||
pub secondary_color: Color,
|
||||
pub tool_options: HashMap<ToolType, ToolOptions>,
|
||||
}
|
||||
|
||||
type SubToolMessageHandler = dyn for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>>;
|
||||
pub trait ToolCommon: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder {}
|
||||
impl<T> ToolCommon for T where T: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder {}
|
||||
|
||||
type Tool = dyn ToolCommon;
|
||||
|
||||
pub struct ToolData {
|
||||
pub active_tool_type: ToolType,
|
||||
pub tools: HashMap<ToolType, Box<SubToolMessageHandler>>,
|
||||
pub tools: HashMap<ToolType, Box<Tool>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ToolData {
|
||||
@@ -52,10 +56,11 @@ impl fmt::Debug for ToolData {
|
||||
}
|
||||
|
||||
impl ToolData {
|
||||
pub fn active_tool_mut(&mut self) -> &mut Box<SubToolMessageHandler> {
|
||||
pub fn active_tool_mut(&mut self) -> &mut Box<Tool> {
|
||||
self.tools.get_mut(&self.active_tool_type).expect("The active tool is not initialized")
|
||||
}
|
||||
pub fn active_tool(&self) -> &SubToolMessageHandler {
|
||||
|
||||
pub fn active_tool(&self) -> &Tool {
|
||||
self.tools.get(&self.active_tool_type).map(|x| x.as_ref()).expect("The active tool is not initialized")
|
||||
}
|
||||
}
|
||||
@@ -98,7 +103,6 @@ impl Default for ToolFsmState {
|
||||
document_tool_data: DocumentToolData {
|
||||
primary_color: Color::BLACK,
|
||||
secondary_color: Color::WHITE,
|
||||
tool_options: default_tool_options(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -114,35 +118,6 @@ impl ToolFsmState {
|
||||
}
|
||||
}
|
||||
|
||||
fn default_tool_options() -> HashMap<ToolType, ToolOptions> {
|
||||
let tool_init = |tool: ToolType| (tool, tool.default_options());
|
||||
[
|
||||
tool_init(ToolType::Select),
|
||||
tool_init(ToolType::Crop),
|
||||
tool_init(ToolType::Navigate),
|
||||
tool_init(ToolType::Eyedropper),
|
||||
tool_init(ToolType::Text),
|
||||
tool_init(ToolType::Fill),
|
||||
tool_init(ToolType::Gradient),
|
||||
tool_init(ToolType::Brush),
|
||||
tool_init(ToolType::Heal),
|
||||
tool_init(ToolType::Clone),
|
||||
tool_init(ToolType::Patch),
|
||||
tool_init(ToolType::BlurSharpen),
|
||||
tool_init(ToolType::Relight),
|
||||
tool_init(ToolType::Path),
|
||||
tool_init(ToolType::Pen),
|
||||
tool_init(ToolType::Freehand),
|
||||
tool_init(ToolType::Spline),
|
||||
tool_init(ToolType::Line),
|
||||
tool_init(ToolType::Rectangle),
|
||||
tool_init(ToolType::Ellipse),
|
||||
tool_init(ToolType::Shape),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum ToolType {
|
||||
@@ -201,36 +176,6 @@ impl fmt::Display for ToolType {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolType {
|
||||
fn default_options(&self) -> ToolOptions {
|
||||
match self {
|
||||
ToolType::Select => ToolOptions::Select { append_mode: SelectAppendMode::New },
|
||||
ToolType::Crop => ToolOptions::Crop {},
|
||||
ToolType::Navigate => ToolOptions::Navigate {},
|
||||
ToolType::Eyedropper => ToolOptions::Eyedropper {},
|
||||
ToolType::Text => ToolOptions::Text { font_size: 14 },
|
||||
ToolType::Fill => ToolOptions::Fill {},
|
||||
ToolType::Gradient => ToolOptions::Gradient {},
|
||||
ToolType::Brush => ToolOptions::Brush {},
|
||||
ToolType::Heal => ToolOptions::Heal {},
|
||||
ToolType::Clone => ToolOptions::Clone {},
|
||||
ToolType::Patch => ToolOptions::Patch {},
|
||||
ToolType::BlurSharpen => ToolOptions::BlurSharpen {},
|
||||
ToolType::Relight => ToolOptions::Relight {},
|
||||
ToolType::Path => ToolOptions::Path {},
|
||||
ToolType::Pen => ToolOptions::Pen { weight: 5 },
|
||||
ToolType::Freehand => ToolOptions::Freehand { weight: 5 },
|
||||
ToolType::Spline => ToolOptions::Spline {},
|
||||
ToolType::Line => ToolOptions::Line { weight: 5 },
|
||||
ToolType::Rectangle => ToolOptions::Rectangle {},
|
||||
ToolType::Ellipse => ToolOptions::Ellipse {},
|
||||
ToolType::Shape => ToolOptions::Shape {
|
||||
shape_type: ShapeType::Polygon { vertices: 6 },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum StandardToolMessageType {
|
||||
Abort,
|
||||
DocumentIsDirty,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::tool::ToolType;
|
||||
use super::tool_options::ToolOptions;
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphene::color::Color;
|
||||
@@ -92,10 +91,6 @@ pub enum ToolMessage {
|
||||
SelectSecondaryColor {
|
||||
color: Color,
|
||||
},
|
||||
SetToolOptions {
|
||||
tool_type: ToolType,
|
||||
tool_options: ToolOptions,
|
||||
},
|
||||
SwapColors,
|
||||
UpdateCursor,
|
||||
UpdateHints,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::tool::{message_to_tool_type, standard_tool_message, update_working_colors, StandardToolMessageType, ToolFsmState};
|
||||
use crate::document::DocumentMessageHandler;
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::layout_message::LayoutTarget;
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphene::color::Color;
|
||||
@@ -60,8 +61,10 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessorMes
|
||||
|
||||
// Notify the frontend about the new active tool to be displayed
|
||||
let tool_name = tool_type.to_string();
|
||||
let tool_options = self.tool_state.document_tool_data.tool_options.get(&tool_type).copied();
|
||||
responses.push_back(FrontendMessage::UpdateActiveTool { tool_name, tool_options }.into());
|
||||
responses.push_back(FrontendMessage::UpdateActiveTool { tool_name }.into());
|
||||
|
||||
// Send Properties to the frontend
|
||||
tool_data.tools.get(&tool_type).unwrap().register_properties(responses, LayoutTarget::ToolOptions);
|
||||
}
|
||||
DocumentIsDirty => {
|
||||
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
||||
@@ -90,11 +93,6 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessorMes
|
||||
|
||||
update_working_colors(document_data, responses);
|
||||
}
|
||||
SetToolOptions { tool_type, tool_options } => {
|
||||
let document_data = &mut self.tool_state.document_tool_data;
|
||||
|
||||
document_data.tool_options.insert(tool_type, tool_options);
|
||||
}
|
||||
SwapColors => {
|
||||
let document_data = &mut self.tool_state.document_tool_data;
|
||||
|
||||
@@ -123,7 +121,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessorMes
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
let mut list = actions!(ToolMessageDiscriminant; ResetColors, SwapColors, ActivateTool, SetToolOptions);
|
||||
let mut list = actions!(ToolMessageDiscriminant; ResetColors, SwapColors, ActivateTool);
|
||||
list.extend(self.tool_state.tool_data.active_tool().actions());
|
||||
|
||||
list
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
pub enum ToolOptions {
|
||||
Select { append_mode: SelectAppendMode },
|
||||
Crop {},
|
||||
Navigate {},
|
||||
Eyedropper {},
|
||||
Text { font_size: u32 },
|
||||
Fill {},
|
||||
Gradient {},
|
||||
Brush {},
|
||||
Heal {},
|
||||
Clone {},
|
||||
Patch {},
|
||||
BlurSharpen {},
|
||||
Relight {},
|
||||
Path {},
|
||||
Pen { weight: u32 },
|
||||
Freehand { weight: u32 },
|
||||
Spline {},
|
||||
Line { weight: u32 },
|
||||
Rectangle {},
|
||||
Ellipse {},
|
||||
Shape { shape_type: ShapeType },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
pub enum SelectAppendMode {
|
||||
New,
|
||||
Add,
|
||||
Subtract,
|
||||
Intersect,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
pub enum ShapeType {
|
||||
Star { vertices: u32 },
|
||||
Polygon { vertices: u32 },
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::message_prelude::*;
|
||||
use crate::viewport_tools::tool::ToolActionHandlerData;
|
||||
|
||||
@@ -25,3 +26,5 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Crop {
|
||||
|
||||
advertise_actions!();
|
||||
}
|
||||
|
||||
impl PropertyHolder for Crop {}
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
@@ -36,6 +37,8 @@ pub enum EllipseMessage {
|
||||
},
|
||||
}
|
||||
|
||||
impl PropertyHolder for Ellipse {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Ellipse {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -48,7 +51,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Ellipse {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -86,6 +89,7 @@ struct EllipseToolData {
|
||||
|
||||
impl Fsm for EllipseToolFsmState {
|
||||
type ToolData = EllipseToolData;
|
||||
type ToolOptions = ();
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -93,6 +97,7 @@ impl Fsm for EllipseToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::MouseMotion;
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
@@ -32,6 +33,8 @@ pub enum EyedropperMessage {
|
||||
RightMouseDown,
|
||||
}
|
||||
|
||||
impl PropertyHolder for Eyedropper {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Eyedropper {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -44,7 +47,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Eyedropper {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -72,6 +75,7 @@ struct EyedropperToolData {}
|
||||
|
||||
impl Fsm for EyedropperToolFsmState {
|
||||
type ToolData = EyedropperToolData;
|
||||
type ToolOptions = ();
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -79,6 +83,7 @@ impl Fsm for EyedropperToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
_tool_data: &DocumentToolData,
|
||||
_data: &mut Self::ToolData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::MouseMotion;
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
@@ -32,6 +33,8 @@ pub enum FillMessage {
|
||||
RightMouseDown,
|
||||
}
|
||||
|
||||
impl PropertyHolder for Fill {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Fill {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -44,7 +47,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Fill {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -72,6 +75,7 @@ struct FillToolData {}
|
||||
|
||||
impl Fsm for FillToolFsmState {
|
||||
type ToolData = FillToolData;
|
||||
type ToolOptions = ();
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -79,6 +83,7 @@ impl Fsm for FillToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
_data: &mut Self::ToolData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
|
||||
@@ -2,10 +2,10 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::MouseMotion;
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData, ToolType};
|
||||
use crate::viewport_tools::tool_options::ToolOptions;
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
|
||||
use graphene::layers::style;
|
||||
use graphene::Operation;
|
||||
@@ -17,6 +17,17 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Freehand {
|
||||
fsm_state: FreehandToolFsmState,
|
||||
data: FreehandToolData,
|
||||
options: FreehandOptions,
|
||||
}
|
||||
|
||||
pub struct FreehandOptions {
|
||||
line_weight: u32,
|
||||
}
|
||||
|
||||
impl Default for FreehandOptions {
|
||||
fn default() -> Self {
|
||||
Self { line_weight: 5 }
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
@@ -31,6 +42,13 @@ pub enum FreehandMessage {
|
||||
DragStart,
|
||||
DragStop,
|
||||
PointerMove,
|
||||
UpdateOptions(FreehandMessageOptionsUpdate),
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum FreehandMessageOptionsUpdate {
|
||||
LineWeight(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -39,6 +57,23 @@ enum FreehandToolFsmState {
|
||||
Drawing,
|
||||
}
|
||||
|
||||
impl PropertyHolder for Freehand {
|
||||
fn properties(&self) -> WidgetLayout {
|
||||
WidgetLayout::new(vec![LayoutRow::Row {
|
||||
name: "".into(),
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Weight".into(),
|
||||
value: self.options.line_weight as f64,
|
||||
is_integer: true,
|
||||
min: Some(1.),
|
||||
on_update: WidgetCallback::new(|number_input| FreehandMessage::UpdateOptions(FreehandMessageOptionsUpdate::LineWeight(number_input.value as u32)).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Freehand {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -51,7 +86,14 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Freehand {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
if let ToolMessage::Freehand(FreehandMessage::UpdateOptions(action)) = action {
|
||||
match action {
|
||||
FreehandMessageOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -84,6 +126,7 @@ struct FreehandToolData {
|
||||
|
||||
impl Fsm for FreehandToolFsmState {
|
||||
type ToolData = FreehandToolData;
|
||||
type ToolOptions = FreehandOptions;
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -91,6 +134,7 @@ impl Fsm for FreehandToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
@@ -110,10 +154,7 @@ impl Fsm for FreehandToolFsmState {
|
||||
|
||||
data.points.push(pos);
|
||||
|
||||
data.weight = match tool_data.tool_options.get(&ToolType::Freehand) {
|
||||
Some(&ToolOptions::Freehand { weight }) => weight,
|
||||
_ => 5,
|
||||
};
|
||||
data.weight = tool_options.line_weight;
|
||||
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::mouse::ViewportPosition;
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::snapping::SnapHandler;
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData, ToolType};
|
||||
use crate::viewport_tools::tool_options::ToolOptions;
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
|
||||
use graphene::layers::style;
|
||||
use graphene::Operation;
|
||||
@@ -20,6 +20,17 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Line {
|
||||
fsm_state: LineToolFsmState,
|
||||
data: LineToolData,
|
||||
options: LineOptions,
|
||||
}
|
||||
|
||||
pub struct LineOptions {
|
||||
line_weight: u32,
|
||||
}
|
||||
|
||||
impl Default for LineOptions {
|
||||
fn default() -> Self {
|
||||
Self { line_weight: 5 }
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
@@ -38,6 +49,30 @@ pub enum LineMessage {
|
||||
lock_angle: Key,
|
||||
snap_angle: Key,
|
||||
},
|
||||
UpdateOptions(LineOptionsUpdate),
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum LineOptionsUpdate {
|
||||
LineWeight(u32),
|
||||
}
|
||||
|
||||
impl PropertyHolder for Line {
|
||||
fn properties(&self) -> WidgetLayout {
|
||||
WidgetLayout::new(vec![LayoutRow::Row {
|
||||
name: "".into(),
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Weight".into(),
|
||||
value: self.options.line_weight as f64,
|
||||
is_integer: true,
|
||||
min: Some(0.),
|
||||
on_update: WidgetCallback::new(|number_input| LineMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value as u32)).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Line {
|
||||
@@ -52,7 +87,14 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Line {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
if let ToolMessage::Line(LineMessage::UpdateOptions(action)) = action {
|
||||
match action {
|
||||
LineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -95,6 +137,7 @@ struct LineToolData {
|
||||
|
||||
impl Fsm for LineToolFsmState {
|
||||
type ToolData = LineToolData;
|
||||
type ToolOptions = LineOptions;
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -102,6 +145,7 @@ impl Fsm for LineToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
@@ -118,10 +162,7 @@ impl Fsm for LineToolFsmState {
|
||||
data.path = Some(vec![generate_uuid()]);
|
||||
responses.push_back(DocumentMessage::DeselectAllLayers.into());
|
||||
|
||||
data.weight = match tool_data.tool_options.get(&ToolType::Line) {
|
||||
Some(&ToolOptions::Line { weight }) => weight,
|
||||
_ => 5,
|
||||
};
|
||||
data.weight = tool_options.line_weight;
|
||||
|
||||
responses.push_back(
|
||||
Operation::AddLine {
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
@@ -37,6 +38,8 @@ pub enum NavigateMessage {
|
||||
ZoomCanvasBegin,
|
||||
}
|
||||
|
||||
impl PropertyHolder for Navigate {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Navigate {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -49,7 +52,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Navigate {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -89,6 +92,7 @@ struct NavigateToolData {
|
||||
|
||||
impl Fsm for NavigateToolFsmState {
|
||||
type ToolData = NavigateToolData;
|
||||
type ToolOptions = ();
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -96,6 +100,7 @@ impl Fsm for NavigateToolFsmState {
|
||||
_document: &DocumentMessageHandler,
|
||||
_tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
messages: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
@@ -38,6 +39,8 @@ pub enum PathMessage {
|
||||
PointerMove,
|
||||
}
|
||||
|
||||
impl PropertyHolder for Path {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Path {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -50,7 +53,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Path {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -105,6 +108,7 @@ struct PathToolSelection {
|
||||
|
||||
impl Fsm for PathToolFsmState {
|
||||
type ToolData = PathToolData;
|
||||
type ToolOptions = ();
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -112,6 +116,7 @@ impl Fsm for PathToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
_tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
|
||||
@@ -2,11 +2,11 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::snapping::SnapHandler;
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData, ToolType};
|
||||
use crate::viewport_tools::tool_options::ToolOptions;
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
|
||||
use graphene::layers::style;
|
||||
use graphene::Operation;
|
||||
@@ -18,6 +18,17 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Pen {
|
||||
fsm_state: PenToolFsmState,
|
||||
data: PenToolData,
|
||||
options: PenOptions,
|
||||
}
|
||||
|
||||
pub struct PenOptions {
|
||||
line_weight: u32,
|
||||
}
|
||||
|
||||
impl Default for PenOptions {
|
||||
fn default() -> Self {
|
||||
Self { line_weight: 5 }
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
@@ -34,6 +45,7 @@ pub enum PenMessage {
|
||||
DragStop,
|
||||
PointerMove,
|
||||
Undo,
|
||||
UpdateOptions(PenOptionsUpdate),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -42,6 +54,29 @@ enum PenToolFsmState {
|
||||
Drawing,
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum PenOptionsUpdate {
|
||||
LineWeight(u32),
|
||||
}
|
||||
|
||||
impl PropertyHolder for Pen {
|
||||
fn properties(&self) -> WidgetLayout {
|
||||
WidgetLayout::new(vec![LayoutRow::Row {
|
||||
name: "".into(),
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Weight".into(),
|
||||
value: self.options.line_weight as f64,
|
||||
is_integer: true,
|
||||
min: Some(0.),
|
||||
on_update: WidgetCallback::new(|number_input| PenMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value as u32)).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Pen {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -54,7 +89,14 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Pen {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
if let ToolMessage::Pen(PenMessage::UpdateOptions(action)) = action {
|
||||
match action {
|
||||
PenOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -89,6 +131,7 @@ struct PenToolData {
|
||||
|
||||
impl Fsm for PenToolFsmState {
|
||||
type ToolData = PenToolData;
|
||||
type ToolOptions = PenOptions;
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -96,6 +139,7 @@ impl Fsm for PenToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
@@ -119,10 +163,7 @@ impl Fsm for PenToolFsmState {
|
||||
data.points.push(pos);
|
||||
data.next_point = pos;
|
||||
|
||||
data.weight = match tool_data.tool_options.get(&ToolType::Pen) {
|
||||
Some(&ToolOptions::Pen { weight }) => weight,
|
||||
_ => 5,
|
||||
};
|
||||
data.weight = tool_options.line_weight;
|
||||
|
||||
responses.push_back(make_operation(data, tool_data, true));
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
@@ -36,6 +37,8 @@ pub enum RectangleMessage {
|
||||
},
|
||||
}
|
||||
|
||||
impl PropertyHolder for Rectangle {}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Rectangle {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -48,7 +51,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Rectangle {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -85,6 +88,7 @@ struct RectangleToolData {
|
||||
|
||||
impl Fsm for RectangleToolFsmState {
|
||||
type ToolData = RectangleToolData;
|
||||
type ToolOptions = ();
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -92,6 +96,7 @@ impl Fsm for RectangleToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::mouse::ViewportPosition;
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::{IconButton, LayoutRow, PopoverButton, PropertyHolder, Separator, SeparatorDirection, SeparatorType, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::snapping::SnapHandler;
|
||||
@@ -51,6 +52,179 @@ pub enum SelectMessage {
|
||||
},
|
||||
}
|
||||
|
||||
impl PropertyHolder for Select {
|
||||
fn properties(&self) -> WidgetLayout {
|
||||
WidgetLayout::new(vec![LayoutRow::Row {
|
||||
name: "".into(),
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignLeft".into(),
|
||||
tooltip: "Align Left".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::X,
|
||||
aggregate: AlignAggregate::Min,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignHorizontalCenter".into(),
|
||||
tooltip: "Align Horizontal Center".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::X,
|
||||
aggregate: AlignAggregate::Center,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignRight".into(),
|
||||
tooltip: "Align Right".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::X,
|
||||
aggregate: AlignAggregate::Max,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignTop".into(),
|
||||
tooltip: "Align Top".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::Y,
|
||||
aggregate: AlignAggregate::Min,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignVerticalCenter".into(),
|
||||
tooltip: "Align Vertical Center".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::Y,
|
||||
aggregate: AlignAggregate::Center,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "AlignBottom".into(),
|
||||
tooltip: "Align Bottom".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| {
|
||||
DocumentMessage::AlignSelectedLayers {
|
||||
axis: AlignAxis::Y,
|
||||
aggregate: AlignAggregate::Max,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
title: "Align".into(),
|
||||
text: "The contents of this popover menu are coming soon".into(),
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Section,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "FlipHorizontal".into(),
|
||||
tooltip: "Flip Horizontal".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| SelectMessage::FlipHorizontal.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "FlipVertical".into(),
|
||||
tooltip: "Flip Vertical".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| SelectMessage::FlipVertical.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
title: "Flip".into(),
|
||||
text: "The contents of this popover menu are coming soon".into(),
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Section,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanUnion".into(),
|
||||
tooltip: "Boolean Union".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(197) }.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanSubtractFront".into(),
|
||||
tooltip: "Boolean Subtract Front".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(197) }.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanSubtractBack".into(),
|
||||
tooltip: "Boolean Subtract Back".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(197) }.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanIntersect".into(),
|
||||
tooltip: "Boolean Intersect".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(197) }.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "BooleanDifference".into(),
|
||||
tooltip: "Boolean Difference".into(),
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| FrontendMessage::DisplayDialogComingSoon { issue: Some(197) }.into()),
|
||||
..IconButton::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
separator_type: SeparatorType::Related,
|
||||
})),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
title: "Boolean".into(),
|
||||
text: "The contents of this popover menu are coming soon".into(),
|
||||
})),
|
||||
],
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Select {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
if action == ToolMessage::UpdateHints {
|
||||
@@ -63,7 +237,7 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Select {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &(), data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -141,6 +315,7 @@ fn transform_from_box(pos1: DVec2, pos2: DVec2) -> [f64; 6] {
|
||||
|
||||
impl Fsm for SelectToolFsmState {
|
||||
type ToolData = SelectToolData;
|
||||
type ToolOptions = ();
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -148,6 +323,7 @@ impl Fsm for SelectToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
_tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
_tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
|
||||
@@ -3,10 +3,10 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData, ToolType};
|
||||
use crate::viewport_tools::tool_options::{ShapeType, ToolOptions};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
|
||||
use graphene::layers::style;
|
||||
use graphene::Operation;
|
||||
@@ -18,6 +18,17 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Shape {
|
||||
fsm_state: ShapeToolFsmState,
|
||||
data: ShapeToolData,
|
||||
options: ShapeOptions,
|
||||
}
|
||||
|
||||
pub struct ShapeOptions {
|
||||
vertices: u8,
|
||||
}
|
||||
|
||||
impl Default for ShapeOptions {
|
||||
fn default() -> Self {
|
||||
Self { vertices: 6 }
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
@@ -35,6 +46,30 @@ pub enum ShapeMessage {
|
||||
center: Key,
|
||||
lock_ratio: Key,
|
||||
},
|
||||
UpdateOptions(ShapeOptionsUpdate),
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum ShapeOptionsUpdate {
|
||||
Vertices(u8),
|
||||
}
|
||||
|
||||
impl PropertyHolder for Shape {
|
||||
fn properties(&self) -> WidgetLayout {
|
||||
WidgetLayout::new(vec![LayoutRow::Row {
|
||||
name: "".into(),
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
label: "Sides".into(),
|
||||
value: self.options.vertices as f64,
|
||||
is_integer: true,
|
||||
min: Some(3.),
|
||||
max: Some(256.),
|
||||
on_update: WidgetCallback::new(|number_input| ShapeMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(number_input.value as u8)).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Shape {
|
||||
@@ -49,7 +84,14 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Shape {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
if let ToolMessage::Shape(ShapeMessage::UpdateOptions(action)) = action {
|
||||
match action {
|
||||
ShapeOptionsUpdate::Vertices(vertices) => self.options.vertices = vertices,
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -87,6 +129,7 @@ struct ShapeToolData {
|
||||
|
||||
impl Fsm for ShapeToolFsmState {
|
||||
type ToolData = ShapeToolData;
|
||||
type ToolOptions = ShapeOptions;
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -94,6 +137,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
@@ -109,12 +153,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
shape_data.path = Some(vec![generate_uuid()]);
|
||||
responses.push_back(DocumentMessage::DeselectAllLayers.into());
|
||||
data.sides = match tool_data.tool_options.get(&ToolType::Shape) {
|
||||
Some(&ToolOptions::Shape {
|
||||
shape_type: ShapeType::Polygon { vertices },
|
||||
}) => vertices as u8,
|
||||
_ => 6,
|
||||
};
|
||||
data.sides = tool_options.vertices;
|
||||
|
||||
responses.push_back(
|
||||
Operation::AddNgon {
|
||||
|
||||
@@ -3,10 +3,10 @@ use crate::document::DocumentMessageHandler;
|
||||
use crate::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::input::keyboard::{Key, MouseMotion};
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::widgets::{LayoutRow, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::message_prelude::*;
|
||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData, ToolType};
|
||||
use crate::viewport_tools::tool_options::ToolOptions;
|
||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene::intersection::Quad;
|
||||
@@ -19,6 +19,17 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Text {
|
||||
fsm_state: TextToolFsmState,
|
||||
data: TextToolData,
|
||||
options: TextOptions,
|
||||
}
|
||||
|
||||
pub struct TextOptions {
|
||||
font_size: u32,
|
||||
}
|
||||
|
||||
impl Default for TextOptions {
|
||||
fn default() -> Self {
|
||||
Self { font_size: 14 }
|
||||
}
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
@@ -41,6 +52,30 @@ pub enum TextMessage {
|
||||
UpdateBounds {
|
||||
new_text: String,
|
||||
},
|
||||
UpdateOptions(TextOptionsUpdate),
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum TextOptionsUpdate {
|
||||
FontSize(u32),
|
||||
}
|
||||
|
||||
impl PropertyHolder for Text {
|
||||
fn properties(&self) -> WidgetLayout {
|
||||
WidgetLayout::new(vec![LayoutRow::Row {
|
||||
name: "".into(),
|
||||
widgets: vec![WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: " px".into(),
|
||||
label: "Font Size".into(),
|
||||
value: self.options.font_size as f64,
|
||||
is_integer: true,
|
||||
min: Some(1.),
|
||||
on_update: WidgetCallback::new(|number_input| TextMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value as u32)).into()),
|
||||
..NumberInput::default()
|
||||
}))],
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Text {
|
||||
@@ -55,7 +90,14 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Text {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
if let ToolMessage::Text(TextMessage::UpdateOptions(action)) = action {
|
||||
match action {
|
||||
TextOptionsUpdate::FontSize(font_size) => self.options.font_size = font_size,
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, &self.options, data.2, responses);
|
||||
|
||||
if self.fsm_state != new_state {
|
||||
self.fsm_state = new_state;
|
||||
@@ -137,6 +179,7 @@ fn update_overlays(document: &DocumentMessageHandler, data: &mut TextToolData, r
|
||||
|
||||
impl Fsm for TextToolFsmState {
|
||||
type ToolData = TextToolData;
|
||||
type ToolOptions = TextOptions;
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
@@ -144,6 +187,7 @@ impl Fsm for TextToolFsmState {
|
||||
document: &DocumentMessageHandler,
|
||||
tool_data: &DocumentToolData,
|
||||
data: &mut Self::ToolData,
|
||||
tool_options: &Self::ToolOptions,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
@@ -200,10 +244,7 @@ impl Fsm for TextToolFsmState {
|
||||
// Creating new text
|
||||
else if state == TextToolFsmState::Ready {
|
||||
let transform = DAffine2::from_translation(input.mouse.position).to_cols_array();
|
||||
let font_size = match tool_data.tool_options.get(&ToolType::Text) {
|
||||
Some(&ToolOptions::Text { font_size }) => font_size,
|
||||
_ => 14,
|
||||
};
|
||||
let font_size = tool_options.font_size;
|
||||
data.path = vec![generate_uuid()];
|
||||
|
||||
responses.push_back(
|
||||
|
||||
Reference in New Issue
Block a user