mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Major overhaul of input and communication systems
* Add input manager * WIP lifetime hell * Hell yeah, dark lifetime magic * Replace events with actions in tools * Fix borrow in GlobalEventHandler * Fix typo in response-handler * Distribute dispatch structure * Add translation from events to actions * Port default key actions to input_mapper * Split actions macro * Add handling for Ambiguous Mouse events * Fix warnings and clippy lints * Add actions macro * WIP rework * Add AsMessage macro * Add implementation for derived enums * Add macro implementation for top level message * Add #[child] attribute to indicate derivation * Replace some mentions of Actions and Responses with Message * It compiles !!! * Add functionality to some message handlers * Add document rendering * ICE * Rework the previous code while keeping basic functionality * Reduce parent-top-level macro args to only two * Add workaround for ICE * Fix cyclic reference in document.rs * Make derive_transitive_child a bit more powerful This addresses the todo that was left, enabling arbitrary expressions to be passed as the last parameter of a #[parent] attribute * Adapt frontend message format * Make responses use VecDeque Our responses are a queue so we should use a queue type for them * Move traits to sensible location * Are we rectangle yet? * Simplify, improve & document `derive_discriminant` * Change `child` to `sub_discriminant` This only applies to `ToDiscriminant`. Code using `#[impl_message]` continues to work. * Add docs for `derive_transitive_child` * Finish docs and improve macros The improvements are that impl_message now uses trait resolution to obtain the parent's discriminant and that derive_as_message now allows for non-unit variants (which we don't use but it's nice to have, just in case) * Remove logging call * Move files around and cleanup structure * Fix proc macro doc tests * Improve actions_fn!() macro * Add ellipse tool * Pass populated actions list to the input mapper * Add KeyState bitvector * Merge mouse buttons into "keyboard" * Add macro for initialization of key mapper table * Add syntactic sugar for the macro * Implement mapping function * Translate the remaining tools * Fix shape tool * Add keybindings for line and pen tool * Fix modifiers * Cleanup * Add doc comments for the actions macro * Fix formatting * Rename MouseMove to PointerMove * Add keybinds for tools * Apply review suggestions * Rename KeyMappings -> KeyMappingEntries * Apply review changes Co-authored-by: T0mstone <realt0mstone@gmail.com> Co-authored-by: Paul Kupper <kupper.pa@gmail.com>
This commit is contained in:
committed by
Keavon Chambers
parent
c08b2d4b31
commit
4b19a459b7
@@ -55,7 +55,7 @@ impl Color {
|
||||
pub fn components(&self) -> (f32, f32, f32, f32) {
|
||||
(self.red, self.green, self.blue, self.alpha)
|
||||
}
|
||||
pub fn to_hex(&self) -> String {
|
||||
pub fn as_hex(&self) -> String {
|
||||
format!(
|
||||
"{:02X?}{:02X?}{:02X?}{:02X?}",
|
||||
(self.r() * 255.) as u8,
|
||||
|
||||
@@ -184,10 +184,9 @@ impl Document {
|
||||
/// Mutate the document by applying the `operation` to it. If the operation necessitates a
|
||||
/// reaction from the frontend, responses may be returned.
|
||||
pub fn handle_operation(&mut self, operation: Operation) -> Result<Option<Vec<DocumentResponse>>, DocumentError> {
|
||||
self.work_operations.push(operation.clone());
|
||||
let responses = match operation {
|
||||
let responses = match &operation {
|
||||
Operation::AddCircle { path, insert_index, cx, cy, r, style } => {
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Circle(layers::Circle::new((cx, cy), r, style))), insert_index)?;
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Circle(layers::Circle::new((*cx, *cy), *r, *style))), *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged])
|
||||
}
|
||||
@@ -201,7 +200,7 @@ impl Document {
|
||||
rot,
|
||||
style,
|
||||
} => {
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Ellipse(layers::Ellipse::new((cx, cy), (rx, ry), rot, style))), insert_index)?;
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Ellipse(layers::Ellipse::new((*cx, *cy), (*rx, *ry), *rot, *style))), *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged])
|
||||
}
|
||||
@@ -214,7 +213,7 @@ impl Document {
|
||||
y1,
|
||||
style,
|
||||
} => {
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Rect(Rect::new((x0, y0), (x1, y1), style))), insert_index)?;
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Rect(Rect::new((*x0, *y0), (*x1, *y1), *style))), *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged])
|
||||
}
|
||||
@@ -227,14 +226,14 @@ impl Document {
|
||||
y1,
|
||||
style,
|
||||
} => {
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Line(Line::new((x0, y0), (x1, y1), style))), insert_index)?;
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Line(Line::new((*x0, *y0), (*x1, *y1), *style))), *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged])
|
||||
}
|
||||
Operation::AddPen { path, insert_index, points, style } => {
|
||||
let points: Vec<kurbo::Point> = points.into_iter().map(|it| it.into()).collect();
|
||||
let polyline = PolyLine::new(points, style);
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline)), insert_index)?;
|
||||
let points: Vec<kurbo::Point> = points.iter().map(|&it| it.into()).collect();
|
||||
let polyline = PolyLine::new(points, *style);
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline)), *insert_index)?;
|
||||
Some(vec![DocumentResponse::DocumentChanged])
|
||||
}
|
||||
Operation::AddShape {
|
||||
@@ -247,24 +246,27 @@ impl Document {
|
||||
sides,
|
||||
style,
|
||||
} => {
|
||||
let s = Shape::new((x0, y0), (x1, y1), sides, style);
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Shape(s)), insert_index)?;
|
||||
let s = Shape::new((*x0, *y0), (*x1, *y1), *sides, *style);
|
||||
self.add_layer(&path, Layer::new(LayerDataTypes::Shape(s)), *insert_index)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged])
|
||||
}
|
||||
Operation::DeleteLayer { path } => {
|
||||
self.delete(&path)?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged])
|
||||
let (path, _) = split_path(path.as_slice()).unwrap_or_else(|_| (&[], 0));
|
||||
let children = self.layer_panel(path)?;
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::ExpandFolder { path: path.to_vec(), children }])
|
||||
}
|
||||
Operation::AddFolder { path } => {
|
||||
self.set_layer(&path, Layer::new(LayerDataTypes::Folder(Folder::default())))?;
|
||||
|
||||
Some(vec![DocumentResponse::DocumentChanged])
|
||||
let children = self.layer_panel(path.as_slice())?;
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::ExpandFolder { path: path.clone(), children }])
|
||||
}
|
||||
Operation::MountWorkingFolder { path } => {
|
||||
self.work_mount_path = path.clone();
|
||||
self.work_operations.clear();
|
||||
self.work_mount_path = path;
|
||||
self.work = Folder::default();
|
||||
self.work_mounted = true;
|
||||
None
|
||||
@@ -286,18 +288,18 @@ impl Document {
|
||||
let mut path: Vec<LayerId> = vec![];
|
||||
std::mem::swap(&mut path, &mut self.work_mount_path);
|
||||
std::mem::swap(&mut ops, &mut self.work_operations);
|
||||
let len = ops.len() - 1;
|
||||
self.work_mounted = false;
|
||||
self.work_mount_path = vec![];
|
||||
self.work = Folder::default();
|
||||
let mut responses = vec![];
|
||||
for operation in ops.into_iter().take(len) {
|
||||
for operation in ops.into_iter() {
|
||||
if let Some(mut op_responses) = self.handle_operation(operation)? {
|
||||
responses.append(&mut op_responses);
|
||||
}
|
||||
}
|
||||
|
||||
let children = self.layer_panel(path.as_slice())?;
|
||||
// TODO: Return `responses` and add deduplication in the future
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::ExpandFolder { path, children }])
|
||||
}
|
||||
Operation::ToggleVisibility { path } => {
|
||||
@@ -306,9 +308,15 @@ impl Document {
|
||||
layer.cache_dirty = true;
|
||||
});
|
||||
let children = self.layer_panel(&path.as_slice()[..path.len() - 1])?;
|
||||
Some(vec![DocumentResponse::ExpandFolder { path: vec![], children }])
|
||||
Some(vec![DocumentResponse::DocumentChanged, DocumentResponse::ExpandFolder { path: vec![], children }])
|
||||
}
|
||||
};
|
||||
if !matches!(
|
||||
operation,
|
||||
Operation::CommitTransaction | Operation::MountWorkingFolder { .. } | Operation::DiscardWorkingFolder | Operation::ClearWorkingFolder
|
||||
) {
|
||||
self.work_operations.push(operation);
|
||||
}
|
||||
Ok(responses)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ impl Fill {
|
||||
}
|
||||
pub fn render(&self) -> String {
|
||||
match self.color {
|
||||
Some(c) => format!("fill: #{};", c.to_hex()),
|
||||
Some(c) => format!("fill: #{};", c.as_hex()),
|
||||
None => "fill: none;".to_string(),
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ impl Stroke {
|
||||
Self { color, width }
|
||||
}
|
||||
pub fn render(&self) -> String {
|
||||
format!("stroke: #{};stroke-width:{};", self.color.to_hex(), self.width)
|
||||
format!("stroke: #{};stroke-width:{};", self.color.as_hex(), self.width)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::{layers::style, LayerId};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub enum Operation {
|
||||
AddCircle {
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub struct LayerPanelEntry {
|
||||
pub name: String,
|
||||
pub visible: bool,
|
||||
@@ -14,7 +14,7 @@ pub struct LayerPanelEntry {
|
||||
pub path: Vec<LayerId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub enum LayerType {
|
||||
Folder,
|
||||
Shape,
|
||||
@@ -71,7 +71,7 @@ impl LayerPanelEntry {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[repr(C)]
|
||||
// TODO - Make Copy when possible
|
||||
pub enum DocumentResponse {
|
||||
|
||||
Reference in New Issue
Block a user