mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #5267 from Textualize/loading-input-disable
disable loading widgets
This commit is contained in:
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
### Fixed
|
||||
|
||||
- Fixed offset applied to docked widgets https://github.com/Textualize/textual/pull/5264
|
||||
- Fixed loading widgets responding to input https://github.com/Textualize/textual/pull/5267
|
||||
|
||||
## [0.86.3] - 2024-11-19
|
||||
|
||||
|
||||
@@ -562,7 +562,7 @@ class Screen(Generic[ScreenResultType], Widget):
|
||||
except NoWidget:
|
||||
return None
|
||||
|
||||
if widget.has_class("-textual-system"):
|
||||
if widget.has_class("-textual-system") or widget.loading:
|
||||
# Clicking Textual system widgets should not focus anything
|
||||
return None
|
||||
|
||||
@@ -1421,6 +1421,8 @@ class Screen(Generic[ScreenResultType], Widget):
|
||||
if focusable_widget:
|
||||
self.set_focus(focusable_widget, scroll_visible=False)
|
||||
event.style = self.get_style_at(event.screen_x, event.screen_y)
|
||||
if widget.loading:
|
||||
return
|
||||
if widget is self:
|
||||
event._set_forwarded()
|
||||
self.post_message(event)
|
||||
|
||||
@@ -8,8 +8,10 @@ from rich.text import Text
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from textual.app import RenderResult
|
||||
|
||||
from textual import on
|
||||
from textual.color import Gradient
|
||||
from textual.events import Mount
|
||||
from textual.events import InputEvent, Mount
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
@@ -56,6 +58,12 @@ class LoadingIndicator(Widget):
|
||||
self._start_time = time()
|
||||
self.auto_refresh = 1 / 16
|
||||
|
||||
@on(InputEvent)
|
||||
def on_input(self, event: InputEvent) -> None:
|
||||
"""Prevent all input events from bubbling, thus disabling widgets in a loading state."""
|
||||
event.stop()
|
||||
event.prevent_default()
|
||||
|
||||
def render(self) -> RenderResult:
|
||||
if self.app.animation_level == "none":
|
||||
return Text("Loading...")
|
||||
|
||||
@@ -445,6 +445,45 @@ async def test_loading():
|
||||
assert label._cover_widget is None
|
||||
|
||||
|
||||
async def test_loading_button():
|
||||
"""Test loading indicator renders buttons unclickable."""
|
||||
|
||||
counter = 0
|
||||
|
||||
class LoadingApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Button("Hello, World", action="app.inc")
|
||||
|
||||
def action_inc(self) -> None:
|
||||
nonlocal counter
|
||||
counter += 1
|
||||
|
||||
async with LoadingApp().run_test() as pilot:
|
||||
# Sanity check
|
||||
assert counter == 0
|
||||
|
||||
button = pilot.app.query_one(Button)
|
||||
button.active_effect_duration = 0
|
||||
|
||||
# Click the button to advance the counter
|
||||
await pilot.click(button)
|
||||
assert counter == 1
|
||||
|
||||
# Set the button to loading state
|
||||
button.loading = True
|
||||
|
||||
# A click should do nothing
|
||||
await pilot.click(button)
|
||||
assert counter == 1
|
||||
|
||||
# Set the button to not loading
|
||||
button.loading = False
|
||||
|
||||
# Click should advance counter
|
||||
await pilot.click(button)
|
||||
assert counter == 2
|
||||
|
||||
|
||||
async def test_is_mounted_property():
|
||||
class TestWidgetIsMountedApp(App):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user