From 7ab1f93acfbcca0801c5946378243b7f93116771 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 31 Mar 2025 11:47:50 -0700 Subject: [PATCH] Add and use `TwoRowColToplevelLayout` This tries to find a split point in the list of toplevels to maximize the scale factor when calling `RowColToplevelLayout` twice. If it doesn't get a better scale factor, it just uses a single row/column. Some things don't seem quite right, but the existing layout is not perfect, and this can help. Without the added cross axis spacing, there's overlap, so there may be something wrong with the requested sizes... --- src/widgets/toplevels/mod.rs | 6 +- src/widgets/toplevels/toplevel_layout/mod.rs | 2 + .../two_row_col_toplevel_layout.rs | 72 +++++++++++++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/widgets/toplevels/toplevel_layout/two_row_col_toplevel_layout.rs diff --git a/src/widgets/toplevels/mod.rs b/src/widgets/toplevels/mod.rs index 356f681..b92df11 100644 --- a/src/widgets/toplevels/mod.rs +++ b/src/widgets/toplevels/mod.rs @@ -11,19 +11,19 @@ use cosmic::iced::{ use std::marker::PhantomData; mod toplevel_layout; -use toplevel_layout::{LayoutToplevel, RowColToplevelLayout, ToplevelLayout}; +use toplevel_layout::{LayoutToplevel, ToplevelLayout, TwoRowColToplevelLayout}; pub fn toplevels(children: Vec>) -> Toplevels { Toplevels { // TODO configurable - layout: RowColToplevelLayout::new(Axis::Horizontal, 16), + layout: TwoRowColToplevelLayout::new(Axis::Horizontal, 16), children, _msg: PhantomData, } } pub struct Toplevels<'a, Msg> { - layout: RowColToplevelLayout, + layout: TwoRowColToplevelLayout, children: Vec>, _msg: PhantomData, } diff --git a/src/widgets/toplevels/toplevel_layout/mod.rs b/src/widgets/toplevels/toplevel_layout/mod.rs index e0cc75d..481f623 100644 --- a/src/widgets/toplevels/toplevel_layout/mod.rs +++ b/src/widgets/toplevels/toplevel_layout/mod.rs @@ -7,6 +7,8 @@ use std::marker::PhantomData; mod axis_toplevel_layout; mod row_col_toplevel_layout; pub(crate) use row_col_toplevel_layout::RowColToplevelLayout; +mod two_row_col_toplevel_layout; +pub(crate) use two_row_col_toplevel_layout::TwoRowColToplevelLayout; #[derive(Debug)] pub(crate) struct LayoutToplevel<'a, S = Size> { diff --git a/src/widgets/toplevels/toplevel_layout/two_row_col_toplevel_layout.rs b/src/widgets/toplevels/toplevel_layout/two_row_col_toplevel_layout.rs new file mode 100644 index 0000000..25ab152 --- /dev/null +++ b/src/widgets/toplevels/toplevel_layout/two_row_col_toplevel_layout.rs @@ -0,0 +1,72 @@ +use cosmic::iced::{advanced::layout::flex::Axis, Length}; + +use super::{ + axis_toplevel_layout::{AxisPoint, AxisRectangle, AxisSize, AxisToplevelLayout}, + row_col_toplevel_layout::RowColToplevelLayout, + LayoutToplevel, +}; + +pub(crate) struct TwoRowColToplevelLayout(RowColToplevelLayout); + +impl TwoRowColToplevelLayout { + pub fn new(axis: Axis, spacing: u32) -> Self { + Self(RowColToplevelLayout::new(axis, spacing)) + } +} + +impl AxisToplevelLayout for TwoRowColToplevelLayout { + fn axis(&self) -> &Axis { + &self.0.axis + } + + fn size(&self) -> AxisSize { + AxisSize { + main: Length::Fill, + cross: Length::Shrink, + } + } + + fn layout( + &self, + max_limit: AxisSize, + toplevels: &[LayoutToplevel<'_, AxisSize>], + ) -> impl Iterator { + let requested_main_total = self.0.requested_main_total(&toplevels); + let scale_factor = self.0.scale_factor(max_limit, toplevels); + + let half_max_limit = AxisSize { + main: max_limit.main, + cross: max_limit.cross / 2. - self.0.spacing as f32, + }; + + // See if two row layout is better + // TODO not a good fix if there is a large window and many smaller ones? + if requested_main_total > max_limit.main && toplevels.len() > 1 { + // decide best way to partition list + let (split_point, two_row_scale_factor) = (1..toplevels.len()) + .map(|i| { + let (top_row, bottom_row) = toplevels.split_at(i); + let top_scale_factor = self.0.scale_factor(half_max_limit, top_row); + let bottom_scale_factor = self.0.scale_factor(half_max_limit, bottom_row); + (i, top_scale_factor.min(bottom_scale_factor)) + }) + .max_by(|(_, scale1), (_, scale2)| scale1.total_cmp(scale2)) + .unwrap(); + // Better layout + if two_row_scale_factor > scale_factor { + // TODO padding + let row1 = self.0.layout(half_max_limit, &toplevels[..split_point]); + let row2 = self + .0 + .layout(half_max_limit, &toplevels[split_point..]) + .map(move |mut rect| { + rect.origin.cross += max_limit.cross / 2. + self.0.spacing as f32; + rect + }); + return itertools::Either::Left(row1.chain(row2)); + } + } + + itertools::Either::Right(self.0.layout(max_limit, toplevels)) + } +}