1use 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
19pub trait WidgetExt<'a>: Widget + Sized + 'a {
21 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 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 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 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 fn fixed_width(
57 self,
58 width: usize,
59 ) -> SizeConstraint<RangeInclusive<usize>, RangeFull, Self> {
60 SizeConstraint::new(self, width..=width, ..)
61 }
62
63 fn fixed_height(
65 self,
66 height: usize,
67 ) -> SizeConstraint<RangeFull, RangeInclusive<usize>, Self> {
68 SizeConstraint::new(self, .., height..=height)
69 }
70
71 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 fn to_flex_child(self) -> flexbox::Child<'a> {
86 self.into()
87 }
88
89 fn to_grid_child(self) -> grid::Child<'a> {
91 self.into()
92 }
93
94 fn to_box_sizing(self) -> BoxSizing<Self> {
96 self.into()
97 }
98
99 fn to_scroll(self) -> Scroll<Self> {
101 Scroll::new(self)
102 }
103
104 fn to_ratio(self, ratio: f64) -> Ratio<Self> {
108 Ratio::new(self, ratio)
109 }
110
111 fn to_layout_record(self, record: Rc<Cell<Rect>>) -> LayoutRecord<Self> {
113 LayoutRecord::new(self, record)
114 }
115
116 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 fn cached(self, buffers: &CachedBuffers) -> Cache<Self> {
127 Cache::new(self, buffers)
128 }
129
130 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
141pub trait WidgetStaticExt: Widget + Sized {
143 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
200pub trait StylableExt<'a> {
202 fn styled(self) -> StyledText<'a>;
204 fn with_style(self, style: Style) -> StyledText<'a>;
206 fn fg(self, fg: impl Into<Option<Color>>) -> StyledText<'a>;
208 fn bg(self, bg: impl Into<Option<Color>>) -> StyledText<'a>;
210 fn with_modifier(self, modifier: Modifier) -> StyledText<'a>;
212 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}