From 9841c37d1cd57f1c31812931c2a4361f86a324b8 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 30 Jan 2009 16:44:15 +0000 Subject: Better position connections and only update the model once the item has been dropped --- diff --git a/thoughtview.py b/thoughtview.py index a079476..eeae9e8 100644 --- a/thoughtview.py +++ b/thoughtview.py @@ -25,6 +25,7 @@ from sugar.graphics import style from gaphas.examples import Box, Text from gaphas.connector import Handle +from gaphas.geometry import Rectangle class ThoughtView(Box): @@ -105,3 +106,7 @@ class ThoughtView(Box): self.new_thought_handle.y = 20 return updated + def get_rectangle(self): + x, y = self.get_position() + return Rectangle(x, y, self.width, self.height) + diff --git a/view.py b/view.py index 05d4c72..029f3da 100644 --- a/view.py +++ b/view.py @@ -19,8 +19,8 @@ from gettext import gettext as _ import gobject import gtk -from gaphas import GtkView, Canvas, state, Line -from gaphas.tool import HandleTool, DefaultTool, ConnectHandleTool +from gaphas import GtkView, Canvas, Line, geometry +from gaphas import tool # We'd rather not depend on sugar in this class from sugar.graphics import style @@ -40,24 +40,16 @@ class MindMapView(GtkView): self.canvas = Canvas() - tool = DefaultTool() - tool.prepend(NewThoughtTool()) - self.tool = tool + self.tool = tool.ToolChain(). \ + append(NewThoughtTool()). \ + append(tool.HoverTool()). \ + append(ItemTool()). \ + append(tool.TextEditTool()). \ + append(tool.RubberbandTool()) if model is not None: self.model = model - state.observers.add(self.__state_changed_cb) - - def __state_changed_cb(self, event): - func, args, dict_ = event - if len(args) > 1 and isinstance(args[1], ThoughtView): - thought_view = args[1] - x, y = thought_view.get_position() - row = self.model.find_by_id(thought_view.id) - if row[2] != x or row[3] != y: - self.model.set(row.iter, 2, x, 3, y) - def set_model(self, new_model): logging.debug('set_model %r' % new_model) if self._model == new_model: @@ -90,12 +82,48 @@ class MindMapView(GtkView): model = property(get_model, set_model) + def _calculate_ports(self, child_view, parent_view): + child_rect = child_view.get_rectangle() + parent_rect = parent_view.get_rectangle() + + point_in_child = self._get_closest_intersection(child_rect, parent_rect) + point_in_parent = self._get_closest_intersection(parent_rect, child_rect) + + return point_in_child, point_in_parent + + def _get_closest_intersection(self, rect1, rect2): + center1 = rect1.x + rect1.width / 2, rect1.y + rect1.height / 2 + center2 = rect2.x + rect2.width / 2, rect2.y + rect2.height / 2 + + return self._intersection_rect_line(rect1, (center1, center2)) + + def _intersection_rect_line(self, rect, line): + top_edge = (rect.x, rect.y), (rect.x + rect.width, rect.y) + left_edge = (rect.x, rect.y), (rect.x, rect.y + rect.height) + bottom_edge = (rect.x, rect.y + rect.height), \ + (rect.x + rect.width, rect.y + rect.height) + right_edge = (rect.x + rect.width, rect.y + rect.height), \ + (rect.x + rect.width, rect.y) + + for edge in (top_edge, left_edge, bottom_edge, right_edge): + point = geometry.intersect_line_line(line[0], line[1], + edge[0], edge[1]) + if point is not None: + return point + + raise ValueError('Rect %r and line %r dont intersect!' % (rect, line,)) + def _connect_items(self, parent_view, thought_view): line = Line() self.canvas.add(line) - handle_tool = ConnectHandleTool() - handle_tool.connect(self, line, line.handles()[0], parent_view.get_position()) - handle_tool.connect(self, line, line.opposite(line.handles()[0]), thought_view.get_position()) + + handle_tool = tool.ConnectHandleTool() + + start, end = self._calculate_ports(thought_view, parent_view) + + line_handle = line.handles()[0] + handle_tool.connect(self, line, line_handle, start) + handle_tool.connect(self, line, line.opposite(line_handle), end) def __row_changed_cb(self, model, path, iter): row = model[iter] @@ -113,11 +141,9 @@ class MindMapView(GtkView): else: thought_view.name = row[1] thought_view.set_position(row[2], row[3]) - thought_view.x = row[2] - thought_view.y = row[3] thought_view.color = row[4] - self.canvas.request_update(thought_view) + self.canvas.request_update(thought_view) def __row_deleted_cb(self, model, path): logging.debug('__row_deleted_cb %r' % path) @@ -132,23 +158,23 @@ class MindMapView(GtkView): return item return None -class NewThoughtTool(HandleTool): +class NewThoughtTool(tool.HandleTool): def __init__(self): - HandleTool.__init__(self) + tool.HandleTool.__init__(self) self._new_connection = None self._parent_thought = None def grab_handle(self, item, handle): logging.debug('NewThoughtTool.grab_handle %r %r' % (item, handle)) - HandleTool.grab_handle(self, item, handle) + tool.HandleTool.grab_handle(self, item, handle) def ungrab_handle(self): logging.debug('NewThoughtTool.ungrab_handle') - HandleTool.ungrab_handle(self) + tool.HandleTool.ungrab_handle(self) def on_button_release(self, context, event): - logging.debug('NewThoughtTool.ungrab_handle') + logging.debug('NewThoughtTool.on_button_release') if self._new_connection is not None: context.view.canvas.remove(self._new_connection) @@ -157,7 +183,7 @@ class NewThoughtTool(HandleTool): context.view.model.create_new_thought(x=event.x, y=event.y, parent_id=self._parent_thought.id) - HandleTool.on_button_release(self, context, event) + tool.HandleTool.on_button_release(self, context, event) def move(self, view, item, handle, pos): #logging.debug('NewThoughtTool.move') @@ -181,17 +207,35 @@ class NewThoughtTool(HandleTool): view.canvas.request_update(self._new_connection) #logging.debug('moved line end to %r %r' % (end_handle.x, end_handle.y)) else: - HandleTool.move(self, view, item, handle, pos) + tool.HandleTool.move(self, view, item, handle, pos) def glue(self, view, item, handle, vpos): #logging.debug('NewThoughtTool.glue') - HandleTool.glue(self, view, item, handle, vpos) + tool.HandleTool.glue(self, view, item, handle, vpos) def connect(self, view, item, handle, vpos): logging.debug('NewThoughtTool.connect') - HandleTool.connect(self, view, item, handle, vpos) + tool.HandleTool.connect(self, view, item, handle, vpos) def disconnect(self, view, item, handle): logging.debug('NewThoughtTool.disconnect') - HandleTool.disconnect(self, view, item, handle) + tool.HandleTool.disconnect(self, view, item, handle) + +class ItemTool(tool.ItemTool): + + def __init__(self): + tool.ItemTool.__init__(self) + + def on_button_release(self, context, event): + logging.debug('ItemTool.on_button_release') + + for item in self._movable_items: + if not isinstance(item, ThoughtView): + continue + x, y = item.get_position() + row = context.view.model.find_by_id(item.id) + if row[2] != x or row[3] != y: + context.view.model.set(row.iter, 2, x, 3, y) + + tool.ItemTool.on_button_release(self, context, event) -- cgit v0.9.1