Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.project17
-rw-r--r--.pydevproject9
-rw-r--r--MANIFEST3
-rw-r--r--TODO12
-rw-r--r--activity/activity.info4
-rw-r--r--keyboard.py3
-rw-r--r--po/TypingTurtle.pot (renamed from po/TypingTurtleApp.pot)26
-rwxr-xr-xtypingturtle.py241
8 files changed, 198 insertions, 117 deletions
diff --git a/.project b/.project
deleted file mode 100644
index 216fd33..0000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>typingTurtle</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.python.pydev.PyDevBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.python.pydev.pythonNature</nature>
- </natures>
-</projectDescription>
diff --git a/.pydevproject b/.pydevproject
deleted file mode 100644
index 9711a53..0000000
--- a/.pydevproject
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse-pydev version="1.0"?>
-
-<pydev_project>
-<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
-<path>/typingTurtle/src</path>
-</pydev_pathproperty>
-<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.4</pydev_property>
-</pydev_project>
diff --git a/MANIFEST b/MANIFEST
index 971b69d..e01a7cd 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -12,4 +12,5 @@ images/bronze-medal.jpg
images/silver-medal.jpg
lessons/en_US/homerow_intro.lesson
lessons/en_US/dummy.lesson
-po/TypingTurtleApp.pot
+lessons/en_US/long.lesson
+po/TypingTurtle.pot
diff --git a/TODO b/TODO
index c9482eb..aa8e894 100644
--- a/TODO
+++ b/TODO
@@ -1,13 +1,21 @@
Typing Turtle
First Release
-+ Write to Loser, Wes about developing artwork.
+- Write to Loser, Wes about developing artwork.
++ WPM meter updated in 1sec timer.
+- Draw incorrect characters in red.
+- Support backspace, backspace to previous lines.
++ Handle ends of line in a sane manner.
++ Implement two step types: key learning and text copying.
+- Split text into lines for long lessons.
+- Try out an insensitive gtk.Entry instead of the gtk.Label.
+ Write to authors of prior typing activity to inquire about layered artwork. Need to make a .xo file to demo.
+ Better flow at the end of a level. Report the result on the Lesson screen: Need more work, Medal received, etc.
+ Status message on the main screen. "You unlocked a new lesson!" for example. Eventually have the turtle 'say' it.
+ Scroll lessons list to the first non-medaled lesson?
+ Implement a long text copying lesson and fix bugs in the scrolling and typing.
-+ Ability of lessons to list bronze medals in other lessons as prerequisites.
++ Some sort of lesson sorting criteria.
++ Ability of lessons to list medals in other lessons as prerequisites.
+ Graphical WPM and accuracy meters.
- Working medals assignment: "You got a medal!" popup, display next to lesson.
- Nice looking keyboard.
diff --git a/activity/activity.info b/activity/activity.info
index 41a3f3d..aadcd0a 100644
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -1,7 +1,7 @@
[Activity]
-name = TypingTurtleApp
+name = Typing Turtle
activity_version = 1
host_version = 1
-service_name = org.laptop.community.TypingTurtleApp
+service_name = org.laptop.community.TypingTurtle
icon = Colors-activity
exec = sugar-activity typingturtle.TypingTurtle
diff --git a/keyboard.py b/keyboard.py
index 5683a30..5dc92c3 100644
--- a/keyboard.py
+++ b/keyboard.py
@@ -423,9 +423,6 @@ class Keyboard(gtk.EventBox):
return True
def _key_press_cb(self, widget, event):
- # Useful line for determining what scan code matches what key.
- #print "HW: %x" % event.hardware_keycode
-
if self.key_map.has_key(event.hardware_keycode):
key = self.key_map[event.hardware_keycode]
key.pressed = True
diff --git a/po/TypingTurtleApp.pot b/po/TypingTurtle.pot
index 07456fd..dc33920 100644
--- a/po/TypingTurtleApp.pot
+++ b/po/TypingTurtle.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-11-15 04:36+0000\n"
+"POT-Creation-Date: 2008-11-17 04:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,17 +16,17 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: activity/activity.info:2
-msgid "TypingTurtleApp"
+#: activity/activity.info:2 typingturtle.py:429 typingturtle.py:530
+msgid "Typing Turtle"
msgstr ""
-#: typingturtle.py:52
+#: typingturtle.py:56
msgid ""
"Certificate of\n"
"Achievement"
msgstr ""
-#: typingturtle.py:56
+#: typingturtle.py:60
#, python-format
msgid ""
"This certifies that on <i><b><u>%(date)s</u></b></i>,\n"
@@ -34,34 +34,30 @@ msgid ""
"in Typing Turtle lesson <i><b><u>%(lesson)s</u></b></i>."
msgstr ""
-#: typingturtle.py:60
+#: typingturtle.py:64
#, python-format
msgid "<b>Words Per Minute:</b> %(wpm)d"
msgstr ""
-#: typingturtle.py:63 typingturtle.py:185
+#: typingturtle.py:67 typingturtle.py:201
#, python-format
msgid "<b>Accuracy:</b> %(accuracy)d%%"
msgstr ""
-#: typingturtle.py:70 typingturtle.py:106
+#: typingturtle.py:74 typingturtle.py:110
msgid "Go Back"
msgstr ""
-#: typingturtle.py:186
+#: typingturtle.py:202
#, python-format
msgid "<b>WPM:</b> %(wpm)d"
msgstr ""
-#: typingturtle.py:324 typingturtle.py:425
-msgid "Typing Turtle"
-msgstr ""
-
-#: typingturtle.py:327
+#: typingturtle.py:432
msgid ""
"Welcome to Typing Turtle! To begin, select a lesson from the list below."
msgstr ""
-#: typingturtle.py:335
+#: typingturtle.py:440
msgid "Available Lessons"
msgstr ""
diff --git a/typingturtle.py b/typingturtle.py
index b3bec12..0f2156b 100755
--- a/typingturtle.py
+++ b/typingturtle.py
@@ -37,8 +37,12 @@ log = logging.getLogger('Typing Turtle')
log.setLevel(logging.DEBUG)
logging.basicConfig()
+# Import onscreen keyboard.
import keyboard
+# Paragraph symbol unicode character.
+PARAGRAPH_CODE = u'\xb6'
+
class MedalScreen(gtk.EventBox):
def __init__(self, medal, activity):
gtk.EventBox.__init__(self)
@@ -123,9 +127,33 @@ class LessonScreen(gtk.VBox):
hbox.pack_start(self.accuracylabel, True, False, 10)
hbox.pack_end(title, False, False, 10)
- self.lessontext = gtk.Label()
- #self.lessontext.set_alignment(0, 0)
- self.lessontext.set_line_wrap(True)
+ # Set up font styles.
+ self.tagtable = gtk.TextTagTable()
+ instructions_tag = gtk.TextTag('instructions')
+ instructions_tag.props.size = 10000
+
+ self.tagtable.add(instructions_tag)
+ text_tag = gtk.TextTag('text')
+ text_tag.props.family = 'Monospace'
+ self.tagtable.add(text_tag)
+
+ correct_copy_tag = gtk.TextTag('correct-copy')
+ correct_copy_tag.props.family = 'Monospace'
+ correct_copy_tag.props.foreground = '#0000ff'
+ self.tagtable.add(correct_copy_tag)
+
+ incorrect_copy_tag = gtk.TextTag('incorrect-copy')
+ incorrect_copy_tag.props.family = 'Monospace'
+ incorrect_copy_tag.props.foreground = '#ff0000'
+ self.tagtable.add(incorrect_copy_tag)
+
+ # Set up the scrolling lesson text.
+ self.lessonbuffer = gtk.TextBuffer(self.tagtable)
+ self.lessontext = gtk.TextView(self.lessonbuffer)
+ self.lessontext.set_editable(False)
+ #self.lessontext.set_cursor_visible(False)
+ self.lessontext.set_left_margin(20)
+ self.lessontext.set_right_margin(20)
self.lessonscroll = gtk.ScrolledWindow()
self.lessonscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
@@ -150,34 +178,22 @@ class LessonScreen(gtk.VBox):
def begin_lesson(self):
self.step = None
- self.markup = ''
+ self.line = None
+ self.line_marks = None
self.total_keys = 0
self.correct_keys = 0
self.incorrect_keys = 0
- self.count_words()
-
self.next_step_idx = 0
self.advance_step()
self.start_time = None
- def count_words(self):
- self.total_words = 0
- for s in self.lesson['steps']:
- in_word = False
- for c in s['text']:
- if not in_word and not c.isspace():
- self.total_words += 1
- in_word = True
- elif c.isspace():
- in_word = False
-
def update_stats(self):
self.total_time = time.time() - self.start_time
if self.total_time >= 1.0:
- self.wpm = 60.0 * self.total_words / self.total_time
+ self.wpm = 60 * (len(self.step['text']) / 5) / self.total_time
else:
self.wpm = 1.0
self.accuracy = 100.0 * self.correct_keys / self.total_keys
@@ -185,9 +201,9 @@ class LessonScreen(gtk.VBox):
self.accuracylabel.set_markup(_('<b>Accuracy:</b> %(accuracy)d%%') % { 'accuracy' : int(self.accuracy) } )
self.wpmlabel.set_markup(_('<b>WPM:</b> %(wpm)d') % { 'wpm': int(self.wpm) } )
- def add_text(self, text):
- self.markup += text
- self.lessontext.set_markup('<span size="10000">' + self.markup + '_' + '</span>')
+ def wrap_line(self, line):
+ #return [line[:10], line[10:]]
+ return [line]
def advance_step(self):
if self.next_step_idx < len(self.lesson['steps']):
@@ -196,20 +212,149 @@ class LessonScreen(gtk.VBox):
self.step = self.lesson['steps'][self.next_step_idx]
self.next_step_idx = self.next_step_idx + 1
- self.markup = ''
+ # Clear the text buffer and output the instructions.
+ self.lessonbuffer.set_text('')
+ self.lessonbuffer.insert_with_tags_by_name(
+ self.lessonbuffer.get_end_iter(), self.step['instructions'] + '\n\n', 'instructions')
+
+ # Replace newlines with paragraph marks.
+ text = unicode(self.step['text'])
+
+ # Split text into lines.
+ self.lines = text.split('\n')
+
+ # Append paragraph codes.
+ self.lines = [l + PARAGRAPH_CODE for l in self.lines]
+
+ # TODO: Split by line length in addition to by paragraphs.
+ for i in range(0, len(self.lines)):
+ line = self.lines[i]
+ if len(line) > 30:
+ self.lines[i:i+1] = self.wrap_line(line)
+
+ # Fill text buffer with text lines, each followed by room for the user to type.
+ self.line_marks = {}
+ line_idx = 0
+ for l in self.lines:
+ self.lessonbuffer.insert_with_tags_by_name(
+ self.lessonbuffer.get_end_iter(), l.encode('utf-8') + '\n', 'text')
- self.add_text(self.step['instructions'] + '\n\n')
- self.add_text('<span font_family="monospace">' + self.step['text'] + '</span>\n')
+ self.line_marks[line_idx] = self.lessonbuffer.create_mark(None, self.lessonbuffer.get_end_iter(), True)
+ self.lessonbuffer.insert_at_cursor('\n')
- self.char_idx = 0
- self.hilite_next_key()
+ line_idx += 1
+
+ self.line_idx = 0
+ self.begin_line()
else:
self.finish_lesson()
- def finish_lesson(self):
- self.step = None
+ def begin_line(self):
+ self.line = self.lines[self.line_idx]
+ self.line_mark = self.line_marks[self.line_idx]
+
+ self.char_idx = 0
+ self.hilite_next_key()
+
+ def key_press_cb(self, widget, event):
+ # Ignore hotkeys.
+ if event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK):
+ return
+
+ # Extract information about the key pressed.
+ key = gtk.gdk.keyval_to_unicode(event.keyval)
+ if key != 0: key = chr(key)
+ key_name = gtk.gdk.keyval_name(event.keyval)
+
+ # Convert Return keys to paragraph symbols.
+ if key_name == 'Return':
+ key = PARAGRAPH_CODE
+
+ print "key_press_cb: key=%s key_name=%s event.keyval=%d" % (key, key_name, event.keyval)
+
+ # Handle backspace by deleting text and optionally moving up lines.
+ if key_name == 'BackSpace':
+ # Move to previous line if at the end of the current one.
+ if self.char_idx == 0 and self.line_idx > 0:
+ self.line_idx -= 1
+ self.begin_line()
+
+ self.char_idx = len(self.line)
+
+ # Then delete the current character.
+ if self.char_idx > 0:
+ self.char_idx -= 1
+
+ iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
+ iter.forward_chars(self.char_idx)
+
+ iter_end = iter.copy()
+ iter_end.forward_char()
+
+ self.lessonbuffer.delete(iter, iter_end)
+
+ # Process normal key presses.
+ elif key != 0:
+
+ # Timer starts with first keypress.
+ if not self.start_time:
+ self.start_time = time.time()
+
+ # Check to see if they pressed the correct key.
+ if key == self.line[self.char_idx]:
+ tag_name = 'correct-copy'
+ self.correct_keys += 1
+ self.total_keys += 1
+
+ else:
+ # TODO - Play 'incorrect key' sound here.
+
+ tag_name = 'incorrect-copy'
+ self.incorrect_keys += 1
+ self.total_keys += 1
+
+ # Insert the key itno the bufffer.
+ iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
+ iter.forward_chars(self.char_idx)
+
+ self.lessonbuffer.insert_with_tags_by_name(iter, key, tag_name)
+ # Advance to the next character (or else).
+ self.char_idx += 1
+ if self.char_idx >= len(self.line):
+ if self.line_idx < len(self.lines):
+ self.line_idx += 1
+ self.begin_line()
+ else:
+ self.advance_step()
+
+ self.update_stats()
+
+ self.hilite_next_key()
+
+ return False
+
+ def hilite_next_key(self):
+ # Hilite the next key on the virtual keyboard.
+ self.keyboard.clear_hilite()
+ if len(self.line) > 0:
+ key = self.keyboard.find_key_by_letter(self.line[self.char_idx])
+ if key:
+ key.set_hilite(True)
+
+ # Move the cursor to the insert location.
+ iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
+ iter.forward_chars(self.char_idx)
+ self.lessonbuffer.place_cursor(iter)
+
+ # Gain focus (this causes the cursor line to draw).
+ self.lessontext.grab_focus()
+
+ # Scroll the TextView so the cursor is on screen.
+ self.lessontext.scroll_to_mark(self.lessonbuffer.get_insert(), 0)
+
+ def finish_lesson(self):
self.activity.pop_screen()
self.update_stats()
@@ -270,46 +415,6 @@ class LessonScreen(gtk.VBox):
# Show the new medal (regardless of whether it was recorded).
self.activity.push_screen(MedalScreen(medal, self.activity))
- def key_press_cb(self, widget, event):
- if not self.step:
- return False
-
- # Timer starts with first keypress.
- if not self.start_time:
- self.start_time = time.time()
-
- # Check to see if they pressed the correct key.
- if event.keyval == ord(self.step['text'][self.char_idx]):
- self.correct_keys += 1
- self.total_keys += 1
-
- self.add_text('<span font_family="monospace">' + chr(event.keyval) + '</span>')
-
- self.keyboard.clear_hilite()
- self.keyboard.queue_draw()
-
- self.char_idx += 1
- if self.char_idx >= len(self.step['text']):
- self.add_text('\n\n')
- self.advance_step()
-
- else:
- self.hilite_next_key()
-
- else:
- # TODO - Play 'incorrect key' sound here.
-
- self.incorrect_keys += 1
- self.total_keys += 1
-
- self.update_stats()
-
- return False
-
- def hilite_next_key(self):
- key = self.keyboard.find_key_by_letter(self.step['text'][self.char_idx])
- key.set_hilite(True)
-
def stop_cb(self, widget):
self.activity.pop_screen()