Cursors: Caps Lock gives the drawing tools the crosshair

While Caps Lock is on, the tools whose pointer General > Use Precise
Cursors changes show the crosshair, and the saved preference does not
change. The desktop app reads the lock state on macOS, Windows and
Linux X11 and passes each change to the new view.capsLock command. A
change to view.capsLock or usePreciseCursors through app.run refreshes
the canvas pointer. The view.preciseCursors command is labeled Use
Precise Cursors, so command search finds it by the preference's name.
This commit is contained in:
dotCooCoo
2026-11-01 01:59:54 +07:00
committed by bflatastic
parent 1bb52c5b38
commit dfd07552c8
16 changed files with 321 additions and 10 deletions
+8
View File
@@ -9,6 +9,7 @@ use super::{CommandSpec, Result, Session, always, bad, bool_or, cmd, f64_req, fi
pub fn specs() -> Vec<CommandSpec> {
vec![
cmd!(query "view.capsLock", "Caps Lock Cursor State", [], None, "{on?: bool} set or read the Caps Lock state the desktop app reports; while it is on, the drawing tools show the precise crosshair, and Use Precise Cursors keeps its saved value → {on}", always, caps_lock),
cmd!(
"view.saved.new",
"New View…",
@@ -54,6 +55,13 @@ fn glyph_snap_options(s: &mut Session, p: &Value) -> Result<Value> {
Ok(json!(s.prefs.glyph_snap))
}
fn caps_lock(s: &mut Session, p: &Value) -> Result<Value> {
if let Some(on) = p.get("on") {
s.caps_lock = on.as_bool().ok_or_else(|| bad("view.capsLock", "on must be true or false"))?;
}
Ok(json!({ "on": s.caps_lock }))
}
fn transparency_grid(s: &mut Session, p: &Value) -> Result<Value> {
let st = s.doc_mut()?;
st.transparency_grid = p.get("on").and_then(Value::as_bool).unwrap_or(!st.transparency_grid);
+2
View File
@@ -48,6 +48,8 @@ pub struct Session {
docs: Vec<DocState>,
active: Option<usize>,
pub prefs: Prefs,
/// Whether Caps Lock is on, as the desktop host reports it; never saved with the preferences.
pub caps_lock: bool,
/// Fill/stroke for new art (the toolbar proxy).
pub paint: PaintDefaults,
/// Which proxy is in front (true = Fill, false = Stroke) — the X key toggles.
+1
View File
@@ -28,6 +28,7 @@ impl Session {
docs: vec![],
active: None,
prefs: Prefs::default(),
caps_lock: false,
paint: PaintDefaults::default(),
fill_active: true,
clipboard: Clipboard::default(),
+25
View File
@@ -389,6 +389,31 @@ fn use_precise_cursors_makes_drawing_cursors_crosshairs() {
assert_eq!(cursor(&mut s, "eyedropper"), Cursor::Eyedropper);
}
/// Caps Lock gives the drawing tools the precise crosshair while it is on, and leaves the saved
/// Use Precise Cursors preference as it was.
#[test]
fn caps_lock_gives_drawing_tools_the_crosshair() {
use vectorcraft_tools::{Cursor, Mods};
let mut s = Session::new();
s.execute("file.new", &json!({"width": 400, "height": 400})).unwrap();
let cursor = |s: &mut Session, tool: &str| {
s.select_tool(tool, ViewInfo::default()).unwrap();
s.cursor(vectorcraft_geom::Point::new(300.0, 300.0), Mods::default(), ViewInfo::default())
};
assert_eq!(s.execute("view.capsLock", &json!({})).unwrap(), json!({"on": false}));
assert_eq!(s.execute("view.capsLock", &json!({"on": true})).unwrap(), json!({"on": true}));
assert!(!s.prefs.use_precise_cursors, "Caps Lock leaves the preference off");
assert_eq!(cursor(&mut s, "pen"), Cursor::Crosshair);
assert_eq!(cursor(&mut s, "lasso"), Cursor::Crosshair);
assert_eq!(cursor(&mut s, "eyedropper"), Cursor::Crosshair);
assert_eq!(cursor(&mut s, "selection"), Cursor::Arrow);
assert_eq!(s.execute("view.capsLock", &json!({"on": false})).unwrap(), json!({"on": false}));
assert_eq!(cursor(&mut s, "pen"), Cursor::Pen);
assert_eq!(cursor(&mut s, "eyedropper"), Cursor::Eyedropper);
assert!(s.execute("view.capsLock", &json!({"on": 1})).is_err());
assert_eq!(s.execute("view.capsLock", &json!({})).unwrap(), json!({"on": false}));
}
/// Use Precise Cursors changes the pointer of the tools that have one of their own: off, each shows its own
/// pointer; on, a crosshair. Every other drawing tool shows a crosshair either way, and the Type tool and the
/// Scissors over no path keep theirs.
+3 -3
View File
@@ -604,8 +604,8 @@ impl Session {
v
}
/// The pointer the active tool shows at `p` (General › Use Precise Cursors makes the drawing
/// tools' a crosshair, [`Cursor::precise`]). With Cmd held, the pointer of the selection tool a
/// The pointer the active tool shows at `p` (General › Use Precise Cursors, or Caps Lock while
/// it is on, makes the drawing tools' a crosshair, [`Cursor::precise`]). With Cmd held, the pointer of the selection tool a
/// press would borrow ([`Self::lend_selection_tool`]).
pub fn cursor(&mut self, p: Point, mods: Mods, view: ViewInfo) -> Cursor {
let lent = if mods.cmd && self.lender.is_none() { self.lent_tool(self.tool.id(), &mods) } else { None };
@@ -619,7 +619,7 @@ impl Session {
}
None => self.with_tool_cx(view, |t, cx| t.cursor(cx, p, mods)),
};
if self.prefs.use_precise_cursors { c.precise() } else { c }
if self.prefs.use_precise_cursors || self.caps_lock { c.precise() } else { c }
}
pub fn tool_options(&self) -> Value {