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:
parent
ec75a46c6d
commit
ce5295348e
2 changed files with 166 additions and 27 deletions
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue