[gnome-games/wip/exalm/tnum: 7/24] ui: Introduce CollectionActionWindow
- From: Alexander Mikhaylenko <alexm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games/wip/exalm/tnum: 7/24] ui: Introduce CollectionActionWindow
- Date: Wed, 19 Aug 2020 18:22:23 +0000 (UTC)
commit 72849f9161fce357fb1e72d332d21d4c5ba65335
Author: Neville <nevilleantony98 gmail com>
Date: Thu Aug 6 23:52:46 2020 +0530
ui: Introduce CollectionActionWindow
This will be used to present actions on collections such as:
- Add games to collections
- Create a collection
a single filter. This makes hiding/showing the "+" row easier.
Also add pill-button style class
data/gtk-style.css | 6 +
data/org.gnome.Games.gresource.xml | 1 +
data/ui/collection-action-window.ui | 316 +++++++++++++++++++++++++++++++++++
src/meson.build | 1 +
src/ui/collection-action-window.vala | 259 ++++++++++++++++++++++++++++
5 files changed, 583 insertions(+)
---
diff --git a/data/gtk-style.css b/data/gtk-style.css
index 7d6bc0fe8..fb6f932f5 100644
--- a/data/gtk-style.css
+++ b/data/gtk-style.css
@@ -35,6 +35,12 @@
border-right: none;
}
+.pill-button {
+ border-radius: 9999px;
+ -gtk-outline-radius: 9999px;
+ padding: 6px 32px;
+}
+
gamescollectionsmainpage grid {
background: mix(@theme_base_color, @theme_bg_color, 0.5);
min-width: 116px;
diff --git a/data/org.gnome.Games.gresource.xml b/data/org.gnome.Games.gresource.xml
index 6e5cd1e57..0f6a64217 100644
--- a/data/org.gnome.Games.gresource.xml
+++ b/data/org.gnome.Games.gresource.xml
@@ -12,6 +12,7 @@
<file preprocess="xml-stripblanks">gesture/stick-symbolic.svg</file>
<file preprocess="xml-stripblanks">ui/application-window.ui</file>
<file preprocess="xml-stripblanks">ui/checkmark-item.ui</file>
+ <file preprocess="xml-stripblanks">ui/collection-action-window.ui</file>
<file preprocess="xml-stripblanks">ui/collection-empty.ui</file>
<file preprocess="xml-stripblanks">ui/collection-icon-view.ui</file>
<file preprocess="xml-stripblanks">ui/collection-list-item.ui</file>
diff --git a/data/ui/collection-action-window.ui b/data/ui/collection-action-window.ui
new file mode 100644
index 000000000..36d07f1b9
--- /dev/null
+++ b/data/ui/collection-action-window.ui
@@ -0,0 +1,316 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.24"/>
+ <template class="GamesCollectionActionWindow" parent="HdyWindow">
+ <property name="title" translatable="yes">Add to Collection</property>
+ <property name="default-height">550</property>
+ <property name="default-width">400</property>
+ <property name="window-position">center-on-parent</property>
+ <signal name="key-press-event" after="yes" handler="on_key_pressed"/>
+ <child>
+ <object class="HdyDeck" id="deck">
+ <property name="visible">True</property>
+ <property name="can-swipe-back" bind-source="GamesCollectionActionWindow"
bind-property="create-collection-page-only" bind-flags="invert-boolean|sync-create"/>
+ <signal name="notify::visible-child" handler="on_visible_child_changed"/>
+ <child>
+ <object class="GtkBox" id="add_to_collection_page">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="HdyHeaderBar">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Add to Collection</property>
+ <property name="show-close-button">False</property>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use_underline">True</property>
+ <property name="action-name">collection-action.go-back</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="add_button">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Add</property>
+ <property name="use_underline">True</property>
+ <property name="sensitive" bind-source="GamesCollectionActionWindow"
bind-property="is-user-collections-empty" bind-flags="invert-boolean|sync-create"/>
+ <property name="action-name">collection-action.add-to-collection</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="active" bind-source="GamesCollectionActionWindow"
bind-property="is-search-mode" bind-flags="bidirectional"/>
+ <property name="sensitive" bind-source="GamesCollectionActionWindow"
bind-property="is-user-collections-empty" bind-flags="invert-boolean|sync-create"/>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">edit-find-symbolic</property>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject">
+ <property name="accessible-name" translatable="yes">Search</property>
+ </object>
+ </child>
+ <style>
+ <class name="image-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdySearchBar" id="search_bar">
+ <property name="visible">True</property>
+ <property name="search-mode-enabled" bind-source="GamesCollectionActionWindow"
bind-property="is-search-mode" bind-flags="sync-create|bidirectional"/>
+ <child>
+ <object class="HdyClamp">
+ <property name="visible">True</property>
+ <property name="maximum-size">400</property>
+ <child>
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="visible">True</property>
+ <signal name="search-changed" handler="on_search_text_notify"/>
+ <signal name="activate" handler="create_collection"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="user_collections_page_stack">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="list_page">
+ <property name="visible">True</property>
+ <property name="hscrollbar-policy">never</property>
+ <child>
+ <object class="HdyClamp">
+ <property name="visible">True</property>
+ <property name="maximum-size">400</property>
+ <property name="margin">18</property>
+ <child>
+ <object class="GtkListBox" id="list_box">
+ <property name="visible">True</property>
+ <property name="activate-on-single-click">True</property>
+ <property name="selection-mode">none</property>
+ <signal name="row-activated" handler="on_listbox_row_activated"/>
+ <style>
+ <class name="content"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="empty_page">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="vexpand">True</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="pixel-size">96</property>
+ <property name="icon-name">folder-symbolic</property>
+ <property name="margin">18</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">No collections found</property>
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ <property name="margin">6</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="2"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Add a new collection to add games to
it.</property>
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Create Collection</property>
+ <property name="halign">center</property>
+ <property name="margin">24</property>
+ <property name="action-name">collection-action.new-collection</property>
+ <child internal-child="accessible">
+ <object class="AtkObject">
+ <property name="accessible-name" translatable="yes">Create a
collection</property>
+ </object>
+ </child>
+ <style>
+ <class name="suggested-action"/>
+ <class name="pill-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="create_collection_page">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="HdyHeaderBar">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Add a Collection</property>
+ <property name="show-close-button" bind-source="GamesCollectionActionWindow"
bind-property="create-collection-page-only"/>
+ <child>
+ <object class="GtkButton">
+ <property name="visible" bind-source="GamesCollectionActionWindow"
bind-property="create-collection-page-only" bind-flags="invert-boolean|sync-create"/>
+ <property name="action-name">collection-action.go-back</property>
+ <child internal-child="accessible">
+ <object class="AtkObject">
+ <property name="accessible-name" translatable="yes">Back</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">go-previous-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="image-button"/>
+ </style>
+ </object>
+ </child>
+ <style>
+ <class name="titlebar"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="pixel-size">96</property>
+ <property name="icon-name">folder-new-symbolic</property>
+ <property name="vexpand">True</property>
+ <property name="margin-top">18</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Enter a collection name</property>
+ <property name="valign">end</property>
+ <property name="margin-top">18</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name_entry">
+ <property name="visible">True</property>
+ <property name="width-request">300</property>
+ <property name="height-request">10</property>
+ <property name="halign">center</property>
+ <property name="margin">12</property>
+ <signal name="notify::text" handler="on_collection_name_entry_changed"/>
+ <signal name="activate" handler="create_collection"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="error_label">
+ <property name="visible">True</property>
+ <property name="valign">end</property>
+ <property name="margin-bottom">12</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">C_reate</property>
+ <property name="use-underline">True</property>
+ <property name="sensitive" bind-source="GamesCollectionActionWindow"
bind-property="is-collection-name-valid" bind-flags="sync-create"/>
+ <property name="margin">24</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="action-name">collection-action.create-collection</property>
+ <child internal-child="accessible">
+ <object class="AtkObject">
+ <property name="accessible-name" translatable="yes">Create collection</property>
+ </object>
+ </child>
+ <style>
+ <class name="suggested-action"/>
+ <class name="pill-button"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkListBoxRow" id="add_row">
+ <property name="visible">True</property>
+ <property name="activatable">True</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">list-add-symbolic</property>
+ <property name="margin">12</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/src/meson.build b/src/meson.build
index 67b9c9098..0d44a3f2c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -113,6 +113,7 @@ vala_sources = [
'ui/application.vala',
'ui/application-window.vala',
'ui/checkmark-item.vala',
+ 'ui/collection-action-window.vala',
'ui/collection-empty.vala',
'ui/collection-icon-view.vala',
'ui/collection-list-item.vala',
diff --git a/src/ui/collection-action-window.vala b/src/ui/collection-action-window.vala
new file mode 100644
index 000000000..75d371121
--- /dev/null
+++ b/src/ui/collection-action-window.vala
@@ -0,0 +1,259 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[GtkTemplate (ui = "/org/gnome/Games/ui/collection-action-window.ui")]
+private class Games.CollectionActionWindow : Hdy.Window {
+ public signal void confirmed (Collection[] collections);
+
+ [GtkChild]
+ private Hdy.Deck deck;
+ [GtkChild]
+ private Gtk.Box add_to_collection_page;
+ [GtkChild]
+ private Gtk.Box create_collection_page;
+ [GtkChild]
+ private Gtk.Stack user_collections_page_stack;
+ [GtkChild]
+ private Gtk.ScrolledWindow list_page;
+ [GtkChild]
+ private Gtk.Box empty_page;
+ [GtkChild]
+ private Gtk.Entry name_entry;
+ [GtkChild]
+ private Gtk.Label error_label;
+ [GtkChild]
+ private Gtk.ListBox list_box;
+ [GtkChild]
+ private Hdy.SearchBar search_bar;
+ [GtkChild]
+ private Gtk.SearchEntry search_entry;
+ [GtkChild]
+ private Gtk.ListBoxRow add_row;
+
+ private CollectionManager collection_manager;
+ private SimpleActionGroup action_group;
+ private const ActionEntry[] action_entries = {
+ { "go-back", go_back },
+ { "new-collection", new_collection },
+ { "create-collection", create_collection },
+ { "add-to-collection", add_to_collection }
+ };
+
+ public string[] filtering_terms;
+ private string filtering_text {
+ set {
+ if (value == null)
+ filtering_terms = null;
+ else
+ filtering_terms = value.split (" ");
+
+ list_box.invalidate_filter ();
+ }
+ }
+
+ private CollectionModel _collection_model;
+ public CollectionModel collection_model {
+ get { return _collection_model; }
+ set {
+ _collection_model = value;
+
+ list_box.bind_model (collection_model, add_collection_row);
+ list_box.set_filter_func (list_box_filter);
+ list_box.invalidate_filter ();
+ }
+ }
+
+ private bool _is_user_collections_empty;
+ public bool is_user_collections_empty {
+ get { return _is_user_collections_empty; }
+ set {
+ _is_user_collections_empty = value;
+
+ if (is_user_collections_empty)
+ user_collections_page_stack.visible_child = empty_page;
+ else
+ user_collections_page_stack.visible_child = list_page;
+ }
+ }
+
+ public bool is_search_mode { get; set; }
+ public bool is_collection_name_valid { get; set; }
+ public bool create_collection_page_only { get; construct; }
+ public Collection insensitive_collection { get; construct; }
+
+ construct {
+ if (create_collection_page_only)
+ deck.visible_child = create_collection_page;
+ else
+ deck.visible_child = add_to_collection_page;
+
+ collection_manager = Application.get_default ().get_collection_manager ();
+
+ is_user_collections_empty = collection_manager.n_user_collections == 0;
+ collection_manager.collection_added.connect ((collection) => {
+ is_user_collections_empty = collection_manager.n_user_collections == 0;
+ });
+
+ action_group = new SimpleActionGroup ();
+ action_group.add_action_entries (action_entries, this);
+ insert_action_group ("collection-action", action_group);
+
+ search_bar.connect_entry (search_entry);
+ }
+
+ public CollectionActionWindow (bool create_collection_page_only = true, Collection? collection =
null) {
+ Object (create_collection_page_only : create_collection_page_only, insensitive_collection :
collection);
+ }
+
+ private Gtk.Widget add_collection_row (Object object) {
+ var collection = object as Collection;
+ if (collection.get_collection_type () == Collection.CollectionType.PLACEHOLDER)
+ return add_row;
+
+ var row = new CollectionListItem (collection);
+ row.sensitive = collection != insensitive_collection;
+ row.show ();
+
+ return row;
+ }
+
+ private bool list_box_filter (Gtk.ListBoxRow row) {
+ bool show_row;
+
+ if (row is CollectionListItem) {
+ var list_item = row as CollectionListItem;
+ var collection = list_item.collection;
+ var type = collection.get_collection_type ();
+
+ show_row = (type == Collection.CollectionType.USER) &&
+ ((is_search_mode && collection.matches_search_terms (filtering_terms))
||
+ (!is_search_mode));
+ }
+ else
+ show_row = !is_search_mode || filtering_terms.length == 0;
+
+ row.visible = show_row;
+ return show_row;
+ }
+
+ private void add_to_collection () {
+ Collection[] collections = {};
+
+ foreach (var child in list_box.get_children ()) {
+ var row = child as CollectionListItem;
+ if (row == null || row.collection.get_collection_type () !=
Collection.CollectionType.USER)
+ continue;
+
+ var check_button = row.activatable_widget as Gtk.CheckButton;
+ if (check_button.active)
+ collections += row.collection;
+ }
+
+ confirmed (collections);
+ close ();
+ }
+
+ private void new_collection () {
+ deck.navigate (Hdy.NavigationDirection.FORWARD);
+ }
+
+ private void go_back () {
+ if (create_collection_page_only || !deck.navigate (Hdy.NavigationDirection.BACK))
+ close ();
+ }
+
+ [GtkCallback]
+ private void create_collection () {
+ if (!is_collection_name_valid)
+ return;
+
+ collection_manager.create_user_collection (name_entry.text.strip ());
+ go_back ();
+ }
+
+ [GtkCallback]
+ private void on_listbox_row_activated (Gtk.ListBoxRow row) {
+ if (row == add_row)
+ new_collection ();
+ }
+
+ [GtkCallback]
+ public bool on_key_pressed (Gdk.EventKey event) {
+ var default_modifiers = Gtk.accelerator_get_default_mod_mask ();
+
+ uint keyval;
+ var keymap = Gdk.Keymap.get_for_display (get_display ());
+ keymap.translate_keyboard_state (event.hardware_keycode, event.state,
+ event.group, out keyval, null, null, null);
+
+ if (keyval == Gdk.Key.Escape) {
+ if (deck.visible_child == create_collection_page) {
+ go_back ();
+ return Gdk.EVENT_STOP;
+ }
+
+ if (is_search_mode) {
+ is_search_mode = false;
+ return Gdk.EVENT_STOP;
+ }
+
+ go_back ();
+ return Gdk.EVENT_STOP;
+ }
+
+ if (((event.state & default_modifiers) == Gdk.ModifierType.MOD1_MASK) &&
+ (((get_direction () == Gtk.TextDirection.LTR) && keyval == Gdk.Key.Left) ||
+ ((get_direction () == Gtk.TextDirection.RTL) && keyval == Gdk.Key.Right)) &&
+ !create_collection_page_only && deck.navigate (Hdy.NavigationDirection.BACK))
+ return Gdk.EVENT_STOP;
+
+ if (is_user_collections_empty)
+ return Gdk.EVENT_PROPAGATE;
+
+ if (deck.visible_child == add_to_collection_page &&
+ (keyval == Gdk.Key.f || keyval == Gdk.Key.F) &&
+ (event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK) {
+ is_search_mode = !is_search_mode;
+ return Gdk.EVENT_STOP;
+ }
+
+ return search_bar.handle_event (event);
+ }
+
+ [GtkCallback]
+ private void on_collection_name_entry_changed () {
+ var name = name_entry.text.strip ();
+ if (name == "") {
+ error_label.label = _("Collection name cannot be empty");
+ name_entry.get_style_context ().add_class ("error");
+ is_collection_name_valid = false;
+ return;
+ }
+
+ if (collection_manager.does_collection_title_exist (name)) {
+ error_label.label = _("A collection with this name already exists");
+ name_entry.get_style_context ().add_class ("error");
+ is_collection_name_valid = false;
+ return;
+ }
+
+ name_entry.get_style_context ().remove_class ("error");
+ error_label.label = null;
+ is_collection_name_valid = true;
+ }
+
+ [GtkCallback]
+ private void on_visible_child_changed () {
+ if (deck.visible_child == create_collection_page) {
+ name_entry.text = "";
+ error_label.label = "";
+ name_entry.get_style_context ().remove_class ("error");
+ name_entry.grab_focus_without_selecting ();
+ }
+ }
+
+ [GtkCallback]
+ private void on_search_text_notify () {
+ filtering_text = search_entry.text;
+ search_entry.grab_focus_without_selecting ();
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]