[iagno] Improve HistoryButton.



commit 771db5e3a21d62cf894ebda810b69ce370139928
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Sun Jan 26 02:18:47 2020 +0100

    Improve HistoryButton.
    
    Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1772813
    and partially syncs code with the one from Four-in-a-row.

 data/ui/history-button.ui |  28 ++++++--
 src/history-button.vala   | 171 +++++++++++++++++++++++++++++++++-------------
 src/iagno.vala            |  41 ++++++++---
 3 files changed, 176 insertions(+), 64 deletions(-)
---
diff --git a/data/ui/history-button.ui b/data/ui/history-button.ui
index a6c56c4..28ff6a7 100644
--- a/data/ui/history-button.ui
+++ b/data/ui/history-button.ui
@@ -4,7 +4,7 @@
 
   Copyright 2012 Tiffany Antopolski and Robert Ancell
   Copyright 2013, 2014, 2015 Michael Catanzaro
-  Copyright 2014, 2015, 2019 Arnaud Bonatti
+  Copyright 2014, 2015, 2019, 2020 Arnaud Bonatti
 
   GNOME Reversi is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -25,8 +25,28 @@
     <property name="valign">center</property>
     <property name="can-focus">True</property>
     <property name="focus-on-click">False</property>
-    <style>
-      <class name="history-button"/>
-    </style>
+    <property name="width-request">56</property>
+    <child>
+      <object class="GtkStack" id="stack">
+        <property name="visible">True</property>
+        <property name="visible-child">drawing</property>
+        <child>
+          <object class="GtkDrawingArea" id="drawing">
+            <property name="visible">True</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="ellipsize">end</property>
+            <!-- Translators: label of the game status button (in the headerbar, next to the hamburger 
button), when the game is finished; please keep the string as small as possible (3~5 characters) -->
+            <property name="label" translatable="yes">End!</property>
+          </object>
+          <packing>
+            <property name="name">label</property>
+          </packing>
+        </child>
+      </object>
+    </child>
   </template>
 </interface>
diff --git a/src/history-button.vala b/src/history-button.vala
index b79c192..4cd3673 100644
--- a/src/history-button.vala
+++ b/src/history-button.vala
@@ -23,79 +23,152 @@ using Gtk;
 [GtkTemplate (ui = "/org/gnome/Reversi/ui/history-button.ui")]
 private class HistoryButton : MenuButton, AdaptativeWidget
 {
-    private GLib.Menu history_menu;
-    private GLib.Menu finish_menu;
+    [CCode (notify = false)] public ThemeManager theme_manager { private get; protected construct; }
 
-    private string history_button_light_label;
-    private string history_button_dark_label;
+    [GtkChild] private Stack stack;
+    [GtkChild] private DrawingArea drawing;
 
-    construct
+    internal HistoryButton (GLib.Menu menu, ThemeManager theme_manager)
     {
-        history_menu = new GLib.Menu ();
-        /* Translators: history menu entry (with a mnemonic that appears pressing Alt) */
-        history_menu.append (_("_Undo last move"), "ui.undo");
-        history_menu.freeze ();
+        Object (menu_model: menu, theme_manager: theme_manager);
+    }
 
-        finish_menu = new GLib.Menu ();
-        /* Translators: history menu entry, when game is finished, after final animation; undoes the 
animation (with a mnemonic that appears pressing Alt) */
-        finish_menu.append (_("_Show final board"), "ui.undo");
-        finish_menu.freeze ();
+    construct
+    {
+        drawing.configure_event.connect (configure_drawing);
+        drawing.draw.connect (update_drawing);
+        theme_manager.theme_changed.connect (() => {
+                if (!drawing_configured)
+                    return;
+                tiles_pattern = null;
+                if (current_player != Player.NONE)
+                    drawing.queue_draw ();
+            });
+    }
 
-        bool dir_is_ltr = get_locale_direction () == TextDirection.LTR;
-        history_button_light_label = dir_is_ltr ? "‎⮚ ⚪" : /* yes */ "‏⮘ ⚪";    /* both have an LTR/RTL mark 
*/
-        history_button_dark_label  = dir_is_ltr ? "‎⮚ ⚫" : /* yes */ "‏⮘ ⚫";    /* both have an LTR/RTL mark 
*/
+    protected override void set_window_size (AdaptativeWidget.WindowSize new_size)
+    {
+    }
 
-        new_game ();
+    internal void set_player (Player player)
+    {
+        current_player = player;
+        if (player == Player.NONE)
+            stack.set_visible_child_name ("label");
+        else
+        {
+            stack.set_visible_child (drawing);
+            drawing.queue_draw ();
+        }
     }
 
-    private bool is_extra_thin = true;
-    protected override void set_window_size (AdaptativeWidget.WindowSize new_size)
+    internal inline void update_menu (GLib.Menu menu)
     {
-        bool _is_extra_thin = AdaptativeWidget.WindowSize.is_extra_thin (new_size);
-        if (_is_extra_thin == is_extra_thin)
-            return;
-        is_extra_thin = _is_extra_thin;
-        update_label (last_player);
+        set_menu_model (menu);
     }
 
     /*\
-    * * internal calls
+    * * drawing
     \*/
 
-    internal inline void update_menu (bool finish_animation)
+    private bool drawing_configured = false;
+    private int drawing_height      = int.MIN;
+    private int drawing_width       = int.MIN;
+    private double arrow_half_width = - double.MAX;
+    private int board_x             = int.MIN;
+    private int board_y             = int.MIN;
+    private const int pixbuf_margin = 1;
+
+    private Gdk.Pixbuf tileset_pixbuf;
+
+    private bool configure_drawing ()
     {
-        set_menu_model (finish_animation ? finish_menu : history_menu);
+        int height          = drawing.get_allocated_height ();
+        int width           = drawing.get_allocated_width ();
+        int new_height      = (int) double.min (height, width / 2.0);
+
+        drawing_height      = new_height;
+        arrow_half_width    = ((double) drawing_height - 2.0 * pixbuf_margin) / 4.0;
+        tiles_pattern       = null;
+
+        bool vertical_fill  = height == new_height;
+        drawing_width       =  vertical_fill ? (int) (new_height * 2.0) : width;
+        board_x             =  vertical_fill ? (int) ((width  - drawing_width)  / 2.0) : 0;
+        board_y             = !vertical_fill ? (int) ((height - drawing_height) / 2.0) : 0;
+
+        drawing_configured  = true;
+        return true;
+    }
+
+    private Cairo.Pattern? tiles_pattern = null;
+    private void init_pattern (Cairo.Context cr)    // TODO unduplicate with ReversiView
+    {
+        Cairo.Surface surface = new Cairo.Surface.similar (cr.get_target (), Cairo.Content.COLOR_ALPHA, 
drawing_height * 8,
+                                                                                                        
drawing_height * 4);
+        Cairo.Context context = new Cairo.Context (surface);
+        Rsvg.DimensionData size = theme_manager.tileset_handle.get_dimensions ();
+        context.scale ((double) drawing_height * 8.0 / (double) size.width,
+                       (double) drawing_height * 4.0 / (double) size.height);
+        theme_manager.tileset_handle.render_cairo (context);
+        tiles_pattern = new Cairo.Pattern.for_surface (surface);
+    }
+
+    private bool update_drawing (Cairo.Context cr)
+    {
+        if (!drawing_configured)
+            return false;
+
+        if (tiles_pattern == null)
+            init_pattern (cr);
+
+        draw_arrow (cr);
+        draw_piece (cr);
+        return true;
     }
 
-    internal inline void new_game ()
+    private const double arrow_margin_top = 3.0;
+    private void draw_arrow (Cairo.Context cr)
     {
-        update_label (Player.DARK);
-        update_menu (/* final animation */ false);
+        cr.save ();
+
+        cr.set_line_cap (Cairo.LineCap.ROUND);
+        cr.set_line_join (Cairo.LineJoin.ROUND);
+
+        cr.set_source_rgba (/* red */ 0.5, /* green */ 0.5, /* blue */ 0.5, 1.0);
+        cr.set_line_width (/* looks good */ 2.0);
+
+        cr.translate (board_x, board_y);
+        cr.move_to (      arrow_half_width, arrow_margin_top);
+        cr.line_to (3.0 * arrow_half_width, drawing_height / 2.0);
+        cr.line_to (      arrow_half_width, drawing_height - arrow_margin_top);
+        cr.stroke ();
+
+        cr.restore ();
     }
 
-    private Player last_player = Player.NONE;
-    internal void update_label (Player player)
+    private Player current_player = Player.NONE;
+    private void draw_piece (Cairo.Context cr)
     {
-        last_player = player;
-        switch (player)
+        int pixmap;
+        switch (current_player)
         {
-            case Player.LIGHT:
-                    set_label (history_button_light_label);  break;
-            case Player.DARK:
-                    set_label (history_button_dark_label);   break;
-            case Player.NONE:
-                if (is_extra_thin)
-                    /* Translators: label of the game status button (in the headerbar, next to the hamburger 
button), at the end of the game; this string is for when the window is really small, so keep the string as 
small as possible (3~5 characters) */
-                    set_label (_("End!"));
-
-                else
-                    /* Translators: label of the game status button (in the headerbar, next to the hamburger 
button), at the end of the game, if the window is not too thin */
-                    set_label (_("Finished!"));              break;
+            case Player.NONE    : return;
+            case Player.DARK    : pixmap = 1;   break;
+            case Player.LIGHT   : pixmap = 31;  break;
             default: assert_not_reached ();
         }
 
-        Widget? history_label = get_child ();
-        if (history_label != null && (!) history_label is Label)
-            ((Label) (!) history_label).set_ellipsize (Pango.EllipsizeMode.END);
+        cr.save ();
+        Cairo.Matrix matrix = Cairo.Matrix.identity ();
+        int x = board_x + drawing_width - drawing_height;
+        matrix.translate (/* texture x */ (pixmap % 8) * drawing_height - /* x position */ x,
+                          /* texture y */ (pixmap / 8) * drawing_height - /* y position */ board_y);
+        ((!) tiles_pattern).set_matrix (matrix);
+        cr.set_source ((!) tiles_pattern);
+        cr.rectangle (x, board_y, drawing_height, drawing_height);
+
+        cr.clip ();
+        cr.paint ();
+        cr.restore ();
     }
 }
diff --git a/src/iagno.vala b/src/iagno.vala
index d1fa006..5145ee5 100644
--- a/src/iagno.vala
+++ b/src/iagno.vala
@@ -54,6 +54,9 @@ private class Iagno : Gtk.Application, BaseApplication
     private HistoryButton history_button_1;
     private HistoryButton history_button_2;
 
+    private GLib.Menu history_menu;
+    private GLib.Menu finish_menu;
+
     private ThemeManager theme_manager = new ThemeManager ();
 
     /* Computer player (if there is one) */
@@ -397,14 +400,28 @@ private class Iagno : Gtk.Application, BaseApplication
         appearance_menu.append_section (_("Highlights"), section);
         appearance_menu.freeze ();
 
-        history_button_1 = new HistoryButton ();
-        history_button_2 = new HistoryButton ();
-        view.notify_final_animation.connect ((undoing) => { history_button_1.update_menu (!undoing);
-                                                            history_button_2.update_menu (!undoing); });
+        /* history buttons */
+
+        history_menu = new GLib.Menu ();
+        /* Translators: history menu entry (with a mnemonic that appears pressing Alt) */
+        history_menu.append (_("_Undo last move"), "ui.undo");
+        history_menu.freeze ();
+
+        finish_menu = new GLib.Menu ();
+        /* Translators: history menu entry, when game is finished, after final animation; undoes the 
animation (with a mnemonic that appears pressing Alt) */
+        finish_menu.append (_("_Show final board"), "ui.undo");
+        finish_menu.freeze ();
+
+        history_button_1 = new HistoryButton (history_menu, theme_manager);
+        history_button_2 = new HistoryButton (history_menu, theme_manager);
+        view.notify_final_animation.connect ((undoing) => {
+                history_button_1.update_menu (undoing ? history_menu : finish_menu);
+                history_button_2.update_menu (undoing ? history_menu : finish_menu);
+            });
         history_button_1.show ();
         history_button_2.show ();
 
-        /* Window */
+        /* window */
         init_night_mode ();
         window = new GameWindow ("/org/gnome/Reversi/ui/iagno.css",
                                  PROGRAM_NAME,
@@ -697,8 +714,10 @@ private class Iagno : Gtk.Application, BaseApplication
         game.turn_ended.connect (turn_ended_cb);
         view.game = game;
 
-        history_button_1.new_game ();
-        history_button_2.new_game ();
+        history_button_1.set_player (Player.DARK);
+        history_button_2.set_player (Player.DARK);
+        history_button_1.update_menu (history_menu);
+        history_button_2.update_menu (history_menu);
 
         if (two_players)
             computer = null;
@@ -842,8 +861,8 @@ private class Iagno : Gtk.Application, BaseApplication
         requires (game_is_set)
     {
         window.finish_game ();
-        history_button_1.update_label (Player.NONE);
-        history_button_2.update_label (Player.NONE);
+        history_button_1.set_player (Player.NONE);
+        history_button_2.set_player (Player.NONE);
 
         if ((!game.reverse && game.n_light_tiles > game.n_dark_tiles)
          || ( game.reverse && game.n_light_tiles < game.n_dark_tiles))
@@ -900,8 +919,8 @@ private class Iagno : Gtk.Application, BaseApplication
     {
         /* for the move that just ended */
         play_sound (Sound.FLIP);
-        history_button_1.update_label (game.current_color);
-        history_button_2.update_label (game.current_color);
+        history_button_1.set_player (game.current_color);
+        history_button_2.set_player (game.current_color);
     }
 
     private void set_window_title ()


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