added keybinding extendable logic
This commit is contained in:
parent
976318b958
commit
0cf3a8fe30
|
@ -5,7 +5,8 @@ import threading, subprocess, time
|
||||||
# Gtk imports
|
# Gtk imports
|
||||||
import gi
|
import gi
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk, GLib
|
gi.require_version('Gdk', '3.0')
|
||||||
|
from gi.repository import Gtk, Gdk, GLib
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from .mixins.dummy_mixin import DummyMixin
|
from .mixins.dummy_mixin import DummyMixin
|
||||||
|
@ -50,6 +51,27 @@ class Controller(DummyMixin, Controller_Data):
|
||||||
def handle_file_from_ipc(self, path):
|
def handle_file_from_ipc(self, path):
|
||||||
print(f"Path From IPC: {path}")
|
print(f"Path From IPC: {path}")
|
||||||
|
|
||||||
|
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 = self.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__}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_clipboard_data(self):
|
def get_clipboard_data(self):
|
||||||
proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE)
|
proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE)
|
||||||
|
|
|
@ -18,6 +18,8 @@ class Controller_Data:
|
||||||
self.builder = self.settings.get_builder()
|
self.builder = self.settings.get_builder()
|
||||||
self.window = self.settings.get_main_window()
|
self.window = self.settings.get_main_window()
|
||||||
self.logger = self.settings.get_logger()
|
self.logger = self.settings.get_logger()
|
||||||
|
self.keybindings = self.settings.get_keybindings()
|
||||||
|
|
||||||
|
|
||||||
self.home_path = self.settings.get_home_path()
|
self.home_path = self.settings.get_home_path()
|
||||||
self.success_color = self.settings.get_success_color()
|
self.success_color = self.settings.get_success_color()
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
# Python imports
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Gtk imports
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gdk', '3.0')
|
||||||
|
from gi.repository import Gdk
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def err(log = ""):
|
||||||
|
"""Print an error message"""
|
||||||
|
print(log)
|
||||||
|
|
||||||
|
|
||||||
|
class KeymapError(Exception):
|
||||||
|
"""Custom exception for errors in keybinding configurations"""
|
||||||
|
|
||||||
|
MODIFIER = re.compile('<([^<]+)>')
|
||||||
|
class Keybindings:
|
||||||
|
"""Class to handle loading and lookup of Terminator keybindings"""
|
||||||
|
|
||||||
|
modifiers = {
|
||||||
|
'ctrl': Gdk.ModifierType.CONTROL_MASK,
|
||||||
|
'control': Gdk.ModifierType.CONTROL_MASK,
|
||||||
|
'primary': Gdk.ModifierType.CONTROL_MASK,
|
||||||
|
'shift': Gdk.ModifierType.SHIFT_MASK,
|
||||||
|
'alt': Gdk.ModifierType.MOD1_MASK,
|
||||||
|
'super': Gdk.ModifierType.SUPER_MASK,
|
||||||
|
'hyper': Gdk.ModifierType.HYPER_MASK,
|
||||||
|
'mod2': Gdk.ModifierType.MOD2_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
empty = {}
|
||||||
|
keys = None
|
||||||
|
_masks = None
|
||||||
|
_lookup = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.keymap = Gdk.Keymap.get_default()
|
||||||
|
self.configure({})
|
||||||
|
|
||||||
|
def configure(self, bindings):
|
||||||
|
"""Accept new bindings and reconfigure with them"""
|
||||||
|
self.keys = bindings
|
||||||
|
self.reload()
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
"""Parse bindings and mangle into an appropriate form"""
|
||||||
|
self._lookup = {}
|
||||||
|
self._masks = 0
|
||||||
|
|
||||||
|
for action, bindings in list(self.keys.items()):
|
||||||
|
if isinstance(bindings, list):
|
||||||
|
bindings = (*bindings,)
|
||||||
|
elif not isinstance(bindings, tuple):
|
||||||
|
bindings = (bindings,)
|
||||||
|
|
||||||
|
|
||||||
|
for binding in bindings:
|
||||||
|
if not binding or binding == "None":
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
keyval, mask = self._parsebinding(binding)
|
||||||
|
# Does much the same, but with poorer error handling.
|
||||||
|
#keyval, mask = Gtk.accelerator_parse(binding)
|
||||||
|
except KeymapError as e:
|
||||||
|
err ("keybinding reload failed to parse binding '%s': %s" % (binding, e))
|
||||||
|
else:
|
||||||
|
if mask & Gdk.ModifierType.SHIFT_MASK:
|
||||||
|
if keyval == Gdk.KEY_Tab:
|
||||||
|
keyval = Gdk.KEY_ISO_Left_Tab
|
||||||
|
mask &= ~Gdk.ModifierType.SHIFT_MASK
|
||||||
|
else:
|
||||||
|
keyvals = Gdk.keyval_convert_case(keyval)
|
||||||
|
if keyvals[0] != keyvals[1]:
|
||||||
|
keyval = keyvals[1]
|
||||||
|
mask &= ~Gdk.ModifierType.SHIFT_MASK
|
||||||
|
else:
|
||||||
|
keyval = Gdk.keyval_to_lower(keyval)
|
||||||
|
|
||||||
|
self._lookup.setdefault(mask, {})
|
||||||
|
self._lookup[mask][keyval] = action
|
||||||
|
self._masks |= mask
|
||||||
|
|
||||||
|
def _parsebinding(self, binding):
|
||||||
|
"""Parse an individual binding using Gtk's binding function"""
|
||||||
|
mask = 0
|
||||||
|
modifiers = re.findall(MODIFIER, binding)
|
||||||
|
|
||||||
|
if modifiers:
|
||||||
|
for modifier in modifiers:
|
||||||
|
mask |= self._lookup_modifier(modifier)
|
||||||
|
|
||||||
|
key = re.sub(MODIFIER, '', binding)
|
||||||
|
if key == '':
|
||||||
|
raise KeymapError('No key found!')
|
||||||
|
|
||||||
|
keyval = Gdk.keyval_from_name(key)
|
||||||
|
|
||||||
|
if keyval == 0:
|
||||||
|
raise KeymapError("Key '%s' is unrecognised..." % key)
|
||||||
|
return (keyval, mask)
|
||||||
|
|
||||||
|
def _lookup_modifier(self, modifier):
|
||||||
|
"""Map modifier names to gtk values"""
|
||||||
|
try:
|
||||||
|
return self.modifiers[modifier.lower()]
|
||||||
|
except KeyError:
|
||||||
|
raise KeymapError("Unhandled modifier '<%s>'" % modifier)
|
||||||
|
|
||||||
|
def lookup(self, event):
|
||||||
|
"""Translate a keyboard event into a mapped key"""
|
||||||
|
try:
|
||||||
|
_found, keyval, _egp, _lvl, consumed = self.keymap.translate_keyboard_state(
|
||||||
|
event.hardware_keycode,
|
||||||
|
Gdk.ModifierType(event.get_state() & ~Gdk.ModifierType.LOCK_MASK),
|
||||||
|
event.group)
|
||||||
|
except TypeError:
|
||||||
|
err ("Keybinding lookup failed to translate keyboard event: %s" % dir(event))
|
||||||
|
return None
|
||||||
|
|
||||||
|
mask = (event.get_state() & ~consumed) & self._masks
|
||||||
|
return self._lookup.get(mask, self.empty).get(keyval, None)
|
|
@ -1,5 +1,5 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
import os
|
import os, json
|
||||||
|
|
||||||
# Gtk imports
|
# Gtk imports
|
||||||
import gi, cairo
|
import gi, cairo
|
||||||
|
@ -12,7 +12,7 @@ from gi.repository import Gdk
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from .logger import Logger
|
from .logger import Logger
|
||||||
|
from .keybindings import Keybindings
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ class Settings:
|
||||||
self._CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
|
self._CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
|
||||||
self._PLUGINS_PATH = f"{self._CONFIG_PATH}/plugins"
|
self._PLUGINS_PATH = f"{self._CONFIG_PATH}/plugins"
|
||||||
self._GLADE_FILE = f"{self._CONFIG_PATH}/Main_Window.glade"
|
self._GLADE_FILE = f"{self._CONFIG_PATH}/Main_Window.glade"
|
||||||
|
self._KEY_BINDINGS = f"{self._CONFIG_PATH}/key-bindings.json"
|
||||||
self._CSS_FILE = f"{self._CONFIG_PATH}/stylesheet.css"
|
self._CSS_FILE = f"{self._CONFIG_PATH}/stylesheet.css"
|
||||||
self._DEFAULT_ICONS = f"{self._CONFIG_PATH}/icons"
|
self._DEFAULT_ICONS = f"{self._CONFIG_PATH}/icons"
|
||||||
self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png"
|
self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png"
|
||||||
|
@ -35,6 +36,8 @@ class Settings:
|
||||||
|
|
||||||
if not os.path.exists(self._GLADE_FILE):
|
if not os.path.exists(self._GLADE_FILE):
|
||||||
self._GLADE_FILE = f"{self._USR_PATH}/Main_Window.glade"
|
self._GLADE_FILE = f"{self._USR_PATH}/Main_Window.glade"
|
||||||
|
if not os.path.exists(self._KEY_BINDINGS):
|
||||||
|
self._KEY_BINDINGS = f"{self._USR_SOLARFM}/key-bindings.json"
|
||||||
if not os.path.exists(self._CSS_FILE):
|
if not os.path.exists(self._CSS_FILE):
|
||||||
self._CSS_FILE = f"{self._USR_PATH}/stylesheet.css"
|
self._CSS_FILE = f"{self._USR_PATH}/stylesheet.css"
|
||||||
if not os.path.exists(self._WINDOW_ICON):
|
if not os.path.exists(self._WINDOW_ICON):
|
||||||
|
@ -54,6 +57,11 @@ class Settings:
|
||||||
self._warning_color = "#ffa800"
|
self._warning_color = "#ffa800"
|
||||||
self._error_color = "#ff0000"
|
self._error_color = "#ff0000"
|
||||||
|
|
||||||
|
self._keybindings = Keybindings()
|
||||||
|
with open(self._KEY_BINDINGS) as file:
|
||||||
|
keybindings = json.load(file)["keybindings"]
|
||||||
|
self._keybindings.configure(keybindings)
|
||||||
|
|
||||||
self._main_window = None
|
self._main_window = None
|
||||||
self._logger = Logger(self._CONFIG_PATH).get_logger()
|
self._logger = Logger(self._CONFIG_PATH).get_logger()
|
||||||
self._builder = Gtk.Builder()
|
self._builder = Gtk.Builder()
|
||||||
|
@ -105,6 +113,7 @@ class Settings:
|
||||||
|
|
||||||
def get_builder(self): return self._builder
|
def get_builder(self): return self._builder
|
||||||
def get_logger(self): return self._logger
|
def get_logger(self): return self._logger
|
||||||
|
def get_keybindings(self): return self._keybindings
|
||||||
def get_main_window(self): return self._main_window
|
def get_main_window(self): return self._main_window
|
||||||
def get_home_path(self): return self._USER_HOME
|
def get_home_path(self): return self._USER_HOME
|
||||||
def get_plugins_path(self): return self._PLUGINS_PATH
|
def get_plugins_path(self): return self._PLUGINS_PATH
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.22.1 -->
|
<!-- Generated with glade 3.38.2 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.20"/>
|
<requires lib="gtk+" version="3.20"/>
|
||||||
<object class="GtkApplicationWindow" id="Main_Window">
|
<object class="GtkApplicationWindow" id="Main_Window">
|
||||||
<property name="can_focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<child>
|
<signal name="key-release-event" handler="on_global_key_release_controller" swapped="no"/>
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<placeholder/>
|
<placeholder/>
|
||||||
</child>
|
</child>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"keybindings": {
|
||||||
|
"help" : "F1",
|
||||||
|
"rename_files" : ["F2", "<Control>e"],
|
||||||
|
"open_terminal" : "F4",
|
||||||
|
"refresh_tab" : ["F5", "<Control>r"],
|
||||||
|
"delete_files" : "Delete",
|
||||||
|
"tggl_top_main_menubar" : "<Alt>",
|
||||||
|
"trash_files" : "<Shift><Control>t",
|
||||||
|
"tear_down" : "<Control>q",
|
||||||
|
"go_up" : "<Control>Up",
|
||||||
|
"go_home" : "<Control>slash",
|
||||||
|
"grab_focus_path_entry" : "<Control>l",
|
||||||
|
"open_files" : "<Control>o",
|
||||||
|
"show_hide_hidden_files" : "<Control>h",
|
||||||
|
"keyboard_create_tab" : "<Control>t",
|
||||||
|
"keyboard_close_tab" : "<Control>w",
|
||||||
|
"keyboard_copy_files" : "<Control>c",
|
||||||
|
"keyboard_cut_files" : "<Control>x",
|
||||||
|
"paste_files" : "<Control>v",
|
||||||
|
"show_new_file_menu" : "<Control>n"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue