//! The canvas cursor per mode (xfig's `w_cursor.c`): a cross-hair for //! drawing, a hand over what the editing modes pick, the I-beam of the text //! mode, a mover while objects follow the pointer. The UI maps //! [`CursorKind`] to its toolkit's cursor set. use super::{ToolKind, ToolState}; use crate::Engine; /// xfig's cursors, by what they tell the user. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CursorKind { /// `arrow_cursor`: no mode, or Select with nothing to do. Arrow, /// `crosshair_cursor` / `null_cursor`: drawing a shape, placing a point. Crosshair, /// `pick9_cursor` (a hand): pick an object or one of its points. Pick, /// `pick15_cursor` (a dotted box): pick a whole object to transform. PickObject, /// `buster_cursor`: Delete. Delete, /// `text_cursor`: the text mode. Text, /// Objects follow the pointer (Move, Copy, Paste). Move, /// A point or an object is being dragged. Grabbing, } impl Engine { /// The cursor the canvas shows now: by mode, and by what is in progress. pub fn cursor_kind(&self) -> CursorKind { use ToolKind::*; match &self.tool_state { ToolState::Paste { .. } | ToolState::Library { .. } => return CursorKind::Move, ToolState::MoveObjs { dragging: true, .. } | ToolState::PointDrag { .. } => return CursorKind::Grabbing, ToolState::MoveObjs { .. } => return CursorKind::Move, _ => {} } match self.tool { None => CursorKind::Arrow, Text => CursorKind::Text, Delete => CursorKind::Delete, Move | Copy | Align | Break | OpenCompound | Convert | FlipH | FlipV | RotateCcw | RotateCw | Scale => { CursorKind::PickObject } Glue | JoinSplit | Chop | Trim | MovePoint | AddPoint | DeletePoint | Update | Edit | Arrow | OpenClose | Tangent | AngleMeasure | LengthMeasure | AreaMeasure => CursorKind::Pick, _ => CursorKind::Crosshair, } } } #[cfg(test)] mod tests { use super::*; use crate::Engine; #[test] fn the_cursor_follows_the_mode() { let mut e = Engine::new(); let of = |e: &mut Engine, t| { e.set_tool(t); e.cursor_kind() }; assert_eq!(of(&mut e, ToolKind::None), CursorKind::Arrow); assert_eq!(of(&mut e, ToolKind::Box), CursorKind::Crosshair); assert_eq!(of(&mut e, ToolKind::Polyline), CursorKind::Crosshair); assert_eq!(of(&mut e, ToolKind::Text), CursorKind::Text); assert_eq!(of(&mut e, ToolKind::Move), CursorKind::PickObject); assert_eq!(of(&mut e, ToolKind::Delete), CursorKind::Delete); assert_eq!(of(&mut e, ToolKind::Edit), CursorKind::Pick); assert_eq!(of(&mut e, ToolKind::MovePoint), CursorKind::Pick); } #[test] fn every_mode_has_a_cursor_and_busy_states_override() { let mut e = Engine::new(); for t in ToolKind::ALL { e.set_tool(t); let _ = e.cursor_kind(); } e.set_tool(ToolKind::None); let _ = e.execute( "shape.rectangle", &serde_json::json!({"x": 0, "y": 0, "width": 600, "height": 600}), ); let _ = e.execute("edit.copy", &serde_json::json!({})); e.execute("edit.select_all", &serde_json::json!({})).unwrap(); e.execute("edit.copy", &serde_json::json!({})).unwrap(); e.execute("edit.paste", &serde_json::json!({})).unwrap(); assert_eq!(e.cursor_kind(), CursorKind::Move); } } .