fix: stop tracking rects when dragging

subscription counter changes also ensure that when a window is dropped, it gets a new rectangle.
This commit is contained in:
Ashley Wulber 2026-07-09 01:11:43 -04:00 • committed by Jacob Kauffmann
parent 01b5836a97
commit 28a2bfedad
2 changed files with 85 additions and 30 deletions

View file

@ -156,6 +156,7 @@ struct Toplevel {
info: ToplevelInfo, info: ToplevelInfo,
img: Option<backend::CaptureImage>, img: Option<backend::CaptureImage>,
icon: Option<PathBuf>, icon: Option<PathBuf>,
pub pending_move: Option<ExtWorkspaceHandleV1>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -210,6 +211,7 @@ struct App {
action_on_typing_activated: bool, action_on_typing_activated: bool,
rectangle_tracker: Option<RectangleTracker<RectId>>, rectangle_tracker: Option<RectangleTracker<RectId>>,
rects: HashMap<RectId, Rectangle>, rects: HashMap<RectId, Rectangle>,
sub_ctr: u128,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -614,6 +616,7 @@ impl Application for App {
if self.layer_surfaces.remove(&id).is_none() { if self.layer_surfaces.remove(&id).is_none() {
log::error!("removing non-existant layer shell id {}?", id); log::error!("removing non-existant layer shell id {}?", id);
} }
self.sub_ctr += 1;
self.rects.retain(|k, _| k.id != id); self.rects.retain(|k, _| k.id != id);
} }
_ => {} _ => {}
@ -668,6 +671,7 @@ impl Application for App {
handle, handle,
info, info,
img: None, img: None,
pending_move: None,
}); });
// Close workspaces view if a window spawns while open // Close workspaces view if a window spawns while open
#[cfg(not(feature = "mock-backend"))] #[cfg(not(feature = "mock-backend"))]
@ -692,19 +696,30 @@ impl Application for App {
// XX must clean up rectangles after the window has moved // XX must clean up rectangles after the window has moved
t_w = Some((handle.id(), info.workspace.clone())); 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; toplevel.info = info;
tasks.push(task); tasks.push(task);
} }
if let Some((t, w)) = t_w { if let Some((t, w)) = t_w {
for w in w { for w in w {
let old_len = self.rects.len();
self.rects.retain(|id, _| { self.rects.retain(|id, _| {
!(id.toplevel_id.as_ref().is_some_and(|old| *old == t) id.toplevel_id.as_ref().is_none_or(|old| *old != t)
&& id || id
.workspaces_id .workspaces_id
.as_ref() .as_ref()
.is_some_and(|old| !old.contains(&w.id()))) .is_some_and(|old| old.contains(&w.id()))
}); });
if old_len != self.rects.len() {
self.sub_ctr += 1;
}
} }
} }
if self.visible { if self.visible {
@ -803,7 +818,19 @@ impl Application for App {
self.send_wayland_cmd(backend::Cmd::CloseToplevel(toplevel_handle)); self.send_wayland_cmd(backend::Cmd::CloseToplevel(toplevel_handle));
} }
Msg::StartDrag(drag_surface) => { Msg::StartDrag(drag_surface) => {
// if let DragSurface::Toplevel(t) = &drag_surface {}
self.drag_surface = Some((drag_surface, Default::default())); 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) => { Msg::DndEnter(drop_target, _x, _y, _mimes) => {
self.drop_target = Some(drop_target); self.drop_target = Some(drop_target);
@ -814,6 +841,21 @@ impl Application for App {
if self.drop_target == Some(drop_target) { if self.drop_target == Some(drop_target) {
self.drop_target = None; 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) => { Msg::DndToplevelDrop(_toplevel) => {
if let Some((DragSurface::Toplevel(handle), _)) = &self.drag_surface { if let Some((DragSurface::Toplevel(handle), _)) = &self.drag_surface {
@ -822,26 +864,34 @@ impl Application for App {
DropTarget::WorkspaceSidebarEntry(workspace, output) DropTarget::WorkspaceSidebarEntry(workspace, output)
| DropTarget::OutputToplevels(workspace, output), | DropTarget::OutputToplevels(workspace, output),
) => { ) => {
let mut w = None; if let Some(t) = self.toplevels.for_handle_mut(handle)
self.rects.retain(|id, _| { && !t.info.workspace.contains(&workspace.id())
id.toplevel_id.as_ref().is_none_or(|t| { {
let keep = *t != handle.id(); self.rects.retain(|r, _| {
if !keep { r.toplevel_id.as_ref().is_none_or(|o| *o != handle.id())
w.clone_from(&id.workspaces_id); });
} self.sub_ctr += 1;
keep
}) t.pending_move = Some(workspace.clone());
}); }
self.send_wayland_cmd(backend::Cmd::MoveToplevelToWorkspace( self.send_wayland_cmd(backend::Cmd::MoveToplevelToWorkspace(
handle.clone(), handle.clone(),
workspace, workspace,
output, output,
)); ));
if let Some(w) = w { self.drag_surface = None;
return Task::batch(w.into_iter().map(|w| {
self.update_active_workspace(w).unwrap_or(Task::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( Some(
DropTarget::WorkspacesBar(_) DropTarget::WorkspacesBar(_)
@ -1068,7 +1118,7 @@ impl Application for App {
config_subscription, config_subscription,
comp_config_subscription, comp_config_subscription,
bg_subscription, bg_subscription,
rectangle_tracker_subscription(0).map(|update| Msg::Rectangle(update.1)), rectangle_tracker_subscription(self.sub_ctr).map(|update| Msg::Rectangle(update.1)),
]; ];
if let Some(conn) = self.conn.clone() { if let Some(conn) = self.conn.clone() {
subscriptions.push(backend::subscription(conn).map(Msg::Wayland)); subscriptions.push(backend::subscription(conn).map(Msg::Wayland));

View file

@ -1,4 +1,3 @@
use cosmic::Apply;
use cosmic::cctk::cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1; 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::cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2;
use cosmic::cctk::wayland_client::Proxy; use cosmic::cctk::wayland_client::Proxy;
@ -12,6 +11,7 @@ use cosmic::iced::platform_specific::shell::subsurface_widget::Subsurface;
use cosmic::iced::widget::{column, row}; use cosmic::iced::widget::{column, row};
use cosmic::iced::{self, Alignment, Border, Length}; use cosmic::iced::{self, Alignment, Border, Length};
use cosmic::widget::{self, Widget, rectangle_tracker}; use cosmic::widget::{self, Widget, rectangle_tracker};
use cosmic::{Apply, Element};
use cosmic_comp_config::workspace::WorkspaceLayout; use cosmic_comp_config::workspace::WorkspaceLayout;
use std::collections::HashSet; use std::collections::HashSet;
@ -579,17 +579,22 @@ fn toplevel_preview(
.spacing(8) .spacing(8)
.padding([0, 0, 2, 0]) .padding([0, 0, 2, 0])
.align_y(Alignment::Center); .align_y(Alignment::Center);
let alpha = if is_being_dragged { 0.5 } else { 1.0 }; let alpha = if is_being_dragged { 0.5 } else { 1.0 };
let preview = widget::button::custom(rectangle_track.container( let content = capture_image(toplevel.img.as_ref(), alpha);
RectId {
id: window_id, let preview = widget::button::custom(if toplevel.pending_move.is_some() || is_being_dragged {
toplevel_id: Some(toplevel.handle.id()), Element::from(content)
widget_id: None, } else {
workspaces_id: Some(toplevel.info.workspace.iter().map(|h| h.id()).collect()), Element::from(rectangle_track.container(
}, RectId {
capture_image(toplevel.img.as_ref(), alpha), 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( .selected(
toplevel toplevel
.info .info