1use nalgebra::Vector2;
2use std::sync::Arc;
3use std::time::{Duration, Instant};
4use taffy::{
5 AvailableSpace, Dimension, NodeId, PrintTree, Size, Style, TaffyResult, TaffyTree,
6 TraversePartialTree,
7};
8use winit::application::ApplicationHandler;
9use winit::event::WindowEvent;
10use winit::event_loop::ActiveEventLoop;
11use winit::window::{Window, WindowAttributes, WindowId};
12
13use crate::app::context::AppContext;
14use crate::app::font_ctx::FontContext;
15use crate::app::info::AppInfo;
16use crate::app::update::{Update, UpdateManager};
17use crate::config::MayConfig;
18use crate::layout::{LayoutNode, StyleNode};
19use crate::plugin::PluginManager;
20use crate::vgi::{Scene, VectorGraphicsInterface};
21use crate::widget::Widget;
22use maycoon_theme::theme::Theme;
23
24pub struct AppHandler<T, W, S, F, V>
26where
27 T: Theme,
28 W: Widget,
29 F: Fn(AppContext, S) -> W,
30 V: VectorGraphicsInterface,
31{
32 config: MayConfig<T, V>,
33 attrs: WindowAttributes,
34 window: Option<Arc<Window>>,
35 scene: V::Scene,
36 taffy: TaffyTree,
37 window_node: NodeId,
38 builder: F,
39 state: Option<S>,
40 widget: Option<W>,
41 info: AppInfo,
42 update: UpdateManager,
43 last_update: Instant,
44 plugins: PluginManager<T, V>,
45 graphics: V,
46}
47
48impl<T, W, S, F, V> AppHandler<T, W, S, F, V>
49where
50 T: Theme,
51 W: Widget,
52 F: Fn(AppContext, S) -> W,
53 V: VectorGraphicsInterface,
54{
55 #[inline(always)]
57 #[tracing::instrument(level = "trace", skip_all)]
58 pub fn new(
59 attrs: WindowAttributes,
60 config: MayConfig<T, V>,
61 builder: F,
62 state: S,
63 font_context: FontContext,
64 update: UpdateManager,
65 plugins: PluginManager<T, V>,
66 ) -> Self {
67 tracing::trace!("creating taffy tree");
68 let mut taffy = TaffyTree::with_capacity(16);
69
70 let window_node = taffy
72 .new_leaf(Style::default())
73 .expect("Failed to create window node");
74
75 let size = config.window.size;
76
77 let graphics = config.graphics.clone();
78
79 Self {
80 attrs,
81 window: None,
82 config,
83 scene: Scene::new(),
84 taffy,
85 widget: None,
86 info: AppInfo {
87 font_context,
88 size,
89 ..Default::default()
90 },
91 window_node,
92 builder,
93 state: Some(state),
94 update,
95 last_update: Instant::now(),
96 plugins,
97 graphics: V::new(graphics).expect("Failed to create vector graphics interface"),
98 }
99 }
100
101 #[inline(always)]
103 #[tracing::instrument(level = "trace", skip_all)]
104 pub fn context(&self) -> AppContext {
105 AppContext::new(self.update.clone(), self.info.diagnostics)
106 }
107
108 #[tracing::instrument(level = "trace", skip(self, style))]
110 fn layout_widget(&mut self, parent: NodeId, style: &StyleNode) -> TaffyResult<()> {
111 let node = self.taffy.new_leaf(style.style.clone().into())?;
112
113 self.taffy.add_child(parent, node)?;
114
115 for child in &style.children {
116 self.layout_widget(node, child)?;
117 }
118
119 Ok(())
120 }
121
122 #[inline(always)]
124 #[tracing::instrument(level = "trace", skip(self))]
125 fn compute_layout(&mut self) -> TaffyResult<()> {
126 self.taffy.compute_layout(
127 self.window_node,
128 Size::<AvailableSpace> {
129 width: AvailableSpace::Definite(
130 self.window.as_ref().unwrap().inner_size().width as f32,
131 ),
132 height: AvailableSpace::Definite(
133 self.window.as_ref().unwrap().inner_size().height as f32,
134 ),
135 },
136 )?;
137 Ok(())
138 }
139
140 #[inline(always)]
142 #[tracing::instrument(level = "trace", skip(self, style))]
143 fn collect_layout(&mut self, node: NodeId, style: &StyleNode) -> TaffyResult<LayoutNode> {
144 tracing::trace!("collecting node layout {node:?}");
145
146 let mut children = Vec::with_capacity(style.children.capacity());
147
148 for (i, style) in style.children.iter().enumerate() {
149 let child = self.taffy.child_at_index(node, i)?;
150
151 tracing::trace!("collecting layout of child {child:?}");
152
153 children.push(self.collect_layout(child, style)?);
154 }
155
156 Ok(LayoutNode {
157 layout: self.taffy.get_final_layout(node),
158 children,
159 })
160 }
161
162 #[inline(always)]
164 #[tracing::instrument(level = "trace", skip(self))]
165 fn request_redraw(&self) {
166 tracing::trace!("requesting redraw");
167 self.window.as_ref().unwrap().request_redraw();
168 }
169
170 #[inline(always)]
172 #[tracing::instrument(level = "trace", skip(self))]
173 fn render(
174 &mut self,
175 window: Arc<Window>,
176 event_loop: &ActiveEventLoop,
177 ) -> Result<(), V::Error> {
178 tracing::trace!("rendering via vector graphics interface");
179 self.graphics.render(
180 window,
181 event_loop,
182 &self.scene,
183 self.config.theme.window_background(),
184 )?;
185
186 Ok(())
187 }
188
189 #[inline(always)]
191 #[tracing::instrument(level = "trace", skip_all)]
192 fn update(&mut self, event_loop: &ActiveEventLoop) {
193 tracing::trace!("updating plugins");
195 self.plugins.run(|pl| {
196 pl.on_update(
197 &mut self.config,
198 self.window.as_ref().expect("Window not initialized"),
199 &mut self.scene,
200 &mut self.taffy,
201 self.window_node,
202 &mut self.info,
203 &self.update,
204 &mut self.last_update,
205 event_loop,
206 )
207 });
208
209 if self.taffy.child_count(self.window_node) == 0 {
211 tracing::trace_span!("complete layout").in_scope(|| {
212 let style = self.widget.as_ref().unwrap().layout_style();
213
214 self.layout_widget(self.window_node, &style)
215 .expect("Failed to layout window");
216
217 self.compute_layout().expect("Failed to compute layout");
218
219 self.update.insert(Update::FORCE);
220 });
221 }
222
223 let style = self.widget.as_ref().unwrap().layout_style();
224
225 let mut layout_node = self
226 .collect_layout(
227 self.taffy.child_at_index(self.window_node, 0).unwrap(),
228 &style,
229 )
230 .expect("Failed to collect layout");
231
232 let context = self.context();
234
235 tracing::trace!("updating widget");
236 self.update.insert(
237 self.widget
238 .as_mut()
239 .unwrap()
240 .update(&layout_node, context, &self.info),
241 );
242
243 if self.update.get().intersects(Update::LAYOUT | Update::FORCE) {
245 tracing::trace_span!("layout").in_scope(|| {
246 self.taffy
248 .set_children(self.window_node, &[])
249 .expect("Failed to set children");
250
251 let style = self.widget.as_ref().unwrap().layout_style();
252
253 self.layout_widget(self.window_node, &style)
254 .expect("Failed to layout window");
255
256 self.compute_layout().expect("Failed to compute layout");
257
258 layout_node = self
259 .collect_layout(
260 self.taffy.child_at_index(self.window_node, 0).unwrap(),
261 &style,
262 )
263 .expect("Failed to collect layout");
264 });
265 }
266
267 if self.update.get().intersects(Update::FORCE | Update::DRAW) {
269 tracing::trace_span!("draw").in_scope(|| {
270 tracing::trace!("resetting vector graphics interface scene");
272 self.scene.reset();
273
274 let context = self.context();
275
276 tracing::trace!("drawing root widget");
277 self.widget.as_mut().unwrap().render(
278 &mut self.scene,
279 &mut self.config.theme,
280 &layout_node,
281 &self.info,
282 context,
283 );
284
285 let window = self.window.clone().expect("Window not initialized");
286
287 if window.inner_size().width != 0 && window.inner_size().height != 0 {
289 self.render(window, event_loop)
290 .expect("Failed rendering process");
291 } else {
292 tracing::debug!("skipping render due to invalid surface");
293 }
294 });
295 }
296
297 if self.update.get().intersects(Update::EVAL | Update::FORCE) {
299 tracing::trace!("re-evaluating application state");
300 self.request_redraw();
301 }
302
303 if self.update.get().intersects(Update::EXIT) {
305 tracing::trace!("exiting event loop");
306 event_loop.exit();
307 return;
308 }
309
310 tracing::trace!("resetting app info and update states");
312 self.info.reset();
313 self.update.clear();
314
315 tracing::trace!("updating diagnostics");
317
318 self.info.diagnostics.first_run = false;
319
320 if self.last_update.elapsed() >= Duration::from_secs(1) {
321 self.last_update = Instant::now();
322
323 self.info.diagnostics.updates_per_sec =
325 (self.info.diagnostics.updates_per_sec + self.info.diagnostics.updates) / 2;
326
327 self.info.diagnostics.updates = 0;
329 } else {
330 self.info.diagnostics.updates += 1;
332 }
333
334 tracing::debug!("updates per sec: {}", self.info.diagnostics.updates_per_sec);
335 }
336
337 #[cold]
338 fn close(&mut self, window: Arc<Window>, event_loop: &ActiveEventLoop) {
339 tracing::trace!("close requested");
340
341 self.graphics
342 .destroy(window, event_loop)
343 .expect("Failed to destroy vector graphics interface");
344
345 if self.config.window.close_on_request {
346 tracing::info!("exiting event loop");
347 event_loop.exit();
348 }
349 }
350}
351
352impl<T, W, S, F, V> ApplicationHandler for AppHandler<T, W, S, F, V>
353where
354 T: Theme,
355 W: Widget,
356 F: Fn(AppContext, S) -> W,
357 V: VectorGraphicsInterface,
358{
359 #[inline(always)]
360 #[tracing::instrument(level = "trace", skip_all)]
361 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
362 tracing::debug!("resuming plugins");
363 self.plugins.run(|pl| {
364 pl.on_resume(
365 &mut self.config,
366 &mut self.scene,
367 &mut self.taffy,
368 self.window_node,
369 &mut self.info,
370 &self.update,
371 &mut self.last_update,
372 event_loop,
373 )
374 });
375
376 tracing::info!("creating window");
377 let window = Arc::new(
378 event_loop
379 .create_window(self.attrs.clone())
380 .expect("Failed to create window"),
381 );
382
383 tracing::info!("initializing layout");
384 let size = window.inner_size();
385
386 self.taffy
387 .set_style(
388 self.window_node,
389 Style {
390 size: Size::<Dimension> {
391 width: Dimension::length(size.width as f32),
392 height: Dimension::length(size.height as f32),
393 },
394 ..Default::default()
395 },
396 )
397 .expect("Failed to set window node style");
398
399 tracing::info!("initializing vector graphics interface");
400 self.graphics
401 .init(window.clone(), event_loop)
402 .expect("Failed to initialize vector graphics interface");
403
404 tracing::info!("building root widget");
405 self.widget = Some((self.builder)(
406 AppContext::new(self.update.clone(), self.info.diagnostics),
407 self.state.take().unwrap(),
408 ));
409
410 self.window = Some(window);
411 }
412
413 #[inline(always)]
414 #[tracing::instrument(level = "trace", skip_all, fields(event =
415 ?event))]
416 fn window_event(
417 &mut self,
418 event_loop: &ActiveEventLoop,
419 window_id: WindowId,
420 mut event: WindowEvent,
421 ) {
422 tracing::trace!("running plugin window event");
423 self.plugins.run(|pl| {
424 pl.on_window_event(
425 &mut event,
426 &mut self.config,
427 self.window.as_ref().unwrap(),
428 &mut self.scene,
429 &mut self.taffy,
430 self.window_node,
431 &mut self.info,
432 &self.update,
433 &mut self.last_update,
434 event_loop,
435 )
436 });
437
438 if let Some(window) = &self.window
439 && window.id() == window_id
440 {
441 match event {
442 WindowEvent::Resized(new_size) => {
443 tracing::debug!("resizing window to {new_size:?}");
444
445 if new_size.width != 0 && new_size.height != 0 {
446 tracing::trace!("resizing vector graphics interface");
447 self.graphics
448 .resize(
449 window.clone(),
450 event_loop,
451 Vector2::new(new_size.width, new_size.height),
452 )
453 .expect("Failed to resize vector graphics interface");
454
455 tracing::trace!("resizing root layout node");
456 self.taffy
457 .set_style(
458 self.window_node,
459 Style {
460 size: Size::<Dimension> {
461 width: Dimension::length(new_size.width as f32),
462 height: Dimension::length(new_size.height as f32),
463 },
464 ..Default::default()
465 },
466 )
467 .expect("Failed to set window node style");
468
469 self.info.size =
470 Vector2::new(new_size.width as f64, new_size.height as f64);
471
472 self.request_redraw();
473
474 self.update.insert(Update::DRAW | Update::LAYOUT);
475 } else {
476 tracing::trace!("window size is 0x0, ignoring resize event");
477 }
478 },
479
480 WindowEvent::CloseRequested => {
481 self.close(window.clone(), event_loop);
482 },
483
484 WindowEvent::RedrawRequested => {
485 window.request_redraw();
486 self.update(event_loop);
487 },
488
489 WindowEvent::CursorLeft { .. } => {
490 self.info.cursor_pos = None;
491 self.request_redraw();
492 },
493
494 WindowEvent::CursorMoved { position, .. } => {
495 self.info.cursor_pos = Some(Vector2::new(position.x as f32, position.y as f32));
496 self.request_redraw();
497 },
498
499 WindowEvent::KeyboardInput {
500 event,
501 device_id,
502 is_synthetic,
503 } => {
504 if !is_synthetic {
505 tracing::trace!("keyboard input {event:?}");
506
507 self.info.keys.push((device_id, event));
508 self.request_redraw();
509 }
510 },
511
512 WindowEvent::MouseInput {
513 device_id,
514 button,
515 state,
516 } => {
517 tracing::trace!("mouse input {button:?} {state:?}");
518
519 self.info.buttons.push((device_id, button, state));
520 self.request_redraw();
521 },
522
523 WindowEvent::MouseWheel { delta, .. } => {
524 tracing::trace!("mouse wheel {delta:?}");
525 self.info.mouse_scroll_delta = Some(delta);
526 self.request_redraw();
527 },
528
529 WindowEvent::Destroyed => tracing::info!("window destroyed"),
530
531 _ => (),
532 }
533 }
534 }
535
536 #[cold]
537 #[tracing::instrument(level = "trace", skip_all)]
538 fn suspended(&mut self, event_loop: &ActiveEventLoop) {
539 tracing::trace!("destroying vector graphics interface");
540 let window = self.window.clone().unwrap();
541 self.graphics
542 .uninit(window, event_loop)
543 .expect("Failed to destroy vector graphics interface");
544
545 tracing::trace!("destroying window");
546 self.window = None;
547
548 tracing::trace!("running plugin suspensions");
549 self.plugins.run(|pl| {
550 pl.on_suspended(
551 &mut self.config,
552 &mut self.scene,
553 &mut self.taffy,
554 self.window_node,
555 &mut self.info,
556 &self.update,
557 &mut self.last_update,
558 event_loop,
559 )
560 });
561
562 self.info.reset();
563 }
564}