iced_widget/
row.rs

1//! Distribute content horizontally.
2use crate::core::alignment::{self, Alignment};
3use crate::core::event::{self, Event};
4use crate::core::layout::{self, Layout};
5use crate::core::mouse;
6use crate::core::overlay;
7use crate::core::renderer;
8use crate::core::widget::{Operation, Tree};
9use crate::core::{
10    Clipboard, Element, Length, Padding, Pixels, Rectangle, Shell, Size,
11    Vector, Widget,
12};
13
14/// A container that distributes its contents horizontally.
15///
16/// # Example
17/// ```no_run
18/// # mod iced { pub mod widget { pub use iced_widget::*; } }
19/// # pub type State = ();
20/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
21/// use iced::widget::{button, row};
22///
23/// #[derive(Debug, Clone)]
24/// enum Message {
25///     // ...
26/// }
27///
28/// fn view(state: &State) -> Element<'_, Message> {
29///     row![
30///         "I am to the left!",
31///         button("I am in the middle!"),
32///         "I am to the right!",
33///     ].into()
34/// }
35/// ```
36#[allow(missing_debug_implementations)]
37pub struct Row<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
38    spacing: f32,
39    padding: Padding,
40    width: Length,
41    height: Length,
42    align: Alignment,
43    clip: bool,
44    children: Vec<Element<'a, Message, Theme, Renderer>>,
45}
46
47impl<'a, Message, Theme, Renderer> Row<'a, Message, Theme, Renderer>
48where
49    Renderer: crate::core::Renderer,
50{
51    /// Creates an empty [`Row`].
52    pub fn new() -> Self {
53        Self::from_vec(Vec::new())
54    }
55
56    /// Creates a [`Row`] with the given capacity.
57    pub fn with_capacity(capacity: usize) -> Self {
58        Self::from_vec(Vec::with_capacity(capacity))
59    }
60
61    /// Creates a [`Row`] with the given elements.
62    pub fn with_children(
63        children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
64    ) -> Self {
65        let iterator = children.into_iter();
66
67        Self::with_capacity(iterator.size_hint().0).extend(iterator)
68    }
69
70    /// Creates a [`Row`] from an already allocated [`Vec`].
71    ///
72    /// Keep in mind that the [`Row`] will not inspect the [`Vec`], which means
73    /// it won't automatically adapt to the sizing strategy of its contents.
74    ///
75    /// If any of the children have a [`Length::Fill`] strategy, you will need to
76    /// call [`Row::width`] or [`Row::height`] accordingly.
77    pub fn from_vec(
78        children: Vec<Element<'a, Message, Theme, Renderer>>,
79    ) -> Self {
80        Self {
81            spacing: 0.0,
82            padding: Padding::ZERO,
83            width: Length::Shrink,
84            height: Length::Shrink,
85            align: Alignment::Start,
86            clip: false,
87            children,
88        }
89    }
90
91    /// Sets the horizontal spacing _between_ elements.
92    ///
93    /// Custom margins per element do not exist in iced. You should use this
94    /// method instead! While less flexible, it helps you keep spacing between
95    /// elements consistent.
96    pub fn spacing(mut self, amount: impl Into<Pixels>) -> Self {
97        self.spacing = amount.into().0;
98        self
99    }
100
101    /// Sets the [`Padding`] of the [`Row`].
102    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
103        self.padding = padding.into();
104        self
105    }
106
107    /// Sets the width of the [`Row`].
108    pub fn width(mut self, width: impl Into<Length>) -> Self {
109        self.width = width.into();
110        self
111    }
112
113    /// Sets the height of the [`Row`].
114    pub fn height(mut self, height: impl Into<Length>) -> Self {
115        self.height = height.into();
116        self
117    }
118
119    /// Sets the vertical alignment of the contents of the [`Row`] .
120    pub fn align_y(mut self, align: impl Into<alignment::Vertical>) -> Self {
121        self.align = Alignment::from(align.into());
122        self
123    }
124
125    /// Sets whether the contents of the [`Row`] should be clipped on
126    /// overflow.
127    pub fn clip(mut self, clip: bool) -> Self {
128        self.clip = clip;
129        self
130    }
131
132    /// Adds an [`Element`] to the [`Row`].
133    pub fn push(
134        mut self,
135        child: impl Into<Element<'a, Message, Theme, Renderer>>,
136    ) -> Self {
137        let child = child.into();
138        let child_size = child.as_widget().size_hint();
139
140        self.width = self.width.enclose(child_size.width);
141        self.height = self.height.enclose(child_size.height);
142
143        self.children.push(child);
144        self
145    }
146
147    /// Adds an element to the [`Row`], if `Some`.
148    pub fn push_maybe(
149        self,
150        child: Option<impl Into<Element<'a, Message, Theme, Renderer>>>,
151    ) -> Self {
152        if let Some(child) = child {
153            self.push(child)
154        } else {
155            self
156        }
157    }
158
159    /// Extends the [`Row`] with the given children.
160    pub fn extend(
161        self,
162        children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
163    ) -> Self {
164        children.into_iter().fold(self, Self::push)
165    }
166
167    /// Turns the [`Row`] into a [`Wrapping`] row.
168    ///
169    /// The original alignment of the [`Row`] is preserved per row wrapped.
170    pub fn wrap(self) -> Wrapping<'a, Message, Theme, Renderer> {
171        Wrapping { row: self }
172    }
173}
174
175impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer>
176where
177    Renderer: crate::core::Renderer,
178{
179    fn default() -> Self {
180        Self::new()
181    }
182}
183
184impl<'a, Message, Theme, Renderer: crate::core::Renderer>
185    FromIterator<Element<'a, Message, Theme, Renderer>>
186    for Row<'a, Message, Theme, Renderer>
187{
188    fn from_iter<
189        T: IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
190    >(
191        iter: T,
192    ) -> Self {
193        Self::with_children(iter)
194    }
195}
196
197impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
198    for Row<'a, Message, Theme, Renderer>
199where
200    Renderer: crate::core::Renderer,
201{
202    fn children(&self) -> Vec<Tree> {
203        self.children.iter().map(Tree::new).collect()
204    }
205
206    fn diff(&mut self, tree: &mut Tree) {
207        tree.diff_children(&mut self.children);
208    }
209
210    fn size(&self) -> Size<Length> {
211        Size {
212            width: self.width,
213            height: self.height,
214        }
215    }
216
217    fn layout(
218        &self,
219        tree: &mut Tree,
220        renderer: &Renderer,
221        limits: &layout::Limits,
222    ) -> layout::Node {
223        layout::flex::resolve(
224            layout::flex::Axis::Horizontal,
225            renderer,
226            limits,
227            self.width,
228            self.height,
229            self.padding,
230            self.spacing,
231            self.align,
232            &self.children,
233            &mut tree.children,
234        )
235    }
236
237    fn operate(
238        &self,
239        tree: &mut Tree,
240        layout: Layout<'_>,
241        renderer: &Renderer,
242        operation: &mut dyn Operation,
243    ) {
244        operation.container(None, layout.bounds(), &mut |operation| {
245            self.children
246                .iter()
247                .zip(&mut tree.children)
248                .zip(layout.children())
249                .for_each(|((child, state), c_layout)| {
250                    child.as_widget().operate(
251                        state,
252                        c_layout.with_virtual_offset(layout.virtual_offset()),
253                        renderer,
254                        operation,
255                    );
256                });
257        });
258    }
259
260    fn on_event(
261        &mut self,
262        tree: &mut Tree,
263        event: Event,
264        layout: Layout<'_>,
265        cursor: mouse::Cursor,
266        renderer: &Renderer,
267        clipboard: &mut dyn Clipboard,
268        shell: &mut Shell<'_, Message>,
269        viewport: &Rectangle,
270    ) -> event::Status {
271        self.children
272            .iter_mut()
273            .zip(&mut tree.children)
274            .zip(layout.children())
275            .map(|((child, state), c_layout)| {
276                child.as_widget_mut().on_event(
277                    state,
278                    event.clone(),
279                    c_layout.with_virtual_offset(layout.virtual_offset()),
280                    cursor,
281                    renderer,
282                    clipboard,
283                    shell,
284                    viewport,
285                )
286            })
287            .fold(event::Status::Ignored, event::Status::merge)
288    }
289
290    fn mouse_interaction(
291        &self,
292        tree: &Tree,
293        layout: Layout<'_>,
294        cursor: mouse::Cursor,
295        viewport: &Rectangle,
296        renderer: &Renderer,
297    ) -> mouse::Interaction {
298        self.children
299            .iter()
300            .zip(&tree.children)
301            .zip(layout.children())
302            .map(|((child, state), c_layout)| {
303                child.as_widget().mouse_interaction(
304                    state,
305                    c_layout.with_virtual_offset(layout.virtual_offset()),
306                    cursor,
307                    viewport,
308                    renderer,
309                )
310            })
311            .max()
312            .unwrap_or_default()
313    }
314
315    fn draw(
316        &self,
317        tree: &Tree,
318        renderer: &mut Renderer,
319        theme: &Theme,
320        style: &renderer::Style,
321        layout: Layout<'_>,
322        cursor: mouse::Cursor,
323        viewport: &Rectangle,
324    ) {
325        if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
326            let viewport = if self.clip {
327                &clipped_viewport
328            } else {
329                viewport
330            };
331
332            for ((child, state), c_layout) in self
333                .children
334                .iter()
335                .zip(&tree.children)
336                .zip(layout.children())
337                .filter(|(_, layout)| layout.bounds().intersects(viewport))
338            {
339                child.as_widget().draw(
340                    state,
341                    renderer,
342                    theme,
343                    style,
344                    c_layout.with_virtual_offset(layout.virtual_offset()),
345                    cursor,
346                    viewport,
347                );
348            }
349        }
350    }
351
352    fn overlay<'b>(
353        &'b mut self,
354        tree: &'b mut Tree,
355        layout: Layout<'_>,
356        renderer: &Renderer,
357        translation: Vector,
358    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
359        overlay::from_children(
360            &mut self.children,
361            tree,
362            layout,
363            renderer,
364            translation,
365        )
366    }
367
368    #[cfg(feature = "a11y")]
369    /// get the a11y nodes for the widget
370    fn a11y_nodes(
371        &self,
372        layout: Layout<'_>,
373        state: &Tree,
374        cursor: mouse::Cursor,
375    ) -> iced_accessibility::A11yTree {
376        use iced_accessibility::A11yTree;
377        A11yTree::join(
378            self.children
379                .iter()
380                .zip(layout.children())
381                .zip(state.children.iter())
382                .map(|((c, c_layout), state)| {
383                    c.as_widget().a11y_nodes(
384                        c_layout.with_virtual_offset(layout.virtual_offset()),
385                        state,
386                        cursor,
387                    )
388                }),
389        )
390    }
391
392    fn drag_destinations(
393        &self,
394        state: &Tree,
395        layout: Layout<'_>,
396        renderer: &Renderer,
397        dnd_rectangles: &mut crate::core::clipboard::DndDestinationRectangles,
398    ) {
399        for ((e, c_layout), state) in self
400            .children
401            .iter()
402            .zip(layout.children())
403            .zip(state.children.iter())
404        {
405            e.as_widget().drag_destinations(
406                state,
407                c_layout.with_virtual_offset(layout.virtual_offset()),
408                renderer,
409                dnd_rectangles,
410            );
411        }
412    }
413}
414
415impl<'a, Message, Theme, Renderer> From<Row<'a, Message, Theme, Renderer>>
416    for Element<'a, Message, Theme, Renderer>
417where
418    Message: 'a,
419    Theme: 'a,
420    Renderer: crate::core::Renderer + 'a,
421{
422    fn from(row: Row<'a, Message, Theme, Renderer>) -> Self {
423        Self::new(row)
424    }
425}
426
427/// A [`Row`] that wraps its contents.
428///
429/// Create a [`Row`] first, and then call [`Row::wrap`] to
430/// obtain a [`Row`] that wraps its contents.
431///
432/// The original alignment of the [`Row`] is preserved per row wrapped.
433#[allow(missing_debug_implementations)]
434pub struct Wrapping<
435    'a,
436    Message,
437    Theme = crate::Theme,
438    Renderer = crate::Renderer,
439> {
440    row: Row<'a, Message, Theme, Renderer>,
441}
442
443impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
444    for Wrapping<'a, Message, Theme, Renderer>
445where
446    Renderer: crate::core::Renderer,
447{
448    fn children(&self) -> Vec<Tree> {
449        self.row.children()
450    }
451
452    fn diff(&mut self, tree: &mut Tree) {
453        self.row.diff(tree);
454    }
455
456    fn size(&self) -> Size<Length> {
457        self.row.size()
458    }
459
460    fn layout(
461        &self,
462        tree: &mut Tree,
463        renderer: &Renderer,
464        limits: &layout::Limits,
465    ) -> layout::Node {
466        let limits = limits
467            .width(self.row.width)
468            .height(self.row.height)
469            .shrink(self.row.padding);
470
471        let spacing = self.row.spacing;
472        let max_width = limits.max().width;
473
474        let mut children: Vec<layout::Node> = Vec::new();
475        let mut intrinsic_size = Size::ZERO;
476        let mut row_start = 0;
477        let mut row_height = 0.0;
478        let mut x = 0.0;
479        let mut y = 0.0;
480
481        let align_factor = match self.row.align {
482            Alignment::Start => 0.0,
483            Alignment::Center => 2.0,
484            Alignment::End => 1.0,
485        };
486
487        let align = |row_start: std::ops::Range<usize>,
488                     row_height: f32,
489                     children: &mut Vec<layout::Node>| {
490            if align_factor != 0.0 {
491                for node in &mut children[row_start] {
492                    let height = node.size().height;
493
494                    node.translate_mut(Vector::new(
495                        0.0,
496                        (row_height - height) / align_factor,
497                    ));
498                }
499            }
500        };
501
502        for (i, child) in self.row.children.iter().enumerate() {
503            let node = child.as_widget().layout(
504                &mut tree.children[i],
505                renderer,
506                &limits,
507            );
508
509            let child_size = node.size();
510
511            if x != 0.0 && x + child_size.width > max_width {
512                intrinsic_size.width = intrinsic_size.width.max(x - spacing);
513
514                align(row_start..i, row_height, &mut children);
515
516                y += row_height + spacing;
517                x = 0.0;
518                row_start = i;
519                row_height = 0.0;
520            }
521
522            row_height = row_height.max(child_size.height);
523
524            children.push(node.move_to((
525                x + self.row.padding.left,
526                y + self.row.padding.top,
527            )));
528
529            x += child_size.width + spacing;
530        }
531
532        if x != 0.0 {
533            intrinsic_size.width = intrinsic_size.width.max(x - spacing);
534        }
535
536        intrinsic_size.height = y + row_height;
537        align(row_start..children.len(), row_height, &mut children);
538
539        let size =
540            limits.resolve(self.row.width, self.row.height, intrinsic_size);
541
542        layout::Node::with_children(size.expand(self.row.padding), children)
543    }
544
545    fn operate(
546        &self,
547        tree: &mut Tree,
548        layout: Layout<'_>,
549        renderer: &Renderer,
550        operation: &mut dyn Operation,
551    ) {
552        self.row.operate(tree, layout, renderer, operation);
553    }
554
555    fn on_event(
556        &mut self,
557        tree: &mut Tree,
558        event: Event,
559        layout: Layout<'_>,
560        cursor: mouse::Cursor,
561        renderer: &Renderer,
562        clipboard: &mut dyn Clipboard,
563        shell: &mut Shell<'_, Message>,
564        viewport: &Rectangle,
565    ) -> event::Status {
566        self.row.on_event(
567            tree, event, layout, cursor, renderer, clipboard, shell, viewport,
568        )
569    }
570
571    fn mouse_interaction(
572        &self,
573        tree: &Tree,
574        layout: Layout<'_>,
575        cursor: mouse::Cursor,
576        viewport: &Rectangle,
577        renderer: &Renderer,
578    ) -> mouse::Interaction {
579        self.row
580            .mouse_interaction(tree, layout, cursor, viewport, renderer)
581    }
582
583    fn draw(
584        &self,
585        tree: &Tree,
586        renderer: &mut Renderer,
587        theme: &Theme,
588        style: &renderer::Style,
589        layout: Layout<'_>,
590        cursor: mouse::Cursor,
591        viewport: &Rectangle,
592    ) {
593        self.row
594            .draw(tree, renderer, theme, style, layout, cursor, viewport);
595    }
596
597    fn overlay<'b>(
598        &'b mut self,
599        tree: &'b mut Tree,
600        layout: Layout<'_>,
601        renderer: &Renderer,
602        translation: Vector,
603    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
604        self.row.overlay(tree, layout, renderer, translation)
605    }
606}
607
608impl<'a, Message, Theme, Renderer> From<Wrapping<'a, Message, Theme, Renderer>>
609    for Element<'a, Message, Theme, Renderer>
610where
611    Message: 'a,
612    Theme: 'a,
613    Renderer: crate::core::Renderer + 'a,
614{
615    fn from(row: Wrapping<'a, Message, Theme, Renderer>) -> Self {
616        Self::new(row)
617    }
618}