"use strict"; /* exported init */ const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); const Main = imports.ui.main; const Panel = imports.ui.panel; const BoxOrderManager = Me.imports.extensionModules.BoxOrderManager; class Extension { constructor() { } enable() { this.settings = ExtensionUtils.getSettings(); this._boxOrderManager = new BoxOrderManager.BoxOrderManager(); /// Stuff to do on startup(extension enable). // Initially handle new top bar items and order top bar boxes. this.#handleNewItemsAndOrderTopBar(); // Overwrite `Panel._addToPanelBox` method with one handling new items // and also handle AppIndicators getting ready, to handle new items. this.#overwritePanelAddToPanelBox(); this._boxOrderManager.connect("appIndicatorReady", () => { this.#handleNewItemsAndOrderTopBar(); }); // Handle changes of configured box orders. this._settingsHandlerIds = []; const addConfiguredBoxOrderChangeHandler = (box) => { let handlerId = this.settings.connect(`changed::${box}-box-order`, () => { this.#handleNewItemsAndOrderTopBar(); }); this._settingsHandlerIds.push(handlerId); }; addConfiguredBoxOrderChangeHandler("left"); addConfiguredBoxOrderChangeHandler("center"); addConfiguredBoxOrderChangeHandler("right"); } disable() { // Revert the overwrite of `Panel._addToPanelBox`. Panel.Panel.prototype._addToPanelBox = Panel.Panel.prototype._originalAddToPanelBox; // Set `Panel._originalAddToPanelBox` to `undefined`. Panel._originalAddToPanelBox = undefined; // Disconnect signals. for (const handlerId of this._settingsHandlerIds) { this.settings.disconnect(handlerId); } this._boxOrderManager.disconnectSignals(); this.settings = null; this._boxOrderManager = null; } //////////////////////////////////////////////////////////////////////////// /// Methods used on extension enable. /// //////////////////////////////////////////////////////////////////////////// /** * Overwrite `Panel._addToPanelBox` with a custom method, which simply calls * the original one and handles new items and orders the top bar afterwards. */ #overwritePanelAddToPanelBox() { // Add the original `Panel._addToPanelBox` method as // `Panel._originalAddToPanelBox`. Panel.Panel.prototype._originalAddToPanelBox = Panel.Panel.prototype._addToPanelBox; const handleNewItemsAndOrderTopBar = () => { this.#handleNewItemsAndOrderTopBar(); }; // Overwrite `Panel._addToPanelBox`. Panel.Panel.prototype._addToPanelBox = function(role, indicator, position, box) { // Simply call the original `_addToPanelBox` and order the top bar // and handle new items afterwards. this._originalAddToPanelBox(role, indicator, position, box); handleNewItemsAndOrderTopBar(); }; } //////////////////////////////////////////////////////////////////////////// /// Helper methods holding logic needed by other methods. /// //////////////////////////////////////////////////////////////////////////// /** * This method orders the top bar items of the specified box according to * the configured box orders. * @param {string} box - The box to order. */ #orderTopBarItems(box) { // Get the valid box order. const validBoxOrder = this._boxOrderManager.createValidBoxOrder(box); // Get the relevant box of `Main.panel`. let panelBox; switch (box) { case "left": panelBox = Main.panel._leftBox; break; case "center": panelBox = Main.panel._centerBox; break; case "right": panelBox = Main.panel._rightBox; break; } /// Go through the items (or rather their roles) of the validBoxOrder /// and order the panelBox accordingly. for (let i = 0; i < validBoxOrder.length; i++) { const role = validBoxOrder[i]; // Get the indicator container associated with the current role. const associatedIndicatorContainer = Main.panel.statusArea[role].container; associatedIndicatorContainer.get_parent().remove_child(associatedIndicatorContainer); if (box === "right") { // If the target panel box is the right panel box, insert the // indicator container at index `-1`, which just adds it to the // end (correct order is ensured, since `validBoxOrder` is // sorted correctly and we're looping over it in order). // This way unaccounted-for indicator containers will be at the // left, which is preferred, since the box is logically // right-to-left. // The same applies for indicator containers, which are just // temporarily unaccounted for (like for indicator containers of // not yet ready app indicators), since them being at the right // for a probably temporary stay causes all the indicator // containers to shift. panelBox.insert_child_at_index(associatedIndicatorContainer, -1); } else { panelBox.insert_child_at_index(associatedIndicatorContainer, i); } } // To handle the case, where the box order got set to a permutation // of an outdated box order, it would be wise, if the caller updated the // box order now to include the items present in the top bar. } /** * This method handles all new items currently present in the top bar and * orders the items of all top bar boxes. */ #handleNewItemsAndOrderTopBar() { this._boxOrderManager.saveNewTopBarItems(); this.#orderTopBarItems("left"); this.#orderTopBarItems("center"); this.#orderTopBarItems("right"); // In `this.#orderTopBarItems` it says to update the box orders to // include potentially new items, since the ordering might have been // based on an outdated box order. However, since we already handle new // top bar items at the beginning of this method, this isn't a concern. } } function init() { return new Extension(); }