From 01a06943a2da842156913c0c3986995e2f7e7a2a Mon Sep 17 00:00:00 2001 From: Simon Schampijer Date: Thu, 15 Mar 2012 17:56:16 +0100 Subject: [PATCH] Mimic the behaviour and style of the Sugar GTK+ 2 toolbutton palettes in GTK+ 3 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 --- 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 760c99a5..7785d106 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 041e94f2..7e317f7d 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 7ca13d8b..a1ab8e4a 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: