Skip to main content

wisp_storybook/stories/
s_cursor_overlay.rs

1//! Story: editor cursor overlay (ED.19 / M-EDIT).
2//!
3//! The cursor is the only performer on a screen recording's stage, so the
4//! editor grooms it: a scaled, dark-outlined white pointer with an expanding
5//! click ripple, drawn as a `wisp` `Graphics` overlay over the framed screen.
6//! This mirrors `wisp::RecordingScene::set_cursor` — the pointer triangle +
7//! ripple discs the cursor node draws each frame at the captured position
8//! (mapped through the same transform as the screen, so it rides the zoom).
9//!
10//! Single-bind-group graphics (no blur) → runs on every CI OS; NOT in
11//! `LAVAPIPE_INCOMPATIBLE`.
12
13use wisp::application::Application;
14use wisp::math::Rect;
15use wisp::{Color, Fill, Graphics, Stage};
16
17pub fn story() -> crate::story::Story {
18    crate::story::Story {
19        id: "editor-cursor-overlay",
20        category: "Editor",
21        title: "Cursor overlay — pointer + click ripple",
22        milestone: "ED.19",
23        writeup: include_str!("writeups/editor_cursor.md"),
24        build,
25        tick: None,
26    }
27}
28
29fn build(_app: &Application, stage: &mut Stage) {
30    // A light "screen" so the cursor + ripple read clearly (the storybook
31    // light-backdrop convention for alpha-blended overlays).
32    let mut screen = Graphics::new();
33    screen.fill(Fill::LinearGradient {
34        start: glam::Vec2::new(0.0, 1.0),
35        end: glam::Vec2::new(0.0, -1.0),
36        color_a: Color::rgb_u8(244, 247, 250),
37        color_b: Color::rgb_u8(206, 216, 228),
38    });
39    screen.draw_rect(Rect::new(-1.0, -1.0, 2.0, 2.0));
40    let _ = stage.add_child(stage.root(), screen);
41
42    // The cursor overlay node: a click ripple under a dark-outlined white
43    // pointer, at the click point.
44    let point = glam::Vec2::new(0.08, 0.04);
45    let half = 0.13;
46    let mut overlay = Graphics::new();
47    // Expanding click ripple — two fading discs (mid-age + young).
48    overlay.fill(Fill::Solid(Color::rgba(0.25, 0.5, 0.95, 0.18)));
49    overlay.draw_ellipse(point, glam::Vec2::splat(half * 2.6));
50    overlay.fill(Fill::Solid(Color::rgba(0.25, 0.5, 0.95, 0.30)));
51    overlay.draw_ellipse(point, glam::Vec2::splat(half * 1.6));
52    // A single convex arrow quad, dark outline behind a white fill — the same
53    // shape RecordingScene::set_cursor draws.
54    let pointer = |overlay: &mut Graphics, s: f32, color: Color| {
55        let scale = half * s;
56        let p = |x: f32, y: f32| point + glam::Vec2::new(x, y) * scale;
57        overlay.fill(Fill::Solid(color));
58        overlay.draw_polygon(&[p(0.0, 0.0), p(0.0, -1.5), p(0.45, -1.75), p(1.05, -0.95)]);
59    };
60    pointer(&mut overlay, 1.25, Color::rgb_u8(20, 20, 20));
61    pointer(&mut overlay, 1.0, Color::WHITE);
62    let _ = stage.add_child(stage.root(), overlay);
63}