[gnome-games] ui: Add the SavestatesList widget



commit 2816850f56b745d6e57bfe2d7a387bd0529e1c79
Author: Yetizone <andreii lisita gmail com>
Date:   Wed Aug 7 18:44:24 2019 +0300

    ui: Add the SavestatesList widget

 data/gtk-style.css                 |  10 +++
 data/org.gnome.Games.gresource.xml |   1 +
 data/ui/savestates-list.ui         |  76 +++++++++++++++++
 src/meson.build                    |   2 +
 src/ui/savestates-list-state.vala  |   6 ++
 src/ui/savestates-list.vala        | 171 +++++++++++++++++++++++++++++++++++++
 6 files changed, 266 insertions(+)
---
diff --git a/data/gtk-style.css b/data/gtk-style.css
index e1e66c52..410a5f59 100644
--- a/data/gtk-style.css
+++ b/data/gtk-style.css
@@ -12,6 +12,16 @@
   padding: 0px;
 }
 
+.savestate-thumbnail {
+  min-width: 64px;
+  min-height: 64px;
+  color: rgba(255, 255, 255, 0.5);
+  background: rgba (0, 0, 0, .5);
+  border: 1px solid rgba (0, 0, 0, .5);
+  margin: 6px;
+  border-radius: 5px;
+}
+
 gamesgamethumbnail {
        background-color: mix (@theme_base_color, @theme_bg_color, 0.5);
        border-width: 1px;
diff --git a/data/org.gnome.Games.gresource.xml b/data/org.gnome.Games.gresource.xml
index 91fef297..d157b0a0 100644
--- a/data/org.gnome.Games.gresource.xml
+++ b/data/org.gnome.Games.gresource.xml
@@ -49,6 +49,7 @@
     <file preprocess="xml-stripblanks">ui/resume-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/resume-failed-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/savestate-listbox-row.ui</file>
+    <file preprocess="xml-stripblanks">ui/savestates-list.ui</file>
     <file preprocess="xml-stripblanks">ui/search-bar.ui</file>
     <file preprocess="xml-stripblanks">ui/shortcuts-window.ui</file>
   </gresource>
diff --git a/data/ui/savestates-list.ui b/data/ui/savestates-list.ui
new file mode 100644
index 00000000..4fca186f
--- /dev/null
+++ b/data/ui/savestates-list.ui
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <template class="GamesSavestatesList" parent="GtkBox">
+    <child>
+      <object class="GtkRevealer" id="revealer">
+        <property name="visible">True</property>
+        <property name="reveal-child">False</property>
+        <property name="transition-type">slide-left</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkSeparator">
+                <property name="visible">True</property>
+                <style>
+                  <class name="sidebar"/>
+                </style>
+              </object>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolled_window">
+                <property name="visible">True</property>
+                <property name="vexpand">True</property>
+                <property name="width-request">350</property>
+                <child>
+                  <object class="GtkListBox" id="list_box">
+                    <property name="visible">True</property>
+                    <signal name="row-activated" after="yes" handler="on_row_activated"/>
+                    <style>
+                      <class name="sidebar"/>
+                    </style>
+                    <child>
+                      <object class="GtkListBoxRow" id="new_savestate_row">
+                        <property name="visible">True</property>
+                        <style>
+                          <class name="savestate-row"/>
+                        </style>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkImage">
+                                <property name="visible">True</property>
+                                <property name="icon-name">list-add-symbolic</property>
+                                <property name="pixel-size">32</property>
+                                <style>
+                                  <class name="savestate-thumbnail"/>
+                                </style>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="margin">12</property>
+                                <property name="label">Create new savestate</property>
+                                <attributes>
+                                  <!-- "1.2" is the value of "large" -->
+                                  <attribute name="scale" value="1.2"/>
+                                </attributes>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/meson.build b/src/meson.build
index 021e7427..467fb9e4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -181,6 +181,8 @@ vala_sources = [
   'ui/resume-dialog.vala',
   'ui/resume-failed-dialog.vala',
   'ui/savestate-listbox-row.vala',
+  'ui/savestates-list.vala',
+  'ui/savestates-list-state.vala',
   'ui/search-bar.vala',
   'ui/shortcuts-window.vala',
   'ui/ui-view.vala',
diff --git a/src/ui/savestates-list-state.vala b/src/ui/savestates-list-state.vala
new file mode 100644
index 00000000..0915d300
--- /dev/null
+++ b/src/ui/savestates-list-state.vala
@@ -0,0 +1,6 @@
+private class Games.SavestatesListState : Object {
+       public signal void load_clicked ();
+       public signal void delete_clicked ();
+
+       public bool is_revealed { get; set; }
+}
diff --git a/src/ui/savestates-list.vala b/src/ui/savestates-list.vala
new file mode 100644
index 00000000..22429fed
--- /dev/null
+++ b/src/ui/savestates-list.vala
@@ -0,0 +1,171 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[GtkTemplate (ui = "/org/gnome/Games/ui/savestates-list.ui")]
+private class Games.SavestatesList : Gtk.Box {
+       [GtkChild]
+       private Gtk.Revealer revealer;
+       [GtkChild]
+       private Gtk.ListBox list_box;
+       [GtkChild]
+       private Gtk.ListBoxRow new_savestate_row;
+
+       public bool is_revealed {
+               get { return revealer.reveal_child; }
+               set { revealer.reveal_child = value; }
+       }
+
+       private SavestatesListState _state;
+       public SavestatesListState state {
+               get { return _state; }
+               set {
+                       if (_state != null)
+                               _state.notify["is-revealed"].disconnect (on_state_changed);
+
+                       _state = value;
+
+                       if (value != null) {
+                               value.notify["is-revealed"].connect (on_state_changed);
+                               value.load_clicked.connect (on_load_clicked);
+                               value.delete_clicked.connect (on_delete_clicked);
+                       }
+               }
+       }
+
+       private Runner _runner;
+       public Runner runner {
+               get { return _runner; }
+               set {
+                       _runner = value;
+
+                       // Remove current savestate rows
+                       var list_rows =  list_box.get_children ();
+                       foreach (var row in list_rows) {
+                               if (row != new_savestate_row)
+                                       list_box.remove (row);
+                       }
+
+                       if (value == null)
+                               return;
+
+                       // value != null
+                       var savestates = _runner.get_savestates ();
+                       foreach (var savestate in savestates) {
+                               var list_row = new SavestateListBoxRow (savestate);
+
+                               list_box.add (list_row);
+                       }
+               }
+       }
+
+       construct {
+               list_box.set_header_func (update_header);
+       }
+
+       [GtkCallback]
+       private void on_row_activated (Gtk.ListBoxRow activated_row) {
+               if (activated_row == new_savestate_row) {
+                       var savestate = runner.try_create_savestate (false);
+
+                       if (savestate != null) {
+                               var savestate_row = new SavestateListBoxRow (savestate);
+
+                               list_box.insert (savestate_row, 1);
+                               select_and_preview_row (savestate_row);
+                       }
+                       else {
+                               // Savestate creation failed
+                               list_box.select_row (list_box.get_row_at_index (1));
+
+                               // TODO: Perhaps we should warn the user that the creation of
+                               // the savestate failed via an in-app notification ?
+                       }
+               } else {
+                       var savestate_row = activated_row as SavestateListBoxRow;
+                       var savestate = savestate_row.savestate;
+
+                       runner.preview_savestate (savestate);
+               }
+       }
+
+       private void on_load_clicked () {
+               if (!try_runner_load_previewed_savestate ()) {
+                       // TODO: Here we could show a dialog with one button like
+                       // "Failed to load savestate [Ok]"
+               }
+
+               state.is_revealed = false;
+       }
+
+       private bool try_runner_load_previewed_savestate () {
+               try {
+                       _runner.load_previewed_savestate ();
+               }
+               catch (Error e) {
+                       critical ("Failed to load savestate: %s", e.message);
+
+                       return false;
+               }
+
+               // Nothing went wrong
+               return true;
+       }
+
+       private void on_state_changed () {
+               revealer.reveal_child = state.is_revealed;
+
+               if (state.is_revealed) {
+                       list_box.select_row (null);
+                       runner.capture_current_state_pixbuf ();
+                       runner.pause ();
+               }
+               else
+                       runner.resume ();
+       }
+
+       private void on_delete_clicked () {
+               var selected_row = list_box.get_selected_row ();
+               var selected_row_index = selected_row.get_index ();
+               var savestate_row = selected_row as SavestateListBoxRow;
+               var savestate = savestate_row.savestate;
+
+               runner.delete_savestate (savestate);
+               list_box.remove (selected_row);
+
+               // Select and preview a new row
+               var next_row = list_box.get_row_at_index (selected_row_index);
+
+               if (next_row == null) { // The last row in the list has been deleted
+                       var nr_rows = list_box.get_children ().length ();
+
+                       if (nr_rows == 1) {
+                               // The only remaining row in the list is the create savestate one
+                               runner.preview_current_state ();
+                       }
+                       else {
+                               // The last row of the list has been deleted but there are still
+                               // rows remaining in the list
+                               var last_row = list_box.get_row_at_index (selected_row_index - 1);
+                               select_and_preview_row (last_row);
+                       }
+
+                       return;
+               }
+
+               select_and_preview_row (next_row);
+       }
+
+       private void update_header (Gtk.ListBoxRow row, Gtk.ListBoxRow? before) {
+               if (before != null && row.get_header () == null) {
+                       var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
+                       row.set_header (separator);
+               }
+       }
+
+       private void select_and_preview_row (Gtk.ListBoxRow row) {
+               var savestate_row = row as SavestateListBoxRow;
+               var savestate = savestate_row.savestate;
+
+               list_box.select_row (row);
+               runner.preview_savestate (savestate);
+       }
+}


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