[gnome-games] ui: Use the SavestatesList widget
- From: Alexander Mikhaylenko <alexm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games] ui: Use the SavestatesList widget
- Date: Fri, 9 Aug 2019 13:40:24 +0000 (UTC)
commit a9b47cd3f8c4a566692d086998528c664923b247
Author: Yetizone <andreii lisita gmail com>
Date: Wed Aug 7 19:10:23 2019 +0300
ui: Use the SavestatesList widget
data/ui/display-box.ui | 13 ++++-
data/ui/display-header-bar.ui | 121 ++++++++++++++++++++++++++++++++++++-----
src/ui/display-box.vala | 28 +++++++++-
src/ui/display-header-bar.vala | 71 +++++++++++++++++++++---
src/ui/display-view.vala | 35 +++++++++---
5 files changed, 237 insertions(+), 31 deletions(-)
---
diff --git a/data/ui/display-box.ui b/data/ui/display-box.ui
index db028f54..7cef4bdf 100644
--- a/data/ui/display-box.ui
+++ b/data/ui/display-box.ui
@@ -25,8 +25,19 @@
</packing>
</child>
<child>
- <object class="GtkEventBox" id="display_bin">
+ <object class="GtkBox" id="display_box">
<property name="visible">True</property>
+ <child>
+ <object class="GtkEventBox" id="display_bin">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GamesSavestatesList" id="savestates_list">
+ <property name="visible">True</property>
+ </object>
+ </child>
</object>
<packing>
<property name="name">display</property>
diff --git a/data/ui/display-header-bar.ui b/data/ui/display-header-bar.ui
index 61514a69..8474e6af 100644
--- a/data/ui/display-header-bar.ui
+++ b/data/ui/display-header-bar.ui
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
- <template class="GamesDisplayHeaderBar" parent="GtkBin">
+ <template class="GamesDisplayHeaderBar" parent="GtkStack">
<property name="visible">True</property>
<signal name="notify::is-fullscreen" handler="on_fullscreen_changed"/>
<signal name="notify::can-fullscreen" handler="on_fullscreen_changed"/>
<child>
- <object class="GtkHeaderBar" id="header_bar">
+ <object class="GtkHeaderBar" id="ingame_header_bar">
<property name="visible">True</property>
<property name="title" translatable="yes">Games</property>
<property name="show_close_button">True</property>
@@ -35,24 +35,28 @@
</object>
</child>
<child>
- <object class="GtkButton" id="fullscreen">
+ <object class="GtkButton" id="restore">
<property name="visible">False</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
- <signal name="clicked" handler="on_fullscreen_clicked"/>
+ <property name="margin-left">5</property>
+ <signal name="clicked" handler="on_restore_clicked"/>
<style>
<class name="image-button"/>
</style>
+ <style>
+ <class name="flat"/>
+ </style>
<child internal-child="accessible">
- <object class="AtkObject" id="a11y-fullscreen">
- <property name="accessible-name" translatable="yes">Fullscreen</property>
+ <object class="AtkObject" id="a11y-restore">
+ <property name="accessible-name" translatable="yes">Restore</property>
</object>
</child>
<child>
- <object class="GtkImage" id="fullscreen_image">
+ <object class="GtkImage" id="restore_image">
<property name="visible">True</property>
- <property name="icon-name">view-fullscreen-symbolic</property>
+ <property name="icon-name">view-restore-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
@@ -62,24 +66,46 @@
</packing>
</child>
<child>
- <object class="GtkButton" id="restore">
+ <object class="GtkMenuButton" id="secondary_menu_button">
+ <property name="popover">secondary_menu</property>
+ <property name="use-underline">True</property>
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="can-focus">False</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">view-more-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="fullscreen">
<property name="visible">False</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
- <signal name="clicked" handler="on_restore_clicked"/>
+ <signal name="clicked" handler="on_fullscreen_clicked"/>
<style>
<class name="image-button"/>
</style>
<child internal-child="accessible">
- <object class="AtkObject" id="a11y-restore">
- <property name="accessible-name" translatable="yes">Restore</property>
+ <object class="AtkObject" id="a11y-fullscreen">
+ <property name="accessible-name" translatable="yes">Fullscreen</property>
</object>
</child>
<child>
- <object class="GtkImage" id="restore_image">
+ <object class="GtkImage" id="fullscreen_image">
<property name="visible">True</property>
- <property name="icon-name">view-restore-symbolic</property>
+ <property name="icon-name">view-fullscreen-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
@@ -104,5 +130,72 @@
</child>
</object>
</child>
+ <child>
+ <object class="GtkHeaderBar" id="savestates_header_bar">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="load">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ <property name="label" translatable="yes">_Load</property>
+ <signal name="clicked" handler="on_savestates_load_clicked"/>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <signal name="clicked" handler="on_savestates_delete_clicked"/>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ <property name="label" translatable="yes">_Cancel</property>
+ <signal name="clicked" handler="on_savestates_cancel_clicked"/>
+ </object>
+ <packing>
+ <property name="pack-type">start</property>
+ </packing>
+ </child>
+ </object>
+ </child>
</template>
+ <object class="GtkPopoverMenu" id="secondary_menu">
+ <child>
+ <object class="GtkBox" id="secondary_menu_box">
+ <property name="visible">True</property>
+ <property name="margin">6</property>
+ <child>
+ <object class="GtkModelButton" id="savestates_menu_button">
+ <property name="visible">True</property>
+ <property name="text" translatable="yes">_Savestates</property>
+ <signal name="clicked" handler="on_secondary_menu_savestates_clicked"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkSizeGroup">
+ <property name="mode">GTK_SIZE_GROUP_HORIZONTAL</property>
+ <widgets>
+ <widget name="load"/>
+ <widget name="delete"/>
+ <widget name="cancel"/>
+ </widgets>
+ </object>
</interface>
diff --git a/src/ui/display-box.vala b/src/ui/display-box.vala
index 1162066c..f268d8ea 100644
--- a/src/ui/display-box.vala
+++ b/src/ui/display-box.vala
@@ -10,11 +10,21 @@ private class Games.DisplayBox : Gtk.Bin {
get { return fullscreen_header_bar; }
}
+ public SavestatesListState savestates_list_state {
+ get { return savestates_list.state; }
+ set {
+ value.notify["is-revealed"].connect (on_savestates_list_state_changed);
+
+ savestates_list.state = value;
+ fullscreen_header_bar.savestates_list_state = value;
+ }
+ }
+
private Runner _runner;
public Runner runner {
get { return _runner; }
set {
- stack.visible_child = display_bin;
+ stack.visible_child = display_box;
_runner = value;
remove_display ();
@@ -25,6 +35,8 @@ private class Games.DisplayBox : Gtk.Bin {
var display = runner.get_display ();
set_display (display);
+
+ savestates_list.runner = value;
}
}
@@ -35,11 +47,15 @@ private class Games.DisplayBox : Gtk.Bin {
[GtkChild]
private ErrorDisplay error_display;
[GtkChild]
+ private Gtk.Box display_box;
+ [GtkChild]
private Gtk.EventBox display_bin;
[GtkChild]
private DisplayHeaderBar fullscreen_header_bar;
- private Binding fullscreen_binding;
+ [GtkChild]
+ private SavestatesList savestates_list;
+ private Binding fullscreen_binding;
private long timeout_id;
construct {
@@ -49,6 +65,10 @@ private class Games.DisplayBox : Gtk.Bin {
timeout_id = -1;
}
+ public DisplayBox (SavestatesListState savestates_list_state) {
+ Object (savestates_list_state: savestates_list_state);
+ }
+
public void display_running_game_failed (Game game, string error_message) {
stack.visible_child = error_display;
error_display.running_game_failed (game, error_message);
@@ -94,4 +114,8 @@ private class Games.DisplayBox : Gtk.Bin {
return runner.gamepad_button_press_event (button);
}
+
+ public void on_savestates_list_state_changed () {
+ fullscreen_box.autohide = !savestates_list.state.is_revealed;
+ }
}
diff --git a/src/ui/display-header-bar.vala b/src/ui/display-header-bar.vala
index 7e572fc4..9d1e9939 100644
--- a/src/ui/display-header-bar.vala
+++ b/src/ui/display-header-bar.vala
@@ -1,18 +1,32 @@
// This file is part of GNOME Games. License: GPL-3.0+.
[GtkTemplate (ui = "/org/gnome/Games/ui/display-header-bar.ui")]
-private class Games.DisplayHeaderBar : Gtk.Bin {
+private class Games.DisplayHeaderBar : Gtk.Stack {
public signal void back ();
[GtkChild]
private MediaMenuButton media_button;
+ private SavestatesListState _savestates_list_state;
+ public SavestatesListState savestates_list_state {
+ get { return _savestates_list_state; }
+ set {
+ _savestates_list_state = value;
+
+ if (value != null)
+ value.notify["is-revealed"].connect (on_savestates_list_state_changed);
+ }
+ }
+
public string game_title {
- set { header_bar.title = value; }
+ set {
+ ingame_header_bar.title = value;
+ savestates_header_bar.title = value;
+ }
}
public bool show_title_buttons {
- set { header_bar.show_close_button = value; }
+ set { ingame_header_bar.show_close_button = value; }
}
public bool can_fullscreen { get; set; }
@@ -31,8 +45,12 @@ private class Games.DisplayHeaderBar : Gtk.Bin {
_runner = value;
input_mode_switcher.runner = value;
- if (runner != null)
+ if (runner != null) {
extra_widget = runner.get_extra_widget ();
+
+ secondary_menu_button.sensitive = runner.supports_savestates;
+ secondary_menu_button.visible = runner.can_support_savestates;
+ }
else
extra_widget = null;
}
@@ -46,28 +64,40 @@ private class Games.DisplayHeaderBar : Gtk.Bin {
return;
if (extra_widget != null)
- header_bar.remove (extra_widget);
+ ingame_header_bar.remove (extra_widget);
_extra_widget = value;
if (extra_widget != null)
- header_bar.pack_end (extra_widget);
+ ingame_header_bar.pack_end (extra_widget);
}
}
[GtkChild]
- private Gtk.HeaderBar header_bar;
+ private Gtk.HeaderBar ingame_header_bar;
[GtkChild]
private Gtk.Button fullscreen;
[GtkChild]
private Gtk.Button restore;
+ [GtkChild]
+ private Gtk.MenuButton secondary_menu_button;
+ [GtkChild]
+ private Gtk.HeaderBar savestates_header_bar;
private Settings settings;
+ public DisplayHeaderBar (SavestatesListState savestates_list_state) {
+ Object (savestates_list_state: savestates_list_state);
+ }
+
construct {
settings = new Settings ("org.gnome.Games");
}
+ public void hide_secondary_menu_button () {
+ secondary_menu_button.visible = false;
+ }
+
[GtkCallback]
private void on_fullscreen_changed () {
fullscreen.visible = can_fullscreen && !is_fullscreen;
@@ -90,4 +120,31 @@ private class Games.DisplayHeaderBar : Gtk.Bin {
is_fullscreen = false;
settings.set_boolean ("fullscreen", false);
}
+
+ [GtkCallback]
+ private void on_secondary_menu_savestates_clicked () {
+ savestates_list_state.is_revealed = true;
+ }
+
+ [GtkCallback]
+ private void on_savestates_load_clicked () {
+ savestates_list_state.load_clicked ();
+ }
+
+ [GtkCallback]
+ private void on_savestates_delete_clicked () {
+ savestates_list_state.delete_clicked ();
+ }
+
+ [GtkCallback]
+ private void on_savestates_cancel_clicked () {
+ savestates_list_state.is_revealed = false;
+ }
+
+ private void on_savestates_list_state_changed () {
+ if (savestates_list_state.is_revealed)
+ set_visible_child (savestates_header_bar);
+ else
+ set_visible_child (ingame_header_bar);
+ }
}
diff --git a/src/ui/display-view.vala b/src/ui/display-view.vala
index d6102db3..8b542cfa 100644
--- a/src/ui/display-view.vala
+++ b/src/ui/display-view.vala
@@ -52,6 +52,8 @@ private class Games.DisplayView : Object, UiView {
private ResumeFailedDialog resume_failed_dialog;
private QuitDialog quit_dialog;
+ private SavestatesListState savestates_list_state;
+
private long focus_out_timeout_id;
public DisplayView (Gtk.Window window) {
@@ -59,14 +61,16 @@ private class Games.DisplayView : Object, UiView {
}
construct {
- box = new DisplayBox ();
- header_bar = new DisplayHeaderBar ();
+ savestates_list_state = new SavestatesListState ();
+ box = new DisplayBox (savestates_list_state);
+ header_bar = new DisplayHeaderBar (savestates_list_state);
box.back.connect (on_display_back);
header_bar.back.connect (on_display_back);
settings = new Settings ("org.gnome.Games");
+ // Bind the is_fullscreen property between the header_bar and the box
box_fullscreen_binding = bind_property ("is-fullscreen", box, "is-fullscreen",
BindingFlags.BIDIRECTIONAL);
header_bar_fullscreen_binding = bind_property ("is-fullscreen", header_bar,
@@ -94,14 +98,15 @@ private class Games.DisplayView : Object, UiView {
if ((event.keyval == Gdk.Key.f || event.keyval == Gdk.Key.F) &&
(event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK &&
- header_bar.can_fullscreen) {
+ header_bar.can_fullscreen && !savestates_list_state.is_revealed) {
is_fullscreen = !is_fullscreen;
settings.set_boolean ("fullscreen", is_fullscreen);
return true;
}
- if (event.keyval == Gdk.Key.F11 && header_bar.can_fullscreen) {
+ if (event.keyval == Gdk.Key.F11 && header_bar.can_fullscreen &&
+ !savestates_list_state.is_revealed) {
is_fullscreen = !is_fullscreen;
settings.set_boolean ("fullscreen", is_fullscreen);
@@ -148,7 +153,7 @@ private class Games.DisplayView : Object, UiView {
switch (button) {
case EventCode.BTN_MODE:
- back ();
+ on_display_back ();
return true;
default:
@@ -165,6 +170,9 @@ private class Games.DisplayView : Object, UiView {
}
private void on_display_back () {
+ if (savestates_list_state.is_revealed)
+ return;
+
back ();
}
@@ -349,6 +357,15 @@ private class Games.DisplayView : Object, UiView {
box.runner.pause ();
+ if (!box.runner.can_support_savestates) {
+ // Game does not and will not support savestates (e.g. Steam games)
+ // => Progress cannot be saved so game can be quit safely
+ box.runner.stop ();
+ return true;
+ }
+
+ box.runner.capture_current_state_pixbuf ();
+
if (box.runner.try_create_savestate (true) != null) {
// Progress saved => can quit game safely
box.runner.stop ();
@@ -394,6 +411,8 @@ private class Games.DisplayView : Object, UiView {
}
private void reset_display_page () {
+ header_bar.hide_secondary_menu_button ();
+
header_bar.can_fullscreen = false;
box.header_bar.can_fullscreen = false;
header_bar.runner = null;
@@ -411,8 +430,10 @@ private class Games.DisplayView : Object, UiView {
if (!can_update_pause ())
return;
- if (window.is_active)
- box.runner.resume ();
+ if (window.is_active) {
+ if (!savestates_list_state.is_revealed)
+ box.runner.resume ();
+ }
else if (with_delay)
focus_out_timeout_id = Timeout.add (FOCUS_OUT_DELAY_MILLISECONDS,
on_focus_out_delay_elapsed);
else
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]