[Sugar-devel] [PATCH TypingTurtle 1/2] Port to Cairo
Manuel Kaufmann
humitos at gmail.com
Thu Aug 2 09:45:15 EDT 2012
This will ease the port to GTK+ 3
Signed-off-by: Manuel Kaufmann <humitos at gmail.com>
---
balloongame.py | 151 ++++++++++++++++++++++++++++------------------
keyboard.py | 187 +++++++++++++++++++++++++++------------------------------
titlescene.py | 44 ++++++++------
3 files changed, 206 insertions(+), 176 deletions(-)
diff --git a/balloongame.py b/balloongame.py
index 56a6a34..7224893 100644
--- a/balloongame.py
+++ b/balloongame.py
@@ -14,7 +14,10 @@
# You should have received a copy of the GNU General Public License
# along with Typing Turtle. If not, see <http://www.gnu.org/licenses/>.
+import math
import random, datetime
+import pangocairo
+
from gettext import gettext as _
import gobject, pygtk, gtk, pango
@@ -192,28 +195,35 @@ class BalloonGame(gtk.VBox):
return True
- def draw_results(self, gc):
+ def draw_results(self, cr):
# Draw background.
w = self.bounds.width - 400
h = self.bounds.height - 200
x = self.bounds.width/2 - w/2
y = self.bounds.height/2 - h/2
- gc.foreground = self.area.get_colormap().alloc_color(50000,50000,50000)
- self.area.window.draw_rectangle(gc, True, x, y, w, h)
- gc.foreground = self.area.get_colormap().alloc_color(0,0,0)
- self.area.window.draw_rectangle(gc, False, x, y, w, h)
+ cr.set_source_rgb(0.762, 0.762, 0.762)
+ cr.rectangle(x, y, w, h)
+ cr.fill()
- # Draw text
- gc.foreground = self.area.get_colormap().alloc_color(0,0,0)
+ cr.set_source_rgb(0, 0, 0)
+ cr.rectangle(x, y, w, h)
+ cr.stroke()
+ # Draw text
title = _('You finished!') + '\n'
- layout = self.area.create_pango_layout(title)
- layout.set_font_description(pango.FontDescription('Serif Bold 16'))
- size = layout.get_size()
- tx = x+w/2-(size[0]/pango.SCALE)/2
+
+ pango_cr = pangocairo.CairoContext(cr)
+ pango_cr.set_source_rgb(0, 0, 0)
+ pango_layout = cr.create_layout()
+ pango_layout.set_font_description(pango.FontDescription('Serif Bold 16'))
+ pango_layout.set_text(title)
+ size = pango_layout.get_size()
+ tx = x + (w / 2) - (size[0] / pango.SCALE) / 2
ty = y + 100
- self.area.window.draw_layout(gc, tx, ty, layout)
+ pango_cr.move_to(tx, ty)
+ pango_cr.show_layout(pango_layout)
+ pango_cr.stroke()
report = ''
report += _('Your score was %(score)d.') % { 'score': self.score } + '\n'
@@ -222,12 +232,18 @@ class BalloonGame(gtk.VBox):
report += '\n'
report += _('Press the ENTER key to continue.')
- layout = self.area.create_pango_layout(report)
- layout.set_font_description(pango.FontDescription('Times 12'))
- size = layout.get_size()
- tx = x+w/2-(size[0]/pango.SCALE)/2
- ty = y + 200
- self.area.window.draw_layout(gc, tx, ty, layout)
+ pango_cr = pangocairo.CairoContext(cr)
+ pango_cr.set_source_rgb(0, 0, 0)
+ pango_layout = cr.create_layout()
+ pango_layout.set_font_description(pango.FontDescription('Times 12'))
+ pango_layout.set_text(report)
+ size = pango_layout.get_size()
+ sx = x + w / 2 - (size[0] / pango.SCALE) / 2
+ sy = y + 200
+ pango_cr.move_to(sx, sy)
+ pango_cr.show_layout(pango_layout)
+ pango_cr.stroke()
+
def finish_game(self):
self.finished = True
@@ -290,29 +306,35 @@ class BalloonGame(gtk.VBox):
h = int(b.size*1.5 + 10)
self.area.queue_draw_area(x, y, w, h)
- def draw_balloon(self, gc, b):
+ def draw_balloon(self, cr, b):
x = int(b.x)
y = int(b.y)
-
+
# Draw the string.
- gc.foreground = self.area.get_colormap().alloc_color(0,0,0)
- self.area.window.draw_line(gc,
- int(b.x), int(b.y+b.size/2),
- int(b.x), int(b.y+b.size))
-
+ cr.set_source_rgb(0, 0, 0)
+ cr.move_to(int(b.x), int(b.y + b.size / 2))
+ cr.line_to(int(b.x), int(b.y + b.size))
+ cr.stroke()
+
# Draw the balloon.
- gc.foreground = self.area.get_colormap().alloc_color(b.color[0],b.color[1],b.color[2])
- self.area.window.draw_arc(gc, True, x-b.size/2, y-b.size/2, b.size, b.size, 0, 360*64)
-
- # Draw the text.
- gc.foreground = self.area.get_colormap().alloc_color(0,0,0)
- layout = self.area.create_pango_layout(b.word)
- layout.set_font_description(pango.FontDescription('Sans 12'))
- size = layout.get_size()
- tx = x-(size[0]/pango.SCALE)/2
- ty = y-(size[1]/pango.SCALE)/2
- self.area.window.draw_layout(gc, tx, ty, layout)
-
+ cr.save()
+ cr.set_source_rgb(b.color[0], b.color[1], b.color[2])
+ cr.arc(b.x, b.y, b.size / 2, 0, 2 * math.pi)
+ cr.fill()
+ cr.restore()
+
+ pango_cr = pangocairo.CairoContext(cr)
+ pango_cr.set_source_rgb(0, 0, 0)
+ pango_layout = cr.create_layout()
+ pango_layout.set_font_description(pango.FontDescription('Sans 12'))
+ pango_layout.set_text(unicode(b.word))
+ size = pango_layout.get_size()
+ x = x - (size[0] / pango.SCALE) / 2
+ y = y - (size[1] / pango.SCALE) / 2
+ pango_cr.move_to(x, y)
+ pango_cr.show_layout(pango_layout)
+ pango_cr.stroke()
+
def add_score(self, num):
self.score += num
self.queue_draw_score()
@@ -325,45 +347,54 @@ class BalloonGame(gtk.VBox):
y = 20
self.queue_draw_area(x, y, x+size[0], y+size[1])
- def draw_score(self, gc):
- layout = self.area.create_pango_layout(_('SCORE: %d') % self.score)
- layout.set_font_description(pango.FontDescription('Times 14'))
- size = layout.get_size()
- x = self.bounds.width-20-size[0]/pango.SCALE
+ def draw_score(self, cr):
+ pango_cr = pangocairo.CairoContext(cr)
+ pango_cr.set_source_rgb(0, 0, 0)
+ pango_layout = cr.create_layout()
+ pango_layout.set_font_description(pango.FontDescription('Times 14'))
+ pango_layout.set_text(_('SCORE: %d') % self.score)
+ size = pango_layout.get_size()
+ x = self.bounds.width - 20 - size[0] / pango.SCALE
y = 20
- self.area.window.draw_layout(gc, x, y, layout)
+ pango_cr.move_to(x, y)
+ pango_cr.show_layout(pango_layout)
+ pango_cr.stroke()
- def draw_instructions(self, gc):
+ def draw_instructions(self, cr):
# Draw instructions.
- gc.foreground = self.area.get_colormap().alloc_color(0,0,0)
-
- layout = self.area.create_pango_layout(_('Type the words to pop the balloons!'))
- layout.set_font_description(pango.FontDescription('Times 14'))
- size = layout.get_size()
- x = (self.bounds.width - size[0]/pango.SCALE)/2
- y = self.bounds.height-20 - size[1]/pango.SCALE
- self.area.window.draw_layout(gc, x, y, layout)
+ pango_cr = pangocairo.CairoContext(cr)
+ pango_cr.set_source_rgb(0, 0, 0)
+ pango_layout = cr.create_layout()
+ pango_layout.set_font_description(pango.FontDescription('Times 14'))
+ pango_layout.set_text(_('Type the words to pop the balloons!'))
+ size = pango_layout.get_size()
+ x = (self.bounds.width - size[0] / pango.SCALE) / 2
+ y = self.bounds.height - 20 - size[1] / pango.SCALE
+ pango_cr.move_to(x, y)
+ pango_cr.show_layout(pango_layout)
+ pango_cr.stroke()
def draw(self):
self.bounds = self.area.get_allocation()
- gc = self.area.window.new_gc()
-
+ cr = self.area.window.cairo_create()
+
# Draw background.
- gc.foreground = self.area.get_colormap().alloc_color(60000,60000,65535)
- self.area.window.draw_rectangle(gc, True, 0, 0, self.bounds.width, self.bounds.height)
+ cr.set_source_rgb(0.915, 0.915, 1)
+ cr.rectangle(0, 0, self.bounds.width, self.bounds.height)
+ cr.fill()
# Draw the balloons.
for b in self.balloons:
- self.draw_balloon(gc, b)
+ self.draw_balloon(cr, b)
if self.finished:
- self.draw_results(gc)
+ self.draw_results(cr)
else:
- self.draw_instructions(gc)
+ self.draw_instructions(cr)
- self.draw_score(gc)
+ self.draw_score(cr)
def expose_cb(self, area, event):
self.draw()
diff --git a/keyboard.py b/keyboard.py
index 35daeed..25e870e 100644
--- a/keyboard.py
+++ b/keyboard.py
@@ -16,12 +16,14 @@
#!/usr/bin/env python
# vi:sw=4 et
-import pygtk
-pygtk.require('2.0')
import gtk
+import cairo
+import copy
import rsvg
import os, glob, re
import pango
+import pangocairo
+import StringIO
from port import json
import subprocess
from layouts.olpc import OLPC_LAYOUT
@@ -130,8 +132,7 @@ class KeyboardImages:
scale_width = int(scale_width * 1.1625)
for filename in glob.iglob('images/OLPC*.svg'):
- image = gtk.gdk.pixbuf_new_from_file_at_scale(filename, scale_width,
- self.height, False)
+ image = rsvg.Handle(file=filename)
name = os.path.basename(filename)
self.images[name] = image
@@ -383,30 +384,27 @@ class KeyboardWidget(KeyboardData, gtk.DrawingArea):
k['key-width'] = int(k['key-width'] * width_scale)
k['key-height'] = int(k['key-height'] * height_scale)
- self._make_all_key_images()
-
- def _make_key_images(self, key):
- key['key-images'] = {}
- for group in [0, 1]:
- for state in [0, gtk.gdk.SHIFT_MASK, gtk.gdk.MOD5_MASK, gtk.gdk.SHIFT_MASK|gtk.gdk.MOD5_MASK]:
- key['key-images'][(state, group)] = self.get_key_image(key, state, group)
+ def _draw_key(self, k, cr):
+ bounds = self.get_allocation()
- def _make_all_key_images(self):
- for key in self.keys:
- self._make_key_images(key)
+ # HACK: this is a hack used when the widget is not shown yet,
+ # in that case bounds will be gtk.gdk.Rectangle(-1, -1, 1, 1)
+ # and the key will be outside the canvas. This is used only
+ # for the first key that appears below the instructions
+ if bounds.x == -1:
+ screen_x = screen_y = 0
+ else:
+ screen_x = int(bounds.width - self.image.width) / 2
+ screen_y = int(bounds.height - self.image.height) / 2
- def _draw_key(self, k, draw, gc, for_pixmap, w=0, h=0):
- x1 = 0
- y1 = 0
- x2 = w
- y2 = h
+ x1 = k['key-x'] + screen_x
+ y1 = k['key-y'] + screen_y
+ x2 = x1 + k['key-width']
+ y2 = y1 + k['key-height']
- # Outline rounded box.
- gc.foreground = self.get_colormap().alloc_color(int(0.4*65536),int(0.7*65536),int(0.4*65536))
-
corner = 5
points = [
- (x1 + corner, y1),
+ (x1 + corner, y1),
(x2 - corner, y1),
(x2, y1 + corner),
(x2, y2 - corner),
@@ -414,26 +412,40 @@ class KeyboardWidget(KeyboardData, gtk.DrawingArea):
(x1 + corner, y2),
(x1, y2 - corner),
(x1, y1 + corner)
- ]
- draw.draw_polygon(gc, True, points)
-
- # Inner text.
- gc.foreground = self.get_colormap().alloc_color(int(1.0*65536),int(1.0*65536),int(1.0*65536))
+ ]
+
+ cr.save()
+ cr.new_path()
+ cr.set_source_rgb(0.396, 0.698, 0.392)
+ cr.set_line_width(2)
+ cr.move_to(*points[0])
+ for point in points:
+ cr.line_to(*point)
+ cr.line_to(*points[0])
+ cr.close_path()
+ cr.fill_preserve()
+ cr.stroke()
+ cr.restore()
text = ''
if k['key-label']:
text = k['key-label']
else:
- text = self.get_letter_for_key_state_group(k, self.active_state, self.active_group)
-
- try:
- layout = self.create_pango_layout(unicode(text))
- layout.set_font_description(pango.FontDescription('Monospace'))
- draw.draw_layout(gc, x1+8, y2-23, layout)
- except:
- pass
-
- def _expose_hands(self, gc):
+ text = self.get_letter_for_key_state_group(
+ k, self.active_state, self.active_group)
+
+ pango_context = pangocairo.CairoContext(cr)
+ pango_context.set_source_rgb(0, 0, 0)
+
+ pango_layout = pango_context.create_layout()
+ pango_layout.set_font_description(pango.FontDescription('Monospace'))
+ pango_layout.set_text(unicode(text))
+
+ pango_context.move_to(x1 + 8, y2 - 23)
+ pango_context.show_layout(pango_layout)
+ cr.stroke()
+
+ def _expose_hands(self, cr):
lhand_image = self.image.images['OLPC_Lhand_HOMEROW.svg']
rhand_image = self.image.images['OLPC_Rhand_HOMEROW.svg']
@@ -459,39 +471,32 @@ class KeyboardWidget(KeyboardData, gtk.DrawingArea):
# TODO: Do something about ALTGR.
- bounds = self.get_allocation()
- screen_x = int(bounds.width-self.image.width)/2
- screen_y = int(bounds.height-self.image.height)/2
+ # bounds = self.get_allocation()
+ # screen_x = int(bounds.width-self.image.width)/2
+ # screen_y = int(bounds.height-self.image.height)/2
+
+ # README: these values (cairo.Matrix) are taken seeing the image on the
+ # screen, I think we should find a way to calculate them
+ cr.save()
+ matrix = cairo.Matrix(xx=0.3, yy=0.2, x0=10, y0=-20)
+ cr.transform(matrix)
+ lhand_image.render_cairo(cr)
- self.window.draw_pixbuf(gc, lhand_image, 0, 0, screen_x, screen_y + HAND_YOFFSET)
- self.window.draw_pixbuf(gc, rhand_image, 0, 0, screen_x, screen_y + HAND_YOFFSET)
+ cr.restore()
+ matrix = cairo.Matrix(xx=0.325, yy=0.2, x0=-5, y0=-20)
+ cr.transform(matrix)
+ rhand_image.render_cairo(cr)
def _expose_cb(self, area, event):
- gc = self.window.new_gc()
-
- bounds = self.get_allocation()
- screen_x = int(bounds.width-self.image.width)/2
- screen_y = int(bounds.height-self.image.height)/2
+ cr = self.window.cairo_create()
# Draw the keys.
for k in self.keys:
- x1 = k['key-x'] + screen_x
- y1 = k['key-y'] + screen_y
- x2 = x1 + k['key-width']
- y2 = y1 + k['key-height']
-
- # Index cached key images by state and group.
- state = self.active_state & (gtk.gdk.SHIFT_MASK|gtk.gdk.MOD5_MASK)
- index = (state, self.active_group)
- image = k['key-images'].get(index)
-
- if image:
- self.window.draw_image(gc, image, 0, 0, x1, y1, x2-x1, y2-y1)
-
+ self._draw_key(k, cr)
+
# Draw overlay images.
if self.draw_hands:
- self._expose_hands(gc)
-
+ self._expose_hands(cr)
return True
def key_press_release_cb(self, widget, event):
@@ -512,14 +517,10 @@ class KeyboardWidget(KeyboardData, gtk.DrawingArea):
sig = self.format_key_sig(event.hardware_keycode, event.state, event.group)
if not self.letter_map.has_key(sig):
self.letter_map[sig] = event.string
- self._make_key_images(key)
self.queue_draw()
return False
- def _keys_changed_cb(self, keymap):
- self._make_key_images()
-
def clear_hilite(self):
self.hilite_letter = None
self.queue_draw()
@@ -535,43 +536,31 @@ class KeyboardWidget(KeyboardData, gtk.DrawingArea):
def get_key_pixbuf(self, key, state=0, group=0, scale=1):
w = int(key['key-width'] * scale)
h = int(key['key-height'] * scale)
-
+
old_state, old_group = self.active_state, self.active_group
self.active_state, self.active_group = state, group
-
- pixmap = gtk.gdk.Pixmap(self.root_window.window, w, h)
- gc = pixmap.new_gc()
-
- gc.foreground = self.get_colormap().alloc_color('#d0d0d0')
- pixmap.draw_rectangle(gc, True, 0, 0, w, h)
- self._draw_key(key, pixmap, gc, True, w, h)
-
- pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, w, h)
- pb.get_from_drawable(pixmap, self.root_window.window.get_colormap(), 0, 0, 0, 0,w, h)
-
- self.active_state, self.active_group = old_state, old_group
+ surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
+ cr = cairo.Context(surface)
+ cr.set_source_rgb(1, 1, 1)
+ cr.rectangle(0, 0, w, h)
+ cr.fill()
- return pb
+ # Duplicate the Key to be able to change its position values
+ key = copy.deepcopy(key)
+ key['key-x'] = 0
+ key['key-y'] = 0
- def get_key_image(self, key, state=0, group=0, scale=1):
- w = int(key['key-width'] * scale)
- h = int(key['key-height'] * scale)
-
- old_state, old_group = self.active_state, self.active_group
- self.active_state, self.active_group = state, group
-
- pixmap = gtk.gdk.Pixmap(self.root_window.window, w, h)
- gc = pixmap.new_gc()
-
- gc.foreground = self.get_colormap().alloc_color('#d0d0d0')
- pixmap.draw_rectangle(gc, True, 0, 0, w, h)
+ self._draw_key(key, cr)
+
+ # Convert cairo.Surface to Pixbuf
+ pixbuf_data = StringIO.StringIO()
+ surface.write_to_png(pixbuf_data)
+ pxb_loader = gtk.gdk.PixbufLoader(image_type='png')
+ pxb_loader.write(pixbuf_data.getvalue())
+ temp_pix = pxb_loader.get_pixbuf()
+ pxb_loader.close()
- self._draw_key(key, pixmap, gc, True, w, h)
-
- image = pixmap.get_image(0, 0, w, h)
-
self.active_state, self.active_group = old_state, old_group
- return image
-
+ return temp_pix
diff --git a/titlescene.py b/titlescene.py
index 7cc2d68..4dc6ae4 100644
--- a/titlescene.py
+++ b/titlescene.py
@@ -20,13 +20,15 @@ from gettext import gettext as _
# Import PyGTK.
import gobject, pygtk, gtk, pango
+import pangocairo
+
class TitleScene(gtk.DrawingArea):
# Maximum portion of the screen the background can fill vertically.
BACKGROUND_HEIGHT_RATIO = 0.6
# Border from top right of screen to draw title at.
- TITLE_OFFSET = (20, 30)
+ TITLE_OFFSET = (20, 50)
# Font used to display the title.
TITLE_FONT = 'Times 45'
@@ -52,30 +54,38 @@ class TitleScene(gtk.DrawingArea):
def expose_cb(self, area, event):
bounds = self.get_allocation()
-
- gc = self.get_style().fg_gc[gtk.STATE_NORMAL]
+
+ cr = self.window.cairo_create()
# Background picture.
x = (bounds.width - self.backgroundpixbuf.get_width())/2
- self.window.draw_pixbuf(
- gc, self.backgroundpixbuf, 0, 0,
- x, 0, self.backgroundpixbuf.get_width(), self.backgroundpixbuf.get_height())
- pc = self.create_pango_context()
-
- self.layout = self.create_pango_layout('')
- self.layout.set_font_description(pango.FontDescription(TitleScene.TITLE_FONT))
-
- self.layout.set_text(self.title_original)
- original_size = self.layout.get_size()
- self.x_text = (bounds.width-original_size[0]/pango.SCALE)-TitleScene.TITLE_OFFSET[0]
+ cr.set_source_pixbuf(self.backgroundpixbuf, 0, 0)
+ cr.rectangle(x, 0, self.backgroundpixbuf.get_width(),
+ self.backgroundpixbuf.get_height())
+ cr.paint()
+
+ cr = pangocairo.CairoContext(cr)
+ cr.set_source_rgb(0, 0, 0)
+ self.pango_layout = cr.create_layout()
+ self.pango_layout.set_font_description(
+ pango.FontDescription(TitleScene.TITLE_FONT))
+ self.pango_layout.set_text(unicode(self.title_original))
+
+ original_size = self.pango_layout.get_size()
+ self.x_text = (bounds.width - original_size[0] / pango.SCALE) - \
+ TitleScene.TITLE_OFFSET[0]
self.y_text = TitleScene.TITLE_OFFSET[1]
+
gobject.timeout_add(50, self.timer_cb)
def draw_text(self):
# Animated Typing Turtle title.
- gc = self.get_style().fg_gc[gtk.STATE_NORMAL]
- self.layout.set_text(self.title_text)
- self.window.draw_layout(gc, self.x_text, self.y_text, self.layout)
+ cr = self.window.cairo_create()
+
+ cr.move_to(self.x_text, self.y_text)
+ self.pango_layout.set_text(unicode(self.title_text))
+ cr.show_layout(self.pango_layout)
+ cr.stroke()
def timer_cb(self):
if len(self.title_src) > 0:
--
1.7.11.2
More information about the Sugar-devel
mailing list