Repository: qtile/qtile

Issue:
TreeTab and Zoomy layouts: changing a window to floating should also show the previous tiled window
EDIT: This bug was originally reported for the Max layout, but it was later discovered to affect other layouts too.

Steps to reproduce:
1. Open 2 windows in a group with Max layout.
2. Change the focused window to floating: the other window is not shown below it.

The expected behavior would be to also show the previous tiled window when one is changed to floating.



Discussion:

Resolving patch:
diff --git a/libqtile/group.py b/libqtile/group.py
--- a/libqtile/group.py
+++ b/libqtile/group.py
@@ -287,17 +287,18 @@ def remove(self, win):
         # else: TODO: change focus
 
     def mark_floating(self, win, floating):
-        if floating and win in self.floating_layout.clients:
-            # already floating
-            pass
-        elif floating:
-            for i in self.layouts:
-                i.remove(win)
+        if floating:
+            if win in self.floating_layout.clients:
+                # already floating
+                pass
+            else:
+                for i in self.layouts:
+                    i.remove(win)
+                    if win is self.currentWindow:
+                        i.blur()
+                self.floating_layout.add(win)
                 if win is self.currentWindow:
-                    i.blur()
-            self.floating_layout.add(win)
-            if win is self.currentWindow:
-                self.floating_layout.focus(win)
+                    self.floating_layout.focus(win)
         else:
             self.floating_layout.remove(win)
             self.floating_layout.blur()
diff --git a/libqtile/layout/max.py b/libqtile/layout/max.py
--- a/libqtile/layout/max.py
+++ b/libqtile/layout/max.py
@@ -33,14 +33,14 @@ def __init__(self, **config):
         SingleWindow.__init__(self, **config)
         self.clients = []
         self.add_defaults(Max.defaults)
-        self.focused = None
+        self.current = None
 
     def _get_window(self):
-        return self.focused
+        return self.current
 
     def focus(self, client):
         self.group.layoutAll()
-        self.focused = client
+        self.current = client
 
     def focus_first(self):
         if self.clients:
@@ -73,11 +73,11 @@ def focus_previous(self, window):
             return self.clients[idx - 1]
 
     def up(self):
-        client = self.focus_previous(self.focused) or self.focus_last()
+        client = self.focus_previous(self.current) or self.focus_last()
         self.group.focus(client, False)
 
     def down(self):
-        client = self.focus_next(self.focused) or self.focus_first()
+        client = self.focus_next(self.current) or self.focus_first()
         self.group.focus(client, False)
 
     def clone(self, group):
@@ -94,16 +94,25 @@ def add(self, client):
             self.clients.insert(idx + 1, client)
 
     def remove(self, client):
-        if client not in self.clients:
+        try:
+            idx = self.clients.index(client)
+        except ValueError:
             return
-        self.clients.remove(client)
-        if self.clients:
-            return self.clients[0]
         else:
-            self.focused = None
+            del self.clients[idx]
+
+        if client is self.current:
+            try:
+                # The previous client must become current
+                # if idx == 0, using self.clients[-1] is indeed correct
+                self.current = self.clients[idx - 1]
+            except IndexError:
+                self.current = None
+
+        return self.current
 
     def configure(self, client, screen):
-        if self.clients and client is self.focused:
+        if self.clients and client is self.current:
             client.place(
                 screen.x,
                 screen.y,


Test patch:
diff --git a/test/test_layout.py b/test/test_layout.py
--- a/test/test_layout.py
+++ b/test/test_layout.py
@@ -270,9 +270,10 @@ def test_closing_notification(self):
     assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
 
     # Now test a notification that is the last open window in the group
+    self.c.layout.down()
     notification2 = self.testWindow("notification2")
     self.c.window.toggle_floating()
-    # Create and kill 'temp', otherwise self.c.layout.down() won't work
+    # Create and kill 'temp', otherwise self.c.layout.up() won't work
     temp = self.testWindow("temp")
     self.c.layout.up()
     self.c.layout.up()
@@ -283,6 +284,7 @@ def test_closing_notification(self):
 
     # Now test a notification that is neither the first nor the last open
     # window in the group
+    self.c.layout.down()
     notification3 = self.testWindow("notification3")
     self.c.window.toggle_floating()
     four = self.testWindow("four")