[gnome-sudoku] Mobile-friendly layout - better implementation



commit 0576c893f37a8c94dcc49a260f667fa9b5eec031
Author: lajonss <l3n1 dupaw eu>
Date:   Mon Dec 28 01:23:24 2020 +0100

    Mobile-friendly layout - better implementation
    
    Previous attempt (repositioning the control buttons):
    https://gitlab.gnome.org/GNOME/gnome-sudoku/-/merge_requests/32
    
    The appearance of the resulting layout was not satisfactory
    and the difficulty display did not fit the headerbar.
    
    To make the window look better buttons were moved from controls_box
    (placed next to the board) to the headerbar and the primary menu.
    The difficulty display was removed.
    To further improve the usability on small screens clock is now being
    moved to below the board when the window is too narrow.
    
    As a consequence of the changes following entities were removed:
    - main_squeezer (and libhandy dependency),
    - start_box_s (smaller version of the main menu),
    - aspect frames (previously used for controls placement).
    
    https://gitlab.gnome.org/GNOME/gnome-sudoku/-/issues/38

 data/sudoku-window.ui  | 258 +++++++++++++++----------------------------------
 meson.build            |   1 -
 src/gnome-sudoku.vala  |   4 +-
 src/meson.build        |   2 +-
 src/sudoku-window.vala | 102 +++++--------------
 5 files changed, 108 insertions(+), 259 deletions(-)
---
diff --git a/data/sudoku-window.ui b/data/sudoku-window.ui
index ea9e95e..462c238 100644
--- a/data/sudoku-window.ui
+++ b/data/sudoku-window.ui
@@ -2,6 +2,16 @@
 <interface>
   <!-- interface-requires gtk+ 3.10 -->
   <menu id="primary-menu">
+    <section id="controls-menu">
+      <item>
+        <attribute name="label" translatable="yes">_New Puzzle</attribute>
+        <attribute name="action">app.new-game</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Clear Board</attribute>
+        <attribute name="action">app.reset</attribute>
+      </item>
+    </section>
     <section>
       <submenu>
         <attribute name="label" translatable="yes">_Print</attribute>
@@ -100,40 +110,71 @@
           </object>
         </child>
         <child>
-          <object class="GtkMenuButton">
+          <object class="GtkBox">
             <property name="visible">True</property>
-            <property name="menu-model">primary-menu</property>
+            <property name="can-focus">False</property>
+            <property name="homogeneous">True</property>
+            <style>
+              <class name="raised"/>
+              <class name="linked"/>
+            </style>
             <child>
-              <object class="GtkImage">
+              <object class="GtkButton" id="play_pause_button">
                 <property name="visible">True</property>
-                <property name="halign">center</property>
-                <property name="icon-name">open-menu-symbolic</property>
+                <property name="sensitive">False</property>
+                <property name="valign">center</property>
+                <property name="tooltip-text" translatable="yes">Pause</property>
+                <property name="can-focus">True</property>
+                <property name="focus-on-click">False</property>
+                <property name="action-name">app.pause</property>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage" id="play_pause_image">
+                    <property name="icon-name">media-playback-pause-symbolic</property>
+                    <property name="visible">True</property>
+                    <property name="icon-size">1</property>
+                  </object>
+                </child>
               </object>
             </child>
-          </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="can-focus">False</property>
-            <property name="spacing">6</property>
             <child>
-              <object class="GtkImage" id="clock_image">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
-                <property name="icon-name">preferences-system-time-symbolic</property>
-                <property name="icon-size">1</property>
+              <object class="GtkButton" id="play_custom_game_button">
+                <property name="visible">False</property>
+                <property name="sensitive">False</property>
+                <property name="valign">center</property>
+                <property name="tooltip-text" translatable="yes">Start playing the custom puzzle you have 
created</property>
+                <property name="can-focus">True</property>
+                <property name="focus-on-click">False</property>
+                <property name="action-name">app.play-custom-game</property>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage">
+                    <property name="icon-name">emblem-ok-symbolic</property>
+                    <property name="visible">True</property>
+                    <property name="icon-size">1</property>
+                  </object>
+                </child>
               </object>
             </child>
             <child>
-              <object class="GtkLabel" id="clock_label">
+              <object class="GtkMenuButton">
                 <property name="visible">True</property>
-                <property name="can-focus">False</property>
-                <property name="halign">center</property>
+                <property name="menu-model">primary-menu</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="halign">center</property>
+                    <property name="icon-name">open-menu-symbolic</property>
+                  </object>
+                </child>
               </object>
+              <packing>
+                <property name="pack-type">end</property>
+              </packing>
             </child>
           </object>
           <packing>
@@ -167,21 +208,19 @@
         <property name="orientation">vertical</property>
         <property name="visible">True</property>
         <child>
-          <object class="HdySqueezer" id="main_squeezer">
+          <object class="GtkStack"> <!-- main_squeezer -->
             <property name="visible">True</property>
             <property name="margin">25</property>
             <property name="homogeneous">False</property>
             <property name="transition-type">crossfade</property>
             <property name="interpolate-size">True</property>
-            <signal name="draw" handler="main_squeezer_draw_cb" swapped="no"/>
             <child>
               <object class="GtkBox" id="start_box">
                 <property name="visible">True</property>
                 <property name="orientation">vertical</property>
-                <property name="halign">center</property>
+                <property name="halign">fill</property>
                 <property name="valign">center</property>
                 <property name="margin">0</property>
-                <property name="width-request">350</property>
                 <property name="height-request">350</property>
                 <child>
                   <object class="GtkFrame">
@@ -241,179 +280,40 @@
               </object> <!-- End of start_box -->
             </child>
             <child>
-              <object class="GtkBox" id="start_box_s">
-                <property name="visible">True</property>
-                <property name="orientation">vertical</property>
-                <property name="halign">center</property>
-                <property name="valign">center</property>
-                <property name="margin">0</property>
-                <property name="width-request">200</property>
-                <property name="height-request">200</property>
-                <child>
-                  <object class="GtkFrame">
-                    <property name="visible">True</property>
-                    <property name="shadow-type">GTK_SHADOW_IN</property>
-                    <child>
-                      <object class="SudokuMainMenu">
-                        <child>
-                          <object class="SudokuMainMenuItem">
-                            <property name="label" translatable="yes">_Easy</property>
-                            <property name="action-name">app.start-game</property>
-                            <property name="action-target">1</property>  <!-- 1 corresponds to enum 
DifficultyCategory.EASY -->
-                            <property name="height-request">40</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="SudokuMainMenuItem">
-                            <property name="label" translatable="yes">_Medium</property>
-                            <property name="action-name">app.start-game</property>
-                            <property name="action-target">2</property>  <!-- 2 corresponds to enum 
DifficultyCategory.MEDIUM -->
-                            <property name="height-request">40</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="SudokuMainMenuItem">
-                            <property name="label" translatable="yes">_Hard</property>
-                            <property name="action-name">app.start-game</property>
-                            <property name="action-target">3</property>  <!-- 3 corresponds to enum 
DifficultyCategory.HARD -->
-                            <property name="height-request">40</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="SudokuMainMenuItem">
-                            <property name="label" translatable="yes">_Very Hard</property>
-                            <property name="action-name">app.start-game</property>
-                            <property name="action-target">4</property>  <!-- 4 corresponds to enum 
DifficultyCategory.VERY_HARD -->
-                            <property name="height-request">40</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="SudokuMainMenuItem">
-                            <property name="label" translatable="yes">_Create your own puzzle</property>
-                            <property name="action-name">app.create-game</property>
-                            <property name="has-separator">True</property>
-                            <property name="height-request">40</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
-              </object> <!-- End of start_box_s -->
-            </child>
-            <child>
-              <object class="GtkAspectFrame" id="frame_h">
+              <object class="GtkFrame" id="frame">
                 <property name="visible">True</property>
                 <property name="shadow-type">GTK_SHADOW_NONE</property>
-                <property name="obey-child">False</property>
-                <property name="ratio">1.4</property>
                 <child>
                   <object class="GtkBox" id="game_box">
                     <property name="visible">True</property>
                     <property name="spacing">25</property>
+                    <property name="orientation">vertical</property>
                     <child>
-                      <object class="GtkButtonBox" id="controls_box">
+                      <object class="GtkBox" id="clock_box">
                         <property name="visible">True</property>
-                        <property name="halign">end</property>
-                        <property name="valign">end</property>
                         <property name="can-focus">False</property>
-                        <property name="orientation">vertical</property>
                         <property name="spacing">6</property>
-                        <property name="homogeneous">True</property>
-                        <child>
-                          <object class="GtkButton" id="play_pause_button">
-                            <property name="visible">True</property>
-                            <property name="can-focus">True</property>
-                            <property name="receives-default">True</property>
-                            <property name="action-name">app.pause</property>
-                            <property name="use-underline">True</property>
-                            <property name="valign">fill</property>
-                            <property name="halign">fill</property>
-                            <child>
-                              <object class="GtkLabel" id="play_pause_label">
-                                <property name="visible">True</property>
-                                <property name="can-focus">False</property>
-                                <property name="margin">12</property>
-                                <property name="label" translatable="yes">_Pause</property>
-                                <property name="use-underline">True</property>
-                              </object>
-                            </child>
-                          </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="pack-type">end</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
+                        <property name="halign">center</property>
                         <child>
-                          <object class="GtkButton">
+                          <object class="GtkImage" id="clock_image">
                             <property name="visible">True</property>
-                            <property name="use-underline">True</property>
-                            <property name="label" translatable="yes">_Clear Board</property>
-                            <property name="halign">fill</property>
-                            <property name="valign">center</property>
-                            <property name="action-name">app.reset</property>
-                            <property name="tooltip-text" translatable="yes">Reset the board to its original 
state</property>
-                            <property name="valign">fill</property>
-                            <property name="halign">fill</property>
+                            <property name="can-focus">False</property>
+                            <property name="icon-name">preferences-system-time-symbolic</property>
+                            <property name="icon-size">1</property>
                           </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="position">1</property>
-                          </packing>
                         </child>
                         <child>
-                          <object class="GtkButton">
+                          <object class="GtkLabel" id="clock_label">
                             <property name="visible">True</property>
-                            <property name="use-underline">True</property>
-                            <property name="label" translatable="yes">_New Puzzle</property>
-                            <property name="halign">fill</property>
-                            <property name="valign">center</property>
-                            <property name="action-name">app.new-game</property>
-                            <property name="tooltip-text" translatable="yes">Start a new puzzle</property>
-                            <property name="valign">fill</property>
-                            <property name="halign">fill</property>
+                            <property name="can-focus">False</property>
+                            <property name="halign">center</property>
                           </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="position">2</property>
-                          </packing>
                         </child>
-                        <child>
-                          <object class="GtkButton" id="play_custom_game_button">
-                            <property name="visible">False</property>
-                            <property name="use-underline">True</property>
-                            <property name="label" translatable="yes">_Start Playing</property>
-                            <property name="halign">fill</property>
-                            <property name="valign">center</property>
-                            <property name="action-name">app.play-custom-game</property>
-                            <property name="tooltip-text" translatable="yes">Start playing the custom puzzle 
you have created</property>
-                            <property name="valign">fill</property>
-                            <property name="halign">fill</property>
-                          </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="position">3</property>
-                          </packing>
-                        </child>
-                      </object>  <!-- End of controls_box -->
+                      </object>
                     </child>
                   </object> <!-- End of game_box -->
                 </child>
-              </object> <!-- End of frame_h -->
-            </child>
-            <child>
-              <object class="GtkAspectFrame" id="frame_v">
-                <property name="visible">True</property>
-                <property name="shadow-type">GTK_SHADOW_NONE</property>
-                <property name="obey-child">False</property>
-                <property name="ratio">0.6</property>
-              </object> <!-- End of frame_v-->
+              </object> <!-- End of frame -->
             </child>
           </object> <!-- End of main_squeezer -->
           <packing>
diff --git a/meson.build b/meson.build
index 953d460..627b478 100644
--- a/meson.build
+++ b/meson.build
@@ -34,7 +34,6 @@ gio = dependency('gio-2.0', version: '>= @0@'.format(glib_version))
 glib = dependency('glib-2.0', version: '>= @0@'.format(glib_version))
 gtk = dependency('gtk+-3.0', version: '>= 3.24.0')
 json_glib = dependency('json-glib-1.0')
-libhandy = dependency('libhandy-1')
 qqwing = dependency('qqwing', version: '>= 1.3.4')
 threads = dependency('threads')
 
diff --git a/src/gnome-sudoku.vala b/src/gnome-sudoku.vala
index 8760e4c..ee442c7 100644
--- a/src/gnome-sudoku.vala
+++ b/src/gnome-sudoku.vala
@@ -196,7 +196,6 @@ public class Sudoku : Gtk.Application
     {
         if (game.paused)
         {
-            window.display_unpause_button ();
             clear_action.set_enabled (false);
             undo_action.set_enabled (false);
             redo_action.set_enabled (false);
@@ -204,13 +203,13 @@ public class Sudoku : Gtk.Application
         }
         else if (game.get_total_time_played () > 0)
         {
-            window.display_pause_button ();
             clear_action.set_enabled (!game.is_empty ());
             undo_action.set_enabled (!game.is_undostack_null ());
             redo_action.set_enabled (!game.is_redostack_null ());
             new_game_action.set_enabled (true);
         }
 
+        window.display_pause_button ();
         view.queue_draw ();
     }
 
@@ -322,6 +321,7 @@ public class Sudoku : Gtk.Application
         print_action.set_enabled (true);
         undo_action.set_enabled (false);
         redo_action.set_enabled (false);
+        new_game_action.set_enabled (true);
 
         clear_action.set_enabled (!game.is_empty ());
         play_custom_game_action.set_enabled (!game.is_empty ());
diff --git a/src/meson.build b/src/meson.build
index 9cf093c..a654ce7 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -15,7 +15,7 @@ gnome_sudoku_sources = [
   resources
 ]
 
-gnome_sudoku_dependencies = [gtk, libhandy, libsudoku_dep]
+gnome_sudoku_dependencies = [gtk, libsudoku_dep]
 
 gnome_sudoku = executable(meson.project_name(), gnome_sudoku_sources,
   vala_args: gnome_sudoku_vala_args,
diff --git a/src/sudoku-window.vala b/src/sudoku-window.vala
index dcd8cc0..93d715c 100644
--- a/src/sudoku-window.vala
+++ b/src/sudoku-window.vala
@@ -25,23 +25,20 @@ using Gtk;
 public class SudokuWindow : ApplicationWindow
 {
     [GtkChild] private HeaderBar headerbar;
-    [GtkChild] private Hdy.Squeezer main_squeezer;
     [GtkChild] private Box start_box;
-    [GtkChild] private Box start_box_s;
-    [GtkChild] private AspectFrame frame_v;
-    [GtkChild] private AspectFrame frame_h;
+    [GtkChild] private Frame frame;
     [GtkChild] private Box game_box; // Holds the view
-    [GtkChild] private ButtonBox controls_box;
 
     [GtkChild] private Box undo_redo_box;
     [GtkChild] private Button back_button;
 
+    [GtkChild] private Box clock_box;
     [GtkChild] private Label clock_label;
     [GtkChild] private Image clock_image;
 
     [GtkChild] private Button play_custom_game_button;
     [GtkChild] private Button play_pause_button;
-    [GtkChild] private Label play_pause_label;
+    [GtkChild] private Image play_pause_image;
 
     private bool window_is_maximized;
     private bool window_is_fullscreen;
@@ -49,16 +46,16 @@ public class SudokuWindow : ApplicationWindow
     private int window_width;
     private int window_height;
 
+    private bool clock_in_headerbar;
+
     private GLib.Settings settings;
 
     public SudokuView? view { get; private set; }
 
     private SudokuGame? game = null;
 
-    private AspectFrame previous_layout;
-    private Orientation main_squeezer_orientation;
-
     private const int board_size = 140;
+    private const int clock_in_headerbar_min_width = 450;
 
     public SudokuWindow (GLib.Settings settings)
     {
@@ -67,16 +64,6 @@ public class SudokuWindow : ApplicationWindow
         set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
         if (settings.get_boolean ("window-is-maximized"))
             maximize ();
-
-        previous_layout = frame_h;
-
-        const int BUTTON_W = 120;
-        const int BUTTON_H = 60;
-        var spacing = 2 * game_box.spacing + 2 * main_squeezer.margin;
-        var board_and_spacing = board_size + spacing;
-        var board_with_buttons = board_and_spacing + game_box.spacing + 2 * controls_box.spacing;
-        frame_h.width_request = board_with_buttons + BUTTON_W;
-        frame_v.height_request = board_and_spacing + 3 * BUTTON_H + 4 * controls_box.spacing;
     }
 
     ~SudokuWindow ()
@@ -95,7 +82,7 @@ public class SudokuWindow : ApplicationWindow
 
         int width, height;
         get_size (out width, out height);
-        update_layout (width, height);
+        set_clock_placed_in_headerbar (width > clock_in_headerbar_min_width);
 
         if (window_is_maximized || window_is_fullscreen || window_is_tiled)
             return;
@@ -126,22 +113,6 @@ public class SudokuWindow : ApplicationWindow
         return base.window_state_event (event);
     }
 
-    private void update_layout (int width, int height)
-    {
-        main_squeezer_orientation = height > width ? Orientation.HORIZONTAL : Orientation.VERTICAL;
-        frame_h.ratio = height < 350 ? 1.9f : 1.4f;
-        apply_main_squeezer_orientation ();
-    }
-
-    private void apply_main_squeezer_orientation ()
-    {
-        var child = main_squeezer.visible_child;
-        if (child == start_box || child == start_box_s)
-            main_squeezer.orientation = main_squeezer_orientation;
-        else
-            main_squeezer.orientation = Orientation.HORIZONTAL;
-    }
-
     public void will_start_game ()
     {
         back_button.sensitive = false;
@@ -190,15 +161,17 @@ public class SudokuWindow : ApplicationWindow
     public void set_board_visible (bool visible)
     {
         start_box.visible = !visible;
-        start_box_s.visible = !visible;
-        frame_h.visible = visible;
-        frame_v.visible = visible;
-        apply_main_squeezer_orientation ();
+        play_custom_game_button.visible = visible && game.mode == GameMode.CREATE;
+        if (visible && game.mode != GameMode.CREATE)
+            display_pause_button ();
+        else
+            play_pause_button.visible = false;
+        frame.visible = visible;
     }
 
     public bool is_board_visible ()
     {
-        return frame_h.visible;
+        return frame.visible;
     }
 
     public void show_game_view ()
@@ -236,7 +209,7 @@ public class SudokuWindow : ApplicationWindow
         requires (game != null)
     {
         if (game.mode == GameMode.PLAY)
-            headerbar.title = game.board.difficulty_category.to_string ();
+            headerbar.title = _("Sudoku");
         else
             headerbar.title = _("Create Puzzle");
     }
@@ -244,13 +217,7 @@ public class SudokuWindow : ApplicationWindow
     public void display_pause_button ()
     {
         play_pause_button.show ();
-        play_pause_label.label = _("_Pause");
-    }
-
-    public void display_unpause_button ()
-    {
-        play_pause_button.show ();
-        play_pause_label.label = _("_Resume");
+        play_pause_image.icon_name = game.paused ? "media-playback-start-symbolic" : 
"media-playback-pause-symbolic";
     }
 
     private void tick_cb ()
@@ -265,38 +232,21 @@ public class SudokuWindow : ApplicationWindow
             clock_label.set_text ("%02d∶\xE2\x80\x8E%02d".printf (minutes, seconds));
     }
 
-    private void set_layout (AspectFrame new_layout)
+    private void set_clock_placed_in_headerbar (bool value)
     {
-        if (new_layout == frame_h)
+        if (value == clock_in_headerbar)
+            return;
+
+        clock_in_headerbar = value;
+        if(value)
         {
-            controls_box.halign = Align.END;
-            controls_box.orientation = Orientation.VERTICAL;
-            game_box.orientation = Orientation.HORIZONTAL;
+            game_box.remove (clock_box);
+            headerbar.pack_end (clock_box);
         }
         else
         {
-            controls_box.halign = Align.CENTER;
-            controls_box.orientation = Orientation.VERTICAL;
-            game_box.orientation = Orientation.VERTICAL;
+            headerbar.remove (clock_box);
+            game_box.pack_end (clock_box);
         }
-        previous_layout.remove (game_box);
-        new_layout.add (game_box);
-        previous_layout = new_layout;
-    }
-
-    private bool check_layout_change ()
-    {
-        var layout = main_squeezer.visible_child;
-        if (!(layout is AspectFrame))
-            return false;
-        var changed = layout != previous_layout;
-        if (changed)
-            set_layout ((AspectFrame) layout);
-        return changed;
-    }
-
-    [GtkCallback] private bool main_squeezer_draw_cb ()
-    {
-        return check_layout_change ();
     }
 }


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