1pub mod declarative;
2pub mod immediate;
3pub mod retained;
4
5use crate::{
6 TesselateToGraphics, Vertex, asset_manager::AssetsManager, interactions::AppInteractionsEngine,
7 render_worker::RenderWorkersViewModel, text_measurements::AppTextMeasurementsEngine,
8};
9use glutin::{
10 event::{ElementState, Event, VirtualKeyCode, WindowEvent},
11 window::Window,
12};
13use raui_core::{
14 application::Application,
15 interactive::default_interactions_engine::DefaultInteractionsEngine,
16 layout::{CoordsMapping, CoordsMappingScaling, default_layout_engine::DefaultLayoutEngine},
17 view_model::ViewModel,
18 widget::utils::{Color, Rect},
19};
20use raui_tesselate_renderer::{TesselateRenderer, TessselateRendererDebug};
21use spitfire_fontdue::TextRenderer;
22use spitfire_glow::{
23 app::AppControl,
24 graphics::{Graphics, GraphicsBatch, Shader, Texture},
25 renderer::{GlowTextureFormat, GlowUniformValue},
26};
27use std::{collections::HashMap, time::Instant};
28
29pub use spitfire_glow::app::{App, AppConfig};
30
31#[cfg(debug_assertions)]
32const DEBUG_VERTEX: &str = r#"#version 300 es
33 layout(location = 0) in vec2 a_position;
34 out vec4 v_color;
35 uniform mat4 u_projection_view;
36
37 void main() {
38 gl_Position = u_projection_view * vec4(a_position, 0.0, 1.0);
39 }
40 "#;
41
42#[cfg(debug_assertions)]
43const DEBUG_FRAGMENT: &str = r#"#version 300 es
44 precision highp float;
45 precision highp int;
46 out vec4 o_color;
47 uniform float u_time;
48
49 vec3 hsv2rgb(vec3 c) {
50 vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
51 vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
52 return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
53 }
54
55 void main() {
56 vec2 pixel = floor(gl_FragCoord.xy);
57 float hue = fract((floor(pixel.x) + floor(pixel.y)) * 0.01 + u_time);
58 o_color = vec4(hsv2rgb(vec3(hue, 1.0, 1.0)), 1.0);
59 }
60 "#;
61
62macro_rules! hash_map {
63 ($($key:ident => $value:expr),* $(,)?) => {{
64 let mut result = HashMap::default();
65 $(
66 result.insert(stringify!($key).into(), $value);
67 )*
68 result
69 }};
70}
71
72pub(crate) struct SharedApp {
73 #[allow(clippy::type_complexity)]
74 on_update: Option<Box<dyn FnMut(&mut Application, &mut AppControl)>>,
75 #[allow(clippy::type_complexity)]
77 on_redraw: Option<
78 Box<dyn FnMut(f32, &mut Graphics<Vertex>, &mut TextRenderer<Color>, &mut AppControl)>,
79 >,
80 #[allow(clippy::type_complexity)]
81 on_event: Option<
82 Box<
83 dyn FnMut(
84 &mut Application,
85 Event<()>,
86 &mut Window,
87 &mut DefaultInteractionsEngine,
88 ) -> bool,
89 >,
90 >,
91 application: Application,
92 interactions: AppInteractionsEngine,
93 text_renderer: TextRenderer<Color>,
94 timer: Instant,
95 time: f32,
96 assets: AssetsManager,
97 coords_mapping: CoordsMapping,
98 pub coords_mapping_scaling: CoordsMappingScaling,
99 missing_texutre: Option<Texture>,
100 glyphs_texture: Option<Texture>,
101 colored_shader: Option<Shader>,
102 textured_shader: Option<Shader>,
103 text_shader: Option<Shader>,
104 #[cfg(debug_assertions)]
105 debug_shader: Option<Shader>,
106 #[cfg(debug_assertions)]
107 pub show_raui_aabb_mode: u8,
108 #[cfg(debug_assertions)]
109 pub show_raui_aabb_key: VirtualKeyCode,
110 #[cfg(debug_assertions)]
111 pub print_raui_tree_key: VirtualKeyCode,
112 #[cfg(debug_assertions)]
113 pub print_raui_layout_key: VirtualKeyCode,
114 #[cfg(debug_assertions)]
115 pub print_raui_interactions_key: VirtualKeyCode,
116}
117
118impl Default for SharedApp {
119 fn default() -> Self {
120 let mut application = Application::default();
121 application.setup(raui_core::widget::setup);
122 application.setup(raui_material::setup);
123 application.view_models.insert(
124 RenderWorkersViewModel::VIEW_MODEL.to_owned(),
125 ViewModel::new(RenderWorkersViewModel::default(), Default::default()),
126 );
127 Self {
128 on_update: None,
129 on_redraw: None,
130 on_event: None,
131 application,
132 interactions: Default::default(),
133 text_renderer: TextRenderer::new(1024, 1024),
134 timer: Instant::now(),
135 time: 0.0,
136 assets: Default::default(),
137 coords_mapping: Default::default(),
138 coords_mapping_scaling: Default::default(),
139 missing_texutre: None,
140 glyphs_texture: None,
141 colored_shader: None,
142 textured_shader: None,
143 text_shader: None,
144 #[cfg(debug_assertions)]
145 debug_shader: None,
146 #[cfg(debug_assertions)]
147 show_raui_aabb_mode: 0,
148 #[cfg(debug_assertions)]
149 show_raui_aabb_key: VirtualKeyCode::F9,
150 #[cfg(debug_assertions)]
151 print_raui_tree_key: VirtualKeyCode::F10,
152 #[cfg(debug_assertions)]
153 print_raui_layout_key: VirtualKeyCode::F11,
154 #[cfg(debug_assertions)]
155 print_raui_interactions_key: VirtualKeyCode::F12,
156 }
157 }
158}
159
160impl SharedApp {
161 fn init(&mut self, graphics: &mut Graphics<Vertex>) {
162 self.missing_texutre = Some(graphics.pixel_texture([255, 255, 255]).unwrap());
163 self.glyphs_texture = Some(graphics.pixel_texture([0, 0, 0]).unwrap());
164 self.colored_shader = Some(
165 graphics
166 .shader(Shader::COLORED_VERTEX_2D, Shader::PASS_FRAGMENT)
167 .unwrap(),
168 );
169 self.textured_shader = Some(
170 graphics
171 .shader(Shader::TEXTURED_VERTEX_2D, Shader::TEXTURED_FRAGMENT)
172 .unwrap(),
173 );
174 self.text_shader = Some(
175 graphics
176 .shader(Shader::TEXT_VERTEX, Shader::TEXT_FRAGMENT)
177 .unwrap(),
178 );
179 #[cfg(debug_assertions)]
180 {
181 self.debug_shader = Some(graphics.shader(DEBUG_VERTEX, DEBUG_FRAGMENT).unwrap());
182 }
183 }
184
185 fn redraw(&mut self, graphics: &mut Graphics<Vertex>, control: &mut AppControl) {
186 let elapsed = self.timer.elapsed();
187 self.timer = Instant::now();
188 self.time += elapsed.as_secs_f32();
189 if let Some(callback) = self.on_update.as_mut() {
190 callback(&mut self.application, control);
191 }
192 self.text_renderer.clear();
193 if let Some(callback) = self.on_redraw.as_mut() {
194 callback(
195 elapsed.as_secs_f32(),
196 graphics,
197 &mut self.text_renderer,
198 control,
199 );
200 }
201 {
202 self.application
203 .view_models
204 .get_mut(RenderWorkersViewModel::VIEW_MODEL)
205 .unwrap()
206 .write::<RenderWorkersViewModel>()
207 .unwrap()
208 .maintain(
209 graphics,
210 &mut self.assets,
211 self.colored_shader.as_ref().unwrap(),
212 self.textured_shader.as_ref().unwrap(),
213 self.text_shader.as_ref().unwrap(),
214 );
215 }
216 self.assets.maintain();
217 self.application.animations_delta_time = elapsed.as_secs_f32();
218 self.coords_mapping = CoordsMapping::new_scaling(
219 Rect {
220 left: 0.0,
221 right: graphics.state.main_camera.screen_size.x,
222 top: 0.0,
223 bottom: graphics.state.main_camera.screen_size.y,
224 },
225 self.coords_mapping_scaling,
226 );
227 if self.application.process() {
228 self.assets.load(self.application.rendered_tree(), graphics);
229 let mut layout_engine = DefaultLayoutEngine::new(AppTextMeasurementsEngine {
230 assets: &self.assets,
231 });
232 let _ = self
233 .application
234 .layout(&self.coords_mapping, &mut layout_engine);
235 } else {
236 self.assets.load(self.application.rendered_tree(), graphics);
237 }
238 let _ = self.application.interact(&mut self.interactions);
239 self.application.consume_signals();
240 let matrix = graphics
241 .state
242 .main_camera
243 .world_projection_matrix()
244 .into_col_array();
245 graphics.state.stream.batch_end();
246 for shader in [
247 self.colored_shader.clone(),
248 self.textured_shader.clone(),
249 self.text_shader.clone(),
250 #[cfg(debug_assertions)]
251 self.debug_shader.clone(),
252 ] {
253 graphics.state.stream.batch(GraphicsBatch {
254 shader,
255 uniforms: hash_map! {
256 u_image => GlowUniformValue::I1(0),
257 u_projection_view => GlowUniformValue::M4(matrix),
258 u_time => GlowUniformValue::F1(self.time),
259 },
260 ..Default::default()
261 });
262 graphics.state.stream.batch_end();
263 }
264 let mut converter = TesselateToGraphics {
265 colored_shader: self.colored_shader.as_ref().unwrap(),
266 textured_shader: self.textured_shader.as_ref().unwrap(),
267 text_shader: self.text_shader.as_ref().unwrap(),
268 #[cfg(debug_assertions)]
269 debug_shader: self.debug_shader.as_ref(),
270 glyphs_texture: self.glyphs_texture.as_ref().unwrap(),
271 missing_texture: self.missing_texutre.as_ref().unwrap(),
272 assets: &self.assets,
273 clip_stack: Vec::with_capacity(64),
274 viewport_height: graphics.state.main_camera.screen_size.y as _,
275 projection_view_matrix: matrix,
276 };
277 let mut renderer = TesselateRenderer::new(
278 &self.assets,
279 &mut converter,
280 &mut graphics.state.stream,
281 &mut self.text_renderer,
282 if cfg!(debug_assertions) {
283 match self.show_raui_aabb_mode {
284 0 => None,
285 1 => Some(TessselateRendererDebug {
286 render_non_visual_nodes: false,
287 }),
288 2 => Some(TessselateRendererDebug {
289 render_non_visual_nodes: true,
290 }),
291 _ => unreachable!(),
292 }
293 } else {
294 None
295 },
296 );
297 let _ = self.application.render(&self.coords_mapping, &mut renderer);
298 let [w, h, d] = self.text_renderer.atlas_size();
299 self.glyphs_texture.as_mut().unwrap().upload(
300 w as _,
301 h as _,
302 d as _,
303 GlowTextureFormat::Monochromatic,
304 Some(self.text_renderer.image()),
305 );
306 }
307
308 fn event(&mut self, event: Event<()>, window: &mut Window) -> bool {
309 self.interactions.event(&event, &self.coords_mapping);
310 if let Event::WindowEvent {
311 event: WindowEvent::Resized(_),
312 ..
313 } = &event
314 {
315 self.application.mark_dirty();
316 }
317 #[cfg(debug_assertions)]
318 if let Event::WindowEvent {
319 event: WindowEvent::KeyboardInput { input, .. },
320 ..
321 } = &event
322 && input.state == ElementState::Pressed
323 && let Some(key) = input.virtual_keycode
324 {
325 if key == self.show_raui_aabb_key {
326 self.show_raui_aabb_mode = (self.show_raui_aabb_mode + 1) % 3;
327 println!(
328 "* SHOW RAUI LAYOUT AABB MODE: {:#?}",
329 self.show_raui_aabb_mode
330 );
331 } else if key == self.print_raui_tree_key {
332 println!("* RAUI TREE: {:#?}", self.application.rendered_tree());
333 } else if key == self.print_raui_layout_key {
334 println!("* RAUI LAYOUT: {:#?}", self.application.layout_data());
335 } else if key == self.print_raui_interactions_key {
336 println!("* RAUI INTERACTIONS: {:#?}", self.interactions);
337 }
338 }
339 self.on_event
340 .as_mut()
341 .map(|callback| {
342 callback(
343 &mut self.application,
344 event,
345 window,
346 &mut self.interactions.engine,
347 )
348 })
349 .unwrap_or(true)
350 }
351}