Add support for more values in QtColor config type.
Recent changes in the completion highlighter mandate that config.val.colors.completion.match be changed from a QssColor to a QtColor. However, the latter accepts fewer formats. To avoid breaking configs, this allows QtColors to be specified using all the same formats as QssColors, excluding gradients. I separated the QssColor and QtColor tests as the previous approach of generating the tests made adding tests for QtColor more complicated. While working on this I discovered that Qt's css parser is potentially broken around parsing hsv percentages and filed https://bugreports.qt.io/browse/QTBUG-70897. For consistency, I made our parser similarly broken. You can show the bug in qutebrowser right now by noting that the following have different effects: ``` set colors.completion.odd.bg 'hsv(100%, 100%, 100%)' set colors.completion.odd.bg 'hsv(358, 255, 255)' ```
This commit is contained in:
parent
0fed563a02
commit
66cc5f5ea4
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user