ori_core/
view.rs

1use std::any::{self, Any};
2
3use glam::Vec2;
4
5use crate::{
6    BoxConstraints, Callback, DrawContext, Event, EventContext, LayoutContext, RequestRedrawEvent,
7    SendSync, SharedSignal, Style,
8};
9
10/// A [`View`] is a component that can be rendered to the screen.
11#[allow(unused_variables)]
12pub trait View: SendSync + 'static {
13    /// The state of the view.
14    type State: SendSync + 'static;
15
16    /// Builds the state of the view.
17    fn build(&self) -> Self::State;
18
19    /// Returns the style of the view.
20    fn style(&self) -> Style {
21        Style::default()
22    }
23
24    /// Handles an event.
25    fn event(&self, state: &mut Self::State, cx: &mut EventContext, event: &Event) {}
26
27    /// Handle layout and returns the size of the view.
28    ///
29    /// This method should return a size that fits the [`BoxConstraints`].
30    ///
31    /// The default implementation returns the minimum size.
32    fn layout(&self, state: &mut Self::State, cx: &mut LayoutContext, bc: BoxConstraints) -> Vec2 {
33        bc.min
34    }
35
36    /// Draws the view.
37    fn draw(&self, state: &mut Self::State, cx: &mut DrawContext) {}
38}
39
40/// A [`View`] that with an unknown state.
41///
42/// This is used to store a [`View`] in a [`Node`](crate::Node).
43pub trait AnyView: SendSync {
44    fn style(&self) -> Style;
45
46    fn event(&self, state: &mut dyn Any, cx: &mut EventContext, event: &Event);
47
48    fn layout(&self, state: &mut dyn Any, cx: &mut LayoutContext, bc: BoxConstraints) -> Vec2;
49
50    fn draw(&self, state: &mut dyn Any, cx: &mut DrawContext);
51}
52
53impl<T: View> AnyView for T {
54    fn style(&self) -> Style {
55        self.style()
56    }
57
58    fn event(&self, state: &mut dyn Any, cx: &mut EventContext, event: &Event) {
59        if let Some(state) = state.downcast_mut::<T::State>() {
60            self.event(state, cx, event);
61        } else {
62            tracing::warn!("invalid state type on {}", any::type_name::<T>());
63        }
64    }
65
66    fn layout(&self, state: &mut dyn Any, cx: &mut LayoutContext, bc: BoxConstraints) -> Vec2 {
67        if let Some(state) = state.downcast_mut::<T::State>() {
68            self.layout(state, cx, bc)
69        } else {
70            tracing::warn!("invalid state type on {}", any::type_name::<T>());
71            bc.min
72        }
73    }
74
75    fn draw(&self, state: &mut dyn Any, cx: &mut DrawContext) {
76        if let Some(state) = state.downcast_mut::<T::State>() {
77            self.draw(state, cx);
78        } else {
79            tracing::warn!("invalid state type on {}", any::type_name::<T>());
80        }
81    }
82}
83
84/// When a view is wrapped in a signal, the view will be redrawn when the signal
85/// changes.
86impl<V: View + SendSync> View for SharedSignal<V> {
87    type State = (Callback<'static, ()>, V::State);
88
89    fn build(&self) -> Self::State {
90        (Callback::default(), self.get_untracked().build())
91    }
92
93    fn style(&self) -> Style {
94        self.get_untracked().style()
95    }
96
97    fn event(&self, (_, state): &mut Self::State, cx: &mut EventContext, event: &Event) {
98        self.get().event(state, cx, event);
99    }
100
101    fn layout(
102        &self,
103        (_, state): &mut Self::State,
104        cx: &mut LayoutContext,
105        bc: BoxConstraints,
106    ) -> Vec2 {
107        self.get().layout(state, cx, bc)
108    }
109
110    fn draw(&self, (callback, state): &mut Self::State, cx: &mut DrawContext) {
111        // redraw when the signal changes
112        let event_sink = cx.event_sink.clone();
113        *callback = Callback::new(move |&()| event_sink.send(RequestRedrawEvent));
114
115        self.emitter().subscribe_weak(callback.downgrade());
116        self.get().draw(state, cx);
117    }
118}
119
120impl View for () {
121    type State = ();
122
123    fn build(&self) -> Self::State {}
124
125    fn event(&self, _state: &mut Self::State, _cx: &mut EventContext, _event: &Event) {}
126
127    fn layout(
128        &self,
129        _state: &mut Self::State,
130        _cx: &mut LayoutContext,
131        bc: BoxConstraints,
132    ) -> Vec2 {
133        bc.min
134    }
135
136    fn draw(&self, _state: &mut Self::State, _cx: &mut DrawContext) {}
137}