component.rs

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