refactor(engine): simplify canvas and board handlers

This commit is contained in:
Dylan M. Taylor
2026-10-28 16:00:36 +00:00
parent 23f57c8660
commit 5e16683963
22 changed files with 72 additions and 31 deletions
+4 -1
View File
@@ -1,7 +1,10 @@
use super::derived::{ends_now, place_route, route};
use super::edit::set_label;
use super::items::add;
use super::*;
use super::{
BoardItem, Connector, Document, End, LINE, LINE_WEIGHT, NodeId, NodeKind, Paint, Point, Result, Session, Side, StrokeCap, StrokeJoin, Value,
VectorNetwork, bad, cap_of, get, id_of, json, num, req_targets, require, routing_of, string,
};
/// One connector end from the params: a layer on this page, or a point.
fn end_param(cmd: &str, s: &Session, p: &Value, key: &str, side_key: &str) -> Result<End> {
+4 -1
View File
@@ -1,6 +1,9 @@
use super::edit::set_font_size;
use super::items::{owner_of_text, shape_padding, sticky_font_size, text_of};
use super::*;
use super::{
Affine, BTreeSet, BezPath, BoardItem, Connector, Document, End, NodeId, NodeKind, PathEl, Point, Rect, Routing, Shape, Side, Vec2, VectorNetwork,
is_board,
};
// ---------------------------------------------------------------------------------------------
// Derived data (the normalize pass).
+5 -1
View File
@@ -1,7 +1,11 @@
use super::connectors::connector_of;
use super::derived::ends_now;
use super::items::{add, add_text, shape_padding, text_of};
use super::*;
use super::{
Affine, AutoLayout, BTreeMap, BoardItem, BoardShape, Color, Connector, DocError, Document, End, INK, NodeId, NodeKind, Paint, Result, Routing,
Session, Sizing, StrokeCap, TextAlignH, TextAutoResize, TextData, Value, VectorNetwork, bad, cap_of, color_param, json, line_shade, num,
req_string, req_targets, require, routing_of, shape_of, string,
};
/// The label of connector `id`, if it has one.
pub fn label_of(doc: &Document, id: NodeId) -> Option<NodeId> {
+7 -1
View File
@@ -1,4 +1,10 @@
use super::*;
use super::{
AUTHOR_INK, Affine, AutoLayout, BlendMode, BoardItem, BoardShape, Color, Constraints, CrossAlign, Document, Effect, EffectKind, EngineError,
HConstraint, HIGHLIGHTER, INK, LayoutMode, MARKER, MainAlign, Node, NodeId, NodeKind, Paint, Result, SHAPE, STAMP_SIZE, STICKY, STICKY_GAP,
STICKY_PAD, Session, Shadow, Sizing, StrokeAlign, StrokeCap, StrokeJoin, TextAlignH, TextAutoResize, TextData, VConstraint, Value, VectorNetwork,
author, bad, boolean, color_param, get, id, id_of, json, line_shade, num, num_or, parent_of, req_num, require, shape_of, stamp_of, string,
textedit,
};
/// A new layer at the top of `parent`, at `at` in it, made by `f`.
pub(super) fn add(
+4 -1
View File
@@ -2,7 +2,10 @@ use super::connectors::{end_json, other_end};
use super::derived::ends_now;
use super::edit::label_of;
use super::items::edit_text;
use super::*;
use super::{
BoardItem, Document, ERASER_PX, End, EngineError, Gesture, NodeId, Point, PointerEvent, Result, SHAPE, STICKY, Session, Side, ToolId, id_of,
is_board, json,
};
// ---------------------------------------------------------------------------------------------
// The board tools.
+1 -1
View File
@@ -1,7 +1,7 @@
//! Where something dropped at a page point lands: the frame under the point (or the page) and, in
//! an auto layout frame, the child slot by pointer position plus the insertion line to draw.
use figcraft_doc::*;
use figcraft_doc::{Document, LayoutMode, NodeId, NodeKind};
use figcraft_geom::Point;
use serde::Serialize;
+2 -2
View File
@@ -21,14 +21,14 @@
use std::collections::BTreeSet;
use std::sync::Arc;
use figcraft_doc::*;
use figcraft_doc::{Corners, Document, Node, NodeId, NodeKind, StrokeProps, VectorNetwork, WindingRule};
use figcraft_geom::kurbo::PathEl;
use figcraft_geom::{Affine, BezPath, Point, Rect, Shape};
use serde_json::{Value, json};
use crate::cmd::CommandSpec;
use crate::cmd::edit::{roots, z_sorted};
use crate::params::*;
use crate::params::{f64_of, get, string, targets};
use crate::tools::{Cursor, Gesture, PointerEvent, PointerKind, ToolId};
use crate::{EngineError, Result, Session, bad, spec};
+1 -1
View File
@@ -6,7 +6,7 @@ use std::collections::BTreeMap;
use figcraft_color::{ImageScaleMode, PaintKind};
use figcraft_doc::geometry::select_target;
use figcraft_doc::*;
use figcraft_doc::{Document, LayoutMode, Node, NodeId, NodeKind, Sizing, StrokeAlign, StrokeCap, TextAutoResize, TextData, TextPath, VectorNetwork};
use figcraft_geom::{Affine, Point, Rect, Vec2};
use serde::Serialize;
use serde_json::json;
+4 -1
View File
@@ -1,5 +1,8 @@
use super::handles::{PATH_REACH_PX, label_at, locked_in, path_candidates, readable_text_fills};
use super::*;
use super::{
Affine, Document, Gesture, Node, NodeId, NodeKind, Point, PointerEvent, Rect, Result, Session, Sizing, TextAutoResize, TextData, TextPath,
ToolId, frame_at, json, select_target, textedit, within_slop,
};
impl Session {
/// Handle a double-click gesture: finish a pen path, rename or click labels/widgets,
+5 -1
View File
@@ -2,7 +2,11 @@ use super::handles::{
SNAP_PX, handle_at, in_quad, label_at, locked_in, on_corner_square, paints_below, resized_box, scale_factor, selection_box, selection_frame,
shape_hint,
};
use super::*;
use super::{
Affine, ArcHandle, ArcHint, Cursor, Document, Gesture, Guide, Handle, ImageScaleMode, LayoutMode, NodeId, NodeKind, PaintKind, Point,
PointerEvent, Rect, ResizeStart, Result, ScaleHint, Session, Sizing, Targets, TextAutoResize, ToolId, Vec2, move_world, roots, select_target,
textedit, within_slop,
};
impl Session {
pub(super) fn move_down(&mut self, ev: &PointerEvent) -> Result<()> {
+3 -1
View File
@@ -1,6 +1,8 @@
use super::handles::SNAP_PX;
use super::pen::take_enclosed;
use super::*;
use super::{
Affine, Cursor, Gesture, Node, NodeId, NodeKind, Point, PointerEvent, Rect, Result, Session, StrokeCap, Targets, ToolId, Vec2, frame_at, textedit,
};
impl Session {
pub(super) fn draw_down(&mut self, ev: &PointerEvent) -> Result<()> {
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Affine, BTreeMap, Cursor, Document, Handle, NodeId, NodeKind, Point, ShapeHandle, ShapeHint, roots};
/// The frame whose name label (see [`ToolState::labels`]) is under `p` on `page`: the topmost
/// one drawn, if it can be selected (still there, on the page, visible and unlocked).
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Document, Gesture, Node, NodeId, Overlay, Point, PointerEvent, PointerKind, Result, Session, ShapeHandle, ToolId, json, textedit};
impl Session {
/// Keys the current mode handles before menu shortcuts (vector edit mode's tools and point
+4 -1
View File
@@ -1,5 +1,8 @@
use super::handles::{PENCIL_STROKE, selected_end_at};
use super::*;
use super::{
Affine, Document, Gesture, Guide, Node, NodeId, NodeKind, PenPath, Point, PointerEvent, Result, Session, StrokeAlign, ToolId, Vec2,
VectorNetwork, at_45_degrees, frame_at, json, pen_handles,
};
/// Pen pressure along drawn samples as width profile points `[t, scale]`: `t` by length, at most
/// 32 points, medium pressure (0.5) at the stroke weight, from a tenth to twice it. `None` when no
+4 -1
View File
@@ -1,5 +1,8 @@
use super::handles::{handle_at, label_at, locked_in, on_corner_square, resize_cursor, selected_end_at};
use super::*;
use super::{
Cursor, Document, Gesture, NodeId, NodeKind, Overlay, PenCursor, Point, PointerEvent, PointerKind, Rect, Result, Session, ToolId, at_45_degrees,
frame_at, json, select_target,
};
impl Session {
/// The pointer left the canvas: what hovering there showed goes (the distances Alt or Dev Mode
+2 -2
View File
@@ -24,14 +24,14 @@ mod width_sides_tests;
#[cfg(test)]
mod width_tests;
use figcraft_doc::*;
use figcraft_doc::{Corners, Document, HandleMirroring, NodeId, NodeKind, StrokeAlign, StrokeCap, StrokeJoin, VectorNetwork, Vertex, WindingRule};
use figcraft_geom::{Affine, Point, Rect, Vec2};
use serde::Serialize;
use serde_json::{Value, json};
use crate::cmd::CommandSpec;
use crate::cmd::props::enum_of;
use crate::params::*;
use crate::params::{boolean, f64_of, get, id, num, num_or, req_num, string, targets};
use crate::{EngineError, Result, Session, bad, spec};
pub(crate) use pointer::VGesture;
+1 -1
View File
@@ -2,7 +2,7 @@
//! deleting, cutting and joining. Pure functions on a [`VectorNetwork`]; the session wrappers in
//! `vectoredit.rs` re-fit the node afterwards.
use figcraft_doc::*;
use figcraft_doc::{HandleMirroring, Region, Segment, VectorNetwork, Vertex, WindingRule};
use figcraft_geom::kurbo::{CubicBez, ParamCurve, ParamCurveNearest};
use figcraft_geom::{Affine, Point, Vec2};
+14 -9
View File
@@ -5,7 +5,7 @@
use std::sync::Arc;
use figcraft_doc::*;
use figcraft_doc::{Document, HandleMirroring, NodeId, NodeKind, VectorNetwork, Vertex};
use figcraft_geom::{Affine, Point, Rect, Vec2};
use super::point_snap;
@@ -510,20 +510,25 @@ impl Session {
Ok(())
}
/// Another edited layer under the press (or, with the paint bucket, whose area it is in).
fn pressed_layer(doc: &Document, ve: &VectorEdit, ev: &PointerEvent, tol: f64) -> Option<NodeId> {
match hit_any(doc, ve, ev.p(), tol) {
Some((id, _)) => Some(id),
None if ve.tool == VTool::Paint => {
let inside = |id: NodeId| network(doc, id).is_some_and(|n| n.face_at(doc.world_transform(id).inverse() * ev.p()).is_some());
(!inside(ve.id)).then(|| ve.others.iter().map(|o| o.id).find(|id| inside(*id))).flatten()
}
None => None,
}
}
/// Returns false when the click left edit mode (the caller then runs the normal tool).
fn vector_down(&mut self, ve: &VectorEdit, ev: &PointerEvent) -> Result<bool> {
let doc = self.doc()?.clone();
let tol = 6.0 / ev.zoom.max(1e-6);
// A press on another edited layer's point, handle or segment (or, with the paint bucket,
// area) makes it the active layer; a pen drawing a path keeps to its own layer.
let other = match hit_any(&doc, ve, ev.p(), tol) {
Some((id, _)) => Some(id),
None if ve.tool == VTool::Paint => {
let inside = |id: NodeId| network(&doc, id).is_some_and(|n| n.face_at(doc.world_transform(id).inverse() * ev.p()).is_some());
(!inside(ve.id)).then(|| ve.others.iter().map(|o| o.id).find(|id| inside(*id))).flatten()
}
None => None,
};
let other = Self::pressed_layer(&doc, ve, ev, tol);
let switched;
let ve = match other {
Some(id) if id != ve.id && !(ve.tool == VTool::Pen && ve.anchor.is_some()) => {
@@ -11,7 +11,7 @@ use serde_json::{Value, json};
use super::{VHit, VTool, VectorEdit, default_points, edit, editing, indices, network, ops, selected_points, selection_position};
use crate::cmd::CommandSpec;
use crate::params::*;
use crate::params::{boolean, f64_of, get, num, num_or, req_num};
use crate::tools::{Handle, handle_at_corners};
use crate::{Result, Session, bad, spec};
+2 -2
View File
@@ -10,7 +10,7 @@
//! The points live in the stroke's [`WidthProfile`] and apply to every subpath (positions are
//! fractions of each subpath's length).
use figcraft_doc::*;
use figcraft_doc::{Document, NodeId, WidthProfile, WidthSide};
use figcraft_geom::{Point, Vec2};
use figcraft_pathops::width::{Polyline, nearest, polylines};
use serde::Serialize;
@@ -18,7 +18,7 @@ use serde_json::{Value, json};
use super::{editing, set_state};
use crate::cmd::CommandSpec;
use crate::params::*;
use crate::params::{get, num, req_index, req_num, string};
use crate::tools::{Gesture, PointerEvent};
use crate::{Result, Session, bad, spec};
@@ -7,6 +7,7 @@ use serde_json::{Value, json};
use super::*;
use crate::EngineError;
use crate::tools::{PointerEvent, PointerKind};
use figcraft_doc::{ExportFormat, WidthSide};
fn ex(s: &mut Session, c: &str, p: Value) -> Value {
s.execute(c, &p).unwrap_or_else(|e| panic!("{c}: {e}"))
@@ -6,6 +6,7 @@ use serde_json::{Value, json};
use super::*;
use crate::EngineError;
use crate::tools::{PointerEvent, PointerKind};
use figcraft_doc::{ExportFormat, WidthPreset, WidthProfile, WidthSide};
fn ex(s: &mut Session, c: &str, p: Value) -> Value {
s.execute(c, &p).unwrap_or_else(|e| panic!("{c}: {e}"))