[gnome-shell] extensions-app: Add extension filtering



commit 9ef59b3b4fd3a12c22091cec7f8020c0d5792138
Author: Florian Müllner <fmuellner gnome org>
Date:   Wed Nov 11 02:37:20 2020 +0100

    extensions-app: Add extension filtering
    
    It's possible for the list of installed extension to get long
    enough to make searching more convenient than scrolling.
    
    Support that by implementing the standard search pattern[0]
    with a hidden search bar and a toggle in the headerbar.
    
    [0] https://developer.gnome.org/hig/stable/search.html.en
    
    https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3382

 .../extensions-app/data/ui/extensions-window.ui    | 31 ++++++++++++++
 subprojects/extensions-app/js/main.js              | 48 ++++++++++++++++++++++
 2 files changed, 79 insertions(+)
---
diff --git a/subprojects/extensions-app/data/ui/extensions-window.ui 
b/subprojects/extensions-app/data/ui/extensions-window.ui
index dd36924484..1ae5bfc991 100644
--- a/subprojects/extensions-app/data/ui/extensions-window.ui
+++ b/subprojects/extensions-app/data/ui/extensions-window.ui
@@ -89,6 +89,22 @@
             <property name="pack_type">end</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkToggleButton" id="searchButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="icon_name">edit-find-symbolic</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
         <child>
           <object class="GtkSwitch">
             <property name="visible">True</property>
@@ -105,6 +121,21 @@
       <object class="GtkBox">
         <property name="visible">True</property>
         <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkSearchBar" id="searchBar">
+            <property name="visible">True</property>
+            <property name="search-mode-enabled"
+                      bind-source="searchButton"
+                      bind-property="active"
+                      bind-flags="bidirectional"/>
+            <child>
+              <object class="GtkSearchEntry" id="searchEntry">
+                <property name="visible">True</property>
+                <property name="max-width-chars">35</property>
+              </object>
+            </child>
+          </object>
+        </child>
         <child>
           <object class="GtkStack" id="mainStack">
             <property name="visible">True</property>
diff --git a/subprojects/extensions-app/js/main.js b/subprojects/extensions-app/js/main.js
index 2e8b01804b..31a717df7f 100644
--- a/subprojects/extensions-app/js/main.js
+++ b/subprojects/extensions-app/js/main.js
@@ -89,6 +89,9 @@ var ExtensionsWindow = GObject.registerClass({
         'mainBox',
         'mainStack',
         'scrolledWindow',
+        'searchBar',
+        'searchButton',
+        'searchEntry',
         'updatesBar',
         'updatesLabel',
     ],
@@ -122,11 +125,45 @@ var ExtensionsWindow = GObject.registerClass({
         });
         this.add_action(action);
 
+        const accelGroup = new Gtk.AccelGroup();
+        this._searchButton.add_accelerator('clicked',
+            accelGroup, Gdk.KEY_f, Gdk.ModifierType.CONTROL_MASK, 0);
+        this._searchButton.add_accelerator('clicked',
+            accelGroup, Gdk.KEY_s, Gdk.ModifierType.CONTROL_MASK, 0);
+        this.add_accel_group(accelGroup);
+
+        this.connect('key-press-event',
+            (w, event) => this._searchBar.handle_event(event));
+
+        this._searchTerms = [];
+        this._searchEntry.connect('search-changed', () => {
+            const { text } = this._searchEntry;
+            if (text === '')
+                this._searchTerms = [];
+            else
+                [this._searchTerms] = GLib.str_tokenize_and_fold(text, null);
+
+            this._userList.invalidate_filter();
+            this._systemList.invalidate_filter();
+        });
+
         this._userList.set_sort_func(this._sortList.bind(this));
         this._userList.set_header_func(this._updateHeader.bind(this));
+        this._userList.set_filter_func(this._filterList.bind(this));
+        this._userList.set_placeholder(new Gtk.Label({
+            label: _('No Matches'),
+            margin: 12,
+            visible: true,
+        }));
 
         this._systemList.set_sort_func(this._sortList.bind(this));
         this._systemList.set_header_func(this._updateHeader.bind(this));
+        this._systemList.set_filter_func(this._filterList.bind(this));
+        this._systemList.set_placeholder(new Gtk.Label({
+            label: _('No Matches'),
+            margin: 12,
+            visible: true,
+        }));
 
         this._shellProxy.connectSignal('ExtensionStateChanged',
             this._onExtensionStateChanged.bind(this));
@@ -215,6 +252,11 @@ var ExtensionsWindow = GObject.registerClass({
         return row1.name.localeCompare(row2.name);
     }
 
+    _filterList(row) {
+        return this._searchTerms.every(
+            t => row.keywords.some(k => k.startsWith(t)));
+    }
+
     _updateHeader(row, before) {
         if (!before || row.get_header())
             return;
@@ -352,6 +394,8 @@ var ExtensionRow = GObject.registerClass({
         this._extension = extension;
         this._prefsModule = null;
 
+        [this._keywords] = GLib.str_tokenize_and_fold(this.name, null);
+
         this._actionGroup = new Gio.SimpleActionGroup();
         this.insert_action_group('row', this._actionGroup);
 
@@ -470,6 +514,10 @@ var ExtensionRow = GObject.registerClass({
             ? this._extension.error : _('The extension had an error');
     }
 
+    get keywords() {
+        return this._keywords;
+    }
+
     _updateState() {
         let state = this._extension.state === ExtensionState.ENABLED;
 


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