element.rs

  1pub use crate::ViewContext;
  2use anyhow::Result;
  3use gpui::geometry::vector::Vector2F;
  4pub use gpui::{Layout, LayoutId};
  5use smallvec::SmallVec;
  6
  7pub trait Element<V: 'static>: 'static + IntoElement<V> {
  8    type PaintState;
  9
 10    fn layout(
 11        &mut self,
 12        view: &mut V,
 13        cx: &mut ViewContext<V>,
 14    ) -> Result<(LayoutId, Self::PaintState)>
 15    where
 16        Self: Sized;
 17
 18    fn paint(
 19        &mut self,
 20        view: &mut V,
 21        parent_origin: Vector2F,
 22        layout: &Layout,
 23        state: &mut Self::PaintState,
 24        cx: &mut ViewContext<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    /// Applies a given function `then` to the current element if `condition` is true.
 39    /// This function is used to conditionally modify the element based on a given condition.
 40    /// If `condition` is false, it just returns the current element as it is.
 41    ///
 42    /// # Parameters
 43    /// - `self`: The current element
 44    /// - `condition`: The boolean condition based on which `then` is applied to the element.
 45    /// - `then`: A function that takes in the current element and returns a possibly modified element.
 46    ///
 47    /// # Return
 48    /// It returns the potentially modified element.
 49    fn when(mut self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
 50    where
 51        Self: Sized,
 52    {
 53        if condition {
 54            self = then(self);
 55        }
 56        self
 57    }
 58}
 59
 60/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
 61trait AnyStatefulElement<V> {
 62    fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId>;
 63    fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>);
 64}
 65
 66/// A wrapper around an element that stores its layout state.
 67struct StatefulElement<V: 'static, E: Element<V>> {
 68    element: E,
 69    phase: ElementPhase<V, E>,
 70}
 71
 72enum ElementPhase<V: 'static, E: Element<V>> {
 73    Init,
 74    PostLayout {
 75        layout_id: LayoutId,
 76        paint_state: E::PaintState,
 77    },
 78    #[allow(dead_code)]
 79    PostPaint {
 80        layout: Layout,
 81        paint_state: E::PaintState,
 82    },
 83    Error(String),
 84}
 85
 86impl<V: 'static, E: Element<V>> std::fmt::Debug for ElementPhase<V, E> {
 87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 88        match self {
 89            ElementPhase::Init => write!(f, "Init"),
 90            ElementPhase::PostLayout { layout_id, .. } => {
 91                write!(f, "PostLayout with layout id: {:?}", layout_id)
 92            }
 93            ElementPhase::PostPaint { layout, .. } => {
 94                write!(f, "PostPaint with layout: {:?}", layout)
 95            }
 96            ElementPhase::Error(err) => write!(f, "Error: {}", err),
 97        }
 98    }
 99}
100
101impl<V: 'static, E: Element<V>> Default for ElementPhase<V, E> {
102    fn default() -> Self {
103        Self::Init
104    }
105}
106
107/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
108impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
109    fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId> {
110        let result;
111        self.phase = match self.element.layout(view, cx) {
112            Ok((layout_id, paint_state)) => {
113                result = Ok(layout_id);
114                ElementPhase::PostLayout {
115                    layout_id,
116                    paint_state,
117                }
118            }
119            Err(error) => {
120                let message = error.to_string();
121                result = Err(error);
122                ElementPhase::Error(message)
123            }
124        };
125        result
126    }
127
128    fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>) {
129        self.phase = match std::mem::take(&mut self.phase) {
130            ElementPhase::PostLayout {
131                layout_id,
132                mut paint_state,
133            } => match cx.computed_layout(layout_id) {
134                Ok(layout) => {
135                    self.element
136                        .paint(view, parent_origin, &layout, &mut paint_state, cx);
137                    ElementPhase::PostPaint {
138                        layout,
139                        paint_state,
140                    }
141                }
142                Err(error) => ElementPhase::Error(error.to_string()),
143            },
144            ElementPhase::PostPaint {
145                layout,
146                mut paint_state,
147            } => {
148                self.element
149                    .paint(view, parent_origin, &layout, &mut paint_state, cx);
150                ElementPhase::PostPaint {
151                    layout,
152                    paint_state,
153                }
154            }
155            phase @ ElementPhase::Error(_) => phase,
156
157            phase @ _ => {
158                panic!("invalid element phase to call paint: {:?}", phase);
159            }
160        };
161    }
162}
163
164/// A dynamic element.
165pub struct AnyElement<V>(Box<dyn AnyStatefulElement<V>>);
166
167impl<V> AnyElement<V> {
168    pub fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId> {
169        self.0.layout(view, cx)
170    }
171
172    pub fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>) {
173        self.0.paint(view, parent_origin, cx)
174    }
175}
176
177pub trait ParentElement<V: 'static> {
178    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
179
180    fn child(mut self, child: impl IntoElement<V>) -> Self
181    where
182        Self: Sized,
183    {
184        self.children_mut().push(child.into_element().into_any());
185        self
186    }
187
188    fn children<I, E>(mut self, children: I) -> Self
189    where
190        I: IntoIterator<Item = E>,
191        E: IntoElement<V>,
192        Self: Sized,
193    {
194        self.children_mut().extend(
195            children
196                .into_iter()
197                .map(|child| child.into_element().into_any()),
198        );
199        self
200    }
201}
202
203pub trait IntoElement<V: 'static> {
204    type Element: Element<V>;
205
206    fn into_element(self) -> Self::Element;
207}