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