From 28a2bfedade35d759d4fd8ab8e945f9a32e6e5a3 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 9 Jul 2026 01:11:43 -0400 Subject: [PATCH] fix: stop tracking rects when dragging subscription counter changes also ensure that when a window is dropped, it gets a new rectangle. --- src/main.rs | 88 ++++++++++++++++++++++++++++++++++++++----------- src/view/mod.rs | 27 ++++++++------- 2 files changed, 85 insertions(+), 30 deletions(-) diff --git a/src/main.rs b/src/main.rs index 93ed615..f55cd52 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,6 +156,7 @@ struct Toplevel { info: ToplevelInfo, img: Option, icon: Option, + pub pending_move: Option, } #[derive(Clone)] @@ -210,6 +211,7 @@ struct App { action_on_typing_activated: bool, rectangle_tracker: Option>, rects: HashMap, + sub_ctr: u128, } #[derive(Debug, Default)] @@ -614,6 +616,7 @@ 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); } _ => {} @@ -668,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"))] @@ -692,19 +696,30 @@ impl Application for 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; 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_some_and(|old| *old == t) - && 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()))) + .is_some_and(|old| old.contains(&w.id())) }); + if old_len != self.rects.len() { + self.sub_ctr += 1; + } } } if self.visible { @@ -803,7 +818,19 @@ impl Application for App { 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); @@ -814,6 +841,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 { @@ -822,26 +864,34 @@ impl Application for App { DropTarget::WorkspaceSidebarEntry(workspace, output) | DropTarget::OutputToplevels(workspace, output), ) => { - let mut w = None; - self.rects.retain(|id, _| { - id.toplevel_id.as_ref().is_none_or(|t| { - let keep = *t != handle.id(); - if !keep { - w.clone_from(&id.workspaces_id); - } - keep - }) - }); + 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, )); - if let Some(w) = w { - return Task::batch(w.into_iter().map(|w| { - self.update_active_workspace(w).unwrap_or(Task::none()) - })); - } + 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(_) @@ -1068,7 +1118,7 @@ impl Application for App { config_subscription, comp_config_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() { subscriptions.push(backend::subscription(conn).map(Msg::Wayland)); diff --git a/src/view/mod.rs b/src/view/mod.rs index cc2e1e2..ae2b727 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -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::workspace::v2::client::zcosmic_workspace_handle_v2; 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::{self, Alignment, Border, Length}; use cosmic::widget::{self, Widget, rectangle_tracker}; +use cosmic::{Apply, Element}; use cosmic_comp_config::workspace::WorkspaceLayout; use std::collections::HashSet; @@ -579,17 +579,22 @@ fn toplevel_preview( .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(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()), - }, - capture_image(toplevel.img.as_ref(), alpha), - )) + 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