Compare commits
24 commits
pre-1.6-me
...
local/yoda
| Author | SHA1 | Date | |
|---|---|---|---|
| bdcdef9f5c | |||
| 9e11562a43 | |||
|
|
28e257daa8 | ||
|
|
f1ceefe4b1 | ||
| d65f381006 | |||
|
|
38d09960b7 | ||
|
|
35c2a4eae5 | ||
| 53a177c85b | |||
| d2910685cc | |||
|
|
9c5f5a2584 | ||
|
|
ce0977e189 | ||
|
|
8faab4c2a9 | ||
|
|
e28e408cc4 | ||
|
|
6511d2f1a6 | ||
|
|
3da8907d7a | ||
|
|
9e4c9e4bb2 | ||
|
|
28a2bfedad | ||
|
|
01b5836a97 | ||
|
|
20ac821954 | ||
|
|
a8998b3eb0 | ||
|
|
4b103b0b07 | ||
|
|
fa4d8d68a4 | ||
|
|
127b966e3e | ||
|
|
4bafd82500 |
12 changed files with 836 additions and 575 deletions
932
Cargo.lock
generated
932
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -92,3 +92,11 @@ mime = { path = "../window_clipboard/mime" }
|
|||
[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" }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
workspace = Espai de treball { $number }
|
||||
new-workspace = Nou espai de treball
|
||||
0
i18n/fil/cosmic_workspaces.ftl
Normal file
0
i18n/fil/cosmic_workspaces.ftl
Normal file
2
i18n/gl/cosmic_workspaces.ftl
Normal file
2
i18n/gl/cosmic_workspaces.ftl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
workspace = Espazo de traballo { $number }
|
||||
new-workspace = Novo espazo de traballo
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
workspace = Ruang kerja { $number }
|
||||
workspace = Ruang Kerja { $number }
|
||||
new-workspace = Ruang Kerja Baru
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
workspace = Qadanda xebatê { $number }
|
||||
workspace = Qada xebatê { $number }
|
||||
new-workspace = Qada xebatê nû
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
workspace = Espaci de trabalh { $number }
|
||||
new-workspace = Espaci de trabalh novèl
|
||||
0
i18n/sat/cosmic_workspaces.ftl
Normal file
0
i18n/sat/cosmic_workspaces.ftl
Normal file
|
|
@ -1,2 +1,2 @@
|
|||
workspace = 工作區 { $number }
|
||||
new-workspace = 新建工作區
|
||||
new-workspace = 新增工作區
|
||||
|
|
|
|||
318
src/main.rs
318
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)]
|
||||
|
|
@ -150,6 +156,7 @@ struct Toplevel {
|
|||
info: ToplevelInfo,
|
||||
img: Option<backend::CaptureImage>,
|
||||
icon: Option<PathBuf>,
|
||||
pub pending_move: Option<ExtWorkspaceHandleV1>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -174,6 +181,14 @@ struct Conf {
|
|||
bg: cosmic_bg_config::state::State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RectId {
|
||||
id: window::Id,
|
||||
toplevel_id: Option<ObjectId>,
|
||||
workspaces_id: Option<Vec<ObjectId>>,
|
||||
widget_id: Option<widget::Id>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct App {
|
||||
capture_filter: backend::CaptureFilter,
|
||||
|
|
@ -194,6 +209,9 @@ 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>,
|
||||
sub_ctr: u128,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -234,16 +252,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>> {
|
||||
|
|
@ -383,7 +409,7 @@ impl App {
|
|||
// TODO: If compositor supports overlap notify, also use that?
|
||||
// Or otherwise verify the panel is actually running.
|
||||
for config in self.panel_configs.values().flatten() {
|
||||
if config.autohide.is_some() && !config.exclusive_zone {
|
||||
if config.autohide_enabled() && !config.exclusive_zone {
|
||||
let dimention_constraints = config.get_dimensions(
|
||||
Some((output.width as u32, output.height as u32)),
|
||||
None,
|
||||
|
|
@ -421,6 +447,72 @@ impl App {
|
|||
}
|
||||
regions
|
||||
}
|
||||
|
||||
fn update_active_workspace(
|
||||
&mut self,
|
||||
workspace_handle: ObjectId,
|
||||
) -> Option<Task<cosmic::Action<Msg>>> {
|
||||
if let Some((cur_window, _)) = self.layer_surfaces.iter().find(|(_, layer_surface)| {
|
||||
self.workspaces
|
||||
.for_output(&layer_surface.output)
|
||||
.any(|w| w.handle().id() == workspace_handle)
|
||||
}) {
|
||||
let active = cosmic::theme::active();
|
||||
let rad = active
|
||||
.cosmic()
|
||||
.radius_s()
|
||||
.map(|x| if x < 4.0 { x } else { x + 8.0 });
|
||||
|
||||
let mut rects = Vec::new();
|
||||
let mut strips: Vec<Rectangle> = self
|
||||
.rects
|
||||
.iter()
|
||||
.filter_map(|(id, rect)| {
|
||||
if self.drag_surface.as_ref().is_some_and(|(s, _)| match s {
|
||||
DragSurface::Workspace(_) => false,
|
||||
DragSurface::Toplevel(ext_foreign_toplevel_handle_v1) => id
|
||||
.toplevel_id
|
||||
.as_ref()
|
||||
.is_some_and(|t| *t == ext_foreign_toplevel_handle_v1.id()),
|
||||
}) || id.id != *cur_window
|
||||
|| id
|
||||
.workspaces_id
|
||||
.as_ref()
|
||||
.is_some_and(|l| !l.iter().any(|o| *o == workspace_handle))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
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 {
|
||||
rects.push(*rect);
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(cosmic::surface::corner_radius::rounded_rect_strips(
|
||||
*rect, rad,
|
||||
))
|
||||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
strips.append(&mut rects);
|
||||
|
||||
return Some(
|
||||
cosmic::iced::platform_specific::shell::commands::blur::blur(
|
||||
*cur_window,
|
||||
Some(strips),
|
||||
)
|
||||
.discard(),
|
||||
);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Application for App {
|
||||
|
|
@ -429,7 +521,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 +537,27 @@ impl Application for App {
|
|||
|
||||
fn update(&mut self, message: Msg) -> Task<cosmic::Action<Msg>> {
|
||||
match message {
|
||||
Msg::Rectangle(u) => match u {
|
||||
RectangleUpdate::Rectangle(r) => {
|
||||
self.rects.insert(r.0.clone(), r.1);
|
||||
if self.visible {
|
||||
let to_update: Vec<_> = self
|
||||
.workspaces
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|w| w.is_active().then(|| w.handle().id()))
|
||||
.collect();
|
||||
return Task::batch(
|
||||
to_update
|
||||
.into_iter()
|
||||
.filter_map(|w| self.update_active_workspace(w)),
|
||||
);
|
||||
}
|
||||
}
|
||||
RectangleUpdate::Init(tracker) => {
|
||||
self.rectangle_tracker = Some(tracker);
|
||||
}
|
||||
},
|
||||
Msg::SourceFinished => {
|
||||
self.drag_surface = None;
|
||||
}
|
||||
|
|
@ -501,6 +616,8 @@ impl Application for App {
|
|||
if self.layer_surfaces.remove(&id).is_none() {
|
||||
log::error!("removing non-existant layer shell id {}?", id);
|
||||
}
|
||||
self.sub_ctr += 1;
|
||||
self.rects.retain(|k, _| k.id != id);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
|
@ -512,6 +629,7 @@ impl Application for App {
|
|||
backend::Event::Workspaces(mut workspaces) => {
|
||||
workspaces.sort_by(|(_, w1), (_, w2)| w1.coordinates.cmp(&w2.coordinates));
|
||||
let old_workspaces = mem::take(&mut self.workspaces);
|
||||
let mut new_active = Vec::new();
|
||||
for (outputs, workspace) in workspaces {
|
||||
// XXX efficiency
|
||||
let old_workspace = old_workspaces.for_handle(&workspace.handle);
|
||||
|
|
@ -520,15 +638,25 @@ impl Application for App {
|
|||
let dnd_source_id = old_workspace
|
||||
.map_or_else(iced::id::Id::unique, |w| w.dnd_source_id.clone());
|
||||
|
||||
self.workspaces.0.push(Workspace {
|
||||
let w = Workspace {
|
||||
info: workspace,
|
||||
outputs,
|
||||
img,
|
||||
has_cursor,
|
||||
dnd_source_id,
|
||||
});
|
||||
};
|
||||
if w.is_active() {
|
||||
new_active.push(w.handle().clone());
|
||||
}
|
||||
self.workspaces.0.push(w);
|
||||
}
|
||||
self.update_capture_filter();
|
||||
if self.visible {
|
||||
return Task::batch(new_active.into_iter().map(|new| {
|
||||
self.update_active_workspace(new.id())
|
||||
.unwrap_or(Task::none())
|
||||
}));
|
||||
}
|
||||
}
|
||||
backend::Event::NewToplevel(handle, info) => {
|
||||
log::debug!("New toplevel: {info:?}");
|
||||
|
|
@ -543,6 +671,7 @@ impl Application for App {
|
|||
handle,
|
||||
info,
|
||||
img: None,
|
||||
pending_move: None,
|
||||
});
|
||||
// Close workspaces view if a window spawns while open
|
||||
#[cfg(not(feature = "mock-backend"))]
|
||||
|
|
@ -552,6 +681,8 @@ impl Application for App {
|
|||
return icon_task;
|
||||
}
|
||||
backend::Event::UpdateToplevel(handle, info) => {
|
||||
let mut t_w = None;
|
||||
let mut tasks = Vec::new();
|
||||
if let Some(toplevel) = self.toplevels.for_handle_mut(&handle) {
|
||||
let mut task = Task::none();
|
||||
if toplevel.info.app_id != info.app_id {
|
||||
|
|
@ -562,15 +693,70 @@ impl Application for App {
|
|||
)
|
||||
.map(cosmic::Action::App);
|
||||
}
|
||||
// XX must clean up rectangles after the window has moved
|
||||
t_w = Some((handle.id(), info.workspace.clone()));
|
||||
|
||||
if toplevel
|
||||
.pending_move
|
||||
.as_ref()
|
||||
.is_some_and(|w| info.workspace.contains(&w.id()))
|
||||
{
|
||||
toplevel.pending_move = None;
|
||||
}
|
||||
toplevel.info = info;
|
||||
return task;
|
||||
|
||||
tasks.push(task);
|
||||
}
|
||||
if let Some((t, w)) = t_w {
|
||||
for w in w {
|
||||
let old_len = self.rects.len();
|
||||
self.rects.retain(|id, _| {
|
||||
id.toplevel_id.as_ref().is_none_or(|old| *old != t)
|
||||
|| id
|
||||
.workspaces_id
|
||||
.as_ref()
|
||||
.is_some_and(|old| old.contains(&w.id()))
|
||||
});
|
||||
if old_len != self.rects.len() {
|
||||
self.sub_ctr += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.visible {
|
||||
let to_update: Vec<_> = self
|
||||
.workspaces
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|w| w.is_active().then(|| w.handle().id()))
|
||||
.collect();
|
||||
tasks.push(Task::batch(
|
||||
to_update
|
||||
.into_iter()
|
||||
.filter_map(|w| self.update_active_workspace(w)),
|
||||
));
|
||||
}
|
||||
|
||||
return Task::batch(tasks);
|
||||
}
|
||||
backend::Event::CloseToplevel(handle) => {
|
||||
if let Some(idx) = self.toplevels.0.iter().position(|x| x.handle == handle)
|
||||
{
|
||||
self.toplevels.0.remove(idx);
|
||||
}
|
||||
if let Some(k) = self
|
||||
.rects
|
||||
.keys()
|
||||
.find(|k| k.toplevel_id.as_ref().is_some_and(|t| handle.id() == *t))
|
||||
.cloned()
|
||||
{
|
||||
self.rects.remove(&k);
|
||||
if let Some(w) = k.workspaces_id {
|
||||
return Task::batch(
|
||||
w.into_iter()
|
||||
.filter_map(|w| self.update_active_workspace(w)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
backend::Event::WorkspaceCapture(handle, image) => {
|
||||
//println!("Workspace capture");
|
||||
|
|
@ -604,7 +790,11 @@ impl Application for App {
|
|||
{
|
||||
return self.hide();
|
||||
}
|
||||
self.send_wayland_cmd(backend::Cmd::ActivateWorkspace(workspace_handle));
|
||||
|
||||
self.send_wayland_cmd(backend::Cmd::ActivateWorkspace(workspace_handle.clone()));
|
||||
if let Some(value) = self.update_active_workspace(workspace_handle.id()) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Msg::ActivateToplevel(toplevel_handle) => {
|
||||
self.send_wayland_cmd(backend::Cmd::ActivateToplevel(toplevel_handle));
|
||||
|
|
@ -623,13 +813,38 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
*/
|
||||
if self.visible {
|
||||
let to_update: Vec<_> = self
|
||||
.workspaces
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|w| w.is_active().then(|| w.handle().id()))
|
||||
.collect();
|
||||
return Task::batch(
|
||||
to_update
|
||||
.into_iter()
|
||||
.filter_map(|w| self.update_active_workspace(w)),
|
||||
);
|
||||
}
|
||||
}
|
||||
Msg::CloseToplevel(toplevel_handle) => {
|
||||
// TODO confirmation?
|
||||
self.send_wayland_cmd(backend::Cmd::CloseToplevel(toplevel_handle));
|
||||
}
|
||||
Msg::StartDrag(drag_surface) => {
|
||||
// if let DragSurface::Toplevel(t) = &drag_surface {}
|
||||
self.drag_surface = Some((drag_surface, Default::default()));
|
||||
let to_update: Vec<_> = self
|
||||
.workspaces
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|w| w.is_active().then(|| w.handle().id()))
|
||||
.collect();
|
||||
return Task::batch(
|
||||
to_update
|
||||
.into_iter()
|
||||
.filter_map(|w| self.update_active_workspace(w)),
|
||||
);
|
||||
}
|
||||
Msg::DndEnter(drop_target, _x, _y, _mimes) => {
|
||||
self.drop_target = Some(drop_target);
|
||||
|
|
@ -640,6 +855,21 @@ impl Application for App {
|
|||
if self.drop_target == Some(drop_target) {
|
||||
self.drop_target = None;
|
||||
}
|
||||
self.sub_ctr += 1;
|
||||
|
||||
if self.visible {
|
||||
let to_update: Vec<_> = self
|
||||
.workspaces
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|w| w.is_active().then(|| w.handle().id()))
|
||||
.collect();
|
||||
return Task::batch(
|
||||
to_update
|
||||
.into_iter()
|
||||
.filter_map(|w| self.update_active_workspace(w)),
|
||||
);
|
||||
}
|
||||
}
|
||||
Msg::DndToplevelDrop(_toplevel) => {
|
||||
if let Some((DragSurface::Toplevel(handle), _)) = &self.drag_surface {
|
||||
|
|
@ -648,11 +878,34 @@ impl Application for App {
|
|||
DropTarget::WorkspaceSidebarEntry(workspace, output)
|
||||
| DropTarget::OutputToplevels(workspace, output),
|
||||
) => {
|
||||
self.send_wayland_cmd(backend::Cmd::MoveToplevelToWorkspace(
|
||||
handle.clone(),
|
||||
workspace,
|
||||
output,
|
||||
));
|
||||
if let Some(t) = self.toplevels.for_handle_mut(handle)
|
||||
&& !t.info.workspace.contains(&workspace.id())
|
||||
{
|
||||
self.rects.retain(|r, _| {
|
||||
r.toplevel_id.as_ref().is_none_or(|o| *o != handle.id())
|
||||
});
|
||||
self.sub_ctr += 1;
|
||||
|
||||
t.pending_move = Some(workspace.clone());
|
||||
self.send_wayland_cmd(backend::Cmd::MoveToplevelToWorkspace(
|
||||
handle.clone(),
|
||||
workspace,
|
||||
output,
|
||||
));
|
||||
}
|
||||
self.drag_surface = None;
|
||||
|
||||
let to_update: Vec<_> = self
|
||||
.workspaces
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|w| w.is_active().then(|| w.handle().id()))
|
||||
.collect();
|
||||
return Task::batch(
|
||||
to_update
|
||||
.into_iter()
|
||||
.filter_map(|w| self.update_active_workspace(w)),
|
||||
);
|
||||
}
|
||||
Some(
|
||||
DropTarget::WorkspacesBar(_)
|
||||
|
|
@ -879,6 +1132,7 @@ impl Application for App {
|
|||
config_subscription,
|
||||
comp_config_subscription,
|
||||
bg_subscription,
|
||||
rectangle_tracker_subscription(self.sub_ctr).map(|update| Msg::Rectangle(update.1)),
|
||||
];
|
||||
if let Some(conn) = self.conn.clone() {
|
||||
subscriptions.push(backend::subscription(conn).map(Msg::Wayland));
|
||||
|
|
@ -895,8 +1149,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()
|
||||
|
|
|
|||
141
src/view/mod.rs
141
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::{Apply, Element};
|
||||
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(false).base.into()
|
||||
theme.cosmic().primary(theme.transparent).base.into()
|
||||
};
|
||||
let icon_color = if is_pinned {
|
||||
theme.cosmic().accent.on.into()
|
||||
} else {
|
||||
theme.cosmic().primary(false).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,32 +492,48 @@ 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(false).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,
|
||||
workspaces_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()
|
||||
}
|
||||
|
||||
fn toplevel_preview(toplevel: &Toplevel, is_being_dragged: bool) -> cosmic::Element<'static, Msg> {
|
||||
fn toplevel_preview(
|
||||
toplevel: &Toplevel,
|
||||
is_being_dragged: bool,
|
||||
window_id: window::Id,
|
||||
rectangle_track: &rectangle_tracker::RectangleTracker<RectId>,
|
||||
) -> cosmic::Element<'static, Msg> {
|
||||
let cosmic::cosmic_theme::Spacing {
|
||||
space_xxs, space_s, ..
|
||||
} = cosmic::theme::active().cosmic().spacing;
|
||||
|
|
@ -524,6 +552,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)
|
||||
|
|
@ -549,17 +579,30 @@ fn toplevel_preview(toplevel: &Toplevel, is_being_dragged: bool) -> cosmic::Elem
|
|||
.spacing(8)
|
||||
.padding([0, 0, 2, 0])
|
||||
.align_y(Alignment::Center);
|
||||
|
||||
let alpha = if is_being_dragged { 0.5 } else { 1.0 };
|
||||
let preview = widget::button::custom(capture_image(toplevel.img.as_ref(), alpha))
|
||||
.selected(
|
||||
toplevel
|
||||
.info
|
||||
.state
|
||||
.contains(&zcosmic_toplevel_handle_v1::State::Activated),
|
||||
)
|
||||
.class(cosmic::theme::Button::Image)
|
||||
.on_press(Msg::ActivateToplevel(toplevel.handle.clone()));
|
||||
let content = capture_image(toplevel.img.as_ref(), alpha);
|
||||
|
||||
let preview = widget::button::custom(if toplevel.pending_move.is_some() || is_being_dragged {
|
||||
Element::from(content)
|
||||
} else {
|
||||
Element::from(rectangle_track.container(
|
||||
RectId {
|
||||
id: window_id,
|
||||
toplevel_id: Some(toplevel.handle.id()),
|
||||
widget_id: None,
|
||||
workspaces_id: Some(toplevel.info.workspace.iter().map(|h| h.id()).collect()),
|
||||
},
|
||||
content,
|
||||
))
|
||||
})
|
||||
.selected(
|
||||
toplevel
|
||||
.info
|
||||
.state
|
||||
.contains(&zcosmic_toplevel_handle_v1::State::Activated),
|
||||
)
|
||||
.class(cosmic::theme::Button::Image)
|
||||
.on_press(Msg::ActivateToplevel(toplevel.handle.clone()));
|
||||
|
||||
widget::mouse_area(crate::widgets::size_cross_nth(
|
||||
vec![title.into(), preview.into()],
|
||||
|
|
@ -570,23 +613,26 @@ 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),
|
||||
toplevel_preview(toplevel, is_being_dragged, window_id, rectangle_track),
|
||||
!is_being_dragged,
|
||||
);
|
||||
let toplevel2 = toplevel.clone();
|
||||
let track = rectangle_track.clone();
|
||||
dnd_source_with_drag_surface(
|
||||
DragToplevel {},
|
||||
DragSurface::Toplevel(toplevel.handle.clone()),
|
||||
None,
|
||||
preview.into(),
|
||||
move || toplevel_preview(&toplevel2, true),
|
||||
move || toplevel_preview(&toplevel2, true, window_id, &track),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -594,13 +640,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