[Sugar-devel] Shell-port GTK+ 2 ---> GTK+ 3 (was: Re: [PATCH sugar-toolkit (shell-port)] SugarEventIcon: Port the CanvasIcon to a hippo-free implementation)

Simon Schampijer simon at schampijer.de
Sat Jun 23 14:21:32 EDT 2012


Hi,

here a few more informations to the approach I am taken. Daniel Narvaez 
asked me yesterday about it, so there might have been some questions 
floating around already :)

The first part of the work is getting the shell hippo free and using GTK 
widgets instead. The views have to be ported to GTKContainers and in the 
toolkit we have to replace the CanvasIcon with an EventBox (for 
receiving the events) that contains an IconBuffer to draw the icon 
itself. We renamed that widget to EventIcon in the process. The API 
stays the same, just for more clarity size has been renamed to pixel_size.

I have pushed the current state of the shell port to a temporary repo 
[1] and this toolkit-gtk2 patch to a temporary repo [2] as well. So the 
toolkit-gtk2 part will never really land but it is good to have it 
around for making the shell port.

Once this no-hippo work is done we can run the autoconverter script 
(GTK2-GTK3) and then do the cleanup for GTK+ 3. Same way as we did for 
the toolkit port.

The two branches are both tracked in sugar-build as well [3].

Regards,
    Simon

[1] http://git.sugarlabs.org/~erikos/sugar/erikos-shell-port
[2] http://git.sugarlabs.org/~erikos/sugar-toolkit/erikos-shell-port
[3] http://git.sugarlabs.org/~dnarvaez/sugar-build/shell-port


On 06/22/2012 03:58 PM, Simon Schampijer wrote:
> The icon consists of an GtkEventBox and an IconBuffer. The
> GtkEventBox is a subclass of GtkBin which has its own window
> and therefor is used to catch events for our IconBuffer
> which does not have it's own window.
>
> The EventIcon does emit the 'activated' signal when clicked
> and uses the CursorInvoker to invoke a palette the same
> way as the CanvasIcon did. The CursorInvoker had to be adopted
> accordingly and made it hippo free as well.
>
> We keep the same API as with the CanvasIcon, only the 'size'
> property is changed to be called 'pixel_size' in order to
> make clearer which values it expects to be passed. We don't
> expect a GtkIconSize to be passed here.
>
> Another option would have been to put a SugarIcon inside a
> a GtkEventBox and make the properties available through an
> icon property but the API would have not been as nice and
> logically it seems to make more sense to have the IconBuffer
> being the base for both the SugarIcon and the SugarEventIcon.
>
> There are a few unnecessary docstrings removed from the
> icon class.
>
> This patch is highly based on the work from Walter Bender,
> Daniel Drake and Raul Gutierrez Segales.
>
> Signed-off-by: Simon Schampijer <simon at laptop.org>
>
> [1] http://developer.gnome.org/gtk/2.24/GtkEventBox.html
> [2] http://developer.gnome.org/gtk/2.24/gtk-Themeable-Stock-Images.html#GtkIconSize
> ---
>   src/sugar/graphics/icon.py          |  407 ++++-------------------------------
>   src/sugar/graphics/palettewindow.py |   25 ++-
>   2 files changed, 61 insertions(+), 371 deletions(-)
>
> diff --git a/src/sugar/graphics/icon.py b/src/sugar/graphics/icon.py
> index 3f540d7..2fd136e 100644
> --- a/src/sugar/graphics/icon.py
> +++ b/src/sugar/graphics/icon.py
> @@ -27,9 +27,9 @@ import logging
>
>   import gobject
>   import gtk
> -import hippo
>   import cairo
>
> +from sugar.graphics import style
>   from sugar.graphics.xocolor import XoColor
>   from sugar.util import LRU
>
> @@ -367,16 +367,6 @@ class Icon(gtk.Image):
>           self._buffer.file_name = self.props.file
>
>       def do_size_request(self, requisition):
> -        """
> -        Parameters
> -        ----------
> -        requisition :
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           self._sync_image_properties()
>           surface = self._buffer.get_surface()
>           if surface:
> @@ -389,16 +379,6 @@ class Icon(gtk.Image):
>               requisition[0] = requisition[1] = 0
>
>       def do_expose_event(self, event):
> -        """
> -        Parameters
> -        ----------
> -        event :
> -
> -        Returns:
> -        --------
> -        None
> -
> -        """
>           self._sync_image_properties()
>           sensitive = (self.state != gtk.STATE_INSENSITIVE)
>           surface = self._buffer.get_surface(sensitive, self)
> @@ -436,16 +416,6 @@ class Icon(gtk.Image):
>               cr.paint_with_alpha(self._alpha)
>
>       def set_xo_color(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value :
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.xo_color != value:
>               self._buffer.xo_color = value
>               self.queue_draw()
> @@ -454,78 +424,28 @@ class Icon(gtk.Image):
>           type=object, getter=None, setter=set_xo_color)
>
>       def set_fill_color(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value :
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.fill_color != value:
>               self._buffer.fill_color = value
>               self.queue_draw()
>
>       def get_fill_color(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        fill_color :
> -
> -        """
>           return self._buffer.fill_color
>
>       fill_color = gobject.property(
>           type=object, getter=get_fill_color, setter=set_fill_color)
>
>       def set_stroke_color(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value :
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.stroke_color != value:
>               self._buffer.stroke_color = value
>               self.queue_draw()
>
>       def get_stroke_color(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        stroke_color :
> -
> -        """
>           return self._buffer.stroke_color
>
>       stroke_color = gobject.property(
>           type=object, getter=get_stroke_color, setter=set_stroke_color)
>
>       def set_badge_name(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.badge_name != value:
>               self._buffer.badge_name = value
>               self.queue_resize()
> @@ -553,272 +473,144 @@ class Icon(gtk.Image):
>           type=float, setter=set_scale)
>
>
> -class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
> +class EventIcon(gtk.EventBox):
>
> -    __gtype_name__ = 'CanvasIcon'
> +    __gtype_name__ = 'SugarEventIcon'
> +    __gsignals__ = {
> +        'activated': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, []),
> +    }
>
>       def __init__(self, **kwargs):
>           from sugar.graphics.palette import CanvasInvoker
>
>           self._buffer = _IconBuffer()
> -        self._palette_invoker = CanvasInvoker()
>           self._alpha = 1.0
>
> -        hippo.CanvasBox.__init__(self, **kwargs)
> +        gtk.EventBox.__init__(self)
> +        for key, value in kwargs.iteritems():
> +            self.set_property(key, value)
>
> +        self._palette_invoker = CanvasInvoker()
>           self._palette_invoker.attach(self)
>
> +        # HACK to supress the grey background around the icon
> +        # won't be needed in GTK3
> +        self.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color())
> +
>           self.connect('destroy', self.__destroy_cb)
>
> -    def _emit_paint_needed_icon_area(self):
> +    def do_expose_event(self, event):
>           surface = self._buffer.get_surface()
>           if surface:
> -            width, height = self.get_allocation()
> -            s_width = surface.get_width()
> -            s_height = surface.get_height()
> +            allocation = self.get_allocation()
> +
> +            x = (allocation.width - surface.get_width()) / 2
> +            y = (allocation.height - surface.get_height()) / 2
>
> -            x = (width - s_width) / 2
> -            y = (height - s_height) / 2
> +            cr = self.window.cairo_create()
> +            cr.set_source_surface(surface, x, y)
> +            if self._alpha == 1.0:
> +                cr.paint()
> +            else:
> +                cr.paint_with_alpha(self._alpha)
>
> -            self.emit_paint_needed(x, y, s_width, s_height)
> +    def do_size_request(self, req):
> +        surface = self._buffer.get_surface()
> +        if surface:
> +            req.width = surface.get_width()
> +            req.height = surface.get_height()
> +        elif self._buffer.width:
> +            req.width = self._buffer.width
> +            req.height = self._buffer.height
> +        else:
> +            req.width = 0
> +            req.height = 0
>
>       def __destroy_cb(self, icon):
>           if self._palette_invoker is not None:
>               self._palette_invoker.detach()
>
>       def set_file_name(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        \"\"\"
> -
> -        """
>           if self._buffer.file_name != value:
>               self._buffer.file_name = value
> -            self.emit_paint_needed(0, 0, -1, -1)
> +            self.queue_draw()
>
>       def get_file_name(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        file name :
> -
> -        """
>           return self._buffer.file_name
>
>       file_name = gobject.property(
>           type=object, getter=get_file_name, setter=set_file_name)
>
>       def set_icon_name(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.icon_name != value:
>               self._buffer.icon_name = value
> -            self.emit_paint_needed(0, 0, -1, -1)
> +            self.queue_draw()
>
>       def get_icon_name(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        icon name :
> -
> -        """
>           return self._buffer.icon_name
>
>       icon_name = gobject.property(
>           type=object, getter=get_icon_name, setter=set_icon_name)
>
>       def set_xo_color(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.xo_color != value:
>               self._buffer.xo_color = value
> -            self._emit_paint_needed_icon_area()
> +            self.queue_draw()
>
>       xo_color = gobject.property(
>           type=object, getter=None, setter=set_xo_color)
>
>       def set_fill_color(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.fill_color != value:
>               self._buffer.fill_color = value
> -            self._emit_paint_needed_icon_area()
> +            self.queue_draw()
>
>       def get_fill_color(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        fill color :
> -
> -        """
>           return self._buffer.fill_color
>
>       fill_color = gobject.property(
>           type=object, getter=get_fill_color, setter=set_fill_color)
>
>       def set_stroke_color(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.stroke_color != value:
>               self._buffer.stroke_color = value
> -            self._emit_paint_needed_icon_area()
> +            self.queue_draw()
>
>       def get_stroke_color(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        stroke color :
> -
> -        """
>           return self._buffer.stroke_color
>
>       stroke_color = gobject.property(
>           type=object, getter=get_stroke_color, setter=set_stroke_color)
>
>       def set_background_color(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.background_color != value:
>               self._buffer.background_color = value
> -            self.emit_paint_needed(0, 0, -1, -1)
> +            self.queue_draw()
>
>       def get_background_color(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        fill color :
> -
> -        """
>           return self._buffer.background_color
>
>       background_color = gobject.property(
>           type=object, getter=get_background_color, setter=set_background_color)
>
>       def set_size(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.width != value:
>               self._buffer.width = value
>               self._buffer.height = value
> -            self.emit_request_changed()
> +            self.queue_resize()
>
>       def get_size(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        size :
> -
> -        """
>           return self._buffer.width
>
> -    size = gobject.property(
> +    pixel_size = gobject.property(
>           type=object, getter=get_size, setter=set_size)
>
>       def set_scale(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value:
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.scale != value:
>               self._buffer.scale = value
> -            self.emit_request_changed()
> +            self.queue_resize()
>
>       def get_scale(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        scale :
> -
> -        """
>           return self._buffer.scale
>
>       scale = gobject.property(
> @@ -827,136 +619,31 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
>       def set_alpha(self, alpha):
>           if self._alpha != alpha:
>               self._alpha = alpha
> -            self.emit_paint_needed(0, 0, -1, -1)
> +            self.queue_draw()
>
>       alpha = gobject.property(
>           type=float, setter=set_alpha)
>
>       def set_cache(self, value):
> -        """
> -        Parameters
> -        ----------
> -        cache
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           self._buffer.cache = value
>
>       def get_cache(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        cache :
> -
> -        """
>           return self._buffer.cache
>
>       cache = gobject.property(
>           type=bool, default=False, getter=get_cache, setter=set_cache)
>
>       def set_badge_name(self, value):
> -        """
> -        Parameters
> -        ----------
> -        value :
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
>           if self._buffer.badge_name != value:
>               self._buffer.badge_name = value
> -            self.emit_paint_needed(0, 0, -1, -1)
> +            self.queue_draw()
>
>       def get_badge_name(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        badge name :
> -
> -        """
>           return self._buffer.badge_name
>
>       badge_name = gobject.property(
>           type=object, getter=get_badge_name, setter=set_badge_name)
>
> -    def do_paint_below_children(self, cr, damaged_box):
> -        """
> -        Parameters
> -        ----------
> -        cr :
> -
> -        damaged_box :
> -
> -        Returns
> -        -------
> -        None
> -
> -        """
> -        surface = self._buffer.get_surface()
> -        if surface:
> -            width, height = self.get_allocation()
> -
> -            x = (width - surface.get_width()) / 2
> -            y = (height - surface.get_height()) / 2
> -
> -            cr.set_source_surface(surface, x, y)
> -            if self._alpha == 1.0:
> -                cr.paint()
> -            else:
> -                cr.paint_with_alpha(self._alpha)
> -
> -    def do_get_content_width_request(self):
> -        """
> -        Parameters
> -        ----------
> -        None
> -
> -        Returns
> -        -------
> -        width :
> -
> -        """
> -        surface = self._buffer.get_surface()
> -        if surface:
> -            size = surface.get_width()
> -        elif self._buffer.width:
> -            size = self._buffer.width
> -        else:
> -            size = 0
> -
> -        return size, size
> -
> -    def do_get_content_height_request(self, for_width):
> -        surface = self._buffer.get_surface()
> -        if surface:
> -            size = surface.get_height()
> -        elif self._buffer.height:
> -            size = self._buffer.height
> -        else:
> -            size = 0
> -
> -        return size, size
> -
> -    def do_button_press_event(self, event):
> -        if event.button == 1:
> -            self.emit_activated()
> -            return True
> -        else:
> -            return False
> -
>       def create_palette(self):
>           return None
>
> diff --git a/src/sugar/graphics/palettewindow.py b/src/sugar/graphics/palettewindow.py
> index 5281e54..f61faca 100644
> --- a/src/sugar/graphics/palettewindow.py
> +++ b/src/sugar/graphics/palettewindow.py
> @@ -25,7 +25,6 @@ import logging
>
>   import gtk
>   import gobject
> -import hippo
>
>   from sugar.graphics import palettegroup
>   from sugar.graphics import animator
> @@ -793,7 +792,8 @@ class CanvasInvoker(Invoker):
>           Invoker.__init__(self)
>
>           self._position_hint = self.AT_CURSOR
> -        self._motion_hid = None
> +        self._motion_enter_hid = None
> +        self._motion_leave_hid = None
>           self._release_hid = None
>           self._item = None
>
> @@ -804,14 +804,17 @@ class CanvasInvoker(Invoker):
>           Invoker.attach(self, parent)
>
>           self._item = parent
> -        self._motion_hid = self._item.connect('motion-notify-event',
> -                                              self.__motion_notify_event_cb)
> +        self._motion_enter_hid = self._item.connect(
> +            'enter-notify-event', self.__enter_notify_event_cb)
> +        self._motion_leave_hid = self._item.connect(
> +            'leave-notify-event', self.__leave_notify_event_cb)
>           self._release_hid = self._item.connect('button-release-event',
>                                                  self.__button_release_event_cb)
>
>       def detach(self):
>           Invoker.detach(self)
> -        self._item.disconnect(self._motion_hid)
> +        self._item.disconnect(self._motion_enter_hid)
> +        self._item.disconnect(self._motion_leave_hid)
>           self._item.disconnect(self._release_hid)
>
>       def get_default_position(self):
> @@ -826,12 +829,12 @@ class CanvasInvoker(Invoker):
>           else:
>               return gtk.gdk.Rectangle()
>
> -    def __motion_notify_event_cb(self, button, event):
> -        if event.detail == hippo.MOTION_DETAIL_ENTER:
> -            self.notify_mouse_enter()
> -        elif event.detail == hippo.MOTION_DETAIL_LEAVE:
> -            self.notify_mouse_leave()
> +    def __enter_notify_event_cb(self, button, event):
> +        self.notify_mouse_enter()
> +        return False
>
> +    def __leave_notify_event_cb(self, button, event):
> +        self.notify_mouse_leave()
>           return False
>
>       def __button_release_event_cb(self, button, event):
> @@ -842,7 +845,7 @@ class CanvasInvoker(Invoker):
>               return False
>
>       def get_toplevel(self):
> -        return hippo.get_canvas_for_item(self._item).get_toplevel()
> +        return self._item.get_toplevel()
>
>
>   class ToolInvoker(WidgetInvoker):
>




More information about the Sugar-devel mailing list