feat: frosted glass
This commit is contained in:
parent
42add3f075
commit
4bafd82500
4 changed files with 588 additions and 525 deletions
888
Cargo.lock
generated
888
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -55,3 +55,11 @@ mock-backend = []
|
|||
[profile.dev]
|
||||
# Not usable at opt-level 0, at least with software renderer
|
||||
opt-level = 1
|
||||
|
||||
# [patch.'https://github.com/pop-os/libcosmic']
|
||||
# libcosmic = { path = "../libcosmic" }
|
||||
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||
# cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
||||
# libcosmic = { git = "https://github.com/pop-os/libcosmic//", branch = "theme-v2" }
|
||||
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//", branch = "theme-v2" }
|
||||
# cosmic-theme = { git = "https://github.com/pop-os/libcosmic//", branch = "theme-v2" }
|
||||
|
|
|
|||
109
src/main.rs
109
src/main.rs
|
|
@ -11,20 +11,25 @@ use cctk::wayland_client::{Connection, Proxy};
|
|||
use cctk::wayland_protocols::ext::workspace::v1::client::ext_workspace_handle_v1;
|
||||
use clap::Parser;
|
||||
use cosmic::app::{Application, CosmicFlags};
|
||||
use cosmic::cctk::wayland_client::backend::ObjectId;
|
||||
use cosmic::core::Auto;
|
||||
use cosmic::iced::clipboard::dnd::{DndEvent, SourceEvent};
|
||||
use cosmic::iced::event::wayland::{Event as WaylandEvent, LayerEvent, OutputEvent};
|
||||
use cosmic::iced::keyboard::key::{Key, Named};
|
||||
use cosmic::iced::mouse::ScrollDelta;
|
||||
use cosmic::iced::platform_specific::shell::commands::layer_surface::{
|
||||
destroy_layer_surface, get_layer_surface,
|
||||
};
|
||||
use cosmic::iced::platform_specific::shell::commands::layer_surface::destroy_layer_surface;
|
||||
use cosmic::iced::runtime::platform_specific::wayland::CornerRadius;
|
||||
use cosmic::iced::runtime::platform_specific::wayland::layer_surface::{
|
||||
IcedOutput, SctkLayerSurfaceSettings,
|
||||
IcedMargin, IcedOutput, SctkLayerSurfaceSettings,
|
||||
};
|
||||
use cosmic::iced::window::Id as SurfaceId;
|
||||
use cosmic::iced::{self, Size, Subscription, Task};
|
||||
use cosmic::iced::window::{self, Id as SurfaceId};
|
||||
use cosmic::iced::{self, Rectangle, Size, Subscription, Task};
|
||||
use cosmic::scroll::DiscreteScrollState;
|
||||
use cosmic::{cctk, dbus_activation};
|
||||
use cosmic::surface::action::LiveSettings;
|
||||
use cosmic::widget::rectangle_tracker::{
|
||||
RectangleTracker, RectangleUpdate, rectangle_tracker_subscription,
|
||||
};
|
||||
use cosmic::{cctk, dbus_activation, widget};
|
||||
use cosmic_comp_config::CosmicCompConfig;
|
||||
use cosmic_config::CosmicConfigEntry;
|
||||
use cosmic_config::cosmic_config_derive::CosmicConfigEntry;
|
||||
|
|
@ -114,6 +119,7 @@ enum Msg {
|
|||
PanelConfig(CosmicPanelConfig),
|
||||
ActionOnTyping(String),
|
||||
Ignore,
|
||||
Rectangle(RectangleUpdate<RectId>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -174,6 +180,13 @@ struct Conf {
|
|||
bg: cosmic_bg_config::state::State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RectId {
|
||||
id: window::Id,
|
||||
toplevel_id: Option<ObjectId>,
|
||||
widget_id: Option<widget::Id>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct App {
|
||||
capture_filter: backend::CaptureFilter,
|
||||
|
|
@ -194,6 +207,8 @@ struct App {
|
|||
dbus_interface: Option<dbus::Interface>,
|
||||
panel_configs: HashMap<String, Option<CosmicPanelConfig>>,
|
||||
action_on_typing_activated: bool,
|
||||
rectangle_tracker: Option<RectangleTracker<RectId>>,
|
||||
rects: HashMap<RectId, Rectangle>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -234,16 +249,24 @@ impl App {
|
|||
output: output.clone(),
|
||||
},
|
||||
);
|
||||
get_layer_surface(SctkLayerSurfaceSettings {
|
||||
id,
|
||||
keyboard_interactivity: KeyboardInteractivity::Exclusive,
|
||||
namespace: "cosmic-workspace-overview".into(),
|
||||
layer: Layer::Top,
|
||||
size: Some((None, None)),
|
||||
output: IcedOutput::Output(output),
|
||||
anchor: Anchor::all(),
|
||||
..Default::default()
|
||||
})
|
||||
cosmic::surface::surface_task::<Msg>(cosmic::surface::action::simple_layer_shell::<Msg>(
|
||||
|| LiveSettings {
|
||||
padding: Some(IcedMargin::default()),
|
||||
corners: Some(CornerRadius::default()),
|
||||
blur: Some(false),
|
||||
},
|
||||
move || SctkLayerSurfaceSettings {
|
||||
id,
|
||||
keyboard_interactivity: KeyboardInteractivity::Exclusive,
|
||||
namespace: "cosmic-workspace-overview".into(),
|
||||
layer: Layer::Top,
|
||||
size: Some((None, None)),
|
||||
output: IcedOutput::Output(output.clone()),
|
||||
anchor: Anchor::all(),
|
||||
..Default::default()
|
||||
},
|
||||
None::<fn() -> cosmic::Element<'static, cosmic::Action<Msg>>>,
|
||||
))
|
||||
}
|
||||
|
||||
fn destroy_surface(&mut self, output: &wl_output::WlOutput) -> Task<cosmic::Action<Msg>> {
|
||||
|
|
@ -429,7 +452,9 @@ impl Application for App {
|
|||
type Flags = Args;
|
||||
const APP_ID: &'static str = "com.system76.CosmicWorkspaces";
|
||||
|
||||
fn init(core: cosmic::app::Core, _flags: Self::Flags) -> (Self, Task<cosmic::Action<Msg>>) {
|
||||
fn init(mut core: cosmic::app::Core, _flags: Self::Flags) -> (Self, Task<cosmic::Action<Msg>>) {
|
||||
core.set_app_type(cosmic::core::AppType::System);
|
||||
core.set_auto_blur(Auto::Popup | Auto::Window);
|
||||
(
|
||||
Self {
|
||||
core,
|
||||
|
|
@ -443,6 +468,45 @@ impl Application for App {
|
|||
|
||||
fn update(&mut self, message: Msg) -> Task<cosmic::Action<Msg>> {
|
||||
match message {
|
||||
Msg::Rectangle(u) => match u {
|
||||
RectangleUpdate::Rectangle(r) => {
|
||||
// TODO should we reuse previously tracked rectangles on surface creation
|
||||
self.rects.insert(r.0.clone(), r.1);
|
||||
let active = cosmic::theme::active();
|
||||
let rad = active
|
||||
.cosmic()
|
||||
.radius_s()
|
||||
.map(|x| if x < 4.0 { x } else { x + 8.0 });
|
||||
let strips = self
|
||||
.rects
|
||||
.iter()
|
||||
.filter(|(id, _)| r.0.id == id.id)
|
||||
.map(|(id, r)| {
|
||||
let rad = if id.toplevel_id.is_none() {
|
||||
CornerRadius {
|
||||
top_left: rad[0] as u32,
|
||||
top_right: rad[1] as u32,
|
||||
bottom_left: rad[3] as u32,
|
||||
bottom_right: rad[2] as u32,
|
||||
}
|
||||
} else {
|
||||
CornerRadius::default()
|
||||
};
|
||||
|
||||
cosmic::surface::corner_radius::rounded_rect_strips(*r, rad)
|
||||
})
|
||||
.flatten();
|
||||
|
||||
return cosmic::iced::platform_specific::shell::commands::blur::blur(
|
||||
r.0.id,
|
||||
Some(strips.collect()),
|
||||
)
|
||||
.discard();
|
||||
}
|
||||
RectangleUpdate::Init(tracker) => {
|
||||
self.rectangle_tracker = Some(tracker);
|
||||
}
|
||||
},
|
||||
Msg::SourceFinished => {
|
||||
self.drag_surface = None;
|
||||
}
|
||||
|
|
@ -879,6 +943,7 @@ impl Application for App {
|
|||
config_subscription,
|
||||
comp_config_subscription,
|
||||
bg_subscription,
|
||||
rectangle_tracker_subscription(0).map(|update| Msg::Rectangle(update.1)),
|
||||
];
|
||||
if let Some(conn) = self.conn.clone() {
|
||||
subscriptions.push(backend::subscription(conn).map(Msg::Wayland));
|
||||
|
|
@ -895,8 +960,12 @@ impl Application for App {
|
|||
}
|
||||
|
||||
fn view_window(&self, id: iced::window::Id) -> cosmic::Element<'_, Self::Message> {
|
||||
if let Some(surface) = self.layer_surfaces.get(&id) {
|
||||
return view::layer_surface(self, surface);
|
||||
if let Some((surface, rectangle_track)) = self
|
||||
.layer_surfaces
|
||||
.get(&id)
|
||||
.zip(self.rectangle_tracker.as_ref())
|
||||
{
|
||||
return view::layer_surface(self, surface, id, rectangle_track);
|
||||
}
|
||||
log::error!("non-existant layer shell id {}?", id);
|
||||
cosmic::widget::text("workspaces").into()
|
||||
|
|
|
|||
108
src/view/mod.rs
108
src/view/mod.rs
|
|
@ -1,22 +1,23 @@
|
|||
use cosmic::Apply;
|
||||
use cosmic::cctk::cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1;
|
||||
use cosmic::cctk::cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2;
|
||||
use cosmic::cctk::wayland_client::Proxy;
|
||||
use cosmic::cctk::wayland_client::protocol::wl_output;
|
||||
use cosmic::cctk::wayland_protocols::ext::workspace::v1::client::ext_workspace_handle_v1;
|
||||
use cosmic::iced::advanced::layout::flex::Axis;
|
||||
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
|
||||
use cosmic::iced::core::Shadow;
|
||||
use cosmic::iced::core::text::{Ellipsize, EllipsizeHeightLimit};
|
||||
use cosmic::iced::core::{Shadow, window};
|
||||
use cosmic::iced::platform_specific::shell::subsurface_widget::Subsurface;
|
||||
use cosmic::iced::widget::{column, row};
|
||||
use cosmic::iced::{self, Alignment, Border, Length};
|
||||
use cosmic::widget::{self, Widget};
|
||||
use cosmic::widget::{self, Widget, rectangle_tracker};
|
||||
use cosmic_comp_config::workspace::WorkspaceLayout;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::backend::{self, CaptureImage};
|
||||
use crate::dnd::{Drag, DragSurface, DragToplevel, DragWorkspace, DropTarget};
|
||||
use crate::{App, LayerSurface, Msg, Toplevel, Workspace};
|
||||
use crate::{App, LayerSurface, Msg, RectId, Toplevel, Workspace};
|
||||
|
||||
fn dnd_source_with_drag_surface<D: AsMimeTypes + Send + Clone + 'static>(
|
||||
drag_content: D,
|
||||
|
|
@ -69,6 +70,8 @@ where
|
|||
pub(crate) fn layer_surface<'a>(
|
||||
app: &'a App,
|
||||
surface: &'a LayerSurface,
|
||||
window_id: window::Id,
|
||||
rectangle_track: &rectangle_tracker::RectangleTracker<RectId>,
|
||||
) -> cosmic::Element<'a, Msg> {
|
||||
let mut drag_toplevel = None;
|
||||
let mut drag_workspace = None;
|
||||
|
|
@ -89,6 +92,7 @@ pub(crate) fn layer_surface<'a>(
|
|||
.flat_map(|t| &t.info.workspace)
|
||||
.collect::<HashSet<_>>();
|
||||
let layout = app.conf.workspace_config.workspace_layout;
|
||||
// track this rectangle
|
||||
let sidebar = workspaces_sidebar(
|
||||
app.workspaces.for_output(&surface.output),
|
||||
&workspaces_with_toplevels,
|
||||
|
|
@ -96,6 +100,8 @@ pub(crate) fn layer_surface<'a>(
|
|||
layout,
|
||||
app.drop_target.as_ref(),
|
||||
drag_workspace,
|
||||
window_id,
|
||||
rectangle_track,
|
||||
);
|
||||
let toplevels = toplevel_previews(
|
||||
app.toplevels.0.iter().filter(|i| {
|
||||
|
|
@ -111,6 +117,8 @@ pub(crate) fn layer_surface<'a>(
|
|||
}),
|
||||
layout,
|
||||
drag_toplevel,
|
||||
window_id,
|
||||
rectangle_track,
|
||||
);
|
||||
// TODO multiple active workspaces? Not currently supported by cosmic.
|
||||
let first_active_workspace = app
|
||||
|
|
@ -146,6 +154,7 @@ pub(crate) fn layer_surface<'a>(
|
|||
let container = widget::container(container).padding(panel_regions);
|
||||
|
||||
let output = surface.output.clone();
|
||||
|
||||
widget::mouse_area(container)
|
||||
.on_scroll(move |delta| Msg::OnScroll(output.clone(), delta))
|
||||
.into()
|
||||
|
|
@ -162,12 +171,12 @@ fn pin_button_style(theme: &cosmic::Theme, is_pinned: bool) -> cosmic::widget::b
|
|||
let bg_color = if is_pinned {
|
||||
theme.cosmic().accent.base.into()
|
||||
} else {
|
||||
theme.cosmic().primary.base.into()
|
||||
theme.cosmic().primary(theme.transparent).base.into()
|
||||
};
|
||||
let icon_color = if is_pinned {
|
||||
theme.cosmic().accent.on.into()
|
||||
} else {
|
||||
theme.cosmic().primary.on.into()
|
||||
theme.cosmic().primary(theme.transparent).on.into()
|
||||
};
|
||||
cosmic::widget::button::Style {
|
||||
icon_color: Some(icon_color),
|
||||
|
|
@ -322,6 +331,7 @@ fn workspace_item(
|
|||
{
|
||||
button = button.on_press(Msg::ActivateWorkspace(workspace.handle().clone()));
|
||||
}
|
||||
|
||||
button.into()
|
||||
}
|
||||
|
||||
|
|
@ -420,6 +430,8 @@ fn workspaces_sidebar<'a>(
|
|||
layout: WorkspaceLayout,
|
||||
drop_target: Option<&DropTarget>,
|
||||
drag_workspace: Option<&'a backend::ExtWorkspaceHandleV1>,
|
||||
window_id: window::Id,
|
||||
rectangle_track: &rectangle_tracker::RectangleTracker<RectId>,
|
||||
) -> cosmic::Element<'a, Msg> {
|
||||
let mut sidebar_entries = Vec::new();
|
||||
for workspace in workspaces {
|
||||
|
|
@ -480,26 +492,36 @@ fn workspaces_sidebar<'a>(
|
|||
widget::container(crate::widgets::workspace_bar(sidebar_entries, axis)).padding(8.0);
|
||||
|
||||
widget::container(
|
||||
widget::container(sidebar_entries_container)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.class(cosmic::theme::Container::custom(|theme| {
|
||||
cosmic::iced::widget::container::Style {
|
||||
text_color: Some(theme.cosmic().on_bg_color().into()),
|
||||
icon_color: Some(theme.cosmic().on_bg_color().into()),
|
||||
background: Some(iced::Color::from(theme.cosmic().background.base).into()),
|
||||
border: Border {
|
||||
radius: theme
|
||||
.cosmic()
|
||||
.radius_s()
|
||||
.map(|x| if x < 4.0 { x } else { x + 8.0 })
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
shadow: Shadow::default(),
|
||||
snap: true,
|
||||
}
|
||||
})),
|
||||
rectangle_track.container(
|
||||
RectId {
|
||||
id: window_id,
|
||||
toplevel_id: None,
|
||||
widget_id: None,
|
||||
},
|
||||
widget::container(sidebar_entries_container)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.class(cosmic::theme::Container::custom(|theme| {
|
||||
cosmic::iced::widget::container::Style {
|
||||
text_color: Some(theme.cosmic().on_bg_color().into()),
|
||||
icon_color: Some(theme.cosmic().on_bg_color().into()),
|
||||
background: Some(
|
||||
iced::Color::from(theme.cosmic().background(theme.transparent).base)
|
||||
.into(),
|
||||
),
|
||||
border: Border {
|
||||
radius: theme
|
||||
.cosmic()
|
||||
.radius_s()
|
||||
.map(|x| if x < 4.0 { x } else { x + 8.0 })
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
shadow: Shadow::default(),
|
||||
snap: true,
|
||||
}
|
||||
})),
|
||||
),
|
||||
)
|
||||
.padding(8)
|
||||
.into()
|
||||
|
|
@ -524,6 +546,8 @@ fn toplevel_preview(toplevel: &Toplevel, is_being_dragged: bool) -> cosmic::Elem
|
|||
.align_y(Alignment::Center);
|
||||
|
||||
let title = row![
|
||||
// TODO tracker for each of these buttons?
|
||||
// So that they can be blurred individually like the sidebar?
|
||||
widget::button::custom(label)
|
||||
.on_press(Msg::ActivateToplevel(toplevel.handle.clone()))
|
||||
.class(cosmic::theme::Button::Icon)
|
||||
|
|
@ -532,7 +556,7 @@ fn toplevel_preview(toplevel: &Toplevel, is_being_dragged: bool) -> cosmic::Elem
|
|||
.class(cosmic::theme::Container::custom(|theme| {
|
||||
cosmic::iced::widget::container::Style {
|
||||
background: Some(
|
||||
iced::Color::from(theme.cosmic().background.component.base).into(),
|
||||
iced::Color::from(theme.cosmic().background(false).component.base).into(),
|
||||
),
|
||||
border: Border {
|
||||
color: theme.cosmic().bg_divider().into(),
|
||||
|
|
@ -570,15 +594,24 @@ fn toplevel_preview(toplevel: &Toplevel, is_being_dragged: bool) -> cosmic::Elem
|
|||
.into()
|
||||
}
|
||||
|
||||
fn toplevel_previews_entry(
|
||||
toplevel: &Toplevel,
|
||||
fn toplevel_previews_entry<'a>(
|
||||
toplevel: &'a Toplevel,
|
||||
is_being_dragged: bool,
|
||||
) -> cosmic::Element<'_, Msg> {
|
||||
window_id: window::Id,
|
||||
rectangle_track: &rectangle_tracker::RectangleTracker<RectId>,
|
||||
) -> cosmic::Element<'a, Msg> {
|
||||
// Dragged window still takes up space until moved, but isn't rendered while drag surface is
|
||||
// shown.
|
||||
let preview = crate::widgets::visibility_wrapper(
|
||||
toplevel_preview(toplevel, is_being_dragged),
|
||||
!is_being_dragged,
|
||||
let preview = rectangle_track.container(
|
||||
RectId {
|
||||
id: window_id,
|
||||
toplevel_id: Some(toplevel.handle.id()),
|
||||
widget_id: None,
|
||||
},
|
||||
crate::widgets::visibility_wrapper(
|
||||
toplevel_preview(toplevel, is_being_dragged),
|
||||
!is_being_dragged,
|
||||
),
|
||||
);
|
||||
let toplevel2 = toplevel.clone();
|
||||
dnd_source_with_drag_surface(
|
||||
|
|
@ -594,13 +627,22 @@ fn toplevel_previews<'a>(
|
|||
toplevels: impl Iterator<Item = &'a Toplevel>,
|
||||
layout: WorkspaceLayout,
|
||||
drag_toplevel: Option<&'a backend::ExtForeignToplevelHandleV1>,
|
||||
window_id: window::Id,
|
||||
rectangle_track: &rectangle_tracker::RectangleTracker<RectId>,
|
||||
) -> cosmic::Element<'a, Msg> {
|
||||
let (width, height) = match layout {
|
||||
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)))
|
||||
.map(|t| {
|
||||
toplevel_previews_entry(
|
||||
t,
|
||||
drag_toplevel == Some(&t.handle),
|
||||
window_id,
|
||||
rectangle_track,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
//row(entries)
|
||||
widget::mouse_area(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue