diff options
author | Joe Lee <joe@jotaro.com> | 2009-11-28 18:46:39 (GMT) |
---|---|---|
committer | Joe Lee <joe@jotaro.com> | 2009-11-28 18:51:46 (GMT) |
commit | 3160d80d26740fc8de72576b11764acfb955d14c (patch) | |
tree | bd9b724ecabf07a6bbc854b3f8a39a3f9f46ac01 | |
parent | 11db2b95d3b9fe67f3136d0ca975c94ce2e45d5d (diff) |
Modified to use stuck strip instead of window
-rw-r--r-- | MANIFEST | 1 | ||||
-rw-r--r-- | gridwidget.py | 7 | ||||
-rw-r--r-- | icons/edit-undo-many.svg | 9 | ||||
-rw-r--r-- | implodeactivity.py | 143 | ||||
-rw-r--r-- | implodegame.py | 39 | ||||
-rw-r--r-- | sugarless.py | 117 |
6 files changed, 197 insertions, 119 deletions
@@ -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() + |