Refactor: Introduce BoxOrderManager

Introduce new `BoxOrderManager` to basically act as a heavy wrapper
around the box orders stored in settings, so that the other extension
code has easy methods to get, set and interact with box orders.
Refactor most of the code initially with it in mind.
Note that `#overwritePanelAddToPanelBox` needs more refactoring to
properly make use of that new idea (and it also needs refactoring in
general).
This commit is contained in:
Julian 2023-01-27 23:48:23 +01:00
parent ba373e9e49
commit d51703eb4e
Signed by: julian
GPG Key ID: 094C2AC34192FA11
3 changed files with 221 additions and 241 deletions

View File

@ -8,7 +8,7 @@ const Main = imports.ui.main;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
const AppIndicatorKStatusNotifierItemManager = Me.imports.extensionModules.AppIndicatorKStatusNotifierItemManager; const AppIndicatorKStatusNotifierItemManager = Me.imports.extensionModules.AppIndicatorKStatusNotifierItemManager;
const BoxOrderCreator = Me.imports.extensionModules.BoxOrderCreator; const BoxOrderManager = Me.imports.extensionModules.BoxOrderManager;
class Extension { class Extension {
constructor() { constructor() {
@ -21,12 +21,13 @@ class Extension {
// handle AppIndicator/KStatusNotifierItem items. // handle AppIndicator/KStatusNotifierItem items.
this._appIndicatorKStatusNotifierItemManager = new AppIndicatorKStatusNotifierItemManager.AppIndicatorKStatusNotifierItemManager(); this._appIndicatorKStatusNotifierItemManager = new AppIndicatorKStatusNotifierItemManager.AppIndicatorKStatusNotifierItemManager();
// Create an instance of BoxOrderCreator for the creation of special box this._boxOrderManager = new BoxOrderManager.BoxOrderManager(this._appIndicatorKStatusNotifierItemManager);
// orders.
this._boxOrderCreator = new BoxOrderCreator.BoxOrderCreator(this._appIndicatorKStatusNotifierItemManager);
this.#addNewItemsToBoxOrders(); // Stuff to do on startup(extension enable).
this.#orderTopBarItemsOfAllBoxes(); this._boxOrderManager.saveNewTopBarItems();
this.#orderTopBarItems("left");
this.#orderTopBarItems("center");
this.#orderTopBarItems("right");
this.#overwritePanelAddToPanelBox(); this.#overwritePanelAddToPanelBox();
// Handle changes of configured box orders. // Handle changes of configured box orders.
@ -36,27 +37,10 @@ class Extension {
let handlerId = this.settings.connect(`changed::${box}-box-order`, () => { let handlerId = this.settings.connect(`changed::${box}-box-order`, () => {
this.#orderTopBarItems(box); this.#orderTopBarItems(box);
/// For the case, where the currently saved box order is based // For the case, where the currently saved box order is based on
/// on a permutation of an outdated box order, get an updated // a permutation of an outdated box order, save new top bar
/// box order and save it, if needed. // items.
let updatedBoxOrder; this._boxOrderManager.saveNewTopBarItems();
switch (box) {
case "left":
updatedBoxOrder = this.#createUpdatedBoxOrders().left;
break;
case "center":
updatedBoxOrder = this.#createUpdatedBoxOrders().center;
break;
case "right":
updatedBoxOrder = this.#createUpdatedBoxOrders().right;
break;
}
// Only save the updated box order to settings, if it is
// different, to avoid looping.
const currentBoxOrder = this.settings.get_strv(`${box}-box-order`);
if (JSON.stringify(currentBoxOrder) !== JSON.stringify(updatedBoxOrder)) {
this.settings.set_strv(`${box}-box-order`, updatedBoxOrder);
}
}); });
this._settingsHandlerIds.push(handlerId); this._settingsHandlerIds.push(handlerId);
}; };
@ -84,27 +68,6 @@ class Extension {
/// Methods used on extension enable. /// /// Methods used on extension enable. ///
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
/**
* This method adds all new items currently present in the Gnome Shell top
* bar to the box orders.
*/
#addNewItemsToBoxOrders() {
const boxOrders = this.#createUpdatedBoxOrders();
this.settings.set_strv("left-box-order", boxOrders.left);
this.settings.set_strv("center-box-order", boxOrders.center);
this.settings.set_strv("right-box-order", boxOrders.right);
}
/**
* This methods orders the top bar items of all boxes according to the
* configred box orders using `this.#orderTopBarItems`.
*/
#orderTopBarItemsOfAllBoxes() {
this.#orderTopBarItems("left");
this.#orderTopBarItems("center");
this.#orderTopBarItems("right");
}
/** /**
* An object containing a position and box overwrite. * An object containing a position and box overwrite.
* @typedef PositionAndBoxOverwrite * @typedef PositionAndBoxOverwrite
@ -170,7 +133,7 @@ class Extension {
right: this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this.settings.get_strv("right-box-order")), right: this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this.settings.get_strv("right-box-order")),
}; };
// Also get the restricted valid box order of the target box. // Also get the restricted valid box order of the target box.
const restrictedValidBoxOrderOfTargetBox = this._boxOrderCreator.createRestrictedValidBoxOrder(box); const restrictedValidBoxOrderOfTargetBox = this._boxOrderManager.createRestrictedValidBoxOrder(box);
// Get the index of the role for each box order. // Get the index of the role for each box order.
const indices = { const indices = {
@ -239,21 +202,21 @@ class Extension {
if (indices.left !== -1) { if (indices.left !== -1) {
return { return {
position: determineInsertionIndex(indices.left, this._boxOrderCreator.createRestrictedValidBoxOrder("left"), resolvedBoxOrders.left), position: determineInsertionIndex(indices.left, this._boxOrderManager.createRestrictedValidBoxOrder("left"), resolvedBoxOrders.left),
box: "left" box: "left"
}; };
} }
if (indices.center !== -1) { if (indices.center !== -1) {
return { return {
position: determineInsertionIndex(indices.center, this._boxOrderCreator.createRestrictedValidBoxOrder("center"), resolvedBoxOrders.center), position: determineInsertionIndex(indices.center, this._boxOrderManager.createRestrictedValidBoxOrder("center"), resolvedBoxOrders.center),
box: "center" box: "center"
}; };
} }
if (indices.right !== -1) { if (indices.right !== -1) {
return { return {
position: determineInsertionIndex(indices.right, this._boxOrderCreator.createRestrictedValidBoxOrder("right"), resolvedBoxOrders.right), position: determineInsertionIndex(indices.right, this._boxOrderManager.createRestrictedValidBoxOrder("right"), resolvedBoxOrders.right),
box: "right" box: "right"
}; };
} }
@ -305,75 +268,6 @@ class Extension {
* @property {string[]} right - The box order for the right top bar box. * @property {string[]} right - The box order for the right top bar box.
*/ */
/**
* This method adds all new items currently present in the Gnome Shell top
* bar to the correct box order and returns the new box orders.
* @returns {BoxOrders} - The updated box orders.
*/
#createUpdatedBoxOrders() {
// Load the configured box orders from settings.
const boxOrders = {
left: this.settings.get_strv("left-box-order"),
center: this.settings.get_strv("center-box-order"),
right: this.settings.get_strv("right-box-order"),
};
// Get items (or rather their roles) currently present in the Gnome
// Shell top bar and index them using their associated indicator
// container.
let indicatorContainerRoleMap = new Map();
for (const role in Main.panel.statusArea) {
indicatorContainerRoleMap.set(Main.panel.statusArea[role].container, role);
}
// Get the indicator containers (of the items) currently present in the
// Gnome Shell top bar boxes.
const boxOrderIndicatorContainers = {
left: Main.panel._leftBox.get_children(),
center: Main.panel._centerBox.get_children(),
// Reverse this array, since the items in the left and center box
// are logically LTR, while the items in the right box are RTL.
right: Main.panel._rightBox.get_children().reverse()
};
// This function goes through the items (or rather their indicator
// containers) of the given box and adds new items (or rather their
// roles) to the box order.
const addNewItemsToBoxOrder = (boxIndicatorContainers, boxOrder, box) => {
for (const indicatorContainer of boxIndicatorContainers) {
// First get the role associated with the current indicator
// container.
const associatedRole = indicatorContainerRoleMap.get(indicatorContainer);
if (!associatedRole) continue;
// Handle an AppIndicator/KStatusNotifierItem item differently.
if (associatedRole.startsWith("appindicator-")) {
this._appIndicatorKStatusNotifierItemManager.handleAppIndicatorKStatusNotifierItemItem(indicatorContainer, associatedRole, boxOrder, boxOrders, box === "right");
continue;
}
// Add the role to the box order, if it isn't in in one already.
if (!boxOrders.left.includes(associatedRole)
&& !boxOrders.center.includes(associatedRole)
&& !boxOrders.right.includes(associatedRole)) {
if (box === "right") {
// Add the items to the beginning for this array, since
// its RTL.
boxOrder.unshift(associatedRole);
} else {
boxOrder.push(associatedRole);
}
}
}
};
addNewItemsToBoxOrder(boxOrderIndicatorContainers.left, boxOrders.left, "left");
addNewItemsToBoxOrder(boxOrderIndicatorContainers.center, boxOrders.center, "center");
addNewItemsToBoxOrder(boxOrderIndicatorContainers.right, boxOrders.right, "right");
return boxOrders;
}
/** /**
* This method orders the top bar items of the specified box according to * This method orders the top bar items of the specified box according to
* the configured box orders. * the configured box orders.
@ -381,7 +275,7 @@ class Extension {
*/ */
#orderTopBarItems(box) { #orderTopBarItems(box) {
// Get the valid box order. // Get the valid box order.
const validBoxOrder = this._boxOrderCreator.createValidBoxOrder(box); const validBoxOrder = this._boxOrderManager.createValidBoxOrder(box);
// Get the relevant box of `Main.panel`. // Get the relevant box of `Main.panel`.
let panelBox; let panelBox;

View File

@ -1,119 +0,0 @@
"use strict";
/* exported BoxOrderCreator */
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
/**
* A class exposing methods, which create special box orders.
*/
var BoxOrderCreator = class BoxOrderCreator {
/**
* @param {AppIndicatorKStatusNotifierItemManager}
* appIndicatorKStatusNotifierItemManager - An instance of
* AppIndicatorKStatusNotifierItemManager to be used in the methods of
* `this`.
*/
constructor(appIndicatorKStatusNotifierItemManager) {
this._appIndicatorKStatusNotifierItemManager = appIndicatorKStatusNotifierItemManager;
this._settings = ExtensionUtils.getSettings();
}
/**
* This function creates a valid box order for the given box.
* This means it returns a box order for the box, where only roles are
* included, which have their associated indicator container already in some
* box of the Gnome Shell top bar.
* @param {string} box - The box to return the valid box order for.
* Must be one of the following values:
* - "left"
* - "center"
* - "right"
* @returns {string[]} - The valid box order.
*/
createValidBoxOrder(box) {
// Get a resolved box order.
let boxOrder = this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this._settings.get_strv(`${box}-box-order`));
// Get the indicator containers (of the items) currently present in the
// Gnome Shell top bar.
const boxIndicatorContainers = [ ];
const addIndicatorContainersOfBox = (panelBox) => {
for (const indicatorContainer of panelBox.get_children()) {
boxIndicatorContainers.push(indicatorContainer);
}
};
addIndicatorContainersOfBox(Main.panel._leftBox);
addIndicatorContainersOfBox(Main.panel._centerBox);
addIndicatorContainersOfBox(Main.panel._rightBox);
// Create an indicator containers set from the indicator containers for
// fast easy access.
const boxIndicatorContainersSet = new Set(boxIndicatorContainers);
// Go through the box order and only add items to the valid box order,
// where their indicator is present in the Gnome Shell top bar
// currently.
let validBoxOrder = [ ];
for (const role of boxOrder) {
// Get the indicator container associated with the current role.
const associatedIndicatorContainer = Main.panel.statusArea[role]?.container;
if (boxIndicatorContainersSet.has(associatedIndicatorContainer)) validBoxOrder.push(role);
}
return validBoxOrder;
}
/**
* This function creates a restricted valid box order for the given box.
* This means it returns a box order for the box, where only roles are
* included, which have their associated indicator container already in the
* specified box.
* @param {string} box - The box to return the valid box order for.
* Must be one of the following values:
* - "left"
* - "center"
* - "right"
* @returns {string[]} - The restricted valid box order.
*/
createRestrictedValidBoxOrder(box) {
// Get a resolved box order and get the indicator containers (of the
// items) which are currently present in the Gnome Shell top bar in the
// specified box.
let boxOrder = this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this._settings.get_strv(`${box}-box-order`));
let boxIndicatorContainers;
switch (box) {
case "left":
boxIndicatorContainers = Main.panel._leftBox.get_children();
break;
case "center":
boxIndicatorContainers = Main.panel._centerBox.get_children();
break;
case "right":
boxIndicatorContainers = Main.panel._rightBox.get_children();
break;
}
// Create an indicator containers set from the indicator containers for
// fast easy access.
const boxIndicatorContainersSet = new Set(boxIndicatorContainers);
// Go through the box order and only add items to the restricted valid
// box order, where their indicator is present in the Gnome Shell top
// bar in the specified box currently.
let restrictedValidBoxOrder = [ ];
for (const role of boxOrder) {
// Get the indicator container associated with the current role.
const associatedIndicatorContainer = Main.panel.statusArea[role]?.container;
if (boxIndicatorContainersSet.has(associatedIndicatorContainer)) restrictedValidBoxOrder.push(role);
}
return restrictedValidBoxOrder;
}
};

View File

@ -0,0 +1,205 @@
"use strict";
/* exported BoxOrderManager */
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
/**
* An object containing a box order for the left, center and right top bar
* box.
* @typedef {Object} BoxOrders
* @property {string[]} left - The box order for the left top bar box.
* @property {string[]} center - The box order for the center top bar box.
* @property {string[]} right - The box order for the right top bar box.
*/
/**
* This class provides methods get, set and interact with box orders, while
* taking over the work of translating between what is stored in settings and
* 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 {
/**
* @param {AppIndicatorKStatusNotifierItemManager}
* appIndicatorKStatusNotifierItemManager - An instance of
* AppIndicatorKStatusNotifierItemManager to be used in the methods of
* `this`.
*/
constructor(appIndicatorKStatusNotifierItemManager) {
this._appIndicatorKStatusNotifierItemManager = appIndicatorKStatusNotifierItemManager;
this._settings = ExtensionUtils.getSettings();
}
/**
* 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
* have their associated indicator container already in some box of the
* Gnome Shell top bar.
* @param {string} box - The top bar box to return the valid box order for.
* Must be one of the following values:
* - "left"
* - "center"
* - "right"
* @returns {string[]} - The valid box order.
*/
createValidBoxOrder(box) {
// Get a resolved box order.
let boxOrder = this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this._settings.get_strv(`${box}-box-order`));
// ToDo: simplify.
// Get the indicator containers (of the items) currently present in the
// Gnome Shell top bar.
const indicatorContainers = [
Main.panel._leftBox.get_children(),
Main.panel._centerBox.get_children(),
Main.panel._rightBox.get_children()
].flat();
// Create an indicator containers set from the indicator containers for
// fast easy access.
const indicatorContainerSet = new Set(indicatorContainers);
// Go through the box order and only add items to the valid box order,
// where their indicator is present in the Gnome Shell top bar
// currently.
let validBoxOrder = [ ];
for (const role of boxOrder) {
// Get the indicator container associated with the current role.
const associatedIndicatorContainer = Main.panel.statusArea[role]?.container;
if (indicatorContainerSet.has(associatedIndicatorContainer)) validBoxOrder.push(role);
}
return validBoxOrder;
}
/**
* This method returns a restricted valid box order for the given top bar
* box.
* This means it returns a box order, where only roles are included, which
* have their associated indicator container already in the specified box.
* @param {string} box - The box to return the valid box order for.
* Must be one of the following values:
* - "left"
* - "center"
* - "right"
* @returns {string[]} - The restricted valid box order.
*/
createRestrictedValidBoxOrder(box) {
// Get a resolved box order.
let boxOrder = this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this._settings.get_strv(`${box}-box-order`));
// Get the indicator containers (of the items), which are currently
// present the in specified Gnome Shell top bar box.
let indicatorContainers;
switch (box) {
case "left":
indicatorContainers = Main.panel._leftBox.get_children();
break;
case "center":
indicatorContainers = Main.panel._centerBox.get_children();
break;
case "right":
indicatorContainers = Main.panel._rightBox.get_children();
break;
}
// Create an indicator container set from the indicator containers for
// fast easy access.
const indicatorContainerSet = new Set(indicatorContainers);
// Go through the box order and only add items to the restricted valid
// box order, where their indicator is present in the specified Gnome
// Shell top bar box.
let restrictedValidBoxOrder = [ ];
for (const role of boxOrder) {
// Get the indicator container associated with the current role.
const associatedIndicatorContainer = Main.panel.statusArea[role]?.container;
if (indicatorContainerSet.has(associatedIndicatorContainer)) restrictedValidBoxOrder.push(role);
}
return restrictedValidBoxOrder;
}
/**
* This method saves all new items currently present in the Gnome Shell top
* bar to the correct box orders.
*/
saveNewTopBarItems() {
// Load the configured box orders from settings.
const boxOrders = {
left: this._settings.get_strv("left-box-order"),
center: this._settings.get_strv("center-box-order"),
right: this._settings.get_strv("right-box-order"),
};
// Get roles (of items) currently present in the Gnome Shell top bar and
// index them using their associated indicator container.
let indicatorContainerRoleMap = new Map();
for (const role in Main.panel.statusArea) {
indicatorContainerRoleMap.set(Main.panel.statusArea[role].container, role);
}
// Get the indicator containers (of the items) currently present in the
// Gnome Shell top bar boxes.
const boxIndicatorContainers = {
left: Main.panel._leftBox.get_children(),
center: Main.panel._centerBox.get_children(),
// Reverse this array, since the items in the left and center box
// are logically LTR, while the items in the right box are RTL.
right: Main.panel._rightBox.get_children().reverse()
};
// This function goes through the indicator containers of the given box
// and adds roles of new items to the box order.
const addNewItemsToBoxOrder = (indicatorContainers, boxOrder, box) => {
for (const indicatorContainer of indicatorContainers) {
// First get the role associated with the current indicator
// container.
const role = indicatorContainerRoleMap.get(indicatorContainer);
if (!role) continue;
// Handle an AppIndicator/KStatusNotifierItem item differently.
if (role.startsWith("appindicator-")) {
this._appIndicatorKStatusNotifierItemManager.handleAppIndicatorKStatusNotifierItemItem(indicatorContainer, role, boxOrder, boxOrders, box === "right");
continue;
}
// Add the role to the box order, if it isn't in in one already.
if (!boxOrders.left.includes(role)
&& !boxOrders.center.includes(role)
&& !boxOrders.right.includes(role)) {
if (box === "right") {
// Add the items to the beginning for this array, since
// its RTL.
boxOrder.unshift(role);
} else {
boxOrder.push(role);
}
}
}
};
addNewItemsToBoxOrder(boxIndicatorContainers.left, boxOrders.left, "left");
addNewItemsToBoxOrder(boxIndicatorContainers.center, boxOrders.center, "center");
addNewItemsToBoxOrder(boxIndicatorContainers.right, boxOrders.right, "right");
// This function saves the given box order to settings.
const saveBoxOrderToSettings = (boxOrder, box) => {
const currentBoxOrder = this._settings.get_strv(`${box}-box-order`);
// Only save the updated box order to settings, if it is different,
// to avoid loops, when listening on settings changes.
if (JSON.stringify(currentBoxOrder) !== JSON.stringify(boxOrder)) {
this._settings.set_strv(`${box}-box-order`, boxOrder);
}
};
saveBoxOrderToSettings(boxOrders.left, "left");
saveBoxOrderToSettings(boxOrders.center, "center");
saveBoxOrderToSettings(boxOrders.right, "right");
}
};