[gnome-games/wip/exalm/libhandy: 58/58] ui: Switch to custom sidebar



commit f4ed309059a2b8c7c6987a27dea799c27ae4bb82
Author: Alexander Mikhaylenko <exalm7659 gmail com>
Date:   Wed Sep 5 18:14:14 2018 +0500

    ui: Switch to custom sidebar

 data/gtk-style.css              |   9 ++
 data/ui/preferences-window.ui   |   9 +-
 src/meson.build                 |   1 +
 src/ui/preferences-sidebar.vala | 225 ++++++++++++++++++++++++++++++++++++++++
 src/ui/preferences-window.vala  |   6 +-
 5 files changed, 247 insertions(+), 3 deletions(-)
---
diff --git a/data/gtk-style.css b/data/gtk-style.css
index 83ae1888..9bfe4bb3 100644
--- a/data/gtk-style.css
+++ b/data/gtk-style.css
@@ -30,3 +30,12 @@ gamesgamethumbnail {
                    @theme_unfocused_bg_color;
        border-color: @unfocused_borders;
 }
+
+stacksidebar * {
+  border-left: none;
+  border-right: none;
+}
+
+separator.sidebar {
+  min-width: 0;
+}
diff --git a/data/ui/preferences-window.ui b/data/ui/preferences-window.ui
index 6cdec08c..a18319c2 100644
--- a/data/ui/preferences-window.ui
+++ b/data/ui/preferences-window.ui
@@ -25,6 +25,9 @@
           <object class="GtkSeparator" id="header_separator">
             <property name="orientation">vertical</property>
             <property name="visible">True</property>
+            <style>
+              <class name="sidebar"/>
+            </style>
           </object>
         </child>
       </object>
@@ -41,8 +44,7 @@
             <property name="orientation">vertical</property>
             <property name="visible">True</property>
             <child>
-              <object class="GtkStackSidebar" id="sidebar">
-                <property name="margin-top">4</property>
+              <object class="GamesPreferencesSidebar" id="sidebar">
                 <property name="stack">stack</property>
                 <property name="vexpand">True</property>
                 <property name="visible">True</property>
@@ -58,6 +60,9 @@
           <object class="GtkSeparator" id="separator">
             <property name="orientation">vertical</property>
             <property name="visible">True</property>
+            <style>
+              <class name="sidebar"/>
+            </style>
           </object>
         </child>
         <child>
diff --git a/src/meson.build b/src/meson.build
index 26b2ec3e..3a3ade7f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -160,6 +160,7 @@ vala_sources = [
   'ui/preferences-page-plugins.vala',
   'ui/preferences-page-plugins-item.vala',
   'ui/preferences-page-video.vala',
+  'ui/preferences-sidebar.vala',
   'ui/preferences-window.vala',
   'ui/quit-dialog.vala',
   'ui/remote-display.vala',
diff --git a/src/ui/preferences-sidebar.vala b/src/ui/preferences-sidebar.vala
new file mode 100644
index 00000000..27d5fda9
--- /dev/null
+++ b/src/ui/preferences-sidebar.vala
@@ -0,0 +1,225 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+private class Games.PreferencesSidebar: Gtk.Bin {
+       private Gtk.Stack _stack;
+       public Gtk.Stack stack {
+               get {
+                       return _stack;
+               }
+               set {
+                       if (_stack != null) {
+                               disconnect_stack_signals ();
+                               clear_sidebar ();
+                               _stack = null;
+                       }
+
+                       _stack = value;
+
+                       if (_stack != null) {
+                               populate_sidebar ();
+                               connect_stack_signals ();
+                       }
+
+                       queue_resize ();
+               }
+       }
+
+       private bool _folded;
+       public bool folded {
+               get { return _folded; }
+               set {
+                       _folded = value;
+
+                       if (_folded)
+                               list.selection_mode = Gtk.SelectionMode.NONE;
+                       else {
+                               list.selection_mode = Gtk.SelectionMode.SINGLE;
+                               on_child_changed ();
+                       }
+               }
+       }
+
+       public signal void row_selected ();
+
+       private Gtk.ListBox list;
+       private HashTable<Gtk.Widget, Gtk.ListBoxRow> rows;
+       private bool in_child_changed;
+
+       private ulong on_stack_child_added_id;
+       private ulong on_stack_child_removed_id;
+       private ulong on_child_changed_id;
+
+       static construct {
+               set_css_name ("stacksidebar");
+       }
+
+       construct {
+               var sw = new Gtk.ScrolledWindow (null, null);
+               sw.show ();
+               sw.set_no_show_all (true);
+               sw.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
+
+               add (sw);
+
+               list = new Gtk.ListBox ();
+               list.show ();
+
+               sw.add (list);
+
+               list.set_sort_func (sort_list);
+
+               list.row_activated.connect (row_activated);
+
+               get_style_context ().add_class ("sidebar");
+
+               rows = new HashTable<Gtk.Widget, Gtk.ListBoxRow> (null, null);
+       }
+
+       private int sort_list (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
+               int left = 0;
+               int right = 0;
+
+               if (row1 != null) {
+                       var item = row1.get_child ();
+                       Gtk.Widget widget = item.get_data ("stack-child");
+                       stack.child_get (widget, "position", out left, null);
+               }
+
+               if (row2 != null) {
+                       var item = row2.get_child ();
+                       Gtk.Widget widget = item.get_data ("stack-child");
+                       stack.child_get (widget, "position", out right, null);
+               }
+
+               if (left < right)
+                       return -1;
+
+               if (left > right)
+                       return 1;
+
+               return 0;
+       }
+
+       private void row_activated (Gtk.ListBox box, Gtk.ListBoxRow? row) {
+               if (in_child_changed)
+                       return;
+
+               if (row == null)
+                       return;
+
+               var bin = row as Gtk.Bin;
+               var item = bin.get_child ();
+               Gtk.Widget widget = item.get_data ("stack-child");
+               stack.visible_child = widget;
+
+               row_selected ();
+       }
+
+       private void update_row (Gtk.Widget widget, Gtk.Widget row) {
+               var title = "";
+               var needs_attention = false;
+
+               stack.child_get (widget, "title", out title, null);
+               stack.child_get (widget, "needs-attention", out needs_attention, null);
+
+               var bin = row as Gtk.Bin;
+               var item = bin.get_child () as Gtk.Label;
+               item.set_text (title);
+
+               row.visible = widget.visible && title != null;
+
+               if (needs_attention)
+                       row.get_style_context ().add_class (Gtk.STYLE_CLASS_NEEDS_ATTENTION);
+               else
+                       row.get_style_context ().remove_class (Gtk.STYLE_CLASS_NEEDS_ATTENTION);
+       }
+
+       private void on_position_updated (Object object, ParamSpec param) {
+               list.invalidate_sort ();
+       }
+
+       private void on_child_updated (Object object, ParamSpec param) {
+               var widget = object as Gtk.Widget;
+               var row = rows[widget];
+               update_row (widget, row);
+       }
+
+       private void add_child (Gtk.Widget widget) {
+               /* Check we don't actually already know about this widget */
+               if (widget in rows)
+                       return;
+
+               /* Make a pretty item when we add kids */
+               var item = new Gtk.Label ("");
+               item.set_halign (Gtk.Align.START);
+               item.set_valign (Gtk.Align.START);
+               var row = new Gtk.ListBoxRow ();
+               row.add (item);
+               item.show ();
+
+               update_row (widget, row);
+
+               /* Hook up for events */
+               widget.child_notify["title"].connect (on_child_updated);
+               widget.child_notify["needs-attention"].connect (on_child_updated);
+               widget.notify["visible"].connect (on_child_updated);
+               widget.child_notify["position"].connect (on_position_updated);
+
+               item.set_data ("stack-child", widget);
+               rows[widget] = row;
+               list.add (row);
+       }
+
+       private void remove_child (Gtk.Widget widget) {
+               var row = rows[widget];
+               if (row == null)
+                       return;
+
+               list.remove (row);
+               rows.remove (widget);
+}
+
+       private void populate_sidebar () {
+               stack.foreach (add_child);
+
+               var widget = stack.get_visible_child ();
+               if (widget != null) {
+                       var row = rows[widget];
+                       list.select_row (row);
+               }
+       }
+
+       private void clear_sidebar () {
+               stack.foreach (remove_child);
+       }
+
+       private void on_child_changed () {
+               var child = stack.get_visible_child ();
+               var row = rows[child];
+               if (row != null) {
+                       in_child_changed = true;
+                       list.select_row (row);
+                       in_child_changed = false;
+               }
+       }
+
+       private void on_stack_child_added (Gtk.Widget widget) {
+               add_child (widget);
+       }
+
+       private void on_stack_child_removed (Gtk.Widget widget) {
+               remove_child (widget);
+       }
+
+       private void disconnect_stack_signals () {
+               stack.disconnect (on_stack_child_added_id);
+               stack.disconnect (on_stack_child_removed_id);
+               stack.disconnect (on_child_changed_id);
+       }
+
+       private void connect_stack_signals () {
+               on_stack_child_added_id = stack.add.connect (on_stack_child_added);
+               on_stack_child_removed_id = stack.remove.connect (on_stack_child_removed);
+               on_child_changed_id = stack.notify["visible-child"].connect (on_child_changed);
+       }
+}
diff --git a/src/ui/preferences-window.vala b/src/ui/preferences-window.vala
index cac46cb2..a0a3a738 100644
--- a/src/ui/preferences-window.vala
+++ b/src/ui/preferences-window.vala
@@ -13,6 +13,8 @@ private class Games.PreferencesWindow : Gtk.Window {
        [GtkChild]
        private Gtk.Box sidebar_vbox;
        [GtkChild]
+       private PreferencesSidebar sidebar;
+       [GtkChild]
        private Gtk.Separator separator;
        [GtkChild]
        private Gtk.Stack stack;
@@ -72,7 +74,8 @@ private class Games.PreferencesWindow : Gtk.Window {
                        var page = child as PreferencesPage;
                        stack.notify["visible-child-name"].connect (page.visible_page_changed);
                });
-               stack.notify["visible-child-name"].connect (visible_child_changed);
+               sidebar.row_selected.connect (visible_child_changed);
+               //stack.notify["visible-child-name"].connect (visible_child_changed);
                visible_child_changed ();
        }
 
@@ -101,6 +104,7 @@ private class Games.PreferencesWindow : Gtk.Window {
                var folded = main_box.get_fold () == Hdy.Fold.FOLDED;
 
                left_header_bar.show_close_button = folded;
+               sidebar.folded = folded;
                back_button.visible = folded;
        }
 }


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