1pub use crate::layout_context::LayoutContext;
2pub use crate::paint_context::PaintContext;
3use anyhow::Result;
4use gpui::geometry::vector::Vector2F;
5pub use gpui::{Layout, LayoutId};
6use smallvec::SmallVec;
7
8pub trait Element<V: 'static>: 'static {
9 type PaintState;
10
11 fn layout(
12 &mut self,
13 view: &mut V,
14 cx: &mut LayoutContext<V>,
15 ) -> Result<(LayoutId, Self::PaintState)>
16 where
17 Self: Sized;
18
19 fn paint(
20 &mut self,
21 view: &mut V,
22 layout: &Layout,
23 state: &mut Self::PaintState,
24 cx: &mut PaintContext<V>,
25 ) where
26 Self: Sized;
27
28 fn into_any(self) -> AnyElement<V>
29 where
30 Self: 'static + Sized,
31 {
32 AnyElement(Box::new(StatefulElement {
33 element: self,
34 phase: ElementPhase::Init,
35 }))
36 }
37}
38
39/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
40trait AnyStatefulElement<V> {
41 fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
42 fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>);
43}
44
45/// A wrapper around an element that stores its layout state.
46struct StatefulElement<V: 'static, E: Element<V>> {
47 element: E,
48 phase: ElementPhase<V, E>,
49}
50
51enum ElementPhase<V: 'static, E: Element<V>> {
52 Init,
53 PostLayout {
54 layout_id: LayoutId,
55 paint_state: E::PaintState,
56 },
57 #[allow(dead_code)]
58 PostPaint {
59 layout: Layout,
60 paint_state: E::PaintState,
61 },
62 Error(String),
63}
64
65impl<V: 'static, E: Element<V>> Default for ElementPhase<V, E> {
66 fn default() -> Self {
67 Self::Init
68 }
69}
70
71/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
72impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
73 fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
74 let result;
75 self.phase = match self.element.layout(view, cx) {
76 Ok((layout_id, paint_state)) => {
77 result = Ok(layout_id);
78 ElementPhase::PostLayout {
79 layout_id,
80 paint_state,
81 }
82 }
83 Err(error) => {
84 let message = error.to_string();
85 result = Err(error);
86 ElementPhase::Error(message)
87 }
88 };
89 result
90 }
91
92 fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>) {
93 self.phase = match std::mem::take(&mut self.phase) {
94 ElementPhase::PostLayout {
95 layout_id,
96 mut paint_state,
97 } => match cx.computed_layout(layout_id) {
98 Ok(mut layout) => {
99 layout.bounds = layout.bounds + parent_origin;
100 self.element.paint(view, &layout, &mut paint_state, cx);
101 ElementPhase::PostPaint {
102 layout,
103 paint_state,
104 }
105 }
106 Err(error) => ElementPhase::Error(error.to_string()),
107 },
108 phase @ ElementPhase::Error(_) => phase,
109 _ => panic!("invalid element phase to call paint"),
110 };
111 }
112}
113
114/// A dynamic element.
115pub struct AnyElement<V>(Box<dyn AnyStatefulElement<V>>);
116
117impl<V> AnyElement<V> {
118 pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
119 self.0.layout(view, cx)
120 }
121
122 pub fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>) {
123 self.0.paint(view, parent_origin, cx)
124 }
125}
126
127pub trait ParentElement<V: 'static> {
128 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
129
130 fn child(mut self, child: impl IntoElement<V>) -> Self
131 where
132 Self: Sized,
133 {
134 self.children_mut().push(child.into_element().into_any());
135 self
136 }
137
138 fn children<I, E>(mut self, children: I) -> Self
139 where
140 I: IntoIterator<Item = E>,
141 E: IntoElement<V>,
142 Self: Sized,
143 {
144 self.children_mut().extend(
145 children
146 .into_iter()
147 .map(|child| child.into_element().into_any()),
148 );
149 self
150 }
151}
152
153pub trait IntoElement<V: 'static> {
154 type Element: Element<V>;
155
156 fn into_element(self) -> Self::Element;
157}