tuviv/
prelude.rs

1//! Traits which you probably want to include.
2
3use std::{
4    borrow::Cow,
5    cell::Cell,
6    ops::{RangeBounds, RangeFull, RangeInclusive},
7    rc::Rc,
8};
9
10use le::{align::Alignment, layout::Rect};
11
12use crate::widgets::{
13    cache::CachedBuffers, flexbox, grid, Align, BoxSizing, Cache, Filler,
14    LayoutRecord, Ratio, RefWidget, Scroll, SizeConstraint, Stack,
15};
16pub use crate::Backend;
17use crate::{Color, Modifier, Style, StyledText, Widget};
18
19/// Convinience functions based on a builder-pattern
20pub trait WidgetExt<'a>: Widget + Sized + 'a {
21    /// Aligns the widget on the X axis
22    fn align_x(self, alignment: Alignment) -> Align<Self> {
23        let mut align = Align::new(self);
24        align.align_y = Alignment::Expand;
25        align.align_x = alignment;
26        align
27    }
28
29    /// Aligns the widget on the Y axis
30    fn align_y(self, alignment: Alignment) -> Align<Self> {
31        let mut align = Align::new(self);
32        align.align_x = Alignment::Expand;
33        align.align_y = alignment;
34        align
35    }
36
37    /// Centers the widget
38    fn centered(self) -> Align<Self> {
39        let mut align = Align::new(self);
40        align.align_x = Alignment::Center;
41        align.align_y = Alignment::Center;
42        align
43    }
44
45    /// Makes the widget a fixed size
46    fn fixed_size(
47        self,
48        width: usize,
49        height: usize,
50    ) -> SizeConstraint<RangeInclusive<usize>, RangeInclusive<usize>, Self>
51    {
52        SizeConstraint::new(self, width..=width, height..=height)
53    }
54
55    /// Makes the widget a fixed width
56    fn fixed_width(
57        self,
58        width: usize,
59    ) -> SizeConstraint<RangeInclusive<usize>, RangeFull, Self> {
60        SizeConstraint::new(self, width..=width, ..)
61    }
62
63    /// Makes the widget a fixed height
64    fn fixed_height(
65        self,
66        height: usize,
67    ) -> SizeConstraint<RangeFull, RangeInclusive<usize>, Self> {
68        SizeConstraint::new(self, .., height..=height)
69    }
70
71    /// Gives with widget size constraints
72    fn constrained_size<H, W>(
73        self,
74        width: W,
75        height: H,
76    ) -> SizeConstraint<W, H, Self>
77    where
78        H: RangeBounds<usize>,
79        W: RangeBounds<usize>,
80    {
81        SizeConstraint::new(self, width, height)
82    }
83
84    /// Make the widget a [`Flexbox`](crate::widgets::Flexbox) child
85    fn to_flex_child(self) -> flexbox::Child<'a> {
86        self.into()
87    }
88
89    /// Make the widget a [`Grid`](crate::widgets::Grid) child
90    fn to_grid_child(self) -> grid::Child<'a> {
91        self.into()
92    }
93
94    /// Wraps the widget in a [`BoxSizing`]
95    fn to_box_sizing(self) -> BoxSizing<Self> {
96        self.into()
97    }
98
99    /// Wraps the widget in a [`Scroll`]
100    fn to_scroll(self) -> Scroll<Self> {
101        Scroll::new(self)
102    }
103
104    /// Wraps the widget in a [`Ratio`]
105    ///
106    /// The ratio is x รท y
107    fn to_ratio(self, ratio: f64) -> Ratio<Self> {
108        Ratio::new(self, ratio)
109    }
110
111    /// Wraps the widget in a [`LayoutRecord`]
112    fn to_layout_record(self, record: Rc<Cell<Rect>>) -> LayoutRecord<Self> {
113        LayoutRecord::new(self, record)
114    }
115
116    /// Wraps the widget in a [`LayoutRecord`] with the `mapping` field
117    fn to_mapped_layout_record<F: Fn(Rect) -> Rect>(
118        self,
119        record: Rc<Cell<Rect>>,
120        mapping: F,
121    ) -> LayoutRecord<Self, F> {
122        LayoutRecord::with_mapping(self, record, mapping)
123    }
124
125    /// Wraps the widget in a [`Cache`]
126    fn cached(self, buffers: &CachedBuffers) -> Cache<Self> {
127        Cache::new(self, buffers)
128    }
129
130    /// Backs the widget with a background [`Stack`]
131    fn stacked_background<I: Into<Option<Color>>>(self, color: I) -> Stack<'a> {
132        let color = color.into();
133        if let Some(color) = color {
134            Stack::new().child(Filler::new(" ".bg(color))).child(self)
135        } else {
136            Stack::new().child(Filler::new(" ".styled())).child(self)
137        }
138    }
139}
140
141/// A trait for static widgets
142pub trait WidgetStaticExt: Widget + Sized {
143    /// Wraps this in a [`RefWidget`]
144    fn to_ref(self) -> RefWidget<Self> {
145        self.into()
146    }
147}
148
149impl<'a, T> WidgetExt<'a> for T where T: Widget + 'a + Sized {}
150
151impl<T> WidgetStaticExt for T where T: Widget + 'static {}
152
153impl<'a, T> From<T> for flexbox::Child<'a>
154where
155    T: Widget + 'a,
156{
157    fn from(them: T) -> Self {
158        flexbox::Child::new(Box::new(them))
159    }
160}
161
162impl<T: Widget> From<T> for BoxSizing<T> {
163    fn from(them: T) -> Self {
164        Self::new(them, crate::border::Border::BLANK)
165    }
166}
167
168impl<T: Widget> From<T> for Scroll<T> {
169    fn from(them: T) -> Self {
170        Self::new(them)
171    }
172}
173
174impl<T> From<T> for RefWidget<T>
175where
176    T: Widget + 'static,
177{
178    fn from(them: T) -> Self {
179        Self::new(them)
180    }
181}
182
183impl<'a, T> From<T> for grid::Child<'a>
184where
185    T: Widget + 'a,
186{
187    fn from(them: T) -> grid::Child<'a> {
188        grid::Child::new(
189            Box::new(them),
190            grid::Props {
191                column: 0,
192                row: 0,
193                column_span: 1,
194                row_span: 1,
195            },
196        )
197    }
198}
199
200/// A helper for anything that can be styled
201pub trait StylableExt<'a> {
202    /// Creates a [`StyledText`] out of this
203    fn styled(self) -> StyledText<'a>;
204    /// Gives this a specific style
205    fn with_style(self, style: Style) -> StyledText<'a>;
206    /// Gives this a specific foreground colour
207    fn fg(self, fg: impl Into<Option<Color>>) -> StyledText<'a>;
208    /// Gives this a specific background colour
209    fn bg(self, bg: impl Into<Option<Color>>) -> StyledText<'a>;
210    /// Gives this a modifier
211    fn with_modifier(self, modifier: Modifier) -> StyledText<'a>;
212    /// Removes a modifier from this
213    fn without_modifier(self, modifier: Modifier) -> StyledText<'a>;
214}
215
216impl<'a, T> StylableExt<'a> for T
217where
218    Cow<'a, str>: From<T>,
219{
220    fn styled(self) -> StyledText<'a> {
221        StyledText::from(self)
222    }
223    fn fg(self, fg: impl Into<Option<Color>>) -> StyledText<'a> {
224        self.styled().fg(fg)
225    }
226    fn bg(self, bg: impl Into<Option<Color>>) -> StyledText<'a> {
227        self.styled().bg(bg)
228    }
229    fn with_modifier(self, modifier: Modifier) -> StyledText<'a> {
230        self.styled().with_modifier(modifier)
231    }
232    fn without_modifier(self, modifier: Modifier) -> StyledText<'a> {
233        self.styled().without_modifier(modifier)
234    }
235    fn with_style(self, style: Style) -> StyledText<'a> {
236        StyledText {
237            style,
238            text: self.into(),
239        }
240    }
241}