diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3085106dd..f43453c25 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Removed
- `Placeholder.reset_color_cycle`
+- Removed `Widget.reset_focus` (now called `Widget.blur`) https://github.com/Textualize/textual/issues/2642
## [0.26.0] - 2023-05-20
diff --git a/src/textual/app.py b/src/textual/app.py
index aca3be101..fe30f2e1b 100644
--- a/src/textual/app.py
+++ b/src/textual/app.py
@@ -2075,7 +2075,7 @@ class App(Generic[ReturnType], DOMNode):
Args:
widget: A Widget to unregister
"""
- widget.reset_focus()
+ widget.blur()
if isinstance(widget._parent, Widget):
widget._parent._nodes._remove(widget)
widget._detach()
diff --git a/src/textual/screen.py b/src/textual/screen.py
index 796f9eb84..29c6ab95b 100644
--- a/src/textual/screen.py
+++ b/src/textual/screen.py
@@ -386,6 +386,7 @@ class Screen(Generic[ScreenResultType], Widget):
focusable_widgets = self.focus_chain
if not focusable_widgets:
# If there's nothing to focus... give up now.
+ self.set_focus(None)
return
try:
diff --git a/src/textual/widget.py b/src/textual/widget.py
index d1e7bc64f..a67c3fe27 100644
--- a/src/textual/widget.py
+++ b/src/textual/widget.py
@@ -2712,6 +2712,7 @@ class Widget(DOMNode):
def watch_disabled(self) -> None:
"""Update the styles of the widget and its children when disabled is toggled."""
+ self.blur()
self._update_styles()
def _size_updated(
@@ -3017,8 +3018,10 @@ class Widget(DOMNode):
self.app.call_later(set_focus, self)
return self
- def reset_focus(self) -> Self:
- """Reset the focus (move it to the next available widget).
+ def blur(self) -> Self:
+ """Blur (un-focus) the widget.
+
+ Focus will be moved to the next available widget in the focus chain..
Returns:
The `Widget` instance.
@@ -3172,7 +3175,7 @@ class Widget(DOMNode):
def _on_hide(self, event: events.Hide) -> None:
if self.has_focus:
- self.reset_focus()
+ self.blur()
def _on_scroll_to_region(self, message: messages.ScrollToRegion) -> None:
self.scroll_to_region(message.region, animate=True)
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
index e39be06d6..7013a574a 100644
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -709,6 +709,164 @@
'''
# ---
+# name: test_blur_on_disabled
+ '''
+
+
+ '''
+# ---
# name: test_border_alpha
'''
diff --git a/tests/snapshot_tests/snapshot_apps/blur_on_disabled.py b/tests/snapshot_tests/snapshot_apps/blur_on_disabled.py
new file mode 100644
index 000000000..3bfa0e122
--- /dev/null
+++ b/tests/snapshot_tests/snapshot_apps/blur_on_disabled.py
@@ -0,0 +1,20 @@
+from textual.app import App, ComposeResult
+from textual.widgets import Input
+
+
+class BlurApp(App):
+ BINDINGS = [("f3", "disable")]
+
+ def compose(self) -> ComposeResult:
+ yield Input()
+
+ def on_ready(self) -> None:
+ self.query_one(Input).focus()
+
+ def action_disable(self) -> None:
+ self.query_one(Input).disabled = True
+
+
+if __name__ == "__main__":
+ app = BlurApp()
+ app.run()
diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py
index 874f096d9..fd22c0ca3 100644
--- a/tests/snapshot_tests/test_snapshots.py
+++ b/tests/snapshot_tests/test_snapshots.py
@@ -514,3 +514,11 @@ def test_select_rebuild(snap_compare):
SNAPSHOT_APPS_DIR / "select_rebuild.py",
press=["space", "escape", "tab", "enter", "tab", "space"],
)
+
+
+def test_blur_on_disabled(snap_compare):
+ # https://github.com/Textualize/textual/issues/2641
+ assert snap_compare(
+ SNAPSHOT_APPS_DIR / "blur_on_disabled.py",
+ press=[*"foo", "f3", *"this should not appear"],
+ )