component.rs

  1use std::{any::Any, marker::PhantomData};
  2
  3use pathfinder_geometry::{rect::RectF, vector::Vector2F};
  4
  5use crate::{AnyElement, Element, SizeConstraint, ViewContext};
  6
  7use super::Empty;
  8
  9/// The core stateless component trait, simply rendering an element tree
 10pub trait Component {
 11    fn render<V: 'static>(self, cx: &mut ViewContext<V>) -> AnyElement<V>;
 12
 13    fn element<V: 'static>(self) -> ComponentAdapter<V, Self>
 14    where
 15        Self: Sized,
 16    {
 17        ComponentAdapter::new(self)
 18    }
 19
 20    fn stylable(self) -> StylableAdapter<Self>
 21    where
 22        Self: Sized,
 23    {
 24        StylableAdapter::new(self)
 25    }
 26
 27    fn stateful<V: 'static>(self) -> StatefulAdapter<Self, V>
 28    where
 29        Self: Sized,
 30    {
 31        StatefulAdapter::new(self)
 32    }
 33}
 34
 35/// Allows a a component's styles to be rebound in a simple way.
 36pub trait Stylable: Component {
 37    type Style: Clone;
 38
 39    fn with_style(self, style: Self::Style) -> Self;
 40}
 41
 42/// This trait models the typestate pattern for a component's style,
 43/// enforcing at compile time that a component is only usable after
 44/// it has been styled while still allowing for late binding of the
 45/// styling information
 46pub trait SafeStylable {
 47    type Style: Clone;
 48    type Output: Component;
 49
 50    fn with_style(self, style: Self::Style) -> Self::Output;
 51}
 52
 53/// All stylable components can trivially implement SafeStylable
 54impl<C: Stylable> SafeStylable for C {
 55    type Style = C::Style;
 56
 57    type Output = C;
 58
 59    fn with_style(self, style: Self::Style) -> Self::Output {
 60        self.with_style(style)
 61    }
 62}
 63
 64/// Allows converting an unstylable component into a stylable one
 65/// by using `()` as the style type
 66pub struct StylableAdapter<C: Component> {
 67    component: C,
 68}
 69
 70impl<C: Component> StylableAdapter<C> {
 71    pub fn new(component: C) -> Self {
 72        Self { component }
 73    }
 74}
 75
 76impl<C: Component> SafeStylable for StylableAdapter<C> {
 77    type Style = ();
 78
 79    type Output = C;
 80
 81    fn with_style(self, _: Self::Style) -> Self::Output {
 82        self.component
 83    }
 84}
 85
 86/// This is a secondary trait for components that can be styled
 87/// which rely on their view's state. This is useful for components that, for example,
 88/// want to take click handler callbacks Unfortunately, the generic bound on the
 89/// Component trait makes it incompatible with the stateless components above.
 90// So let's just replicate them for now
 91pub trait StatefulComponent<V: 'static> {
 92    fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
 93
 94    fn element(self) -> ComponentAdapter<V, Self>
 95    where
 96        Self: Sized,
 97    {
 98        ComponentAdapter::new(self)
 99    }
100
101    fn styleable(self) -> StatefulStylableAdapter<Self, V>
102    where
103        Self: Sized,
104    {
105        StatefulStylableAdapter::new(self)
106    }
107
108    fn stateless(self) -> StatelessElementAdapter
109    where
110        Self: Sized + 'static,
111    {
112        StatelessElementAdapter::new(self.element().into_any())
113    }
114}
115
116/// It is trivial to convert stateless components to stateful components, so lets
117/// do so en masse. Note that the reverse is impossible without a helper.
118impl<V: 'static, C: Component> StatefulComponent<V> for C {
119    fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
120        self.render(cx)
121    }
122}
123
124/// Same as stylable, but generic over a view type
125pub trait StatefulStylable<V: 'static>: StatefulComponent<V> {
126    type Style: Clone;
127
128    fn with_style(self, style: Self::Style) -> Self;
129}
130
131/// Same as SafeStylable, but generic over a view type
132pub trait StatefulSafeStylable<V: 'static> {
133    type Style: Clone;
134    type Output: StatefulComponent<V>;
135
136    fn with_style(self, style: Self::Style) -> Self::Output;
137}
138
139/// Converting from stateless to stateful
140impl<V: 'static, C: SafeStylable> StatefulSafeStylable<V> for C {
141    type Style = C::Style;
142
143    type Output = C::Output;
144
145    fn with_style(self, style: Self::Style) -> Self::Output {
146        self.with_style(style)
147    }
148}
149
150// A helper for converting stateless components into stateful ones
151pub struct StatefulAdapter<C, V> {
152    component: C,
153    phantom: std::marker::PhantomData<V>,
154}
155
156impl<C: Component, V: 'static> StatefulAdapter<C, V> {
157    pub fn new(component: C) -> Self {
158        Self {
159            component,
160            phantom: std::marker::PhantomData,
161        }
162    }
163}
164
165impl<C: Component, V: 'static> StatefulComponent<V> for StatefulAdapter<C, V> {
166    fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
167        self.component.render(cx)
168    }
169}
170
171// A helper for converting stateful but style-less components into stylable ones
172// by using `()` as the style type
173pub struct StatefulStylableAdapter<C: StatefulComponent<V>, V: 'static> {
174    component: C,
175    phantom: std::marker::PhantomData<V>,
176}
177
178impl<C: StatefulComponent<V>, V: 'static> StatefulStylableAdapter<C, V> {
179    pub fn new(component: C) -> Self {
180        Self {
181            component,
182            phantom: std::marker::PhantomData,
183        }
184    }
185}
186
187impl<C: StatefulComponent<V>, V: 'static> StatefulSafeStylable<V>
188    for StatefulStylableAdapter<C, V>
189{
190    type Style = ();
191
192    type Output = C;
193
194    fn with_style(self, _: Self::Style) -> Self::Output {
195        self.component
196    }
197}
198
199/// A way of erasing the view generic from an element, useful
200/// for wrapping up an explicit element tree into stateless
201/// components
202pub struct StatelessElementAdapter {
203    element: Box<dyn Any>,
204}
205
206impl StatelessElementAdapter {
207    pub fn new<V: 'static>(element: AnyElement<V>) -> Self {
208        StatelessElementAdapter {
209            element: Box::new(element) as Box<dyn Any>,
210        }
211    }
212}
213
214impl Component for StatelessElementAdapter {
215    fn render<V: 'static>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
216        *self
217            .element
218            .downcast::<AnyElement<V>>()
219            .expect("Don't move elements out of their view :(")
220    }
221}
222
223// For converting elements into stateful components
224pub struct StatefulElementAdapter<V: 'static> {
225    element: AnyElement<V>,
226    _phantom: std::marker::PhantomData<V>,
227}
228
229impl<V: 'static> StatefulElementAdapter<V> {
230    pub fn new(element: AnyElement<V>) -> Self {
231        Self {
232            element,
233            _phantom: std::marker::PhantomData,
234        }
235    }
236}
237
238impl<V: 'static> StatefulComponent<V> for StatefulElementAdapter<V> {
239    fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
240        self.element
241    }
242}
243
244/// A convenient shorthand for creating an empty component.
245impl Component for () {
246    fn render<V: 'static>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
247        Empty::new().into_any()
248    }
249}
250
251impl Stylable for () {
252    type Style = ();
253
254    fn with_style(self, _: Self::Style) -> Self {
255        ()
256    }
257}
258
259// For converting components back into Elements
260pub struct ComponentAdapter<V: 'static, E> {
261    component: Option<E>,
262    element: Option<AnyElement<V>>,
263    phantom: PhantomData<V>,
264}
265
266impl<E, V: 'static> ComponentAdapter<V, E> {
267    pub fn new(e: E) -> Self {
268        Self {
269            component: Some(e),
270            element: None,
271            phantom: PhantomData,
272        }
273    }
274}
275
276impl<V: 'static, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdapter<V, C> {
277    type LayoutState = ();
278
279    type PaintState = ();
280
281    fn layout(
282        &mut self,
283        constraint: SizeConstraint,
284        view: &mut V,
285        cx: &mut ViewContext<V>,
286    ) -> (Vector2F, Self::LayoutState) {
287        if self.element.is_none() {
288            let element = self
289                .component
290                .take()
291                .expect("Component can only be rendered once")
292                .render(view, cx);
293            self.element = Some(element);
294        }
295        let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
296        (constraint, ())
297    }
298
299    fn paint(
300        &mut self,
301        bounds: RectF,
302        visible_bounds: RectF,
303        _: &mut Self::LayoutState,
304        view: &mut V,
305        cx: &mut ViewContext<V>,
306    ) -> Self::PaintState {
307        self.element
308            .as_mut()
309            .expect("Layout should always be called before paint")
310            .paint(bounds.origin(), visible_bounds, view, cx)
311    }
312
313    fn rect_for_text_range(
314        &self,
315        range_utf16: std::ops::Range<usize>,
316        _: RectF,
317        _: RectF,
318        _: &Self::LayoutState,
319        _: &Self::PaintState,
320        view: &V,
321        cx: &ViewContext<V>,
322    ) -> Option<RectF> {
323        self.element
324            .as_ref()
325            .and_then(|el| el.rect_for_text_range(range_utf16, view, cx))
326    }
327
328    fn debug(
329        &self,
330        _: RectF,
331        _: &Self::LayoutState,
332        _: &Self::PaintState,
333        view: &V,
334        cx: &ViewContext<V>,
335    ) -> serde_json::Value {
336        serde_json::json!({
337            "type": "ComponentAdapter",
338            "component": std::any::type_name::<C>(),
339            "child": self.element.as_ref().map(|el| el.debug(view, cx)),
340        })
341    }
342}