Skip to main content

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