From c106d481637b44d91608bec513c5242056c7227a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 25 Aug 2019 17:22:44 +0530 Subject: [PATCH] UI for changing keyboard shortcuts is done --- src/pyj/read_book/iframe.pyj | 2 +- src/pyj/read_book/prefs/keyboard.pyj | 111 ++++++++++++++++++++++++--- src/pyj/read_book/shortcuts.pyj | 18 +++++ 3 files changed, 120 insertions(+), 11 deletions(-) diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 2e42f6a1c6..3fb207081f 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -153,7 +153,7 @@ class IframeBoss: self._handle_gesture = paged_handle_gesture self.anchor_funcs = paged_anchor_funcs update_settings(data.settings) - self.keyboard_shortcut_map = create_shortcut_map(data.keyboard_shortcuts) + self.keyboard_shortcut_map = create_shortcut_map(data.settings.keyboard_shortcuts) set_current_spine_item({'name':data.name, 'is_first':index is 0, 'is_last':index is spine.length - 1, 'initial_position':data.initial_position}) self.last_cfi = None for name in self.blob_url_map: diff --git a/src/pyj/read_book/prefs/keyboard.pyj b/src/pyj/read_book/prefs/keyboard.pyj index 10994e6597..aa5fa4a592 100644 --- a/src/pyj/read_book/prefs/keyboard.pyj +++ b/src/pyj/read_book/prefs/keyboard.pyj @@ -7,8 +7,10 @@ from gettext import gettext as _ from book_list.globals import get_session_data from book_list.item_list import create_item, create_item_list -from dom import unique_id -from read_book.shortcuts import GROUP_DESC, SHORTCUTS +from dom import clear, svgicon, unique_id +from read_book.shortcuts import ( + GROUP_DESC, SHORTCUTS, key_as_text, keyevent_as_shortcut, shortcut_differs +) from widgets import create_button @@ -17,7 +19,10 @@ def get_container(): def restore_defaults(): - pass + for item in get_container().querySelectorAll('[data-user-data]'): + q = JSON.parse(item.dataset.userData) + q.shortcuts = SHORTCUTS[q.name].shortcuts + item.dataset.userData = JSON.stringify(q) def as_groups(shortcuts): @@ -34,21 +39,92 @@ def sort_group_key(group, sc_name): return group[sc_name].short.toLowerCase() -def sc_as_item(sc_name, sc): - return create_item(sc.short, action=customize_shortcut.bind(None, sc_name), subtitle=sc.long, data=JSON.stringify({'name': sc_name, 'shortcuts': sc.shortcuts})) +def sc_as_item(sc_name, sc, shortcuts): + cuts = shortcuts or sc.shortcuts + return create_item(sc.short, action=customize_shortcut.bind(None, sc_name), subtitle=sc.long, data=JSON.stringify({'name': sc_name, 'shortcuts': cuts})) + + + +def remove_key(evt): + key_container = evt.currentTarget.parentNode.parentNode + key_container.parentNode.removeChild(key_container) + + +def key_widget(key): + return E.tr( + data_shortcut=JSON.stringify(keyevent_as_shortcut(key)), + E.td(style="padding: 0.5rem; border-right: solid 3px; margin-right: 0.5rem; margin-top: 0.5rem", key_as_text(key)), + E.td(E.a(class_='simple-link', '\xa0', svgicon('remove'), ' ', _('Remove'), onclick=remove_key)), + ) + + +def close_customize_shortcut(apply_changes): + container = get_container() + container.firstChild.style.display = 'block' + container.lastChild.style.display = 'none' + if apply_changes: + shortcuts = v'[]' + for x in container.lastChild.querySelectorAll('[data-shortcut]'): + sc = JSON.parse(x.dataset.shortcut) + shortcuts.push(sc) + sc_name = container.lastChild.dataset.scName + for item in container.querySelectorAll('[data-user-data]'): + q = JSON.parse(item.dataset.userData) + if q.name is sc_name: + q.shortcuts = shortcuts + item.dataset.userData = JSON.stringify(q) + break + + +def add_key_widget(): + uid = unique_id('add-new-shortcut') + return E.div(style='margin-top: 1ex; margin-bottom: 1ex', + id=uid, + E.div(create_button(_('Add a new shortcut'), icon="plus", action=def (evt): + div = document.getElementById(uid) + div.firstChild.style.display = 'none' + div.lastChild.style.display = 'block' + div.lastChild.querySelector('input').focus() + )), + E.div(style='display:none', + E.input(readonly='readonly', + value=_('Press the key combination to use as a shortcut'), + style='width: 90vw', + onkeydown=def (evt): + evt.preventDefault() + evt.stopPropagation() + if evt.key not in v"['Control', 'Meta', 'Alt', 'Shift']": + key_con = get_container().querySelector('.key-container') + key_con.appendChild(key_widget(evt)) + div = document.getElementById(uid) + div.firstChild.style.display = 'block' + div.lastChild.style.display = 'none' + )) + ) def customize_shortcut(sc_name): container = get_container() container.firstChild.style.display = 'none' container.lastChild.style.display = 'block' + shortcuts = v'[]' + for item in container.querySelectorAll('[data-user-data]'): + q = JSON.parse(item.dataset.userData) + if q.name is sc_name: + shortcuts = q.shortcuts + break + container = container.lastChild + clear(container) + container.dataset.scName = sc_name sc = SHORTCUTS[sc_name] container.appendChild(E.h4(sc.short)) if sc.long: container.appendChild(E.div(sc.long, style='font-style: italic; font-size: smaller; margin-top: 1ex')) - container.appendChild(E.div(_('Existing shortcuts:'))) - for key in sc.shortcuts: - container.appendChild(E.div(style='margin-top: 1ex', key_widget(key))) + container.appendChild(E.div(style='margin-top: 1rem', _('Existing shortcuts:'))) + key_con = container.appendChild(E.table(class_="key-container")) + for key in shortcuts: + key_con.appendChild(key_widget(key)) + container.appendChild(E.div(style='margin-top:1ex;', add_key_widget())) container.appendChild(E.div(style='margin-top:1ex; display:flex; justify-content: flex-end', create_button(_('OK'), action=close_customize_shortcut.bind(None, True)), E.span('\xa0'), @@ -64,6 +140,8 @@ def create_keyboard_panel(container): container.appendChild(E.div()) container.appendChild(E.div(style='display: none')) container = container.firstChild + sd = get_session_data() + custom_shortcuts = sd.get('keyboard_shortcuts') groups = as_groups(SHORTCUTS) items = [] for group_name in Object.keys(groups): @@ -71,7 +149,7 @@ def create_keyboard_panel(container): group = groups[group_name] for sc_name in sorted(Object.keys(group), key=sort_group_key.bind(None, group)): sc = group[sc_name] - items.push(sc_as_item(sc_name, sc)) + items.push(sc_as_item(sc_name, sc, custom_shortcuts[sc_name])) container.appendChild(E.div()) create_item_list(container.lastChild, items) @@ -83,7 +161,20 @@ def create_keyboard_panel(container): develop = create_keyboard_panel +def shortcuts_differ(a, b): + if a.length is not b.length: + return True + for x, y in zip(a, b): + if shortcut_differs(x, y): + return True + return False + + def commit_keyboard(onchange): sd = get_session_data() vals = {} - sd.set('standalone_misc_settings', vals) + for item in get_container().querySelectorAll('[data-user-data]'): + q = JSON.parse(item.dataset.userData) + if shortcuts_differ(q.shortcuts, SHORTCUTS[q.name].shortcuts): + vals[q.name] = q.shortcuts + sd.set('keyboard_shortcuts', vals) diff --git a/src/pyj/read_book/shortcuts.pyj b/src/pyj/read_book/shortcuts.pyj index 6159567340..69df6c66b5 100644 --- a/src/pyj/read_book/shortcuts.pyj +++ b/src/pyj/read_book/shortcuts.pyj @@ -41,6 +41,10 @@ def keyevent_as_shortcut(evt): } +def shortcut_differs(a, b): + return not (a.key is b.key and a.altKey is b.altKey and a.ctrlKey is b.ctrlKey and a.metaKey is b.metaKey and a.shiftKey is b.shiftKey) + + def keyevent_to_index(evt): parts = v'[]' for mod in v"['altKey', 'ctrlKey', 'metaKey', 'shiftKey']": @@ -48,6 +52,20 @@ def keyevent_to_index(evt): return parts.join('') + evt.key +def key_as_text(evt): + mods = v'[]' + for x in ('alt', 'ctrl', 'meta', 'shift'): + if evt[x + 'Key']: + mods.push(x.capitalize()) + mods = '+'.join(mods) + if mods: + mods += '+' + key = evt.key + if key is ' ': + key = 'Space' + return mods + key + + GROUP_DESC = { 'scroll': _('Navigation') }