From 28670f8e48a0df12b17510adcff60eb00f117df6 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 2 Jul 2017 16:05:04 +0200 Subject: [PATCH] Move config.style into config.config and refactor it --- qutebrowser/app.py | 2 +- qutebrowser/browser/downloadview.py | 4 +- qutebrowser/browser/hints.py | 4 +- qutebrowser/completion/completiondelegate.py | 13 ++-- qutebrowser/completion/completionwidget.py | 4 +- qutebrowser/config/config.py | 66 ++++++++++++++++++- qutebrowser/config/style.py | 62 ------------------ qutebrowser/mainwindow/messageview.py | 4 +- qutebrowser/mainwindow/prompt.py | 4 +- qutebrowser/mainwindow/statusbar/bar.py | 6 +- qutebrowser/mainwindow/statusbar/progress.py | 4 +- qutebrowser/mainwindow/statusbar/url.py | 6 +- qutebrowser/misc/keyhintwidget.py | 4 +- qutebrowser/misc/utilcmds.py | 2 +- tests/helpers/stubs.py | 2 +- tests/unit/config/test_config.py | 58 ++++++++++++++++ tests/unit/config/test_style.py | 69 -------------------- 17 files changed, 153 insertions(+), 161 deletions(-) delete mode 100644 qutebrowser/config/style.py delete mode 100644 tests/unit/config/test_style.py diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 582bcbd23..bbe98232f 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -44,7 +44,7 @@ import qutebrowser import qutebrowser.resources from qutebrowser.completion.models import instances as completionmodels from qutebrowser.commands import cmdutils, runners, cmdexc -from qutebrowser.config import style, config, websettings, configexc +from qutebrowser.config import config, websettings, configexc from qutebrowser.browser import (urlmarks, adblock, history, browsertab, downloads) from qutebrowser.browser.network import proxy diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py index 2c39de163..ac44d75d9 100644 --- a/qutebrowser/browser/downloadview.py +++ b/qutebrowser/browser/downloadview.py @@ -26,7 +26,7 @@ from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu, QStyleFactory from qutebrowser.browser import downloads -from qutebrowser.config import style +from qutebrowser.config import config from qutebrowser.utils import qtutils, utils, objreg @@ -76,7 +76,7 @@ class DownloadView(QListView): def __init__(self, win_id, parent=None): super().__init__(parent) self.setStyle(QStyleFactory.create('Fusion')) - style.set_register_stylesheet(self) + config.set_register_stylesheet(self) self.setResizeMode(QListView.Adjust) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 32f52c161..c5c3cce73 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -29,7 +29,7 @@ from string import ascii_lowercase from PyQt5.QtCore import pyqtSlot, QObject, Qt, QUrl from PyQt5.QtWidgets import QLabel -from qutebrowser.config import config, style +from qutebrowser.config import config from qutebrowser.keyinput import modeman, modeparsers from qutebrowser.browser import webelem from qutebrowser.commands import userscripts, cmdexc, cmdutils, runners @@ -80,7 +80,7 @@ class HintLabel(QLabel): self.elem = elem self.setAttribute(Qt.WA_StyledBackground, True) - style.set_register_stylesheet(self) + config.set_register_stylesheet(self) self._context.tab.contents_size_changed.connect(self._move_to_elem) self._move_to_elem() diff --git a/qutebrowser/completion/completiondelegate.py b/qutebrowser/completion/completiondelegate.py index 19b655ac7..c7ca94a16 100644 --- a/qutebrowser/completion/completiondelegate.py +++ b/qutebrowser/completion/completiondelegate.py @@ -30,8 +30,8 @@ from PyQt5.QtCore import QRectF, QSize, Qt from PyQt5.QtGui import (QIcon, QPalette, QTextDocument, QTextOption, QAbstractTextDocumentLayout) -from qutebrowser.config import config, style -from qutebrowser.utils import qtutils +from qutebrowser.config import config +from qutebrowser.utils import qtutils, jinja class CompletionItemDelegate(QStyledItemDelegate): @@ -187,12 +187,15 @@ class CompletionItemDelegate(QStyledItemDelegate): self._doc = QTextDocument(self) self._doc.setDefaultFont(self._opt.font) self._doc.setDefaultTextOption(text_option) - self._doc.setDefaultStyleSheet(style.get_stylesheet(""" + self._doc.setDocumentMargin(2) + + stylesheet = """ .highlight { color: {{ conf.colors.completion.match.fg }}; } - """)) - self._doc.setDocumentMargin(2) + """ + template = jinja.environment.from_string(stylesheet) + self._doc.setDefaultStyleSheet(template.render(conf=config.val)) if index.parent().isValid(): pattern = index.model().pattern diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py index f4d095ce9..440b0dc78 100644 --- a/qutebrowser/completion/completionwidget.py +++ b/qutebrowser/completion/completionwidget.py @@ -26,7 +26,7 @@ subclasses to provide completions. from PyQt5.QtWidgets import QStyle, QTreeView, QSizePolicy, QStyleFactory from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel, QSize -from qutebrowser.config import config, style +from qutebrowser.config import config from qutebrowser.completion import completiondelegate from qutebrowser.completion.models import base from qutebrowser.utils import utils, usertypes @@ -118,7 +118,7 @@ class CompletionView(QTreeView): self._delegate = completiondelegate.CompletionItemDelegate(self) self.setItemDelegate(self._delegate) self.setStyle(QStyleFactory.create('Fusion')) - style.set_register_stylesheet(self) + config.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.setHeaderHidden(True) self.setAlternatingRowColors(True) diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index df6b08a2f..ff57e0e06 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -24,11 +24,12 @@ import os.path import contextlib import functools -from PyQt5.QtCore import pyqtSignal, QObject, QUrl +import sip +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl from qutebrowser.config import configdata, configexc, configtypes, configfiles from qutebrowser.utils import (utils, objreg, message, standarddir, log, - usertypes) + usertypes, jinja) from qutebrowser.commands import cmdexc, cmdutils, runners @@ -539,6 +540,67 @@ class ConfigContainer: return attr +def set_register_stylesheet(obj, *, stylesheet=None, update=True): + """Set the stylesheet for an object based on it's STYLESHEET attribute. + + Also, register an update when the config is changed. + + Args: + obj: The object to set the stylesheet for and register. + Must have a STYLESHEET attribute if stylesheet is not given. + stylesheet: The stylesheet to use. + update: Whether to update the stylesheet on config changes. + """ + observer = StyleSheetObserver(obj, stylesheet=stylesheet) + observer.register(update=update) + + +class StyleSheetObserver(QObject): + + """Set the stylesheet on the given object and update it on changes. + + Attributes: + _obj: The object to observe. + _stylesheet: The stylesheet template to use. + """ + + def __init__(self, obj, stylesheet): + super().__init__(parent=obj) + self._obj = obj + if stylesheet is None: + self._stylesheet = obj.STYLESHEET + else: + self._stylesheet = stylesheet + + def _get_stylesheet(self): + """Format a stylesheet based on a template. + + Return: + The formatted template as string. + """ + template = jinja.environment.from_string(self._stylesheet) + return template.render(conf=val) + + @pyqtSlot() + def _update_stylesheet(self): + """Update the stylesheet for obj.""" + if not sip.isdeleted(self._obj): + self._obj.setStyleSheet(self._get_stylesheet()) + + def register(self, update): + """Do a first update and listen for more. + + Args: + update: if False, don't listen for future updates. + """ + qss = self._get_stylesheet() + log.config.vdebug("stylesheet for {}: {}".format( + self._obj.__class__.__name__, qss)) + self._obj.setStyleSheet(qss) + if update: + instance.changed.connect(self._update_stylesheet) + + def init(parent=None): """Initialize the config. diff --git a/qutebrowser/config/style.py b/qutebrowser/config/style.py deleted file mode 100644 index 301d6db97..000000000 --- a/qutebrowser/config/style.py +++ /dev/null @@ -1,62 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2014-2017 Florian Bruhin (The Compiler) -# -# This file is part of qutebrowser. -# -# qutebrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# qutebrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with qutebrowser. If not, see . - -"""Utilities related to the look&feel of qutebrowser.""" - -import functools - -import sip - -from qutebrowser.config import config -from qutebrowser.utils import log, jinja - - -def get_stylesheet(template_str): - """Format a stylesheet based on a template. - - Args: - template_str: The stylesheet template as string. - - Return: - The formatted template as string. - """ - template = jinja.environment.from_string(template_str) - return template.render(conf=config.val) - - -def set_register_stylesheet(obj): - """Set the stylesheet for an object based on it's STYLESHEET attribute. - - Also, register an update when the config is changed. - - Args: - obj: The object to set the stylesheet for and register. - Must have a STYLESHEET attribute. - """ - qss = get_stylesheet(obj.STYLESHEET) - log.config.vdebug("stylesheet for {}: {}".format( - obj.__class__.__name__, qss)) - obj.setStyleSheet(qss) - config.instance.changed.connect(functools.partial(_update_stylesheet, obj)) - - -def _update_stylesheet(obj): - """Update the stylesheet for obj.""" - if not sip.isdeleted(obj): - obj.setStyleSheet(get_stylesheet(obj.STYLESHEET)) diff --git a/qutebrowser/mainwindow/messageview.py b/qutebrowser/mainwindow/messageview.py index 370028105..ee22ee80d 100644 --- a/qutebrowser/mainwindow/messageview.py +++ b/qutebrowser/mainwindow/messageview.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer, Qt, QSize from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSizePolicy -from qutebrowser.config import config, style +from qutebrowser.config import config from qutebrowser.utils import usertypes @@ -65,7 +65,7 @@ class Message(QLabel): raise ValueError("Invalid level {!r}".format(level)) # We don't bother with set_register_stylesheet here as it's short-lived # anyways. - self.setStyleSheet(style.get_stylesheet(stylesheet)) + config.set_register_stylesheet(self, stylesheet=stylesheet, update=False) class MessageView(QWidget): diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index a0045093e..1ea81c75f 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -30,7 +30,7 @@ from PyQt5.QtWidgets import (QWidget, QGridLayout, QVBoxLayout, QLineEdit, QLabel, QFileSystemModel, QTreeView, QSizePolicy) from qutebrowser.browser import downloads -from qutebrowser.config import style, config +from qutebrowser.config import config from qutebrowser.utils import usertypes, log, utils, qtutils, objreg, message from qutebrowser.keyinput import modeman from qutebrowser.commands import cmdutils, cmdexc @@ -268,7 +268,7 @@ class PromptContainer(QWidget): self.setObjectName('PromptContainer') self.setAttribute(Qt.WA_StyledBackground, True) - style.set_register_stylesheet(self) + config.set_register_stylesheet(self) message.global_bridge.prompt_done.connect(self._on_prompt_done) prompt_queue.show_prompts.connect(self._on_show_prompts) diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py index 555aee57c..df26d8de1 100644 --- a/qutebrowser/mainwindow/statusbar/bar.py +++ b/qutebrowser/mainwindow/statusbar/bar.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy from qutebrowser.browser import browsertab -from qutebrowser.config import config, style +from qutebrowser.config import config from qutebrowser.utils import usertypes, log, objreg, utils from qutebrowser.mainwindow.statusbar import (command, progress, keystring, percentage, url, tabindex) @@ -147,7 +147,7 @@ class StatusBar(QWidget): objreg.register('statusbar', self, scope='window', window=win_id) self.setObjectName(self.__class__.__name__) self.setAttribute(Qt.WA_StyledBackground) - style.set_register_stylesheet(self) + config.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) @@ -259,7 +259,7 @@ class StatusBar(QWidget): self._color_flags.caret = ColorFlags.CaretMode.on else: self._color_flags.caret = ColorFlags.CaretMode.off - self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) + config.set_register_stylesheet(self, update=False) def _set_mode_text(self, mode): """Set the mode text.""" diff --git a/qutebrowser/mainwindow/statusbar/progress.py b/qutebrowser/mainwindow/statusbar/progress.py index f0800a07d..7677a796b 100644 --- a/qutebrowser/mainwindow/statusbar/progress.py +++ b/qutebrowser/mainwindow/statusbar/progress.py @@ -22,7 +22,7 @@ from PyQt5.QtCore import pyqtSlot, QSize from PyQt5.QtWidgets import QProgressBar, QSizePolicy -from qutebrowser.config import style +from qutebrowser.config import config from qutebrowser.utils import utils, usertypes @@ -45,7 +45,7 @@ class Progress(QProgressBar): def __init__(self, parent=None): super().__init__(parent) - style.set_register_stylesheet(self) + config.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.setTextVisible(False) self.hide() diff --git a/qutebrowser/mainwindow/statusbar/url.py b/qutebrowser/mainwindow/statusbar/url.py index 87f226c0d..f75960f88 100644 --- a/qutebrowser/mainwindow/statusbar/url.py +++ b/qutebrowser/mainwindow/statusbar/url.py @@ -22,7 +22,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtProperty, Qt, QUrl from qutebrowser.mainwindow.statusbar import textbase -from qutebrowser.config import style +from qutebrowser.config import config from qutebrowser.utils import usertypes, urlutils @@ -81,7 +81,7 @@ class UrlText(textbase.TextBase): """Override TextBase.__init__ to elide in the middle by default.""" super().__init__(parent, Qt.ElideMiddle) self.setObjectName(self.__class__.__name__) - style.set_register_stylesheet(self) + config.set_register_stylesheet(self) self._hover_url = None self._normal_url = None self._normal_url_type = UrlType.normal @@ -109,7 +109,7 @@ class UrlText(textbase.TextBase): else: self.setText('') self._urltype = UrlType.normal - self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) + config.set_register_stylesheet(self, update=False) @pyqtSlot(str) def on_load_status_changed(self, status_str): diff --git a/qutebrowser/misc/keyhintwidget.py b/qutebrowser/misc/keyhintwidget.py index 6d3c6e370..41e4a72d9 100644 --- a/qutebrowser/misc/keyhintwidget.py +++ b/qutebrowser/misc/keyhintwidget.py @@ -30,7 +30,7 @@ import fnmatch from PyQt5.QtWidgets import QLabel, QSizePolicy from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt -from qutebrowser.config import config, style +from qutebrowser.config import config from qutebrowser.utils import objreg, utils, usertypes @@ -68,7 +68,7 @@ class KeyHintView(QLabel): self.hide() self._show_timer = usertypes.Timer(self, 'keyhint_show') self._show_timer.timeout.connect(self.show) - style.set_register_stylesheet(self) + config.set_register_stylesheet(self) def __repr__(self): return utils.get_repr(self, win_id=self._win_id) diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index 0d157d571..d5e55c063 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -37,7 +37,7 @@ from PyQt5.QtWidgets import QApplication # pylint: disable=unused-import from qutebrowser.browser import qutescheme from qutebrowser.utils import log, objreg, usertypes, message, debug, utils from qutebrowser.commands import cmdutils, runners, cmdexc -from qutebrowser.config import style, configdata +from qutebrowser.config import configdata from qutebrowser.misc import consolewidget diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index b8f9698ec..20354f9cb 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -444,9 +444,9 @@ class ConfigStub(QObject): """Set a value in the config.""" try: self.data[name] = value - self.changed.emit(name) except KeyError: raise configexc.NoOptionError(name) + self.changed.emit(name) class UrlMarkManagerStub(QObject): diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index eea431644..0d993a63d 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -18,4 +18,62 @@ """Tests for qutebrowser.config.config.""" +import pytest +from PyQt5.QtCore import QObject + from qutebrowser.config import config + + +class Obj(QObject): + + def __init__(self, stylesheet=None, parent=None): + super().__init__(parent) + if stylesheet is not None: + self.STYLESHEET = stylesheet # pylint: disable=invalid-name + self.rendered_stylesheet = None + + def setStyleSheet(self, stylesheet): + self.rendered_stylesheet = stylesheet + + +def test_get_stylesheet(config_stub): + config_stub.val.colors.completion.bg = 'magenta' + observer = config.StyleSheetObserver( + Obj(), stylesheet="{{ conf.colors.completion.bg }}") + assert observer._get_stylesheet() == 'magenta' + + +@pytest.mark.parametrize('delete', [True, False]) +@pytest.mark.parametrize('stylesheet_param', [True, False]) +@pytest.mark.parametrize('update', [True, False]) +def test_set_register_stylesheet(delete, stylesheet_param, update, qtbot, + config_stub, caplog): + config_stub.val.colors.completion.fg = 'magenta' + stylesheet = "{{ conf.colors.completion.fg }}" + + with caplog.at_level(9): # VDEBUG + if stylesheet_param: + obj = Obj() + config.set_register_stylesheet(obj, stylesheet=stylesheet, + update=update) + else: + obj = Obj(stylesheet) + config.set_register_stylesheet(obj, update=update) + + assert len(caplog.records) == 1 + assert caplog.records[0].message == 'stylesheet for Obj: magenta' + + assert obj.rendered_stylesheet == 'magenta' + + if delete: + with qtbot.waitSignal(obj.destroyed): + obj.deleteLater() + + config_stub.val.colors.completion.fg = 'yellow' + + if delete or not update: + expected = 'magenta' + else: + expected = 'yellow' + + assert obj.rendered_stylesheet == expected diff --git a/tests/unit/config/test_style.py b/tests/unit/config/test_style.py deleted file mode 100644 index caa444ae9..000000000 --- a/tests/unit/config/test_style.py +++ /dev/null @@ -1,69 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2017 Florian Bruhin (The Compiler) - -# This file is part of qutebrowser. -# -# qutebrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# qutebrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with qutebrowser. If not, see . - -"""Tests for qutebrowser.config.style.""" - -import logging - -import pytest -from PyQt5.QtCore import QObject - -from qutebrowser.config import style - - -def test_get_stylesheet(config_stub): - config_stub.val.colors.completion.bg = 'magenta' - rendered = style.get_stylesheet("{{ conf.colors.completion.bg }}") - assert rendered == 'magenta' - - -class Obj(QObject): - - def __init__(self, stylesheet, parent=None): - super().__init__(parent) - self.STYLESHEET = stylesheet # pylint: disable=invalid-name - self.rendered_stylesheet = None - - def setStyleSheet(self, stylesheet): - self.rendered_stylesheet = stylesheet - - -@pytest.mark.parametrize('delete', [True, False]) -def test_set_register_stylesheet(delete, qtbot, config_stub, caplog): - config_stub.val.colors.completion.fg = 'magenta' - obj = Obj("{{ conf.colors.completion.fg }}") - - with caplog.at_level(9): # VDEBUG - style.set_register_stylesheet(obj) - - assert len(caplog.records) == 1 - assert caplog.records[0].message == 'stylesheet for Obj: magenta' - - assert obj.rendered_stylesheet == 'magenta' - - if delete: - with qtbot.waitSignal(obj.destroyed): - obj.deleteLater() - - config_stub.val.colors.completion.fg = 'yellow' - - if delete: - expected = 'magenta' - else: - expected = 'yellow' - assert obj.rendered_stylesheet == expected