[gnome-games/wip/exalm/mobile: 7/9] platforms-view: Make adaptive



commit 97838b47b00e589adf75d19395e75550f56fd099
Author: Alexander Mikhaylenko <exalm7659 gmail com>
Date:   Thu May 23 15:11:59 2019 +0500

    platforms-view: Make adaptive
    
    Add a leaflet to PlatformsView. When folded, open subview when clicking
    on a platform.
    
    Add a second headerbar to CollectionHeaderBar, show it when viewing a
    platform when the window is narrow. This headerbar contains a back button
    and another search button.
    
    Implement Alt+Left (or Alt+Right, depending on text direction) to go back
    to platforms list when folded.

 data/ui/collection-header-bar.ui  | 194 +++++++++++++++++++++++++-------------
 data/ui/platforms-view.ui         |  38 +++++---
 src/ui/collection-box.vala        |   6 +-
 src/ui/collection-header-bar.vala |  44 ++++++++-
 src/ui/collection-view.vala       |   9 ++
 src/ui/platforms-view.vala        | 107 ++++++++++++++++++---
 6 files changed, 301 insertions(+), 97 deletions(-)
---
diff --git a/data/ui/collection-header-bar.ui b/data/ui/collection-header-bar.ui
index 3402063a..c2d47aeb 100644
--- a/data/ui/collection-header-bar.ui
+++ b/data/ui/collection-header-bar.ui
@@ -1,102 +1,168 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.24"/>
-  <template class="GamesCollectionHeaderBar" parent="GtkHeaderBar">
+  <template class="GamesCollectionHeaderBar" parent="GtkStack">
     <property name="visible">True</property>
-    <property name="show_close_button">True</property>
+    <property name="transition-type">slide-left-right</property>
     <child>
-      <object class="GtkButton" id="add_game">
+      <object class="GtkHeaderBar" id="main_header_bar">
         <property name="visible">True</property>
-        <property name="valign">center</property>
-        <property name="use-underline">True</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>
+        <property name="show_close_button">True</property>
         <child>
-          <object class="GtkImage" id="add_game_image">
+          <object class="GtkButton" id="add_game">
             <property name="visible">True</property>
-            <property name="icon-name">list-add-symbolic</property>
-            <property name="icon-size">1</property>
+            <property name="valign">center</property>
+            <property name="use-underline">True</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>
-      </object>
-      <packing>
-        <property name="pack-type">start</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkMenuButton" id="menu">
-        <property name="popover">menu_popover</property>
-        <!-- Translators: tooltip for the application menu button -->
-        <property name="tooltip-text" translatable="yes">Menu</property>
-        <property name="use-underline">True</property>
-        <property name="valign">center</property>
-        <property name="visible">True</property>
-        <style>
-          <class name="image-button"/>
-        </style>
-        <child>
-          <object class="GtkImage" id="menu_image">
+        <child type="title">
+          <object class="HdySqueezer" id="title_squeezer">
             <property name="visible">True</property>
-            <property name="icon-name">open-menu-symbolic</property>
-            <property name="icon-size">1</property>
+            <property name="transition-type">crossfade</property>
+            <signal name="notify::visible-child" handler="on_squeezer_visible_child_changed"/>
+            <child>
+              <object class="HdyViewSwitcher" id="view_switcher">
+                <property name="visible">True</property>
+                <property name="policy">wide</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="title_label">
+                <property name="visible">True</property>
+                <style>
+                  <class name="title"/>
+                </style>
+              </object>
+            </child>
           </object>
         </child>
-      </object>
-      <packing>
-        <property name="pack-type">end</property>
-      </packing>
-    </child>
-    <child type="title">
-      <object class="HdySqueezer" id="title_squeezer">
-        <property name="visible">True</property>
-        <property name="transition-type">crossfade</property>
-        <signal name="notify::visible-child" handler="on_squeezer_visible_child_changed"/>
         <child>
-          <object class="HdyViewSwitcher" id="view_switcher">
+          <object class="GtkMenuButton" id="menu">
+            <property name="popover">menu_popover</property>
+            <!-- Translators: tooltip for the application menu button -->
+            <property name="tooltip-text" translatable="yes">Menu</property>
+            <property name="use-underline">True</property>
+            <property name="valign">center</property>
             <property name="visible">True</property>
-            <property name="policy">wide</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>
-          <object class="GtkLabel" id="title_label">
+          <object class="GtkToggleButton" id="search">
             <property name="visible">True</property>
+            <property name="valign">center</property>
+            <property name="use-underline">True</property>
             <style>
-              <class name="title"/>
+              <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="name">main</property>
+      </packing>
     </child>
     <child>
-      <object class="GtkToggleButton" id="search">
+      <object class="GtkHeaderBar" id="subview_header_bar">
         <property name="visible">True</property>
-        <property name="valign">center</property>
-        <property name="use-underline">True</property>
-        <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>
+        <child>
+          <object class="GtkButton">
+            <property name="visible">True</property>
+            <property name="valign">center</property>
+            <property name="use-underline">True</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="GtkToggleButton" id="search_subview">
             <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="use-underline">True</property>
+            <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>
+        <property name="name">subview</property>
       </packing>
     </child>
   </template>
diff --git a/data/ui/platforms-view.ui b/data/ui/platforms-view.ui
index b0f548cc..68dceeaf 100644
--- a/data/ui/platforms-view.ui
+++ b/data/ui/platforms-view.ui
@@ -1,31 +1,39 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.24"/>
-  <template class="GamesPlatformsView" parent="GtkBox">
+  <template class="GamesPlatformsView" parent="GtkBin">
     <property name="visible">True</property>
     <property name="expand">True</property>
     <signal name="map" after="yes" handler="on_map"/>
     <signal name="unmap" after="no" handler="on_unmap"/>
     <child>
-      <object class="GtkScrolledWindow">
+      <object class="HdyLeaflet" id="leaflet">
         <property name="visible">True</property>
-        <property name="vexpand">True</property>
-        <property name="width-request">250</property>
+        <property name="mode-transition-type">slide</property>
+        <property name="child-transition-type">slide</property>
+        <signal name="notify::folded" handler="on_leaflet_folded_changed"/>
         <child>
-          <object class="GtkListBox" id="list_box">
+          <object class="GtkScrolledWindow" id="scrolled_window">
             <property name="visible">True</property>
-            <signal name="row-activated" handler="on_list_box_row_activated"/>
-            <style>
-              <class name="sidebar"/>
-            </style>
+            <property name="vexpand">True</property>
+            <property name="width-request">250</property>
+            <child>
+              <object class="GtkListBox" id="list_box">
+                <property name="visible">True</property>
+                <signal name="row-activated" handler="on_list_box_row_activated"/>
+                <style>
+                  <class name="sidebar"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GamesCollectionIconView" id="collection_view">
+            <property name="visible">True</property>
+            <property name="expand">True</property>
           </object>
         </child>
-      </object>
-    </child>
-    <child>
-      <object class="GamesCollectionIconView" id="collection_view">
-        <property name="visible">True</property>
-        <property name="expand">True</property>
       </object>
     </child>
   </template>
diff --git a/src/ui/collection-box.vala b/src/ui/collection-box.vala
index ae621b81..c7c0b4fc 100644
--- a/src/ui/collection-box.vala
+++ b/src/ui/collection-box.vala
@@ -46,7 +46,11 @@ private class Games.CollectionBox : Gtk.Box {
                get { return _adaptive_state; }
                construct {
                        _adaptive_state = value;
+
+                       platform_view.adaptive_state = adaptive_state;
+
                        adaptive_state.notify["is-showing-bottom-bar"].connect (update_bottom_bar);
+                       adaptive_state.notify["is-subview-open"].connect (update_bottom_bar);
                }
        }
 
@@ -180,6 +184,6 @@ private class Games.CollectionBox : Gtk.Box {
        }
 
        private void update_bottom_bar () {
-               view_switcher_bar.reveal = adaptive_state.is_showing_bottom_bar;
+               view_switcher_bar.reveal = adaptive_state.is_showing_bottom_bar && 
!adaptive_state.is_subview_open;
        }
 }
diff --git a/src/ui/collection-header-bar.vala b/src/ui/collection-header-bar.vala
index 0cb9da68..9042bb79 100644
--- a/src/ui/collection-header-bar.vala
+++ b/src/ui/collection-header-bar.vala
@@ -1,7 +1,7 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 [GtkTemplate (ui = "/org/gnome/Games/ui/collection-header-bar.ui")]
-private class Games.CollectionHeaderBar : Gtk.HeaderBar {
+private class Games.CollectionHeaderBar : Gtk.Stack {
        public bool search_mode { get; set; }
        private Gtk.Stack _viewstack;
        public Gtk.Stack viewstack {
@@ -31,6 +31,19 @@ private class Games.CollectionHeaderBar : Gtk.HeaderBar {
                }
        }
 
+       private AdaptiveState _adaptive_state;
+       public AdaptiveState adaptive_state {
+               get { return _adaptive_state; }
+               construct {
+                       _adaptive_state = value;
+                       adaptive_state.notify["is-folded"].connect (update_folded);
+                       adaptive_state.notify["is-subview-open"].connect (update_subview);
+                       adaptive_state.notify["subview-title"].connect (update_subview_title);
+               }
+       }
+
+       [GtkChild]
+       private Gtk.HeaderBar subview_header_bar;
        [GtkChild]
        private Hdy.Squeezer title_squeezer;
        [GtkChild]
@@ -39,15 +52,19 @@ private class Games.CollectionHeaderBar : Gtk.HeaderBar {
        private Hdy.ViewSwitcher view_switcher;
        [GtkChild]
        private Gtk.ToggleButton search;
-       private Binding search_binding;
+       [GtkChild]
+       private Gtk.ToggleButton search_subview;
 
-       public AdaptiveState adaptive_state { get; construct; }
+       private Binding search_binding;
+       private Binding search_subview_binding;
 
        private ulong viewstack_child_changed_id;
 
        construct {
                search_binding = bind_property ("search-mode", search, "active",
                                                BindingFlags.BIDIRECTIONAL);
+               search_subview_binding = bind_property ("search-mode", search_subview,
+                                                       "active", BindingFlags.BIDIRECTIONAL);
        }
 
        public CollectionHeaderBar (AdaptiveState adaptive_state) {
@@ -77,4 +94,25 @@ private class Games.CollectionHeaderBar : Gtk.HeaderBar {
 
                title_label.label = title;
        }
+
+       private void update_subview () {
+               bool show_subview = adaptive_state.is_subview_open && adaptive_state.is_folded;
+               visible_child_name = show_subview ? "subview" : "main";
+       }
+
+       private void update_folded () {
+               bool show_subview = adaptive_state.is_subview_open && adaptive_state.is_folded;
+
+               // FIXME: have a transition here. Crossfade doens't work well at the moment
+               set_visible_child_full (show_subview ? "subview" : "main", Gtk.StackTransitionType.NONE);
+       }
+
+       private void update_subview_title () {
+               subview_header_bar.title = adaptive_state.subview_title;
+       }
+
+       [GtkCallback]
+       private void on_subview_back_clicked () {
+               adaptive_state.is_subview_open = false;
+       }
 }
diff --git a/src/ui/collection-view.vala b/src/ui/collection-view.vala
index 8455e5b9..be34a2cc 100644
--- a/src/ui/collection-view.vala
+++ b/src/ui/collection-view.vala
@@ -109,6 +109,15 @@ private class Games.CollectionView : Object, UiView {
        public bool on_key_pressed (Gdk.EventKey event) {
                var default_modifiers = Gtk.accelerator_get_default_mod_mask ();
 
+               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)) &&
+                    adaptive_state.is_subview_open) {
+                       adaptive_state.is_subview_open = false;
+
+                       return true;
+               }
+
                if ((event.keyval == Gdk.Key.f || event.keyval == Gdk.Key.F) &&
                    (event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK) {
                        if (!search_mode)
diff --git a/src/ui/platforms-view.vala b/src/ui/platforms-view.vala
index 37f4c471..144af338 100644
--- a/src/ui/platforms-view.vala
+++ b/src/ui/platforms-view.vala
@@ -1,12 +1,27 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 [GtkTemplate (ui = "/org/gnome/Games/ui/platforms-view.ui")]
-private class Games.PlatformsView : Gtk.Box {
+private class Games.PlatformsView : Gtk.Bin {
        public signal void game_activated (Game game);
 
+       [GtkChild]
+       private Hdy.Leaflet leaflet;
+       [GtkChild]
+       private Gtk.ScrolledWindow scrolled_window;
+       [GtkChild]
+       private Gtk.ListBox list_box;
+       [GtkChild]
+       private CollectionIconView collection_view;
+       [GtkChild]
+       private GamepadBrowse gamepad_browse;
+
        private ulong model_items_changed_id;
+       private ulong adaptive_state_folded_id;
+       private ulong adaptive_state_subview_id;
+
        private GenericSet<Platform> platforms;
        private Platform selected_platform;
+       private bool has_used_gamepad;
 
        private string[] filtering_terms;
        public string filtering_text {
@@ -52,14 +67,28 @@ private class Games.PlatformsView : Gtk.Box {
                }
        }
 
-       [GtkChild]
-       private CollectionIconView collection_view;
+       private AdaptiveState _adaptive_state;
+       public AdaptiveState adaptive_state {
+               get { return _adaptive_state; }
+               set {
+                       if (adaptive_state_subview_id != 0) {
+                               adaptive_state.disconnect (adaptive_state_subview_id);
+                               adaptive_state_subview_id = 0;
+                       }
 
-       [GtkChild]
-       private Gtk.ListBox list_box;
+                       if (adaptive_state_folded_id != 0) {
+                               adaptive_state.disconnect (adaptive_state_folded_id);
+                               adaptive_state_folded_id = 0;
+                       }
 
-       [GtkChild]
-       private GamepadBrowse gamepad_browse;
+                       _adaptive_state = value;
+
+                       if (adaptive_state != null) {
+                               adaptive_state_subview_id = adaptive_state.notify["is-subview-open"].connect 
(update_subview);
+                               adaptive_state_folded_id = adaptive_state.notify["is-folded"].connect 
(update_selection_mode);
+                       }
+               }
+       }
 
        construct {
                platforms = new GenericSet<Platform> (Platform.hash, Platform.equal);
@@ -71,6 +100,10 @@ private class Games.PlatformsView : Gtk.Box {
                });
 
                collection_view.set_game_filter (filter_game);
+
+               on_leaflet_folded_changed ();
+
+               has_used_gamepad = false;
        }
 
        private int sort_rows (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
@@ -146,7 +179,9 @@ private class Games.PlatformsView : Gtk.Box {
                        if (first_row == null)
                                return false;
 
-                       list_box.select_row (first_row);
+                       has_used_gamepad = true;
+                       update_selection_mode ();
+
                        // This is needed to start moving the cursor with the gamepad only.
                        first_row.focus (direction);
 
@@ -156,15 +191,16 @@ private class Games.PlatformsView : Gtk.Box {
                switch (direction) {
                case Gtk.DirectionType.UP:
                        list_box.move_cursor (Gtk.MovementStep.DISPLAY_LINES, -1);
-                       list_box.activate_cursor_row ();
+                       select_platform_for_row (list_box.get_selected_row ());
 
                        return true;
                case Gtk.DirectionType.DOWN:
                        list_box.move_cursor (Gtk.MovementStep.DISPLAY_LINES, 1);
-                       list_box.activate_cursor_row ();
+                       select_platform_for_row (list_box.get_selected_row ());
 
                        return true;
                case Gtk.DirectionType.RIGHT:
+                       adaptive_state.is_subview_open = true;
                        collection_view.select_default_game (Gtk.DirectionType.RIGHT);
 
                        return true;
@@ -175,7 +211,8 @@ private class Games.PlatformsView : Gtk.Box {
 
        [GtkCallback]
        private bool on_gamepad_accept () {
-               list_box.activate_cursor_row ();
+               adaptive_state.is_subview_open = true;
+               collection_view.select_default_game (Gtk.DirectionType.RIGHT);
 
                return true;
        }
@@ -183,14 +220,22 @@ private class Games.PlatformsView : Gtk.Box {
        [GtkCallback]
        private bool on_gamepad_cancel () {
                collection_view.unselect_game ();
+               adaptive_state.is_subview_open = false;
 
                return true;
        }
 
        [GtkCallback]
        private void on_list_box_row_activated (Gtk.ListBoxRow row_item) {
+               select_platform_for_row (row_item);
+
+               adaptive_state.is_subview_open = true;
+       }
+
+       private void select_platform_for_row (Gtk.ListBoxRow row_item) {
                var row = row_item as PlatformListItem;
                selected_platform = row.platform;
+               adaptive_state.subview_title = selected_platform.get_name ();
 
                collection_view.invalidate_flow_box_filter ();
                collection_view.reset_scroll_position ();
@@ -214,14 +259,28 @@ private class Games.PlatformsView : Gtk.Box {
                }
        }
 
-       public void select_default_row () {
+       public void select_first_visible_row () {
                foreach (var child in list_box.get_children ()) {
                        var row = child as Gtk.ListBoxRow;
 
                        if (row.visible) {
                                list_box.select_row (row);
                                row.focus (Gtk.DirectionType.LEFT);
-                               on_list_box_row_activated (row);
+                               select_platform_for_row (row);
+                               break;
+                       }
+               }
+       }
+
+       private void select_current_row () {
+               if (adaptive_state.is_folded && !has_used_gamepad)
+                       return;
+
+               foreach (var child in list_box.get_children ()) {
+                       var platform_item = child as PlatformListItem;
+
+                       if (Platform.equal (platform_item.platform, selected_platform)) {
+                               list_box.select_row (platform_item);
                                break;
                        }
                }
@@ -257,6 +316,26 @@ private class Games.PlatformsView : Gtk.Box {
                        row.visible = is_row_visible;
                }
 
-               select_default_row ();
+               select_first_visible_row ();
+       }
+
+       private void update_selection_mode () {
+               if (!adaptive_state.is_folded || has_used_gamepad)
+                       list_box.selection_mode = Gtk.SelectionMode.SINGLE;
+               else
+                       list_box.selection_mode = Gtk.SelectionMode.NONE;
+               select_current_row ();
+       }
+
+       [GtkCallback]
+       private void on_leaflet_folded_changed () {
+               adaptive_state.is_folded = leaflet.folded;
+       }
+
+       private void update_subview () {
+               if (adaptive_state.is_subview_open)
+                       leaflet.visible_child = collection_view;
+               else
+                       leaflet.visible_child = scrolled_window;
        }
 }


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