[dconf-editor] Introduce OverlayedList.



commit afb15a0adb78444fb3257669b99ad8d679183823
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Wed Nov 14 11:57:45 2018 +0100

    Introduce OverlayedList.

 editor/bookmarks-list.vala                      | 392 ++++++++++--------------
 editor/bookmarks.vala                           |  10 +-
 editor/browser-view.ui                          |   1 +
 editor/browser-view.vala                        |   9 +-
 editor/dconf-editor.gresource.xml               |   2 +-
 editor/dconf-window.vala                        |  11 +-
 editor/meson.build                              |   3 +-
 editor/{bookmarks-list.ui => overlayed-list.ui} |  14 +-
 editor/overlayed-list.vala                      | 232 ++++++++++++++
 editor/registry-placeholder.vala                |   5 +
 10 files changed, 425 insertions(+), 254 deletions(-)
---
diff --git a/editor/bookmarks-list.vala b/editor/bookmarks-list.vala
index 9975661..6dfc130 100644
--- a/editor/bookmarks-list.vala
+++ b/editor/bookmarks-list.vala
@@ -17,47 +17,24 @@
 
 using Gtk;
 
-[GtkTemplate (ui = "/ca/desrt/dconf-editor/ui/bookmarks-list.ui")]
-private class BookmarksList : Overlay
+private class BookmarksList : OverlayedList
 {
-    [GtkChild] private ScrolledWindow   scrolled;
-    [GtkChild] private ListBox          bookmarks_list_box;
-    [GtkChild] private Box              edit_mode_box;
-
-    [GtkChild] private ModelButton enter_edit_mode_button;
-    [GtkChild] private ModelButton leave_edit_mode_button;
-    public string edit_mode_action_prefix
-    {
-        construct
-        {
-            // TODO sanitize "value"
-            enter_edit_mode_button.set_detailed_action_name (value + ".set-edit-mode(true)");
-            leave_edit_mode_button.set_detailed_action_name (value + ".set-edit-mode(false)");
-        }
-    }
-
-    public bool needs_shadows
-    {
-        construct
-        {
-            if (value)
-                scrolled.shadow_type = ShadowType.ETCHED_IN;
-            else
-                scrolled.shadow_type = ShadowType.NONE;
-        }
-    }
-
-    [GtkChild] private RegistryPlaceholder placeholder;
-    public bool big_placeholder { internal construct { placeholder.big = value; }}
-
     private HashTable<string, Bookmark> bookmarks_hashtable = new HashTable<string, Bookmark> (str_hash, 
str_equal);
-    private Bookmark? last_row = null;
-    private uint n_bookmarks = 0;
 
     private string schema_id = "ca.desrt.dconf-editor.Bookmarks";   // TODO move in a library
     private GLib.Settings settings;
     ulong bookmarks_changed_handler = 0;
 
+    construct
+    {
+        placeholder_icon = "starred-symbolic";
+        placeholder_text = _("Bookmarks will\nbe added here");
+        add_placeholder ();
+
+        first_mode_name = _("Use");
+        second_mode_name = _("Edit");
+    }
+
     public string schema_path
     {
         internal set
@@ -71,9 +48,9 @@ private class BookmarksList : Overlay
             bool is_writable = settings.is_writable ("bookmarks");
 
             create_bookmark_rows (bookmarks_variant);
-            edit_mode_box.set_visible (is_writable && !has_empty_list_class);
+            change_editability (is_writable);
 
-            bookmarks_changed (settings.get_value ("bookmarks"), settings.is_writable ("bookmarks"));
+            bookmarks_changed (bookmarks_variant, is_writable);
 
             destroy.connect (() => {
                     settings.disconnect (bookmarks_changed_handler);
@@ -83,10 +60,12 @@ private class BookmarksList : Overlay
     }
 
     internal signal void bookmarks_changed (Variant bookmarks_variant, bool writable);
+    internal signal void update_bookmarks_icons (Variant bookmarks_variant);
     private void on_bookmarks_changed (GLib.Settings _settings, string key)
     {
         Variant bookmarks_variant = _settings.get_value (key);
         create_bookmark_rows (bookmarks_variant);
+        update_bookmarks_icons (bookmarks_variant); // FIXME flickering
         bookmarks_changed (bookmarks_variant, _settings.is_writable (key));
     }
 
@@ -95,65 +74,32 @@ private class BookmarksList : Overlay
     {
         bool is_writable = _settings.is_writable (key);
         writability_changed (is_writable);
-        edit_mode_box.set_visible (is_writable);
-    }
-
-    internal signal void selection_changed ();
-
-    internal enum SelectionState {
-        EMPTY,
-        UNIQUE,
-        FIRST,
-        LAST,
-        MIDDLE,
-        MULTIPLE
-    }
-
-    internal SelectionState get_selection_state ()
-    {
-        List<weak ListBoxRow> selected_rows = bookmarks_list_box.get_selected_rows ();
-        uint n_selected_rows = selected_rows.length ();
-
-        if (n_selected_rows == 0)
-            return SelectionState.EMPTY;
-        if (n_selected_rows >= 2)
-            return SelectionState.MULTIPLE;
-
-        int index = selected_rows.nth_data (0).get_index ();
-        bool is_first = index == 0;
-        bool is_last = bookmarks_list_box.get_row_at_index (index + 1) == null;
-        if (is_first && is_last)
-            return SelectionState.UNIQUE;
-        if (is_first)
-            return SelectionState.FIRST;
-        if (is_last)
-            return SelectionState.LAST;
-        return SelectionState.MIDDLE;
+        change_editability (is_writable);
     }
 
     internal void enter_edit_mode ()
     {
-        bookmarks_list_box.grab_focus ();
+        main_list_box.grab_focus ();
 
-        bookmarks_list_box.@foreach ((widget) => { ((Bookmark) widget).set_actionable (false); });
-        bookmarks_list_box.set_activate_on_single_click (false);
-        bookmarks_list_box.set_selection_mode (SelectionMode.MULTIPLE);
+        main_list_box.@foreach ((widget) => { ((Bookmark) widget).set_actionable (false); });
+        main_list_box.set_activate_on_single_click (false);
+        main_list_box.set_selection_mode (SelectionMode.MULTIPLE);
     }
 
     internal bool leave_edit_mode ()
     {
 
-        ListBoxRow? row = (ListBoxRow?) bookmarks_list_box.get_focus_child ();  // broken, the child needs 
to have the global focus...
+        ListBoxRow? row = (ListBoxRow?) main_list_box.get_focus_child ();  // broken, the child needs to 
have the global focus...
         bool give_focus_to_switch = row == null;
         if (give_focus_to_switch)
         {
-            List<weak ListBoxRow> selected_rows = bookmarks_list_box.get_selected_rows ();
+            List<weak ListBoxRow> selected_rows = main_list_box.get_selected_rows ();
             row = selected_rows.nth_data (0);
         }
 
-        bookmarks_list_box.@foreach ((widget) => { ((Bookmark) widget).set_actionable (true); });
-        bookmarks_list_box.set_activate_on_single_click (true);
-        bookmarks_list_box.set_selection_mode (SelectionMode.SINGLE);
+        main_list_box.@foreach ((widget) => { ((Bookmark) widget).set_actionable (true); });
+        main_list_box.set_activate_on_single_click (true);
+        main_list_box.set_selection_mode (SelectionMode.SINGLE);
 
         if (row != null)
             select_row_for_real ((!) row);
@@ -181,39 +127,21 @@ private class BookmarksList : Overlay
         return unduplicated_bookmarks;
     }
 
-    private bool has_empty_list_class = false;
     internal bool create_bookmark_rows (Variant bookmarks_variant)
     {
-        _create_bookmark_rows (bookmarks_variant, ref bookmarks_list_box, ref bookmarks_hashtable, ref 
last_row, ref n_bookmarks);
-        bool no_bookmarks = n_bookmarks == 0;
-
-        if (no_bookmarks && !has_empty_list_class)
-        {
-            bookmarks_list_box.get_style_context ().add_class ("empty-list");
-            has_empty_list_class = true;
-            edit_mode_box.hide ();
-        }
-        else if (!no_bookmarks && has_empty_list_class)
-        {
-            bookmarks_list_box.get_style_context ().remove_class ("empty-list");
-            has_empty_list_class = false;
-            edit_mode_box.show ();
-        }
-
-        return no_bookmarks;
+        _create_bookmark_rows (bookmarks_variant, ref main_list_store, ref main_list_box, ref 
bookmarks_hashtable);
+        return n_items == 0;
     }
-    private static void _create_bookmark_rows (Variant bookmarks_variant, ref ListBox bookmarks_list_box, 
ref HashTable<string, Bookmark> bookmarks_hashtable, ref Bookmark? last_row, ref uint n_bookmarks)
+    private static void _create_bookmark_rows (Variant bookmarks_variant, ref GLib.ListStore 
main_list_store, ref ListBox main_list_box, ref HashTable<string, Bookmark> bookmarks_hashtable)
     {
         string saved_bookmark_name = "";
-        ListBoxRow? selected_row = bookmarks_list_box.get_selected_row ();
+        ListBoxRow? selected_row = main_list_box.get_selected_row ();
         if (selected_row != null && ((!) selected_row) is Bookmark)
             saved_bookmark_name = ((Bookmark) (!) selected_row).bookmark_name;
         selected_row = null;
 
-        bookmarks_list_box.@foreach ((widget) => widget.destroy ());
+        main_list_store.remove_all ();
         bookmarks_hashtable.remove_all ();
-        last_row = null;
-        n_bookmarks = 0;
 
         string [] bookmarks = bookmarks_variant.get_strv ();
         string [] unduplicated_bookmarks = new string [0];
@@ -226,20 +154,18 @@ private class BookmarksList : Overlay
             unduplicated_bookmarks += bookmark;
 
             Bookmark bookmark_row = new Bookmark (bookmark);
-            bookmarks_list_box.add (bookmark_row);
+            main_list_store.append (bookmark_row);
             bookmark_row.show ();
             bookmarks_hashtable.insert (bookmark, bookmark_row);
-            last_row = bookmark_row;
 
             if (saved_bookmark_name == bookmark)
                 selected_row = bookmark_row;
-            n_bookmarks ++;
         }
 
         if (selected_row == null)
-            selected_row = bookmarks_list_box.get_row_at_index (0);
+            selected_row = main_list_box.get_row_at_index (0);
         if (selected_row != null)
-            bookmarks_list_box.select_row ((!) selected_row);
+            main_list_box.select_row ((!) selected_row);
     }
 
     internal void update_bookmark_icon (string bookmark, BookmarkIcon icon)
@@ -281,66 +207,18 @@ private class BookmarksList : Overlay
         }
     }
 
-    /*\
-    * * keyboard
-    \*/
-
-    internal void down_pressed ()
-    {
-        ListBoxRow? row = bookmarks_list_box.get_selected_row ();
-        if (row == null)
-            row = bookmarks_list_box.get_row_at_index (0);
-        else
-            row = bookmarks_list_box.get_row_at_index (((!) row).get_index () + 1);
-
-        if (row == null)
-            return;
-        bookmarks_list_box.select_row ((!) row);
-        ((!) row).grab_focus ();
-    }
-
-    internal void up_pressed ()
-    {
-        ListBoxRow? row = bookmarks_list_box.get_selected_row ();
-        if (row == null)
-            row = last_row;
-        else
-        {
-            int index = ((!) row).get_index ();
-            if (index <= 0)
-                return;
-            row = bookmarks_list_box.get_row_at_index (index - 1);
-        }
-
-        if (row == null)
-            return;
-        bookmarks_list_box.select_row ((!) row);
-        ((!) row).grab_focus ();
-    }
-
-    internal void select_all ()
-    {
-        bookmarks_list_box.select_all ();
-    }
-
-    internal void unselect_all ()
-    {
-        bookmarks_list_box.unselect_all ();
-    }
-
     /*\
     * * remote action entries
     \*/
 
-    internal signal void update_bookmarks_icons (Variant bookmarks_variant);
     internal void trash_bookmark ()
     {
-        ListBoxRow? row = (ListBoxRow?) bookmarks_list_box.get_focus_child ();
+        ListBoxRow? row = (ListBoxRow?) main_list_box.get_focus_child ();
         bool focused_row_will_survive = row != null && !((!) row).is_selected ();
 
         string [] bookmarks_to_remove = new string [0];
         int upper_index = int.MAX;
-        bookmarks_list_box.selected_foreach ((_list_box, selected_row) => {
+        main_list_box.selected_foreach ((_list_box, selected_row) => {
                 if (!(selected_row is Bookmark))
                     assert_not_reached ();
                 bookmarks_to_remove += ((Bookmark) selected_row).bookmark_name;
@@ -357,16 +235,16 @@ private class BookmarksList : Overlay
 
         if (!focused_row_will_survive)
         {
-            row = bookmarks_list_box.get_row_at_index (upper_index + 1);
+            row = main_list_box.get_row_at_index (upper_index + 1);
             if (row == null)
             {
                 if (upper_index > 0)
-                    row = bookmarks_list_box.get_row_at_index (upper_index - 1);
+                    row = main_list_box.get_row_at_index (upper_index - 1);
                 // TODO else quit mode
             }
         }
         if (row != null)
-            bookmarks_list_box.select_row ((!) row);
+            main_list_box.select_row ((!) row);
 
         remove_bookmarks (settings, bookmarks_to_remove);
         update_bookmarks_icons (settings.get_value ("bookmarks"));
@@ -374,9 +252,9 @@ private class BookmarksList : Overlay
 
     internal void move_top ()
     {
-//        bookmarks_list_box.selected_foreach ((_list_box, selected_row) => {
+//        main_list_box.selected_foreach ((_list_box, selected_row) => {
 
-        ListBoxRow? row = bookmarks_list_box.get_selected_row ();
+        ListBoxRow? row = main_list_box.get_selected_row ();
         if (row == null)
             return; // TODO assert_not_reached?
 
@@ -386,19 +264,34 @@ private class BookmarksList : Overlay
 
         if (index == 0)
             return;
-        bookmarks_list_box.remove ((!) row);
-        bookmarks_list_box.prepend ((!) row);
-        select_row_for_real ((!) row);
 
-        Adjustment adjustment = bookmarks_list_box.get_adjustment ();
-        adjustment.set_value (adjustment.get_lower ());
+        string [] old_bookmarks = settings.get_strv ("bookmarks");
+        string [] new_bookmarks = new string [0];
+        uint position = 0;
 
-        update_bookmarks_after_move ();
+        new_bookmarks += old_bookmarks [index];
+        foreach (string bookmark in old_bookmarks)
+        {
+            if (index != position)
+                new_bookmarks += bookmark;
+            position++;
+        }
+
+        SignalHandler.block (main_list_store, content_changed_handler);
+        SignalHandler.block (settings, bookmarks_changed_handler);
+        settings.set_strv ("bookmarks", new_bookmarks);
+        GLib.Settings.sync ();   // TODO better? really needed?
+        SignalHandler.unblock (settings, bookmarks_changed_handler);
+        SignalHandler.unblock (main_list_store, content_changed_handler);
+        on_bookmarks_changed (settings, "bookmarks");
+
+        Adjustment adjustment = main_list_box.get_adjustment ();
+        adjustment.set_value (adjustment.get_lower ());
     }
 
     internal void move_up ()
     {
-        ListBoxRow? row = bookmarks_list_box.get_selected_row ();
+        ListBoxRow? row = main_list_box.get_selected_row ();
         if (row == null)
             return; // TODO assert_not_reached?
 
@@ -409,7 +302,7 @@ private class BookmarksList : Overlay
         if (index == 0)
             return;
 
-        ListBoxRow? prev_row = bookmarks_list_box.get_row_at_index (index - 1);
+        ListBoxRow? prev_row = main_list_box.get_row_at_index (index - 1);
         if (prev_row == null)
             assert_not_reached ();
 
@@ -419,25 +312,51 @@ private class BookmarksList : Overlay
         if (row_child == null)
             assert_not_reached ();
         ((!) row_child).get_allocation (out row_allocation);
-        Adjustment adjustment = bookmarks_list_box.get_adjustment ();
-        int proposed_adjustemnt_value = row_allocation.y + (int) ((row_allocation.height - 
list_allocation.height) / 3.0);
-        bool should_adjust = adjustment.get_value () > proposed_adjustemnt_value;
+//        Adjustment adjustment = main_list_box.get_adjustment ();
+//        int proposed_adjustment_value = row_allocation.y + (int) ((row_allocation.height - 
list_allocation.height) / 3.0);
+//        bool should_adjust = adjustment.get_value () > proposed_adjustment_value;
+
+        string [] old_bookmarks = settings.get_strv ("bookmarks");
+        string [] new_bookmarks = new string [0];
+        uint position = 0;
+
+        foreach (string bookmark in old_bookmarks)
+        {
+            if (index == position + 1)
+                new_bookmarks += old_bookmarks [index];
+            if (index != position)
+                new_bookmarks += bookmark;
+            position++;
+        }
+
+        SignalHandler.block (main_list_store, content_changed_handler);
+        SignalHandler.block (settings, bookmarks_changed_handler);
+        settings.set_strv ("bookmarks", new_bookmarks);
+        GLib.Settings.sync ();   // TODO better? really needed?
+        SignalHandler.unblock (settings, bookmarks_changed_handler);
+        SignalHandler.unblock (main_list_store, content_changed_handler);
+        on_bookmarks_changed (settings, "bookmarks");
+
+//        main_list_box.unselect_row ((!) row);
+
+//        SignalHandler.block (main_list_store, content_changed_handler);
+//        main_list_store.remove (index - 1);
+//        main_list_box.remove ((!) prev_row);
 
-        bookmarks_list_box.unselect_row ((!) row);
-        bookmarks_list_box.remove ((!) prev_row);
+//        if (should_adjust)
+//            adjustment.set_value (proposed_adjustment_value);
 
-        if (should_adjust)
-            adjustment.set_value (proposed_adjustemnt_value);
+//        main_list_store.insert (index, (!) prev_row);
+//        SignalHandler.unblock (main_list_store, content_changed_handler);
 
-        bookmarks_list_box.insert ((!) prev_row, index);
-        bookmarks_list_box.select_row ((!) row);
+//        main_list_box.select_row ((!) row);
 
-        update_bookmarks_after_move ();
+//        update_bookmarks_after_move ();
     }
 
     internal void move_down ()
     {
-        ListBoxRow? row = bookmarks_list_box.get_selected_row ();
+        ListBoxRow? row = main_list_box.get_selected_row ();
         if (row == null)
             return; // TODO assert_not_reached?
 
@@ -445,7 +364,7 @@ private class BookmarksList : Overlay
         if (index < 0)
             assert_not_reached ();
 
-        ListBoxRow? next_row = bookmarks_list_box.get_row_at_index (index + 1);
+        ListBoxRow? next_row = main_list_box.get_row_at_index (index + 1);
         if (next_row == null)
             return;
 
@@ -455,27 +374,53 @@ private class BookmarksList : Overlay
         if (row_child == null)
             assert_not_reached ();
         ((!) row_child).get_allocation (out row_allocation);
-        Adjustment adjustment = bookmarks_list_box.get_adjustment ();
-        int proposed_adjustemnt_value = row_allocation.y + (int) (2 * (row_allocation.height - 
list_allocation.height) / 3.0);
-        bool should_adjust = adjustment.get_value () < proposed_adjustemnt_value;
+//        Adjustment adjustment = main_list_box.get_adjustment ();
+//        int proposed_adjustment_value = row_allocation.y + (int) (2 * (row_allocation.height - 
list_allocation.height) / 3.0);
+//        bool should_adjust = adjustment.get_value () < proposed_adjustment_value;
 
-        bookmarks_list_box.unselect_row ((!) row);
-        bookmarks_list_box.remove ((!) next_row);
+        string [] old_bookmarks = settings.get_strv ("bookmarks");
+        string [] new_bookmarks = new string [0];
+        uint position = 0;
 
-        if (should_adjust)
-            adjustment.set_value (proposed_adjustemnt_value);
+        foreach (string bookmark in old_bookmarks)
+        {
+            if (index != position)
+                new_bookmarks += bookmark;
+            if (position == index + 1)
+                new_bookmarks += old_bookmarks [index];
+            position++;
+        }
 
-        bookmarks_list_box.insert ((!) next_row, index);
-        bookmarks_list_box.select_row ((!) row);
+        SignalHandler.block (main_list_store, content_changed_handler);
+        SignalHandler.block (settings, bookmarks_changed_handler);
+        settings.set_strv ("bookmarks", new_bookmarks);
+        GLib.Settings.sync ();   // TODO better? really needed?
+        SignalHandler.unblock (settings, bookmarks_changed_handler);
+        SignalHandler.unblock (main_list_store, content_changed_handler);
+        on_bookmarks_changed (settings, "bookmarks");
+
+//        main_list_box.unselect_row ((!) row);
+
+//        SignalHandler.block (main_list_store, content_changed_handler);
+//        main_list_store.remove (index - 1);
+//        main_list_box.remove ((!) next_row);
 
-        update_bookmarks_after_move ();
+//        if (should_adjust)
+//            adjustment.set_value (proposed_adjustment_value);
+
+//        main_list_store.insert (index, (!) next_row);
+//        SignalHandler.unblock (main_list_store, content_changed_handler);
+
+//        main_list_box.select_row ((!) row);
+
+//        update_bookmarks_after_move ();
     }
 
     internal void move_bottom ()
     {
-//        bookmarks_list_box.selected_foreach ((_list_box, selected_row) => {
+//        main_list_box.selected_foreach ((_list_box, selected_row) => {
 
-        ListBoxRow? row = bookmarks_list_box.get_selected_row ();
+        ListBoxRow? row = main_list_box.get_selected_row ();
         if (row == null)
             return; // TODO assert_not_reached?
 
@@ -483,48 +428,37 @@ private class BookmarksList : Overlay
         if (index < 0)
             assert_not_reached ();
 
-        bookmarks_list_box.remove ((!) row);
-        bookmarks_list_box.insert ((!) row, -1);
-        select_row_for_real ((!) row);
-
-        Adjustment adjustment = bookmarks_list_box.get_adjustment ();
-        adjustment.set_value (adjustment.get_upper ());
-
-        update_bookmarks_after_move ();
-    }
+        string [] old_bookmarks = settings.get_strv ("bookmarks");
+        string [] new_bookmarks = new string [0];
+        uint position = 0;
 
-    private void select_row_for_real (ListBoxRow row)   // ahem...
-    {
-        bookmarks_list_box.unselect_row (row);
-        bookmarks_list_box.select_row (row);
-    }
+        foreach (string bookmark in old_bookmarks)
+        {
+            if (index != position)
+                new_bookmarks += bookmark;
+            position++;
+        }
+        new_bookmarks += old_bookmarks [index];
 
-    /*\
-    * * callbacks
-    \*/
+        SignalHandler.block (main_list_store, content_changed_handler);
+        SignalHandler.block (settings, bookmarks_changed_handler);
+        settings.set_strv ("bookmarks", new_bookmarks);
+        GLib.Settings.sync ();   // TODO better? really needed?
+        SignalHandler.unblock (settings, bookmarks_changed_handler);
+        SignalHandler.unblock (main_list_store, content_changed_handler);
+        on_bookmarks_changed (settings, "bookmarks");
 
-    [GtkCallback]
-    private void on_selection_changed ()
-    {
-        selection_changed ();
+        Adjustment adjustment = main_list_box.get_adjustment ();
+        adjustment.set_value (adjustment.get_upper ());
     }
 
-    [GtkCallback]
-    private void on_content_changed ()
-    {
-        List<weak Widget> widgets = bookmarks_list_box.get_children ();
-        if (widgets.length () == 0)
-            edit_mode_box.hide ();
-        else
-            edit_mode_box.show ();
-    }
     /*\
     * * Bookmarks management
     \*/
 
-    private void update_bookmarks_after_move ()
+/*    private void update_bookmarks_after_move ()
     {
-        string [] new_bookmarks = get_bookmarks_list (ref bookmarks_list_box);
+        string [] new_bookmarks = get_bookmarks_list (ref main_list_box);
 
         string [] old_bookmarks = settings.get_strv ("bookmarks");  // be cool :-)
         foreach (string bookmark in old_bookmarks)
@@ -536,12 +470,12 @@ private class BookmarksList : Overlay
         GLib.Settings.sync ();   // TODO better? really needed?
         SignalHandler.unblock (settings, bookmarks_changed_handler);
     }
-    private static string [] get_bookmarks_list (ref ListBox bookmarks_list_box)
+    private static string [] get_bookmarks_list (ref ListBox main_list_box)
     {
         string [] bookmarks = new string [0];
-        bookmarks_list_box.@foreach ((widget) => { bookmarks += ((Bookmark) widget).bookmark_name; });
+        main_list_box.@foreach ((widget) => { bookmarks += ((Bookmark) widget).bookmark_name; });
         return bookmarks;
-    }
+    } */
 
     internal void append_bookmark (string bookmark, ViewType type)
     {
diff --git a/editor/bookmarks.vala b/editor/bookmarks.vala
index 4f5c031..96c1bbf 100644
--- a/editor/bookmarks.vala
+++ b/editor/bookmarks.vala
@@ -243,10 +243,10 @@ private class Bookmarks : MenuButton
     private void update_actions ()
         requires (actions_init_done)
     {
-        BookmarksList.SelectionState selection_state = bookmarks_list.get_selection_state ();
+        OverlayedList.SelectionState selection_state = bookmarks_list.get_selection_state ();
 
-        bool has_selected_items = selection_state != BookmarksList.SelectionState.EMPTY;
-        bool has_one_selected_item = has_selected_items && (selection_state != 
BookmarksList.SelectionState.MULTIPLE);
+        bool has_selected_items = selection_state != OverlayedList.SelectionState.EMPTY;
+        bool has_one_selected_item = has_selected_items && (selection_state != 
OverlayedList.SelectionState.MULTIPLE);
 
         bool enable_move_top_action     = has_one_selected_item;    // TODO has_selected_items;
         bool enable_move_up_action      = has_one_selected_item;
@@ -255,12 +255,12 @@ private class Bookmarks : MenuButton
 
         if (has_one_selected_item)
         {
-            if (selection_state == BookmarksList.SelectionState.UNIQUE || selection_state == 
BookmarksList.SelectionState.FIRST)
+            if (selection_state == OverlayedList.SelectionState.UNIQUE || selection_state == 
OverlayedList.SelectionState.FIRST)
             {
                 enable_move_top_action = false;
                 enable_move_up_action = false;
             }
-            if (selection_state == BookmarksList.SelectionState.UNIQUE || selection_state == 
BookmarksList.SelectionState.LAST)
+            if (selection_state == OverlayedList.SelectionState.UNIQUE || selection_state == 
OverlayedList.SelectionState.LAST)
             {
                 enable_move_down_action = false;
                 enable_move_bottom_action = false;
diff --git a/editor/browser-view.ui b/editor/browser-view.ui
index b9dea1b..2fc95ce 100644
--- a/editor/browser-view.ui
+++ b/editor/browser-view.ui
@@ -28,6 +28,7 @@
         <property name="edit-mode-action-prefix">bmk</property>
         <property name="schema-path">/ca/desrt/dconf-editor/</property>
         <signal name="selection-changed" handler="on_selection_changed"/>
+        <signal name="update_bookmarks_icons" handler="on_update_bookmarks_icons"/>
       </object>
     </child>
   </template>
diff --git a/editor/browser-view.vala b/editor/browser-view.vala
index 3635438..85184df 100644
--- a/editor/browser-view.vala
+++ b/editor/browser-view.vala
@@ -250,7 +250,7 @@ private class BrowserView : Stack, AdaptativeWidget
         return bookmarks_list.leave_edit_mode ();
     }
 
-    internal BookmarksList.SelectionState get_bookmarks_selection_state ()
+    internal OverlayedList.SelectionState get_bookmarks_selection_state ()
     {
         return bookmarks_list.get_selection_state ();
     }
@@ -290,6 +290,13 @@ private class BrowserView : Stack, AdaptativeWidget
 
     internal signal void bookmarks_selection_changed ();
 
+    internal signal void update_bookmarks_icons (Variant bookmarks_variant);
+    [GtkCallback]
+    private void on_update_bookmarks_icons (Variant bookmarks_variant)
+    {
+        update_bookmarks_icons (bookmarks_variant);
+    }
+
     /*\
     * * Views
     \*/
diff --git a/editor/dconf-editor.gresource.xml b/editor/dconf-editor.gresource.xml
index 510f7e3..0dc5ad0 100644
--- a/editor/dconf-editor.gresource.xml
+++ b/editor/dconf-editor.gresource.xml
@@ -5,7 +5,6 @@
     <file preprocess="xml-stripblanks">bookmark.ui</file>
     <file preprocess="xml-stripblanks">bookmarks.ui</file>
     <file preprocess="xml-stripblanks">bookmarks-controller.ui</file>
-    <file preprocess="xml-stripblanks">bookmarks-list.ui</file>
     <file preprocess="xml-stripblanks">browser-headerbar.ui</file>
     <file preprocess="xml-stripblanks">browser-infobar.ui</file>
     <file preprocess="xml-stripblanks">browser-stack.ui</file>
@@ -21,6 +20,7 @@
     <file preprocess="xml-stripblanks">large-pathbar-item.ui</file>
     <file preprocess="xml-stripblanks">modifications-list.ui</file>
     <file preprocess="xml-stripblanks">modifications-revealer.ui</file>
+    <file preprocess="xml-stripblanks">overlayed-list.ui</file>
     <file preprocess="xml-stripblanks">pathentry.ui</file>
     <file preprocess="xml-stripblanks">pathwidget.ui</file>
     <file preprocess="xml-stripblanks">property-row.ui</file>
diff --git a/editor/dconf-window.vala b/editor/dconf-window.vala
index d0d5ee2..87c909d 100644
--- a/editor/dconf-window.vala
+++ b/editor/dconf-window.vala
@@ -139,6 +139,7 @@ private class DConfWindow : ApplicationWindow
         install_bmk_action_entries ();
 
         headerbar.update_bookmarks_icons.connect (update_bookmarks_icons_from_variant);
+        browser_view.update_bookmarks_icons.connect (update_bookmarks_icons_from_variant);
 
         use_shortpaths_changed_handler = settings.changed ["use-shortpaths"].connect_after (reload_view);
         settings.bind ("use-shortpaths", model, "use-shortpaths", 
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
@@ -889,10 +890,10 @@ private class DConfWindow : ApplicationWindow
     private void update_actions ()
         requires (actions_init_done)
     {
-        BookmarksList.SelectionState selection_state = browser_view.get_bookmarks_selection_state ();
+        OverlayedList.SelectionState selection_state = browser_view.get_bookmarks_selection_state ();
 
-        bool has_selected_items = selection_state != BookmarksList.SelectionState.EMPTY;
-        bool has_one_selected_item = has_selected_items && (selection_state != 
BookmarksList.SelectionState.MULTIPLE);
+        bool has_selected_items = selection_state != OverlayedList.SelectionState.EMPTY;
+        bool has_one_selected_item = has_selected_items && (selection_state != 
OverlayedList.SelectionState.MULTIPLE);
 
         bool enable_move_top_action     = has_one_selected_item;    // TODO has_selected_items;
         bool enable_move_up_action      = has_one_selected_item;
@@ -901,12 +902,12 @@ private class DConfWindow : ApplicationWindow
 
         if (has_one_selected_item)
         {
-            if (selection_state == BookmarksList.SelectionState.UNIQUE || selection_state == 
BookmarksList.SelectionState.FIRST)
+            if (selection_state == OverlayedList.SelectionState.UNIQUE || selection_state == 
OverlayedList.SelectionState.FIRST)
             {
                 enable_move_top_action = false;
                 enable_move_up_action = false;
             }
-            if (selection_state == BookmarksList.SelectionState.UNIQUE || selection_state == 
BookmarksList.SelectionState.LAST)
+            if (selection_state == OverlayedList.SelectionState.UNIQUE || selection_state == 
OverlayedList.SelectionState.LAST)
             {
                 enable_move_down_action = false;
                 enable_move_bottom_action = false;
diff --git a/editor/meson.build b/editor/meson.build
index 3062cbc..e1b3b19 100644
--- a/editor/meson.build
+++ b/editor/meson.build
@@ -86,6 +86,7 @@ sources = files(
   'modifications-list.vala',
   'modifications-revealer.vala',
   'night-light-monitor.vala',
+  'overlayed-list.vala',
   'pathentry.vala',
   'pathwidget.vala',
   'registry-info.vala',
@@ -103,7 +104,6 @@ resource_data = files(
   'adaptative-pathbar.ui',
   'bookmarks.ui',
   'bookmarks-controller.ui',
-  'bookmarks-list.ui',
   'bookmark.ui',
   'browser-headerbar.ui',
   'browser-infobar.ui',
@@ -121,6 +121,7 @@ resource_data = files(
   'large-pathbar.ui',
   'modifications-list.ui',
   'modifications-revealer.ui',
+  'overlayed-list.ui',
   'pathentry.ui',
   'pathwidget.ui',
   'property-row.ui',
diff --git a/editor/bookmarks-list.ui b/editor/overlayed-list.ui
similarity index 75%
rename from editor/bookmarks-list.ui
rename to editor/overlayed-list.ui
index 7b64085..e95ab94 100644
--- a/editor/bookmarks-list.ui
+++ b/editor/overlayed-list.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface domain="dconf-editor">
   <!-- interface-requires gtk+ 3.0 -->
-  <template class="BookmarksList" parent="GtkOverlay">
+  <template class="OverlayedList" parent="GtkOverlay">
     <property name="expand">True</property>
     <property name="valign">fill</property>
     <child>
@@ -14,20 +14,12 @@
         <property name="propagate-natural-width">True</property>
         <property name="propagate-natural-height">True</property>
         <child>
-          <object class="GtkListBox" id="bookmarks_list_box">
+          <object class="GtkListBox" id="main_list_box">
             <property name="visible">True</property>
             <signal name="selected-rows-changed" handler="on_selection_changed"/>
-            <signal name="add" handler="on_content_changed"/>
-            <signal name="remove" handler="on_content_changed"/>
             <style>
               <class name="padding-bottom"/>
             </style>
-            <child type="placeholder">
-              <object class="RegistryPlaceholder" id="placeholder">
-                <property name="label" translatable="yes">Bookmarks will&#xA;be added here</property>
-                <property name="icon-name">starred-symbolic</property> <!-- or starred-symbolic? or 
dconf-editor-symbolic? -->
-              </object>
-            </child>
           </object>
         </child>
       </object>
@@ -49,7 +41,6 @@
             <property name="centered">True</property>
             <property name="iconic">True</property>
             <property name="focus-on-click">False</property>
-            <property name="text" translatable="yes">Use</property>
             <style>
               <class name="left-on-ltr"/>
             </style>
@@ -62,7 +53,6 @@
             <property name="centered">True</property>
             <property name="iconic">True</property>
             <property name="focus-on-click">False</property>
-            <property name="text" translatable="yes">Edit</property>
             <style>
               <class name="right-on-ltr"/>
             </style>
diff --git a/editor/overlayed-list.vala b/editor/overlayed-list.vala
new file mode 100644
index 0000000..8c9f367
--- /dev/null
+++ b/editor/overlayed-list.vala
@@ -0,0 +1,232 @@
+/*
+  This file is part of Dconf Editor
+
+  Dconf Editor is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  Dconf Editor is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with Dconf Editor.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+[GtkTemplate (ui = "/ca/desrt/dconf-editor/ui/overlayed-list.ui")]
+private abstract class OverlayedList : Overlay
+{
+    [GtkChild] protected ListBox        main_list_box;
+               private StyleContext     main_list_box_context;
+               protected GLib.ListStore main_list_store = new GLib.ListStore (typeof (Widget));
+
+    [GtkChild] protected ScrolledWindow scrolled;
+    [GtkChild] private   Box            edit_mode_box;
+
+    /*\
+    * * differed construct
+    \*/
+
+    construct
+    {
+        main_list_box_context = main_list_box.get_style_context ();
+        connect_handlers ();
+        main_list_box.bind_model (main_list_store, create_rows);
+    }
+
+    private Widget create_rows (Object item)
+    {
+        return (Widget) item;
+    }
+
+
+    [GtkChild] private ModelButton enter_edit_mode_button;
+    [GtkChild] private ModelButton leave_edit_mode_button;
+    public string edit_mode_action_prefix
+    {
+        construct
+        {
+            // TODO sanitize "value"
+            enter_edit_mode_button.set_detailed_action_name (value + ".set-edit-mode(true)");
+            leave_edit_mode_button.set_detailed_action_name (value + ".set-edit-mode(false)");
+        }
+    }
+    public string first_mode_name   { protected set { leave_edit_mode_button.text = value; }}
+    public string second_mode_name  { protected set { enter_edit_mode_button.text = value; }}
+
+    public bool needs_shadows
+    {
+        construct
+        {
+            if (value)
+                scrolled.shadow_type = ShadowType.ETCHED_IN;
+            else
+                scrolled.shadow_type = ShadowType.NONE;
+        }
+    }
+
+    protected string placeholder_icon;
+    protected string placeholder_text;
+    public bool big_placeholder { private get; internal construct; }
+    protected void add_placeholder ()
+    {
+        RegistryPlaceholder placeholder = new RegistryPlaceholder (placeholder_icon, placeholder_text, 
big_placeholder);
+        main_list_box.set_placeholder (placeholder);
+    }
+
+    /*\
+    * * keyboard
+    \*/
+
+    internal void down_pressed ()
+    {
+        ListBoxRow? row = main_list_box.get_selected_row ();
+        if (row == null)
+            row = main_list_box.get_row_at_index (0);
+        else
+            row = main_list_box.get_row_at_index (((!) row).get_index () + 1);
+
+        if (row == null)
+            return;
+        main_list_box.select_row ((!) row);
+        ((!) row).grab_focus ();
+    }
+
+    internal void up_pressed ()
+    {
+        if (n_items == 0)
+            return;
+
+        ListBoxRow? row = main_list_box.get_selected_row ();
+        if (row == null)
+            row = main_list_box.get_row_at_index ((int) n_items - 1);
+        else
+        {
+            int index = ((!) row).get_index ();
+            if (index <= 0)
+                return;
+            row = main_list_box.get_row_at_index (index - 1);
+        }
+
+        if (row == null)
+            assert_not_reached ();
+
+        main_list_box.select_row ((!) row);
+        ((!) row).grab_focus ();
+    }
+
+    internal void select_all ()
+    {
+        main_list_box.select_all ();
+    }
+
+    internal void unselect_all ()
+    {
+        main_list_box.unselect_all ();
+    }
+
+    protected void select_row_for_real (ListBoxRow row)   // ahem...
+    {
+        main_list_box.unselect_row (row);
+        main_list_box.select_row (row);
+    }
+
+    /*\
+    * * selection state
+    \*/
+
+    internal signal void selection_changed ();
+
+    [GtkCallback]
+    private void on_selection_changed ()
+    {
+        selection_changed ();
+    }
+
+    internal enum SelectionState {
+        EMPTY,
+        UNIQUE,
+        FIRST,
+        LAST,
+        MIDDLE,
+        MULTIPLE
+    }
+
+    internal SelectionState get_selection_state ()
+    {
+        List<weak ListBoxRow> selected_rows = main_list_box.get_selected_rows ();
+        uint n_selected_rows = selected_rows.length ();
+
+        if (n_selected_rows == 0)
+            return SelectionState.EMPTY;
+        if (n_selected_rows >= 2)
+            return SelectionState.MULTIPLE;
+
+        int index = selected_rows.nth_data (0).get_index ();
+        bool is_first = index == 0;
+        bool is_last = main_list_box.get_row_at_index (index + 1) == null;
+        if (is_first && is_last)
+            return SelectionState.UNIQUE;
+        if (is_first)
+            return SelectionState.FIRST;
+        if (is_last)
+            return SelectionState.LAST;
+        return SelectionState.MIDDLE;
+    }
+
+    /*\
+    * * overlay visibility
+    \*/
+
+    protected ulong content_changed_handler = 0;
+
+    protected uint n_items { protected get; private set; default = 0; }
+    private bool is_editable = true;
+
+    protected void change_editability (bool new_value)
+    {
+        is_editable = new_value;
+        update_edit_mode_box_visibility ();
+    }
+
+    private void connect_handlers ()   // connect and disconnect manually or bad things happen on destroy
+    {
+        content_changed_handler = main_list_store.items_changed.connect (on_content_changed);
+
+        destroy.connect (() => {
+                main_list_store.disconnect (content_changed_handler);
+            });
+    }
+
+    private void on_content_changed (GLib.ListModel main_list_model, uint position, uint removed, uint added)
+    {
+        n_items += added;
+        n_items -= removed;
+        update_has_empty_list_class (n_items == 0);
+        update_edit_mode_box_visibility ();
+    }
+
+    private bool has_empty_list_class = false;
+    private void update_has_empty_list_class (bool list_is_empty)
+    {
+        if (list_is_empty && !has_empty_list_class)
+        {
+            main_list_box_context.add_class ("empty-list");
+            has_empty_list_class = true;
+        }
+        else if (!list_is_empty && has_empty_list_class)
+        {
+            has_empty_list_class = false;
+            main_list_box_context.remove_class ("empty-list");
+        }
+    }
+
+    private inline void update_edit_mode_box_visibility ()
+    {
+        edit_mode_box.visible = is_editable && n_items != 0;
+    }
+}
diff --git a/editor/registry-placeholder.vala b/editor/registry-placeholder.vala
index dc7b181..6e9f06d 100644
--- a/editor/registry-placeholder.vala
+++ b/editor/registry-placeholder.vala
@@ -46,4 +46,9 @@ private class RegistryPlaceholder : Grid
     {
         placeholder_image.icon_name = icon_name;
     }
+
+    internal RegistryPlaceholder (string _icon_name, string _label, bool _big)
+    {
+        Object (icon_name:_icon_name, label: _label, big: _big);
+    }
 }



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