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::Column;
18
19#[allow(missing_debug_implementations)]
21pub struct FlushRow<'a, Message, Theme = iced_core::Theme, Renderer = iced_widget::Renderer> {
22 spacing: Pixels,
23 padding: Padding,
24 width: Length,
25 height: Length,
26 max_height: 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> FlushRow<'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 = Column<'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<Column<'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_height: 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_height(mut self, max_height: impl Into<Pixels>) -> Self {
119 self.max_height = max_height.into().0;
120 self
121 }
122
123 #[must_use]
125 pub fn align_y(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<Column<'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(
161 self,
162 child: Option<impl Into<Column<'a, Message, Theme, Renderer>>>,
163 ) -> Self {
164 if let Some(child) = child {
165 self.push(child)
166 } else {
167 self
168 }
169 }
170
171 #[must_use]
173 pub fn extend(
174 self,
175 children: impl IntoIterator<Item = Column<'a, Message, Theme, Renderer>>,
176 ) -> Self {
177 children.into_iter().fold(self, Self::push)
178 }
179}
180
181#[allow(clippy::mismatching_type_param_order)]
182impl<'a, Message: 'a, Renderer> Default for FlushRow<'a, Message, Renderer>
183where
184 Renderer: iced_core::Renderer + 'a,
185{
186 fn default() -> Self {
187 Self::new()
188 }
189}
190
191impl<'a, Message: 'a, Theme: 'a, Renderer: iced_core::Renderer + 'a>
192 FromIterator<Column<'a, Message, Theme, Renderer>> for FlushRow<'a, Message, Theme, Renderer>
193{
194 fn from_iter<T: IntoIterator<Item = Column<'a, Message, Theme, Renderer>>>(iter: T) -> Self {
195 Self::with_children(iter)
196 }
197}
198
199impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
200 for FlushRow<'_, Message, Theme, Renderer>
201where
202 Renderer: iced_core::Renderer,
203{
204 fn children(&self) -> Vec<Tree> {
205 self.children.iter().map(Tree::new).collect()
206 }
207
208 fn diff(&self, tree: &mut Tree) {
209 tree.diff_children(&self.children);
210 }
211
212 fn size(&self) -> Size<Length> {
213 Size {
214 width: self.width,
215 height: self.height,
216 }
217 }
218
219 fn layout(
220 &mut self,
221 tree: &mut Tree,
222 renderer: &Renderer,
223 limits: &layout::Limits,
224 ) -> layout::Node {
225 let limits = limits.max_height(self.max_height);
226 let node = layout::flex::resolve(
227 layout::flex::Axis::Horizontal,
228 renderer,
229 &limits,
230 self.width,
231 self.height,
232 self.padding,
233 self.spacing.0,
234 self.align,
235 &mut self.children,
236 &mut tree.children,
237 );
238 let mut container_y = f32::MAX;
239 let mut container_height = 0.0f32;
240 for column in node.children() {
241 if column.size().height > container_height {
242 container_height = column.size().height;
243 }
244 if column.bounds().y < container_y {
245 container_y = column.bounds().y;
246 }
247 }
248 let mut children = Vec::<Node>::new();
249 for column in node.children() {
250 let mut column_children = Vec::<Node>::new();
251 let bounds = column.bounds();
252 let height_diff = container_height - bounds.height;
253 if !column.children().is_empty() {
254 for element in column.children() {
255 let bounds = element.bounds();
256 let y = bounds.y
257 + match self.align {
258 Alignment::Start => 0.0,
259 Alignment::Center => height_diff / 2.0,
260 Alignment::End => height_diff,
261 };
262 let mut element_node =
263 Node::with_children(element.size(), element.children().to_owned());
264 element_node.move_to_mut(Point::new(bounds.x, y));
265 column_children.push(element_node);
266 }
267 if column_children.len() > 1 {
268 match self.align {
269 Alignment::Start => {
270 let element = column_children.last().expect("Always exists.");
271 let bounds = element.bounds();
272 let mut position = bounds.position();
273 let mut element_node =
274 Node::with_children(bounds.size(), element.children().to_owned());
275 position.y += height_diff;
276 element_node.move_to_mut(position);
277 let node = column_children.last_mut().expect("Always exists.");
278 *node = element_node;
279 }
280 Alignment::Center => {}
281 Alignment::End => {
282 let element = column_children.first().expect("Always exists.");
283 let bounds = element.bounds();
284 let mut position = bounds.position();
285 let mut element_node =
286 Node::with_children(bounds.size(), element.children().to_owned());
287 position.y -= height_diff;
288 element_node.move_to_mut(position);
289 let node = column_children.first_mut().expect("Always exists.");
290 *node = element_node;
291 }
292 }
293 }
294 }
295 let mut column_node = Node::with_children(
296 Size::new(column.size().width, container_height),
297 column_children,
298 );
299 column_node.move_to_mut(Point::new(bounds.x, container_y));
300 children.push(column_node);
301 }
302 Node::with_children(node.size(), children)
303 }
304
305 fn operate(
306 &mut self,
307 tree: &mut Tree,
308 layout: Layout<'_>,
309 renderer: &Renderer,
310 operation: &mut dyn Operation<()>,
311 ) {
312 operation.container(None, layout.bounds());
313 operation.traverse(&mut |operation| {
314 self.children
315 .iter_mut()
316 .zip(&mut tree.children)
317 .zip(layout.children())
318 .for_each(|((child, state), layout)| {
319 child
320 .as_widget_mut()
321 .operate(state, layout, renderer, operation);
322 });
323 });
324 }
325
326 fn update(
327 &mut self,
328 tree: &mut Tree,
329 event: &Event,
330 layout: Layout<'_>,
331 cursor: mouse::Cursor,
332 renderer: &Renderer,
333 clipboard: &mut dyn Clipboard,
334 shell: &mut Shell<'_, Message>,
335 viewport: &Rectangle,
336 ) {
337 for ((child, state), layout) in self
338 .children
339 .iter_mut()
340 .zip(&mut tree.children)
341 .zip(layout.children())
342 {
343 child.as_widget_mut().update(
344 state, event, layout, cursor, renderer, clipboard, shell, viewport,
345 );
346 }
347 }
348
349 fn mouse_interaction(
350 &self,
351 tree: &Tree,
352 layout: Layout<'_>,
353 cursor: mouse::Cursor,
354 viewport: &Rectangle,
355 renderer: &Renderer,
356 ) -> mouse::Interaction {
357 self.children
358 .iter()
359 .zip(&tree.children)
360 .zip(layout.children())
361 .map(|((child, state), layout)| {
362 child
363 .as_widget()
364 .mouse_interaction(state, layout, cursor, viewport, renderer)
365 })
366 .max()
367 .unwrap_or_default()
368 }
369
370 fn draw(
371 &self,
372 tree: &Tree,
373 renderer: &mut Renderer,
374 theme: &Theme,
375 style: &renderer::Style,
376 layout: Layout<'_>,
377 cursor: mouse::Cursor,
378 viewport: &Rectangle,
379 ) {
380 if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
381 for ((child, state), layout) in self
382 .children
383 .iter()
384 .zip(&tree.children)
385 .zip(layout.children())
386 {
387 child.as_widget().draw(
388 state,
389 renderer,
390 theme,
391 style,
392 layout,
393 cursor,
394 if self.clip {
395 &clipped_viewport
396 } else {
397 viewport
398 },
399 );
400 }
401 }
402 }
403
404 fn overlay<'b>(
405 &'b mut self,
406 tree: &'b mut Tree,
407 layout: Layout<'b>,
408 renderer: &Renderer,
409 viewport: &Rectangle,
410 translation: Vector,
411 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
412 overlay::from_children(
413 &mut self.children,
414 tree,
415 layout,
416 renderer,
417 viewport,
418 translation,
419 )
420 }
421}
422
423impl<'a, Message, Theme, Renderer> From<FlushRow<'a, Message, Theme, Renderer>>
424 for Element<'a, Message, Theme, Renderer>
425where
426 Message: 'a,
427 Theme: 'a,
428 Renderer: iced_core::Renderer + 'a,
429{
430 fn from(row: FlushRow<'a, Message, Theme, Renderer>) -> Self {
431 Self::new(row)
432 }
433}