Wiring up Keybinding events

This commit is contained in:
itdominator 2023-03-18 17:04:31 -05:00
parent c96b9dbfda
commit 4ade08c736
12 changed files with 158 additions and 84 deletions

View File

@ -9,38 +9,32 @@ from gi.repository import Gdk
from gi.repository import GLib from gi.repository import GLib
# Application imports # Application imports
from .mixins.dummy_mixin import DummyMixin
from .controller_data import ControllerData from .controller_data import ControllerData
from .core_widget import CoreWidget from .core_widget import CoreWidget
from .mixins.signals_mixins import SignalsMixins
class Controller(SignalsMixins, ControllerData):
class Controller(DummyMixin, ControllerData):
def __init__(self, args, unknownargs): def __init__(self, args, unknownargs):
self.setup_controller_data()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self.setup_controller_data()
self.print_hello_world() # A mixin method from the DummyMixin file
logger.info(f"Made it past {self.__class__} loading...")
def _setup_styling(self): def _setup_styling(self):
... ...
def _setup_signals(self): def _setup_signals(self):
... self.window.connect("focus-out-event", self.unset_keys_and_data)
self.window.connect("key-press-event", self.on_global_key_press_controller)
self.window.connect("key-release-event", self.on_global_key_release_controller)
def _subscribe_to_events(self): def _subscribe_to_events(self):
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc) event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
def handle_file_from_ipc(self, path: str) -> None:
print(f"Path From IPC: {path}")
def load_glade_file(self): def load_glade_file(self):
self.builder = Gtk.Builder() self.builder = Gtk.Builder()
self.builder.add_from_file(settings.get_glade_file()) self.builder.add_from_file(settings.get_glade_file())
@ -53,24 +47,3 @@ class Controller(DummyMixin, ControllerData):
def get_core_widget(self): def get_core_widget(self):
return self.core_widget return self.core_widget
def on_global_key_release_controller(self, widget: type, event: type) -> None:
"""Handler for keyboard events"""
keyname = Gdk.keyval_name(event.keyval).lower()
if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
if "control" in keyname:
self.ctrl_down = False
if "shift" in keyname:
self.shift_down = False
if "alt" in keyname:
self.alt_down = False
mapping = keybindings.lookup(event)
if mapping:
getattr(self, mapping)()
return True
else:
print(f"on_global_key_release_controller > key > {keyname}")
print(f"Add logic or remove this from: {self.__class__}")

View File

@ -14,12 +14,15 @@ class ControllerData:
''' ControllerData contains most of the state of the app at ay given time. It also has some support methods. ''' ''' ControllerData contains most of the state of the app at ay given time. It also has some support methods. '''
def setup_controller_data(self) -> None: def setup_controller_data(self) -> None:
self.window = settings.get_main_window() self.window = settings.get_main_window()
self.builder = None self.builder = None
self.core_widget = None self.core_widget = None
self.ctrl_down = False
self.shift_down = False
self.alt_down = False
self.load_glade_file() self.load_glade_file()
self.plugins = PluginsController() self.plugins = PluginsController()
def clear_console(self) -> None: def clear_console(self) -> None:

View File

@ -1,3 +1,3 @@
""" """
Generic Mixins Module Mixins module
""" """

View File

@ -1,14 +0,0 @@
# Python imports
# Lib imports
# Application imports
class DummyMixin:
""" DummyMixin is an example of how mixins are used and structured in a project. """
def print_hello_world(self) -> None:
print("Hello, World!")

View File

@ -0,0 +1,3 @@
"""
Signals module
"""

View File

@ -0,0 +1,17 @@
# Python imports
# Lib imports
# Application imports
class IPCSignalsMixin:
""" IPCSignalsMixin handle messages from another starting solarfm process. """
def print_to_console(self, message=None):
print(message)
def handle_file_from_ipc(self, path: str) -> None:
print(f"Path From IPC: {path}")

View File

@ -0,0 +1,74 @@
# Python imports
import re
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
# Application imports
valid_keyvalue_pat = re.compile(r"[a-z0-9A-Z-_\[\]\(\)\| ]")
class KeyboardSignalsMixin:
""" KeyboardSignalsMixin keyboard hooks controller. """
# TODO: Need to set methods that use this to somehow check the keybindings state instead.
def unset_keys_and_data(self, widget=None, eve=None):
self.ctrl_down = False
self.shift_down = False
self.alt_down = False
def on_global_key_press_controller(self, eve, user_data):
keyname = Gdk.keyval_name(user_data.keyval).lower()
if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
if "control" in keyname:
self.ctrl_down = True
if "shift" in keyname:
self.shift_down = True
if "alt" in keyname:
self.alt_down = True
def on_global_key_release_controller(self, widget, event):
""" Handler for keyboard events """
keyname = Gdk.keyval_name(event.keyval).lower()
if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
if "control" in keyname:
self.ctrl_down = False
if "shift" in keyname:
self.shift_down = False
if "alt" in keyname:
self.alt_down = False
mapping = keybindings.lookup(event)
if mapping:
# See if in controller scope
try:
getattr(self, mapping)()
return True
except Exception:
# Must be plugins scope, event call, OR we forgot to add method to controller scope
if "||" in mapping:
sender, eve_type = mapping.split("||")
else:
sender = ""
eve_type = mapping
self.handle_key_event_system(sender, eve_type)
else:
logger.debug(f"on_global_key_release_controller > key > {keyname}")
if self.ctrl_down:
if not keyname in ["1", "kp_1", "2", "kp_2", "3", "kp_3", "4", "kp_4"]:
self.handle_key_event_system(None, mapping)
else:
...
def handle_key_event_system(self, sender, eve_type):
event_system.emit(eve_type)

View File

@ -0,0 +1,13 @@
# Python imports
# Lib imports
from .signals.ipc_signals_mixin import IPCSignalsMixin
from .signals.keyboard_signals_mixin import KeyboardSignalsMixin
# Application imports
class SignalsMixins(KeyboardSignalsMixin, IPCSignalsMixin):
...

View File

@ -37,10 +37,15 @@ class EditorNotebook(Gtk.Notebook):
event_system.subscribe("set_buffer_style", self.action_controller) event_system.subscribe("set_buffer_style", self.action_controller)
event_system.subscribe("set_buffer_language", self.action_controller) event_system.subscribe("set_buffer_language", self.action_controller)
event_system.subscribe("set_buffer_style", self.action_controller) event_system.subscribe("set_buffer_style", self.action_controller)
event_system.subscribe("open_files", self._open_files)
event_system.subscribe("toggle_highlight_line", self.action_controller) event_system.subscribe("toggle_highlight_line", self.action_controller)
event_system.subscribe("scale_up_text", self.action_controller) event_system.subscribe("keyboard_scale_up_text", self._keyboard_scale_up_text)
event_system.subscribe("scale_down_text", self.action_controller) event_system.subscribe("keyboard_scale_down_text", self._keyboard_scale_down_text)
event_system.subscribe("keyboard_create_tab", self.create_view)
event_system.subscribe("keyboard_close_tab", self._keyboard_close_tab)
def _open_files(self):
print("Open file stub...")
def _add_action_widgets(self): def _add_action_widgets(self):
start_box = Gtk.Box() start_box = Gtk.Box()
@ -85,18 +90,25 @@ class EditorNotebook(Gtk.Notebook):
self.show_all() self.show_all()
self.set_current_page(index) self.set_current_page(index)
def close_tab(self, button, scroll_view, source_view, eve = None): def close_tab(self, button, container, source_view, eve = None):
if self.get_n_pages() == 1: if self.get_n_pages() == 1:
return return
page_num = self.page_num(scroll_view) page_num = self.page_num(container)
watcher = source_view.get_file_watcher() watcher = source_view.get_file_watcher()
if watcher: if watcher:
watcher.cancel() watcher.cancel()
self.remove_page(page_num) self.remove_page(page_num)
def _keyboard_close_tab(self):
self.action_controller("close_tab")
def _keyboard_scale_up_text(self):
self.action_controller("scale_up_text")
def _keyboard_scale_down_text(self):
self.action_controller("scale_down_text")
def _text_search(self, widget = None, eve = None): def _text_search(self, widget = None, eve = None):
self.action_controller("do_text_search", widget.get_text()) self.action_controller("do_text_search", widget.get_text())
@ -118,6 +130,8 @@ class EditorNotebook(Gtk.Notebook):
self.scale_up_text(source_view) self.scale_up_text(source_view)
if action == "scale_down_text": if action == "scale_down_text":
self.scale_down_text(source_view) self.scale_down_text(source_view)
if action == "close_tab":
self.close_tab(None, container, source_view)
def do_text_search(self, query = ""): def do_text_search(self, query = ""):
source_view.scale_down_text() source_view.scale_down_text()

View File

@ -12,16 +12,15 @@ from gi.repository import Gdk
def err(log = ""): def err(log = ""):
"""Print an error message"""
print(log) print(log)
class KeymapError(Exception): class KeymapError(Exception):
"""Custom exception for errors in keybinding configurations""" """ Custom exception for errors in keybinding configurations """
MODIFIER = re.compile('<([^<]+)>') MODIFIER = re.compile('<([^<]+)>')
class Keybindings: class Keybindings:
"""Class to handle loading and lookup of Terminator keybindings""" """ Class to handle loading and lookup of Terminator keybindings """
modifiers = { modifiers = {
'ctrl': Gdk.ModifierType.CONTROL_MASK, 'ctrl': Gdk.ModifierType.CONTROL_MASK,
@ -44,12 +43,12 @@ class Keybindings:
self.configure({}) self.configure({})
def configure(self, bindings): def configure(self, bindings):
"""Accept new bindings and reconfigure with them""" """ Accept new bindings and reconfigure with them """
self.keys = bindings self.keys = bindings
self.reload() self.reload()
def reload(self): def reload(self):
"""Parse bindings and mangle into an appropriate form""" """ Parse bindings and mangle into an appropriate form """
self._lookup = {} self._lookup = {}
self._masks = 0 self._masks = 0
@ -66,10 +65,10 @@ class Keybindings:
try: try:
keyval, mask = self._parsebinding(binding) keyval, mask = self._parsebinding(binding)
# Does much the same, but with poorer error handling. # Does much the same, but with worse error handling.
#keyval, mask = Gtk.accelerator_parse(binding) # keyval, mask = Gtk.accelerator_parse(binding)
except KeymapError as e: except KeymapError as e:
err ("keybinding reload failed to parse binding '%s': %s" % (binding, e)) err(f"Keybinding reload failed to parse binding '{binding}': {e}")
else: else:
if mask & Gdk.ModifierType.SHIFT_MASK: if mask & Gdk.ModifierType.SHIFT_MASK:
if keyval == Gdk.KEY_Tab: if keyval == Gdk.KEY_Tab:
@ -88,7 +87,7 @@ class Keybindings:
self._masks |= mask self._masks |= mask
def _parsebinding(self, binding): def _parsebinding(self, binding):
"""Parse an individual binding using Gtk's binding function""" """ Parse an individual binding using Gtk's binding function """
mask = 0 mask = 0
modifiers = re.findall(MODIFIER, binding) modifiers = re.findall(MODIFIER, binding)
@ -103,25 +102,25 @@ class Keybindings:
keyval = Gdk.keyval_from_name(key) keyval = Gdk.keyval_from_name(key)
if keyval == 0: if keyval == 0:
raise KeymapError("Key '%s' is unrecognised..." % key) raise KeymapError(f"Key '{key}' is unrecognised...")
return (keyval, mask) return (keyval, mask)
def _lookup_modifier(self, modifier): def _lookup_modifier(self, modifier):
"""Map modifier names to gtk values""" """ Map modifier names to gtk values """
try: try:
return self.modifiers[modifier.lower()] return self.modifiers[modifier.lower()]
except KeyError: except KeyError:
raise KeymapError("Unhandled modifier '<%s>'" % modifier) raise KeymapError(f"Unhandled modifier '<{modifier}>'")
def lookup(self, event): def lookup(self, event):
"""Translate a keyboard event into a mapped key""" """ Translate a keyboard event into a mapped key """
try: try:
_found, keyval, _egp, _lvl, consumed = self.keymap.translate_keyboard_state( _found, keyval, _egp, _lvl, consumed = self.keymap.translate_keyboard_state(
event.hardware_keycode, event.hardware_keycode,
Gdk.ModifierType(event.get_state() & ~Gdk.ModifierType.LOCK_MASK), Gdk.ModifierType(event.get_state() & ~Gdk.ModifierType.LOCK_MASK),
event.group) event.group)
except TypeError: except TypeError:
err ("Keybinding lookup failed to translate keyboard event: %s" % dir(event)) err(f"Keybinding lookup failed to translate keyboard event: {dir(event)}")
return None return None
mask = (event.get_state() & ~consumed) & self._masks mask = (event.get_state() & ~consumed) & self._masks

View File

@ -66,6 +66,7 @@ class Settings(StartCheckMixin):
with open(self._KEY_BINDINGS_FILE) as file: with open(self._KEY_BINDINGS_FILE) as file:
print(self._KEY_BINDINGS_FILE)
bindings = json.load(file)["keybindings"] bindings = json.load(file)["keybindings"]
keybindings.configure(bindings) keybindings.configure(bindings)

View File

@ -1,23 +1,14 @@
{ {
"keybindings": { "keybindings": {
"help" : "F1", "help" : "F1",
"rename_files" : ["F2", "<Control>e"],
"open_terminal" : "F4", "open_terminal" : "F4",
"refresh_tab" : ["F5", "<Control>r"], "tggl_top_main_menubar" : "<Alt>h",
"delete_files" : "Delete",
"tggl_top_main_menubar" : "<Alt>",
"trash_files" : "<Shift><Control>t",
"tear_down" : "<Control>q", "tear_down" : "<Control>q",
"go_up" : "<Control>Up", "toggle_highlight_line" : "<Control>h",
"go_home" : "<Control>slash",
"grab_focus_path_entry" : "<Control>l",
"open_files" : "<Control>o", "open_files" : "<Control>o",
"show_hide_hidden_files" : "<Control>h",
"keyboard_create_tab" : "<Control>t", "keyboard_create_tab" : "<Control>t",
"keyboard_close_tab" : "<Control>w", "keyboard_close_tab" : "<Control>w",
"keyboard_copy_files" : "<Control>c", "keyboard_scale_up_text" : "<Control>equal",
"keyboard_cut_files" : "<Control>x", "keyboard_scale_down_text" : "<Control>minus"
"paste_files" : "<Control>v",
"show_new_file_menu" : "<Control>n"
} }
} }