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