diff --git a/jgui/surface/structures.py b/jgui/surface/structures.py index caeba65..a4397ba 100644 --- a/jgui/surface/structures.py +++ b/jgui/surface/structures.py @@ -1,3 +1,4 @@ +import colorsys class StructureBase(object): @classmethod @@ -16,8 +17,12 @@ class StructureBase(object): d[a] = getattr(self, a) return d + def _dict_items(self): + for a in reversed(self._attrs()): + yield a, getattr(self,a) + def _dict_string(self): - return ', '.join('{}={}'.format(key, val) for key, val in self._dict().items()) + return ', '.join('{}={}'.format(key, val) for key, val in self._dict_items()) def __repr__(self): return unicode(self) @@ -28,6 +33,25 @@ class StructureBase(object): def __unicode__(self): return u'{} [{}]'.format(self.__class__.__name__, self._dict_string()) + def __eq__(self, other): + eq = True + for attr in self._attrs(): + val = getattr(self, attr) + if hasattr(other, attr): + eq &= getattr(other, attr) == val + else: + return False + return eq + + def __getitem__(self, index): + return list(self)[index] + + def __len__(self): + return len(self._attrs()) + + def __ne__(self, other): + return not(self == other) + @classmethod def is_like(cls, obj): """Checks if the object is like the current class @@ -37,6 +61,13 @@ class StructureBase(object): return False return True + @classmethod + def from_value(cls, value): + try: + return cls(*value) + except TypeError: + return cls() + class Position(StructureBase): def __init__(self, x=0, y=0): self.x = x @@ -50,28 +81,15 @@ class Position(StructureBase): other = Position.from_value(other) return Position(self.x-other.x, self.y-other.y) - def __eq__(self, other): - return self.x == other.x and self.y == other.y - - def __ne__(self, other): - return not(self == other) - - @classmethod - def from_value(cls, value): - if not value: - return cls() - if cls.is_like(value): - return value - elif len(value) >= 2: - return cls(value[0], value[1]) - else: - return cls() + def __iter__(self): + yield self.x + yield self.y class Size(StructureBase): def __init__(self, width=0, height=0): - self.height = height self.width = width + self.height = height def __add__(self, other): other = Size.from_value(other) @@ -81,23 +99,83 @@ class Size(StructureBase): other = Size.from_value(other) return Size(self.width-other.width, self.height-other.height) - def __eq__(self, other): - return self.width == other.width and self.height == other.height + def __iter__(self): + yield self.width + yield self.height - def __ne__(self, other): - return not(self == other) + +class Color(StructureBase): + def __init__(self, r=0, g=0, b=0, a=1): + self.r = float(r) + self.g = float(g) + self.b = float(b) + self.a = float(a) + + def __add__(self, other): + """Blend two colors using self as the background and other as the foreground""" + other = Color.from_value(other) + r = Color() + r.a = 1 - (1-other.a)*(1-self.a) + r_a_inv = self.a*(1-other.a)/r.a + o_factor = other.a/r.a + r.r = other.r*o_factor + self.r*r_a_inv + r.g = other.g*o_factor + self.g*r_a_inv + r.b = other.b*o_factor + self.b*r_a_inv + return r + + def brightness(self, value): + h, s, v = colorsys.rgb_to_hsv(self.r, self.g, self.b) + v = value + self.r, self.g, self.b = colorsys.hsv_to_rgb(h, s, v) + + def hue(self, value): + h, s, v = colorsys.rgb_to_hsv(self.r, self.g, self.b) + h = value + self.r, self.g, self.b = colorsys.hsv_to_rgb(h, s, v) + + def saturation(self, value): + h, s, v = colorsys.rgb_to_hsv(self.r, self.g, self.b) + s = value + self.r, self.g, self.b = colorsys.hsv_to_rgb(h, s, v) + + def from_hsv(self, h, s, v): + self.r, self.g, self.b = colorsys.hsv_to_rgb(h, s, v) + return self + + def __iter__(self): + yield self.r + yield self.g + yield self.b + yield self.a + + @classmethod + def hex_to_rgba(cls, hex_val): + """Converts a hex string in the format 0xFFAABB/CC or an integer to rgba""" + if isinstance(hex_val, (long, int)): + hexstring = "{0:x}".format(abs(hex_val)) + elif isinstance(hex_val, basestring): + hexstring = hexval[2:] + else: + return None + if len(hexstring) % 2 != 0: + hexstring = '0' + hexstring + if len(hexstring) != 6 and len(hexstring) != 8: + return None + ba = [] + for i in xrange(0, len(hexstring), 2): + ba.append(int(hexstring[i:i+2], 16)) + c = cls(*ba) + return c @classmethod def from_value(cls, value): - if not value: + try: + return cls(*value) + except TypeError: + c = cls.hex_to_rgba(value) + if c is not None: + return c return cls() - if cls.is_like(value): - return value - elif len(value) >= 2: - return cls(value[0], value[1]) - else: - return cls() - class Rectangle(StructureBase): def __init__(self, position=None, size=None): @@ -118,6 +196,10 @@ class Rectangle(StructureBase): def position(self, position): self._position = Position.from_value(position) + def __iter__(self): + yield list(self.position) + yield list(self.size) + @property def size(self): return self._size diff --git a/jgui/surface/surface.py b/jgui/surface/surface.py index a2b6df9..848f794 100644 --- a/jgui/surface/surface.py +++ b/jgui/surface/surface.py @@ -1,6 +1,6 @@ import cairo import math -from .structures import Size, Position, Rectangle +from .structures import Size, Position, Rectangle, Color from ..events.events import WindowEventSource class Surface(object): @@ -44,6 +44,8 @@ class WindowSurface(object): def draw_circle(self, position, size, color=(1,1,1), outline_width=1.0, outline_color=(0,0,0), start_angle=0.0, end_angle=360.0): + color = Color.from_value(color) + outline_color = Color.from_value(outline_color) context = self.context position = Position.from_value(position) size = Size.from_value(size) @@ -69,6 +71,8 @@ class WindowSurface(object): def draw_rounded_rect(self, position, size, color=(1,1,1), outline_width=1, outline_color=(0,0,0), corner_radius=10): position = Position.from_value(position) size = Size.from_value(size) + color = Color.from_value(color) + outline_color = Color.from_value(outline_color) context = self.context radius = corner_radius @@ -85,7 +89,7 @@ class WindowSurface(object): context.arc(x + radius + outline_width/2.0, y + radius + outline_width/2.0, radius, 180 * degrees, 270 * degrees) context.close_path() - context.set_source_rgb(*color) + context.set_source_rgba(*color) context.fill_preserve() context.set_source_rgba(*outline_color) context.set_line_width(outline_width) @@ -105,7 +109,7 @@ class WindowSurface(object): class Window(WindowEventSource, WindowSurface): - def __init__(self, name, position=None, size=None, context=None, draggable=False): + def __init__(self, name, position=None, size=None, context=None, draggable=False, resizable=False): super(Window, self).__init__() self.context = context self.name = name @@ -122,6 +126,10 @@ class Window(WindowEventSource, WindowSurface): self.focused = False self.visible = True self.draggable = draggable + self.resizable = resizable + + if resizable: + pass if self.draggable: self.accept('mouse-left-drag', self.drag) @@ -132,7 +140,7 @@ class Window(WindowEventSource, WindowSurface): obj.position = mouse_pos - self.mouse_diff def click(self, obj, mouse_pos): - self.mouse_diff = mouse_pos - self.position + self.mouse_diff = mouse_pos - obj.position def click_up(self, obj, mouse_pos): self.mouse_diff = Position(0, 0) @@ -211,6 +219,7 @@ class Window(WindowEventSource, WindowSurface): if not self.mouse_in: print 'mouse-enter', self.name self.dispatch('mouse-enter', self) + self.dispatch('hover', self) self.mouse_in = True else: if self.mouse_in: