improv(view): make workspace item sizing more dynamic
This makes the `workspace_bar` more responsive to different child sizes, which enables fixing the size of the smaller dimension of the screencopy, while allowing it to expand unrestricted in the larger dimension (to match the aspect ratio).
This commit is contained in:
parent
d4ddae7799
commit
2b7a8b133e
5 changed files with 322 additions and 289 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// Coppied from cosmic-app-list
|
||||
// - Put in a library? libcosmic?
|
||||
|
||||
use freedesktop_desktop_entry::DesktopEntry;
|
||||
use cosmic::desktop::fde;
|
||||
use itertools::Itertools;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
|
@ -47,9 +47,9 @@ fn default_app_icon() -> PathBuf {
|
|||
|
||||
fn desktop_info_for_app_ids(mut app_ids: Vec<String>) -> Vec<DesktopInfo> {
|
||||
let app_ids_clone = app_ids.clone();
|
||||
let mut ret = freedesktop_desktop_entry::Iter::new(freedesktop_desktop_entry::default_paths())
|
||||
let mut ret = fde::Iter::new(fde::default_paths())
|
||||
.filter_map(|path| {
|
||||
DesktopEntry::from_path::<String>(path.clone(), None)
|
||||
fde::DesktopEntry::from_path::<String>(path.clone(), None)
|
||||
.ok()
|
||||
.and_then(|de| {
|
||||
if let Some(i) = app_ids.iter().position(|s| {
|
||||
|
|
|
|||
146
src/view/mod.rs
146
src/view/mod.rs
|
|
@ -8,7 +8,7 @@ use cosmic::{
|
|||
advanced::layout::flex::Axis,
|
||||
clipboard::mime::AllowedMimeTypes,
|
||||
widget::{column, row},
|
||||
Border,
|
||||
Border, Length,
|
||||
},
|
||||
iced_core::{text::Wrapping, Shadow},
|
||||
iced_winit::platform_specific::wayland::subsurface_widget::Subsurface,
|
||||
|
|
@ -99,14 +99,14 @@ pub(crate) fn layer_surface<'a>(
|
|||
WorkspaceLayout::Vertical => widget::layer_container(
|
||||
row![sidebar, toplevels]
|
||||
.spacing(12)
|
||||
.height(iced::Length::Fill)
|
||||
.width(iced::Length::Fill),
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill),
|
||||
),
|
||||
WorkspaceLayout::Horizontal => widget::layer_container(
|
||||
column![sidebar, toplevels]
|
||||
.spacing(12)
|
||||
.height(iced::Length::Fill)
|
||||
.width(iced::Length::Fill),
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill),
|
||||
),
|
||||
};
|
||||
let output = surface.output.clone();
|
||||
|
|
@ -147,40 +147,90 @@ fn workspace_item_appearance(
|
|||
fn workspace_item<'a>(
|
||||
workspace: &'a Workspace,
|
||||
_output: &wl_output::WlOutput,
|
||||
layout: WorkspaceLayout,
|
||||
is_drop_target: bool,
|
||||
) -> cosmic::Element<'static, Msg> {
|
||||
let image = capture_image(workspace.img.as_ref(), 1.0);
|
||||
let (image, image_height) = if let Some(img) = workspace.img.as_ref() {
|
||||
let is_rotated = matches!(
|
||||
img.transform,
|
||||
wl_output::Transform::_90
|
||||
| wl_output::Transform::_270
|
||||
| wl_output::Transform::Flipped90
|
||||
| wl_output::Transform::Flipped270
|
||||
);
|
||||
let (effective_width, effective_height) = if is_rotated {
|
||||
// If rotated, swap width and height
|
||||
(img.height, img.width)
|
||||
} else {
|
||||
(img.width, img.height)
|
||||
};
|
||||
|
||||
let fixed_size = 126.0;
|
||||
if effective_width > effective_height {
|
||||
(
|
||||
// Landscape: fix height
|
||||
widget::container(capture_image(Some(img), 1.0)).max_height(fixed_size),
|
||||
fixed_size,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
// Portrait: fix width
|
||||
widget::container(capture_image(Some(img), 1.0)).max_width(fixed_size),
|
||||
fixed_size * effective_height as f32 / effective_width as f32,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(
|
||||
widget::container(capture_image(None, 1.0))
|
||||
.max_width(224.0)
|
||||
.max_height(126.0),
|
||||
126.0,
|
||||
)
|
||||
};
|
||||
|
||||
let workspace_name = widget::text::body(fl!(
|
||||
"workspace",
|
||||
HashMap::from([("number", &workspace.name)])
|
||||
));
|
||||
|
||||
// Needed to prevent text getting pushed out when scaling on Vertical layout
|
||||
let content = match layout {
|
||||
WorkspaceLayout::Horizontal => column![image, workspace_name]
|
||||
.align_x(iced::Alignment::Center)
|
||||
.spacing(4)
|
||||
.apply(widget::container),
|
||||
WorkspaceLayout::Vertical => column![image.height(Length::Fill), workspace_name]
|
||||
.align_x(iced::Alignment::Center)
|
||||
.spacing(4)
|
||||
.apply(widget::container)
|
||||
.max_height(image_height + 21.0 + 4.0), // text height + spacing
|
||||
};
|
||||
|
||||
let is_active = workspace.is_active;
|
||||
// TODO editable name?
|
||||
widget::button::custom(
|
||||
column![
|
||||
image,
|
||||
widget::text::body(fl!(
|
||||
"workspace",
|
||||
HashMap::from([("number", &workspace.name)])
|
||||
))
|
||||
]
|
||||
.align_x(iced::Alignment::Center)
|
||||
.spacing(4),
|
||||
)
|
||||
.selected(workspace.is_active)
|
||||
.class(cosmic::theme::Button::Custom {
|
||||
active: Box::new(move |_focused, theme| {
|
||||
workspace_item_appearance(theme, is_active, is_drop_target)
|
||||
}),
|
||||
disabled: Box::new(|_theme| unreachable!()),
|
||||
hovered: Box::new(move |_focused, theme| workspace_item_appearance(theme, is_active, true)),
|
||||
pressed: Box::new(move |_focused, theme| workspace_item_appearance(theme, is_active, true)),
|
||||
})
|
||||
.on_press(Msg::ActivateWorkspace(workspace.handle.clone()))
|
||||
.padding(8)
|
||||
.width(iced::Length::Fixed(240.0))
|
||||
.into()
|
||||
widget::button::custom(content)
|
||||
.selected(workspace.is_active)
|
||||
.class(cosmic::theme::Button::Custom {
|
||||
active: Box::new(move |_focused, theme| {
|
||||
workspace_item_appearance(theme, is_active, is_drop_target)
|
||||
}),
|
||||
disabled: Box::new(|_theme| unreachable!()),
|
||||
hovered: Box::new(move |_focused, theme| {
|
||||
workspace_item_appearance(theme, is_active, true)
|
||||
}),
|
||||
pressed: Box::new(move |_focused, theme| {
|
||||
workspace_item_appearance(theme, is_active, true)
|
||||
}),
|
||||
})
|
||||
.on_press(Msg::ActivateWorkspace(workspace.handle.clone()))
|
||||
.padding(8)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn workspace_sidebar_entry<'a>(
|
||||
workspace: &'a Workspace,
|
||||
output: &'a wl_output::WlOutput,
|
||||
layout: WorkspaceLayout,
|
||||
is_drop_target: bool,
|
||||
) -> cosmic::Element<'a, Msg> {
|
||||
/* XXX
|
||||
|
|
@ -190,7 +240,7 @@ fn workspace_sidebar_entry<'a>(
|
|||
iced::mouse::Interaction::Idle
|
||||
};
|
||||
*/
|
||||
let item = workspace_item(workspace, output, is_drop_target);
|
||||
let item = workspace_item(workspace, output, layout, is_drop_target);
|
||||
/* TODO allow moving workspaces (needs compositor support)
|
||||
let workspace_clone = workspace.clone(); // TODO avoid clone
|
||||
let output_clone = output.clone();
|
||||
|
|
@ -227,19 +277,15 @@ fn workspaces_sidebar<'a>(
|
|||
drop_target: Option<&backend::ExtWorkspaceHandleV1>,
|
||||
) -> cosmic::Element<'a, Msg> {
|
||||
let sidebar_entries = workspaces
|
||||
.map(|w| workspace_sidebar_entry(w, output, drop_target == Some(&w.handle)))
|
||||
.map(|w| workspace_sidebar_entry(w, output, layout, drop_target == Some(&w.handle)))
|
||||
.collect();
|
||||
let axis = match layout {
|
||||
WorkspaceLayout::Vertical => Axis::Vertical,
|
||||
WorkspaceLayout::Horizontal => Axis::Horizontal,
|
||||
let (axis, width, height) = match layout {
|
||||
WorkspaceLayout::Vertical => (Axis::Vertical, Length::Shrink, Length::Fill),
|
||||
WorkspaceLayout::Horizontal => (Axis::Horizontal, Length::Fill, Length::Shrink),
|
||||
};
|
||||
let sidebar_entries_container =
|
||||
widget::container(crate::widgets::workspace_bar(sidebar_entries, axis)).padding(8.0);
|
||||
|
||||
let (width, height) = match layout {
|
||||
WorkspaceLayout::Vertical => (iced::Length::Fixed(256.0), iced::Length::Shrink),
|
||||
WorkspaceLayout::Horizontal => (iced::Length::Shrink, iced::Length::Fill),
|
||||
};
|
||||
widget::container(
|
||||
widget::container(sidebar_entries_container)
|
||||
.width(width)
|
||||
|
|
@ -304,8 +350,8 @@ fn toplevel_preview(toplevel: &Toplevel, is_being_dragged: bool) -> cosmic::Elem
|
|||
}
|
||||
}))
|
||||
.apply(widget::container)
|
||||
.width(iced::Length::FillPortion(5)),
|
||||
widget::horizontal_space().width(iced::Length::Fixed(8.0)),
|
||||
.width(Length::FillPortion(5)),
|
||||
widget::horizontal_space().width(Length::Fixed(8.0)),
|
||||
close_button(Msg::CloseToplevel(toplevel.handle.clone()))
|
||||
]
|
||||
.padding([0, 0, 4, 0])
|
||||
|
|
@ -326,7 +372,7 @@ fn toplevel_preview(toplevel: &Toplevel, is_being_dragged: bool) -> cosmic::Elem
|
|||
)
|
||||
//.spacing(4)
|
||||
//.align_items(iced::Alignment::Center)
|
||||
//.width(iced::Length::Fill)
|
||||
//.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
|
|
@ -367,8 +413,8 @@ fn toplevel_previews<'a>(
|
|||
drag_toplevel: Option<&'a backend::ExtForeignToplevelHandleV1>,
|
||||
) -> cosmic::Element<'a, Msg> {
|
||||
let (width, height) = match layout {
|
||||
WorkspaceLayout::Vertical => (iced::Length::FillPortion(4), iced::Length::Fill),
|
||||
WorkspaceLayout::Horizontal => (iced::Length::Fill, iced::Length::FillPortion(4)),
|
||||
WorkspaceLayout::Vertical => (Length::FillPortion(4), Length::Fill),
|
||||
WorkspaceLayout::Horizontal => (Length::Fill, Length::FillPortion(4)),
|
||||
};
|
||||
let entries = toplevels
|
||||
.map(|t| toplevel_previews_entry(t, drag_toplevel == Some(&t.handle)))
|
||||
|
|
@ -401,12 +447,12 @@ fn bg_element<'a>(
|
|||
widget::image::Handle::from_path(path),
|
||||
)
|
||||
.content_fit(iced::ContentFit::Cover)
|
||||
.width(iced::Length::Fill)
|
||||
.height(iced::Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into(),
|
||||
Some(Source::Color(color)) => widget::layer_container(widget::horizontal_space())
|
||||
.width(iced::Length::Fill)
|
||||
.height(iced::Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.class(cosmic::theme::Container::Custom(Box::new(move |_| {
|
||||
let color = color.clone();
|
||||
cosmic::iced::widget::container::Style {
|
||||
|
|
@ -441,8 +487,8 @@ fn bg_element<'a>(
|
|||
"/usr/share/backgrounds/pop/kate-hazen-COSMIC-desktop-wallpaper.png",
|
||||
))
|
||||
.content_fit(iced::ContentFit::Cover)
|
||||
.width(iced::Length::Fill)
|
||||
.height(iced::Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ pub struct WorkspaceBar<'a, Msg> {
|
|||
impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WorkspaceBar<'_, Msg> {
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Fill,
|
||||
height: Length::Fill,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,48 +72,45 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WorkspaceBar<'_, Msg>
|
|||
renderer: &cosmic::Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
// TODO configurable
|
||||
let spacing = 8.0;
|
||||
|
||||
/*
|
||||
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 total_spacing = spacing * (self.children.len().saturating_sub(1)).max(0) as f32;
|
||||
let max_main =
|
||||
(self.axis.main(limits.max()) - total_spacing) / self.children().len() as f32;
|
||||
// TODO configurable
|
||||
let spacing = 8.0;
|
||||
|
||||
let total_spacing = 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());
|
||||
let mut total_main = 0.0;
|
||||
let mut max_child_cross = 0.0;
|
||||
let nodes = self
|
||||
.children
|
||||
.iter()
|
||||
.zip(tree.children.iter_mut())
|
||||
.map(|(child, tree)| {
|
||||
.enumerate()
|
||||
.map(|(i, (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.as_widget().layout(tree, renderer, &child_limits);
|
||||
let child_size = layout.size();
|
||||
let (x, y) = self.axis.pack(total_main, 0.0);
|
||||
layout = layout.move_to(Point::new(x, y));
|
||||
total_main += self.axis.main(layout.size()) + spacing;
|
||||
max_child_cross = f32::max(max_child_cross, self.axis.cross(child_size));
|
||||
let main = self.axis.main(child_size);
|
||||
// XXX Don't add spacing for 0 length `dnd_source` placeholder widget
|
||||
if main != 0.0 {
|
||||
total_main += main;
|
||||
if i < self.children.len() - 1 {
|
||||
total_main += spacing;
|
||||
}
|
||||
}
|
||||
layout
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (total_width, total_height) = self.axis.pack(total_main, max_cross);
|
||||
let (total_width, total_height) = self.axis.pack(total_main, max_child_cross);
|
||||
let size = Size::new(total_width, total_height);
|
||||
layout::Node::with_children(size, nodes)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue