//! Distribute content horizontally. use crate::core::alignment::{self, Alignment}; use crate::core::layout::{self, Layout}; use crate::core::mouse; use crate::core::overlay; use crate::core::renderer; use crate::core::widget::{Operation, Tree}; use crate::core::{ Clipboard, Element, Event, Length, Padding, Pixels, Rectangle, Shell, Size, Vector, Widget, }; /// A container that distributes its contents horizontally. /// /// # Example /// ```no_run /// # mod iced { pub mod widget { pub use iced_widget::*; } } /// # pub type State = (); /// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// use iced::widget::{button, row}; /// /// #[derive(Debug, Clone)] /// enum Message { /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// row![ /// "I am to the left!", /// button("I am in the middle!"), /// "I am to the right!", /// ].into() /// } /// ``` #[allow(missing_debug_implementations)] pub struct Row<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> { spacing: f32, padding: Padding, width: Length, height: Length, align: Alignment, clip: bool, children: Vec>, } impl<'a, Message, Theme, Renderer> Row<'a, Message, Theme, Renderer> where Renderer: crate::core::Renderer, { /// Creates an empty [`Row`]. pub fn new() -> Self { Self::from_vec(Vec::new()) } /// Creates a [`Row`] with the given capacity. pub fn with_capacity(capacity: usize) -> Self { Self::from_vec(Vec::with_capacity(capacity)) } /// Creates a [`Row`] with the given elements. pub fn with_children( children: impl IntoIterator>, ) -> Self { let iterator = children.into_iter(); Self::with_capacity(iterator.size_hint().0).extend(iterator) } /// Creates a [`Row`] from an already allocated [`Vec`]. /// /// Keep in mind that the [`Row`] will not inspect the [`Vec`], which means /// it won't automatically adapt to the sizing strategy of its contents. /// /// If any of the children have a [`Length::Fill`] strategy, you will need to /// call [`Row::width`] or [`Row::height`] accordingly. pub fn from_vec( children: Vec>, ) -> Self { Self { spacing: 0.0, padding: Padding::ZERO, width: Length::Shrink, height: Length::Shrink, align: Alignment::Start, clip: false, children, } } /// Sets the horizontal spacing _between_ elements. /// /// Custom margins per element do not exist in iced. You should use this /// method instead! While less flexible, it helps you keep spacing between /// elements consistent. pub fn spacing(mut self, amount: impl Into) -> Self { self.spacing = amount.into().0; self } /// Sets the [`Padding`] of the [`Row`]. pub fn padding>(mut self, padding: P) -> Self { self.padding = padding.into(); self } /// Sets the width of the [`Row`]. pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); self } /// Sets the height of the [`Row`]. pub fn height(mut self, height: impl Into) -> Self { self.height = height.into(); self } /// Sets the vertical alignment of the contents of the [`Row`] . pub fn align_y(mut self, align: impl Into) -> Self { self.align = Alignment::from(align.into()); self } /// Sets whether the contents of the [`Row`] should be clipped on /// overflow. pub fn clip(mut self, clip: bool) -> Self { self.clip = clip; self } /// Adds an [`Element`] to the [`Row`]. pub fn push( mut self, child: impl Into>, ) -> Self { let child = child.into(); let child_size = child.as_widget().size_hint(); self.width = self.width.enclose(child_size.width); self.height = self.height.enclose(child_size.height); self.children.push(child); self } /// Adds an element to the [`Row`], if `Some`. pub fn push_maybe( self, child: Option>>, ) -> Self { if let Some(child) = child { self.push(child) } else { self } } /// Extends the [`Row`] with the given children. pub fn extend( self, children: impl IntoIterator>, ) -> Self { children.into_iter().fold(self, Self::push) } /// Turns the [`Row`] into a [`Wrapping`] row. /// /// The original alignment of the [`Row`] is preserved per row wrapped. pub fn wrap(self) -> Wrapping<'a, Message, Theme, Renderer> { Wrapping { row: self } } } impl Default for Row<'_, Message, Renderer> where Renderer: crate::core::Renderer, { fn default() -> Self { Self::new() } } impl<'a, Message, Theme, Renderer: crate::core::Renderer> FromIterator> for Row<'a, Message, Theme, Renderer> { fn from_iter< T: IntoIterator>, >( iter: T, ) -> Self { Self::with_children(iter) } } impl Widget for Row<'_, Message, Theme, Renderer> where Renderer: crate::core::Renderer, { fn children(&self) -> Vec { self.children.iter().map(Tree::new).collect() } fn diff(&self, tree: &mut Tree) { tree.diff_children(&self.children); } fn size(&self) -> Size { Size { width: self.width, height: self.height, } } fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { layout::flex::resolve( layout::flex::Axis::Horizontal, renderer, limits, self.width, self.height, self.padding, self.spacing, self.align, &self.children, &mut tree.children, ) } fn operate( &self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { operation.container(None, layout.bounds(), &mut |operation| { self.children .iter() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child .as_widget() .operate(state, layout, renderer, operation); }); }); } fn update( &mut self, tree: &mut Tree, event: &Event, layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { for ((child, state), layout) in self .children .iter_mut() .zip(&mut tree.children) .zip(layout.children()) { child.as_widget_mut().update( state, event, layout, cursor, renderer, clipboard, shell, viewport, ); } } fn mouse_interaction( &self, tree: &Tree, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.children .iter() .zip(&tree.children) .zip(layout.children()) .map(|((child, state), layout)| { child.as_widget().mouse_interaction( state, layout, cursor, viewport, renderer, ) }) .max() .unwrap_or_default() } fn draw( &self, tree: &Tree, renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, ) { if let Some(clipped_viewport) = layout.bounds().intersection(viewport) { let viewport = if self.clip { &clipped_viewport } else { viewport }; for ((child, state), layout) in self .children .iter() .zip(&tree.children) .zip(layout.children()) .filter(|(_, layout)| layout.bounds().intersects(viewport)) { child.as_widget().draw( state, renderer, theme, style, layout, cursor, viewport, ); } } } fn overlay<'b>( &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, translation: Vector, ) -> Option> { overlay::from_children( &mut self.children, tree, layout, renderer, translation, ) } } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: 'a, Theme: 'a, Renderer: crate::core::Renderer + 'a, { fn from(row: Row<'a, Message, Theme, Renderer>) -> Self { Self::new(row) } } /// A [`Row`] that wraps its contents. /// /// Create a [`Row`] first, and then call [`Row::wrap`] to /// obtain a [`Row`] that wraps its contents. /// /// The original alignment of the [`Row`] is preserved per row wrapped. #[allow(missing_debug_implementations)] pub struct Wrapping< 'a, Message, Theme = crate::Theme, Renderer = crate::Renderer, > { row: Row<'a, Message, Theme, Renderer>, } impl Widget for Wrapping<'_, Message, Theme, Renderer> where Renderer: crate::core::Renderer, { fn children(&self) -> Vec { self.row.children() } fn diff(&self, tree: &mut Tree) { self.row.diff(tree); } fn size(&self) -> Size { self.row.size() } fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { let limits = limits .width(self.row.width) .height(self.row.height) .shrink(self.row.padding); let spacing = self.row.spacing; let max_width = limits.max().width; let mut children: Vec = Vec::new(); let mut intrinsic_size = Size::ZERO; let mut row_start = 0; let mut row_height = 0.0; let mut x = 0.0; let mut y = 0.0; let align_factor = match self.row.align { Alignment::Start => 0.0, Alignment::Center => 2.0, Alignment::End => 1.0, }; let align = |row_start: std::ops::Range, row_height: f32, children: &mut Vec| { if align_factor != 0.0 { for node in &mut children[row_start] { let height = node.size().height; node.translate_mut(Vector::new( 0.0, (row_height - height) / align_factor, )); } } }; for (i, child) in self.row.children.iter().enumerate() { let node = child.as_widget().layout( &mut tree.children[i], renderer, &limits, ); let child_size = node.size(); if x != 0.0 && x + child_size.width > max_width { intrinsic_size.width = intrinsic_size.width.max(x - spacing); align(row_start..i, row_height, &mut children); y += row_height + spacing; x = 0.0; row_start = i; row_height = 0.0; } row_height = row_height.max(child_size.height); children.push(node.move_to(( x + self.row.padding.left, y + self.row.padding.top, ))); x += child_size.width + spacing; } if x != 0.0 { intrinsic_size.width = intrinsic_size.width.max(x - spacing); } intrinsic_size.height = y + row_height; align(row_start..children.len(), row_height, &mut children); let size = limits.resolve(self.row.width, self.row.height, intrinsic_size); layout::Node::with_children(size.expand(self.row.padding), children) } fn operate( &self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { self.row.operate(tree, layout, renderer, operation); } fn update( &mut self, tree: &mut Tree, event: &Event, layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { self.row.update( tree, event, layout, cursor, renderer, clipboard, shell, viewport, ); } fn mouse_interaction( &self, tree: &Tree, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.row .mouse_interaction(tree, layout, cursor, viewport, renderer) } fn draw( &self, tree: &Tree, renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, ) { self.row .draw(tree, renderer, theme, style, layout, cursor, viewport); } fn overlay<'b>( &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, translation: Vector, ) -> Option> { self.row.overlay(tree, layout, renderer, translation) } } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: 'a, Theme: 'a, Renderer: crate::core::Renderer + 'a, { fn from(row: Wrapping<'a, Message, Theme, Renderer>) -> Self { Self::new(row) } }