Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added "inherited bindings" -- BINDINGS classvar will be merged with base classes, unless inherit_bindings is set to False
- Added `Tree` widget which replaces `TreeControl`.
- Added widget `Placeholder` https://github.com/Textualize/textual/issues/1200.
- Added `keys` as a `textual` command https://github.com/Textualize/textual/issues/1342

### Changed

Expand Down
4 changes: 4 additions & 0 deletions docs/guide/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ Bindings are particularly useful for configurable hot-keys. Bindings can also be

In a future version of Textual it will also be possible to specify bindings in a configuration file, which will allow users to override app bindings.

!!! tip

Run `textual keys` to learn which keys are available and what their names are for binding.

## Mouse Input

Textual will send events in response to mouse movement and mouse clicks. These events contain the coordinates of the mouse cursor relative to the terminal or widget.
Expand Down
8 changes: 8 additions & 0 deletions src/textual/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,11 @@ def colors():
from textual.cli.previews import colors

colors.app.run()


@run.command("keys")
def keys():
"""Explore keys and their names, that are available in Textual."""
from textual.cli.keys.app import TextualKeys

TextualKeys().run()
Empty file.
114 changes: 114 additions & 0 deletions src/textual/cli/keys/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""Provides a view of keys available to a textual application."""

from textual.app import App, ComposeResult
from textual.containers import Vertical, Horizontal
from textual.widgets import Header, Footer, Label
from textual.events import Key


class TextualKeys(App[None]):
"""Textual key event display application."""

CSS = """
#titles {
dock: top;
height: 1;
padding-right: 2;
background: $primary;
}

#titles Label {
width: 1fr;
border-left: solid $primary-lighten-2;
padding-left: 1;
text-style: bold;
}

#keys {
overflow-y: scroll;
}

.event {
height: auto;
}

.event Label {
border-left: solid $primary-lighten-2;
padding-left: 1;
}

.event Label.noprint {
color: $text-muted;
text-style: italic;
}

.zebra-on {
background: $surface-lighten-2;
}

.zebra-off {
background: $surface-lighten-1;
}

.event Label {
width: 1fr;
}
"""

TITLE = "Textual Keys"
"""str: The title of the application."""

def __init__(self) -> None:
"""Initialise the app."""
super().__init__()
self.zebra = False

def compose(self) -> ComposeResult:
"""Compose the layout of the application.

Returns:
ComposeResult: The result of laying out the app's display.
"""
yield Header()
yield Vertical(
Horizontal(
Label("Bindable Name"),
Label("Printable Character"),
Label("Aliases"),
id="titles",
),
Vertical(id="keys"),
)
yield Footer()

def printed(self, event: Key) -> Label:
"""Format the printed representation of the key.

Args:
event (Key): The key event to render.

Returns:
Label: A `Label` for showing the key.
"""
if event.char is None:
return Label("None", classes="noprint")
if event.is_printable:
return Label(event.char)
return Label(f"Unprintable ({ord( event.char )})", classes="noprint")

def on_key(self, event: Key) -> None:
"""Handle a keyboard event.

Args:
event (Key): The keyboard event to handle.
"""
self.query_one("#keys", Vertical).mount(
Horizontal(
Label(event.key),
self.printed(event),
Label("\n".join(event.key_aliases)),
classes=f"event zebra-{'on' if self.zebra else 'off'}",
),
before=0,
)
self.zebra = not self.zebra