1use std::ops::ControlFlow;
2use std::time::Duration;
3
4use anathema_geometry::{Pos, Size};
5use anathema_value_resolver::{AttributeStorage, Scope};
6use anathema_widgets::components::events::Event;
7use anathema_widgets::error::Result;
8use anathema_widgets::layout::{Constraints, LayoutCtx, LayoutFilter, PositionFilter, Viewport};
9use anathema_widgets::paint::PaintFilter;
10use anathema_widgets::{GlyphMap, LayoutForEach, PaintChildren, PositionChildren, WidgetTreeView};
11
12pub mod testing;
13pub mod tui;
14
15pub trait Backend {
16 fn size(&self) -> Size;
17
18 fn next_event(&mut self, timeout: Duration) -> Option<Event>;
19
20 fn resize(&mut self, new_size: Size, glyph_map: &mut GlyphMap);
21
22 fn paint<'bp>(
24 &mut self,
25 glyph_map: &mut GlyphMap,
26 widgets: PaintChildren<'_, 'bp>,
27 attribute_storage: &AttributeStorage<'bp>,
28 );
29
30 fn render(&mut self, glyph_map: &mut GlyphMap);
32
33 fn clear(&mut self);
35
36 fn finalize(&mut self) {}
38}
39
40pub struct WidgetCycle<'rt, 'bp, T> {
44 backend: &'rt mut T,
45 tree: WidgetTreeView<'rt, 'bp>,
46 constraints: Constraints,
47}
48
49impl<'rt, 'bp, T: Backend> WidgetCycle<'rt, 'bp, T> {
50 pub fn new(backend: &'rt mut T, tree: WidgetTreeView<'rt, 'bp>, constraints: Constraints) -> Self {
51 Self {
52 backend,
53 tree,
54 constraints,
55 }
56 }
57
58 fn fixed(&mut self, ctx: &mut LayoutCtx<'_, 'bp>, needs_layout: bool) -> Result<()> {
59 if needs_layout {
63 self.layout(ctx, LayoutFilter)?;
64 }
65
66 self.position(ctx.attribute_storage, *ctx.viewport, PositionFilter::fixed());
70
71 self.paint(ctx, PaintFilter::fixed());
75
76 Ok(())
77 }
78
79 fn floating(&mut self, ctx: &mut LayoutCtx<'_, 'bp>) -> Result<()> {
80 self.position(ctx.attribute_storage, *ctx.viewport, PositionFilter::floating());
84
85 self.paint(ctx, PaintFilter::floating());
89
90 Ok(())
91 }
92
93 pub fn run(&mut self, ctx: &mut LayoutCtx<'_, 'bp>, needs_layout: bool) -> Result<()> {
94 self.fixed(ctx, needs_layout)?;
95 self.floating(ctx)?;
96 Ok(())
97 }
98
99 fn layout(&mut self, ctx: &mut LayoutCtx<'_, 'bp>, filter: LayoutFilter) -> Result<()> {
100 #[cfg(feature = "profile")]
101 puffin::profile_function!();
102 let tree = self.tree.view();
103
104 let scope = Scope::root();
105 let mut for_each = LayoutForEach::new(tree, &scope, filter, None);
106 let constraints = self.constraints;
107 _ = for_each.each(ctx, |ctx, widget, children| {
108 _ = widget.layout(children, constraints, ctx)?;
109 Ok(ControlFlow::Break(()))
110 })?;
111 Ok(())
112 }
113
114 fn position(&mut self, attributes: &AttributeStorage<'bp>, viewport: Viewport, filter: PositionFilter) {
115 #[cfg(feature = "profile")]
116 puffin::profile_function!();
117
118 let mut for_each = PositionChildren::new(self.tree.view(), attributes, filter);
119 _ = for_each.each(|widget, children| {
120 widget.position(children, Pos::ZERO, attributes, viewport);
121 ControlFlow::Break(())
122 });
123 }
124
125 fn paint(&mut self, ctx: &mut LayoutCtx<'_, 'bp>, filter: PaintFilter) {
126 #[cfg(feature = "profile")]
127 puffin::profile_function!();
128
129 let for_each = PaintChildren::new(self.tree.view(), ctx.attribute_storage, filter);
130 self.backend.paint(ctx.glyph_map, for_each, ctx.attribute_storage);
131 }
132}