[dconf-editor] Make popovers work correctly in delayed mode.



commit 0b8e19b4645aba279d31b1c3cdf64ea4d6b92ac5
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Sat Jun 18 02:55:46 2016 +0200

    Make popovers work correctly in delayed mode.

 editor/dconf-model.vala            |    2 +
 editor/dconf-view.vala             |    4 +-
 editor/dconf-window.vala           |   50 ++++++++++++++--
 editor/key-list-box-row.vala       |  108 +++++++++++++++++++++++++----------
 editor/modifications-revealer.vala |   51 ++++++++++++++++-
 5 files changed, 171 insertions(+), 44 deletions(-)
---
diff --git a/editor/dconf-model.vala b/editor/dconf-model.vala
index fe11d56..53fceba 100644
--- a/editor/dconf-model.vala
+++ b/editor/dconf-model.vala
@@ -179,7 +179,9 @@ public abstract class Key : SettingObject
     public string type_string { get; protected set; default = "*"; }
     public Variant properties { owned get; protected set; }
 
+    public bool planned_change = false;
     public Variant? planned_value { get; set; default = null; }
+
     public abstract Variant value { owned get; set; }
 
     public signal void value_changed ();
diff --git a/editor/dconf-view.vala b/editor/dconf-view.vala
index 157770f..17e0d6a 100644
--- a/editor/dconf-view.vala
+++ b/editor/dconf-view.vala
@@ -149,7 +149,7 @@ private class KeyEditorChildEnum : MenuButton, KeyEditorChild
         this.label = variant.get_type () == VariantType.STRING ? variant.get_string () : variant.print 
(false);
 
         ContextPopover popover = new ContextPopover ();
-        popover.create_buttons_list (key, false);
+        popover.create_buttons_list (key, false, false);
         popover.set_relative_to (this);
         popover.value_changed.connect ((gvariant) => {
                 variant = gvariant;
@@ -231,7 +231,7 @@ private class KeyEditorChildNullableBool : MenuButton, KeyEditorChild
             this.label = Key.cool_boolean_text_value (((!) maybe_variant).get_boolean ());
 
         ContextPopover popover = new ContextPopover ();
-        popover.create_buttons_list (key, false);
+        popover.create_buttons_list (key, false, false);
         popover.set_relative_to (this);
         popover.value_changed.connect ((gvariant) => {
                 variant = gvariant;
diff --git a/editor/dconf-window.vala b/editor/dconf-window.vala
index f2f91f4..190d501 100644
--- a/editor/dconf-window.vala
+++ b/editor/dconf-window.vala
@@ -42,6 +42,8 @@ class DConfWindow : ApplicationWindow
     private GLib.Settings settings = new GLib.Settings ("ca.desrt.dconf-editor.Settings");
     [GtkChild] private Bookmarks bookmarks_button;
 
+    private GLib.ListStore rows_possibly_with_popover = new GLib.ListStore (typeof (ClickableListBoxRow));
+
     [GtkChild] private ModificationsRevealer revealer;
 
     [GtkChild] private SearchBar search_bar;
@@ -57,6 +59,9 @@ class DConfWindow : ApplicationWindow
         add_action_entries (action_entries, this);
         add_action (settings.create_action ("delayed-apply-menu"));
 
+        settings.changed["delayed-apply-menu"].connect (invalidate_popovers);
+        revealer.invalidate_popovers.connect (invalidate_popovers);
+
         set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
         if (settings.get_boolean ("window-is-fullscreen"))
             fullscreen ();
@@ -193,6 +198,8 @@ class DConfWindow : ApplicationWindow
     [GtkCallback]
     private bool scroll_to_path (string full_name)
     {
+        invalidate_popovers ();
+
         if (full_name == "/")
         {
             dir_tree_selection.unselect_all ();
@@ -240,13 +247,17 @@ class DConfWindow : ApplicationWindow
             Key key = (Key) item;
             if (key.has_schema)
             {
-                row = new KeyListBoxRowEditable ((GSettingsKey) key);
-                ((KeyListBoxRow) row).set_key_value.connect ((variant) => { set_glib_key_value 
((GSettingsKey) key, variant); });
+                GSettingsKey gkey = (GSettingsKey) key;
+                row = new KeyListBoxRowEditable (gkey);
+                ((KeyListBoxRow) row).set_key_value.connect ((variant) => { set_glib_key_value (gkey, 
variant); });
+                ((KeyListBoxRow) row).change_dismissed.connect (() => { revealer.dismiss_glib_change (gkey); 
});
             }
             else
             {
-                row = new KeyListBoxRowEditableNoSchema ((DConfKey) key);
-                ((KeyListBoxRow) row).set_key_value.connect ((variant) => { set_dconf_key_value ((DConfKey) 
key, variant); });
+                DConfKey dkey = (DConfKey) key;
+                row = new KeyListBoxRowEditableNoSchema (dkey);
+                ((KeyListBoxRow) row).set_key_value.connect ((variant) => { set_dconf_key_value (dkey, 
variant); });
+                ((KeyListBoxRow) row).change_dismissed.connect (() => { revealer.dismiss_dconf_change 
(dkey); });
             }
             row.on_row_clicked.connect (() => { new_key_editor (key); });
             // TODO bug: row is always visually activated after the dialog destruction if mouse is over at 
this time
@@ -373,6 +384,14 @@ class DConfWindow : ApplicationWindow
         ListBoxRow list_box_row = (ListBoxRow) widget.get_parent ();
         key_list_box.select_row (list_box_row);
         list_box_row.grab_focus ();
+
+        ClickableListBoxRow row = (ClickableListBoxRow) widget;
+        if (event.button == Gdk.BUTTON_SECONDARY)
+        {
+            row.show_right_click_popover (settings.get_boolean ("delayed-apply-menu"), (int) (event.x - 
row.get_allocated_width () / 2.0));
+            rows_possibly_with_popover.append (row);
+        }
+
         return false;
     }
 
@@ -384,13 +403,26 @@ class DConfWindow : ApplicationWindow
         ((ClickableListBoxRow) list_box_row.get_child ()).on_row_clicked ();
     }
 
+    private void invalidate_popovers ()
+    {
+        uint position = 0;
+        ClickableListBoxRow? row = (ClickableListBoxRow?) rows_possibly_with_popover.get_item (0);
+        while (row != null)
+        {
+            row.destroy_popover ();
+            position++;
+            row = (ClickableListBoxRow?) rows_possibly_with_popover.get_item (position);
+        }
+        rows_possibly_with_popover.remove_all ();
+    }
+
     /*\
     * * Revealer stuff
     \*/
 
     private void set_dconf_key_value (DConfKey key, Variant? new_value)
     {
-        if (settings.get_boolean ("delayed-apply-menu"))
+        if (settings.get_boolean ("delayed-apply-menu") || key.planned_change)
             revealer.add_delayed_dconf_settings (key, new_value);
         else if (new_value != null)
             key.value = new_value;
@@ -400,7 +432,7 @@ class DConfWindow : ApplicationWindow
 
     private void set_glib_key_value (GSettingsKey key, Variant? new_value)
     {
-        if (settings.get_boolean ("delayed-apply-menu"))
+        if (settings.get_boolean ("delayed-apply-menu") || key.planned_change)
             revealer.add_delayed_glib_settings (key, new_value);
         else if (new_value != null)
             key.value = new_value;
@@ -415,11 +447,13 @@ class DConfWindow : ApplicationWindow
     private void reset ()
     {
         reset_generic (key_model, false);
+        invalidate_popovers ();
     }
 
     private void reset_recursively ()
     {
         reset_generic (key_model, true);
+        invalidate_popovers ();
     }
 
     private void reset_generic (GLib.ListStore? objects, bool recursively)   // TODO notification if nothing 
to reset
@@ -535,7 +569,9 @@ class DConfWindow : ApplicationWindow
                     bookmarks_button.active = false;
                 if (info_button.active)
                     info_button.active = false;
-                ((ClickableListBoxRow) ((!) selected_row).get_child ()).show_right_click_popover ();
+                ClickableListBoxRow row = (ClickableListBoxRow) ((!) selected_row).get_child ();
+                row.show_right_click_popover (settings.get_boolean ("delayed-apply-menu"));
+                rows_possibly_with_popover.append (row);
             }
             else if (info_button.active == false)
             {
diff --git a/editor/key-list-box-row.vala b/editor/key-list-box-row.vala
index 9931349..72050a9 100644
--- a/editor/key-list-box-row.vala
+++ b/editor/key-list-box-row.vala
@@ -28,34 +28,26 @@ private abstract class ClickableListBoxRow : EventBox
     \*/
 
     private ContextPopover? nullable_popover = null;
-    protected virtual bool generate_popover (ContextPopover popover) { return false; }      // no popover 
should be created
+    protected virtual bool generate_popover (ContextPopover popover, bool delayed_apply_menu) { return 
false; }      // no popover should be created
 
-    protected void destroy_popover ()
+    public void destroy_popover ()
     {
         if (nullable_popover != null)       // check sometimes not useful
             ((!) nullable_popover).destroy ();
     }
 
-    public override bool button_press_event (Gdk.EventButton event)     // list_box_row selection is done 
elsewhere
-    {
-        if (event.button == Gdk.BUTTON_SECONDARY)
-            show_right_click_popover ((int) (event.x - this.get_allocated_width () / 2.0));
-
-        return false;
-    }
-
     public void hide_right_click_popover ()
     {
         if (nullable_popover != null)
             ((!) nullable_popover).hide ();
     }
 
-    public void show_right_click_popover (int event_x = 0)
+    public void show_right_click_popover (bool delayed_apply_menu, int event_x = 0)
     {
         if (nullable_popover == null)
         {
             nullable_popover = new ContextPopover ();
-            if (!generate_popover ((!) nullable_popover))
+            if (!generate_popover ((!) nullable_popover, delayed_apply_menu))
             {
                 ((!) nullable_popover).destroy ();  // TODO better, again
                 nullable_popover = null;
@@ -95,7 +87,7 @@ private class FolderListBoxRow : ClickableListBoxRow
         return full_name;
     }
 
-    protected override bool generate_popover (ContextPopover popover)
+    protected override bool generate_popover (ContextPopover popover, bool unused)  // TODO better
     {
         popover.new_action ("open", () => { on_row_clicked (); });
         popover.new_copy_action (get_text ());
@@ -112,6 +104,7 @@ private abstract class KeyListBoxRow : ClickableListBoxRow
     [GtkChild] protected Label key_info_label;
 
     public signal void set_key_value (Variant? new_value);
+    public signal void change_dismissed ();
 
     protected static string cool_text_value (Key key)   // TODO better
     {
@@ -155,7 +148,7 @@ private class KeyListBoxRowEditableNoSchema : KeyListBoxRow
         return key.is_ghost ? _("%s (key erased)").printf (key.full_name) : key.full_name + " " + 
key.value.print (false);
     }
 
-    protected override bool generate_popover (ContextPopover popover)
+    protected override bool generate_popover (ContextPopover popover, bool delayed_apply_menu)
     {
         if (key.is_ghost)
         {
@@ -169,13 +162,25 @@ private class KeyListBoxRowEditableNoSchema : KeyListBoxRow
         if (key.type_string == "b" || key.type_string == "mb")
         {
             popover.new_section ();
-            popover.create_buttons_list (key, false);
+            popover.create_buttons_list (key, true, delayed_apply_menu);
 
+            popover.change_dismissed.connect (() => {
+                    destroy_popover ();
+                    change_dismissed ();
+                });
             popover.value_changed.connect ((gvariant) => {
                     destroy_popover ();
                     set_key_value (gvariant);
                 });
         }
+        else if (delayed_apply_menu && key.planned_change)
+        {
+            popover.new_section ();
+            popover.new_action ("dismiss", () => {
+                    destroy_popover ();
+                    change_dismissed ();
+                });
+        }
         return true;
     }
 }
@@ -206,7 +211,7 @@ private class KeyListBoxRowEditable : KeyListBoxRow
         return key.descriptor + " " + key.value.print (false);
     }
 
-    protected override bool generate_popover (ContextPopover popover)
+    protected override bool generate_popover (ContextPopover popover, bool delayed_apply_menu)
     {
         popover.new_action ("customize", () => { on_row_clicked (); });
         popover.new_copy_action (get_text ());
@@ -214,14 +219,18 @@ private class KeyListBoxRowEditable : KeyListBoxRow
         if (key.type_string == "b" || key.type_string == "<enum>" || key.type_string == "mb")
         {
             popover.new_section ();
-            popover.create_buttons_list (key, true);
+            popover.create_buttons_list (key, true, delayed_apply_menu);
 
+            popover.change_dismissed.connect (() => {
+                    destroy_popover ();
+                    change_dismissed ();
+                });
             popover.value_changed.connect ((gvariant) => {
                     destroy_popover ();
                     set_key_value (gvariant);
                 });
         }
-        else if (key.type_string == "<flags>")
+        else if (!delayed_apply_menu && !key.planned_change && key.type_string == "<flags>")
         {
             popover.new_section ();
 
@@ -238,10 +247,24 @@ private class KeyListBoxRowEditable : KeyListBoxRow
                     set_key_value (gvariant);
                 });
         }
+        else if (key.planned_change)
+        {
+            popover.new_section ();
+            popover.new_action ("dismiss", () => {
+                    destroy_popover ();
+                    change_dismissed ();
+                });
+            if (key.planned_value != null)
+                popover.new_action ("default1", () => {
+                        destroy_popover ();
+                        set_key_value (null);
+                    });
+        }
         else if (!key.is_default)
         {
             popover.new_section ();
             popover.new_action ("default1", () => {
+                    destroy_popover ();
                     set_key_value (null);
                 });
         }
@@ -266,6 +289,7 @@ private class ContextPopover : Popover
 
     // public signals
     public signal void value_changed (Variant? gvariant);
+    public signal void change_dismissed ();
 
     public ContextPopover ()
     {
@@ -306,6 +330,9 @@ private class ContextPopover : Popover
             current_section.append (_("Set to default"), group_dot_action);
         else if (action_action == "default2")
             new_multi_default_action (group_dot_action);
+        else if (action_action == "dismiss")
+            /* Translators: "dismiss change" action in the right-click menu on a key with pending changes */
+            current_section.append (_("Dismiss change"), group_dot_action);
         else if (action_action == "open")
             /* Translators: "open folder" action in the right-click menu on a folder */
             current_section.append (_("Open"), group_dot_action);
@@ -382,7 +409,7 @@ private class ContextPopover : Popover
     * * Choices
     \*/
 
-    public void create_buttons_list (Key key, bool nullable)
+    public void create_buttons_list (Key key, bool has_default_value, bool delayed_apply_menu)
     {
         set_group ("enum");
         const string ACTION_NAME = "choice";
@@ -390,19 +417,34 @@ private class ContextPopover : Popover
 
         VariantType original_type = key.value.get_type ();
         VariantType nullable_type = new VariantType.maybe (original_type);
-        string nullable_type_string = nullable_type.dup_string ();
-        Variant variant = new Variant.maybe (original_type, key.has_schema && ((GSettingsKey) 
key).is_default ? null : key.value);
+        VariantType nullable_nullable_type = new VariantType.maybe (nullable_type);
+        string nullable_nullable_type_string = nullable_nullable_type.dup_string ();
 
-        current_group.add_action (new SimpleAction.stateful (ACTION_NAME, nullable_type, variant));
+        Variant value_variant = key.planned_change ? key.planned_value : key.value;
+        Variant variant = new Variant.maybe (original_type, key.has_schema && ((GSettingsKey) 
key).is_default ? null : value_variant);
+        Variant nullable_variant = new Variant.maybe (nullable_type, delayed_apply_menu && 
!key.planned_change ? null : variant);
+        current_group.add_action (new SimpleAction.stateful (ACTION_NAME, nullable_nullable_type, 
nullable_variant));
 
-        if (nullable)
-            new_multi_default_action (group_dot_action + "(@" + nullable_type_string + " nothing)");
+        if (has_default_value)
+        {
+            bool complete_menu = delayed_apply_menu || key.planned_change;
+
+            if (complete_menu)
+                /* Translators: "no change" option in the right-click menu on a key when on delayed mode */
+                current_section.append (_("No change"), group_dot_action + "(@" + 
nullable_nullable_type_string + " nothing)");
+
+            if (key.has_schema)
+                new_multi_default_action (group_dot_action + "(@" + nullable_nullable_type_string + " just 
nothing)");
+            else if (complete_menu)
+                /* Translators: "erase key" option in the right-click menu on a key without schema when on 
delayed mode */
+                current_section.append (_("Erase key"), group_dot_action + "(@" + 
nullable_nullable_type_string + " just nothing)");
+        }
 
         switch (key.type_string)
         {
             case "b":
-                current_section.append (Key.cool_boolean_text_value (true), group_dot_action + "(@mb true)");
-                current_section.append (Key.cool_boolean_text_value (false), group_dot_action + "(@mb 
false)");
+                current_section.append (Key.cool_boolean_text_value (true), group_dot_action + "(@mmb 
true)");
+                current_section.append (Key.cool_boolean_text_value (false), group_dot_action + "(@mmb 
false)");
                 break;
             case "<enum>":      // defined by the schema
                 Variant range = ((GSettingsKey) key).range_content;
@@ -410,17 +452,21 @@ private class ContextPopover : Popover
                 if (size == 0)      // TODO special case also 1?
                     assert_not_reached ();
                 for (uint index = 0; index < size; index++)
-                    current_section.append (range.get_child_value (index).print (false), group_dot_action + 
"(@ms '" + range.get_child_value (index).get_string () + "')");        // TODO use int settings.get_enum ()
+                    current_section.append (range.get_child_value (index).print (false), group_dot_action + 
"(@mms '" + range.get_child_value (index).get_string () + "')");        // TODO use int settings.get_enum ()
                 break;
             case "mb":
-                current_section.append (Key.cool_boolean_text_value (null), group_dot_action + "(@mmb just 
nothing)");
-                current_section.append (Key.cool_boolean_text_value (true), group_dot_action + "(@mmb 
true)");
-                current_section.append (Key.cool_boolean_text_value (false), group_dot_action + "(@mmb 
false)");
+                current_section.append (Key.cool_boolean_text_value (null), group_dot_action + "(@mmmb just 
just nothing)");
+                current_section.append (Key.cool_boolean_text_value (true), group_dot_action + "(@mmmb 
true)");
+                current_section.append (Key.cool_boolean_text_value (false), group_dot_action + "(@mmmb 
false)");
                 break;
         }
 
         ((GLib.ActionGroup) current_group).action_state_changed [ACTION_NAME].connect ((unknown_string, 
tmp_variant) => {
-                value_changed (tmp_variant.get_maybe ());
+                Variant? change_variant = tmp_variant.get_maybe ();
+                if (change_variant != null)
+                    value_changed (change_variant.get_maybe ());
+                else
+                    change_dismissed ();
             });
 
         finalize_menu ();
diff --git a/editor/modifications-revealer.vala b/editor/modifications-revealer.vala
index 192ec1a..9a187a2 100644
--- a/editor/modifications-revealer.vala
+++ b/editor/modifications-revealer.vala
@@ -27,12 +27,15 @@ class ModificationsRevealer : Revealer
     private HashTable<string, DConfKey>         dconf_keys_awaiting_hashtable = new HashTable<string, 
DConfKey>     (str_hash, str_equal);
     private HashTable<string, GSettingsKey> gsettings_keys_awaiting_hashtable = new HashTable<string, 
GSettingsKey> (str_hash, str_equal);
 
+    public signal void invalidate_popovers ();
+
     /*\
     * * Public calls
     \*/
 
     public void add_delayed_dconf_settings (DConfKey key, Variant? new_value)
     {
+        key.planned_change = true;
         key.planned_value = new_value;
         dconf_keys_awaiting_hashtable.insert (key.full_name, key);
 
@@ -41,12 +44,31 @@ class ModificationsRevealer : Revealer
 
     public void add_delayed_glib_settings (GSettingsKey key, Variant? new_value)
     {
+        key.planned_change = true;
         key.planned_value = new_value;
         gsettings_keys_awaiting_hashtable.insert (key.descriptor, key);
 
         update ();
     }
 
+    public void dismiss_dconf_change (DConfKey key)
+    {
+        key.planned_change = false;
+        key.planned_value = null;
+        dconf_keys_awaiting_hashtable.remove (key.full_name);
+
+        update ();
+    }
+
+    public void dismiss_glib_change (GSettingsKey key)
+    {
+        key.planned_change = false;
+        key.planned_value = null;
+        gsettings_keys_awaiting_hashtable.remove (key.descriptor);
+
+        update ();
+    }
+
     /*\
     * * Buttons callbacks
     \*/
@@ -55,11 +77,12 @@ class ModificationsRevealer : Revealer
     private void apply_delayed_settings ()
     {
         set_reveal_child (false);
+        invalidate_popovers ();
 
         /* GSettings stuff */
 
         HashTable<string, GLib.Settings> delayed_settings_hashtable = new HashTable<string, GLib.Settings> 
(str_hash, str_equal);
-        gsettings_keys_awaiting_hashtable.foreach_remove ((full_name, key) => {
+        gsettings_keys_awaiting_hashtable.foreach_remove ((descriptor, key) => {
                 GLib.Settings? settings = delayed_settings_hashtable.lookup (key.schema_id);
                 if (settings == null)
                 {
@@ -72,6 +95,7 @@ class ModificationsRevealer : Revealer
                     ((!) settings).reset (key.name);
                 else
                     ((!) settings).set_value (key.name, (!) key.planned_value);
+                key.planned_change = false;
 
                 return true;
             });
@@ -86,6 +110,7 @@ class ModificationsRevealer : Revealer
 
                 if (key.planned_value == null)
                     key.is_ghost = true;
+                key.planned_change = false;
 
                 return true;
             });
@@ -101,9 +126,21 @@ class ModificationsRevealer : Revealer
     private void dismiss_delayed_settings ()
     {
         set_reveal_child (false);
+        invalidate_popovers ();
 
-        gsettings_keys_awaiting_hashtable.remove_all ();
-        dconf_keys_awaiting_hashtable.remove_all ();
+        /* GSettings stuff */
+
+        gsettings_keys_awaiting_hashtable.foreach_remove ((descriptor, key) => {
+                key.planned_change = false;
+                return true;
+            });
+
+        /* DConf stuff */
+
+        dconf_keys_awaiting_hashtable.foreach_remove ((full_name, key) => {
+                key.planned_change = false;
+                return true;
+            });
     }
 
     /*\
@@ -111,8 +148,14 @@ class ModificationsRevealer : Revealer
     \*/
 
     private void update ()
-        requires (dconf_keys_awaiting_hashtable.length != 0 || gsettings_keys_awaiting_hashtable.length != 0)
     {
+        if (dconf_keys_awaiting_hashtable.length == 0 && gsettings_keys_awaiting_hashtable.length == 0)
+        {
+            set_reveal_child (false);
+            label.set_text ("");
+            return;
+        }
+
         if (dconf_keys_awaiting_hashtable.length == 0)
             label.set_text (_("%u gsettings operations awaiting.").printf 
(gsettings_keys_awaiting_hashtable.length));
         else if (gsettings_keys_awaiting_hashtable.length == 0)


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