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):
|
||||
|
||||
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_ = {}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()))
|
||||
|
@ -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)
|
||||
|
@ -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']
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user