[gnome-mahjongg] Port to gtk4, libadwaita



commit 1c2118627e474f769bff553b21082ca3aab55ca8
Author: Günther Wagner <info gunibert de>
Date:   Thu Nov 25 20:51:45 2021 +0100

    Port to gtk4, libadwaita
    
    I tried to clean also the codebase up and make it easier for the future
    to change the layout. This is part of the Initiative
    https://gitlab.gnome.org/GNOME/Initiatives/-/wikis/Dark-Style-Preference

 data/icons/question-round-symbolic.svg | 150 ++++++++++++
 data/org.gnome.Mahjongg.gresource.xml  |   7 +
 data/style.css                         |   3 +
 data/ui/preferences.ui                 |  36 +++
 data/ui/score-dialog.ui                |  44 ++++
 data/ui/window.ui                      |  84 +++++++
 flatpak/org.gnome.Mahjongg.json        |  36 +++
 meson.build                            |   5 +-
 src/game-view.vala                     |  80 +++----
 src/gnome-mahjongg.vala                | 412 +++++++--------------------------
 src/meson.build                        |   7 +-
 src/preferences.vala                   |  91 ++++++++
 src/score-dialog.vala                  |  56 ++---
 src/window.vala                        |  41 ++++
 14 files changed, 630 insertions(+), 422 deletions(-)
---
diff --git a/data/icons/question-round-symbolic.svg b/data/icons/question-round-symbolic.svg
new file mode 100644
index 0000000..06a653d
--- /dev/null
+++ b/data/icons/question-round-symbolic.svg
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink";>
+    <filter id="a" height="100%" width="100%" x="0%" y="0%">
+        <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
+    </filter>
+    <mask id="b">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/>
+        </g>
+    </mask>
+    <clipPath id="c">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="d">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
+        </g>
+    </mask>
+    <clipPath id="e">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="f">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
+        </g>
+    </mask>
+    <clipPath id="g">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="h">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
+        </g>
+    </mask>
+    <clipPath id="i">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="j">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
+        </g>
+    </mask>
+    <clipPath id="k">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="l">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
+        </g>
+    </mask>
+    <clipPath id="m">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="n">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
+        </g>
+    </mask>
+    <clipPath id="o">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="p">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/>
+        </g>
+    </mask>
+    <clipPath id="q">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="r">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/>
+        </g>
+    </mask>
+    <clipPath id="s">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="t">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.4"/>
+        </g>
+    </mask>
+    <clipPath id="u">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="v">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.4"/>
+        </g>
+    </mask>
+    <clipPath id="w">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="x">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/>
+        </g>
+    </mask>
+    <clipPath id="y">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <mask id="z">
+        <g filter="url(#a)">
+            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/>
+        </g>
+    </mask>
+    <clipPath id="A">
+        <path d="m 0 0 h 1024 v 800 h -1024 z"/>
+    </clipPath>
+    <g clip-path="url(#c)" mask="url(#b)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 562.460938 212.058594 h 10.449218 c -1.183594 0.492187 -1.296875 2.460937 0 3 h 
-10.449218 z m 0 0" fill="#2e3436"/>
+    </g>
+    <g clip-path="url(#e)" mask="url(#d)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 16 632 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
+    </g>
+    <g clip-path="url(#g)" mask="url(#f)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 17 631 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
+    </g>
+    <g clip-path="url(#i)" mask="url(#h)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 18 634 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
+    </g>
+    <g clip-path="url(#k)" mask="url(#j)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 16 634 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
+    </g>
+    <g clip-path="url(#m)" mask="url(#l)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 17 635 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
+    </g>
+    <g clip-path="url(#o)" mask="url(#n)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 19 635 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
+    </g>
+    <g clip-path="url(#q)" mask="url(#p)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 136 660 v 7 h 7 v -7 z m 0 0" fill="#2e3436"/>
+    </g>
+    <g clip-path="url(#s)" mask="url(#r)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 199 642 h 3 v 12 h -3 z m 0 0" fill="#2e3436"/>
+    </g>
+    <g clip-path="url(#u)" mask="url(#t)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 209.5 144.160156 c 0.277344 0 0.5 0.222656 0.5 0.5 v 1 c 0 0.277344 -0.222656 0.5 -0.5 
0.5 s -0.5 -0.222656 -0.5 -0.5 v -1 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0" fill="#2e3436"/>
+    </g>
+    <g clip-path="url(#w)" mask="url(#v)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 206.5 144.160156 c 0.277344 0 0.5 0.222656 0.5 0.5 v 1 c 0 0.277344 -0.222656 0.5 -0.5 
0.5 s -0.5 -0.222656 -0.5 -0.5 v -1 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0" fill="#2e3436"/>
+    </g>
+    <g clip-path="url(#y)" mask="url(#x)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 229.5 143.160156 c -0.546875 0 -1 0.457032 -1 1 c 0 0.546875 0.453125 1 1 1 s 1 -0.453125 
1 -1 c 0 -0.542968 -0.453125 -1 -1 -1 z m 0 0" fill="#2e3436"/>
+    </g>
+    <g clip-path="url(#A)" mask="url(#z)" transform="matrix(1 0 0 1 -20 -60)">
+        <path d="m 226.453125 143.160156 c -0.519531 0 -0.953125 0.433594 -0.953125 0.953125 v 0.09375 c 0 
0.519531 0.433594 0.953125 0.953125 0.953125 h 0.09375 c 0.519531 0 0.953125 -0.433594 0.953125 -0.953125 v 
-0.09375 c 0 -0.519531 -0.433594 -0.953125 -0.953125 -0.953125 z m 0 0" fill="#2e3436"/>
+    </g>
+    <path d="m 8 1 c -3.867188 0 -7 3.132812 -7 7 s 3.132812 7 7 7 s 7 -3.132812 7 -7 s -3.132812 -7 -7 -7 z 
m 0.152344 2.007812 c 0.726562 0.035157 1.433594 0.335938 1.96875 0.871094 c 0.855468 0.855469 1.113281 
2.152344 0.648437 3.269532 c -0.328125 0.796874 -0.984375 1.390624 -1.769531 1.671874 v 1.179688 h -2 v -2 c 
0 -0.550781 0.449219 -1 1 -1 c 0.40625 0 0.769531 -0.242188 0.921875 -0.617188 c 0.15625 -0.375 0.074219 
-0.800781 -0.214844 -1.089843 c -0.289062 -0.289063 -0.714843 -0.371094 -1.089843 -0.214844 c -0.375 0.152344 
-0.617188 0.515625 -0.617188 0.921875 h -2 c 0 -1.210938 0.734375 -2.308594 1.851562 -2.769531 c 0.417969 
-0.175781 0.863282 -0.246094 1.300782 -0.222657 z m -0.152344 7.992188 c 0.550781 0 1 0.449219 1 1 s 
-0.449219 1 -1 1 s -1 -0.449219 -1 -1 s 0.449219 -1 1 -1 z m 0 0" fill="#2e3436"/>
+</svg>
diff --git a/data/org.gnome.Mahjongg.gresource.xml b/data/org.gnome.Mahjongg.gresource.xml
index 0f95661..8999864 100644
--- a/data/org.gnome.Mahjongg.gresource.xml
+++ b/data/org.gnome.Mahjongg.gresource.xml
@@ -3,4 +3,11 @@
   <gresource prefix="/org/gnome/Mahjongg/gtk">
     <file preprocess="xml-stripblanks" compressed="true">help-overlay.ui</file>
   </gresource>
+  <gresource prefix="/org/gnome/Mahjongg">
+    <file>ui/window.ui</file>
+    <file>ui/preferences.ui</file>
+    <file>ui/score-dialog.ui</file>
+    <file alias="icons/scalable/actions/question-round-symbolic.svg" 
preprocess="xml-stripblanks">icons/question-round-symbolic.svg</file>
+    <file>style.css</file>
+  </gresource>
 </gresources>
diff --git a/data/style.css b/data/style.css
new file mode 100644
index 0000000..07826e5
--- /dev/null
+++ b/data/style.css
@@ -0,0 +1,3 @@
+.mainview {
+  background: none;
+}
diff --git a/data/ui/preferences.ui b/data/ui/preferences.ui
new file mode 100644
index 0000000..cece868
--- /dev/null
+++ b/data/ui/preferences.ui
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="PreferencesWindow" parent="AdwPreferencesWindow">
+    <property name="default-height">-1</property>
+    <property name="default-width">-1</property>
+    <property name="search-enabled">false</property>
+    <child>
+      <object class="AdwPreferencesPage">
+        <child>
+          <object class="AdwPreferencesGroup">
+            <child>
+              <object class="AdwComboRow" id="themes_row">
+                <property name="title">Theme</property>
+              </object>
+            </child>
+            <child>
+              <object class="AdwComboRow" id="layout_row">
+                <property name="title">Layout</property>
+              </object>
+            </child>
+            <child>
+              <object class="AdwActionRow">
+                <property name="title">Background color</property>
+                <child>
+                  <object class="GtkColorButton" id="background_btn">
+                    <property name="valign">center</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/ui/score-dialog.ui b/data/ui/score-dialog.ui
new file mode 100644
index 0000000..9938a97
--- /dev/null
+++ b/data/ui/score-dialog.ui
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="ScoreDialog" parent="GtkDialog">
+    <property name="use-header-bar">1</property>
+    <property name="deletable">false</property>
+    <child internal-child="content_area">
+      <object class="GtkBox">
+        <property name="margin-top">12</property>
+        <property name="margin-bottom">12</property>
+        <property name="margin-start">12</property>
+        <property name="margin-end">12</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkBox">
+            <property name="halign">center</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="label">Layout: </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="layouts">
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <child>
+              <object class="GtkTreeView" id="scores">
+                <property name="vexpand">true</property>
+              </object>
+            </child>
+            <style>
+              <class name="frame"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/ui/window.ui b/data/ui/window.ui
new file mode 100644
index 0000000..fbd139b
--- /dev/null
+++ b/data/ui/window.ui
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="MahjonggWindow" parent="GtkApplicationWindow">
+    <child type="titlebar">
+      <object class="AdwHeaderBar">
+        <property name="title-widget">
+          <object class="AdwWindowTitle" id="titlewidget">
+            <style>
+              <class name="numeric"/>
+            </style>
+          </object>
+        </property>
+        <child type="start">
+          <object class="GtkButton">
+            <property name="icon-name">edit-undo-symbolic</property>
+            <property name="action-name">app.undo</property>
+          </object>
+        </child>
+        <child type="start">
+          <object class="GtkButton">
+            <property name="icon-name">edit-redo-symbolic</property>
+            <property name="action-name">app.redo</property>
+          </object>
+        </child>
+        <child type="end">
+          <object class="GtkMenuButton">
+            <property name="icon-name">open-menu-symbolic</property>
+            <property name="menu-model">menu</property>
+            <property name="primary">true</property>
+          </object>
+        </child>
+        <child type="end">
+          <object class="GtkButton">
+            <property name="icon-name">question-round-symbolic</property>
+            <property name="action-name">app.hint</property>
+          </object>
+        </child>
+        <child type="end">
+          <object class="GtkButton" id="pause_btn">
+            <property name="icon-name">media-playback-pause-symbolic</property>
+            <property name="action-name">app.pause</property>
+          </object>
+        </child>
+      </object>
+    </child>
+    <style>
+      <class name="mainview"/>
+    </style>
+  </template>
+  <menu id="menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">New Game</attribute>
+        <attribute name="action">app.new-game</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Restart Game</attribute>
+        <attribute name="action">app.restart-game</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Scores</attribute>
+        <attribute name="action">app.scores</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Preferences</attribute>
+        <attribute name="action">app.preferences</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>
+        <attribute name="action">win.show-help-overlay</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Help</attribute>
+        <attribute name="action">app.help</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">About Mahjongg</attribute>
+        <attribute name="action">app.about</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/flatpak/org.gnome.Mahjongg.json b/flatpak/org.gnome.Mahjongg.json
new file mode 100644
index 0000000..cade7cf
--- /dev/null
+++ b/flatpak/org.gnome.Mahjongg.json
@@ -0,0 +1,36 @@
+{
+    "app-id" : "org.gnome.Mahjongg",
+    "runtime" : "org.gnome.Platform",
+    "sdk" : "org.gnome.Sdk",
+    "runtime-version" : "master",
+    "command" : "gnome-mahjongg",
+    "finish-args" : [
+        "--share=ipc",
+        "--socket=fallback-x11",
+        "--socket=wayland",
+        "--device=dri",
+        "--filesystem=xdg-run/dconf",
+        "--filesystem=~/.config/dconf:ro",
+        "--talk-name=ca.desrt.dconf",
+        "--env=DCONF_USER_CONFIG_DIR=.config/dconf",
+        "--share=network"
+    ],
+    "modules" : [
+        {
+            "name" : "gnome-mahjongg",
+            "buildsystem" : "meson",
+            "sources" : [
+                {
+                    "type" : "git",
+                    "url" : "https://gitlab.gnome.org/GNOME/gnome-mahjongg.git";
+                }
+            ]
+        }
+    ],
+    "cleanup" : [
+        "/share/man"
+    ],
+    "build-options" : {
+        "env" : {        }
+    }
+}
diff --git a/meson.build b/meson.build
index 6b3dca9..be16c59 100644
--- a/meson.build
+++ b/meson.build
@@ -20,8 +20,9 @@ bindir      = join_paths (get_option ('prefix'), get_option ('bindir'))
 # Dependencies
 glib_dep    = dependency ('glib-2.0', version: '>= 2.40.0')
 gio_dep     = dependency ('gio-2.0', version: '>= 2.40.0')
-gtk_dep     = dependency ('gtk+-3.0', version: '>= 3.14.0')
-librsvg_dep = dependency ('librsvg-2.0', version: '>= 2.32.0')
+gtk_dep     = dependency ('gtk4', version: '>= 4.5.0')
+adwaita_dep = dependency ('libadwaita-1')
+librsvg_dep = dependency ('librsvg-2.0', version: '>= 2.46.0')
 
 subdir ('po')
 subdir ('data')
diff --git a/src/game-view.vala b/src/game-view.vala
index 2d120e9..bc4f0c4 100644
--- a/src/game-view.vala
+++ b/src/game-view.vala
@@ -32,7 +32,7 @@ public class GameView : Gtk.DrawingArea
     private uint   theme_resize_timer;
     private uint   theme_timer_id;
 
-    private Gtk.GestureMultiPress click_controller;     // for keeping in memory
+    private Gtk.GestureClick click_controller;     // for keeping in memory
 
     private Game? _game;
     public Game? game
@@ -81,16 +81,11 @@ public class GameView : Gtk.DrawingArea
             update_dimensions ();
 
             if (pixbuf != null) {
-                var region = get_window ().get_visible_region ();
-                var draw_ctx = get_window ().begin_draw_frame (region);
-                var cr = draw_ctx.get_cairo_context ();
-
-                var theme_surface = new Cairo.Surface.similar (cr.get_target (), Cairo.Content.COLOR_ALPHA, 
theme_width, theme_height);
+                var theme_surface = get_native ().get_surface ().create_similar_surface 
(Cairo.Content.COLOR_ALPHA, theme_width, theme_height);
                 var ctx = new Cairo.Context (theme_surface);
                 Gdk.cairo_set_source_pixbuf (ctx, pixbuf, 0, 0);
                 ctx.paint();
                 tile_pattern = new Cairo.Pattern.for_surface (theme_surface);
-                get_window ().end_draw_frame (draw_ctx);
             }
 
             if (theme_handle != null) {
@@ -105,32 +100,32 @@ public class GameView : Gtk.DrawingArea
     {
         can_focus = true;
         theme_timer_id = 0;
-        add_events (Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK);
         init_mouse ();
-        size_allocate.connect(() => {
-            /* Recalculate dimensions */
-            update_dimensions ();
+        set_draw_func (draw_func);
+    }
 
-            /* Resize the rsvg theme lazily after 300ms of the last resize event */
-            if (theme_timer_id != 0) {
-                GLib.Source.remove(theme_timer_id);
-                theme_timer_id = 0;
-            }
+    public override void size_allocate (int width, int height, int baseline) {
+        update_dimensions ();
 
-            if (theme_handle != null) {
-                theme_resize_timer = 2;
-                theme_timer_id = GLib.Timeout.add(100, () => {
-                    if (theme_resize_timer == 0) {
-                        resize_theme ();
-                        theme_timer_id = 0;
-                        return false;
-                    }
-
-                    theme_resize_timer--;
-                    return true;
-                });
-            }
-        });
+        /* Resize the rsvg theme lazily after 300ms of the last resize event */
+        if (theme_timer_id != 0) {
+            GLib.Source.remove(theme_timer_id);
+            theme_timer_id = 0;
+        }
+
+        if (theme_handle != null) {
+            theme_resize_timer = 2;
+            theme_timer_id = GLib.Timeout.add(100, () => {
+                if (theme_resize_timer == 0) {
+                    resize_theme ();
+                    theme_timer_id = 0;
+                    return false;
+                }
+
+                theme_resize_timer--;
+                return true;
+            });
+        }
     }
 
     private void resize_theme () {
@@ -147,17 +142,13 @@ public class GameView : Gtk.DrawingArea
             theme_height = (int) height;
         }
 
+
         while (theme_width < rendered_theme_width) {
             theme_width += theme_width;
             theme_height += theme_height;
         }
 
-
-        var region = get_window ().get_visible_region ();
-        var draw_ctx = get_window ().begin_draw_frame (region);
-        var cr = draw_ctx.get_cairo_context ();
-
-        var theme_surface = new Cairo.Surface.similar (cr.get_target (), Cairo.Content.COLOR_ALPHA, 
theme_width, theme_height);
+        var theme_surface = get_native ().get_surface ().create_similar_surface (Cairo.Content.COLOR_ALPHA, 
theme_width, theme_height);
         var ctx = new Cairo.Context (theme_surface);
 
         try {
@@ -166,8 +157,6 @@ public class GameView : Gtk.DrawingArea
             queue_draw();
         } catch (Error e) {
             warning ("Could not upscale theme");
-        } finally {
-            get_window ().end_draw_frame (draw_ctx);
         }
     }
 
@@ -304,9 +293,7 @@ public class GameView : Gtk.DrawingArea
     private void redraw_tile_cb (Tile tile)
     {
         update_dimensions ();
-        int x, y;
-        get_tile_position (tile, out x, out y);
-        queue_draw_area (x, y, tile_pattern_width, tile_pattern_height);
+        queue_draw ();
     }
 
     private void paused_changed_cb ()
@@ -314,25 +301,24 @@ public class GameView : Gtk.DrawingArea
         queue_draw ();
     }
 
-    public override bool draw (Cairo.Context cr)
+    public void draw_func (Gtk.DrawingArea area, Cairo.Context cr, int width, int height)
     {
         if (game == null)
-            return false;
+            return;
 
         Gdk.cairo_set_source_rgba (cr, background_color);
         cr.paint ();
         draw_game (cr);
-
-        return true;
     }
 
     private inline void init_mouse ()
     {
-        click_controller = new Gtk.GestureMultiPress (this);    // only reacts to Gdk.BUTTON_PRIMARY
+        click_controller = new Gtk.GestureClick ();    // only reacts to Gdk.BUTTON_PRIMARY
+        this.add_controller (click_controller);
         click_controller.pressed.connect (on_click);
     }
 
-    private inline void on_click (Gtk.GestureMultiPress _click_controller, int n_press, double event_x, 
double event_y)
+    private inline void on_click (Gtk.GestureClick _click_controller, int n_press, double event_x, double 
event_y)
     {
         if (game == null || game.paused)
             return;
diff --git a/src/gnome-mahjongg.vala b/src/gnome-mahjongg.vala
index 28ed6c8..08a47ad 100644
--- a/src/gnome-mahjongg.vala
+++ b/src/gnome-mahjongg.vala
@@ -8,7 +8,7 @@
  * license.
  */
 
-public class Mahjongg : Gtk.Application
+public class Mahjongg : Adw.Application
 {
     private Settings settings;
 
@@ -16,19 +16,9 @@ public class Mahjongg : Gtk.Application
 
     private List<Map> maps = new List<Map> ();
 
-    private Gtk.ApplicationWindow window;
-    private Gtk.Label title;
-    private Gtk.MenuButton menu_button;
-    private int window_width;
-    private int window_height;
-    private bool is_maximized;
-    private bool is_tiled;
+    private MahjonggWindow window;
 
     private GameView game_view;
-    private Gtk.Button pause_button;
-    private Gtk.Label moves_label;
-    private Gtk.Label clock_label;
-    private Gtk.Dialog? preferences_dialog = null;
 
     private const OptionEntry[] option_entries =
     {
@@ -48,7 +38,6 @@ public class Mahjongg : Gtk.Application
         { "preferences",   preferences_cb  },
         { "help",          help_cb         },
         { "about",         about_cb        },
-        { "hamburger",     hamburger_cb    },
         { "quit",          quit_cb         }
     };
 
@@ -73,149 +62,23 @@ public class Mahjongg : Gtk.Application
         set_accels_for_action ("app.help",      {                 "F1"      });
         set_accels_for_action ("app.quit",      {        "<Primary>q",
                                                          "<Primary>w"       });
-        set_accels_for_action ("app.hamburger", {                 "F10"     });
 
         settings = new Settings ("org.gnome.Mahjongg");
-
         load_maps ();
 
         history = new History (Path.build_filename (Environment.get_user_data_dir (), "gnome-mahjongg", 
"history"));
         history.load ();
 
-        window = new Gtk.ApplicationWindow (this);
-        window.size_allocate.connect (size_allocate_cb);
-        window.window_state_event.connect (window_state_event_cb);
-        window.set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
-        if (settings.get_boolean ("window-is-maximized"))
-            window.maximize ();
-
-        var status_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10);
-
-        var group_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
-        var label = new Gtk.Label (_("Moves Left:"));
-        group_box.pack_start (label, false, false, 0);
-        var spacer = new Gtk.Label (" ");
-        group_box.pack_start (spacer, false, false, 0);
-        moves_label = new Gtk.Label ("");
-        group_box.pack_start (moves_label, false, false, 0);
-        status_box.pack_start (group_box, false, false, 0);
-
-        clock_label = new Gtk.Label ("");
-        status_box.pack_start (clock_label, false, false, 0);
-
-        var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
-
         game_view = new GameView ();
-        view_click_controller = new Gtk.GestureMultiPress (game_view);
+        view_click_controller = new Gtk.GestureClick ();
         view_click_controller.pressed.connect (on_click);
-        game_view.set_size_request (600, 400);
-
-        title = new Gtk.Label ("");
-        title.get_style_context ().add_class ("title");
-
-        var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
-        hbox.get_style_context ().add_class ("linked");
-
-        var undo_button = new Gtk.Button.from_icon_name ("edit-undo-symbolic", Gtk.IconSize.BUTTON);
-        undo_button.valign = Gtk.Align.CENTER;
-        undo_button.action_name = "app.undo";
-        undo_button.set_tooltip_text (_("Undo your last move"));
-        hbox.pack_start (undo_button);
-
-        var redo_button = new Gtk.Button.from_icon_name ("edit-redo-symbolic", Gtk.IconSize.BUTTON);
-        redo_button.valign = Gtk.Align.CENTER;
-        redo_button.action_name = "app.redo";
-        redo_button.set_tooltip_text (_("Redo your last move"));
-        hbox.pack_start (redo_button);
-
-        var hint_button = new Gtk.Button.from_icon_name ("dialog-question-symbolic", Gtk.IconSize.BUTTON);
-        hint_button.valign = Gtk.Align.CENTER;
-        hint_button.action_name = "app.hint";
-        hint_button.set_tooltip_text (_("Receive a hint for your next move"));
-
-        pause_button = new Gtk.Button.from_icon_name ("media-playback-pause-symbolic", Gtk.IconSize.BUTTON);
-        pause_button.valign = Gtk.Align.CENTER;
-        pause_button.action_name = "app.pause";
-        pause_button.set_tooltip_text (_("Pause the game"));
-
-        var title_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 2);
-        title_box.pack_start (title, false, false, 0);
-        status_box.halign = Gtk.Align.CENTER;
-        title_box.pack_start (status_box, false, false, 0);
-
-        var menu = new Menu ();
-        var section = new Menu ();
-        section.append (_("_New Game"),         "app.new-game");
-        section.append (_("_Restart Game"),     "app.restart-game");
-        section.append (_("_Scores"),           "app.scores");
-        section.freeze ();
-        menu.append_section (/* no title */ null, section);
-        section = new Menu ();
-        section.append (_("_Preferences"),          "app.preferences");
-        section.append (_("_Keyboard Shortcuts"),   "win.show-help-overlay");
-        section.append (_("_Help"),                 "app.help");
-        section.append (_("_About Mahjongg"),       "app.about");
-        section.freeze ();
-        menu.append_section (/* no title */ null, section);
-        menu.freeze ();
-
-        menu_button = new Gtk.MenuButton ();
-        menu_button.valign = Gtk.Align.CENTER;
-        menu_button.set_menu_model (menu);
-        menu_button.set_image (new Gtk.Image.from_icon_name ("open-menu-symbolic", Gtk.IconSize.BUTTON));
-
-        var header_bar = new Gtk.HeaderBar ();
-        header_bar.set_custom_title (title_box);
-        header_bar.set_show_close_button (true);
-        header_bar.pack_start (hbox);
-        header_bar.pack_end (menu_button);
-        header_bar.pack_end (hint_button);
-        header_bar.pack_end (pause_button);
-        window.set_titlebar (header_bar);
-
-        vbox.pack_start (game_view, true, true, 0);
-
-        window.add (vbox);
-        window.show_all ();
-
-        settings.changed.connect (conf_value_changed_cb);
-
-        new_game ();
-
-        game_view.grab_focus ();
+        game_view.add_controller (view_click_controller);
 
-        conf_value_changed_cb (settings, "tileset");
-        conf_value_changed_cb (settings, "bgcolour");
-        tick_cb ();
-    }
-
-    private void size_allocate_cb (Gtk.Allocation allocation)
-    {
-        if (is_maximized || is_tiled)
-            return;
-        window.get_size (out window_width, out window_height);
-    }
-
-    private bool window_state_event_cb (Gdk.EventWindowState event)
-    {
-        if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
-            is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
-        /* We don’t save this state, but track it for saving size allocation */
-        if ((event.changed_mask & Gdk.WindowState.TILED) != 0)
-            is_tiled = (event.new_window_state & Gdk.WindowState.TILED) != 0;
-        return false;
-    }
+        window = new MahjonggWindow (this, game_view);
 
-    protected override void shutdown ()
-    {
-        base.shutdown ();
-
-        /* 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", is_maximized);
-        settings.apply ();
+        settings.bind("window-width", window, "default-width", SettingsBindFlags.DEFAULT);
+        settings.bind("window-height", window, "default-height", SettingsBindFlags.DEFAULT);
+        settings.bind("window-is-maximized", window, "maximized", SettingsBindFlags.DEFAULT);
     }
 
     protected override int handle_local_options (GLib.VariantDict options)
@@ -234,6 +97,16 @@ public class Mahjongg : Gtk.Application
     public override void activate ()
     {
         window.present ();
+
+        settings.changed.connect (conf_value_changed_cb);
+
+        new_game ();
+
+        game_view.grab_focus ();
+
+        conf_value_changed_cb (settings, "tileset");
+        conf_value_changed_cb (settings, "bgcolour");
+        tick_cb ();
     }
 
     private void update_ui ()
@@ -257,17 +130,6 @@ public class Mahjongg : Gtk.Application
             undo_action.set_enabled (game_view.game.can_undo);
             redo_action.set_enabled (game_view.game.can_redo);
         }
-
-        moves_label.set_text ("%2u".printf (game_view.game.moves_left));
-    }
-
-    private void theme_changed_cb (Gtk.ComboBox widget)
-    {
-        Gtk.TreeIter iter;
-        widget.get_active_iter (out iter);
-        string theme;
-        widget.model.get (iter, 1, out theme);
-        settings.set_string ("tileset", theme);
     }
 
     private void conf_value_changed_cb (Settings settings, string key)
@@ -296,35 +158,26 @@ public class Mahjongg : Gtk.Application
                                     _("Use _new map"), Gtk.ResponseType.ACCEPT,
                                     null);
                 dialog.set_default_response (Gtk.ResponseType.ACCEPT);
-                var response = dialog.run ();
-                if (response == Gtk.ResponseType.ACCEPT)
-                    new_game ();
-                dialog.destroy ();
+                dialog.response.connect ( (resp_id) => {
+                    if (resp_id == Gtk.ResponseType.ACCEPT)
+                        new_game ();
+                    dialog.destroy ();
+                });
+                dialog.show ();
             }
             else
                 new_game ();
         }
     }
 
-    private Gtk.GestureMultiPress view_click_controller;    // for keeping in memory
-    private inline void on_click (Gtk.GestureMultiPress _view_click_controller, int n_press, double event_x, 
double event_y)
+    private Gtk.GestureClick view_click_controller;    // for keeping in memory
+    private inline void on_click (Gtk.GestureClick _view_click_controller, int n_press, double event_x, 
double event_y)
     {
         /* Cancel pause on click */
         if (game_view.game.paused)
             pause_cb ();
     }
 
-    private void background_changed_cb (Gtk.ColorButton widget)
-    {
-        var colour = widget.get_rgba ();
-        settings.set_string ("bgcolour", "#%04x%04x%04x".printf ((int) (colour.red * 65536 + 0.5), (int) 
(colour.green * 65536 + 0.5), (int) (colour.blue * 65536 + 0.5)));
-    }
-
-    private void map_changed_cb (Gtk.ComboBox widget)
-    {
-        settings.set_string ("mapset", maps.nth_data (widget.active).name);
-    }
-
     enum NoMovesDialogResponse
     {
         UNDO,
@@ -344,11 +197,7 @@ public class Mahjongg : Gtk.Application
             var entry = new HistoryEntry (date, game_view.game.map.score_name, duration);
             history.add (entry);
             history.save ();
-
-            if (show_scores (entry, true) == Gtk.ResponseType.CLOSE)
-                window.destroy ();
-            else
-                new_game ();
+            show_scores (entry, true);
         }
         else if (!game_view.game.can_move)
         {
@@ -366,149 +215,58 @@ public class Mahjongg : Gtk.Application
                                 _("_New game"), NoMovesDialogResponse.NEW_GAME,
                                 allow_shuffle ? _("_Shuffle") : null, NoMovesDialogResponse.SHUFFLE);
 
-            var result = dialog.run ();
-            /* Shuffling may cause the dialog to appear again immediately,
-               so we destroy BEFORE doing anything with the result. */
-            dialog.destroy ();
-
-            switch (result)
-            {
-            case NoMovesDialogResponse.UNDO:
-                undo_cb ();
-                break;
-            case NoMovesDialogResponse.SHUFFLE:
-                shuffle_cb ();
-                break;
-            case NoMovesDialogResponse.RESTART:
-                restart_game ();
-                break;
-            case NoMovesDialogResponse.NEW_GAME:
-                new_game ();
-                break;
-            case Gtk.ResponseType.DELETE_EVENT:
-                break;
-            default:
-                assert_not_reached ();
-            }
+            dialog.response.connect ( (resp_id) => {
+                /* Shuffling may cause the dialog to appear again immediately,
+                   so we destroy BEFORE doing anything with the result. */
+                switch (resp_id)
+                {
+                case NoMovesDialogResponse.UNDO:
+                    undo_cb ();
+                    break;
+                case NoMovesDialogResponse.SHUFFLE:
+                    shuffle_cb ();
+                    break;
+                case NoMovesDialogResponse.RESTART:
+                    restart_game ();
+                    break;
+                case NoMovesDialogResponse.NEW_GAME:
+                    new_game ();
+                    break;
+                case Gtk.ResponseType.DELETE_EVENT:
+                    break;
+                default:
+                    assert_not_reached ();
+                }
+                dialog.destroy ();
+            });
+            dialog.show ();
         }
     }
 
-    private int show_scores (HistoryEntry? selected_entry = null, bool show_quit = false)
+    private void show_scores (HistoryEntry? selected_entry = null, bool show_quit = false)
     {
         var dialog = new ScoreDialog (history, selected_entry, show_quit, maps);
         dialog.modal = true;
         dialog.transient_for = window;
+        dialog.response.connect ((resp_id) => {
+            if (resp_id == Gtk.ResponseType.CLOSE)
+                window.destroy ();
+            else if (resp_id == Gtk.ResponseType.OK)
+                new_game ();
+            dialog.destroy ();
+        });
 
-        var result = dialog.run ();
-        dialog.destroy ();
-
-        return result;
+        dialog.show ();
     }
 
     private void preferences_cb ()
     {
-        if (preferences_dialog != null)
-        {
-            preferences_dialog.present ();
-            return;
-        }
-
-        bool dialogs_use_header;
-        Gtk.Settings.get_default ().get ("gtk-dialogs-use-header", out dialogs_use_header);
-
-        preferences_dialog = new Gtk.Dialog.with_buttons (_("Preferences"),
-                                                          window,
-                                                          dialogs_use_header ? 
Gtk.DialogFlags.USE_HEADER_BAR : 0,
-                                                          null);
-        preferences_dialog.set_border_width (5);
-        var dialog_content_area = (Gtk.Box) preferences_dialog.get_content_area ();
-        dialog_content_area.set_spacing (2);
-        preferences_dialog.set_resizable (false);
-        preferences_dialog.set_default_response (Gtk.ResponseType.CLOSE);
-        preferences_dialog.response.connect (preferences_dialog_response_cb);
-
-        var grid = new Gtk.Grid ();
-        grid.border_width = 5;
-        grid.set_row_spacing (6);
-        grid.set_column_spacing (18);
-
-        var label = new Gtk.Label.with_mnemonic (_("_Theme:"));
-        label.set_alignment (0, 0.5f);
-        grid.attach (label, 0, 0, 1, 1);
-
-        var themes = load_themes ();
-        var theme_combo = new Gtk.ComboBox ();
-        var theme_store = new Gtk.ListStore (2, typeof (string), typeof (string));
-        theme_combo.model = theme_store;
-        var renderer = new Gtk.CellRendererText ();
-        theme_combo.pack_start (renderer, true);
-        theme_combo.add_attribute (renderer, "text", 0);
-        foreach (var theme in themes)
-        {
-            var tokens = theme.split (".", -1);
-            var name = tokens[0];
-
-            Gtk.TreeIter iter;
-            theme_store.append (out iter);
-            theme_store.set (iter, 0, name, 1, theme, -1);
-
-            if (theme == settings.get_string ("tileset"))
-                theme_combo.set_active_iter (iter);
-        }
-        theme_combo.changed.connect (theme_changed_cb);
-        theme_combo.set_hexpand (true);
-        grid.attach (theme_combo, 1, 0, 1, 1);
-        label.set_mnemonic_widget (theme_combo);
-
-        label = new Gtk.Label.with_mnemonic (_("_Layout:"));
-        label.set_alignment (0, 0.5f);
-        grid.attach (label, 0, 1, 1, 1);
-
-        var map_combo = new Gtk.ComboBox ();
-        var map_store = new Gtk.ListStore (2, typeof (string), typeof (string));
-        map_combo.model = map_store;
-        renderer = new Gtk.CellRendererText ();
-        map_combo.pack_start (renderer, true);
-        map_combo.add_attribute (renderer, "text", 0);
-        foreach (var map in maps)
-        {
-            var display_name = dpgettext2 (null, "mahjongg map name", map.name);
-
-            Gtk.TreeIter iter;
-            map_store.append (out iter);
-            map_store.set (iter, 0, display_name, 1, map, -1);
-
-            if (settings.get_string ("mapset") == map.name)
-                map_combo.set_active_iter (iter);
-        }
-        map_combo.changed.connect (map_changed_cb);
-        map_combo.set_hexpand (true);
-        grid.attach (map_combo, 1, 1, 1, 1);
-        label.set_mnemonic_widget (map_combo);
-
-        label = new Gtk.Label.with_mnemonic (_("_Background color:"));
-        label.set_alignment (0, 0.5f);
-        grid.attach (label, 0, 2, 1, 1);
-
-        var widget = new Gtk.ColorButton ();
-        widget.set_rgba (game_view.background_color);
-        widget.color_set.connect (background_changed_cb);
-        widget.set_hexpand (true);
-        grid.attach (widget, 1, 2, 1, 1);
-        label.set_mnemonic_widget (widget);
-
-        dialog_content_area.pack_start (grid, true, true, 0);
-
-        if (!dialogs_use_header)
-            preferences_dialog.add_button (_("_Close"), Gtk.ResponseType.CLOSE);
-
-        preferences_dialog.show_all ();
-    }
-
-    private void preferences_dialog_response_cb (Gtk.Dialog dialog, int response)
-    {
-        preferences_dialog.destroy ();
-        preferences_dialog = null;
+        var preferences = new PreferencesWindow (settings);
+        preferences.populate_themes (load_themes ());
+        preferences.populate_layouts (maps);
+        preferences.populate_background (game_view.background_color);
+        preferences.set_transient_for (window);
+        preferences.show();
     }
 
     private List<string> load_themes ()
@@ -563,11 +321,6 @@ public class Mahjongg : Gtk.Application
         game_view.game.shuffle_remaining ();
     }
 
-    private inline void hamburger_cb ()
-    {
-        menu_button.active = !menu_button.active;
-    }
-
     private void about_cb ()
     {
         string[] authors =
@@ -580,6 +333,7 @@ public class Mahjongg : Gtk.Application
             "Philippe Chavin",
             "Callum McKenzie",
             "Robert Ancell",
+            "Günther Wagner",
             "",
             _("Maps:"),
             "Rexford Newbould",
@@ -627,16 +381,13 @@ public class Mahjongg : Gtk.Application
         game_view.game.set_hint (null, null);
         game_view.game.selected_tile = null;
 
-        var pause_image = (Gtk.Image) pause_button.image;
         if (game_view.game.paused)
         {
-            pause_image.icon_name = "media-playback-start-symbolic";
-            pause_button.set_tooltip_text (_("Unpause the game"));
+            window.pause ();
         }
         else
         {
-            pause_image.icon_name = "media-playback-pause-symbolic";
-            pause_button.set_tooltip_text (_("Pause the game"));
+            window.unpause ();
         }
 
         update_ui ();
@@ -704,8 +455,7 @@ public class Mahjongg : Gtk.Application
         game_view.game.tick.connect (tick_cb);
 
         /* Set window title */
-        var display_name = dpgettext2 (null, "mahjongg map name", game_view.game.map.name);
-        title.set_label (_(display_name));
+        window.set_map_title (game_view);
 
         update_ui ();
 
@@ -713,12 +463,12 @@ public class Mahjongg : Gtk.Application
         tick_cb ();
 
         /* Reset the pause button in case it was set to resume */
-        var pause_image = (Gtk.Image) pause_button.image;
-        pause_image.icon_name = "media-playback-pause-symbolic";
+        window.unpause ();
     }
 
     private void tick_cb ()
     {
+        string clock;
         var elapsed = 0;
         if (game_view.game != null)
             elapsed = (int) (game_view.game.elapsed + 0.5);
@@ -726,21 +476,16 @@ public class Mahjongg : Gtk.Application
         var minutes = (elapsed - hours * 3600) / 60;
         var seconds = elapsed - hours * 3600 - minutes * 60;
         if (hours > 0)
-            clock_label.set_text ("%02d∶\xE2\x80\x8E%02d∶\xE2\x80\x8E%02d".printf (hours, minutes, seconds));
+            clock = "%02d∶\xE2\x80\x8E%02d∶\xE2\x80\x8E%02d".printf (hours, minutes, seconds);
         else
-            clock_label.set_text ("%02d∶\xE2\x80\x8E%02d".printf (minutes, seconds));
+            clock = "%02d∶\xE2\x80\x8E%02d".printf (minutes, seconds);
+
+        window.set_subtitle (game_view, clock);
     }
 
     private void help_cb ()
     {
-        try
-        {
-            Gtk.show_uri_on_window (window, "help:gnome-mahjongg", Gtk.get_current_event_time ());
-        }
-        catch (Error e)
-        {
-            warning ("Failed to show help: %s", e.message);
-        }
+        Gtk.show_uri (window, "help:gnome-mahjongg", Gdk.CURRENT_TIME);
     }
 
     private void load_maps ()
@@ -794,6 +539,7 @@ public class Mahjongg : Gtk.Application
         Environment.set_application_name (_("Mahjongg"));
         Gtk.Window.set_default_icon_name ("org.gnome.Mahjongg");
 
+        typeof(GameView).ensure();
         var app = new Mahjongg ();
         var result = app.run (args);
 
diff --git a/src/meson.build b/src/meson.build
index 4227c3a..6170dde 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -9,12 +9,15 @@ gnome_mahjongg = executable (
     'gnome-mahjongg.vala',
     'history.vala',
     'map.vala',
-    'score-dialog.vala'
+    'score-dialog.vala',
+    'window.vala',
+    'preferences.vala',
   ],
   dependencies: [
     glib_dep,
     gtk_dep,
-    librsvg_dep
+    adwaita_dep,
+    librsvg_dep,
   ],
   vala_args: [ '--pkg=posix' ],
   c_args: [
diff --git a/src/preferences.vala b/src/preferences.vala
new file mode 100644
index 0000000..ae05406
--- /dev/null
+++ b/src/preferences.vala
@@ -0,0 +1,91 @@
+[GtkTemplate (ui = "/org/gnome/Mahjongg/ui/preferences.ui")]
+public class PreferencesWindow : Adw.PreferencesWindow {
+
+    [GtkChild]
+    private unowned Adw.ComboRow themes_row;
+
+    [GtkChild]
+    private unowned Adw.ComboRow layout_row;
+
+    [GtkChild]
+    private unowned Gtk.ColorButton background_btn;
+
+    private Settings settings;
+
+    public PreferencesWindow (Settings settings) {
+        this.settings = settings;
+        themes_row.set_expression (new Gtk.PropertyExpression (typeof (ThemeItem), null, "name"));
+    }
+
+    public void populate_themes (List<string> themes)
+    {
+        var model = new ListStore(typeof(ThemeItem));
+        foreach (var theme in themes)
+        {
+            model.append (new ThemeItem (theme));
+        }
+
+        themes_row.set_model (model);
+        for (int i = 0; i < model.get_n_items(); i++)
+        {
+            if (settings.get_string ("tileset") == ((ThemeItem)model.get_item (i)).filename)
+            {
+                themes_row.set_selected (i);
+                break;
+            }
+        }
+
+        themes_row.notify["selected"].connect ( (s, p) => {
+            var thememodel = themes_row.model as ListModel;
+            var theme = ((ThemeItem)thememodel.get_item (themes_row.selected)).filename;
+            settings.set_string ("tileset", theme);
+        });
+    }
+
+    public void populate_layouts (List<Map> layouts)
+    {
+        var model = new Gtk.StringList(null);
+        foreach (var map in layouts)
+        {
+            var display_name = dpgettext2 (null, "mahjongg map name", map.name);
+            model.append (display_name);
+        }
+        layout_row.set_model (model);
+
+        for (int i = 0; i < model.get_n_items(); i++)
+        {
+            if (settings.get_string ("mapset") == model.get_string (i))
+            {
+                layout_row.set_selected (i);
+                break;
+            }
+        }
+
+        layout_row.notify["selected"].connect ( (s, p) => {
+            var layoutmodel = layout_row.model as Gtk.StringList;
+            var layout = layoutmodel.get_string (layout_row.selected);
+            settings.set_string ("mapset", layout);
+        });
+    }
+
+    public void populate_background (Gdk.RGBA background_color)
+    {
+        background_btn.set_rgba (background_color);
+        background_btn.color_set.connect ( () => {
+            var colour = background_btn.get_rgba ();
+            settings.set_string ("bgcolour", colour.to_string ());
+        });
+    }
+}
+
+public class ThemeItem : Object {
+    public string name {get; set;}
+    public string filename {get; set;}
+
+    public ThemeItem (string filename)
+    {
+        var tokens = filename.split (".", -1);
+        this.name = tokens[0];
+        this.filename = filename;
+    }
+}
diff --git a/src/score-dialog.vala b/src/score-dialog.vala
index 65bcaf0..4777f68 100644
--- a/src/score-dialog.vala
+++ b/src/score-dialog.vala
@@ -8,23 +8,36 @@
  * license.
  */
 
+[GtkTemplate (ui = "/org/gnome/Mahjongg/ui/score-dialog.ui")]
 public class ScoreDialog : Gtk.Dialog
 {
+    [GtkChild]
+    private unowned Gtk.ComboBox layouts;
+
+    [GtkChild]
+    private unowned Gtk.TreeView scores;
+
     private History history;
     private HistoryEntry? selected_entry = null;
     private Gtk.ListStore size_model;
     private Gtk.ListStore score_model;
-    private Gtk.ComboBox size_combo;
-    private Gtk.TreeView scores;
     private unowned List<Map> maps;
 
     public ScoreDialog (History history, HistoryEntry? selected_entry = null, bool show_quit = false, 
List<Map> maps)
     {
+        Object(use_header_bar: 1);
         this.maps = maps;
         this.history = history;
         history.entry_added.connect (entry_added_cb);
         this.selected_entry = selected_entry;
 
+        size_model = new Gtk.ListStore (2, typeof (string), typeof (string));
+        layouts.changed.connect (size_changed_cb);
+        layouts.model = size_model;
+        var renderer = new Gtk.CellRendererText ();
+        layouts.pack_start (renderer, true);
+        layouts.add_attribute (renderer, "text", 0);
+
         if (show_quit)
         {
             add_button (_("_Quit"), Gtk.ResponseType.CLOSE);
@@ -34,47 +47,14 @@ public class ScoreDialog : Gtk.Dialog
             add_button (_("OK"), Gtk.ResponseType.DELETE_EVENT);
         set_size_request (200, 300);
 
-        var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
-        vbox.border_width = 6;
-        vbox.show ();
-        get_content_area ().pack_start (vbox, true, true, 0);
-
-        var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
-        hbox.show ();
-        vbox.pack_start (hbox, false, false, 0);
-
-        var label = new Gtk.Label (_("Layout:"));
-        label.show ();
-        hbox.pack_start (label, false, false, 0);
-
-        size_model = new Gtk.ListStore (2, typeof (string), typeof (string));
-
-        size_combo = new Gtk.ComboBox ();
-        size_combo.changed.connect (size_changed_cb);
-        size_combo.model = size_model;
-        var renderer = new Gtk.CellRendererText ();
-        size_combo.pack_start (renderer, true);
-        size_combo.add_attribute (renderer, "text", 0);
-        size_combo.show ();
-        hbox.pack_start (size_combo, true, true, 0);
-
-        var scroll = new Gtk.ScrolledWindow (null, null);
-        scroll.shadow_type = Gtk.ShadowType.ETCHED_IN;
-        scroll.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
-        scroll.show ();
-        vbox.pack_start (scroll, true, true, 0);
-
         score_model = new Gtk.ListStore (3, typeof (string), typeof (string), typeof (int));
 
-        scores = new Gtk.TreeView ();
         renderer = new Gtk.CellRendererText ();
         scores.insert_column_with_attributes (-1, _("Date"), renderer, "text", 0, "weight", 2);
         renderer = new Gtk.CellRendererText ();
         renderer.xalign = 1.0f;
         scores.insert_column_with_attributes (-1, _("Time"), renderer, "text", 1, "weight", 2);
         scores.model = score_model;
-        scores.show ();
-        scroll.add (scores);
 
         foreach (var entry in history.entries)
             entry_added_cb (entry);
@@ -180,12 +160,12 @@ public class ScoreDialog : Gtk.Dialog
             size_model.set (iter, 0, display_name, 1, entry.name);
 
             /* Select this entry if don't have any */
-            if (size_combo.get_active () == -1)
-                size_combo.set_active_iter (iter);
+            if (layouts.get_active () == -1)
+                layouts.set_active_iter (iter);
 
             /* Select this entry if the same category as the selected one */
             if (selected_entry != null && entry.name == selected_entry.name)
-                size_combo.set_active_iter (iter);
+                layouts.set_active_iter (iter);
         }
     }
 }
diff --git a/src/window.vala b/src/window.vala
new file mode 100644
index 0000000..235c645
--- /dev/null
+++ b/src/window.vala
@@ -0,0 +1,41 @@
+[GtkTemplate (ui = "/org/gnome/Mahjongg/ui/window.ui")]
+public class MahjonggWindow : Gtk.ApplicationWindow {
+
+    [GtkChild]
+    private unowned Adw.WindowTitle titlewidget;
+
+    [GtkChild]
+    private unowned Gtk.Button pause_btn;
+
+    public MahjonggWindow (Gtk.Application application, GameView game_view)
+    {
+        Object(application: application);
+        this.set_child (game_view);
+    }
+
+    public void set_map_title (GameView game_view)
+    {
+        var display_name = dpgettext2 (null, "mahjongg map name", game_view.game.map.name);
+        titlewidget.set_title (display_name);
+    }
+
+    public void set_subtitle (GameView game_view, string clock)
+    {
+        if (game_view.game.move_number != 1)
+            titlewidget.set_subtitle ("Moves Left: %2u   %s".printf (game_view.game.moves_left, clock));
+        else
+            titlewidget.set_subtitle ("Moves Left: 0   %s".printf (clock));
+    }
+
+    public void pause ()
+    {
+        pause_btn.icon_name = "media-playback-start-symbolic";
+        pause_btn.set_tooltip_text (_("Unpause the game"));
+    }
+
+    public void unpause ()
+    {
+        pause_btn.icon_name = "media-playback-pause-symbolic";
+        pause_btn.set_tooltip_text (_("Pause the game"));
+    }
+}


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