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