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