Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Drake <dsd@laptop.org>2011-12-11 14:53:03 (GMT)
committer Daniel Drake <dsd@laptop.org>2011-12-11 14:53:03 (GMT)
commitb10d5ffc8d22d18fe9ef940018a5aa54347b84b4 (patch)
tree65c48518eb0a4a4da5c5c3e9360c32c7ec9241f1
parent7803e5cbcedf60b1171eede91aff5f48e0969ac3 (diff)
add some comments describing recent palette work
-rw-r--r--src/sugar3/graphics/palettewindow.py100
1 files changed, 70 insertions, 30 deletions
diff --git a/src/sugar3/graphics/palettewindow.py b/src/sugar3/graphics/palettewindow.py
index 71c035d..86c1611 100644
--- a/src/sugar3/graphics/palettewindow.py
+++ b/src/sugar3/graphics/palettewindow.py
@@ -119,7 +119,71 @@ class _PaletteMenuWidget(Gtk.Menu):
def _position(self, widget, data):
return self._popup_position[0], self._popup_position[1], False
+ def popup(self, invoker):
+ if self._up:
+ return
+
+ # We need to track certain mouse events in order to close the palette
+ # when the mouse leaves the palette and the invoker widget, but
+ # GtkMenu makes our lives hard here.
+ #
+ # GtkMenu takes a grab on the root window, meaning that normal
+ # enter/leave events are not sent to the relevant widgets.
+ # However, connecting enter-notify and leave-notify events in this
+ # GtkMenu subclass mean that we get to see the events being grabbed.
+ # With certain filtering in place (see _enter_notify_cb and
+ # _leave_notify_cb) we are able to accurately determine when the
+ # mouse leaves/enters the palette menu. Some spurious events are
+ # generated but the important thing is that the last event generated
+ # in response to a user action is always reliable (i.e. we will
+ # always get a leave event last if the user left the menu,
+ # even if we get some strange enter events leading up to it).
+ #
+ # This is complicated with submenus; in this case the submenu takes
+ # the grab, so we must also listen for events on any submenus of
+ # the palette and apply the same considerations.
+ #
+ # The remaining challenge is tracking when the mouse enters or leaves
+ # the invoker area. While the appropriate GtkMenu grab is active,
+ # we do get informed of such events, however these events will only
+ # arrive if the user has entered the menu. If the user hovers over
+ # the invoker and then leaves the invoker without entering the palette,
+ # we get no enter/leave event.
+ # We work around this by tracking mouse motion events. When the mouse
+ # moves, we compare the mouse coordinates to the region occupied by the
+ # invoker, and this lets us track enter/leave for the invoker widget.
+
+ self._invoker = invoker
+ self._find_all_menus(self)
+ for menu in self._menus:
+ if self._invoker:
+ menu.connect('motion-notify-event', self._motion_notify_cb)
+ menu.connect('enter-notify-event', self._enter_notify_cb)
+ menu.connect('leave-notify-event', self._leave_notify_cb)
+ self._entered = False
+ self._mouse_in_palette = False
+ self._mouse_in_invoker = False
+ Gtk.Menu.popup(self, None, None, self._position, None, 0, 0)
+ self._up = True
+
+ def popdown(self):
+ if not self._up:
+ return
+ Gtk.Menu.popdown(self)
+
+ for menu in self._menus:
+ menu.disconnect_by_func(self._motion_notify_cb)
+ menu.disconnect_by_func(self._enter_notify_cb)
+ menu.disconnect_by_func(self._leave_notify_cb)
+
+ self._up = False
+ self._menus = []
+ self._invoker = None
+
def _find_all_menus(self, menu):
+ """
+ Recursively find all submenus of menu, adding them to self._menus.
+ """
self._menus.append(menu)
for child in menu.get_children():
if not isinstance(child, Gtk.MenuItem):
@@ -160,44 +224,20 @@ class _PaletteMenuWidget(Gtk.Menu):
def _reevaluate_state(self):
if self._entered:
+ # If we previously advised that the mouse was inside, but now the
+ # mouse is outside both the invoker and the palette, notify that
+ # the mouse has left.
if not self._mouse_in_palette and not self._mouse_in_invoker:
self._entered = False
self.emit('leave-notify')
else:
+ # If we previously advised that the mouse had left, but now the
+ # mouse is inside either the palette or the invoker, notify that
+ # the mouse has entered.
if self._mouse_in_palette or self._mouse_in_invoker:
self._entered = True
self.emit('enter-notify')
- def popup(self, invoker):
- if self._up:
- return
-
- self._invoker = invoker
- self._find_all_menus(self)
- for menu in self._menus:
- if self._invoker:
- menu.connect('motion-notify-event', self._motion_notify_cb)
- menu.connect('enter-notify-event', self._enter_notify_cb)
- menu.connect('leave-notify-event', self._leave_notify_cb)
- self._entered = False
- self._mouse_in_palette = False
- self._mouse_in_invoker = False
- Gtk.Menu.popup(self, None, None, self._position, None, 0, 0)
- self._up = True
-
- def popdown(self):
- if not self._up:
- return
- Gtk.Menu.popdown(self)
-
- for menu in self._menus:
- menu.disconnect_by_func(self._motion_notify_cb)
- menu.disconnect_by_func(self._enter_notify_cb)
- menu.disconnect_by_func(self._leave_notify_cb)
-
- self._up = False
- self._menus = []
- self._invoker = None
class _PaletteWindowWidget(Gtk.Window):