Skip to content

Commit 4c6a248

Browse files
committed
binding order
1 parent fb88c07 commit 4c6a248

File tree

4 files changed

+50
-7
lines changed

4 files changed

+50
-7
lines changed

src/textual/binding.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class Binding:
8383
"""
8484
system: bool = False
8585
"""Make this binding a system binding, which removes it from the key panel."""
86+
order_group: str | None = None
87+
"""Group used in sort, or `None` for default."""
8688

8789
@dataclass(frozen=True)
8890
class Group:
@@ -94,6 +96,9 @@ class Group:
9496
compact: bool = False
9597
"""Show keys in compact form (no spaces)."""
9698

99+
order_group: str | None = None
100+
"""Group used in sort, or `None` for default."""
101+
97102
group: Group | None = None
98103
"""Optional binding group (used to group related bindings in the footer)."""
99104

@@ -165,6 +170,7 @@ def make_bindings(cls, bindings: Iterable[BindingType]) -> Iterable[Binding]:
165170
id=binding.id,
166171
system=binding.system,
167172
group=binding.group,
173+
order_group=binding.order_group,
168174
)
169175

170176

@@ -188,6 +194,7 @@ class BindingsMap:
188194
def __init__(
189195
self,
190196
bindings: Iterable[BindingType] | None = None,
197+
order_group: str = "default",
191198
) -> None:
192199
"""Initialise a collection of bindings.
193200

src/textual/dom.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ class DOMNode(MessagePump):
144144
BINDING_GROUP_TITLE: str | None = None
145145
"""Title of widget used where bindings are displayed (such as in the key panel)."""
146146

147+
BINDING_SORT_GROUP: str = "default"
148+
"""Binding sort group, if not specified in BINDINGS. Used to define Footer sort order."""
149+
147150
BINDINGS: ClassVar[list[BindingType]] = []
148151
"""A list of key bindings."""
149152

@@ -676,11 +679,7 @@ def _merge_bindings(cls) -> BindingsMap:
676679
if issubclass(base, DOMNode):
677680
if not base._inherit_bindings:
678681
bindings.clear()
679-
bindings.append(
680-
BindingsMap(
681-
base.__dict__.get("BINDINGS", []),
682-
)
683-
)
682+
bindings.append(BindingsMap(base.__dict__.get("BINDINGS", [])))
684683

685684
keys: dict[str, list[Binding]] = {}
686685
for bindings_ in bindings:

src/textual/screen.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,9 @@ def allow_select(self) -> bool:
558558
"""Check if this widget permits text selection."""
559559
return self.ALLOW_SELECT
560560

561+
def get_binding_sort_order(self) -> list[str]:
562+
return ["default"]
563+
561564
def get_loading_widget(self) -> Widget:
562565
"""Get a widget to display a loading indicator.
563566

src/textual/widgets/_footer.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ class KeyGroup(HorizontalGroup):
2727
}
2828
"""
2929

30+
def __init__(
31+
self,
32+
id: str | None = None,
33+
classes: str | None = None,
34+
order_group: str = "default",
35+
):
36+
self.order_group = order_group
37+
super().__init__(id=id, classes=classes)
38+
3039

3140
@rich.repr.auto
3241
class FooterKey(Widget):
@@ -87,11 +96,13 @@ def __init__(
8796
disabled: bool = False,
8897
tooltip: str = "",
8998
classes="",
99+
order_group: str = "default",
90100
) -> None:
91101
self.key = key
92102
self.key_display = key_display
93103
self.description = description
94104
self.action = action
105+
self.order_group = order_group
95106
self._disabled = disabled
96107
if disabled:
97108
classes += " -disabled"
@@ -284,7 +295,12 @@ def compose(self) -> ComposeResult:
284295
):
285296
multi_bindings = list(multi_bindings_iterable)
286297
if group is not None and len(multi_bindings) > 1:
287-
with KeyGroup(classes="-compact" if group.compact else ""):
298+
with KeyGroup(
299+
classes="-compact" if group.compact else "",
300+
order_group=(
301+
"default" if group.order_group is None else group.order_group
302+
),
303+
):
288304
for multi_bindings in multi_bindings:
289305
binding, enabled, tooltip = multi_bindings[0]
290306
yield FooterKey(
@@ -296,7 +312,7 @@ def compose(self) -> ComposeResult:
296312
tooltip=tooltip or binding.description,
297313
classes="-grouped",
298314
).data_bind(compact=Footer.compact)
299-
yield FooterLabel(group.description)
315+
yield FooterLabel(group.description)
300316
else:
301317
for multi_bindings in multi_bindings:
302318
binding, enabled, tooltip = multi_bindings[0]
@@ -307,6 +323,11 @@ def compose(self) -> ComposeResult:
307323
binding.action,
308324
disabled=not enabled,
309325
tooltip=tooltip,
326+
order_group=(
327+
"default"
328+
if binding.order_group is None
329+
else binding.order_group
330+
),
310331
).data_bind(compact=Footer.compact)
311332
if self.show_command_palette and self.app.ENABLE_COMMAND_PALETTE:
312333
try:
@@ -324,8 +345,21 @@ def compose(self) -> ComposeResult:
324345
classes="-command-palette",
325346
disabled=not enabled,
326347
tooltip=binding.tooltip or binding.description,
348+
order_group=(
349+
"default"
350+
if binding.order_group is None
351+
else binding.order_group
352+
),
327353
)
328354

355+
sort_order = {
356+
group: index
357+
for index, group in enumerate(self.screen.get_binding_sort_order())
358+
}
359+
self.sort_children(
360+
key=lambda widget: sort_order.get(sort_order.get(widget.order_group, 0))
361+
)
362+
329363
def bindings_changed(self, screen: Screen) -> None:
330364
self._bindings_ready = True
331365
if not screen.app.app_focus:

0 commit comments

Comments
 (0)