[gnome-sudoku/mcatanzaro/gtk4: 2/2] wip




commit 53e6b55d82bc5717936a14ea33ac5362282347b5
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Sun Dec 27 14:39:25 2020 -0600

    wip

 data/print-dialog.ui                   | 175 +++++++-----------
 data/sudoku-window.ui                  | 313 ++++++++++-----------------------
 meson.build                            |   3 +-
 src/gnome-sudoku.vala                  |  14 +-
 src/{main-menu.vala => menu-item.vala} |  20 +--
 src/meson.build                        |   4 +-
 src/number-picker.vala                 |  14 +-
 src/print-dialog.vala                  |  11 +-
 src/sudoku-printer.vala                |  10 +-
 src/sudoku-view.vala                   | 192 +++++++++-----------
 src/sudoku-window.vala                 | 155 +++-------------
 11 files changed, 284 insertions(+), 627 deletions(-)
---
diff --git a/data/print-dialog.ui b/data/print-dialog.ui
index dfe7a7f..de13f11 100644
--- a/data/print-dialog.ui
+++ b/data/print-dialog.ui
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.12"/>
-  <!-- interface-naming-policy toplevel-contextual -->
+  <requires lib="gtk" version="4.0"/>
   <object class="GtkAdjustment" id="adjustment1">
     <property name="lower">1</property>
     <property name="upper">100</property>
@@ -16,210 +15,154 @@
   </object>
   <template class="PrintDialog" parent="GtkDialog">
     <property name="title" translatable="yes">Print Multiple Puzzles</property>
-    <property name="can-focus">False</property>
-    <property name="resizable">False</property>
-    <property name="type-hint">dialog</property>
+    <property name="can-focus">0</property>
+    <property name="resizable">0</property>
     <property name="width-request">460</property>
-    <property name="expand">False</property>
-    <property name="modal">True</property>
+    <property name="hexpand">0</property>
+    <property name="vexpand">0</property>
+    <property name="modal">1</property>
     <child type="action">
       <object class="GtkButton" id="cancelbutton1">
         <property name="label" translatable="yes">_Cancel</property>
-        <property name="visible">True</property>
-        <property name="can-focus">True</property>
-        <property name="can-default">True</property>
-        <property name="receives-default">False</property>
-        <property name="use-underline">True</property>
+        <property name="use-underline">1</property>
         <property name="valign">center</property>
       </object>
     </child>
     <child type="action">
       <object class="GtkButton" id="print_button">
         <property name="label" translatable="yes">_Print</property>
-        <property name="visible">True</property>
-        <property name="can-focus">True</property>
-        <property name="can-default">True</property>
-        <property name="receives-default">False</property>
-        <property name="use-underline">True</property>
+        <property name="use-underline">1</property>
         <property name="valign">center</property>
       </object>
     </child>
-    <child internal-child="vbox">
+    <child>
       <object class="GtkBox">
-        <property name="visible">True</property>
-        <property name="can-focus">False</property>
+        <property name="can-focus">0</property>
         <property name="orientation">vertical</property>
         <child>
           <object class="GtkGrid" id="print_grid">
-            <property name="visible">True</property>
-            <property name="can-focus">False</property>
-            <property name="border-width">12</property>
-            <property name="row-spacing">16</property>
-            <property name="column-spacing">10</property>
-            <property name="margin">6</property>
+            <property name="can-focus">0</property>
+            <property name="row-spacing">18</property>
+            <property name="column-spacing">12</property>
+            <property name="margin-start">12</property>
+            <property name="margin-end">12</property>
+            <property name="margin-top">12</property>
+            <property name="margin-bottom">12</property>
             <property name="orientation">vertical</property>
             <child>
               <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
+                <property name="can-focus">0</property>
                 <property name="label" translatable="yes">_Number of puzzles</property>
-                <property name="use-underline">True</property>
+                <property name="use-underline">1</property>
                 <property name="mnemonic-widget">n_sudokus_button</property>
                 <property name="valign">center</property>
                 <property name="halign">end</property>
                 <property name="width-request">150</property>
                 <property name="xalign">1.0</property>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">0</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">0</property>
-              </packing>
             </child>
             <child>
               <object class="GtkSpinButton" id="n_sudokus_button">
-                <property name="visible">True</property>
-                <property name="can-focus">True</property>
                 <property name="halign">center</property>
-                <property name="primary-icon-activatable">False</property>
-                <property name="secondary-icon-activatable">False</property>
-                <property name="primary-icon-sensitive">True</property>
-                <property name="secondary-icon-sensitive">True</property>
                 <property name="adjustment">adjustment1</property>
                 <property name="climb-rate">1</property>
                 <property name="valign">center</property>
                 <property name="margin-start">12</property>
+                <layout>
+                  <property name="column">1</property>
+                  <property name="row">0</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">0</property>
-              </packing>
             </child>
             <child>
               <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
+                <property name="can-focus">0</property>
                 <property name="label" translatable="yes">_Number of puzzles per page</property>
-                <property name="use-underline">True</property>
+                <property name="use-underline">1</property>
                 <property name="mnemonic-widget">n_sudokus_per_page_button</property>
                 <property name="valign">center</property>
                 <property name="halign">end</property>
                 <property name="width-request">150</property>
                 <property name="xalign">1.0</property>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">1</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">1</property>
-              </packing>
             </child>
             <child>
               <object class="GtkSpinButton" id="n_sudokus_per_page_button">
-                <property name="visible">True</property>
-                <property name="can-focus">True</property>
                 <property name="halign">center</property>
-                <property name="primary-icon-activatable">False</property>
-                <property name="secondary-icon-activatable">False</property>
-                <property name="primary-icon-sensitive">True</property>
-                <property name="secondary-icon-sensitive">True</property>
                 <property name="adjustment">adjustment2</property>
                 <property name="climb-rate">1</property>
                 <property name="valign">center</property>
                 <property name="margin-start">12</property>
+                <layout>
+                  <property name="column">1</property>
+                  <property name="row">1</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">1</property>
-              </packing>
             </child>
             <child>
               <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
+                <property name="can-focus">0</property>
                 <property name="valign">start</property>
                 <property name="label" translatable="yes">Difficulty</property>
                 <property name="halign">end</property>
                 <property name="width-request">150</property>
                 <property name="xalign">1.0</property>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">2</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">2</property>
-              </packing>
             </child>
             <child>
               <object class="GtkBox">
-                <property name="visible">True</property>
                 <property name="orientation">vertical</property>
-                <property name="homogeneous">True</property>
+                <property name="homogeneous">1</property>
                 <property name="halign">center</property>
                 <property name="valign">start</property>
                 <child>
-                  <object class="GtkRadioButton" id="easy_radio_button">
+                  <object class="GtkCheckButton" id="easy_radio_button">
+                    <property name="vexpand">1</property>
                     <property name="label" translatable="yes">_Easy</property>
-                    <property name="visible">True</property>
-                    <property name="can-focus">True</property>
-                    <property name="receives-default">False</property>
-                    <property name="use-underline">True</property>
-                    <property name="draw-indicator">True</property>
+                    <property name="use-underline">1</property>
                   </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
                 </child>
                 <child>
-                  <object class="GtkRadioButton" id="medium_radio_button">
+                  <object class="GtkCheckButton" id="medium_radio_button">
+                    <property name="vexpand">1</property>
                     <property name="label" translatable="yes">_Medium</property>
-                    <property name="visible">True</property>
-                    <property name="can-focus">True</property>
-                    <property name="receives-default">False</property>
-                    <property name="use-underline">True</property>
-                    <property name="draw-indicator">True</property>
+                    <property name="use-underline">1</property>
                     <property name="group">easy_radio_button</property>
                   </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
-                  </packing>
                 </child>
                 <child>
-                  <object class="GtkRadioButton" id="hard_radio_button">
+                  <object class="GtkCheckButton" id="hard_radio_button">
+                    <property name="vexpand">1</property>
                     <property name="label" translatable="yes">_Hard</property>
-                    <property name="visible">True</property>
-                    <property name="can-focus">True</property>
-                    <property name="receives-default">False</property>
-                    <property name="use-underline">True</property>
-                    <property name="draw-indicator">True</property>
+                    <property name="use-underline">1</property>
                     <property name="group">easy_radio_button</property>
                   </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">2</property>
-                  </packing>
                 </child>
                 <child>
-                  <object class="GtkRadioButton" id="very_hard_radio_button">
+                  <object class="GtkCheckButton" id="very_hard_radio_button">
+                    <property name="vexpand">1</property>
                     <property name="label" translatable="yes">_Very Hard</property>
-                    <property name="visible">True</property>
-                    <property name="can-focus">True</property>
-                    <property name="receives-default">False</property>
-                    <property name="use-underline">True</property>
-                    <property name="draw-indicator">True</property>
+                    <property name="use-underline">1</property>
                     <property name="group">easy_radio_button</property>
                   </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">3</property>
-                  </packing>
                 </child>
+                <layout>
+                  <property name="column">1</property>
+                  <property name="row">2</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">2</property>
-              </packing>
             </child>
           </object>
         </child>
diff --git a/data/sudoku-window.ui b/data/sudoku-window.ui
index ea9e95e..4fd2a9a 100644
--- a/data/sudoku-window.ui
+++ b/data/sudoku-window.ui
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <!-- interface-requires gtk+ 3.10 -->
+  <requires lib="gtk" version="4.0"/>
   <menu id="primary-menu">
     <section>
       <submenu>
@@ -42,27 +42,21 @@
     <property name="title" translatable="yes">Sudoku</property>
     <child type="titlebar">
       <object class="GtkHeaderBar" id="headerbar">
-        <property name="visible">True</property>
-        <property name="can-focus">False</property>
-        <property name="title" translatable="yes">Sudoku</property>
-        <property name="show-close-button">True</property>
+        <property name="can-focus">0</property>
         <child>
           <object class="GtkBox" id="undo_redo_box">
-            <property name="visible">True</property>
-            <property name="can-focus">False</property>
-            <property name="homogeneous">True</property>
+            <property name="can-focus">0</property>
+            <property name="homogeneous">1</property>
             <style>
               <class name="raised"/>
               <class name="linked"/>
             </style>
             <child>
               <object class="GtkButton">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
+                <property name="sensitive">0</property>
                 <property name="valign">center</property>
                 <property name="tooltip-text" translatable="yes">Undo your last action</property>
-                <property name="can-focus">True</property>
-                <property name="focus-on-click">False</property>
+                <property name="focus-on-click">0</property>
                 <property name="action-name">app.undo</property>
                 <style>
                   <class name="image-button"/>
@@ -70,7 +64,6 @@
                 <child>
                   <object class="GtkImage">
                     <property name="icon-name">edit-undo-symbolic</property>
-                    <property name="visible">True</property>
                     <property name="icon-size">1</property>
                   </object>
                 </child>
@@ -78,12 +71,10 @@
             </child>
             <child>
               <object class="GtkButton">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
+                <property name="sensitive">0</property>
                 <property name="valign">center</property>
                 <property name="tooltip-text" translatable="yes">Redo your last action</property>
-                <property name="can-focus">True</property>
-                <property name="focus-on-click">False</property>
+                <property name="focus-on-click">0</property>
                 <property name="action-name">app.redo</property>
                 <style>
                   <class name="image-button"/>
@@ -91,7 +82,6 @@
                 <child>
                   <object class="GtkImage">
                     <property name="icon-name">edit-redo-symbolic</property>
-                    <property name="visible">True</property>
                     <property name="icon-size">1</property>
                   </object>
                 </child>
@@ -100,100 +90,81 @@
           </object>
         </child>
         <child>
-          <object class="GtkMenuButton">
-            <property name="visible">True</property>
-            <property name="menu-model">primary-menu</property>
+          <object class="GtkButton" id="back_button">
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="tooltip-text" translatable="yes">Go back to the current game</property>
+            <property name="use-underline">1</property>
+            <property name="action-name">app.back</property>
+            <style>
+              <class name="image-button"/>
+            </style>
             <child>
               <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="halign">center</property>
-                <property name="icon-name">open-menu-symbolic</property>
+                <property name="icon-name">go-previous-symbolic</property>
+                <property name="icon-size">1</property>
               </object>
             </child>
           </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
         </child>
-        <child>
+        <child type="end">
+          <object class="GtkMenuButton">
+            <property name="icon-name">open-menu-symbolic</property>
+            <property name="menu-model">primary-menu</property>
+          </object>
+        </child>
+        <child type="end">
           <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="can-focus">False</property>
+            <property name="can-focus">0</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="can-focus">0</property>
                 <property name="icon-name">preferences-system-time-symbolic</property>
                 <property name="icon-size">1</property>
               </object>
             </child>
             <child>
               <object class="GtkLabel" id="clock_label">
-                <property name="visible">True</property>
-                <property name="can-focus">False</property>
+                <property name="can-focus">0</property>
                 <property name="halign">center</property>
               </object>
             </child>
           </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkButton" id="back_button">
-            <property name="visible">False</property>
-            <property name="halign">center</property>
-            <property name="valign">center</property>
-            <property name="tooltip-text" translatable="yes">Go back to the current game</property>
-            <property name="use-underline">True</property>
-            <property name="action-name">app.back</property>
-            <style>
-              <class name="image-button"/>
-            </style>
-            <child>
-              <object class="GtkImage">
-                <property name="icon-name">go-previous-symbolic</property>
-                <property name="visible">True</property>
-                <property name="icon-size">1</property>
-              </object>
-            </child>
-          </object>
         </child>
       </object>
     </child>
     <child>
-      <object class="GtkBox"> <!-- vbox -->
+      <object class="GtkBox">
         <property name="orientation">vertical</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdySqueezer" id="main_squeezer">
-            <property name="visible">True</property>
-            <property name="margin">25</property>
-            <property name="homogeneous">False</property>
+          <object class="GtkStack" id="main_stack">
+            <property name="vexpand">1</property>
+            <property name="margin-start">25</property>
+            <property name="margin-end">25</property>
+            <property name="margin-top">25</property>
+            <property name="margin-bottom">25</property>
+            <property name="vhomogeneous">0</property>
+            <property name="hhomogeneous">0</property>
             <property name="transition-type">crossfade</property>
-            <property name="interpolate-size">True</property>
-            <signal name="draw" handler="main_squeezer_draw_cb" swapped="no"/>
+            <property name="interpolate-size">1</property>
             <child>
               <object class="GtkBox" id="start_box">
-                <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">350</property>
                 <property name="height-request">350</property>
                 <child>
                   <object class="GtkFrame">
-                    <property name="visible">True</property>
-                    <property name="shadow-type">GTK_SHADOW_IN</property>
+                    <property name="vexpand">1</property>
                     <child>
-                      <object class="SudokuMainMenu">
+                      <object class="GtkListBox" id="main_menu">
                         <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="action-target">1</property>
                             <property name="height-request">80</property>
                           </object>
                         </child>
@@ -201,7 +172,7 @@
                           <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="action-target">2</property>
                             <property name="height-request">80</property>
                           </object>
                         </child>
@@ -209,7 +180,7 @@
                           <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="action-target">3</property>
                             <property name="height-request">80</property>
                           </object>
                         </child>
@@ -217,7 +188,7 @@
                           <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="action-target">4</property>
                             <property name="height-request">80</property>
                           </object>
                         </child>
@@ -232,195 +203,93 @@
                       </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 -->
-            </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 -->
+              </object>
             </child>
             <child>
-              <object class="GtkAspectFrame" id="frame_h">
-                <property name="visible">True</property>
-                <property name="shadow-type">GTK_SHADOW_NONE</property>
-                <property name="obey-child">False</property>
+              <object class="GtkAspectFrame">
+                <property name="obey-child">0</property>
                 <property name="ratio">1.4</property>
-                <child>
+                <property name="child">
                   <object class="GtkBox" id="game_box">
-                    <property name="visible">True</property>
                     <property name="spacing">25</property>
                     <child>
-                      <object class="GtkButtonBox" id="controls_box">
-                        <property name="visible">True</property>
+                      <object class="GtkBox" id="controls_box">
                         <property name="halign">end</property>
                         <property name="valign">end</property>
-                        <property name="can-focus">False</property>
+                        <property name="can-focus">0</property>
                         <property name="orientation">vertical</property>
                         <property name="spacing">6</property>
-                        <property name="homogeneous">True</property>
+                        <property name="homogeneous">1</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="vexpand">1</property>
+                            <property name="receives-default">1</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="margin-top">12</property>
+                                <property name="margin-bottom">12</property>
                                 <property name="label" translatable="yes">_Pause</property>
-                                <property name="use-underline">True</property>
+                                <property name="use-underline">1</property>
                               </object>
                             </child>
                           </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="pack-type">end</property>
-                            <property name="position">0</property>
-                          </packing>
                         </child>
                         <child>
                           <object class="GtkButton">
-                            <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="vexpand">1</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>
+                            <child>
+                              <object class="GtkLabel" id="play_pause_label">
+                                <property name="margin-top">12</property>
+                                <property name="margin-bottom">12</property>
+                                <property name="label" translatable="yes">_Clear Board</property>
+                                <property name="use-underline">1</property>
+                              </object>
+                            </child>
                           </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="position">1</property>
-                          </packing>
                         </child>
                         <child>
                           <object class="GtkButton">
-                            <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="vexpand">1</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>
+                            <child>
+                              <object class="GtkLabel" id="play_pause_label">
+                                <property name="margin-top">12</property>
+                                <property name="margin-bottom">12</property>
+                                <property name="label" translatable="yes">_New Puzzle</property>
+                                <property name="use-underline">1</property>
+                              </object>
+                            </child>
                           </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="vexpand">1</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>
+                            <child>
+                              <object class="GtkLabel" id="play_pause_label">
+                                <property name="margin-top">12</property>
+                                <property name="margin-bottom">12</property>
+                                <property name="label" translatable="yes">_Start Playing</property>
+                                <property name="use-underline">1</property>
+                              </object>
+                            </child>
                           </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>
+                </property>
+              </object>
             </child>
-          </object> <!-- End of main_squeezer -->
-          <packing>
-            <property name="expand">True</property>
-          </packing>
+          </object>
         </child>
-      </object> <!-- End of vbox -->
+      </object>
     </child>
-  </template> <!-- End of SudokuWindow -->
+  </template>
 </interface>
diff --git a/meson.build b/meson.build
index 953d460..04c390b 100644
--- a/meson.build
+++ b/meson.build
@@ -32,9 +32,8 @@ glib_version = '2.40.0'
 gee = dependency('gee-0.8')
 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')
+gtk = dependency('gtk4')
 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..207e489 100644
--- a/src/gnome-sudoku.vala
+++ b/src/gnome-sudoku.vala
@@ -1,7 +1,7 @@
 /* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
  * Copyright © 2014 Parin Porecha
- * Copyright © 2014 Michael Catanzaro
+ * Copyright © 2014, 2020 Michael Catanzaro
  *
  * This file is part of GNOME Sudoku.
  *
@@ -88,7 +88,10 @@ public class Sudoku : Gtk.Application
         Object (application_id: "org.gnome.Sudoku", flags: ApplicationFlags.FLAGS_NONE);
         add_main_option_entries (option_entries);
 
+#if 0
         typeof (SudokuMainMenu).ensure ();
+#endif
+
         typeof (SudokuMainMenuItem).ensure ();
     }
 
@@ -462,14 +465,7 @@ public class Sudoku : Gtk.Application
 
     private void help_cb ()
     {
-        try
-        {
-            show_uri_on_window (window, "help:gnome-sudoku", get_current_event_time ());
-        }
-        catch (GLib.Error e)
-        {
-            GLib.warning ("Unable to open help: %s", e.message);
-        }
+        show_uri (window, "help:gnome-sudoku", Gdk.CURRENT_TIME);
     }
 
     private const string[] authors = { "Robert Ancell <robert ancell gmail com>",
diff --git a/src/main-menu.vala b/src/menu-item.vala
similarity index 73%
rename from src/main-menu.vala
rename to src/menu-item.vala
index f99e502..5aa8fee 100644
--- a/src/main-menu.vala
+++ b/src/menu-item.vala
@@ -21,23 +21,6 @@
 using Gtk;
 using Gdk;
 
-private class SudokuMainMenu : ListBox
-{
-    construct
-    {
-        visible = true;
-        can_focus = false;
-        set_header_func ((row) => {
-            var mi = row as SudokuMainMenuItem;
-            if (mi != null && mi.has_separator)
-            {
-                var separator = new Separator (Orientation.HORIZONTAL);
-                mi.set_header (separator);
-            }
-        });
-    }
-}
-
 private class SudokuMainMenuItem : ListBoxRow
 {
     public string label { get; set construct; }
@@ -52,7 +35,6 @@ private class SudokuMainMenuItem : ListBoxRow
         label_widget.visible = true;
         label_widget.can_focus = false;
         label_widget.use_underline = true;
-
-        add (label_widget);
+        child = label_widget;
     }
 }
diff --git a/src/meson.build b/src/meson.build
index 9cf093c..10ae493 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -6,7 +6,7 @@ gnome_sudoku_vala_args = [
 gnome_sudoku_sources = [
   'config.vapi',
   'gnome-sudoku.vala',
-  'main-menu.vala',
+  'menu-item.vala',
   'number-picker.vala',
   'print-dialog.vala',
   'sudoku-printer.vala',
@@ -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/number-picker.vala b/src/number-picker.vala
index d8c701e..507b9f8 100644
--- a/src/number-picker.vala
+++ b/src/number-picker.vala
@@ -53,8 +53,7 @@ private class NumberPicker : Grid
                 label.margin_end = earmark ? 16 : 8;
                 label.margin_top = earmark ? 0 : 4;
                 label.margin_bottom = earmark ? 8 : 4;
-                button.add (label);
-                label.show ();
+                button.child = label;
 
                 if (!earmark)
                     button.clicked.connect (() => {
@@ -71,8 +70,6 @@ private class NumberPicker : Grid
 
                 if (n == 5)
                     button.grab_focus ();
-
-                button.show ();
             }
         }
 
@@ -84,8 +81,7 @@ private class NumberPicker : Grid
 
             var label = new Label ("<big>%s</big>".printf (_("Clear")));
             label.use_markup = true;
-            clear_button.add (label);
-            label.show ();
+            clear_button.child = label;
 
             clear_button.clicked.connect (() => {
                 number_picked (0);
@@ -94,10 +90,12 @@ private class NumberPicker : Grid
 
         this.valign = Align.CENTER;
         this.halign = Align.CENTER;
-        this.margin = 2;
+        this.margin_start = 2;
+        this.margin_end = 2;
+        this.margin_top = 2;
+        this.margin_bottom = 2;
         this.row_spacing = 3;
         this.column_spacing = 3;
-        this.show ();
     }
 
     public void set_clear_button_visibility (bool visible)
diff --git a/src/print-dialog.vala b/src/print-dialog.vala
index 1a696fe..a8e793f 100644
--- a/src/print-dialog.vala
+++ b/src/print-dialog.vala
@@ -31,10 +31,10 @@ public class PrintDialog : Dialog
     [GtkChild] private Grid print_grid;
     [GtkChild] private SpinButton n_sudokus_button;
     [GtkChild] private SpinButton n_sudokus_per_page_button;
-    [GtkChild] private RadioButton easy_radio_button;
-    [GtkChild] private RadioButton medium_radio_button;
-    [GtkChild] private RadioButton hard_radio_button;
-    [GtkChild] private RadioButton very_hard_radio_button;
+    [GtkChild] private CheckButton easy_radio_button;
+    [GtkChild] private CheckButton medium_radio_button;
+    [GtkChild] private CheckButton hard_radio_button;
+    [GtkChild] private CheckButton very_hard_radio_button;
 
     private Revealer revealer;
     private Spinner spinner;
@@ -60,7 +60,7 @@ public class PrintDialog : Dialog
 
         spinner = new Spinner ();
         revealer = new Revealer ();
-        revealer.add (spinner);
+        revealer.child = spinner;
         revealer.valign = Align.CENTER;
         ((HeaderBar) get_header_bar ()).pack_end (revealer);
 
@@ -106,7 +106,6 @@ public class PrintDialog : Dialog
     public bool start_spinner_cb ()
     {
         revealer.set_transition_type (RevealerTransitionType.SLIDE_LEFT);
-        revealer.show_all ();
         spinner.start ();
         revealer.set_reveal_child (true);
         return Source.REMOVE;
diff --git a/src/sudoku-printer.vala b/src/sudoku-printer.vala
index 7b5fcbf..72fbb4e 100644
--- a/src/sudoku-printer.vala
+++ b/src/sudoku-printer.vala
@@ -41,10 +41,12 @@ public class SudokuPrinter : GLib.Object {
         }
         catch (GLib.Error e)
         {
-            new MessageDialog (window, DialogFlags.MODAL,
-                               MessageType.ERROR, ButtonsType.CLOSE,
-                               /* Error message if printing fails */
-                               "%s\n%s".printf (_("Error printing file:"), e.message)).run ();
+            var dialog = new MessageDialog (window, DialogFlags.MODAL,
+                                            MessageType.ERROR, ButtonsType.CLOSE,
+                                            /* Error message if printing fails */
+                                            "%s\n%s".printf (_("Error printing file:"), e.message));
+            dialog.response.connect (() => dialog.destroy ());
+            dialog.show ();
         }
 
         return PrintOperationResult.ERROR;
diff --git a/src/sudoku-view.vala b/src/sudoku-view.vala
index df401f0..e6b782f 100644
--- a/src/sudoku-view.vala
+++ b/src/sudoku-view.vala
@@ -1,7 +1,7 @@
 /* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
  * Copyright © 2014 Parin Porecha
- * Copyright © 2014 Michael Catanzaro
+ * Copyright © 2014, 2020 Michael Catanzaro
  *
  * This file is part of GNOME Sudoku.
  *
@@ -92,7 +92,7 @@ private class SudokuCellView : DrawingArea
     private NumberPicker number_picker;
     private NumberPicker earmark_picker;
 
-    private EventControllerKey key_controller;      // for keeping in memory
+    private ModifierType key_modifiers;
 
     public SudokuCellView (int row, int col, ref SudokuGame game)
     {
@@ -100,49 +100,59 @@ private class SudokuCellView : DrawingArea
         this.row = row;
         this.col = col;
 
-        init_keyboard ();
+        var focus_controller = new EventControllerFocus ();
+        focus_controller.leave.connect (on_focus_leave);
+        add_controller (focus_controller);
+
+        var key_controller = new EventControllerKey ();
+        key_controller.key_pressed.connect (on_key_pressed);
+        add_controller (key_controller);
+
+        var click_controller = new Gtk.GestureClick ();
+        click_controller.button = 0; // listen for every button
+        click_controller.pressed.connect (on_click);
+        add_controller (click_controller);
 
         value = game.board [row, col];
 
         // background_color is set in the SudokuView, as it manages the color of the cells
 
         can_focus = true;
-        events = EventMask.EXPOSURE_MASK | EventMask.BUTTON_PRESS_MASK | EventMask.BUTTON_RELEASE_MASK | 
EventMask.KEY_PRESS_MASK;
 
         if (is_fixed && game.mode == GameMode.PLAY)
             return;
 
-        focus_out_event.connect (focus_out_cb);
         game.cell_changed.connect (cell_changed_cb);
+
+        set_draw_func (draw);
     }
 
-    public override bool button_press_event (EventButton event)
+    private void on_click (Gtk.GestureClick click_controller, int n_press, double event_x, double event_y)
     {
-        if (event.button != 1 && event.button != 3)
-            return false;
+        uint button = click_controller.get_current_button ();
+        if (button != 1 && button != 3)
+            return;
 
-        if (!is_focus)
+        if (!is_focus ())
             grab_focus ();
         if (game.mode == GameMode.PLAY && (is_fixed || game.paused))
-            return false;
+            return;
 
         if (popover != null || earmark_popover != null)
         {
             hide_both_popovers ();
-            return false;
+            return;
         }
 
-        if (event.button == 1)            // Left-Click
+        if (button == 1) // Left-Click
         {
-            if (!_show_possibilities && (event.state & ModifierType.CONTROL_MASK) > 0 && game.mode == 
GameMode.PLAY)
+            if (!_show_possibilities && (key_modifiers & ModifierType.CONTROL_MASK) > 0 && game.mode == 
GameMode.PLAY)
                 show_earmark_picker ();
             else
                 show_number_picker ();
         }
-        else if (!_show_possibilities && event.button == 3 && game.mode == GameMode.PLAY)         // 
Right-Click
+        else if (!_show_possibilities && button == 3 && game.mode == GameMode.PLAY) // Right-Click
             show_earmark_picker ();
-
-        return false;
     }
 
     private void create_earmark_picker ()
@@ -162,7 +172,7 @@ private class SudokuCellView : DrawingArea
     private void show_number_picker ()
     {
         if (earmark_popover != null)
-            earmark_popover.hide ();
+            earmark_popover.popdown ();
 
         number_picker = new NumberPicker (ref game.board);
         number_picker.number_picked.connect ((o, number) => {
@@ -171,47 +181,39 @@ private class SudokuCellView : DrawingArea
                 notify_property ("value");
             this.game.board.disable_all_earmarks (row, col);
 
-            popover.hide ();
+            popover.popdown ();
         });
         number_picker.set_clear_button_visibility (value != 0);
 
-        popover = new Popover (this);
-        popover.add (number_picker);
-        popover.modal = false;
+        popover = new Popover ();
+        popover.set_parent (this);
+        popover.child = number_picker;
+        popover.autohide = true;
         popover.position = PositionType.BOTTOM;
+        popover.popup ();
         popover.notify["visible"].connect (()=> {
             if (!popover.visible)
                 destroy_popover (ref popover, ref number_picker);
         });
-        popover.focus_out_event.connect (() => {
-            popover.hide ();
-            return true;
-        });
-
-        popover.show ();
     }
 
     private void show_earmark_picker ()
     {
         if (popover != null)
-            popover.hide ();
+            popover.popdown ();
 
         create_earmark_picker ();
 
-        earmark_popover = new Popover (this);
-        earmark_popover.add (earmark_picker);
-        earmark_popover.modal = false;
+        earmark_popover = new Popover ();
+        earmark_popover.set_parent (this);
+        earmark_popover.child = earmark_picker;
+        earmark_popover.autohide = true;
         earmark_popover.position = PositionType.BOTTOM;
+        earmark_popover.popup ();
         earmark_popover.notify["visible"].connect (()=> {
             if (!earmark_popover.visible)
                 destroy_popover (ref earmark_popover, ref earmark_picker);
         });
-        earmark_popover.focus_out_event.connect (() => {
-            earmark_popover.hide ();
-            return true;
-        });
-
-        earmark_popover.show ();
     }
 
     private void destroy_popover (ref Popover popover, ref NumberPicker picker)
@@ -232,10 +234,9 @@ private class SudokuCellView : DrawingArea
             earmark_popover.hide ();
     }
 
-    private bool focus_out_cb (Widget widget, EventFocus event)
+    private void on_focus_leave (Gtk.EventControllerFocus controller)
     {
         hide_both_popovers ();
-        return false;
     }
 
     /* Key mapping function to help convert Gdk.keyval_name string to numbers */
@@ -265,14 +266,10 @@ private class SudokuCellView : DrawingArea
         return -1;
     }
 
-    private inline void init_keyboard ()  // called on construct
+    private bool on_key_pressed (EventControllerKey key_controller, uint keyval, uint keycode, ModifierType 
state)
     {
-        key_controller = new EventControllerKey (this);
-        key_controller.key_pressed.connect (on_key_pressed);
-    }
+        key_modifiers = state;
 
-    private inline bool on_key_pressed (EventControllerKey _key_controller, uint keyval, uint keycode, 
ModifierType state)
-    {
         if (game.mode == GameMode.PLAY && (is_fixed || game.paused))
             return false;
         string k_name = keyval_name (keyval);
@@ -332,10 +329,10 @@ private class SudokuCellView : DrawingArea
         return false;
     }
 
-    public override bool draw (Cairo.Context c)
+    private void draw (Gtk.DrawingArea self, Cairo.Context c, int width, int height)
     {
         RGBA background_color;
-        if (_selected && is_focus)
+        if (_selected && is_focus ())
             background_color = selected_bg_color;
         else if (is_fixed)
             background_color = fixed_cell_color;
@@ -344,7 +341,7 @@ private class SudokuCellView : DrawingArea
         else
             background_color = free_cell_color;
         c.set_source_rgba (background_color.red, background_color.green, background_color.blue, 
background_color.alpha);
-        c.rectangle (0, 0, get_allocated_width (), get_allocated_height ());
+        c.rectangle (0, 0, width, height);
         c.fill();
 
         if (_show_warnings && game.board.broken_coords.contains (Coord (row, col)))
@@ -357,21 +354,19 @@ private class SudokuCellView : DrawingArea
             c.set_source_rgb (0.0, 0.0, 0.0);
 
         if (game.paused)
-            return false;
+            return;
 
         if (value != 0)
         {
-            double height = (double) get_allocated_height ();
-            double width = (double) get_allocated_width ();
             string text = "%d".printf (value);
 
             c.set_font_size (height / size_ratio);
             print_centered (c, text, width, height);
-            return false;
+            return;
         }
 
         if (is_fixed && game.mode == GameMode.PLAY)
-            return false;
+            return;
 
         bool[] marks = null;
         if (!_show_possibilities)
@@ -388,8 +383,8 @@ private class SudokuCellView : DrawingArea
             double possibility_size = get_allocated_height () / size_ratio / 2;
             c.set_font_size (possibility_size);
 
-            double height = (double) get_allocated_height () / game.board.block_rows;
-            double width = (double) get_allocated_width () / game.board.block_cols;
+            double h = (double) height / game.board.block_rows;
+            double w = (double) width / game.board.block_cols;
 
             int num = 0;
             for (int row_tmp = 0; row_tmp < game.board.block_rows; row_tmp++)
@@ -408,8 +403,8 @@ private class SudokuCellView : DrawingArea
                         var text = "%d".printf (num);
 
                         c.save ();
-                        c.translate (col_tmp * width, (game.board.block_rows - row_tmp - 1) * height);
-                        print_centered (c, text, width, height);
+                        c.translate (col_tmp * w, (game.board.block_rows - row_tmp - 1) * h);
+                        print_centered (c, text, w, h);
                         c.restore ();
                     }
                 }
@@ -418,12 +413,10 @@ private class SudokuCellView : DrawingArea
 
         if (_show_warnings && (value == 0 && game.board.count_possibilities (row, col) == 0))
         {
-            c.set_font_size (get_allocated_height () / size_ratio);
+            c.set_font_size (height / size_ratio);
             c.set_source_rgb (1.0, 0.0, 0.0);
-            print_centered (c, "X", get_allocated_width (), get_allocated_height ());
+            print_centered (c, "X", width, height);
         }
-
-        return false;
     }
 
     private void print_centered (Cairo.Context c, string text, double width, double height)
@@ -456,20 +449,20 @@ private class SudokuCellView : DrawingArea
     }
 }
 
-public const RGBA fixed_cell_color = {0.8, 0.8, 0.8, 1.0};
-public const RGBA free_cell_color = {1.0, 1.0, 1.0, 1.0};
-public const RGBA highlight_color = {0.93, 0.93, 0.93, 1.0};
-public const RGBA selected_bg_color = {0.7, 0.8, 0.9, 1.0};
+public const RGBA fixed_cell_color = {0.8f, 0.8f, 0.8f, 1.0f};
+public const RGBA free_cell_color = {1.0f, 1.0f, 1.0f, 1.0f};
+public const RGBA highlight_color = {0.93f, 0.93f, 0.93f, 1.0f};
+public const RGBA selected_bg_color = {0.7f, 0.8f, 0.9f, 1.0f};
 
-public class SudokuView : AspectFrame
+public class SudokuView : Widget
 {
     public SudokuGame game;
     private SudokuCellView[,] cells;
 
     private bool previous_board_broken_state = false;
 
-    private Overlay overlay;
-    private DrawingArea drawing;
+    private Overlay? overlay;
+    private DrawingArea drawing_area;
     private Grid grid;
 
     private int selected_row = -1;
@@ -491,26 +484,16 @@ public class SudokuView : AspectFrame
 
     public SudokuView (SudokuGame game)
     {
-        shadow_type = ShadowType.NONE;
-        obey_child = false;
-        ratio = 1;
-
         overlay = new Overlay ();
-        add (overlay);
-
-        drawing = new DrawingArea ();
-        drawing.draw.connect (draw_board);
+        overlay.hexpand = true;
+        overlay.vexpand = true;
+        overlay.set_parent (this);
 
-        if (grid != null)
-            overlay.remove (grid);
+        drawing_area = new DrawingArea ();
+        drawing_area.set_draw_func (draw_board);
+        overlay.add_overlay (drawing_area);
 
         this.game = game;
-        this.game.paused_changed.connect(() => {
-            if (this.game.paused)
-                drawing.show ();
-            else
-                drawing.hide ();
-        });
 
         var css_provider = new CssProvider ();
         css_provider.load_from_resource ("/org/gnome/Sudoku/ui/gnome-sudoku.css");
@@ -522,6 +505,7 @@ public class SudokuView : AspectFrame
         grid.row_homogeneous = true;
         grid.get_style_context ().add_class ("board");
         grid.get_style_context ().add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+        overlay.child = grid;
 
         var blocks = new Grid[game.board.block_rows, game.board.block_cols];
         for (var block_row = 0; block_row < game.board.block_rows; block_row++)
@@ -547,29 +531,26 @@ public class SudokuView : AspectFrame
             for (var col = 0; col < game.board.cols; col++)
             {
                 var cell = new SudokuCellView (row, col, ref this.game);
+                var focus_controller = new EventControllerFocus ();
+                cell.add_controller (focus_controller);
+
                 var cell_row = row;
                 var cell_col = col;
 
-                cell.focus_in_event.connect (() => {
+                focus_controller.enter.connect (() => {
                     if (game.paused)
-                        return false;
-
+                        return;
                     this.set_selected (cell_row, cell_col);
                     this.update_highlights ();
                     queue_draw ();
-
-                    return false;
                 });
 
-                cell.focus_out_event.connect (() => {
+                focus_controller.leave.connect (() => {
                     if (game.paused)
-                        return false;
-
+                        return;
                     this.set_selected (-1, -1);
                     this.update_highlights ();
                     queue_draw ();
-
-                    return false;
                 });
 
                 cell.notify["value"].connect ((s, p)=> {
@@ -586,12 +567,15 @@ public class SudokuView : AspectFrame
                 blocks[row / game.board.block_rows, col / game.board.block_cols].attach (cell, col % 
game.board.block_cols, row % game.board.block_rows);
             }
         }
+    }
 
-        overlay.add_overlay (drawing);
-        overlay.add (grid);
-        grid.show_all ();
-        overlay.show ();
-        drawing.hide ();
+    ~SudokuView ()
+    {
+        if (overlay != null)
+        {
+            overlay.unparent ();
+            overlay = null;
+        }
     }
 
     private void update_highlights ()
@@ -618,28 +602,24 @@ public class SudokuView : AspectFrame
         }
     }
 
-    private bool draw_board (Cairo.Context c)
+    private void draw_board (Gtk.DrawingArea drawing_area, Cairo.Context c, int width, int height)
     {
         if (game.paused)
         {
-            int board_length = grid.get_allocated_width ();
-
             c.set_source_rgba (0, 0, 0, 0.75);
             c.paint ();
 
             c.select_font_face ("Sans", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD);
-            c.set_font_size (get_allocated_width () * 0.125);
+            c.set_font_size (width * 0.125);
 
             /* Text on overlay when game is paused */
             var text = _("Paused");
             Cairo.TextExtents extents;
             c.text_extents (text, out extents);
-            c.move_to (board_length/2.0 - extents.width/2.0, board_length/2.0 + extents.height/2.0);
+            c.move_to (width/2.0 - extents.width/2.0, width/2.0 + extents.height/2.0);
             c.set_source_rgb (1, 1, 1);
             c.show_text (text);
         }
-
-        return false;
     }
 
     public void clear ()
diff --git a/src/sudoku-window.vala b/src/sudoku-window.vala
index dcd8cc0..1bdb11f 100644
--- a/src/sudoku-window.vala
+++ b/src/sudoku-window.vala
@@ -24,14 +24,8 @@ using Gtk;
 [GtkTemplate (ui = "/org/gnome/Sudoku/ui/sudoku-window.ui")]
 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 Box game_box; // Holds the view
-    [GtkChild] private ButtonBox controls_box;
+    [GtkChild] private Box game_box;
 
     [GtkChild] private Box undo_redo_box;
     [GtkChild] private Button back_button;
@@ -43,11 +37,7 @@ public class SudokuWindow : ApplicationWindow
     [GtkChild] private Button play_pause_button;
     [GtkChild] private Label play_pause_label;
 
-    private bool window_is_maximized;
-    private bool window_is_fullscreen;
-    private bool window_is_tiled;
-    private int window_width;
-    private int window_height;
+    [GtkChild] private ListBox main_menu;
 
     private GLib.Settings settings;
 
@@ -55,9 +45,6 @@ public class SudokuWindow : ApplicationWindow
 
     private SudokuGame? game = null;
 
-    private AspectFrame previous_layout;
-    private Orientation main_squeezer_orientation;
-
     private const int board_size = 140;
 
     public SudokuWindow (GLib.Settings settings)
@@ -68,80 +55,27 @@ public class SudokuWindow : ApplicationWindow
         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;
+        main_menu.can_focus = false;
+        main_menu.set_header_func ((row) => {
+            var mi = row as SudokuMainMenuItem;
+            if (mi != null && mi.has_separator)
+            {
+                var separator = new Separator (Orientation.HORIZONTAL);
+                mi.set_header (separator);
+            }
+        });
     }
 
     ~SudokuWindow ()
     {
         /* Save window state */
         settings.delay ();
-        settings.set_int ("window-width", window_width);
-        settings.set_int ("window-height", window_height);
-        settings.set_boolean ("window-is-maximized", window_is_maximized || window_is_fullscreen);
+        settings.set_int ("window-width", default_width);
+        settings.set_int ("window-height", default_height);
+        settings.set_boolean ("window-is-maximized", maximized);
         settings.apply ();
     }
 
-    public override void size_allocate (Allocation allocation)
-    {
-        base.size_allocate (allocation);
-
-        int width, height;
-        get_size (out width, out height);
-        update_layout (width, height);
-
-        if (window_is_maximized || window_is_fullscreen || window_is_tiled)
-            return;
-
-        window_width = width;
-        window_height = height;
-    }
-
-    private const Gdk.WindowState tiled_state = Gdk.WindowState.TILED
-                                              | Gdk.WindowState.TOP_TILED
-                                              | Gdk.WindowState.BOTTOM_TILED
-                                              | Gdk.WindowState.LEFT_TILED
-                                              | Gdk.WindowState.RIGHT_TILED;
-
-    public override bool window_state_event (Gdk.EventWindowState event)
-    {
-        if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
-            window_is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
-
-        /* fullscreen: saved as maximized */
-        if ((event.changed_mask & Gdk.WindowState.FULLSCREEN) != 0)
-            window_is_fullscreen = (event.new_window_state & Gdk.WindowState.FULLSCREEN) != 0;
-
-        /* We don’t save this state, but track it for saving size allocation */
-        if ((event.changed_mask & tiled_state) != 0)
-            window_is_tiled = (event.new_window_state & tiled_state) != 0;
-
-        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;
@@ -171,15 +105,14 @@ public class SudokuWindow : ApplicationWindow
         view.highlighter = settings.get_boolean ("highlighter");
 
         view.show ();
-        game_box.pack_start (view);
-        game_box.child_set_property (view, "position", 0);
+        game_box.prepend (view);
 
         back_button.sensitive = true;
     }
 
     public void show_new_game_screen ()
     {
-        headerbar.title = _("Select Difficulty");
+        title = _("Select Difficulty");
         set_board_visible (false);
         back_button.visible = game != null;
         undo_redo_box.visible = false;
@@ -190,15 +123,12 @@ 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 ();
+        game_box.visible = visible;
     }
 
     public bool is_board_visible ()
     {
-        return frame_h.visible;
+        return game_box.visible;
     }
 
     public void show_game_view ()
@@ -224,7 +154,10 @@ public class SudokuWindow : ApplicationWindow
             play_pause_button.visible = false;
         }
 
-        set_headerbar_title ();
+        if (game.mode == GameMode.PLAY)
+            title = game.board.difficulty_category.to_string ();
+        else
+            title = _("Create Puzzle");
     }
 
     public void board_completed ()
@@ -232,15 +165,6 @@ public class SudokuWindow : ApplicationWindow
         play_custom_game_button.visible = false;
     }
 
-    public void set_headerbar_title ()
-        requires (game != null)
-    {
-        if (game.mode == GameMode.PLAY)
-            headerbar.title = game.board.difficulty_category.to_string ();
-        else
-            headerbar.title = _("Create Puzzle");
-    }
-
     public void display_pause_button ()
     {
         play_pause_button.show ();
@@ -264,39 +188,4 @@ public class SudokuWindow : ApplicationWindow
         else
             clock_label.set_text ("%02d∶\xE2\x80\x8E%02d".printf (minutes, seconds));
     }
-
-    private void set_layout (AspectFrame new_layout)
-    {
-        if (new_layout == frame_h)
-        {
-            controls_box.halign = Align.END;
-            controls_box.orientation = Orientation.VERTICAL;
-            game_box.orientation = Orientation.HORIZONTAL;
-        }
-        else
-        {
-            controls_box.halign = Align.CENTER;
-            controls_box.orientation = Orientation.VERTICAL;
-            game_box.orientation = Orientation.VERTICAL;
-        }
-        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]