Files
alacritty-config/alacritty_config_gui.py

1003 lines
34 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
import os
import platform
import sys
import yaml
try:
from PyQt5 import QtWidgets, QtGui, QtCore
except:
print('alacritty-config-gui depends on PyQt5. I couldnt locate PyQt5.')
print('Sorry about that.')
print('\nRunning `pip3 install PyQt5` could help.')
sys.exit(1)
ALACRITTY_CONFIG = os.path.expanduser('~/.config/alacritty/alacritty.yml')
NAME = 'Alacritty Config'
MODIFIERS = ['None', 'Command', 'Control', 'Option', 'Super', 'Shift', 'Alt']
# TODO
# visual bell
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__()
self.set_value(value.replace('0x', '#'))
self.clicked.connect(self.handle_clicked)
def handle_clicked(self):
color = QtWidgets.QColorDialog.getColor(QtGui.QColor(self._value))
if color.isValid():
self.set_value(color.name())
def set_value(self, value):
self._value = value
color = QtGui.QColor(self._value)
self.setFlat(True)
self.setAutoFillBackground(True)
self.setStyleSheet('''
QPushButton {{
color: {0};
background-color: {0};
border-style: outset;
}}
QPushButton:checked{{
color: {0};
background-color: {0};
border-style: outset;
}}
QPushButton:hover{{
background-color: {0};
border-style: outset;
}}
'''.format(color.name()))
self.update()
def value(self):
return self._value.replace('#', '0x')
class FontSelect(QtWidgets.QPushButton):
def __init__(self, family, style):
super().__init__()
self.set_value(family, style)
self.clicked.connect(self.handle_clicked)
def handle_clicked(self):
font, valid = QtWidgets.QFontDialog.getFont()
if valid:
self.set_value(font.family(), font.styleName())
def set_value(self, family, style):
self.family = family
self.style = style
self.setStyleSheet('''
QPushButton {{
background-color: white;
font-family: {0};
}}
QPushButton:checked{{
background-color: white;
}}
QPushButton:hover{{
background-color: white;
}}
'''.format(family))
self.setText(self.family)
self.update()
def value(self):
return {
'family': self.family,
'style': self.style
}
class Spoiler(QtWidgets.QWidget):
def __init__(self, title):
super().__init__()
self.toggle = QtWidgets.QToolButton()
self.toggle.setStyleSheet('QToolButton { border: none; }')
self.toggle.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.toggle.setArrowType(QtCore.Qt.ArrowType.RightArrow)
self.toggle.setText(title)
self.toggle.setCheckable(True)
self.toggle.setChecked(False)
self.header = QtWidgets.QFrame()
self.header.setFrameShape(QtWidgets.QFrame.HLine)
self.header.setFrameShadow(QtWidgets.QFrame.Sunken)
self.header.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum
)
self.content = QtWidgets.QScrollArea()
self.content.setStyleSheet('''
QScrollArea { border: none; background-color: transparent; }
''')
self.content.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
)
self.content.setMaximumHeight(0)
self.content.setMinimumHeight(0)
self.animation = QtCore.QParallelAnimationGroup()
self.animation.addAnimation(
QtCore.QPropertyAnimation(self, b'minimumHeight')
)
self.animation.addAnimation(
QtCore.QPropertyAnimation(self, b'maximumHeight')
)
self.animation.addAnimation(
QtCore.QPropertyAnimation(self.content, b'maximumHeight')
)
self.layout = QtWidgets.QGridLayout()
self.layout.addWidget(self.toggle, 0, 0, 1, 1, QtCore.Qt.AlignLeft)
self.layout.addWidget(self.header, 0, 2, 1, 1)
self.layout.addWidget(self.content, 1, 0, 1, 3)
self.layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.layout)
self.toggle.clicked.connect(self.update_state)
def update_state(self, checked):
self.toggle.setArrowType(
QtCore.Qt.ArrowType.DownArrow if checked
else QtCore.Qt.ArrowType.RightArrow
)
self.animation.setDirection(
QtCore.QAbstractAnimation.Forward if checked
else QtCore.QAbstractAnimation.Backward
)
self.animation.start()
def set_layout(self, layout):
self.content.setLayout(layout)
collapsed_height = self.sizeHint().height() - self.content.maximumHeight()
content_height = layout.sizeHint().height()
for i in range(self.animation.animationCount()-1):
animation = self.animation.animationAt(i)
animation.setDuration(300)
animation.setStartValue(collapsed_height)
animation.setEndValue(collapsed_height + content_height)
last = self.animation.animationAt(self.animation.animationCount() - 1)
last.setDuration(30)
last.setStartValue(0)
last.setEndValue(content_height)
class CommandWidget(QtWidgets.QLineEdit):
def value(self):
res = self.text().split(' ')
return {'program': res[0], 'args': res[1:]}
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 []))
def __init__(self):
super().__init__()
self.result = {}
self.layout = QtWidgets.QFormLayout()
self.layout.setFieldGrowthPolicy(
QtWidgets.QFormLayout.AllNonFixedFieldsGrow
)
self.key = QtWidgets.QLineEdit()
self.layout.addRow(QtWidgets.QLabel('Key'), self.key)
self.mods = QtWidgets.QListWidget()
for option in self.MODIFIERS:
self.mods.addItem(option)
self.mods.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.layout.addRow(QtWidgets.QLabel('Mods'), self.mods)
self.tabs = QtWidgets.QTabWidget()
self.action = QtWidgets.QComboBox()
for option in self.action_options:
self.action.addItem(option)
self.tabs.addTab(self.action, 'Action')
self.chars = QtWidgets.QLineEdit()
self.tabs.addTab(self.chars, 'Chars')
self.command = CommandWidget('')
self.tabs.addTab(self.command, 'Command')
self.layout.addWidget(self.tabs)
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
mods = self.mods.selectedItems()
if len(mods):
self.result['mods'] = '|'.join(mod.text() for mod in mods)
idx = self.tabs.currentIndex()
title = self.tabs.tabText(idx).lower()
if title == 'action':
self.result['action'] = self.action.text()
elif title == 'chars':
self.result['chars'] = self.chars.text()
elif title == 'command':
self.result['command'] = self.command.value()
self.accept()
class MouseBindingDialog(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 []))
mouse_options = [
'Middle', 'Left', 'Right', '1', '2', '3', '4', '5', '6', '7', '8', '9'
]
def __init__(self):
super().__init__()
self.result = {}
self.layout = QtWidgets.QFormLayout()
self.layout.setFieldGrowthPolicy(
QtWidgets.QFormLayout.AllNonFixedFieldsGrow
)
self.mouse = QtWidgets.QComboBox()
for option in self.mouse_options:
self.mouse.addItem(option)
self.layout.addRow(QtWidgets.QLabel('Mouse'), self.mouse)
self.mods = QtWidgets.QListWidget()
for option in MODIFIERS:
self.mods.addItem(option)
self.mods.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.layout.addRow(QtWidgets.QLabel('Mods'), self.mods)
self.action = QtWidgets.QComboBox()
for option in self.action_options:
self.action.addItem(option)
self.layout.addRow(QtWidgets.QLabel('Action'), self.action)
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):
mouse = self.mouse.currentText()
# TODO: this is an ugly heuristic. should we parse yaml here?
self.result['mouse'] = int(mouse) if mouse.isdigit() else mouse
mods = self.mods.selectedItems()
if len(mods):
self.result['mods'] = '|'.join(mod.text() for mod in mods)
self.result['action'] = self.action.currentText()
self.accept()
class MultiListWidget(QtWidgets.QWidget):
def __init__(self, elems):
super().__init__()
self.lst = None
self.elems = elems
self.init()
def init(self):
self.layout = QtWidgets.QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
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.elems), 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.elems):
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 = self.dialog()
if dialog.exec() == QtWidgets.QDialog.Accepted:
key_binding = dialog.result
self.elems.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.elems[i]
self.update()
def value(self):
return self.elems
class KeyBindingsWidget(MultiListWidget):
def __init__(self, *args):
super().__init__(*args)
self.dialog = KeyBindingDialog
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
class MouseBindingsWidget(MultiListWidget):
def __init__(self, *args):
super().__init__(*args)
self.dialog = MouseBindingDialog
def itemize(self, binding):
key = binding['mouse']
mods = binding.get('mods')
if mods:
key = '{} {}'.format(' '.join(mods.split('|')), key)
action = binding.get('action')
return key, action
class ConfigWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.widgets = {}
def prettify(self, s):
return ' '.join(x.capitalize() for x in s.split('_'))
def render_section(self, widgets, sup=None, name=None):
layout = QtWidgets.QFormLayout()
layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
for wname, widget in widgets.items():
if type(widget) is dict:
self.render_section(widget, layout, wname)
else:
layout.addRow(QtWidgets.QLabel(self.prettify(wname)), widget)
if name and sup is not None:
layout.setContentsMargins(10, 0, 0, 0)
spoiler = Spoiler(self.prettify(name))
spoiler.set_layout(layout)
sup.addWidget(spoiler)
layout.setAlignment(QtCore.Qt.AlignTop)
return layout
def render_state(self):
self.layout = self.render_section(self.widgets)
self.layout.setContentsMargins(100, 10, 100, 10)
self.setLayout(self.layout)
def widget_value(self, widget):
typ = type(widget)
if typ is QtWidgets.QComboBox:
return widget.currentText()
if typ is QtWidgets.QCheckBox:
return widget.isChecked()
if typ is QtWidgets.QLineEdit:
return widget.text()
if typ is QtWidgets.QListWidget:
res = []
for i in range(widget.count()):
item = widget.item(i).text()
if item:
res.append(item)
return res
return widget.value()
def widgets_to_state(self, widgets):
state = {}
for name, widget in widgets.items():
if not name:
return self.widget_value(widget)
elif type(widget) is dict:
state[name] = self.widgets_to_state(widget)
else:
state[name] = self.widget_value(widget)
return state
def gather_state(self):
return self.widgets_to_state(self.widgets)
class General(ConfigWidget):
cursor_style_options = ['Block', 'Underline', 'Beam']
def __init__(self, config):
super().__init__()
self.widgets['background_opacity'] = QtWidgets.QDoubleSpinBox()
self.widgets['background_opacity'].setValue(config.get('background_opacity'))
self.widgets['cursor_style'] = QtWidgets.QComboBox()
for option in self.cursor_style_options:
self.widgets['cursor_style'].addItem(option)
cur = config.get('cursor_style')
if cur in self.cursor_style_options:
self.widgets['cursor_style'].setCurrentIndex(
self.cursor_style_options.index(cur)
)
self.widgets['custom_cursor_colors'] = QtWidgets.QCheckBox()
self.widgets['custom_cursor_colors'].setChecked(
config.get('custom_cursor_colors', False)
)
self.widgets['draw_bold_text_with_bright_colors'] = QtWidgets.QCheckBox()
self.widgets['draw_bold_text_with_bright_colors'].setChecked(
config.get('draw_bold_text_with_bright_colors', False)
)
self.widgets['dynamic_title'] = QtWidgets.QCheckBox()
self.widgets['dynamic_title'].setChecked(
config.get('dynamic_title', False)
)
self.widgets['hide_cursor_when_typing'] = QtWidgets.QCheckBox()
self.widgets['hide_cursor_when_typing'].setChecked(
config.get('hide_cursor_when_typing', False)
)
self.widgets['live_config_reload'] = QtWidgets.QCheckBox()
self.widgets['live_config_reload'].setChecked(
config.get('live_config_reload', False)
)
self.widgets['tabspaces'] = QtWidgets.QSpinBox()
self.widgets['tabspaces'].setValue(config.get('tabspaces'))
self.widgets['unfocused_hollow_cursor'] = QtWidgets.QCheckBox()
self.widgets['unfocused_hollow_cursor'].setChecked(
config.get('unfocused_hollow_cursor', False)
)
self.render_state()
class Debug(ConfigWidget):
log_levels = ['None', 'Error', 'Warn', 'Info', 'Debug', 'Trace']
def __init__(self, config):
super().__init__()
self.widgets['render_timer'] = QtWidgets.QCheckBox()
self.widgets['render_timer'].setChecked(
config.get('render_timer', False)
)
self.widgets['persistent_logging'] = QtWidgets.QCheckBox()
self.widgets['persistent_logging'].setChecked(
config.get('persistent_logging', False)
)
self.widgets['log_level'] = QtWidgets.QComboBox()
for option in self.log_levels:
self.widgets['log_level'].addItem(option)
dec = config.get('log_level')
if dec in self.log_levels:
self.widgets['log_level'].setCurrentIndex(
self.log_levels.index(dec)
)
self.widgets['print_events'] = QtWidgets.QCheckBox()
self.widgets['print_events'].setChecked(
config.get('print_events', False)
)
self.widgets['ref_test'] = QtWidgets.QCheckBox()
self.widgets['ref_test'].setChecked(config.get('ref_test', False))
self.render_state()
class Env(ConfigWidget):
def __init__(self, config):
super().__init__()
self.widgets['TERM'] = QtWidgets.QLineEdit(config.get('TERM'))
self.render_state()
class Selection(ConfigWidget):
def __init__(self, config):
super().__init__()
self.widgets['semantic_escape_chars'] = QtWidgets.QLineEdit(
config.get('semantic_escape_chars')
)
self.widgets['save_to_clipboard'] = QtWidgets.QCheckBox()
self.widgets['save_to_clipboard'].setChecked(
config.get('save_to_clipboard', False)
)
self.render_state()
class Shell(ConfigWidget):
def __init__(self, config):
super().__init__()
self.widgets['program'] = QtWidgets.QLineEdit(config.get('program'))
self.widgets['args'] = QtWidgets.QListWidget()
for elem in config.get('args', []):
self.widgets['args'].addItem(elem)
for _ in range(3):
widget = QtWidgets.QListWidgetItem('')
widget.setFlags(widget.flags() | QtCore.Qt.ItemIsEditable)
self.widgets['args'].addItem(widget)
self.render_state()
class Font(ConfigWidget):
def __init__(self, config):
super().__init__()
glyph_offset_x = QtWidgets.QSpinBox()
glyph_offset_x.setValue(config.get('glyph_offset', {}).get('x'))
glyph_offset_y = QtWidgets.QSpinBox()
glyph_offset_y.setValue(config.get('glyph_offset', {}).get('y'))
offset_x = QtWidgets.QSpinBox()
offset_x.setValue(config.get('offset', {}).get('x'))
offset_y = QtWidgets.QSpinBox()
offset_y.setValue(config.get('offset', {}).get('y'))
scale_with_dpi = QtWidgets.QCheckBox()
scale_with_dpi.setChecked(config.get('scale_with_dpi'))
size = QtWidgets.QDoubleSpinBox()
size.setValue(config.get('size'))
use_thin_strokes = QtWidgets.QCheckBox()
use_thin_strokes.setChecked(config.get('use_thin_strokes'))
normal_config = config.get('normal', {})
normal = FontSelect(normal_config['family'], normal_config['style'])
italic_config = config.get('italic', {})
italic = FontSelect(italic_config['family'], italic_config['style'])
bold_config = config.get('bold', {})
bold = FontSelect(bold_config['family'], bold_config['style'])
self.widgets = {
'scale_with_dpi': scale_with_dpi,
'size': size,
'use_thin_strokes': use_thin_strokes,
'glyph_offset': {
'x': glyph_offset_x,
'y': glyph_offset_y,
},
'offset': {
'x': offset_x,
'y': offset_y,
},
'normal': normal,
'italic': italic,
'bold': bold,
}
self.render_state()
class Colors(ConfigWidget):
def __init__(self, config):
super().__init__()
primary_background = ColorSelect(
config.get('primary', {}).get('background')
)
primary_foreground = ColorSelect(
config.get('primary', {}).get('foreground')
)
cursor_background = ColorSelect(
config.get('cursor', {}).get('cursor')
)
cursor_foreground = ColorSelect(
config.get('cursor', {}).get('text')
)
normal_black = ColorSelect(config.get('normal', {}).get('black'))
normal_blue = ColorSelect(config.get('normal', {}).get('blue'))
normal_cyan = ColorSelect(config.get('normal', {}).get('cyan'))
normal_green = ColorSelect(config.get('normal', {}).get('green'))
normal_magenta = ColorSelect(config.get('normal', {}).get('magenta'))
normal_red = ColorSelect(config.get('normal', {}).get('red'))
normal_white = ColorSelect(config.get('normal', {}).get('white'))
normal_yellow = ColorSelect(config.get('normal', {}).get('yellow'))
bright_black = ColorSelect(config.get('bright', {}).get('black'))
bright_blue = ColorSelect(config.get('bright', {}).get('blue'))
bright_cyan = ColorSelect(config.get('bright', {}).get('cyan'))
bright_green = ColorSelect(config.get('bright', {}).get('green'))
bright_magenta = ColorSelect(config.get('bright', {}).get('magenta'))
bright_red = ColorSelect(config.get('bright', {}).get('red'))
bright_white = ColorSelect(config.get('bright', {}).get('white'))
bright_yellow = ColorSelect(config.get('bright', {}).get('yellow'))
dim_black = ColorSelect(config.get('dim', {}).get('black'))
dim_blue = ColorSelect(config.get('dim', {}).get('blue'))
dim_cyan = ColorSelect(config.get('dim', {}).get('cyan'))
dim_green = ColorSelect(config.get('dim', {}).get('green'))
dim_magenta = ColorSelect(config.get('dim', {}).get('magenta'))
dim_red = ColorSelect(config.get('dim', {}).get('red'))
dim_white = ColorSelect(config.get('dim', {}).get('white'))
dim_yellow = ColorSelect(config.get('dim', {}).get('yellow'))
self.widgets = {
'primary': {
'background': primary_background,
'foreground': primary_foreground,
},
'cursor': {
'cursor': cursor_background,
'text': cursor_foreground,
},
'normal': {
'black': normal_black,
'blue': normal_blue,
'cyan': normal_cyan,
'green': normal_green,
'magenta': normal_magenta,
'red': normal_red,
'white': normal_white,
'yellow': normal_yellow,
},
'bright': {
'black': bright_black,
'blue': bright_blue,
'cyan': bright_cyan,
'green': bright_green,
'magenta': bright_magenta,
'red': bright_red,
'white': bright_white,
'yellow': bright_yellow,
},
'dim': {
'black': dim_black,
'blue': dim_blue,
'cyan': dim_cyan,
'green': dim_green,
'magenta': dim_magenta,
'red': dim_red,
'white': dim_white,
'yellow': dim_yellow,
},
}
self.render_state()
class Window(ConfigWidget):
decoration_options = (
['full', 'none'] +
(['transparent', 'buttonless'] if platform.system() == 'Darwin' else [])
)
startup_options = (
['Windowed', 'FullScreen', 'Maximized'] +
(['SimpleFullScreen'] if platform.system() == 'Darwin' else [])
)
def __init__(self, config):
super().__init__()
decorations = QtWidgets.QComboBox()
for option in self.decoration_options:
decorations.addItem(option)
dec = config.get('decorations')
if dec in self.decoration_options:
decorations.setCurrentIndex(self.decoration_options.index(dec))
startup_mode = QtWidgets.QComboBox()
for mode in self.startup_options:
startup_mode.addItem(mode)
dec = config.get('startup_mode')
if dec in self.startup_options:
startup_mode.setCurrentIndex(self.startup_options.index(dec))
columns = QtWidgets.QSpinBox()
columns.setMaximum(300)
columns.setValue(config.get('dimensions', {}).get('columns', 80))
lines = QtWidgets.QSpinBox()
columns.setMaximum(200)
lines.setValue(config.get('dimensions', {}).get('lines', 24))
padding_x = QtWidgets.QSpinBox()
padding_x.setValue(config.get('padding', {}).get('x', 0))
padding_y = QtWidgets.QSpinBox()
padding_y.setValue(config.get('padding', {}).get('y', 0))
dynamic_padding = QtWidgets.QCheckBox()
dynamic_padding.setChecked(config.get('dynamic_padding', False))
title = QtWidgets.QLineEdit(config.get('title', 'Alacritty'))
self.widgets = {
'title': title,
'decorations': decorations,
'startup_mode': startup_mode,
'dynamic_padding': dynamic_padding,
'padding': {
'x': padding_x,
'y': padding_y,
},
'dimensions': {
'columns': columns,
'lines': lines,
}
}
if platform.system() == 'Linux':
self.widgets['class'] = QtWidgets.QLineEdit(
config.get('class', 'Alacritty')
)
self.render_state()
class Scrolling(ConfigWidget):
def __init__(self, config):
super().__init__()
history = QtWidgets.QSpinBox()
history.setMaximum(2**30)
history.setValue(config.get('history', 10000))
multiplier = QtWidgets.QSpinBox()
multiplier.setValue(config.get('multiplier', 3))
faux_multiplier = QtWidgets.QSpinBox()
faux_multiplier.setValue(config.get('faux_multiplier', 3))
autoscroll = QtWidgets.QCheckBox()
autoscroll.setChecked(config.get('autoscroll', False))
self.widgets = {
'history': history,
'multiplier': multiplier,
'faux_multiplier': faux_multiplier,
'faux_multiplier': faux_multiplier,
'autoscroll': autoscroll,
}
self.render_state()
class KeyBindings(ConfigWidget):
def __init__(self, config):
super().__init__()
key_bindings = KeyBindingsWidget(config)
self.widgets = {
'': key_bindings,
}
self.render_state()
class MouseBindings(ConfigWidget):
def __init__(self, config):
super().__init__()
mouse_bindings = MouseBindingsWidget(config)
self.widgets = {
'': mouse_bindings,
}
self.render_state()
class Mouse(ConfigWidget):
def __init__(self, config):
super().__init__()
double_threshold = QtWidgets.QSpinBox()
double_threshold.setMaximum(10000)
double_threshold.setValue(config.get('double_click', {}).get('threshold'))
triple_threshold = QtWidgets.QSpinBox()
triple_threshold.setMaximum(10000)
triple_threshold.setValue(config.get('triple_click', {}).get('threshold'))
hide_when_typing = QtWidgets.QCheckBox()
hide_when_typing.setChecked(config.get('hide_when_typing', False))
launcher = CommandWidget(config.get('url', {}).get('launcher', ''))
modifiers = QtWidgets.QComboBox()
for modifier in MODIFIERS:
modifiers.addItem(modifier)
dec = config.get('modifiers')
if dec in MODIFIERS:
modifiers.setCurrentIndex(self.modifier_options.index(dec))
else:
modifiers.setCurrentIndex(0)
self.widgets = {
'hide_when_typing': hide_when_typing,
'url': {
'launcher': launcher
},
'modifiers': modifiers,
'double_click': {
'threshold': double_threshold,
},
'triple_click': {
'threshold': triple_threshold,
},
}
self.render_state()
class Config(QtWidgets.QWidget):
def __init__(self, config, dry=False):
super().__init__()
self.config = config
self.dry = dry
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
self.add_tabs()
self.add_buttons()
def add_tabs(self):
self.tabs = QtWidgets.QTabWidget()
self.tabs.addTab(General(self.config), 'General')
self.tabs.setTabToolTip(0, 'General')
self.tabs.addTab(Window(self.config.get('window', {})), 'Window')
self.tabs.setTabToolTip(1, 'Window')
self.tabs.addTab(Font(self.config.get('font', {})), 'Font')
self.tabs.setTabToolTip(2, 'Font')
self.tabs.addTab(Debug(self.config.get('debug', {})), 'Debug')
self.tabs.setTabToolTip(3, 'Debug')
self.tabs.addTab(Env(self.config.get('env', {})), 'Env')
self.tabs.setTabToolTip(4, 'Env')
self.tabs.addTab(Selection(self.config.get('selection', {})), 'Selection')
self.tabs.setTabToolTip(5, 'Selection')
self.tabs.addTab(Shell(self.config.get('shell', {})), 'Shell')
self.tabs.setTabToolTip(6, 'Shell')
self.tabs.addTab(Colors(self.config.get('colors', {})), 'Colors')
self.tabs.setTabToolTip(7, 'Colors')
self.tabs.addTab(Scrolling(self.config.get('scrolling', {})), 'Scrolling')
self.tabs.setTabToolTip(8, 'Scrolling')
self.tabs.addTab(
KeyBindings(self.config.get('key_bindings', [])), 'Key Bindings'
)
self.tabs.setTabToolTip(9, 'Key Bindings')
self.tabs.addTab(
MouseBindings(self.config.get('mouse_bindings', [])), 'Mouse Bindings'
)
self.tabs.setTabToolTip(10, 'Mouse Bindings')
self.tabs.addTab(Mouse(self.config.get('mouse', {})), 'Mouse')
self.tabs.setTabToolTip(11, 'Mouse')
self.layout.addWidget(self.tabs)
def add_buttons(self):
self.buttons = QtWidgets.QDialogButtonBox()
ok_button = self.buttons.addButton(self.buttons.Ok)
self.buttons.addButton(self.buttons.Cancel)
self.buttons.accepted.connect(self.save)
self.buttons.rejected.connect(sys.exit)
self.layout.addWidget(self.buttons)
def gather_state(self):
state = self.config
for idx in range(self.tabs.count()):
tab = self.tabs.widget(idx)
title = '_'.join(self.tabs.tabText(idx).lower().split(' '))
old = state.get(title, {})
if type(old) == list:
state[title].extend(e for e in tab.gather_state() if e not in old)
else:
if title == 'general':
state = {**state, **tab.gather_state()}
else:
state[title] = {**state.get(title, {}), **tab.gather_state()}
return state
def save(self):
state = yaml.dump(self.gather_state())
if self.dry:
print(state)
else:
with open(ALACRITTY_CONFIG, 'w+') as f:
f.write(state)
sys.exit()
def main():
dry = False
if len(sys.argv) == 2 and sys.argv[1] in ['--dry', '-d']:
dry = True
app = QtWidgets.QApplication([NAME])
app.setApplicationName(NAME)
window = QtWidgets.QMainWindow()
with open(ALACRITTY_CONFIG) as f:
config = yaml.safe_load(f.read())
window.setCentralWidget(Config(config, dry=dry))
window.setWindowFlags(QtCore.Qt.FramelessWindowHint)
window.setWindowTitle(NAME)
window.setUnifiedTitleAndToolBarOnMac(True)
window.setGeometry(
QtWidgets.QStyle.alignedRect(
QtCore.Qt.LeftToRight,
QtCore.Qt.AlignCenter,
QtCore.QSize(500, 300),
app.desktop().availableGeometry()
)
)
window.show()
app.exec_()
if __name__ == '__main__':
main()