~ihabunek/toot-discuss

tooi: Menu: add support for keyboard shortcuts v1 PROPOSED

Ivan Habunek: 3
 Menu: add support for keyboard shortcuts
 GotoScreen: add keyboard shortcuts
 MenuItem: Highlight shortcut keys

 3 files changed, 40 insertions(+), 22 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~ihabunek/toot-discuss/patches/48498/mbox | git am -3
Learn more about email & git

[PATCH tooi 1/3] Menu: add support for keyboard shortcuts Export this patch

---
 tooi/widgets/menu.py | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/tooi/widgets/menu.py b/tooi/widgets/menu.py
index e11b295..f2911e6 100644
--- a/tooi/widgets/menu.py
+++ b/tooi/widgets/menu.py
@@ -1,5 +1,7 @@
from typing import cast

from textual import events
from textual.app import log
from textual.message import Message
from textual.widgets import ListItem, Static
from tooi.widgets.list_view import ListView
@@ -19,6 +21,7 @@ class Menu(ListView):
    ):
        self.menu_items = menu_items
        self.initial_index = initial_index
        self.items_by_key = {i.key: i for i in menu_items if i.key}
        super().__init__(*menu_items)

    def on_list_view_selected(self, message: ListView.Selected):
@@ -26,6 +29,12 @@ class Menu(ListView):
        menu_item = cast(MenuItem, message.item)
        self.post_message(self.ItemSelected(menu_item))

    def on_key(self, event: events.Key):
        # TODO: prevent overrriding keys needed to operate the menu ("q", "j", "k", ...)
        if item := self.items_by_key.get(event.key):
            event.stop()
            self.post_message(self.ItemSelected(item))

    def action_cursor_up(self):
        if self.index == 0:
            self.post_message(self.FocusPrevious())
@@ -45,8 +54,9 @@ class Menu(ListView):


class MenuItem(ListItem):
    def __init__(self, code: str, label: str):
    def __init__(self, code: str, label: str, key: str | None = None):
        self.code = code
        self.key = key
        self._static = Static(f"< {label} >")
        super().__init__(self._static)

-- 
2.43.0

[PATCH tooi 2/3] GotoScreen: add keyboard shortcuts Export this patch

Refactors the widget to use Menu and uses the newly added feature to add
keyboard shortcuts to menu items.

This enables cobos such as "gh" to open the home timeline.
---
 tooi/screens/goto.py | 33 ++++++++++++++-------------------
 1 file changed, 14 insertions(+), 19 deletions(-)

diff --git a/tooi/screens/goto.py b/tooi/screens/goto.py
index 91de775..3aae8ed 100644
--- a/tooi/screens/goto.py
+++ b/tooi/screens/goto.py
@@ -1,12 +1,13 @@
from textual import on
from textual.app import ComposeResult, log
from textual.binding import Binding
from textual.message import Message
from textual.widgets import Input, ListItem, Static
from textual.widgets import Input, Static

from tooi.messages import GotoHomeTimeline, GotoLocalTimeline, ShowNotifications
from tooi.messages import GotoFederatedTimeline, ShowHashtagPicker, GotoPersonalTimeline
from tooi.screens.modal import ModalScreen
from tooi.widgets.list_view import ListView
from tooi.widgets.menu import Menu, MenuItem


class GotoScreen(ModalScreen[Message | None]):
@@ -17,27 +18,21 @@ class GotoScreen(ModalScreen[Message | None]):
    """

    def compose_modal(self) -> ComposeResult:
        self.list_view = ListView(
            ListItem(Static("< Home timeline >"), id="goto_home"),
            ListItem(Static("< Personal timeline >"), id="goto_personal"),
            ListItem(Static("< Local timeline >"), id="goto_local"),
            ListItem(Static("< Federated timeline >"), id="goto_federated"),
            ListItem(Static("< Notifications >"), id="goto_notifications"),
            ListItem(Static("< Hashtag timeline >"), id="goto_hashtag"),
        )
        self.status = Static("")

        yield Static("Go to", classes="modal_title")
        yield self.list_view
        yield self.status
        yield Menu(
            MenuItem(code="goto_personal", label="Personal timeline", key="p"),
            MenuItem(code="goto_home", label="Home timeline", key="h"),
            MenuItem(code="goto_local", label="Local timeline", key="l"),
            MenuItem(code="goto_federated", label="Federated timeline", key="f"),
            MenuItem(code="goto_notifications", label="Notifications", key="n"),
            MenuItem(code="goto_hashtag", label="Hashtag timeline", key="t"),
        )

    def on_list_view_selected(self, message: ListView.Selected):
    @on(Menu.ItemSelected)
    def on_item_selected(self, message: Menu.ItemSelected):
        message.stop()

        if not message.item:
            self.dismiss(None)

        match message.item.id:
        match message.item.code:
            case "goto_home":
                self.dismiss(GotoHomeTimeline())
            case "goto_personal":
-- 
2.43.0

[PATCH tooi 3/3] MenuItem: Highlight shortcut keys Export this patch

---
 tooi/widgets/menu.py | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/tooi/widgets/menu.py b/tooi/widgets/menu.py
index f2911e6..b6113fc 100644
--- a/tooi/widgets/menu.py
+++ b/tooi/widgets/menu.py
@@ -1,7 +1,9 @@
import re

from typing import cast
from rich.text import Text

from textual import events
from textual.app import log
from textual.message import Message
from textual.widgets import ListItem, Static
from tooi.widgets.list_view import ListView
@@ -57,8 +59,19 @@ class MenuItem(ListItem):
    def __init__(self, code: str, label: str, key: str | None = None):
        self.code = code
        self.key = key
        self._static = Static(f"< {label} >")
        self._static = Static(self.make_label(label, key))
        super().__init__(self._static)

    def update(self, value: str):
        self._static.update(f"< {value} >")

    def make_label(self, label: str, key: str | None) -> Text:
        label = f"< {label} >"
        text = Text(label)

        # Attempt to automatically mark the shortcuts to menu items
        if key is not None and len(key) == 1:
            if match := re.search(key, label, re.IGNORECASE):
                text.stylize("bold underline", match.start(), match.end())

        return text
-- 
2.43.0