Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe Lee <joe@jotaro.com>2009-11-28 18:46:39 (GMT)
committer Joe Lee <joe@jotaro.com>2009-11-28 18:51:46 (GMT)
commit3160d80d26740fc8de72576b11764acfb955d14c (patch)
treebd9b724ecabf07a6bbc854b3f8a39a3f9f46ac01
parent11db2b95d3b9fe67f3136d0ca975c94ce2e45d5d (diff)
Modified to use stuck strip instead of window
-rw-r--r--MANIFEST1
-rw-r--r--gridwidget.py7
-rw-r--r--icons/edit-undo-many.svg9
-rw-r--r--implodeactivity.py143
-rw-r--r--implodegame.py39
-rw-r--r--sugarless.py117
6 files changed, 197 insertions, 119 deletions
diff --git a/MANIFEST b/MANIFEST
index ea2087d..6675a21 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -9,6 +9,7 @@ helpwidget.py
icons/easy-level.svg
icons/edit-redo.svg
icons/edit-undo.svg
+icons/edit-undo-many.svg
icons/hard-level.svg
icons/medium-level.svg
icons/new-game.svg
diff --git a/gridwidget.py b/gridwidget.py
index d702c34..5e75e1b 100644
--- a/gridwidget.py
+++ b/gridwidget.py
@@ -182,7 +182,7 @@ class GridWidget(gtk.DrawingArea):
return True
# Ignore key presses while animating.
if self._is_animating():
- return action is not None
+ return False
if not self._board_drawer.board_is_valid():
self._board_drawer.set_selected_cell(None)
return action is not None
@@ -205,8 +205,9 @@ class GridWidget(gtk.DrawingArea):
'right' : ( 1, 0)}
if action in offsets:
offset = offsets[action]
- self._board_drawer.move_selected_cell(*offset)
- return action is not None
+ return self._board_drawer.move_selected_cell(*offset)
+ else:
+ return False
@_log_errors
def do_motion_notify_event(self, event):
diff --git a/icons/edit-undo-many.svg b/icons/edit-undo-many.svg
new file mode 100644
index 0000000..215714c
--- /dev/null
+++ b/icons/edit-undo-many.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="55px" height="55px">
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:3;stroke-linejoin:round;stroke-linecap:round"
+ d="M 19,27 L 15,21 L 19,15
+ M 15,21 h 5 c 6,0 6.5,4 6.5,8.5 s -0.5,8.5 -6.5,8.5 h -2
+ M 33,27 L 29,21 L 33,15
+ M 29,21 h 5 c 6,0 6.5,4 6.5,8.5 s -0.5,8.5 -6.5,8.5 h -2"/>
+</svg>
diff --git a/implodeactivity.py b/implodeactivity.py
index 539d959..b9ef814 100644
--- a/implodeactivity.py
+++ b/implodeactivity.py
@@ -54,21 +54,29 @@ import gobject
from keymap import KEY_MAP
class ImplodeActivity(Activity):
- def hello(self, widget, data=None):
- logging.info("Hello World")
-
def __init__(self, handle):
super(ImplodeActivity, self).__init__(handle)
_logger.debug('Starting implode activity...')
self._game = ImplodeGame()
- self._game.connect('stuck', self._stuck_cb)
+
+ game_box = gtk.VBox()
+ game_box.pack_start(self._game)
+ self._stuck_strip = _StuckStrip()
self._configure_toolbars()
- self.set_canvas(self._game)
+ self.set_canvas(game_box)
+
+ # Show everything except the stuck strip.
self.show_all()
+ game_box.pack_end(self._stuck_strip, expand=False)
+
+ self._game.connect('show-stuck', self._show_stuck_cb)
+ self._stuck_strip.connect('undo-clicked', self._stuck_undo_cb)
+ game_box.connect('key-press-event', self._key_press_event_cb)
+
self._game.grab_focus()
last_game_path = self._get_last_game_path()
@@ -107,10 +115,35 @@ class ImplodeActivity(Activity):
f.write(content)
f.close()
- def _stuck_cb(self, state):
- stuck_window = _StuckWindow(self._game)
- stuck_window.set_transient_for(self.get_toplevel())
- stuck_window.show_all()
+ def _show_stuck_cb(self, state, data=None):
+ if data:
+ self._stuck_strip.show_all()
+ else:
+ if self._stuck_strip.focus_child:
+ self._game.grab_focus()
+ self._stuck_strip.hide()
+
+ def _stuck_undo_cb(self, state, data=None):
+ self._game.undo_to_solvable_state()
+
+ def _key_press_event_cb(self, source, event):
+ # Make the game navigable by keypad controls.
+ action = KEY_MAP.get(event.keyval, None)
+ if action is None:
+ return False
+ if not self._stuck_strip.flags() & gtk.VISIBLE:
+ return True
+ if self._game.focus_child:
+ if action == 'down':
+ self._stuck_strip.button.grab_focus()
+ return True
+ elif self._stuck_strip.focus_child:
+ if action == 'up':
+ self._game.grab_focus()
+ elif action == 'select':
+ self._stuck_strip.button.activate()
+ return True
+ return True
def _configure_toolbars(self):
"""Create, set, and show a toolbar box with an activity button, game
@@ -242,45 +275,6 @@ class _DialogWindow(gtk.Window):
self.window.set_accept_focus(True)
-class _StuckWindow(_DialogWindow):
- # A dialog window to prompt the user when a game can't be finished.
- def __init__(self, game):
- super(_StuckWindow, self).__init__('help-icon', _("Stuck"))
-
- width = gtk.gdk.screen_width() / 2
- height = gtk.gdk.screen_height() / 2
- self.set_size_request(width, height)
-
- label = gtk.Label(_("Stuck? You can still solve the puzzle."))
- label.set_line_wrap(True)
- self.content_vbox.pack_start(label,
- expand=False,
- padding=style.DEFAULT_SPACING)
-
- def add_button(icon_name, label, func):
- icon = Icon()
- icon.set_from_icon_name(icon_name, gtk.ICON_SIZE_LARGE_TOOLBAR)
- button = gtk.Button()
- button.set_image(icon)
- button.set_label(label)
- self.content_vbox.pack_start(button,
- expand=True,
- padding=style.DEFAULT_SPACING)
-
- def callback(source):
- self.destroy()
- func()
- button.connect('clicked', callback)
-
- return button
-
- undo = add_button('edit-undo', _("Undo"), game.undo_to_solvable_state)
- new = add_button('new-game', _("New game"), game.new_game)
-
- _add_button_nav_override(new, undo)
- _add_button_nav_override(undo, new)
- undo.grab_focus()
-
class _HelpWindow(_DialogWindow):
# A dialog window to display the game instructions.
def __init__(self):
@@ -406,25 +400,36 @@ class _HelpNavBar(gtk.HButtonBox):
def set_can_next_stage(self, can_next_stage):
self._forward_button.set_sensitive(can_next_stage)
-# It is important that the "stuck" window buttons be navigable with the keypad,
-# so that the bulk of the game can be played in tablet mode on the XO. To
-# facilitate this, we add a key press override for one button to:
-# - Make it switch focus to the other button on a directional keypress
-# - Make it activate the button on a select keypress.
-#
-# There is probably a better way to do this... I have tried a number of
-# different key capture/focus approaches, and the gtk in my Sugar emulator so
-# far has not cooperated... which is odd, since the gtk on my desktop does the
-# right thing by default.
-def _add_button_nav_override(button, other_button):
- def key_press_event_cb(widget, event, data=None):
- action = KEY_MAP.get(event.keyval, None)
- if action in ('left', 'right', 'up', 'down'):
- other_button.grab_focus()
- return True
- if action == 'select':
- button.activate()
- return True
- return False
- button.connect('key-press-event', key_press_event_cb)
+
+class _StuckStrip(gtk.HBox):
+ __gsignals__ = {
+ 'undo-clicked' : (gobject.SIGNAL_RUN_LAST, None, ()),
+ }
+ def __init__(self, *args, **kwargs):
+ super(_StuckStrip, self).__init__(*args, **kwargs)
+
+ spacer1 = gtk.Label('')
+ self.pack_start(spacer1, expand=True)
+
+ spacer2 = gtk.Label('')
+ self.pack_end(spacer2, expand=True)
+
+ self.set_spacing(10)
+
+ self.set_border_width(10)
+
+ label = gtk.Label(_("Stuck? You can still solve the puzzle."))
+ self.pack_start(label, expand=False)
+
+ icon = Icon()
+ icon.set_from_icon_name('edit-undo-many', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.button = gtk.Button(stock=gtk.STOCK_UNDO)
+ self.button.set_image(icon)
+ self.button.set_label(_("Undo some moves"))
+ self.pack_end(self.button, expand=False)
+
+ def callback(source):
+ self.emit('undo-clicked')
+ self.button.connect('clicked', callback)
+
diff --git a/implodegame.py b/implodegame.py
index a073c18..56dd83c 100644
--- a/implodegame.py
+++ b/implodegame.py
@@ -33,7 +33,7 @@ import gridwidget
# Amount of time to wait after the player is stuck to display the "stuck"
# dialog, in seconds.
-_STUCK_DELAY = 1.5
+_STUCK_DELAY = 0.5
# Amount of time to wait between undos when undoing the board to a solvable
# state after the player gets stuck, in seconds.
@@ -43,7 +43,7 @@ class ImplodeGame(gtk.EventBox):
"""Gtk widget for playing the implode game."""
__gsignals__ = {
- 'stuck' : (gobject.SIGNAL_RUN_LAST, None, ())
+ 'show-stuck': (gobject.SIGNAL_RUN_LAST, None, (int,)),
}
def __init__(self, *args, **kwargs):
@@ -75,9 +75,10 @@ class ImplodeGame(gtk.EventBox):
def grab_focus(self):
self._grid.grab_focus()
- self._grid.select_center_cell()
+ #self._grid.select_center_cell()
def new_game(self):
+ self._hide_stuck()
self._stop_animation()
self._seed = self._random.randint(0, 99999)
size_frag_dict = {
@@ -89,10 +90,12 @@ class ImplodeGame(gtk.EventBox):
self._reset_board()
def replay_game(self):
+ self._hide_stuck()
self._stop_animation()
self._reset_board()
def undo(self):
+ self._hide_stuck()
self._stop_animation()
if len(self._undo_stack) == 0:
return
@@ -111,6 +114,7 @@ class ImplodeGame(gtk.EventBox):
# player's later board states for solvability, so that we don't need to
# undo as many moves.
+ self._hide_stuck()
self._stop_animation()
if len(self._undo_stack) == 0:
return
@@ -136,7 +140,6 @@ class ImplodeGame(gtk.EventBox):
self._anim = Anim(update_func, end_anim_func)
self._anim.start()
-
def _get_moves_so_far(self):
# Returns a list of the moves so far.
return [move for (board, move) in self._undo_stack]
@@ -151,9 +154,8 @@ class ImplodeGame(gtk.EventBox):
self._grid.set_board(self._board)
self._grid.set_win_draw_flag(False)
-
def redo(self):
- _logger.debug('Redo.')
+ self._hide_stuck()
self._stop_animation()
if len(self._redo_stack) == 0:
return
@@ -165,6 +167,8 @@ class ImplodeGame(gtk.EventBox):
# Force board refresh.
self._grid.set_board(self._board)
+ self._check_for_lose_state()
+
def set_level(self, level):
self._difficulty = level
@@ -196,6 +200,7 @@ class ImplodeGame(gtk.EventBox):
def set_game_state(self, state):
# Sets the game state using a dictionary of atomic subobjects.
+ self._hide_stuck()
self._stop_animation()
def decode_board(state):
# Decodes a board (and maybe an appended move) from the given state
@@ -226,6 +231,8 @@ class ImplodeGame(gtk.EventBox):
else:
self._winning_moves = []
+ self._check_for_lose_state()
+
def _reset_board(self):
# Regenerates the board with the current seed.
(self._board, self._winning_moves) = \
@@ -239,7 +246,17 @@ class ImplodeGame(gtk.EventBox):
def _piece_selected_cb(self, widget, x, y):
# Handles piece selection.
+
+ # We check contiguous before stopping the animation because we don't
+ # want a click on the game board in a losing state to stop the "stuck"
+ # animation.
+ if len(self._board.get_contiguous(x, y)) < 3:
+ return
+
+ self._hide_stuck()
self._stop_animation()
+ # We recalc contiguous here because _stop_animation may modify board
+ # contents (e.g. the undo-many animation).
contiguous = self._board.get_contiguous(x, y)
if len(contiguous) >= 3:
def remove_func(anim_stopped=False):
@@ -289,6 +306,10 @@ class ImplodeGame(gtk.EventBox):
else:
self._init_win()
else:
+ self._check_for_lose_state()
+
+ def _check_for_lose_state(self):
+ if not self._board.is_empty():
all_contiguous = self._board.get_all_contiguous()
if len(all_contiguous) == 0:
self._init_lose()
@@ -310,7 +331,11 @@ class ImplodeGame(gtk.EventBox):
def end_anim_func(anim_stopped):
if not anim_stopped:
- self.emit('stuck')
+ self.emit('show-stuck', 1)
self._anim = Anim(update_func, end_anim_func)
self._anim.start()
+
+ def _hide_stuck(self):
+ self.emit('show-stuck', 0)
+
diff --git a/sugarless.py b/sugarless.py
index 213bad4..3a88be1 100644
--- a/sugarless.py
+++ b/sugarless.py
@@ -28,6 +28,7 @@ import os
import implodegame
from helpwidget import HelpWidget
+from keymap import KEY_MAP
_DEFAULT_SPACING = 15
@@ -86,16 +87,29 @@ class ImplodeWindow(gtk.Window):
add_button(gtk.STOCK_HELP, self._help_clicked)
- main_box = gtk.VBox(False, 0)
- main_box.pack_start(toolbar, False)
- main_box.pack_start(self.game, True, True, 0)
+ self._stuck_strip = _StuckStrip()
+
+ game_box = gtk.VBox()
+ game_box.pack_start(self.game)
+ game_box.pack_end(self._stuck_strip, expand=False)
+
+ main_box = gtk.VBox()
+ main_box.pack_start(toolbar, expand=False)
+ main_box.pack_end(game_box)
self.add(main_box)
- self.game.connect('stuck', self._stuck_cb)
+ # Show everything except the stuck strip.
+ main_box.show_all()
+ self._stuck_strip.hide()
+
+ self.game.connect('show-stuck', self._show_stuck_cb)
+ self._stuck_strip.connect('undo-clicked', self._stuck_undo_cb)
+ game_box.connect('key-press-event', self._key_press_event_cb)
- self.show_all()
self.game.grab_focus()
+ self.show()
+
def _delete_event_cb(self, window, event):
gtk.main_quit()
return False
@@ -114,41 +128,35 @@ class ImplodeWindow(gtk.Window):
help_window.set_transient_for(self.get_toplevel())
help_window.show_all()
- def _stuck_cb(self, state):
- stuck_window = _StuckWindow(self.game)
- stuck_window.set_transient_for(self.get_toplevel())
- stuck_window.show_all()
-
-
-class _StuckWindow(gtk.Window):
- def __init__(self, game):
- super(_StuckWindow, self).__init__()
-
- self.set_size_request(320, 240)
- self.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
- self.set_modal(True)
-
- vbox = gtk.VBox()
- self.add(vbox)
-
- label = gtk.Label("Stuck? You can still solve the puzzle.")
- label.set_line_wrap(True)
- vbox.pack_start(label, expand=False, padding=_DEFAULT_SPACING)
-
- def add_button(id, label, func):
- button = gtk.Button(stock=id, label=label)
- vbox.pack_start(button, expand=True, padding=_DEFAULT_SPACING)
-
- def callback(source):
- self.destroy()
- func()
- button.connect('clicked', callback)
-
- return button
-
- add_button(gtk.STOCK_UNDO, "Undo", game.undo_to_solvable_state)
- add_button(gtk.STOCK_NEW, "New game", game.new_game)
-
+ def _show_stuck_cb(self, state, data=None):
+ if data:
+ self._stuck_strip.show_all()
+ else:
+ if self._stuck_strip.focus_child:
+ self.game.grab_focus()
+ self._stuck_strip.hide()
+
+ def _stuck_undo_cb(self, state, data=None):
+ self.game.undo_to_solvable_state()
+
+ def _key_press_event_cb(self, source, event):
+ # Make the game navigable by keypad controls.
+ action = KEY_MAP.get(event.keyval, None)
+ if action is None:
+ return False
+ if not self._stuck_strip.flags() & gtk.VISIBLE:
+ return True
+ if self.game.focus_child:
+ if action == 'down':
+ self._stuck_strip.button.grab_focus()
+ return True
+ elif self._stuck_strip.focus_child:
+ if action == 'up':
+ self.game.grab_focus()
+ elif action == 'select':
+ self._stuck_strip.button.activate()
+ return True
+ return True
class _HelpWindow(gtk.Window):
def __init__(self):
@@ -232,6 +240,34 @@ class _HelpNavBar(gtk.HButtonBox):
self._forward_button.set_sensitive(can_next_stage)
+class _StuckStrip(gtk.HBox):
+ __gsignals__ = {
+ 'undo-clicked' : (gobject.SIGNAL_RUN_LAST, None, ()),
+ }
+ def __init__(self, *args, **kwargs):
+ super(_StuckStrip, self).__init__(*args, **kwargs)
+
+ spacer1 = gtk.Label('')
+ self.pack_start(spacer1, expand=True)
+
+ spacer2 = gtk.Label('')
+ self.pack_end(spacer2, expand=True)
+
+ self.set_spacing(10)
+
+ self.set_border_width(10)
+
+ label = gtk.Label("Stuck? You can still solve the puzzle.")
+ self.pack_start(label, expand=False)
+
+ self.button = gtk.Button(stock=gtk.STOCK_UNDO)
+ self.button.set_label("Undo some moves")
+ self.pack_end(self.button, expand=False)
+
+ def callback(source):
+ self.emit('undo-clicked')
+ self.button.connect('clicked', callback)
+
def main():
w = ImplodeWindow()
@@ -239,3 +275,4 @@ def main():
if __name__ == "__main__":
main()
+