BitFinTrainer / trading_cli /widgets /ordered_footer.py
luohoa97's picture
Deploy BitNet-Transformer Trainer
d5b7ee9 verified
"""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()