1use crate::{Layout, Painter, Pos2, Rect, Region, Vec2, grid, vec2};
2use emath::GuiRounding as _;
3
4#[cfg(debug_assertions)]
5use crate::{Align2, Color32, Stroke};
6
7pub(crate) struct Placer {
8 grid: Option<grid::GridLayout>,
10 layout: Layout,
11 region: Region,
12}
13
14impl Placer {
15 pub(crate) fn new(max_rect: Rect, layout: Layout) -> Self {
16 let region = layout.region_from_max_rect(max_rect);
17 Self {
18 grid: None,
19 layout,
20 region,
21 }
22 }
23
24 #[inline(always)]
25 pub(crate) fn set_grid(&mut self, grid: grid::GridLayout) {
26 self.grid = Some(grid);
27 }
28
29 pub(crate) fn save_grid(&mut self) {
30 if let Some(grid) = &mut self.grid {
31 grid.save();
32 }
33 }
34
35 #[inline(always)]
36 pub(crate) fn grid(&self) -> Option<&grid::GridLayout> {
37 self.grid.as_ref()
38 }
39
40 #[inline(always)]
41 pub(crate) fn is_grid(&self) -> bool {
42 self.grid.is_some()
43 }
44
45 #[inline(always)]
46 pub(crate) fn layout(&self) -> &Layout {
47 &self.layout
48 }
49
50 #[inline(always)]
51 pub(crate) fn prefer_right_to_left(&self) -> bool {
52 self.layout.prefer_right_to_left()
53 }
54
55 #[inline(always)]
56 pub(crate) fn min_rect(&self) -> Rect {
57 self.region.min_rect
58 }
59
60 #[inline(always)]
61 pub(crate) fn max_rect(&self) -> Rect {
62 self.region.max_rect
63 }
64
65 #[inline(always)]
66 pub(crate) fn force_set_min_rect(&mut self, min_rect: Rect) {
67 self.region.min_rect = min_rect;
68 }
69
70 #[inline(always)]
71 pub(crate) fn cursor(&self) -> Rect {
72 self.region.cursor
73 }
74
75 #[inline(always)]
76 pub(crate) fn set_cursor(&mut self, cursor: Rect) {
77 self.region.cursor = cursor;
78 }
79}
80
81impl Placer {
82 pub(crate) fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect {
83 if let Some(grid) = &self.grid {
84 grid.align_size_within_rect(size, outer)
85 } else {
86 self.layout.align_size_within_rect(size, outer)
87 }
88 }
89
90 pub(crate) fn available_rect_before_wrap(&self) -> Rect {
91 if let Some(grid) = &self.grid {
92 grid.available_rect(&self.region)
93 } else {
94 self.layout.available_rect_before_wrap(&self.region)
95 }
96 .round_ui()
97 }
98
99 pub(crate) fn available_size(&self) -> Vec2 {
102 if let Some(grid) = &self.grid {
103 grid.available_rect(&self.region).size()
104 } else {
105 self.layout.available_size(&self.region)
106 }
107 }
108
109 pub(crate) fn next_space(&self, child_size: Vec2, item_spacing: Vec2) -> Rect {
114 debug_assert!(
115 0.0 <= child_size.x && 0.0 <= child_size.y,
116 "Negative child size: {child_size:?}"
117 );
118 self.region.sanity_check();
119 if let Some(grid) = &self.grid {
120 grid.next_cell(self.region.cursor, child_size)
121 } else {
122 self.layout
123 .next_frame(&self.region, child_size, item_spacing)
124 }
125 }
126
127 pub(crate) fn next_widget_position(&self) -> Pos2 {
129 if let Some(grid) = &self.grid {
130 grid.next_cell(self.region.cursor, Vec2::ZERO).center()
131 } else {
132 self.layout.next_widget_position(&self.region)
133 }
134 }
135
136 pub(crate) fn justify_and_align(&self, rect: Rect, child_size: Vec2) -> Rect {
138 debug_assert!(!rect.any_nan(), "rect: {rect:?}");
139 debug_assert!(!child_size.any_nan(), "child_size is NaN: {child_size:?}");
140
141 if let Some(grid) = &self.grid {
142 grid.justify_and_align(rect, child_size)
143 } else {
144 self.layout.justify_and_align(rect, child_size)
145 }
146 }
147
148 pub(crate) fn advance_cursor(&mut self, amount: f32) {
153 debug_assert!(
154 self.grid.is_none(),
155 "You cannot advance the cursor when in a grid layout"
156 );
157 self.layout.advance_cursor(&mut self.region, amount);
158 }
159
160 pub(crate) fn advance_after_rects(
166 &mut self,
167 frame_rect: Rect,
168 widget_rect: Rect,
169 item_spacing: Vec2,
170 ) {
171 debug_assert!(!frame_rect.any_nan(), "frame_rect: {frame_rect:?}");
172 debug_assert!(
173 !widget_rect.any_nan(),
174 "widget_rect is NaN: {widget_rect:?}"
175 );
176 self.region.sanity_check();
177
178 if let Some(grid) = &mut self.grid {
179 grid.advance(&mut self.region.cursor, frame_rect, widget_rect);
180 } else {
181 self.layout.advance_after_rects(
182 &mut self.region.cursor,
183 frame_rect,
184 widget_rect,
185 item_spacing,
186 );
187 }
188
189 self.expand_to_include_rect(frame_rect); self.region.sanity_check();
192 }
193
194 pub(crate) fn end_row(&mut self, item_spacing: Vec2, painter: &Painter) {
197 if let Some(grid) = &mut self.grid {
198 grid.end_row(&mut self.region.cursor, painter);
199 } else {
200 self.layout.end_row(&mut self.region, item_spacing);
201 }
202 }
203
204 pub(crate) fn set_row_height(&mut self, height: f32) {
206 self.layout.set_row_height(&mut self.region, height);
207 }
208}
209
210impl Placer {
211 pub(crate) fn expand_to_include_rect(&mut self, rect: Rect) {
213 self.region.expand_to_include_rect(rect);
214 }
215
216 pub(crate) fn expand_to_include_x(&mut self, x: f32) {
218 self.region.expand_to_include_x(x);
219 }
220
221 pub(crate) fn expand_to_include_y(&mut self, y: f32) {
223 self.region.expand_to_include_y(y);
224 }
225
226 fn next_widget_space_ignore_wrap_justify(&self, size: Vec2) -> Rect {
227 self.layout
228 .next_widget_space_ignore_wrap_justify(&self.region, size)
229 }
230
231 pub(crate) fn set_max_width(&mut self, width: f32) {
234 let rect = self.next_widget_space_ignore_wrap_justify(vec2(width, 0.0));
235 let region = &mut self.region;
236 region.max_rect.min.x = rect.min.x;
237 region.max_rect.max.x = rect.max.x;
238 region.max_rect |= region.min_rect; region.cursor.min.x = region.max_rect.min.x;
241 region.cursor.max.x = region.max_rect.max.x;
242
243 region.sanity_check();
244 }
245
246 pub(crate) fn set_max_height(&mut self, height: f32) {
249 let rect = self.next_widget_space_ignore_wrap_justify(vec2(0.0, height));
250 let region = &mut self.region;
251 region.max_rect.min.y = rect.min.y;
252 region.max_rect.max.y = rect.max.y;
253 region.max_rect |= region.min_rect; region.cursor.min.y = region.max_rect.min.y;
256 region.cursor.max.y = region.max_rect.max.y;
257
258 region.sanity_check();
259 }
260
261 pub(crate) fn set_min_width(&mut self, width: f32) {
264 if width <= 0.0 {
265 return;
266 }
267 let rect = self.next_widget_space_ignore_wrap_justify(vec2(width, 0.0));
268 self.region.expand_to_include_x(rect.min.x);
269 self.region.expand_to_include_x(rect.max.x);
270 }
271
272 pub(crate) fn set_min_height(&mut self, height: f32) {
275 if height <= 0.0 {
276 return;
277 }
278 let rect = self.next_widget_space_ignore_wrap_justify(vec2(0.0, height));
279 self.region.expand_to_include_y(rect.min.y);
280 self.region.expand_to_include_y(rect.max.y);
281 }
282}
283
284impl Placer {
285 #[cfg(debug_assertions)]
286 pub(crate) fn debug_paint_cursor(&self, painter: &crate::Painter, text: impl ToString) {
287 let stroke = Stroke::new(1.0, Color32::DEBUG_COLOR);
288
289 if let Some(grid) = &self.grid {
290 let rect = grid.next_cell(self.cursor(), Vec2::splat(0.0));
291 painter.rect_stroke(rect, 1.0, stroke, epaint::StrokeKind::Inside);
292 let align = Align2::CENTER_CENTER;
293 painter.debug_text(align.pos_in_rect(&rect), align, stroke.color, text);
294 } else {
295 self.layout
296 .paint_text_at_cursor(painter, &self.region, stroke, text);
297 }
298 }
299}