Repos / mcross / 526da28ed3
commit 526da28ed3db85ceb5664dacba584876a3cdab5c
Author: Bùi Thành Nhân <hi@imnhan.com>
Date: Sun May 31 16:40:36 2020 +0700
implement alt-shortcuts for buttons
diff --git a/src/mcross/gui/controller.py b/src/mcross/gui/controller.py
index dfaccea..7a6662c 100644
--- a/src/mcross/gui/controller.py
+++ b/src/mcross/gui/controller.py
@@ -21,6 +21,7 @@
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")
diff --git a/src/mcross/gui/view.py b/src/mcross/gui/view.py
index d4a8a5a..0bc57da 100644
--- a/src/mcross/gui/view.py
+++ b/src/mcross/gui/view.py
@@ -13,7 +13,7 @@
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 emit(self, record):
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 @@ def __init__(self, root: Tk, model: Model):
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 @@ def __init__(self, root: Tk, model: Model):
# 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 @@ def on_ctrl_l(ev):
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")
diff --git a/src/mcross/gui/widgets.py b/src/mcross/gui/widgets.py
index 846bd54..e04cbc6 100644
--- a/src/mcross/gui/widgets.py
+++ b/src/mcross/gui/widgets.py
@@ -34,3 +34,43 @@ def select_all(self, ev=None):
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"])