diff --git a/src/extension.js b/src/extension.js index a859cd0..523311a 100644 --- a/src/extension.js +++ b/src/extension.js @@ -43,6 +43,15 @@ class Extension { addConfiguredBoxOrderChangeHandler("left"); addConfiguredBoxOrderChangeHandler("center"); addConfiguredBoxOrderChangeHandler("right"); + + // Handle AppIndicators getting ready. + this._boxOrderManager.connect("appIndicatorReady", () => { + this._boxOrderManager.saveNewTopBarItems(); + this.#orderTopBarItems("left"); + this.#orderTopBarItems("center"); + this.#orderTopBarItems("right"); + }); + } disable() { @@ -55,8 +64,10 @@ class Extension { for (const handlerId of this._settingsHandlerIds) { this.settings.disconnect(handlerId); } + this._boxOrderManager.disconnectSignals(); this.settings = null; + this._boxOrderManager = null; } //////////////////////////////////////////////////////////////////////////// @@ -88,12 +99,6 @@ class Extension { // Overwrite `Panel._addToPanelBox`. Panel.Panel.prototype._addToPanelBox = function (role, indicator, position, box) { - // Handle the case where the new item is a - // AppIndicator/KStatusNotifierItem. - if (role.startsWith("appindicator-")) { - // Just throw an error for now. - throw new Error("AppIndicator/KStatusNotifierItem addition is currently broken/not implemented."); - } // Simply call the original `_addToPanelBox` and order the top bar // and handle new items afterwards. this._originalAddToPanelBox(role, indicator, position, box); diff --git a/src/extensionModules/BoxOrderManager.js b/src/extensionModules/BoxOrderManager.js index 36bc2d2..b178e00 100644 --- a/src/extensionModules/BoxOrderManager.js +++ b/src/extensionModules/BoxOrderManager.js @@ -1,6 +1,8 @@ "use strict"; /* exported BoxOrderManager */ +const GObject = imports.gi.GObject; + const ExtensionUtils = imports.misc.extensionUtils; const Main = imports.ui.main; @@ -20,8 +22,15 @@ const Main = imports.ui.main; * what is really useable by the other extension code. * It's basically a heavy wrapper around the box orders stored in the settings. */ -var BoxOrderManager = class BoxOrderManager { - constructor() { +var BoxOrderManager = GObject.registerClass({ + Signals: { + "appIndicatorReady": {} + } +}, class BoxOrderManager extends GObject.Object { + constructor(params = {}) { + super(params); + + this._appIndicatorReadyHandlerIdMap = new Map(); this._appIndicatorItemApplicationRoleMap = new Map(); this._settings = ExtensionUtils.getSettings(); @@ -31,6 +40,10 @@ var BoxOrderManager = class BoxOrderManager { * Handles an AppIndicator/KStatusNotifierItem item by associating the role * of the given item with the application of the * AppIndicator/KStatusNotifier item and returning a placeholder role. + * In the case, where the application can't be determined, this method + * throws an error. However it also makes sure that once the app indicators + * "ready" signal emits, this classes "appIndicatorReady" signal emits as + * well. * @param {string} indicatorContainer - The container of the indicator of the * AppIndicator/KStatusNotifierItem item. * @param {string} role - The role of the AppIndicator/KStatusNotifierItem @@ -38,7 +51,18 @@ var BoxOrderManager = class BoxOrderManager { * @returns {string} The placeholder role. */ #handleAppIndicatorItem(indicatorContainer, role) { - let application = indicatorContainer.get_child()._indicator.id; + const appIndicator = indicatorContainer.get_child()._indicator; + let application = appIndicator.id; + + if (!application && this._appIndicatorReadyHandlerIdMap) { + const handlerId = appIndicator.connect("ready", () => { + this.emit("appIndicatorReady"); + appIndicator.disconnect(handlerId); + this._appIndicatorReadyHandlerIdMap.delete(handlerId); + }); + this._appIndicatorReadyHandlerIdMap.set(handlerId, appIndicator); + throw new Error("Application can't be determined."); + } // Since the Dropbox client appends its PID to the id, drop the PID and // the hyphen before it. @@ -101,6 +125,20 @@ var BoxOrderManager = class BoxOrderManager { return resolvedBoxOrder; } + /** + * Disconnects all signals (and disables future signal connection). + * This is typically used before nulling an instance of this class to make + * sure all signals are disconnected. + */ + disconnectSignals() { + for (const [handlerId, appIndicator] of this._appIndicatorReadyHandlerIdMap) { + if (handlerId && appIndicator) { + appIndicator.disconnect(handlerId); + } + } + this._appIndicatorReadyHandlerIdMap = null; + } + /** * This method returns a valid box order for the given top bar box. * This means it returns a box order, where only roles are included, which @@ -184,7 +222,14 @@ var BoxOrderManager = class BoxOrderManager { // Handle an AppIndicator/KStatusNotifierItem item differently. if (role.startsWith("appindicator-")) { - role = this.#handleAppIndicatorItem(indicatorContainer, role); + try { + role = this.#handleAppIndicatorItem(indicatorContainer, role); + } catch (e) { + if (e.message !== "Application can't be determined.") { + throw(e); + } + continue; + } } // Add the role to the box order, if it isn't in in one already. @@ -220,4 +265,4 @@ var BoxOrderManager = class BoxOrderManager { saveBoxOrderToSettings(boxOrders.center, "center"); saveBoxOrderToSettings(boxOrders.right, "right"); } -}; +});