[gnome-tetravex] Allow Undo.



commit 309516d526169587b546dbd6cb139abb3e545a41
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Fri Sep 20 23:30:20 2019 +0200

    Allow Undo.
    
    Progress on #2.

 src/gnome-tetravex.vala | 32 +++++++++++++++++++++++++----
 src/puzzle-view.vala    | 16 +++++++++++++++
 src/puzzle.vala         | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+), 4 deletions(-)
---
diff --git a/src/gnome-tetravex.vala b/src/gnome-tetravex.vala
index b35368d..ab01b89 100644
--- a/src/gnome-tetravex.vala
+++ b/src/gnome-tetravex.vala
@@ -39,6 +39,7 @@ private class Tetravex : Gtk.Application
     private Stack new_game_solve_stack;
     private Stack play_pause_stack;
 
+    private SimpleAction undo_action;
     private SimpleAction pause_action;
     private SimpleAction solve_action;
     private SimpleAction finish_action;
@@ -68,6 +69,7 @@ private class Tetravex : Gtk.Application
         { "move-down",      move_down                                       },
         { "move-left",      move_left                                       },
         { "move-right",     move_right                                      },
+        { "undo",           undo_cb                                         },
         { "size",           radio_cb,       "s",    "'2'",  size_changed    },
         { "help",           help_cb                                         },
         { "about",          about_cb                                        }
@@ -109,6 +111,7 @@ private class Tetravex : Gtk.Application
         set_accels_for_action ("app.move-down",     {"<Primary>Down"    });
         set_accels_for_action ("app.move-left",     {"<Primary>Left"    });
         set_accels_for_action ("app.move-right",    {"<Primary>Right"   });
+        set_accels_for_action ("app.undo",          {"<Primary>z"       });
         // F1 and friends are managed manually
 
         Builder builder = new Builder.from_resource ("/org/gnome/Tetravex/gnome-tetravex.ui");
@@ -145,6 +148,11 @@ private class Tetravex : Gtk.Application
         menu_button.set_menu_model (appmenu);
         headerbar.pack_end (menu_button);
 
+        Button undo_button = new Button.from_icon_name ("edit-undo-symbolic");
+        undo_button.set_action_name ("app.undo");
+        undo_button.show ();
+        headerbar.pack_start (undo_button);
+
         Grid grid = (Grid) builder.get_object ("grid");
 
         view = new PuzzleView ();
@@ -212,10 +220,14 @@ private class Tetravex : Gtk.Application
         box.set_margin_bottom (20);
         grid.attach (box, 1, 1, 1, 1);
 
+        undo_action   = (SimpleAction) lookup_action ("undo");
         pause_action  = (SimpleAction) lookup_action ("pause");
         solve_action  = (SimpleAction) lookup_action ("solve");
         finish_action = (SimpleAction) lookup_action ("finish");
+
+        undo_action.set_enabled (false);
         finish_action.set_enabled (false);
+
         view.notify ["tile-selected"].connect (() => {
                 if (!puzzle_init_done)
                     return;
@@ -332,6 +344,8 @@ private class Tetravex : Gtk.Application
         puzzle.tick.connect (tick_cb);
         puzzle.solved.connect (solved_cb);
         puzzle.notify ["is-solved-right"].connect (solved_right_cb);
+        puzzle.notify ["can-undo"].connect (() =>
+            undo_action.set_enabled (puzzle.can_undo && !puzzle.is_solved && !puzzle.paused));
         puzzle.show_end_game.connect (show_end_game_cb);
         view.puzzle = puzzle;
         tick_cb ();
@@ -341,7 +355,7 @@ private class Tetravex : Gtk.Application
             puzzle.paused = true;
             start_paused = false;
         }
-        update_button_states ();
+        update_bottom_button_states ();
     }
 
     private void tick_cb ()
@@ -360,6 +374,7 @@ private class Tetravex : Gtk.Application
 
     private void solved_cb (Puzzle puzzle)
     {
+        undo_action.set_enabled (false);
         pause_action.set_enabled (false);
         solve_action.set_enabled (false);
         finish_action.set_enabled (false);
@@ -455,7 +470,7 @@ private class Tetravex : Gtk.Application
         if (puzzle.paused)
         {
             puzzle.paused = false;
-            update_button_states ();
+            update_bottom_button_states ();
             return true;
         }
 
@@ -543,13 +558,22 @@ private class Tetravex : Gtk.Application
     }
     private void move_right ()  { puzzle.move_right (); }
 
+    private void undo_cb ()
+    {
+        if (view.tile_selected)
+            view.release_selected_tile ();
+        else
+            view.undo ();
+    }
+
     private void pause_cb (SimpleAction action, Variant? parameter)
     {
         puzzle.paused = !puzzle.paused;
-        update_button_states ();
+        undo_action.set_enabled (puzzle.can_undo && !puzzle.is_solved && !puzzle.paused);
+        update_bottom_button_states ();
     }
 
-    private void update_button_states ()
+    private void update_bottom_button_states ()
     {
         if (puzzle.is_solved_right)
         {
diff --git a/src/puzzle-view.vala b/src/puzzle-view.vala
index 76bf21e..bca0d66 100644
--- a/src/puzzle-view.vala
+++ b/src/puzzle-view.vala
@@ -611,4 +611,20 @@ private class PuzzleView : Gtk.DrawingArea
             for (uint8 y = 0; y < puzzle.size; y++)
                 puzzle.switch_tiles (x + puzzle.size, y, x, y, final_animation_duration);
     }
+
+    internal void release_selected_tile ()
+    {
+        uint8 selected_x, selected_y;
+        puzzle.get_tile_location (((!) selected_tile).tile, out selected_x, out selected_y);
+        move_tile_to_location ((!) selected_tile, selected_x, selected_y, animation_duration);
+        ((!) selected_tile).snap_to_cursor = true;
+        selected_tile = null;
+        tile_selected = false;
+    }
+
+    internal void undo ()
+    {
+        last_selected_tile = null;
+        puzzle.undo ();
+    }
 }
diff --git a/src/puzzle.vala b/src/puzzle.vala
index 102ef6a..39bf6c5 100644
--- a/src/puzzle.vala
+++ b/src/puzzle.vala
@@ -237,6 +237,10 @@ private class Puzzle : Object
     [CCode (notify = false)] internal bool game_in_progress { internal get; private set; default = false; }
     [CCode (notify = true)]  internal bool is_solved_right  { internal get; private set; default = false; }
     internal void switch_tiles (uint8 x0, uint8 y0, uint8 x1, uint8 y1, uint delay_if_finished = 0)
+    {
+        _switch_tiles (x0, y0, x1, y1, delay_if_finished, /* undoing */ false);
+    }
+    private void _switch_tiles (uint8 x0, uint8 y0, uint8 x1, uint8 y1, uint delay_if_finished, bool undoing)
     {
         if (x0 == x1 && y0 == y1)
             return;
@@ -269,6 +273,9 @@ private class Puzzle : Object
             is_solved_right = true;
         else if (is_solved_right)
             is_solved_right = false;
+
+        if (!undoing)
+            add_to_history (x0, y0, x1, y1);
     }
 
     /*\
@@ -416,4 +423,51 @@ private class Puzzle : Object
 
         return false;
     }
+
+    /*\
+    * * history
+    \*/
+
+    [CCode (notify = true)] internal bool can_undo { internal get; private set; default = false; }
+    private uint history_length = 0;
+
+    private List<Inversion> reversed_history = new List<Inversion> ();
+    private const uint animation_duration = 250; // FIXME might better be in view
+
+    private class Inversion : Object
+    {
+        public uint8 x0 { internal get; protected construct; }
+        public uint8 y0 { internal get; protected construct; }
+        public uint8 x1 { internal get; protected construct; }
+        public uint8 y1 { internal get; protected construct; }
+
+        internal Inversion (uint8 x0, uint8 y0, uint8 x1, uint8 y1)
+        {
+            Object (x0: x0, y0: y0, x1: x1, y1: y1);
+        }
+    }
+
+    private void add_to_history (uint8 x0, uint8 y0, uint8 x1, uint8 y1)
+    {
+        Inversion history_entry = new Inversion (x0, y0, x1, y1);
+        reversed_history.prepend (history_entry);
+
+        history_length++;
+        can_undo = true;
+    }
+
+    internal void undo ()
+    {
+        unowned List<Inversion>? history_entry = reversed_history.first ();
+        if (history_entry == null)
+            return;
+
+        unowned Inversion inversion = ((!) history_entry).data;
+        _switch_tiles (inversion.x0, inversion.y0, inversion.x1, inversion.y1, animation_duration, /* 
undoing */ true);
+        reversed_history.remove (inversion);
+
+        history_length--;
+        if (history_length == 0)
+            can_undo = false;
+    }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]