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