[gnome-tetravex] Allow playing with keyboard.



commit a44cbaebb69ad38dac102067f62e37566e0d6661
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Sun Oct 13 04:46:40 2019 +0200

    Allow playing with keyboard.
    
    Fixes #9.

 src/gnome-tetravex.vala    |   9 +-
 src/help-overlay.ui        |  47 ++++++++
 src/puzzle-view.vala       | 267 ++++++++++++++++++++++++++++++++++++++++++++-
 src/theme-extrusion.vala   |  66 ++++++++---
 src/theme-neoretro.vala    | 115 ++++++++++---------
 src/theme-nostalgia.vala   |  36 +++++-
 src/theme-synesthesia.vala |  36 +++++-
 7 files changed, 500 insertions(+), 76 deletions(-)
---
diff --git a/src/gnome-tetravex.vala b/src/gnome-tetravex.vala
index 03819ef..a4959a4 100644
--- a/src/gnome-tetravex.vala
+++ b/src/gnome-tetravex.vala
@@ -214,16 +214,19 @@ private class Tetravex : Gtk.Application
         MenuModel appmenu = (MenuModel) menu_builder.get_object ("app-menu");
         hamburger_button = new MenuButton ();
         hamburger_button.set_image (new Image.from_icon_name ("open-menu-symbolic", IconSize.BUTTON));
+        ((Widget) hamburger_button).set_focus_on_click (false);
         hamburger_button.show ();
         hamburger_button.set_menu_model (appmenu);
         headerbar.pack_end (hamburger_button);
 
         Button undo_button = new Button.from_icon_name ("edit-undo-symbolic");
         undo_button.set_action_name ("app.undo");
+        ((Widget) undo_button).set_focus_on_click (false);
         undo_button.show ();
 
         Button redo_button = new Button.from_icon_name ("edit-redo-symbolic");
         redo_button.set_action_name ("app.redo");
+        ((Widget) redo_button).set_focus_on_click (false);
         redo_button.show ();
 
         Box undo_redo_box = new Box (Orientation.HORIZONTAL, /* spacing */ 0);
@@ -238,6 +241,7 @@ private class Tetravex : Gtk.Application
         view = new PuzzleView ();
         view.hexpand = true;
         view.vexpand = true;
+        view.can_focus = true;
         view.button_release_event.connect (view_button_release_event);
         settings.bind ("theme", view, "theme-id", SettingsBindFlags.GET | SettingsBindFlags.NO_SENSITIVITY);
 
@@ -367,7 +371,8 @@ private class Tetravex : Gtk.Application
                     valign: Align.CENTER,
                     margin_start: 35,
                     margin_end: 35,
-                    image: _image);
+                    image: _image,
+                    focus_on_click: false);
 
             sizegroup.add_widget (this);
         }
@@ -439,6 +444,8 @@ private class Tetravex : Gtk.Application
             puzzle.paused = true;
             start_paused = false;
         }
+        else
+            view.grab_focus ();
         update_bottom_button_states ();
     }
 
diff --git a/src/help-overlay.ui b/src/help-overlay.ui
index ed6036c..4d18b99 100644
--- a/src/help-overlay.ui
+++ b/src/help-overlay.ui
@@ -64,6 +64,29 @@
             </child>
           </object>
         </child>
+        <child>
+          <object class="GtkShortcutsGroup">
+            <property name="visible">True</property>
+            <!-- Translators: title of a section in the Keyboard Shortcuts dialog; contains "Select where to 
play" and "Play on selected tile" -->
+            <property name="title" translatable="yes" context="shortcut window">Play with keyboard</property>
+            <child>
+              <object class="GtkShortcutsShortcut">
+                <property name="visible">True</property>
+                <!-- Translators: Left/Right/Up/Down arrows actions description in the Keyboard Shortcuts 
dialog, moves highlight -->
+                <property name="title" translatable="yes" context="shortcut window">Move keyboard 
highlight</property>
+                <property name="accelerator">Left Right Up Down</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkShortcutsShortcut">
+                <property name="visible">True</property>
+                <!-- Translators: Return/space actions description in the Keyboard Shortcuts dialog; depend 
on context -->
+                <property name="title" translatable="yes" context="shortcut window">Select, unselect, or 
move selected tile</property>
+                <property name="accelerator">Return space</property>
+              </object>
+            </child>
+          </object>
+        </child>
         <child>
           <object class="GtkShortcutsGroup">
             <property name="visible">1</property>
@@ -163,6 +186,30 @@
                 <property name="title" translatable="yes" context="shortcut window">Move all the pieces in 
the left box right by one</property>
               </object>
             </child>
+            <child>
+              <object class="GtkShortcutsShortcut">
+                <property name="visible">True</property>
+                <!-- Translators: numbers (1 to 6) actions description in the Keyboard Shortcuts dialog, 
section Advanced gameplay; depending on game size, you can only use 1 to 2 or up to 1 to 6 -->
+                <property name="title" translatable="yes" context="shortcut window">Select the given row, up 
to game size</property>
+                <property name="accelerator">1...6</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkShortcutsShortcut">
+                <property name="visible">True</property>
+                <!-- Translators: alphabetical chars (a to f) actions description in the Keyboard Shortcuts 
dialog, section Advanced gameplay; depending on game size, you can only use a to b or up to a to f; the board 
is splitted in two parts, that is for the left one -->
+                <property name="title" translatable="yes" context="shortcut window">Select the given column 
in the left board, up to game size</property>
+                <property name="accelerator">a...f</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkShortcutsShortcut">
+                <property name="visible">True</property>
+                <!-- Translators: capital alphabetical chars (A to F) actions description in the Keyboard 
Shortcuts dialog, section Advanced gameplay; depending on game size, you can only use A to B or up to A to F; 
the board is splitted in two parts, that is for the right one -->
+                <property name="title" translatable="yes" context="shortcut window">Select the given column 
in the right board, up to game size</property>
+                <property name="accelerator">&lt;Shift&gt;A...&lt;Shift&gt;F</property>
+              </object>
+            </child>
           </object>
         </child>
       </object>
diff --git a/src/puzzle-view.vala b/src/puzzle-view.vala
index 902b9d0..00a73dc 100644
--- a/src/puzzle-view.vala
+++ b/src/puzzle-view.vala
@@ -30,8 +30,9 @@ private abstract class Theme : Object
     internal abstract void configure (uint size);
     internal abstract void draw_arrow (Cairo.Context context);
     internal abstract void draw_socket (Cairo.Context context);
+    internal abstract void draw_highlight (Cairo.Context context, bool has_tile);
     internal abstract void draw_paused_tile (Cairo.Context context);
-    internal abstract void draw_tile (Cairo.Context context, Tile tile);
+    internal abstract void draw_tile (Cairo.Context context, Tile tile, bool highlight);
     internal abstract void set_animation_level (uint8 animation_level /* 0-16 */);
 }
 
@@ -118,6 +119,8 @@ private class PuzzleView : Gtk.DrawingArea
                 sockets_ys = new double [2 * _puzzle.size, _puzzle.size];
             }
 
+            clear_keyboard_highlight (/* only selection */ false);
+            _puzzle.solved.connect (() => clear_keyboard_highlight (/* only selection */ false));
             _puzzle.tile_moved.connect (tile_moved_cb);
             _puzzle.notify ["paused"].connect (queue_draw);
             queue_resize ();
@@ -208,6 +211,14 @@ private class PuzzleView : Gtk.DrawingArea
                          (int) tilesize + theme.overdraw_top + theme.overdraw_bottom);
     }
 
+    private void queue_draw_tile (uint8 x, uint8 y)
+    {
+        queue_draw_area ((int) sockets_xs [x, y] - theme.overdraw_left,
+                         (int) sockets_ys [x, y] - theme.overdraw_top,
+                         (int) tilesize + theme.overdraw_left + theme.overdraw_right,
+                         (int) tilesize + theme.overdraw_top + theme.overdraw_bottom);
+    }
+
     private void move_tile_to_location (TileImage image, uint x, uint y, double duration = 0)
     {
         double target_x = x_offset + (double) (x * tilesize);
@@ -481,20 +492,37 @@ private class PuzzleView : Gtk.DrawingArea
         else if (last_selected_tile != null)
             draw_image (context, (!) last_selected_tile);
 
+        /* Draw highlight */
+        if (show_highlight)
+        {
+            context.save ();
+            context.translate (sockets_xs [highlight_x, highlight_y], sockets_ys [highlight_x, highlight_y]);
+            theme.draw_highlight (context, puzzle.get_tile (highlight_x, highlight_y) != null);
+            context.restore ();
+        }
+
         /* Draw pause overlay */
         if (puzzle.paused)
             draw_pause_overlay (context);
 
         return false;
     }
-    private inline void draw_image (Cairo.Context context, TileImage image)
+    private void draw_image (Cairo.Context context, TileImage image)
     {
         context.save ();
         context.translate ((int) image.x, (int) image.y);
         if (puzzle.paused)
             theme.draw_paused_tile (context);
         else
-            theme.draw_tile (context, image.tile);
+        {
+            uint8 tile_x;
+            uint8 tile_y;
+            puzzle.get_tile_location (image.tile, out tile_x, out tile_y);
+            bool highlight = show_highlight && tile_selection
+                          && kbd_selected_x == tile_x
+                          && kbd_selected_y == tile_y;
+            theme.draw_tile (context, image.tile, highlight);
+        }
         context.restore ();
     }
     private inline void draw_pause_overlay (Cairo.Context context)
@@ -621,6 +649,7 @@ private class PuzzleView : Gtk.DrawingArea
     {
         if (puzzle.paused || puzzle.is_solved)
             return false;
+        clear_keyboard_highlight (/* only selection */ false);
 
         if (event.button == 1 || event.button == 3)
             return main_button_pressed (event);
@@ -659,7 +688,7 @@ private class PuzzleView : Gtk.DrawingArea
             {
                 uint8 x;
                 uint8 y;
-                if (selected_tile_is_last_tile (out x, out y))
+                if (only_one_remaining_tile (out x, out y))
                 {
                     uint8 selected_x, selected_y;
                     puzzle.get_tile_location (((!) selected_tile).tile, out selected_x, out selected_y);
@@ -688,7 +717,7 @@ private class PuzzleView : Gtk.DrawingArea
 
         return false;
     }
-    private inline bool selected_tile_is_last_tile (out uint8 empty_x, out uint8 empty_y)
+    private inline bool only_one_remaining_tile (out uint8 empty_x, out uint8 empty_y)
     {
         bool empty_found = false;
         empty_x = uint8.MAX;    // garbage
@@ -713,6 +742,7 @@ private class PuzzleView : Gtk.DrawingArea
     {
         if (puzzle.paused || puzzle.is_solved)
             return false;
+        clear_keyboard_highlight (/* only selection */ false);
 
         if (event.button == 1 && selected_tile != null && selection_timeout == 0)
             drop_tile (event.x, event.y);
@@ -836,4 +866,231 @@ private class PuzzleView : Gtk.DrawingArea
         arrow_pattern = null;
         socket_pattern = null;
     }
+
+    /*\
+    * * keyboard
+    \*/
+
+    private bool show_highlight = false;
+
+    private bool highlight_set = false;
+    private uint8     highlight_x = uint8.MAX;
+    private uint8     highlight_y = uint8.MAX;
+    private uint8 old_highlight_x = uint8.MAX;
+    private uint8 old_highlight_y = uint8.MAX;
+
+    private bool tile_selection = false;
+    private uint8 kbd_selected_x = uint8.MAX;
+    private uint8 kbd_selected_y = uint8.MAX;
+
+    protected override bool key_press_event (Gdk.EventKey event)
+    {
+        if (!puzzle_init_done)
+            return false;
+
+        if (puzzle.is_solved || puzzle.paused)
+            return false;
+
+        if (tile_selected)
+            return false;
+
+        string key = (!) (Gdk.keyval_name (event.keyval) ?? "");
+        if (key == "")
+            return false;
+
+        if (highlight_set && show_highlight && (key == "space" || key == "Return" || key == "KP_Enter"))
+        {
+            if (tile_selection)
+            {
+                if (highlight_x == kbd_selected_x && highlight_y == kbd_selected_y)
+                {
+                    clear_keyboard_highlight (/* only selection */ true);
+                    return true;
+                }
+                if (puzzle.can_switch (highlight_x, highlight_y, kbd_selected_x, kbd_selected_y))
+                {
+                    puzzle.switch_tiles (highlight_x, highlight_y, kbd_selected_x, kbd_selected_y);
+                    clear_keyboard_highlight (/* only selection */ true);
+                    return true;
+                }
+                if (puzzle.get_tile (highlight_x, highlight_y) != null)
+                {
+                    tile_selection = false;
+                    queue_draw_tile (kbd_selected_x, kbd_selected_y);
+                    kbd_selected_x = highlight_x;
+                    kbd_selected_y = highlight_y;
+                    tile_selection = true;
+                    queue_draw_tile (highlight_x, highlight_y);
+                    return true;
+                }
+            }
+            else
+            {
+                Tile? tile = puzzle.get_tile (highlight_x, highlight_y);
+                if (tile == null)
+                    return true;
+
+                if (highlight_x >= puzzle.size)
+                {
+                    uint8 x;
+                    uint8 y;
+                    if (only_one_remaining_tile (out x, out y))
+                    {
+                        uint8 selected_x, selected_y;
+                        puzzle.get_tile_location ((!) tile, out selected_x, out selected_y);
+                        if (puzzle.can_switch (selected_x, selected_y, x, y))
+                        {
+                            puzzle.switch_tiles (selected_x, selected_y, x, y, final_animation_duration);
+                            return true;
+                        }
+                    }
+                }
+                tile_selection = true;
+                kbd_selected_x = highlight_x;
+                kbd_selected_y = highlight_y;
+                queue_draw_tile (highlight_x, highlight_y);
+            }
+            return true;
+        }
+
+        if ((puzzle.size <= 2 && (key == "c" || key == "C" || key == "3" || key == "KP_3")) ||
+            (puzzle.size <= 3 && (key == "d" || key == "D" || key == "4" || key == "KP_4")) ||
+            (puzzle.size <= 4 && (key == "e" || key == "E" || key == "5" || key == "KP_5")) ||
+            (puzzle.size <= 5 && (key == "f" || key == "F" || key == "6" || key == "KP_6")) ||
+            (puzzle.size <= 6 && (key == "g" || key == "G" || key == "7" || key == "KP_7")) ||
+            (puzzle.size <= 7 && (key == "h" || key == "H" || key == "8" || key == "KP_8")) ||
+            (puzzle.size <= 8 && (key == "i" || key == "I" || key == "9" || key == "KP_9")) ||
+            (puzzle.size <= 9 && (key == "j" || key == "J" || key == "0" || key == "KP_0")))
+            return false;
+
+        old_highlight_x = highlight_x;
+        old_highlight_y = highlight_y;
+        switch (key)
+        {
+            case "Up":
+            case "KP_Up":
+                set_highlight_position_if_needed ();
+                if (highlight_y > 0) highlight_y--;
+                break;
+            case "Left":
+            case "KP_Left":
+                set_highlight_position_if_needed ();
+                if (highlight_x > 0) highlight_x--;
+                break;
+            case "Right":
+            case "KP_Right":
+                set_highlight_position_if_needed ();
+                if (highlight_x < puzzle.size * 2 - 1) highlight_x++;
+                break;
+            case "Down":
+            case "KP_Down":
+                set_highlight_position_if_needed ();
+                if (highlight_y < puzzle.size - 1) highlight_y++;
+                break;
+
+            case "space":
+            case "Return":
+            case "KP_Enter":
+                set_highlight_position_if_needed ();
+                break;
+
+            case "Escape": break;
+
+            case "a": set_highlight_position_if_needed (); highlight_x = 0; break;
+            case "b": set_highlight_position_if_needed (); highlight_x = 1; break;
+            case "c": set_highlight_position_if_needed (); highlight_x = 2; break;
+            case "d": set_highlight_position_if_needed (); highlight_x = 3; break;
+            case "e": set_highlight_position_if_needed (); highlight_x = 4; break;
+            case "f": set_highlight_position_if_needed (); highlight_x = 5; break;
+
+            case "A": set_highlight_position_if_needed (); highlight_x = puzzle.size    ; break;
+            case "B": set_highlight_position_if_needed (); highlight_x = puzzle.size + 1; break;
+            case "C": set_highlight_position_if_needed (); highlight_x = puzzle.size + 2; break;
+            case "D": set_highlight_position_if_needed (); highlight_x = puzzle.size + 3; break;
+            case "E": set_highlight_position_if_needed (); highlight_x = puzzle.size + 4; break;
+            case "F": set_highlight_position_if_needed (); highlight_x = puzzle.size + 5; break;
+
+            case "1": case "KP_1": set_highlight_position_if_needed (); highlight_y = 0; break;
+            case "2": case "KP_2": set_highlight_position_if_needed (); highlight_y = 1; break;
+            case "3": case "KP_3": set_highlight_position_if_needed (); highlight_y = 2; break;
+            case "4": case "KP_4": set_highlight_position_if_needed (); highlight_y = 3; break;
+            case "5": case "KP_5": set_highlight_position_if_needed (); highlight_y = 4; break;
+            case "6": case "KP_6": set_highlight_position_if_needed (); highlight_y = 5; break;
+
+            case "Home":
+            case "KP_Home":
+                set_highlight_position_if_needed ();
+                highlight_x = 0;
+                break;
+            case "End":
+            case "KP_End":
+                set_highlight_position_if_needed ();
+                highlight_x = puzzle.size * 2 - 1;
+                break;
+            case "Page_Up":
+            case "KP_Page_Up":
+                set_highlight_position_if_needed ();
+                highlight_y = 0;
+                break;
+            case "Page_Down":
+            case "KP_Next":     // TODO use KP_Page_Down instead of KP_Next, probably a gtk+ or vala bug; 
check also KP_Prior
+                set_highlight_position_if_needed ();
+                highlight_y = puzzle.size - 1;
+                break;
+
+            // allow <Tab> and <Shift><Tab> to change focus
+            default:
+                return false;
+        }
+
+        highlight_set = true;
+
+        if (key == "Escape")
+        {
+            if (tile_selection)
+                clear_keyboard_highlight (/* only selection */ true);
+            else
+                clear_keyboard_highlight (/* only selection */ false);
+        }
+        else
+            show_highlight = true;
+
+        queue_draw_tile (old_highlight_x, old_highlight_y);
+        if ((old_highlight_x != highlight_x)
+         || (old_highlight_y != highlight_y))
+            queue_draw_tile (highlight_x, highlight_y);
+        return true;
+    }
+
+    private void set_highlight_position_if_needed ()
+    {
+        if (highlight_set)
+            /* If keyboard highlight is already set (and visible), this is good. */
+            return;
+
+        // TODO better
+        highlight_x = puzzle.size;
+        highlight_y = 0;
+    }
+
+    private void clear_keyboard_highlight (bool only_selection)
+    {
+        if (!only_selection)
+        {
+            show_highlight = false;
+            queue_draw_tile (highlight_x, highlight_y);
+            highlight_set = false;
+            highlight_x = uint8.MAX;
+            highlight_y = uint8.MAX;
+            old_highlight_x = uint8.MAX;
+            old_highlight_y = uint8.MAX;
+        }
+        if (tile_selection)
+        {
+            tile_selection = false;
+            queue_draw_tile (kbd_selected_x, kbd_selected_y);
+            kbd_selected_x = uint8.MAX;
+            kbd_selected_y = uint8.MAX;
+        }
+    }
 }
diff --git a/src/theme-extrusion.vala b/src/theme-extrusion.vala
index e06b4ad..17ce04f 100644
--- a/src/theme-extrusion.vala
+++ b/src/theme-extrusion.vala
@@ -24,9 +24,11 @@ private class ExtrusionTheme : Theme
     * * colors arrays
     \*/
 
-    private Cairo.Pattern tile_colors_h [10];
-    private Cairo.Pattern tile_colors_v [10];
-    private Cairo.Pattern tile_shadows  [10];
+    private Cairo.Pattern tile_colors_h     [10];
+    private Cairo.Pattern tile_colors_v     [10];
+    private Cairo.Pattern tile_highlights_h [10];
+    private Cairo.Pattern tile_highlights_v [10];
+    private Cairo.Pattern tile_shadows      [10];
 
     private unowned Cairo.Pattern text_colors [10];
     private Cairo.Pattern black_text_color = new Cairo.Pattern.rgb (0.0, 0.0, 0.0);
@@ -48,14 +50,16 @@ private class ExtrusionTheme : Theme
         make_color_pattern (8, "c061cb", true  );   // purple       // Purple 2
         make_color_pattern (9, "f6f5f4", false );   // white        // Light 2
 
-        paused_color_h = make_dir_color_pattern ("CCCCCC", /* vertical */ false);
-        paused_color_v = make_dir_color_pattern ("CCCCCC", /* vertical */ true);
+        paused_color_h = make_dir_color_pattern ("CCCCCC", /* vertical */ false, 1.0);
+        paused_color_v = make_dir_color_pattern ("CCCCCC", /* vertical */ true , 1.0);
     }
 
     private void make_color_pattern (uint position, string color, bool white_text)
     {
-        tile_colors_h [position] = make_dir_color_pattern (color, /* vertical */ false);
-        tile_colors_v [position] = make_dir_color_pattern (color, /* vertical */ true);
+        tile_colors_h     [position] = make_dir_color_pattern (color, /* vertical */ false, 1.00);
+        tile_colors_v     [position] = make_dir_color_pattern (color, /* vertical */ true , 1.00);
+        tile_highlights_h [position] = make_dir_color_pattern (color, /* vertical */ false, 1.35);
+        tile_highlights_v [position] = make_dir_color_pattern (color, /* vertical */ true , 1.35);
 
         tile_shadows  [position] = make_shadow_color_pattern (color);
 
@@ -65,11 +69,11 @@ private class ExtrusionTheme : Theme
             text_colors [position] = black_text_color;
     }
 
-    private static Cairo.Pattern make_dir_color_pattern (string color, bool vertical)
+    private static Cairo.Pattern make_dir_color_pattern (string color, bool vertical, double color_factor)
     {
-        double r0 = (hex_value (color [0]) * 16 + hex_value (color [1])) / 255.0;
-        double g0 = (hex_value (color [2]) * 16 + hex_value (color [3])) / 255.0;
-        double b0 = (hex_value (color [4]) * 16 + hex_value (color [5])) / 255.0;
+        double r0 = (hex_value (color [0]) * 16 + hex_value (color [1])) / 255.0 * color_factor;
+        double g0 = (hex_value (color [2]) * 16 + hex_value (color [3])) / 255.0 * color_factor;
+        double b0 = (hex_value (color [4]) * 16 + hex_value (color [5])) / 255.0 * color_factor;
 
         double r1 = double.min (r0 + 0.01, 1.0);
         double g1 = double.min (g0 + 0.01, 1.0);
@@ -128,6 +132,9 @@ private class ExtrusionTheme : Theme
 
     private double arrow_opacity;
 
+    /* highlight */
+    private Cairo.Pattern highlight_tile_pattern;
+
     /* tile only */
     private const uint radius_percent = 8;
     private Cairo.Matrix matrix;
@@ -166,12 +173,23 @@ private class ExtrusionTheme : Theme
         arrow_clip_w = 2.0 * arrow_x + arrow_w;
         arrow_clip_h = 2.0 * new_size;
 
+        /* highlight */
+        half_tile_size = new_size * 0.5;    // also for tile
+        double highlight_radius = new_size * 0.45;
+        highlight_tile_pattern = new Cairo.Pattern.radial (half_tile_size, half_tile_size, 0.0,
+                                                           half_tile_size, half_tile_size, highlight_radius);
+        highlight_tile_pattern.add_color_stop_rgba (0.0, 1.0, 1.0, 1.0, 1.0);
+        highlight_tile_pattern.add_color_stop_rgba (0.2, 1.0, 1.0, 1.0, 0.8);
+        highlight_tile_pattern.add_color_stop_rgba (0.3, 1.0, 1.0, 1.0, 0.5);
+        highlight_tile_pattern.add_color_stop_rgba (0.4, 1.0, 1.0, 1.0, 0.2);
+        highlight_tile_pattern.add_color_stop_rgba (0.5, 1.0, 1.0, 1.0, 0.1);
+        highlight_tile_pattern.add_color_stop_rgba (1.0, 1.0, 1.0, 1.0, 0.0);
+
         /* tile */
         matrix = Cairo.Matrix.identity ();
         matrix.scale (1.0 / new_size, 1.0 / new_size);
         tile_margin = uint.max ((uint) (new_size * 0.03), 2);
         tile_size = (int) new_size - (int) tile_margin * 2;
-        half_tile_size = new_size * 0.5;
         overdraw_top = (int) (1.5 * tile_margin);
         extrusion = -overdraw_top;
 
@@ -232,6 +250,23 @@ private class ExtrusionTheme : Theme
         context.restore ();
     }
 
+    /*\
+    * * drawing highlight
+    \*/
+
+    internal override void draw_highlight (Cairo.Context context, bool has_tile)
+    {
+        context.save ();
+
+        if (has_tile)
+            context.translate (0.0, extrusion);
+
+        context.set_source (highlight_tile_pattern);
+        context.rectangle (0.0, 0.0, /* width and height */ size, size);
+        context.fill ();
+        context.restore ();
+    }
+
     /*\
     * * drawing tiles
     \*/
@@ -242,7 +277,7 @@ private class ExtrusionTheme : Theme
         draw_tile_background (context, paused_color_h, paused_color_v, paused_color_h, paused_color_v);
     }
 
-    internal override void draw_tile (Cairo.Context context, Tile tile)
+    internal override void draw_tile (Cairo.Context context, Tile tile, bool highlight)
     {
         tile_colors_h [tile.north].set_matrix (matrix);
         tile_colors_h [tile.east ].set_matrix (matrix);
@@ -254,7 +289,10 @@ private class ExtrusionTheme : Theme
         tile_colors_v [tile.west ].set_matrix (matrix);
 
         draw_tile_shadow (context, tile_shadows [tile.north], tile_shadows [tile.east], tile_shadows 
[tile.south], tile_shadows [tile.west]);
-        draw_tile_background (context, tile_colors_h [tile.north], tile_colors_v [tile.east], tile_colors_h 
[tile.south], tile_colors_v [tile.west]);
+        if (highlight)
+            draw_tile_background (context, tile_highlights_h [tile.north], tile_highlights_v [tile.east], 
tile_highlights_h [tile.south], tile_highlights_v [tile.west]);
+        else
+            draw_tile_background (context, tile_colors_h [tile.north], tile_colors_v [tile.east], 
tile_colors_h [tile.south], tile_colors_v [tile.west]);
 
         context.select_font_face ("Sans", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
         context.set_font_size (font_size);
diff --git a/src/theme-neoretro.vala b/src/theme-neoretro.vala
index 6f5a1ae..1900122 100644
--- a/src/theme-neoretro.vala
+++ b/src/theme-neoretro.vala
@@ -24,8 +24,10 @@ private class NeoRetroTheme : Theme
     * * colors arrays
     \*/
 
-    private Cairo.Pattern tile_colors_h [10];
-    private Cairo.Pattern tile_colors_v [10];
+    private Cairo.Pattern tile_colors_h     [10];
+    private Cairo.Pattern tile_colors_v     [10];
+    private Cairo.Pattern tile_highlights_h [10];
+    private Cairo.Pattern tile_highlights_v [10];
 
     private unowned Cairo.Pattern text_colors [10];
     private Cairo.Pattern black_text_color = new Cairo.Pattern.rgb (0.0, 0.0, 0.0);
@@ -47,14 +49,16 @@ private class NeoRetroTheme : Theme
         make_color_pattern (8, "a021a6", true  );   // 40   75  325 // purple
         make_color_pattern (9, "e2e2e2", false );   // 90           // white
 
-        paused_color_h = make_h_color_pattern ("CCCCCC");
-        paused_color_v = make_v_color_pattern ("CCCCCC");
+        paused_color_h = make_dir_color_pattern ("CCCCCC", /* vertical */ false, 1.0);
+        paused_color_v = make_dir_color_pattern ("CCCCCC", /* vertical */ true , 1.0);
     }
 
     private void make_color_pattern (uint position, string color, bool white_text)
     {
-        tile_colors_h [position] = make_h_color_pattern (color);
-        tile_colors_v [position] = make_v_color_pattern (color);
+        tile_colors_h     [position] = make_dir_color_pattern (color, /* vertical */ false, 1.0);
+        tile_colors_v     [position] = make_dir_color_pattern (color, /* vertical */ true , 1.0);
+        tile_highlights_h [position] = make_dir_color_pattern (color, /* vertical */ false, 1.4);
+        tile_highlights_v [position] = make_dir_color_pattern (color, /* vertical */ true , 1.4);
 
         if (white_text)
             text_colors [position] = white_text_color;
@@ -62,11 +66,11 @@ private class NeoRetroTheme : Theme
             text_colors [position] = black_text_color;
     }
 
-    private static Cairo.Pattern make_h_color_pattern (string color)
+    private static Cairo.Pattern make_dir_color_pattern (string color, bool vertical, double color_factor)
     {
-        double r0 = (hex_value (color [0]) * 16 + hex_value (color [1])) / 255.0;
-        double g0 = (hex_value (color [2]) * 16 + hex_value (color [3])) / 255.0;
-        double b0 = (hex_value (color [4]) * 16 + hex_value (color [5])) / 255.0;
+        double r0 = (hex_value (color [0]) * 16 + hex_value (color [1])) / 255.0 * color_factor;
+        double g0 = (hex_value (color [2]) * 16 + hex_value (color [3])) / 255.0 * color_factor;
+        double b0 = (hex_value (color [4]) * 16 + hex_value (color [5])) / 255.0 * color_factor;
 
         double r1 = double.min (r0 + 0.10, 1.0);
         double g1 = double.min (g0 + 0.10, 1.0);
@@ -80,35 +84,11 @@ private class NeoRetroTheme : Theme
         double g5 = double.min (g0 + 0.15, 1.0);
         double b5 = double.min (b0 + 0.15, 1.0);
 
-        Cairo.Pattern pattern = new Cairo.Pattern.linear (0.0, 0.0, 0.0, 1.0);
-        pattern.add_color_stop_rgba (0.00,  r2,  g2,  b2, 1.0);
-        pattern.add_color_stop_rgba (0.08,  r1,  g1,  b1, 1.0);
-        pattern.add_color_stop_rgba (0.50,  r5,  g5,  b5, 1.0);
-        pattern.add_color_stop_rgba (0.92,  r1,  g1,  b1, 1.0);
-        pattern.add_color_stop_rgba (1.00,  r0,  g0,  b0, 1.0);
-
-        return pattern;
-    }
-
-    private static Cairo.Pattern make_v_color_pattern (string color)
-    {
-        double r0 = (hex_value (color [0]) * 16.0 + hex_value (color [1]) + 0.02) / 255.0;
-        double g0 = (hex_value (color [2]) * 16.0 + hex_value (color [3]) + 0.02) / 255.0;
-        double b0 = (hex_value (color [4]) * 16.0 + hex_value (color [5]) + 0.02) / 255.0;
-
-        double r1 = double.min (r0 + 0.10, 1.0);
-        double g1 = double.min (g0 + 0.10, 1.0);
-        double b1 = double.min (b0 + 0.10, 1.0);
-
-        double r2 = double.min (r0 + 0.20, 1.0);
-        double g2 = double.min (g0 + 0.20, 1.0);
-        double b2 = double.min (b0 + 0.20, 1.0);
-
-        double r5 = double.min (r0 + 0.15, 1.0);
-        double g5 = double.min (g0 + 0.15, 1.0);
-        double b5 = double.min (b0 + 0.15, 1.0);
-
-        Cairo.Pattern pattern = new Cairo.Pattern.linear (0.0, 0.0, 1.0, 0.0);
+        Cairo.Pattern pattern;
+        if (vertical)
+            pattern = new Cairo.Pattern.linear (0.0, 0.0, 1.0, 0.0);
+        else
+            pattern = new Cairo.Pattern.linear (0.0, 0.0, 0.0, 1.0);
         pattern.add_color_stop_rgba (0.00,  r2,  g2,  b2, 1.0);
         pattern.add_color_stop_rgba (0.08,  r1,  g1,  b1, 1.0);
         pattern.add_color_stop_rgba (0.50,  r5,  g5,  b5, 1.0);
@@ -157,6 +137,9 @@ private class NeoRetroTheme : Theme
     private Cairo.MeshPattern socket_pattern;
     private Cairo.Matrix matrix;                // also used for tile
 
+    /* highlight */
+    private Cairo.Pattern highlight_tile_pattern;
+
     /* tile only */
     private uint tile_margin;
     private int tile_size;
@@ -195,10 +178,21 @@ private class NeoRetroTheme : Theme
 
         init_socket_pattern ();
 
+        /* highlight */
+        half_tile_size = new_size * 0.5;    // also for tile
+        double highlight_radius = new_size * 0.45;
+        highlight_tile_pattern = new Cairo.Pattern.radial (half_tile_size, half_tile_size, 0.0,
+                                                           half_tile_size, half_tile_size, highlight_radius);
+        highlight_tile_pattern.add_color_stop_rgba (0.0, 1.0, 1.0, 1.0, 1.0);
+        highlight_tile_pattern.add_color_stop_rgba (0.2, 1.0, 1.0, 1.0, 0.8);
+        highlight_tile_pattern.add_color_stop_rgba (0.3, 1.0, 1.0, 1.0, 0.5);
+        highlight_tile_pattern.add_color_stop_rgba (0.4, 1.0, 1.0, 1.0, 0.2);
+        highlight_tile_pattern.add_color_stop_rgba (0.5, 1.0, 1.0, 1.0, 0.1);
+        highlight_tile_pattern.add_color_stop_rgba (1.0, 1.0, 1.0, 1.0, 0.0);
+
         /* tile */
         tile_margin = uint.min ((uint) (new_size * 0.05), 2) - 1;
         tile_size = (int) new_size - (int) tile_margin * 2;
-        half_tile_size = new_size * 0.5;
 
         /* numbers */
         font_size = new_size * 4.0 / 19.0;
@@ -320,6 +314,17 @@ private class NeoRetroTheme : Theme
         context.restore ();
     }
 
+    /*\
+    * * drawing highlight
+    \*/
+
+    internal override void draw_highlight (Cairo.Context context, bool has_tile)
+    {
+        context.set_source (highlight_tile_pattern);
+        context.rectangle (0.0, 0.0, /* width and height */ size, size);
+        context.fill ();
+    }
+
     /*\
     * * drawing tiles
     \*/
@@ -329,18 +334,26 @@ private class NeoRetroTheme : Theme
         draw_tile_background (context, paused_color_h, paused_color_v, paused_color_h, paused_color_v);
     }
 
-    internal override void draw_tile (Cairo.Context context, Tile tile)
+    internal override void draw_tile (Cairo.Context context, Tile tile, bool highlight)
     {
-        tile_colors_h [tile.north].set_matrix (matrix);
-        tile_colors_h [tile.east ].set_matrix (matrix);
-        tile_colors_h [tile.south].set_matrix (matrix);
-        tile_colors_h [tile.west ].set_matrix (matrix);
-        tile_colors_v [tile.north].set_matrix (matrix);
-        tile_colors_v [tile.east ].set_matrix (matrix);
-        tile_colors_v [tile.south].set_matrix (matrix);
-        tile_colors_v [tile.west ].set_matrix (matrix);
-
-        draw_tile_background (context, tile_colors_h [tile.north], tile_colors_v [tile.east], tile_colors_h 
[tile.south], tile_colors_v [tile.west]);
+        if (highlight)
+        {
+            tile_highlights_h [tile.north].set_matrix (matrix);
+            tile_highlights_h [tile.south].set_matrix (matrix);
+            tile_highlights_v [tile.east ].set_matrix (matrix);
+            tile_highlights_v [tile.west ].set_matrix (matrix);
+
+            draw_tile_background (context, tile_highlights_h [tile.north], tile_highlights_v [tile.east], 
tile_highlights_h [tile.south], tile_highlights_v [tile.west]);
+        }
+        else
+        {
+            tile_colors_h [tile.north].set_matrix (matrix);
+            tile_colors_h [tile.south].set_matrix (matrix);
+            tile_colors_v [tile.east ].set_matrix (matrix);
+            tile_colors_v [tile.west ].set_matrix (matrix);
+
+            draw_tile_background (context, tile_colors_h [tile.north], tile_colors_v [tile.east], 
tile_colors_h [tile.south], tile_colors_v [tile.west]);
+        }
 
         context.select_font_face ("Sans", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
         context.set_font_size (font_size);
diff --git a/src/theme-nostalgia.vala b/src/theme-nostalgia.vala
index d3f37e0..4d71977 100644
--- a/src/theme-nostalgia.vala
+++ b/src/theme-nostalgia.vala
@@ -102,6 +102,9 @@ private class NostalgiaTheme : Theme
     private double size_minus_socket_depth;
     private double size_minus_two_socket_depths;
 
+    /* highlight */
+    private Cairo.Pattern highlight_tile_pattern;
+
     /* tile only */
     private double tile_depth;
     private double size_minus_tile_depth;
@@ -128,13 +131,24 @@ private class NostalgiaTheme : Theme
         configure_arrow (new_size);
         configure_socket (new_size);
 
+        /* highlight */
+        half_tile_size = new_size * 0.5;    // also for tile
+        double highlight_radius = new_size * 0.45;
+        highlight_tile_pattern = new Cairo.Pattern.radial (half_tile_size, half_tile_size, 0.0,
+                                                           half_tile_size, half_tile_size, highlight_radius);
+        highlight_tile_pattern.add_color_stop_rgba (0.0, 1.0, 1.0, 1.0, 1.0);
+        highlight_tile_pattern.add_color_stop_rgba (0.2, 1.0, 1.0, 1.0, 0.8);
+        highlight_tile_pattern.add_color_stop_rgba (0.3, 1.0, 1.0, 1.0, 0.5);
+        highlight_tile_pattern.add_color_stop_rgba (0.4, 1.0, 1.0, 1.0, 0.2);
+        highlight_tile_pattern.add_color_stop_rgba (0.5, 1.0, 1.0, 1.0, 0.1);
+        highlight_tile_pattern.add_color_stop_rgba (1.0, 1.0, 1.0, 1.0, 0.0);
+
         /* tiles */
         tile_depth = double.min (new_size * 0.05, 4.0);
         size_minus_tile_depth = (double) new_size - tile_depth;
         tile_dx = (Math.SQRT2 + 1.0) * tile_depth;
         tile_dy =  Math.SQRT2        * tile_depth;
         size_minus_tile_dx = (double) new_size - tile_dx;
-        half_tile_size = new_size * 0.5;
         half_tile_size_minus_dy = half_tile_size - tile_dy;
         half_tile_size_plus_dy = half_tile_size + tile_dy;
         size_minus_one = (double) (new_size - 1);
@@ -268,6 +282,17 @@ private class NostalgiaTheme : Theme
         context.fill ();
     }
 
+    /*\
+    * * drawing highlight
+    \*/
+
+    internal override void draw_highlight (Cairo.Context context, bool has_tile)
+    {
+        context.set_source (highlight_tile_pattern);
+        context.rectangle (0.0, 0.0, /* width and height */ size, size);
+        context.fill ();
+    }
+
     /*\
     * * drawing tiles
     \*/
@@ -277,7 +302,7 @@ private class NostalgiaTheme : Theme
         draw_tile_background (context, paused_color, paused_color, paused_color, paused_color);
     }
 
-    internal override void draw_tile (Cairo.Context context, Tile tile)
+    internal override void draw_tile (Cairo.Context context, Tile tile, bool highlight)
     {
         draw_tile_background (context, tile_colors [tile.north], tile_colors [tile.east], tile_colors 
[tile.south], tile_colors [tile.west]);
 
@@ -287,6 +312,13 @@ private class NostalgiaTheme : Theme
         draw_number (context, text_colors [tile.south], half_tile_size, south_number_y, tile.south);
         draw_number (context, text_colors [tile.east ], east_number_x , half_tile_size, tile.east);
         draw_number (context, text_colors [tile.west ], west_number_x , half_tile_size, tile.west);
+
+        if (highlight)
+        {
+            context.set_source_rgba (1.0, 1.0, 1.0, 0.3);
+            context.rectangle (0.0, 0.0, size, size);
+            context.fill ();
+        }
     }
 
     private void draw_tile_background (Cairo.Context context, Cairo.Pattern north_color, Cairo.Pattern 
east_color, Cairo.Pattern south_color, Cairo.Pattern west_color)
diff --git a/src/theme-synesthesia.vala b/src/theme-synesthesia.vala
index 74eecb4..470fe64 100644
--- a/src/theme-synesthesia.vala
+++ b/src/theme-synesthesia.vala
@@ -27,6 +27,7 @@ private class SynesthesiaTheme : Theme
     private Cairo.Pattern text_colors [10];
     private Cairo.Pattern paused_color;
     private Cairo.Pattern tile_color;
+    private Cairo.Pattern highlight_color;
 
     construct
     {                                                       // based on GNOME color palette
@@ -43,6 +44,7 @@ private class SynesthesiaTheme : Theme
 
         paused_color = make_color_pattern ("CCCCCC");
         tile_color = make_color_pattern ("CDCADA", /* transparency */ true);
+        highlight_color = make_color_pattern ("DACACD", /* transparency */ true);
     }
 
     private static Cairo.Pattern make_color_pattern (string color, bool transparency = false)
@@ -85,6 +87,9 @@ private class SynesthesiaTheme : Theme
     private Cairo.MeshPattern socket_pattern;
     private Cairo.Matrix matrix;                // also used for tile
 
+    /* highlight */
+    private Cairo.Pattern highlight_tile_pattern;
+
     /* tile only */
     private uint tile_margin;
     private int tile_size;
@@ -119,10 +124,21 @@ private class SynesthesiaTheme : Theme
         size_minus_two_tile_depths = (double) (new_size - tile_depth * 2);
         init_socket_pattern ();
 
+        /* highlight */
+        half_size = new_size * 0.5;    // also for tile
+        double highlight_radius = new_size * 0.45;
+        highlight_tile_pattern = new Cairo.Pattern.radial (half_size, half_size, 0.0,
+                                                           half_size, half_size, highlight_radius);
+        highlight_tile_pattern.add_color_stop_rgba (0.0, 1.0, 1.0, 1.0, 1.0);
+        highlight_tile_pattern.add_color_stop_rgba (0.2, 1.0, 1.0, 1.0, 0.8);
+        highlight_tile_pattern.add_color_stop_rgba (0.3, 1.0, 1.0, 1.0, 0.5);
+        highlight_tile_pattern.add_color_stop_rgba (0.4, 1.0, 1.0, 1.0, 0.2);
+        highlight_tile_pattern.add_color_stop_rgba (0.5, 1.0, 1.0, 1.0, 0.1);
+        highlight_tile_pattern.add_color_stop_rgba (1.0, 1.0, 1.0, 1.0, 0.0);
+
         /* tile */
         tile_margin = uint.min ((uint) (new_size * 0.05), 2) - 1;
         tile_size = (int) new_size - (int) tile_margin * 2;
-        half_size = new_size * 0.5;
         half_tile_size = tile_size * 0.5;
 
         /* numbers */
@@ -215,6 +231,17 @@ private class SynesthesiaTheme : Theme
         context.restore ();
     }
 
+    /*\
+    * * drawing highlight
+    \*/
+
+    internal override void draw_highlight (Cairo.Context context, bool has_tile)
+    {
+        context.set_source (highlight_tile_pattern);
+        context.rectangle (0.0, 0.0, /* width and height */ size, size);
+        context.fill ();
+    }
+
     /*\
     * * drawing tiles
     \*/
@@ -224,9 +251,12 @@ private class SynesthesiaTheme : Theme
         draw_tile_background (context, paused_color);
     }
 
-    internal override void draw_tile (Cairo.Context context, Tile tile)
+    internal override void draw_tile (Cairo.Context context, Tile tile, bool highlight)
     {
-        draw_tile_background (context, tile_color);
+        if (highlight)
+            draw_tile_background (context, highlight_color);
+        else
+            draw_tile_background (context, tile_color);
 
         context.select_font_face ("Cantarell", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
         context.set_font_size (font_size);



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