Skip to main content

wisp_storybook/stories/
s_text_caption_block.rs

1//! Story: word-wrapped caption block with rounded background
2//! (M-TEXT.9 / AUT-83).
3//!
4//! Demonstrates `wisp::text::CaptionBlock` — a composed scene fragment
5//! that wraps text inside a fixed width and pads a rounded-rect
6//! background around it.
7
8use glam::Vec2;
9use wisp::application::Application;
10use wisp::render::Renderer;
11use wisp::text::{CaptionBlock, TextPreset, TextTexturePipeline, WispText};
12use wisp::texture::render_texture::RenderTexture;
13use wisp::{Color, Container, Fill, Graphics, Sprite, Stage};
14
15use crate::story::Story;
16
17pub fn story() -> Story {
18    Story {
19        id: "text-caption-block",
20        category: "Text",
21        title: "Word-wrapped caption block",
22        milestone: "M-TEXT.9",
23        writeup: include_str!("writeups/text_caption_block.md"),
24        build,
25        tick: None,
26    }
27}
28
29fn build(app: &Application, stage: &mut Stage) {
30    let pipeline = TextTexturePipeline::new(app, wgpu::TextureFormat::Rgba8UnormSrgb);
31    let root = stage.root();
32
33    // ── Subdued cool backdrop (sprite-based, so it stays behind) ──
34    let backdrop_rt = {
35        let renderer = Renderer::new(app, wgpu::TextureFormat::Rgba8UnormSrgb).expect("renderer");
36        let rt = RenderTexture::with_format(app, 256, 256, wgpu::TextureFormat::Rgba8UnormSrgb);
37        let mut bg_stage = Stage::new();
38        let mut g = Graphics::new();
39        g.fill(Fill::Solid(Color::rgba_u8(50, 70, 100, 255)));
40        g.draw_rect(wisp::math::Rect::new(-1.0, -1.0, 2.0, 2.0));
41        g.fill(Fill::Solid(Color::rgba_u8(60, 90, 130, 255)));
42        g.draw_rounded_rect(wisp::math::Rect::new(-0.6, -0.3, 1.2, 0.6), 0.08);
43        let _ = bg_stage.add_child(bg_stage.root(), g);
44        let _ = renderer.render_stage(
45            app,
46            rt.view(),
47            Color::rgba(0.15, 0.18, 0.22, 1.0),
48            &bg_stage,
49        );
50        rt
51    };
52    let mut backdrop = Sprite::from_texture(backdrop_rt.as_texture());
53    backdrop.container.transform.position = Vec2::new(-1.0, -1.0);
54    backdrop.container.transform.scale = Vec2::new(2.0, 2.0);
55    let _ = stage.add_child(root, backdrop);
56
57    // ── Caption block #1: short, single-line ──────────────────
58    let short = CaptionBlock::from_text(
59        WispText::new("Recording").with_style(TextPreset::Caption.style().with_color(Color::WHITE)),
60    )
61    .with_width(0.85)
62    .with_padding(0.05)
63    .with_radius(0.06)
64    .with_background(Color::rgba_u8(20, 25, 30, 220));
65
66    let short_layout = short.layout(app, &pipeline);
67    let short_position = Vec2::new(-0.425, 0.65);
68    let mut short_container = Container::new();
69    short_container.transform.position = short_position;
70    let short_id = stage
71        .add_child(root, short_container)
72        .expect("short container");
73    let _ = stage.add_child(short_id, short_layout.background);
74    let _ = stage.add_child(short_id, short_layout.text_sprite);
75
76    // ── Caption block #2: multi-line wrap ─────────────────────
77    let long = CaptionBlock::from_text(
78        WispText::new("This longer caption wraps inside the fixed width and pads cleanly.")
79            .with_style(TextPreset::Caption.style().with_color(Color::WHITE)),
80    )
81    .with_width(0.85)
82    .with_padding(0.05)
83    .with_radius(0.06)
84    .with_background(Color::rgba_u8(20, 25, 30, 220));
85
86    let long_layout = long.layout(app, &pipeline);
87    let mut long_container = Container::new();
88    long_container.transform.position =
89        Vec2::new(-0.425, short_position.y - short_layout.height_ndc - 0.06);
90    let long_id = stage
91        .add_child(root, long_container)
92        .expect("long container");
93    let _ = stage.add_child(long_id, long_layout.background);
94    let _ = stage.add_child(long_id, long_layout.text_sprite);
95}