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:
Kovid Goyal 2019-10-15 10:36:07 +05:30
parent 5469beeeb6
commit a1d51ea302
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 71 additions and 35 deletions

View File

@ -845,8 +845,10 @@ def setup_unix_signals(self):
class Application(QApplication):
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):
self.ignore_palette_changes = False
QNetworkProxyFactory.setUseSystemConfiguration(True)
if iswindows:
self.windows_app_uid = None
@ -1011,6 +1013,10 @@ class Application(QApplication):
prints('Using calibre Qt style:', self.using_calibre_style)
if self.using_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()
if self.is_dark_theme:
pal = self.palette()
@ -1022,7 +1028,25 @@ class Application(QApplication):
# Workaround for https://bugreports.qt.io/browse/QTBUG-75321
# Buttontext is set to black for some reason
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):
icon_map = self.__icon_map_memory_ = {}

View File

@ -555,10 +555,10 @@ class BookInfo(HTMLDisplay):
ac.data = (None, None, None)
ac.triggered.connect(self.remove_item_triggered)
self.setFocusPolicy(Qt.NoFocus)
self.document().setDefaultStyleSheet(self.default_css + css())
self.setDefaultStyleSheet(css())
def refresh_css(self):
self.document().setDefaultStyleSheet(self.default_css + css(True))
self.setDefaultStyleSheet(css(True))
def remove_item_triggered(self):
field, value, book_id = self.remove_item_action.data

View File

@ -83,7 +83,7 @@ class Details(HTMLDisplay):
def __init__(self, book_info, parent=None):
HTMLDisplay.__init__(self, parent)
self.book_info = book_info
self.document().setDefaultStyleSheet(self.default_css + css())
self.setDefaultStyleSheet(css())
def sizeHint(self):
return QSize(350, 350)

View File

@ -42,10 +42,6 @@ from calibre.db import SPOOL_SIZE
from calibre.ebooks.oeb.polish.main import SUPPORTED as EDIT_SUPPORTED
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=''):
d = QMessageBox(parent)
@ -297,8 +293,7 @@ class TitleSortEdit(TitleEdit, ToMetadataMixin):
def update_state(self, *args):
ts = title_sort(self.title_edit.current_val, lang=self.book_lang)
normal = ts == self.current_val
col = OK_COLOR if normal else ERR_COLOR
self.setStyleSheet(INDICATOR_SHEET % col)
self.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not normal))
tt = self.tooltips[0 if normal else 1]
self.setToolTip(tt)
self.setWhatsThis(tt)
@ -508,8 +503,7 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin):
au = self.author_sort_from_authors(string_to_authors(au))
normal = au == self.current_val
col = OK_COLOR if normal else ERR_COLOR
self.setStyleSheet(INDICATOR_SHEET % col)
self.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not normal))
tt = self.tooltips[0 if normal else 1]
self.setToolTip(tt)
self.setWhatsThis(tt)
@ -1596,15 +1590,15 @@ class IdentifiersEdit(QLineEdit, ToMetadataMixin):
tt = self.BASE_TT
extra = ''
if not isbn:
col = 'none'
sheet = ''
elif check_isbn(isbn) is not None:
col = OK_COLOR
sheet = QApplication.instance().stylesheet_for_line_edit()
extra = '\n\n'+_('This ISBN is valid')
else:
col = ERR_COLOR
sheet = QApplication.instance().stylesheet_for_line_edit(True)
extra = '\n\n' + _('This ISBN is invalid')
self.setToolTip(tt+extra)
self.setStyleSheet(INDICATOR_SHEET % col)
self.setStyleSheet(sheet)
def paste_identifier(self):
identifier_found = self.parse_clipboard_for_identifier()
@ -1725,16 +1719,16 @@ class ISBNDialog(QDialog): # {{{
def checkText(self, txt):
isbn = unicode_type(txt)
if not isbn:
col = 'none'
sheet = ''
extra = ''
elif check_isbn(isbn) is not None:
col = OK_COLOR
sheet = QApplication.instance().stylesheet_for_line_edit()
extra = _('This ISBN is valid')
else:
col = ERR_COLOR
sheet = QApplication.instance().stylesheet_for_line_edit(True)
extra = _('This ISBN is invalid')
self.line_edit.setToolTip(extra)
self.line_edit.setStyleSheet(INDICATOR_SHEET % col)
self.line_edit.setStyleSheet(sheet)
def text(self):
return check_isbn(unicode_type(self.line_edit.text()))

View File

@ -108,7 +108,6 @@ class SearchBox2(QComboBox): # {{{
def __init__(self, parent=None, add_clear_action=True):
QComboBox.__init__(self, parent)
self.normal_background = 'rgba(255, 255, 255, 0%)'
self.line_edit = SearchLineEdit(self)
self.setLineEdit(self.line_edit)
if add_clear_action:
@ -161,8 +160,7 @@ class SearchBox2(QComboBox): # {{{
def normalize_state(self):
self.setToolTip(self.tool_tip_text)
self.line_edit.setStyleSheet(
'QLineEdit{color:none;background-color:%s;}' % self.normal_background)
self.line_edit.setStyleSheet('')
def text(self):
return self.currentText()
@ -190,10 +188,10 @@ class SearchBox2(QComboBox): # {{{
self.clear(emit_search=False)
return
self._in_a_search = ok
col = 'rgba(0,255,0,20%)' if ok else 'rgba(255,0,0,20%)'
if not self.colorize:
col = self.normal_background
self.line_edit.setStyleSheet('QLineEdit{color:black;background-color:%s;}' % col)
if self.colorize:
self.line_edit.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not ok))
else:
self.line_edit.setStyleSheet('')
# Comes from the lineEdit control
def key_pressed(self, event):
@ -320,7 +318,6 @@ class SavedSearchBox(QComboBox): # {{{
def __init__(self, parent=None):
QComboBox.__init__(self, parent)
self.normal_background = 'rgba(255, 255, 255, 0%)'
self.line_edit = SearchLineEdit(self)
self.setLineEdit(self.line_edit)

View File

@ -167,8 +167,9 @@ class TagsView(QTreeView): # {{{
# Allowing keyboard focus looks bad in the Qt Fusion style and is useless
# anyway since the enter/spacebar keys do nothing
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 = '''
QTreeView {
background-color: palette(window);
@ -190,6 +191,9 @@ class TagsView(QTreeView): # {{{
}
'''.replace('PAD', unicode_type(gprefs['tag_browser_item_padding'])) + (
'' 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.itemDelegate().old_look = gprefs['tag_browser_old_look']

View File

@ -434,13 +434,11 @@ class HTMLDisplay(QTextBrowser):
def __init__(self, parent=None):
QTextBrowser.__init__(self, parent)
self.default_css = ''
self.last_set_html = ''
self.default_css = self.external_css = ''
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)
self.document().setDefaultStyleSheet(self.default_css)
app.palette_changed.connect(self.palette_changed)
self.palette_changed()
font = self.font()
f = QFontInfo(font)
delta = tweaks['change_book_details_font_size_by'] + 1
@ -456,6 +454,25 @@ class HTMLDisplay(QTextBrowser):
self.setAcceptDrops(False)
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):
if not qurl.scheme() and qurl.hasFragment() and qurl.toString().startswith('#'):
frag = qurl.fragment(qurl.FullyDecoded)