Skip to content

Commit

Permalink
feat(shortcuts): enhance shortcut handling with WX to VK code mapping
Browse files Browse the repository at this point in the history
- Add `WXK_TO_VK_CODE_MAP` to map WX key codes to virtual key (VK) codes.
- Rename `get_key_name_from_win32con` to `get_vk_code_display_name` for better clarity.
- Implement `get_vk_code` to facilitate conversion from WX to VK codes and integrate with existing logic.
- Update `ShortcutCaptureDialog` to correctly capture shortcuts using both modifier and VK codes.
- Improve UI with `update_ui` method and bind key events for enhanced interaction and shortcut setting consistency.
  • Loading branch information
AAClause committed Nov 5, 2024
1 parent 4cd2f8e commit d5d9f90
Showing 1 changed file with 116 additions and 15 deletions.
131 changes: 116 additions & 15 deletions basilisk/gui/global_shortcuts_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,115 @@ class HotkeyAction:
win32con.MOD_SHIFT: _("Shift"),
}

KEY_MAP: Dict[int, str] = {win32con.VK_SNAPSHOT: "PrintScreen"}
WXK_TO_VK_CODE_MAP: Dict[int, int] = {
wx.WXK_ESCAPE: win32con.VK_ESCAPE,
wx.WXK_F1: win32con.VK_F1,
wx.WXK_F2: win32con.VK_F2,
wx.WXK_F3: win32con.VK_F3,
wx.WXK_F4: win32con.VK_F4,
wx.WXK_F5: win32con.VK_F5,
wx.WXK_F6: win32con.VK_F6,
wx.WXK_F7: win32con.VK_F7,
wx.WXK_F8: win32con.VK_F8,
wx.WXK_F9: win32con.VK_F9,
wx.WXK_F10: win32con.VK_F10,
wx.WXK_F11: win32con.VK_F11,
wx.WXK_F12: win32con.VK_F12,
wx.WXK_F13: win32con.VK_F13,
wx.WXK_F14: win32con.VK_F14,
wx.WXK_F15: win32con.VK_F15,
wx.WXK_F16: win32con.VK_F16,
wx.WXK_F17: win32con.VK_F17,
wx.WXK_F18: win32con.VK_F18,
wx.WXK_F19: win32con.VK_F19,
wx.WXK_F20: win32con.VK_F20,
wx.WXK_F21: win32con.VK_F21,
wx.WXK_F22: win32con.VK_F22,
wx.WXK_F23: win32con.VK_F23,
wx.WXK_F24: win32con.VK_F24,
wx.WXK_SPACE: win32con.VK_SPACE,
wx.WXK_PAGEUP: win32con.VK_PRIOR,
wx.WXK_PAGEDOWN: win32con.VK_NEXT,
wx.WXK_HOME: win32con.VK_HOME,
wx.WXK_END: win32con.VK_END,
wx.WXK_LEFT: win32con.VK_LEFT,
wx.WXK_RIGHT: win32con.VK_RIGHT,
wx.WXK_UP: win32con.VK_UP,
wx.WXK_DOWN: win32con.VK_DOWN,
wx.WXK_INSERT: win32con.VK_INSERT,
wx.WXK_DELETE: win32con.VK_DELETE,
wx.WXK_MULTIPLY: win32con.VK_MULTIPLY,
wx.WXK_ADD: win32con.VK_ADD,
wx.WXK_SEPARATOR: win32con.VK_SEPARATOR,
wx.WXK_SUBTRACT: win32con.VK_SUBTRACT,
wx.WXK_DECIMAL: win32con.VK_DECIMAL,
wx.WXK_DIVIDE: win32con.VK_DIVIDE,
wx.WXK_NUMPAD0: win32con.VK_NUMPAD0,
wx.WXK_NUMPAD1: win32con.VK_NUMPAD1,
wx.WXK_NUMPAD2: win32con.VK_NUMPAD2,
wx.WXK_NUMPAD3: win32con.VK_NUMPAD3,
wx.WXK_NUMPAD4: win32con.VK_NUMPAD4,
wx.WXK_NUMPAD5: win32con.VK_NUMPAD5,
wx.WXK_NUMPAD6: win32con.VK_NUMPAD6,
wx.WXK_NUMPAD7: win32con.VK_NUMPAD7,
wx.WXK_NUMPAD8: win32con.VK_NUMPAD8,
wx.WXK_NUMPAD9: win32con.VK_NUMPAD9,
wx.WXK_NUMLOCK: win32con.VK_NUMLOCK,
wx.WXK_SCROLL: win32con.VK_SCROLL,
wx.WXK_BROWSER_BACK: win32con.VK_BROWSER_BACK,
wx.WXK_BROWSER_FORWARD: win32con.VK_BROWSER_FORWARD,
wx.WXK_VOLUME_MUTE: win32con.VK_VOLUME_MUTE,
wx.WXK_VOLUME_UP: win32con.VK_VOLUME_UP,
wx.WXK_VOLUME_DOWN: win32con.VK_VOLUME_DOWN,
wx.WXK_MEDIA_PREV_TRACK: win32con.VK_MEDIA_PREV_TRACK,
wx.WXK_MEDIA_NEXT_TRACK: win32con.VK_MEDIA_NEXT_TRACK,
wx.WXK_MEDIA_PLAY_PAUSE: win32con.VK_MEDIA_PLAY_PAUSE,
wx.WXK_BACK: win32con.VK_BACK,
wx.WXK_TAB: win32con.VK_TAB,
wx.WXK_RETURN: win32con.VK_RETURN,
wx.WXK_SHIFT: win32con.VK_SHIFT,
wx.WXK_CONTROL: win32con.VK_CONTROL,
wx.WXK_MENU: win32con.VK_MENU,
wx.WXK_PAUSE: win32con.VK_PAUSE,
wx.WXK_CAPITAL: win32con.VK_CAPITAL,
}

DISPLAY_NAME_KEY_MAP: Dict[int, str] = {win32con.VK_SNAPSHOT: "PrintScreen"}


def get_key_name_from_win32con(key_code: int) -> Optional[str]:
def get_vk_code_display_name(key_code: int) -> Optional[str]:
"""Retrieve the key name from win32con module."""
if ord('0') <= key_code <= ord('9') or ord('A') <= key_code <= ord('Z'):
return chr(key_code)
if key_code in DISPLAY_NAME_KEY_MAP:
return DISPLAY_NAME_KEY_MAP[key_code]
for attr_name in dir(win32con):
if attr_name.startswith("VK_"):
if getattr(win32con, attr_name) == key_code:
log.debug(f"Found key code: {attr_name}")
return attr_name[3:]
log.debug(f"Key code not found: {key_code}")
return None


def shortcut_to_string(shortcut: Tuple[int, int]) -> str:
"""Converts a shortcut tuple to a human-readable string."""
modifiers, raw_key_code = shortcut
modifiers, vk_code = shortcut
mod_parts = [name for mod, name in MODIFIER_MAP.items() if modifiers & mod]
key_name = get_key_name_from_win32con(raw_key_code) or chr(raw_key_code)
key_name = get_vk_code_display_name(vk_code)
return '+'.join(mod_parts + [key_name])


def get_vk_code(wx_key_code: int) -> Optional[int]:
"""Converts a WX key code to a VK code."""
if wx_key_code in WXK_TO_VK_CODE_MAP:
return WXK_TO_VK_CODE_MAP[wx_key_code]
elif ord('0') <= wx_key_code <= ord('9') or ord('A') <= wx_key_code <= ord(
'Z'
):
return wx_key_code
return None


class GlobalShortcutsDialog(wx.Dialog):
def __init__(self, parent: Optional[wx.Window] = None):
super().__init__(
Expand All @@ -74,6 +161,7 @@ def __init__(self, parent: Optional[wx.Window] = None):
),
}
self.init_ui()
self.update_ui()

def init_ui(self) -> None:
panel = wx.Panel(self)
Expand All @@ -92,13 +180,14 @@ def init_ui(self) -> None:
_("Shortcut"),
width=140,
)
self.populate_shortcut_list()
self.action_list.Bind(wx.EVT_KEY_DOWN, self.on_action_list)

assign_btn = wx.Button(panel, label="Assign")
assign_btn.Bind(wx.EVT_BUTTON, self.on_set_shortcut)

close_button = wx.Button(panel, wx.ID_CLOSE)
close_button.Bind(wx.EVT_BUTTON, self.on_close)
self.SetEscapeId(wx.ID_CLOSE)

main_sizer.Add(
self.action_list, proportion=1, flag=wx.EXPAND | wx.ALL, border=5
Expand All @@ -116,6 +205,15 @@ def populate_shortcut_list(self) -> None:
self.action_list.InsertItem(idx, action)
self.action_list.SetItem(idx, 1, shortcut_to_string(shortcut))

def update_ui(self) -> None:
self.populate_shortcut_list()

def on_action_list(self, event: wx.KeyEvent) -> None:
if event.GetKeyCode() == wx.WXK_RETURN:
self.on_set_shortcut(event)
else:
event.Skip()

def on_set_shortcut(self, event: wx.Event) -> None:
selected_index = self.action_list.GetFirstSelected()
if selected_index == -1:
Expand All @@ -131,7 +229,7 @@ def on_set_shortcut(self, event: wx.Event) -> None:
action = self.action_list.GetItemText(selected_index)
dlg = ShortcutCaptureDialog(self)
if dlg.ShowModal() == wx.ID_OK:
new_shortcut = dlg.get_shortcut()
new_shortcut = dlg.shortcut
if new_shortcut:
self.current_shortcuts[action] = new_shortcut
wx.MessageBox(
Expand All @@ -145,6 +243,8 @@ def on_set_shortcut(self, event: wx.Event) -> None:
wx.OK | wx.ICON_INFORMATION,
)
self.populate_shortcut_list()
self.action_list.Select(selected_index, on=True)
self.action_list.Focus(selected_index)

def on_close(self, event: wx.Event) -> None:
self.EndModal(wx.ID_CLOSE)
Expand All @@ -158,7 +258,7 @@ def __init__(self, parent: Optional[wx.Window] = None):
title=_("Press the Shortcut"),
size=(250, 100),
)
self.shortcut: Optional[Tuple[int, int]] = None
self._shortcut: Optional[Tuple[int, int]] = None
self.init_ui()

def init_ui(self) -> None:
Expand All @@ -174,7 +274,7 @@ def init_ui(self) -> None:
panel.Bind(wx.EVT_KEY_UP, self.on_key_up)

def on_key_up(self, event: wx.KeyEvent) -> None:
raw_key_code = event.GetRawKeyCode()
key_code = event.GetKeyCode()

mods = 0
if win32api.GetAsyncKeyState(
Expand All @@ -187,10 +287,10 @@ def on_key_up(self, event: wx.KeyEvent) -> None:
mods |= win32con.MOD_CONTROL
if win32api.GetAsyncKeyState(win32con.VK_SHIFT):
mods |= win32con.MOD_SHIFT

log.debug(f"Key down: {mods=}, key_code={raw_key_code=}")
if raw_key_code and mods:
self.shortcut = (mods, raw_key_code)
vk_code = get_vk_code(key_code)
log.debug(f"Key down: {mods=}, {key_code=}")
if mods and vk_code:
self._shortcut = (mods, vk_code)
self.EndModal(wx.ID_OK)
else:
log.debug("Invalid shortcut key combination.")
Expand All @@ -204,5 +304,6 @@ def on_key_up(self, event: wx.KeyEvent) -> None:
wx.OK | wx.ICON_WARNING,
)

def get_shortcut(self) -> Optional[Tuple[int, int]]:
return self.shortcut
@property
def shortcut(self) -> Optional[Tuple[int, int]]:
return self._shortcut

0 comments on commit d5d9f90

Please sign in to comment.