[dconf-editor] Introduce DConfView.



commit 00484d833a15817cacba5b8be5d6570c6ac4a053
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Mon Dec 17 18:19:36 2018 +0100

    Introduce DConfView.

 editor/browser-view.vala     |  403 ++--------------
 editor/browser-window.vala   |    4 +-
 editor/dconf-view.vala       | 1059 +++++++++++-------------------------------
 editor/dconf-window.vala     |  125 ++---
 editor/key-editor-child.vala |  950 +++++++++++++++++++++++++++++++++++++
 editor/key-list-box-row.vala |    4 +-
 editor/meson.build           |    1 +
 editor/registry-info.ui      |    2 +-
 editor/registry-list.vala    |   10 +-
 9 files changed, 1331 insertions(+), 1227 deletions(-)
---
diff --git a/editor/browser-view.vala b/editor/browser-view.vala
index b4f4eed..ce89edb 100644
--- a/editor/browser-view.vala
+++ b/editor/browser-view.vala
@@ -50,36 +50,22 @@ private class SimpleSettingObject : Object
 
 private class BrowserView : BaseView, AdaptativeWidget
 {
-    [CCode (notify = false)] internal uint16 last_context_id { get; private set; default = 
ModelUtils.undefined_context_id; }
+    [CCode (notify = false)] internal uint16 last_context_id { internal get; private set; default = 
ModelUtils.undefined_context_id; }
 
-    private BrowserInfoBar  info_bar;
-    private BrowserStack    current_child;
+    [CCode (notify = false)] public BrowserContent browser_content { private get; construct; }
+
+    protected BrowserInfoBar info_bar;
+    protected SortingOptions sorting_options;
 
-    private SortingOptions sorting_options;
     private GLib.ListStore? key_model = null;
 
     protected override void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
         base.set_window_size (new_size);
-        current_child.set_window_size (new_size);
-        bookmarks_list.set_window_size (new_size);
-        if (modifications_list_created)
-            modifications_list.set_window_size (new_size);
+        browser_content.set_window_size (new_size);
     }
 
-    private ModificationsHandler _modifications_handler;
-    [CCode (notify = false)] internal ModificationsHandler modifications_handler
-    {
-        private get { return _modifications_handler; }
-        set {
-            _modifications_handler = value;
-            current_child.modifications_handler = value;
-            sorting_options = new SortingOptions (value.model);
-            sorting_options.notify ["case-sensitive"].connect (on_case_sensitive_changed);
-            _modifications_handler.delayed_changes_changed.connect (update_in_window_modifications);
-        }
-    }
-    private void on_case_sensitive_changed ()
+    protected void on_case_sensitive_changed ()
     {
         if (current_view != ViewType.FOLDER)
             return;
@@ -103,34 +89,7 @@ private class BrowserView : BaseView, AdaptativeWidget
         info_bar.show ();
         main_grid.add (info_bar);
 
-        current_child = new BrowserStack ();
-        current_child.show ();
-        main_grid.add (current_child);
-
-        create_bookmarks_list ();
-    }
-
-    internal override bool is_in_in_window_mode ()
-    {
-        return (in_window_bookmarks || in_window_modifications || base.is_in_in_window_mode ());
-    }
-
-    internal override void show_default_view ()
-    {
-        if (in_window_bookmarks)
-        {
-            if (in_window_bookmarks_edit_mode)
-                leave_bookmarks_edit_mode ();
-            in_window_bookmarks = false;
-            set_visible_child_name ("main-view");
-        }
-        else if (in_window_modifications)
-        {
-            in_window_modifications = false;
-            set_visible_child_name ("main-view");
-        }
-        else
-            base.show_default_view ();
+        main_grid.add (browser_content);
     }
 
     /*\
@@ -146,14 +105,7 @@ private class BrowserView : BaseView, AdaptativeWidget
 
     private const GLib.ActionEntry [] action_entries =
     {
-        { "refresh-folder", refresh_folder },
-
-        { "set-key-value",               set_key_value,                 "(sqv)"  },
-        { "set-to-default",              set_to_default,                "(sq)"   },
-        { "delay-erase",                 delay_erase,                   "s"      },  // see also ui.erase(s)
-
-        { "toggle-dconf-key-switch",     toggle_dconf_key_switch,       "(sb)"   },
-        { "toggle-gsettings-key-switch", toggle_gsettings_key_switch,   "(sqbb)" }
+        { "refresh-folder", refresh_folder }
     };
 
     private void refresh_folder (/* SimpleAction action, Variant? path_variant */)
@@ -163,227 +115,6 @@ private class BrowserView : BaseView, AdaptativeWidget
         hide_reload_warning ();
     }
 
-    private void set_key_value (SimpleAction action, Variant? value_variant)
-        requires (value_variant != null)
-    {
-        string full_name;
-        uint16 context_id;
-        Variant key_value_request;
-        ((!) value_variant).@get ("(sqv)", out full_name, out context_id, out key_value_request);
-
-        if (modifications_handler.get_current_delay_mode ())
-            modifications_handler.add_delayed_setting (full_name, key_value_request, context_id);
-        else if (!ModelUtils.is_dconf_context_id (context_id))
-            modifications_handler.set_gsettings_key_value (full_name, context_id, key_value_request);
-        else
-            modifications_handler.set_dconf_key_value (full_name, key_value_request);
-    }
-
-    private void delay_erase (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
-    {
-        string full_name_or_empty = ((!) path_variant).get_string ();
-        if (full_name_or_empty == "")
-            return;
-        modifications_handler.enter_delay_mode ();
-        modifications_handler.add_delayed_setting (full_name_or_empty, null, ModelUtils.dconf_context_id);
-    }
-
-    private void set_to_default (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
-    {
-        string full_name;
-        uint16 context_id;
-        ((!) path_variant).@get ("(sq)", out full_name, out context_id);
-        modifications_handler.set_to_default (full_name, context_id);
-        invalidate_popovers ();
-    }
-
-    private void toggle_dconf_key_switch (SimpleAction action, Variant? value_variant)
-        requires (value_variant != null)
-    {
-        if (modifications_handler.get_current_delay_mode ())
-            assert_not_reached ();
-
-        string full_name;
-        bool key_value_request;
-        ((!) value_variant).@get ("(sb)", out full_name, out key_value_request);
-
-        modifications_handler.set_dconf_key_value (full_name, key_value_request);
-    }
-
-    private void toggle_gsettings_key_switch (SimpleAction action, Variant? value_variant)
-        requires (value_variant != null)
-    {
-        if (modifications_handler.get_current_delay_mode ())
-            assert_not_reached ();
-
-        string full_name;
-        uint16 context_id;
-        bool key_value_request;
-        bool key_default_value;
-        ((!) value_variant).@get ("(sqbb)", out full_name, out context_id, out key_value_request, out 
key_default_value);
-
-        if (key_value_request == key_default_value)
-            modifications_handler.set_to_default (full_name, context_id);
-        else
-            modifications_handler.set_gsettings_key_value (full_name, context_id, new Variant.boolean 
(key_value_request));
-    }
-
-    /*\
-    * * modifications
-    \*/
-
-    [CCode (notify = false)] internal bool in_window_modifications           { internal get; private set; 
default = false; }
-
-    private bool modifications_list_created = false;
-    private ModificationsList modifications_list;
-
-    private void create_modifications_list ()
-    {
-        modifications_list = new ModificationsList (/* needs shadows   */ false,
-                                                    /* big placeholder */ true);
-        modifications_list.set_window_size (saved_window_size);
-        // modifications_list.selection_changed.connect (() => ...);
-        modifications_list.show ();
-        add (modifications_list);
-        modifications_list_created = true;
-    }
-
-    internal void show_modifications_view ()
-        requires (modifications_list_created == true)
-    {
-        if (in_window_bookmarks || in_window_about)
-            show_default_view ();
-
-        modifications_list.reset ();
-
-        set_visible_child (modifications_list);
-        in_window_modifications = true;
-    }
-
-    private void update_in_window_modifications ()
-    {
-        if (!modifications_list_created)
-            create_modifications_list ();
-
-        GLib.ListStore modifications_liststore = modifications_handler.get_delayed_settings ();
-        modifications_list.bind_model (modifications_liststore, delayed_setting_row_create);
-
-        if (in_window_modifications && modifications_handler.mode == ModificationsMode.NONE)
-            show_default_view ();
-    }
-    private Widget delayed_setting_row_create (Object object)
-    {
-        SimpleSettingObject sso = (SimpleSettingObject) object;
-        return ModificationsRevealer.create_delayed_setting_row (modifications_handler, sso.name, 
sso.full_name, sso.context_id);
-    }
-
-    /*\
-    * * bookmarks
-    \*/
-
-    [CCode (notify = false)] internal bool in_window_bookmarks           { internal get; private set; 
default = false; }
-    [CCode (notify = false)] internal bool in_window_bookmarks_edit_mode { internal get; private set; 
default = false; }
-
-    private BookmarksList bookmarks_list;
-
-    private void create_bookmarks_list ()
-    {
-        bookmarks_list = new BookmarksList (/* needs shadows            */ false,
-                                            /* big placeholder          */ true,
-                                            /* edit-mode action prefix  */ "bmk",
-                                            /* schema path              */ "/ca/desrt/dconf-editor/");
-        bookmarks_list.selection_changed.connect (on_bookmarks_selection_changed);
-        bookmarks_list.update_bookmarks_icons.connect (on_update_bookmarks_icons);
-        bookmarks_list.show ();
-        add (bookmarks_list);
-    }
-
-    private string [] old_bookmarks = new string [0];
-
-    internal void show_bookmarks_view (string [] bookmarks)
-    {
-        if (in_window_modifications || in_window_about)
-            show_default_view ();
-
-        bookmarks_list.reset ();
-
-        if (bookmarks != old_bookmarks)
-        {
-            Variant variant = new Variant.strv (bookmarks);
-            bookmarks_list.create_bookmark_rows (variant);
-
-            old_bookmarks = bookmarks;
-        }
-        set_visible_child (bookmarks_list);
-        in_window_bookmarks = true;
-    }
-
-    internal void update_bookmark_icon (string bookmark, BookmarkIcon icon)
-    {
-        bookmarks_list.update_bookmark_icon (bookmark, icon);
-    }
-
-    internal void enter_bookmarks_edit_mode ()
-        requires (in_window_bookmarks == true)
-    {
-        bookmarks_list.enter_edit_mode ();
-        in_window_bookmarks_edit_mode = true;
-    }
-
-    internal bool leave_bookmarks_edit_mode ()
-        requires (in_window_bookmarks == true)
-    {
-        in_window_bookmarks_edit_mode = false;
-        return bookmarks_list.leave_edit_mode ();
-    }
-
-    internal OverlayedList.SelectionState get_bookmarks_selection_state ()
-    {
-        return bookmarks_list.get_selection_state ();
-    }
-
-    internal void trash_bookmark ()
-    {
-        bookmarks_list.trash_bookmark ();
-    }
-
-    internal void move_top ()
-    {
-        bookmarks_list.move_top ();
-    }
-
-    internal void move_up ()
-    {
-        bookmarks_list.move_up ();
-    }
-
-    internal void move_down ()
-    {
-        bookmarks_list.move_down ();
-    }
-
-    internal void move_bottom ()
-    {
-        bookmarks_list.move_bottom ();
-    }
-
-    private void on_bookmarks_selection_changed ()
-    {
-        if (!in_window_bookmarks)
-            return;
-        bookmarks_selection_changed ();
-    }
-
-    internal signal void bookmarks_selection_changed ();
-
-    internal signal void update_bookmarks_icons (Variant bookmarks_variant);
-    private void on_update_bookmarks_icons (Variant bookmarks_variant)
-    {
-        update_bookmarks_icons (bookmarks_variant);
-    }
-
     /*\
     * * Views
     \*/
@@ -393,27 +124,26 @@ private class BrowserView : BaseView, AdaptativeWidget
         key_model = _key_model;
         sorting_options.sort_key_model ((!) key_model);
 
-        current_child.prepare_folder_view ((!) key_model, is_ancestor);
+        browser_content.prepare_folder_view ((!) key_model, is_ancestor);
         hide_reload_warning ();
     }
 
     internal void select_row (string selected)
         requires (ViewType.displays_objects_list (current_view))
     {
-        current_child.select_row (selected, last_context_id, !is_in_in_window_mode ());
+        browser_content.select_row (selected, last_context_id, !is_in_in_window_mode ());
     }
 
     internal void prepare_object_view (string full_name, uint16 context_id, Variant properties, bool 
is_parent)
     {
-        current_child.prepare_object_view (full_name, context_id, properties, is_parent);
+        browser_content.prepare_object_view (full_name, context_id, properties, is_parent);
         hide_reload_warning ();
         last_context_id = context_id;
     }
 
-    internal void set_path (ViewType type, string path)
+    internal virtual void set_path (ViewType type, string path)
     {
-        current_child.set_path (type, path);
-        modifications_handler.path_changed ();
+        browser_content.set_path (type, path);
         invalidate_popovers ();
     }
 
@@ -421,7 +151,7 @@ private class BrowserView : BaseView, AdaptativeWidget
     * * Reload
     \*/
 
-    private void hide_reload_warning ()
+    protected void hide_reload_warning ()
     {
         info_bar.hide_warning ();
     }
@@ -432,127 +162,54 @@ private class BrowserView : BaseView, AdaptativeWidget
             info_bar.show_warning ("soft-reload-folder");
     }
 
-    internal void set_search_parameters (string current_path, string [] bookmarks)
-    {
-        hide_reload_warning ();
-        current_child.set_search_parameters (current_path, last_context_id, bookmarks, sorting_options);
-    }
-
-    internal bool check_reload (ViewType type, string path, bool show_infobar)
-    {
-        SettingsModel model = modifications_handler.model;
-
-        if (type == ViewType.FOLDER || (type == ViewType.CONFIG && ModelUtils.is_folder_path (path)))
-        {
-            if (!current_child.check_reload_folder (model.get_children (path)))
-                return false;
-            if (show_infobar)
-            {
-                info_bar.show_warning ("hard-reload-folder");
-                return false;
-            }
-        }
-        else if (type == ViewType.OBJECT || type == ViewType.CONFIG)
-        {
-            if (model.key_exists (path, last_context_id))
-            {
-                RegistryVariantDict properties = new RegistryVariantDict.from_aqv (model.get_key_properties 
(path, last_context_id, (uint16) PropertyQuery.HASH));
-                uint properties_hash;
-                if (!properties.lookup (PropertyQuery.HASH, "u", out properties_hash))
-                    assert_not_reached ();
-                if (!current_child.check_reload_object (properties_hash))
-                    return false;
-            }
-            if (show_infobar)
-            {
-                info_bar.show_warning ("hard-reload-object");
-                return false;
-            }
-        }
-        else if (type == ViewType.SEARCH)
-            assert_not_reached ();
-        else
-            assert_not_reached ();
-        return true;
-    }
-
     /*\
     * * Proxy calls
     \*/
 
     internal void row_grab_focus ()
     {
-        current_child.row_grab_focus ();
+        browser_content.row_grab_focus ();
     }
 
-    [CCode (notify = false)] internal ViewType current_view { get { return current_child.current_view; }}
+    [CCode (notify = false)] internal ViewType current_view { get { return browser_content.current_view; }}
 
-    // popovers invalidation and toggles hiding/revealing
-    internal override void close_popovers () { current_child.discard_row_popover (); }
-    internal void invalidate_popovers ()     { current_child.invalidate_popovers (); }
-
-    internal void hide_or_show_toggles (bool show) { current_child.hide_or_show_toggles (show); }
+    // popovers invalidation
+    internal override void close_popovers () { browser_content.discard_row_popover (); }
+    internal void invalidate_popovers ()     { browser_content.invalidate_popovers (); }
 
     // keyboard
-    internal bool return_pressed ()   { return current_child.return_pressed ();   }
-    internal bool next_match ()
+    internal bool return_pressed ()   { return browser_content.return_pressed ();   }
+    internal virtual bool next_match ()
     {
-        if (in_window_bookmarks)
-            return bookmarks_list.next_match ();
-        if (in_window_modifications)
-            return modifications_list.next_match ();
         if (in_window_about)
             return false;       // TODO scroll down at last line
         else
-            return current_child.next_match ();
+            return browser_content.next_match ();
     }
-    internal bool previous_match ()
+    internal virtual bool previous_match ()
     {
-        if (in_window_bookmarks)
-            return bookmarks_list.previous_match ();
-        if (in_window_modifications)
-            return modifications_list.previous_match ();
         if (in_window_about)
             return false;
         else
-            return current_child.previous_match ();
+            return browser_content.previous_match ();
     }
 
-    internal bool toggle_row_popover ()     // Menu
+    internal virtual bool toggle_row_popover ()     // Menu
     {
-        if (in_window_bookmarks)
-            return false;
-        return current_child.toggle_row_popover ();
+        return browser_content.toggle_row_popover ();
     }
 
-    internal void toggle_boolean_key ()      { current_child.toggle_boolean_key ();      }
-    internal void set_selected_to_default () { current_child.set_selected_to_default (); }
-
     // current row property
-    internal string get_selected_row_name () { return current_child.get_selected_row_name (); }
+    internal string get_selected_row_name () { return browser_content.get_selected_row_name (); }
     internal override bool handle_copy_text (out string copy_text)
     {
-        if (in_window_bookmarks)
-            return bookmarks_list.handle_copy_text (out copy_text);
-        if (in_window_modifications)
-            return modifications_list.handle_copy_text (out copy_text);
         if (base.handle_copy_text (out copy_text))
             return true;
-        return current_child.handle_copy_text (out copy_text);
+        return browser_content.handle_copy_text (out copy_text);
     }
     internal bool handle_alt_copy_text (out string copy_text)
     {
-        return current_child.handle_alt_copy_text (out copy_text);
-    }
-
-    // values changes
-    internal void gkey_value_push (string full_name, uint16 context_id, Variant key_value, bool 
is_key_default)
-    {
-        current_child.gkey_value_push (full_name, context_id, key_value, is_key_default);
-    }
-    internal void dkey_value_push (string full_name, Variant? key_value_or_null)
-    {
-        current_child.dkey_value_push (full_name, key_value_or_null);
+        return browser_content.handle_alt_copy_text (out copy_text);
     }
 }
 
diff --git a/editor/browser-window.vala b/editor/browser-window.vala
index 0df217b..caa4e0b 100644
--- a/editor/browser-window.vala
+++ b/editor/browser-window.vala
@@ -258,7 +258,7 @@ private abstract class BrowserWindow : BaseWindow
         if (reload)
         {
             reload_search_action.set_enabled (false);
-            browser_view.set_search_parameters (saved_view, ((DConfHeaderBar) headerbar).get_bookmarks ());
+            reconfigure_search ();
             reload_search_next = false;
         }
         if (mode != PathEntry.SearchMode.UNCLEAR)
@@ -271,6 +271,8 @@ private abstract class BrowserWindow : BaseWindow
             headerbar.entry_grab_focus (false);
     }
 
+    protected abstract void reconfigure_search ();
+
     /*\
     * * window state
     \*/
diff --git a/editor/dconf-view.vala b/editor/dconf-view.vala
index 8a5b8fa..f255d9c 100644
--- a/editor/dconf-view.vala
+++ b/editor/dconf-view.vala
@@ -17,934 +17,423 @@
 
 using Gtk;
 
-private interface KeyEditorChild : Widget
+private class DConfView : BrowserView, AdaptativeWidget
 {
-    internal signal void value_has_changed (bool is_valid);
+    private BrowserStack dconf_content;
 
-    internal abstract Variant get_variant ();
-    internal signal void child_activated ();
-
-    internal abstract void reload (Variant gvariant);
-
-    protected const string out_of_range_text = _("Given value is out of range.");
-
-    /*\
-    * * for entries and textviews
-    \*/
-
-    protected static void set_lock_on (Object buffer, ulong deleted_text_handler, ulong 
inserted_text_handler)
-        requires (deleted_text_handler != 0)
-        requires (inserted_text_handler != 0)
-    {
-        SignalHandler.block (buffer, deleted_text_handler);
-        SignalHandler.block (buffer, inserted_text_handler);
-    }
-    protected static void set_lock_off (Object buffer, ulong deleted_text_handler, ulong 
inserted_text_handler)
-        requires (deleted_text_handler != 0)
-        requires (inserted_text_handler != 0)
+    construct
     {
-        SignalHandler.unblock (buffer, deleted_text_handler);
-        SignalHandler.unblock (buffer, inserted_text_handler);
-    }
-}
+        install_action_entries ();
 
-private class KeyEditorChildSingle : Label, KeyEditorChild
-{
-    private Variant variant;
-
-    internal KeyEditorChildSingle (Variant key_value, string text)
-    {
-        variant = key_value;
-        set_label (text);
-        show ();
+        create_bookmarks_list ();
     }
 
-    internal Variant get_variant ()
+    internal DConfView ()
     {
-        return variant;
+        BrowserStack _dconf_content = new BrowserStack ();
+        _dconf_content.show ();
+        Object (browser_content: (BrowserContent) _dconf_content);
+        dconf_content = _dconf_content;
     }
 
-    internal void reload (Variant gvariant) {}
-}
-
-private class KeyEditorChildEnum : MenuButton, KeyEditorChild
-{
-    private Variant variant;
-    private GLib.Action action;
-
-    internal KeyEditorChildEnum (Variant initial_value, bool delay_mode, bool has_planned_change, Variant 
range_content)
+    protected override void set_window_size (AdaptativeWidget.WindowSize new_size)
     {
-        this.visible = true;
-        this.hexpand = true;
-        this.halign = Align.START;
-        this.use_popover = true;
-        this.width_request = 100;
-
-        ContextPopover popover = new ContextPopover ();
-        action = popover.create_buttons_list (false, delay_mode, has_planned_change, "<enum>", 
range_content, initial_value);
-        popover.set_relative_to (this);
-
-        popover.value_changed.connect (on_popover_value_changed);
-        reload (initial_value);
-        this.set_popover ((Popover) popover);
-    }
-    private void on_popover_value_changed (ContextPopover _popover, Variant? gvariant)
-        requires (gvariant != null)
-    {
-        reload ((!) gvariant);
-        _popover.closed ();
-
-        value_has_changed (true);
+        base.set_window_size (new_size);
+        bookmarks_list.set_window_size (new_size);
+        if (modifications_list_created)
+            modifications_list.set_window_size (new_size);
     }
 
-    internal Variant get_variant ()
+    private ModificationsHandler _modifications_handler;
+    [CCode (notify = false)] internal ModificationsHandler modifications_handler
     {
-        return variant;
+        private get { return _modifications_handler; }
+        set
+        {
+            _modifications_handler = value;
+            dconf_content.modifications_handler = value;
+            sorting_options = new SortingOptions (value.model);
+            sorting_options.notify ["case-sensitive"].connect (on_case_sensitive_changed);
+            _modifications_handler.delayed_changes_changed.connect (update_in_window_modifications);
+        }
     }
 
-    internal void reload (Variant gvariant)
+    internal override bool is_in_in_window_mode ()
     {
-        variant = gvariant;
-        VariantType type = gvariant.get_type ();
-        label = type == VariantType.STRING ? gvariant.get_string () : gvariant.print (false);
-        action.change_state (new Variant.maybe (null, new Variant.maybe (type, gvariant)));
+        return (in_window_bookmarks || in_window_modifications || base.is_in_in_window_mode ());
     }
-}
 
-private class KeyEditorChildFlags : Grid, KeyEditorChild
-{
-    private string [] all_flags;
-    private ContextPopover popover = new ContextPopover ();
-
-    private Variant variant;
-    private Label label = new Label ("");
-
-    internal KeyEditorChildFlags (Variant initial_value, string [] _all_flags)
-    {
-        all_flags = _all_flags;
-        this.visible = true;
-        this.hexpand = true;
-        this.orientation = Orientation.HORIZONTAL;
-        this.column_spacing = 8;
-
-        MenuButton button = new MenuButton ();  // TODO change icon when popover will go up
-        button.visible = true;
-        button.use_popover = true;
-        button.halign = Align.START;
-        button.get_style_context ().add_class ("image-button");
-        this.add (button);
-
-        label.visible = true;
-        label.halign = Align.START;
-        label.hexpand = true;
-        this.add (label);
-
-        popover.create_flags_list (initial_value.get_strv (), all_flags);
-        popover.set_relative_to (button);
-        popover.value_changed.connect (on_popover_value_changed);
-        reload (initial_value);
-        button.set_popover ((Popover) popover);
-    }
-    private void on_popover_value_changed (Popover _popover, Variant? gvariant)
-        requires (gvariant != null)
+    internal override void show_default_view ()
     {
-        reload ((!) gvariant);
-        value_has_changed (true);
+        if (in_window_bookmarks)
+        {
+            if (in_window_bookmarks_edit_mode)
+                leave_bookmarks_edit_mode ();
+            in_window_bookmarks = false;
+            set_visible_child_name ("main-view");
+        }
+        else if (in_window_modifications)
+        {
+            in_window_modifications = false;
+            set_visible_child_name ("main-view");
+        }
+        else
+            base.show_default_view ();
     }
 
-    internal void update_flags (string [] active_flags)
-    {
-        foreach (string flag in all_flags)
-            popover.update_flag_status (flag, flag in active_flags);
-    }
+    /*\
+    * * Action entries
+    \*/
 
-    internal Variant get_variant ()
+    private void install_action_entries ()
     {
-        return variant;
+        SimpleActionGroup action_group = new SimpleActionGroup ();
+        action_group.add_action_entries (action_entries, this);
+        insert_action_group ("view", action_group);
     }
 
-    internal void reload (Variant gvariant)
+    private const GLib.ActionEntry [] action_entries =
     {
-        this.variant = gvariant;
-        label.label = gvariant.print (false);
-    }
-}
+        { "set-key-value",               set_key_value,                 "(sqv)"  },
+        { "set-to-default",              set_to_default,                "(sq)"   },
+        { "delay-erase",                 delay_erase,                   "s"      },  // see also ui.erase(s)
 
-private class KeyEditorChildNullableBool : MenuButton, KeyEditorChild
-{
-    private Variant variant;
-    private Variant? maybe_variant;
-    private GLib.Action action;
+        { "toggle-dconf-key-switch",     toggle_dconf_key_switch,       "(sb)"   },
+        { "toggle-gsettings-key-switch", toggle_gsettings_key_switch,   "(sqbb)" }
+    };
 
-    internal KeyEditorChildNullableBool (Variant initial_value, bool delay_mode, bool has_planned_change, 
bool has_schema)
+    private void set_key_value (SimpleAction action, Variant? value_variant)
+        requires (value_variant != null)
     {
-        this.visible = true;
-        this.hexpand = true;
-        this.halign = Align.START;
-        this.use_popover = true;
-        this.width_request = 100;
-
-        Variant? meaningless_variant_or_null = null;  // only used for adding or not "set to default"
-        if (has_schema)
-            meaningless_variant_or_null = new Variant.boolean (true);
-
-        ContextPopover popover = new ContextPopover ();
-        action = popover.create_buttons_list (false, delay_mode, has_planned_change, "mb", 
meaningless_variant_or_null, initial_value);
-        popover.set_relative_to (this);
+        string full_name;
+        uint16 context_id;
+        Variant key_value_request;
+        ((!) value_variant).@get ("(sqv)", out full_name, out context_id, out key_value_request);
 
-        popover.value_changed.connect (on_popover_value_changed);
-        reload (initial_value);
-        this.set_popover ((Popover) popover);
+        if (modifications_handler.get_current_delay_mode ())
+            modifications_handler.add_delayed_setting (full_name, key_value_request, context_id);
+        else if (!ModelUtils.is_dconf_context_id (context_id))
+            modifications_handler.set_gsettings_key_value (full_name, context_id, key_value_request);
+        else
+            modifications_handler.set_dconf_key_value (full_name, key_value_request);
     }
-    private void on_popover_value_changed (Popover _popover, Variant? gvariant)
-        requires (gvariant != null)
-    {
-        reload ((!) gvariant);
-        _popover.closed ();
 
-        value_has_changed (true);
+    private void delay_erase (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
+    {
+        string full_name_or_empty = ((!) path_variant).get_string ();
+        if (full_name_or_empty == "")
+            return;
+        modifications_handler.enter_delay_mode ();
+        modifications_handler.add_delayed_setting (full_name_or_empty, null, ModelUtils.dconf_context_id);
     }
 
-    internal Variant get_variant ()
+    private void set_to_default (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
     {
-        return variant;
+        string full_name;
+        uint16 context_id;
+        ((!) path_variant).@get ("(sq)", out full_name, out context_id);
+        modifications_handler.set_to_default (full_name, context_id);
+        invalidate_popovers ();
     }
 
-    internal void reload (Variant gvariant)
+    private void toggle_dconf_key_switch (SimpleAction action, Variant? value_variant)
+        requires (value_variant != null)
     {
-        variant = gvariant;
-        maybe_variant = variant.get_maybe ();
+        if (modifications_handler.get_current_delay_mode ())
+            assert_not_reached ();
 
-        if (maybe_variant == null)
-            label = Key.cool_boolean_text_value (null);
-        else
-            label = Key.cool_boolean_text_value (((!) maybe_variant).get_boolean ());
+        string full_name;
+        bool key_value_request;
+        ((!) value_variant).@get ("(sb)", out full_name, out key_value_request);
 
-        action.change_state (new Variant.maybe (null, new Variant.maybe (new VariantType ("mb"), gvariant)));
+        modifications_handler.set_dconf_key_value (full_name, key_value_request);
     }
-}
 
-private class KeyEditorChildBool : Box, KeyEditorChild // might be managed by action, but can't find a way 
to ensure one-and-only-one button is active  // https://bugzilla.gnome.org/show_bug.cgi?id=769876
-{
-    private ToggleButton button_true;
-
-    internal KeyEditorChildBool (bool initial_value)
+    private void toggle_gsettings_key_switch (SimpleAction action, Variant? value_variant)
+        requires (value_variant != null)
     {
-        this.visible = true;
-        this.hexpand = true;
-        this.orientation = Orientation.HORIZONTAL;
-        this.halign = Align.START;
-        this.homogeneous = true;
-        this.width_request = 100;
-        this.get_style_context ().add_class ("linked");
-
-        ToggleButton button_false = new ToggleButton ();
-        button_false.visible = true;
-        button_false.label = Key.cool_boolean_text_value (false);
-        this.add (button_false);
-
-        button_true = new ToggleButton ();
-        button_true.visible = true;
-        button_true.label = Key.cool_boolean_text_value (true);
-        this.add (button_true);
-
-        button_true.active = initial_value;
-        button_true.bind_property ("active", button_false, "active", 
BindingFlags.INVERT_BOOLEAN|BindingFlags.SYNC_CREATE|BindingFlags.BIDIRECTIONAL);
+        if (modifications_handler.get_current_delay_mode ())
+            assert_not_reached ();
 
-        button_true.toggled.connect (() => value_has_changed (true));
-    }
+        string full_name;
+        uint16 context_id;
+        bool key_value_request;
+        bool key_default_value;
+        ((!) value_variant).@get ("(sqbb)", out full_name, out context_id, out key_value_request, out 
key_default_value);
 
-    internal Variant get_variant ()
-    {
-        return new Variant.boolean (button_true.active);
+        if (key_value_request == key_default_value)
+            modifications_handler.set_to_default (full_name, context_id);
+        else
+            modifications_handler.set_gsettings_key_value (full_name, context_id, new Variant.boolean 
(key_value_request));
     }
 
-    internal void reload (Variant gvariant)
-    {
-        button_true.active = gvariant.get_boolean ();
-    }
-}
+    /*\
+    * * modifications
+    \*/
 
-private abstract class KeyEditorChildNumberCustom : Entry, KeyEditorChild
-{
-    protected Variant variant;
+    [CCode (notify = false)] internal bool in_window_modifications           { internal get; private set; 
default = false; }
 
-    protected ulong deleted_text_handler = 0;
-    protected ulong inserted_text_handler = 0;
+    private bool modifications_list_created = false;
+    private ModificationsList modifications_list;
 
-    construct
+    private void create_modifications_list ()
     {
-        get_style_context ().add_class ("key-editor-child-entry");
+        modifications_list = new ModificationsList (/* needs shadows   */ false,
+                                                    /* big placeholder */ true);
+        modifications_list.set_window_size (saved_window_size);
+        // modifications_list.selection_changed.connect (() => ...);
+        modifications_list.show ();
+        add (modifications_list);
+        modifications_list_created = true;
     }
 
-    protected void connect_entry ()
+    internal void show_modifications_view ()
+        requires (modifications_list_created == true)
     {
-        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
-        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed (test_value ()));
-        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed (test_value ()));
-        ulong entry_activated_handler = activate.connect (() => { if (test_value ()) child_activated (); });
-        ulong entry_sensitive_handler = notify ["sensitive"].connect (set_error_class);
+        if (in_window_bookmarks || in_window_about)
+            show_default_view ();
 
-        destroy.connect (() => {
-                ref_buffer.disconnect (deleted_text_handler);
-                ref_buffer.disconnect (inserted_text_handler);
-                disconnect (entry_activated_handler);
-                disconnect (entry_sensitive_handler);
-            });
-    }
+        modifications_list.reset ();
 
-    private bool value_has_error = false;
-    private void set_error_class ()
-    {
-        StyleContext context = get_style_context ();
-        if (value_has_error)
-        {
-            if (is_sensitive ())
-            {
-                if (!context.has_class ("error"))
-                    context.add_class ("error");
-            }
-            else if (context.has_class ("error"))
-                context.remove_class ("error");
-        }
-        else if (is_sensitive () && context.has_class ("error"))
-            context.remove_class ("error");
+        set_visible_child (modifications_list);
+        in_window_modifications = true;
     }
 
-    protected void show_error (bool show)
+    private void update_in_window_modifications ()
     {
-        value_has_error = show;
-        if (show)
-            secondary_icon_name = "dialog-error-symbolic";
-        else
-            set_icon_from_icon_name (EntryIconPosition.SECONDARY, null);
-        set_error_class ();
-    }
+        if (!modifications_list_created)
+            create_modifications_list ();
 
-    internal Variant get_variant ()
-    {
-        return variant;
-    }
+        GLib.ListStore modifications_liststore = modifications_handler.get_delayed_settings ();
+        modifications_list.bind_model (modifications_liststore, delayed_setting_row_create);
 
-    internal void reload (Variant gvariant)
-    {
-        KeyEditorChild.set_lock_on (buffer, deleted_text_handler, inserted_text_handler);
-        this.text = gvariant.print (false);
-        if (!test_value ())
-            assert_not_reached ();
-        KeyEditorChild.set_lock_off (buffer, deleted_text_handler, inserted_text_handler);
+        if (in_window_modifications && modifications_handler.mode == ModificationsMode.NONE)
+            show_default_view ();
     }
 
-    protected abstract bool test_value ();
-}
-
-private class KeyEditorChildNumberDouble : KeyEditorChildNumberCustom
-{
-    private double min;
-    private double max;
-
-    internal KeyEditorChildNumberDouble (Variant initial_value, Variant? range_content_or_null)
+    private Widget delayed_setting_row_create (Object object)
     {
-        this.variant = initial_value;
-
-        this.visible = true;
-        this.hexpand = true;
-        this.secondary_icon_activatable = false;
+        SimpleSettingObject sso = (SimpleSettingObject) object;
+        return ModificationsRevealer.create_delayed_setting_row (modifications_handler, sso.name, 
sso.full_name, sso.context_id);
+    }
 
-        this.text = initial_value.print (false);
+    /*\
+    * * bookmarks
+    \*/
 
-        if (range_content_or_null != null)
-        {
-            min = ((!) range_content_or_null).get_child_value (0).get_double ();
-            max = ((!) range_content_or_null).get_child_value (1).get_double ();
-        }
-        else
-        {
-            min = -double.MAX; // https://gitlab.gnome.org/GNOME/vala/issues/680
-            max = double.MAX;
-        }
+    [CCode (notify = false)] internal bool in_window_bookmarks           { internal get; private set; 
default = false; }
+    [CCode (notify = false)] internal bool in_window_bookmarks_edit_mode { internal get; private set; 
default = false; }
 
-        connect_entry ();
-    }
+    private BookmarksList bookmarks_list;
 
-    private void switch_icon_tooltip_text (bool range_error)
+    private void create_bookmarks_list ()
     {
-        if (range_error)
-            set_icon_tooltip_text (EntryIconPosition.SECONDARY, out_of_range_text);
-        else
-            set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("Failed to parse as double."));
+        bookmarks_list = new BookmarksList (/* needs shadows            */ false,
+                                            /* big placeholder          */ true,
+                                            /* edit-mode action prefix  */ "bmk",
+                                            /* schema path              */ "/ca/desrt/dconf-editor/");
+        bookmarks_list.selection_changed.connect (on_bookmarks_selection_changed);
+        bookmarks_list.update_bookmarks_icons.connect (on_update_bookmarks_icons);
+        bookmarks_list.show ();
+        add (bookmarks_list);
     }
 
-    protected override bool test_value ()
-    {
-        Variant? tmp_variant;
-        string tmp_text = this.text; // don't put in the try{} for correct C code
-        try
-        {
-            tmp_variant = Variant.parse (VariantType.DOUBLE, tmp_text);
-        }
-        catch (VariantParseError e)
-        {
-            switch_icon_tooltip_text (false);
-            show_error (true);
-            return false;
-        }
+    private string [] old_bookmarks = new string [0];
 
-        double variant_value = ((!) tmp_variant).get_double ();
-        if ((variant_value < min) || (variant_value > max))
-        {
-            switch_icon_tooltip_text (true);
-            show_error (true);
-            return false;
-        }
-        else
-        {
-            variant = (!) tmp_variant;
-            show_error (false);
-            return true;
-        }
-    }
-}
-
-private class KeyEditorChildNumberInt64 : KeyEditorChildNumberCustom
-{
-    private int64 min;
-    private int64 max;
-
-    internal KeyEditorChildNumberInt64 (Variant initial_value, Variant? range_content_or_null)
+    internal void show_bookmarks_view (string [] bookmarks)
     {
-        this.variant = initial_value;
-
-        this.visible = true;
-        this.hexpand = true;
-        this.secondary_icon_activatable = false;
-
-        this.text = initial_value.print (false);
-
-        if (range_content_or_null != null)
-        {
-            min = ((!) range_content_or_null).get_child_value (0).get_int64 ();
-            max = ((!) range_content_or_null).get_child_value (1).get_int64 ();
-        }
-        else
-        {
-            min = int64.MIN;
-            max = int64.MAX;
-        }
+        if (in_window_modifications || in_window_about)
+            show_default_view ();
 
-        connect_entry ();
-    }
+        bookmarks_list.reset ();
 
-    private void switch_icon_tooltip_text (bool range_error)
-    {
-        if (range_error)
-            set_icon_tooltip_text (EntryIconPosition.SECONDARY, out_of_range_text);
-        else
-            set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("Failed to parse as 64-bit integer."));
-    }
-
-    protected override bool test_value ()
-    {
-        Variant? tmp_variant;
-        string tmp_text = this.text; // don't put in the try{} for correct C code
-        try
-        {
-            tmp_variant = Variant.parse (VariantType.INT64, tmp_text);
-        }
-        catch (VariantParseError e)
+        if (bookmarks != old_bookmarks)
         {
-            switch_icon_tooltip_text (false);
-            show_error (true);
-            return false;
-        }
+            Variant variant = new Variant.strv (bookmarks);
+            bookmarks_list.create_bookmark_rows (variant);
 
-        int64 variant_value = ((!) tmp_variant).get_int64 ();
-        if ((variant_value < min) || (variant_value > max))
-        {
-            switch_icon_tooltip_text (true);
-            show_error (true);
-            return false;
-        }
-        else
-        {
-            variant = (!) tmp_variant;
-            show_error (false);
-            return true;
+            old_bookmarks = bookmarks;
         }
+        set_visible_child (bookmarks_list);
+        in_window_bookmarks = true;
     }
-}
 
-private class KeyEditorChildNumberUint64 : KeyEditorChildNumberCustom
-{
-    private uint64 min;
-    private uint64 max;
-
-    internal KeyEditorChildNumberUint64 (Variant initial_value, Variant? range_content_or_null)
+    internal void update_bookmark_icon (string bookmark, BookmarkIcon icon)
     {
-        this.variant = initial_value;
-
-        this.visible = true;
-        this.hexpand = true;
-        this.secondary_icon_activatable = false;
-
-        this.text = initial_value.print (false);
-
-        if (range_content_or_null != null)
-        {
-            min = ((!) range_content_or_null).get_child_value (0).get_uint64 ();
-            max = ((!) range_content_or_null).get_child_value (1).get_uint64 ();
-        }
-        else
-        {
-            min = uint64.MIN;
-            max = uint64.MAX;
-        }
-
-        connect_entry ();
+        bookmarks_list.update_bookmark_icon (bookmark, icon);
     }
 
-    private void switch_icon_tooltip_text (bool range_error)
+    internal void enter_bookmarks_edit_mode ()
+        requires (in_window_bookmarks == true)
     {
-        if (range_error)
-            set_icon_tooltip_text (EntryIconPosition.SECONDARY, out_of_range_text);
-        else
-            set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("Failed to parse as unsigned 64-bit 
integer."));
+        bookmarks_list.enter_edit_mode ();
+        in_window_bookmarks_edit_mode = true;
     }
 
-    protected override bool test_value ()
+    internal bool leave_bookmarks_edit_mode ()
+        requires (in_window_bookmarks == true)
     {
-        Variant? tmp_variant;
-        string tmp_text = this.text; // don't put in the try{} for correct C code
-        try
-        {
-            tmp_variant = Variant.parse (VariantType.UINT64, tmp_text);
-        }
-        catch (VariantParseError e)
-        {
-            switch_icon_tooltip_text (false);
-            show_error (true);
-            return false;
-        }
-
-        uint64 variant_value = ((!) tmp_variant).get_uint64 ();
-        if ((variant_value < min) || (variant_value > max))
-        {
-            switch_icon_tooltip_text (true);
-            show_error (true);
-            return false;
-        }
-        else
-        {
-            variant = (!) tmp_variant;
-            show_error (false);
-            return true;
-        }
+        in_window_bookmarks_edit_mode = false;
+        return bookmarks_list.leave_edit_mode ();
     }
-}
-
-private class KeyEditorChildNumberInt : SpinButton, KeyEditorChild
-{
-    private Variant variant;
 
-    private string key_type;
-
-    private int64 min_int64;
-    private int64 max_int64;
-
-    private ulong deleted_text_handler = 0;
-    private ulong inserted_text_handler = 0;
-
-    internal KeyEditorChildNumberInt (Variant initial_value, string type_string, Variant? 
range_content_or_null)
-        requires (type_string == "y" || type_string == "n" || type_string == "q" || type_string == "i" || 
type_string == "u" || type_string == "h")     // "x" and "t" are managed elsewhere
+    internal OverlayedList.SelectionState get_bookmarks_selection_state ()
     {
-        this.variant = initial_value;
-        this.key_type = type_string;
-
-        this.visible = true;
-        this.hexpand = true;
-        this.halign = Align.START;
-
-        double min_double, max_double;
-        if (range_content_or_null != null)
-        {
-            Variant min_variant = ((!) range_content_or_null).get_child_value (0);
-            Variant max_variant = ((!) range_content_or_null).get_child_value (1);
-            min_double = get_variant_as_double (min_variant);
-            max_double = get_variant_as_double (max_variant);
-            min_int64  = get_variant_as_int64  (min_variant);
-            max_int64  = get_variant_as_int64  (max_variant);
-        }
-        else
-        {
-            get_min_and_max_double (out min_double, out max_double, type_string);
-            get_min_and_max_int64  (out min_int64,  out max_int64,  type_string);
-        }
-
-        Adjustment adjustment = new Adjustment (get_variant_as_double (initial_value), min_double, 
max_double, 1.0, 5.0, 0.0);
-        this.configure (adjustment, 1.0, 0);
-
-        this.update_policy = SpinButtonUpdatePolicy.IF_VALID;
-        this.snap_to_ticks = true;
-        this.numeric = true;
-        this.input_purpose = InputPurpose.NUMBER;   // TODO could be DIGITS for UnsignedInt
-        this.max_width_chars = 30;
-
-        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
-        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed (test_value ()));
-        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed (test_value ()));
-        ulong entry_activated_handler = activate.connect (() => { if (test_value ()) child_activated (); 
update (); });
-        ulong entry_sensitive_handler = notify ["sensitive"].connect (set_error_class);
-
-        destroy.connect (() => {
-                ref_buffer.disconnect (deleted_text_handler);
-                ref_buffer.disconnect (inserted_text_handler);
-                disconnect (entry_activated_handler);
-                disconnect (entry_sensitive_handler);
-            });
+        return bookmarks_list.get_selection_state ();
     }
 
-    private static void get_min_and_max_double (out double min, out double max, string variant_type)
+    internal void trash_bookmark ()
     {
-        switch (variant_type)
-        {
-            case "y": min = (double) uint8.MIN;     max = (double) uint8.MAX;   break;
-            case "n": min = (double) int16.MIN;     max = (double) int16.MAX;   break;
-            case "q": min = (double) uint16.MIN;    max = (double) uint16.MAX;  break;
-            case "i": min = (double) int32.MIN;     max = (double) int32.MAX;   break;
-            case "u": min = (double) uint32.MIN;    max = (double) uint32.MAX;  break;
-            case "h": min = (double) int32.MIN;     max = (double) int32.MAX;   break;
-            default: assert_not_reached ();
-        }
-    }
-    private static void get_min_and_max_int64 (out int64 min, out int64 max, string variant_type)
-    {
-        switch (variant_type)
-        {
-            case "y": min = (int64) uint8.MIN;      max = (int64) uint8.MAX;    break;
-            case "n": min = (int64) int16.MIN;      max = (int64) int16.MAX;    break;
-            case "q": min = (int64) uint16.MIN;     max = (int64) uint16.MAX;   break;
-            case "i": min = (int64) int32.MIN;      max = (int64) int32.MAX;    break;
-            case "u": min = (int64) uint32.MIN;     max = (int64) uint32.MAX;   break;
-            case "h": min = (int64) int32.MIN;      max = (int64) int32.MAX;    break;
-            default: assert_not_reached ();
-        }
+        bookmarks_list.trash_bookmark ();
     }
 
-    private static double get_variant_as_double (Variant variant)
-    {
-        switch (variant.classify ())
-        {
-            case Variant.Class.BYTE:    return (double) variant.get_byte ();
-            case Variant.Class.INT16:   return (double) variant.get_int16 ();
-            case Variant.Class.UINT16:  return (double) variant.get_uint16 ();
-            case Variant.Class.INT32:   return (double) variant.get_int32 ();
-            case Variant.Class.UINT32:  return (double) variant.get_uint32 ();
-            case Variant.Class.HANDLE:  return (double) variant.get_handle ();
-            default: assert_not_reached ();
-        }
-    }
-    private static int64 get_variant_as_int64 (Variant variant)
+    internal void move_top ()
     {
-        switch (variant.classify ())
-        {
-            case Variant.Class.BYTE:    return (int64) variant.get_byte ();
-            case Variant.Class.INT16:   return (int64) variant.get_int16 ();
-            case Variant.Class.UINT16:  return (int64) variant.get_uint16 ();
-            case Variant.Class.INT32:   return (int64) variant.get_int32 ();
-            case Variant.Class.UINT32:  return (int64) variant.get_uint32 ();
-            case Variant.Class.HANDLE:  return (int64) variant.get_handle ();
-            default: assert_not_reached ();
-        }
+        bookmarks_list.move_top ();
     }
 
-    internal Variant get_variant ()
+    internal void move_up ()
     {
-        return variant;
+        bookmarks_list.move_up ();
     }
 
-    internal void reload (Variant gvariant)       // TODO "key_editor_child_number_int_real_reload: 
assertion 'gvariant != NULL' failed" two times when ghosting a key
+    internal void move_down ()
     {
-        KeyEditorChild.set_lock_on (buffer, deleted_text_handler, inserted_text_handler);
-        this.set_value (get_variant_as_double (gvariant));
-        KeyEditorChild.set_lock_off (buffer, deleted_text_handler, inserted_text_handler);
+        bookmarks_list.move_down ();
     }
 
-    private bool value_has_error = false;
-    private void set_error_class ()
+    internal void move_bottom ()
     {
-        StyleContext context = get_style_context ();
-        if (is_sensitive ())
-        {
-            if (value_has_error)
-            {
-                if (!context.has_class ("error"))
-                    context.add_class ("error");
-            }
-            else if (context.has_class ("error"))
-                context.remove_class ("error");
-        }
-        else if (context.has_class ("error"))
-            context.remove_class ("error");
+        bookmarks_list.move_bottom ();
     }
 
-    private bool test_value ()
+    private void on_bookmarks_selection_changed ()
     {
-        Variant? tmp_variant;
-        string tmp_text = this.text; // don't put in the try{} for correct C code
-        try
-        {
-            tmp_variant = Variant.parse (VariantType.INT64, tmp_text);
-        }
-        catch (VariantParseError e)
-        {
-            value_has_error = true;
-            set_error_class ();
-            return false;
-        }
-
-        int64 variant_value = ((!) tmp_variant).get_int64 ();
-        if ((variant_value < min_int64) || (variant_value > max_int64))
-        {
-            value_has_error = true;
-            set_error_class ();
+        if (!in_window_bookmarks)
+            return;
+        bookmarks_selection_changed ();
+    }
 
-            return false;
-        }
-        else
-        {
-            value_has_error = false;
-            set_error_class ();
+    internal signal void bookmarks_selection_changed ();
 
-            variant = get_variant_from_int64 (variant_value, key_type);
-            return true;
-        }
-    }
-    private static Variant get_variant_from_int64 (int64 int64_value, string key_type)
+    internal signal void update_bookmarks_icons (Variant bookmarks_variant);
+    private void on_update_bookmarks_icons (Variant bookmarks_variant)
     {
-        switch (key_type)
-        {
-            case "y": return new Variant.byte   ((uint8)  int64_value);
-            case "n": return new Variant.int16  ((int16)  int64_value);
-            case "q": return new Variant.uint16 ((uint16) int64_value);
-            case "i": return new Variant.int32  ((int32)  int64_value);
-            case "u": return new Variant.uint32 ((uint32) int64_value);
-            case "h": return new Variant.handle ((int32)  int64_value);
-            default: assert_not_reached ();
-        }
+        update_bookmarks_icons (bookmarks_variant);
     }
-}
 
-private class KeyEditorChildArray : Grid, KeyEditorChild
-{
-    private TextView text_view;
-    private Revealer error_revealer;
-    private string key_type;
-    private Variant variant;
-
-    private ulong deleted_text_handler = 0;
-    private ulong inserted_text_handler = 0;
+    /*\
+    * * views
+    \*/
 
-    construct
+    internal override void set_path (ViewType type, string path)
     {
-        get_style_context ().add_class ("key-editor-child-array");
+        dconf_content.set_path (type, path);
+        modifications_handler.path_changed ();
+        invalidate_popovers ();
     }
 
-    internal KeyEditorChildArray (string type_string, Variant initial_value)
-    {
-        this.visible = true;
-        this.hexpand = true;
-        this.vexpand = false;
-        orientation = Orientation.VERTICAL;
-        get_style_context ().add_class ("frame");
-
-        this.key_type = type_string;
-        this.variant = initial_value;
-
-        ScrolledWindow scrolled_window = new ScrolledWindow (null, null);
-        scrolled_window.visible = true;
-
-        text_view = new TextView ();
-        text_view.visible = true;
-        text_view.expand = true;
-        text_view.wrap_mode = WrapMode.WORD;
-        text_view.monospace = true;
-        text_view.key_press_event.connect (on_key_press_event);
-        // https://bugzilla.gnome.org/show_bug.cgi?id=789676
-        text_view.button_press_event.connect_after (event_stop);
-        text_view.button_release_event.connect_after (event_stop);
-
-        scrolled_window.add (text_view);
-        add (scrolled_window);
-
-        error_revealer = new Revealer ();
-        error_revealer.visible = true;
-        error_revealer.transition_type = RevealerTransitionType.SLIDE_UP;
-        error_revealer.reveal_child = false;
-        add (error_revealer);
-
-        ActionBar error_bar = new ActionBar ();
-        error_bar.visible = true;
-        error_revealer.add (error_bar);
-
-        Image error_icon = new Image.from_icon_name ("dialog-error-symbolic", IconSize.BUTTON);
-        error_icon.visible = true;
-        error_bar.pack_start (error_icon);
-
-        Label error_label = new Label (_("This value is invalid for the key type."));
-        error_label.visible = true;
-        error_label.wrap = true;
-        error_bar.pack_start (error_label);
-
-        text_view.buffer.text = initial_value.print (false);
+    /*\
+    * * reload
+    \*/
 
-        TextBuffer ref_buffer = text_view.buffer;    // an TextBuffer doesn't emit a "destroy" signal
-        deleted_text_handler = ref_buffer.delete_range.connect_after (() => value_has_changed (test_value 
()));
-        inserted_text_handler = ref_buffer.insert_text.connect_after (() => value_has_changed (test_value 
()));
-        destroy.connect (() => {
-                ref_buffer.disconnect (deleted_text_handler);
-                ref_buffer.disconnect (inserted_text_handler);
-            });
-    }
-    private bool on_key_press_event (Gdk.EventKey event)
-    {
-        string keyval_name = (!) (Gdk.keyval_name (event.keyval) ?? "");
-        if ((keyval_name == "Return" || keyval_name == "KP_Enter")
-        && ((event.state & Gdk.ModifierType.MODIFIER_MASK) == 0)
-        && (test_value ()))
-        {
-            child_activated ();
-            return Gdk.EVENT_STOP;
-        }
-        return base.key_press_event (event);
-    }
-    private static bool event_stop ()
+    internal void set_search_parameters (string current_path, string [] bookmarks)
     {
-        return Gdk.EVENT_STOP;
+        hide_reload_warning ();
+        dconf_content.set_search_parameters (current_path, last_context_id, bookmarks, sorting_options);
     }
 
-    private bool test_value ()
+    internal bool check_reload (ViewType type, string path, bool show_infobar)
     {
-        string tmp_text = text_view.buffer.text; // don't put in the try{} for correct C code
-        try
-        {
-            Variant? tmp_variant = Variant.parse (new VariantType (key_type), tmp_text);
-            variant = (!) tmp_variant;
-
-            StyleContext context = get_style_context ();
-            if (context.has_class ("error"))
-                context.remove_class ("error");
-            error_revealer.reveal_child = false;
+        SettingsModel model = modifications_handler.model;
 
-            return true;
+        if (type == ViewType.FOLDER || (type == ViewType.CONFIG && ModelUtils.is_folder_path (path)))
+        {
+            if (!dconf_content.check_reload_folder (model.get_children (path)))
+                return false;
+            if (show_infobar)
+            {
+                info_bar.show_warning ("hard-reload-folder");
+                return false;
+            }
         }
-        catch (VariantParseError e)
+        else if (type == ViewType.OBJECT || type == ViewType.CONFIG)
         {
-            StyleContext context = get_style_context ();
-            if (!context.has_class ("error"))
-                context.add_class ("error");
-            error_revealer.reveal_child = true;
-
-            return false;
+            if (model.key_exists (path, last_context_id))
+            {
+                RegistryVariantDict properties = new RegistryVariantDict.from_aqv (model.get_key_properties 
(path, last_context_id, (uint16) PropertyQuery.HASH));
+                uint properties_hash;
+                if (!properties.lookup (PropertyQuery.HASH, "u", out properties_hash))
+                    assert_not_reached ();
+                if (!dconf_content.check_reload_object (properties_hash))
+                    return false;
+            }
+            if (show_infobar)
+            {
+                info_bar.show_warning ("hard-reload-object");
+                return false;
+            }
         }
-    }
-
-    internal Variant get_variant ()
-    {
-        return variant;
-    }
-
-    internal void reload (Variant gvariant)
-    {
-        KeyEditorChild.set_lock_on (text_view.buffer, deleted_text_handler, inserted_text_handler);
-        text_view.buffer.text = gvariant.print (false);
-        if (!test_value ())
+        else if (type == ViewType.SEARCH)
             assert_not_reached ();
-        KeyEditorChild.set_lock_off (text_view.buffer, deleted_text_handler, inserted_text_handler);
+        else
+            assert_not_reached ();
+        return true;
     }
-}
 
-private class KeyEditorChildDefault : Entry, KeyEditorChild
-{
-    private string key_type;
-    private Variant variant;
-    private bool is_string;
+    /*\
+    * * Proxy calls
+    \*/
 
-    private ulong deleted_text_handler = 0;
-    private ulong inserted_text_handler = 0;
+    internal void hide_or_show_toggles (bool show) { dconf_content.hide_or_show_toggles (show); }
 
-    construct
+    // keyboard
+    internal override bool next_match ()
     {
-        get_style_context ().add_class ("key-editor-child-entry");
+        if (in_window_bookmarks)
+            return bookmarks_list.next_match ();
+        if (in_window_modifications)
+            return modifications_list.next_match ();
+        return base.next_match ();
     }
-
-    internal KeyEditorChildDefault (string type_string, Variant initial_value)
+    internal override bool previous_match ()
     {
-        this.key_type = type_string;
-        this.variant = initial_value;
-
-        this.visible = true;
-        this.hexpand = true;
-        this.secondary_icon_activatable = false;
-        this.set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("This value is invalid for the key 
type."));
-
-        this.is_string = type_string == "s" || type_string == "o" || type_string == "g";
-        this.text = is_string ? initial_value.get_string () : initial_value.print (false);
-
-        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
-        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed (test_value ()));
-        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed (test_value ()));
-        ulong entry_activate_handler = activate.connect (() => { if (test_value ()) child_activated (); });
-
-        destroy.connect (() => {
-                ref_buffer.disconnect (deleted_text_handler);
-                ref_buffer.disconnect (inserted_text_handler);
-                disconnect (entry_activate_handler);
-            });
+        if (in_window_bookmarks)
+            return bookmarks_list.previous_match ();
+        if (in_window_modifications)
+            return modifications_list.previous_match ();
+        return base.previous_match ();
     }
 
-    private bool test_value ()
+    internal override bool toggle_row_popover ()     // Menu
     {
-        if (key_type == "s")
-        {
-            variant = new Variant.string (this.text);
-            return true;
-        }
-
-        string tmp_text = is_string ? @"'$text'" : this.text; // don't put in the try{} for correct C code
-        try
-        {
-            Variant? tmp_variant = Variant.parse (new VariantType (key_type), tmp_text);
-            variant = (!) tmp_variant;
-
-            StyleContext context = get_style_context ();
-            if (context.has_class ("error"))
-                context.remove_class ("error");
-            set_icon_from_icon_name (EntryIconPosition.SECONDARY, null);
+        if (in_window_bookmarks)
+            return false;
+        return base.toggle_row_popover ();
+     }
 
-            return true;
-        }
-        catch (VariantParseError e)
-        {
-            StyleContext context = get_style_context ();
-            if (!context.has_class ("error"))
-                context.add_class ("error");
-            secondary_icon_name = "dialog-error-symbolic";
+    internal void toggle_boolean_key ()      { dconf_content.toggle_boolean_key ();      }
+    internal void set_selected_to_default () { dconf_content.set_selected_to_default (); }
 
-            return false;
-        }
+    // current row property
+    internal override bool handle_copy_text (out string copy_text)
+    {
+        if (in_window_bookmarks)
+            return bookmarks_list.handle_copy_text (out copy_text);
+        if (in_window_modifications)
+            return modifications_list.handle_copy_text (out copy_text);
+        return base.handle_copy_text (out copy_text);
     }
 
-    internal Variant get_variant ()
+    // values changes
+    internal void gkey_value_push (string full_name, uint16 context_id, Variant key_value, bool 
is_key_default)
     {
-        return variant;
+        dconf_content.gkey_value_push (full_name, context_id, key_value, is_key_default);
     }
-
-    internal void reload (Variant gvariant)
+    internal void dkey_value_push (string full_name, Variant? key_value_or_null)
     {
-        KeyEditorChild.set_lock_on (buffer, deleted_text_handler, inserted_text_handler);
-        this.text = is_string ? gvariant.get_string () : gvariant.print (false);
-        if (!test_value ())
-            assert_not_reached ();
-        KeyEditorChild.set_lock_off (buffer, deleted_text_handler, inserted_text_handler);
+        dconf_content.dkey_value_push (full_name, key_value_or_null);
     }
 }
diff --git a/editor/dconf-window.vala b/editor/dconf-window.vala
index 8e482f8..5edf61d 100644
--- a/editor/dconf-window.vala
+++ b/editor/dconf-window.vala
@@ -96,13 +96,13 @@ private class DConfWindow : BrowserWindow
     private ulong behaviour_changed_handler = 0;
 
     private ulong headerbar_update_bookmarks_icons_handler = 0;
-    private ulong browserview_update_bookmarks_icons_handler = 0;
+    private ulong main_view_update_bookmarks_icons_handler = 0;
 
     private ulong delayed_changes_changed_handler = 0;
     private ulong bookmarks_selection_changed_handler = 0;
 
     private DConfHeaderBar headerbar;
-    private BrowserView    browser_view;
+    private DConfView      main_view;
 
     private StyleContext context;
     construct
@@ -112,7 +112,7 @@ private class DConfWindow : BrowserWindow
         context.add_class ("dconf-editor");
 
         headerbar = (DConfHeaderBar) nta_headerbar;
-        browser_view = (BrowserView) base_view;
+        main_view = (DConfView) base_view;
 
         create_modifications_revealer ();
 
@@ -120,24 +120,24 @@ private class DConfWindow : BrowserWindow
         install_kbd_action_entries ();
         install_bmk_action_entries ();
 
-        bookmarks_selection_changed_handler = browser_view.bookmarks_selection_changed.connect 
(on_bookmarks_selection_changed);
+        bookmarks_selection_changed_handler = main_view.bookmarks_selection_changed.connect 
(on_bookmarks_selection_changed);
 
-          headerbar_update_bookmarks_icons_handler =    headerbar.update_bookmarks_icons.connect 
(update_bookmarks_icons_from_variant);
-        browserview_update_bookmarks_icons_handler = browser_view.update_bookmarks_icons.connect 
(update_bookmarks_icons_from_variant);
+        headerbar_update_bookmarks_icons_handler = headerbar.update_bookmarks_icons.connect 
(update_bookmarks_icons_from_variant);
+        main_view_update_bookmarks_icons_handler = main_view.update_bookmarks_icons.connect 
(update_bookmarks_icons_from_variant);
     }
 
     internal DConfWindow (bool disable_warning, string? schema, string? path, string? key_name, 
NightLightMonitor night_light_monitor)
     {
         DConfHeaderBar _headerbar = new DConfHeaderBar (night_light_monitor);
-        BrowserView _browser_view = new BrowserView ();
-        Object (nta_headerbar: (NightTimeAwareHeaderBar) _headerbar, base_view: (BaseView) _browser_view);
+        DConfView _main_view = new DConfView ();
+        Object (nta_headerbar: (NightTimeAwareHeaderBar) _headerbar, base_view: (BaseView) _main_view);
 
         use_shortpaths_changed_handler = settings.changed ["use-shortpaths"].connect_after (reload_view);
         settings.bind ("use-shortpaths", model, "use-shortpaths", 
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
 
         modifications_handler = new ModificationsHandler (model);
         revealer.modifications_handler = modifications_handler;
-        browser_view.modifications_handler = modifications_handler;
+        main_view.modifications_handler = modifications_handler;
         delayed_changes_changed_handler = modifications_handler.delayed_changes_changed.connect (() => {
                 uint total_changes_count = modifications_handler.dconf_changes_count + 
modifications_handler.gsettings_changes_count;
                 if (total_changes_count == 0)
@@ -295,19 +295,19 @@ private class DConfWindow : BrowserWindow
             if (!internal_changes)  // TODO do not react to value changes
                 reload_search_action.set_enabled (true);
         }
-        else if (browser_view.check_reload (current_type, current_path, !internal_changes))    // handle 
infobars in needed
+        else if (main_view.check_reload (current_type, current_path, !internal_changes))    // handle 
infobars in needed
             reload_view ();
 
         headerbar.update_ghosts (((SettingsModel) _model).get_fallback_path (headerbar.get_complete_path 
()));
     }
     private void propagate_gkey_value_push (string full_name, uint16 context_id, Variant key_value, bool 
is_key_default)
     {
-        browser_view.gkey_value_push (full_name, context_id, key_value, is_key_default);
+        main_view.gkey_value_push (full_name, context_id, key_value, is_key_default);
         revealer.gkey_value_push     (full_name, context_id, key_value, is_key_default);
     }
     private void propagate_dkey_value_push (string full_name, Variant? key_value_or_null)
     {
-        browser_view.dkey_value_push (full_name, key_value_or_null);
+        main_view.dkey_value_push (full_name, key_value_or_null);
         revealer.dkey_value_push     (full_name, key_value_or_null);
     }
 
@@ -374,11 +374,11 @@ private class DConfWindow : BrowserWindow
     {
         ((ConfigurationEditor) get_application ()).clean_copy_notification ();
 
-        browser_view.disconnect (bookmarks_selection_changed_handler);
+        main_view.disconnect (bookmarks_selection_changed_handler);
         modifications_handler.disconnect (delayed_changes_changed_handler);
 
         headerbar.disconnect (headerbar_update_bookmarks_icons_handler);
-        browser_view.disconnect (browserview_update_bookmarks_icons_handler);
+        main_view.disconnect (main_view_update_bookmarks_icons_handler);
 
         settings.disconnect (settings_user_paths_changed_handler);
         settings.disconnect (settings_enabled_mappings_changed_handler);
@@ -455,7 +455,7 @@ private class DConfWindow : BrowserWindow
 
     private void apply_delayed_settings (/* SimpleAction action, Variant? path_variant */)
     {
-        if (browser_view.in_window_modifications)
+        if (main_view.in_window_modifications)
             show_default_view ();
         modifications_handler.apply_delayed_settings ();
         invalidate_popovers_with_ui_reload ();
@@ -463,7 +463,7 @@ private class DConfWindow : BrowserWindow
 
     private void dismiss_delayed_settings (/* SimpleAction action, Variant? path_variant */)
     {
-        if (browser_view.in_window_modifications)
+        if (main_view.in_window_modifications)
             show_default_view ();
         modifications_handler.dismiss_delayed_settings ();
         invalidate_popovers_with_ui_reload ();
@@ -473,7 +473,7 @@ private class DConfWindow : BrowserWindow
         requires (path_variant != null)
     {
         modifications_handler.dismiss_change (((!) path_variant).get_string ());
-        browser_view.invalidate_popovers ();
+        main_view.invalidate_popovers ();
         reload_view ();
     }
 
@@ -487,8 +487,8 @@ private class DConfWindow : BrowserWindow
     private void invalidate_popovers_with_ui_reload ()
     {
         bool delay_mode = modifications_handler.get_current_delay_mode ();
-        browser_view.hide_or_show_toggles (!delay_mode);
-        browser_view.invalidate_popovers ();
+        main_view.hide_or_show_toggles (!delay_mode);
+        main_view.invalidate_popovers ();
         headerbar.delay_mode = delay_mode;
     }
 
@@ -498,17 +498,17 @@ private class DConfWindow : BrowserWindow
 
     protected override void show_default_view ()
     {
-        if (browser_view.in_window_bookmarks)
+        if (main_view.in_window_bookmarks)
         {
-            if (browser_view.in_window_bookmarks_edit_mode)
+            if (main_view.in_window_bookmarks_edit_mode)
                 leave_edit_mode ();     // TODO place after
             headerbar.show_default_view ();
-            browser_view.show_default_view ();
+            main_view.show_default_view ();
         }
-        else if (browser_view.in_window_modifications)
+        else if (main_view.in_window_modifications)
         {
             headerbar.show_default_view ();
-            browser_view.show_default_view ();
+            main_view.show_default_view ();
         }
         else
             base.show_default_view ();
@@ -520,7 +520,7 @@ private class DConfWindow : BrowserWindow
 
         headerbar.show_use_bookmarks_view ();
         string [] bookmarks = headerbar.get_bookmarks ();
-        browser_view.show_bookmarks_view (bookmarks);
+        main_view.show_bookmarks_view (bookmarks);
         update_bookmarks_icons_from_array (bookmarks);
     }
 
@@ -529,7 +529,7 @@ private class DConfWindow : BrowserWindow
         close_in_window_panels ();
 
         headerbar.show_modifications_view ();
-        browser_view.show_modifications_view ();
+        main_view.show_modifications_view ();
     }
 
     /*\
@@ -597,7 +597,7 @@ private class DConfWindow : BrowserWindow
     {
         if (AdaptativeWidget.WindowSize.is_extra_thin (window_size)
          || AdaptativeWidget.WindowSize.is_extra_flat (window_size))
-            browser_view.update_bookmark_icon (bookmark, icon);
+            main_view.update_bookmark_icon (bookmark, icon);
         else
             headerbar.update_bookmark_icon (bookmark, icon);
     }
@@ -615,7 +615,7 @@ private class DConfWindow : BrowserWindow
         if (disable_popovers != _disable_popovers)
         {
             disable_popovers = _disable_popovers;
-            if (browser_view.in_window_bookmarks)
+            if (main_view.in_window_bookmarks)
                 show_default_view ();
         }
 
@@ -624,7 +624,7 @@ private class DConfWindow : BrowserWindow
         if (disable_action_bar != _disable_action_bar)
         {
             disable_action_bar = _disable_action_bar;
-            if (browser_view.in_window_modifications)
+            if (main_view.in_window_modifications)
                 show_default_view ();
         }
     }
@@ -644,7 +644,7 @@ private class DConfWindow : BrowserWindow
     private void update_actions ()
         requires (actions_init_done)
     {
-        Bookmarks._update_actions (browser_view.get_bookmarks_selection_state (), ref move_top_action, ref 
move_up_action, ref move_down_action, ref move_bottom_action, ref trash_bookmark_action);
+        Bookmarks._update_actions (main_view.get_bookmarks_selection_state (), ref move_top_action, ref 
move_up_action, ref move_down_action, ref move_bottom_action, ref trash_bookmark_action);
     }
 
     private void install_bmk_action_entries ()
@@ -693,14 +693,14 @@ private class DConfWindow : BrowserWindow
         update_actions ();
 
         headerbar.show_edit_bookmarks_view ();
-        browser_view.enter_bookmarks_edit_mode ();
+        main_view.enter_bookmarks_edit_mode ();
     }
 
     private void leave_edit_mode ()
     {
         edit_mode_state_action.set_state (false);
 
-        bool give_focus_to_info_button = browser_view.leave_bookmarks_edit_mode ();
+        bool give_focus_to_info_button = main_view.leave_bookmarks_edit_mode ();
         headerbar.show_use_bookmarks_view ();
 
 /*        if (give_focus_to_info_button)
@@ -709,28 +709,28 @@ private class DConfWindow : BrowserWindow
 
     private void trash_bookmark (/* SimpleAction action, Variant? variant */)
     {
-        browser_view.trash_bookmark ();
+        main_view.trash_bookmark ();
 //        update_bookmarks_icons_from_array (new_bookmarks);
     }
 
     private void move_top       (/* SimpleAction action, Variant? variant */)
     {
-        browser_view.move_top ();
+        main_view.move_top ();
     }
 
     private void move_up        (/* SimpleAction action, Variant? variant */)
     {
-        browser_view.move_up ();
+        main_view.move_up ();
     }
 
     private void move_down      (/* SimpleAction action, Variant? variant */)
     {
-        browser_view.move_down ();
+        main_view.move_down ();
     }
 
     private void move_bottom    (/* SimpleAction action, Variant? variant */)
     {
-        browser_view.move_bottom ();
+        main_view.move_bottom ();
     }
 
     private void on_bookmarks_selection_changed ()
@@ -764,15 +764,15 @@ private class DConfWindow : BrowserWindow
 
     private void toggle_bookmark                        (/* SimpleAction action, Variant? variant */)
     {
-        browser_view.close_popovers ();
+        main_view.close_popovers ();
         if (!AdaptativeWidget.WindowSize.is_phone_size (window_size)
          && !AdaptativeWidget.WindowSize.is_extra_thin (window_size))
         {
-            if (browser_view.in_window_modifications)
+            if (main_view.in_window_modifications)
                 show_default_view ();
             headerbar.click_bookmarks_button ();
         }
-        else if (browser_view.in_window_bookmarks)
+        else if (main_view.in_window_bookmarks)
             show_default_view ();
         else
             show_use_bookmarks_view ();
@@ -783,7 +783,7 @@ private class DConfWindow : BrowserWindow
         if (is_in_in_window_mode ())        // TODO better
             return;
 
-        browser_view.close_popovers ();
+        main_view.close_popovers ();
         headerbar.bookmark_current_path ();
     }
 
@@ -792,7 +792,7 @@ private class DConfWindow : BrowserWindow
         if (is_in_in_window_mode ())        // TODO better
             return;
 
-        browser_view.close_popovers ();
+        main_view.close_popovers ();
         headerbar.unbookmark_current_path ();
     }
 
@@ -804,7 +804,7 @@ private class DConfWindow : BrowserWindow
         if (!AdaptativeWidget.WindowSize.is_extra_thin (window_size)
          && !AdaptativeWidget.WindowSize.is_extra_flat (window_size))
             revealer.toggle_modifications_list ();
-        else if (browser_view.in_window_modifications)
+        else if (main_view.in_window_modifications)
             show_default_view ();
         else
             show_modifications_view ();
@@ -812,14 +812,14 @@ private class DConfWindow : BrowserWindow
 
     private void escape_pressed                         (/* SimpleAction action, Variant? variant */)
     {
-        if (browser_view.in_window_bookmarks)
+        if (main_view.in_window_bookmarks)
         {
-            if (browser_view.in_window_bookmarks_edit_mode)
+            if (main_view.in_window_bookmarks_edit_mode)
                 leave_edit_mode ();
             else
                 show_default_view ();
         }
-        else if (browser_view.in_window_modifications || in_window_about)
+        else if (main_view.in_window_modifications || in_window_about)
             show_default_view ();
         else if (headerbar.search_mode_enabled)
             stop_search ();
@@ -832,8 +832,8 @@ private class DConfWindow : BrowserWindow
         if (row_action_blocked ())
             return;
 
-        browser_view.close_popovers ();
-        browser_view.toggle_boolean_key ();
+        main_view.close_popovers ();
+        main_view.toggle_boolean_key ();
     }
 
     private void set_to_default                         (/* SimpleAction action, Variant? variant */)
@@ -846,12 +846,12 @@ private class DConfWindow : BrowserWindow
             reload_view ();
             return;
         }
-        browser_view.close_popovers ();
-        string selected_row = browser_view.get_selected_row_name ();
+        main_view.close_popovers ();
+        string selected_row = main_view.get_selected_row_name ();
         if (selected_row.has_suffix ("/"))
             reset_path ((!) selected_row, true);
         else
-            browser_view.set_selected_to_default ();
+            main_view.set_selected_to_default ();
     }
 
     /*\
@@ -894,12 +894,17 @@ private class DConfWindow : BrowserWindow
     * * Path requests
     \*/
 
+    protected override void reconfigure_search ()
+    {
+        main_view.set_search_parameters (saved_view, ((DConfHeaderBar) headerbar).get_bookmarks ());
+    }
+
     protected override void close_in_window_panels ()
     {
         hide_notification ();
         headerbar.close_popovers ();
         revealer.hide_modifications_list ();
-        if (browser_view.in_window_bookmarks || browser_view.in_window_modifications || in_window_about)
+        if (main_view.in_window_bookmarks || main_view.in_window_modifications || in_window_about)
             show_default_view ();
     }
 
@@ -910,7 +915,7 @@ private class DConfWindow : BrowserWindow
 
     protected override void request_config (string full_name)
     {
-        browser_view.prepare_object_view (full_name, ModelUtils.folder_context_id,
+        main_view.prepare_object_view (full_name, ModelUtils.folder_context_id,
                                           model.get_folder_properties (full_name),
                                           true);
         update_current_path (ViewType.CONFIG, strdup (full_name));
@@ -926,13 +931,13 @@ private class DConfWindow : BrowserWindow
         if (notify_missing && (fallback_path != full_name))
             cannot_find_folder (full_name); // do not place after, full_name is in some cases changed by 
set_directory()...
 
-        browser_view.prepare_folder_view (create_key_model (fallback_path, model.get_children 
(fallback_path, true, true)), current_path.has_prefix (fallback_path));
+        main_view.prepare_folder_view (create_key_model (fallback_path, model.get_children (fallback_path, 
true, true)), current_path.has_prefix (fallback_path));
         update_current_path (ViewType.FOLDER, fallback_path);
 
         if (selected_or_empty == "")
-            browser_view.select_row (headerbar.get_selected_child (fallback_path));
+            main_view.select_row (headerbar.get_selected_child (fallback_path));
         else
-            browser_view.select_row (selected_or_empty);
+            main_view.select_row (selected_or_empty);
 
         stop_search ();
         // headerbar.search_mode_enabled = false; // do last to avoid flickering RegistryView before 
PropertiesView when selecting a search result
@@ -978,7 +983,7 @@ private class DConfWindow : BrowserWindow
         }
         else
         {
-            browser_view.prepare_object_view (full_name, context_id,
+            main_view.prepare_object_view (full_name, context_id,
                                               model.get_key_properties (full_name, context_id, 0),
                                               current_path == ModelUtils.get_parent_path (full_name));
             update_current_path (ViewType.OBJECT, strdup (full_name));
@@ -1000,10 +1005,10 @@ private class DConfWindow : BrowserWindow
             return true;
         if (revealer.handle_copy_text (out copy_text))      // for delayed settings popovers
             return true;
-        if (browser_view.handle_copy_text (out copy_text))  // for in-window panels and for content
+        if (main_view.handle_copy_text (out copy_text))  // for in-window panels and for content
             return true;
         if (current_type == ViewType.OBJECT)
-            copy_text = model.get_suggested_key_copy_text (current_path, browser_view.last_context_id);
+            copy_text = model.get_suggested_key_copy_text (current_path, main_view.last_context_id);
         if (BaseWindow.is_empty_text (copy_text))
             copy_text = current_path;
         return true;
@@ -1015,7 +1020,7 @@ private class DConfWindow : BrowserWindow
 
         if (headerbar.search_mode_enabled)
         {
-            if (!browser_view.handle_alt_copy_text (out copy_text))
+            if (!main_view.handle_alt_copy_text (out copy_text))
                 copy_text = saved_view;
         }
         else
diff --git a/editor/key-editor-child.vala b/editor/key-editor-child.vala
new file mode 100644
index 0000000..8a5b8fa
--- /dev/null
+++ b/editor/key-editor-child.vala
@@ -0,0 +1,950 @@
+/*
+  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;
+
+private interface KeyEditorChild : Widget
+{
+    internal signal void value_has_changed (bool is_valid);
+
+    internal abstract Variant get_variant ();
+    internal signal void child_activated ();
+
+    internal abstract void reload (Variant gvariant);
+
+    protected const string out_of_range_text = _("Given value is out of range.");
+
+    /*\
+    * * for entries and textviews
+    \*/
+
+    protected static void set_lock_on (Object buffer, ulong deleted_text_handler, ulong 
inserted_text_handler)
+        requires (deleted_text_handler != 0)
+        requires (inserted_text_handler != 0)
+    {
+        SignalHandler.block (buffer, deleted_text_handler);
+        SignalHandler.block (buffer, inserted_text_handler);
+    }
+    protected static void set_lock_off (Object buffer, ulong deleted_text_handler, ulong 
inserted_text_handler)
+        requires (deleted_text_handler != 0)
+        requires (inserted_text_handler != 0)
+    {
+        SignalHandler.unblock (buffer, deleted_text_handler);
+        SignalHandler.unblock (buffer, inserted_text_handler);
+    }
+}
+
+private class KeyEditorChildSingle : Label, KeyEditorChild
+{
+    private Variant variant;
+
+    internal KeyEditorChildSingle (Variant key_value, string text)
+    {
+        variant = key_value;
+        set_label (text);
+        show ();
+    }
+
+    internal Variant get_variant ()
+    {
+        return variant;
+    }
+
+    internal void reload (Variant gvariant) {}
+}
+
+private class KeyEditorChildEnum : MenuButton, KeyEditorChild
+{
+    private Variant variant;
+    private GLib.Action action;
+
+    internal KeyEditorChildEnum (Variant initial_value, bool delay_mode, bool has_planned_change, Variant 
range_content)
+    {
+        this.visible = true;
+        this.hexpand = true;
+        this.halign = Align.START;
+        this.use_popover = true;
+        this.width_request = 100;
+
+        ContextPopover popover = new ContextPopover ();
+        action = popover.create_buttons_list (false, delay_mode, has_planned_change, "<enum>", 
range_content, initial_value);
+        popover.set_relative_to (this);
+
+        popover.value_changed.connect (on_popover_value_changed);
+        reload (initial_value);
+        this.set_popover ((Popover) popover);
+    }
+    private void on_popover_value_changed (ContextPopover _popover, Variant? gvariant)
+        requires (gvariant != null)
+    {
+        reload ((!) gvariant);
+        _popover.closed ();
+
+        value_has_changed (true);
+    }
+
+    internal Variant get_variant ()
+    {
+        return variant;
+    }
+
+    internal void reload (Variant gvariant)
+    {
+        variant = gvariant;
+        VariantType type = gvariant.get_type ();
+        label = type == VariantType.STRING ? gvariant.get_string () : gvariant.print (false);
+        action.change_state (new Variant.maybe (null, new Variant.maybe (type, gvariant)));
+    }
+}
+
+private class KeyEditorChildFlags : Grid, KeyEditorChild
+{
+    private string [] all_flags;
+    private ContextPopover popover = new ContextPopover ();
+
+    private Variant variant;
+    private Label label = new Label ("");
+
+    internal KeyEditorChildFlags (Variant initial_value, string [] _all_flags)
+    {
+        all_flags = _all_flags;
+        this.visible = true;
+        this.hexpand = true;
+        this.orientation = Orientation.HORIZONTAL;
+        this.column_spacing = 8;
+
+        MenuButton button = new MenuButton ();  // TODO change icon when popover will go up
+        button.visible = true;
+        button.use_popover = true;
+        button.halign = Align.START;
+        button.get_style_context ().add_class ("image-button");
+        this.add (button);
+
+        label.visible = true;
+        label.halign = Align.START;
+        label.hexpand = true;
+        this.add (label);
+
+        popover.create_flags_list (initial_value.get_strv (), all_flags);
+        popover.set_relative_to (button);
+        popover.value_changed.connect (on_popover_value_changed);
+        reload (initial_value);
+        button.set_popover ((Popover) popover);
+    }
+    private void on_popover_value_changed (Popover _popover, Variant? gvariant)
+        requires (gvariant != null)
+    {
+        reload ((!) gvariant);
+        value_has_changed (true);
+    }
+
+    internal void update_flags (string [] active_flags)
+    {
+        foreach (string flag in all_flags)
+            popover.update_flag_status (flag, flag in active_flags);
+    }
+
+    internal Variant get_variant ()
+    {
+        return variant;
+    }
+
+    internal void reload (Variant gvariant)
+    {
+        this.variant = gvariant;
+        label.label = gvariant.print (false);
+    }
+}
+
+private class KeyEditorChildNullableBool : MenuButton, KeyEditorChild
+{
+    private Variant variant;
+    private Variant? maybe_variant;
+    private GLib.Action action;
+
+    internal KeyEditorChildNullableBool (Variant initial_value, bool delay_mode, bool has_planned_change, 
bool has_schema)
+    {
+        this.visible = true;
+        this.hexpand = true;
+        this.halign = Align.START;
+        this.use_popover = true;
+        this.width_request = 100;
+
+        Variant? meaningless_variant_or_null = null;  // only used for adding or not "set to default"
+        if (has_schema)
+            meaningless_variant_or_null = new Variant.boolean (true);
+
+        ContextPopover popover = new ContextPopover ();
+        action = popover.create_buttons_list (false, delay_mode, has_planned_change, "mb", 
meaningless_variant_or_null, initial_value);
+        popover.set_relative_to (this);
+
+        popover.value_changed.connect (on_popover_value_changed);
+        reload (initial_value);
+        this.set_popover ((Popover) popover);
+    }
+    private void on_popover_value_changed (Popover _popover, Variant? gvariant)
+        requires (gvariant != null)
+    {
+        reload ((!) gvariant);
+        _popover.closed ();
+
+        value_has_changed (true);
+    }
+
+    internal Variant get_variant ()
+    {
+        return variant;
+    }
+
+    internal void reload (Variant gvariant)
+    {
+        variant = gvariant;
+        maybe_variant = variant.get_maybe ();
+
+        if (maybe_variant == null)
+            label = Key.cool_boolean_text_value (null);
+        else
+            label = Key.cool_boolean_text_value (((!) maybe_variant).get_boolean ());
+
+        action.change_state (new Variant.maybe (null, new Variant.maybe (new VariantType ("mb"), gvariant)));
+    }
+}
+
+private class KeyEditorChildBool : Box, KeyEditorChild // might be managed by action, but can't find a way 
to ensure one-and-only-one button is active  // https://bugzilla.gnome.org/show_bug.cgi?id=769876
+{
+    private ToggleButton button_true;
+
+    internal KeyEditorChildBool (bool initial_value)
+    {
+        this.visible = true;
+        this.hexpand = true;
+        this.orientation = Orientation.HORIZONTAL;
+        this.halign = Align.START;
+        this.homogeneous = true;
+        this.width_request = 100;
+        this.get_style_context ().add_class ("linked");
+
+        ToggleButton button_false = new ToggleButton ();
+        button_false.visible = true;
+        button_false.label = Key.cool_boolean_text_value (false);
+        this.add (button_false);
+
+        button_true = new ToggleButton ();
+        button_true.visible = true;
+        button_true.label = Key.cool_boolean_text_value (true);
+        this.add (button_true);
+
+        button_true.active = initial_value;
+        button_true.bind_property ("active", button_false, "active", 
BindingFlags.INVERT_BOOLEAN|BindingFlags.SYNC_CREATE|BindingFlags.BIDIRECTIONAL);
+
+        button_true.toggled.connect (() => value_has_changed (true));
+    }
+
+    internal Variant get_variant ()
+    {
+        return new Variant.boolean (button_true.active);
+    }
+
+    internal void reload (Variant gvariant)
+    {
+        button_true.active = gvariant.get_boolean ();
+    }
+}
+
+private abstract class KeyEditorChildNumberCustom : Entry, KeyEditorChild
+{
+    protected Variant variant;
+
+    protected ulong deleted_text_handler = 0;
+    protected ulong inserted_text_handler = 0;
+
+    construct
+    {
+        get_style_context ().add_class ("key-editor-child-entry");
+    }
+
+    protected void connect_entry ()
+    {
+        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
+        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed (test_value ()));
+        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed (test_value ()));
+        ulong entry_activated_handler = activate.connect (() => { if (test_value ()) child_activated (); });
+        ulong entry_sensitive_handler = notify ["sensitive"].connect (set_error_class);
+
+        destroy.connect (() => {
+                ref_buffer.disconnect (deleted_text_handler);
+                ref_buffer.disconnect (inserted_text_handler);
+                disconnect (entry_activated_handler);
+                disconnect (entry_sensitive_handler);
+            });
+    }
+
+    private bool value_has_error = false;
+    private void set_error_class ()
+    {
+        StyleContext context = get_style_context ();
+        if (value_has_error)
+        {
+            if (is_sensitive ())
+            {
+                if (!context.has_class ("error"))
+                    context.add_class ("error");
+            }
+            else if (context.has_class ("error"))
+                context.remove_class ("error");
+        }
+        else if (is_sensitive () && context.has_class ("error"))
+            context.remove_class ("error");
+    }
+
+    protected void show_error (bool show)
+    {
+        value_has_error = show;
+        if (show)
+            secondary_icon_name = "dialog-error-symbolic";
+        else
+            set_icon_from_icon_name (EntryIconPosition.SECONDARY, null);
+        set_error_class ();
+    }
+
+    internal Variant get_variant ()
+    {
+        return variant;
+    }
+
+    internal void reload (Variant gvariant)
+    {
+        KeyEditorChild.set_lock_on (buffer, deleted_text_handler, inserted_text_handler);
+        this.text = gvariant.print (false);
+        if (!test_value ())
+            assert_not_reached ();
+        KeyEditorChild.set_lock_off (buffer, deleted_text_handler, inserted_text_handler);
+    }
+
+    protected abstract bool test_value ();
+}
+
+private class KeyEditorChildNumberDouble : KeyEditorChildNumberCustom
+{
+    private double min;
+    private double max;
+
+    internal KeyEditorChildNumberDouble (Variant initial_value, Variant? range_content_or_null)
+    {
+        this.variant = initial_value;
+
+        this.visible = true;
+        this.hexpand = true;
+        this.secondary_icon_activatable = false;
+
+        this.text = initial_value.print (false);
+
+        if (range_content_or_null != null)
+        {
+            min = ((!) range_content_or_null).get_child_value (0).get_double ();
+            max = ((!) range_content_or_null).get_child_value (1).get_double ();
+        }
+        else
+        {
+            min = -double.MAX; // https://gitlab.gnome.org/GNOME/vala/issues/680
+            max = double.MAX;
+        }
+
+        connect_entry ();
+    }
+
+    private void switch_icon_tooltip_text (bool range_error)
+    {
+        if (range_error)
+            set_icon_tooltip_text (EntryIconPosition.SECONDARY, out_of_range_text);
+        else
+            set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("Failed to parse as double."));
+    }
+
+    protected override bool test_value ()
+    {
+        Variant? tmp_variant;
+        string tmp_text = this.text; // don't put in the try{} for correct C code
+        try
+        {
+            tmp_variant = Variant.parse (VariantType.DOUBLE, tmp_text);
+        }
+        catch (VariantParseError e)
+        {
+            switch_icon_tooltip_text (false);
+            show_error (true);
+            return false;
+        }
+
+        double variant_value = ((!) tmp_variant).get_double ();
+        if ((variant_value < min) || (variant_value > max))
+        {
+            switch_icon_tooltip_text (true);
+            show_error (true);
+            return false;
+        }
+        else
+        {
+            variant = (!) tmp_variant;
+            show_error (false);
+            return true;
+        }
+    }
+}
+
+private class KeyEditorChildNumberInt64 : KeyEditorChildNumberCustom
+{
+    private int64 min;
+    private int64 max;
+
+    internal KeyEditorChildNumberInt64 (Variant initial_value, Variant? range_content_or_null)
+    {
+        this.variant = initial_value;
+
+        this.visible = true;
+        this.hexpand = true;
+        this.secondary_icon_activatable = false;
+
+        this.text = initial_value.print (false);
+
+        if (range_content_or_null != null)
+        {
+            min = ((!) range_content_or_null).get_child_value (0).get_int64 ();
+            max = ((!) range_content_or_null).get_child_value (1).get_int64 ();
+        }
+        else
+        {
+            min = int64.MIN;
+            max = int64.MAX;
+        }
+
+        connect_entry ();
+    }
+
+    private void switch_icon_tooltip_text (bool range_error)
+    {
+        if (range_error)
+            set_icon_tooltip_text (EntryIconPosition.SECONDARY, out_of_range_text);
+        else
+            set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("Failed to parse as 64-bit integer."));
+    }
+
+    protected override bool test_value ()
+    {
+        Variant? tmp_variant;
+        string tmp_text = this.text; // don't put in the try{} for correct C code
+        try
+        {
+            tmp_variant = Variant.parse (VariantType.INT64, tmp_text);
+        }
+        catch (VariantParseError e)
+        {
+            switch_icon_tooltip_text (false);
+            show_error (true);
+            return false;
+        }
+
+        int64 variant_value = ((!) tmp_variant).get_int64 ();
+        if ((variant_value < min) || (variant_value > max))
+        {
+            switch_icon_tooltip_text (true);
+            show_error (true);
+            return false;
+        }
+        else
+        {
+            variant = (!) tmp_variant;
+            show_error (false);
+            return true;
+        }
+    }
+}
+
+private class KeyEditorChildNumberUint64 : KeyEditorChildNumberCustom
+{
+    private uint64 min;
+    private uint64 max;
+
+    internal KeyEditorChildNumberUint64 (Variant initial_value, Variant? range_content_or_null)
+    {
+        this.variant = initial_value;
+
+        this.visible = true;
+        this.hexpand = true;
+        this.secondary_icon_activatable = false;
+
+        this.text = initial_value.print (false);
+
+        if (range_content_or_null != null)
+        {
+            min = ((!) range_content_or_null).get_child_value (0).get_uint64 ();
+            max = ((!) range_content_or_null).get_child_value (1).get_uint64 ();
+        }
+        else
+        {
+            min = uint64.MIN;
+            max = uint64.MAX;
+        }
+
+        connect_entry ();
+    }
+
+    private void switch_icon_tooltip_text (bool range_error)
+    {
+        if (range_error)
+            set_icon_tooltip_text (EntryIconPosition.SECONDARY, out_of_range_text);
+        else
+            set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("Failed to parse as unsigned 64-bit 
integer."));
+    }
+
+    protected override bool test_value ()
+    {
+        Variant? tmp_variant;
+        string tmp_text = this.text; // don't put in the try{} for correct C code
+        try
+        {
+            tmp_variant = Variant.parse (VariantType.UINT64, tmp_text);
+        }
+        catch (VariantParseError e)
+        {
+            switch_icon_tooltip_text (false);
+            show_error (true);
+            return false;
+        }
+
+        uint64 variant_value = ((!) tmp_variant).get_uint64 ();
+        if ((variant_value < min) || (variant_value > max))
+        {
+            switch_icon_tooltip_text (true);
+            show_error (true);
+            return false;
+        }
+        else
+        {
+            variant = (!) tmp_variant;
+            show_error (false);
+            return true;
+        }
+    }
+}
+
+private class KeyEditorChildNumberInt : SpinButton, KeyEditorChild
+{
+    private Variant variant;
+
+    private string key_type;
+
+    private int64 min_int64;
+    private int64 max_int64;
+
+    private ulong deleted_text_handler = 0;
+    private ulong inserted_text_handler = 0;
+
+    internal KeyEditorChildNumberInt (Variant initial_value, string type_string, Variant? 
range_content_or_null)
+        requires (type_string == "y" || type_string == "n" || type_string == "q" || type_string == "i" || 
type_string == "u" || type_string == "h")     // "x" and "t" are managed elsewhere
+    {
+        this.variant = initial_value;
+        this.key_type = type_string;
+
+        this.visible = true;
+        this.hexpand = true;
+        this.halign = Align.START;
+
+        double min_double, max_double;
+        if (range_content_or_null != null)
+        {
+            Variant min_variant = ((!) range_content_or_null).get_child_value (0);
+            Variant max_variant = ((!) range_content_or_null).get_child_value (1);
+            min_double = get_variant_as_double (min_variant);
+            max_double = get_variant_as_double (max_variant);
+            min_int64  = get_variant_as_int64  (min_variant);
+            max_int64  = get_variant_as_int64  (max_variant);
+        }
+        else
+        {
+            get_min_and_max_double (out min_double, out max_double, type_string);
+            get_min_and_max_int64  (out min_int64,  out max_int64,  type_string);
+        }
+
+        Adjustment adjustment = new Adjustment (get_variant_as_double (initial_value), min_double, 
max_double, 1.0, 5.0, 0.0);
+        this.configure (adjustment, 1.0, 0);
+
+        this.update_policy = SpinButtonUpdatePolicy.IF_VALID;
+        this.snap_to_ticks = true;
+        this.numeric = true;
+        this.input_purpose = InputPurpose.NUMBER;   // TODO could be DIGITS for UnsignedInt
+        this.max_width_chars = 30;
+
+        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
+        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed (test_value ()));
+        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed (test_value ()));
+        ulong entry_activated_handler = activate.connect (() => { if (test_value ()) child_activated (); 
update (); });
+        ulong entry_sensitive_handler = notify ["sensitive"].connect (set_error_class);
+
+        destroy.connect (() => {
+                ref_buffer.disconnect (deleted_text_handler);
+                ref_buffer.disconnect (inserted_text_handler);
+                disconnect (entry_activated_handler);
+                disconnect (entry_sensitive_handler);
+            });
+    }
+
+    private static void get_min_and_max_double (out double min, out double max, string variant_type)
+    {
+        switch (variant_type)
+        {
+            case "y": min = (double) uint8.MIN;     max = (double) uint8.MAX;   break;
+            case "n": min = (double) int16.MIN;     max = (double) int16.MAX;   break;
+            case "q": min = (double) uint16.MIN;    max = (double) uint16.MAX;  break;
+            case "i": min = (double) int32.MIN;     max = (double) int32.MAX;   break;
+            case "u": min = (double) uint32.MIN;    max = (double) uint32.MAX;  break;
+            case "h": min = (double) int32.MIN;     max = (double) int32.MAX;   break;
+            default: assert_not_reached ();
+        }
+    }
+    private static void get_min_and_max_int64 (out int64 min, out int64 max, string variant_type)
+    {
+        switch (variant_type)
+        {
+            case "y": min = (int64) uint8.MIN;      max = (int64) uint8.MAX;    break;
+            case "n": min = (int64) int16.MIN;      max = (int64) int16.MAX;    break;
+            case "q": min = (int64) uint16.MIN;     max = (int64) uint16.MAX;   break;
+            case "i": min = (int64) int32.MIN;      max = (int64) int32.MAX;    break;
+            case "u": min = (int64) uint32.MIN;     max = (int64) uint32.MAX;   break;
+            case "h": min = (int64) int32.MIN;      max = (int64) int32.MAX;    break;
+            default: assert_not_reached ();
+        }
+    }
+
+    private static double get_variant_as_double (Variant variant)
+    {
+        switch (variant.classify ())
+        {
+            case Variant.Class.BYTE:    return (double) variant.get_byte ();
+            case Variant.Class.INT16:   return (double) variant.get_int16 ();
+            case Variant.Class.UINT16:  return (double) variant.get_uint16 ();
+            case Variant.Class.INT32:   return (double) variant.get_int32 ();
+            case Variant.Class.UINT32:  return (double) variant.get_uint32 ();
+            case Variant.Class.HANDLE:  return (double) variant.get_handle ();
+            default: assert_not_reached ();
+        }
+    }
+    private static int64 get_variant_as_int64 (Variant variant)
+    {
+        switch (variant.classify ())
+        {
+            case Variant.Class.BYTE:    return (int64) variant.get_byte ();
+            case Variant.Class.INT16:   return (int64) variant.get_int16 ();
+            case Variant.Class.UINT16:  return (int64) variant.get_uint16 ();
+            case Variant.Class.INT32:   return (int64) variant.get_int32 ();
+            case Variant.Class.UINT32:  return (int64) variant.get_uint32 ();
+            case Variant.Class.HANDLE:  return (int64) variant.get_handle ();
+            default: assert_not_reached ();
+        }
+    }
+
+    internal Variant get_variant ()
+    {
+        return variant;
+    }
+
+    internal void reload (Variant gvariant)       // TODO "key_editor_child_number_int_real_reload: 
assertion 'gvariant != NULL' failed" two times when ghosting a key
+    {
+        KeyEditorChild.set_lock_on (buffer, deleted_text_handler, inserted_text_handler);
+        this.set_value (get_variant_as_double (gvariant));
+        KeyEditorChild.set_lock_off (buffer, deleted_text_handler, inserted_text_handler);
+    }
+
+    private bool value_has_error = false;
+    private void set_error_class ()
+    {
+        StyleContext context = get_style_context ();
+        if (is_sensitive ())
+        {
+            if (value_has_error)
+            {
+                if (!context.has_class ("error"))
+                    context.add_class ("error");
+            }
+            else if (context.has_class ("error"))
+                context.remove_class ("error");
+        }
+        else if (context.has_class ("error"))
+            context.remove_class ("error");
+    }
+
+    private bool test_value ()
+    {
+        Variant? tmp_variant;
+        string tmp_text = this.text; // don't put in the try{} for correct C code
+        try
+        {
+            tmp_variant = Variant.parse (VariantType.INT64, tmp_text);
+        }
+        catch (VariantParseError e)
+        {
+            value_has_error = true;
+            set_error_class ();
+            return false;
+        }
+
+        int64 variant_value = ((!) tmp_variant).get_int64 ();
+        if ((variant_value < min_int64) || (variant_value > max_int64))
+        {
+            value_has_error = true;
+            set_error_class ();
+
+            return false;
+        }
+        else
+        {
+            value_has_error = false;
+            set_error_class ();
+
+            variant = get_variant_from_int64 (variant_value, key_type);
+            return true;
+        }
+    }
+    private static Variant get_variant_from_int64 (int64 int64_value, string key_type)
+    {
+        switch (key_type)
+        {
+            case "y": return new Variant.byte   ((uint8)  int64_value);
+            case "n": return new Variant.int16  ((int16)  int64_value);
+            case "q": return new Variant.uint16 ((uint16) int64_value);
+            case "i": return new Variant.int32  ((int32)  int64_value);
+            case "u": return new Variant.uint32 ((uint32) int64_value);
+            case "h": return new Variant.handle ((int32)  int64_value);
+            default: assert_not_reached ();
+        }
+    }
+}
+
+private class KeyEditorChildArray : Grid, KeyEditorChild
+{
+    private TextView text_view;
+    private Revealer error_revealer;
+    private string key_type;
+    private Variant variant;
+
+    private ulong deleted_text_handler = 0;
+    private ulong inserted_text_handler = 0;
+
+    construct
+    {
+        get_style_context ().add_class ("key-editor-child-array");
+    }
+
+    internal KeyEditorChildArray (string type_string, Variant initial_value)
+    {
+        this.visible = true;
+        this.hexpand = true;
+        this.vexpand = false;
+        orientation = Orientation.VERTICAL;
+        get_style_context ().add_class ("frame");
+
+        this.key_type = type_string;
+        this.variant = initial_value;
+
+        ScrolledWindow scrolled_window = new ScrolledWindow (null, null);
+        scrolled_window.visible = true;
+
+        text_view = new TextView ();
+        text_view.visible = true;
+        text_view.expand = true;
+        text_view.wrap_mode = WrapMode.WORD;
+        text_view.monospace = true;
+        text_view.key_press_event.connect (on_key_press_event);
+        // https://bugzilla.gnome.org/show_bug.cgi?id=789676
+        text_view.button_press_event.connect_after (event_stop);
+        text_view.button_release_event.connect_after (event_stop);
+
+        scrolled_window.add (text_view);
+        add (scrolled_window);
+
+        error_revealer = new Revealer ();
+        error_revealer.visible = true;
+        error_revealer.transition_type = RevealerTransitionType.SLIDE_UP;
+        error_revealer.reveal_child = false;
+        add (error_revealer);
+
+        ActionBar error_bar = new ActionBar ();
+        error_bar.visible = true;
+        error_revealer.add (error_bar);
+
+        Image error_icon = new Image.from_icon_name ("dialog-error-symbolic", IconSize.BUTTON);
+        error_icon.visible = true;
+        error_bar.pack_start (error_icon);
+
+        Label error_label = new Label (_("This value is invalid for the key type."));
+        error_label.visible = true;
+        error_label.wrap = true;
+        error_bar.pack_start (error_label);
+
+        text_view.buffer.text = initial_value.print (false);
+
+        TextBuffer ref_buffer = text_view.buffer;    // an TextBuffer doesn't emit a "destroy" signal
+        deleted_text_handler = ref_buffer.delete_range.connect_after (() => value_has_changed (test_value 
()));
+        inserted_text_handler = ref_buffer.insert_text.connect_after (() => value_has_changed (test_value 
()));
+        destroy.connect (() => {
+                ref_buffer.disconnect (deleted_text_handler);
+                ref_buffer.disconnect (inserted_text_handler);
+            });
+    }
+    private bool on_key_press_event (Gdk.EventKey event)
+    {
+        string keyval_name = (!) (Gdk.keyval_name (event.keyval) ?? "");
+        if ((keyval_name == "Return" || keyval_name == "KP_Enter")
+        && ((event.state & Gdk.ModifierType.MODIFIER_MASK) == 0)
+        && (test_value ()))
+        {
+            child_activated ();
+            return Gdk.EVENT_STOP;
+        }
+        return base.key_press_event (event);
+    }
+    private static bool event_stop ()
+    {
+        return Gdk.EVENT_STOP;
+    }
+
+    private bool test_value ()
+    {
+        string tmp_text = text_view.buffer.text; // don't put in the try{} for correct C code
+        try
+        {
+            Variant? tmp_variant = Variant.parse (new VariantType (key_type), tmp_text);
+            variant = (!) tmp_variant;
+
+            StyleContext context = get_style_context ();
+            if (context.has_class ("error"))
+                context.remove_class ("error");
+            error_revealer.reveal_child = false;
+
+            return true;
+        }
+        catch (VariantParseError e)
+        {
+            StyleContext context = get_style_context ();
+            if (!context.has_class ("error"))
+                context.add_class ("error");
+            error_revealer.reveal_child = true;
+
+            return false;
+        }
+    }
+
+    internal Variant get_variant ()
+    {
+        return variant;
+    }
+
+    internal void reload (Variant gvariant)
+    {
+        KeyEditorChild.set_lock_on (text_view.buffer, deleted_text_handler, inserted_text_handler);
+        text_view.buffer.text = gvariant.print (false);
+        if (!test_value ())
+            assert_not_reached ();
+        KeyEditorChild.set_lock_off (text_view.buffer, deleted_text_handler, inserted_text_handler);
+    }
+}
+
+private class KeyEditorChildDefault : Entry, KeyEditorChild
+{
+    private string key_type;
+    private Variant variant;
+    private bool is_string;
+
+    private ulong deleted_text_handler = 0;
+    private ulong inserted_text_handler = 0;
+
+    construct
+    {
+        get_style_context ().add_class ("key-editor-child-entry");
+    }
+
+    internal KeyEditorChildDefault (string type_string, Variant initial_value)
+    {
+        this.key_type = type_string;
+        this.variant = initial_value;
+
+        this.visible = true;
+        this.hexpand = true;
+        this.secondary_icon_activatable = false;
+        this.set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("This value is invalid for the key 
type."));
+
+        this.is_string = type_string == "s" || type_string == "o" || type_string == "g";
+        this.text = is_string ? initial_value.get_string () : initial_value.print (false);
+
+        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
+        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed (test_value ()));
+        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed (test_value ()));
+        ulong entry_activate_handler = activate.connect (() => { if (test_value ()) child_activated (); });
+
+        destroy.connect (() => {
+                ref_buffer.disconnect (deleted_text_handler);
+                ref_buffer.disconnect (inserted_text_handler);
+                disconnect (entry_activate_handler);
+            });
+    }
+
+    private bool test_value ()
+    {
+        if (key_type == "s")
+        {
+            variant = new Variant.string (this.text);
+            return true;
+        }
+
+        string tmp_text = is_string ? @"'$text'" : this.text; // don't put in the try{} for correct C code
+        try
+        {
+            Variant? tmp_variant = Variant.parse (new VariantType (key_type), tmp_text);
+            variant = (!) tmp_variant;
+
+            StyleContext context = get_style_context ();
+            if (context.has_class ("error"))
+                context.remove_class ("error");
+            set_icon_from_icon_name (EntryIconPosition.SECONDARY, null);
+
+            return true;
+        }
+        catch (VariantParseError e)
+        {
+            StyleContext context = get_style_context ();
+            if (!context.has_class ("error"))
+                context.add_class ("error");
+            secondary_icon_name = "dialog-error-symbolic";
+
+            return false;
+        }
+    }
+
+    internal Variant get_variant ()
+    {
+        return variant;
+    }
+
+    internal void reload (Variant gvariant)
+    {
+        KeyEditorChild.set_lock_on (buffer, deleted_text_handler, inserted_text_handler);
+        this.text = is_string ? gvariant.get_string () : gvariant.print (false);
+        if (!test_value ())
+            assert_not_reached ();
+        KeyEditorChild.set_lock_off (buffer, deleted_text_handler, inserted_text_handler);
+    }
+}
diff --git a/editor/key-list-box-row.vala b/editor/key-list-box-row.vala
index 6bc1a80..6f0256b 100644
--- a/editor/key-list-box-row.vala
+++ b/editor/key-list-box-row.vala
@@ -315,7 +315,7 @@ private class KeyListBoxRow : ClickableListBoxRow, AdaptativeWidget
             if (has_schema)
             {
                 variant = new Variant ("(sq)", full_name, context_id);
-                actionable.set_detailed_action_name ("bro.set-to-default(" + variant.print (true) + ")");
+                actionable.set_detailed_action_name ("view.set-to-default(" + variant.print (true) + ")");
             }
             else
             {
@@ -326,7 +326,7 @@ private class KeyListBoxRow : ClickableListBoxRow, AdaptativeWidget
         else
         {
             variant = new Variant ("(sqv)", full_name, context_id, (!) new_value);
-            actionable.set_detailed_action_name ("bro.set-key-value(" + variant.print (true) + ")");
+            actionable.set_detailed_action_name ("view.set-key-value(" + variant.print (true) + ")");
         }
         Container child = (Container) get_child ();
         child.add (actionable);
diff --git a/editor/meson.build b/editor/meson.build
index 4b5f747..da15164 100644
--- a/editor/meson.build
+++ b/editor/meson.build
@@ -87,6 +87,7 @@ sources = files(
   'dconf-view.vala',
   'dconf-window.vala',
   'delayed-setting-view.vala',
+  'key-editor-child.vala',
   'key-list-box-row.vala',
   'large-pathbar.vala',
   'model-utils.vala',
diff --git a/editor/registry-info.ui b/editor/registry-info.ui
index 69e580a..40f1bbc 100644
--- a/editor/registry-info.ui
+++ b/editor/registry-info.ui
@@ -110,7 +110,7 @@
                       <object class="GtkButton" id="erase_button">
                         <property name="visible">True</property>
                         <property name="label" translatable="yes">Erase key</property>
-                        <property name="action-name">bro.delay-erase</property>
+                        <property name="action-name">view.delay-erase</property>
                         <property name="action-target">''</property>
                       </object>
                     </child>
diff --git a/editor/registry-list.vala b/editor/registry-list.vala
index d163363..ac0837f 100644
--- a/editor/registry-list.vala
+++ b/editor/registry-list.vala
@@ -595,7 +595,7 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
         {
             bool key_value_boolean = key_value.get_boolean ();
             Variant switch_variant = new Variant ("(sqbb)", row.full_name, row.context_id, 
!key_value_boolean, key_value_boolean ? is_key_default : !is_key_default);
-            row.update_switch (key_value_boolean, "bro.toggle-gsettings-key-switch(" + switch_variant.print 
(true) + ")");
+            row.update_switch (key_value_boolean, "view.toggle-gsettings-key-switch(" + switch_variant.print 
(true) + ")");
         }
 
         StyleContext css_context = row.get_style_context ();
@@ -626,7 +626,7 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
             {
                 bool key_value_boolean = ((!) key_value).get_boolean ();
                 Variant switch_variant = new Variant ("(sb)", row.full_name, !key_value_boolean);
-                row.update_switch (key_value_boolean, "bro.toggle-dconf-key-switch(" + switch_variant.print 
(false) + ")");
+                row.update_switch (key_value_boolean, "view.toggle-dconf-key-switch(" + switch_variant.print 
(false) + ")");
                 row.use_switch (true);
             }
 
@@ -1001,7 +1001,7 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
             popover.new_section ();
 
             if (!is_key_default)
-                popover.new_gaction ("default2", "bro.set-to-default(" + variant_sq.print (true) + ")");
+                popover.new_gaction ("default2", "view.set-to-default(" + variant_sq.print (true) + ")");
 
             string [] all_flags = range_content.get_strv ();
             popover.create_flags_list (modifications_handler.get_key_custom_value (full_name, 
context_id).get_strv (), all_flags);
@@ -1020,12 +1020,12 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
             popover.new_gaction ("dismiss", "ui.dismiss-change(" + variant_s.print (false) + ")");
 
             if (planned_value != null)
-                popover.new_gaction ("default1", "bro.set-to-default(" + variant_sq.print (true) + ")");
+                popover.new_gaction ("default1", "view.set-to-default(" + variant_sq.print (true) + ")");
         }
         else if (!is_key_default)
         {
             popover.new_section ();
-            popover.new_gaction ("default1", "bro.set-to-default(" + variant_sq.print (true) + ")");
+            popover.new_gaction ("default1", "view.set-to-default(" + variant_sq.print (true) + ")");
         }
         properties.clear ();
         return true;


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