[gnome-games/wip/exalm/views: 16/17] application-window: Extract CollectionView and DisplayView
- From: Alexander Mikhaylenko <alexm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games/wip/exalm/views: 16/17] application-window: Extract CollectionView and DisplayView
- Date: Mon, 25 Feb 2019 12:06:21 +0000 (UTC)
commit 48ebd0410cfb8c89b57c29afa48bc51628b88a41
Author: Alexander Mikhaylenko <exalm7659 gmail com>
Date: Mon Feb 25 17:01:13 2019 +0500
application-window: Extract CollectionView and DisplayView
Introduce CollectionView and DisplayView containing the relevant code from
ApplicationWindow. Make ApplicationWindow create them the two views.
data/ui/application-window.ui | 35 ---
src/meson.build | 2 +
src/ui/application-window.vala | 537 +++++------------------------------------
src/ui/collection-view.vala | 118 +++++++++
src/ui/display-view.vala | 443 +++++++++++++++++++++++++++++++++
5 files changed, 621 insertions(+), 514 deletions(-)
---
diff --git a/data/ui/application-window.ui b/data/ui/application-window.ui
index b134363a..98a05970 100644
--- a/data/ui/application-window.ui
+++ b/data/ui/application-window.ui
@@ -14,24 +14,6 @@
<child>
<object class="GtkStack" id="content_box">
<property name="visible">True</property>
- <child>
- <object class="GamesCollectionBox" id="collection_box">
- <property name="visible">True</property>
- <signal name="game-activated" handler="on_game_activated"/>
- </object>
- <packing>
- <property name="name">collection</property>
- </packing>
- </child>
- <child>
- <object class="GamesDisplayBox" id="display_box">
- <property name="visible">True</property>
- <signal name="back" handler="on_display_back"/>
- </object>
- <packing>
- <property name="name">display</property>
- </packing>
- </child>
</object>
</child>
<child type="titlebar">
@@ -40,23 +22,6 @@
<child>
<object class="GtkStack" id="header_bar">
<property name="visible">True</property>
- <child>
- <object class="GamesCollectionHeaderBar" id="collection_header_bar">
- <property name="visible">True</property>
- </object>
- <packing>
- <property name="name">collection</property>
- </packing>
- </child>
- <child>
- <object class="GamesDisplayHeaderBar" id="display_header_bar">
- <property name="visible">True</property>
- <signal name="back" handler="on_display_back"/>
- </object>
- <packing>
- <property name="name">display</property>
- </packing>
- </child>
</object>
</child>
</object>
diff --git a/src/meson.build b/src/meson.build
index eea378ec..87aae655 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -131,10 +131,12 @@ vala_sources = [
'ui/collection-box.vala',
'ui/collection-icon-view.vala',
'ui/collection-header-bar.vala',
+ 'ui/collection-view.vala',
'ui/developer-list-item.vala',
'ui/developers-view.vala',
'ui/display-box.vala',
'ui/display-header-bar.vala',
+ 'ui/display-view.vala',
'ui/dummy-display.vala',
'ui/empty-collection.vala',
'ui/error-display.vala',
diff --git a/src/ui/application-window.vala b/src/ui/application-window.vala
index c8e9b414..59736f48 100644
--- a/src/ui/application-window.vala
+++ b/src/ui/application-window.vala
@@ -3,43 +3,29 @@
[GtkTemplate (ui = "/org/gnome/Games/ui/application-window.ui")]
private class Games.ApplicationWindow : Gtk.ApplicationWindow {
private const uint WINDOW_SIZE_UPDATE_DELAY_MILLISECONDS = 500;
- private const uint FOCUS_OUT_DELAY_MILLISECONDS = 500;
private const string CONTRIBUTE_URI = "https://wiki.gnome.org/Apps/Games/Contribute";
- private UiState _ui_state;
- public UiState ui_state {
- get { return _ui_state; }
+ private UiView _current_view;
+ public UiView current_view {
+ get { return _current_view; }
set {
- if (value == ui_state)
+ if (value == current_view)
return;
- _ui_state = value;
+ if (current_view != null)
+ current_view.is_view_active = false;
- switch (ui_state) {
- case UiState.COLLECTION:
- content_box.visible_child = collection_box;
- header_bar.visible_child = collection_header_bar;
+ _current_view = value;
- is_fullscreen = false;
+ content_box.visible_child = current_view.content_box;
+ header_bar.visible_child = current_view.title_bar;
- if (display_box.runner != null) {
- display_box.runner.stop ();
- display_box.runner = null;
- }
-
- break;
- case UiState.DISPLAY:
- content_box.visible_child = display_box;
- header_bar.visible_child = display_header_bar;
-
- search_mode = false;
-
- break;
- }
+ if (current_view != null)
+ current_view.is_view_active = true;
assert (application is Application);
- (application as Application).set_pause_loading (ui_state != UiState.COLLECTION);
+ (application as Application).set_pause_loading (current_view != collection_view);
konami_code.reset ();
}
@@ -49,7 +35,7 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
public bool is_fullscreen {
get { return _is_fullscreen; }
set {
- _is_fullscreen = value && (ui_state == UiState.DISPLAY);
+ _is_fullscreen = value && (current_view == display_view);
if (_is_fullscreen)
fullscreen ();
@@ -58,67 +44,34 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
}
}
- private bool _search_mode;
- public bool search_mode {
- get { return _search_mode; }
- set { _search_mode = value && (ui_state == UiState.COLLECTION); }
- }
-
- public bool is_collection_empty { get; set; }
-
public bool loading_notification { get; set; }
[GtkChild]
private Gtk.Stack content_box;
- [GtkChild]
- private CollectionBox collection_box;
- [GtkChild]
- private DisplayBox display_box;
-
[GtkChild]
private Gtk.Stack header_bar;
- [GtkChild]
- private CollectionHeaderBar collection_header_bar;
- [GtkChild]
- private DisplayHeaderBar display_header_bar;
+
+ private CollectionView collection_view;
+ private DisplayView display_view;
private Settings settings;
- private Binding box_search_binding;
- private Binding box_fullscreen_binding;
- private Binding box_empty_collection_binding;
- private Binding header_bar_search_binding;
- private Binding header_bar_fullscreen_binding;
- private Binding header_bar_empty_collection_binding;
+ private Binding fullscreen_binding;
private Binding loading_notification_binding;
- private Cancellable run_game_cancellable;
- private Cancellable quit_game_cancellable;
-
- private ResumeDialog resume_dialog;
- private ResumeFailedDialog resume_failed_dialog;
- private QuitDialog quit_dialog;
-
private long window_size_update_timeout;
- private long focus_out_timeout_id;
private uint inhibit_cookie;
private Gtk.ApplicationInhibitFlags inhibit_flags;
private KonamiCode konami_code;
- public ListModel collection {
- construct {
- collection_box.collection = value;
- value.items_changed.connect (() => {
- is_collection_empty = value.get_n_items () == 0;
- });
- is_collection_empty = value.get_n_items () == 0;
- }
- }
+ public ListModel collection { get; construct; }
public ApplicationWindow (Application application, ListModel collection) {
Object (application: application, collection: collection);
+
+ current_view = collection_view;
}
construct {
@@ -136,30 +89,30 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
if (settings.get_boolean ("window-maximized"))
maximize ();
- box_search_binding = bind_property ("search-mode", collection_box, "search-mode",
- BindingFlags.BIDIRECTIONAL);
- loading_notification_binding = bind_property ("loading-notification", collection_box,
"loading-notification",
- BindingFlags.DEFAULT);
- header_bar_search_binding = bind_property ("search-mode", collection_header_bar,
"search-mode",
- BindingFlags.BIDIRECTIONAL);
+ collection_view = new CollectionView (this, collection);
+ display_view = new DisplayView (this);
- box_fullscreen_binding = bind_property ("is-fullscreen", display_box, "is-fullscreen",
- BindingFlags.BIDIRECTIONAL);
- header_bar_fullscreen_binding = bind_property ("is-fullscreen", display_header_bar,
"is-fullscreen",
- BindingFlags.BIDIRECTIONAL);
+ content_box.add (collection_view.content_box);
+ content_box.add (display_view.content_box);
+ header_bar.add (collection_view.title_bar);
+ header_bar.add (display_view.title_bar);
- box_empty_collection_binding = bind_property ("is-collection-empty", collection_box,
"is-collection-empty",
- BindingFlags.BIDIRECTIONAL);
- header_bar_empty_collection_binding = bind_property ("is-collection-empty",
collection_header_bar, "is-collection-empty",
- BindingFlags.BIDIRECTIONAL);
+ collection_view.game_activated.connect (on_game_activated);
+ display_view.back.connect (on_display_back);
+
+ loading_notification_binding = bind_property ("loading-notification",
+ collection_view,
+ "loading-notification",
+ BindingFlags.DEFAULT);
+
+ fullscreen_binding = bind_property ("is-fullscreen", display_view,
+ "is-fullscreen",
+ BindingFlags.BIDIRECTIONAL);
konami_code = new KonamiCode (this);
konami_code.code_performed.connect (on_konami_code_performed);
- collection_header_bar.viewstack = collection_box.viewstack;
-
window_size_update_timeout = -1;
- focus_out_timeout_id = -1;
inhibit_cookie = 0;
inhibit_flags = 0;
@@ -172,21 +125,8 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
}
public void run_game (Game game) {
- // If there is a game already running we have to quit it first
- if (display_box.runner != null && !quit_game())
- return;
-
- if (run_game_cancellable != null)
- run_game_cancellable.cancel ();
-
- var cancellable = new Cancellable ();
- run_game_cancellable = cancellable;
-
- run_game_with_cancellable (game, cancellable);
-
- // Only reset the cancellable if another one didn't replace it.
- if (run_game_cancellable == cancellable)
- run_game_cancellable = null;
+ current_view = display_view;
+ display_view.run_game (game);
inhibit (Gtk.ApplicationInhibitFlags.IDLE | Gtk.ApplicationInhibitFlags.LOGOUT);
}
@@ -197,22 +137,7 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
if (!visible)
return true;
- if (run_game_cancellable != null)
- run_game_cancellable.cancel ();
-
- if (quit_game_cancellable != null)
- quit_game_cancellable.cancel ();
-
- var cancellable = new Cancellable ();
- quit_game_cancellable = cancellable;
-
- var result = quit_game_with_cancellable (cancellable);
-
- // Only reset the cancellable if another one didn't replace it.
- if (quit_game_cancellable == cancellable)
- quit_game_cancellable = null;
-
- return result;
+ return display_view.quit_game ();
}
public override void size_allocate (Gtk.Allocation allocation) {
@@ -241,21 +166,12 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
return true;
}
- return handle_collection_key_event (event) || handle_display_key_event (event);
+ return current_view.on_key_pressed (event);
}
[GtkCallback]
public bool on_button_pressed (Gdk.EventButton event) {
- // Mouse button 8 is the navigation previous button
- if (event.button == 8) {
- if (ui_state != UiState.DISPLAY)
- return false;
-
- on_display_back ();
- return true;
- }
-
- return false;
+ return current_view.on_button_pressed (event);
}
[GtkCallback]
@@ -264,13 +180,14 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
settings.set_boolean ("window-maximized", is_maximized);
is_fullscreen = (bool) (event.new_window_state & Gdk.WindowState.FULLSCREEN);
- update_pause (false);
+ if (current_view == display_view)
+ display_view.update_pause (false);
if (!(bool) (event.changed_mask & Gdk.WindowState.FOCUSED))
return false;
var focused = (bool) (event.new_window_state & Gdk.WindowState.FOCUSED);
- var playing = (ui_state == UiState.DISPLAY);
+ var playing = (current_view == display_view);
if (focused && playing)
inhibit (Gtk.ApplicationInhibitFlags.IDLE);
@@ -282,260 +199,38 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
}
public bool gamepad_button_press_event (Manette.Event event) {
- switch (ui_state) {
- case UiState.COLLECTION:
- return is_active && collection_box.gamepad_button_press_event (event);
- case UiState.DISPLAY:
- if (resume_dialog != null)
- return resume_dialog.is_active && resume_dialog.gamepad_button_press_event
(event);
-
- if (resume_failed_dialog != null)
- return resume_failed_dialog.is_active &&
resume_failed_dialog.gamepad_button_press_event (event);
-
- if (quit_dialog != null)
- return quit_dialog.is_active && quit_dialog.gamepad_button_press_event
(event);
-
- if (!is_active || !get_mapped ())
- return false;
-
- uint16 button;
- if (!event.get_button (out button))
- return false;
-
- switch (button) {
- case EventCode.BTN_MODE:
- ui_state = UiState.COLLECTION;
-
- return true;
- default:
- return false;
- }
- default:
- return false;
- }
+ return current_view.gamepad_button_press_event (event);
}
public bool gamepad_button_release_event (Manette.Event event) {
- switch (ui_state) {
- case UiState.COLLECTION:
- return is_active && collection_box.gamepad_button_release_event (event);
- default:
- return false;
- }
+ if (current_view == collection_view)
+ return collection_view.gamepad_button_release_event (event);
+
+ return false;
}
public bool gamepad_absolute_axis_event (Manette.Event event) {
- switch (ui_state) {
- case UiState.COLLECTION:
- return is_active && collection_box.gamepad_absolute_axis_event (event);
- default:
- return false;
- }
+ if (current_view == collection_view)
+ return collection_view.gamepad_absolute_axis_event (event);
+
+ return false;
}
- [GtkCallback]
private void on_game_activated (Game game) {
run_game (game);
}
- [GtkCallback]
private void on_display_back () {
if (quit_game ())
- ui_state = UiState.COLLECTION;
+ current_view = collection_view;
uninhibit (Gtk.ApplicationInhibitFlags.IDLE | Gtk.ApplicationInhibitFlags.LOGOUT);
}
- private void run_game_with_cancellable (Game game, Cancellable cancellable) {
- display_header_bar.game_title = game.name;
- display_box.header_bar.game_title = game.name;
- ui_state = UiState.DISPLAY;
-
- // Reset the UI parts depending on the runner to avoid an
- // inconsistent state is case we couldn't retrieve it.
- reset_display_page ();
-
- var runner = try_get_runner (game);
- if (runner == null)
- return;
-
- display_header_bar.can_fullscreen = runner.can_fullscreen;
- display_box.header_bar.can_fullscreen = runner.can_fullscreen;
- display_header_bar.runner = runner;
- display_box.runner = runner;
- display_header_bar.media_set = runner.media_set;
- display_box.header_bar.media_set = runner.media_set;
-
- is_fullscreen = settings.get_boolean ("fullscreen") && runner.can_fullscreen;
-
- if (!runner.can_resume) {
- try_run_with_cancellable (runner, false, cancellable);
- return;
- }
-
- var response = Gtk.ResponseType.NONE;
- if (runner.can_resume)
- response = prompt_resume_with_cancellable (cancellable);
-
- if (response != Gtk.ResponseType.NONE) {
- var resume = (response == Gtk.ResponseType.ACCEPT);
-
- if (!try_run_with_cancellable (runner, resume, cancellable))
- prompt_resume_fail_with_cancellable (runner, cancellable);
- }
- }
-
- private Runner? try_get_runner (Game game) {
- try {
- var runner = game.get_runner ();
- string error_message;
- if (runner.check_is_valid (out error_message))
- return runner;
-
- reset_display_page ();
- display_box.display_running_game_failed (game, error_message);
-
- return null;
- }
- catch (Error e) {
- warning (e.message);
- reset_display_page ();
- display_box.display_running_game_failed (game, _("An unexpected error occurred."));
-
- return null;
- }
- }
-
- private Gtk.ResponseType prompt_resume_with_cancellable (Cancellable cancellable) {
- if (resume_dialog != null)
- return Gtk.ResponseType.NONE;
-
- resume_dialog = new ResumeDialog ();
- resume_dialog.transient_for = this;
-
- cancellable.cancelled.connect (() => {
- resume_dialog.destroy ();
- resume_dialog = null;
- });
-
- var response = resume_dialog.run ();
-
- // The null check is necessary because the dialog could already
- // be canceled by this point
- if (resume_dialog != null) {
- resume_dialog.destroy ();
- resume_dialog = null;
- }
-
- return (Gtk.ResponseType) response;
- }
-
- private bool try_run_with_cancellable (Runner runner, bool resume, Cancellable cancellable) {
- try {
- if (resume)
- display_box.runner.resume ();
- else
- runner.start ();
-
- return true;
- }
- catch (Error e) {
- warning (e.message);
-
- return false;
- }
- }
-
- private void prompt_resume_fail_with_cancellable (Runner runner, Cancellable cancellable) {
- if (resume_failed_dialog != null)
- return;
-
- resume_failed_dialog = new ResumeFailedDialog ();
- resume_failed_dialog.transient_for = this;
-
- cancellable.cancelled.connect (() => {
- resume_failed_dialog.destroy ();
- resume_failed_dialog = null;
- });
-
- var response = resume_failed_dialog.run ();
- resume_failed_dialog.destroy ();
- resume_failed_dialog = null;
-
- if (cancellable.is_cancelled ())
- response = Gtk.ResponseType.CANCEL;
-
- if (response == Gtk.ResponseType.CANCEL) {
- display_box.runner = null;
- ui_state = UiState.COLLECTION;
-
- return;
- }
-
- try {
- runner.start ();
- }
- catch (Error e) {
- warning (e.message);
- }
- }
-
- public bool quit_game_with_cancellable (Cancellable cancellable) {
- if (display_box.runner == null)
- return true;
-
- display_box.runner.pause ();
-
- if (display_box.runner.can_quit_safely) {
- display_box.runner.stop();
-
- return true;
- }
-
- if (quit_dialog != null)
- return false;
-
- quit_dialog = new QuitDialog ();
- quit_dialog.transient_for = this;
-
- cancellable.cancelled.connect (() => {
- quit_dialog.destroy ();
- quit_dialog = null;
- });
-
- var response = quit_dialog.run ();
-
- // The null check is necessary because the dialog could already
- // be canceled by this point
- if (quit_dialog != null) {
- quit_dialog.destroy ();
- quit_dialog = null;
- }
-
- if (cancellable.is_cancelled ())
- return cancel_quitting_game ();
-
- if (response == Gtk.ResponseType.ACCEPT)
- return true;
-
- return cancel_quitting_game ();
- }
-
- private bool cancel_quitting_game () {
- if (display_box.runner != null)
- try {
- display_box.runner.resume ();
- }
- catch (Error e) {
- warning (e.message);
- }
-
- return false;
- }
-
[GtkCallback]
private void on_active_changed () {
- update_pause (true);
+ if (current_view == display_view)
+ display_view.update_pause (true);
}
private Gdk.Rectangle? get_geometry () {
@@ -575,113 +270,6 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
return false;
}
- private void update_pause (bool with_delay) {
- if (focus_out_timeout_id != -1) {
- Source.remove ((uint) focus_out_timeout_id);
- focus_out_timeout_id = -1;
- }
-
- if (!can_update_pause ())
- return;
-
- if (is_active)
- try {
- display_box.runner.resume ();
- }
- catch (Error e) {
- warning (e.message);
- }
- else if (with_delay)
- focus_out_timeout_id = Timeout.add (FOCUS_OUT_DELAY_MILLISECONDS,
on_focus_out_delay_elapsed);
- else
- display_box.runner.pause ();
- }
-
- private bool on_focus_out_delay_elapsed () {
- focus_out_timeout_id = -1;
-
- if (!can_update_pause ())
- return false;
-
- if (!is_active)
- display_box.runner.pause ();
-
- return false;
- }
-
- private bool can_update_pause () {
- if (ui_state != UiState.DISPLAY)
- return false;
-
- if (display_box.runner == null)
- return false;
-
- if (run_game_cancellable != null)
- return false;
-
- if (quit_game_cancellable != null)
- return false;
-
- return true;
- }
-
- private bool handle_collection_key_event (Gdk.EventKey event) {
- if (ui_state != UiState.COLLECTION)
- return false;
-
- var default_modifiers = Gtk.accelerator_get_default_mod_mask ();
-
- if ((event.keyval == Gdk.Key.f || event.keyval == Gdk.Key.F) &&
- (event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK) {
- if (!search_mode)
- search_mode = true;
-
- return true;
- }
-
- return collection_box.search_bar_handle_event (event);
- }
-
- private bool handle_display_key_event (Gdk.EventKey event) {
- if (ui_state != UiState.DISPLAY)
- return false;
-
- var default_modifiers = Gtk.accelerator_get_default_mod_mask ();
-
- if ((event.keyval == Gdk.Key.f || event.keyval == Gdk.Key.F) &&
- (event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK &&
- display_header_bar.can_fullscreen) {
- is_fullscreen = !is_fullscreen;
- settings.set_boolean ("fullscreen", is_fullscreen);
-
- return true;
- }
-
- if (event.keyval == Gdk.Key.F11 && display_header_bar.can_fullscreen) {
- is_fullscreen = !is_fullscreen;
- settings.set_boolean ("fullscreen", is_fullscreen);
-
- return true;
- }
-
- if (event.keyval == Gdk.Key.Escape && display_header_bar.can_fullscreen) {
- is_fullscreen = false;
- settings.set_boolean ("fullscreen", false);
-
- return true;
- }
-
- if (((event.state & default_modifiers) == Gdk.ModifierType.MOD1_MASK) &&
- (((get_direction () == Gtk.TextDirection.LTR) && event.keyval == Gdk.Key.Left) ||
- ((get_direction () == Gtk.TextDirection.RTL) && event.keyval == Gdk.Key.Right))) {
- on_display_back ();
-
- return true;
- }
-
- return false;
- }
-
private void inhibit (Gtk.ApplicationInhibitFlags flags) {
if ((inhibit_flags & flags) == flags)
return;
@@ -713,17 +301,8 @@ private class Games.ApplicationWindow : Gtk.ApplicationWindow {
inhibit_flags = new_flags;
}
- private void reset_display_page () {
- display_header_bar.can_fullscreen = false;
- display_box.header_bar.can_fullscreen = false;
- display_header_bar.runner = null;
- display_box.runner = null;
- display_header_bar.media_set = null;
- display_box.header_bar.media_set = null;
- }
-
private void on_konami_code_performed () {
- if (ui_state != UiState.COLLECTION)
+ if (current_view != collection_view)
return;
try {
diff --git a/src/ui/collection-view.vala b/src/ui/collection-view.vala
new file mode 100644
index 00000000..1f6ea1ea
--- /dev/null
+++ b/src/ui/collection-view.vala
@@ -0,0 +1,118 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+private class Games.CollectionView : Object, UiView {
+ public signal void game_activated (Game game);
+
+ private CollectionBox box;
+ private CollectionHeaderBar header_bar;
+
+ public Gtk.Widget content_box {
+ get { return box; }
+ }
+
+ public Gtk.Widget title_bar {
+ get { return header_bar; }
+ }
+
+ private bool _is_view_active;
+ public bool is_view_active {
+ get { return _is_view_active; }
+ set {
+ if (is_view_active == value)
+ return;
+
+ _is_view_active = value;
+
+ if (!is_view_active)
+ search_mode = false;
+ }
+ }
+
+ public ApplicationWindow window { get; construct set; }
+
+ private ListModel _collection;
+ public ListModel collection {
+ get { return _collection; }
+ construct set {
+ _collection = value;
+
+ collection.items_changed.connect (() => {
+ is_collection_empty = collection.get_n_items () == 0;
+ });
+ is_collection_empty = collection.get_n_items () == 0;
+ }
+ }
+
+ public bool loading_notification { get; set; }
+ public bool search_mode { get; set; }
+ public bool is_collection_empty { get; set; }
+
+ private Binding loading_notification_binding;
+ private Binding box_search_binding;
+ private Binding box_empty_collection_binding;
+ private Binding header_bar_search_binding;
+ private Binding header_bar_empty_collection_binding;
+
+ construct {
+ box = new CollectionBox (collection);
+ header_bar = new CollectionHeaderBar ();
+ box.game_activated.connect (game => {
+ game_activated (game);
+ });
+
+ header_bar.viewstack = box.viewstack;
+ is_collection_empty = true;
+
+ loading_notification_binding = bind_property ("loading-notification", box,
+ "loading-notification",
+ BindingFlags.DEFAULT);
+
+ box_search_binding = bind_property ("search-mode", box, "search-mode",
+ BindingFlags.BIDIRECTIONAL);
+ header_bar_search_binding = bind_property ("search-mode", header_bar,
+ "search-mode",
+ BindingFlags.BIDIRECTIONAL);
+
+ box_empty_collection_binding = bind_property ("is-collection-empty", box,
+ "is-collection-empty",
+ BindingFlags.BIDIRECTIONAL);
+ header_bar_empty_collection_binding = bind_property ("is-collection-empty",
+ header_bar,
+ "is-collection-empty",
+ BindingFlags.BIDIRECTIONAL);
+ }
+
+ public CollectionView (ApplicationWindow window, ListModel collection) {
+ Object (window: window, collection: collection);
+ }
+
+ public bool on_button_pressed (Gdk.EventButton event) {
+ return false;
+ }
+
+ public bool on_key_pressed (Gdk.EventKey event) {
+ var default_modifiers = Gtk.accelerator_get_default_mod_mask ();
+
+ if ((event.keyval == Gdk.Key.f || event.keyval == Gdk.Key.F) &&
+ (event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK) {
+ if (!search_mode)
+ search_mode = true;
+
+ return true;
+ }
+
+ return box.search_bar_handle_event (event);
+ }
+
+ public bool gamepad_button_press_event (Manette.Event event) {
+ return window.is_active && box.gamepad_button_press_event (event);
+ }
+
+ public bool gamepad_button_release_event (Manette.Event event) {
+ return window.is_active && box.gamepad_button_release_event (event);
+ }
+
+ public bool gamepad_absolute_axis_event (Manette.Event event) {
+ return window.is_active && box.gamepad_absolute_axis_event (event);
+ }
+}
diff --git a/src/ui/display-view.vala b/src/ui/display-view.vala
new file mode 100644
index 00000000..ff8560d6
--- /dev/null
+++ b/src/ui/display-view.vala
@@ -0,0 +1,443 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+private class Games.DisplayView : Object, UiView {
+ private const uint FOCUS_OUT_DELAY_MILLISECONDS = 500;
+
+ public signal void back ();
+
+ private DisplayBox box;
+ private DisplayHeaderBar header_bar;
+
+ public Gtk.Widget content_box {
+ get { return box; }
+ }
+
+ public Gtk.Widget title_bar {
+ get { return header_bar; }
+ }
+
+ private bool _is_view_active;
+ public bool is_view_active {
+ get { return _is_view_active; }
+ set {
+ if (is_view_active == value)
+ return;
+
+ _is_view_active = value;
+
+ if (!is_view_active) {
+ is_fullscreen = false;
+
+ if (box.runner != null) {
+ box.runner.stop ();
+ box.runner = null;
+ }
+ }
+ }
+ }
+
+ public ApplicationWindow window { get; construct set; }
+
+ public bool is_fullscreen { get; set; }
+
+ private Settings settings;
+
+ private Binding box_fullscreen_binding;
+ private Binding header_bar_fullscreen_binding;
+
+ private Cancellable run_game_cancellable;
+ private Cancellable quit_game_cancellable;
+
+ private ResumeDialog resume_dialog;
+ private ResumeFailedDialog resume_failed_dialog;
+ private QuitDialog quit_dialog;
+
+ private long focus_out_timeout_id;
+
+ public DisplayView (ApplicationWindow window) {
+ Object (window: window);
+ }
+
+ construct {
+ box = new DisplayBox ();
+ header_bar = new DisplayHeaderBar ();
+
+ box.back.connect (on_display_back);
+ header_bar.back.connect (on_display_back);
+
+ settings = new Settings ("org.gnome.Games");
+
+ box_fullscreen_binding = bind_property ("is-fullscreen", box, "is-fullscreen",
+ BindingFlags.BIDIRECTIONAL);
+ header_bar_fullscreen_binding = bind_property ("is-fullscreen", header_bar,
+ "is-fullscreen",
+ BindingFlags.BIDIRECTIONAL);
+
+ focus_out_timeout_id = -1;
+ }
+
+ public bool on_button_pressed (Gdk.EventButton event) {
+ // Mouse button 8 is the navigation previous button
+ if (event.button == 8) {
+ back ();
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool on_key_pressed (Gdk.EventKey event) {
+ var default_modifiers = Gtk.accelerator_get_default_mod_mask ();
+
+ if ((event.keyval == Gdk.Key.f || event.keyval == Gdk.Key.F) &&
+ (event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK &&
+ header_bar.can_fullscreen) {
+ is_fullscreen = !is_fullscreen;
+ settings.set_boolean ("fullscreen", is_fullscreen);
+
+ return true;
+ }
+
+ if (event.keyval == Gdk.Key.F11 && header_bar.can_fullscreen) {
+ is_fullscreen = !is_fullscreen;
+ settings.set_boolean ("fullscreen", is_fullscreen);
+
+ return true;
+ }
+
+ if (event.keyval == Gdk.Key.Escape && header_bar.can_fullscreen) {
+ is_fullscreen = false;
+ settings.set_boolean ("fullscreen", false);
+
+ return true;
+ }
+
+ if (((event.state & default_modifiers) == Gdk.ModifierType.MOD1_MASK) &&
+ (((window.get_direction () == Gtk.TextDirection.LTR) && event.keyval == Gdk.Key.Left) ||
+ ((window.get_direction () == Gtk.TextDirection.RTL) && event.keyval == Gdk.Key.Right))) {
+ on_display_back ();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool gamepad_button_press_event (Manette.Event event) {
+ if (resume_dialog != null)
+ return resume_dialog.is_active && resume_dialog.gamepad_button_press_event (event);
+
+ if (resume_failed_dialog != null)
+ return resume_failed_dialog.is_active &&
resume_failed_dialog.gamepad_button_press_event (event);
+
+ if (quit_dialog != null)
+ return quit_dialog.is_active && quit_dialog.gamepad_button_press_event (event);
+
+ if (!window.is_active || !window.get_mapped ())
+ return false;
+
+ uint16 button;
+ if (!event.get_button (out button))
+ return false;
+
+ switch (button) {
+ case EventCode.BTN_MODE:
+ back ();
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public bool gamepad_button_release_event (Manette.Event event) {
+ return false;
+ }
+
+ public bool gamepad_absolute_axis_event (Manette.Event event) {
+ return false;
+ }
+
+ private void on_display_back () {
+ back ();
+ }
+
+ public void run_game (Game game) {
+ // If there is a game already running we have to quit it first
+ if (box.runner != null && !quit_game ())
+ return;
+
+ if (run_game_cancellable != null)
+ run_game_cancellable.cancel ();
+
+ var cancellable = new Cancellable ();
+ run_game_cancellable = cancellable;
+
+ run_game_with_cancellable (game, cancellable);
+
+ // Only reset the cancellable if another one didn't replace it.
+ if (run_game_cancellable == cancellable)
+ run_game_cancellable = null;
+ }
+
+ private void run_game_with_cancellable (Game game, Cancellable cancellable) {
+ header_bar.game_title = game.name;
+ box.header_bar.game_title = game.name;
+
+ // Reset the UI parts depending on the runner to avoid an
+ // inconsistent state is case we couldn't retrieve it.
+ reset_display_page ();
+
+ var runner = try_get_runner (game);
+ if (runner == null)
+ return;
+
+ header_bar.can_fullscreen = runner.can_fullscreen;
+ box.header_bar.can_fullscreen = runner.can_fullscreen;
+ header_bar.runner = runner;
+ box.runner = runner;
+ header_bar.media_set = runner.media_set;
+ box.header_bar.media_set = runner.media_set;
+
+ is_fullscreen = settings.get_boolean ("fullscreen") && runner.can_fullscreen;
+
+ if (!runner.can_resume) {
+ try_run_with_cancellable (runner, false, cancellable);
+ return;
+ }
+
+ var response = Gtk.ResponseType.NONE;
+ if (runner.can_resume)
+ response = prompt_resume_with_cancellable (cancellable);
+
+ if (response != Gtk.ResponseType.NONE) {
+ var resume = (response == Gtk.ResponseType.ACCEPT);
+
+ if (!try_run_with_cancellable (runner, resume, cancellable))
+ prompt_resume_fail_with_cancellable (runner, cancellable);
+ }
+ }
+
+ private Runner? try_get_runner (Game game) {
+ try {
+ var runner = game.get_runner ();
+ string error_message;
+ if (runner.check_is_valid (out error_message))
+ return runner;
+
+ reset_display_page ();
+ box.display_running_game_failed (game, error_message);
+
+ return null;
+ }
+ catch (Error e) {
+ warning (e.message);
+ reset_display_page ();
+ box.display_running_game_failed (game, _("An unexpected error occurred."));
+
+ return null;
+ }
+ }
+
+ private Gtk.ResponseType prompt_resume_with_cancellable (Cancellable cancellable) {
+ if (resume_dialog != null)
+ return Gtk.ResponseType.NONE;
+
+ resume_dialog = new ResumeDialog ();
+ resume_dialog.transient_for = window;
+
+ cancellable.cancelled.connect (() => {
+ resume_dialog.destroy ();
+ resume_dialog = null;
+ });
+
+ var response = resume_dialog.run ();
+
+ // The null check is necessary because the dialog could already
+ // be canceled by this point
+ if (resume_dialog != null) {
+ resume_dialog.destroy ();
+ resume_dialog = null;
+ }
+
+ return (Gtk.ResponseType) response;
+ }
+
+ private bool try_run_with_cancellable (Runner runner, bool resume, Cancellable cancellable) {
+ try {
+ if (resume)
+ box.runner.resume ();
+ else
+ runner.start ();
+
+ return true;
+ }
+ catch (Error e) {
+ warning (e.message);
+
+ return false;
+ }
+ }
+
+ private void prompt_resume_fail_with_cancellable (Runner runner, Cancellable cancellable) {
+ if (resume_failed_dialog != null)
+ return;
+
+ resume_failed_dialog = new ResumeFailedDialog ();
+ resume_failed_dialog.transient_for = window;
+
+ cancellable.cancelled.connect (() => {
+ resume_failed_dialog.destroy ();
+ resume_failed_dialog = null;
+ });
+
+ var response = resume_failed_dialog.run ();
+ resume_failed_dialog.destroy ();
+ resume_failed_dialog = null;
+
+ if (cancellable.is_cancelled ())
+ response = Gtk.ResponseType.CANCEL;
+
+ if (response == Gtk.ResponseType.CANCEL) {
+ box.runner = null;
+ back ();
+
+ return;
+ }
+
+ try {
+ runner.start ();
+ }
+ catch (Error e) {
+ warning (e.message);
+ }
+ }
+
+ public bool quit_game () {
+ if (run_game_cancellable != null)
+ run_game_cancellable.cancel ();
+
+ if (quit_game_cancellable != null)
+ quit_game_cancellable.cancel ();
+
+ var cancellable = new Cancellable ();
+ quit_game_cancellable = cancellable;
+
+ var result = quit_game_with_cancellable (cancellable);
+
+ // Only reset the cancellable if another one didn't replace it.
+ if (quit_game_cancellable == cancellable)
+ quit_game_cancellable = null;
+
+ return result;
+ }
+
+ public bool quit_game_with_cancellable (Cancellable cancellable) {
+ if (box.runner == null)
+ return true;
+
+ box.runner.pause ();
+
+ if (box.runner.can_quit_safely) {
+ box.runner.stop ();
+
+ return true;
+ }
+
+ if (quit_dialog != null)
+ return false;
+
+ quit_dialog = new QuitDialog ();
+ quit_dialog.transient_for = window;
+
+ cancellable.cancelled.connect (() => {
+ quit_dialog.destroy ();
+ quit_dialog = null;
+ });
+
+ var response = quit_dialog.run ();
+
+ // The null check is necessary because the dialog could already
+ // be canceled by this point
+ if (quit_dialog != null) {
+ quit_dialog.destroy ();
+ quit_dialog = null;
+ }
+
+ if (cancellable.is_cancelled ())
+ return cancel_quitting_game ();
+
+ if (response == Gtk.ResponseType.ACCEPT)
+ return true;
+
+ return cancel_quitting_game ();
+ }
+
+ private bool cancel_quitting_game () {
+ if (box.runner != null)
+ try {
+ box.runner.resume ();
+ }
+ catch (Error e) {
+ warning (e.message);
+ }
+
+ return false;
+ }
+
+ private void reset_display_page () {
+ header_bar.can_fullscreen = false;
+ box.header_bar.can_fullscreen = false;
+ header_bar.runner = null;
+ box.runner = null;
+ header_bar.media_set = null;
+ box.header_bar.media_set = null;
+ }
+
+ public void update_pause (bool with_delay) {
+ if (focus_out_timeout_id != -1) {
+ Source.remove ((uint) focus_out_timeout_id);
+ focus_out_timeout_id = -1;
+ }
+
+ if (!can_update_pause ())
+ return;
+
+ if (window.is_active)
+ try {
+ box.runner.resume ();
+ }
+ catch (Error e) {
+ warning (e.message);
+ }
+ else if (with_delay)
+ focus_out_timeout_id = Timeout.add (FOCUS_OUT_DELAY_MILLISECONDS,
on_focus_out_delay_elapsed);
+ else
+ box.runner.pause ();
+ }
+
+ private bool on_focus_out_delay_elapsed () {
+ focus_out_timeout_id = -1;
+
+ if (!can_update_pause ())
+ return false;
+
+ if (!window.is_active)
+ box.runner.pause ();
+
+ return false;
+ }
+
+ private bool can_update_pause () {
+ if (box.runner == null)
+ return false;
+
+ if (run_game_cancellable != null)
+ return false;
+
+ if (quit_game_cancellable != null)
+ return false;
+
+ return true;
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]