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['show_notes_in_tag_browser'] = False
|
||||||
defs['icons_on_right_in_tag_browser'] = True
|
defs['icons_on_right_in_tag_browser'] = True
|
||||||
defs['cover_browser_narrow_view_position'] = 'automatic'
|
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):
|
def migrate_tweak(tweak_name, pref_name):
|
||||||
# If the tweak has been changed then leave the tweak in the file so
|
# 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 os
|
||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager, suppress
|
||||||
|
from functools import lru_cache
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QApplication, QByteArray, QColor, QDataStream, QIcon, QIODeviceBase, QObject,
|
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
|
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
|
QPalette.unserialize_from_bytes = unserialize_palette
|
||||||
|
|
||||||
|
|
||||||
def dark_palette():
|
def default_dark_palette():
|
||||||
p = QPalette()
|
p = QPalette()
|
||||||
disabled_color = QColor(127,127,127)
|
disabled_color = QColor(127,127,127)
|
||||||
p.setColor(QPalette.ColorRole.Window, dark_color)
|
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.ToolTipBase, dark_color)
|
||||||
p.setColor(QPalette.ColorRole.ToolTipText, dark_text_color)
|
p.setColor(QPalette.ColorRole.ToolTipText, dark_text_color)
|
||||||
p.setColor(QPalette.ColorRole.Text, 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.Button, dark_color)
|
||||||
p.setColor(QPalette.ColorRole.ButtonText, dark_text_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.BrightText, Qt.GlobalColor.red)
|
||||||
p.setColor(QPalette.ColorRole.Link, dark_link_color)
|
p.setColor(QPalette.ColorRole.Link, dark_link_color)
|
||||||
p.setColor(QPalette.ColorRole.LinkVisited, Qt.GlobalColor.darkMagenta)
|
p.setColor(QPalette.ColorRole.LinkVisited, Qt.GlobalColor.darkMagenta)
|
||||||
|
|
||||||
p.setColor(QPalette.ColorRole.Highlight, QColor(0x0b, 0x45, 0xc4))
|
p.setColor(QPalette.ColorRole.Highlight, QColor(0x0b, 0x45, 0xc4))
|
||||||
p.setColor(QPalette.ColorRole.HighlightedText, dark_text_color)
|
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.HighlightedText, disabled_color)
|
||||||
|
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_color)
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
def light_palette(): # {{{
|
def default_light_palette():
|
||||||
p = QPalette()
|
p = QPalette()
|
||||||
disabled_color = QColor(120,120,120)
|
disabled_color = QColor(120,120,120)
|
||||||
p.setColor(QPalette.ColorRole.Window, light_color)
|
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.ToolTipBase, light_color)
|
||||||
p.setColor(QPalette.ColorRole.ToolTipText, light_text_color)
|
p.setColor(QPalette.ColorRole.ToolTipText, light_text_color)
|
||||||
p.setColor(QPalette.ColorRole.Text, 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.Button, light_color)
|
||||||
p.setColor(QPalette.ColorRole.ButtonText, light_text_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.BrightText, Qt.GlobalColor.red)
|
||||||
p.setColor(QPalette.ColorRole.Link, light_link_color)
|
p.setColor(QPalette.ColorRole.Link, light_link_color)
|
||||||
p.setColor(QPalette.ColorRole.LinkVisited, Qt.GlobalColor.magenta)
|
p.setColor(QPalette.ColorRole.LinkVisited, Qt.GlobalColor.magenta)
|
||||||
|
|
||||||
p.setColor(QPalette.ColorRole.Highlight, QColor(48, 140, 198))
|
p.setColor(QPalette.ColorRole.Highlight, QColor(48, 140, 198))
|
||||||
p.setColor(QPalette.ColorRole.HighlightedText, Qt.GlobalColor.white)
|
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)
|
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, disabled_color)
|
||||||
|
|
||||||
return p
|
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 = { # {{{
|
standard_pixmaps = { # {{{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user