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