"""
Includes the help dialog for DIPLOMAT's main gui editor. Displays toolbar actions and keyboard shortcuts.
"""
from typing import Tuple, Optional, List, Union, Callable
from diplomat.utils._bit_or import _bit_or
import wx
Opt = Optional
[docs]
def is_ascii(s):
"""
Meant for internal use: Checks if the passed string is made of only ascii characters.
"""
return all(ord(c) < 128 for c in s)
[docs]
class HelpDialog(wx.Dialog):
"""
A custom wx.Dialog. This dialog can be used to describe UI shortcuts and their related keyboard shortcuts, allowing
the user to see how to use a UI.
"""
# Modifier key to string for displaying in the help dialog.
MOD_TO_STR = {wx.ACCEL_CTRL: "Ctrl", wx.ACCEL_ALT: "Alt", wx.ACCEL_SHIFT: "Shift"}
# These get displayed for keyboard shortcuts which are not alphanumeric...
MIS_KEYS = {
wx.WXK_RIGHT: "\u2192",
wx.WXK_LEFT: "\u2190",
wx.WXK_UP: "\u2191",
wx.WXK_DOWN: "\u2193",
wx.WXK_SPACE: "Space",
wx.WXK_BACK: "Backspace",
}
[docs]
def __init__(
self,
parent,
entries: List[
Tuple[
Union[wx.Bitmap, Callable[[wx.Colour, Tuple[int, int]], wx.Bitmap]],
Opt[Tuple[int, int]],
str,
]
],
image_sizes: Tuple[int, int],
wid=wx.ID_ANY,
title="Help",
pos=wx.DefaultPosition,
size=wx.DefaultSize,
style=_bit_or(wx.DEFAULT_DIALOG_STYLE, wx.RESIZE_BORDER),
name="helpDialog",
):
"""
Construct a new help dialog.
:param parent: The parent window.
:param entries: A list of tuples, each tuple containing a wx.Bitmap being the action icon, an optional
wx.AcceleratorTable style shortcut (None to disable), and a string description describing the
action. These will be parsed and shown to the user in the help dialog as a table.
:param image_sizes: The size of all of the images passed to this method, a tuple of two integers.
:param wid: wx ID of the window, and integer. Defaults to wx.ID_ANY.
:param title: The string title of the help dialog, defaults to "Help".
:param pos: WX Position of dialog. Defaults to wx.DefaultPosition.
:param size: WX Size of the dialog. Defaults to wx.DefaultSize.
:param style: WX Dialog Style. See wx.Dialog docs for possible options.
:param name: WX internal name of widget.
"""
super().__init__(parent, wid, title, pos, size, style, name)
self._main_sizer = wx.BoxSizer(wx.VERTICAL)
self._main_panel = wx.Panel(self)
self._sub_sizer = wx.BoxSizer(wx.VERTICAL)
self._list = wx.ListCtrl(self._main_panel, style=wx.LC_REPORT | wx.LC_ALIGN_TOP)
self._list.AppendColumn("Tool Icon")
self._list.AppendColumn("Shortcut")
self._list.AppendColumn("Description")
self._img_lst = wx.ImageList(*image_sizes, True)
self._dynamic_images = []
for i, (icon, shortcut, desc) in enumerate(entries):
if not isinstance(icon, wx.Bitmap):
self._dynamic_images.append((i, icon))
icon = icon(self.GetForegroundColour(), image_sizes)
self._img_lst.Add(icon)
self._list.SetImageList(self._img_lst, wx.IMAGE_LIST_SMALL)
for i, (icon, shortcut, desc) in enumerate(entries):
self._list.InsertItem(i, i)
self._list.SetItem(i, 1, self.shortcut_to_string(shortcut))
self._list.SetItem(i, 2, desc)
# Resize the columns:
for i in range(self._list.GetColumnCount()):
self._list.SetColumnWidth(i, wx.LIST_AUTOSIZE)
self._sub_sizer.Add(self._list, 1, wx.EXPAND)
self._main_panel.SetSizerAndFit(self._sub_sizer)
self._btns = self.CreateButtonSizer(wx.CLOSE)
self._main_sizer.Add(self._main_panel, 1, wx.EXPAND)
if self._btns is not None:
self._main_sizer.Add(self._btns, 0, wx.EXPAND)
self.SetSizerAndFit(self._main_sizer)
self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self._theme_change)
def _theme_change(self, evt):
img_size = self._img_lst.GetSize().Get()
for i, icon_generator in self._dynamic_images:
self._img_lst.Replace(
i, icon_generator(self.GetForegroundColour(), img_size)
)
evt.Skip()
[docs]
@classmethod
def shortcut_to_string(cls, shortcut: Opt[Tuple[int, int]]):
"""
Meant for internal use: Converts a wx.AcceleratorTable style shortcut to a string for display to the user.
"""
if shortcut is None:
return ""
if isinstance(shortcut, str):
return shortcut
accel, letter = shortcut
key_list = [
string
for accel_btn, string in cls.MOD_TO_STR.items()
if ((accel & accel_btn) == accel_btn)
]
if letter is not None:
letter_c = chr(letter)
if letter_c.isalnum() and is_ascii(letter_c) and (not letter_c.isspace()):
key_list.append(letter_c)
else:
letter_c = cls.MIS_KEYS.get(letter, None)
if letter_c is not None:
key_list.append(letter_c)
if len(key_list) == 0:
return ""
return "+".join(key_list)