Refactor keybindings and footer management: streamline keybinding processing and enhance footer item display with key-description pairs for improved clarity and usability.

This commit is contained in:
Philip Henning 2025-08-16 22:37:29 +02:00
parent 3f0892fb7b
commit 6107b43ac5
3 changed files with 133 additions and 24 deletions

View file

@ -147,26 +147,27 @@ class HostsManagerApp(App):
# Process keybindings and add to appropriate sections # Process keybindings and add to appropriate sections
for binding in self.BINDINGS: for binding in self.BINDINGS:
# Skip tuple-style bindings and only process Binding objects
if not hasattr(binding, "show"):
continue
# Only show bindings marked with show=True # Only show bindings marked with show=True
if hasattr(binding, "show") and binding.show: if binding.show:
# Get the display key # Get the display key
key_display = getattr(binding, "key_display", None) or binding.key key_display = getattr(binding, "key_display", None) or binding.key
# Get the description # Get the description
description = binding.description or binding.action description = binding.description or binding.action
# Format the item
item = f"{key_display}: {description}"
# Determine positioning from id attribute # Determine positioning from id attribute
binding_id = getattr(binding, "id", None) binding_id = getattr(binding, "id", None)
if binding_id and binding_id.startswith("left:"): if binding_id and binding_id.startswith("left:"):
footer.add_left_item(item) footer.add_left_item(key_display, description)
elif binding_id and binding_id.startswith("right:"): elif binding_id and binding_id.startswith("right:"):
footer.add_right_item(item) footer.add_right_item(key_display, description)
else: else:
# Default to right if no specific positioning # Default to right if no specific positioning
footer.add_right_item(item) footer.add_right_item(key_display, description)
# Status section will be updated by update_status # Status section will be updated by update_status
self._update_footer_status() self._update_footer_status()

View file

@ -11,6 +11,42 @@ from textual.app import ComposeResult
from textual.containers import Horizontal from textual.containers import Horizontal
from textual.widgets import Static from textual.widgets import Static
from textual.widget import Widget from textual.widget import Widget
from rich.text import Text
class FooterKey(Widget):
"""A key/action pair widget for the footer, styled similar to Textual's original FooterKey."""
DEFAULT_CSS = """
FooterKey {
width: auto;
height: 1;
content-align: center middle;
padding: 0 1;
}
.footer-key--key {
text-style: bold;
color: $text;
}
.footer-key--description {
color: $text-muted;
}
"""
def __init__(self, key: str, description: str, **kwargs):
super().__init__(**kwargs)
self.key = key
self.description = description
def render(self) -> Text:
"""Render the key-description pair with proper styling."""
text = Text()
text.append(f"{self.key}", style="bold")
text.append(" ")
text.append(self.description, style="dim")
return text
class CustomFooter(Widget): class CustomFooter(Widget):
@ -39,7 +75,6 @@ class CustomFooter(Widget):
.footer-left { .footer-left {
width: auto; width: auto;
text-align: left; text-align: left;
text-style: dim;
height: 1; height: 1;
content-align: left middle; content-align: left middle;
} }
@ -52,7 +87,6 @@ class CustomFooter(Widget):
.footer-right { .footer-right {
width: auto; width: auto;
text-align: right; text-align: right;
text-style: dim;
height: 1; height: 1;
content-align: right middle; content-align: right middle;
} }
@ -73,12 +107,25 @@ class CustomFooter(Widget):
height: 1; height: 1;
content-align: right middle; content-align: right middle;
} }
/* Enhanced styling for footer key components */
.footer-left .footer-key--key,
.footer-right .footer-key--key {
text-style: bold;
color: $text;
}
.footer-left .footer-key--description,
.footer-right .footer-key--description {
color: $text-muted;
text-style: dim;
}
""" """
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self._left_items = [] self._left_items = [] # List of FooterKey widgets
self._right_items = [] self._right_items = [] # List of FooterKey widgets
self._status_text = "" self._status_text = ""
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
@ -90,16 +137,43 @@ class CustomFooter(Widget):
yield Static("", id="footer-separator", classes="footer-separator") yield Static("", id="footer-separator", classes="footer-separator")
yield Static("", id="footer-status", classes="footer-status") yield Static("", id="footer-status", classes="footer-status")
def add_left_item(self, item: str) -> None: def add_left_item(self, key: str, description: str) -> None:
"""Add an item to the left section.""" """Add a key-description pair to the left section."""
self._left_items.append(item) footer_key = FooterKey(key, description)
self._left_items.append(footer_key)
self._update_left_section() self._update_left_section()
def add_right_item(self, item: str) -> None: def add_right_item(self, key: str, description: str) -> None:
"""Add an item to the right section.""" """Add a key-description pair to the right section."""
self._right_items.append(item) footer_key = FooterKey(key, description)
self._right_items.append(footer_key)
self._update_right_section() self._update_right_section()
def add_left_item_legacy(self, item: str) -> None:
"""Add a legacy item (key: description format) to the left section."""
if ": " in item:
key, description = item.split(": ", 1)
self.add_left_item(key, description)
else:
self.add_left_item(item, "")
def add_right_item_legacy(self, item: str) -> None:
"""Add a legacy item (key: description format) to the right section."""
if ": " in item:
key, description = item.split(": ", 1)
self.add_right_item(key, description)
else:
self.add_right_item(item, "")
# Backward compatibility - temporarily add the old single parameter methods
def add_left_item_old(self, item: str) -> None:
"""Backward compatibility method."""
self.add_left_item_legacy(item)
def add_right_item_old(self, item: str) -> None:
"""Backward compatibility method."""
self.add_right_item_legacy(item)
def clear_left_items(self) -> None: def clear_left_items(self) -> None:
"""Clear all items from the left section.""" """Clear all items from the left section."""
self._left_items.clear() self._left_items.clear()
@ -119,7 +193,19 @@ class CustomFooter(Widget):
"""Update the left section display.""" """Update the left section display."""
try: try:
left_static = self.query_one("#footer-left", Static) left_static = self.query_one("#footer-left", Static)
left_static.update(" ".join(self._left_items)) if self._left_items:
# Combine all FooterKey renderings with spacing
combined_text = Text()
for i, footer_key in enumerate(self._left_items):
if i > 0:
combined_text.append(" ") # Add spacing between items
# Render individual key-description pair with styling
combined_text.append(footer_key.key, style="bold")
combined_text.append(" ")
combined_text.append(footer_key.description, style="dim")
left_static.update(combined_text)
else:
left_static.update("")
except Exception: except Exception:
pass # Widget not ready yet pass # Widget not ready yet
@ -127,7 +213,19 @@ class CustomFooter(Widget):
"""Update the right section display.""" """Update the right section display."""
try: try:
right_static = self.query_one("#footer-right", Static) right_static = self.query_one("#footer-right", Static)
right_static.update(" ".join(self._right_items)) if self._right_items:
# Combine all FooterKey renderings with spacing
combined_text = Text()
for i, footer_key in enumerate(self._right_items):
if i > 0:
combined_text.append(" ") # Add spacing between items
# Render individual key-description pair with styling
combined_text.append(footer_key.key, style="bold")
combined_text.append(" ")
combined_text.append(footer_key.description, style="dim")
right_static.update(combined_text)
else:
right_static.update("")
except Exception: except Exception:
pass # Widget not ready yet pass # Widget not ready yet

View file

@ -12,8 +12,20 @@ HOSTS_MANAGER_BINDINGS = [
Binding("a", "add_entry", "Add new entry", show=True, id="left:add_entry"), Binding("a", "add_entry", "Add new entry", show=True, id="left:add_entry"),
Binding("d", "delete_entry", "Delete entry", show=True, id="left:delete_entry"), Binding("d", "delete_entry", "Delete entry", show=True, id="left:delete_entry"),
Binding("e", "edit_entry", "Edit entry", show=True, id="left:edit_entry"), Binding("e", "edit_entry", "Edit entry", show=True, id="left:edit_entry"),
Binding("space", "toggle_entry", "Toggle active/inactive", show=True, id="left:toggle_entry"), Binding(
Binding("ctrl+e", "toggle_edit_mode", "Toggle edit mode", show=True, id="left:toggle_edit_mode"), "space",
"toggle_entry",
"Toggle active/inactive",
show=True,
id="left:toggle_entry",
),
Binding(
"ctrl+e",
"toggle_edit_mode",
"Toggle edit mode",
show=True,
id="left:toggle_edit_mode",
),
Binding("c", "config", "Configuration", show=True, id="right:config"), Binding("c", "config", "Configuration", show=True, id="right:config"),
Binding( Binding(
"question_mark", "question_mark",
@ -26,9 +38,7 @@ HOSTS_MANAGER_BINDINGS = [
Binding("q", "quit", "Quit", show=True, id="right:quit"), Binding("q", "quit", "Quit", show=True, id="right:quit"),
Binding("r", "reload", "Reload hosts file", show=False), Binding("r", "reload", "Reload hosts file", show=False),
Binding("i", "sort_by_ip", "Sort by IP address", show=False), Binding("i", "sort_by_ip", "Sort by IP address", show=False),
Binding( Binding("h", "sort_by_hostname", "Sort by hostname", show=False),
"h", "sort_by_hostname", "Sort by hostname", show=False
),
Binding("ctrl+s", "save_file", "Save hosts file", show=False), Binding("ctrl+s", "save_file", "Save hosts file", show=False),
Binding("shift+up", "move_entry_up", "Move entry up", show=False), Binding("shift+up", "move_entry_up", "Move entry up", show=False),
Binding("shift+down", "move_entry_down", "Move entry down", show=False), Binding("shift+down", "move_entry_down", "Move entry down", show=False),