1use anyhow::Result;
2use gpui::{geometry::rect::RectF, EngineLayout};
3use smallvec::SmallVec;
4use std::marker::PhantomData;
5use util::ResultExt;
6
7pub use crate::layout_context::LayoutContext;
8pub use crate::paint_context::PaintContext;
9
10type LayoutId = gpui::LayoutId;
11
12pub trait Element<V: 'static>: 'static {
13 type Layout;
14
15 fn layout(
16 &mut self,
17 view: &mut V,
18 cx: &mut LayoutContext<V>,
19 ) -> Result<Layout<V, Self::Layout>>
20 where
21 Self: Sized;
22
23 fn paint(
24 &mut self,
25 view: &mut V,
26 layout: &mut Layout<V, Self::Layout>,
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 layout: None,
38 }))
39 }
40}
41
42/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
43trait AnyStatefulElement<V> {
44 fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
45 fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>);
46}
47
48/// A wrapper around an element that stores its layout state.
49struct StatefulElement<V: 'static, E: Element<V>> {
50 element: E,
51 layout: Option<Layout<V, E::Layout>>,
52}
53
54/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
55impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
56 fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
57 let layout = self.element.layout(view, cx)?;
58 let layout_id = layout.id;
59 self.layout = Some(layout);
60 Ok(layout_id)
61 }
62
63 fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
64 let layout = self.layout.as_mut().expect("paint called before layout");
65 if layout.engine_layout.is_none() {
66 layout.engine_layout = dbg!(cx.computed_layout(dbg!(layout.id)).log_err())
67 }
68 self.element.paint(view, layout, cx)
69 }
70}
71
72/// A dynamic element.
73pub struct AnyElement<V>(Box<dyn AnyStatefulElement<V>>);
74
75impl<V> AnyElement<V> {
76 pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
77 self.0.layout(view, cx)
78 }
79
80 pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
81 self.0.paint(view, cx)
82 }
83}
84
85pub struct Layout<V, D> {
86 id: LayoutId,
87 engine_layout: Option<EngineLayout>,
88 element_data: Option<D>,
89 view_type: PhantomData<V>,
90}
91
92impl<V: 'static, D> Layout<V, D> {
93 pub fn new(id: LayoutId, element_data: D) -> Self {
94 Self {
95 id,
96 engine_layout: None,
97 element_data: Some(element_data),
98 view_type: PhantomData,
99 }
100 }
101
102 pub fn id(&self) -> LayoutId {
103 self.id
104 }
105
106 pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
107 self.engine_layout(cx).bounds
108 }
109
110 pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
111 self.engine_layout(cx).order
112 }
113
114 pub fn update<F, T>(&mut self, update: F) -> T
115 where
116 F: FnOnce(&mut Self, &mut D) -> T,
117 {
118 self.element_data
119 .take()
120 .map(|mut element_data| {
121 let result = update(self, &mut element_data);
122 self.element_data = Some(element_data);
123 result
124 })
125 .expect("reentrant calls to Layout::update are not supported")
126 }
127
128 fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
129 self.engine_layout
130 .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
131 }
132}
133
134pub trait ParentElement<V: 'static> {
135 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
136
137 fn child(mut self, child: impl IntoElement<V>) -> Self
138 where
139 Self: Sized,
140 {
141 self.children_mut().push(child.into_element().into_any());
142 self
143 }
144
145 fn children<I, E>(mut self, children: I) -> Self
146 where
147 I: IntoIterator<Item = E>,
148 E: IntoElement<V>,
149 Self: Sized,
150 {
151 self.children_mut().extend(
152 children
153 .into_iter()
154 .map(|child| child.into_element().into_any()),
155 );
156 self
157 }
158}
159
160pub trait IntoElement<V: 'static> {
161 type Element: Element<V>;
162
163 fn into_element(self) -> Self::Element;
164}