Repository: qtile/qtile

Issue:
CurrentLayoutIcon widget does not load icons for group-specific layouts
### Issue description

When using the `CurrentLayoutIcon` widget with layouts that are defined group-only,
(For example: 
```py
layouts = [
    # Some layouts here
]
groups = [
    Group("0", layouts=[
        # Any layouts not included in `layouts` above
    ])
]
```
)
the icon does not properly load and display for layouts that are only defined for the group specifically.

This issue occurs because in the setup function for loading icons https://github.com/qtile/qtile/blob/fd93e74c110f23846af5f721195b6c1f90b873f8/libqtile/widget/currentlayout.py#L207-L235
 it only loads icons for layouts found by `_get_layout_names`, which only includes layouts defined under `layouts` in the main configuration https://github.com/qtile/qtile/blob/fd93e74c110f23846af5f721195b6c1f90b873f8/libqtile/widget/currentlayout.py#L174-L181

This means that layouts are accessible that do not have icons that are loaded, leading to errors in the form
```
2023-09-24 23:31:50,483 ERROR libqtile currentlayout.py:draw():L158 No icon for layout <layout name>
```
Indicating that the issue isn't that an icon file wasn't found (I have them present in `~/.icons`, and no error message saying they aren't found), but rather that an icon file was never loaded.

A potential solution would be to also go through each group in `_get_layout_names` to also include those layouts, deduplicating as necessary.

### Version

0.22.1

### Backend

X11 (default)

### Config

_No response_

### Logs

_No response_

### Required

- [X] I have searched past issues to see if this bug has already been reported, and it hasn't been.
- [X] I understand that people give their precious time for free, and thus I've done my very best to make this problem as easy as possible to investigate.


Discussion:
What the issue looks like in practice:
![image](https://github.com/qtile/qtile/assets/22727114/3d850926-dc00-4758-9da6-af93f9eaf704)
When cycling through, it just inherits what was previously rendered, because nothing gets rerendered in the area.
Thanks for the detailed report. I'm happy to add to my to do list unless you want to have a go at fixing the issue yourself...

Resolving patch:
diff --git a/libqtile/widget/currentlayout.py b/libqtile/widget/currentlayout.py
--- a/libqtile/widget/currentlayout.py
+++ b/libqtile/widget/currentlayout.py
@@ -25,6 +25,7 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
+import itertools
 import os
 
 from libqtile import bar, hook
@@ -160,7 +161,8 @@ def draw(self):
                 self.drawer.clear(self.background or self.bar.background)
                 self.drawer.ctx.save()
                 self.drawer.ctx.translate(
-                    (self.width - surface.width) / 2, (self.bar.height - surface.height) / 2
+                    (self.width - surface.width) / 2,
+                    (self.bar.height - surface.height) / 2,
                 )
                 self.drawer.ctx.set_source(surface.pattern)
                 self.drawer.ctx.paint()
@@ -173,12 +175,16 @@ def draw(self):
 
     def _get_layout_names(self):
         """
-        Returns a list of tuples of lowercased layout name and class name strings for each available layout.
+        Returns a sequence of tuples of layout name and lowercased class name
+        strings for each available layout.
         """
-        return [
-            (layout.name, layout.__class__.__name__.lower())
-            for layout in self.qtile.config.layouts
-        ]
+
+        layouts = itertools.chain(
+            self.qtile.config.layouts,
+            (layout for group in self.qtile.config.groups for layout in group.layouts),
+        )
+
+        return set((layout.name, layout.__class__.__name__.lower()) for layout in layouts)
 
     def _update_icon_paths(self):
         self.icon_paths = []


Test patch: