termint/widgets/
widget.rs

1use std::{
2    any::{Any, TypeId},
3    fmt,
4};
5
6use crate::{
7    buffer::Buffer,
8    geometry::{Rect, Vec2},
9    widgets::cache::Cache,
10};
11
12/// Trait implemented by all the widgets.
13///
14/// A widget is a visual component that can render itself to a [`Buffer`] and
15/// report its size requirements for layout purposes.
16///
17/// Use [`Element`] to store and manipulate widgets in a uniform way.
18///
19/// Users will use [`Widget`] trait directly only when implementing custom
20/// widget, otherwise they will use built-in widgets like [`Span`], [`List`]
21/// and so on.
22pub trait Widget: Any {
23    /// Renders the widget into the given [`Buffer`] within the provided
24    /// [`Rect`] bounds.
25    fn render(&self, buffer: &mut Buffer, rect: Rect, cache: &mut Cache);
26
27    /// Returns the height of the [`Widget`] based on the width of the given
28    /// size.
29    fn height(&self, size: &Vec2) -> usize;
30
31    /// Returns the width of the [`Widget`] based on the height of the given
32    /// size.
33    fn width(&self, size: &Vec2) -> usize;
34
35    /// Gets widget's children
36    fn children(&self) -> Vec<&Element> {
37        vec![]
38    }
39}
40
41impl dyn Widget {
42    pub fn as_any(&self) -> &dyn Any {
43        self
44    }
45
46    pub fn as_any_mut(&mut self) -> &mut dyn Any {
47        self
48    }
49}
50
51impl fmt::Debug for dyn Widget {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        write!(f, "Converted widget")
54    }
55}
56
57/// A container for any widget implementing the [`Widget`] trait.
58///
59/// This is the primary type used to store and manipulate widgets in layout
60/// trees. `Element` wraps a widget in a dynamic trait object.
61///
62/// Use [`Element::new`] to convert a widget into an `Element`.
63#[derive(Debug)]
64pub struct Element {
65    pub type_id: TypeId,
66    pub widget: Box<dyn Widget>,
67}
68
69impl Element {
70    /// Creates a new [`Element`] from a given widget.
71    ///
72    /// This is commonly used to wrap widgets when composing layouts.
73    ///
74    /// # Example
75    /// ```
76    /// # use termint::widgets::{Span, Element};
77    /// let span = Span::new("Hello");
78    /// let element = Element::new(span);
79    /// ```
80    pub fn new<W>(widget: W) -> Self
81    where
82        W: Widget + 'static,
83    {
84        Self {
85            type_id: TypeId::of::<W>(),
86            widget: Box::new(widget),
87        }
88    }
89
90    pub fn map<W: Widget + 'static, F: FnOnce(W) -> W>(self, f: F) -> Self {
91        let Element { type_id: _, widget } = self;
92        let boxed_any: Box<dyn Any> = widget;
93
94        match boxed_any.downcast::<W>() {
95            Ok(bx) => {
96                let new = f(*bx);
97                Self {
98                    type_id: TypeId::of::<W>(),
99                    widget: Box::new(new),
100                }
101            }
102            Err(orig) => {
103                let widget: Box<dyn Widget> = *orig
104                    .downcast::<Box<dyn Widget>>()
105                    .expect("Original type must be Box<dyn Widget>");
106                let type_id = widget.as_ref().type_id();
107
108                Self { type_id, widget }
109            }
110        }
111    }
112
113    /// Downcasts widget stored in the [`Element`] to given type, returns
114    /// optional reference to the downcast widget on success.
115    pub fn downcast_ref<W: Widget>(&self) -> Option<&W> {
116        self.widget.as_ref().as_any().downcast_ref::<W>()
117    }
118
119    /// Downcasts widget stored in the [`Element`] to given type, returns
120    /// optional mutable reference to the downcast widget on success.
121    pub fn downcast_mut<W: Widget>(&mut self) -> Option<&mut W> {
122        self.widget.as_mut().as_any_mut().downcast_mut::<W>()
123    }
124}
125
126impl Widget for Element {
127    fn render(&self, buffer: &mut Buffer, rect: Rect, cache: &mut Cache) {
128        self.widget.render(buffer, rect, cache)
129    }
130
131    fn height(&self, size: &Vec2) -> usize {
132        self.widget.height(size)
133    }
134
135    fn width(&self, size: &Vec2) -> usize {
136        self.widget.width(size)
137    }
138
139    fn children(&self) -> Vec<&Element> {
140        self.widget.children()
141    }
142}