~nhanb/mcross

526da28ed3db85ceb5664dacba584876a3cdab5c — Bùi Thành Nhân 11 months ago 2d19c7d
implement alt-shortcuts for buttons
3 files changed, 66 insertions(+), 11 deletions(-)

M src/mcross/gui/controller.py
M src/mcross/gui/view.py
M src/mcross/gui/widgets.py
M src/mcross/gui/controller.py => src/mcross/gui/controller.py +1 -0
@@ 21,6 21,7 @@ statusbar_logger = logging.getLogger("statusbar")
class Controller:
    def __init__(self):
        self.root = Tk()
        self.root.alt_shortcuts = set()
        self.model = Model()
        self.view = View(self.root, self.model)
        self.root.title("McRoss Browser")

M src/mcross/gui/view.py => src/mcross/gui/view.py +25 -11
@@ 13,7 13,7 @@ from ..document import (
    TextNode,
)
from .model import Model
from .widgets import McEntry, ReadOnlyText
from .widgets import AltButton, McEntry, ReadOnlyText

# OS-specific values
if sys.platform == "win32":


@@ 58,9 58,9 @@ def register_status_bar_log_handler(status_bar: ttk.Label):
class View:
    model: Model
    address_bar: ttk.Entry
    go_btn: ttk.Button
    back_btn: ttk.Button
    forward_btn: ttk.Button
    go_btn: AltButton
    back_btn: AltButton
    forward_btn: AltButton
    text: Text
    status_bar: ttk.Label



@@ 90,11 90,23 @@ class View:
        register_status_bar_log_handler(status_bar)

        # Back/Forward buttons
        back_btn = ttk.Button(
            row1, text="◀", width=3, command=lambda: self.back_callback()
        back_btn = AltButton(
            row1,
            text="◀",
            width=3,
            command=lambda: self.back_callback(),
            root=root,
            alt_char_index=0,
            alt_key="Left",
        )
        forward_btn = ttk.Button(
            row1, text="▶", width=3, command=lambda: self.forward_callback()
        forward_btn = AltButton(
            row1,
            text="▶",
            width=3,
            command=lambda: self.forward_callback(),
            root=root,
            alt_char_index=0,
            alt_key="Right",
        )
        back_btn.pack(side="left", padx=2)
        forward_btn.pack(side="left", padx=2)


@@ 108,7 120,7 @@ class View:
        # Address bar
        address_bar = McEntry(row1)
        self.address_bar = address_bar
        address_bar.pack(side="left", fill="both", expand=True, padx=3, pady=3)
        address_bar.pack(side="left", fill="both", expand=True, pady=3)
        address_bar.bind("<Return>", self._on_go)
        address_bar.bind("<KP_Enter>", self._on_go)
        address_bar.focus_set()


@@ 120,9 132,11 @@ class View:
        root.bind("<Control-l>", on_ctrl_l)

        # Go button
        go_btn = ttk.Button(row1, text="三三ᕕ( ᐛ )ᕗ", command=self._on_go, width=10)
        go_btn = AltButton(
            row1, text="Go", root=root, alt_char_index=0, command=self._on_go, width=5
        )
        self.go_btn = go_btn
        go_btn.pack(side="left", pady=3)
        go_btn.pack(side="left", padx=2, pady=3)

        # Main viewport implemented as a Text widget.
        text = ReadOnlyText(row2, wrap="word")

M src/mcross/gui/widgets.py => src/mcross/gui/widgets.py +40 -0
@@ 34,3 34,43 @@ class McEntry(ttk.Entry):
        self.select_range(0, "end")
        self.icursor("end")
        return "break"


class AltButton(ttk.Button):
    """
    Like Button but also supports Alt-<Key> shortcut (like Qt's Accelerator Keys).
    Accepts 3 extra args:
    - root: The root tk instance.
    - alt_char_index: Character index to be underlined when Alt is held down.
    - alt_key (optional): Explicit key name to pass to bind().
      If alt_key is not provided then it will be inferred from alt_char_index.
    """

    def __init__(self, *args, root, alt_char_index, alt_key=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.root = root

        alt_key = alt_key or self["text"][alt_char_index].lower()
        assert alt_key not in root.alt_shortcuts, f"Duplicate shortcut for {alt_key}"
        root.alt_shortcuts.add(alt_key)

        root.bind("<Alt_L>", self._alt_down, add="+")
        root.bind("<KeyRelease-Alt_L>", self._alt_up, add="+")
        root.bind(f"<Alt-{alt_key}>", self._alt_button_down)
        root.bind(f"<Alt-KeyRelease-{alt_key}>", self._alt_button_up)

        self.alt_char_index = alt_char_index
        self.alt_key = alt_key

    def _alt_down(self, event):
        self.config(underline=self.alt_char_index)

    def _alt_up(self, event):
        self.config(underline=-1)

    def _alt_button_down(self, event):
        self.state(["pressed"])

    def _alt_button_up(self, event):
        self.invoke()
        self.state(["!pressed"])