From d819eb05476aa9e8ae5ef67def1a15458064568c Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 17 Aug 2024 16:38:57 +0100 Subject: [PATCH 1/7] fix command palette key --- src/textual/app.py | 32 ++-- src/textual/binding.py | 8 + .../test_command_palette_key_change.svg | 154 ++++++++++++++++++ .../snapshot_apps/command_palette_key.py | 15 ++ tests/snapshot_tests/test_snapshots.py | 5 + 5 files changed, 204 insertions(+), 10 deletions(-) create mode 100644 tests/snapshot_tests/__snapshots__/test_snapshots/test_command_palette_key_change.svg create mode 100644 tests/snapshot_tests/snapshot_apps/command_palette_key.py diff --git a/src/textual/app.py b/src/textual/app.py index 6bb0fbe827..22774ffd36 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -367,18 +367,13 @@ class MyApp(App[None]): """ COMMAND_PALETTE_BINDING: ClassVar[str] = "ctrl+p" - """The key that launches the command palette (if enabled).""" + """The key that launches the command palette (if enabled by [`App.ENABLE_COMMAND_PALETTE`][textual.app.App.ENABLE_COMMAND_PALETTE]).""" + + COMMAND_PALETTE_DISPLAY: ClassVar[str | None] = None + """How the command palette key should be displayed in the footer (or `None` for default).""" BINDINGS: ClassVar[list[BindingType]] = [ - Binding("ctrl+c", "quit", "Quit", show=False, priority=True), - Binding( - COMMAND_PALETTE_BINDING, - "command_palette", - "palette", - show=False, - priority=True, - tooltip="Open command palette", - ), + Binding("ctrl+c", "quit", "Quit", show=False, priority=True) ] """The default key bindings.""" @@ -650,6 +645,23 @@ def __init__( # Size of previous inline update self._previous_inline_height: int | None = None + if self.ENABLE_COMMAND_PALETTE: + for _key, binding in self._bindings: + if binding.action in {"command_palette", "app.command_palette"}: + break + else: + self._bindings._add_binding( + Binding( + self.COMMAND_PALETTE_BINDING, + "command_palette", + "palette", + show=False, + key_display=self.COMMAND_PALETTE_DISPLAY, + priority=True, + tooltip="Open command palette", + ), + ) + def validate_title(self, title: Any) -> str: """Make sure the title is set to a string.""" return str(title) diff --git a/src/textual/binding.py b/src/textual/binding.py index 3b0825d8a2..fe358e8a80 100644 --- a/src/textual/binding.py +++ b/src/textual/binding.py @@ -123,6 +123,14 @@ def make_bindings(bindings: Iterable[BindingType]) -> Iterable[Binding]: for binding in make_bindings(bindings or {}): self.key_to_bindings.setdefault(binding.key, []).append(binding) + def _add_binding(self, binding: Binding) -> None: + """Add a new binding. + + Args: + binding: New Binding to add. + """ + self.key_to_bindings.setdefault(binding.key, []).append(binding) + def __iter__(self) -> Iterator[tuple[str, Binding]]: """Iterating produces a sequence of (KEY, BINDING) tuples.""" return iter( diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_command_palette_key_change.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_command_palette_key_change.svg new file mode 100644 index 0000000000..98b149044d --- /dev/null +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_command_palette_key_change.svg @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NewPaletteBindingApp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +^\ palette + + + diff --git a/tests/snapshot_tests/snapshot_apps/command_palette_key.py b/tests/snapshot_tests/snapshot_apps/command_palette_key.py new file mode 100644 index 0000000000..179b90156a --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/command_palette_key.py @@ -0,0 +1,15 @@ +from textual.app import App, ComposeResult +from textual.widgets import Footer + + +class NewPaletteBindingApp(App): + COMMAND_PALETTE_BINDING = "ctrl+backslash" + COMMAND_PALETTE_DISPLAY = "ctrl+\\" + + def compose(self) -> ComposeResult: + yield Footer() + + +if __name__ == "__main__": + app = NewPaletteBindingApp() + app.run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index a818ab788e..e0ba37e876 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -1438,3 +1438,8 @@ async def run_before(pilot: Pilot): assert snap_compare( SNAPSHOT_APPS_DIR / "command_palette_dismiss.py", run_before=run_before ) + + +def test_command_palette_key_change(snap_compare): + """Regression test for https://github.com/Textualize/textual/issues/4887""" + assert snap_compare(SNAPSHOT_APPS_DIR / "command_palette_key.py") From 9dc68c480e86d16988d17612ed471ee12dec329b Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 17 Aug 2024 16:39:49 +0100 Subject: [PATCH 2/7] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ffcc3ba3..cf63078a9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fix crash when `validate_on` value isn't a set https://github.com/Textualize/textual/pull/4868 - Fix `Input.cursor_blink` having no effect on the blink cycle after mounting https://github.com/Textualize/textual/pull/4869 +- Fix command palette key override https://github.com/Textualize/textual/pull/4890 ## [0.76.0] From eea79da5b1511c427dddfd429b5dac69af86a05f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 17 Aug 2024 16:42:33 +0100 Subject: [PATCH 3/7] formatting --- src/textual/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/app.py b/src/textual/app.py index 22774ffd36..4666aefbf7 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -659,7 +659,7 @@ def __init__( key_display=self.COMMAND_PALETTE_DISPLAY, priority=True, tooltip="Open command palette", - ), + ) ) def validate_title(self, title: Any) -> str: From 711d9384c489a64ab5849d73593ba09d003eba2f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 20 Aug 2024 14:25:40 +0100 Subject: [PATCH 4/7] centralize key display --- CHANGELOG.md | 5 +- src/textual/app.py | 42 +++--- src/textual/binding.py | 9 ++ src/textual/keys.py | 26 +--- src/textual/widgets/_footer.py | 24 +--- src/textual/widgets/_key_panel.py | 26 +--- .../test_command_palette_key_change.svg | 120 +++++++++--------- .../test_footer_classic_styling.svg | 116 ++++++++--------- .../test_snapshots/test_key_display.svg | 120 +++++++++--------- tests/test_keys.py | 32 +++-- 10 files changed, 242 insertions(+), 278 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e58c5ab6b3..9819da6e2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,17 +16,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added "Show keys" option to system commands to show a summary of key bindings. https://github.com/Textualize/textual/pull/4876 - Added "split" CSS style, currently undocumented, and may change. https://github.com/Textualize/textual/pull/4876 - Added `Region.get_spacing_between` https://github.com/Textualize/textual/pull/4876 +- Added `App.COMMAND_PALETTE_KEY` to change default command palette key binding +- Added `App.get_key_display` ### Changed - Removed caps_lock and num_lock modifiers https://github.com/Textualize/textual/pull/4861 - Keys such as escape and space are now displayed in lower case in footer https://github.com/Textualize/textual/pull/4876 +- Changed default command palette binding to ctrl+p +- Removed `ctrl_to_caret` and `upper_case_keys` from Footer. These can be implemented in `App.get_key_display`. ### Fixed - Fix crash when `validate_on` value isn't a set https://github.com/Textualize/textual/pull/4868 - Fix `Input.cursor_blink` having no effect on the blink cycle after mounting https://github.com/Textualize/textual/pull/4869 -- Fix command palette key override https://github.com/Textualize/textual/pull/4890 ## [0.76.0] diff --git a/src/textual/app.py b/src/textual/app.py index 21dcfad2a7..cd8efc47e1 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -96,8 +96,8 @@ from .keys import ( REPLACED_KEYS, _character_to_key, - _get_key_display, _get_unicode_name_from_key, + format_key, ) from .messages import CallbackType, Prune from .notifications import Notification, Notifications, Notify, SeverityLevel @@ -1331,27 +1331,35 @@ def bind( keys, action, description, show=show, key_display=key_display ) - def get_key_display( - self, key: str, upper_case_keys: bool = False, ctrl_to_caret: bool = True - ) -> str: - """For a given key, return how it should be displayed in an app - (e.g. in the Footer widget). - By key, we refer to the string used in the "key" argument for - a Binding instance. By overriding this method, you can ensure that - keys are displayed consistently throughout your app, without - needing to add a key_display to every binding. + def get_key_display(self, binding: Binding) -> str: + """Format a bound key for display in footer / key panel etc. + + !!! note + You can implement this in a subclass if you want to change how keys are displayed in your app. Args: - key: The binding key string. - upper_case_keys: Upper case printable keys. - ctrl_to_caret: Replace `ctrl+` with `^`. + binding: A Binding. Returns: - The display string for the input key. + A string used to represent the key. """ - return _get_key_display( - key, upper_case_keys=upper_case_keys, ctrl_to_caret=ctrl_to_caret - ) + # Dev has overridden the key display, so use that + if binding.key_display: + return binding.key_display + + # Extract modifiers + modifiers, key = binding.parse_key() + + # Format the key (replace unicode names with character) + key = format_key(key) + + # Convert ctrl modifier to caret + if "ctrl" in modifiers: + modifiers.pop(modifiers.index("ctrl")) + key = f"^{key}" + # Join everything with + + key_tokens = modifiers + [key] + return "+".join(key_tokens) async def _press_keys(self, keys: Iterable[str]) -> None: """A task to send key events.""" diff --git a/src/textual/binding.py b/src/textual/binding.py index fe358e8a80..5fe58cb487 100644 --- a/src/textual/binding.py +++ b/src/textual/binding.py @@ -53,6 +53,15 @@ class Binding: tooltip: str = "" """Optional tooltip to show in footer.""" + def parse_key(self) -> tuple[list[str], str]: + """Parse a key in to a list of modifiers, and the actual key. + + Returns: + A tuple of (MODIFIER LIST, KEY). + """ + *modifiers, key = self.key.split("+") + return modifiers, key + class ActiveBinding(NamedTuple): """Information about an active binding (returned from [active_bindings][textual.screen.Screen.active_bindings]).""" diff --git a/src/textual/keys.py b/src/textual/keys.py index 72e4cdc395..9391d11e7e 100644 --- a/src/textual/keys.py +++ b/src/textual/keys.py @@ -281,24 +281,12 @@ def _get_key_aliases(key: str) -> list[str]: return [key] + KEY_ALIASES.get(key, []) -def _get_key_display( +def format_key( key: str, - upper_case_keys: bool = False, - ctrl_to_caret: bool = True, ) -> str: """Given a key (i.e. the `key` string argument to Binding __init__), return the value that should be displayed in the app when referring to this key (e.g. in the Footer widget).""" - if "+" in key: - key_components = key.split("+") - caret = False - if ctrl_to_caret and "ctrl" in key_components: - key_components.remove("ctrl") - caret = True - key_display = ("^" if caret else "") + "+".join( - [_get_key_display(key) for key in key_components] - ) - return key_display display_alias = KEY_DISPLAY_ALIASES.get(key) if display_alias: @@ -307,14 +295,12 @@ def _get_key_display( original_key = REPLACED_KEYS.get(key, key) tentative_unicode_name = _get_unicode_name_from_key(original_key) try: - unicode_character = unicodedata.lookup(tentative_unicode_name) + unicode = unicodedata.lookup(tentative_unicode_name) except KeyError: - return tentative_unicode_name - - # Check if printable. `delete` for example maps to a control sequence - # which we don't want to write to the terminal. - if unicode_character.isprintable(): - return unicode_character.upper() if upper_case_keys else unicode_character + pass + else: + if unicode.isprintable(): + return unicode return tentative_unicode_name diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py index 3db09a2938..a6eedcb3b6 100644 --- a/src/textual/widgets/_footer.py +++ b/src/textual/widgets/_footer.py @@ -73,8 +73,6 @@ class FooterKey(Widget): } """ - upper_case_keys = reactive(False) - ctrl_to_caret = reactive(True) compact = reactive(True) def __init__( @@ -106,10 +104,6 @@ def render(self) -> Text: description_padding = self.get_component_styles( "footer-key--description" ).padding - if self.upper_case_keys: - key_display = key_display.upper() - if self.ctrl_to_caret and key_display.lower().startswith("ctrl+"): - key_display = "^" + key_display.split("+", 1)[1] description = self.description label_text = Text.assemble( ( @@ -158,10 +152,6 @@ class Footer(ScrollableContainer, can_focus=False, can_focus_children=False): } """ - upper_case_keys = reactive(False) - """Upper case key display.""" - ctrl_to_caret = reactive(True) - """Convert 'ctrl+' prefix to '^'.""" compact = reactive(False) """Display in compact style.""" _bindings_ready = reactive(False, repaint=False) @@ -176,8 +166,6 @@ def __init__( id: str | None = None, classes: str | None = None, disabled: bool = False, - upper_case_keys: bool = False, - ctrl_to_caret: bool = True, show_command_palette: bool = True, ) -> None: """A footer to show key bindings. @@ -199,8 +187,6 @@ def __init__( classes=classes, disabled=disabled, ) - self.set_reactive(Footer.upper_case_keys, upper_case_keys) - self.set_reactive(Footer.ctrl_to_caret, ctrl_to_caret) self.set_reactive(Footer.show_command_palette, show_command_palette) def compose(self) -> ComposeResult: @@ -221,16 +207,12 @@ def compose(self) -> ComposeResult: binding, enabled, tooltip = multi_bindings[0] yield FooterKey( binding.key, - binding.key_display or self.app.get_key_display(binding.key), + self.app.get_key_display(binding), binding.description, binding.action, disabled=not enabled, tooltip=tooltip, - ).data_bind( - Footer.upper_case_keys, - Footer.ctrl_to_caret, - Footer.compact, - ) + ).data_bind(Footer.compact) if self.show_command_palette and self.app.ENABLE_COMMAND_PALETTE: for key, binding in self.app._bindings: if binding.action in ( @@ -239,7 +221,7 @@ def compose(self) -> ComposeResult: ): yield FooterKey( key, - binding.key_display or binding.key, + self.app.get_key_display(binding), binding.description, binding.action, classes="-command-palette", diff --git a/src/textual/widgets/_key_panel.py b/src/textual/widgets/_key_panel.py index 7f4441322c..d60724e22e 100644 --- a/src/textual/widgets/_key_panel.py +++ b/src/textual/widgets/_key_panel.py @@ -9,7 +9,6 @@ from ..app import ComposeResult from ..binding import Binding from ..containers import VerticalScroll -from ..reactive import reactive from ..widgets import Static if TYPE_CHECKING: @@ -28,11 +27,6 @@ class BindingsTable(Static): } """ - upper_case_keys = reactive(False) - """Upper case key display.""" - ctrl_to_caret = reactive(True) - """Convert 'ctrl+' prefix to '^'.""" - def render_bindings_table(self) -> Table: """Render a table with all the key bindings. @@ -66,15 +60,7 @@ def render_description(binding: Binding) -> Text: 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, - upper_case_keys=self.upper_case_keys, - ctrl_to_caret=self.ctrl_to_caret, - ), - style=key_style, - ), + Text(self.app.get_key_display(binding), style=key_style), render_description(binding), ) @@ -113,18 +99,10 @@ class KeyPanel(VerticalScroll, can_focus=False): } """ - upper_case_keys = reactive(False) - """Upper case key display.""" - ctrl_to_caret = reactive(True) - """Convert 'ctrl+' prefix to '^'.""" - DEFAULT_CLASSES = "-textual-system" def compose(self) -> ComposeResult: - yield BindingsTable(shrink=True, expand=False).data_bind( - KeyPanel.upper_case_keys, - KeyPanel.ctrl_to_caret, - ) + yield BindingsTable(shrink=True, expand=False) async def on_mount(self) -> None: async def bindings_changed(screen: Screen) -> None: diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_command_palette_key_change.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_command_palette_key_change.svg index 98b149044d..9da9db84a4 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_command_palette_key_change.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_command_palette_key_change.svg @@ -19,136 +19,136 @@ font-weight: 700; } - .terminal-2156308295-matrix { + .terminal-229419081-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2156308295-title { + .terminal-229419081-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2156308295-r1 { fill: #e1e1e1 } -.terminal-2156308295-r2 { fill: #c5c8c6 } -.terminal-2156308295-r3 { fill: #e2e3e3 } -.terminal-2156308295-r4 { fill: #4c5055 } -.terminal-2156308295-r5 { fill: #fea62b;font-weight: bold } -.terminal-2156308295-r6 { fill: #a7a9ab } + .terminal-229419081-r1 { fill: #e1e1e1 } +.terminal-229419081-r2 { fill: #c5c8c6 } +.terminal-229419081-r3 { fill: #e2e3e3 } +.terminal-229419081-r4 { fill: #4c5055 } +.terminal-229419081-r5 { fill: #fea62b;font-weight: bold } +.terminal-229419081-r6 { fill: #a7a9ab } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - NewPaletteBindingApp + NewPaletteBindingApp - - - - - - - - - - - - - - - - - - - - - - - - - - -^\ palette + + + + + + + + + + + + + + + + + + + + + + + + + + +ctrl+\ palette diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_footer_classic_styling.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_footer_classic_styling.svg index ee0ad99610..1c88ee485d 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_footer_classic_styling.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_footer_classic_styling.svg @@ -19,135 +19,135 @@ font-weight: 700; } - .terminal-407601934-matrix { + .terminal-2379907918-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-407601934-title { + .terminal-2379907918-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-407601934-r1 { fill: #e1e1e1 } -.terminal-407601934-r2 { fill: #c5c8c6 } -.terminal-407601934-r3 { fill: #dde8f3;font-weight: bold } -.terminal-407601934-r4 { fill: #ddedf9 } -.terminal-407601934-r5 { fill: #308fd9 } + .terminal-2379907918-r1 { fill: #e1e1e1 } +.terminal-2379907918-r2 { fill: #c5c8c6 } +.terminal-2379907918-r3 { fill: #dde8f3;font-weight: bold } +.terminal-2379907918-r4 { fill: #ddedf9 } +.terminal-2379907918-r5 { fill: #308fd9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ClassicFooterStylingApp + ClassicFooterStylingApp - + - - - - - - - - - - - - - - - - - - - - - - - - - ^T  Toggle Dark mode  ^Q  Quit                                    ^p palette  + + + + + + + + + + + + + + + + + + + + + + + + + ^t  Toggle Dark mode  ^q  Quit                                    ^p palette  diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_key_display.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_key_display.svg index 98d70360f1..160601a0a0 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_key_display.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_key_display.svg @@ -19,136 +19,136 @@ font-weight: 700; } - .terminal-121821627-matrix { + .terminal-2027215108-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-121821627-title { + .terminal-2027215108-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-121821627-r1 { fill: #e1e1e1 } -.terminal-121821627-r2 { fill: #c5c8c6 } -.terminal-121821627-r3 { fill: #fea62b;font-weight: bold } -.terminal-121821627-r4 { fill: #a7a9ab } -.terminal-121821627-r5 { fill: #e2e3e3 } -.terminal-121821627-r6 { fill: #4c5055 } + .terminal-2027215108-r1 { fill: #e1e1e1 } +.terminal-2027215108-r2 { fill: #c5c8c6 } +.terminal-2027215108-r3 { fill: #fea62b;font-weight: bold } +.terminal-2027215108-r4 { fill: #a7a9ab } +.terminal-2027215108-r5 { fill: #e2e3e3 } +.terminal-2027215108-r6 { fill: #4c5055 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - KeyDisplayApp + KeyDisplayApp - - - - - - - - - - - - - - - - - - - - - - - - - - - ? Question  ^q Quit app  Escape! Escape  a Letter A ^p palette + + + + + + + + + + + + + + + + + + + + + + + + + + + ? Question  ^q Quit app  esc Escape  a Letter A ^p palette diff --git a/tests/test_keys.py b/tests/test_keys.py index 5d9f8be194..66b279a8fb 100644 --- a/tests/test_keys.py +++ b/tests/test_keys.py @@ -1,7 +1,8 @@ import pytest from textual.app import App -from textual.keys import _character_to_key, _get_key_display +from textual.binding import Binding +from textual.keys import _character_to_key, format_key @pytest.mark.parametrize( @@ -50,22 +51,19 @@ def action_increment(self) -> None: assert counter == 3 -def test_get_key_display(): - assert _get_key_display("minus") == "-" - +def test_format_key(): + assert format_key("minus") == "-" -def test_get_key_display_when_used_in_conjunction(): - """Test a key display is the same if used in conjunction with another key. - For example, "ctrl+right_square_bracket" should display the bracket as "]", - the same as it would without the ctrl modifier. - Regression test for #3035 https://github.com/Textualize/textual/issues/3035 - """ - - right_square_bracket = _get_key_display("right_square_bracket") - ctrl_right_square_bracket = _get_key_display("ctrl+right_square_bracket") - assert ctrl_right_square_bracket == f"^{right_square_bracket}" +def test_get_key_display(): + app = App() - left = _get_key_display("left") - ctrl_left = _get_key_display("ctrl+left") - assert ctrl_left == f"^{left}" + assert app.get_key_display(Binding("p", "", "")) == "p" + assert app.get_key_display(Binding("ctrl+p", "", "")) == "^p" + assert app.get_key_display(Binding("right_square_bracket", "", "")) == "]" + assert app.get_key_display(Binding("ctrl+right_square_bracket", "", "")) == "^]" + assert ( + app.get_key_display(Binding("shift+ctrl+right_square_bracket", "", "")) + == "shift+^]" + ) + assert app.get_key_display(Binding("delete", "", "")) == "delete" From 698f6cfce63b560cf7dfd79ee7a0fbbd21dd85c4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 20 Aug 2024 14:26:42 +0100 Subject: [PATCH 5/7] snapshot --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9819da6e2c..153f34d6f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added "split" CSS style, currently undocumented, and may change. https://github.com/Textualize/textual/pull/4876 - Added `Region.get_spacing_between` https://github.com/Textualize/textual/pull/4876 - Added `App.COMMAND_PALETTE_KEY` to change default command palette key binding -- Added `App.get_key_display` +- Added `App.get_key_display` https://github.com/Textualize/textual/pull/4890 ### Changed From cfdab2bebeddd0d0efd517050b50e25a88e94a27 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 20 Aug 2024 14:34:35 +0100 Subject: [PATCH 6/7] naming --- src/textual/keys.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/textual/keys.py b/src/textual/keys.py index 9391d11e7e..0e47d15320 100644 --- a/src/textual/keys.py +++ b/src/textual/keys.py @@ -281,9 +281,7 @@ def _get_key_aliases(key: str) -> list[str]: return [key] + KEY_ALIASES.get(key, []) -def format_key( - key: str, -) -> str: +def format_key(key: str) -> str: """Given a key (i.e. the `key` string argument to Binding __init__), return the value that should be displayed in the app when referring to this key (e.g. in the Footer widget).""" @@ -295,12 +293,12 @@ def format_key( original_key = REPLACED_KEYS.get(key, key) tentative_unicode_name = _get_unicode_name_from_key(original_key) try: - unicode = unicodedata.lookup(tentative_unicode_name) + unicode_name = unicodedata.lookup(tentative_unicode_name) except KeyError: pass else: - if unicode.isprintable(): - return unicode + if unicode_name.isprintable(): + return unicode_name return tentative_unicode_name From 87167daba473569467a9c825c93438d1d77a2d75 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 20 Aug 2024 15:11:03 +0100 Subject: [PATCH 7/7] changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 153f34d6f4..ae6ae055ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,14 +16,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added "Show keys" option to system commands to show a summary of key bindings. https://github.com/Textualize/textual/pull/4876 - Added "split" CSS style, currently undocumented, and may change. https://github.com/Textualize/textual/pull/4876 - Added `Region.get_spacing_between` https://github.com/Textualize/textual/pull/4876 -- Added `App.COMMAND_PALETTE_KEY` to change default command palette key binding +- Added `App.COMMAND_PALETTE_KEY` to change default command palette key binding https://github.com/Textualize/textual/pull/4867 - Added `App.get_key_display` https://github.com/Textualize/textual/pull/4890 ### Changed - Removed caps_lock and num_lock modifiers https://github.com/Textualize/textual/pull/4861 - Keys such as escape and space are now displayed in lower case in footer https://github.com/Textualize/textual/pull/4876 -- Changed default command palette binding to ctrl+p +- Changed default command palette binding to `ctrl+p` https://github.com/Textualize/textual/pull/4867 - Removed `ctrl_to_caret` and `upper_case_keys` from Footer. These can be implemented in `App.get_key_display`. ### Fixed