[dconf-editor] Introduce BrowserWindow.



commit ed6187ec9d2b5de3bde59d3e038d9d332fa490ae
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Fri Dec 7 13:03:34 2018 +0100

    Introduce BrowserWindow.

 editor/adaptative-pathbar.vala                |   13 +-
 editor/bookmarks-list.vala                    |   12 +-
 editor/bookmarks.ui                           |    2 +-
 editor/bookmarks.vala                         |    2 +-
 editor/browser-headerbar.vala                 |   35 +-
 editor/browser-view.vala                      |   11 +-
 editor/{dconf-editor.ui => browser-window.ui} |    7 +-
 editor/browser-window.vala                    |  924 +++++++++++++++++++++
 editor/dconf-editor.gresource.xml             |    2 +-
 editor/dconf-editor.vala                      |   32 +-
 editor/dconf-window.vala                      | 1098 +++++--------------------
 editor/key-list-box-row.vala                  |    6 +-
 editor/large-pathbar.ui                       |    6 +-
 editor/large-pathbar.vala                     |   25 +-
 editor/meson.build                            |    3 +-
 editor/modifications-revealer.vala            |    2 +-
 editor/pathentry.ui                           |    4 +-
 editor/pathwidget.ui                          |    4 +-
 editor/pathwidget.vala                        |    4 +
 editor/registry-list.vala                     |   36 +-
 editor/short-pathbar.vala                     |   19 +-
 21 files changed, 1284 insertions(+), 963 deletions(-)
---
diff --git a/editor/adaptative-pathbar.vala b/editor/adaptative-pathbar.vala
index b60eb91..98395b4 100644
--- a/editor/adaptative-pathbar.vala
+++ b/editor/adaptative-pathbar.vala
@@ -164,6 +164,16 @@ private class AdaptativePathbar : Stack, Pathbar, AdaptativeWidget
         assert_not_reached ();
     }
 
+    internal void get_fallback_path_and_complete_path (out string fallback_path, out string complete_path)
+    {
+        if (large_pathbar_created)
+            large_pathbar.get_fallback_path_and_complete_path (out fallback_path, out complete_path);
+        else if (short_pathbar_created)
+            short_pathbar.get_fallback_path_and_complete_path (out fallback_path, out complete_path);
+        else
+            assert_not_reached ();
+    }
+
     internal void update_ghosts (string non_ghost_path, bool is_search)
     {
         if (large_pathbar_created)
@@ -185,6 +195,7 @@ private interface Pathbar
 
     /* complex proxy calls */
     internal abstract string get_complete_path ();
+    internal abstract void get_fallback_path_and_complete_path (out string fallback_path, out string 
complete_path);
 
     internal virtual string get_selected_child (string current_path)
     {
@@ -201,6 +212,6 @@ private interface Pathbar
     /* called from inside the pathbar, by ShortPathbar and LargePathbarItem (so cannot make "protected") */
     internal static void add_copy_path_entry (ref GLib.Menu section)
     {
-        section.append (_("Copy current path"), "kbd.copy-path"); // or "app.copy(\"" + 
get_action_target_value ().get_string () + "\")"
+        section.append (_("Copy current path"), "key.copy-path"); // or "app.copy(\"" + 
get_action_target_value ().get_string () + "\")"
     }
 }
diff --git a/editor/bookmarks-list.vala b/editor/bookmarks-list.vala
index b7804c2..6ff2a2f 100644
--- a/editor/bookmarks-list.vala
+++ b/editor/bookmarks-list.vala
@@ -547,20 +547,20 @@ private class Bookmark : OverlayedListRow
         {
             case ViewType.SEARCH:
                 Variant variant = new Variant.string (bookmark_text);
-                detailed_action_name = "ui.open-search(" + variant.print (false) + ")";
-                inactive_action_name = "ui.empty('')";
+                detailed_action_name = "browser.open-search(" + variant.print (false) + ")";
+                inactive_action_name = "browser.empty('')";
                 return;
 
             case ViewType.FOLDER:
                 Variant variant = new Variant.string (bookmark_text);
-                detailed_action_name = "ui.open-folder(" + variant.print (false) + ")";
-                inactive_action_name = "ui.empty('')";
+                detailed_action_name = "browser.open-folder(" + variant.print (false) + ")";
+                inactive_action_name = "browser.empty('')";
                 return;
 
             case ViewType.OBJECT:
                 Variant variant = new Variant ("(sq)", bookmark_text, ModelUtils.undefined_context_id);  // 
TODO save context
-                detailed_action_name = "ui.open-object(" + variant.print (true) + ")";
-                inactive_action_name = "ui.empty(('',uint16 65535))";
+                detailed_action_name = "browser.open-object(" + variant.print (true) + ")";
+                inactive_action_name = "browser.empty(('',uint16 65535))";
                 return;
 
             case ViewType.CONFIG:
diff --git a/editor/bookmarks.ui b/editor/bookmarks.ui
index 3b3eae2..2b351e0 100644
--- a/editor/bookmarks.ui
+++ b/editor/bookmarks.ui
@@ -45,7 +45,7 @@
                   <object class="GtkSwitch" id="bookmarked_switch">
                     <property name="visible">True</property>
                     <property name="halign">end</property>
-                    <property name="action-name">ui.empty</property>
+                    <property name="action-name">browser.empty</property>
                     <property name="action-target">('',byte 255)</property>
                     <child internal-child="accessible">
                       <object class="AtkObject">
diff --git a/editor/bookmarks.vala b/editor/bookmarks.vala
index 1b1bd6d..d32861d 100644
--- a/editor/bookmarks.vala
+++ b/editor/bookmarks.vala
@@ -415,7 +415,7 @@ private class Bookmarks : MenuButton
     {
         if (bookmarked == bookmarked_switch.active)
             return;
-        bookmarked_switch.set_detailed_action_name ("ui.empty(('',byte 255))");
+        bookmarked_switch.set_detailed_action_name ("browser.empty(('',byte 255))");
         bookmarked_switch.active = bookmarked;
     }
 }
diff --git a/editor/browser-headerbar.vala b/editor/browser-headerbar.vala
index 73985cd..24254af 100644
--- a/editor/browser-headerbar.vala
+++ b/editor/browser-headerbar.vala
@@ -37,6 +37,20 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
     private ViewType current_type = ViewType.FOLDER;
     private string current_path = "/";
 
+    private bool _delay_mode = false;
+    internal bool delay_mode
+    {
+        private  get { return _delay_mode; }
+        internal set
+        {
+            if (_delay_mode == value)
+                return;
+            _delay_mode = value;
+            update_modifications_button ();
+            update_hamburger_menu ();
+        }
+    }
+
     internal signal void search_changed ();
     internal signal void search_stopped ();
     internal signal void update_bookmarks_icons (Variant bookmarks_variant);
@@ -69,7 +83,7 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
         disable_action_bar = _disable_popovers
                           || AdaptativeWidget.WindowSize.is_extra_flat (new_size);
 
-        update_hamburger_menu (delay_mode);
+        update_hamburger_menu ();
         update_modifications_button ();
 
         path_widget.set_window_size (new_size);
@@ -80,6 +94,10 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
     internal string text                { get { return path_widget.text; }}
 
     internal string get_complete_path ()    { return path_widget.get_complete_path (); }
+    internal void get_fallback_path_and_complete_path (out string fallback_path, out string complete_path)
+    {
+        path_widget.get_fallback_path_and_complete_path (out fallback_path, out complete_path);
+    }
     internal void toggle_pathbar_menu ()    { path_widget.toggle_pathbar_menu (); }
     internal string [] get_bookmarks ()     { return bookmarks_button.get_bookmarks (); }
 
@@ -103,6 +121,8 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
 
         path_widget.set_path (type, path);
         bookmarks_button.set_path (type, path);
+
+        update_hamburger_menu ();
     }
 
     internal bool has_popover ()
@@ -193,7 +213,7 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
         in_window_about = true;
         update_modifications_button ();
         info_button.hide ();
-        go_back_button.set_action_name ("ui.hide-in-window-about");
+        go_back_button.set_action_name ("browser.hide-in-window-about");
         go_back_button.show ();
         bookmarks_stack.hexpand = false;    // hack 1/7
         title_label.set_label (_("About"));
@@ -431,15 +451,8 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
             info_button.active = !info_button.active;
     }
 
-    private bool delay_mode = false;
-    internal void update_hamburger_menu (bool? new_delay_mode = null)
+    internal void update_hamburger_menu ()
     {
-        if (new_delay_mode != null)
-        {
-            delay_mode = (!) new_delay_mode;
-            update_modifications_button ();
-        }
-
         GLib.Menu menu = new GLib.Menu ();
 
 /*        if (current_type == ViewType.OBJECT && !ModelUtils.is_folder_path (current_path))   // TODO a 
better way to copy various representations of a key name/value/path
@@ -504,7 +517,7 @@ private class BrowserHeaderBar : HeaderBar, AdaptativeWidget
         append_or_not_night_mode_entry (night_time, dark_theme, auto_night, ref section);
         if (!disable_popovers)    // TODO else...
             section.append (_("Keyboard Shortcuts"), "win.show-help-overlay");
-        section.append (_("About Dconf Editor"), "ui.about");
+        section.append (_("About Dconf Editor"), "browser.about");
         section.freeze ();
         menu.append_section (null, section);
     }
diff --git a/editor/browser-view.vala b/editor/browser-view.vala
index 7272001..572e338 100644
--- a/editor/browser-view.vala
+++ b/editor/browser-view.vala
@@ -100,9 +100,14 @@ private class BrowserView : Stack, AdaptativeWidget
         info_bar.add_label ("soft-reload-folder", _("Sort preferences have changed. Do you want to refresh 
the view?"),
                                                   _("Refresh"), "bro.refresh-folder");
         info_bar.add_label ("hard-reload-folder", _("This folder content has changed. Do you want to reload 
the view?"),
-                                                  _("Reload"), "ui.reload-folder");
+                                                  _("Reload"), "browser.reload-folder");
         info_bar.add_label ("hard-reload-object", _("This key’s properties have changed. Do you want to 
reload the view?"),
-                                                  _("Reload"), "ui.reload-object");   // TODO also for key 
removing?
+                                                  _("Reload"), "browser.reload-object");   // TODO also for 
key removing?
+    }
+
+    internal bool is_in_in_window_mode ()
+    {
+        return (in_window_bookmarks || in_window_modifications || in_window_about);
     }
 
     /*\
@@ -206,7 +211,7 @@ private class BrowserView : Stack, AdaptativeWidget
     * * in-window about
     \*/
 
-    internal bool in_window_about                   { internal get; private set; default = false; }
+    private bool in_window_about = false;
 
     private bool about_list_created = false;
     private AboutList about_list;
diff --git a/editor/dconf-editor.ui b/editor/browser-window.ui
similarity index 84%
rename from editor/dconf-editor.ui
rename to editor/browser-window.ui
index a99d480..153d78d 100644
--- a/editor/dconf-editor.ui
+++ b/editor/browser-window.ui
@@ -1,17 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface domain="dconf-editor">
   <!-- interface-requires gtk+ 3.0 -->
-  <template class="DConfWindow" parent="AdaptativeWindow">
+  <template class="BrowserWindow" parent="AdaptativeWindow">
     <property name="visible">False</property>
-    <property name="title" translatable="yes">dconf Editor</property>
     <property name="height-request">283</property> <!-- 294 max for Purism Librem 5 landscape -->
     <property name="width-request">349</property>  <!-- 360 max for Purism Librem 5 portrait -->
     <signal name="key-press-event" handler="on_key_press_event"/>
     <signal name="button-press-event" handler="on_button_press_event"/>
     <signal name="destroy" handler="on_destroy"/>
-    <style>
-      <class name="dconf-editor"/>
-    </style>
     <child type="titlebar">
       <object class="BrowserHeaderBar" id="headerbar">
         <property name="visible">True</property>
@@ -30,7 +26,6 @@
               <object class="BrowserView" id="browser_view">
                 <property name="visible">True</property>
                 <property name="vexpand">True</property>
-                <signal name="bookmarks-selection-changed" handler="on_bookmarks_selection_changed"/>
               </object>
             </child>
           </object>
diff --git a/editor/browser-window.vala b/editor/browser-window.vala
new file mode 100644
index 0000000..8231276
--- /dev/null
+++ b/editor/browser-window.vala
@@ -0,0 +1,924 @@
+/*
+  This file is part of Dconf Editor
+
+  Dconf Editor is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  Dconf Editor is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with Dconf Editor.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+[GtkTemplate (ui = "/ca/desrt/dconf-editor/ui/browser-window.ui")]
+private abstract class BrowserWindow : AdaptativeWindow, AdaptativeWidget
+{
+    private const string root_path = "/";   // TODO allow changing that
+
+    protected string    current_path    = root_path;
+    protected ViewType  current_type    = ViewType.FOLDER;
+    protected ViewType  saved_type      = ViewType.FOLDER;
+    protected string    saved_view      = "/";
+    protected string    saved_selection = "";
+
+    [GtkChild] protected BrowserHeaderBar headerbar;
+    [GtkChild] protected BrowserView      browser_view;
+    [GtkChild] protected Grid             main_grid;
+
+    construct
+    {
+        install_browser_action_entries ();
+        install_key_action_entries ();
+
+        init_night_mode ();
+        bind_mouse_config ();
+
+        adaptative_children.append (headerbar);
+        adaptative_children.append (browser_view);
+        adaptative_children.append (notifications_revealer);
+        adaptative_children.append (this);
+    }
+
+    /*\
+    * * action entries
+    \*/
+
+    private SimpleAction disabled_state_action;
+    private SimpleAction open_path_action;
+
+    protected SimpleAction reload_search_action;
+    protected bool reload_search_next = true;
+
+    private void install_browser_action_entries ()
+    {
+        SimpleActionGroup action_group = new SimpleActionGroup ();
+        action_group.add_action_entries (browser_action_entries, this);
+        insert_action_group ("browser", action_group);
+
+        disabled_state_action = (SimpleAction) action_group.lookup_action ("disabled-state");
+        disabled_state_action.set_enabled (false);
+
+        open_path_action = (SimpleAction) action_group.lookup_action ("open-path");
+
+        reload_search_action = (SimpleAction) action_group.lookup_action ("reload-search");
+        reload_search_action.set_enabled (false);
+    }
+
+    private const GLib.ActionEntry [] browser_action_entries =
+    {
+        { "empty",          empty, "*" },
+        { "empty-null",     empty },
+        { "disabled-state", empty, "(sq)", "('',uint16 65535)" },
+
+        { "open-folder", open_folder, "s" },
+        { "open-object", open_object, "(sq)" },
+        { "open-config", open_config, "s" },
+        { "open-search", open_search, "s" },
+        { "next-search", next_search, "s" },
+        { "open-parent", open_parent, "s" },
+
+        { "open-path", open_path, "(sq)", "('/',uint16 " + ModelUtils.folder_context_id_string + ")" },
+
+        { "reload-folder", reload_folder },
+        { "reload-object", reload_object },
+        { "reload-search", reload_search },
+
+        { "hide-search",   hide_search },
+        { "show-search",   show_search },
+        { "toggle-search", toggle_search, "b", "false" },
+
+        { "hide-in-window-about",           hide_in_window_about },
+        { "about",                          about }
+    };
+
+    private void empty (/* SimpleAction action, Variant? variant */) {}
+
+    private void open_folder (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
+    {
+        close_in_window_panels ();
+
+        string full_name = ((!) path_variant).get_string ();
+
+        request_folder (full_name, "");
+    }
+
+    private void open_object (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
+    {
+        close_in_window_panels ();
+
+        string full_name;
+        uint16 context_id;
+        ((!) path_variant).@get ("(sq)", out full_name, out context_id);
+
+        request_object (full_name, context_id);
+    }
+
+    private void open_config (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
+    {
+        headerbar.close_popovers ();
+
+        string full_name = ((!) path_variant).get_string ();    // TODO use current_path instead?
+
+        request_config (full_name);
+    }
+
+    private void open_search (SimpleAction action, Variant? search_variant)
+        requires (search_variant != null)
+    {
+        close_in_window_panels ();
+
+        string search = ((!) search_variant).get_string ();
+
+        request_search (true, PathEntry.SearchMode.EDIT_PATH_SELECT_ALL, search);
+    }
+
+    private void next_search (SimpleAction action, Variant? search_variant)
+        requires (search_variant != null)
+    {
+        saved_type = ViewType.FOLDER;
+        saved_view = ((!) search_variant).get_string ();
+
+        request_search (true, PathEntry.SearchMode.EDIT_PATH_MOVE_END, saved_view);
+    }
+
+    private void open_parent (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
+    {
+        close_in_window_panels ();
+
+        string full_name = ((!) path_variant).get_string ();
+
+        request_folder (ModelUtils.get_parent_path (full_name), full_name);
+    }
+
+    private void open_path (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
+    {
+        close_in_window_panels ();
+
+        string full_name;
+        uint16 context_id;
+        ((!) path_variant).@get ("(sq)", out full_name, out context_id);
+
+        action.set_state ((!) path_variant);
+
+        if (ModelUtils.is_folder_context_id (context_id))
+            request_folder (full_name, "");
+        else
+            request_object (full_name, context_id);
+    }
+
+    private void reload_folder (/* SimpleAction action, Variant? path_variant */)
+    {
+        request_folder (current_path, browser_view.get_selected_row_name ());
+    }
+
+    private void reload_object (/* SimpleAction action, Variant? path_variant */)
+    {
+        request_object (current_path, ModelUtils.undefined_context_id, false);
+    }
+
+    private void reload_search (/* SimpleAction action, Variant? path_variant */)
+    {
+        request_search (true);
+    }
+
+    private void hide_search (/* SimpleAction action, Variant? path_variant */)
+    {
+        stop_search ();
+    }
+    protected void stop_search ()
+    {
+        if (headerbar.search_mode_enabled)
+            search_stopped_cb ();
+    }
+
+    private void show_search (/* SimpleAction action, Variant? path_variant */)
+    {
+        request_search (true, PathEntry.SearchMode.EDIT_PATH_SELECT_ALL);
+    }
+
+    private void toggle_search (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
+    {
+        bool search_request = ((!) path_variant).get_boolean ();
+        action.change_state (search_request);
+        if (search_request && !headerbar.search_mode_enabled)
+            request_search (true, PathEntry.SearchMode.EDIT_PATH_SELECT_ALL);
+        else if (!search_request && headerbar.search_mode_enabled)
+            stop_search ();
+    }
+
+    /*\
+    * * global callbacks
+    \*/
+
+    [GtkCallback]
+    private void on_destroy ()
+    {
+        before_destroy ();
+        base.destroy ();
+    }
+
+    protected abstract void before_destroy ();
+
+    /*\
+    * * adaptative stuff
+    \*/
+
+    private bool disable_popovers = false;
+    private void set_window_size (AdaptativeWidget.WindowSize new_size)
+    {
+        bool _disable_popovers = AdaptativeWidget.WindowSize.is_phone_size (new_size)
+                              || AdaptativeWidget.WindowSize.is_extra_thin (new_size);
+        if (disable_popovers != _disable_popovers)
+        {
+            disable_popovers = _disable_popovers;
+            if (in_window_about)
+                hide_in_window_about ();
+        }
+
+        chain_set_window_size (new_size);
+    }
+
+    protected abstract void chain_set_window_size (AdaptativeWidget.WindowSize new_size);
+
+    /*\
+    * * actions and callbacks helpers
+    \*/
+
+    protected void update_current_path (ViewType type, string path)
+    {
+        if (type == ViewType.OBJECT || type == ViewType.FOLDER)
+        {
+            saved_type = type;
+            saved_view = path;
+            reload_search_next = true;
+        }
+        else if (current_type == ViewType.FOLDER)
+            saved_selection = browser_view.get_selected_row_name ();
+        else if (current_type == ViewType.OBJECT)
+            saved_selection = "";
+
+        current_type = type;
+        current_path = path;
+
+        browser_view.set_path (type, path);
+        headerbar.set_path (type, path);
+
+        Variant variant = new Variant ("(sq)", path, (type == ViewType.FOLDER) || (type == ViewType.CONFIG) 
? ModelUtils.folder_context_id : ModelUtils.undefined_context_id);
+        open_path_action.set_state (variant);
+        disabled_state_action.set_state (variant);
+    }
+
+    protected abstract void request_folder (string full_name, string selected_or_empty = "", bool 
notify_missing = true);
+    protected abstract void request_object (string full_name, uint16 context_id = 
ModelUtils.undefined_context_id, bool notify_missing = true, string schema_id = "");
+    protected abstract void request_config (string full_name);
+
+    protected void request_search (bool reload, PathEntry.SearchMode mode = PathEntry.SearchMode.UNCLEAR, 
string? search = null)
+    {
+        string selected_row = browser_view.get_selected_row_name ();
+        if (reload)
+        {
+            reload_search_action.set_enabled (false);
+            browser_view.set_search_parameters (saved_view, headerbar.get_bookmarks ());
+            reload_search_next = false;
+        }
+        if (mode != PathEntry.SearchMode.UNCLEAR)
+            headerbar.prepare_search (mode, search);
+        string search_text = search == null ? headerbar.text : (!) search;
+        update_current_path (ViewType.SEARCH, search_text);
+        if (mode != PathEntry.SearchMode.UNCLEAR)
+            browser_view.select_row (selected_row);
+        if (!headerbar.entry_has_focus)
+            headerbar.entry_grab_focus (false);
+    }
+
+    /*\
+    * * window state
+    \*/
+
+    // query
+    protected bool is_in_in_window_mode ()
+    {
+        return browser_view.is_in_in_window_mode ();
+    }
+
+    private bool navigation_blocked ()
+    {
+        if (headerbar.search_mode_enabled)
+            return true;
+        if (browser_view.is_in_in_window_mode ())
+            return true;
+        return false;
+    }
+
+    protected bool row_action_blocked ()
+    {
+        if (headerbar.has_popover ())
+            return true;
+        if (browser_view.is_in_in_window_mode ())
+            return true;
+        return false;
+    }
+
+    // action
+    protected virtual void close_in_window_panels ()
+    {
+        headerbar.close_popovers ();
+        if (in_window_about)
+            hide_in_window_about ();
+    }
+
+    /*\
+    * * search callbacks
+    \*/
+
+    [GtkCallback]
+    private void search_changed_cb ()
+    {
+        request_search (reload_search_next);
+    }
+
+    [GtkCallback]
+    private void search_stopped_cb ()
+    {
+        browser_view.row_grab_focus ();
+
+        reload_search_action.set_enabled (false);
+        if (saved_type == ViewType.FOLDER)
+            request_folder (saved_view, saved_selection);
+        else
+            update_current_path (saved_type, strdup (saved_view));
+        reload_search_next = true;
+    }
+
+    /*\
+    * * navigation methods
+    \*/
+
+    private void go_backward (bool shift)
+    {
+        if (navigation_blocked ())
+            return;
+
+        headerbar.close_popovers ();        // by symmetry with go_forward()
+        browser_view.discard_row_popover ();
+
+        if (current_path == root_path)
+            return;
+        if (shift)
+            request_folder (root_path);
+        else
+            request_folder (ModelUtils.get_parent_path (current_path), current_path.dup ());
+    }
+
+    private void go_forward (bool shift)
+    {
+        if (navigation_blocked ())
+            return;
+
+        string fallback_path;
+        string complete_path;
+        headerbar.get_fallback_path_and_complete_path (out fallback_path, out complete_path);
+
+        headerbar.close_popovers ();
+        browser_view.discard_row_popover ();
+
+        if (current_path == complete_path)  // TODO something?
+            return;
+
+        if (shift)
+        {
+            if (ModelUtils.is_key_path (fallback_path))
+                request_object (fallback_path);
+            else if (fallback_path != current_path)
+                request_folder (fallback_path);
+            else if (ModelUtils.is_key_path (complete_path))
+                request_object (complete_path);
+            else
+                request_folder (complete_path);
+        }
+        else
+        {
+            int index_of_last_slash = complete_path.index_of ("/", ((!) current_path).length);
+            if (index_of_last_slash != -1)
+                request_folder (complete_path.slice (0, index_of_last_slash + 1));
+            else if (ModelUtils.is_key_path (complete_path))
+                request_object (complete_path);
+            else
+                request_folder (complete_path);
+        }
+    }
+
+    protected void reload_view ()   // not used by BrowserWindow
+    {
+        if (browser_view.current_view == ViewType.FOLDER)
+            request_folder (current_path, browser_view.get_selected_row_name ());
+        else if (browser_view.current_view == ViewType.OBJECT)
+            request_object (current_path, ModelUtils.undefined_context_id, false);
+        else if (browser_view.current_view == ViewType.SEARCH)
+            request_search (true);
+    }
+
+    /*\
+    * * keyboad action entries
+    \*/
+
+    private void install_key_action_entries ()
+    {
+        SimpleActionGroup action_group = new SimpleActionGroup ();
+        action_group.add_action_entries (key_action_entries, this);
+        insert_action_group ("key", action_group);
+    }
+
+    private const GLib.ActionEntry [] key_action_entries =
+    {
+        { "copy",               copy                },  // <P>c
+        { "copy-path",          copy_path           },  // <P>C
+
+        { "next-match",         next_match          },  // <P>g // usual shortcut for "next-match"     in a 
SearchEntry; see also "Down"
+        { "previous-match",     previous_match      },  // <P>G // usual shortcut for "previous-match" in a 
SearchEntry; see also "Up"
+
+        { "request-config",     _request_config     },  // <P>i // TODO fusion with ui.open-config?
+
+        { "toggle-search",      _toggle_search      },  // <P>f // TODO unduplicate (at least name)
+        { "edit-path-end",      edit_path_end       },  // <P>l
+        { "edit-path-last",     edit_path_last      },  // <P>L
+
+        { "paste",              paste               },  // <P>v
+        { "paste-force",        paste_force         },  // <P>V
+
+        { "open-root",          open_root           },  // <S><A>Up
+        { "open-parent",        open_current_parent },  //    <A>Up
+        { "open-child",         open_child          },  //    <A>Down
+        { "open-last-child",    open_last_child     },  // <S><A>Down
+
+        { "toggle-hamburger",   toggle_hamburger    },  // F10
+        { "menu",               menu_pressed        },  // Menu
+    };
+
+    /*\
+    * * keyboard copy actions
+    \*/
+
+    protected abstract string get_copy_text ();
+    protected abstract string get_copy_path_text ();
+
+    private void copy                                   (/* SimpleAction action, Variant? path_variant */)
+    {
+        Widget? focus = get_focus ();
+        if (focus != null)
+        {
+            if ((!) focus is Entry)
+            {
+                ((Entry) (!) focus).copy_clipboard ();
+                return;
+            }
+            if ((!) focus is TextView)
+            {
+                ((TextView) (!) focus).copy_clipboard ();
+                return;
+            }
+        }
+
+        browser_view.discard_row_popover ();
+
+        _copy (get_copy_text ());
+    }
+
+    private void copy_path                              (/* SimpleAction action, Variant? path_variant */)
+    {
+        if (is_in_in_window_mode ())        // TODO better
+            return;
+
+        browser_view.discard_row_popover ();
+
+        _copy (get_copy_path_text ());
+    }
+
+    private inline void _copy (string text)
+    {
+        ((ConfigurationEditor) get_application ()).copy (text);
+    }
+
+    /*\
+    * * keyboard "Down" and "<Primary>g" (next match), "Up" and "<Primary><Shift>G" (previous match)
+    \*/
+
+    // actions; NOTE: no use of method return value
+    private void     next_match (/* SimpleAction action, Variant? variant */) {     _next_match (); }
+    private void previous_match (/* SimpleAction action, Variant? variant */) { _previous_match (); }
+
+    // real method; NOTE: returns bool
+    private bool _next_match ()
+    {
+        bool interception_result;
+        if (intercept_next_match (out interception_result))     // for hypothetical popovers
+            return interception_result;
+        else
+            return browser_view.next_match ();                  // for in-window things and main list
+    }
+    private bool _previous_match ()
+    {
+        bool interception_result;
+        if (intercept_previous_match (out interception_result)) // for hypothetical popovers
+            return interception_result;
+        else
+            return browser_view.previous_match ();              // for in-window things and main list
+    }
+
+    // override if you know something more has to be done
+    protected virtual bool intercept_next_match     (out bool interception_result)
+    {
+        interception_result = false;    // garbage
+        return false;
+    }
+    protected virtual bool intercept_previous_match (out bool interception_result)
+    {
+        interception_result = false;    // garbage
+        return false;
+    }
+
+    /*\
+    * * config
+    \*/
+
+    private void _request_config                        (/* SimpleAction action, Variant? variant */)  // 
TODO unduplicate method name
+    {
+        if (is_in_in_window_mode ())        // TODO better
+            return;
+
+        if (browser_view.current_view == ViewType.FOLDER)
+            request_config (current_path);
+    }
+
+    /*\
+    * * keyboard search actions
+    \*/
+
+    private void _toggle_search                         (/* SimpleAction action, Variant? variant */)   // 
TODO unduplicate?
+    {
+        if (is_in_in_window_mode ())        // TODO better
+            return;
+
+        headerbar.close_popovers ();    // should never be needed if headerbar.search_mode_enabled
+        browser_view.discard_row_popover ();   // could be needed if headerbar.search_mode_enabled
+
+        if (!headerbar.search_mode_enabled)
+            request_search (true, PathEntry.SearchMode.SEARCH);
+        else if (!headerbar.entry_has_focus)
+            headerbar.entry_grab_focus (true);
+        else if (headerbar.text.has_prefix ("/"))
+            request_search (true, PathEntry.SearchMode.SEARCH);
+        else
+            stop_search ();
+    }
+
+    private void edit_path_end                          (/* SimpleAction action, Variant? variant */)
+    {
+        if (navigation_blocked ())
+            return;
+
+        request_search (true, PathEntry.SearchMode.EDIT_PATH_MOVE_END);
+    }
+
+    private void edit_path_last                         (/* SimpleAction action, Variant? variant */)
+    {
+        if (navigation_blocked ())
+            return;
+
+        request_search (true, PathEntry.SearchMode.EDIT_PATH_SELECT_LAST_WORD);
+    }
+
+    /*\
+    * * keyboard paste actions
+    \*/
+
+    private void paste                                  (/* SimpleAction action, Variant? variant */)
+    {
+        if (is_in_in_window_mode ())
+            return;
+
+        Widget? focus = get_focus ();
+        if (focus != null)
+        {
+            if ((!) focus is Entry)
+            {
+                ((Entry) (!) focus).paste_clipboard ();
+                return;
+            }
+            if ((!) focus is TextView)
+            {
+                ((TextView) (!) focus).paste_clipboard ();
+                return;
+            }
+        }
+
+        search_clipboard_content ();
+    }
+
+    private void paste_force                            (/* SimpleAction action, Variant? variant */)
+    {
+        close_in_window_panels ();
+        search_clipboard_content ();
+    }
+
+    private void search_clipboard_content ()
+    {
+        Gdk.Display? display = Gdk.Display.get_default ();
+        if (display == null)    // ?
+            return;
+
+        string? clipboard_content = Clipboard.get_default ((!) display).wait_for_text ();
+        if (clipboard_content != null)
+            request_search (true, PathEntry.SearchMode.EDIT_PATH_MOVE_END, clipboard_content);
+        else
+            request_search (true, PathEntry.SearchMode.SEARCH);
+    }
+
+    /*\
+    * * keyboard navigation actions
+    \*/
+
+    private void open_root                              (/* SimpleAction action, Variant? variant */)
+    {
+        go_backward (true);
+    }
+
+    private void open_current_parent                    (/* SimpleAction action, Variant? variant */)
+    {
+        if (browser_view.current_view == ViewType.CONFIG)   // assumes "navigation_blocked () == false"
+            request_folder (current_path);
+        else
+            go_backward (false);
+    }
+
+    private void open_child                             (/* SimpleAction action, Variant? variant */)
+    {
+        go_forward (false);
+    }
+
+    private void open_last_child                        (/* SimpleAction action, Variant? variant */)
+    {
+        go_forward (true);
+    }
+
+    /*\
+    * * keyboard open menus actions
+    \*/
+
+    private void toggle_hamburger                       (/* SimpleAction action, Variant? variant */)
+    {
+        headerbar.toggle_hamburger_menu ();
+    }
+
+    private void menu_pressed                           (/* SimpleAction action, Variant? variant */)
+    {
+        if (browser_view.toggle_row_popover ()) // handles in-window bookmarks
+            headerbar.close_popovers ();
+        else
+        {
+            headerbar.toggle_hamburger_menu ();
+            browser_view.discard_row_popover ();
+        }
+    }
+
+    /*\
+    * * about dialog or in-window panel
+    \*/
+
+    private void about (/* SimpleAction action, Variant? path_variant */)
+    {
+        if (!AdaptativeWidget.WindowSize.is_phone_size (window_size)
+         && !AdaptativeWidget.WindowSize.is_extra_thin (window_size))
+            show_about_dialog ();       // TODO hide the dialog if visible
+        else
+            toggle_in_window_about ();
+    }
+
+    private void show_about_dialog ()
+    {
+        string [] authors = AboutDialogInfos.authors;
+        Gtk.show_about_dialog (this,
+                               "program-name",          AboutDialogInfos.program_name,
+                               "version",               AboutDialogInfos.version,
+                               "comments",              AboutDialogInfos.comments,
+                               "copyright",             AboutDialogInfos.copyright,
+                               "license-type",          AboutDialogInfos.license_type,
+                               "wrap-license", true,
+                               "authors",               authors,
+                               "translator-credits",    AboutDialogInfos.translator_credits,
+                               "logo-icon-name",        AboutDialogInfos.logo_icon_name,
+                               "website",               AboutDialogInfos.website,
+                               "website-label",         AboutDialogInfos.website_label,
+                               null);
+    }
+
+    // in-window about
+    protected bool in_window_about { protected get; private set; default = false; }
+
+    private void toggle_in_window_about ()
+    {
+        if (in_window_about)
+            hide_in_window_about ();
+        else
+            show_in_window_about ();
+    }
+
+    private inline void show_in_window_about ()
+        requires (in_window_about == false)
+    {
+        in_window_about = true;
+        headerbar.show_in_window_about ();
+        browser_view.show_in_window_about ();
+    }
+
+    protected void hide_in_window_about (/* SimpleAction action, Variant? path_variant */)
+        requires (in_window_about == true)
+    {
+        in_window_about = false;
+        headerbar.hide_in_window_about ();
+        browser_view.hide_in_window_about ();
+    }
+
+    /*\
+    * * keyboard callback
+    \*/
+
+    [GtkCallback]
+    private bool on_key_press_event (Widget widget, Gdk.EventKey event)
+    {
+        uint keyval = event.keyval;
+        string name = (!) (Gdk.keyval_name (keyval) ?? "");
+
+        if (name == "F1") // TODO fix dance done with the F1 & <Primary>F1 shortcuts that show help overlay
+        {
+            browser_view.discard_row_popover ();
+            if ((event.state & Gdk.ModifierType.SHIFT_MASK) == 0)
+                return false;   // help overlay
+            about ();
+            return true;
+        }
+
+        /* for changing row during search; cannot use set_accels_for_action() else popovers are not handled 
anymore */
+        if (name == "Down" && (event.state & Gdk.ModifierType.MOD1_MASK) == 0)  // see also <ctrl>g
+            return _next_match ();
+        if (name == "Up"   && (event.state & Gdk.ModifierType.MOD1_MASK) == 0)  // see also <ctrl>G
+            return _previous_match ();
+
+        if (is_in_in_window_mode ())
+            return false;
+
+        /* don't use "else if", or some widgets will not be hidden on <ctrl>F10 or such things */
+        if (name == "F10" && (event.state & Gdk.ModifierType.SHIFT_MASK) != 0)
+        {
+            Widget? focus = get_focus ();
+            if (focus != null && (((!) focus is Entry) || ((!) focus is TextView))) // && 
browser_view.current_view != ViewType.SEARCH
+                return false;
+
+            headerbar.toggle_pathbar_menu ();
+            return true;
+        }
+
+        if (name == "Return" || name == "KP_Enter")
+        {
+            if (browser_view.current_view == ViewType.SEARCH
+             && headerbar.entry_has_focus
+             && browser_view.return_pressed ())
+                return true;
+            return false;
+        }
+
+        if (headerbar.has_popover ())
+            return false;
+
+        if (!headerbar.search_mode_enabled &&
+            // see gtk_search_entry_is_keynav() in gtk+/gtk/gtksearchentry.c:388
+            (keyval == Gdk.Key.Tab          || keyval == Gdk.Key.KP_Tab         ||
+             keyval == Gdk.Key.Up           || keyval == Gdk.Key.KP_Up          ||
+             keyval == Gdk.Key.Down         || keyval == Gdk.Key.KP_Down        ||
+             keyval == Gdk.Key.Left         || keyval == Gdk.Key.KP_Left        ||
+             keyval == Gdk.Key.Right        || keyval == Gdk.Key.KP_Right       ||
+             keyval == Gdk.Key.Home         || keyval == Gdk.Key.KP_Home        ||
+             keyval == Gdk.Key.End          || keyval == Gdk.Key.KP_End         ||
+             keyval == Gdk.Key.Page_Up      || keyval == Gdk.Key.KP_Page_Up     ||
+             keyval == Gdk.Key.Page_Down    || keyval == Gdk.Key.KP_Page_Down   ||
+             ((event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)) != 0) ||
+             name == "space" || name == "KP_Space"))
+            return false;
+
+        Widget? focus = get_focus ();
+        bool focus_is_text_widget = focus != null && (((!) focus is Entry) || ((!) focus is TextView));
+        if ((!focus_is_text_widget)
+         && (event.is_modifier == 0)
+         && (event.length != 0)
+//       && (name != "F10")     // else <Shift>F10 toggles the search_entry popup; see if a976aa9740 fixes 
that in Gtk+ 4
+         && (headerbar.handle_event (event)))
+            return true;
+
+        return false;
+    }
+
+    /*\
+    * * mouse callback
+    \*/
+
+    protected bool mouse_use_extra_buttons  { private get; protected set; default = true; }
+    protected int  mouse_back_button        { private get; protected set; default = 8; }
+    protected int  mouse_forward_button     { private get; protected set; default = 9; }
+
+    private void bind_mouse_config ()
+    {
+        GLib.Settings settings = new GLib.Settings ("ca.desrt.dconf-editor.Settings");  // FIXME
+
+        settings.bind ("mouse-use-extra-buttons", this,
+                       "mouse-use-extra-buttons", SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
+        settings.bind ("mouse-back-button",       this,
+                       "mouse-back-button",       SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
+        settings.bind ("mouse-forward-button",    this,
+                       "mouse-forward-button",    SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
+    }
+
+    [GtkCallback]
+    private bool on_button_press_event (Widget widget, Gdk.EventButton event)
+    {
+        if (!mouse_use_extra_buttons)
+            return false;
+
+        if (event.button == mouse_back_button)
+        {
+            if (mouse_back_button == mouse_forward_button)
+            {
+                warning (_("The same mouse button is set for going backward and forward. Doing nothing."));
+                return false;
+            }
+
+            go_backward ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0);
+            return true;
+        }
+        if (event.button == mouse_forward_button)
+        {
+            go_forward ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0);
+            return true;
+        }
+        return false;
+    }
+
+    /*\
+    * * night mode
+    \*/
+
+    // for construct only
+    public bool initial_night_time           { private get; protected construct; }
+    public bool initial_dark_theme           { private get; protected construct; }
+    public bool initial_automatic_night_mode { private get; protected construct; }
+
+    private void init_night_mode ()
+    {
+        headerbar.night_time           = initial_night_time;
+        headerbar.dark_theme           = initial_dark_theme;
+        headerbar.automatic_night_mode = initial_automatic_night_mode;
+        // menu is already updated three times at startup, let's not add one
+    }
+
+    // for updates
+    internal void night_time_changed (Object nlm, ParamSpec thing)
+    {
+        headerbar.night_time = NightLightMonitor.NightTime.should_use_dark_theme (((NightLightMonitor) 
nlm).night_time);
+        headerbar.update_hamburger_menu ();
+    }
+
+    internal void dark_theme_changed (Object nlm, ParamSpec thing)
+    {
+        headerbar.dark_theme = ((NightLightMonitor) nlm).dark_theme;
+        headerbar.update_hamburger_menu ();
+    }
+
+    internal void automatic_night_mode_changed (Object nlm, ParamSpec thing)
+    {
+        headerbar.automatic_night_mode = ((NightLightMonitor) nlm).automatic_night_mode;
+        // update menu not needed
+    }
+
+    /*\
+    * * notifications
+    \*/
+
+    [GtkChild] private NotificationsRevealer notifications_revealer;
+
+    protected void show_notification (string notification)
+    {
+        notifications_revealer.show_notification (notification);
+    }
+}
diff --git a/editor/dconf-editor.gresource.xml b/editor/dconf-editor.gresource.xml
index 6d7212d..6264462 100644
--- a/editor/dconf-editor.gresource.xml
+++ b/editor/dconf-editor.gresource.xml
@@ -8,9 +8,9 @@
     <file preprocess="xml-stripblanks">browser-infobar.ui</file>
     <file preprocess="xml-stripblanks">browser-stack.ui</file>
     <file preprocess="xml-stripblanks">browser-view.ui</file>
+    <file preprocess="xml-stripblanks">browser-window.ui</file>
     <file preprocess="xml-stripblanks">config-list-box-row.ui</file>
     <file>dconf-editor.css</file>
-    <file preprocess="xml-stripblanks">dconf-editor.ui</file>
     <file preprocess="xml-stripblanks">delayed-setting-view.ui</file>
     <file preprocess="xml-stripblanks">folder-list-box-row.ui</file>
     <file preprocess="xml-stripblanks">key-list-box-row.ui</file>
diff --git a/editor/dconf-editor.vala b/editor/dconf-editor.vala
index ae840f3..e8cd740 100644
--- a/editor/dconf-editor.vala
+++ b/editor/dconf-editor.vala
@@ -297,30 +297,30 @@ private class ConfigurationEditor : Gtk.Application
         add_action_entries (action_entries, this);
         set_accels_for_action ("kbd.toggle-bookmark",   {        "<Primary>b",
                                                           "<Shift><Primary>b" });
-        set_accels_for_action ("kbd.copy",              {        "<Primary>c" });
-        set_accels_for_action ("kbd.copy-path",         { "<Shift><Primary>c" });
+        set_accels_for_action ("key.copy",              {        "<Primary>c" });
+        set_accels_for_action ("key.copy-path",         { "<Shift><Primary>c" });
         set_accels_for_action ("kbd.bookmark",          {        "<Primary>d" });
         set_accels_for_action ("kbd.unbookmark",        { "<Shift><Primary>d" });
-        set_accels_for_action ("kbd.toggle-search",     {        "<Primary>f" });   // TODO 
<Shift><Primary>f something?
-        set_accels_for_action ("kbd.next-match",        {        "<Primary>g" });
-        set_accels_for_action ("kbd.previous-match",    { "<Shift><Primary>g" });
-        set_accels_for_action ("kbd.request-config",    {        "<Primary>i" });   // <Shift><Primary>i is 
gtk editor
+        set_accels_for_action ("key.toggle-search",     {        "<Primary>f" });   // TODO 
<Shift><Primary>f something?
+        set_accels_for_action ("key.next-match",        {        "<Primary>g" });
+        set_accels_for_action ("key.previous-match",    { "<Shift><Primary>g" });
+        set_accels_for_action ("key.request-config",    {        "<Primary>i" });   // <Shift><Primary>i is 
gtk editor
         set_accels_for_action ("kbd.modifications",     {        "<Alt>i"     });
-        set_accels_for_action ("kbd.edit-path-end",     {        "<Primary>l" });
-        set_accels_for_action ("kbd.edit-path-last",    { "<Shift><Primary>l" });
+        set_accels_for_action ("key.edit-path-end",     {        "<Primary>l" });
+        set_accels_for_action ("key.edit-path-last",    { "<Shift><Primary>l" });
         set_accels_for_action ("app.quit",              {        "<Primary>q" });
         set_accels_for_action ("app.apply-and-quit",    { "<Shift><Primary>q" });
-        set_accels_for_action ("kbd.paste",             {        "<Primary>v" });   // 
https://bugzilla.gnome.org/show_bug.cgi?id=762257 is WONTFIX
-        set_accels_for_action ("kbd.paste-force",       { "<Shift><Primary>v" });
+        set_accels_for_action ("key.paste",             {        "<Primary>v" });   // 
https://bugzilla.gnome.org/show_bug.cgi?id=762257 is WONTFIX
+        set_accels_for_action ("key.paste-force",       { "<Shift><Primary>v" });
 
-        set_accels_for_action ("kbd.open-root",         { "<Shift><Alt>Up"    });
-        set_accels_for_action ("kbd.open-parent",       {        "<Alt>Up"    });
-        set_accels_for_action ("kbd.open-child",        {        "<Alt>Down"  });
-        set_accels_for_action ("kbd.open-last-child",   { "<Shift><Alt>Down"  });
+        set_accels_for_action ("key.open-root",         { "<Shift><Alt>Up"    });
+        set_accels_for_action ("key.open-parent",       {        "<Alt>Up"    });
+        set_accels_for_action ("key.open-child",        {        "<Alt>Down"  });
+        set_accels_for_action ("key.open-last-child",   { "<Shift><Alt>Down"  });
 
-        set_accels_for_action ("kbd.toggle-hamburger",  {          "F10"      });
+        set_accels_for_action ("key.toggle-hamburger",  {          "F10"      });
         set_accels_for_action ("kbd.escape",            {          "Escape"   });
-        set_accels_for_action ("kbd.menu",              {          "Menu"     });
+        set_accels_for_action ("key.menu",              {          "Menu"     });
 
         set_accels_for_action ("kbd.set-to-default",    { "<Primary>Delete",
                                                           "<Primary>KP_Delete",
diff --git a/editor/dconf-window.vala b/editor/dconf-window.vala
index ca1c1dd..35efa74 100644
--- a/editor/dconf-window.vala
+++ b/editor/dconf-window.vala
@@ -85,31 +85,13 @@ internal enum ViewType {
     }
 }
 
-[GtkTemplate (ui = "/ca/desrt/dconf-editor/ui/dconf-editor.ui")]
-private class DConfWindow : AdaptativeWindow, AdaptativeWidget
+private class DConfWindow : BrowserWindow
 {
-    private ViewType current_type = ViewType.FOLDER;
-    private string current_path = "/";
-    private ViewType saved_type = ViewType.FOLDER;
-    private string saved_view = "/";
-    private string saved_selection = "";
-
     private SettingsModel model = new SettingsModel ();
     private ModificationsHandler modifications_handler;
 
-    internal bool mouse_extra_buttons   { private get; set; default = true; }
-    internal int mouse_back_button      { private get; set; default = 8; }
-    internal int mouse_forward_button   { private get; set; default = 9; }
-
     private GLib.Settings settings = new GLib.Settings ("ca.desrt.dconf-editor.Settings");
 
-    [GtkChild] private BrowserHeaderBar headerbar;
-    [GtkChild] private BrowserView browser_view;
-    [GtkChild] private NotificationsRevealer notifications_revealer;
-
-    [GtkChild] private Grid main_grid;
-    private ModificationsRevealer revealer;
-
     private ulong use_shortpaths_changed_handler = 0;
     private ulong behaviour_changed_handler = 0;
 
@@ -117,30 +99,28 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
     private ulong browserview_update_bookmarks_icons_handler = 0;
 
     private ulong delayed_changes_changed_handler = 0;
+    private ulong bookmarks_selection_changed_handler = 0;
 
     private StyleContext context;
     construct
     {
+        title = _("dconf Editor");
         context = get_style_context ();
+        context.add_class ("dconf-editor");
 
-        revealer = new ModificationsRevealer ();
-        revealer.visible = true;
-        main_grid.add (revealer);
-
-        adaptative_children.append (headerbar);
-        adaptative_children.append (browser_view);
-        adaptative_children.append (revealer);
-        adaptative_children.append (this);
-    }
-
-    internal DConfWindow (bool disable_warning, string? schema, string? path, string? key_name, bool 
_night_time, bool _dark_theme, bool _automatic_night_mode)
-    {
-        init_night_mode (_night_time, _dark_theme, _automatic_night_mode);
+        create_modifications_revealer ();
 
         install_ui_action_entries ();
         install_kbd_action_entries ();
         install_bmk_action_entries ();
 
+        bookmarks_selection_changed_handler = browser_view.bookmarks_selection_changed.connect 
(on_bookmarks_selection_changed);
+    }
+
+    internal DConfWindow (bool disable_warning, string? schema, string? path, string? key_name, bool 
night_time, bool dark_theme, bool automatic_night_mode)
+    {
+        Object (initial_night_time: night_time, initial_dark_theme: dark_theme, 
initial_automatic_night_mode: automatic_night_mode);
+
         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);
 
@@ -172,10 +152,6 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         if (!disable_warning && settings.get_boolean ("show-warning"))
             show.connect (show_initial_warning);
 
-        settings.bind ("mouse-use-extra-buttons", this, "mouse-extra-buttons", 
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
-        settings.bind ("mouse-back-button", this, "mouse-back-button", 
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
-        settings.bind ("mouse-forward-button", this, "mouse-forward-button", 
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
-
         /* init current_path */
         bool restore_view = settings.get_boolean ("restore-view");
         string? settings_saved_view = null;
@@ -332,36 +308,21 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
     }
 
     /*\
-    * * Night mode
+    * * ModificationsRevealer
     \*/
 
-    private void init_night_mode (bool _night_time, bool _dark_theme, bool _automatic_night_mode)
-    {
-        headerbar.night_time = _night_time;
-        headerbar.dark_theme = _dark_theme;
-        headerbar.automatic_night_mode = _automatic_night_mode;
-    }
-
-    internal void night_time_changed (Object nlm, ParamSpec thing)
-    {
-        headerbar.night_time = NightLightMonitor.NightTime.should_use_dark_theme (((NightLightMonitor) 
nlm).night_time);
-        headerbar.update_hamburger_menu ();
-    }
-
-    internal void dark_theme_changed (Object nlm, ParamSpec thing)
-    {
-        headerbar.dark_theme = ((NightLightMonitor) nlm).dark_theme;
-        headerbar.update_hamburger_menu ();
-    }
+    private ModificationsRevealer revealer;
 
-    internal void automatic_night_mode_changed (Object nlm, ParamSpec thing)
+    private void create_modifications_revealer ()
     {
-        headerbar.automatic_night_mode = ((NightLightMonitor) nlm).automatic_night_mode;
-        // update menu not needed
+        revealer = new ModificationsRevealer ();
+        revealer.visible = true;
+        main_grid.add (revealer);
+        adaptative_children.append (revealer);
     }
 
     /*\
-    * * Window management callbacks
+    * * initial warning
     \*/
 
     private void show_initial_warning ()
@@ -384,6 +345,10 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         dialog.destroy ();
     }
 
+    /*\
+    * * quitting
+    \*/
+
     internal bool quit_if_no_pending_changes ()
     {
         if (modifications_handler.has_pending_changes ())
@@ -401,11 +366,11 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         destroy ();
     }
 
-    [GtkCallback]
-    private void on_destroy ()
+    protected override void before_destroy ()
     {
         ((ConfigurationEditor) get_application ()).clean_copy_notification ();
 
+        browser_view.disconnect (bookmarks_selection_changed_handler);
         modifications_handler.disconnect (delayed_changes_changed_handler);
 
         headerbar.disconnect (headerbar_update_bookmarks_icons_handler);
@@ -425,71 +390,21 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         settings.set_string ("saved-view", saved_view);
         settings.set_string ("saved-pathbar-path", headerbar.get_complete_path ());
         settings.apply ();
-
-        base.destroy ();
     }
 
     /*\
     * * Main UI action entries
     \*/
 
-    private SimpleAction open_path_action;
-    private SimpleAction disabled_state_action;
-
-    private SimpleAction reload_search_action;
-    private bool reload_search_next = true;
-
     private void install_ui_action_entries ()
     {
         SimpleActionGroup action_group = new SimpleActionGroup ();
         action_group.add_action_entries (ui_action_entries, this);
         insert_action_group ("ui", action_group);
-
-        disabled_state_action = (SimpleAction) action_group.lookup_action ("disabled-state");
-        disabled_state_action.set_enabled (false);
-
-        open_path_action = (SimpleAction) action_group.lookup_action ("open-path");
-
-        reload_search_action = (SimpleAction) action_group.lookup_action ("reload-search");
-        reload_search_action.set_enabled (false);
     }
 
     private const GLib.ActionEntry [] ui_action_entries =
     {
-        { "empty",          empty, "*" },
-        { "empty-null",     empty },
-        { "disabled-state", empty, "(sq)", "('',uint16 65535)" },
-
-        { "notify-folder-emptied", notify_folder_emptied, "s" },
-        { "notify-object-deleted", notify_object_deleted, "(sq)" },
-
-        { "open-folder", open_folder, "s" },
-        { "open-object", open_object, "(sq)" },
-        { "open-config", open_config, "s" },
-        { "open-search", open_search, "s" },
-        { "next-search", next_search, "s" },
-        { "open-parent", open_parent, "s" },
-
-        { "open-path", open_path, "(sq)", "('/',uint16 " + ModelUtils.folder_context_id_string + ")" },
-
-        { "reload-folder", reload_folder },
-        { "reload-object", reload_object },
-        { "reload-search", reload_search },
-
-        { "hide-search",   hide_search },
-        { "show-search",   show_search },
-
-        { "toggle-search", toggle_search, "b", "false" },
-        { "update-bookmarks-icons", update_bookmarks_icons, "as" },
-
-        { "show-in-window-bookmarks",       show_in_window_bookmarks },
-        { "hide-in-window-bookmarks",       hide_in_window_bookmarks },
-
-        { "show-in-window-modifications",   show_in_window_modifications },
-        { "hide-in-window-modifications",   hide_in_window_modifications },
-
-        { "hide-in-window-about",           hide_in_window_about },
-
         { "reset-recursive", reset_recursively, "s" },
         { "reset-visible", reset_visible, "s" },
 
@@ -500,166 +415,130 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         { "dismiss-change", dismiss_change, "s" },  // here because needs to be accessed from 
DelayedSettingView rows
         { "erase", erase_dconf_key, "s" },          // here because needs a reload_view as we enter 
delay_mode
 
-        { "about", about_cb }
-    };
+        { "show-in-window-bookmarks",       show_in_window_bookmarks },
+        { "hide-in-window-bookmarks",       hide_in_window_bookmarks },
 
-    private void empty (/* SimpleAction action, Variant? variant */) {}
+        { "show-in-window-modifications",   show_in_window_modifications },
+        { "hide-in-window-modifications",   hide_in_window_modifications },
 
-    private void notify_folder_emptied (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
-    {
-        string full_name = ((!) path_variant).get_string ();
+        { "update-bookmarks-icons", update_bookmarks_icons, "as" },
 
-        show_notification (_("Folder “%s” is now empty.").printf (full_name));
-    }
+        { "notify-folder-emptied", notify_folder_emptied, "s" },
+        { "notify-object-deleted", notify_object_deleted, "(sq)" }
+    };
 
-    private void notify_object_deleted (SimpleAction action, Variant? path_variant)
+    private void reset_recursively (SimpleAction action, Variant? path_variant)
         requires (path_variant != null)
     {
-        string full_name;
-        uint16 unused;  // GAction parameter type switch is a little touchy, see pathbar.vala
-        ((!) path_variant).@get ("(sq)", out full_name, out unused);
-
-        show_notification (_("Key “%s” has been deleted.").printf (full_name));
+        reset_path (((!) path_variant).get_string (), true);
     }
 
-    private void open_folder (SimpleAction action, Variant? path_variant)
+    private void reset_visible (SimpleAction action, Variant? path_variant)
         requires (path_variant != null)
     {
-        headerbar.close_popovers ();
-        if (browser_view.in_window_bookmarks)
-            hide_in_window_bookmarks ();
-
-        string full_name = ((!) path_variant).get_string ();
-
-        request_folder (full_name, "");
+        reset_path (((!) path_variant).get_string (), false);
     }
 
-    private void open_object (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
+    private void reset_path (string path, bool recursively)
     {
-        headerbar.close_popovers ();
-        revealer.hide_modifications_list ();
-        if (browser_view.in_window_bookmarks)
-            hide_in_window_bookmarks ();
-        else if (browser_view.in_window_modifications)
-            hide_in_window_modifications ();
-
-        string full_name;
-        uint16 context_id;
-        ((!) path_variant).@get ("(sq)", out full_name, out context_id);
-
-        request_object (full_name, context_id);
+        enter_delay_mode ();
+        revealer.reset_objects (path, model.get_children (path), recursively);
     }
 
-    private void open_config (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
+    private void enter_delay_mode (/* SimpleAction action, Variant? path_variant */)
     {
-        headerbar.close_popovers ();
-
-        string full_name = ((!) path_variant).get_string ();    // TODO use current_path instead?
-
-        request_config (full_name);
+        modifications_handler.enter_delay_mode ();
+        invalidate_popovers_with_ui_reload ();
     }
 
-    private void open_search (SimpleAction action, Variant? search_variant)
-        requires (search_variant != null)
+    private void apply_delayed_settings (/* SimpleAction action, Variant? path_variant */)
     {
-        headerbar.close_popovers ();
-        if (browser_view.in_window_bookmarks)
-            hide_in_window_bookmarks ();
-
-        string search = ((!) search_variant).get_string ();
-
-        request_search (true, PathEntry.SearchMode.EDIT_PATH_SELECT_ALL, search);
+        if (browser_view.in_window_modifications)
+            hide_in_window_modifications ();
+        modifications_handler.apply_delayed_settings ();
+        invalidate_popovers_with_ui_reload ();
     }
 
-    private void next_search (SimpleAction action, Variant? search_variant)
-        requires (search_variant != null)
+    private void dismiss_delayed_settings (/* SimpleAction action, Variant? path_variant */)
     {
-        saved_type = ViewType.FOLDER;
-        saved_view = ((!) search_variant).get_string ();
-
-        request_search (true, PathEntry.SearchMode.EDIT_PATH_MOVE_END, saved_view);
+        if (browser_view.in_window_modifications)
+            hide_in_window_modifications ();
+        modifications_handler.dismiss_delayed_settings ();
+        invalidate_popovers_with_ui_reload ();
     }
 
-    private void open_parent (SimpleAction action, Variant? path_variant)
+    private void dismiss_change (SimpleAction action, Variant? path_variant)
         requires (path_variant != null)
     {
-        if (browser_view.in_window_bookmarks)
-            hide_in_window_bookmarks ();
-
-        string full_name = ((!) path_variant).get_string ();
-        request_folder (ModelUtils.get_parent_path (full_name), full_name);
+        modifications_handler.dismiss_change (((!) path_variant).get_string ());
+        browser_view.invalidate_popovers ();
+        reload_view ();
     }
 
-    private void open_path (SimpleAction action, Variant? path_variant)
+    private void erase_dconf_key (SimpleAction action, Variant? path_variant)
         requires (path_variant != null)
     {
-        headerbar.close_popovers ();
-        revealer.hide_modifications_list ();
-        if (browser_view.in_window_bookmarks)
-            hide_in_window_bookmarks ();
-        else if (browser_view.in_window_modifications)
-            hide_in_window_modifications ();
-
-        string full_name;
-        uint16 context_id;
-        ((!) path_variant).@get ("(sq)", out full_name, out context_id);
-
-        action.set_state ((!) path_variant);
-
-        if (ModelUtils.is_folder_context_id (context_id))
-            request_folder (full_name, "");
-        else
-            request_object (full_name, context_id);
+        modifications_handler.erase_dconf_key (((!) path_variant).get_string ());
+        invalidate_popovers_with_ui_reload ();
     }
 
-    private void reload_folder (/* SimpleAction action, Variant? path_variant */)
+    private void invalidate_popovers_with_ui_reload ()
     {
-        request_folder (current_path, browser_view.get_selected_row_name ());
+        bool delay_mode = modifications_handler.get_current_delay_mode ();
+        browser_view.hide_or_show_toggles (!delay_mode);
+        browser_view.invalidate_popovers ();
+        headerbar.delay_mode = delay_mode;
     }
 
-    private void reload_object (/* SimpleAction action, Variant? path_variant */)
-    {
-        request_object (current_path, ModelUtils.undefined_context_id, false);
-    }
+    /*\
+    * * showing or hiding panels
+    \*/
 
-    private void reload_search (/* SimpleAction action, Variant? path_variant */)
+    private void show_in_window_bookmarks (/* SimpleAction action, Variant? path_variant */)
     {
-        request_search (true);
+        headerbar.show_in_window_bookmarks ();
+        string [] bookmarks = headerbar.get_bookmarks ();
+        browser_view.show_in_window_bookmarks (bookmarks);
+        update_bookmarks_icons_from_array (bookmarks);
     }
 
-    private void hide_search (/* SimpleAction action, Variant? path_variant */)
+    private void hide_in_window_bookmarks (/* SimpleAction action, Variant? path_variant */)
+        requires (browser_view.in_window_bookmarks == true)
     {
-        stop_search ();
+        if (browser_view.in_window_bookmarks_edit_mode)
+            leave_edit_mode ();     // TODO place after
+        headerbar.hide_in_window_bookmarks ();
+        browser_view.hide_in_window_bookmarks ();
     }
 
-    private void show_search (/* SimpleAction action, Variant? path_variant */)
+    private void show_in_window_modifications (/* SimpleAction action, Variant? path_variant */)
     {
-        request_search (true, PathEntry.SearchMode.EDIT_PATH_SELECT_ALL);
+        headerbar.show_in_window_modifications ();
+        browser_view.show_in_window_modifications ();
     }
 
-    private void toggle_search (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
+    private void hide_in_window_modifications (/* SimpleAction action, Variant? path_variant */)
+        requires (browser_view.in_window_modifications == true)
     {
-        bool search_request = ((!) path_variant).get_boolean ();
-        action.change_state (search_request);
-        if (search_request && !headerbar.search_mode_enabled)
-            request_search (true, PathEntry.SearchMode.EDIT_PATH_SELECT_ALL);
-        else if (!search_request && headerbar.search_mode_enabled)
-            stop_search ();
+        headerbar.hide_in_window_modifications ();
+        browser_view.hide_in_window_modifications ();
     }
 
+    /*\
+    * * updating bookmarks icons
+    \*/
+
     private void update_bookmarks_icons (SimpleAction action, Variant? bookmarks_variant)
         requires (bookmarks_variant != null)
     {
         update_bookmarks_icons_from_variant ((!) bookmarks_variant);
     }
+
     private void update_bookmarks_icons_from_variant (Variant variant)
     {
         update_bookmarks_icons_from_array (variant.get_strv ());
     }
+
     private void update_bookmarks_icons_from_array (string [] bookmarks)
     {
         if (bookmarks.length == 0)
@@ -705,6 +584,7 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
             }
         }
     }
+
     private void update_bookmark_icon (string bookmark, BookmarkIcon icon)
     {
         if (AdaptativeWidget.WindowSize.is_extra_thin (window_size)
@@ -714,189 +594,64 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
             headerbar.update_bookmark_icon (bookmark, icon);
     }
 
-    private void show_in_window_bookmarks (/* SimpleAction action, Variant? path_variant */)
-    {
-        headerbar.show_in_window_bookmarks ();
-        string [] bookmarks = headerbar.get_bookmarks ();
-        browser_view.show_in_window_bookmarks (bookmarks);
-        update_bookmarks_icons_from_array (bookmarks);
-    }
+    /*\
+    * * adaptative stuff
+    \*/
 
-    private void hide_in_window_bookmarks (/* SimpleAction action, Variant? path_variant */)
-        requires (browser_view.in_window_bookmarks == true)
+    private bool disable_popovers = false;
+    private bool disable_action_bar = false;
+    protected override void chain_set_window_size (AdaptativeWidget.WindowSize new_size)
     {
-        if (browser_view.in_window_bookmarks_edit_mode)
-            leave_edit_mode ();     // TODO place after
-        headerbar.hide_in_window_bookmarks ();
-        browser_view.hide_in_window_bookmarks ();
-    }
+        bool _disable_popovers = AdaptativeWidget.WindowSize.is_phone_size (new_size)
+                              || AdaptativeWidget.WindowSize.is_extra_thin (new_size);
+        if (disable_popovers != _disable_popovers)
+        {
+            disable_popovers = _disable_popovers;
+            if (browser_view.in_window_bookmarks)
+                hide_in_window_bookmarks ();
+        }
 
-    private void show_in_window_modifications (/* SimpleAction action, Variant? path_variant */)
-    {
-        headerbar.show_in_window_modifications ();
-        browser_view.show_in_window_modifications ();
+        bool _disable_action_bar = _disable_popovers
+                                || AdaptativeWidget.WindowSize.is_extra_flat (new_size);
+        if (disable_action_bar != _disable_action_bar)
+        {
+            disable_action_bar = _disable_action_bar;
+            if (browser_view.in_window_modifications)
+                hide_in_window_modifications ();
+        }
     }
 
-    private void hide_in_window_modifications (/* SimpleAction action, Variant? path_variant */)
-        requires (browser_view.in_window_modifications == true)
-    {
-        headerbar.hide_in_window_modifications ();
-        browser_view.hide_in_window_modifications ();
-    }
+    /*\
+    * * bookmarks action entries
+    \*/
 
-    private void hide_in_window_about (/* SimpleAction action, Variant? path_variant */)
-        requires (browser_view.in_window_about == true)
-    {
-        headerbar.hide_in_window_about ();
-        browser_view.hide_in_window_about ();
-    }
+    bool actions_init_done = false;
+    private SimpleAction move_top_action;
+    private SimpleAction move_up_action;
+    private SimpleAction move_down_action;
+    private SimpleAction move_bottom_action;
+    private SimpleAction trash_bookmark_action;
+    private SimpleAction edit_mode_state_action;
 
-    private void reset_recursively (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
+    private void update_actions ()
+        requires (actions_init_done)
     {
-        reset_path (((!) path_variant).get_string (), true);
+        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);
     }
 
-    private void reset_visible (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
+    private void install_bmk_action_entries ()
     {
-        reset_path (((!) path_variant).get_string (), false);
-    }
-
-    private void reset_path (string path, bool recursively)
-    {
-        enter_delay_mode ();
-        revealer.reset_objects (path, model.get_children (path), recursively);
-    }
-
-    private void enter_delay_mode (/* SimpleAction action, Variant? path_variant */)
-    {
-        modifications_handler.enter_delay_mode ();
-        invalidate_popovers_with_ui_reload ();
-    }
-
-    private void apply_delayed_settings (/* SimpleAction action, Variant? path_variant */)
-    {
-        if (browser_view.in_window_modifications)
-            hide_in_window_modifications ();
-        modifications_handler.apply_delayed_settings ();
-        invalidate_popovers_with_ui_reload ();
-    }
-
-    private void dismiss_delayed_settings (/* SimpleAction action, Variant? path_variant */)
-    {
-        if (browser_view.in_window_modifications)
-            hide_in_window_modifications ();
-        modifications_handler.dismiss_delayed_settings ();
-        invalidate_popovers_with_ui_reload ();
-    }
-
-    private void dismiss_change (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
-    {
-        modifications_handler.dismiss_change (((!) path_variant).get_string ());
-        browser_view.invalidate_popovers ();
-        reload_view ();
-    }
-
-    private void erase_dconf_key (SimpleAction action, Variant? path_variant)
-        requires (path_variant != null)
-    {
-        modifications_handler.erase_dconf_key (((!) path_variant).get_string ());
-        invalidate_popovers_with_ui_reload ();
-    }
-
-    private void about_cb ()    // register as "win.about"?
-    {
-        if (!AdaptativeWidget.WindowSize.is_phone_size (window_size)
-         && !AdaptativeWidget.WindowSize.is_extra_thin (window_size))
-        {   // TODO hide the dialog if visible
-            string [] authors = AboutDialogInfos.authors;
-            Gtk.show_about_dialog (this,
-                                   "program-name",          AboutDialogInfos.program_name,
-                                   "version",               AboutDialogInfos.version,
-                                   "comments",              AboutDialogInfos.comments,
-                                   "copyright",             AboutDialogInfos.copyright,
-                                   "license-type",          AboutDialogInfos.license_type,
-                                   "wrap-license", true,
-                                   "authors",               authors,
-                                   "translator-credits",    AboutDialogInfos.translator_credits,
-                                   "logo-icon-name",        AboutDialogInfos.logo_icon_name,
-                                   "website",               AboutDialogInfos.website,
-                                   "website-label",         AboutDialogInfos.website_label,
-                                   null);
-        }
-        else if (browser_view.in_window_about)
-            hide_in_window_about ();
-        else
-            show_in_window_about ();
-    }
-    private inline void show_in_window_about ()
-    {
-        headerbar.show_in_window_about ();
-        browser_view.show_in_window_about ();
-    }
-
-    /*\
-    * * adaptative stuff that needs to be there
-    \*/
-
-    private bool disable_popovers = false;
-    private bool disable_action_bar = false;
-    private void set_window_size (AdaptativeWidget.WindowSize new_size)
-    {
-        bool _disable_popovers = AdaptativeWidget.WindowSize.is_phone_size (new_size)
-                              || AdaptativeWidget.WindowSize.is_extra_thin (new_size);
-        if (disable_popovers != _disable_popovers)
-        {
-            disable_popovers = _disable_popovers;
-            if (browser_view.in_window_bookmarks)
-                hide_in_window_bookmarks ();
-            else if (browser_view.in_window_about)
-                hide_in_window_about ();
-        }
-
-        bool _disable_action_bar = _disable_popovers
-                                || AdaptativeWidget.WindowSize.is_extra_flat (new_size);
-        if (disable_action_bar != _disable_action_bar)
-        {
-            disable_action_bar = _disable_action_bar;
-            if (browser_view.in_window_modifications)
-                hide_in_window_modifications ();
-        }
-    }
-
-    /*\
-    * * bookmarks action entries
-    \*/
-
-    bool actions_init_done = false;
-    private SimpleAction move_top_action;
-    private SimpleAction move_up_action;
-    private SimpleAction move_down_action;
-    private SimpleAction move_bottom_action;
-    private SimpleAction trash_bookmark_action;
-    private SimpleAction edit_mode_state_action;
-
-    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);
-    }
-
-    private void install_bmk_action_entries ()
-    {
-        SimpleActionGroup action_group = new SimpleActionGroup ();
-        action_group.add_action_entries (bmk_action_entries, this);
-        insert_action_group ("bmk", action_group);
-
-        move_top_action         = (SimpleAction) action_group.lookup_action ("move-top");
-        move_up_action          = (SimpleAction) action_group.lookup_action ("move-up");
-        move_down_action        = (SimpleAction) action_group.lookup_action ("move-down");
-        move_bottom_action      = (SimpleAction) action_group.lookup_action ("move-bottom");
-        trash_bookmark_action   = (SimpleAction) action_group.lookup_action ("trash-bookmark");
-        edit_mode_state_action  = (SimpleAction) action_group.lookup_action ("set-edit-mode");
-        actions_init_done = true;
+        SimpleActionGroup action_group = new SimpleActionGroup ();
+        action_group.add_action_entries (bmk_action_entries, this);
+        insert_action_group ("bmk", action_group);
+
+        move_top_action         = (SimpleAction) action_group.lookup_action ("move-top");
+        move_up_action          = (SimpleAction) action_group.lookup_action ("move-up");
+        move_down_action        = (SimpleAction) action_group.lookup_action ("move-down");
+        move_bottom_action      = (SimpleAction) action_group.lookup_action ("move-bottom");
+        trash_bookmark_action   = (SimpleAction) action_group.lookup_action ("trash-bookmark");
+        edit_mode_state_action  = (SimpleAction) action_group.lookup_action ("set-edit-mode");
+        actions_init_done = true;
     }
 
     private const GLib.ActionEntry [] bmk_action_entries =
@@ -970,7 +725,6 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         browser_view.move_bottom ();
     }
 
-    [GtkCallback]
     private void on_bookmarks_selection_changed ()
     {
         update_actions ();
@@ -987,37 +741,14 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         insert_action_group ("kbd", action_group);
     }
 
-    private bool is_in_in_window_mode ()
-    {
-        return (browser_view.in_window_bookmarks || browser_view.in_window_modifications || 
browser_view.in_window_about);
-    }
-
     private const GLib.ActionEntry [] kbd_action_entries =
     {
-        // keyboard calls
         { "toggle-bookmark",    toggle_bookmark     },  // <P>b & <P>B
-        { "copy",               copy                },  // <P>c
-        { "copy-path",          copy_path           },  // <P>C
         { "bookmark",           bookmark            },  // <P>d
         { "unbookmark",         unbookmark          },  // <P>D
-        { "toggle-search",      _toggle_search      },  // <P>f // TODO unduplicate names
-        { "next-match",         next_match          },  // <P>g // usual shortcut for "next-match"     in a 
SearchEntry; see also "Down"
-        { "previous-match",     previous_match      },  // <P>G // usual shortcut for "previous-match" in a 
SearchEntry; see also "Up"
-        { "request-config",     _request_config     },  // <P>i // TODO fusion with ui.open-config?
         { "modifications",      modifications_list  },  // <A>i
-        { "edit-path-end",      edit_path_end       },  // <P>l
-        { "edit-path-last",     edit_path_last      },  // <P>L
-        { "paste",              paste               },  // <P>v
-        { "paste-force",        paste_force         },  // <P>V
-
-        { "open-root",          open_root           },  // <S><A>Up
-        { "open-parent",        open_current_parent },  //    <A>Up
-        { "open-child",         open_child          },  //    <A>Down
-        { "open-last-child",    open_last_child     },  // <S><A>Down
 
-        { "toggle-hamburger",   toggle_hamburger    },  // F10
         { "escape",             escape_pressed      },  // Escape
-        { "menu",               menu_pressed        },  // Menu
 
         { "toggle-boolean",     toggle_boolean      },  // <P>Return & <P>KP_Enter
         { "set-to-default",     set_to_default      }   // <P>Delete & <P>KP_Delete & decimalpoint & period 
& KP_Decimal
@@ -1039,55 +770,6 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
             show_in_window_bookmarks ();
     }
 
-    private void copy                                   (/* SimpleAction action, Variant? path_variant */)
-    {
-        Widget? focus = get_focus ();
-        if (focus != null)
-        {
-            if ((!) focus is Entry)
-            {
-                ((Entry) (!) focus).copy_clipboard ();
-                return;
-            }
-            if ((!) focus is TextView)
-            {
-                ((TextView) (!) focus).copy_clipboard ();
-                return;
-            }
-        }
-
-        model.copy_action_called ();
-
-        browser_view.discard_row_popover (); // TODO avoid duplicate get_selected_row () call
-
-        string? selected_row_text = browser_view.get_copy_text ();
-        if (selected_row_text == null && current_type == ViewType.OBJECT)
-            selected_row_text = model.get_suggested_key_copy_text (current_path, 
browser_view.last_context_id);
-        ConfigurationEditor application = (ConfigurationEditor) get_application ();
-        application.copy (selected_row_text == null ? current_path : (!) selected_row_text);
-    }
-
-    private void copy_path                              (/* SimpleAction action, Variant? path_variant */)
-    {
-        if (is_in_in_window_mode ())        // TODO better
-            return;
-
-        browser_view.discard_row_popover ();
-
-        if (headerbar.search_mode_enabled)
-        {
-            model.copy_action_called ();
-            string selected_row_text = browser_view.get_copy_path_text () ?? saved_view;
-            ((ConfigurationEditor) get_application ()).copy (selected_row_text);
-        }
-        else
-        {
-            if (browser_view.current_view == ViewType.OBJECT)
-                model.copy_action_called ();
-            ((ConfigurationEditor) get_application ()).copy (current_path);
-        }
-    }
-
     private void bookmark                               (/* SimpleAction action, Variant? variant */)
     {
         if (is_in_in_window_mode ())        // TODO better
@@ -1106,61 +788,6 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         headerbar.unbookmark_current_path ();
     }
 
-    private void _toggle_search                         (/* SimpleAction action, Variant? variant */)
-    {
-        if (is_in_in_window_mode ())        // TODO better
-            return;
-
-        headerbar.close_popovers ();    // should never be needed if headerbar.search_mode_enabled
-        browser_view.discard_row_popover ();   // could be needed if headerbar.search_mode_enabled
-
-        if (!headerbar.search_mode_enabled)
-            request_search (true, PathEntry.SearchMode.SEARCH);
-        else if (!headerbar.entry_has_focus)
-            headerbar.entry_grab_focus (true);
-        else if (headerbar.text.has_prefix ("/"))
-            request_search (true, PathEntry.SearchMode.SEARCH);
-        else
-            stop_search ();
-    }
-
-    private void next_match                             (/* SimpleAction action, Variant? variant */)   // 
See also "Down"
-    {
-        _next_match ();         // NOTE returns bool
-    }
-    private bool _next_match ()
-    {
-        if (headerbar.has_popover ())                   // for bookmarks popover
-            return headerbar.next_match ();
-        if (revealer.get_modifications_list_state ())   // for modifications popover
-            return revealer.next_match ();
-        else
-            return browser_view.next_match ();          // for in-window things and main list
-    }
-
-    private void previous_match                         (/* SimpleAction action, Variant? variant */)   // 
See also "Up"
-    {
-        _previous_match ();     // NOTE returns bool
-    }
-    private bool _previous_match ()
-    {
-        if (headerbar.has_popover ())                   // for bookmarks popover
-            return headerbar.previous_match ();
-        if (revealer.get_modifications_list_state ())   // for modifications popover
-            return revealer.previous_match ();
-        else
-            return browser_view.previous_match ();      // for in-window things and main list
-    }
-
-    private void _request_config                        (/* SimpleAction action, Variant? variant */)  // 
TODO unduplicate method name
-    {
-        if (is_in_in_window_mode ())        // TODO better
-            return;
-
-        if (browser_view.current_view == ViewType.FOLDER)
-            request_config (current_path);
-    }
-
     private void modifications_list                     (/* SimpleAction action, Variant? variant */)
     {
         if (!modifications_handler.get_current_delay_mode ())
@@ -1175,111 +802,6 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
             show_in_window_modifications ();
     }
 
-    private void edit_path_end                          (/* SimpleAction action, Variant? variant */)
-    {
-        if (is_in_in_window_mode ())
-            return;
-
-        if (!headerbar.search_mode_enabled)
-            request_search (true, PathEntry.SearchMode.EDIT_PATH_MOVE_END);
-    }
-
-    private void edit_path_last                         (/* SimpleAction action, Variant? variant */)
-    {
-        if (is_in_in_window_mode ())
-            return;
-
-        if (!headerbar.search_mode_enabled)
-            request_search (true, PathEntry.SearchMode.EDIT_PATH_SELECT_LAST_WORD);
-    }
-
-    private void paste                                  (/* SimpleAction action, Variant? variant */)
-    {
-        if (is_in_in_window_mode ())
-            return;
-
-        Widget? focus = get_focus ();
-        if (focus != null)
-        {
-            if ((!) focus is Entry)
-            {
-                ((Entry) (!) focus).paste_clipboard ();
-                return;
-            }
-            if ((!) focus is TextView)
-            {
-                ((TextView) (!) focus).paste_clipboard ();
-                return;
-            }
-        }
-
-        search_clipboard_content ();
-    }
-    private void search_clipboard_content ()
-    {
-        Gdk.Display? display = Gdk.Display.get_default ();
-        if (display == null)    // ?
-            return;
-
-        string? clipboard_content = Clipboard.get_default ((!) display).wait_for_text ();
-        if (clipboard_content != null)
-            request_search (true, PathEntry.SearchMode.EDIT_PATH_MOVE_END, clipboard_content);
-        else
-            request_search (true, PathEntry.SearchMode.SEARCH);
-    }
-
-    private void paste_force                            (/* SimpleAction action, Variant? variant */)
-    {
-        if (browser_view.in_window_bookmarks)
-            hide_in_window_bookmarks ();
-        else if (browser_view.in_window_modifications)
-            hide_in_window_modifications ();
-        else if (browser_view.in_window_about)
-            hide_in_window_about ();
-
-        search_clipboard_content ();
-    }
-
-    private void open_root                              (/* SimpleAction action, Variant? variant */)
-    {
-        if (is_in_in_window_mode ())
-            return;
-
-        go_backward (true);
-    }
-
-    private void open_current_parent                    (/* SimpleAction action, Variant? variant */)
-    {
-        if (is_in_in_window_mode ())
-            return;
-
-        if (browser_view.current_view == ViewType.CONFIG)
-            request_folder (current_path);
-        else
-            go_backward (false);
-    }
-
-    private void open_child                             (/* SimpleAction action, Variant? variant */)
-    {
-        if (is_in_in_window_mode ())
-            return;
-
-        go_forward (false);
-    }
-
-    private void open_last_child                        (/* SimpleAction action, Variant? variant */)
-    {
-        if (is_in_in_window_mode ())
-            return;
-
-        go_forward (true);
-    }
-
-    private void toggle_hamburger                       (/* SimpleAction action, Variant? variant */)
-    {
-        headerbar.toggle_hamburger_menu ();
-    }
-
     private void escape_pressed                         (/* SimpleAction action, Variant? variant */)
     {
         if (browser_view.in_window_bookmarks)
@@ -1291,7 +813,7 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         }
         else if (browser_view.in_window_modifications)
             hide_in_window_modifications ();
-        else if (browser_view.in_window_about)
+        else if (in_window_about)
             hide_in_window_about ();
         else if (headerbar.search_mode_enabled)
             stop_search ();
@@ -1299,22 +821,9 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
             request_folder (current_path);
     }
 
-    private void menu_pressed                           (/* SimpleAction action, Variant? variant */)
-    {
-        if (browser_view.toggle_row_popover ()) // handles in-window bookmarks
-            headerbar.close_popovers ();
-        else
-        {
-            headerbar.toggle_hamburger_menu ();
-            browser_view.discard_row_popover ();
-        }
-    }
-
     private void toggle_boolean                         (/* SimpleAction action, Variant? variant */)
     {
-        if (headerbar.has_popover ())
-            return;
-        if (is_in_in_window_mode ())
+        if (row_action_blocked ())
             return;
 
         browser_view.discard_row_popover ();
@@ -1323,9 +832,7 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
 
     private void set_to_default                         (/* SimpleAction action, Variant? variant */)
     {
-        if (headerbar.has_popover ())
-            return;
-        if (is_in_in_window_mode ())
+        if (row_action_blocked ())
             return;
 
         if (revealer.dismiss_selected_modification ())
@@ -1341,16 +848,64 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
             browser_view.set_selected_to_default ();
     }
 
+    /*\
+    * * keyboard calls helpers
+    \*/
+
+    protected override bool intercept_next_match (out bool interception_result)
+    {
+        if (headerbar.has_popover ())                   // for bookmarks popover
+        {
+            interception_result = headerbar.next_match ();
+            return true;
+        }
+        if (revealer.get_modifications_list_state ())   // for modifications popover
+        {
+            interception_result = revealer.next_match ();
+            return true;
+        }
+        interception_result = false; // garbage
+        return false;
+    }
+
+    protected override bool intercept_previous_match (out bool interception_result)
+    {
+        if (headerbar.has_popover ())                   // for bookmarks popover
+        {
+            interception_result = headerbar.previous_match ();
+            return true;
+        }
+        if (revealer.get_modifications_list_state ())   // for modifications popover
+        {
+            interception_result = revealer.previous_match ();
+            return true;
+        }
+        interception_result = false; // garbage
+        return false;
+    }
+
     /*\
     * * Path requests
     \*/
 
+    protected override void close_in_window_panels ()
+    {
+        headerbar.close_popovers ();
+        revealer.hide_modifications_list ();
+        if (browser_view.in_window_bookmarks)
+            hide_in_window_bookmarks ();
+        else if (browser_view.in_window_modifications)
+            hide_in_window_modifications ();
+        else if (in_window_about)
+            hide_in_window_about ();
+    }
+
     public static bool is_path_invalid (string path)
     {
         return path.has_prefix ("/") && (path.contains ("//") || path.contains (" "));
     }
 
-    private void request_config (string full_name)
+    protected override void request_config (string full_name)
     {
         browser_view.prepare_object_view (full_name, ModelUtils.folder_context_id,
                                           model.get_folder_properties (full_name),
@@ -1361,7 +916,7 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         // headerbar.search_mode_enabled = false; // do last to avoid flickering RegistryView before 
PropertiesView when selecting a search result
     }
 
-    private void request_folder (string full_name, string selected_or_empty = "", bool notify_missing = true)
+    protected override void request_folder (string full_name, string selected_or_empty = "", bool 
notify_missing = true)
     {
         string fallback_path = model.get_fallback_path (full_name);
 
@@ -1402,7 +957,7 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         return key_model;
     }
 
-    private void request_object (string full_name, uint16 context_id = ModelUtils.undefined_context_id, bool 
notify_missing = true, string schema_id = "")
+    protected override void request_object (string full_name, uint16 context_id = 
ModelUtils.undefined_context_id, bool notify_missing = true, string schema_id = "")
     {
         context_id = model.get_fallback_context (full_name, context_id, schema_id);
 
@@ -1430,268 +985,61 @@ private class DConfWindow : AdaptativeWindow, AdaptativeWidget
         // headerbar.search_mode_enabled = false; // do last to avoid flickering RegistryView before 
PropertiesView when selecting a search result
     }
 
-    private void request_search (bool reload, PathEntry.SearchMode mode = PathEntry.SearchMode.UNCLEAR, 
string? search = null)
-    {
-        string selected_row = browser_view.get_selected_row_name ();
-        if (reload)
-        {
-            reload_search_action.set_enabled (false);
-            browser_view.set_search_parameters (saved_view, headerbar.get_bookmarks ());
-            reload_search_next = false;
-        }
-        if (mode != PathEntry.SearchMode.UNCLEAR)
-            headerbar.prepare_search (mode, search);
-        string search_text = search == null ? headerbar.text : (!) search;
-        update_current_path (ViewType.SEARCH, search_text);
-        if (mode != PathEntry.SearchMode.UNCLEAR)
-            browser_view.select_row (selected_row);
-        if (!headerbar.entry_has_focus)
-            headerbar.entry_grab_focus (false);
-    }
-
-    private void reload_view ()
-    {
-        if (browser_view.current_view == ViewType.FOLDER)
-            request_folder (current_path, browser_view.get_selected_row_name ());
-        else if (browser_view.current_view == ViewType.OBJECT)
-            request_object (current_path, ModelUtils.undefined_context_id, false);
-        else if (browser_view.current_view == ViewType.SEARCH)
-            request_search (true);
-    }
-
-    /*\
-    * * Path changing
-    \*/
-
-    private void update_current_path (ViewType type, string path)
-    {
-        if (type == ViewType.OBJECT || type == ViewType.FOLDER)
-        {
-            saved_type = type;
-            saved_view = path;
-            reload_search_next = true;
-        }
-        else if (current_type == ViewType.FOLDER)
-            saved_selection = browser_view.get_selected_row_name ();
-        else if (current_type == ViewType.OBJECT)
-            saved_selection = "";
-
-        current_type = type;
-        current_path = path;
-
-        browser_view.set_path (type, path);
-        headerbar.set_path (type, path);
-        headerbar.update_hamburger_menu (modifications_handler.get_current_delay_mode ());
-
-        Variant variant = new Variant ("(sq)", path, (type == ViewType.FOLDER) || (type == ViewType.CONFIG) 
? ModelUtils.folder_context_id : ModelUtils.undefined_context_id);
-        open_path_action.set_state (variant);
-        disabled_state_action.set_state (variant);
-    }
-
-    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 ();
-        headerbar.update_hamburger_menu (delay_mode);
-    }
-
     /*\
-    * * Search callbacks
+    * * navigation helpers
     \*/
 
-    [GtkCallback]
-    private void search_changed_cb ()
+    protected override string get_copy_text ()
     {
-        request_search (reload_search_next);
-    }
-
-    [GtkCallback]
-    private void search_stopped_cb ()
-    {
-        browser_view.row_grab_focus ();
+        model.copy_action_called ();
 
-        reload_search_action.set_enabled (false);
-        if (saved_type == ViewType.FOLDER)
-            request_folder (saved_view, saved_selection);
-        else
-            update_current_path (saved_type, strdup (saved_view));
-        reload_search_next = true;
+        string? selected_row_text = browser_view.get_copy_text ();
+        if (selected_row_text == null && current_type == ViewType.OBJECT)
+            selected_row_text = model.get_suggested_key_copy_text (current_path, 
browser_view.last_context_id);
+        return selected_row_text == null ? current_path : (!) selected_row_text;
     }
 
-    private void stop_search ()
+    protected override string get_copy_path_text ()
     {
         if (headerbar.search_mode_enabled)
-            search_stopped_cb ();
-    }
-
-    /*\
-    * * Global callbacks
-    \*/
-
-    [GtkCallback]
-    private bool on_button_press_event (Widget widget, Gdk.EventButton event)
-    {
-        if (!mouse_extra_buttons)
-            return false;
-
-        if (event.button == mouse_back_button)
         {
-            if (mouse_back_button == mouse_forward_button)
-            {
-                warning (_("The same mouse button is set for going backward and forward. Doing nothing."));
-                return false;
-            }
-
-            go_backward ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0);
-            return true;
-        }
-        if (event.button == mouse_forward_button)
-        {
-            go_forward ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0);
-            return true;
-        }
-        return false;
-    }
-
-    [GtkCallback]
-    private bool on_key_press_event (Widget widget, Gdk.EventKey event)
-    {
-        uint keyval = event.keyval;
-        string name = (!) (Gdk.keyval_name (keyval) ?? "");
-
-        if (name == "F1") // TODO fix dance done with the F1 & <Primary>F1 shortcuts that show help overlay
-        {
-            browser_view.discard_row_popover ();
-            if ((event.state & Gdk.ModifierType.SHIFT_MASK) == 0)
-                return false;   // help overlay
-            about_cb ();
-            return true;
-        }
-
-        /* for changing row during search; cannot use set_accels_for_action() else popovers are not handled 
anymore */
-        if (name == "Down" && (event.state & Gdk.ModifierType.MOD1_MASK) == 0)  // see also <ctrl>g
-            return _next_match ();
-        if (name == "Up"   && (event.state & Gdk.ModifierType.MOD1_MASK) == 0)  // see also <ctrl>G
-            return _previous_match ();
-
-        if (is_in_in_window_mode ())
-            return false;
-
-        /* don't use "else if", or some widgets will not be hidden on <ctrl>F10 or such things */
-        if (name == "F10" && (event.state & Gdk.ModifierType.SHIFT_MASK) != 0)
-        {
-            Widget? focus = get_focus ();
-            if (focus != null && (((!) focus is Entry) || ((!) focus is TextView))) // && 
browser_view.current_view != ViewType.SEARCH
-                return false;
-
-            headerbar.toggle_pathbar_menu ();
-            return true;
-        }
-
-        if (name == "Return" || name == "KP_Enter")
-        {
-            if (browser_view.current_view == ViewType.SEARCH
-             && headerbar.entry_has_focus
-             && browser_view.return_pressed ())
-                return true;
-            return false;
+            model.copy_action_called ();
+            return browser_view.get_copy_path_text () ?? saved_view;
         }
 
-        if (headerbar.has_popover ())
-            return false;
-
-        if (!headerbar.search_mode_enabled &&
-            // see gtk_search_entry_is_keynav() in gtk+/gtk/gtksearchentry.c:388
-            (keyval == Gdk.Key.Tab          || keyval == Gdk.Key.KP_Tab         ||
-             keyval == Gdk.Key.Up           || keyval == Gdk.Key.KP_Up          ||
-             keyval == Gdk.Key.Down         || keyval == Gdk.Key.KP_Down        ||
-             keyval == Gdk.Key.Left         || keyval == Gdk.Key.KP_Left        ||
-             keyval == Gdk.Key.Right        || keyval == Gdk.Key.KP_Right       ||
-             keyval == Gdk.Key.Home         || keyval == Gdk.Key.KP_Home        ||
-             keyval == Gdk.Key.End          || keyval == Gdk.Key.KP_End         ||
-             keyval == Gdk.Key.Page_Up      || keyval == Gdk.Key.KP_Page_Up     ||
-             keyval == Gdk.Key.Page_Down    || keyval == Gdk.Key.KP_Page_Down   ||
-             ((event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)) != 0) ||
-             name == "space" || name == "KP_Space"))
-            return false;
-
-        Widget? focus = get_focus ();
-        bool focus_is_text_widget = focus != null && (((!) focus is Entry) || ((!) focus is TextView));
-        if ((!focus_is_text_widget)
-         && (event.is_modifier == 0)
-         && (event.length != 0)
-//       && (name != "F10")     // else <Shift>F10 toggles the search_entry popup; see if a976aa9740 fixes 
that in Gtk+ 4
-         && (headerbar.handle_event (event)))
-            return true;
+        if (browser_view.current_view == ViewType.OBJECT)
+            model.copy_action_called ();
 
-        return false;
+        return current_path;
     }
 
-    private void go_backward (bool shift)
-    {
-        if (headerbar.search_mode_enabled)
-            return;
-
-        headerbar.close_popovers ();        // by symmetry with go_forward()
-        browser_view.discard_row_popover ();
-
-        if (current_path == "/")
-            return;
-        if (shift)
-            request_folder ("/");
-        else
-            request_folder (ModelUtils.get_parent_path (current_path), current_path.dup ());
-    }
+    /*\
+    * * Non-existant path notifications // TODO unduplicate
+    \*/
 
-    private void go_forward (bool shift)
+    private void notify_folder_emptied (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
     {
-        if (headerbar.search_mode_enabled)
-            return;
-
-        string complete_path = headerbar.get_complete_path ();
-
-        headerbar.close_popovers ();
-        browser_view.discard_row_popover ();
-
-        if (current_path == complete_path)  // TODO something?
-            return;
+        string full_name = ((!) path_variant).get_string ();
 
-        if (shift)
-        {
-            string fallback_path = model.get_fallback_path (complete_path);
-            if (ModelUtils.is_key_path (fallback_path))
-                request_object (fallback_path);
-            else if (fallback_path != current_path)
-                request_folder (fallback_path);
-            else
-                request_folder (complete_path);
-        }
-        else
-        {
-            int index_of_last_slash = complete_path.index_of ("/", ((!) current_path).length);
-            if (index_of_last_slash != -1)
-                request_folder (complete_path.slice (0, index_of_last_slash + 1));
-            else if (ModelUtils.is_key_path (complete_path))
-                request_object (complete_path);
-            else
-                request_folder (complete_path);
-        }
+        show_notification (_("Folder “%s” is now empty.").printf (full_name));
     }
 
-    /*\
-    * * Non-existant path notifications
-    \*/
-
-    private void show_notification (string notification)
+    private void notify_object_deleted (SimpleAction action, Variant? path_variant)
+        requires (path_variant != null)
     {
-        notifications_revealer.show_notification (notification);
+        string full_name;
+        uint16 unused;  // GAction parameter type switch is a little touchy, see pathbar.vala
+        ((!) path_variant).@get ("(sq)", out full_name, out unused);
+
+        show_notification (_("Key “%s” has been deleted.").printf (full_name));
     }
 
     private void cannot_find_key (string full_name)
     {
         show_notification (_("Cannot find key “%s”.").printf (full_name));
     }
+
     private void cannot_find_folder (string full_name)
     {
         show_notification (_("There’s nothing in requested folder “%s”.").printf (full_name));
diff --git a/editor/key-list-box-row.vala b/editor/key-list-box-row.vala
index 5a48124..b5aca8d 100644
--- a/editor/key-list-box-row.vala
+++ b/editor/key-list-box-row.vala
@@ -241,9 +241,9 @@ private class KeyListBoxRow : ClickableListBoxRow, AdaptativeWidget
             ((!) boolean_switch).halign = Align.END;
             ((!) boolean_switch).valign = Align.CENTER;
             if (has_schema)
-                ((!) boolean_switch).set_detailed_action_name ("ui.empty(('',uint16 0,true,true))");
+                ((!) boolean_switch).set_detailed_action_name ("browser.empty(('',uint16 0,true,true))");
             else
-                ((!) boolean_switch).set_detailed_action_name ("ui.empty(('',true))");
+                ((!) boolean_switch).set_detailed_action_name ("browser.empty(('',true))");
 
             _use_switch = true;
             hide_or_show_switch ();
@@ -396,7 +396,7 @@ private class KeyListBoxRow : ClickableListBoxRow, AdaptativeWidget
     internal void update_switch (bool key_value_boolean, string detailed_action_name)
         requires (boolean_switch != null)
     {
-        ((!) boolean_switch).set_action_name ("ui.empty");
+        ((!) boolean_switch).set_action_name ("browser.empty");
         ((!) boolean_switch).set_active (key_value_boolean);
         ((!) boolean_switch).set_detailed_action_name (detailed_action_name);
     }
diff --git a/editor/large-pathbar.ui b/editor/large-pathbar.ui
index b2fa502..68ce48a 100644
--- a/editor/large-pathbar.ui
+++ b/editor/large-pathbar.ui
@@ -9,9 +9,9 @@
     <child>
       <object class="LargePathbarItem" id="root_button">
         <property name="visible">True</property>
-        <property name="action-name">ui.open-folder</property>
-        <property name="alternative-action">ui.open-folder('/')</property>
-        <property name="default-action">ui.open-folder('/')</property>
+        <property name="action-name">browser.open-folder</property>
+        <property name="alternative-action">browser.open-folder('/')</property>
+        <property name="default-action">browser.open-folder('/')</property>
         <property name="action-target">'/'</property>
         <style>
           <class name="root-button"/>
diff --git a/editor/large-pathbar.vala b/editor/large-pathbar.vala
index 3408bba..48ea4c2 100644
--- a/editor/large-pathbar.vala
+++ b/editor/large-pathbar.vala
@@ -28,6 +28,16 @@ private class LargePathbar : Box, Pathbar
     {
         return complete_path;
     }
+    private string fallback_path = "";
+    internal void get_fallback_path_and_complete_path (out string _fallback_path, out string _complete_path)
+    {
+        if (fallback_path != "" && ModelUtils.is_folder_path (fallback_path) && complete_path.has_prefix 
(fallback_path))
+            _fallback_path = fallback_path;
+        else
+            _fallback_path = complete_path;
+
+        _complete_path = complete_path;
+    }
 
     construct
     {
@@ -67,7 +77,7 @@ private class LargePathbar : Box, Pathbar
         if (invisible_button != null)
         {
             Variant variant = new Variant.string (complete_path);
-            ((!) invisible_button).set_detailed_action_name ("ui.open-search(" + variant.print (false) + 
")");
+            ((!) invisible_button).set_detailed_action_name ("browser.open-search(" + variant.print (false) 
+ ")");
         }
         update_active_button_cursor (type, ref active_button);
     }
@@ -163,6 +173,7 @@ private class LargePathbar : Box, Pathbar
 
     internal void update_ghosts (string non_ghost_path, bool is_search)
     {
+        fallback_path = non_ghost_path;
         string action_target = "";
         @foreach ((child) => {
                 StyleContext context = child.get_style_context ();
@@ -190,7 +201,7 @@ private class LargePathbar : Box, Pathbar
                         else
                         {
                             item.set_cursor_type (LargePathbarItem.CursorType.CONTEXT);
-                            item.set_action_name ("ui.empty");
+                            item.set_action_name ("browser.empty");
                         }
                         if (non_ghost_path.has_prefix (action_target))
                             context.remove_class ("inexistent");
@@ -227,7 +238,7 @@ private class LargePathbar : Box, Pathbar
         else
         {
             active_button.set_cursor_type (LargePathbarItem.CursorType.CONTEXT);
-            active_button.set_action_name ("ui.empty");
+            active_button.set_action_name ("browser.empty");
         }
     }
 
@@ -267,13 +278,13 @@ private class LargePathbar : Box, Pathbar
     {
         Variant variant = new Variant.string (complete_path);
         string _variant = variant.print (false);
-        path_bar_item = new LargePathbarItem (label, "ui.open-folder(" + _variant + ")", 
"ui.notify-folder-emptied(" + _variant + ")");
+        path_bar_item = new LargePathbarItem (label, "browser.open-folder(" + _variant + ")", 
"ui.notify-folder-emptied(" + _variant + ")");
     }
     private static inline void init_object_path_bar_item (string label, string complete_path, out 
LargePathbarItem path_bar_item)
     {
         Variant variant = new Variant ("(sq)", complete_path, ModelUtils.undefined_context_id);
         string _variant = variant.print (true);
-        path_bar_item = new LargePathbarItem (label, "ui.open-object(" + _variant + ")", 
"ui.notify-object-deleted(" + _variant + ")");
+        path_bar_item = new LargePathbarItem (label, "browser.open-object(" + _variant + ")", 
"ui.notify-object-deleted(" + _variant + ")");
     }
 
     private void activate_item (LargePathbarItem item, bool state)   // never called when current_view is 
search
@@ -290,7 +301,7 @@ private class LargePathbar : Box, Pathbar
         {
             item.is_active = true;
             item.set_cursor_type (LargePathbarItem.CursorType.CONTEXT);
-            item.set_action_name ("ui.empty");
+            item.set_action_name ("browser.empty");
             item.get_style_context ().add_class ("active");
         }
         else
@@ -307,7 +318,7 @@ private class InvisibleButton : Button
 {
     construct
     {
-        set_detailed_action_name ("ui.open-search('/')");
+        set_detailed_action_name ("browser.open-search('/')");
         hexpand = true;
         vexpand = true;
         can_focus = false;
diff --git a/editor/meson.build b/editor/meson.build
index ab25c67..77de73d 100644
--- a/editor/meson.build
+++ b/editor/meson.build
@@ -76,6 +76,7 @@ sources = files(
   'browser-infobar.vala',
   'browser-stack.vala',
   'browser-view.vala',
+  'browser-window.vala',
   'dconf-editor.vala',
   'dconf-model.vala',
   'dconf-view.vala',
@@ -111,9 +112,9 @@ resource_data = files(
   'browser-infobar.ui',
   'browser-stack.ui',
   'browser-view.ui',
+  'browser-window.ui',
   'config-list-box-row.ui',
   'dconf-editor.css',
-  'dconf-editor.ui',
   'delayed-setting-view.ui',
   'folder-list-box-row.ui',
   'help-overlay.ui',
diff --git a/editor/modifications-revealer.vala b/editor/modifications-revealer.vala
index 93a6b4b..77ff19b 100644
--- a/editor/modifications-revealer.vala
+++ b/editor/modifications-revealer.vala
@@ -235,7 +235,7 @@ private class ModificationsRevealer : Revealer, AdaptativeWidget
         if (modifications_handler.get_current_delay_mode ())
         {
             Variant variant = new Variant ("(sq)", full_name, context_id);
-            view.set_detailed_action_name ("ui.open-object(" + variant.print (true) + ")");
+            view.set_detailed_action_name ("browser.open-object(" + variant.print (true) + ")");
         }
         view.show ();
         return view;
diff --git a/editor/pathentry.ui b/editor/pathentry.ui
index a1a31ee..0be0a5d 100644
--- a/editor/pathentry.ui
+++ b/editor/pathentry.ui
@@ -33,7 +33,7 @@
     <child>
       <object class="GtkButton" id="hide_search_button">
         <property name="visible">False</property>
-        <property name="action-name">ui.hide-search</property>
+        <property name="action-name">browser.hide-search</property>
         <style>
           <class name="image-button"/>
         </style>
@@ -54,7 +54,7 @@
     <child>
       <object class="GtkButton" id="reload_search_button">
         <property name="visible">True</property>
-        <property name="action-name">ui.reload-search</property>
+        <property name="action-name">browser.reload-search</property>
         <style>
           <class name="image-button"/>
           <class name="suggested-action"/>
diff --git a/editor/pathwidget.ui b/editor/pathwidget.ui
index 275fec9..b54ba70 100644
--- a/editor/pathwidget.ui
+++ b/editor/pathwidget.ui
@@ -41,7 +41,7 @@
                 <property name="focus-on-click">False</property>
                 <property name="iconic">True</property>
                 <property name="centered">True</property>
-                <property name="action-name">ui.show-search</property>
+                <property name="action-name">browser.show-search</property>
                 <style>
                   <class name="image-button"/>
                 </style>
@@ -69,7 +69,7 @@
         <property name="focus-on-click">False</property>
         <property name="iconic">True</property>
         <property name="centered">True</property>
-        <property name="action-name">ui.toggle-search</property>
+        <property name="action-name">browser.toggle-search</property>
         <property name="action-target">true</property>
         <style>
           <class name="image-button"/>
diff --git a/editor/pathwidget.vala b/editor/pathwidget.vala
index cb1fff0..7b17001 100644
--- a/editor/pathwidget.vala
+++ b/editor/pathwidget.vala
@@ -130,6 +130,10 @@ private class PathWidget : Box, AdaptativeWidget
     {
         return pathbar.get_complete_path ();
     }
+    internal void get_fallback_path_and_complete_path (out string fallback_path, out string complete_path)
+    {
+        pathbar.get_fallback_path_and_complete_path (out fallback_path, out complete_path);
+    }
 
     internal void update_ghosts (string fallback_path)
     {
diff --git a/editor/registry-list.vala b/editor/registry-list.vala
index 9fdba45..989e529 100644
--- a/editor/registry-list.vala
+++ b/editor/registry-list.vala
@@ -537,7 +537,7 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
         if (row is SearchListBoxRow)
         {
             wrapper.get_style_context ().add_class ("f-or-s-row");
-            wrapper.action_name = "ui.open-search";
+            wrapper.action_name = "browser.open-search";
             wrapper.set_action_target ("s", row.full_name);
         }
         else if (row is ReturnListBoxRow)
@@ -545,12 +545,12 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
             wrapper.get_style_context ().add_class ("f-or-s-row");
             if (ModelUtils.is_folder_context_id (row.context_id))
             {
-                wrapper.action_name = "ui.open-folder";
+                wrapper.action_name = "browser.open-folder";
                 wrapper.set_action_target ("s", row.full_name);
             }
             else
             {
-                wrapper.action_name = "ui.open-object";
+                wrapper.action_name = "browser.open-object";
                 wrapper.set_action_target ("(sq)", row.full_name, row.context_id);
             }
         }
@@ -560,19 +560,19 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
             if (row is FolderListBoxRow)
             {
                 if (((FolderListBoxRow) row).path_search)
-                    wrapper.action_name = "ui.next-search";
+                    wrapper.action_name = "browser.next-search";
                 else
-                    wrapper.action_name = "ui.open-folder";
+                    wrapper.action_name = "browser.open-folder";
             }
             else if (row is ConfigListBoxRow)
-                wrapper.action_name = "ui.open-config";
+                wrapper.action_name = "browser.open-config";
             else assert_not_reached ();
             wrapper.set_action_target ("s", row.full_name);
         }
         else if (row is KeyListBoxRow)
         {
             wrapper.get_style_context ().add_class ("key-row");
-            wrapper.action_name = "ui.open-object";
+            wrapper.action_name = "browser.open-object";
             wrapper.set_action_target ("(sq)", row.full_name, row.context_id);
         }
         else assert_not_reached ();
@@ -842,7 +842,7 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
         ContextPopover popover = (!) row.nullable_popover;
         Variant variant = new Variant.string (row.full_name);
 
-        popover.new_gaction ("open-search", "ui.open-search(" + variant.print (false) + ")");
+        popover.new_gaction ("open-search", "browser.open-search(" + variant.print (false) + ")");
         popover.new_gaction ("copy", "app.copy(" + _get_folder_or_search_copy_text_variant (row).print 
(false) + ")");
 
         return true;
@@ -856,7 +856,7 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
         ContextPopover popover = (!) row.nullable_popover;
         Variant variant = new Variant.string (row.full_name);
 
-        popover.new_gaction ("open-config", "ui.open-config(" + variant.print (false) + ")");
+        popover.new_gaction ("open-config", "browser.open-config(" + variant.print (false) + ")");
 //        popover.new_gaction ("copy", "app.copy(" + _get_folder_or_search_copy_text_variant (row).print 
(false) + ")");
 
         return true;
@@ -871,12 +871,12 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
         if (row.context_id == ModelUtils.folder_context_id)
         {
             Variant variant = new Variant.string (row.full_name);
-            popover.new_gaction ("go-back", "ui.open-folder(" + variant.print (false) + ")");
+            popover.new_gaction ("go-back", "browser.open-folder(" + variant.print (false) + ")");
         }
         else
         {
             Variant variant_sq = new Variant ("(sq)", row.full_name, row.context_id);
-            popover.new_gaction ("go-back", "ui.open-object(" + variant_sq.print (true) + ")");
+            popover.new_gaction ("go-back", "browser.open-object(" + variant_sq.print (true) + ")");
         }
 
         return true;
@@ -892,11 +892,11 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
 
         if (row.search_result_mode)
         {
-            popover.new_gaction ("open-parent", "ui.open-parent(" + variant.print (false) + ")");
+            popover.new_gaction ("open-parent", "browser.open-parent(" + variant.print (false) + ")");
             popover.new_section ();
         }
 
-        popover.new_gaction ("open-folder", "ui.open-folder(" + variant.print (false) + ")");
+        popover.new_gaction ("open-folder", "browser.open-folder(" + variant.print (false) + ")");
         popover.new_gaction ("copy", "app.copy(" + _get_folder_or_search_copy_text_variant (row).print 
(false) + ")");
 
         popover.new_section ();
@@ -939,13 +939,13 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
 
         if (row.search_result_mode)
         {
-            popover.new_gaction ("open-parent", "ui.open-parent(" + variant_s.print (false) + ")");
+            popover.new_gaction ("open-parent", "browser.open-parent(" + variant_s.print (false) + ")");
             popover.new_section ();
         }
 
         if (key_conflict == KeyConflict.HARD)
         {
-            popover.new_gaction ("detail", "ui.open-object(" + variant_sq.print (true) + ")");
+            popover.new_gaction ("detail", "browser.open-object(" + variant_sq.print (true) + ")");
             popover.new_gaction ("copy", "app.copy(" + copy_text_variant.print (false) + ")");
             properties.clear ();
             return true; // anything else is value-related, so we are done
@@ -955,7 +955,7 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
         bool planned_change = modifications_handler.key_has_planned_change (full_name);
         Variant? planned_value = modifications_handler.get_key_planned_value (full_name);
 
-        popover.new_gaction ("customize", "ui.open-object(" + variant_sq.print (true) + ")");
+        popover.new_gaction ("customize", "browser.open-object(" + variant_sq.print (true) + ")");
         popover.new_gaction ("copy", "app.copy(" + copy_text_variant.print (false) + ")");
 
         if (type_string == "b" || type_string == "<enum>" || type_string == "mb"
@@ -1047,11 +1047,11 @@ private abstract class RegistryList : Grid, BrowsableView, AdaptativeWidget
 
         if (row.search_result_mode)
         {
-            popover.new_gaction ("open-parent", "ui.open-parent(" + variant_s.print (false) + ")");
+            popover.new_gaction ("open-parent", "browser.open-parent(" + variant_s.print (false) + ")");
             popover.new_section ();
         }
 
-        popover.new_gaction ("customize", "ui.open-object(" + variant_sq.print (true) + ")");
+        popover.new_gaction ("customize", "browser.open-object(" + variant_sq.print (true) + ")");
         popover.new_gaction ("copy", "app.copy(" + copy_text_variant.print (false) + ")");
 
         bool planned_change = modifications_handler.key_has_planned_change (row.full_name);
diff --git a/editor/short-pathbar.vala b/editor/short-pathbar.vala
index 10f1634..e7c316c 100644
--- a/editor/short-pathbar.vala
+++ b/editor/short-pathbar.vala
@@ -27,6 +27,15 @@ private class ShortPathbar : Grid, Pathbar
     {
         return complete_path;
     }
+    internal void get_fallback_path_and_complete_path (out string _fallback_path, out string _complete_path)
+    {
+        if (non_ghost_path != "" && ModelUtils.is_folder_path (non_ghost_path) && complete_path.has_prefix 
(non_ghost_path))
+            _fallback_path = non_ghost_path;
+        else
+            _fallback_path = complete_path;
+
+        _complete_path = complete_path;
+    }
 
     [GtkChild] private MenuButton   menu_button;
     [GtkChild] private Label        view_label;
@@ -101,7 +110,7 @@ private class ShortPathbar : Grid, Pathbar
         string tmp_path = "/";
 
         if (complete_path != "/")
-            menu.append ("/", "ui.open-path(('/',uint16 " + ModelUtils.folder_context_id_string + "))");
+            menu.append ("/", "browser.open-path(('/',uint16 " + ModelUtils.folder_context_id_string + "))");
 
         // other folders
         foreach (string item in split)
@@ -109,9 +118,9 @@ private class ShortPathbar : Grid, Pathbar
             tmp_path += item + "/";
             Variant variant = new Variant ("(sq)", tmp_path, ModelUtils.folder_context_id);
             if (non_ghost_path.has_prefix (tmp_path))
-                menu.append (item, "ui.open-path(" + variant.print (true) + ")");  // TODO append or prepend?
+                menu.append (item, "browser.open-path(" + variant.print (true) + ")");  // TODO append or 
prepend?
             else
-                menu.append (item, "ui.disabled-state(" + variant.print (true) + ")");  // TODO append or 
prepend?
+                menu.append (item, "browser.disabled-state(" + variant.print (true) + ")");  // TODO append 
or prepend?
         }
 
         // key or nothing
@@ -125,9 +134,9 @@ private class ShortPathbar : Grid, Pathbar
             uint16 context_id = is_folder ? ModelUtils.folder_context_id : ModelUtils.undefined_context_id;
             Variant variant = new Variant ("(sq)", tmp_path, context_id);
             if (non_ghost_path.has_prefix (tmp_path))   // FIXME problem if key and folder named similarly
-                menu.append (last, "ui.open-path(" + variant.print (true) + ")");
+                menu.append (last, "browser.open-path(" + variant.print (true) + ")");
             else
-                menu.append (last, "ui.disabled-state(" + variant.print (true) + ")");  // TODO append or 
prepend?
+                menu.append (last, "browser.disabled-state(" + variant.print (true) + ")");  // TODO append 
or prepend?
         }
 
         section.freeze ();


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