1use iced_core::{
10 Alignment, Clipboard, Element, Layout, Length, Padding, Pixels, Point, Rectangle, Shell, Size,
11 Vector, Widget, alignment,
12 event::Event,
13 layout::{self, Node},
14 mouse, overlay, renderer,
15 widget::{Operation, tree::Tree},
16};
17use iced_widget::Row;
18
19#[allow(missing_debug_implementations)]
21pub struct FlushColumn<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> {
22 spacing: Pixels,
23 padding: Padding,
24 width: Length,
25 height: Length,
26 max_width: f32,
27 align: Alignment,
28 clip: bool,
29 children: Vec<Element<'a, Message, Theme, Renderer>>,
30 flush: bool,
31}
32
33impl<'a, Message: 'a, Theme: 'a, Renderer> FlushColumn<'a, Message, Theme, Renderer>
34where
35 Renderer: iced_core::Renderer + 'a,
36{
37 #[must_use]
39 pub fn new() -> Self {
40 Self::from_vec(Vec::new())
41 }
42
43 #[must_use]
45 pub fn with_capacity(capacity: usize) -> Self {
46 Self::from_vec(Vec::with_capacity(capacity))
47 }
48
49 #[must_use]
51 pub fn with_children(
52 children: impl IntoIterator<Item = Row<'a, Message, Theme, Renderer>>,
53 ) -> Self {
54 let iterator = children.into_iter();
55 Self::with_capacity(iterator.size_hint().0).extend(iterator)
56 }
57
58 #[must_use]
66 pub fn from_vec(children: Vec<Row<'a, Message, Theme, Renderer>>) -> Self {
67 let children = children
68 .into_iter()
69 .map(|x| Element::into(x.into()))
70 .collect();
71 Self {
72 spacing: Pixels::ZERO,
73 padding: Padding::ZERO,
74 width: Length::Shrink,
75 height: Length::Shrink,
76 max_width: f32::INFINITY,
77 align: Alignment::Start,
78 clip: false,
79 children,
80 flush: true,
81 }
82 }
83
84 #[must_use]
90 pub fn spacing(mut self, amount: impl Into<Pixels>) -> Self {
91 self.spacing = amount.into();
92 self
93 }
94
95 #[must_use]
97 pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
98 self.padding = padding.into();
99 self
100 }
101
102 #[must_use]
104 pub fn width(mut self, width: impl Into<Length>) -> Self {
105 self.width = width.into();
106 self
107 }
108
109 #[must_use]
111 pub fn height(mut self, height: impl Into<Length>) -> Self {
112 self.height = height.into();
113 self
114 }
115
116 #[must_use]
118 pub fn max_width(mut self, max_width: impl Into<Pixels>) -> Self {
119 self.max_width = max_width.into().0;
120 self
121 }
122
123 #[must_use]
125 pub fn align_x(mut self, align: impl Into<alignment::Vertical>) -> Self {
126 self.align = Alignment::from(align.into());
127 self
128 }
129
130 #[must_use]
133 pub fn clip(mut self, clip: bool) -> Self {
134 self.clip = clip;
135 self
136 }
137
138 #[must_use]
142 pub fn flush(mut self, flush: bool) -> Self {
143 self.flush = flush;
144 self
145 }
146
147 #[must_use]
149 pub fn push(mut self, child: impl Into<Row<'a, Message, Theme, Renderer>>) -> Self {
150 let child = child.into();
151 let child_size = child.size_hint();
152 self.width = self.width.enclose(child_size.width);
153 self.height = self.height.enclose(child_size.height);
154 self.children.push(child.into());
155 self
156 }
157
158 #[must_use]
160 pub fn push_maybe(self, child: Option<impl Into<Row<'a, Message, Theme, Renderer>>>) -> Self {
161 if let Some(child) = child {
162 self.push(child)
163 } else {
164 self
165 }
166 }
167
168 #[must_use]
170 pub fn extend(
171 self,
172 children: impl IntoIterator<Item = Row<'a, Message, Theme, Renderer>>,
173 ) -> Self {
174 children.into_iter().fold(self, Self::push)
175 }
176}
177
178#[allow(clippy::mismatching_type_param_order)]
179impl<'a, Message: 'a, Renderer> Default for FlushColumn<'a, Message, Renderer>
180where
181 Renderer: iced_core::Renderer + 'a,
182{
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188impl<'a, Message: 'a, Theme: 'a, Renderer: iced_core::Renderer + 'a>
189 FromIterator<Row<'a, Message, Theme, Renderer>> for FlushColumn<'a, Message, Theme, Renderer>
190{
191 fn from_iter<T: IntoIterator<Item = Row<'a, Message, Theme, Renderer>>>(iter: T) -> Self {
192 Self::with_children(iter)
193 }
194}
195
196impl<'a, Message: 'a, Theme: 'a, Renderer> Widget<Message, Theme, Renderer>
197 for FlushColumn<'a, Message, Theme, Renderer>
198where
199 Renderer: iced_core::Renderer,
200{
201 fn children(&self) -> Vec<Tree> {
202 self.children.iter().map(Tree::new).collect()
203 }
204
205 fn diff(&self, tree: &mut Tree) {
206 tree.diff_children(&self.children);
207 }
208
209 fn size(&self) -> Size<Length> {
210 Size {
211 width: self.width,
212 height: self.height,
213 }
214 }
215
216 fn layout(
217 &mut self,
218 tree: &mut Tree,
219 renderer: &Renderer,
220 limits: &layout::Limits,
221 ) -> layout::Node {
222 let limits = limits.max_width(self.max_width);
223 let node = layout::flex::resolve(
224 layout::flex::Axis::Vertical,
225 renderer,
226 &limits,
227 self.width,
228 self.height,
229 self.padding,
230 self.spacing.0,
231 self.align,
232 &mut self.children,
233 &mut tree.children,
234 );
235 let mut container_x = f32::MAX;
236 let mut container_width = 0.0f32;
237 for row in node.children() {
238 if row.size().width > container_width {
239 container_width = row.size().width;
240 }
241 if row.bounds().x < container_x {
242 container_x = row.bounds().x;
243 }
244 }
245 let mut children = Vec::<Node>::new();
246 for row in node.children() {
247 let mut row_children = Vec::<Node>::new();
248 let bounds = row.bounds();
249 let width_diff = container_width - bounds.width;
250 if !row.children().is_empty() {
251 for element in row.children() {
252 let bounds = element.bounds();
253 let x = bounds.x
254 + match self.align {
255 Alignment::Start => 0.0,
256 Alignment::Center => width_diff / 2.0,
257 Alignment::End => width_diff,
258 };
259 let mut element_node =
260 Node::with_children(element.size(), element.children().to_owned());
261 element_node.move_to_mut(Point::new(x, bounds.y));
262 row_children.push(element_node);
263 }
264 if row_children.len() > 1 {
265 match self.align {
266 Alignment::Start => {
267 let element = row_children.last().expect("Always exists.");
268 let bounds = element.bounds();
269 let mut position = bounds.position();
270 let mut element_node =
271 Node::with_children(bounds.size(), element.children().to_owned());
272 position.x += width_diff;
273 element_node.move_to_mut(position);
274 let node = row_children.last_mut().expect("Always exists.");
275 *node = element_node;
276 }
277 Alignment::Center => {}
278 Alignment::End => {
279 let element = row_children.first().expect("Always exists.");
280 let bounds = element.bounds();
281 let mut position = bounds.position();
282 let mut element_node =
283 Node::with_children(bounds.size(), element.children().to_owned());
284 position.x -= width_diff;
285 element_node.move_to_mut(position);
286 let node = row_children.first_mut().expect("Always exists.");
287 *node = element_node;
288 }
289 }
290 }
291 }
292 let mut row_node =
293 Node::with_children(Size::new(container_width, row.size().height), row_children);
294 row_node.move_to_mut(Point::new(container_x, bounds.y));
295 children.push(row_node);
296 }
297 Node::with_children(node.size(), children)
298 }
299
300 fn operate(
301 &mut self,
302 tree: &mut Tree,
303 layout: Layout<'_>,
304 renderer: &Renderer,
305 operation: &mut dyn Operation<()>,
306 ) {
307 operation.container(None, layout.bounds());
308 operation.traverse(&mut |operation| {
309 self.children
310 .iter_mut()
311 .zip(&mut tree.children)
312 .zip(layout.children())
313 .for_each(|((child, state), layout)| {
314 child
315 .as_widget_mut()
316 .operate(state, layout, renderer, operation);
317 });
318 });
319 }
320
321 fn update(
322 &mut self,
323 tree: &mut Tree,
324 event: &Event,
325 layout: Layout<'_>,
326 cursor: mouse::Cursor,
327 renderer: &Renderer,
328 clipboard: &mut dyn Clipboard,
329 shell: &mut Shell<'_, Message>,
330 viewport: &Rectangle,
331 ) {
332 for ((child, state), layout) in self
333 .children
334 .iter_mut()
335 .zip(&mut tree.children)
336 .zip(layout.children())
337 {
338 child.as_widget_mut().update(
339 state, event, layout, cursor, renderer, clipboard, shell, viewport,
340 );
341 }
342 }
343
344 fn mouse_interaction(
345 &self,
346 tree: &Tree,
347 layout: Layout<'_>,
348 cursor: mouse::Cursor,
349 viewport: &Rectangle,
350 renderer: &Renderer,
351 ) -> mouse::Interaction {
352 self.children
353 .iter()
354 .zip(&tree.children)
355 .zip(layout.children())
356 .map(|((child, state), layout)| {
357 child
358 .as_widget()
359 .mouse_interaction(state, layout, cursor, viewport, renderer)
360 })
361 .max()
362 .unwrap_or_default()
363 }
364
365 fn draw(
366 &self,
367 tree: &Tree,
368 renderer: &mut Renderer,
369 theme: &Theme,
370 style: &renderer::Style,
371 layout: Layout<'_>,
372 cursor: mouse::Cursor,
373 viewport: &Rectangle,
374 ) {
375 if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
376 for ((child, state), layout) in self
377 .children
378 .iter()
379 .zip(&tree.children)
380 .zip(layout.children())
381 {
382 child.as_widget().draw(
383 state,
384 renderer,
385 theme,
386 style,
387 layout,
388 cursor,
389 if self.clip {
390 &clipped_viewport
391 } else {
392 viewport
393 },
394 );
395 }
396 }
397 }
398
399 fn overlay<'b>(
400 &'b mut self,
401 tree: &'b mut Tree,
402 layout: Layout<'b>,
403 renderer: &Renderer,
404 viewport: &Rectangle,
405 translation: Vector,
406 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
407 overlay::from_children(
408 &mut self.children,
409 tree,
410 layout,
411 renderer,
412 viewport,
413 translation,
414 )
415 }
416}
417
418impl<'a, Message, Theme, Renderer> From<FlushColumn<'a, Message, Theme, Renderer>>
419 for Element<'a, Message, Theme, Renderer>
420where
421 Message: 'a,
422 Theme: 'a,
423 Renderer: iced_core::Renderer + 'a,
424{
425 fn from(column: FlushColumn<'a, Message, Theme, Renderer>) -> Self {
426 Self::new(column)
427 }
428}