Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/sugar/activity/activity.py3
-rw-r--r--src/sugar/tutorius/actions.py5
-rw-r--r--src/sugar/tutorius/gtkutils.py6
-rw-r--r--src/sugar/tutorius/overlayer.py182
4 files changed, 92 insertions, 104 deletions
diff --git a/src/sugar/activity/activity.py b/src/sugar/activity/activity.py
index c5dca45..0ad1d91 100644
--- a/src/sugar/activity/activity.py
+++ b/src/sugar/activity/activity.py
@@ -76,6 +76,7 @@ from sugar.graphics.xocolor import XoColor
from sugar.datastore import datastore
from sugar.session import XSMPClient
from sugar import wm
+from sugar.tutorius.services import ObjectStore
_ = lambda msg: gettext.dgettext('sugar-toolkit', msg)
@@ -508,6 +509,8 @@ class Activity(Window, gtk.Container):
"""
Window.__init__(self)
+ ObjectStore().activity = self
+
# process titles will only show 15 characters
# but they get truncated anyway so if more characters
# are supported in the future we will get a better view
diff --git a/src/sugar/tutorius/actions.py b/src/sugar/tutorius/actions.py
index 7ecf86b..915b11b 100644
--- a/src/sugar/tutorius/actions.py
+++ b/src/sugar/tutorius/actions.py
@@ -19,6 +19,7 @@ This module defines Actions that can be done and undone on a state
from sugar.tutorius import gtkutils
from dialog import TutoriusDialog
+from sugar.tutorius.services import ObjectStore
import overlayer
@@ -125,11 +126,11 @@ class BubbleMessage(Action):
Show the dialog
"""
# get or inject overlayer
- self.overlay = gtkutils.activity()._overlayer #FIXME:handle subwin
+ self.overlay = ObjectStore().activity._overlayer #FIXME:handle subwin
if not self._bubble:
x, y = self.position
- self._bubble = overlayer.TextBubble(text=self._message, tailpos=self._tailpos) #TODO: add tail
+ self._bubble = overlayer.TextBubble(text=self._message, tailpos=self._tailpos) #TODO: add tail handling
self._bubble.show()
self.overlay.put(self._bubble, x, y)
self.overlay.queue_draw()
diff --git a/src/sugar/tutorius/gtkutils.py b/src/sugar/tutorius/gtkutils.py
index 450872d..073a7f3 100644
--- a/src/sugar/tutorius/gtkutils.py
+++ b/src/sugar/tutorius/gtkutils.py
@@ -18,12 +18,6 @@
Utility classes and functions that are gtk related
"""
-def activity(activity=None, singleton=[]):
- if activity:
- singleton.append(activity)
- return singleton[0]
-
-
def find_widget(base, target_fqdn):
"""Find a widget by digging into a parent widget's children tree
@param base the parent widget
diff --git a/src/sugar/tutorius/overlayer.py b/src/sugar/tutorius/overlayer.py
index 0da1841..23a203b 100644
--- a/src/sugar/tutorius/overlayer.py
+++ b/src/sugar/tutorius/overlayer.py
@@ -23,11 +23,15 @@ import gtk
import cairo
import pangocairo
-class CanvasDrawable(object):
- """Defines the CanvasDrawable protocol"""
- should_expose = None
- def drawWithContext(self, context): pass
-
+#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):
"""
@@ -41,57 +45,27 @@ class Overlayer(gtk.Layout):
# no overlayed child yet
self.__overlayed = None
-## win.connect("expose-event", self.__transparent_overlay)
self.__realizer = self.connect("expose-event", self.__init_realized)
self.connect("size-allocate", self.__size_allocate)
self.show()
-## win.show_all()
-
-## win.connect_after("expose-event", self.__expose_overlay)
-
- def reparent(self, parent):
- """Reparent this widget as child of parent instead."""
- self.__realizer = self.connect("expose-event", self.__init_realized)
- gtk.Layout.reparent(self, parent)
-
- def inject(self, child):
- """
- Insert this widget at "child" level so that it can overlay "child".
- """
- assert not self.__overlayed, "trying to reinject while already injected"
- parent = child.parent
- self.__overlayed = child
- child.reparent(self)
- parent.add(self)
- self.__realizer = parent.connect("expose-event", self.__init_realized)
+ 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, "should_expose"):
+ if hasattr(child, "no_expose"):
# if the widget has the CanvasDrawable protocol, use it.
- # protocol is defined as
- # should_expose property with setter to flag as not drawn using
- # expose-events
- # drawWithContext(context) method to pass a context to draw with.
- child.should_expose = True
+ child.no_expose = True
gtk.Layout.put(self, child, x, y)
- def eject(self):
- """
- Remove this widget from the widget hierarchy without affecting its
- children.
- """
- parent = self.parent
- parent.disconnect(self.__render_handle)
- assert hasattr(parent, "remove"), "parent should be a container"
- self.parent.remove(self)
- self.__overlayed.reparent(parent)
- self.__overlayed = None
def __init_realized(self, widget, event):
"""
@@ -134,11 +108,11 @@ class Overlayer(gtk.Layout):
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()
+ 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
@@ -146,12 +120,15 @@ class Overlayer(gtk.Layout):
#draw overlay
for drawn_child in self.get_children():
- if hasattr(drawn_child, "drawWithContext"):
- drawn_child.drawWithContext(ctx)
+ 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"""
+ """
+ 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
@@ -161,26 +138,35 @@ class Overlayer(gtk.Layout):
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 associate with the bubble tail
- @param tailpos an optional position for the tail if no speaker
+ @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)
##self.set_app_paintable(True) # else may be blank
# FIXME ensure previous call does not interfere with widget stacking
+ self.__label = None
+ self.__text_dimentions = None
+ self.__exposer = None # refer to expose event handler
+
self.label = text
self.speaker = speaker
self.tailpos = tailpos
- self.lineWidth = 5
+ self.line_width = 5
self.connect("expose-event", self.__on_expose)
- def drawWithContext(self, context):
+ 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
@@ -191,46 +177,58 @@ class TextBubble(gtk.Widget):
height = self.allocation.height
xradius = width/2
yradius = height/2
- width -= self.lineWidth
- height -= self.lineWidth
+ 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:
-## speaker_alloc = self.speaker.get_allocation()
context.move_to(xradius-40, yradius)
context.line_to(self.tailpos[0], self.tailpos[1])
-## context.line_to(speaker_alloc.x-self.allocation.x,
-## speaker_alloc.y - self.allocation.y)
context.line_to(xradius+40, yradius)
- context.set_source_rgb(1.0, 1.0, 0.0)
- context.fill_preserve()
- context.set_line_width(self.lineWidth)
+ context.set_line_width(self.line_width)
context.set_source_rgb(0.0, 0.0, 0.0)
- context.stroke()
-
- # bubble
- context.move_to(self.lineWidth, yradius)
- context.curve_to(self.lineWidth, self.lineWidth,
- self.lineWidth, self.lineWidth, xradius, self.lineWidth)
- context.curve_to(width, self.lineWidth,
- width, self.lineWidth, width, yradius)
+ 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.lineWidth, height,
- self.lineWidth, height, self.lineWidth, yradius)
+ 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_preserve()
- context.set_line_width(self.lineWidth)
- context.set_source_rgb(0.0, 0.0, 0.0)
- context.stroke()
+ context.fill()
# text
# FIXME create layout 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),
+ 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)
@@ -238,36 +236,24 @@ class TextBubble(gtk.Widget):
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()
def __on_expose(self, widget, event):
"""Redraw event callback."""
- # TODO
+ # TODO: handle gtk window management in case there is no overlay
ctx = self.window.cairo_create()
- # set drawing region. Useless since this widget has its own window.
-## region = gtk.gdk.region_rectangle(self.allocation)
-## region.intersect(gtk.gdk.region_rectangle(event.area))
-## ctx.region(region)
-## ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
-## ctx.clip()
-
- ##import pdb; pdb.set_trace()
- ##ctx.set_operator(cairo.OPERATOR_CLEAR)
- ##ctx.paint()
- ##ctx.set_operator(cairo.OPERATOR_OVER)
-
- self.drawWithContext(ctx)
+ self.draw_with_context(ctx)
return True
def _set_label(self, value):
"""Sets the label and flags the widget to be redrawn."""
- # yes we use the setter
self.__label = value
# FIXME hack to calculate size. necessary because may not have been
- # realized
+ # realized. We create a fake surface to use builtin math.
surf = cairo.SVGSurface("/dev/null", 0, 0)
ctx = cairo.Context(surf)
pangoctx = pangocairo.CairoContext(ctx)
@@ -277,13 +263,16 @@ class TextBubble(gtk.Widget):
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
+ # 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):
@@ -297,9 +286,10 @@ class TextBubble(gtk.Widget):
elif not (self.__exposer or value):
self.__exposer = self.connect(self.__on_expose)
- should_expose = property(fset=_set_will_expose)
+ no_expose = property(fset=_set_will_expose,
+ doc="Whether the widget should handle exposition events or not.")
- label = property(fget=_get_label, fset=_set_label,\
+ 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)