diff --git a/src/main.rs b/src/main.rs index 6c2d34a..6add5f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,6 +47,7 @@ use std::{ mod view; mod wayland; +mod widgets; // Include `pid` in mime. Want to drag between our surfaces, but not another // process, if we use Wayland object ids. @@ -489,7 +490,16 @@ impl Application for App { } } } - Msg::CloseWorkspace(_workspace_handle) => {} + Msg::CloseWorkspace(_workspace_handle) => { + // XXX close specific workspace + if let WorkspaceAmount::Static(n) = &mut self.conf.workspace_config.workspace_amount + { + *n -= 1; + self.conf + .cosmic_comp_config + .set("workspaces", &self.conf.workspace_config); + } + } Msg::CloseToplevel(toplevel_handle) => { // TODO confirmation? if let Some(toplevel_manager) = self.toplevel_manager.as_ref() { diff --git a/src/view/mod.rs b/src/view/mod.rs index be6eccb..d82919c 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -6,6 +6,7 @@ use cosmic::{ cctk, iced::{ self, + advanced::layout::flex::Axis, widget::{column, row}, }, widget, @@ -67,15 +68,13 @@ pub(crate) fn workspace_item<'a>( workspace: &'a Workspace, output: &wl_output::WlOutput, ) -> cosmic::Element<'a, Msg> { + let image = capture_image(workspace.img_for_output.get(output)); column![ close_button(Msg::CloseWorkspace(workspace.handle.clone())), - widget::button(column![ - capture_image(workspace.img_for_output.get(output)), - widget::text(&workspace.name) - ]) - .selected(workspace.is_active) - .style(cosmic::theme::Button::Image) - .on_press(Msg::ActivateWorkspace(workspace.handle.clone())), + widget::button(column![image, widget::text(&workspace.name)]) + .selected(workspace.is_active) + .style(cosmic::theme::Button::Image) + .on_press(Msg::ActivateWorkspace(workspace.handle.clone())), ] .height(iced::Length::Fill) .into() @@ -126,10 +125,11 @@ fn workspaces_sidebar<'a>( .into(), ); } - let sidebar_entries_container: cosmic::Element<'_, _> = match layout { - WorkspaceLayout::Vertical => column(sidebar_entries).into(), - WorkspaceLayout::Horizontal => row(sidebar_entries).into(), + let axis = match layout { + WorkspaceLayout::Vertical => Axis::Vertical, + WorkspaceLayout::Horizontal => Axis::Horizontal, }; + let sidebar_entries_container = crate::widgets::workspace_bar(sidebar_entries, axis); widget::container(sidebar_entries_container) .width(iced::Length::Fill) .height(iced::Length::Fill) diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs new file mode 100644 index 0000000..a08542e --- /dev/null +++ b/src/widgets/mod.rs @@ -0,0 +1,69 @@ +use cosmic::iced::{ + self, + advanced::{layout, mouse, renderer, widget::Tree, Layout, Widget}, + Length, Rectangle, +}; +use std::marker::PhantomData; + +mod workspace_bar; +pub use workspace_bar::workspace_bar; + +pub fn layout_wrapper>(inner: T) -> LayoutWrapper { + LayoutWrapper { + inner, + _msg: PhantomData, + } +} + +pub struct LayoutWrapper> { + inner: T, + _msg: PhantomData, +} + +impl> Widget + for LayoutWrapper +{ + fn width(&self) -> Length { + self.inner.width() + } + + fn height(&self) -> Length { + self.inner.height() + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &cosmic::Renderer, + limits: &layout::Limits, + ) -> layout::Node { + dbg!(limits); + dbg!(self.inner.layout(tree, renderer, limits)) + } + + fn draw( + &self, + state: &Tree, + renderer: &mut cosmic::Renderer, + theme: &cosmic::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.inner + .draw(state, renderer, theme, style, layout, cursor, viewport) + } + + fn children(&self) -> Vec { + self.inner.children() + } +} + +impl<'a, Msg: 'a, T: Widget + 'a> From> + for cosmic::Element<'a, Msg> +{ + fn from(widget: LayoutWrapper) -> Self { + cosmic::Element::new(widget) + } +} diff --git a/src/widgets/workspace_bar.rs b/src/widgets/workspace_bar.rs new file mode 100644 index 0000000..7bf40b4 --- /dev/null +++ b/src/widgets/workspace_bar.rs @@ -0,0 +1,227 @@ +// Custom varian of row/column + +use cosmic::iced::{ + self, + 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 workspace_bar<'a, Msg>( + children: Vec>, + axis: Axis, +) -> WorkspaceBar<'a, Msg> { + WorkspaceBar { + axis, + children, + _msg: PhantomData, + } +} + +pub struct WorkspaceBar<'a, Msg> { + axis: Axis, + children: Vec>, + _msg: PhantomData, +} + +impl<'a, Msg> Widget for WorkspaceBar<'a, Msg> { + fn width(&self) -> Length { + Length::Shrink + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &cosmic::Renderer, + limits: &layout::Limits, + ) -> layout::Node { + /* + layout::flex::resolve( + layout::flex::Axis::Vertical, + renderer, + &limits, + iced::Padding::ZERO, + 0.0, // spacing + iced::Alignment::Start, + &self.children, + &mut tree.children, + ) + */ + + if self.children.is_empty() { + return layout::Node::new(limits.min()); + } + + let max_main = self.axis.main(limits.max()) / self.children().len() as f32; + let max_cross = self.axis.cross(limits.max()); + let mut total_main = 0.0; + let nodes = self + .children + .iter() + .zip(tree.children.iter_mut()) + .map(|(child, tree)| { + 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); + let (x, y) = self.axis.pack(total_main, 0.0); + 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); + 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: WorkspaceBar<'a, Msg>) -> Self { + cosmic::Element::new(widget) + } +}