We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CurrentLayoutIcon failing wiith custom layout names
### The issue:

Qtile version: 0.21.0

Relevant logs:
```
2022-06-25 11:37:44,763 ERROR libqtile currentlayout.py:draw():L146 No icon for layout shiftytile
2022-06-25 11:37:47,899 ERROR libqtile currentlayout.py:draw():L146 No icon for layout monadtile
```

(EDIT: My browser ate the rest of this issue. Rewriting.)

I have a couple of tile layouts with custom names and parameters:
```python
    layout.Tile(name='monadtile',
                add_on_top=False,
                shift_windows=False,
                margin_on_single=False,
                border_on_single=False,
                expand=True,
                ratio=0.47,
                **layout_theme),
    layout.Columns(**layout_theme),
    layout.Tile(name='shiftytile',
                add_on_top=True,
                shift_windows=True,
                margin_on_single=False,
                expand=False,
                **layout_theme),
```

The correspondiing widget config is
```python
        widget.CurrentLayoutIcon(
            custom_icon_paths = [os.path.expanduser("~/.config/qtile/icons")],
            foreground = colors[0],
            background = pair.cols[0],
            padding = 0,
            scale = 0.6
        ),
```

I have icons named `layout-monadtile.png` and `layout-shiftytile.png` in `~/.config/qtile/icons`. They don't show up, and I get the above messages in qtile.log.

The problem is the following. The widget (`libqtile/widget/currentlayout.py`) obtains a list of layout names using 
```python
    def _get_layout_names(self):
        """
        Returns the list of lowercased strings for each available layout name.
        """
        return [layout.__class__.__name__.lower() for layout in self.qtile.config.layouts]
```

I logged it to see what it finds:
``` python
logger.warn(",".join([layout.__class__.__name__.lower() for layout in qtile.config.layouts]))
```

And it finds this:
``` python
2022-06-25 12:20:05,007 WARNING libqtile groups.py:layout_names_test():L51 tile,columns,tile
```

and not `monadtile,columns,shiftytile`. I changed `_get_layout_names` to
```python
    def _get_layout_names(self):
        """
        Returns the list of lowercased strings for each available layout name.
        """
        return [layout.name.lower() for layout in self.qtile.config.layouts]
        
```

and the icons for all layouts (including the custom `monadtile` and `shiftytile`) now show up. However I'm guessing there's a reason to use `layout.__class__.__name__` instead of `layout.name`. Any ideas?


### Required:

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


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `libqtile/widget/currentlayout.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2011 Florian Mounier
3 # Copyright (c) 2011 Kenji_Takahashi
4 # Copyright (c) 2012 roger
5 # Copyright (c) 2012, 2014 Tycho Andersen
6 # Copyright (c) 2012 Maximilian Köhl
7 # Copyright (c) 2013 Craig Barnes
8 # Copyright (c) 2014 Sean Vig
9 # Copyright (c) 2014 Adi Sieker
10 #
11 # Permission is hereby granted, free of charge, to any person obtaining a copy
12 # of this software and associated documentation files (the "Software"), to deal
13 # in the Software without restriction, including without limitation the rights
14 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 # copies of the Software, and to permit persons to whom the Software is
16 # furnished to do so, subject to the following conditions:
17 #
18 # The above copyright notice and this permission notice shall be included in
19 # all copies or substantial portions of the Software.
20 #
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 # SOFTWARE.
28 import os
29 
30 import cairocffi
31 
32 from libqtile import bar, hook
33 from libqtile.log_utils import logger
34 from libqtile.widget import base
35 
36 
37 class CurrentLayout(base._TextBox):
38     """
39     Display the name of the current layout of the current group of the screen,
40     the bar containing the widget, is on.
41     """
42 
43     def __init__(self, width=bar.CALCULATED, **config):
44         base._TextBox.__init__(self, "", width, **config)
45 
46     def _configure(self, qtile, bar):
47         base._TextBox._configure(self, qtile, bar)
48         layout_id = self.bar.screen.group.current_layout
49         self.text = self.bar.screen.group.layouts[layout_id].name
50         self.setup_hooks()
51 
52         self.add_callbacks(
53             {
54                 "Button1": qtile.cmd_next_layout,
55                 "Button2": qtile.cmd_prev_layout,
56             }
57         )
58 
59     def setup_hooks(self):
60         def hook_response(layout, group):
61             if group.screen is not None and group.screen == self.bar.screen:
62                 self.text = layout.name
63                 self.bar.draw()
64 
65         hook.subscribe.layout_change(hook_response)
66 
67 
68 class CurrentLayoutIcon(base._TextBox):
69     """
70     Display the icon representing the current layout of the
71     current group of the screen on which the bar containing the widget is.
72 
73     If you are using custom layouts, a default icon with question mark
74     will be displayed for them. If you want to use custom icon for your own
75     layout, for example, `FooGrid`, then create a file named
76     "layout-foogrid.png" and place it in `~/.icons` directory. You can as well
77     use other directories, but then you need to specify those directories
78     in `custom_icon_paths` argument for this plugin.
79 
80     The order of icon search is:
81 
82     - dirs in `custom_icon_paths` config argument
83     - `~/.icons`
84     - built-in qtile icons
85     """
86 
87     orientations = base.ORIENTATION_HORIZONTAL
88 
89     defaults = [
90         ("scale", 1, "Scale factor relative to the bar height.  " "Defaults to 1"),
91         (
92             "custom_icon_paths",
93             [],
94             "List of folders where to search icons before"
95             "using built-in icons or icons in ~/.icons dir.  "
96             "This can also be used to provide"
97             "missing icons for custom layouts.  "
98             "Defaults to empty list.",
99         ),
100     ]
101 
102     def __init__(self, **config):
103         base._TextBox.__init__(self, "", **config)
104         self.add_defaults(CurrentLayoutIcon.defaults)
105         self.scale = 1.0 / self.scale
106 
107         self.length_type = bar.STATIC
108         self.length = 0
109 
110     def _configure(self, qtile, bar):
111         base._TextBox._configure(self, qtile, bar)
112         layout_id = self.bar.screen.group.current_layout
113         self.text = self.bar.screen.group.layouts[layout_id].name
114         self.current_layout = self.text
115         self.icons_loaded = False
116         self.icon_paths = []
117         self.surfaces = {}
118         self._update_icon_paths()
119         self._setup_images()
120         self._setup_hooks()
121 
122         self.add_callbacks(
123             {
124                 "Button1": qtile.cmd_next_layout,
125                 "Button2": qtile.cmd_prev_layout,
126             }
127         )
128 
129     def _setup_hooks(self):
130         """
131         Listens for layout change and performs a redraw when it occurs.
132         """
133 
134         def hook_response(layout, group):
135             if group.screen is not None and group.screen == self.bar.screen:
136                 self.current_layout = layout.name
137                 self.bar.draw()
138 
139         hook.subscribe.layout_change(hook_response)
140 
141     def draw(self):
142         if self.icons_loaded:
143             try:
144                 surface = self.surfaces[self.current_layout]
145             except KeyError:
146                 logger.error("No icon for layout %s", self.current_layout)
147             else:
148                 self.drawer.clear(self.background or self.bar.background)
149                 self.drawer.ctx.set_source(surface)
150                 self.drawer.ctx.paint()
151                 self.drawer.draw(offsetx=self.offset, offsety=self.offsety, width=self.length)
152         else:
153             # Fallback to text
154             self.text = self.current_layout[0].upper()
155             base._TextBox.draw(self)
156 
157     def _get_layout_names(self):
158         """
159         Returns the list of lowercased strings for each available layout name.
160         """
161         return [layout.__class__.__name__.lower() for layout in self.qtile.config.layouts]
162 
163     def _update_icon_paths(self):
164         self.icon_paths = []
165 
166         # We allow user to override icon search path
167         self.icon_paths.extend(self.custom_icon_paths)
168 
169         # We also look in ~/.icons/ and ~/.local/share/icons
170         self.icon_paths.append(os.path.expanduser("~/.icons"))
171         self.icon_paths.append(os.path.expanduser("~/.local/share/icons"))
172 
173         # Default icons are in libqtile/resources/layout-icons.
174         # If using default config without any custom icons,
175         # this path will be used.
176         root = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-2])
177         self.icon_paths.append(os.path.join(root, "resources", "layout-icons"))
178 
179     def find_icon_file_path(self, layout_name):
180         icon_filename = "layout-{}.png".format(layout_name)
181         for icon_path in self.icon_paths:
182             icon_file_path = os.path.join(icon_path, icon_filename)
183             if os.path.isfile(icon_file_path):
184                 return icon_file_path
185 
186     def _setup_images(self):
187         """
188         Loads layout icons.
189         """
190         for layout_name in self._get_layout_names():
191             icon_file_path = self.find_icon_file_path(layout_name)
192             if icon_file_path is None:
193                 logger.warning('No icon found for layout "%s"', layout_name)
194                 icon_file_path = self.find_icon_file_path("unknown")
195 
196             try:
197                 img = cairocffi.ImageSurface.create_from_png(icon_file_path)
198             except (cairocffi.Error, IOError) as e:
199                 # Icon file is guaranteed to exist at this point.
200                 # If this exception happens, it means the icon file contains
201                 # an invalid image or is not readable.
202                 self.icons_loaded = False
203                 logger.exception(
204                     'Failed to load icon from file "%s", error was: %s', icon_file_path, e.message
205                 )
206                 return
207 
208             input_width = img.get_width()
209             input_height = img.get_height()
210 
211             sp = input_height / (self.bar.height - 1)
212 
213             width = input_width / sp
214             if width > self.length:
215                 self.length = int(width) + self.actual_padding * 2
216 
217             imgpat = cairocffi.SurfacePattern(img)
218 
219             scaler = cairocffi.Matrix()
220 
221             scaler.scale(sp, sp)
222             scaler.scale(self.scale, self.scale)
223             factor = (1 - 1 / self.scale) / 2
224             scaler.translate(-width * factor, -width * factor)
225             scaler.translate(self.actual_padding * -1, 0)
226             imgpat.set_matrix(scaler)
227 
228             imgpat.set_filter(cairocffi.FILTER_BEST)
229             self.surfaces[layout_name] = imgpat
230 
231         self.icons_loaded = True
232
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/libqtile/widget/currentlayout.py b/libqtile/widget/currentlayout.py
--- a/libqtile/widget/currentlayout.py
+++ b/libqtile/widget/currentlayout.py
@@ -156,9 +156,12 @@
 
     def _get_layout_names(self):
         """
-        Returns the list of lowercased strings for each available layout name.
+        Returns a list of tuples of lowercased layout name and class name strings for each available layout.
         """
-        return [layout.__class__.__name__.lower() for layout in self.qtile.config.layouts]
+        return [
+            (layout.name, layout.__class__.__name__.lower())
+            for layout in self.qtile.config.layouts
+        ]
 
     def _update_icon_paths(self):
         self.icon_paths = []
@@ -187,9 +190,18 @@
         """
         Loads layout icons.
         """
-        for layout_name in self._get_layout_names():
-            icon_file_path = self.find_icon_file_path(layout_name)
-            if icon_file_path is None:
+        for names in self._get_layout_names():
+            layout_name = names[0]
+            # Python doesn't have an ordered set but we can use a dictionary instead
+            # First key is the layout's name (which may have been set by the user),
+            # the second is the class name. If these are the same (i.e. the user hasn't
+            # set a name) then there is only one key in the dictionary.
+            layouts = dict.fromkeys(names)
+            for layout in layouts.keys():
+                icon_file_path = self.find_icon_file_path(layout)
+                if icon_file_path:
+                    break
+            else:
                 logger.warning('No icon found for layout "%s"', layout_name)
                 icon_file_path = self.find_icon_file_path("unknown")

{"golden_diff": "diff --git a/libqtile/widget/currentlayout.py b/libqtile/widget/currentlayout.py\n--- a/libqtile/widget/currentlayout.py\n+++ b/libqtile/widget/currentlayout.py\n@@ -156,9 +156,12 @@\n \n     def _get_layout_names(self):\n         \"\"\"\n-        Returns the list of lowercased strings for each available layout name.\n+        Returns a list of tuples of lowercased layout name and class name strings for each available layout.\n         \"\"\"\n-        return [layout.__class__.__name__.lower() for layout in self.qtile.config.layouts]\n+        return [\n+            (layout.name, layout.__class__.__name__.lower())\n+            for layout in self.qtile.config.layouts\n+        ]\n \n     def _update_icon_paths(self):\n         self.icon_paths = []\n@@ -187,9 +190,18 @@\n         \"\"\"\n         Loads layout icons.\n         \"\"\"\n-        for layout_name in self._get_layout_names():\n-            icon_file_path = self.find_icon_file_path(layout_name)\n-            if icon_file_path is None:\n+        for names in self._get_layout_names():\n+            layout_name = names[0]\n+            # Python doesn't have an ordered set but we can use a dictionary instead\n+            # First key is the layout's name (which may have been set by the user),\n+            # the second is the class name. If these are the same (i.e. the user hasn't\n+            # set a name) then there is only one key in the dictionary.\n+            layouts = dict.fromkeys(names)\n+            for layout in layouts.keys():\n+                icon_file_path = self.find_icon_file_path(layout)\n+                if icon_file_path:\n+                    break\n+            else:\n                 logger.warning('No icon found for layout \"%s\"', layout_name)\n                 icon_file_path = self.find_icon_file_path(\"unknown\")\n", "issue": "CurrentLayoutIcon failing wiith custom layout names\n### The issue:\r\n\r\nQtile version: 0.21.0\r\n\r\nRelevant logs:\r\n```\r\n2022-06-25 11:37:44,763 ERROR libqtile currentlayout.py:draw():L146 No icon for layout shiftytile\r\n2022-06-25 11:37:47,899 ERROR libqtile currentlayout.py:draw():L146 No icon for layout monadtile\r\n```\r\n\r\n(EDIT: My browser ate the rest of this issue. Rewriting.)\r\n\r\nI have a couple of tile layouts with custom names and parameters:\r\n```python\r\n    layout.Tile(name='monadtile',\r\n                add_on_top=False,\r\n                shift_windows=False,\r\n                margin_on_single=False,\r\n                border_on_single=False,\r\n                expand=True,\r\n                ratio=0.47,\r\n                **layout_theme),\r\n    layout.Columns(**layout_theme),\r\n    layout.Tile(name='shiftytile',\r\n                add_on_top=True,\r\n                shift_windows=True,\r\n                margin_on_single=False,\r\n                expand=False,\r\n                **layout_theme),\r\n```\r\n\r\nThe correspondiing widget config is\r\n```python\r\n        widget.CurrentLayoutIcon(\r\n            custom_icon_paths = [os.path.expanduser(\"~/.config/qtile/icons\")],\r\n            foreground = colors[0],\r\n            background = pair.cols[0],\r\n            padding = 0,\r\n            scale = 0.6\r\n        ),\r\n```\r\n\r\nI have icons named `layout-monadtile.png` and `layout-shiftytile.png` in `~/.config/qtile/icons`. They don't show up, and I get the above messages in qtile.log.\r\n\r\nThe problem is the following. The widget (`libqtile/widget/currentlayout.py`) obtains a list of layout names using \r\n```python\r\n    def _get_layout_names(self):\r\n        \"\"\"\r\n        Returns the list of lowercased strings for each available layout name.\r\n        \"\"\"\r\n        return [layout.__class__.__name__.lower() for layout in self.qtile.config.layouts]\r\n```\r\n\r\nI logged it to see what it finds:\r\n``` python\r\nlogger.warn(\",\".join([layout.__class__.__name__.lower() for layout in qtile.config.layouts]))\r\n```\r\n\r\nAnd it finds this:\r\n``` python\r\n2022-06-25 12:20:05,007 WARNING libqtile groups.py:layout_names_test():L51 tile,columns,tile\r\n```\r\n\r\nand not `monadtile,columns,shiftytile`. I changed `_get_layout_names` to\r\n```python\r\n    def _get_layout_names(self):\r\n        \"\"\"\r\n        Returns the list of lowercased strings for each available layout name.\r\n        \"\"\"\r\n        return [layout.name.lower() for layout in self.qtile.config.layouts]\r\n        \r\n```\r\n\r\nand the icons for all layouts (including the custom `monadtile` and `shiftytile`) now show up. However I'm guessing there's a reason to use `layout.__class__.__name__` instead of `layout.name`. Any ideas?\r\n\r\n\r\n### Required:\r\n\r\n- [X] I have searched past issues to see if this bug has already been reported.\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2011 Florian Mounier\n# Copyright (c) 2011 Kenji_Takahashi\n# Copyright (c) 2012 roger\n# Copyright (c) 2012, 2014 Tycho Andersen\n# Copyright (c) 2012 Maximilian K\u00f6hl\n# Copyright (c) 2013 Craig Barnes\n# Copyright (c) 2014 Sean Vig\n# Copyright (c) 2014 Adi Sieker\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\nimport os\n\nimport cairocffi\n\nfrom libqtile import bar, hook\nfrom libqtile.log_utils import logger\nfrom libqtile.widget import base\n\n\nclass CurrentLayout(base._TextBox):\n    \"\"\"\n    Display the name of the current layout of the current group of the screen,\n    the bar containing the widget, is on.\n    \"\"\"\n\n    def __init__(self, width=bar.CALCULATED, **config):\n        base._TextBox.__init__(self, \"\", width, **config)\n\n    def _configure(self, qtile, bar):\n        base._TextBox._configure(self, qtile, bar)\n        layout_id = self.bar.screen.group.current_layout\n        self.text = self.bar.screen.group.layouts[layout_id].name\n        self.setup_hooks()\n\n        self.add_callbacks(\n            {\n                \"Button1\": qtile.cmd_next_layout,\n                \"Button2\": qtile.cmd_prev_layout,\n            }\n        )\n\n    def setup_hooks(self):\n        def hook_response(layout, group):\n            if group.screen is not None and group.screen == self.bar.screen:\n                self.text = layout.name\n                self.bar.draw()\n\n        hook.subscribe.layout_change(hook_response)\n\n\nclass CurrentLayoutIcon(base._TextBox):\n    \"\"\"\n    Display the icon representing the current layout of the\n    current group of the screen on which the bar containing the widget is.\n\n    If you are using custom layouts, a default icon with question mark\n    will be displayed for them. If you want to use custom icon for your own\n    layout, for example, `FooGrid`, then create a file named\n    \"layout-foogrid.png\" and place it in `~/.icons` directory. You can as well\n    use other directories, but then you need to specify those directories\n    in `custom_icon_paths` argument for this plugin.\n\n    The order of icon search is:\n\n    - dirs in `custom_icon_paths` config argument\n    - `~/.icons`\n    - built-in qtile icons\n    \"\"\"\n\n    orientations = base.ORIENTATION_HORIZONTAL\n\n    defaults = [\n        (\"scale\", 1, \"Scale factor relative to the bar height.  \" \"Defaults to 1\"),\n        (\n            \"custom_icon_paths\",\n            [],\n            \"List of folders where to search icons before\"\n            \"using built-in icons or icons in ~/.icons dir.  \"\n            \"This can also be used to provide\"\n            \"missing icons for custom layouts.  \"\n            \"Defaults to empty list.\",\n        ),\n    ]\n\n    def __init__(self, **config):\n        base._TextBox.__init__(self, \"\", **config)\n        self.add_defaults(CurrentLayoutIcon.defaults)\n        self.scale = 1.0 / self.scale\n\n        self.length_type = bar.STATIC\n        self.length = 0\n\n    def _configure(self, qtile, bar):\n        base._TextBox._configure(self, qtile, bar)\n        layout_id = self.bar.screen.group.current_layout\n        self.text = self.bar.screen.group.layouts[layout_id].name\n        self.current_layout = self.text\n        self.icons_loaded = False\n        self.icon_paths = []\n        self.surfaces = {}\n        self._update_icon_paths()\n        self._setup_images()\n        self._setup_hooks()\n\n        self.add_callbacks(\n            {\n                \"Button1\": qtile.cmd_next_layout,\n                \"Button2\": qtile.cmd_prev_layout,\n            }\n        )\n\n    def _setup_hooks(self):\n        \"\"\"\n        Listens for layout change and performs a redraw when it occurs.\n        \"\"\"\n\n        def hook_response(layout, group):\n            if group.screen is not None and group.screen == self.bar.screen:\n                self.current_layout = layout.name\n                self.bar.draw()\n\n        hook.subscribe.layout_change(hook_response)\n\n    def draw(self):\n        if self.icons_loaded:\n            try:\n                surface = self.surfaces[self.current_layout]\n            except KeyError:\n                logger.error(\"No icon for layout %s\", self.current_layout)\n            else:\n                self.drawer.clear(self.background or self.bar.background)\n                self.drawer.ctx.set_source(surface)\n                self.drawer.ctx.paint()\n                self.drawer.draw(offsetx=self.offset, offsety=self.offsety, width=self.length)\n        else:\n            # Fallback to text\n            self.text = self.current_layout[0].upper()\n            base._TextBox.draw(self)\n\n    def _get_layout_names(self):\n        \"\"\"\n        Returns the list of lowercased strings for each available layout name.\n        \"\"\"\n        return [layout.__class__.__name__.lower() for layout in self.qtile.config.layouts]\n\n    def _update_icon_paths(self):\n        self.icon_paths = []\n\n        # We allow user to override icon search path\n        self.icon_paths.extend(self.custom_icon_paths)\n\n        # We also look in ~/.icons/ and ~/.local/share/icons\n        self.icon_paths.append(os.path.expanduser(\"~/.icons\"))\n        self.icon_paths.append(os.path.expanduser(\"~/.local/share/icons\"))\n\n        # Default icons are in libqtile/resources/layout-icons.\n        # If using default config without any custom icons,\n        # this path will be used.\n        root = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-2])\n        self.icon_paths.append(os.path.join(root, \"resources\", \"layout-icons\"))\n\n    def find_icon_file_path(self, layout_name):\n        icon_filename = \"layout-{}.png\".format(layout_name)\n        for icon_path in self.icon_paths:\n            icon_file_path = os.path.join(icon_path, icon_filename)\n            if os.path.isfile(icon_file_path):\n                return icon_file_path\n\n    def _setup_images(self):\n        \"\"\"\n        Loads layout icons.\n        \"\"\"\n        for layout_name in self._get_layout_names():\n            icon_file_path = self.find_icon_file_path(layout_name)\n            if icon_file_path is None:\n                logger.warning('No icon found for layout \"%s\"', layout_name)\n                icon_file_path = self.find_icon_file_path(\"unknown\")\n\n            try:\n                img = cairocffi.ImageSurface.create_from_png(icon_file_path)\n            except (cairocffi.Error, IOError) as e:\n                # Icon file is guaranteed to exist at this point.\n                # If this exception happens, it means the icon file contains\n                # an invalid image or is not readable.\n                self.icons_loaded = False\n                logger.exception(\n                    'Failed to load icon from file \"%s\", error was: %s', icon_file_path, e.message\n                )\n                return\n\n            input_width = img.get_width()\n            input_height = img.get_height()\n\n            sp = input_height / (self.bar.height - 1)\n\n            width = input_width / sp\n            if width > self.length:\n                self.length = int(width) + self.actual_padding * 2\n\n            imgpat = cairocffi.SurfacePattern(img)\n\n            scaler = cairocffi.Matrix()\n\n            scaler.scale(sp, sp)\n            scaler.scale(self.scale, self.scale)\n            factor = (1 - 1 / self.scale) / 2\n            scaler.translate(-width * factor, -width * factor)\n            scaler.translate(self.actual_padding * -1, 0)\n            imgpat.set_matrix(scaler)\n\n            imgpat.set_filter(cairocffi.FILTER_BEST)\n            self.surfaces[layout_name] = imgpat\n\n        self.icons_loaded = True\n", "path": "libqtile/widget/currentlayout.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2011 Florian Mounier\n# Copyright (c) 2011 Kenji_Takahashi\n# Copyright (c) 2012 roger\n# Copyright (c) 2012, 2014 Tycho Andersen\n# Copyright (c) 2012 Maximilian K\u00f6hl\n# Copyright (c) 2013 Craig Barnes\n# Copyright (c) 2014 Sean Vig\n# Copyright (c) 2014 Adi Sieker\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\nimport os\n\nimport cairocffi\n\nfrom libqtile import bar, hook\nfrom libqtile.log_utils import logger\nfrom libqtile.widget import base\n\n\nclass CurrentLayout(base._TextBox):\n    \"\"\"\n    Display the name of the current layout of the current group of the screen,\n    the bar containing the widget, is on.\n    \"\"\"\n\n    def __init__(self, width=bar.CALCULATED, **config):\n        base._TextBox.__init__(self, \"\", width, **config)\n\n    def _configure(self, qtile, bar):\n        base._TextBox._configure(self, qtile, bar)\n        layout_id = self.bar.screen.group.current_layout\n        self.text = self.bar.screen.group.layouts[layout_id].name\n        self.setup_hooks()\n\n        self.add_callbacks(\n            {\n                \"Button1\": qtile.cmd_next_layout,\n                \"Button2\": qtile.cmd_prev_layout,\n            }\n        )\n\n    def setup_hooks(self):\n        def hook_response(layout, group):\n            if group.screen is not None and group.screen == self.bar.screen:\n                self.text = layout.name\n                self.bar.draw()\n\n        hook.subscribe.layout_change(hook_response)\n\n\nclass CurrentLayoutIcon(base._TextBox):\n    \"\"\"\n    Display the icon representing the current layout of the\n    current group of the screen on which the bar containing the widget is.\n\n    If you are using custom layouts, a default icon with question mark\n    will be displayed for them. If you want to use custom icon for your own\n    layout, for example, `FooGrid`, then create a file named\n    \"layout-foogrid.png\" and place it in `~/.icons` directory. You can as well\n    use other directories, but then you need to specify those directories\n    in `custom_icon_paths` argument for this plugin.\n\n    The order of icon search is:\n\n    - dirs in `custom_icon_paths` config argument\n    - `~/.icons`\n    - built-in qtile icons\n    \"\"\"\n\n    orientations = base.ORIENTATION_HORIZONTAL\n\n    defaults = [\n        (\"scale\", 1, \"Scale factor relative to the bar height.  \" \"Defaults to 1\"),\n        (\n            \"custom_icon_paths\",\n            [],\n            \"List of folders where to search icons before\"\n            \"using built-in icons or icons in ~/.icons dir.  \"\n            \"This can also be used to provide\"\n            \"missing icons for custom layouts.  \"\n            \"Defaults to empty list.\",\n        ),\n    ]\n\n    def __init__(self, **config):\n        base._TextBox.__init__(self, \"\", **config)\n        self.add_defaults(CurrentLayoutIcon.defaults)\n        self.scale = 1.0 / self.scale\n\n        self.length_type = bar.STATIC\n        self.length = 0\n\n    def _configure(self, qtile, bar):\n        base._TextBox._configure(self, qtile, bar)\n        layout_id = self.bar.screen.group.current_layout\n        self.text = self.bar.screen.group.layouts[layout_id].name\n        self.current_layout = self.text\n        self.icons_loaded = False\n        self.icon_paths = []\n        self.surfaces = {}\n        self._update_icon_paths()\n        self._setup_images()\n        self._setup_hooks()\n\n        self.add_callbacks(\n            {\n                \"Button1\": qtile.cmd_next_layout,\n                \"Button2\": qtile.cmd_prev_layout,\n            }\n        )\n\n    def _setup_hooks(self):\n        \"\"\"\n        Listens for layout change and performs a redraw when it occurs.\n        \"\"\"\n\n        def hook_response(layout, group):\n            if group.screen is not None and group.screen == self.bar.screen:\n                self.current_layout = layout.name\n                self.bar.draw()\n\n        hook.subscribe.layout_change(hook_response)\n\n    def draw(self):\n        if self.icons_loaded:\n            try:\n                surface = self.surfaces[self.current_layout]\n            except KeyError:\n                logger.error(\"No icon for layout %s\", self.current_layout)\n            else:\n                self.drawer.clear(self.background or self.bar.background)\n                self.drawer.ctx.set_source(surface)\n                self.drawer.ctx.paint()\n                self.drawer.draw(offsetx=self.offset, offsety=self.offsety, width=self.length)\n        else:\n            # Fallback to text\n            self.text = self.current_layout[0].upper()\n            base._TextBox.draw(self)\n\n    def _get_layout_names(self):\n        \"\"\"\n        Returns a list of tuples of lowercased layout name and class name strings for each available layout.\n        \"\"\"\n        return [\n            (layout.name, layout.__class__.__name__.lower())\n            for layout in self.qtile.config.layouts\n        ]\n\n    def _update_icon_paths(self):\n        self.icon_paths = []\n\n        # We allow user to override icon search path\n        self.icon_paths.extend(self.custom_icon_paths)\n\n        # We also look in ~/.icons/ and ~/.local/share/icons\n        self.icon_paths.append(os.path.expanduser(\"~/.icons\"))\n        self.icon_paths.append(os.path.expanduser(\"~/.local/share/icons\"))\n\n        # Default icons are in libqtile/resources/layout-icons.\n        # If using default config without any custom icons,\n        # this path will be used.\n        root = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-2])\n        self.icon_paths.append(os.path.join(root, \"resources\", \"layout-icons\"))\n\n    def find_icon_file_path(self, layout_name):\n        icon_filename = \"layout-{}.png\".format(layout_name)\n        for icon_path in self.icon_paths:\n            icon_file_path = os.path.join(icon_path, icon_filename)\n            if os.path.isfile(icon_file_path):\n                return icon_file_path\n\n    def _setup_images(self):\n        \"\"\"\n        Loads layout icons.\n        \"\"\"\n        for names in self._get_layout_names():\n            layout_name = names[0]\n            # Python doesn't have an ordered set but we can use a dictionary instead\n            # First key is the layout's name (which may have been set by the user),\n            # the second is the class name. If these are the same (i.e. the user hasn't\n            # set a name) then there is only one key in the dictionary.\n            layouts = dict.fromkeys(names)\n            for layout in layouts.keys():\n                icon_file_path = self.find_icon_file_path(layout)\n                if icon_file_path:\n                    break\n            else:\n                logger.warning('No icon found for layout \"%s\"', layout_name)\n                icon_file_path = self.find_icon_file_path(\"unknown\")\n\n            try:\n                img = cairocffi.ImageSurface.create_from_png(icon_file_path)\n            except (cairocffi.Error, IOError) as e:\n                # Icon file is guaranteed to exist at this point.\n                # If this exception happens, it means the icon file contains\n                # an invalid image or is not readable.\n                self.icons_loaded = False\n                logger.exception(\n                    'Failed to load icon from file \"%s\", error was: %s', icon_file_path, e.message\n                )\n                return\n\n            input_width = img.get_width()\n            input_height = img.get_height()\n\n            sp = input_height / (self.bar.height - 1)\n\n            width = input_width / sp\n            if width > self.length:\n                self.length = int(width) + self.actual_padding * 2\n\n            imgpat = cairocffi.SurfacePattern(img)\n\n            scaler = cairocffi.Matrix()\n\n            scaler.scale(sp, sp)\n            scaler.scale(self.scale, self.scale)\n            factor = (1 - 1 / self.scale) / 2\n            scaler.translate(-width * factor, -width * factor)\n            scaler.translate(self.actual_padding * -1, 0)\n            imgpat.set_matrix(scaler)\n\n            imgpat.set_filter(cairocffi.FILTER_BEST)\n            self.surfaces[layout_name] = imgpat\n\n        self.icons_loaded = True\n", "path": "libqtile/widget/currentlayout.py"}]}