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

⎇ 92dd0e5bfb2ca4456006da99437f4100e156d856 llimphi-text/examples/hello_text.rs Rust · 167 lines · 5.8 KB History Who touched what raw

feat: llimphi standalone — framework UI soberano extraído del monorepo e65e9cc62 · Sergio ·

1//! Texto via parley sobre vello: párrafo wrappeable + shaping (kerning,
2//! ligatures, bidi, fallback CJK/emoji).
3//!
4//! Corre con: `cargo run -p llimphi-text --example hello_text --release`.
5
6use std::sync::Arc;
7
8use llimphi_hal::winit::application::ApplicationHandler;
9use llimphi_hal::winit::dpi::LogicalSize;
10use llimphi_hal::winit::event::WindowEvent;
11use llimphi_hal::winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
12use llimphi_hal::winit::window::{Window, WindowAttributes, WindowId};
13use llimphi_hal::{Hal, Surface, WinitSurface};
14use llimphi_text::peniko::{color::palette, Color};
15use llimphi_text::{draw_block, Alignment, TextBlock, Typesetter};
16
17const PARRAFO: &str = "Llimphi pinta vector preciso sobre el silicio: \
18geometrías exactas, sin cajas negras. شكراً 你好 — el shaping de parley \
19maneja kerning, ligaduras y fallback CJK/Arabic en la misma línea.";
20
21struct State {
22    window: Arc<Window>,
23    hal: Hal,
24    surface: WinitSurface,
25    renderer: llimphi_raster::Renderer,
26    scene: llimphi_raster::vello::Scene,
27    typesetter: Typesetter,
28}
29
30struct App {
31    state: Option<State>,
32}
33
34impl ApplicationHandler for App {
35    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
36        if self.state.is_some() {
37            return;
38        }
39        let window = event_loop
40            .create_window(
41                WindowAttributes::default()
42                    .with_title("llimphi · hello_text")
43                    .with_inner_size(LogicalSize::new(960u32, 540u32)),
44            )
45            .expect("create window");
46        let window = Arc::new(window);
47        let hal = pollster::block_on(Hal::new(None)).expect("hal");
48        let surface = WinitSurface::new(&hal, window.clone()).expect("surface");
49        let renderer = llimphi_raster::Renderer::new(&hal).expect("renderer");
50        let typesetter = Typesetter::new();
51        window.request_redraw();
52        self.state = Some(State {
53            window,
54            hal,
55            surface,
56            renderer,
57            scene: llimphi_raster::vello::Scene::new(),
58            typesetter,
59        });
60    }
61
62    fn window_event(
63        &mut self,
64        event_loop: &ActiveEventLoop,
65        _id: WindowId,
66        event: WindowEvent,
67    ) {
68        let Some(state) = self.state.as_mut() else {
69            return;
70        };
71        match event {
72            WindowEvent::CloseRequested => event_loop.exit(),
73            WindowEvent::Resized(size) => {
74                state.surface.resize(size.width, size.height);
75                state.window.request_redraw();
76            }
77            WindowEvent::RedrawRequested => {
78                let frame = match state.surface.acquire() {
79                    Ok(f) => f,
80                    Err(_) => {
81                        let (w, h) = state.surface.size();
82                        state.surface.resize(w, h);
83                        state.window.request_redraw();
84                        return;
85                    }
86                };
87                let (w, _h) = frame.size();
88                let margin_x = 64.0_f64;
89                let margin_y = 64.0_f64;
90                let inner_w = (w as f32 - 2.0 * margin_x as f32).max(100.0);
91                state.scene.reset();
92
93                // Título centrado
94                draw_block(
95                    &mut state.scene,
96                    &mut state.typesetter,
97                    &TextBlock {
98                        text: "Llimphi",
99                        size_px: 96.0,
100                        color: Color::from_rgba8(220, 230, 240, 255),
101                        origin: (margin_x, margin_y),
102                        max_width: Some(inner_w),
103                        alignment: Alignment::Center,
104                        line_height: 1.0,
105                    
106                        italic: false,
107                        font_family: None,
108                    },
109                );
110
111                // Subtítulo centrado
112                draw_block(
113                    &mut state.scene,
114                    &mut state.typesetter,
115                    &TextBlock {
116                        text: "motor gráfico soberano · parley + vello",
117                        size_px: 20.0,
118                        color: Color::from_rgba8(140, 160, 180, 255),
119                        origin: (margin_x, margin_y + 110.0),
120                        max_width: Some(inner_w),
121                        alignment: Alignment::Center,
122                        line_height: 1.0,
123                    
124                        italic: false,
125                        font_family: None,
126                    },
127                );
128
129                // Párrafo justificado con wrap
130                draw_block(
131                    &mut state.scene,
132                    &mut state.typesetter,
133                    &TextBlock {
134                        text: PARRAFO,
135                        size_px: 22.0,
136                        color: Color::from_rgba8(200, 210, 220, 255),
137                        origin: (margin_x, margin_y + 170.0),
138                        max_width: Some(inner_w),
139                        alignment: Alignment::Justify,
140                        line_height: 1.4,
141                    
142                        italic: false,
143                        font_family: None,
144                    },
145                );
146
147                if let Err(e) = state.renderer.render(
148                    &state.hal,
149                    &state.scene,
150                    &frame,
151                    palette::css::BLACK,
152                ) {
153                    eprintln!("render error: {e}");
154                }
155                state.surface.present(frame, &state.hal);
156            }
157            _ => {}
158        }
159    }
160}
161
162fn main() {
163    let event_loop = EventLoop::new().expect("event loop");
164    event_loop.set_control_flow(ControlFlow::Wait);
165    let mut app = App { state: None };
166    event_loop.run_app(&mut app).expect("run app");
167}