diff --git a/subprojects/extensions-app/data/ui/extensions-window.ui b/subprojects/extensions-app/data/ui/extensions-window.ui
index 7047f130c..ba8470ce3 100644
--- a/subprojects/extensions-app/data/ui/extensions-window.ui
+++ b/subprojects/extensions-app/data/ui/extensions-window.ui
@@ -12,6 +12,43 @@
+
+
+ true
+ substring
+
+
+
+
+
+ sortModel
+
+
+
+
+
+
+
+
+
+ sortModel
+
+
+ true
+
+
+
+
+
+
800
500
@@ -139,6 +176,10 @@
User Extensions
+
none
@@ -155,6 +196,10 @@
System Extensions
+
none
diff --git a/subprojects/extensions-app/js/extensionManager.js b/subprojects/extensions-app/js/extensionManager.js
index 7671fb75e..0824c99c7 100644
--- a/subprojects/extensions-app/js/extensionManager.js
+++ b/subprojects/extensions-app/js/extensionManager.js
@@ -108,8 +108,6 @@ const Extension = GObject.registerClass({
const {name} = metadata;
if (this._name !== name) {
- [this._keywords] = GLib.str_tokenize_and_fold(name, null);
-
this._name = name;
this.notify('name');
}
@@ -207,10 +205,6 @@ const Extension = GObject.registerClass({
return this._version;
}
- get keywords() {
- return this._keywords;
- }
-
get error() {
if (!this.hasError)
return '';
@@ -268,6 +262,10 @@ export const ExtensionManager = GObject.registerClass({
'user-extensions-enabled', null, null,
GObject.ParamFlags.READWRITE,
true),
+ 'extensions': GObject.ParamSpec.object(
+ 'extensions', null, null,
+ GObject.ParamFlags.READABLE,
+ Gio.ListModel),
'n-updates': GObject.ParamSpec.int(
'n-updates', null, null,
GObject.ParamFlags.READABLE,
@@ -278,16 +276,13 @@ export const ExtensionManager = GObject.registerClass({
false),
},
Signals: {
- 'extension-added': {param_types: [Extension]},
- 'extension-removed': {param_types: [Extension]},
- 'extension-changed': {param_types: [Extension], flags: GObject.SignalFlags.DETAILED},
'extensions-loaded': {},
},
}, class ExtensionManager extends GObject.Object {
constructor() {
super();
- this._extensions = new Map();
+ this._extensions = new Gio.ListStore({itemType: Extension});
this._proxyReady = false;
this._shellProxy = new GnomeShellProxy(Gio.DBus.session,
@@ -312,6 +307,10 @@ export const ExtensionManager = GObject.registerClass({
this._loadExtensions().catch(console.error);
}
+ get extensions() {
+ return this._extensions;
+ }
+
get userExtensionsEnabled() {
return this._shellProxy.UserExtensionsEnabled ?? false;
}
@@ -322,7 +321,7 @@ export const ExtensionManager = GObject.registerClass({
get nUpdates() {
let nUpdates = 0;
- for (const ext of this._extensions.values()) {
+ for (const ext of this._extensions) {
if (ext.isUser && ext.hasUpdate)
nUpdates++;
}
@@ -355,43 +354,37 @@ export const ExtensionManager = GObject.registerClass({
this._shellProxy.CheckForUpdatesAsync().catch(console.error);
}
- _addExtension(extension) {
- const {uuid} = extension;
- if (this._extensions.has(uuid))
- return;
-
- this._extensions.set(uuid, extension);
- this.emit('extension-added', extension);
- }
-
- _removeExtension(extension) {
- const {uuid} = extension;
- if (this._extensions.delete(uuid))
- this.emit('extension-removed', extension);
- }
-
async _loadExtensions() {
const [extensionsMap] = await this._shellProxy.ListExtensionsAsync();
for (let uuid in extensionsMap) {
const extension = new Extension(extensionsMap[uuid]);
- this._addExtension(extension);
+ this._extensions.append(extension);
}
this.emit('extensions-loaded');
}
+ _findExtension(uuid) {
+ const len = this._extensions.get_n_items();
+ for (let pos = 0; pos < len; pos++) {
+ const extension = this._extensions.get_item(pos);
+ if (extension.uuid === uuid)
+ return [extension, pos];
+ }
+
+ return [null, -1];
+ }
+
_onExtensionStateChanged(p, sender, [uuid, newState]) {
- const extension = this._extensions.get(uuid);
+ const [extension, pos] = this._findExtension(uuid);
if (extension)
extension.update(newState);
if (!extension)
- this._addExtension(new Extension(newState));
+ this._extensions.append(new Extension(newState));
else if (extension.state === ExtensionState.UNINSTALLED)
- this._removeExtension(extension);
- else
- this.emit(`extension-changed::${uuid}`, extension);
+ this._extensions.remove(pos);
if (this._updatesCheckId)
return;
diff --git a/subprojects/extensions-app/js/extensionsWindow.js b/subprojects/extensions-app/js/extensionsWindow.js
index 4af3630ea..03e9d8be8 100644
--- a/subprojects/extensions-app/js/extensionsWindow.js
+++ b/subprojects/extensions-app/js/extensionsWindow.js
@@ -18,6 +18,10 @@ export const ExtensionsWindow = GObject.registerClass({
GTypeName: 'ExtensionsWindow',
Template: 'resource:///org/gnome/Extensions/ui/extensions-window.ui',
InternalChildren: [
+ 'sortModel',
+ 'searchFilter',
+ 'userListModel',
+ 'systemListModel',
'userGroup',
'userList',
'systemGroup',
@@ -54,20 +58,9 @@ export const ExtensionsWindow = GObject.registerClass({
},
}]);
- 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._searchEntry.connect('search-changed',
+ () => (this._searchFilter.search = this._searchEntry.text));
- this._userList.invalidate_filter();
- this._systemList.invalidate_filter();
- });
-
- this._userList.set_sort_func(this._sortList.bind(this));
- this._userList.set_filter_func(this._filterList.bind(this));
this._userList.set_placeholder(new Gtk.Label({
label: _('No Matches'),
margin_start: 12,
@@ -76,9 +69,8 @@ export const ExtensionsWindow = GObject.registerClass({
margin_bottom: 12,
}));
this._userList.connect('row-activated', (_list, row) => row.activate());
+ this._userGroup.connect('notify::visible', () => this._syncVisiblePage());
- this._systemList.set_sort_func(this._sortList.bind(this));
- this._systemList.set_filter_func(this._filterList.bind(this));
this._systemList.set_placeholder(new Gtk.Label({
label: _('No Matches'),
margin_start: 12,
@@ -87,6 +79,7 @@ export const ExtensionsWindow = GObject.registerClass({
margin_bottom: 12,
}));
this._systemList.connect('row-activated', (_list, row) => row.activate());
+ this._systemGroup.connect('notify::visible', () => this._syncVisiblePage());
const {extensionManager} = this.application;
extensionManager.connect('notify::failed',
@@ -97,19 +90,16 @@ export const ExtensionsWindow = GObject.registerClass({
this._onUserExtensionsEnabledChanged.bind(this));
this._onUserExtensionsEnabledChanged();
- extensionManager.connect('extension-added',
- (mgr, extension) => this._addExtensionRow(extension));
- extensionManager.connect('extension-removed',
- (mgr, extension) => this._removeExtensionRow(extension));
- extensionManager.connect('extension-changed',
- (mgr, extension) => {
- const row = this._findExtensionRow(extension);
- const isUser = row?.get_parent() === this._userList;
- if (extension.isUser !== isUser) {
- this._removeExtensionRow(extension);
- this._addExtensionRow(extension);
- }
- });
+ this._sortModel.model = extensionManager.extensions;
+
+ this._userList.bind_model(new Gtk.FilterListModel({
+ filter: this._searchFilter,
+ model: this._userListModel,
+ }), extension => new ExtensionRow(extension));
+ this._systemList.bind_model(new Gtk.FilterListModel({
+ filter: this._searchFilter,
+ model: this._systemListModel,
+ }), extension => new ExtensionRow(extension));
extensionManager.connect('extensions-loaded',
() => this._extensionsLoaded());
@@ -189,56 +179,12 @@ export const ExtensionsWindow = GObject.registerClass({
null);
}
- _sortList(row1, row2) {
- const {name: name1} = row1.extension;
- const {name: name2} = row2.extension;
- return name1.localeCompare(name2);
- }
-
- _filterList(row) {
- const {keywords} = row.extension;
- return this._searchTerms.every(
- t => keywords.some(k => k.startsWith(t)));
- }
-
- _findExtensionRow(extension) {
- return [
- ...this._userList,
- ...this._systemList,
- ].find(c => c.extension === extension);
- }
-
_onUserExtensionsEnabledChanged() {
const {userExtensionsEnabled} = this.application.extensionManager;
const action = this.lookup_action('user-extensions-enabled');
action.set_state(new GLib.Variant('b', userExtensionsEnabled));
}
- _addExtensionRow(extension) {
- const row = new ExtensionRow(extension);
-
- if (extension.isUser)
- this._userList.append(row);
- else
- this._systemList.append(row);
-
- this._syncListVisibility();
- }
-
- _removeExtensionRow(extension) {
- const row = this._findExtensionRow(extension);
- if (row)
- row.get_parent().remove(row);
- this._syncListVisibility();
- }
-
- _syncListVisibility() {
- this._userGroup.visible = [...this._userList].length > 1;
- this._systemGroup.visible = [...this._systemList].length > 1;
-
- this._syncVisiblePage();
- }
-
_syncVisiblePage() {
const {extensionManager} = this.application;
@@ -261,7 +207,7 @@ export const ExtensionsWindow = GObject.registerClass({
}
_extensionsLoaded() {
- this._syncListVisibility();
+ this._syncVisiblePage();
this._checkUpdates();
}
});