diff --git a/src/view/mod.rs b/src/view/mod.rs index b2ba293..d8b3e07 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -261,12 +261,14 @@ fn toplevel_previews<'a>( let entries = toplevels .map(|t| toplevel_previews_entry(t, output)) .collect(); - row(entries) + //row(entries) + widget::container(crate::widgets::toplevels(entries)) + .align_x(iced::alignment::Horizontal::Center) .width(width) .height(height) - .spacing(16) + //.spacing(16) .padding(12) - .align_items(iced::Alignment::Center) + //.align_items(iced::Alignment::Center) .into() } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index ba50268..c6fb347 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -17,6 +17,8 @@ mod workspace_item; pub use workspace_item::workspace_item; mod mouse_interaction_wrapper; pub use mouse_interaction_wrapper::mouse_interaction_wrapper; +mod toplevels; +pub use toplevels::toplevels; trait Foo {} diff --git a/src/widgets/toplevels.rs b/src/widgets/toplevels.rs new file mode 100644 index 0000000..ed8f13b --- /dev/null +++ b/src/widgets/toplevels.rs @@ -0,0 +1,221 @@ +use cosmic::iced::{ + advanced::{ + layout::{self, flex::Axis}, + mouse, renderer, + widget::{Operation, OperationOutputWrapper, Tree}, + Clipboard, Layout, Shell, Widget, + }, + event::{self, Event}, + Length, Point, Rectangle, Size, +}; +use std::marker::PhantomData; + +// Duplicate of private methods +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), + } + } +} + +pub fn toplevels<'a, Msg>(children: Vec>) -> Toplevels<'a, Msg> { + Toplevels { + axis: Axis::Horizontal, + children, + _msg: PhantomData, + } +} + +pub struct Toplevels<'a, Msg> { + axis: Axis, + children: Vec>, + _msg: PhantomData, +} + +impl<'a, Msg> Widget for Toplevels<'a, Msg> { + fn width(&self) -> Length { + Length::Fill + } + + fn height(&self) -> Length { + // TODO Make depend on orientation or drop that option + Length::Shrink + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &cosmic::Renderer, + limits: &layout::Limits, + ) -> layout::Node { + // TODO configurable + let spacing = 16; + + // TODO scaling + let total_spacing = 0.max(spacing * (self.children.len() - 1)) as f32; + let max_main = + (self.axis.main(limits.max()) - total_spacing) / self.children().len() as f32; + let max_cross = self.axis.cross(limits.max()); + + // XXX sill allocating maximum main axis? + // - what was it doing before? + let mut total_main = 0.0; + let first = false; + let nodes = self + .children + .iter() + .zip(tree.children.iter_mut()) + .map(|(child, tree)| { + if !first { + total_main += spacing as f32; + } + let (max_width, max_height) = self.axis.pack(max_main, max_cross); + let child_limits = + layout::Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let mut layout = child.layout(tree, renderer, &child_limits); + // Center on cross axis + let cross = ((max_cross - self.axis.cross(layout.size())) / 2.).max(0.); + let (x, y) = self.axis.pack(total_main, cross); + layout.move_to(Point::new(x, y)); + total_main += self.axis.main(layout.size()); + layout + }) + .collect(); + + let (total_width, total_height) = self.axis.pack(total_main, max_cross); + let size = Size::new(total_width, total_height); + limits; + layout::Node::with_children(size, nodes) + } + + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &cosmic::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: &cosmic::Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Msg>, + 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: &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) + } +} diff --git a/src/widgets/workspace_item.rs b/src/widgets/workspace_item.rs index 1d9aca2..d196f7f 100644 --- a/src/widgets/workspace_item.rs +++ b/src/widgets/workspace_item.rs @@ -1,5 +1,7 @@ // TODO rename // combine widgets +// Hack: this widget defines it's width as the second child's width +// So the width of the image will be the overall width. use cosmic::iced::{ advanced::{ @@ -62,7 +64,10 @@ pub struct WorkspaceItem<'a, Msg> { impl<'a, Msg> Widget for WorkspaceItem<'a, Msg> { fn width(&self) -> Length { - Length::Fill + //Length::Fill + // XXX doesn't work when used in standard `row` widget + // But fixes allocation of `dnd_source` wrapping this, within `Workspaces` row + Length::Shrink } fn height(&self) -> Length {