Repository: qtile/qtile

Issue:
window.cmd_focus() and hook.fire("focus_change") do not update border
qtile version: `0.21.0`

I am trying to activate a window through a script (the relevant part of which can be viewed [here](https://codeberg.org/surrealcereal/Qmin/src/branch/main/Qmin.py#L164)), but cannot get the window to focus with both the border and the `WindowName` widget updating.

The related keybind in the config below is pressed at the beginning of each video.

Without `hook.fire("focus_change")`, the `WindowName` does not update: [video](https://i.imgur.com/Oo9tzWE.mp4).
With it, it does; but the border does not: [video](https://i.imgur.com/wDwKfgl.mp4).

The barebones config used in this demonstration, with `hook.fire("focus_change")` being commented out for the relevant demo:
```py
from libqtile.config import Screen, Key
from libqtile.bar import Bar
from libqtile import widget, qtile
from libqtile import hook
from libqtile.command import lazy

ID = 27263032

@lazy.function
def demo(qtile):
    window = qtile.windows_map[ID]
    window.cmd_focus()
    hook.fire("focus_change")

if __name__ in ["config", "__main__"]:
    keys = [
        Key(["mod4"], "u",
            demo(),
        ),
        Key(["mod4"], "r",
            lazy.restart(),
        ),
        Key(["mod4"], "Return",
            lazy.spawn("alacritty -e /bin/zsh"),
        ),
        Key(["mod4"], "w",
            lazy.window.kill(),
        ),
    ]
    screens = [
        Screen(top=Bar(widgets=[widget.WindowName()], size=20)),
        Screen(top=Bar(widgets=[widget.WindowName()], size=20))
    ]
```
[The related reddit post and thread with elParaguayo, who instructed me to create this issue.](https://www.reddit.com/r/qtile/comments/wirt0i/move_focus_in_between_windows_gracefully_using_a/)

Edits are me fleshing out the issue.

### Required:

- [X] I have searched past issues to see if this bug has already been reported.


Discussion:
I need to take a proper look at this but, from my own test, running `qtile cmd-obj -o window window_id -f focus` did not successfully change the focus (window border was still on the current window, window title widget didn't change).
Not sure if this is related @elParaguayo but I'm having issues with this command in my window switcher `client.window[selected_option].focus()` ,the window switcher doesn't switch focus to the window on the same group first time round i have to run it a second time then it goes ?
What @justinesmithies talked about does not work in my case, multiple triggerings of the `focus()` method does not produce different results.
[Video taken with the same `config.py` as in the first comment](https://i.imgur.com/9FlWoXX.mp4)

Resolving patch:
diff --git a/libqtile/backend/wayland/window.py b/libqtile/backend/wayland/window.py
--- a/libqtile/backend/wayland/window.py
+++ b/libqtile/backend/wayland/window.py
@@ -267,8 +267,8 @@ def focus(self, warp: bool = True) -> None:
                 self.y + self._height / 2,
             )
 
-        if self.group:
-            self.group.current_window = self
+        if self.group and self.group.current_window is not self:
+            self.group.focus(self)
 
         hook.fire("client_focus", self)
 
diff --git a/libqtile/backend/x11/window.py b/libqtile/backend/x11/window.py
--- a/libqtile/backend/x11/window.py
+++ b/libqtile/backend/x11/window.py
@@ -1327,12 +1327,12 @@ def focus(self, warp: bool = True) -> None:
         self.qtile.core._root.set_property("_NET_ACTIVE_WINDOW", self.window.wid)
         self._ungrab_click()
 
-        if self.group:
-            self.group.current_window = self
-
         # Check if we need to restack a previously focused fullscreen window
         self.qtile.core.check_stacking(self)
 
+        if self.group and self.group.current_window is not self:
+            self.group.focus(self)
+
         hook.fire("client_focus", self)
 
     @expose_command()


Test patch:
diff --git a/test/test_window.py b/test/test_window.py
--- a/test/test_window.py
+++ b/test/test_window.py
@@ -1,6 +1,6 @@
 import pytest
 
-from libqtile import config, layout, resources
+from libqtile import bar, config, layout, resources, widget
 from libqtile.confreader import Config
 from test.conftest import BareConfig, dualmonitor
 from test.layouts.layout_utils import assert_focused
@@ -305,3 +305,31 @@ def test_set_position(manager):
 
     # Also check if there is only one client now
     assert len(manager.c.layout.info()["clients"]) == 1
+
+
+class WindowNameConfig(BareConfig):
+    screens = [
+        config.Screen(
+            bottom=bar.Bar(
+                [
+                    widget.WindowName(),
+                ],
+                20,
+            ),
+        ),
+    ]
+    layouts = [layout.Columns()]
+
+
+@pytest.mark.parametrize("manager", [WindowNameConfig], indirect=True)
+def test_focus_switch(manager):
+    def _wnd(name):
+        return manager.c.window[{w["name"]: w["id"] for w in manager.c.windows()}[name]]
+
+    manager.test_window("One")
+    manager.test_window("Two")
+
+    assert manager.c.widget["windowname"].info()["text"] == "Two"
+
+    _wnd("One").focus()
+    assert manager.c.widget["windowname"].info()["text"] == "One"