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