[Sugar-devel] [PATCH] Mimic the behaviour and style of the Sugar GTK+ 2 toolbutton palettes in GTK+ 3

Simon Schampijer simon at schampijer.de
Thu Mar 15 13:38:28 EDT 2012


First we needed to port the Palette code to use a minimum size. The default size
is two times the GRID_CELL_SIZE. Since the request-phase of the traditional GTK+
geometry management has been replaced by a height-for-width system [1] we have
to compensate for that. Furthermore we need to pass the invoker from the
PaletteWindow to the _PaletteWindowWidget for the gap calculation code for
drawing the border around the Palette.

We do the drawing of the border for the toolbutton in the base class, moved
this from the ToolbarButton and made sure we are drawing in the right order.
In the ToolButton we draw as well a black background for the ToolButton when
the Palette is up. While the mouse is over the button we can do that in the
theme, but not when the mouse moves over the Palette.

[1] http://developer.gnome.org/gtk3/3.0/ch25s02.html#id1525688

Signed-off-by: Simon Schampijer <simon at laptop.org>
---
 src/sugar3/graphics/palettewindow.py |   92 ++++++++++++++++++++++++----------
 src/sugar3/graphics/toolbarbox.py    |   31 +++--------
 src/sugar3/graphics/toolbutton.py    |   24 +++++----
 3 files changed, 87 insertions(+), 60 deletions(-)

diff --git a/src/sugar3/graphics/palettewindow.py b/src/sugar3/graphics/palettewindow.py
index 760c99a..7785d10 100644
--- a/src/sugar3/graphics/palettewindow.py
+++ b/src/sugar3/graphics/palettewindow.py
@@ -98,16 +98,15 @@ class _PaletteMenuWidget(Gtk.Menu):
         res_, x, y = self.get_toplevel().get_window().get_origin()
         return x, y
 
-    def do_size_request(self, requisition):
-        Gtk.Window.do_size_request(self, requisition)
-        requisition.width = max(requisition.width, style.GRID_CELL_SIZE * 2)
-
     def move(self, x, y):
         self._popup_position = (x, y)
 
     def set_transient_for(self, window):
         pass
 
+    def set_invoker(self, invoker):
+        pass
+
     def _position(self, widget, data):
         return self._popup_position[0], self._popup_position[1], False
 
@@ -252,7 +251,7 @@ class _PaletteWindowWidget(Gtk.Window):
         self.add_accel_group(accel_group)
 
         self._old_alloc = None
-
+        self._invoker = None
         self._should_accept_focus = True
 
     def set_accept_focus(self, focus):
@@ -270,9 +269,13 @@ class _PaletteWindowWidget(Gtk.Window):
         self.get_window().set_accept_focus(self._should_accept_focus)
         self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
 
-    def do_size_request(self, requisition):
-        Gtk.Window.do_size_request(self, requisition)
-        requisition.width = max(requisition.width, style.GRID_CELL_SIZE * 2)
+    def do_get_preferred_width(self):
+        size = 0
+        child = self.get_child()
+        if child:
+            minimum_size, natural_size = child.get_preferred_width()
+            size = max(minimum_size, natural_size, style.GRID_CELL_SIZE * 2)
+        return size, size
 
     def do_size_allocate(self, allocation):
         Gtk.Window.do_size_allocate(self, allocation)
@@ -291,6 +294,46 @@ class _PaletteWindowWidget(Gtk.Window):
         # the configure request handler and finally size_allocate is called.
         self._old_alloc = allocation
 
+    def set_invoker(self, invoker):
+        self._invoker = invoker
+
+    def get_rect(self):
+        win_x, win_y = self.get_origin()
+        rectangle = self.get_allocation()
+
+        x = win_x + rectangle.x
+        y = win_y + rectangle.y
+        minimum, natural = self.get_preferred_size()
+        rect = Gdk.Rectangle()
+        rect.x = x
+        rect.y = y
+        rect.width = minimum.width
+        rect.height = natural.height
+
+        return rect
+
+    def do_draw(self, cr):
+        # Fall trough to the container expose handler.
+        # (Leaving out the window expose handler which redraws everything)
+        Gtk.Window.do_draw(self, cr)
+
+        if self._invoker is not None and self._invoker.has_rectangle_gap():
+            invoker = self._invoker.get_rect()
+            palette = self.get_rect()
+            gap = _calculate_gap(palette, invoker)
+        else:
+            gap = False
+
+        allocation = self.get_allocation()
+        context = self.get_style_context()
+        context.add_class('toolitem')
+        if gap:
+            Gtk.render_frame_gap(context, cr, 0, 0, allocation.width, allocation.height,
+                                 gap[0], gap[1], gap[2])
+        else:
+            Gtk.render_frame(context, cr, 0, 0, allocation.width, allocation.height)
+        return False
+
     def __enter_notify_event_cb(self, widget, event):
         if event.mode == Gdk.CrossingMode.NORMAL and \
                 event.detail != Gdk.NotifyType.INFERIOR:
@@ -427,6 +470,7 @@ class PaletteWindow(GObject.GObject):
         self._widget.connect('leave-notify', self.__leave_notify_cb)
 
         self._set_effective_group_id(self._group_id)
+        self._widget.set_invoker(self._invoker)
 
         self._mouse_detector.connect('motion-slow', self._mouse_slow_cb)
         self._mouse_detector.parent = self._widget
@@ -452,6 +496,7 @@ class PaletteWindow(GObject.GObject):
             self._invoker_hids.remove(hid)
 
         self._invoker = invoker
+        self._widget.set_invoker(self._invoker)
         if invoker is not None:
             self._invoker_hids.append(self._invoker.connect(
                 'mouse-enter', self._invoker_mouse_enter_cb))
@@ -599,17 +644,17 @@ class PaletteWindow(GObject.GObject):
 
     def get_rect(self):
         win_x, win_y = self._widget.get_origin()
-        rectangle = self.get_allocation()
+        rectangle = self._widget.get_allocation()
 
         x = win_x + rectangle.x
         y = win_y + rectangle.y
-        requisition = self._widget.size_request()
+        minimum, natural_ = self._widget.get_preferred_size()
 
         rect = Gdk.Rectangle()
         rect.x = x
         rect.y = y
-        rect.width = requisition.width
-        rect.height = requisition.height
+        rect.width = minimum.width
+        rect.height = minimum.height
 
         return rect
 
@@ -967,25 +1012,18 @@ class WidgetInvoker(Invoker):
     def has_rectangle_gap(self):
         return True
 
-    def draw_rectangle(self, event, palette):
-        x, y = self._widget.allocation.x, self._widget.allocation.y
+    def draw_rectangle(self, cr, palette):
+        allocation = self.parent.get_allocation()
 
-        wstyle = self._widget.get_style()
-        gap = _calculate_gap(self.get_rect(), palette.get_rect())
+        context = self.parent.get_style_context()
+        context.add_class('toolitem')
 
+        gap = _calculate_gap(self.get_rect(), palette.get_rect())
         if gap:
-            wstyle.paint_box_gap(event.window, Gtk.StateType.PRELIGHT,
-                                 Gtk.ShadowType.IN, event.area, self._widget,
-                                 'palette-invoker', x, y,
-                                 self._widget.allocation.width,
-                                 self._widget.allocation.height,
+            Gtk.render_frame_gap(context, cr, 0, 0,
+                                 allocation.width,
+                                 allocation.height,
                                  gap[0], gap[1], gap[2])
-        else:
-            wstyle.paint_box(event.window, Gtk.StateType.PRELIGHT,
-                             Gtk.ShadowType.IN, event.area, self._widget,
-                             'palette-invoker', x, y,
-                             self._widget.allocation.width,
-                             self._widget.allocation.height)
 
     def __enter_notify_event_cb(self, widget, event):
         self.notify_mouse_enter()
diff --git a/src/sugar3/graphics/toolbarbox.py b/src/sugar3/graphics/toolbarbox.py
index 041e94f..7e317f7 100644
--- a/src/sugar3/graphics/toolbarbox.py
+++ b/src/sugar3/graphics/toolbarbox.py
@@ -38,7 +38,7 @@ class ToolbarButton(ToolButton):
 
         self.connect('clicked',
                 lambda widget: self.set_expanded(not self.is_expanded()))
-
+        self.connect_after('draw', self.__drawing_cb)
         self.connect('hierarchy-changed', self.__hierarchy_changed_cb)
 
     def __hierarchy_changed_cb(self, tool_button, previous_toplevel):
@@ -125,32 +125,19 @@ class ToolbarButton(ToolButton):
             return
         page_parent.remove(self.page_widget)
 
-    def do_draw(self, cr):
+    def __drawing_cb(self, button, cr):
         alloc = self.get_allocation()
-
         context = self.get_style_context()
         context.add_class('toolitem')
-
-        arrow_direction = 0
         if not self.is_expanded() or self.props.palette is not None and \
                 self.props.palette.is_up():
-            arrow_direction = math.pi
-
-        if not self.is_expanded() and self.props.palette is not None and \
-            self.props.palette.is_up():
-            # draw a black background
-            cr.set_source_rgba(*style.COLOR_BLACK.get_rgba())
-            cr.rectangle(0, 0, alloc.width, alloc.height)
-            cr.fill()
-
-        Gtk.ToolButton.do_draw(self, cr)
-
-        if self.is_expanded() or self.props.palette is not None and \
-            self.props.palette.is_up():
-            Gtk.render_frame_gap(context, cr, 0, 0, alloc.width, alloc.height,
-                                 Gtk.PositionType.BOTTOM, 0, alloc.width)
-
-        _paint_arrow(self, cr, arrow_direction)
+            ToolButton.do_draw(self, cr)
+            _paint_arrow(self, cr, math.pi)
+            return False
+        Gtk.render_frame_gap(context, cr, 0, 0, alloc.width, alloc.height,
+                             Gtk.PositionType.BOTTOM, 0, alloc.width)
+        _paint_arrow(self, cr, 0)
+        return False
 
 
 class ToolbarBox(Gtk.VBox):
diff --git a/src/sugar3/graphics/toolbutton.py b/src/sugar3/graphics/toolbutton.py
index 7ca13d8..a1ab8e4 100644
--- a/src/sugar3/graphics/toolbutton.py
+++ b/src/sugar3/graphics/toolbutton.py
@@ -142,20 +142,22 @@ class ToolButton(Gtk.ToolButton):
     palette_invoker = GObject.property(
         type=object, setter=set_palette_invoker, getter=get_palette_invoker)
 
-    def do_expose_event(self, event):
+    def do_draw(self, cr):
         child = self.get_child()
-        allocation = self.get_allocation()
+        if self.palette and self.palette.is_up():
+            allocation = self.get_allocation()
+            # draw a black background, has been done by the engine before
+            cr.set_source_rgb(0, 0, 0)
+            cr.rectangle(0, 0, allocation.width, allocation.height)
+            cr.paint()
+
+        Gtk.ToolButton.do_draw(self, cr)
+
         if self.palette and self.palette.is_up():
             invoker = self.palette.props.invoker
-            invoker.draw_rectangle(event, self.palette)
-        elif child.state == Gtk.StateType.PRELIGHT:
-            child.style.paint_box(event.window, Gtk.StateType.PRELIGHT,
-                                  Gtk.ShadowType.NONE, event.area,
-                                  child, 'toolbutton-prelight',
-                                  allocation.x, allocation.y,
-                                  allocation.width, allocation.height)
-
-        Gtk.ToolButton.do_expose_event(self, event)
+            invoker.draw_rectangle(cr, self.palette)
+
+        return False
 
     def do_clicked(self):
         if self.palette:
-- 
1.7.7.6



More information about the Sugar-devel mailing list