Spaces:
Runtime error
Runtime error
| """Custom Footer widget that preserves navigation binding order (1-6 first, quit last). | |
| Always renders bindings — never disappears on screen transitions or resize. | |
| """ | |
| from __future__ import annotations | |
| from collections import defaultdict | |
| from itertools import groupby | |
| from textual.app import ComposeResult | |
| from textual.screen import Screen | |
| from textual.widgets import Footer | |
| from textual.widgets._footer import FooterKey | |
| from textual.binding import Binding | |
| class OrderedFooter(Footer): | |
| """Footer that shows navigation bindings (1-6) first, then quit, then other bindings.""" | |
| NAV_ORDER = {"1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5, "7": 6} | |
| QUIT_KEY = "ctrl+q" | |
| def compose(self) -> ComposeResult: | |
| try: | |
| active_bindings = self.screen.active_bindings | |
| except Exception: | |
| return | |
| bindings = [ | |
| (binding, enabled, tooltip) | |
| for (_, binding, enabled, tooltip) in active_bindings.values() | |
| if binding.show | |
| ] | |
| if not bindings: | |
| return | |
| # Sort: nav keys (1-6) first in order, then ctrl+q, then everything else | |
| def sort_key(item): | |
| binding, enabled, tooltip = item | |
| key = binding.key | |
| if key in self.NAV_ORDER: | |
| return (0, self.NAV_ORDER[key], key) | |
| elif key == self.QUIT_KEY: | |
| return (2, 0, key) | |
| else: | |
| return (1, 0, key) | |
| bindings.sort(key=sort_key) | |
| action_to_bindings: defaultdict[str, list[tuple[Binding, bool, str]]] = defaultdict(list) | |
| for binding, enabled, tooltip in bindings: | |
| action_to_bindings[binding.action].append((binding, enabled, tooltip)) | |
| self.styles.grid_size_columns = len(action_to_bindings) | |
| for group, multi_bindings_iterable in groupby( | |
| action_to_bindings.values(), | |
| lambda multi_bindings_: multi_bindings_[0][0].group, | |
| ): | |
| multi_bindings = list(multi_bindings_iterable) | |
| for multi_binding in multi_bindings: | |
| binding, enabled, tooltip = multi_binding[0] | |
| yield FooterKey( | |
| binding.key, | |
| self.app.get_key_display(binding), | |
| binding.description, | |
| binding.action, | |
| disabled=not enabled, | |
| tooltip=tooltip, | |
| ).data_bind(compact=Footer.compact) | |
| if self.show_command_palette and self.app.ENABLE_COMMAND_PALETTE: | |
| try: | |
| _node, binding, enabled, tooltip = active_bindings[ | |
| self.app.COMMAND_PALETTE_BINDING | |
| ] | |
| except KeyError: | |
| pass | |
| else: | |
| yield FooterKey( | |
| binding.key, | |
| self.app.get_key_display(binding), | |
| binding.description, | |
| binding.action, | |
| classes="-command-palette", | |
| disabled=not enabled, | |
| tooltip=binding.tooltip or binding.description, | |
| ) | |
| def on_mount(self) -> None: | |
| """Force a recompose after mount to catch app-level bindings.""" | |
| self.call_later(self.recompose) | |
| def on_screen_resume(self, event: Screen.ScreenResume) -> None: | |
| """Force footer refresh when screen becomes active.""" | |
| self.recompose() | |
| def on_resize(self) -> None: | |
| """Force footer refresh on terminal resize (maximize/minimize fix).""" | |
| self.recompose() | |