iced_widget/
row.rs

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