From fd1b2c6bd16805c2fc0267321529a06a3e01e0ee Mon Sep 17 00:00:00 2001 From: Ian Foote Date: Sun, 24 Mar 2013 20:07:09 +0000 Subject: [PATCH] Add CssListProperty. --- kivy/properties.pyx | 86 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/kivy/properties.pyx b/kivy/properties.pyx index 6b2e6e56d..05b621022 100644 --- a/kivy/properties.pyx +++ b/kivy/properties.pyx @@ -169,7 +169,7 @@ __all__ = ('Property', 'NumericProperty', 'StringProperty', 'ListProperty', 'ObjectProperty', 'BooleanProperty', 'BoundedNumericProperty', 'OptionProperty', 'ReferenceListProperty', 'AliasProperty', - 'DictProperty') + 'DictProperty', 'CssListProperty') from weakref import ref @@ -177,6 +177,8 @@ cdef float g_dpi = -1 cdef float g_density = -1 cdef float g_fontscale = -1 +NUMERIC_FORMATS = ('in', 'px', 'dp', 'sp', 'pt', 'cm', 'mm') + cpdef float dpi2px(value, ext): # 1in = 2.54cm = 25.4mm = 72pt = 12pc global g_dpi, g_density, g_fontscale @@ -201,7 +203,6 @@ cpdef float dpi2px(value, ext): elif ext == 'mm': return rv * g_dpi / 25.4 - cdef class Property: '''Base class for building more complex properties. @@ -1081,3 +1082,84 @@ cdef class AliasProperty(Property): if obj.__storage[self._name]['setter'](obj, value): obj.__storage[self._name]['value'] = self.get(obj) self.dispatch(obj) + +cdef class CssListProperty(Property): + '''A ListProperty that mimics the css way of defining numeric values such + as padding, margin, etc. + + Accepts a list of 1, 2 or 4 Numeric arguments or a single Numeric argument. + + CssListProperty([1]) represents [1, 1, 1, 1]. + CssListProperty([1, 2]) represents [1, 1, 2, 2]. + CssListProperty(['1px', (2, 'px'), 3, 4.0]) represents [1, 2, 3, 4.0]. + CssListProperty(5) represents [5, 5, 5, 5]. + + .. versionadded:: 1.7.0 + ''' + + def __init__(self, defaultvalue=None, **kw): + defaultvalue = defaultvalue or [0, 0, 0, 0] + super(CssListProperty, self).__init__(defaultvalue, **kw) + + cdef check(self, EventDispatcher obj, value): + if Property.check(self, obj, value): + return True + if type(value) not in (int, float, long, list, tuple, str): + err = '%s.%s accepts only int/float/long/list/tuple/str (got %r)' + raise ValueError(err % (obj.__class__.__name__, self.name, value)) + + cdef convert(self, EventDispatcher obj, x): + if x is None: + return x + + tp = type(x) + if tp is list or tp is tuple: + l = len(x) + if l == 1: + y = self._convert_numeric(obj, x[0]) + return [y, y, y, y] + elif l == 2: + if x[1] in NUMERIC_FORMATS: + # defaultvalue is a list or tuple representing one value + y = self._convert_numeric(obj, x) + return [y, y, y, y] + else: + y = self._convert_numeric(obj, x[0]) + z = self._convert_numeric(obj, x[1]) + return [y, y, z, z] + elif l == 4: + return [self._convert_numeric(obj, y) for y in x] + else: + err = '%s.%s must have 1, 2 or 4 components (got %r)' + raise ValueError(err % (obj.__class__.__name__, self.name, x)) + elif tp is int or tp is long or tp is float or tp is str: + y = self._convert_numeric(obj, x) + return [y, y, y, y] + else: + raise ValueError('%s.%s has an invalid format (got %r)' % ( + obj.__class__.__name__, + self.name, x)) + + cdef _convert_numeric(self, EventDispatcher obj, x): + tp = type(x) + if tp is int or tp is float or tp is long: + return x + if tp is tuple or tp is list: + if len(x) != 2: + raise ValueError('%s.%s must have 2 components (got %r)' % ( + obj.__class__.__name__, + self.name, x)) + return self.parse_list(obj, x[0], x[1]) + elif tp is str: + return self.parse_str(obj, x) + else: + raise ValueError('%s.%s have an invalid format (got %r)' % ( + obj.__class__.__name__, + self.name, x)) + + cdef float parse_str(self, EventDispatcher obj, value): + return self.parse_list(obj, value[:-2], value[-2:]) + + cdef float parse_list(self, EventDispatcher obj, value, str ext): + return dpi2px(value, ext) +