Hand and Zoom cursors drawn like the tool cursors
On Windows the Hand tool, Space and the Zoom tool showed the wrong cursors: winit maps egui's Grab and Grabbing to the four-headed move cursor (IDC_SIZEALL) and has nothing for ZoomIn/ZoomOut, so the hand appeared as arrows and the magnifier not at all. Those four were the only tool cursors left to the system. They are now vector glyphs like the others (an open and a closed hand, a magnifier with a plus or a minus), shown as OS cursors on the desktop and painted on the web, and the canvas routes the Space, Hand and Zoom states through the same path as the tool cursors. Tests: the glyphs rasterize at every scale with the others; only the busy states stay system cursors. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
db7efb7923
commit
9d6f43c149
|
|
@ -442,14 +442,18 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
|||
let space = ui.input(|i| i.key_down(egui::Key::Space));
|
||||
let panning = matches!(ui.data(|d| d.get_temp::<Drag>(drag_id())), Some(Drag::Pan { .. }));
|
||||
let zooming = if space { m.command } else { app.session.tool_id() == "zoom" };
|
||||
let cur = if panning {
|
||||
egui::CursorIcon::Grabbing
|
||||
// The hand and the magnifier are drawn like the tool cursors below: Windows has no
|
||||
// system cursor for them (winit shows the four-headed move cursor for Grab).
|
||||
let view_cursor = if panning {
|
||||
Some(Cursor::HandGrab)
|
||||
} else if zooming {
|
||||
if m.alt { egui::CursorIcon::ZoomOut } else { egui::CursorIcon::ZoomIn }
|
||||
Some(if m.alt { Cursor::ZoomOut } else { Cursor::ZoomIn })
|
||||
} else if space || app.session.tool_id() == "hand" {
|
||||
egui::CursorIcon::Grab
|
||||
} else if let Some(p) = app.hover_doc {
|
||||
let c = app.session.cursor(p, mods(m, space), view_info);
|
||||
Some(Cursor::Hand)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let cur = if let Some(c) = view_cursor.or_else(|| app.hover_doc.map(|p| app.session.cursor(p, mods(m, space), view_info))) {
|
||||
let painter = ui.ctx().layer_painter(egui::LayerId::new(egui::Order::Tooltip, egui::Id::new("tool-cursor")));
|
||||
// The loaded place cursor carries the file's thumbnail.
|
||||
if app.session.tool_id() == "place"
|
||||
|
|
|
|||
|
|
@ -257,8 +257,46 @@ fn shape_builder(p: &mut Ink, o: Pos2, erase: bool) {
|
|||
}
|
||||
}
|
||||
|
||||
/// The Hand tool and Space: an open hand (palm and four fingers, thumb to the left), or the same
|
||||
/// hand closed while it drags the view. Windows has no system hand that isn't a pointing finger
|
||||
/// (winit maps egui's Grab and Grabbing to the four-headed move cursor), so it is drawn like the
|
||||
/// other tool cursors. Hotspot in the palm.
|
||||
fn hand(p: &mut Ink, o: Pos2, closed: bool) {
|
||||
// Palm: a rounded block below the hotspot.
|
||||
let palm = vec![o + vec2(-7.0, -2.0), o + vec2(7.0, -2.0), o + vec2(8.0, 6.0), o + vec2(4.0, 11.0), o + vec2(-5.0, 11.0), o + vec2(-9.0, 5.0)];
|
||||
poly(p, palm, HALO, INK);
|
||||
// Thumb, out to the left.
|
||||
poly(p, vec![o + vec2(-7.0, 0.0), o + vec2(-13.0, -4.0), o + vec2(-14.0, -1.0), o + vec2(-9.0, 5.0)], HALO, INK);
|
||||
// Four fingers: up from the palm (open), or folded over it (closed).
|
||||
for (i, x) in [-5.5f32, -1.5, 2.5, 6.0].into_iter().enumerate() {
|
||||
let top = if closed {
|
||||
-5.0
|
||||
} else if i == 0 || i == 3 {
|
||||
-11.0
|
||||
} else {
|
||||
-14.0
|
||||
};
|
||||
poly(p, vec![o + vec2(x - 1.8, -2.0), o + vec2(x - 1.8, top), o + vec2(x + 1.8, top), o + vec2(x + 1.8, -2.0)], HALO, INK);
|
||||
}
|
||||
}
|
||||
|
||||
/// The Zoom tool: a magnifier with a plus in the lens (Alt: a minus), the lens centred on the
|
||||
/// hotspot, the handle down to the right.
|
||||
fn magnifier(p: &mut Ink, o: Pos2, out: bool) {
|
||||
const R: f32 = 7.0;
|
||||
p.circle_stroke(o, R, Stroke::new(3.0, HALO));
|
||||
let d = vec2(R * 0.72, R * 0.72);
|
||||
line(p, o + d, o + d + vec2(7.0, 7.0));
|
||||
p.circle_filled(o, R, HALO);
|
||||
p.circle_stroke(o, R, Stroke::new(1.2, INK));
|
||||
line(p, o - vec2(3.5, 0.0), o + vec2(3.5, 0.0));
|
||||
if !out {
|
||||
line(p, o - vec2(0.0, 3.5), o + vec2(0.0, 3.5));
|
||||
}
|
||||
}
|
||||
|
||||
/// The shapes of cursor `c` with its hotspot at `p`; `None` for cursors that stay system cursors
|
||||
/// (hand, zoom, busy states).
|
||||
/// (the busy states).
|
||||
fn glyph(c: Cursor, p: Pos2) -> Option<Vec<Shape>> {
|
||||
let ink = &mut Ink::default();
|
||||
match c {
|
||||
|
|
@ -299,6 +337,10 @@ fn glyph(c: Cursor, p: Pos2) -> Option<Vec<Shape>> {
|
|||
Cursor::TypeWidget => type_widget(ink, p),
|
||||
Cursor::ShapeBuilder => shape_builder(ink, p, false),
|
||||
Cursor::ShapeBuilderErase => shape_builder(ink, p, true),
|
||||
Cursor::Hand => hand(ink, p, false),
|
||||
Cursor::HandGrab => hand(ink, p, true),
|
||||
Cursor::ZoomIn => magnifier(ink, p, false),
|
||||
Cursor::ZoomOut => magnifier(ink, p, true),
|
||||
_ => return None,
|
||||
}
|
||||
Some(std::mem::take(&mut ink.0))
|
||||
|
|
@ -437,7 +479,11 @@ mod tests {
|
|||
use crate::VectorcraftApp;
|
||||
|
||||
/// Every cursor with a glyph.
|
||||
const GLYPHS: [Cursor; 34] = [
|
||||
const GLYPHS: [Cursor; 38] = [
|
||||
Cursor::Hand,
|
||||
Cursor::HandGrab,
|
||||
Cursor::ZoomIn,
|
||||
Cursor::ZoomOut,
|
||||
Cursor::Arrow,
|
||||
Cursor::ArrowHollow,
|
||||
Cursor::Move,
|
||||
|
|
@ -492,9 +538,7 @@ mod tests {
|
|||
assert!(img.rgba.as_chunks::<4>().0.iter().any(|p| p[3] == 255), "{c:?}: nothing opaque");
|
||||
}
|
||||
}
|
||||
for c in [Cursor::Hand, Cursor::HandGrab, Cursor::ZoomIn, Cursor::ZoomOut, Cursor::NotAllowed] {
|
||||
assert!(rasterize(c, 1.0).is_none(), "{c:?} stays a system cursor");
|
||||
}
|
||||
assert!(rasterize(Cursor::NotAllowed, 1.0).is_none(), "the busy states stay system cursors");
|
||||
for ppp in [0.0, -1.0, f32::NAN, f32::INFINITY, 1e9] {
|
||||
assert!(rasterize(Cursor::Arrow, ppp).is_none(), "{ppp}");
|
||||
}
|
||||
|
|
@ -529,7 +573,7 @@ mod tests {
|
|||
let b = images.get(Cursor::Pen, 1.5).unwrap();
|
||||
assert!(Arc::ptr_eq(&a.rgba, &b.rgba));
|
||||
assert!(!Arc::ptr_eq(&a.rgba, &images.get(Cursor::Pen, 2.0).unwrap().rgba));
|
||||
assert!(images.get(Cursor::Hand, 1.5).is_none());
|
||||
assert!(images.get(Cursor::NotAllowed, 1.5).is_none());
|
||||
assert!(images.get(Cursor::Arrow, f32::NAN).is_none());
|
||||
assert_eq!(images.0.len(), 2);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue