From 0785a228aa9e580eccfd28472beaf87d941173c0 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Thu, 30 Jan 2014 23:23:52 -0700 Subject: [PATCH] Added window resize handles. Fixed bugs with clicking, dragging and focus events. --- jgui/events/events.py | 2 +- jgui/surface/surface.py | 179 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 167 insertions(+), 14 deletions(-) diff --git a/jgui/events/events.py b/jgui/events/events.py index 4473752..81e2dd0 100644 --- a/jgui/events/events.py +++ b/jgui/events/events.py @@ -35,7 +35,7 @@ class WindowEventSource(EventSource): mouse_button_up_events = ['mouse-right-up', 'mouse-left-up', 'mouse-middle-up'] mouse_button_events = mouse_button_down_events + mouse_button_up_events + mouse_button_drag_events - window_events = ['resize', 'move', 'focus'] + window_events = ['resize', 'move', 'focus', 'focus-lost'] key_events = ['key-down', 'key-up', 'key-repeat'] types = mouse_events + mouse_wheel_events + mouse_button_events +\ window_events + key_events diff --git a/jgui/surface/surface.py b/jgui/surface/surface.py index 848f794..c746d9f 100644 --- a/jgui/surface/surface.py +++ b/jgui/surface/surface.py @@ -12,7 +12,7 @@ class Surface(object): else: self.context = 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)) + self.root_window.add_child(Window('child', Position(0,0), [500,200], self.context, draggable=True, resizable=True)) self.windows = [self.root_window] def setTopZero(self, context): @@ -68,7 +68,7 @@ class WindowSurface(object): context.set_source_rgba(*outline_color) context.stroke() - def draw_rounded_rect(self, position, size, color=(1,1,1), outline_width=1, outline_color=(0,0,0), corner_radius=10): + def draw_rounded_rect(self, position, size, color=(1,1,1), outline_width=1, outline_color=(0,0,0), corner_radius=0): position = Position.from_value(position) size = Size.from_value(size) color = Color.from_value(color) @@ -96,7 +96,7 @@ class WindowSurface(object): context.stroke() def render(self): - self.draw_rounded_rect([0,0], [self.size.width, self.size.height], outline_width=self.size.width/70) + self.draw_rounded_rect([0,0], [self.size.width, self.size.height], outline_width=min(self.size.width, self.size.height)/70 or 1, corner_radius=min(self.size.width, self.size.height)/10 or 3) def draw(self): if self.visible: @@ -109,7 +109,7 @@ class WindowSurface(object): class Window(WindowEventSource, WindowSurface): - def __init__(self, name, position=None, size=None, context=None, draggable=False, resizable=False): + def __init__(self, name, position=None, size=None, context=None, draggable=False, resizable=False, **kwargs): super(Window, self).__init__() self.context = context self.name = name @@ -126,16 +126,130 @@ class Window(WindowEventSource, WindowSurface): self.focused = False self.visible = True self.draggable = draggable + self._resizable = resizable self.resizable = resizable - - if resizable: - pass + for key, value in kwargs.items(): + setattr(self, key, value) if self.draggable: self.accept('mouse-left-drag', self.drag) self.accept('mouse-left', self.click) self.accept('mouse-left-up', self.click_up) + @property + def resizable(self): + return self._resizable + + @resizable.setter + def resizable(self, value): + self._resizable = value + if self._resizable: + self.init_resize_handles() + + def drag_handle(self, obj, mouse_pos): + pass + + def drag_top_handle(self, obj, mouse_pos): + print 'stuff' + + + def init_resize_handles(self): + self.corner_handle_size = Size(20, 20) + self.edge_buffer = Size(10, 10) + self.edge_handle_width = 10 + + self.top_handle = Window('top_handle') + self.top_handle.accept('drag', self.drag_top_handle) + + self.topleft_handle = Window('topleft_handle') + self.topleft_handle.accept('drag', self.drag_handle) + + self.topright_handle = Window('topright_handle') + self.topright_handle.accept('drag', self.drag_handle) + + self.right_handle = Window('right_handle') + self.right_handle.accept('drag', self.drag_handle) + + self.bottomright_handle = Window('bottomright_handle') + self.bottomright_handle.accept('drag', self.drag_handle) + + self.bottom_handle = Window('bottom_handle') + self.bottom_handle.accept('drag', self.drag_handle) + + self.bottomleft_handle = Window('bottomleft_handle') + self.bottomleft_handle.accept('drag', self.drag_handle) + + self.left_handle = Window('left_handle') + self.left_handle.accept('drag', self.drag_handle) + + + self.vertical_edge_size = Size() + self.horizontal_edge_size = Size() + + self.update_resize_handles() + self.add_child(self.top_handle) + self.add_child(self.topleft_handle) + self.add_child(self.topright_handle) + self.add_child(self.left_handle) + self.add_child(self.right_handle) + self.add_child(self.bottom_handle) + self.add_child(self.bottomright_handle) + self.add_child(self.bottomleft_handle) + print self.top_handle.rectangle + + def update_resize_handles(self): + buffer = self.edge_buffer + top = self.top_handle + topleft = self.topleft_handle + topright = self.topright_handle + right = self.right_handle + bottomright = self.bottomright_handle + bottom = self.bottom_handle + bottomleft = self.bottomleft_handle + left = self.left_handle + vertical_edge_size = self.vertical_edge_size + horizontal_edge_size = self.horizontal_edge_size + corner_handle_size = self.corner_handle_size + x, y = 0, 0 + + vertical_edge_size.width = self.size.width-2*corner_handle_size.width + vertical_edge_size.height = self.edge_handle_width + + horizontal_edge_size.width = self.edge_handle_width + horizontal_edge_size.height = self.size.height-2*corner_handle_size.height + + top.position.x = x + corner_handle_size.width + top.position.y = y + top.size = vertical_edge_size + + topleft.position.x = x + topleft.position.y = y + topleft.size = corner_handle_size + + topright.position.x = x + self.size.width-corner_handle_size.width + topright.position.y = y + topright.size = corner_handle_size + + bottom.position.x = x + corner_handle_size.width + bottom.position.y = y + self.size.height-vertical_edge_size.height + bottom.size = vertical_edge_size + + bottomleft.position.x = x + bottomleft.position.y = y + self.size.height-corner_handle_size.height + bottomleft.size = corner_handle_size + + bottomright.position.x = x + self.size.width-corner_handle_size.width + bottomright.position.y = y + self.size.height-corner_handle_size.height + bottomright.size = corner_handle_size + + right.position.x = x + self.size.width-horizontal_edge_size.width + right.position.y = y + corner_handle_size.height + right.size = horizontal_edge_size + + left.position.x = x + left.position.y = y + corner_handle_size.height + left.size = horizontal_edge_size + def drag(self, obj, mouse_pos): obj.position = mouse_pos - self.mouse_diff @@ -160,11 +274,13 @@ class Window(WindowEventSource, WindowSurface): self.dispatch(button, self, self.mouse_pos) else: self.release_focus() + for child in self.children: child.inject_mouse_down(button) + def inject_mouse_up(self, button): - if self.mouse_inside() and self.mouse_inputs[button]: + if self.mouse_held() and self.mouse_inputs[button]: print button+'-up', self.name self.mouse_down = False self.mouse_inputs[button] = False @@ -181,12 +297,14 @@ class Window(WindowEventSource, WindowSurface): for button, down in self.mouse_inputs.items(): if down: self.dispatch('{}-drag'.format(button), self, self.mouse_pos) + self.dispatch('drag', self, self.mouse_pos) self.mouse_pos = mouse_pos for child in self.children: child.inject_mouse_position(pos) def grab_focus(self): self.focused = True + self.dispatch('focus', self) def release_focus(self): if self.focused: @@ -198,18 +316,44 @@ class Window(WindowEventSource, WindowSurface): #then releases on another for key in self.mouse_inputs: self.mouse_inputs[key] = False - + self.dispatch('focus-lost', self) def mouse_inside(self): res = self.rectangle.intersects_with(self.mouse_pos) - for child in self.children: - res &= not child.mouse_inside() + stack = [self] + visited = set() + while len(stack) > 0: + item = stack[-1] + if item.children and not set(item.children).issubset(visited): + stack.extend(item.children) + else: + if self is not item: + res &= not item.rectangle.intersects_with(item.mouse_pos) + visited.add(item) + stack.pop() return res + def get_parents(self): + parents = [] + parent = self.parent + while parent is not None: + parents.insert(0, parent) + parent = parent.parent + return parents + def mouse_held(self): res = self.mouse_down - for child in self.children: - res |= child.mouse_down + stack = self.get_parents()+[self] + visited = set() + while len(stack) > 0: + item = stack[-1] + if item.children and not set(item.children).issubset(visited): + stack.extend(item.children) + else: + if self is not item: + res |= item.mouse_down + visited.add(item) + stack.pop() return res def process_inputs(self): @@ -231,6 +375,7 @@ class Window(WindowEventSource, WindowSurface): if child_window not in self.children: child_window.parent = self child_window.position = child_window.position + self.position + child_window.context = self.context self.children.append(child_window) def remove_child(self, child_window): @@ -246,6 +391,7 @@ class Window(WindowEventSource, WindowSurface): @position.setter def position(self, position): + position = Position.from_value(position) diff = position - self.rectangle.position if diff.x != 0 or diff.y != 0: self.dispatch('move', self, position) @@ -253,12 +399,16 @@ class Window(WindowEventSource, WindowSurface): child.position = child.position + diff self.rectangle.position = position + #if self.resizable: + # self.update_resize_handles() + @property def size(self): return self.rectangle.size @size.setter def size(self, size): + size = Size.from_value(size) diff = size - self.rectangle.size if diff.height != 0 or diff.width != 0: self.dispatch('resize', self, size) @@ -266,3 +416,6 @@ class Window(WindowEventSource, WindowSurface): child.size = child.size + diff self.rectangle.size = size + #if self.resizable: + # self.update_resize_handles() +