diff --git a/alacritty_config_gui.py b/alacritty_config_gui.py index c1346be..29d93cb 100644 --- a/alacritty_config_gui.py +++ b/alacritty_config_gui.py @@ -12,6 +12,16 @@ ALACRITTY_CONFIG = os.path.expanduser("~/.config/alacritty/alacritty.yml") NAME = 'Alacritty Config' +def delete_layout_items(layout): + while layout.count(): + item = layout.takeAt(0) + widget = item.widget() + if widget is not None: + widget.setParent(None) + else: + delete_layout_items(item.layout()) + + class ColorSelect(QtWidgets.QPushButton): def __init__(self, value): super().__init__() @@ -159,6 +169,161 @@ class Spoiler(QtWidgets.QWidget): last.setEndValue(content_height) +class KeyBindingDialog(QtWidgets.QDialog): + action_options = ([ + "", "Copy", "Paste", "PasteSelection", "IncreaseFontSize", "DecreaseFontSize", + "ResetFontSize", "ScrollPageUp", "ScrollPageDown", "ScrollLineUp", + "ScrollLineDown", "ScrollToTop", "ScrollToBottom", "ClearHistory", "Hide", + "Quit", "ToggleFullscreen", "SpawnNewInstance", "ClearLogNotice", "None", + ] + (['ToggleSimpleFullscreen'] if platform.system() == 'Darwin' else [])) + + modifier_options = ["Command", "Control", "Option", "Super", "Shift", "Alt"] + + def __init__(self): + super().__init__() + self.result = {} + self.layout = QtWidgets.QVBoxLayout() + + sub_layout = QtWidgets.QHBoxLayout() + sub_layout.addWidget(QtWidgets.QLabel('Key')) + self.key = QtWidgets.QLineEdit() + sub_layout.addWidget(self.key) + self.layout.addLayout(sub_layout) + + sub_layout = QtWidgets.QHBoxLayout() + sub_layout.addWidget(QtWidgets.QLabel('Mods')) + self.mods = QtWidgets.QListWidget() + for option in self.modifier_options: + self.mods.addItem(option) + self.mods.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + sub_layout.addWidget(self.mods) + self.layout.addLayout(sub_layout) + + sub_layout = QtWidgets.QHBoxLayout() + sub_layout.addWidget(QtWidgets.QLabel('Action')) + self.action = QtWidgets.QComboBox() + for option in self.action_options: + self.action.addItem(option) + sub_layout.addWidget(self.action) + self.layout.addLayout(sub_layout) + + sub_layout = QtWidgets.QHBoxLayout() + sub_layout.addWidget(QtWidgets.QLabel('Chars')) + self.chars = QtWidgets.QLineEdit() + sub_layout.addWidget(self.chars) + self.layout.addLayout(sub_layout) + + buttons = QtWidgets.QDialogButtonBox() + buttons.addButton(buttons.Ok) + buttons.addButton(buttons.Cancel) + buttons.accepted.connect(self.do_accept) + buttons.rejected.connect(self.reject) + self.layout.addWidget(buttons) + + self.setLayout(self.layout) + + def do_accept(self): + key = self.key.text() + # TODO: this is an ugly heuristic. should we parse yaml here? + self.result['key'] = int(key) if key.isdigit() else key + + action = self.action.currentText() + if action: + self.result['action'] = action + + chars = self.chars.text() + if chars: + self.result['chars'] = chars + + mods = self.mods.selectedItems() + + if len(mods): + self.result['mods'] = '|'.join(mod.text() for mod in mods) + + self.accept() + + +class KeyBindingsWidget(QtWidgets.QWidget): + def __init__(self, key_bindings): + super().__init__() + self.key_bindings = key_bindings + self.lst = None + self.init() + + def init(self): + self.layout = QtWidgets.QVBoxLayout() + self.update() + btn_group = QtWidgets.QHBoxLayout() + btn_group.setAlignment(QtCore.Qt.AlignLeft) + self.layout.addLayout(btn_group) + btn = QtWidgets.QPushButton('+') + btn.setMaximumSize(40, 40) + btn.clicked.connect(self.open_dialog) + btn_group.addWidget(btn) + btn = QtWidgets.QPushButton('-') + btn.setMaximumSize(40, 40) + btn.clicked.connect(self.delete_selected) + btn_group.addWidget(btn) + self.setLayout(self.layout) + + def update(self): + if self.lst: + self.lst.setParent(None) + self.lst = QtWidgets.QTableWidget(len(self.key_bindings), 2) + self.lst.setShowGrid(False) + horizontal_header = self.lst.horizontalHeader() + horizontal_header.setVisible(False) + # TODO: why do i need this? + horizontal_header.resizeSection(0, 200) + horizontal_header.resizeSection(1, 200) + self.lst.verticalHeader().setVisible(False) + self.lst.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.lst.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + for i, binding in enumerate(self.key_bindings): + key, action = self.itemize(binding) + self.lst.setItem(i, 0, QtWidgets.QTableWidgetItem(key)) + self.lst.setItem(i, 1, QtWidgets.QTableWidgetItem(action)) + self.layout.insertWidget(0, self.lst) + + def open_dialog(self): + dialog = KeyBindingDialog() + + if dialog.exec() == QtWidgets.QDialog.Accepted: + key_binding = dialog.result + self.key_bindings.append(key_binding) + self.update() + + def delete_selected(self): + to_delete = set() + for i in self.lst.selectedIndexes(): + to_delete.add(i.row()) + + for i in sorted(to_delete, reverse=True): + del self.key_bindings[i] + self.update() + + def itemize(self, binding): + key = binding['key'] + + mods = binding.get('mods') + if mods: + key = '{} {}'.format(' '.join(mods.split('|')), key) + + action = binding.get('action') + + command = binding.get('command') + if command: + action = 'cmd({} {})'.format(command['program'], ' '.join(command['args'])) + + chars = binding.get('chars') + if chars: + action = 'chars({})'.format(chars) + return key, action + + def value(self): + return self.key_bindings + + class ConfigWidget(QtWidgets.QWidget): def __init__(self): super().__init__() @@ -515,6 +680,19 @@ class Scrolling(ConfigWidget): self.render_state() +class KeyBindings(ConfigWidget): + def __init__(self, config): + super().__init__() + + keybindings = KeyBindingsWidget(config) + + self.widgets = { + 'key_bindings': keybindings, + } + + self.render_state() + + class Config(QtWidgets.QWidget): def __init__(self, config): super().__init__() @@ -536,6 +714,9 @@ class Config(QtWidgets.QWidget): self.tabs.addTab(Shell(self.config.get('shell', {})), "Shell") self.tabs.addTab(Colors(self.config.get('colors', {})), "Colors") self.tabs.addTab(Scrolling(self.config.get('scrolling', {})), "Scrolling") + self.tabs.addTab( + KeyBindings(self.config.get('key_bindings', [])), "Key Bindings" + ) self.layout.addWidget(self.tabs)