Skip to content

Commit eb4c7ef

Browse files
authored
Fix issue with modals (#2195)
* Fix issue with modals * changelog * fix binding on button * binding tweak * changelog * snapshots * version bump
1 parent 4fcf44c commit eb4c7ef

File tree

10 files changed

+424
-43
lines changed

10 files changed

+424
-43
lines changed

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8-
## Unreleased
8+
## [0.17.2] - 2023-04-02
99

10-
### Fixed
10+
### [Fixed]
1111

1212
- Fixed bindings persistance https://github.com/Textualize/textual/issues/1613
1313
- The `Markdown` widget now auto-increments ordered lists https://github.com/Textualize/textual/issues/2002
14+
- Fixed modal bindings https://github.com/Textualize/textual/issues/2194
15+
- Fix binding enter to active button https://github.com/Textualize/textual/issues/2194
16+
17+
### [Changed]
18+
19+
- tab and shift+tab are now defined on Screen.
1420

1521
## [0.17.1] - 2023-03-30
1622

@@ -687,6 +693,9 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040
687693
- New handler system for messages that doesn't require inheritance
688694
- Improved traceback handling
689695

696+
[0.17.2]: https://github.com/Textualize/textual/compare/v0.17.1...v0.17.2
697+
[0.17.1]: https://github.com/Textualize/textual/compare/v0.17.0...v0.17.1
698+
[0.17.0]: https://github.com/Textualize/textual/compare/v0.16.0...v0.17.0
690699
[0.16.0]: https://github.com/Textualize/textual/compare/v0.15.1...v0.16.0
691700
[0.15.1]: https://github.com/Textualize/textual/compare/v0.15.0...v0.15.1
692701
[0.15.0]: https://github.com/Textualize/textual/compare/v0.14.0...v0.15.0

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "textual"
3-
version = "0.17.1"
3+
version = "0.17.2"
44
homepage = "https://github.com/Textualize/textual"
55
description = "Modern Text User Interface framework"
66
authors = ["Will McGugan <[email protected]>"]

src/textual/app.py

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,7 @@ class App(Generic[ReturnType], DOMNode):
279279
also the `sub_title` attribute.
280280
"""
281281

282-
BINDINGS = [
283-
Binding("ctrl+c", "quit", "Quit", show=False, priority=True),
284-
Binding("tab", "focus_next", "Focus Next", show=False),
285-
Binding("shift+tab", "focus_previous", "Focus Previous", show=False),
286-
]
282+
BINDINGS = [Binding("ctrl+c", "quit", "Quit", show=False, priority=True)]
287283

288284
title: Reactive[str] = Reactive("", compute=False)
289285
sub_title: Reactive[str] = Reactive("", compute=False)
@@ -1961,38 +1957,34 @@ def bell(self) -> None:
19611957
@property
19621958
def _binding_chain(self) -> list[tuple[DOMNode, Bindings]]:
19631959
"""Get a chain of nodes and bindings to consider.
1960+
19641961
If no widget is focused, returns the bindings from both the screen and the app level bindings.
19651962
Otherwise, combines all the bindings from the currently focused node up the DOM to the root App.
1966-
1967-
Returns:
1968-
List of DOM nodes and their bindings.
19691963
"""
19701964
focused = self.focused
19711965
namespace_bindings: list[tuple[DOMNode, Bindings]]
1972-
screen = self.screen
19731966

19741967
if focused is None:
1975-
if screen.is_modal:
1976-
namespace_bindings = [
1977-
(self.screen, self.screen._bindings),
1978-
]
1979-
else:
1980-
namespace_bindings = [
1981-
(self.screen, self.screen._bindings),
1982-
(self, self._bindings),
1983-
]
1968+
namespace_bindings = [
1969+
(self.screen, self.screen._bindings),
1970+
(self, self._bindings),
1971+
]
19841972
else:
1985-
if screen.is_modal:
1986-
namespace_bindings = [
1987-
(node, node._bindings) for node in focused.ancestors
1988-
]
1989-
else:
1990-
namespace_bindings = [
1991-
(node, node._bindings) for node in focused.ancestors_with_self
1992-
]
1973+
namespace_bindings = [
1974+
(node, node._bindings) for node in focused.ancestors_with_self
1975+
]
19931976

19941977
return namespace_bindings
19951978

1979+
@property
1980+
def _modal_binding_chain(self) -> list[tuple[DOMNode, Bindings]]:
1981+
"""The binding chain, ignoring everything before the last modal."""
1982+
binding_chain = self._binding_chain
1983+
for index, (node, _bindings) in enumerate(binding_chain, 1):
1984+
if node.is_modal:
1985+
return binding_chain[:index]
1986+
return binding_chain
1987+
19961988
async def check_bindings(self, key: str, priority: bool = False) -> bool:
19971989
"""Handle a key press.
19981990
@@ -2004,7 +1996,7 @@ async def check_bindings(self, key: str, priority: bool = False) -> bool:
20041996
True if the key was handled by a binding, otherwise False
20051997
"""
20061998
for namespace, bindings in (
2007-
reversed(self._binding_chain) if priority else self._binding_chain
1999+
reversed(self._binding_chain) if priority else self._modal_binding_chain
20082000
):
20092001
binding = bindings.keys.get(key)
20102002
if binding is not None and binding.priority == priority:

src/textual/dom.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,11 @@ def auto_refresh(self, interval: float | None) -> None:
208208
)
209209
self._auto_refresh = interval
210210

211+
@property
212+
def is_modal(self) -> bool:
213+
"""Is the node a modal?"""
214+
return False
215+
211216
def _automatic_refresh(self) -> None:
212217
"""Perform an automatic refresh (set with auto_refresh property)."""
213218
self.refresh()

src/textual/screen.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from ._compositor import Compositor, MapGeometry
1212
from ._context import visible_screen_stack
1313
from ._types import CallbackType
14+
from .binding import Binding
1415
from .css.match import match
1516
from .css.parse import parse_selectors
1617
from .css.query import QueryType
@@ -45,6 +46,11 @@ class Screen(Widget):
4546
stack_updates: Reactive[int] = Reactive(0, repaint=False)
4647
"""An integer that updates when the screen is resumed."""
4748

49+
BINDINGS = [
50+
Binding("tab", "focus_next", "Focus Next", show=False),
51+
Binding("shift+tab", "focus_previous", "Focus Previous", show=False),
52+
]
53+
4854
def __init__(
4955
self,
5056
name: str | None = None,

src/textual/widgets/_button.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing_extensions import Literal, Self
88

99
from .. import events
10+
from ..binding import Binding
1011
from ..css._error_tools import friendly_list
1112
from ..message import Message
1213
from ..reactive import reactive
@@ -145,6 +146,8 @@ class Button(Static, can_focus=True):
145146
146147
"""
147148

149+
BINDINGS = [Binding("enter", "press", "Press Button", show=False)]
150+
148151
ACTIVE_EFFECT_DURATION = 0.3
149152
"""When buttons are clicked they get the `-active` class for this duration (in seconds)"""
150153

@@ -252,10 +255,9 @@ def _start_active_affect(self) -> None:
252255
self.ACTIVE_EFFECT_DURATION, partial(self.remove_class, "-active")
253256
)
254257

255-
async def _on_key(self, event: events.Key) -> None:
256-
if event.key == "enter" and not self.disabled:
257-
self._start_active_affect()
258-
self.post_message(Button.Pressed(self))
258+
def action_press(self) -> None:
259+
"""Activate a press if"""
260+
self.press()
259261

260262
@classmethod
261263
def success(

0 commit comments

Comments
 (0)