Repository: qtile/qtile

Issue:
Gap on second window monadthreecol
### The issue:

I wanted to toggle the bar off with two windows open in MonadThreeCol layout.
It successfully hid the bar but left a gap on only the secondary window above and below but not the main window. It works as expected in the MonadTall layout.
I expected both windows to successfully span the full screen from top to bottom.

### Required:

- [X] I have searched past issues to see if this bug has already been reported.
Gap on second window monadthreecol
### The issue:

I wanted to toggle the bar off with two windows open in MonadThreeCol layout.
It successfully hid the bar but left a gap on only the secondary window above and below but not the main window. It works as expected in the MonadTall layout.
I expected both windows to successfully span the full screen from top to bottom.

### Required:

- [X] I have searched past issues to see if this bug has already been reported.


Discussion:

Resolving patch:
diff --git a/libqtile/layout/xmonad.py b/libqtile/layout/xmonad.py
--- a/libqtile/layout/xmonad.py
+++ b/libqtile/layout/xmonad.py
@@ -196,9 +196,19 @@ def __init__(self, **config):
         if self.single_margin is None:
             self.single_margin = self.margin
         self.relative_sizes = []
-        self.screen_rect = None
+        self._screen_rect = None
         self.default_ratio = self.ratio
 
+    # screen_rect is a property as the MonadThreeCol layout needs to perform
+    # additional actions when the attribute is modified
+    @property
+    def screen_rect(self):
+        return self._screen_rect
+
+    @screen_rect.setter
+    def screen_rect(self, value):
+        self._screen_rect = value
+
     @property
     def focused(self):
         return self.clients.current_index
@@ -1106,6 +1116,16 @@ def __init__(self, **config):
         MonadTall.__init__(self, **config)
         self.add_defaults(MonadThreeCol.defaults)
 
+    # mypy doesn't like the setter when the getter isn't present
+    # see https://github.com/python/mypy/issues/5936
+    @MonadTall.screen_rect.setter  # type: ignore[attr-defined]
+    def screen_rect(self, value):
+        # If the screen_rect size has change then we need to normalise secondary
+        # windows so they're resized to fill the new space correctly
+        if value != self._screen_rect:
+            self.do_normalize = True
+        self._screen_rect = value
+
     def _configure_specific(self, client, screen_rect, border_color, index):
         """Specific configuration for xmonad three columns."""
         if index == 0:
@@ -1175,9 +1195,17 @@ def _place_client(self, client, left, top, width, height):
         Will prevent double margins by applying east and south margins only
         when the client is the rightmost or the bottommost window.
         """
-        rightmost = left + width - self.screen_rect.x >= self.screen_rect.width
-        bottommost = top + height - self.screen_rect.y >= self.screen_rect.height
-        margin = [self.margin] * 4
+
+        # Create a temporary margin list for the client
+        if isinstance(self.margin, int):
+            margin = [self.margin] * 4
+        else:
+            # We need to copy this list otherwise we'd be modifying self.margin!
+            margin = self.margin.copy()
+
+        rightmost = left + width - self.screen_rect.x + margin[1] >= self.screen_rect.width
+        bottommost = top + height - self.screen_rect.y + margin[2] >= self.screen_rect.height
+
         if not rightmost:
             margin[1] = 0
         if not bottommost:


Test patch: