Repository: qtile/qtile

Issue:
MonadTall cmd_reset should respect the configured ratio
If I set the MonadTall master/other ratio in my config to something other than 0.5, say 0.6:
```python
layouts = [
    layout.MonadTall(ratio=0.6),
]
```
all is good, the master window takes up 0.6 of my screen. But if I mess around with widening/thinning the master and decide to reset it with `cmd_reset` (or a keybinding to `lazy.layout.reset`) it goes back to being 0.5 of my screen, not 0.6 like I have configured.

I believe this is happening because of this line in `layout.MonadTall.cmd_reset`:
```python
def cmd_reset(self, redraw=True):
    "Reset Layout."
    self.ratio = self._med_ratio # <- this is hard-coded to 0.5
    # ...
```
This can be easily fixed by setting the `self._med_ratio` to what the user has provided (in `__init__`). This is how I have fixed it in my personal configuration with some inheritance:
```python
class FixedMonadTall(layout.MonadTall):
    def __init__(self, **config):
        super().__init__(**config)
        self._med_ratio = self.ratio
```
The only other place I see `_med_ratio` used is `layout.MonadTall._maximize_main` and I believe the same behavior would be expected there - if the master is wider than I have configured the default to be (not "wider than 0.5") minimize it, and maximize otherwise.

The same issue happens with MonadWide, but the above change will fix that too because of inheritance.

Thank you for making this amazing WM, it's a joy to use.


Discussion:
That makes sense, and the change would be reasonable! Would you be 
happy to submit a PR with those changes? I do wonder when changing 
things like these if some people are using `cmd_reset` specifically to 
make the ratio 50/50. We would then be breaking their setups. Perhaps 
we should also add a `ratio=None` option to `cmd_reset`, which would 
then default to `self.ratio` as per your suggestion, but would then 
allow for that to overriden (i.e. to 0.5 for those who like the current 
functionality). What do you think?

On Sat, Jan 29 2022 at 09:45:12 -0800, Piotr Machura 
***@***.***> wrote:
> If I set the MonadTall master/other ratio in my config to something 
> other than 0.5, say 0.6:
> 
> layouts = [
>     layout.MonadTall(ratio=0.6),
> ]
> all is good, the master window takes up 0.6 of my screen. But if I 
> mess around with widening/thinning the master and decide to reset it 
> with cmd_reset (or a keybinding to lazy.layout.reset) it goes back to 
> being 0.5 of my screen, not 0.6 like I have configured.
> 
> I believe this is happening because of this line in 
> layout.MonadTall.cmd_reset:
> 
> def cmd_reset(self, redraw=True):
>     "Reset Layout."
>     self.ratio = self._med_ratio # <- this is hard-coded to 0.5
>     # ...
> This can be easily fixed by setting the self._med_ratio to what the 
> user has provided. This is how I have fixed it in my personal 
> configuration:
> 
> class FixedMonadTall(layout.MonadTall):
>     def __init__(self, **config):
>         super().__init__(**config)
>         self._med_ratio = self.ratio
> The only other place I see _med_ratio used is 
> layout.MonadTall._maximize_main and I believe the same behavior would 
> be expected there - if the master is wider than I have configured the 
> default to be (not "wider than 0.5") minimize it, and maximize 
> otherwise.
> 
> The same issue happens with MonadWide, but the above change will 
> affect that too.
> 
> Thank you for making this amazing WM, it's a joy to use.
> 
> —
> Reply to this email directly, view it on GitHub, or unsubscribe.
> Triage notifications on the go with GitHub Mobile for iOS or Android.
> You are receiving this because you are subscribed to this 
> thread.Message ID: ***@***.***>
> 



Sure thing, I'll submit a PR including your suggestion shortly.

Resolving patch:
diff --git a/libqtile/layout/xmonad.py b/libqtile/layout/xmonad.py
--- a/libqtile/layout/xmonad.py
+++ b/libqtile/layout/xmonad.py
@@ -47,7 +47,8 @@ class MonadTall(_SimpleLayoutBase):
     A main pane that contains a single window takes up a vertical portion of
     the screen_rect based on the ratio setting. This ratio can be adjusted with
     the ``cmd_grow_main`` and ``cmd_shrink_main`` or, while the main pane is in
-    focus, ``cmd_grow`` and ``cmd_shrink``.
+    focus, ``cmd_grow`` and ``cmd_shrink``. You may also set the ratio directly
+    with ``cmd_set_ratio``.
 
     ::
 
@@ -143,7 +144,6 @@ class MonadTall(_SimpleLayoutBase):
 
     _left = 0
     _right = 1
-    _med_ratio = 0.5
 
     defaults = [
         ("border_focus", "#ff0000", "Border colour(s) for the focused window."),
@@ -196,6 +196,7 @@ def __init__(self, **config):
             self.single_margin = self.margin
         self.relative_sizes = []
         self.screen_rect = None
+        self.default_ratio = self.ratio
 
     @property
     def focused(self):
@@ -227,6 +228,12 @@ def remove(self, client):
         self.do_normalize = True
         return self.clients.remove(client)
 
+    def cmd_set_ratio(self, ratio):
+        "Directly set the main pane ratio"
+        ratio = min(self.max_ratio, ratio)
+        self.ratio = max(self.min_ratio, ratio)
+        self.group.layout_all()
+
     def cmd_normalize(self, redraw=True):
         "Evenly distribute screen-space among secondary clients"
         n = len(self.clients) - 1  # exclude main client, 0
@@ -238,16 +245,16 @@ def cmd_normalize(self, redraw=True):
             self.group.layout_all()
         self.do_normalize = False
 
-    def cmd_reset(self, redraw=True):
+    def cmd_reset(self, ratio=None, redraw=True):
         "Reset Layout."
-        self.ratio = self._med_ratio
+        self.ratio = ratio or self.default_ratio
         if self.align == self._right:
             self.align = self._left
         self.cmd_normalize(redraw)
 
     def _maximize_main(self):
         "Toggle the main pane between min and max size"
-        if self.ratio <= self._med_ratio:
+        if self.ratio <= 0.5 * (self.max_ratio + self.min_ratio):
             self.ratio = self.max_ratio
         else:
             self.ratio = self.min_ratio


Test patch:
diff --git a/test/layouts/test_xmonad.py b/test/layouts/test_xmonad.py
--- a/test/layouts/test_xmonad.py
+++ b/test/layouts/test_xmonad.py
@@ -560,6 +560,50 @@ def test_wide_flip(manager):
     assert_dimensions(manager, 400, 0, 396, 296)
 
 
+@monadtall_config
+def test_tall_set_and_reset(manager):
+    manager.test_window("one")
+    assert_dimensions(manager, 0, 0, 796, 596)
+
+    manager.test_window("two")
+    assert_focused(manager, "two")
+    assert_dimensions(manager, 400, 0, 396, 596)
+
+    manager.c.layout.set_ratio(0.75)
+    assert_focused(manager, "two")
+    assert_dimensions(manager, 600, 0, 196, 596)
+
+    manager.c.layout.set_ratio(0.25)
+    assert_focused(manager, "two")
+    assert_dimensions(manager, 200, 0, 596, 596)
+
+    manager.c.layout.reset()
+    assert_focused(manager, "two")
+    assert_dimensions(manager, 400, 0, 396, 596)
+
+
+@monadwide_config
+def test_wide_set_and_reset(manager):
+    manager.test_window("one")
+    assert_dimensions(manager, 0, 0, 796, 596)
+
+    manager.test_window("two")
+    assert_focused(manager, "two")
+    assert_dimensions(manager, 0, 300, 796, 296)
+
+    manager.c.layout.set_ratio(0.75)
+    assert_focused(manager, "two")
+    assert_dimensions(manager, 0, 450, 796, 146)
+
+    manager.c.layout.set_ratio(0.25)
+    assert_focused(manager, "two")
+    assert_dimensions(manager, 0, 150, 796, 446)
+
+    manager.c.layout.reset()
+    assert_focused(manager, "two")
+    assert_dimensions(manager, 0, 300, 796, 296)
+
+
 @monadtall_config
 def test_tall_shuffle(manager):
     manager.test_window("one")