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