mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Various improvements to dark mode support
Now colors can be dynamically changed and more widgets react appropriately. Also use err/ok indicator colors that work in both light and dark mode
This commit is contained in:
parent
5469beeeb6
commit
a1d51ea302
@ -845,8 +845,10 @@ def setup_unix_signals(self):
|
|||||||
class Application(QApplication):
|
class Application(QApplication):
|
||||||
|
|
||||||
shutdown_signal_received = pyqtSignal()
|
shutdown_signal_received = pyqtSignal()
|
||||||
|
palette_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, args, force_calibre_style=False, override_program_name=None, headless=False, color_prefs=gprefs, windows_app_uid=None):
|
def __init__(self, args, force_calibre_style=False, override_program_name=None, headless=False, color_prefs=gprefs, windows_app_uid=None):
|
||||||
|
self.ignore_palette_changes = False
|
||||||
QNetworkProxyFactory.setUseSystemConfiguration(True)
|
QNetworkProxyFactory.setUseSystemConfiguration(True)
|
||||||
if iswindows:
|
if iswindows:
|
||||||
self.windows_app_uid = None
|
self.windows_app_uid = None
|
||||||
@ -1011,6 +1013,10 @@ class Application(QApplication):
|
|||||||
prints('Using calibre Qt style:', self.using_calibre_style)
|
prints('Using calibre Qt style:', self.using_calibre_style)
|
||||||
if self.using_calibre_style:
|
if self.using_calibre_style:
|
||||||
self.load_calibre_style()
|
self.load_calibre_style()
|
||||||
|
self.paletteChanged.connect(self.on_palette_change)
|
||||||
|
self.on_palette_change()
|
||||||
|
|
||||||
|
def fix_dark_theme_colors(self):
|
||||||
self.is_dark_theme = is_dark_theme()
|
self.is_dark_theme = is_dark_theme()
|
||||||
if self.is_dark_theme:
|
if self.is_dark_theme:
|
||||||
pal = self.palette()
|
pal = self.palette()
|
||||||
@ -1022,7 +1028,25 @@ class Application(QApplication):
|
|||||||
# Workaround for https://bugreports.qt.io/browse/QTBUG-75321
|
# Workaround for https://bugreports.qt.io/browse/QTBUG-75321
|
||||||
# Buttontext is set to black for some reason
|
# Buttontext is set to black for some reason
|
||||||
pal.setColor(pal.ButtonText, pal.color(pal.WindowText))
|
pal.setColor(pal.ButtonText, pal.color(pal.WindowText))
|
||||||
self.setPalette(pal)
|
self.set_palette(pal)
|
||||||
|
|
||||||
|
def set_palette(self, pal):
|
||||||
|
self.ignore_palette_changes = True
|
||||||
|
self.setPalette(pal)
|
||||||
|
# Needed otherwise Qt does not emit the paletteChanged signal when
|
||||||
|
# appearance is changed.
|
||||||
|
self.setAttribute(Qt.AA_SetPalette, False)
|
||||||
|
self.ignore_palette_changes = False
|
||||||
|
|
||||||
|
def on_palette_change(self):
|
||||||
|
if self.ignore_palette_changes:
|
||||||
|
return
|
||||||
|
self.fix_dark_theme_colors()
|
||||||
|
self.palette_changed.emit()
|
||||||
|
|
||||||
|
def stylesheet_for_line_edit(self, is_error=False):
|
||||||
|
return 'QLineEdit { border: 2px solid %s; border-radius: 3px }' % (
|
||||||
|
'#FF2400' if is_error else '#50c878')
|
||||||
|
|
||||||
def load_calibre_style(self):
|
def load_calibre_style(self):
|
||||||
icon_map = self.__icon_map_memory_ = {}
|
icon_map = self.__icon_map_memory_ = {}
|
||||||
|
@ -555,10 +555,10 @@ class BookInfo(HTMLDisplay):
|
|||||||
ac.data = (None, None, None)
|
ac.data = (None, None, None)
|
||||||
ac.triggered.connect(self.remove_item_triggered)
|
ac.triggered.connect(self.remove_item_triggered)
|
||||||
self.setFocusPolicy(Qt.NoFocus)
|
self.setFocusPolicy(Qt.NoFocus)
|
||||||
self.document().setDefaultStyleSheet(self.default_css + css())
|
self.setDefaultStyleSheet(css())
|
||||||
|
|
||||||
def refresh_css(self):
|
def refresh_css(self):
|
||||||
self.document().setDefaultStyleSheet(self.default_css + css(True))
|
self.setDefaultStyleSheet(css(True))
|
||||||
|
|
||||||
def remove_item_triggered(self):
|
def remove_item_triggered(self):
|
||||||
field, value, book_id = self.remove_item_action.data
|
field, value, book_id = self.remove_item_action.data
|
||||||
|
@ -83,7 +83,7 @@ class Details(HTMLDisplay):
|
|||||||
def __init__(self, book_info, parent=None):
|
def __init__(self, book_info, parent=None):
|
||||||
HTMLDisplay.__init__(self, parent)
|
HTMLDisplay.__init__(self, parent)
|
||||||
self.book_info = book_info
|
self.book_info = book_info
|
||||||
self.document().setDefaultStyleSheet(self.default_css + css())
|
self.setDefaultStyleSheet(css())
|
||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
return QSize(350, 350)
|
return QSize(350, 350)
|
||||||
|
@ -42,10 +42,6 @@ from calibre.db import SPOOL_SIZE
|
|||||||
from calibre.ebooks.oeb.polish.main import SUPPORTED as EDIT_SUPPORTED
|
from calibre.ebooks.oeb.polish.main import SUPPORTED as EDIT_SUPPORTED
|
||||||
from polyglot.builtins import iteritems, unicode_type, range
|
from polyglot.builtins import iteritems, unicode_type, range
|
||||||
|
|
||||||
OK_COLOR = 'rgba(0, 255, 0, 50%)'
|
|
||||||
ERR_COLOR = 'rgba(255, 0, 0, 50%)'
|
|
||||||
INDICATOR_SHEET = 'QLineEdit { border: 2px solid %s; border-radius: 2px }'
|
|
||||||
|
|
||||||
|
|
||||||
def save_dialog(parent, title, msg, det_msg=''):
|
def save_dialog(parent, title, msg, det_msg=''):
|
||||||
d = QMessageBox(parent)
|
d = QMessageBox(parent)
|
||||||
@ -297,8 +293,7 @@ class TitleSortEdit(TitleEdit, ToMetadataMixin):
|
|||||||
def update_state(self, *args):
|
def update_state(self, *args):
|
||||||
ts = title_sort(self.title_edit.current_val, lang=self.book_lang)
|
ts = title_sort(self.title_edit.current_val, lang=self.book_lang)
|
||||||
normal = ts == self.current_val
|
normal = ts == self.current_val
|
||||||
col = OK_COLOR if normal else ERR_COLOR
|
self.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not normal))
|
||||||
self.setStyleSheet(INDICATOR_SHEET % col)
|
|
||||||
tt = self.tooltips[0 if normal else 1]
|
tt = self.tooltips[0 if normal else 1]
|
||||||
self.setToolTip(tt)
|
self.setToolTip(tt)
|
||||||
self.setWhatsThis(tt)
|
self.setWhatsThis(tt)
|
||||||
@ -508,8 +503,7 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin):
|
|||||||
au = self.author_sort_from_authors(string_to_authors(au))
|
au = self.author_sort_from_authors(string_to_authors(au))
|
||||||
|
|
||||||
normal = au == self.current_val
|
normal = au == self.current_val
|
||||||
col = OK_COLOR if normal else ERR_COLOR
|
self.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not normal))
|
||||||
self.setStyleSheet(INDICATOR_SHEET % col)
|
|
||||||
tt = self.tooltips[0 if normal else 1]
|
tt = self.tooltips[0 if normal else 1]
|
||||||
self.setToolTip(tt)
|
self.setToolTip(tt)
|
||||||
self.setWhatsThis(tt)
|
self.setWhatsThis(tt)
|
||||||
@ -1596,15 +1590,15 @@ class IdentifiersEdit(QLineEdit, ToMetadataMixin):
|
|||||||
tt = self.BASE_TT
|
tt = self.BASE_TT
|
||||||
extra = ''
|
extra = ''
|
||||||
if not isbn:
|
if not isbn:
|
||||||
col = 'none'
|
sheet = ''
|
||||||
elif check_isbn(isbn) is not None:
|
elif check_isbn(isbn) is not None:
|
||||||
col = OK_COLOR
|
sheet = QApplication.instance().stylesheet_for_line_edit()
|
||||||
extra = '\n\n'+_('This ISBN is valid')
|
extra = '\n\n'+_('This ISBN is valid')
|
||||||
else:
|
else:
|
||||||
col = ERR_COLOR
|
sheet = QApplication.instance().stylesheet_for_line_edit(True)
|
||||||
extra = '\n\n' + _('This ISBN is invalid')
|
extra = '\n\n' + _('This ISBN is invalid')
|
||||||
self.setToolTip(tt+extra)
|
self.setToolTip(tt+extra)
|
||||||
self.setStyleSheet(INDICATOR_SHEET % col)
|
self.setStyleSheet(sheet)
|
||||||
|
|
||||||
def paste_identifier(self):
|
def paste_identifier(self):
|
||||||
identifier_found = self.parse_clipboard_for_identifier()
|
identifier_found = self.parse_clipboard_for_identifier()
|
||||||
@ -1725,16 +1719,16 @@ class ISBNDialog(QDialog): # {{{
|
|||||||
def checkText(self, txt):
|
def checkText(self, txt):
|
||||||
isbn = unicode_type(txt)
|
isbn = unicode_type(txt)
|
||||||
if not isbn:
|
if not isbn:
|
||||||
col = 'none'
|
sheet = ''
|
||||||
extra = ''
|
extra = ''
|
||||||
elif check_isbn(isbn) is not None:
|
elif check_isbn(isbn) is not None:
|
||||||
col = OK_COLOR
|
sheet = QApplication.instance().stylesheet_for_line_edit()
|
||||||
extra = _('This ISBN is valid')
|
extra = _('This ISBN is valid')
|
||||||
else:
|
else:
|
||||||
col = ERR_COLOR
|
sheet = QApplication.instance().stylesheet_for_line_edit(True)
|
||||||
extra = _('This ISBN is invalid')
|
extra = _('This ISBN is invalid')
|
||||||
self.line_edit.setToolTip(extra)
|
self.line_edit.setToolTip(extra)
|
||||||
self.line_edit.setStyleSheet(INDICATOR_SHEET % col)
|
self.line_edit.setStyleSheet(sheet)
|
||||||
|
|
||||||
def text(self):
|
def text(self):
|
||||||
return check_isbn(unicode_type(self.line_edit.text()))
|
return check_isbn(unicode_type(self.line_edit.text()))
|
||||||
|
@ -108,7 +108,6 @@ class SearchBox2(QComboBox): # {{{
|
|||||||
|
|
||||||
def __init__(self, parent=None, add_clear_action=True):
|
def __init__(self, parent=None, add_clear_action=True):
|
||||||
QComboBox.__init__(self, parent)
|
QComboBox.__init__(self, parent)
|
||||||
self.normal_background = 'rgba(255, 255, 255, 0%)'
|
|
||||||
self.line_edit = SearchLineEdit(self)
|
self.line_edit = SearchLineEdit(self)
|
||||||
self.setLineEdit(self.line_edit)
|
self.setLineEdit(self.line_edit)
|
||||||
if add_clear_action:
|
if add_clear_action:
|
||||||
@ -161,8 +160,7 @@ class SearchBox2(QComboBox): # {{{
|
|||||||
|
|
||||||
def normalize_state(self):
|
def normalize_state(self):
|
||||||
self.setToolTip(self.tool_tip_text)
|
self.setToolTip(self.tool_tip_text)
|
||||||
self.line_edit.setStyleSheet(
|
self.line_edit.setStyleSheet('')
|
||||||
'QLineEdit{color:none;background-color:%s;}' % self.normal_background)
|
|
||||||
|
|
||||||
def text(self):
|
def text(self):
|
||||||
return self.currentText()
|
return self.currentText()
|
||||||
@ -190,10 +188,10 @@ class SearchBox2(QComboBox): # {{{
|
|||||||
self.clear(emit_search=False)
|
self.clear(emit_search=False)
|
||||||
return
|
return
|
||||||
self._in_a_search = ok
|
self._in_a_search = ok
|
||||||
col = 'rgba(0,255,0,20%)' if ok else 'rgba(255,0,0,20%)'
|
if self.colorize:
|
||||||
if not self.colorize:
|
self.line_edit.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not ok))
|
||||||
col = self.normal_background
|
else:
|
||||||
self.line_edit.setStyleSheet('QLineEdit{color:black;background-color:%s;}' % col)
|
self.line_edit.setStyleSheet('')
|
||||||
|
|
||||||
# Comes from the lineEdit control
|
# Comes from the lineEdit control
|
||||||
def key_pressed(self, event):
|
def key_pressed(self, event):
|
||||||
@ -320,7 +318,6 @@ class SavedSearchBox(QComboBox): # {{{
|
|||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QComboBox.__init__(self, parent)
|
QComboBox.__init__(self, parent)
|
||||||
self.normal_background = 'rgba(255, 255, 255, 0%)'
|
|
||||||
|
|
||||||
self.line_edit = SearchLineEdit(self)
|
self.line_edit = SearchLineEdit(self)
|
||||||
self.setLineEdit(self.line_edit)
|
self.setLineEdit(self.line_edit)
|
||||||
|
@ -167,8 +167,9 @@ class TagsView(QTreeView): # {{{
|
|||||||
# Allowing keyboard focus looks bad in the Qt Fusion style and is useless
|
# Allowing keyboard focus looks bad in the Qt Fusion style and is useless
|
||||||
# anyway since the enter/spacebar keys do nothing
|
# anyway since the enter/spacebar keys do nothing
|
||||||
self.setFocusPolicy(Qt.NoFocus)
|
self.setFocusPolicy(Qt.NoFocus)
|
||||||
|
QApplication.instance().palette_changed.connect(self.set_style_sheet, type=Qt.QueuedConnection)
|
||||||
|
|
||||||
def set_look_and_feel(self):
|
def set_style_sheet(self):
|
||||||
stylish_tb = '''
|
stylish_tb = '''
|
||||||
QTreeView {
|
QTreeView {
|
||||||
background-color: palette(window);
|
background-color: palette(window);
|
||||||
@ -190,6 +191,9 @@ class TagsView(QTreeView): # {{{
|
|||||||
}
|
}
|
||||||
'''.replace('PAD', unicode_type(gprefs['tag_browser_item_padding'])) + (
|
'''.replace('PAD', unicode_type(gprefs['tag_browser_item_padding'])) + (
|
||||||
'' if gprefs['tag_browser_old_look'] else stylish_tb))
|
'' if gprefs['tag_browser_old_look'] else stylish_tb))
|
||||||
|
|
||||||
|
def set_look_and_feel(self):
|
||||||
|
self.set_style_sheet()
|
||||||
self.setAlternatingRowColors(gprefs['tag_browser_old_look'])
|
self.setAlternatingRowColors(gprefs['tag_browser_old_look'])
|
||||||
self.itemDelegate().old_look = gprefs['tag_browser_old_look']
|
self.itemDelegate().old_look = gprefs['tag_browser_old_look']
|
||||||
|
|
||||||
|
@ -434,13 +434,11 @@ class HTMLDisplay(QTextBrowser):
|
|||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QTextBrowser.__init__(self, parent)
|
QTextBrowser.__init__(self, parent)
|
||||||
self.default_css = ''
|
self.last_set_html = ''
|
||||||
|
self.default_css = self.external_css = ''
|
||||||
app = QApplication.instance()
|
app = QApplication.instance()
|
||||||
if app.is_dark_theme:
|
app.palette_changed.connect(self.palette_changed)
|
||||||
pal = app.palette()
|
self.palette_changed()
|
||||||
col = pal.color(pal.Link)
|
|
||||||
self.default_css = 'a { color: %s }\n\n' % col.name(col.HexRgb)
|
|
||||||
self.document().setDefaultStyleSheet(self.default_css)
|
|
||||||
font = self.font()
|
font = self.font()
|
||||||
f = QFontInfo(font)
|
f = QFontInfo(font)
|
||||||
delta = tweaks['change_book_details_font_size_by'] + 1
|
delta = tweaks['change_book_details_font_size_by'] + 1
|
||||||
@ -456,6 +454,25 @@ class HTMLDisplay(QTextBrowser):
|
|||||||
self.setAcceptDrops(False)
|
self.setAcceptDrops(False)
|
||||||
self.anchorClicked.connect(self.on_anchor_clicked)
|
self.anchorClicked.connect(self.on_anchor_clicked)
|
||||||
|
|
||||||
|
def setHtml(self, html):
|
||||||
|
self.last_set_html = html
|
||||||
|
QTextBrowser.setHtml(self, html)
|
||||||
|
|
||||||
|
def setDefaultStyleSheet(self, css=''):
|
||||||
|
self.external_css = css
|
||||||
|
self.document().setDefaultStyleSheet(self.default_css + self.external_css)
|
||||||
|
|
||||||
|
def palette_changed(self):
|
||||||
|
app = QApplication.instance()
|
||||||
|
if app.is_dark_theme:
|
||||||
|
pal = app.palette()
|
||||||
|
col = pal.color(pal.Link)
|
||||||
|
self.default_css = 'a { color: %s }\n\n' % col.name(col.HexRgb)
|
||||||
|
else:
|
||||||
|
self.default_css = ''
|
||||||
|
self.document().setDefaultStyleSheet(self.default_css + self.external_css)
|
||||||
|
self.setHtml(self.last_set_html)
|
||||||
|
|
||||||
def on_anchor_clicked(self, qurl):
|
def on_anchor_clicked(self, qurl):
|
||||||
if not qurl.scheme() and qurl.hasFragment() and qurl.toString().startswith('#'):
|
if not qurl.scheme() and qurl.hasFragment() and qurl.toString().startswith('#'):
|
||||||
frag = qurl.fragment(qurl.FullyDecoded)
|
frag = qurl.fragment(qurl.FullyDecoded)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user