tuviv/widgets/
size_constraint.rs1use std::ops::{Bound, RangeBounds};
4
5use le::{Layout, MinimumNatural};
6
7use crate::{
8 le::layout::{Rect, Vec2},
9 terminal::Buffer,
10 Widget,
11};
12
13pub struct SizeConstraint<
15 W: RangeBounds<usize>,
16 H: RangeBounds<usize>,
17 T: Widget,
18> {
19 pub widget: T,
21 pub width: W,
23 pub height: H,
25}
26
27impl<W, H, T> SizeConstraint<W, H, T>
28where
29 W: RangeBounds<usize>,
30 H: RangeBounds<usize>,
31 T: Widget,
32{
33 pub fn new(widget: T, width: W, height: H) -> Self {
37 Self {
38 widget,
39 width,
40 height,
41 }
42 }
43
44 fn clamp_width(&self, width: usize) -> usize {
46 let start = start_bound_n(&self.width);
47 let end = end_bound_n(&self.width);
48 width.clamp(start, end.max(start))
49 }
50
51 fn clamp_height(&self, height: usize) -> usize {
53 let start = start_bound_n(&self.height);
54 let end = end_bound_n(&self.height);
55
56 height.clamp(start, end.max(start))
57 }
58}
59
60fn start_bound_n<R: RangeBounds<usize>>(range: &R) -> usize {
62 bound_n(&range.start_bound()).unwrap_or(0)
63}
64
65fn end_bound_n<R: RangeBounds<usize>>(range: &R) -> usize {
67 bound_n(&range.end_bound()).unwrap_or(usize::MAX)
68}
69
70fn bound_n(bound: &Bound<&usize>) -> Option<usize> {
72 match bound {
73 Bound::Excluded(x) => Some(x.saturating_sub(1)),
74 Bound::Included(x) => Some(**x),
75 Bound::Unbounded => None,
76 }
77}
78
79impl<W, H, T> Layout for SizeConstraint<W, H, T>
80where
81 W: RangeBounds<usize>,
82 H: RangeBounds<usize>,
83 T: Widget,
84{
85 fn width_for_height(&self, height: usize) -> MinimumNatural<usize> {
86 let height = self.clamp_height(height);
87
88 MinimumNatural {
89 minimum: self
90 .clamp_width(self.widget.width_for_height(height).minimum),
91 natural: self
92 .clamp_width(self.widget.width_for_height(height).natural),
93 }
94 }
95
96 fn height_for_width(&self, width: usize) -> MinimumNatural<usize> {
97 let width = self.clamp_width(width);
98 MinimumNatural {
99 minimum: self
100 .clamp_height(self.widget.height_for_width(width).minimum),
101 natural: self
102 .clamp_height(self.widget.height_for_width(width).natural),
103 }
104 }
105
106 fn prefered_size(&self) -> MinimumNatural<Vec2> {
107 let size_bounds = (
109 bound_n(&self.width.end_bound()),
110 bound_n(&self.height.end_bound()),
111 );
112 let widget_size_og = if let (Some(size_x), Some(size_y)) = size_bounds {
113 self.widget
114 .prefered_size_of_container(Vec2::new(size_x, size_y))
115 } else {
116 self.widget.prefered_size()
117 };
118 let mut widget_size = widget_size_og;
119
120 widget_size.minimum.x = self.clamp_width(widget_size_og.minimum.x);
122 widget_size.minimum.y = self.clamp_height(widget_size_og.minimum.y);
123
124 widget_size.natural.x = self.clamp_width(widget_size_og.natural.x);
125 widget_size.natural.y = self.clamp_height(widget_size_og.natural.y);
126
127 if widget_size.minimum.x != widget_size_og.minimum.x {
130 widget_size.minimum.y = self.clamp_height(
131 self.height_for_width(widget_size.minimum.x).minimum,
132 );
133 }
134 if widget_size.minimum.y != widget_size_og.minimum.y {
135 widget_size.minimum.x = self.clamp_width(
136 self.width_for_height(widget_size.minimum.y).minimum,
137 );
138 }
139
140 if widget_size.natural.x != widget_size_og.natural.x {
141 widget_size.natural.y = self.clamp_height(
142 self.height_for_width(widget_size.natural.x).natural,
143 );
144 }
145 if widget_size.natural.y != widget_size_og.natural.y {
146 widget_size.natural.x = self.clamp_width(
147 self.width_for_height(widget_size.natural.y).natural,
148 );
149 }
150
151 widget_size
153 }
154
155 fn prefered_size_of_container(
156 &self,
157 container: Vec2,
158 ) -> MinimumNatural<Vec2> {
159 let container = Vec2::new(
160 self.clamp_width(container.x),
161 self.clamp_height(container.y),
162 );
163 let widget_size_og = self.widget.prefered_size_of_container(container);
164 let mut widget_size = self.widget.prefered_size_of_container(container);
165
166 widget_size.minimum.x = self.clamp_width(widget_size_og.minimum.x);
168 widget_size.minimum.y = self.clamp_height(widget_size_og.minimum.y);
169
170 widget_size.natural.x = self.clamp_width(widget_size_og.natural.x);
171 widget_size.natural.y = self.clamp_height(widget_size_og.natural.y);
172
173 if widget_size.minimum.x != widget_size_og.minimum.x {
176 widget_size.minimum.y = self.clamp_height(
177 self.height_for_width(widget_size.minimum.x).minimum,
178 );
179 }
180 if widget_size.minimum.y != widget_size_og.minimum.y {
181 widget_size.minimum.x = self.clamp_width(
182 self.width_for_height(widget_size.minimum.y).minimum,
183 );
184 }
185
186 if widget_size.natural.x != widget_size_og.natural.x {
187 widget_size.natural.y = self.clamp_height(
188 self.height_for_width(widget_size.natural.x).natural,
189 );
190 }
191 if widget_size.natural.y != widget_size_og.natural.y {
192 widget_size.natural.x = self.clamp_width(
193 self.width_for_height(widget_size.natural.y).natural,
194 );
195 }
196
197 widget_size
199 }
200}
201
202impl<W, H, T> Widget for SizeConstraint<W, H, T>
203where
204 W: RangeBounds<usize>,
205 H: RangeBounds<usize>,
206 T: Widget,
207{
208 fn render(&self, rect: Rect, buffer: &mut Buffer) {
209 let mut rect = rect;
210
211 if self.clamp_width(rect.size.x) != rect.size.x {
212 rect.size.x =
213 self.clamp_width(self.width_for_height(rect.size.y).natural);
214 }
215 if self.clamp_height(rect.size.y) != rect.size.y {
216 rect.size.y =
217 self.clamp_height(self.height_for_width(rect.size.x).natural);
218 }
219
220 self.widget.render(rect, buffer);
221 }
222}