// Copyright 2023 System76 // SPDX-License-Identifier: MPL-2.0 use crate::{Element, Renderer}; use derive_setters::Setters; use iced_core::event::{self, Event}; use iced_core::widget::{Operation, Tree}; use iced_core::{ layout, mouse, overlay, renderer, Clipboard, Layout, Length, Padding, Rectangle, Shell, Widget, }; use iced_renderer::core::widget::OperationOutputWrapper; /// Responsively generates rows and columns of widgets based on its dimmensions. #[derive(Setters)] #[must_use] pub struct FlexRow<'a, Message> { #[setters(skip)] children: Vec>, /// Sets the padding around the widget. padding: Padding, /// Sets the space between each column of items. column_spacing: u16, /// Sets the space between each item in a row. row_spacing: u16, /// Sets the width. width: Length, /// Sets the max width max_width: f32, } impl<'a, Message> FlexRow<'a, Message> { pub const fn new(children: Vec>) -> Self { Self { children, padding: Padding::ZERO, column_spacing: 4, row_spacing: 4, width: Length::Shrink, max_width: f32::INFINITY, } } } impl<'a, Message: 'static + Clone> Widget for FlexRow<'a, Message> { fn children(&self) -> Vec { self.children.iter().map(Tree::new).collect() } fn diff(&mut self, tree: &mut Tree) { tree.diff_children(self.children.as_mut_slice()); } fn width(&self) -> Length { self.width } fn height(&self) -> Length { Length::Shrink } fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node { let limits = limits .max_width(self.max_width) .width(self.width()) .height(self.height()); super::layout::resolve( renderer, &limits, &self.children, self.padding, f32::from(self.column_spacing), f32::from(self.row_spacing), ) } 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 on_event( &mut self, tree: &mut Tree, event: Event, layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { self.children .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .map(|((child, state), layout)| { child.as_widget_mut().on_event( state, event.clone(), layout, cursor, renderer, clipboard, shell, viewport, ) }) .fold(event::Status::Ignored, event::Status::merge) } 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: &crate::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, ) -> Option> { overlay::from_children(&mut self.children, tree, layout, renderer) } #[cfg(feature = "a11y")] /// get the a11y nodes for the widget fn a11y_nodes( &self, layout: Layout<'_>, state: &Tree, p: mouse::Cursor, ) -> iced_accessibility::A11yTree { use iced_accessibility::A11yTree; A11yTree::join( self.children .iter() .zip(layout.children()) .zip(state.children.iter()) .map(|((c, c_layout), state)| c.as_widget().a11y_nodes(c_layout, state, p)), ) } } impl<'a, Message: 'static + Clone> From> for Element<'a, Message> { fn from(flex_row: FlexRow<'a, Message>) -> Self { Self::new(flex_row) } }