Added ability to switch windows by clicking on them.

Added clipping option to window.
Refactored test code into own classes.
This commit is contained in:
Joey Payne 2014-02-03 15:56:20 -07:00
commit ce5295348e
2 changed files with 166 additions and 27 deletions

View file

@ -29,7 +29,7 @@ class EventSource(object):
class WindowEventSource(EventSource): class WindowEventSource(EventSource):
mouse_events = ['mouse-enter', 'mouse-leave', 'drag', 'mouse-move', 'hover'] mouse_events = ['mouse-enter', 'mouse-leave', 'drag', 'mouse-move', 'hover']
mouse_wheel_events = ['scroll-up', 'scroll-down'] mouse_wheel_events = ['scroll']
mouse_button_down_events = ['mouse-right', 'mouse-left', 'mouse-middle'] mouse_button_down_events = ['mouse-right', 'mouse-left', 'mouse-middle']
mouse_button_drag_events = ['mouse-right-drag', 'mouse-left-drag', 'mouse-middle-drag'] mouse_button_drag_events = ['mouse-right-drag', 'mouse-left-drag', 'mouse-middle-drag']
mouse_button_up_events = ['mouse-right-up', 'mouse-left-up', mouse_button_up_events = ['mouse-right-up', 'mouse-left-up',

View file

@ -3,6 +3,8 @@ import math
from .structures import Size, Position, Rectangle, Color from .structures import Size, Position, Rectangle, Color
from ..events.events import WindowEventSource from ..events.events import WindowEventSource
debug = True
class Surface(object): class Surface(object):
def __init__(self, size=None, context=None, render_mouse=True): def __init__(self, size=None, context=None, render_mouse=True):
self.size = Size.from_value(size) self.size = Size.from_value(size)
@ -12,7 +14,6 @@ class Surface(object):
else: else:
self.context = context self.context = context
self.root_window = Window('root', Position(0,0), self.size, self.context) self.root_window = Window('root', Position(0,0), self.size, self.context)
self.root_window.add_child(Window('child', Position(0,0), [500,200], self.context, draggable=True, resizable=True, min_size=Size(40,40)))
self.render_mouse = render_mouse self.render_mouse = render_mouse
if self.render_mouse: if self.render_mouse:
self.mouse_icon = Mouse('mouse', Position(0, 0), Size(12,20), self.context) self.mouse_icon = Mouse('mouse', Position(0, 0), Size(12,20), self.context)
@ -35,9 +36,22 @@ class Surface(object):
def inject_mouse_up(self, button): def inject_mouse_up(self, button):
self.root_window.inject_mouse_up(button) self.root_window.inject_mouse_up(button)
def notify_window_resize(self, width, height):
self.size = Size(width, height)
self.root_window.size = self.size
self.csurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.size.width, self.size.height)
self.context = cairo.Context(self.csurface)
stack = self.windows[:]
while stack:
item = stack.pop()
item.context = self.context
if item.children:
stack.extend(item.children)
def draw(self): def draw(self):
#self.setTopZero(ctx) #self.setTopZero(ctx)
self.context.identity_matrix() #self.context.identity_matrix()
self.context.set_operator(cairo.OPERATOR_CLEAR) self.context.set_operator(cairo.OPERATOR_CLEAR)
self.context.rectangle(0.0, 0.0, self.size.width, self.size.height) self.context.rectangle(0.0, 0.0, self.size.width, self.size.height)
self.context.fill() self.context.fill()
@ -45,6 +59,15 @@ class Surface(object):
for window in self.windows: for window in self.windows:
window.draw() window.draw()
class TestSurface(Surface):
def __init__(self, *args, **kwargs):
super(TestSurface, self).__init__(*args, **kwargs)
self.root_window.add_child(MyWindow('child', Position(0,0), [500,200], self.context, draggable=True, resizable=True, min_size=Size(40,40)))
self.root_window.add_child(TestWindow('child2', Position(200,200), [200,200], self.context, draggable=True))
child3 = TestWindow('child3', Position(300,300), [200,200], self.context, draggable=True)
child3.add_child(TestWindow('child3-1', Position(10,10), [50,50], self.context, draggable=True))
self.root_window.add_child(child3)
class WindowSurface(object): class WindowSurface(object):
line_joins = {'miter': cairo.LINE_JOIN_MITER, line_joins = {'miter': cairo.LINE_JOIN_MITER,
@ -108,7 +131,7 @@ class WindowSurface(object):
context.stroke() context.stroke()
def draw_rounded_rect(self, position, size, color=(1,1,1), line_width=1, line_color=(0,0,0), corner_radius=0): def draw_rounded_rect(self, position, size, color=(1,1,1), line_width=1, line_color=(0,0,0), corner_radius=0, line_dashed=False, clip=False):
position = Position.from_value(position) position = Position.from_value(position)
size = Size.from_value(size) size = Size.from_value(size)
color = Color.from_value(color) color = Color.from_value(color)
@ -122,7 +145,15 @@ class WindowSurface(object):
width = size.width width = size.width
height = size.height height = size.height
context.new_sub_path() if clip: #clips the entire region so any child windows will be confined to the parent
context.arc(x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees)
context.arc(x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees)
context.arc(x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees)
context.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees)
context.close_path()
context.clip()
context.new_path()
context.arc(x + width - radius - line_width/2.0, y + radius + line_width/2.0, radius, -90 * degrees, 0 * degrees) context.arc(x + width - radius - line_width/2.0, y + radius + line_width/2.0, radius, -90 * degrees, 0 * degrees)
context.arc(x + width - radius - line_width/2.0, y + height - radius - line_width/2.0, radius, 0 * degrees, 90 * degrees) context.arc(x + width - radius - line_width/2.0, y + height - radius - line_width/2.0, radius, 0 * degrees, 90 * degrees)
context.arc(x + radius + line_width/2.0, y + height - radius - line_width/2.0, radius, 90 * degrees, 180 * degrees) context.arc(x + radius + line_width/2.0, y + height - radius - line_width/2.0, radius, 90 * degrees, 180 * degrees)
@ -133,15 +164,19 @@ class WindowSurface(object):
context.fill_preserve() context.fill_preserve()
context.set_source_rgba(*line_color) context.set_source_rgba(*line_color)
context.set_line_width(line_width) context.set_line_width(line_width)
context.save()
if line_dashed:
context.set_dash([line_width, line_width])
context.stroke() context.stroke()
context.restore()
def render(self): def render(self):
self.draw_rounded_rect([0,0], [self.size.width, self.size.height], line_width=min(self.size.width, self.size.height)/70 or 1, corner_radius=min(self.size.width, self.size.height)/10 or 3) if debug:
self.draw_rounded_rect([0,0], [self.size.width, self.size.height], color=(0,0,1,0.1), line_color=(0,0,1,0.4), line_width=1.5, corner_radius=2, line_dashed=True)
def draw(self): def draw(self):
if self.visible: if self.visible:
self.context.save() self.context.save()
self.process_inputs()
self.render() self.render()
for child in self.children: for child in self.children:
child.draw() child.draw()
@ -169,6 +204,7 @@ class Window(WindowEventSource, WindowSurface):
self.draggable = draggable self.draggable = draggable
self._resizable = resizable self._resizable = resizable
self.resizable = resizable self.resizable = resizable
self.accept('mouse-move', self.process_mouse_move)
for key, value in kwargs.items(): for key, value in kwargs.items():
setattr(self, key, value) setattr(self, key, value)
@ -449,19 +485,35 @@ class Window(WindowEventSource, WindowSurface):
def inject_mouse_position(self, pos): def inject_mouse_position(self, pos):
mouse_pos = Position.from_value(pos) mouse_pos = Position.from_value(pos)
diff = mouse_pos - self.mouse_pos diff = mouse_pos - self.mouse_pos
old_pos = self.mouse_pos
self.mouse_pos = mouse_pos
if diff != Position(0,0): if diff != Position(0,0):
self.dispatch('mouse-move', self, self.mouse_pos, mouse_pos) self.dispatch('mouse-move', self, old_pos, self.mouse_pos)
if self.focused and self.mouse_down: if self.focused and self.mouse_down:
for button, down in self.mouse_inputs.items(): for button, down in self.mouse_inputs.items():
if down: if down:
self.dispatch('{}-drag'.format(button), self, self.mouse_pos) self.dispatch('{}-drag'.format(button), self, self.mouse_pos)
self.dispatch('drag', self, self.mouse_pos) self.dispatch('drag', self, self.mouse_pos)
self.mouse_pos = mouse_pos
for child in self.children: for child in self.children:
child.inject_mouse_position(pos) child.inject_mouse_position(pos)
def inject_mouse_wheel(self, value):
self.dispatch('scroll', self, value)
def grab_focus(self): def grab_focus(self):
self.focused = True self.focused = True
if self.parent is not None:
children = self.parent.children[:]
children.remove(self)
children.append(self)
self.parent.children = children
parent = self.parent
while parent is not None:
parent.grab_focus()
parent = parent.parent
self.dispatch('focus', self) self.dispatch('focus', self)
def release_focus(self): def release_focus(self):
@ -476,32 +528,49 @@ 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):
parent = self.parent
if parent is None:
return self
while parent is not None:
root = parent
parent = parent.parent
return root
def mouse_inside(self): def mouse_inside(self):
"""
Checks if the mouse is inside the window taking into account
all other windows and draw priorities.
"""
res = self.rectangle.intersects_with(self.mouse_pos) res = self.rectangle.intersects_with(self.mouse_pos)
stack = [self] if not res:
return False
#Check all children of root to see if there is a higher priority
#window than the current one
root = self.get_root()
stack = [root]
visited = set() visited = set()
while len(stack) > 0: while stack:
item = stack[-1] item = stack[-1]
intersects_mouse = item.rectangle.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:
if self is not item: if item is self:
res &= not item.rectangle.intersects_with(item.mouse_pos) break
visited.add(item) else:
stack.pop() res &= not intersects_mouse
return res visited.add(item)
stack.pop()
def get_parents(self): return res
parents = []
parent = self.parent
while parent is not None:
parents.insert(0, parent)
parent = parent.parent
return parents
def mouse_held(self): def mouse_held(self):
res = self.mouse_down res = self.mouse_down
stack = self.get_parents()+[self] stack = [self.get_root()]
visited = set() visited = set()
while len(stack) > 0: while len(stack) > 0:
item = stack[-1] item = stack[-1]
@ -514,7 +583,7 @@ class Window(WindowEventSource, WindowSurface):
stack.pop() stack.pop()
return res return res
def process_inputs(self): def process_mouse_move(self, obj, old_mpos, new_mpos):
mouse_focus = self.mouse_inside() mouse_focus = self.mouse_inside()
if not self.mouse_held(): if not self.mouse_held():
if mouse_focus: if mouse_focus:
@ -579,8 +648,78 @@ class Window(WindowEventSource, WindowSurface):
if self.resizable: if self.resizable:
self.update_resize_handles() self.update_resize_handles()
class Mouse(Window): class MyWindow(Window):
def render(self):
self.draw_rounded_rect([0,0], [self.size.width, self.size.height], line_width=2, corner_radius=3)
class TestWindow(Window):
def draw_test(self, position, size, color=(1,1,1), line_width=1, line_color=(0,0,0), corner_radius=0):
position = Position.from_value(position)
size = Size.from_value(size)
color = Color.from_value(color)
line_color = Color.from_value(line_color)
context = self.context
radius = corner_radius
degrees = math.pi / 180.0
x = position.x + self.position.x
y = position.y + self.position.y
width = size.width
height = size.height
context.new_sub_path()
context.set_source_rgba(0,0,1)
context.set_line_width(line_width)
context.arc(x + width - radius - line_width/2.0, y + radius + line_width/2.0, radius, -90 * degrees, -45 * degrees)
point = context.get_current_point()
context.stroke()
context.move_to(*point)
context.set_source_rgba(1,0,1)
context.set_line_width(10)
context.arc(x + width - radius - line_width/2.0, y + radius + line_width/2.0, radius, -45 * degrees, 0 * degrees)
context.line_to(x + width - line_width/2.0, y + height - radius - line_width/2.0)
context.arc(x + width - radius - line_width/2.0, y + height - radius - line_width/2.0, radius, 0 * degrees, 45 * degrees)
point = context.get_current_point()
context.stroke()
context.move_to(*point)
context.set_source_rgba(1,0,0)
context.set_line_width(line_width)
context.arc(x + width - radius - line_width/2.0, y + height - radius - line_width/2.0, radius, 45 * degrees, 90 * degrees)
context.line_to(x + radius + line_width/2.0, y + height - line_width/2.0)
context.arc(x + radius + line_width/2.0, y + height - radius - line_width/2.0, radius, 90 * degrees, 135 * degrees)
point = context.get_current_point()
context.stroke()
context.move_to(*point)
context.set_source_rgba(0,1,0)
context.set_line_width(line_width)
context.arc(x + radius + line_width/2.0, y + height - radius - line_width/2.0, radius, 135 * degrees, 180 * degrees)
context.line_to(x + line_width/2.0, y + radius + line_width/2.0)
context.arc(x + radius + line_width/2.0, y + radius + line_width/2.0, radius, 180 * degrees, 225 * degrees)
point = context.get_current_point()
context.stroke()
context.move_to(*point)
context.set_source_rgba(0,0,1)
context.set_line_width(line_width)
context.arc(x + radius + line_width/2.0, y + radius + line_width/2.0, radius, 225 * degrees, 270 * degrees)
context.line_to(x + width - radius - line_width/2.0, y + line_width/2.0)
context.stroke()
context.new_sub_path()
context.arc(x + width - radius - line_width/2.0, y + radius + line_width/2.0, radius, -90 * degrees, 0 * degrees)
context.arc(x + width - radius - line_width/2.0, y + height - radius - line_width/2.0, radius, 0 * degrees, 90 * degrees)
context.arc(x + radius + line_width/2.0, y + height - radius - line_width/2.0, radius, 90 * degrees, 180 * degrees)
context.arc(x + radius + line_width/2.0, y + radius + line_width/2.0, radius, 180 * degrees, 270 * degrees)
context.close_path()
context.set_source_rgba(*color)
context.fill()
def render(self):
self.draw_test([0,0], [self.size.width, self.size.height], corner_radius=10)
class Mouse(Window):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(Mouse, self).__init__(*args, **kwargs) super(Mouse, self).__init__(*args, **kwargs)
self.lines = [ self.lines = [
@ -589,7 +728,7 @@ class Mouse(Window):
[self.size.width*0.32, self.size.height*0.675], [self.size.width*0.32, self.size.height*0.675],
[self.size.width*0.52, self.size.height*0.9], [self.size.width*0.52, self.size.height*0.9],
[self.size.width*0.72, self.size.height*0.85], [self.size.width*0.72, self.size.height*0.85],
[self.size.width*0.52, self.size.height*0.6], [self.size.width*0.52, self.size.height*0.62],
[self.size.width*0.92, self.size.height*0.6] [self.size.width*0.92, self.size.height*0.6]
] ]
def render(self): def render(self):