diff options
author | Simon Poirier <simpoir@gmail.com> | 2009-07-11 21:39:46 (GMT) |
---|---|---|
committer | Simon Poirier <simpoir@gmail.com> | 2009-07-11 22:00:30 (GMT) |
commit | 0c3f127c86af818d260966d2292b199757087157 (patch) | |
tree | 62cf941aef5bde83641a17ec492e03d0ecb17386 /src/sugar/tutorius/overlayer.py | |
parent | 9fafb49af210e956d43d6a00106558d1a00d13df (diff) |
repackage
Diffstat (limited to 'src/sugar/tutorius/overlayer.py')
-rw-r--r-- | src/sugar/tutorius/overlayer.py | 503 |
1 files changed, 0 insertions, 503 deletions
diff --git a/src/sugar/tutorius/overlayer.py b/src/sugar/tutorius/overlayer.py deleted file mode 100644 index 931949d..0000000 --- a/src/sugar/tutorius/overlayer.py +++ /dev/null @@ -1,503 +0,0 @@ -""" -This module manages drawing of overlayed widgets. The class responsible for -drawing management (Overlayer) and basic 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 -from math import pi - -from sugar import profile - -# for easy profile access from cairo -color = profile.get_color().get_stroke_color() -xo_line_color = (int(color[1:3], 16)/255.0, - int(color[3:5], 16)/255.0, - int(color[5:7], 16)/255.0) -color = profile.get_color().get_fill_color() -xo_fill_color = (int(color[1:3], 16)/255.0, - int(color[3:5], 16)/255.0, - int(color[5:7], 16)/255.0) -del color - -# 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) - - # be sure to redraw or the overlay may not show - self.queue_draw() - - - 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()[1:]: - 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=[0,0]): - """ - 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.label = text - self.speaker = speaker - self.tailpos = tailpos - self.line_width = 5 - self.padding = 20 - - self._no_expose = False - self.__exposer = None - - 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 - # - # TODO fetch speaker coordinates - - # draw bubble tail if present - if self.tailpos != [0,0]: - context.move_to(xradius-width/4, yradius) - context.line_to(self.tailpos[0], self.tailpos[1]) - context.line_to(xradius+width/4, yradius) - context.set_line_width(self.line_width) - context.set_source_rgb(*xo_line_color) - context.stroke_preserve() - - # bubble border - context.move_to(width-self.padding, 0.0) - context.line_to(self.padding, 0.0) - context.arc_negative(self.padding, self.padding, self.padding, - 3*pi/2, pi) - context.line_to(0.0, height-self.padding) - context.arc_negative(self.padding, height-self.padding, self.padding, - pi, pi/2) - context.line_to(width-self.padding, height) - context.arc_negative(width-self.padding, height-self.padding, - self.padding, pi/2, 0) - context.line_to(width, self.padding) - context.arc_negative(width-self.padding, self.padding, self.padding, - 0.0, -pi/2) - context.set_line_width(self.line_width) - context.set_source_rgb(*xo_line_color) - context.stroke_preserve() - context.set_source_rgb(*xo_fill_color) - context.fill() - - # bubble painting. Redrawing the inside after the tail will combine - if self.tailpos != [0,0]: - context.move_to(xradius-width/4, yradius) - context.line_to(self.tailpos[0], self.tailpos[1]) - context.line_to(xradius+width/4, yradius) - context.set_line_width(self.line_width) - context.set_source_rgb(*xo_fill_color) - context.fill() - - context.set_source_rgb(1.0, 1.0, 1.0) - pangoctx = pangocairo.CairoContext(context) - self._text_layout.set_markup(self.__label) - text_size = self._text_layout.get_pixel_size() - pangoctx.move_to( - int((self.allocation.width-text_size[0])/2), - int((self.allocation.height-text_size[1])/2)) - pangoctx.show_layout(self._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) - self.window = self.get_parent_window() - if not self._no_expose: - self.__exposer = self.connect_after("expose-event", \ - self.__on_expose) - - 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 = "<b>%s</b>"%value - if not self.parent: - return - ctx = self.parent.window.cairo_create() - pangoctx = pangocairo.CairoContext(ctx) - self._text_layout = pangoctx.create_layout() - self._text_layout.set_markup(value) - del pangoctx, ctx#, surf - - def do_size_request(self, requisition): - """Fill requisition with size occupied by the widget.""" - ctx = self.parent.window.cairo_create() - pangoctx = pangocairo.CairoContext(ctx) - self._text_layout = pangoctx.create_layout() - self._text_layout.set_markup(self.__label) - - width, height = self._text_layout.get_pixel_size() - requisition.width = int(width+2*self.padding) - requisition.height = int(height+2*self.padding) - - 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[3:-4] - - def _set_no_expose(self, value): - """setter for no_expose property""" - self._no_expose = value - if not (self.flags() and gtk.REALIZED): - return - - if self.__exposer and value: - self.parent.disconnect(self.__exposer) - self.__exposer = None - elif (not self.__exposer) and (not value): - self.__exposer = self.parent.connect_after("expose-event", - self.__on_expose) - - def _get_no_expose(self): - """getter for no_expose property""" - return self._no_expose - - 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) - -class Rectangle(gtk.Widget): - """ - A CanvasDrawableWidget drawing a rectangle over a specified widget. - """ - def __init__(self, widget, color): - """ - Creates a new Rectangle - - @param widget the widget to cover - @param color the color of the rectangle, as a 4-tuple RGBA - """ - gtk.Widget.__init__(self) - - self.covered = widget - self.color = color - - 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. - """ - if self.covered is None: - # nothing to hide, no coordinates, no drawing - return - mask_alloc = self.covered.get_allocation() - x, y = self.covered.translate_coordinates(self.parent, 0, 0) - - context.rectangle(x, y, mask_alloc.width, mask_alloc.height) - context.set_source_rgba(*self.color) - context.fill() - - def do_realize(self): - """ Setup gdk window creation. """ - self.set_flags(gtk.REALIZED | gtk.NO_WINDOW) - - self.window = self.get_parent_window() - if not isinstance(self.parent, Overlayer): - assert False, "%s should not realize" % type(self).__name__ - print "Danger, Will Robinson! Rectangle parent is not Overlayer" - - def __on_expose(self, widget, event): - """Redraw event callback.""" - assert False, "%s wasn't meant to be exposed by gtk" % \ - type(self).__name__ - ctx = self.window.cairo_create() - - self.draw_with_context(ctx) - - return True - - def do_size_request(self, requisition): - """Fill requisition with size occupied by the masked widget.""" - # This is a bit pointless, as this will always ignore allocation and - # be rendered directly on overlay, but for sanity, let's put some values - # in there. - if not self.covered: - requisition.width = 0 - requisition.height = 0 - return - - masked_alloc = self.covered.get_allocation() - requisition.width = masked_alloc.width - requisition.height = masked_alloc.height - - def do_size_allocate(self, allocation): - """Save zone allocated to the widget.""" - self.allocation = allocation - - 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.") -gobject.type_register(Rectangle) - -class Mask(gtk.EventBox): - """ - A CanvasDrawableWidget drawing a rectangle over a specified widget. - """ - def __init__(self, catch_events=False, pass_thru=()): - """ - Creates a new Rectangle - - @param catch_events whether the Mask should catch events - @param pass_thru the widgets that "punch holes" through this Mask. - Events will pass through to those widgets. - """ - gtk.EventBox.__init__(self) - self.no_expose = True # ignored - self._catch_events = False - self.catch_events = catch_events - self.pass_thru = list(pass_thru) - - def __del__(self): - for widget in self.pass_thru: - widget.drag_unhighlight() - - def mask(self, widget): - """ - Remove the widget from the unmasked list. - @param widget a widget to remask - """ - assert widget in self.pass_thru, \ - "trying to mask already masked widget" - self.pass_thru.remove(widget) - widget.drag_unhighlight() - - def unmask(self, widget): - """ - Add to the unmasked list the widget passed. - A hole will be punched through the mask at that widget's position. - @param widget a widget to unmask - """ - if widget not in self.pass_thru: - widget.drag_highlight() - self.pass_thru.append(widget) - - - def set_catch_events(self, do_catch): - """Sets whether the mask catches events of widgets under it""" - if bool(self._catch_events) ^ bool(do_catch): - if do_catch: - self._catch_events = True - self.grab_add() - else: - self.grab_remove() - self._catch_events = False - - def get_catch_events(self): - """Gets whether the mask catches events of widgets under it""" - return bool(self._catch_handle) - - catch_events = property(fset=set_catch_events, fget=get_catch_events) - - 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. - """ - # Fill parent container - mask_alloc = self.parent.get_allocation() - oldrule = context.get_fill_rule() - context.set_fill_rule(cairo.FILL_RULE_EVEN_ODD) - x, y = self.translate_coordinates(self.parent, 0, 0) - - context.rectangle(x, y, mask_alloc.width, mask_alloc.height) - for hole in self.pass_thru: - alloc = hole.get_allocation() - x, y = hole.translate_coordinates(self.parent, 0, 0) - context.rectangle(x, y, alloc.width, alloc.height) - context.set_source_rgba(0, 0, 0, 0.7) - context.fill() - context.set_fill_rule(oldrule) - - def do_size_request(self, requisition): - """Fill requisition with size occupied by the masked widget.""" - # This is required for the event box to span across all the parent. - alloc = self.parent.get_allocation() - requisition.width = alloc.width - requisition.height = alloc.height - - def do_size_allocate(self, allocation): - """Save zone allocated to the widget.""" - self.allocation = allocation - -gobject.type_register(Mask) - - -# vim:set ts=4 sts=4 sw=4 et: |