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
This commit is contained in:
Florian Müllner 2020-11-11 02:37:20 +01:00 committed by Georges Basile Stavracas Neto
parent 8eec7ae2d8
commit 9ef59b3b4f
2 changed files with 79 additions and 0 deletions

View File

@ -89,6 +89,22 @@
<property name="pack_type">end</property> <property name="pack_type">end</property>
</packing> </packing>
</child> </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> <child>
<object class="GtkSwitch"> <object class="GtkSwitch">
<property name="visible">True</property> <property name="visible">True</property>
@ -105,6 +121,21 @@
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</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> <child>
<object class="GtkStack" id="mainStack"> <object class="GtkStack" id="mainStack">
<property name="visible">True</property> <property name="visible">True</property>

View File

@ -89,6 +89,9 @@ var ExtensionsWindow = GObject.registerClass({
'mainBox', 'mainBox',
'mainStack', 'mainStack',
'scrolledWindow', 'scrolledWindow',
'searchBar',
'searchButton',
'searchEntry',
'updatesBar', 'updatesBar',
'updatesLabel', 'updatesLabel',
], ],
@ -122,11 +125,45 @@ var ExtensionsWindow = GObject.registerClass({
}); });
this.add_action(action); 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_sort_func(this._sortList.bind(this));
this._userList.set_header_func(this._updateHeader.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_sort_func(this._sortList.bind(this));
this._systemList.set_header_func(this._updateHeader.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._shellProxy.connectSignal('ExtensionStateChanged',
this._onExtensionStateChanged.bind(this)); this._onExtensionStateChanged.bind(this));
@ -215,6 +252,11 @@ var ExtensionsWindow = GObject.registerClass({
return row1.name.localeCompare(row2.name); return row1.name.localeCompare(row2.name);
} }
_filterList(row) {
return this._searchTerms.every(
t => row.keywords.some(k => k.startsWith(t)));
}
_updateHeader(row, before) { _updateHeader(row, before) {
if (!before || row.get_header()) if (!before || row.get_header())
return; return;
@ -352,6 +394,8 @@ var ExtensionRow = GObject.registerClass({
this._extension = extension; this._extension = extension;
this._prefsModule = null; this._prefsModule = null;
[this._keywords] = GLib.str_tokenize_and_fold(this.name, null);
this._actionGroup = new Gio.SimpleActionGroup(); this._actionGroup = new Gio.SimpleActionGroup();
this.insert_action_group('row', this._actionGroup); this.insert_action_group('row', this._actionGroup);
@ -470,6 +514,10 @@ var ExtensionRow = GObject.registerClass({
? this._extension.error : _('The extension had an error'); ? this._extension.error : _('The extension had an error');
} }
get keywords() {
return this._keywords;
}
_updateState() { _updateState() {
let state = this._extension.state === ExtensionState.ENABLED; let state = this._extension.state === ExtensionState.ENABLED;