diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 7b9406b309..3156cdc5a6 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -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_ = {} diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index fb6b1e3cd9..e01bc025e8 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -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 diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 995f87f519..6ce5a35759 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -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) diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index dc540f531b..2f45195b0e 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -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())) diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 2a6ac1075d..42b4ca9f60 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -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) diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py index e5b144ece2..888e13060e 100644 --- a/src/calibre/gui2/tag_browser/view.py +++ b/src/calibre/gui2/tag_browser/view.py @@ -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'] diff --git a/src/calibre/gui2/widgets2.py b/src/calibre/gui2/widgets2.py index 4009f10318..336e896bae 100644 --- a/src/calibre/gui2/widgets2.py +++ b/src/calibre/gui2/widgets2.py @@ -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)