Skip to content

Commit

Permalink
Disabled container (#2776)
Browse files Browse the repository at this point in the history
* Add regression test for #2772.

* Remove focus on nested disabled widgets.

* Optimisation.

Related comments: #2776 (comment)

* Fix tests.
  • Loading branch information
rodrigogiraoserrao authored Jun 14, 2023
1 parent 436b118 commit 118e62d
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fixed exceptions in Pilot tests being silently ignored https://github.com/Textualize/textual/pull/2754
- Fixed issue where internal data of `OptionList` could be invalid for short window after `clear_options` https://github.com/Textualize/textual/pull/2754
- Fixed `Tooltip` causing a `query_one` on a lone `Static` to fail https://github.com/Textualize/textual/issues/2723
- Nested widgets wouldn't lose focus when parent is disabled https://github.com/Textualize/textual/issues/2772

### Changed

Expand Down
12 changes: 11 additions & 1 deletion src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2743,7 +2743,17 @@ def watch_has_focus(self, value: bool) -> None:

def watch_disabled(self) -> None:
"""Update the styles of the widget and its children when disabled is toggled."""
self.blur()
from .app import ScreenStackError

try:
if (
self.disabled
and self.app.focused is not None
and self in self.app.focused.ancestors_with_self
):
self.app.focused.blur()
except ScreenStackError:
pass
self._update_styles()

def _size_updated(
Expand Down
67 changes: 66 additions & 1 deletion tests/test_disabled.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
"""Test Widget.disabled."""

import pytest

from textual.app import App, ComposeResult
from textual.containers import VerticalScroll
from textual.containers import Vertical, VerticalScroll
from textual.widgets import (
Button,
Checkbox,
DataTable,
DirectoryTree,
Input,
Label,
ListItem,
ListView,
Markdown,
MarkdownViewer,
OptionList,
RadioButton,
RadioSet,
Select,
Switch,
TextLog,
Tree,
Expand Down Expand Up @@ -82,3 +91,59 @@ async def test_disable_via_container() -> None:
node.has_pseudo_class("disabled") and not node.has_pseudo_class("enabled")
for node in pilot.app.screen.query("#test-container > *")
)


class ChildrenNoFocusDisabledContainer(App[None]):
"""App for regression test for https://github.com/Textualize/textual/issues/2772."""

def compose(self) -> ComposeResult:
with Vertical():
with Vertical():
yield Button()
yield Checkbox()
yield DataTable()
yield DirectoryTree(".")
yield Input()
with ListView():
yield ListItem(Label("one"))
yield ListItem(Label("two"))
yield ListItem(Label("three"))
yield OptionList("one", "two", "three")
with RadioSet():
yield RadioButton("one")
yield RadioButton("two")
yield RadioButton("three")
yield Select([("one", 1), ("two", 2), ("three", 3)])
yield Switch()

def on_mount(self):
dt = self.query_one(DataTable)
dt.add_columns("one", "two", "three")
dt.add_rows([["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]])


@pytest.mark.parametrize(
"widget",
[
Button,
Checkbox,
DataTable,
DirectoryTree,
Input,
ListView,
OptionList,
RadioSet,
Select,
Switch,
],
)
async def test_children_loses_focus_if_container_is_disabled(widget):
"""Regression test for https://github.com/Textualize/textual/issues/2772."""
app = ChildrenNoFocusDisabledContainer()
async with app.run_test() as pilot:
app.query(widget).first().focus()
await pilot.pause()
assert isinstance(app.focused, widget)
app.query(Vertical).first().disabled = True
await pilot.pause()
assert app.focused is None

0 comments on commit 118e62d

Please sign in to comment.