//! Keyed columns distribute content vertically while keeping continuity. use crate::core::layout; use crate::core::mouse; use crate::core::overlay; use crate::core::renderer; use crate::core::widget::Operation; use crate::core::widget::tree::{self, Tree}; use crate::core::{ Alignment, Clipboard, Element, Event, Layout, Length, Padding, Pixels, Rectangle, Shell, Size, Vector, Widget, }; /// A container that distributes its contents vertically while keeping continuity. /// /// # 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::{keyed_column, text}; /// /// enum Message { /// // ... /// } /// /// fn view(state: &State) -> Element<'_, Message> { /// keyed_column((0..=100).map(|i| { /// (i, text!("Item {i}").into()) /// })).into() /// } /// ``` #[allow(missing_debug_implementations)] pub struct Column< 'a, Key, Message, Theme = crate::Theme, Renderer = crate::Renderer, > where Key: Copy + PartialEq, { spacing: f32, padding: Padding, width: Length, height: Length, max_width: f32, align_items: Alignment, keys: Vec, children: Vec>, } impl<'a, Key, Message, Theme, Renderer> Column<'a, Key, Message, Theme, Renderer> where Key: Copy + PartialEq, Renderer: crate::core::Renderer, { /// Creates an empty [`Column`]. pub fn new() -> Self { Self::from_vecs(Vec::new(), Vec::new()) } /// Creates a [`Column`] from already allocated [`Vec`]s. /// /// Keep in mind that the [`Column`] will not inspect the [`Vec`]s, 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 [`Column::width`] or [`Column::height`] accordingly. pub fn from_vecs( keys: Vec, children: Vec>, ) -> Self { Self { spacing: 0.0, padding: Padding::ZERO, width: Length::Shrink, height: Length::Shrink, max_width: f32::INFINITY, align_items: Alignment::Start, keys, children, } } /// Creates a [`Column`] with the given capacity. pub fn with_capacity(capacity: usize) -> Self { Self::from_vecs( Vec::with_capacity(capacity), Vec::with_capacity(capacity), ) } /// Creates a [`Column`] with the given elements. pub fn with_children( children: impl IntoIterator< Item = (Key, Element<'a, Message, Theme, Renderer>), >, ) -> Self { let iterator = children.into_iter(); Self::with_capacity(iterator.size_hint().0).extend(iterator) } /// Sets the vertical 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 [`Column`]. pub fn padding>(mut self, padding: P) -> Self { self.padding = padding.into(); self } /// Sets the width of the [`Column`]. pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); self } /// Sets the height of the [`Column`]. pub fn height(mut self, height: impl Into) -> Self { self.height = height.into(); self } /// Sets the maximum width of the [`Column`]. pub fn max_width(mut self, max_width: impl Into) -> Self { self.max_width = max_width.into().0; self } /// Sets the horizontal alignment of the contents of the [`Column`] . pub fn align_items(mut self, align: Alignment) -> Self { self.align_items = align; self } /// Adds an element to the [`Column`]. pub fn push( mut self, key: Key, 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.keys.push(key); self.children.push(child); self } /// Adds an element to the [`Column`], if `Some`. pub fn push_maybe( self, key: Key, child: Option>>, ) -> Self { if let Some(child) = child { self.push(key, child) } else { self } } /// Extends the [`Column`] with the given children. pub fn extend( self, children: impl IntoIterator< Item = (Key, Element<'a, Message, Theme, Renderer>), >, ) -> Self { children .into_iter() .fold(self, |column, (key, child)| column.push(key, child)) } } impl Default for Column<'_, Key, Message, Renderer> where Key: Copy + PartialEq, Renderer: crate::core::Renderer, { fn default() -> Self { Self::new() } } struct State where Key: Copy + PartialEq, { keys: Vec, } impl Widget for Column<'_, Key, Message, Theme, Renderer> where Renderer: crate::core::Renderer, Key: Copy + PartialEq + 'static, { fn tag(&self) -> tree::Tag { tree::Tag::of::>() } fn state(&self) -> tree::State { tree::State::new(State { keys: self.keys.clone(), }) } fn children(&self) -> Vec { self.children.iter().map(Tree::new).collect() } fn diff(&self, tree: &mut Tree) { let Tree { state, children, .. } = tree; let state = state.downcast_mut::>(); tree::diff_children_custom_with_search( children, &self.children, |tree, child| child.as_widget().diff(tree), |index| { self.keys.get(index).or_else(|| self.keys.last()).copied() != Some(state.keys[index]) }, |child| Tree::new(child.as_widget()), ); if state.keys != self.keys { state.keys.clone_from(&self.keys); } } fn size(&self) -> Size { Size { width: self.width, height: self.height, } } fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { let limits = limits .max_width(self.max_width) .width(self.width) .height(self.height); layout::flex::resolve( layout::flex::Axis::Vertical, renderer, &limits, self.width, self.height, self.padding, self.spacing, self.align_items, &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, ) { for ((child, state), layout) in self .children .iter() .zip(&tree.children) .zip(layout.children()) { 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, Key, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Key: Copy + PartialEq + 'static, Message: 'a, Theme: 'a, Renderer: crate::core::Renderer + 'a, { fn from(column: Column<'a, Key, Message, Theme, Renderer>) -> Self { Self::new(column) } }