1use std::sync::atomic::Ordering;
2use std::time::{Duration, Instant};
3
4use anathema_backend::{Backend, WidgetCycle};
5use anathema_geometry::Size;
6use anathema_state::{Changes, StateId, States, clear_all_changes, clear_all_subs, drain_changes};
7use anathema_store::tree::root_node;
8use anathema_templates::blueprints::Blueprint;
9use anathema_templates::{Document, Globals};
10use anathema_value_resolver::{AttributeStorage, FunctionTable, Scope};
11use anathema_widgets::components::deferred::{CommandKind, DeferredComponents};
12use anathema_widgets::components::events::Event;
13use anathema_widgets::components::{
14 AnyComponentContext, AssociatedEvents, ComponentKind, ComponentRegistry, Emitter, ViewMessage,
15};
16use anathema_widgets::layout::{LayoutCtx, Viewport};
17use anathema_widgets::query::Children;
18use anathema_widgets::tabindex::{Index, TabIndex};
19use anathema_widgets::{
20 Component, Components, Factory, FloatingWidgets, GlyphMap, WidgetContainer, WidgetId, WidgetKind, WidgetTree,
21 eval_blueprint, update_widget,
22};
23use flume::Receiver;
24use notify::RecommendedWatcher;
25
26pub(crate) use self::error::show_error;
27use crate::builder::Builder;
28pub use crate::error::Result;
29use crate::events::GlobalEventHandler;
30use crate::{Error, REBUILD};
31
32mod error;
33mod testing;
34
35pub struct Runtime<G> {
37 pub(super) blueprint: Blueprint,
38 pub(super) globals: Globals,
39 pub(super) factory: Factory,
40 pub(super) states: States,
41 pub(super) component_registry: ComponentRegistry,
42 pub(super) components: Components,
43 pub(super) document: Document,
44 pub(super) floating_widgets: FloatingWidgets,
45 pub(super) assoc_events: AssociatedEvents,
46 pub(super) glyph_map: GlyphMap,
47 pub(super) changes: Changes,
48 pub(super) viewport: Viewport,
49 pub(super) emitter: Emitter,
50 pub(super) sleep_micros: u64,
51 pub(super) message_receiver: flume::Receiver<ViewMessage>,
52 pub(super) dt: Instant,
53 pub(super) _watcher: Option<RecommendedWatcher>,
54 pub(super) deferred_components: DeferredComponents,
55 pub(super) global_event_handler: G,
56 pub(super) function_table: FunctionTable,
57}
58
59impl Runtime<()> {
60 pub fn builder<B: Backend>(doc: Document, backend: &B) -> Builder<()> {
62 Builder::new(doc, backend.size(), ())
63 }
64}
65
66impl<G: GlobalEventHandler> Runtime<G> {
67 pub(crate) fn new(
68 blueprint: Blueprint,
69 globals: Globals,
70 component_registry: ComponentRegistry,
71 document: Document,
72 factory: Factory,
73 message_receiver: Receiver<ViewMessage>,
74 emitter: Emitter,
75 watcher: Option<RecommendedWatcher>,
76 size: Size,
77 fps: u32,
78 global_event_handler: G,
79 function_table: FunctionTable,
80 ) -> Self {
81 let sleep_micros: u64 = ((1.0 / fps as f64) * 1000.0 * 1000.0) as u64;
82
83 Self {
84 component_registry,
85 components: Components::new(),
86 document,
87 factory,
88 states: States::new(),
89 floating_widgets: FloatingWidgets::empty(),
90 assoc_events: AssociatedEvents::new(),
91 glyph_map: GlyphMap::empty(),
92 blueprint,
93 globals,
94 changes: Changes::empty(),
95 viewport: Viewport::new(size),
96 message_receiver,
97 emitter,
98 dt: Instant::now(),
99 _watcher: watcher,
100 deferred_components: DeferredComponents::new(),
101 sleep_micros,
102 global_event_handler,
103 function_table,
104 }
105 }
106
107 pub fn with_frame<B, F>(&mut self, backend: &mut B, mut f: F) -> Result<()>
111 where
112 B: Backend,
113 F: FnMut(&mut B, Frame<'_, '_, G>) -> Result<()>,
114 {
115 let mut tree = WidgetTree::empty();
116 let mut attribute_storage = AttributeStorage::empty();
117 let mut frame = self.next_frame(&mut tree, &mut attribute_storage)?;
118 frame.init_tree()?;
119 f(backend, frame)
120 }
121
122 pub fn run<B: Backend>(&mut self, backend: &mut B) -> Result<()> {
123 let sleep_micros = self.sleep_micros;
124 self.with_frame(backend, |backend, mut frame| {
125 frame.tick(backend)?;
129
130 let mut tabindex = TabIndex::new(&mut frame.tabindex, frame.tree.view());
131 tabindex.next();
132
133 if let Some(current) = frame.tabindex.as_ref() {
134 frame.with_component(current.widget_id, current.state_id, |comp, children, ctx| {
135 comp.dyn_component.any_focus(children, ctx)
136 });
137 }
138
139 loop {
140 frame.tick(backend)?;
141 if frame.layout_ctx.stop_runtime {
142 return Err(Error::Stop);
143 }
144
145 frame.present(backend);
146 frame.cleanup();
147 std::thread::sleep(Duration::from_micros(sleep_micros));
148
149 if REBUILD.swap(false, Ordering::Relaxed) {
150 frame.force_rebuild()?;
151 backend.clear();
152 break Ok(());
153 }
154 }
155 })?;
156
157 Ok(())
158 }
159
160 pub fn next_frame<'frame, 'bp>(
161 &'bp mut self,
162 tree: &'frame mut WidgetTree<'bp>,
163 attribute_storage: &'frame mut AttributeStorage<'bp>,
164 ) -> Result<Frame<'frame, 'bp, G>> {
165 let layout_ctx = LayoutCtx::new(
166 &self.globals,
167 &self.factory,
168 &mut self.states,
169 attribute_storage,
170 &mut self.components,
171 &mut self.component_registry,
172 &mut self.floating_widgets,
173 &mut self.glyph_map,
174 &mut self.viewport,
175 &self.function_table,
176 );
177
178 let inst = Frame {
179 document: &self.document,
180 blueprint: &self.blueprint,
181 tree,
182 layout_ctx,
183 changes: &mut self.changes,
184 sleep_micros: self.sleep_micros,
185
186 assoc_events: &mut self.assoc_events,
187 deferred_components: &mut self.deferred_components,
188
189 emitter: &self.emitter,
190 message_receiver: &self.message_receiver,
191
192 dt: &mut self.dt,
193 needs_layout: true,
194
195 global_event_handler: &self.global_event_handler,
196 tabindex: None,
197 };
198
199 Ok(inst)
200 }
201
202 pub(super) fn reload(&mut self) -> Result<()> {
203 clear_all_changes();
204 clear_all_subs();
205
206 self.components = Components::new();
207 self.floating_widgets = FloatingWidgets::empty();
208
209 self.document.reload_templates()?;
211
212 let (blueprint, globals) = self.document.compile()?;
213 self.blueprint = blueprint;
214 self.globals = globals;
215
216 Ok(())
217 }
218}
219
220pub struct Frame<'rt, 'bp, G> {
221 document: &'bp Document,
222 blueprint: &'bp Blueprint,
223 pub tree: &'rt mut WidgetTree<'bp>,
224 deferred_components: &'rt mut DeferredComponents,
225 layout_ctx: LayoutCtx<'rt, 'bp>,
226 changes: &'rt mut Changes,
227 assoc_events: &'rt mut AssociatedEvents,
228 sleep_micros: u64,
229 emitter: &'rt Emitter,
230 message_receiver: &'rt flume::Receiver<ViewMessage>,
231 dt: &'rt mut Instant,
232 needs_layout: bool,
233 global_event_handler: &'rt G,
234 pub tabindex: Option<Index>,
235}
236
237impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
238 pub fn force_rebuild(mut self) -> Result<()> {
239 for i in 0..self.layout_ctx.components.len() {
241 let Some((widget_id, state_id)) = self.layout_ctx.components.get_ticking(i) else { continue };
242 let event = Event::Unmount;
243 self.send_event_to_component(event, widget_id, state_id);
244 }
245
246 self.return_state_and_component();
247 Ok(())
248 }
249
250 pub fn handle_global_event(&mut self, event: Event) -> Option<Event> {
251 let mut tabindex = TabIndex::new(&mut self.tabindex, self.tree.view());
252
253 let event = self
254 .global_event_handler
255 .handle(event, &mut tabindex, self.deferred_components);
256
257 if tabindex.changed {
258 let prev = tabindex.consume();
259 if let Some(prev) = prev {
260 self.with_component(prev.widget_id, prev.state_id, |comp, children, ctx| {
261 comp.dyn_component.any_blur(children, ctx)
262 });
263 }
264
265 if let Some(current) = self.tabindex.as_ref() {
266 self.with_component(current.widget_id, current.state_id, |comp, children, ctx| {
267 comp.dyn_component.any_focus(children, ctx)
268 });
269 }
270 }
271
272 event
273 }
274
275 pub fn event(&mut self, event: Event) {
276 #[cfg(feature = "profile")]
277 puffin::profile_function!();
278
279 let Some(event) = self.handle_global_event(event) else { return };
280 if let Event::Stop = event {
281 self.layout_ctx.stop_runtime = true;
282 return;
283 }
284
285 match event {
286 Event::Noop => (),
287 Event::Stop => todo!(),
288
289 Event::Blur | Event::Focus | Event::Key(_) => {
291 let Some(Index {
292 widget_id, state_id, ..
293 }) = self.tabindex
294 else {
295 return;
296 };
297 self.send_event_to_component(event, widget_id, state_id);
298 }
299 Event::Mouse(_) | Event::Resize(_) => {
300 for i in 0..self.layout_ctx.components.len() {
301 let Some((widget_id, state_id)) = self.layout_ctx.components.get(i) else { continue };
302 self.send_event_to_component(event, widget_id, state_id);
303 }
304 }
305 Event::Tick(_) | Event::Mount | Event::Unmount => panic!("this event should never be sent to the runtime"),
306 }
307 }
308
309 pub fn init_tree(&mut self) -> Result<()> {
311 let mut ctx = self.layout_ctx.eval_ctx(None);
312 eval_blueprint(
313 self.blueprint,
314 &mut ctx,
315 &Scope::root(),
316 root_node(),
317 &mut self.tree.view(),
318 )?;
319
320 Ok(())
321 }
322
323 pub fn tick<B: Backend>(&mut self, backend: &mut B) -> Result<Duration> {
324 #[cfg(feature = "profile")]
325 puffin::GlobalProfiler::lock().new_frame();
326
327 let now = Instant::now();
328 self.init_new_components();
329 let elapsed = self.handle_messages(now);
330
331 self.poll_events(elapsed, now, backend);
333 self.drain_deferred_commands();
334 self.drain_assoc_events();
335
336 self.apply_changes()?;
340 self.apply_changes()?;
341
342 self.tick_components(self.dt.elapsed());
343 self.cycle(backend)?;
344
345 *self.dt = Instant::now();
347
348 match self.layout_ctx.stop_runtime {
349 false => Ok(now.elapsed()),
350 true => Err(Error::Stop),
351 }
352 }
353
354 pub fn present<B: Backend>(&mut self, backend: &mut B) -> Duration {
355 #[cfg(feature = "profile")]
356 puffin::profile_function!();
357
358 let now = Instant::now();
359 backend.render(self.layout_ctx.glyph_map);
360 backend.clear();
361 now.elapsed()
362 }
363
364 pub fn cleanup(&mut self) {
365 #[cfg(feature = "profile")]
366 puffin::profile_function!();
367 for key in self.tree.drain_removed() {
368 self.layout_ctx.attribute_storage.try_remove(key);
369 self.layout_ctx.floating_widgets.try_remove(key);
370 self.layout_ctx.components.try_remove(key);
371 if let Some(Index { widget_id, .. }) = self.tabindex {
372 if widget_id == key {
373 self.tabindex.take();
374 }
375 }
376 }
377 }
378
379 fn handle_messages(&mut self, fps_now: Instant) -> Duration {
380 #[cfg(feature = "profile")]
381 puffin::profile_function!();
382
383 while let Ok(msg) = self.message_receiver.try_recv() {
384 if let Some((widget_id, state_id)) = self
385 .layout_ctx
386 .components
387 .get_by_component_id(msg.recipient())
388 .map(|e| (e.widget_id, e.state_id))
389 {
390 self.with_component(widget_id, state_id, |comp, elements, ctx| {
391 comp.dyn_component.any_message(elements, ctx, msg.payload())
392 });
393 }
394
395 if fps_now.elapsed().as_micros() as u64 >= self.sleep_micros / 2 {
397 break;
398 }
399 }
400
401 fps_now.elapsed()
402 }
403
404 fn poll_events<B: Backend>(&mut self, remaining: Duration, fps_now: Instant, backend: &mut B) {
405 while let Some(event) = backend.next_event(remaining) {
406 if let Event::Resize(size) = event {
407 self.layout_ctx.viewport.resize(size);
408 self.needs_layout = true;
409 backend.resize(size, self.layout_ctx.glyph_map);
410 }
411
412 if let Event::Stop = event {
413 self.layout_ctx.stop_runtime = true;
414 break;
415 }
416
417 self.event(event);
418
419 if fps_now.elapsed().as_micros() as u64 > self.sleep_micros {
421 break;
422 }
423 }
424 }
425
426 fn drain_deferred_commands(&mut self) {
427 #[cfg(feature = "profile")]
428 puffin::profile_function!();
429
430 let commands = self.deferred_components.drain().collect::<Vec<_>>();
435 for mut cmd in commands {
436 for index in 0..self.layout_ctx.components.len() {
437 let Some((widget_id, state_id)) = self.layout_ctx.components.get(index) else { continue };
438 let Some(comp) = self.tree.get_ref(widget_id) else { continue };
439 let WidgetContainer {
440 kind: WidgetKind::Component(comp),
441 ..
442 } = comp
443 else {
444 continue;
445 };
446 let attributes = self.layout_ctx.attribute_storage.get(widget_id);
447 if !cmd.filter_component(comp, attributes) {
448 continue;
449 }
450
451 if let CommandKind::Focus = cmd.kind {
458 if let Some(index) = self.tabindex.as_ref() {
460 if index.widget_id == widget_id {
461 continue;
462 }
463 }
464
465 let new_index = self
467 .with_component(widget_id, state_id, |comp, children, ctx| {
468 if comp.dyn_component.any_accept_focus() {
469 let index = Index {
470 path: children.parent_path().into(),
471 index: comp.tabindex,
472 widget_id,
473 state_id,
474 };
475
476 comp.dyn_component.any_focus(children, ctx);
477
478 Some(index)
479 } else {
480 None
481 }
482 })
483 .flatten();
484
485 if let Some(index) = new_index {
486 if let Some(old) = self.tabindex.replace(index) {
489 self.with_component(old.widget_id, old.state_id, |comp, children, ctx| {
490 comp.dyn_component.any_blur(children, ctx)
491 });
492 }
493 }
494 }
495
496 if let CommandKind::SendMessage(msg) = cmd.kind {
500 self.with_component(widget_id, state_id, |comp, children, ctx| {
501 comp.dyn_component.any_message(children, ctx, msg);
502 });
503 break;
504 }
505 }
506 }
507 }
508
509 fn drain_assoc_events(&mut self) {
510 #[cfg(feature = "profile")]
511 puffin::profile_function!();
512
513 while let Some(assoc_event) = self.assoc_events.next() {
514 let mut parent = assoc_event.parent;
515 let external_ident = self.document.strings.get_ref_unchecked(assoc_event.external());
516 let internal_ident = self.document.strings.get_ref_unchecked(assoc_event.internal());
517 let sender = self.document.strings.get_ref_unchecked(assoc_event.sender);
518 let sender_id = assoc_event.sender_id;
519 let mut event = assoc_event.to_event(internal_ident, external_ident, sender, sender_id);
520
521 loop {
522 let Some((widget_id, state_id)) = self.layout_ctx.components.get_by_widget_id(parent.into()) else {
523 return;
524 };
525
526 let stop_propagation = self
527 .with_component(widget_id, state_id, |comp, children, ctx| {
528 let next_parent = ctx.parent();
529 comp.dyn_component.any_component_event(children, ctx, &mut event);
530
531 parent = match next_parent {
532 Some(p) => p,
533 None => return true,
534 };
535
536 event.should_stop_propagation()
537 })
538 .unwrap_or(true);
539
540 if stop_propagation {
541 break;
542 }
543 }
544 }
545 }
546
547 fn cycle<B: Backend>(&mut self, backend: &mut B) -> Result<()> {
548 #[cfg(feature = "profile")]
549 puffin::profile_function!();
550
551 let mut cycle = WidgetCycle::new(backend, self.tree.view(), self.layout_ctx.viewport.constraints());
552 cycle.run(&mut self.layout_ctx, self.needs_layout)?;
553
554 self.needs_layout = false;
555 Ok(())
556 }
557
558 fn apply_changes(&mut self) -> Result<()> {
559 #[cfg(feature = "profile")]
560 puffin::profile_function!();
561
562 drain_changes(self.changes);
563
564 if self.changes.is_empty() {
565 return Ok(());
566 }
567
568 self.needs_layout = true;
569 let mut tree = self.tree.view();
570
571 self.changes.iter().try_for_each(|(sub, change)| {
572 sub.iter().try_for_each(|value_id| {
573 let widget_id = value_id.key();
574
575 if let Some(widget) = tree.get_mut(widget_id) {
576 if let WidgetKind::Element(element) = &mut widget.kind {
577 element.invalidate_cache();
578 }
579 }
580
581 if !tree.contains(widget_id) {
583 return Result::Ok(());
584 }
585
586 tree.with_value_mut(widget_id, |_path, widget, tree| {
587 update_widget(widget, value_id, change, tree, &mut self.layout_ctx)
588 })
589 .unwrap_or(Ok(()))?;
590
591 Ok(())
592 })?;
593
594 Result::Ok(())
595 })?;
596
597 self.changes.clear();
598
599 Ok(())
600 }
601
602 fn send_event_to_component(&mut self, event: Event, widget_id: WidgetId, state_id: StateId) {
603 self.with_component(widget_id, state_id, |comp, elements, ctx| {
604 comp.dyn_component.any_event(elements, ctx, event);
605 });
606 }
607
608 fn with_component<F, U>(&mut self, widget_id: WidgetId, state_id: StateId, f: F) -> Option<U>
609 where
610 F: FnOnce(&mut Component<'_>, Children<'_, '_>, AnyComponentContext<'_, '_>) -> U,
611 {
612 let mut tree = self.tree.view();
613
614 tree.with_value_mut(widget_id, |_path, container, children| {
615 let WidgetKind::Component(component) = &mut container.kind else {
616 panic!("this is always a component")
617 };
618
619 let Some(state) = self.layout_ctx.states.get_mut(state_id) else {
620 panic!("a component always has a state")
621 };
622
623 self.layout_ctx
624 .attribute_storage
625 .with_mut(widget_id, |attributes, storage| {
626 let elements = Children::new(children, storage, &mut self.needs_layout);
627
628 let ctx = AnyComponentContext::new(
629 component.parent.map(Into::into),
630 component.name_id,
631 widget_id,
632 state_id,
633 component.assoc_functions,
634 self.assoc_events,
635 self.deferred_components,
636 attributes,
637 Some(state),
638 self.emitter,
639 self.layout_ctx.viewport,
640 &mut self.layout_ctx.stop_runtime,
641 &self.document.strings,
642 );
643
644 f(component, elements, ctx)
645 })
646 })?
647 }
648
649 fn tick_components(&mut self, dt: Duration) {
650 #[cfg(feature = "profile")]
651 puffin::profile_function!();
652
653 for i in 0..self.layout_ctx.components.len() {
654 let Some((widget_id, state_id)) = self.layout_ctx.components.get_ticking(i) else { continue };
655 let event = Event::Tick(dt);
656 self.send_event_to_component(event, widget_id, state_id);
657 }
658 }
659
660 fn init_new_components(&mut self) {
661 while let Some((widget_id, state_id)) = self.layout_ctx.new_components.pop() {
662 self.with_component(widget_id, state_id, |comp, elements, ctx| {
663 comp.dyn_component.any_event(elements, ctx, Event::Mount);
664 });
665 }
666 }
667
668 fn return_state_and_component(self) {
670 let mut tree = WidgetTree::empty();
672 std::mem::swap(&mut tree, self.tree);
673
674 for (_, widget) in tree.values().into_iter() {
675 let WidgetKind::Component(comp) = widget.kind else { continue };
676 let ComponentKind::Instance = comp.kind else { continue };
677 let state = self.layout_ctx.states.remove(comp.state_id).take();
678 self.layout_ctx
679 .component_registry
680 .return_component(comp.component_id, comp.dyn_component, state);
681 }
682 }
683
684 }