tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
⎇ dba855e446ca2bd4e7d146bdc168fb232cad15a4
llimphi-text/examples/hello_text.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
feat: llimphi standalone — framework UI soberano extraído del monorepo
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}