Made the from_value code way more efficient. Added clipping rectangles

so that when a child is hidden by it's parent, it can't be clicked.
This commit is contained in:
Joey Payne 2014-02-06 21:22:44 -07:00
commit 2cae09a0f0
2 changed files with 64 additions and 42 deletions

View file

@ -63,10 +63,13 @@ class StructureBase(object):
@classmethod @classmethod
def from_value(cls, value): def from_value(cls, value):
try: if isinstance(value, cls):
return cls(*value) return value
except TypeError: else:
return cls() try:
return cls(*value)
except TypeError:
return cls()
class Position(StructureBase): class Position(StructureBase):
def __init__(self, x=0, y=0): def __init__(self, x=0, y=0):
@ -87,8 +90,7 @@ class Position(StructureBase):
raise Exception("Cannot multiply position by {}.".format(value.__class__.__name__)) raise Exception("Cannot multiply position by {}.".format(value.__class__.__name__))
def __iter__(self): def __iter__(self):
yield self.x return iter([self.x, self.y])
yield self.y
class Size(StructureBase): class Size(StructureBase):
@ -122,8 +124,7 @@ class Size(StructureBase):
return self.width*self.height return self.width*self.height
def __iter__(self): def __iter__(self):
yield self.width return iter([self.width, self.height])
yield self.height
class FourValueStructure(StructureBase): class FourValueStructure(StructureBase):
@ -146,7 +147,10 @@ class FourValueStructure(StructureBase):
@classmethod @classmethod
def from_value(cls, value): def from_value(cls, value):
return cls(*cls._parse_value(value)) if isinstance(value, cls):
return value
else:
return cls(*cls._parse_value(value))
class BorderRadius(FourValueStructure): class BorderRadius(FourValueStructure):
@ -158,10 +162,7 @@ class BorderRadius(FourValueStructure):
self.bottomleft = kwargs.get('bottomleft', values[3]) self.bottomleft = kwargs.get('bottomleft', values[3])
def __iter__(self): def __iter__(self):
yield self.topleft return iter([self.topleft, self.topright, self.bottomright, self.bottomleft])
yield self.topright
yield self.bottomright
yield self.bottomleft
class Padding(FourValueStructure): class Padding(FourValueStructure):
@ -173,10 +174,7 @@ class Padding(FourValueStructure):
self.left = kwargs.get('left', values[3]) self.left = kwargs.get('left', values[3])
def __iter__(self): def __iter__(self):
yield self.top return iter([self.top, self.right, self.bottom, self.left])
yield self.right
yield self.bottom
yield self.left
class Color(StructureBase): class Color(StructureBase):
@ -218,10 +216,7 @@ class Color(StructureBase):
return self return self
def __iter__(self): def __iter__(self):
yield self.r return iter([self.r, self.g, self.b, self.a])
yield self.g
yield self.b
yield self.a
@classmethod @classmethod
def hex_to_rgba(cls, hex_val): def hex_to_rgba(cls, hex_val):
@ -244,6 +239,8 @@ class Color(StructureBase):
@classmethod @classmethod
def from_value(cls, value): def from_value(cls, value):
if isinstance(value, cls):
return value
try: try:
return cls(*value) return cls(*value)
except TypeError: except TypeError:
@ -272,8 +269,7 @@ class Rectangle(StructureBase):
self._position = Position.from_value(position) self._position = Position.from_value(position)
def __iter__(self): def __iter__(self):
yield list(self.position) return iter([list(self.position), list(self.size)])
yield list(self.size)
@property @property
def size(self): def size(self):
@ -292,16 +288,20 @@ class Rectangle(StructureBase):
def intersection(self, other): def intersection(self, other):
other = Rectangle.from_value(other) other = Rectangle.from_value(other)
if self.contains(other):
return other
if other.contains(self): if other.contains(self):
return self return self
elif self.contains(other):
return other newx = max(self.position.x, other.position.x)
if self.intersects_with(other.position): newy = max(self.position.y, other.position.y)
return Rectangle(other.position, self.position + self.size - other.position)
elif other.intersects_with(self.position): new_width = min(self.position.x + self.size.width, other.position.x + other.size.width) - newx
return Rectangle(self.position, other.position + other.size - self.position) new_height = min(self.position.y + self.size.height, other.position.y + other.size.height) - newy
else:
if new_width <= 0 or new_height <= 0:
return Rectangle() return Rectangle()
return Rectangle([newx, newy], [new_width, new_height])
def intersects_with(self, position): def intersects_with(self, position):
"""Checks if the position is within the bounds of the rectangle including the edges""" """Checks if the position is within the bounds of the rectangle including the edges"""

View file

@ -285,6 +285,7 @@ class Window(WindowEventSource, WindowSurface):
super(Window, self).__init__() super(Window, self).__init__()
self._draggable = False self._draggable = False
self._resizable = False self._resizable = False
self._root = None
self.name = name self.name = name
@ -638,6 +639,16 @@ class Window(WindowEventSource, WindowSurface):
def hide(self): def hide(self):
self.visible = False self.visible = False
def get_clip_parent(self):
parent = self.parent
l = []
while parent is not None:
if parent.clip_children:
l.append(parent)
parent = parent.parent
if l:
return l[-1]
def inject_mouse_down(self, button): def inject_mouse_down(self, button):
if self.mouse_inside(): if self.mouse_inside():
log(button, self.name) log(button, self.name)
@ -706,34 +717,45 @@ class Window(WindowEventSource, WindowSurface):
self.mouse_inputs[key] = False self.mouse_inputs[key] = False
self.dispatch('focus-lost', self) self.dispatch('focus-lost', self)
def get_root(self): @property
parent = self.parent def root(self):
if parent is None: if self._root is None:
return self parent = self.parent
if parent is None:
return self
while parent is not None: while parent is not None:
root = parent root = parent
parent = parent.parent parent = parent.parent
return root self._root = root
return self._root
def mouse_inside(self): def mouse_inside(self):
""" """
Checks if the mouse is inside the window taking into account Checks if the mouse is inside the window taking into account
all other windows and draw priorities. all other windows and draw priorities.
""" """
res = self.rectangle.intersects_with(self.mouse_pos) rec = self.rectangle
clip_parent = self.get_clip_parent()
if clip_parent is not None:
rec = self.rectangle.intersection(clip_parent.rectangle)
res = rec.intersects_with(self.mouse_pos)
if not res: if not res:
return False return False
#Check all children of root to see if there is a higher priority #Check all children of root to see if there is a higher priority
#window than the current one #window than the current one
root = self.get_root() root = self.root
stack = [root] stack = [root]
visited = set() visited = set()
while stack: while stack:
item = stack[-1] item = stack[-1]
intersects_mouse = item.rectangle.intersects_with(self.mouse_pos) rec = item.rectangle
clip_parent = item.get_clip_parent()
if clip_parent is not None:
rec = item.rectangle.intersection(clip_parent.rectangle)
intersects_mouse = rec.intersects_with(self.mouse_pos)
if item.children and not set(item.children).issubset(visited): if item.children and not set(item.children).issubset(visited):
stack.extend(item.children) stack.extend(item.children)
else: else:
@ -748,7 +770,7 @@ class Window(WindowEventSource, WindowSurface):
def mouse_held(self): def mouse_held(self):
res = self.mouse_down res = self.mouse_down
stack = [self.get_root()] stack = [self.root]
visited = set() visited = set()
while len(stack) > 0: while len(stack) > 0:
item = stack[-1] item = stack[-1]