Repository: Textualize/textual

Issue:
Revise tab-to-focus
We have a system that shows a highlight for the focused widget for 2 seconds. When you first press tab it will show the current focus, and a second tab will advance it.

In retrospect, and after a discussion, we should remove this.

It would require that anything focusable have an explicit :focus style. 


Discussion:
I think it would be nice to keep the means of "querying" where you currently are. In case you step away, come back, and get lost. Some apps might not make it too obvious, so it'd be nice to provide an easy way for apps to show you where you are.

Resolving patch:
diff --git a/src/textual/app.py b/src/textual/app.py
--- a/src/textual/app.py
+++ b/src/textual/app.py
@@ -192,7 +192,6 @@ def __init__(
         self.registry: set[MessagePump] = set()
         self.devtools = DevtoolsClient()
         self._return_value: ReturnType | None = None
-        self._focus_timer: Timer | None = None
 
         self.css_monitor = (
             FileMonitor(css_path, self._on_css_change)
@@ -263,10 +262,6 @@ def _move_focus(self, direction: int = 0) -> Widget | None:
         Returns:
             Widget | None: Newly focused widget, or None for no focus.
         """
-        if self._focus_timer:
-            # Cancel the timer that clears the show focus class
-            # We will be creating a new timer to extend the time until the focus is hidden
-            self._focus_timer.stop_no_wait()
         focusable_widgets = self.focus_chain
 
         if not focusable_widgets:
@@ -284,12 +279,10 @@ def _move_focus(self, direction: int = 0) -> Widget | None:
                 self.set_focus(focusable_widgets[0])
             else:
                 # Only move the focus if we are currently showing the focus
-                if direction and self.has_class("-show-focus"):
+                if direction:
                     current_index = (current_index + direction) % len(focusable_widgets)
                     self.set_focus(focusable_widgets[current_index])
 
-        self._focus_timer = self.set_timer(2, self.hide_focus)
-        self.add_class("-show-focus")
         return self.focused
 
     def show_focus(self) -> Widget | None:
@@ -316,15 +309,6 @@ def focus_previous(self) -> Widget | None:
         """
         return self._move_focus(-1)
 
-    def hide_focus(self) -> None:
-        """Hide the focus.
-
-        Returns:
-            Widget | None: Newly focused widget, or None for no focus.
-
-        """
-        self.remove_class("-show-focus")
-
     def compose(self) -> ComposeResult:
         """Yield child widgets for a container."""
         return


Test patch:
diff --git a/tests/test_focus.py b/tests/test_focus.py
--- a/tests/test_focus.py
+++ b/tests/test_focus.py
@@ -31,26 +31,6 @@ async def test_focus_chain():
     assert focused == ["foo", "Paul", "baz"]
 
 
-async def test_show_focus():
-    app = App()
-    app.push_screen(Screen())
-    app.screen.add_children(
-        Focusable(id="foo"),
-        NonFocusable(id="bar"),
-        Focusable(Focusable(id="Paul"), id="container1"),
-        NonFocusable(Focusable(id="Jessica"), id="container2"),
-        Focusable(id="baz"),
-    )
-
-    focused = [widget.id for widget in app.focus_chain]
-    assert focused == ["foo", "Paul", "baz"]
-
-    assert app.focused is None
-    assert not app.has_class("-show-focus")
-    app.show_focus()
-    assert app.has_class("-show-focus")
-
-
 async def test_focus_next_and_previous():
 
     app = App()