mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
UI for customizing palettes
Needs integration in to calibre preferences
This commit is contained in:
parent
085862b062
commit
b47e0d5125
@ -427,6 +427,10 @@ def create_defs():
|
||||
defs['show_notes_in_tag_browser'] = False
|
||||
defs['icons_on_right_in_tag_browser'] = True
|
||||
defs['cover_browser_narrow_view_position'] = 'automatic'
|
||||
defs['dark_palette_name'] = ''
|
||||
defs['light_palette_name'] = ''
|
||||
defs['dark_palettes'] = {}
|
||||
defs['light_palettes'] = {}
|
||||
|
||||
def migrate_tweak(tweak_name, pref_name):
|
||||
# If the tweak has been changed then leave the tweak in the file so
|
||||
|
191
src/calibre/gui2/dialogs/palette.py
Normal file
191
src/calibre/gui2/dialogs/palette.py
Normal file
@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python
|
||||
# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from qt.core import (
|
||||
QCheckBox, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QPalette, QScrollArea,
|
||||
QSize, QSizePolicy, QTabWidget, QVBoxLayout, QWidget, pyqtSignal,
|
||||
)
|
||||
|
||||
from calibre.gui2 import Application, gprefs
|
||||
from calibre.gui2.palette import (
|
||||
default_dark_palette, default_light_palette, palette_colors, palette_from_dict,
|
||||
)
|
||||
from calibre.gui2.widgets2 import ColorButton, Dialog
|
||||
|
||||
|
||||
class Color(QWidget):
|
||||
|
||||
changed = pyqtSignal()
|
||||
|
||||
def __init__(self, key: str, desc: str, parent: 'PaletteColors', palette: QPalette, default_palette: QPalette, mode_name: str, group=''):
|
||||
super().__init__(parent)
|
||||
self.key = key
|
||||
self.setting_key = (key + '-' + group) if group else key
|
||||
self.mode_name = mode_name
|
||||
self.default_palette = default_palette
|
||||
self.color_key = QPalette.ColorGroup.Disabled if group == 'disabled' else QPalette.ColorGroup.Active, getattr(QPalette.ColorRole, key)
|
||||
self.initial_color = palette.color(*self.color_key)
|
||||
self.l = l = QHBoxLayout(self)
|
||||
self.button = b = ColorButton(self.initial_color.name(), self)
|
||||
b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||
b.color_changed.connect(self.color_changed)
|
||||
l.addWidget(b)
|
||||
|
||||
self.la = la = QLabel(desc)
|
||||
la.setBuddy(b)
|
||||
l.addWidget(la)
|
||||
|
||||
def restore_defaults(self):
|
||||
self.button.color = self.default_palette.color(*self.color_key)
|
||||
|
||||
def color_changed(self):
|
||||
self.changed.emit()
|
||||
self.la.setStyleSheet('QLabel { font-style: italic }')
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
ans = self.button.color
|
||||
if ans != self.default_palette.color(*self.color_key):
|
||||
return ans
|
||||
|
||||
|
||||
class PaletteColors(QWidget):
|
||||
|
||||
def __init__(self, palette: QPalette, default_palette: QPalette, mode_name: str, parent=None):
|
||||
super().__init__(parent)
|
||||
self.link_colors = {}
|
||||
self.mode_name = mode_name
|
||||
self.foreground_colors = {}
|
||||
self.background_colors = {}
|
||||
self.default_palette = default_palette
|
||||
|
||||
for key, desc in palette_colors().items():
|
||||
if 'Text' in key:
|
||||
self.foreground_colors[key] = desc
|
||||
elif 'Link' in key:
|
||||
self.link_colors[key] = desc
|
||||
else:
|
||||
self.background_colors[key] = desc
|
||||
|
||||
self.l = l = QVBoxLayout(self)
|
||||
self.colors = []
|
||||
|
||||
def header(text):
|
||||
ans = QLabel(text)
|
||||
f = ans.font()
|
||||
f.setBold(True)
|
||||
ans.setFont(f)
|
||||
return ans
|
||||
|
||||
def c(x, desc):
|
||||
w = Color(x, desc, self, palette, default_palette, mode_name)
|
||||
l.addWidget(w)
|
||||
self.colors.append(w)
|
||||
|
||||
l.addWidget(header(_('Background colors')))
|
||||
for x, desc in self.background_colors.items():
|
||||
c(x, desc)
|
||||
|
||||
l.addWidget(header(_('Foreground (text) colors')))
|
||||
for x, desc in self.foreground_colors.items():
|
||||
c(x, desc)
|
||||
|
||||
l.addWidget(header(_('Foreground (text) colors when disabled')))
|
||||
for x, desc in self.foreground_colors.items():
|
||||
c(x, desc)
|
||||
|
||||
l.addWidget(header(_('Link colors')))
|
||||
for x, desc in self.link_colors.items():
|
||||
c(x, desc)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
ans = {}
|
||||
for w in self.colors:
|
||||
v = w.value
|
||||
if v is not None:
|
||||
ans[w.setting_key] = w.value
|
||||
return ans
|
||||
|
||||
def restore_defaults(self):
|
||||
for w in self.colors:
|
||||
w.restore_defaults()
|
||||
|
||||
|
||||
class PaletteWidget(QWidget):
|
||||
|
||||
def __init__(self, mode_name='light', parent=None):
|
||||
super().__init__(parent)
|
||||
self.mode_name = mode_name
|
||||
self.mode_title = {'dark': _('dark'), 'light': _('light')}[mode_name]
|
||||
self.l = l = QVBoxLayout(self)
|
||||
self.la = la = QLabel(_('These colors will be used for the calibre interface when calibre is in "{}" mode').format(self.mode_title))
|
||||
l.addWidget(la)
|
||||
la.setWordWrap(True)
|
||||
self.use_custom = uc = QCheckBox(_('Use a &custom color scheme'))
|
||||
uc.setChecked(bool(gprefs[f'{mode_name}_palette_name']))
|
||||
l.addWidget(uc)
|
||||
uc.toggled.connect(self.use_custom_toggled)
|
||||
|
||||
pdata = gprefs[f'{mode_name}_palettes'].get('__current__', {})
|
||||
default_palette = default_dark_palette() if mode_name == 'dark' else default_light_palette()
|
||||
palette = palette_from_dict(pdata, default_palette)
|
||||
self.sa = sa = QScrollArea(self)
|
||||
l.addWidget(sa)
|
||||
self.palette_colors = pc = PaletteColors(palette, default_palette, mode_name, self)
|
||||
sa.setWidget(pc)
|
||||
self.use_custom_toggled()
|
||||
|
||||
def sizeHint(self):
|
||||
return QSize(800, 600)
|
||||
|
||||
def use_custom_toggled(self):
|
||||
self.palette_colors.setEnabled(self.use_custom.isChecked())
|
||||
|
||||
def apply_settings(self):
|
||||
val = self.palette_colors.value
|
||||
v = gprefs[f'{self.mode_name}_palettes']
|
||||
v['__current__'] = val
|
||||
gprefs[f'{self.mode_name}_palettes'] = v
|
||||
gprefs[f'{self.mode_name}_palette_name'] = '__current__' if self.use_custom.isChecked() else ''
|
||||
|
||||
def restore_defaults(self):
|
||||
self.use_custom.setChecked(False)
|
||||
self.palette_colors.restore_defaults()
|
||||
|
||||
|
||||
class PaletteConfig(Dialog):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(_('Customize the colors used by calibre'), 'customize-palette', parent=parent)
|
||||
|
||||
def setup_ui(self):
|
||||
self.l = l = QVBoxLayout(self)
|
||||
self.tabs = tabs = QTabWidget(self)
|
||||
l.addWidget(tabs)
|
||||
self.light_tab = lt = PaletteWidget(parent=self)
|
||||
tabs.addTab(lt, _('&Light mode colors'))
|
||||
self.dark_tab = dt = PaletteWidget('dark', parent=self)
|
||||
tabs.addTab(dt, _('&Dark mode colors'))
|
||||
l.addWidget(self.bb)
|
||||
b = self.bb.addButton(_('Restore &defaults'), QDialogButtonBox.ButtonRole.ActionRole)
|
||||
b.clicked.connect(self.restore_defaults)
|
||||
|
||||
def apply_settings(self):
|
||||
with gprefs:
|
||||
self.light_tab.apply_settings()
|
||||
self.dark_tab.apply_settings()
|
||||
|
||||
def restore_defaults(self):
|
||||
self.light_tab.restore_defaults()
|
||||
self.dark_tab.restore_defaults()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = Application([])
|
||||
d = PaletteConfig()
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
d.apply_settings()
|
||||
del d
|
||||
del app
|
@ -3,10 +3,11 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from contextlib import contextmanager, suppress
|
||||
from functools import lru_cache
|
||||
from qt.core import (
|
||||
QApplication, QByteArray, QColor, QDataStream, QIcon, QIODeviceBase, QObject,
|
||||
QPalette, QProxyStyle, QStyle, Qt
|
||||
QPalette, QProxyStyle, QStyle, Qt,
|
||||
)
|
||||
|
||||
from calibre.constants import DEBUG, dark_link_color, ismacos, iswindows
|
||||
@ -64,7 +65,7 @@ QPalette.serialize_as_python = serialize_palette_as_python
|
||||
QPalette.unserialize_from_bytes = unserialize_palette
|
||||
|
||||
|
||||
def dark_palette():
|
||||
def default_dark_palette():
|
||||
p = QPalette()
|
||||
disabled_color = QColor(127,127,127)
|
||||
p.setColor(QPalette.ColorRole.Window, dark_color)
|
||||
@ -75,22 +76,22 @@ def dark_palette():
|
||||
p.setColor(QPalette.ColorRole.ToolTipBase, dark_color)
|
||||
p.setColor(QPalette.ColorRole.ToolTipText, dark_text_color)
|
||||
p.setColor(QPalette.ColorRole.Text, dark_text_color)
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_color)
|
||||
p.setColor(QPalette.ColorRole.Button, dark_color)
|
||||
p.setColor(QPalette.ColorRole.ButtonText, dark_text_color)
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_color)
|
||||
p.setColor(QPalette.ColorRole.BrightText, Qt.GlobalColor.red)
|
||||
p.setColor(QPalette.ColorRole.Link, dark_link_color)
|
||||
p.setColor(QPalette.ColorRole.LinkVisited, Qt.GlobalColor.darkMagenta)
|
||||
|
||||
p.setColor(QPalette.ColorRole.Highlight, QColor(0x0b, 0x45, 0xc4))
|
||||
p.setColor(QPalette.ColorRole.HighlightedText, dark_text_color)
|
||||
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_color)
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, disabled_color)
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_color)
|
||||
|
||||
return p
|
||||
|
||||
|
||||
def light_palette(): # {{{
|
||||
def default_light_palette():
|
||||
p = QPalette()
|
||||
disabled_color = QColor(120,120,120)
|
||||
p.setColor(QPalette.ColorRole.Window, light_color)
|
||||
@ -101,21 +102,81 @@ def light_palette(): # {{{
|
||||
p.setColor(QPalette.ColorRole.ToolTipBase, light_color)
|
||||
p.setColor(QPalette.ColorRole.ToolTipText, light_text_color)
|
||||
p.setColor(QPalette.ColorRole.Text, light_text_color)
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_color)
|
||||
p.setColor(QPalette.ColorRole.Button, light_color)
|
||||
p.setColor(QPalette.ColorRole.ButtonText, light_text_color)
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_color)
|
||||
p.setColor(QPalette.ColorRole.BrightText, Qt.GlobalColor.red)
|
||||
p.setColor(QPalette.ColorRole.Link, light_link_color)
|
||||
p.setColor(QPalette.ColorRole.LinkVisited, Qt.GlobalColor.magenta)
|
||||
|
||||
p.setColor(QPalette.ColorRole.Highlight, QColor(48, 140, 198))
|
||||
p.setColor(QPalette.ColorRole.HighlightedText, Qt.GlobalColor.white)
|
||||
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_color)
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_color)
|
||||
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, disabled_color)
|
||||
|
||||
return p
|
||||
|
||||
# }}}
|
||||
|
||||
@lru_cache
|
||||
def palette_colors():
|
||||
return {
|
||||
'WindowText': _('A general foreground color'),
|
||||
'Text': _('The foreground color for text input widgets'),
|
||||
'ButtonText': _('The foreground color for buttons'),
|
||||
'PlaceholderText': _('Placeholder text in text input widgets'),
|
||||
'ToolTipText': _('The foreground color for tool tips'),
|
||||
'BrightText': _('A "bright" text color'),
|
||||
'HighlightedText': _('The foreground color for highlighted items'),
|
||||
|
||||
'Window': _('A general background color'),
|
||||
'Base': _('The background color for text input widgets'),
|
||||
'Button': _('The background color for buttons'),
|
||||
'AlternateBase': _('The background color for alternate rows in tables and lists'),
|
||||
'ToolTipBase': _('The background color for tool tips'),
|
||||
'Highlight': _('The background color for highlighted items'),
|
||||
|
||||
'Link': _('The color for links'),
|
||||
'LinkVisited': _('The color for visited links'),
|
||||
}
|
||||
|
||||
|
||||
def palette_from_dict(data: dict[str, str], default_palette: QPalette) -> QPalette:
|
||||
|
||||
def s(key, group=QPalette.ColorGroup.All):
|
||||
role = getattr(QPalette.ColorRole, key)
|
||||
grp = ''
|
||||
if group == QPalette.ColorGroup.Disabled:
|
||||
grp = 'disabled-'
|
||||
c = QColor.fromString(data.get(grp + key, ''))
|
||||
if c.isValid():
|
||||
p.setColor(group, role, c)
|
||||
|
||||
p = QPalette()
|
||||
for key in palette_colors():
|
||||
s(key)
|
||||
for key in ('Text', 'ButtonText', 'HighlightedText'):
|
||||
s(key, QPalette.ColorGroup.Disabled)
|
||||
return p.resolve(default_palette)
|
||||
|
||||
|
||||
def dark_palette():
|
||||
from calibre.gui2 import gprefs
|
||||
ans = default_dark_palette()
|
||||
if gprefs['dark_palette_name']:
|
||||
pdata = gprefs['dark_palettes'].get(gprefs['dark_palette_name'])
|
||||
with suppress(Exception):
|
||||
return palette_from_dict(pdata, ans)
|
||||
return ans
|
||||
|
||||
|
||||
def light_palette():
|
||||
from calibre.gui2 import gprefs
|
||||
ans = default_light_palette()
|
||||
if gprefs['light_palette_name']:
|
||||
pdata = gprefs['light_palettes'].get(gprefs['light_palette_name'])
|
||||
with suppress(Exception):
|
||||
return palette_from_dict(pdata, ans)
|
||||
return ans
|
||||
|
||||
|
||||
standard_pixmaps = { # {{{
|
||||
|
Loading…
x
Reference in New Issue
Block a user