mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Added SkipAction exception
This commit is contained in:
@@ -4,6 +4,10 @@ import ast
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class SkipAction(Exception):
|
||||||
|
"""Raise in an action to skip the action (and allow any parent bindings to run)."""
|
||||||
|
|
||||||
|
|
||||||
class ActionError(Exception):
|
class ActionError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from rich.protocol import is_renderable
|
|||||||
from rich.segment import Segment, Segments
|
from rich.segment import Segment, Segments
|
||||||
from rich.traceback import Traceback
|
from rich.traceback import Traceback
|
||||||
|
|
||||||
from . import Logger, LogGroup, LogVerbosity, actions, events, log, messages
|
from . import actions, Logger, LogGroup, LogVerbosity, events, log, messages
|
||||||
from ._animator import DEFAULT_EASING, Animatable, Animator, EasingFunction
|
from ._animator import DEFAULT_EASING, Animatable, Animator, EasingFunction
|
||||||
from ._ansi_sequences import SYNC_END, SYNC_START
|
from ._ansi_sequences import SYNC_END, SYNC_START
|
||||||
from ._callback import invoke
|
from ._callback import invoke
|
||||||
@@ -47,6 +47,7 @@ from ._event_broker import NoHandler, extract_handler_actions
|
|||||||
from ._filter import LineFilter, Monochrome
|
from ._filter import LineFilter, Monochrome
|
||||||
from ._path import _make_path_object_relative
|
from ._path import _make_path_object_relative
|
||||||
from ._typing import Final, TypeAlias
|
from ._typing import Final, TypeAlias
|
||||||
|
from .actions import SkipAction
|
||||||
from .await_remove import AwaitRemove
|
from .await_remove import AwaitRemove
|
||||||
from .binding import Binding, Bindings
|
from .binding import Binding, Bindings
|
||||||
from .css.query import NoMatches
|
from .css.query import NoMatches
|
||||||
@@ -1752,7 +1753,7 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
):
|
):
|
||||||
binding = bindings.keys.get(key)
|
binding = bindings.keys.get(key)
|
||||||
if binding is not None and binding.priority == priority:
|
if binding is not None and binding.priority == priority:
|
||||||
if await self.action(binding.action, namespace) in (True, None):
|
if await self.action(binding.action, namespace):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -1822,30 +1823,41 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
async def _dispatch_action(
|
async def _dispatch_action(
|
||||||
self, namespace: object, action_name: str, params: Any
|
self, namespace: object, action_name: str, params: Any
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
"""Dispatch an action to an action method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
namespace (object): Namespace (object) of action.
|
||||||
|
action_name (str): Name of the action.
|
||||||
|
params (Any): Action parameters.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if handled, otherwise False.
|
||||||
|
"""
|
||||||
|
_rich_traceback_guard = True
|
||||||
|
|
||||||
log(
|
log(
|
||||||
"<action>",
|
"<action>",
|
||||||
namespace=namespace,
|
namespace=namespace,
|
||||||
action_name=action_name,
|
action_name=action_name,
|
||||||
params=params,
|
params=params,
|
||||||
)
|
)
|
||||||
_rich_traceback_guard = True
|
|
||||||
|
|
||||||
public_method_name = f"action_{action_name}"
|
try:
|
||||||
private_method_name = f"_{public_method_name}"
|
private_method = getattr(namespace, f"_action_{action_name}", None)
|
||||||
|
if callable(private_method):
|
||||||
private_method = getattr(namespace, private_method_name, None)
|
await invoke(private_method, *params)
|
||||||
public_method = getattr(namespace, public_method_name, None)
|
return True
|
||||||
|
public_method = getattr(namespace, f"action_{action_name}", None)
|
||||||
if private_method is None and public_method is None:
|
if callable(public_method):
|
||||||
|
await invoke(public_method, *params)
|
||||||
|
return True
|
||||||
log(
|
log(
|
||||||
f"<action> {action_name!r} has no target. Couldn't find methods {public_method_name!r} or {private_method_name!r}"
|
f"<action> {action_name!r} has no target."
|
||||||
|
f" Could not find methods '_action_{action_name}' or 'action_{action_name}'"
|
||||||
)
|
)
|
||||||
|
except SkipAction:
|
||||||
if callable(private_method):
|
# The action method raised this to explicitly not handle the action
|
||||||
return await invoke(private_method, *params)
|
log("<action> {action_name!r} skipped.")
|
||||||
elif callable(public_method):
|
|
||||||
return await invoke(public_method, *params)
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def _broker_event(
|
async def _broker_event(
|
||||||
@@ -1856,7 +1868,7 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
Args:
|
Args:
|
||||||
event_name (str): _description_
|
event_name (str): _description_
|
||||||
event (events.Event): An event object.
|
event (events.Event): An event object.
|
||||||
default_namespace (object | None): TODO: _description_
|
default_namespace (object | None): The default namespace, where one isn't supplied.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if an action was processed.
|
bool: True if an action was processed.
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ from ._layout import Layout
|
|||||||
from ._segment_tools import align_lines
|
from ._segment_tools import align_lines
|
||||||
from ._styles_cache import StylesCache
|
from ._styles_cache import StylesCache
|
||||||
from ._types import Lines
|
from ._types import Lines
|
||||||
|
from .actions import SkipAction
|
||||||
from .await_remove import AwaitRemove
|
from .await_remove import AwaitRemove
|
||||||
from .binding import Binding
|
from .binding import Binding
|
||||||
from .box_model import BoxModel, get_box_model
|
from .box_model import BoxModel, get_box_model
|
||||||
@@ -2403,42 +2404,42 @@ class Widget(DOMNode):
|
|||||||
def _on_scroll_to_region(self, message: messages.ScrollToRegion) -> None:
|
def _on_scroll_to_region(self, message: messages.ScrollToRegion) -> None:
|
||||||
self.scroll_to_region(message.region, animate=True)
|
self.scroll_to_region(message.region, animate=True)
|
||||||
|
|
||||||
def action_scroll_home(self) -> bool | None:
|
def action_scroll_home(self) -> None:
|
||||||
if not self._allow_scroll:
|
if not self._allow_scroll:
|
||||||
return False
|
raise SkipAction()
|
||||||
self.scroll_home()
|
self.scroll_home()
|
||||||
|
|
||||||
def action_scroll_end(self) -> bool | None:
|
def action_scroll_end(self) -> None:
|
||||||
if not self._allow_scroll:
|
if not self._allow_scroll:
|
||||||
return False
|
raise SkipAction()
|
||||||
self.scroll_end()
|
self.scroll_end()
|
||||||
|
|
||||||
def action_scroll_left(self) -> bool | None:
|
def action_scroll_left(self) -> None:
|
||||||
if not self.allow_horizontal_scroll:
|
if not self.allow_horizontal_scroll:
|
||||||
return False
|
raise SkipAction()
|
||||||
self.scroll_left()
|
self.scroll_left()
|
||||||
|
|
||||||
def action_scroll_right(self) -> bool | None:
|
def action_scroll_right(self) -> None:
|
||||||
if not self.allow_horizontal_scroll:
|
if not self.allow_horizontal_scroll:
|
||||||
return False
|
raise SkipAction()
|
||||||
self.scroll_right()
|
self.scroll_right()
|
||||||
|
|
||||||
def action_scroll_up(self) -> bool | None:
|
def action_scroll_up(self) -> None:
|
||||||
if not self.allow_vertical_scroll:
|
if not self.allow_vertical_scroll:
|
||||||
return False
|
raise SkipAction()
|
||||||
self.scroll_up()
|
self.scroll_up()
|
||||||
|
|
||||||
def action_scroll_down(self) -> bool | None:
|
def action_scroll_down(self) -> None:
|
||||||
if not self.allow_vertical_scroll:
|
if not self.allow_vertical_scroll:
|
||||||
return False
|
raise SkipAction()
|
||||||
self.scroll_down()
|
self.scroll_down()
|
||||||
|
|
||||||
def action_page_down(self) -> bool | None:
|
def action_page_down(self) -> None:
|
||||||
if not self.allow_vertical_scroll:
|
if not self.allow_vertical_scroll:
|
||||||
return False
|
raise SkipAction()
|
||||||
self.scroll_page_down()
|
self.scroll_page_down()
|
||||||
|
|
||||||
def action_page_up(self) -> bool | None:
|
def action_page_up(self) -> None:
|
||||||
if not self.allow_vertical_scroll:
|
if not self.allow_vertical_scroll:
|
||||||
return False
|
raise SkipAction()
|
||||||
self.scroll_page_up()
|
self.scroll_page_up()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ background relating to this.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from textual.actions import SkipAction
|
||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.binding import Binding
|
from textual.binding import Binding
|
||||||
from textual.containers import Container
|
from textual.containers import Container
|
||||||
@@ -626,7 +627,7 @@ async def test_skip_action() -> None:
|
|||||||
def action_test(self, text: str) -> bool:
|
def action_test(self, text: str) -> bool:
|
||||||
nonlocal no_handle_invoked
|
nonlocal no_handle_invoked
|
||||||
no_handle_invoked = True
|
no_handle_invoked = True
|
||||||
return False
|
raise SkipAction()
|
||||||
|
|
||||||
class SkipApp(App):
|
class SkipApp(App):
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
|
|||||||
Reference in New Issue
Block a user