mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 08:38:12 +08:00
- Added FSM trait - Added FSM to select and ellipse tools - Moved circle stamping code to ellipse tool
This commit is contained in:
committed by
Keavon Chambers
parent
273aa652df
commit
0d6d6fb81b
@@ -9,4 +9,5 @@ repository = "https://github.com/GraphiteEditor/Graphite"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
kurbo = "0.8.0"
|
||||
|
||||
@@ -27,4 +27,17 @@ impl Document {
|
||||
pub fn render(&self) -> String {
|
||||
self.svg.iter().map(|element| element.render()).collect::<Vec<_>>().join("\n")
|
||||
}
|
||||
|
||||
pub fn handle_operation<F: Fn(String)>(&mut self, operation: &Operation, update_frontend: F) {
|
||||
match *operation {
|
||||
Operation::AddCircle { cx, cy, r } => {
|
||||
self.svg.push(SvgElement::Circle(Circle {
|
||||
center: Point { x: cx, y: cy },
|
||||
radius: r,
|
||||
}));
|
||||
|
||||
update_frontend(self.render());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
pub enum Operation {
|
||||
AddCircle((f64, f64), f64),
|
||||
AddCircle { cx: f64, cy: f64, r: f64 },
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ pub enum Event {
|
||||
ResetColors,
|
||||
MouseDown(MouseState),
|
||||
MouseUp(MouseState),
|
||||
MouseMovement(ViewportPosition),
|
||||
ModifierKeyDown(ModKeys),
|
||||
ModifierKeyUp(ModKeys),
|
||||
KeyPress(Key),
|
||||
MouseMove(ViewportPosition),
|
||||
KeyUp(Key),
|
||||
KeyDown(Key),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
// TODO - Make Copy when possible
|
||||
pub enum Response {
|
||||
UpdateCanvas { document: String },
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pub mod events;
|
||||
use crate::{Color, EditorError, EditorState};
|
||||
use document_core::{Circle, Point, SvgElement};
|
||||
use crate::{Color, Document, EditorError, EditorState};
|
||||
use document_core::Operation;
|
||||
use events::{Event, Response};
|
||||
|
||||
pub type Callback = Box<dyn Fn(Response)>;
|
||||
@@ -9,82 +9,69 @@ pub struct Dispatcher {
|
||||
}
|
||||
|
||||
impl Dispatcher {
|
||||
pub fn handle_event(&self, state: &mut EditorState, event: Event) -> Result<(), EditorError> {
|
||||
pub fn handle_event(&self, editor_state: &mut EditorState, event: &Event) -> Result<(), EditorError> {
|
||||
log::trace!("{:?}", event);
|
||||
|
||||
match event {
|
||||
Event::SelectTool(tool_type) => {
|
||||
state.tool_state.active_tool_type = tool_type;
|
||||
|
||||
Ok(())
|
||||
editor_state.tool_state.active_tool_type = *tool_type;
|
||||
}
|
||||
Event::SelectPrimaryColor(color) => {
|
||||
state.tool_state.primary_color = color;
|
||||
|
||||
Ok(())
|
||||
editor_state.tool_state.primary_color = *color;
|
||||
}
|
||||
Event::SelectSecondaryColor(color) => {
|
||||
state.tool_state.secondary_color = color;
|
||||
|
||||
Ok(())
|
||||
editor_state.tool_state.secondary_color = *color;
|
||||
}
|
||||
Event::SwapColors => {
|
||||
std::mem::swap(&mut state.tool_state.primary_color, &mut state.tool_state.secondary_color);
|
||||
|
||||
Ok(())
|
||||
std::mem::swap(&mut editor_state.tool_state.primary_color, &mut editor_state.tool_state.secondary_color);
|
||||
}
|
||||
Event::ResetColors => {
|
||||
state.tool_state.primary_color = Color::BLACK;
|
||||
state.tool_state.secondary_color = Color::WHITE;
|
||||
|
||||
Ok(())
|
||||
editor_state.tool_state.primary_color = Color::BLACK;
|
||||
editor_state.tool_state.secondary_color = Color::WHITE;
|
||||
}
|
||||
Event::MouseDown(mouse_state) => {
|
||||
state.tool_state.mouse_state = mouse_state;
|
||||
state.tool_state.active_tool()?.handle_input(event);
|
||||
|
||||
Ok(())
|
||||
editor_state.tool_state.mouse_state = *mouse_state;
|
||||
}
|
||||
Event::MouseUp(mouse_state) => {
|
||||
state.tool_state.mouse_state = mouse_state;
|
||||
|
||||
state.document.svg.push(SvgElement::Circle(Circle {
|
||||
center: Point {
|
||||
x: mouse_state.position.x as f64,
|
||||
y: mouse_state.position.y as f64,
|
||||
},
|
||||
radius: 10.0,
|
||||
}));
|
||||
self.emit_response(Response::UpdateCanvas { document: state.document.render() });
|
||||
|
||||
state.tool_state.active_tool()?.handle_input(event);
|
||||
|
||||
Ok(())
|
||||
editor_state.tool_state.mouse_state = *mouse_state;
|
||||
}
|
||||
Event::MouseMovement(pos) => {
|
||||
state.tool_state.mouse_state.position = pos;
|
||||
state.tool_state.active_tool()?.handle_input(event);
|
||||
|
||||
Ok(())
|
||||
Event::MouseMove(pos) => {
|
||||
editor_state.tool_state.mouse_state.position = *pos;
|
||||
}
|
||||
Event::ModifierKeyDown(mod_keys) => {
|
||||
state.tool_state.mod_keys = mod_keys;
|
||||
state.tool_state.active_tool()?.handle_input(event);
|
||||
Event::KeyUp(key) => todo!(),
|
||||
Event::KeyDown(key) => todo!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Event::ModifierKeyUp(mod_keys) => {
|
||||
state.tool_state.mod_keys = mod_keys;
|
||||
state.tool_state.active_tool()?.handle_input(event);
|
||||
let (responses, operations) = editor_state.tool_state.active_tool()?.handle_input(event, &editor_state.document);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Event::KeyPress(key) => todo!(),
|
||||
self.dispatch_operations(&mut editor_state.document, &operations);
|
||||
// TODO - Dispatch Responses
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dispatch_operations(&self, document: &mut Document, operations: &[Operation]) {
|
||||
for operation in operations {
|
||||
self.dispatch_operation(document, operation);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit_response(&self, response: Response) {
|
||||
fn dispatch_operation(&self, document: &mut Document, operation: &Operation) {
|
||||
document.handle_operation(operation, |svg: String| {
|
||||
self.dispatch_response(Response::UpdateCanvas { document: svg });
|
||||
});
|
||||
}
|
||||
|
||||
pub fn dispatch_responses(&self, responses: &[Response]) {
|
||||
for response in responses {
|
||||
// TODO - Remove clone when Response is Copy
|
||||
self.dispatch_response(response.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch_response(&self, response: Response) {
|
||||
let func = &self.callback;
|
||||
// TODO - Remove clone if possible
|
||||
func(response)
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,6 @@ impl Editor {
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: events::Event) -> Result<(), EditorError> {
|
||||
self.dispatcher.handle_event(&mut self.state, event)
|
||||
self.dispatcher.handle_event(&mut self.state, &event)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::Tool;
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Crop;
|
||||
|
||||
impl Tool for Crop {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,54 @@
|
||||
use crate::events::Event;
|
||||
use crate::tools::Tool;
|
||||
use crate::events::MouseKeys;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::{Fsm, Tool};
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Ellipse;
|
||||
pub struct Ellipse {
|
||||
state: EllipseToolState,
|
||||
}
|
||||
|
||||
impl Tool for Ellipse {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
todo!();
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
let mut responses = Vec::new();
|
||||
let mut operations = Vec::new();
|
||||
self.state = self.state.transition(event, document, &mut responses, &mut operations);
|
||||
|
||||
(responses, operations)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum EllipseToolState {
|
||||
Ready,
|
||||
LmbDown,
|
||||
TransformSelected,
|
||||
}
|
||||
|
||||
impl Default for EllipseToolState {
|
||||
fn default() -> Self {
|
||||
EllipseToolState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
impl Fsm for EllipseToolState {
|
||||
fn transition(self, event: &Event, document: &Document, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self {
|
||||
match (self, event) {
|
||||
(EllipseToolState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => EllipseToolState::LmbDown,
|
||||
|
||||
// TODO - Check for left mouse button
|
||||
(EllipseToolState::LmbDown, Event::MouseUp(mouse_state)) => {
|
||||
operations.push(Operation::AddCircle {
|
||||
cx: mouse_state.position.x as f64,
|
||||
cy: mouse_state.position.y as f64,
|
||||
r: 10.0,
|
||||
});
|
||||
|
||||
EllipseToolState::Ready
|
||||
}
|
||||
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::Tool;
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Line;
|
||||
|
||||
impl Tool for Line {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,19 @@ mod sample;
|
||||
mod select;
|
||||
mod shape;
|
||||
|
||||
use crate::events::{Event, ModKeys, MouseState, Trace, TracePoint};
|
||||
use crate::events::{Event, ModKeys, MouseState, Response, Trace, TracePoint};
|
||||
use crate::Color;
|
||||
use crate::Document;
|
||||
use crate::EditorError;
|
||||
use document_core::Operation;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait Tool {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation>;
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>);
|
||||
}
|
||||
|
||||
pub trait Fsm {
|
||||
fn transition(self, event: &Event, document: &Document, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self;
|
||||
}
|
||||
|
||||
pub struct ToolState {
|
||||
@@ -38,7 +43,7 @@ impl Default for ToolState {
|
||||
trace: Trace::new(),
|
||||
primary_color: Color::BLACK,
|
||||
secondary_color: Color::WHITE,
|
||||
active_tool_type: ToolType::Select,
|
||||
active_tool_type: ToolType::Ellipse,
|
||||
tools: gen_tools_hash_map! {
|
||||
Select => select::Select,
|
||||
Crop => crop::Crop,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::Tool;
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Navigate;
|
||||
|
||||
impl Tool for Navigate {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::Tool;
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Path;
|
||||
|
||||
impl Tool for Path {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::Tool;
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Pen;
|
||||
|
||||
impl Tool for Pen {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::Tool;
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Rectangle;
|
||||
|
||||
impl Tool for Rectangle {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
enum RectangleToolState {
|
||||
Ready,
|
||||
Dragging,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::Tool;
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Sample;
|
||||
|
||||
impl Tool for Sample {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,51 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::MouseKeys;
|
||||
use crate::tools::Tool;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::{Fsm, Tool};
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Select(Fsm);
|
||||
pub struct Select {
|
||||
state: SelectToolState,
|
||||
}
|
||||
|
||||
impl Tool for Select {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
match event {
|
||||
Event::MouseDown(state) => {
|
||||
if state.mouse_keys.contains(MouseKeys::LEFT) {
|
||||
self.0 = Fsm::LmbDown;
|
||||
}
|
||||
}
|
||||
Event::MouseUp(state) => {
|
||||
if self.0 == Fsm::LmbDown && state.mouse_keys.contains(MouseKeys::LEFT) {
|
||||
self.0 = Fsm::SelectedObject;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
let mut responses = Vec::new();
|
||||
let mut operations = Vec::new();
|
||||
self.state = self.state.transition(event, document, &mut responses, &mut operations);
|
||||
|
||||
Vec::new()
|
||||
(responses, operations)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum Fsm {
|
||||
enum SelectToolState {
|
||||
Ready,
|
||||
LmbDown,
|
||||
SelectedObject,
|
||||
TransformSelected,
|
||||
}
|
||||
|
||||
impl Default for Fsm {
|
||||
impl Default for SelectToolState {
|
||||
fn default() -> Self {
|
||||
Fsm::Ready
|
||||
SelectToolState::Ready
|
||||
}
|
||||
}
|
||||
|
||||
impl Fsm for SelectToolState {
|
||||
fn transition(self, event: &Event, document: &Document, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self {
|
||||
match (self, event) {
|
||||
(SelectToolState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => SelectToolState::LmbDown,
|
||||
|
||||
(SelectToolState::LmbDown, Event::MouseUp(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => SelectToolState::Ready,
|
||||
|
||||
(SelectToolState::LmbDown, Event::MouseMove(mouse_state)) => SelectToolState::TransformSelected,
|
||||
|
||||
(SelectToolState::TransformSelected, Event::MouseMove(mouse_state)) => self,
|
||||
|
||||
(SelectToolState::TransformSelected, Event::MouseUp(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => SelectToolState::Ready,
|
||||
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::events::Event;
|
||||
use crate::events::{Event, Response};
|
||||
use crate::tools::Tool;
|
||||
use crate::Document;
|
||||
use document_core::Operation;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Shape;
|
||||
|
||||
impl Tool for Shape {
|
||||
fn handle_input(&mut self, event: Event) -> Vec<Operation> {
|
||||
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user