[meld/ui-next] dirdiff: Move custom filter actions to dynamically populated GActions



commit 8e3090802ad41531902da5ba37630f486f882be9
Author: Kai Willadsen <kai willadsen gmail com>
Date:   Sat Feb 23 08:09:09 2019 +1000

    dirdiff: Move custom filter actions to dynamically populated GActions
    
    This really feels like bending `GAction` to do a thing that it wasn't
    really intended to do, but I can't think of any other way to make the
    appropriate UI happen. It's unpleasant here that we have dynamic action
    creation as a window-level concern, but the dynamic action creation
    occurs in `DirDiff` itself. I mean... this is correct, but it doesn't
    feel great.

 data/ui/dirdiff-ui.xml       |  9 -----
 data/ui/meldapp-ui.xml       |  3 --
 meld/const.py                |  2 ++
 meld/dirdiff.py              | 85 +++++++++-----------------------------------
 meld/meldwindow.py           | 25 ++++++++++++-
 meld/menuhelpers.py          | 23 ++++++++++++
 meld/resources/gtk/menus.ui  |  3 ++
 meld/resources/ui/dirdiff.ui |  8 -----
 8 files changed, 69 insertions(+), 89 deletions(-)
---
diff --git a/data/ui/dirdiff-ui.xml b/data/ui/dirdiff-ui.xml
index c5a7e1e7..f1efab76 100644
--- a/data/ui/dirdiff-ui.xml
+++ b/data/ui/dirdiff-ui.xml
@@ -1,16 +1,7 @@
 <ui>
-  <toolbar name="Toolbar">
-    <placeholder name="FilterActions">
-      <toolitem action="CustomFilterMenu"/>
-    </placeholder>
-  </toolbar>
-
   <popup name="Popup">
     <menuitem action="DirCollapseRecursively" />
     <menuitem action="DirExpandRecursively" />
   </popup>
-
-  <popup name="CustomPopup">
-  </popup>
 </ui>
 
diff --git a/data/ui/meldapp-ui.xml b/data/ui/meldapp-ui.xml
index 9f0299b0..c2cabadc 100644
--- a/data/ui/meldapp-ui.xml
+++ b/data/ui/meldapp-ui.xml
@@ -17,13 +17,10 @@
     </menu>
     <menu action="ViewMenu">
       <placeholder name="ViewPlaceholder" />
-      <separator/>
-      <menu action="FileFilters" />
     </menu>
   </menubar>
 
   <toolbar action="Toolbar">
-    <placeholder name="FilterActions" />
   </toolbar>
 
   <popup name="Popup">
diff --git a/meld/const.py b/meld/const.py
index 419319cc..762024a7 100644
--- a/meld/const.py
+++ b/meld/const.py
@@ -26,3 +26,5 @@ NEWLINES = {
     GtkSource.NewlineType.CR_LF: ('\r\n', _("DOS/Windows (CR-LF)")),
     GtkSource.NewlineType.CR: ('\r', _("Mac OS (CR)")),
 }
+
+FILE_FILTER_ACTION_FORMAT = 'folder-custom-filter-{}'
diff --git a/meld/dirdiff.py b/meld/dirdiff.py
index eea08309..38b8323f 100644
--- a/meld/dirdiff.py
+++ b/meld/dirdiff.py
@@ -36,6 +36,7 @@ from gi.repository import Gtk
 from meld import misc
 from meld import tree
 from meld.conf import _, ui_file
+from meld.const import FILE_FILTER_ACTION_FORMAT
 from meld.iohelpers import trash_or_confirm
 from meld.melddoc import MeldDoc
 from meld.misc import all_same, apply_text_filters, with_focused_pane
@@ -593,49 +594,8 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc):
                 last_column = current_column
             treeview.set_headers_visible(extra_cols)
 
-    @Template.Callback()
-    def on_custom_filter_menu_toggled(self, item):
-        if item.get_active():
-            self.custom_popup.connect(
-                "deactivate", lambda popup: item.set_active(False))
-            self.custom_popup.popup_at_widget(
-                self.filter_menu_button,
-                Gdk.Gravity.SOUTH_WEST,
-                Gdk.Gravity.NORTH_WEST,
-                None,
-            )
-
-    def _cleanup_filter_menu_button(self, ui):
-        if self.custom_merge_id:
-            ui.remove_ui(self.custom_merge_id)
-        if self.filter_actiongroup in ui.get_action_groups():
-            ui.remove_action_group(self.filter_actiongroup)
-
-    def _create_filter_menu_button(self, ui):
-        ui.insert_action_group(self.filter_actiongroup, -1)
-        self.custom_merge_id = ui.new_merge_id()
-        for x in self.filter_ui:
-            ui.add_ui(self.custom_merge_id, *x)
-        self.custom_popup = ui.get_widget("/CustomPopup")
-        self.filter_menu_button = ui.get_widget(
-            "/Toolbar/FilterActions/CustomFilterMenu")
-        label = misc.make_tool_button_widget(
-            self.filter_menu_button.props.label)
-        self.filter_menu_button.set_label_widget(label)
-
-    def on_container_switch_in_event(self, ui, window):
-        MeldDoc.on_container_switch_in_event(self, ui, window)
-        self._create_filter_menu_button(ui)
-        self.ui_manager = ui
-
-    def on_container_switch_out_event(self, ui, window):
-        self._cleanup_filter_menu_button(ui)
-        MeldDoc.on_container_switch_out_event(self, ui, window)
-
     def on_file_filters_changed(self, app):
-        self._cleanup_filter_menu_button(self.ui_manager)
         relevant_change = self.create_name_filters()
-        self._create_filter_menu_button(self.ui_manager)
         if relevant_change:
             self.refresh()
 
@@ -647,32 +607,20 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc):
                           if f.active])
         active_filters_changed = old_active != new_active
 
+        # TODO: Rework name_filters to use a map-like structure so that we
+        # don't need _action_name_filter_map.
+        self._action_name_filter_map = {}
         self.name_filters = [copy.copy(f) for f in meldsettings.file_filters]
-        actions = []
-        disabled_actions = []
-        self.filter_ui = []
-        for i, f in enumerate(self.name_filters):
-            name = "Hide%d" % i
-            callback = functools.partial(self._update_name_filter, idx=i)
-            actions.append((
-                name, None, f.label, None, _("Hide %s") % f.label,
-                callback, f.active
-            ))
-            self.filter_ui.append([
-                "/CustomPopup", name, name,
-                Gtk.UIManagerItemType.MENUITEM, False
-            ])
-            self.filter_ui.append([
-                "/Menubar/ViewMenu/FileFilters", name, name,
-                Gtk.UIManagerItemType.MENUITEM, False
-            ])
-            if f.filter is None:
-                disabled_actions.append(name)
-
-        self.filter_actiongroup = Gtk.ActionGroup(name="DirdiffFilterActions")
-        self.filter_actiongroup.add_toggle_actions(actions)
-        for name in disabled_actions:
-            self.filter_actiongroup.get_action(name).set_sensitive(False)
+        for i, filt in enumerate(self.name_filters):
+            action = Gio.SimpleAction.new_stateful(
+                name=FILE_FILTER_ACTION_FORMAT.format(i),
+                parameter_type=None,
+                state=GLib.Variant.new_boolean(filt.active),
+            )
+            action.connect('change-state', self._update_name_filter)
+            action.set_enabled(filt.filter is not None)
+            self.view_action_group.add_action(action)
+            self._action_name_filter_map[action] = filt
 
         return active_filters_changed
 
@@ -1403,8 +1351,9 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc):
         self.props.status_filters = state_strs
         self.refresh()
 
-    def _update_name_filter(self, button, idx):
-        self.name_filters[idx].active = button.get_active()
+    def _update_name_filter(self, action, state):
+        self._action_name_filter_map[action].active = state.get_boolean()
+        action.set_state(state)
         self.refresh()
 
         #
diff --git a/meld/meldwindow.py b/meld/meldwindow.py
index 8e3f1e7c..fcd4c1fd 100644
--- a/meld/meldwindow.py
+++ b/meld/meldwindow.py
@@ -36,6 +36,12 @@ from meld.ui.notebooklabel import NotebookLabel
 from meld.vcview import VcView
 from meld.windowstate import SavedWindowState
 
+
+from meld.const import FILE_FILTER_ACTION_FORMAT
+from meld.settings import meldsettings
+from meld.menuhelpers import replace_menu_section
+
+
 log = logging.getLogger(__name__)
 
 
@@ -86,7 +92,6 @@ class MeldWindow(Gtk.ApplicationWindow):
             ("ViewMenu", None, _("_View")),
             ("FileStatus", None, _("File Status")),
             ("VcStatus", None, _("Version Status")),
-            ("FileFilters", None, _("File Filters")),
         )
         self.actiongroup = Gtk.ActionGroup(name='MainActions')
         self.actiongroup.set_translation_domain("meld")
@@ -179,8 +184,26 @@ class MeldWindow(Gtk.ApplicationWindow):
         self.vc_filter_button.set_popover(
             Gtk.Popover.new_from_model(self.vc_filter_button, vc_filter_model))
 
+        self.update_filename_filters()
+        self.settings_handlers = [
+            meldsettings.connect(
+                "file-filters-changed", self.update_filename_filters),
+        ]
+
         meld.ui.util.extract_accels_from_menu(menu, self.get_application())
 
+    def update_filename_filters(self, *args):
+        filter_items_model = Gio.Menu()
+        for i, filt in enumerate(meldsettings.file_filters):
+            name = FILE_FILTER_ACTION_FORMAT.format(i)
+            filter_items_model.append(
+                label=filt.label, detailed_action=f'view.{name}')
+        section = Gio.MenuItem.new_section(_("Filename"), filter_items_model)
+        section.set_attribute([("id", "s", "custom-filter-section")])
+        app = self.get_application()
+        filter_model = app.get_menu_by_id("folder-status-filter-menu")
+        replace_menu_section(filter_model, section)
+
     def on_widget_drag_data_received(
             self, wid, context, x, y, selection_data, info, time):
         uris = selection_data.get_uris()
diff --git a/meld/menuhelpers.py b/meld/menuhelpers.py
new file mode 100644
index 00000000..c7835971
--- /dev/null
+++ b/meld/menuhelpers.py
@@ -0,0 +1,23 @@
+
+from gi.repository import Gio
+
+
+def replace_menu_section(menu: Gio.Menu, section: Gio.MenuItem):
+    """Replaces an existing section in GMenu `menu` with `section`
+
+    The sections are compared by their `id` attributes, with the
+    matching section in `menu` being replaced by the passed `section`.
+
+    If there is no section in `menu` that matches `section`'s `id`
+    attribute, a ValueError is raised.
+    """
+    section_id = section.get_attribute_value("id").get_string()
+    for idx in range(menu.get_n_items()):
+        item_id = menu.get_item_attribute_value(idx, "id").get_string()
+        if item_id == section_id:
+            break
+    else:
+        # FIXME: Better exception
+        raise ValueError("Section %s not found" % section_id)
+    menu.remove(idx)
+    menu.insert_item(idx, section)
diff --git a/meld/resources/gtk/menus.ui b/meld/resources/gtk/menus.ui
index ee0a239e..e5a2635c 100644
--- a/meld/resources/gtk/menus.ui
+++ b/meld/resources/gtk/menus.ui
@@ -143,6 +143,9 @@
         <attribute name="action">view.folder-status-modified</attribute>
       </item>
     </section>
+    <section>
+      <attribute name="id">custom-filter-section</attribute>
+    </section>
     <section>
       <attribute name="id">options-section</attribute>
       <item>
diff --git a/meld/resources/ui/dirdiff.ui b/meld/resources/ui/dirdiff.ui
index 6dff7aa1..e575ce38 100644
--- a/meld/resources/ui/dirdiff.ui
+++ b/meld/resources/ui/dirdiff.ui
@@ -21,14 +21,6 @@
         <signal name="activate" handler="on_expand_recursive_clicked" swapped="no"/>
       </object>
     </child>
-    <child>
-      <object class="GtkToggleAction" id="CustomFilterMenu">
-        <property name="label" translatable="yes">Filters</property>
-        <property name="tooltip" translatable="yes">Set active filters</property>
-        <property name="is_important">True</property>
-        <signal name="toggled" handler="on_custom_filter_menu_toggled" swapped="no"/>
-      </object>
-    </child>
   </object>
   <template class="DirDiff" parent="GtkVBox">
     <property name="visible">True</property>


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