Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorius/overlayer.py
diff options
context:
space:
mode:
Diffstat (limited to 'tutorius/overlayer.py')
-rw-r--r--tutorius/overlayer.py196
1 files changed, 194 insertions, 2 deletions
diff --git a/tutorius/overlayer.py b/tutorius/overlayer.py
index b967739..9e4adbf 100644
--- a/tutorius/overlayer.py
+++ b/tutorius/overlayer.py
@@ -149,8 +149,8 @@ class Overlayer(gtk.Layout):
# 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)
-
+ if self._overlayed:
+ self._overlayed.set_size_request(allocation.width, allocation.height)
class TextBubble(gtk.Widget):
"""
@@ -319,6 +319,198 @@ class TextBubble(gtk.Widget):
gobject.type_register(TextBubble)
+class TextBubbleWImg(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), imagepath=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.label = text
+ self.speaker = speaker
+ self.tailpos = tailpos
+ self.line_width = 5
+ self.padding = 20
+
+ # image painting
+ self.filename = imagepath
+ self.pixbuf = gtk.gdk.pixbuf_new_from_file(self.filename)
+ self.imgsize = (self.pixbuf.get_width(), self.pixbuf.get_height())
+
+ 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()
+
+ # draw/write text
+ 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.line_width+self.imgsize[1]+self.padding/2))
+ pangoctx.show_layout(self._text_layout)
+
+ # create a new cairo surface to place the image on
+ #surface = cairo.ImageSurface(0,x,y)
+ # create a context to the new surface
+ #ct = cairo.Context(surface)
+
+ # paint image
+ context.set_source_pixbuf(
+ self.pixbuf,
+ int((self.allocation.width-self.imgsize[0])/2),
+ int(self.line_width+self.padding/2))
+
+ context.paint()
+
+ # 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()
+
+ max_width = width
+ if self.imgsize[0] > width:
+ max_width = self.imgsize[0]
+ requisition.width = int(2*self.padding+max_width)
+
+ requisition.height = int(2*self.padding+height+self.imgsize[1])
+
+ 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(TextBubbleWImg)
+
+
class Rectangle(gtk.Widget):
"""
A CanvasDrawableWidget drawing a rectangle over a specified widget.