tawasuyu

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 238TOML 102Markdown 83Shell 24178 Definitions

⎇ main widgets/carousel/examples/carousel_demo.rs Rust · 145 lines · 4.4 KB History Who touched what raw

chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros d251eb32b · Sergio ·

1//! Showcase de `llimphi-widget-carousel`: 4 páginas de color con dots,
2//! flechas, **swipe** (arrastra la página y suelta), **teclado** (←/→) y
3//! **auto-play** (avanza sola cada 3 s; cualquier interacción no la detiene,
4//! es un demo).
5//!
6//! Corre con: `cargo run -p llimphi-widget-carousel --example carousel_demo --release`.
7
8use std::sync::Arc;
9use std::time::Duration;
10
11use llimphi_ui::llimphi_layout::taffy::{
12    prelude::{percent, Size, Style},
13    AlignItems, JustifyContent,
14};
15use llimphi_ui::llimphi_raster::peniko::Color;
16use llimphi_ui::{App, Handle, Key, KeyEvent, KeyState, NamedKey, View};
17use llimphi_widget_carousel::{
18    carousel_view, navigate, CarouselPalette, CarouselSpec, CarouselTransition, CarouselWrap,
19};
20
21const N: usize = 4;
22/// Cuánto avanza la transición por tick (~60fps → ~180ms de deslizamiento).
23const TRANS_STEP: f32 = 0.12;
24
25#[derive(Clone)]
26enum Msg {
27    Goto(usize),
28    Swipe(i32),
29    Tick,
30    TransTick,
31}
32
33struct Model {
34    current: usize,
35    transition: Option<CarouselTransition>,
36}
37
38/// Arranca una transición desde `from` hacia la página actual.
39fn begin(m: &mut Model, from: usize) {
40    if from != m.current {
41        m.transition = Some(CarouselTransition { from, t: 0.0 });
42    }
43}
44
45struct Showcase;
46
47impl App for Showcase {
48    type Model = Model;
49    type Msg = Msg;
50
51    fn title() -> &'static str {
52        "llimphi · carousel (swipe · ←/→ · auto-play)"
53    }
54
55    fn initial_size() -> (u32, u32) {
56        (720, 480)
57    }
58
59    fn init(handle: &Handle<Msg>) -> Model {
60        // Auto-play: un tick cada 3 s que avanza una página.
61        handle.spawn_periodic(Duration::from_secs(3), || Msg::Tick);
62        // Animación de la transición (~60 fps).
63        handle.spawn_periodic(Duration::from_millis(16), || Msg::TransTick);
64        Model { current: 0, transition: None }
65    }
66
67    fn on_key(_model: &Model, event: &KeyEvent) -> Option<Msg> {
68        if event.state != KeyState::Pressed {
69            return None;
70        }
71        match &event.key {
72            Key::Named(NamedKey::ArrowLeft) => Some(Msg::Swipe(-1)),
73            Key::Named(NamedKey::ArrowRight) => Some(Msg::Swipe(1)),
74            _ => None,
75        }
76    }
77
78    fn update(model: Model, msg: Msg, _: &Handle<Msg>) -> Model {
79        let mut m = model;
80        match msg {
81            Msg::Goto(i) => {
82                let from = m.current;
83                m.current = i.min(N - 1);
84                begin(&mut m, from);
85            }
86            Msg::Swipe(d) => {
87                let from = m.current;
88                m.current = navigate(m.current, N, d, CarouselWrap::Wrap);
89                begin(&mut m, from);
90            }
91            Msg::Tick => {
92                let from = m.current;
93                m.current = navigate(m.current, N, 1, CarouselWrap::Wrap);
94                begin(&mut m, from);
95            }
96            Msg::TransTick => {
97                if let Some(mut tr) = m.transition {
98                    tr.t += TRANS_STEP;
99                    m.transition = if tr.t >= 1.0 { None } else { Some(tr) };
100                }
101            }
102        }
103        m
104    }
105
106    fn view(model: &Model) -> View<Msg> {
107        let colors = [
108            Color::from_rgba8(58, 90, 140, 255),
109            Color::from_rgba8(140, 72, 82, 255),
110            Color::from_rgba8(70, 130, 100, 255),
111            Color::from_rgba8(120, 100, 150, 255),
112        ];
113        let pages: Vec<View<Msg>> = (0..N)
114            .map(|i| {
115                View::new(Style {
116                    size: Size { width: percent(1.0_f32), height: percent(1.0_f32) },
117                    align_items: Some(AlignItems::CENTER),
118                    justify_content: Some(JustifyContent::CENTER),
119                    ..Default::default()
120                })
121                .fill(colors[i])
122                .text(
123                    format!("Página {}", i + 1),
124                    28.0,
125                    Color::from_rgba8(240, 244, 250, 255),
126                )
127            })
128            .collect();
129
130        carousel_view(CarouselSpec {
131            pages,
132            current: model.current,
133            wrap: CarouselWrap::Wrap,
134            show_arrows: true,
135            palette: CarouselPalette::default(),
136            on_change: Arc::new(Msg::Goto),
137            on_swipe: Some(Arc::new(Msg::Swipe)),
138            transition: model.transition,
139        })
140    }
141}
142
143fn main() {
144    llimphi_ui::run::<Showcase>();
145}