vtui_core/
component.rs

1use std::cell::{RefCell, RefMut};
2
3use crate::{
4    canvas::{Canvas, LogicalRect},
5    context::EventContext,
6    events::{Event, Message},
7    layout::{Flow, Layer, Measure, compute_split},
8    listeners::{DrawListener, ErasedListenerBucket, ListenerStore},
9    state::{State, StateOwner},
10};
11
12pub type FactoryFn<P> = fn(Component, P) -> Node;
13
14pub trait Props: Clone + 'static {}
15
16impl Props for () {}
17
18#[derive(Default)]
19pub struct Component {
20    inner: RefCell<Spec>,
21}
22
23impl Component {
24    fn get_inner_mut(&self) -> RefMut<'_, Spec> {
25        self.inner.borrow_mut()
26    }
27}
28
29impl Component {
30    pub fn draw(&self, listener: impl Fn(&mut Canvas) + 'static) {
31        self.get_inner_mut().renderer = Some(Box::new(listener));
32    }
33
34    pub fn listen<E: Event>(&self, listener: impl FnMut(&mut EventContext<E>) + 'static) {
35        self.get_inner_mut().listeners.push(Box::new(listener));
36    }
37
38    pub fn state<T: 'static>(&self, value: T) -> State<T> {
39        self.get_inner_mut().state.insert(value)
40    }
41}
42
43pub struct Node {
44    spec: Spec,
45    composition: Composition,
46}
47
48impl From<Component> for Node {
49    fn from(value: Component) -> Self {
50        let Component { inner } = value;
51
52        Self {
53            spec: inner.into_inner(),
54            composition: Composition::default(),
55        }
56    }
57}
58
59impl Node {
60    pub fn new(component: Component) -> Self {
61        Self::from(component)
62    }
63
64    pub fn from_factory<P: Props>(factory: FactoryFn<P>, props: P) -> Self {
65        factory(Component::default(), props)
66    }
67
68    pub(crate) fn get_renderer(&self) -> Option<&DrawListener> {
69        self.spec.renderer.as_ref()
70    }
71
72    pub(crate) fn get_listeners(
73        &mut self,
74        msg: &Message,
75    ) -> Option<&mut Box<dyn ErasedListenerBucket>> {
76        self.spec.listeners.get_mut(msg)
77    }
78
79    pub(crate) fn composition(&self) -> &Composition {
80        &self.composition
81    }
82}
83
84impl Node {
85    pub fn child<P: Props>(mut self, measure: Measure, factory: FactoryFn<P>, props: P) -> Self {
86        let factory = Box::new(move || Node::from_factory(factory, props.clone()));
87        self.composition.push(Child::Static(factory), measure);
88        self
89    }
90
91    pub fn set_flow(mut self, flow: Flow) -> Self {
92        self.composition.flow = flow;
93        self
94    }
95
96    pub(crate) fn get_layer(&self) -> Layer {
97        self.composition.layer
98    }
99}
100
101#[derive(Default)]
102pub(crate) struct Spec {
103    pub renderer: Option<DrawListener>,
104    pub listeners: ListenerStore,
105    pub state: StateOwner,
106}
107
108pub(crate) enum Child {
109    Static(Box<dyn Fn() -> Node>),
110}
111
112#[derive(Default)]
113pub(crate) struct Composition {
114    flow: Flow,
115    layer: Layer,
116    children: Vec<(Child, Measure)>,
117}
118
119impl Composition {
120    pub fn push(&mut self, child: Child, measure: Measure) {
121        self.children.push((child, measure));
122    }
123
124    pub fn children(&self) -> impl Iterator<Item = &(Child, Measure)> {
125        self.children.iter()
126    }
127
128    pub fn split(&self, area: LogicalRect, measures: &[Measure]) -> Vec<LogicalRect> {
129        compute_split(self.flow, area, measures)
130    }
131}