[gnome-games] collection-view: Add selection button to headerbar



commit 23e06600d7c1f9a26177f8ff693516b16605793c
Author: Neville <nevilleantony98 gmail com>
Date:   Fri Jun 5 19:58:02 2020 +0530

    collection-view: Add selection button to headerbar
    
    On enabling selection-mode the main headerbar will be inaccessible until
    the selection is completed/cancelled.
    
    When in PlatformsPage, if view is transitioned from desktop to mobile
    view, the view will be locked to the currently selected platform. Swipe
    back will be disabled until selection is completed/cancelled.
    
    Select all in PlatformsView will only select games belonging to the
    currently selected platform.
    
    This will be used in later commits to implement CollectionsPage

 data/ui/collection-view.ui  | 303 ++++++++++++++++++++++++++++++++------------
 src/ui/collection-view.vala |  75 ++++++++++-
 2 files changed, 296 insertions(+), 82 deletions(-)
---
diff --git a/data/ui/collection-view.ui b/data/ui/collection-view.ui
index 285be521..da3af516 100644
--- a/data/ui/collection-view.ui
+++ b/data/ui/collection-view.ui
@@ -9,129 +9,226 @@
     <signal name="notify::is-folded" handler="on_folded_changed"/>
     <signal name="notify::is-showing-bottom-bar" handler="update_bottom_bar"/>
     <signal name="notify::is-subview-open" handler="update_bottom_bar"/>
-    <signal name="notify::is-collection-empty" handler="update_adaptive_state"/>
+    <signal name="notify::is-selection-mode" handler="on_selection_mode_changed"/>
+    <signal name="notify::is-collection-empty" handler="on_collection_empty_changed"/>
     <child>
-      <object class="HdyDeck" id="deck">
+      <object class="GtkStack" id="header_bar_stack">
         <property name="visible">True</property>
-        <property name="can-swipe-back">True</property>
+        <property name="transition-type">crossfade</property>
         <child>
-          <object class="HdyHeaderBar" id="header_bar">
+          <object class="HdyDeck" id="deck">
             <property name="visible">True</property>
-            <property name="show-close-button">True</property>
-            <property name="centering-policy">strict</property>
+            <property name="can-swipe-back">True</property>
             <child>
-              <object class="GtkButton" id="add_game">
+              <object class="HdyHeaderBar" id="header_bar">
                 <property name="visible">True</property>
-                <property name="valign">center</property>
-                <property name="action-name">app.add-game-files</property>
-                <style>
-                  <class name="image-button"/>
-                </style>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="a11y-back">
-                    <property name="accessible-name" translatable="yes">Add game files…</property>
+                <property name="show-close-button">True</property>
+                <property name="centering-policy">strict</property>
+                <child>
+                  <object class="GtkButton" id="add_game">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <property name="action-name">app.add-game-files</property>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                    <child internal-child="accessible">
+                      <object class="AtkObject" id="a11y-back">
+                        <property name="accessible-name" translatable="yes">Add game files…</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImage" id="add_game_image">
+                        <property name="visible">True</property>
+                        <property name="icon-name">list-add-symbolic</property>
+                        <property name="icon-size">1</property>
+                      </object>
+                    </child>
                   </object>
+                  <packing>
+                    <property name="pack-type">start</property>
+                  </packing>
                 </child>
                 <child>
-                  <object class="GtkImage" id="add_game_image">
+                  <object class="GtkMenuButton" id="menu">
+                    <property name="menu-model">primary_menu</property>
+                    <!-- Translators: tooltip for the application menu button -->
+                    <property name="tooltip-text" translatable="yes">Menu</property>
+                    <property name="valign">center</property>
                     <property name="visible">True</property>
-                    <property name="icon-name">list-add-symbolic</property>
-                    <property name="icon-size">1</property>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                    <child>
+                      <object class="GtkImage" id="menu_image">
+                        <property name="visible">True</property>
+                        <property name="icon-name">open-menu-symbolic</property>
+                        <property name="icon-size">1</property>
+                      </object>
+                    </child>
                   </object>
+                  <packing>
+                    <property name="pack-type">end</property>
+                  </packing>
+                </child>
+                <child type="title">
+                  <object class="HdyViewSwitcherTitle" id="view_switcher_title">
+                    <property name="visible">True</property>
+                    <property name="title" translatable="yes">Games</property>
+                    <property name="stack">viewstack</property>
+                    <property name="view-switcher-enabled" bind-source="GamesCollectionView" 
bind-property="is-collection-empty" bind-flags="bidirectional|sync-create|invert-boolean"/>
+                    <signal name="notify::title-visible" handler="update_adaptive_state"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="selection_button">
+                    <property name="visible" bind-source="GamesCollectionView" 
bind-property="is_selection_available" bind-flags="bidirectional"/>
+                    <property name="valign">center</property>
+                    <property name="action-name">view.toggle-select</property>
+                    <child>
+                      <object class="GtkImage" id="select_image">
+                        <property name="visible">True</property>
+                        <property name="icon-name">object-select-symbolic</property>
+                      </object>
+                    </child>
+                    <child internal-child="accessible">
+                      <object class="AtkObject">
+                        <property name="accessible-name" translatable="yes">Select games</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="pack-type">end</property>
+                  </packing>
                 </child>
-              </object>
-              <packing>
-                <property name="pack-type">start</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkMenuButton" id="menu">
-                <property name="menu-model">primary_menu</property>
-                <!-- Translators: tooltip for the application menu button -->
-                <property name="tooltip-text" translatable="yes">Menu</property>
-                <property name="valign">center</property>
-                <property name="visible">True</property>
-                <style>
-                  <class name="image-button"/>
-                </style>
                 <child>
-                  <object class="GtkImage" id="menu_image">
+                  <object class="GtkToggleButton" id="search">
                     <property name="visible">True</property>
-                    <property name="icon-name">open-menu-symbolic</property>
-                    <property name="icon-size">1</property>
+                    <property name="valign">center</property>
+                    <property name="active" bind-source="GamesCollectionView" bind-property="search-mode" 
bind-flags="bidirectional"/>
+                    <property name="sensitive" bind-source="GamesCollectionView" 
bind-property="is-collection-empty" bind-flags="bidirectional|invert-boolean"/>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                    <child internal-child="accessible">
+                      <object class="AtkObject" id="a11y-search">
+                        <property name="accessible-name" translatable="yes">Search</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImage" id="search_image">
+                        <property name="visible">True</property>
+                        <property name="icon-name">edit-find-symbolic</property>
+                        <property name="icon-size">1</property>
+                      </object>
+                    </child>
                   </object>
+                  <packing>
+                    <property name="pack-type">end</property>
+                  </packing>
                 </child>
               </object>
-              <packing>
-                <property name="pack-type">end</property>
-              </packing>
-            </child>
-            <child type="title">
-              <object class="HdyViewSwitcherTitle" id="view_switcher_title">
-                <property name="visible">True</property>
-                <property name="title" translatable="yes">Games</property>
-                <property name="stack">viewstack</property>
-                <property name="view-switcher-enabled" bind-source="GamesCollectionView" 
bind-property="is-collection-empty" bind-flags="bidirectional|sync-create|invert-boolean"/>
-                <signal name="notify::title-visible" handler="update_adaptive_state"/>
-              </object>
             </child>
             <child>
-              <object class="GtkToggleButton" id="search">
+              <object class="HdyHeaderBar" id="subview_header_bar">
                 <property name="visible">True</property>
-                <property name="valign">center</property>
-                <property name="active" bind-source="GamesCollectionView" bind-property="search-mode" 
bind-flags="bidirectional"/>
-                <property name="sensitive" bind-source="GamesCollectionView" 
bind-property="is-collection-empty" bind-flags="bidirectional|invert-boolean"/>
-                <style>
-                  <class name="image-button"/>
-                </style>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="a11y-search">
-                    <property name="accessible-name" translatable="yes">Search</property>
+                <property name="show_close_button">True</property>
+                <property name="title" bind-source="platforms_page" bind-property="subview-title"/>
+                <child>
+                  <object class="GtkButton">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <signal name="clicked" handler="on_subview_back_clicked"/>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                    <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>
+                        <property name="icon-size">1</property>
+                      </object>
+                    </child>
                   </object>
                 </child>
                 <child>
-                  <object class="GtkImage" id="search_image">
+                  <object class="GtkButton" id="subview_selection_button">
                     <property name="visible">True</property>
-                    <property name="icon-name">edit-find-symbolic</property>
-                    <property name="icon-size">1</property>
+                    <property name="valign">center</property>
+                    <property name="action-name">view.toggle-select</property>"
+                    <child>
+                      <object class="GtkImage" id="subview_select_image">
+                        <property name="visible">True</property>
+                        <property name="icon-name">object-select-symbolic</property>
+                      </object>
+                    </child>
+                    <child internal-child="accessible">
+                      <object class="AtkObject">
+                        <property name="accessible-name" translatable="yes">Select games</property>
+                      </object>
+                    </child>
                   </object>
+                  <packing>
+                    <property name="pack-type">end</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToggleButton" id="search_subview">
+                    <property name="visible">True</property>
+                    <property name="valign">center</property>
+                    <property name="active" bind-source="GamesCollectionView" bind-property="search-mode" 
bind-flags="bidirectional"/>
+                    <property name="sensitive" bind-source="GamesCollectionView" 
bind-property="is-collection-empty" bind-flags="bidirectional|invert-boolean"/>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                    <child internal-child="accessible">
+                      <object class="AtkObject">
+                        <property name="accessible-name" translatable="yes">Search</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="icon-name">edit-find-symbolic</property>
+                        <property name="icon-size">1</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="pack-type">end</property>
+                  </packing>
                 </child>
               </object>
-              <packing>
-                <property name="pack-type">end</property>
-              </packing>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyHeaderBar" id="subview_header_bar">
+          <object class="HdyHeaderBar" id="selection_mode_header_bar">
             <property name="visible">True</property>
-            <property name="show_close_button">True</property>
-            <property name="title" bind-source="platforms_page" bind-property="subview-title"/>
             <child>
-              <object class="GtkButton">
+              <object class="GtkButton" id="cancel_selection_button">
                 <property name="visible">True</property>
                 <property name="valign">center</property>
-                <signal name="clicked" handler="on_subview_back_clicked"/>
-                <style>
-                  <class name="image-button"/>
-                </style>
+                <property name="label" translatable="yes">_Cancel</property>
+                <property name="action-name">view.toggle-select</property>
+                <property name="use-underline">True</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>
-                    <property name="icon-size">1</property>
+                    <property name="accessible-name" translatable="yes">Cancel</property>
                   </object>
                 </child>
               </object>
+              <packing>
+                <property name="pack-type">end</property>
+              </packing>
             </child>
             <child>
-              <object class="GtkToggleButton" id="search_subview">
+              <object class="GtkToggleButton" id="selection_mode_search">
                 <property name="visible">True</property>
                 <property name="valign">center</property>
                 <property name="active" bind-source="GamesCollectionView" bind-property="search-mode" 
bind-flags="bidirectional"/>
@@ -156,6 +253,35 @@
                 <property name="pack-type">end</property>
               </packing>
             </child>
+            <child type="title">
+              <object class="GtkMenuButton">
+                <property name="menu-model">selection-menu</property>
+                <property name="visible">True</property>
+                <property name="relief">none</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Click on items to select them</property>
+                        <property name="ellipsize">end</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">True</property>
+                        <property name="icon-name">pan-down-symbolic</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <style>
+              <class name="selection-mode"/>
+            </style>
           </object>
         </child>
       </object>
@@ -204,6 +330,7 @@
                 <child>
                   <object class="GamesGamesPage" id="games_page">
                     <property name="visible">True</property>
+                    <property name="is-selection-mode" bind-source="GamesCollectionView" 
bind-property="is-selection-mode"/>
                     <signal name="game-activated" handler="on_game_activated"/>
                   </object>
                   <packing>
@@ -216,7 +343,9 @@
                     <property name="visible">True</property>
                     <property name="is-folded" bind-source="GamesCollectionView" bind-property="is-folded" 
bind-flags="bidirectional"/>
                     <property name="is-subview-open" bind-source="GamesCollectionView" 
bind-property="is-subview-open" bind-flags="bidirectional"/>
+                    <property name="is-selection-mode" bind-source="GamesCollectionView" 
bind-property="is-selection-mode"/>
                     <signal name="game-activated" handler="on_game_activated"/>
+                    <signal name="notify::is-subview-open" handler="update_selection_availability"/>
                   </object>
                   <packing>
                     <property name="name">platform</property>
@@ -305,4 +434,16 @@
       </item>
     </section>
   </menu>
+  <menu id="selection-menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Select All</attribute>
+        <attribute name="action">view.select-all</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Select None</attribute>
+        <attribute name="action">view.select-none</attribute>
+      </item>
+    </section>
+  </menu>
 </interface>
diff --git a/src/ui/collection-view.vala b/src/ui/collection-view.vala
index 8e258fb3..47f38a92 100644
--- a/src/ui/collection-view.vala
+++ b/src/ui/collection-view.vala
@@ -9,10 +9,14 @@ private class Games.CollectionView : Gtk.Box, UiView {
        [GtkChild]
        private Hdy.Deck deck;
        [GtkChild]
+       private Gtk.Stack header_bar_stack;
+       [GtkChild]
        private Hdy.HeaderBar header_bar;
        [GtkChild]
        private Hdy.HeaderBar subview_header_bar;
        [GtkChild]
+       private Hdy.HeaderBar selection_mode_header_bar;
+       [GtkChild]
        private Hdy.ViewSwitcherTitle view_switcher_title;
        [GtkChild]
        private ErrorInfoBar error_info_bar;
@@ -100,8 +104,16 @@ private class Games.CollectionView : Gtk.Box, UiView {
        public bool is_folded { get; set; }
        public bool is_showing_bottom_bar { get; set; }
        public bool is_subview_open { get; set; }
+       public bool is_selection_mode { get; set; }
+       public bool is_selection_available { get; set; }
 
        private KonamiCode konami_code;
+       private SimpleActionGroup action_group;
+       private const ActionEntry[] action_entries = {
+               { "select-all",    select_all },
+               { "select-none",   select_none },
+               { "toggle-select", toggle_select }
+       };
 
        construct {
                var icon_name = Config.APPLICATION_ID + "-symbolic";
@@ -111,6 +123,10 @@ private class Games.CollectionView : Gtk.Box, UiView {
 
                konami_code = new KonamiCode (window);
                konami_code.code_performed.connect (on_konami_code_performed);
+
+               action_group = new SimpleActionGroup ();
+               action_group.add_action_entries (action_entries, this);
+               window.insert_action_group ("view", action_group);
        }
 
        public void show_error (string error_message) {
@@ -132,6 +148,7 @@ private class Games.CollectionView : Gtk.Box, UiView {
                if (((event.state & default_modifiers) == Gdk.ModifierType.MOD1_MASK) &&
                    (((window.get_direction () == Gtk.TextDirection.LTR) && keyval == Gdk.Key.Left) ||
                     ((window.get_direction () == Gtk.TextDirection.RTL) && keyval == Gdk.Key.Right)) &&
+                    !is_selection_mode &&
                     deck.navigate (Hdy.NavigationDirection.BACK))
                        return true;
 
@@ -156,6 +173,11 @@ private class Games.CollectionView : Gtk.Box, UiView {
                if (is_collection_empty)
                        return false;
 
+               if (is_selection_mode && keyval == Gdk.Key.Escape) {
+                       toggle_select ();
+                       return true;
+               }
+
                return search_bar.handle_event (event);
        }
 
@@ -175,6 +197,9 @@ private class Games.CollectionView : Gtk.Box, UiView {
 
                switch (button) {
                case EventCode.BTN_TL:
+                       if (is_selection_mode)
+                               return true;
+
                        var views = viewstack.get_children ();
                        unowned List<weak Gtk.Widget> current_view = views.find (viewstack.visible_child);
 
@@ -185,6 +210,9 @@ private class Games.CollectionView : Gtk.Box, UiView {
 
                        return true;
                case EventCode.BTN_TR:
+                       if (is_selection_mode)
+                               return true;
+
                        var views = viewstack.get_children ();
                        unowned List<weak Gtk.Widget> current_view = views.find (viewstack.visible_child);
 
@@ -245,6 +273,47 @@ private class Games.CollectionView : Gtk.Box, UiView {
                search_bar.run_search (query);
        }
 
+       private void select_none () {
+               platforms_page.select_none ();
+               games_page.select_none ();
+       }
+
+       private void select_all () {
+               if (viewstack.visible_child == platforms_page)
+                       platforms_page.select_all ();
+               else
+                       games_page.select_all ();
+       }
+
+       private void toggle_select () {
+               is_selection_mode = !is_selection_mode;
+       }
+
+       [GtkCallback]
+       private void on_selection_mode_changed () {
+               if (is_selection_mode) {
+                       header_bar_stack.visible_child = selection_mode_header_bar;
+               }
+               else {
+                       select_none ();
+                       header_bar_stack.visible_child = deck;
+               }
+
+               update_bottom_bar ();
+       }
+
+       [GtkCallback]
+       private void on_collection_empty_changed () {
+               update_adaptive_state ();
+               update_selection_availability ();
+       }
+
+       [GtkCallback]
+       private void update_selection_availability () {
+               is_selection_available = (viewstack.visible_child != platforms_page || !is_folded)
+                                         && !is_collection_empty;
+       }
+
        [GtkCallback]
        private void on_loading_notification_closed () {
                loading_notification = false;
@@ -261,6 +330,8 @@ private class Games.CollectionView : Gtk.Box, UiView {
                        games_page.reset_scroll_position ();
                else
                        platforms_page.reset ();
+
+               update_selection_availability ();
        }
 
        [GtkCallback]
@@ -300,11 +371,13 @@ private class Games.CollectionView : Gtk.Box, UiView {
                }
 
                update_bottom_bar ();
+               update_selection_availability ();
        }
 
        [GtkCallback]
        private void update_bottom_bar () {
-               view_switcher_bar.reveal = is_showing_bottom_bar && (!is_folded || !is_subview_open);
+               view_switcher_bar.reveal = !is_selection_mode && is_showing_bottom_bar
+                                          && (!is_folded || !is_subview_open);
        }
 
        [GtkCallback]


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