Commit 2816850f authored by AndreiLisita's avatar AndreiLisita 🎮

ui: Add the SavestatesList widget

parent 3e1ec750
......@@ -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;
......
......@@ -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>
......
<?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>
......@@ -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',
......
private class Games.SavestatesListState : Object {
public signal void load_clicked ();
public signal void delete_clicked ();
public bool is_revealed { get; set; }
}
// 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);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment