Skip to content

Commit

Permalink
Delete non-persistent groups when windows are moved to another group (#…
Browse files Browse the repository at this point in the history
…4663)

Save a reference to the original group before removing it from the
client and set it back before calling DGroups._del(client) so it can grab
the reference and schedule it for deletion.

Check if the client has already been scheduled for deletion in order to
avoid scheduling the same client multiple times and generating a
KeyError.

* Add test for deleting non-persistent groups when windows are moved

- Test for a dynamically created group (as a result of creating a
  window inside a exclusive group).
- Test for group manually created with `persist=False`.
  • Loading branch information
ValdezFOmar committed Jan 28, 2024
1 parent 8c9cf12 commit f2f7777
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Qtile x.xx.x, released XXXX-XX-XX:
* features
* bugfixes
- Fix groups marked with `persist=False` not being deleted when their last window is moved to another group.

Qtile 0.24.0, released 2024-01-20:
!!! config breakage/changes !!!
Expand Down
10 changes: 10 additions & 0 deletions libqtile/backend/wayland/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,17 @@ def togroup(
if self.group.screen:
# for floats remove window offset
self.x -= self.group.screen.x
group_ref = self.group
self.group.remove(self)
# delete groups with `persist=False`
if (
not self.qtile.dgroups.groups_map[group_ref.name].persist
and len(group_ref.windows) <= 1
):
# set back original group so _del() can grab it
self.group = group_ref
self.qtile.dgroups._del(self)
self.group = None

if group.screen and self.x < group.screen.x:
self.x += group.screen.x
Expand Down
9 changes: 9 additions & 0 deletions libqtile/backend/x11/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -2009,7 +2009,16 @@ def togroup(self, group_name=None, *, switch_group=False, toggle=False):
if self.group.screen:
# for floats remove window offset
self.x -= self.group.screen.x
group_ref = self.group
self.group.remove(self)
if (
not self.qtile.dgroups.groups_map[group_ref.name].persist
and len(group_ref.windows) <= 1
):
# set back original group so _del() can grab it
self.group = group_ref
self.qtile.dgroups._del(self)
self.group = None

if group.screen and self.x < group.screen.x:
self.x += group.screen.x
Expand Down
3 changes: 2 additions & 1 deletion libqtile/dgroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,5 @@ def delete_client():
del self.timeout[client]

logger.debug("Deleting %s in %ss", group, self.delay)
self.timeout[client] = self.qtile.call_later(self.delay, delete_client)
if client not in self.timeout:
self.timeout[client] = self.qtile.call_later(self.delay, delete_client)
34 changes: 34 additions & 0 deletions test/test_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import libqtile.config
from libqtile import layout
from libqtile.confreader import Config
from test.helpers import Retry


class GroupConfig(Config):
Expand Down Expand Up @@ -102,3 +103,36 @@ def test_toscreen_toggle(manager):
manager.c.group["b"].toscreen(toggle=True)
manager.c.group["b"].toscreen(toggle=True)
assert manager.c.group.info()["name"] == "a" # Toggling twice roundtrips between the two


class NoPersistGroupConfig(GroupConfig):
groups = [
libqtile.config.Group("a"),
libqtile.config.Group("b", exclusive=True),
libqtile.config.Group("c", persist=False),
]


@pytest.mark.parametrize("manager", [NoPersistGroupConfig], indirect=True)
def test_non_persistent_groups(manager):
@Retry(ignore_exceptions=(AssertionError,))
def wait_for_removed(group_name):
assert group_name not in manager.c.get_groups()

window_name = "no_match"
manager.c.group["b"].toscreen()
manager.test_window(window_name)
assert window_name not in manager.c.group.info()["windows"] # Window was moved to a new group
group_name = "TestWindow" # The new group is named after the window's `wm_class` property
assert group_name in manager.c.get_groups()
manager.c.group[group_name].toscreen()
assert manager.c.window.info()["name"] == window_name
manager.c.window.togroup("a")
wait_for_removed(group_name)

window_name = "bar"
manager.c.group["c"].toscreen()
manager.test_window(window_name)
assert manager.c.window.info()["name"] == window_name
manager.c.window.togroup("a")
wait_for_removed(group_name)

0 comments on commit f2f7777

Please sign in to comment.