Repository: qtile/qtile

Issue:
The max_chars key of CurrentLayout widget has no effect.
# Issue description

I'd like to prevent my bar to adjust when the layout changes in the CurrentLayout widget.
Since ae282e38, the max_chars key is available for CurrentLayout widget.
So I tried to use 'max_chars=2' but the layout name is not truncated as expected.
I'll try to fix my bar with the fmt key for the moment, but I guess the base._TextBox.update method should be called by the hook_response method to take into account max_chars.

# Qtile version

git-version (90a07097 at the moment)

# Stack traces

Nothing relevant.

# Configuration

widget.CurrentLayout(max_chars=2),



Discussion:
This is untested by does this fix it for you?:
```diff
diff --git a/libqtile/widget/currentlayout.py b/libqtile/widget/currentlayout.py
index 354a75d9..d00ea956 100644
--- a/libqtile/widget/currentlayout.py
+++ b/libqtile/widget/currentlayout.py
@@ -57,7 +57,10 @@ class CurrentLayout(base._TextBox):
     def setup_hooks(self):
         def hook_response(layout, group):
             if group.screen is not None and group.screen == self.bar.screen:
-                self.text = layout.name
+                if self.max_chars and self.max_chars < len(layout.name):
+                    self.text = layout.name[:self.max_chars]
+                else:
+                    self.text = layout.name
                 self.bar.draw()
         hook.subscribe.layout_change(hook_response)
 
```
Yeah, looks like the `_TextBox` class has this in the wrong place. Might work if we move the `max_chars` parts to the `text` setter. That should work as `update` also sets text that way.

I can take a look at this tomorrow unless you fancy trying before then.

Looks like @m-col and I replied at the same time!
I'm glad we did, because you made me realised that actually that logic I posted in the diff already exiss within _TextBox, so it's a matter of the child class just using it. I.e., instead of above, it ought to do this:
```diff
diff --git a/libqtile/widget/currentlayout.py b/libqtile/widget/currentlayout.py
index 354a75d9..851c383d 100644
--- a/libqtile/widget/currentlayout.py
+++ b/libqtile/widget/currentlayout.py
@@ -57,8 +57,7 @@ class CurrentLayout(base._TextBox):
     def setup_hooks(self):
         def hook_response(layout, group):
             if group.screen is not None and group.screen == self.bar.screen:
-                self.text = layout.name
-                self.bar.draw()
+                self.update(layout.name)
         hook.subscribe.layout_change(hook_response)
 
```
That's definitely one way. I just wonder if it's better to sort at the `_TextBox` level so that other widgets don't need the same fix above i.e. `max_chars` should be applied whether a widget sets the text via `update` or via `self.text=`.
I'm not sure what you mean, `max_chars` is already handled at the level of `TextBox`, it's just that some widgets haven't been updated to use that method rather than setting `self.text` directly.
I think we're talking about the same point but we're approaching it from opposite ends. You're suggesting updating widgets to use the `update` method instead of setting the `text` attribute directly. I was suggesting amending the `_TextBox` class and putting the `max_chars` code in the `text.setter` part so that `max_chars` is applied if text is updated via either route.
Oh I see. Yeah your way is definitely better.
> I can take a look at this tomorrow unless you fancy trying before then.

Don't hesitate, busy time coming, I'll hope to read and enjoy your solution next we.
Thank you both for the quick return.

I was thinking something like this:
``` diff
index d084afb9..ab08645e 100644
--- a/libqtile/widget/base.py
+++ b/libqtile/widget/base.py
@@ -357,6 +357,8 @@ class _TextBox(_Widget):

     @text.setter
     def text(self, value):
+        if len(value) > self.max_chars > 0:
+            value = value[:self.max_chars] + "…"
         self._text = value
         if self.layout:
             self.layout.text = self.formatted_text
@@ -467,8 +469,6 @@ class _TextBox(_Widget):
             text = ""

         old_width = self.layout.width
-        if len(text) > self.max_chars > 0:
-            text = text[:self.max_chars] + "…"
         self.text = text
```

If you're ok with that approach, I'll submit this as a PR.
Yeah that looks good. Do any of the widgets need updating?

On 210411 05:37, elParaguayo wrote:
>I was thinking something like this:
>``` diff
>index d084afb9..ab08645e 100644
>--- a/libqtile/widget/base.py
>+++ b/libqtile/widget/base.py
>@@ -357,6 +357,8 @@ class _TextBox(_Widget):
>
>     @text.setter
>     def text(self, value):
>+        if len(value) > self.max_chars > 0:
>+            value = value[:self.max_chars] + "…"
>         self._text = value
>         if self.layout:
>             self.layout.text = self.formatted_text
>@@ -467,8 +469,6 @@ class _TextBox(_Widget):
>             text = ""
>
>         old_width = self.layout.width
>-        if len(text) > self.max_chars > 0:
>-            text = text[:self.max_chars] + "…"
>         self.text = text
>```
>
>If you're ok with that approach, I'll submit this as a PR.
>
>-- 
>You are receiving this because you were mentioned.
>Reply to this email directly or view it on GitHub:
>https://github.com/qtile/qtile/issues/2374#issuecomment-817300872

They shouldn't do (but haven't checked). This should work whether text is changed via `self.update` or the `self.text = `. It wouldn't work if something sets `self._text` directly, but nothing should be doing that.

Resolving patch:
diff --git a/libqtile/widget/base.py b/libqtile/widget/base.py
--- a/libqtile/widget/base.py
+++ b/libqtile/widget/base.py
@@ -357,6 +357,8 @@ def text(self):
 
     @text.setter
     def text(self, value):
+        if len(value) > self.max_chars > 0:
+            value = value[:self.max_chars] + "…"
         self._text = value
         if self.layout:
             self.layout.text = self.formatted_text
@@ -467,8 +469,6 @@ def update(self, text):
             text = ""
 
         old_width = self.layout.width
-        if len(text) > self.max_chars > 0:
-            text = text[:self.max_chars] + "…"
         self.text = text
 
         # If our width hasn't changed, we just draw ourselves. Otherwise,


Test patch: