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        let child_size = child.as_widget().size();
131
132        if !child_size.is_void() {
133            self.children.push(child);
134        }
135
136        self
137    }
138
139    /// Extends the [`Row`] with the given children.
140    pub fn extend(
141        self,
142        children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
143    ) -> Self {
144        children.into_iter().fold(self, Self::push)
145    }
146
147    /// Turns the [`Row`] into a [`Wrapping`] row.
148    ///
149    /// The original alignment of the [`Row`] is preserved per row wrapped.
150    pub fn wrap(self) -> Wrapping<'a, Message, Theme, Renderer> {
151        Wrapping {
152            row: self,
153            vertical_spacing: None,
154            align_x: alignment::Horizontal::Left,
155        }
156    }
157}
158
159impl<Message, Renderer> Default for Row<'_, Message, Renderer>
160where
161    Renderer: crate::core::Renderer,
162{
163    fn default() -> Self {
164        Self::new()
165    }
166}
167
168impl<'a, Message, Theme, Renderer: crate::core::Renderer>
169    FromIterator<Element<'a, Message, Theme, Renderer>> for Row<'a, Message, Theme, Renderer>
170{
171    fn from_iter<T: IntoIterator<Item = Element<'a, Message, Theme, Renderer>>>(iter: T) -> Self {
172        Self::with_children(iter)
173    }
174}
175
176impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
177    for Row<'_, Message, Theme, Renderer>
178where
179    Renderer: crate::core::Renderer,
180{
181    fn diff(&mut self, tree: &mut Tree) {
182        tree.diff_children(&mut self.children);
183
184        if self.width.is_fit() || self.height.is_fit() {
185            for child in &self.children {
186                let size = child.as_widget().size();
187
188                self.width = self.width.enclose(size.width);
189                self.height = self.height.enclose(size.height);
190            }
191        }
192    }
193
194    fn size(&self) -> Size<Length> {
195        Size {
196            width: self.width,
197            height: self.height,
198        }
199    }
200
201    fn layout(
202        &mut self,
203        tree: &mut Tree,
204        renderer: &Renderer,
205        limits: &layout::Limits,
206    ) -> layout::Node {
207        layout::flex::resolve(
208            layout::flex::Axis::Horizontal,
209            renderer,
210            limits,
211            self.width,
212            self.height,
213            self.padding,
214            self.spacing,
215            self.align,
216            &mut self.children,
217            &mut tree.children,
218        )
219    }
220
221    fn operate(
222        &mut self,
223        tree: &mut Tree,
224        layout: Layout<'_>,
225        renderer: &Renderer,
226        operation: &mut dyn Operation,
227    ) {
228        operation.container(None, layout.bounds());
229        operation.traverse(&mut |operation| {
230            self.children
231                .iter_mut()
232                .zip(&mut tree.children)
233                .zip(layout.children())
234                .for_each(|((child, state), layout)| {
235                    child
236                        .as_widget_mut()
237                        .operate(state, layout, renderer, operation);
238                });
239        });
240    }
241
242    fn update(
243        &mut self,
244        tree: &mut Tree,
245        event: &Event,
246        layout: Layout<'_>,
247        cursor: mouse::Cursor,
248        renderer: &Renderer,
249        shell: &mut Shell<'_, Message>,
250        viewport: &Rectangle,
251    ) {
252        for ((child, tree), layout) in self
253            .children
254            .iter_mut()
255            .zip(&mut tree.children)
256            .zip(layout.children())
257        {
258            child
259                .as_widget_mut()
260                .update(tree, event, layout, cursor, renderer, shell, viewport);
261        }
262    }
263
264    fn mouse_interaction(
265        &self,
266        tree: &Tree,
267        layout: Layout<'_>,
268        cursor: mouse::Cursor,
269        viewport: &Rectangle,
270        renderer: &Renderer,
271    ) -> mouse::Interaction {
272        self.children
273            .iter()
274            .zip(&tree.children)
275            .zip(layout.children())
276            .map(|((child, tree), layout)| {
277                child
278                    .as_widget()
279                    .mouse_interaction(tree, layout, cursor, viewport, renderer)
280            })
281            .max()
282            .unwrap_or_default()
283    }
284
285    fn draw(
286        &self,
287        tree: &Tree,
288        renderer: &mut Renderer,
289        theme: &Theme,
290        style: &renderer::Style,
291        layout: Layout<'_>,
292        cursor: mouse::Cursor,
293        viewport: &Rectangle,
294    ) {
295        if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
296            let viewport = if self.clip {
297                &clipped_viewport
298            } else {
299                viewport
300            };
301
302            for ((child, tree), layout) in self
303                .children
304                .iter()
305                .zip(&tree.children)
306                .zip(layout.children())
307                .filter(|(_, layout)| layout.bounds().intersects(viewport))
308            {
309                child
310                    .as_widget()
311                    .draw(tree, renderer, theme, style, layout, cursor, viewport);
312            }
313        }
314    }
315
316    fn overlay<'b>(
317        &'b mut self,
318        tree: &'b mut Tree,
319        layout: Layout<'b>,
320        renderer: &Renderer,
321        viewport: &Rectangle,
322        translation: Vector,
323    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
324        overlay::from_children(
325            &mut self.children,
326            tree,
327            layout,
328            renderer,
329            viewport,
330            translation,
331        )
332    }
333}
334
335impl<'a, Message, Theme, Renderer> From<Row<'a, Message, Theme, Renderer>>
336    for Element<'a, Message, Theme, Renderer>
337where
338    Message: 'a,
339    Theme: 'a,
340    Renderer: crate::core::Renderer + 'a,
341{
342    fn from(row: Row<'a, Message, Theme, Renderer>) -> Self {
343        Self::new(row)
344    }
345}
346
347/// A [`Row`] that wraps its contents.
348///
349/// Create a [`Row`] first, and then call [`Row::wrap`] to
350/// obtain a [`Row`] that wraps its contents.
351///
352/// The original alignment of the [`Row`] is preserved per row wrapped.
353pub struct Wrapping<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
354    row: Row<'a, Message, Theme, Renderer>,
355    vertical_spacing: Option<f32>,
356    align_x: alignment::Horizontal,
357}
358
359impl<Message, Theme, Renderer> Wrapping<'_, Message, Theme, Renderer> {
360    /// Sets the vertical spacing _between_ lines.
361    pub fn vertical_spacing(mut self, amount: impl Into<Pixels>) -> Self {
362        self.vertical_spacing = Some(amount.into().0);
363        self
364    }
365
366    /// Sets the horizontal alignment of the wrapping [`Row`].
367    pub fn align_x(mut self, align_x: impl Into<alignment::Horizontal>) -> Self {
368        self.align_x = align_x.into();
369        self
370    }
371}
372
373impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
374    for Wrapping<'_, Message, Theme, Renderer>
375where
376    Renderer: crate::core::Renderer,
377{
378    fn diff(&mut self, tree: &mut Tree) {
379        self.row.diff(tree);
380    }
381
382    fn size(&self) -> Size<Length> {
383        self.row.size()
384    }
385
386    fn layout(
387        &mut self,
388        tree: &mut Tree,
389        renderer: &Renderer,
390        limits: &layout::Limits,
391    ) -> layout::Node {
392        let limits = limits
393            .width(self.row.width)
394            .height(self.row.height)
395            .shrink(self.row.padding);
396
397        let child_limits = limits.loose();
398        let spacing = self.row.spacing;
399        let vertical_spacing = self.vertical_spacing.unwrap_or(spacing);
400        let max_width = limits.max().width;
401
402        let mut children: Vec<layout::Node> = Vec::new();
403        let mut intrinsic_size = Size::ZERO;
404        let mut row_start = 0;
405        let mut row_height = 0.0;
406        let mut x = 0.0;
407        let mut y = 0.0;
408
409        let align_factor = match self.row.align {
410            Alignment::Start => 0.0,
411            Alignment::Center => 2.0,
412            Alignment::End => 1.0,
413        };
414
415        let align_y = |row_start: std::ops::Range<usize>,
416                       row_height: f32,
417                       children: &mut Vec<layout::Node>| {
418            if align_factor != 0.0 {
419                for node in &mut children[row_start] {
420                    let height = node.size().height;
421
422                    node.translate_mut(Vector::new(0.0, (row_height - height) / align_factor));
423                }
424            }
425        };
426
427        for (i, child) in self.row.children.iter_mut().enumerate() {
428            let node = child
429                .as_widget_mut()
430                .layout(&mut tree.children[i], renderer, &child_limits);
431
432            let child_size = node.size();
433
434            if x != 0.0 && x + child_size.width > max_width {
435                intrinsic_size.width = intrinsic_size.width.max(x - spacing);
436
437                align_y(row_start..i, row_height, &mut children);
438
439                y += row_height + vertical_spacing;
440                x = 0.0;
441                row_start = i;
442                row_height = 0.0;
443            }
444
445            row_height = row_height.max(child_size.height);
446
447            children.push(node.move_to((x + self.row.padding.left, y + self.row.padding.top)));
448
449            x += child_size.width + spacing;
450        }
451
452        if x != 0.0 {
453            intrinsic_size.width = intrinsic_size.width.max(x - spacing);
454        }
455
456        intrinsic_size.height = y + row_height;
457        align_y(row_start..children.len(), row_height, &mut children);
458
459        let align_factor = match self.align_x {
460            alignment::Horizontal::Left => 0.0,
461            alignment::Horizontal::Center => 2.0,
462            alignment::Horizontal::Right => 1.0,
463        };
464
465        if align_factor != 0.0 {
466            let total_width = intrinsic_size.width;
467
468            let mut row_start = 0;
469
470            for i in 0..children.len() {
471                let bounds = children[i].bounds();
472                let row_width = bounds.x + bounds.width;
473
474                let next_x = children
475                    .get(i + 1)
476                    .map(|node| node.bounds().x)
477                    .unwrap_or_default();
478
479                if next_x == 0.0 {
480                    let translation = Vector::new((total_width - row_width) / align_factor, 0.0);
481
482                    for node in &mut children[row_start..=i] {
483                        node.translate_mut(translation);
484                    }
485
486                    row_start = i + 1;
487                }
488            }
489        }
490
491        let size = limits.resolve(self.row.width, self.row.height, intrinsic_size);
492
493        layout::Node::with_children(size.expand(self.row.padding), children)
494    }
495
496    fn operate(
497        &mut self,
498        tree: &mut Tree,
499        layout: Layout<'_>,
500        renderer: &Renderer,
501        operation: &mut dyn Operation,
502    ) {
503        self.row.operate(tree, layout, renderer, operation);
504    }
505
506    fn update(
507        &mut self,
508        tree: &mut Tree,
509        event: &Event,
510        layout: Layout<'_>,
511        cursor: mouse::Cursor,
512        renderer: &Renderer,
513        shell: &mut Shell<'_, Message>,
514        viewport: &Rectangle,
515    ) {
516        self.row
517            .update(tree, event, layout, cursor, renderer, shell, viewport);
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}