From b5d2eed7265a736d6dd88a2760e7c4fa4a432413 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 6 Feb 2025 15:09:57 -0800 Subject: [PATCH] toplevel_layout: Add `AxisToplevelLayout` helper --- src/widgets/toplevels/mod.rs | 1 + .../toplevel_layout/axis_toplevel_layout.rs | 98 +++++++++++++++++++ src/widgets/toplevels/toplevel_layout/mod.rs | 6 +- .../row_col_toplevel_layout.rs | 66 +++++++------ .../toplevels/toplevel_layout/utils.rs | 31 ------ 5 files changed, 136 insertions(+), 66 deletions(-) create mode 100644 src/widgets/toplevels/toplevel_layout/axis_toplevel_layout.rs delete mode 100644 src/widgets/toplevels/toplevel_layout/utils.rs diff --git a/src/widgets/toplevels/mod.rs b/src/widgets/toplevels/mod.rs index 08a19ca..379214f 100644 --- a/src/widgets/toplevels/mod.rs +++ b/src/widgets/toplevels/mod.rs @@ -65,6 +65,7 @@ impl Widget for Toplevels<'_, Msg> { .zip(tree.children.iter_mut()) .zip(assigned_rects) .map(|((child, tree), assigned_rect)| { + dbg!(assigned_rect); let child_limits = layout::Limits::new(Size::ZERO, assigned_rect.size()); let layout = child.as_widget().layout(tree, renderer, &child_limits); diff --git a/src/widgets/toplevels/toplevel_layout/axis_toplevel_layout.rs b/src/widgets/toplevels/toplevel_layout/axis_toplevel_layout.rs new file mode 100644 index 0000000..e349766 --- /dev/null +++ b/src/widgets/toplevels/toplevel_layout/axis_toplevel_layout.rs @@ -0,0 +1,98 @@ +use cosmic::iced::{advanced::layout::flex::Axis, Length, Point, Rectangle, Size}; +use std::marker::PhantomData; + +use super::{LayoutToplevel, ToplevelLayout}; + +#[derive(Debug, Copy, Clone)] +pub struct AxisPoint { + pub main: f32, + pub cross: f32, +} + +impl AxisPoint { + fn pack(self, axis: &Axis) -> Point { + match axis { + Axis::Horizontal => Point::new(self.main, self.cross), + Axis::Vertical => Point::new(self.cross, self.main), + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct AxisSize { + pub main: T, + pub cross: T, +} + +impl AxisSize { + fn unpack(axis: &Axis, size: Size) -> Self { + match axis { + Axis::Horizontal => AxisSize { + main: size.width, + cross: size.height, + }, + Axis::Vertical => AxisSize { + main: size.height, + cross: size.width, + }, + } + } + + fn pack(self, axis: &Axis) -> Size { + match axis { + Axis::Horizontal => Size::new(self.main, self.cross), + Axis::Vertical => Size::new(self.cross, self.main), + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct AxisRectangle { + pub origin: AxisPoint, + pub size: AxisSize, +} + +impl AxisRectangle { + pub fn new(origin: AxisPoint, size: AxisSize) -> Self { + Self { origin, size } + } + + fn pack(self, axis: &Axis) -> Rectangle { + Rectangle::new(self.origin.pack(axis), self.size.pack(axis)) + } +} + +/// Helper to implement [`ToplevelLayout`] for layouts based on an `[Axis]`, +/// that care only about main vs cross, and not width/height. +pub trait AxisToplevelLayout { + fn axis(&self) -> &Axis; + fn size(&self) -> AxisSize; + fn layout( + &self, + max_limit: AxisSize, + toplevels: Vec>, + ) -> impl Iterator; +} + +impl ToplevelLayout for T { + fn size(&self) -> Size { + self.size().pack(self.axis()) + } + + fn layout( + &self, + max_limit: Size, + toplevels: &[LayoutToplevel<'_>], + ) -> impl Iterator { + let max_limit = AxisSize::unpack(self.axis(), max_limit); + let toplevels = toplevels + .into_iter() + .map(|t| LayoutToplevel { + preferred_size: AxisSize::unpack(self.axis(), t.preferred_size), + _phantom_data: PhantomData, + }) + .collect::>(); + self.layout(max_limit, toplevels) + .map(|rect| rect.pack(self.axis())) + } +} diff --git a/src/widgets/toplevels/toplevel_layout/mod.rs b/src/widgets/toplevels/toplevel_layout/mod.rs index a6cf285..e0cc75d 100644 --- a/src/widgets/toplevels/toplevel_layout/mod.rs +++ b/src/widgets/toplevels/toplevel_layout/mod.rs @@ -4,15 +4,15 @@ use cosmic::iced::{Length, Rectangle, Size}; use std::marker::PhantomData; +mod axis_toplevel_layout; mod row_col_toplevel_layout; -mod utils; pub(crate) use row_col_toplevel_layout::RowColToplevelLayout; #[derive(Debug)] -pub(crate) struct LayoutToplevel<'a> { +pub(crate) struct LayoutToplevel<'a, S = Size> { //toplevel: &'a crate::Toplevel, /// Preferred size of the child widget, if it fill the parent container - pub preferred_size: Size, + pub preferred_size: S, pub _phantom_data: PhantomData<&'a crate::Toplevel>, } diff --git a/src/widgets/toplevels/toplevel_layout/row_col_toplevel_layout.rs b/src/widgets/toplevels/toplevel_layout/row_col_toplevel_layout.rs index ee51f53..4117d2e 100644 --- a/src/widgets/toplevels/toplevel_layout/row_col_toplevel_layout.rs +++ b/src/widgets/toplevels/toplevel_layout/row_col_toplevel_layout.rs @@ -1,6 +1,10 @@ -use cosmic::iced::{advanced::layout::flex::Axis, Length, Point, Rectangle, Size}; +use cosmic::iced::{advanced::layout::flex::Axis, Length}; +use std::marker::PhantomData; -use super::{utils::AxisExt, LayoutToplevel, ToplevelLayout}; +use super::{ + axis_toplevel_layout::{AxisPoint, AxisRectangle, AxisSize, AxisToplevelLayout}, + LayoutToplevel, +}; pub(crate) struct RowColToplevelLayout { pub axis: Axis, @@ -9,57 +13,55 @@ pub(crate) struct RowColToplevelLayout { impl RowColToplevelLayout { // Get total requested main axis length if widget could have all the space - fn requested_main_total(&self, toplevels: &[LayoutToplevel<'_>]) -> f32 { + fn requested_main_total(&self, toplevels: &[LayoutToplevel<'_, AxisSize>]) -> f32 { let total_spacing = self.spacing as usize * (toplevels.len().saturating_sub(1)).max(0); - toplevels - .iter() - .map(|t| self.axis.main(t.preferred_size)) - .sum::() - + total_spacing as f32 + toplevels.iter().map(|t| t.preferred_size.main).sum::() + total_spacing as f32 } } -impl ToplevelLayout for RowColToplevelLayout { - fn size(&self) -> Size { - match self.axis { - Axis::Horizontal => Size { - width: Length::Fill, - height: Length::Shrink, - }, - Axis::Vertical => Size { - width: Length::Shrink, - height: Length::Fill, - }, +impl AxisToplevelLayout for RowColToplevelLayout { + fn axis(&self) -> &Axis { + &self.axis + } + + fn size(&self) -> AxisSize { + AxisSize { + main: Length::Fill, + cross: Length::Shrink, } } fn layout( &self, - max_limit: Size, - toplevels: &[LayoutToplevel<'_>], - ) -> impl Iterator { - let requested_main_total = self.requested_main_total(toplevels); - let scale_factor = (self.axis.main(max_limit) / requested_main_total).min(1.0); - let max_cross = self.axis.cross(max_limit); + max_limit: AxisSize, + toplevels: Vec>, + ) -> impl Iterator { + let requested_main_total = self.requested_main_total(&toplevels); + let scale_factor = (max_limit.main / requested_main_total).min(1.0); // Add padding to center if total requested size doesn't fill available space - let padding = (self.axis.main(max_limit) - requested_main_total).max(0.) / 2.; + let padding = (max_limit.main - requested_main_total).max(0.) / 2.; let mut total_main = padding; let mut first = true; - toplevels.iter().map(move |child| { - let requested_main = self.axis.main(child.preferred_size); + toplevels.into_iter().map(move |child| { if !first { total_main += self.spacing as f32; } first = false; - let max_main = requested_main * scale_factor; + let max_main = child.preferred_size.main * scale_factor; - let (width, height) = self.axis.pack(max_main, max_cross); - let (x, y) = self.axis.pack(total_main, 0.0); + let main = total_main; total_main += max_main; - Rectangle::new(Point::new(x, y), Size::new(width, height)) + + AxisRectangle::new( + AxisPoint { main, cross: 0.0 }, + AxisSize { + main: max_main, + cross: max_limit.cross, + }, + ) }) } } diff --git a/src/widgets/toplevels/toplevel_layout/utils.rs b/src/widgets/toplevels/toplevel_layout/utils.rs deleted file mode 100644 index 5a4ee1d..0000000 --- a/src/widgets/toplevels/toplevel_layout/utils.rs +++ /dev/null @@ -1,31 +0,0 @@ -use cosmic::iced::{advanced::layout::flex::Axis, Size}; - -// Duplicate of private methods -pub(super) trait AxisExt { - fn main(&self, size: Size) -> f32; - fn cross(&self, size: Size) -> f32; - fn pack(&self, main: f32, cross: f32) -> (f32, f32); -} - -impl AxisExt for Axis { - fn main(&self, size: Size) -> f32 { - match self { - Axis::Horizontal => size.width, - Axis::Vertical => size.height, - } - } - - fn cross(&self, size: Size) -> f32 { - match self { - Axis::Horizontal => size.height, - Axis::Vertical => size.width, - } - } - - fn pack(&self, main: f32, cross: f32) -> (f32, f32) { - match self { - Axis::Horizontal => (main, cross), - Axis::Vertical => (cross, main), - } - } -}