diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 1c3bff8fb..842eeaf29 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -905,11 +905,42 @@ class QtColor(BaseType): * transparent (no color) """ + def _parse_value(self, val): + try: + return int(val) + except ValueError: + pass + + mult = 255.0 + if val.endswith('%'): + val = val[:-1] + mult = 255.0 / 100 + return int(float(val) * 255.0 / 100.0) + + try: + return int(float(val) * mult) + except ValueError: + raise configexc.ValidationError(val, "must be a valid color value") + def to_py(self, value): self._basic_py_validation(value, str) if not value: return None + if value.endswith(')'): + openparen = value.index('(') + kind = value[:openparen] + vals = value[openparen+1:-1].split(',') + vals = [self._parse_value(v) for v in vals] + if kind == 'rgba' and len(vals) == 4: + return QColor.fromRgb(*vals) + if kind == 'rgb' and len(vals) == 3: + return QColor.fromRgb(*vals) + if kind == 'hsva' and len(vals) == 4: + return QColor.fromHsv(*vals) + if kind == 'hsv' and len(vals) == 3: + return QColor.fromHsv(*vals) + color = QColor(value) if color.isValid(): return color diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index 46119be7e..83835faa0 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -1222,87 +1222,88 @@ class TestCommand: assert ('cmd2', "desc 2") in items -class ColorTests: +class TestQtColor: - """Generator for tests for TestColors.""" + """Test QtColor.""" - TYPES = [configtypes.QtColor, configtypes.QssColor] + @pytest.mark.parametrize('val, expected', [ + ('#123', QColor('#123')), + ('#112233', QColor('#112233')), + ('#111222333', QColor('#111222333')), + ('#111122223333', QColor('#111122223333')), + ('red', QColor('red')), - TESTS = [ - ('#123', TYPES), - ('#112233', TYPES), - ('#111222333', TYPES), - ('#111122223333', TYPES), - ('red', TYPES), + ('rgb(0, 0, 0)', QColor.fromRgb(0, 0, 0)), + ('rgb(0,0,0)', QColor.fromRgb(0, 0, 0)), - ('#00000G', []), - ('#123456789ABCD', []), - ('#12', []), - ('foobar', []), - ('42', []), - ('foo(1, 2, 3)', []), - ('rgb(1, 2, 3', []), + ('rgba(255, 255, 255, 1.0)', QColor.fromRgb(255, 255, 255, 255)), - ('rgb(0, 0, 0)', [configtypes.QssColor]), - ('rgb(0,0,0)', [configtypes.QssColor]), + # this should be (36, 25, 25) as hue goes to 359 + # however this is consistent with Qt's CSS parser + # https://bugreports.qt.io/browse/QTBUG-70897 + ('hsv(10%,10%,10%)', QColor.fromHsv(25, 25, 25)), + ]) + def test_valid(self, val, expected): + act = configtypes.QtColor().to_py(val) + print(expected.hue(), expected.saturation(), expected.value(), expected.alpha()) + print(act.hue(), act.saturation(), act.value(), act.alpha()) + assert configtypes.QtColor().to_py(val) == expected - ('rgba(255, 255, 255, 1.0)', [configtypes.QssColor]), - ('hsv(10%,10%,10%)', [configtypes.QssColor]), - - ('qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, ' - 'stop: 0.4 gray, stop:1 green)', [configtypes.QssColor]), - ('qconicalgradient(cx:0.5, cy:0.5, angle:30, stop:0 white, ' - 'stop:1 #00FF00)', [configtypes.QssColor]), - ('qradialgradient(cx:0, cy:0, radius: 1, fx:0.5, fy:0.5, ' - 'stop:0 white, stop:1 green)', [configtypes.QssColor]), - ] - - COMBINATIONS = list(itertools.product(TESTS, TYPES)) - - def __init__(self): - self.valid = list(self._generate_valid()) - self.invalid = list(self._generate_invalid()) - - def _generate_valid(self): - for (val, valid_classes), klass in self.COMBINATIONS: - if klass in valid_classes: - yield klass, val - - def _generate_invalid(self): - for (val, valid_classes), klass in self.COMBINATIONS: - if klass not in valid_classes: - yield klass, val - - -class TestColors: - - """Test QtColor/QssColor.""" - - TESTS = ColorTests() - - @pytest.fixture(params=ColorTests.TYPES) - def klass_fixt(self, request): - """Fixture which provides all ColorTests classes. - - Named klass_fix so it has a different name from the parametrized klass, - see https://github.com/pytest-dev/pytest/issues/979. - """ - return request.param - - def test_test_generator(self): - """Some sanity checks for ColorTests.""" - assert self.TESTS.valid - assert self.TESTS.invalid - - @pytest.mark.parametrize('klass, val', TESTS.valid) - def test_to_py_valid(self, klass, val): - expected = QColor(val) if klass is configtypes.QtColor else val - assert klass().to_py(val) == expected - - @pytest.mark.parametrize('klass, val', TESTS.invalid) - def test_to_py_invalid(self, klass, val): + @pytest.mark.parametrize('val', [ + '#00000G', + '#123456789ABCD', + '#12', + 'foobar', + '42', + 'foo(1, 2, 3)', + 'rgb(1, 2, 3', + ]) + def test_invalid(self, val): with pytest.raises(configexc.ValidationError): - klass().to_py(val) + configtypes.QtColor().to_py(val) + + +class TestQssColor: + + """Test QssColor.""" + + @pytest.mark.parametrize('val', [ + '#123', + '#112233', + '#111222333', + '#111122223333', + 'red', + + 'rgb(0, 0, 0)', + 'rgb(0,0,0)', + + 'rgba(255, 255, 255, 1.0)', + 'hsv(10%,10%,10%)', + + 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, ' + 'stop: 0.4 gray, stop:1 green)', + + 'qconicalgradient(cx:0.5, cy:0.5, angle:30, stop:0 white, ' + 'stop:1 #00FF00)', + + 'qradialgradient(cx:0, cy:0, radius: 1, fx:0.5, fy:0.5, ' + 'stop:0 white, stop:1 green)', + ]) + def test_valid(self, val): + assert configtypes.QssColor().to_py(val) == val + + @pytest.mark.parametrize('val', [ + '#00000G', + '#123456789ABCD', + '#12', + 'foobar', + '42', + 'foo(1, 2, 3)', + 'rgb(1, 2, 3', + ]) + def test_invalid(self, val): + with pytest.raises(configexc.ValidationError): + configtypes.QssColor().to_py(val) @attr.s