Adopt EventEmitter class instead of injecting Signal methods

Introduce a new class, EventEmitter, which implements signal
handling for pure JavaScript classes. EventEmitter still
utilizes GJS' addSignalMethods internally.

EventEmitter allows static typechecking to understand the
structure of event-emitting JS classes and makes creating
child classes simpler.

The name 'EventEmitter' mirrors a common name for this pattern
in Node and in JS libraries.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2043>
This commit is contained in:
Evan Welsh
2022-07-04 18:30:44 -04:00
parent 9e30afe678
commit a88e59c1a8
39 changed files with 204 additions and 169 deletions

View File

@ -1,13 +1,16 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported HistoryManager */
const Signals = imports.signals;
const Signals = imports.misc.signals;
const Clutter = imports.gi.Clutter;
const Params = imports.misc.params;
var DEFAULT_LIMIT = 512;
var HistoryManager = class {
var HistoryManager = class extends Signals.EventEmitter {
constructor(params) {
super();
params = Params.parse(params, {
gsettingsKey: null,
limit: DEFAULT_LIMIT,
@ -109,4 +112,3 @@ var HistoryManager = class {
global.settings.set_strv(this._key, this._history);
}
};
Signals.addSignalMethods(HistoryManager.prototype);

View File

@ -2,7 +2,7 @@
/* exported getIBusManager */
const { Gio, GLib, IBus, Meta, Shell } = imports.gi;
const Signals = imports.signals;
const Signals = imports.misc.signals;
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
@ -40,8 +40,10 @@ function getIBusManager() {
return _ibusManager;
}
var IBusManager = class {
var IBusManager = class extends Signals.EventEmitter {
constructor() {
super();
IBus.init();
// This is the longest we'll keep the keyboard frozen until an input
@ -325,4 +327,3 @@ var IBusManager = class {
});
}
};
Signals.addSignalMethods(IBusManager.prototype);

View File

@ -2,7 +2,7 @@
/* exported canLock, getLoginManager, registerSessionWithGDM */
const { GLib, Gio } = imports.gi;
const Signals = imports.signals;
const Signals = imports.misc.signals;
const { loadInterfaceXML } = imports.misc.fileUtils;
@ -87,8 +87,10 @@ function getLoginManager() {
return _loginManager;
}
var LoginManagerSystemd = class {
var LoginManagerSystemd = class extends Signals.EventEmitter {
constructor() {
super();
this._proxy = new SystemdLoginManager(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1');
@ -202,9 +204,8 @@ var LoginManagerSystemd = class {
this.emit('prepare-for-sleep', aboutToSuspend);
}
};
Signals.addSignalMethods(LoginManagerSystemd.prototype);
var LoginManagerDummy = class {
var LoginManagerDummy = class extends Signals.EventEmitter {
getCurrentSessionProxy(_callback) {
// we could return a DummySession object that fakes whatever callers
// expect (at the time of writing: connect() and connectSignal()
@ -236,4 +237,3 @@ var LoginManagerDummy = class {
return null;
}
};
Signals.addSignalMethods(LoginManagerDummy.prototype);

View File

@ -1,8 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ObjectManager */
const { Gio, GLib } = imports.gi;
const Params = imports.misc.params;
const Signals = imports.signals;
const Signals = imports.misc.signals;
// Specified in the D-Bus specification here:
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
@ -25,8 +26,10 @@ const ObjectManagerIface = `
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
var ObjectManager = class {
var ObjectManager = class extends Signals.EventEmitter {
constructor(params) {
super();
params = Params.parse(params, {
connection: null,
name: null,
@ -288,4 +291,3 @@ var ObjectManager = class {
return proxies;
}
};
Signals.addSignalMethods(ObjectManager.prototype);

View File

@ -1,4 +1,4 @@
/* exported TransientSignalHolder, addObjectSignalMethods */
/* exported TransientSignalHolder, connectObject, disconnectObject */
const { GObject } = imports.gi;
const destroyableTypes = [];
@ -214,25 +214,6 @@ function disconnectObject(thisObj, obj) {
SignalManager.getDefault().getSignalTracker(thisObj).untrack(obj);
}
/**
* Add connectObject()/disconnectObject() methods
* to prototype. The prototype must have the connect()
* and disconnect() signal methods.
*
* @param {prototype} proto - a prototype
*/
function addObjectSignalMethods(proto) {
proto['connectObject'] = function (...args) {
connectObject(this, ...args);
};
proto['connect_object'] = proto['connectObject'];
proto['disconnectObject'] = function (obj) {
disconnectObject(this, obj);
};
proto['disconnect_object'] = proto['disconnectObject'];
}
/**
* Register a GObject type as having a 'destroy' signal
* that should disconnect all handlers

22
js/misc/signals.js Normal file
View File

@ -0,0 +1,22 @@
const Signals = imports.signals;
const SignalTracker = imports.misc.signalTracker;
var EventEmitter = class EventEmitter {
connectObject(...args) {
return SignalTracker.connectObject(this, ...args);
}
disconnectObject(...args) {
return SignalTracker.disconnectObject(this, ...args);
}
connect_object(...args) {
return this.connectObject(...args);
}
disconnect_object(...args) {
return this.disconnectObject(...args);
}
};
Signals.addSignalMethods(EventEmitter.prototype);

View File

@ -2,7 +2,7 @@
/* exported getSmartcardManager */
const Gio = imports.gi.Gio;
const Signals = imports.signals;
const Signals = imports.misc.signals;
const ObjectManager = imports.misc.objectManager;
@ -25,8 +25,10 @@ function getSmartcardManager() {
return _smartcardManager;
}
var SmartcardManager = class {
var SmartcardManager = class extends Signals.EventEmitter {
constructor() {
super();
this._objectManager = new ObjectManager.ObjectManager({
connection: Gio.DBus.session,
name: 'org.gnome.SettingsDaemon.Smartcard',
@ -114,4 +116,3 @@ var SmartcardManager = class {
return true;
}
};
Signals.addSignalMethods(SmartcardManager.prototype);

View File

@ -1,7 +1,8 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported WeatherClient */
const { Geoclue, Gio, GLib, GWeather, Shell } = imports.gi;
const Signals = imports.signals;
const Signals = imports.misc.signals;
const PermissionStore = imports.misc.permissionStore;
@ -20,8 +21,10 @@ const WEATHER_APP_ID = 'org.gnome.Weather.desktop';
// Minimum time between updates to show loading indication
var UPDATE_THRESHOLD = 10 * GLib.TIME_SPAN_MINUTE;
var WeatherClient = class {
var WeatherClient = class extends Signals.EventEmitter {
constructor() {
super();
this._loading = false;
this._locationValid = false;
this._lastUpdate = GLib.DateTime.new_from_unix_local(0);
@ -319,4 +322,3 @@ var WeatherClient = class {
this._updateAutoLocation();
}
};
Signals.addSignalMethods(WeatherClient.prototype);