key panel widget

This commit is contained in:
Will McGugan
2024-08-12 15:53:18 +01:00
parent ee5ff3ad16
commit ce84292365
7 changed files with 125 additions and 10 deletions

View File

@@ -4,17 +4,23 @@ from pathlib import Path
from sys import argv
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.reactive import var
from textual.widgets import Footer, MarkdownViewer
from textual.widgets import Footer, KeyPanel, MarkdownViewer
class MarkdownApp(App):
"""A simple Markdown viewer application."""
BINDINGS = [
("t", "toggle_table_of_contents", "TOC"),
("b", "back", "Back"),
("f", "forward", "Forward"),
Binding(
"t",
"toggle_table_of_contents",
"TOC",
tooltip="Toggle the Table of Contents Panel",
),
Binding("b", "back", "Back", tooltip="Navigate back"),
Binding("f", "forward", "Forward", tooltip="Navigate forward"),
]
path = var(Path(__file__).parent / "demo.md")
@@ -27,6 +33,7 @@ class MarkdownApp(App):
def compose(self) -> ComposeResult:
yield Footer()
yield MarkdownViewer()
yield KeyPanel()
async def on_mount(self) -> None:
"""Go to the first path when the app starts."""

View File

@@ -647,7 +647,9 @@ class Compositor:
layers_to_index = {
layer_name: index
for index, layer_name in enumerate(widget.layers)
for index, layer_name in enumerate(
("textual-low", *widget.layers, "textual-high")
)
}
get_layer_index = layers_to_index.get

View File

@@ -37,11 +37,6 @@ class SystemCommands(Provider):
self.app.action_quit,
"Quit the application as soon as possible",
),
(
"Ring the bell",
self.app.action_bell,
"Ring the terminal's 'bell'",
),
)
async def discover(self) -> Hits:

View File

@@ -21,6 +21,7 @@ if typing.TYPE_CHECKING:
from ._footer import Footer
from ._header import Header
from ._input import Input
from ._key_panel import KeyPanel
from ._label import Label
from ._list_item import ListItem
from ._list_view import ListView
@@ -59,6 +60,8 @@ __all__ = [
"Footer",
"Header",
"Input",
"KeyPanel",
"KeyPanel",
"Label",
"ListItem",
"ListView",

View File

@@ -10,6 +10,7 @@ from ._directory_tree import DirectoryTree as DirectoryTree
from ._footer import Footer as Footer
from ._header import Header as Header
from ._input import Input as Input
from ._key_panel import KeyPanel
from ._label import Label as Label
from ._list_item import ListItem as ListItem
from ._list_view import ListView as ListView

View File

@@ -145,6 +145,7 @@ class Footer(ScrollableContainer, can_focus=False, can_focus_children=False):
background: $panel;
color: $text;
dock: bottom;
layer: textual-high;
height: 1;
scrollbar-size: 0 0;
&.-compact {

View File

@@ -0,0 +1,106 @@
from __future__ import annotations
from collections import defaultdict
from typing import TYPE_CHECKING
from rich.table import Table
from rich.text import Text
from ..binding import Binding
from ..reactive import reactive
from ..widgets import Static
if TYPE_CHECKING:
from ..screen import Screen
class KeyPanel(Static):
COMPONENT_CLASSES = {
"footer-key--key",
"footer-key--description",
}
DEFAULT_CSS = """
KeyPanel {
layout: vertical;
dock: right;
# layer: textual-high;
width: 20;
# min-width: 20;
max-width: 33%;
# border-left: vkey $foreground 30%;
padding: 0 1;
height: 1fr;
border-left: vkey $primary;
padding-right: 1;
&>.footer-key--key {
color: $secondary;
text-style: bold;
padding: 0 1;
}
&>.footer-key--description {
color: $text;
}
}
"""
_bindings_ready = reactive(False, repaint=False, recompose=True)
def update_bindings(self) -> None:
bindings = [
(binding, enabled, tooltip)
for (_, binding, enabled, tooltip) in self.screen.active_bindings.values()
]
action_to_bindings: defaultdict[str, list[tuple[Binding, bool, str]]]
action_to_bindings = defaultdict(list)
for binding, enabled, tooltip in bindings:
action_to_bindings[binding.action].append((binding, enabled, tooltip))
table = Table.grid(padding=(0, 1))
key_style = self.get_component_rich_style("footer-key--key")
description_style = self.get_component_rich_style("footer-key--description")
def render_description(binding: Binding) -> Text:
text = Text.from_markup(
binding.description, end="", style=description_style
)
if binding.tooltip:
text.append(" ")
text.append(binding.tooltip, "dim")
return text
table.add_column("", justify="right")
for multi_bindings in action_to_bindings.values():
binding, enabled, tooltip = multi_bindings[0]
table.add_row(
Text(
binding.key_display or self.app.get_key_display(binding.key),
style=key_style,
),
render_description(binding),
)
self.update(table)
def on_mount(self) -> None:
async def bindings_changed(screen: Screen) -> None:
self._bindings_ready = True
if self.is_attached and screen is self.screen:
self.update_bindings()
self.screen.bindings_updated_signal.subscribe(self, bindings_changed)
def on_unmount(self) -> None:
self.screen.bindings_updated_signal.unsubscribe(self)