Repository: qtile/qtile

Issue:
Get Mpd2 widget fixed width
### The issue:

- qtile version: 0.22.1

Mpd2 widget has only the width attribute to control the maximize widget width, it won't always get the same width when the different lengths of song/artist names are.

### Required:

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


Discussion:
Sorry, I'm not entirely sure what behaviour you're after. Setting `width` should fix the width of the widget. Not setting it would mean the width is calculated based on the content. Is there a third option you want?
sorry to have failed to make myself clear. 
e.g this's my widget setup:
```python
                widget.Mpd2(
                    foreground='#ffffff',
                    background='#1f1f1f',
                    status_format='{play_status} {artist} - {title}',
                    idle_format='{play_status} {idle_message}',
                    idle_message='wait for playing....    ',
                    width=300,
                    padding=10,
                    scroll=True,
                    # font= 'JetBrains Mono Bold',
                ),

```

when the songs name is shorter than 300, the bar will shrink
![image](https://user-images.githubusercontent.com/34272057/210327393-ccf31b43-300f-4ab7-9062-65b7cef9bbc5.png)

for the song that has a long name, the bar will maximize to 300 widths.
![image](https://user-images.githubusercontent.com/34272057/210327653-9dc63de5-8599-4ae4-a0df-d7cc9445f907.png)

looks like the `width` attribute only controls the maximum width of a widget. but the additional minimum width control or a permanently fixed width is always for need.


If you disable scrolling, do you get the fixed size?
Yes, but can we get the fixed size when enabling scrolling?
When scrolling is enabled, the `width` value is interpreted as the maximum value. Where text is shorter than this, it reverts to calculating the width of the widget based on the size of the content.

We did originally think about having a separate `scroll_width` parameter which was to deal with this particular issue.

The simplest fix would be to always have a fixed with when using scrolling but that would change behaviour for existing users. If we don't do that then I think we need to add a new parameter (something like `scroll_fix_width=True`) to do this.

@m-col you and I discussed this before so curious if you have a preference here (or a simpler fix that I may have missed).
I think you got it all, both setups seem like they would be desirable so +1 for a simple bool like `scroll_fix_width` as you suggest
OK. Will add this later.

Resolving patch:
diff --git a/libqtile/widget/base.py b/libqtile/widget/base.py
--- a/libqtile/widget/base.py
+++ b/libqtile/widget/base.py
@@ -457,6 +457,12 @@ class _TextBox(_Widget):
             "Whether text should scroll completely away (True) or stop when the end of the text is shown (False)",
         ),
         ("scroll_hide", False, "Whether the widget should hide when scrolling has finished"),
+        (
+            "scroll_fixed_width",
+            False,
+            "When ``scroll=True`` the ``width`` parameter is a maximum width and, when text is shorter than this, the widget will resize. "
+            "Setting ``scroll_fixed_width=True`` will force the widget to have a fixed width, regardless of the size of the text.",
+        ),
     ]  # type: list[tuple[str, Any, str]]
 
     def __init__(self, text=" ", width=bar.CALCULATED, **config):
@@ -557,7 +563,11 @@ def check_width(self):
             self._is_scrolling = True
             self._should_scroll = True
         else:
-            self.length_type = bar.CALCULATED
+            if self.scroll_fixed_width:
+                self.length_type = bar.STATIC
+                self.length = self._scroll_width
+            else:
+                self.length_type = bar.CALCULATED
             self._should_scroll = False
 
     def calculate_length(self):


Test patch:
diff --git a/test/widgets/test_base.py b/test/widgets/test_base.py
--- a/test/widgets/test_base.py
+++ b/test/widgets/test_base.py
@@ -189,6 +189,13 @@ class ScrollingTextConfig(BareConfig):
                     TextBox("NoWidth", name="no_width", scroll=True),
                     TextBox("ShortText", name="short_text", width=100, scroll=True),
                     TextBox("Longer text " * 5, name="longer_text", width=100, scroll=True),
+                    TextBox(
+                        "ShortFixedWidth",
+                        name="fixed_width",
+                        width=200,
+                        scroll=True,
+                        scroll_fixed_width=True,
+                    ),
                 ],
                 32,
             )
@@ -257,3 +264,14 @@ def wait_for_scroll(widget):
 
     # Check actually scrolling
     wait_for_scroll(widget)
+
+
+@scrolling_text_config
+def test_scroll_fixed_width(manager):
+    widget = manager.c.widget["fixed_width"]
+
+    _, layout = widget.eval("self.layout.width")
+    assert int(layout) < 200
+
+    # Widget width is fixed at set width
+    assert widget.info()["width"] == 200