Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/sugar/tutorius/overlayer.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/sugar/tutorius/overlayer.py')
-rw-r--r--src/sugar/tutorius/overlayer.py503
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: