use cosmic::iced::advanced::layout::flex::Axis; use cosmic::iced::advanced::layout::{self}; use cosmic::iced::advanced::widget::{Operation, Tree}; use cosmic::iced::advanced::{Clipboard, Layout, Shell, Widget, mouse, renderer}; use cosmic::iced::event::{self, Event}; use cosmic::iced::{Length, Rectangle, Size, Vector}; use std::marker::PhantomData; mod toplevel_layout; use toplevel_layout::{LayoutToplevel, ToplevelLayout, TwoRowColToplevelLayout}; pub fn toplevels(children: Vec>) -> Toplevels { Toplevels { // TODO configurable layout: TwoRowColToplevelLayout::new(Axis::Horizontal, 16), children, _msg: PhantomData, } } pub struct Toplevels<'a, Msg> { layout: TwoRowColToplevelLayout, children: Vec>, _msg: PhantomData, } impl Widget for Toplevels<'_, Msg> { fn size(&self) -> Size { self.layout.size() } fn layout( &mut self, tree: &mut Tree, renderer: &cosmic::Renderer, limits: &layout::Limits, ) -> layout::Node { // Call `.layout()` on each child with full limits to determine "preferred" sizes let layout_toplevels = self .children .iter_mut() .zip(tree.children.iter_mut()) .map(|(child, tree)| { let preferred_size = child.as_widget_mut().layout(tree, renderer, limits).size(); LayoutToplevel { preferred_size, _phantom_data: PhantomData, } }) .collect::>(); // Assign rectangles for each child using `ToplevelLayout` backend let assigned_rects = self.layout.layout(limits.max(), &layout_toplevels); let nodes = self .children .iter_mut() .zip(tree.children.iter_mut()) .zip(assigned_rects) .map(|((child, tree), assigned_rect)| { let child_limits = layout::Limits::new(Size::ZERO, assigned_rect.size()); let layout = child.as_widget_mut().layout(tree, renderer, &child_limits); // Center on both axes, if child didn't consume full size allocation let centering_offset = Vector::new( ((assigned_rect.size().width - layout.size().width) / 2.).max(0.), ((assigned_rect.size().height - layout.size().height) / 2.).max(0.), ); layout.move_to(assigned_rect.position() + centering_offset) }) .collect(); layout::Node::with_children(limits.max(), nodes) } fn operate( &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &cosmic::Renderer, operation: &mut dyn Operation<()>, ) { operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { self.children .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child .as_widget_mut() .operate(state, layout, renderer, operation); }); }); } fn update( &mut self, tree: &mut Tree, event: &Event, layout: Layout<'_>, cursor: mouse::Cursor, renderer: &cosmic::Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Msg>, 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: &cosmic::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 cosmic::Renderer, theme: &cosmic::Theme, style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, ) { if let Some(viewport) = layout.bounds().intersection(viewport) { 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 children(&self) -> Vec { self.children.iter().map(Tree::new).collect() } fn diff(&mut self, tree: &mut Tree) { tree.diff_children(&mut self.children); } } impl<'a, Msg: 'static> From> for cosmic::Element<'a, Msg> { fn from(widget: Toplevels<'a, Msg>) -> Self { cosmic::Element::new(widget) } }