diff options
Diffstat (limited to 'sugar-toolkit/src/sugar/tutorius/overlayer.py')
-rw-r--r-- | sugar-toolkit/src/sugar/tutorius/overlayer.py | 328 |
1 files changed, 0 insertions, 328 deletions
diff --git a/sugar-toolkit/src/sugar/tutorius/overlayer.py b/sugar-toolkit/src/sugar/tutorius/overlayer.py deleted file mode 100644 index c08ed4c..0000000 --- a/sugar-toolkit/src/sugar/tutorius/overlayer.py +++ /dev/null @@ -1,328 +0,0 @@ -""" -This guy manages drawing of overlayed widgets. The class responsible for drawing -management (Overlayer) and overlayable widgets are defined here. -""" -# Copyright (C) 2009, Tutorius.org -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -import gobject -import gtk -import cairo -import pangocairo - -# This is the CanvasDrawable protocol. Any widget wishing to be drawn on the -# overlay must implement it. See TextBubble for a sample implementation. -#class CanvasDrawable(object): -# """Defines the CanvasDrawable protocol""" -# no_expose = None -# def draw_with_context(self, context): -# """ -# Draws the cairo widget with the passed cairo context. -# This will be called if the widget is child of an overlayer. -# """ -# pass - -class Overlayer(gtk.Layout): - """ - This guy manages drawing of overlayed widgets. Those can be standard GTK - widgets or special "cairoDrawable" widgets which support the defined - interface (see the put method). - - @param overlayed widget to be overlayed. Will be resized to full size. - """ - def __init__(self, overlayed=None): - gtk.Layout.__init__(self) - - self._overlayed = overlayed - if overlayed: - self.put(overlayed, 0, 0) - - self.__realizer = self.connect("expose-event", self.__init_realized) - self.connect("size-allocate", self.__size_allocate) - self.show() - - self.__render_handle = None - - def put(self, child, x, y): - """ - Adds a child widget to be overlayed. This can be, overlay widgets or - normal GTK widgets (though normal widgets will alwas appear under - cairo widgets due to the rendering chain). - - @param child the child to add - @param x the horizontal coordinate for positionning - @param y the vertical coordinate for positionning - """ - if hasattr(child, "draw_with_context"): - # if the widget has the CanvasDrawable protocol, use it. - child.no_expose = True - gtk.Layout.put(self, child, x, y) - - - def __init_realized(self, widget, event): - """ - Initializer to set once widget is realized. - Since an expose event is signaled only to realized widgets, we set this - callback for the first expose run. It should also be called after - beign reparented to ensure the window used for drawing is set up. - """ - assert hasattr(self.window, "set_composited"), \ - "compositing not supported or widget not realized." - self.disconnect(self.__realizer) - del self.__realizer - - self.parent.set_app_paintable(True) - - # the parent is composited, so we can access gtk's rendered buffer - # and overlay over. If we don't composite, we won't be able to read - # pixels and background will be black. - self.window.set_composited(True) - self.__render_handle = self.parent.connect_after("expose-event", \ - self.__expose_overlay) - - def __expose_overlay(self, widget, event): - """expose event handler to draw the thing.""" - #get our child (in this case, the event box) - child = widget.get_child() - - #create a cairo context to draw to the window - ctx = widget.window.cairo_create() - - #the source data is the (composited) event box - ctx.set_source_pixmap(child.window, - child.allocation.x, - child.allocation.y) - - #draw no more than our expose event intersects our child - region = gtk.gdk.region_rectangle(child.allocation) - rect = gtk.gdk.region_rectangle(event.area) - region.intersect(rect) - ctx.region (region) - ctx.clip() - - ctx.set_operator(cairo.OPERATOR_OVER) - # has to be blended and a 1.0 alpha would not make it blend - ctx.paint_with_alpha(0.99) - - #draw overlay - for drawn_child in self.get_children(): - if hasattr(drawn_child, "draw_with_context"): - drawn_child.draw_with_context(ctx) - - - def __size_allocate(self, widget, allocation): - """ - Set size allocation (actual gtk widget size) and propagate it to - overlayed child - """ - self.allocation = allocation - # One may wonder why using size_request instead of size_allocate; - # Since widget is laid out in a Layout box, the Layout will honor the - # requested size. Using size_allocate could make a nasty nested loop in - # some cases. - self._overlayed.set_size_request(allocation.width, allocation.height) - - -class TextBubble(gtk.Widget): - """ - A CanvasDrawableWidget drawing a round textbox and a tail pointing - to a specified widget. - """ - def __init__(self, text, speaker=None, tailpos=None): - """ - Creates a new cairo rendered text bubble. - - @param text the text to render in the bubble - @param speaker the widget to compute the tail position from - @param tailpos (optional) position relative to the bubble to use as - the tail position, if no speaker - """ - gtk.Widget.__init__(self) - - # FIXME: ensure previous call does not interfere with widget stacking, - # as using a gtk.Layout and stacking widgets may reveal a screwed up - # order with the cairo widget on top. - self.__label = None - self.__text_dimentions = None - - self.label = text - self.speaker = speaker - self.tailpos = tailpos - self.line_width = 5 - - self.__exposer = self.connect("expose-event", self.__on_expose) - - def draw_with_context(self, context): - """ - Draw using the passed cairo context instead of creating a new cairo - context. This eases blending between multiple cairo-rendered - widgets. - """ - context.translate(self.allocation.x, self.allocation.y) - width = self.allocation.width - height = self.allocation.height - xradius = width/2 - yradius = height/2 - width -= self.line_width - height -= self.line_width - - # bubble border - context.move_to(self.line_width, yradius) - context.curve_to(self.line_width, self.line_width, - self.line_width, self.line_width, xradius, self.line_width) - context.curve_to(width, self.line_width, - width, self.line_width, width, yradius) - context.curve_to(width, height, width, height, xradius, height) - context.curve_to(self.line_width, height, - self.line_width, height, self.line_width, yradius) - context.set_line_width(self.line_width) - context.set_source_rgb(0.0, 0.0, 0.0) - context.stroke() - - # TODO fetch speaker coordinates - - # draw bubble tail - if self.tailpos: - context.move_to(xradius-40, yradius) - context.line_to(self.tailpos[0], self.tailpos[1]) - context.line_to(xradius+40, yradius) - context.set_line_width(self.line_width) - context.set_source_rgb(0.0, 0.0, 0.0) - context.stroke_preserve() - context.set_source_rgb(1.0, 1.0, 0.0) - context.fill() - - # bubble painting. Redrawing the inside after the tail will combine - # both shapes. - # TODO: we could probably generate the shape at initialization to - # lighten computations. - context.move_to(self.line_width, yradius) - context.curve_to(self.line_width, self.line_width, - self.line_width, self.line_width, xradius, self.line_width) - context.curve_to(width, self.line_width, - width, self.line_width, width, yradius) - context.curve_to(width, height, width, height, xradius, height) - context.curve_to(self.line_width, height, - self.line_width, height, self.line_width, yradius) - context.set_source_rgb(1.0, 1.0, 0.0) - context.fill() - - # text - # FIXME create text layout when setting text or in realize method - context.set_source_rgb(0.0, 0.0, 0.0) - pangoctx = pangocairo.CairoContext(context) - text_layout = pangoctx.create_layout() - text_layout.set_text(self.__label) - pangoctx.move_to( - int((self.allocation.width-self.__text_dimentions[0])/2), - int((self.allocation.height-self.__text_dimentions[1])/2)) - pangoctx.show_layout(text_layout) - - # work done. Be kind to next cairo widgets and reset matrix. - context.identity_matrix() - - def do_realize(self): - """ Setup gdk window creation. """ - self.set_flags(gtk.REALIZED | gtk.NO_WINDOW) - # TODO: cleanup window creation code as lot here probably isn't - # necessary. - # See http://www.learningpython.com/2006/07/25/writing-a-custom-widget-using-pygtk/ - # as the following was taken there. - self.window = self.get_parent_window() - if not isinstance(self.parent, Overlayer): - self.unset_flags(gtk.NO_WINDOW) - self.window = gtk.gdk.Window( - self.get_parent_window(), - width=self.allocation.width, - height=self.allocation.height, - window_type=gtk.gdk.WINDOW_CHILD, - wclass=gtk.gdk.INPUT_OUTPUT, - event_mask=self.get_events()|gtk.gdk.EXPOSURE_MASK) - - # Associate the gdk.Window with ourselves, Gtk+ needs a reference - # between the widget and the gdk window - self.window.set_user_data(self) - - # Attach the style to the gdk.Window, a style contains colors and - # GC contextes used for drawing - self.style.attach(self.window) - - # The default color of the background should be what - # the style (theme engine) tells us. - self.style.set_background(self.window, gtk.STATE_NORMAL) - self.window.move_resize(*self.allocation) - - def __on_expose(self, widget, event): - """Redraw event callback.""" - ctx = self.window.cairo_create() - - self.draw_with_context(ctx) - - return True - - def _set_label(self, value): - """Sets the label and flags the widget to be redrawn.""" - self.__label = value - # FIXME hack to calculate size. necessary because may not have been - # realized. We create a fake surface to use builtin math. This should - # probably be done at realization and/or on text setter. - surf = cairo.SVGSurface("/dev/null", 0, 0) - ctx = cairo.Context(surf) - pangoctx = pangocairo.CairoContext(ctx) - text_layout = pangoctx.create_layout() - text_layout.set_text(value) - self.__text_dimentions = text_layout.get_pixel_size() - del text_layout, pangoctx, ctx, surf - - def do_size_request(self, requisition): - """Fill requisition with size occupied by the widget.""" - width, height = self.__text_dimentions - - # FIXME bogus values follows. will need to replace them with - # padding relative to font size and line border size - requisition.width = int(width+30) - requisition.height = int(height+40) - - def do_size_allocate(self, allocation): - """Save zone allocated to the widget.""" - self.allocation = allocation - - def _get_label(self): - """Getter method for the label property""" - return self.__label - - def _set_no_expose(self, value): - """setter for no_expose property""" - if self.__exposer and value: - self.disconnect(self.__exposer) - self.__exposer = None - elif (not self.__exposer) and (not value): - self.__exposer = self.connect("expose-event", self.__on_expose) - - def _get_no_expose(self): - """getter for no_expose property""" - return not self.__exposer - - no_expose = property(fset=_set_no_expose, fget=_get_no_expose, - doc="Whether the widget should handle exposition events or not.") - - label = property(fget=_get_label, fset=_set_label, - doc="Text label which is to be painted on the top of the widget") - -gobject.type_register(TextBubble) - - -# vim:set ts=4 sts=4 sw=4 et: |