Compare commits

..

No commits in common. "80f394d2d468def69a8c1c9551275c3742eb5f62" and "23baa41225356ca6068a24276e7549afe65579cc" have entirely different histories.

2 changed files with 99 additions and 159 deletions

View File

@ -16,10 +16,9 @@ export default class TopBarOrganizerExtension extends Extension {
// Initially handle new top bar items and order top bar boxes.
this.#handleNewItemsAndOrderTopBar();
// Overwrite the `Panel._addToPanelBox` method with one handling new
// items.
// Overwrite `Panel._addToPanelBox` method with one handling new items
// and also handle AppIndicators getting ready, to handle new items.
this.#overwritePanelAddToPanelBox();
// Handle AppIndicators getting ready, to handle new AppIndicator items.
this._boxOrderManager.connect("appIndicatorReady", () => {
this.#handleNewItemsAndOrderTopBar();
});
@ -96,7 +95,7 @@ export default class TopBarOrganizerExtension extends Extension {
}
// Get the valid box order.
const validBoxOrder = this._boxOrderManager.getValidBoxOrder(box);
const validBoxOrder = this._boxOrderManager.createValidBoxOrder(box);
// Get the relevant box of `Main.panel`.
let panelBox;
@ -112,10 +111,10 @@ export default class TopBarOrganizerExtension extends Extension {
break;
}
/// Go through the items of the validBoxOrder and order the GNOME Shell
/// top bar box accordingly.
/// 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].role;
const role = validBoxOrder[i];
// Get the indicator container associated with the current role.
const associatedIndicatorContainer = Main.panel.statusArea[role].container;

View File

@ -5,18 +5,10 @@ import GObject from "gi://GObject";
import * as Main from "resource:///org/gnome/shell/ui/main.js";
/**
* A resolved box order item containing the items role and settings identifier.
* @typedef {Object} ResolvedBoxOrderItem
* @property {string} settingsId - The settings identifier of the item.
* @property {string} role - The role of the item.
*/
/**
* This class provides an interfaces to the box orders stored in settings.
* It takes care of handling AppIndicator items and resolving from the internal
* item settings identifiers to roles.
* In the end this results in convenient functions, which are directly useful in
* other extension code.
* 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.
*/
export default class BoxOrderManager extends GObject.Object {
static {
@ -28,68 +20,31 @@ export default class BoxOrderManager extends GObject.Object {
}
#appIndicatorReadyHandlerIdMap;
#appIndicatorItemSettingsIdToRolesMap;
#appIndicatorItemApplicationRoleMap;
#settings;
constructor(params = {}, settings) {
super(params);
this.#appIndicatorReadyHandlerIdMap = new Map();
this.#appIndicatorItemSettingsIdToRolesMap = new Map();
this.#appIndicatorItemApplicationRoleMap = new Map();
this.#settings = settings;
}
/**
* Gets a box order for the given top bar box from settings.
* @param {string} box - The top bar box for which to get the box order.
* Must be one of the following values:
* - "left"
* - "center"
* - "right"
* @returns {string[]} - The box order consisting of an array of item
* settings identifiers.
*/
#getBoxOrder(box) {
return this.#settings.get_strv(`${box}-box-order`);
}
/**
* Save the given box order to settings, making sure to only save a changed
* box order, to avoid loops when listening on settings changes.
* @param {string} box - The top bar box for which to save the box order.
* Must be one of the following values:
* - "left"
* - "center"
* - "right"
* @param {string[]} boxOrder - The box order to save. Must be an array of
* item settings identifiers.
*/
#saveBoxOrder(box, boxOrder) {
const currentBoxOrder = this.#getBoxOrder(box);
// Only save the given box order to settings, if it is different, to
// avoid loops when listening on settings changes.
if (JSON.stringify(boxOrder) !== JSON.stringify(currentBoxOrder)) {
this.#settings.set_strv(`${box}-box-order`, boxOrder);
}
}
/**
* Handles an AppIndicator/KStatusNotifierItem item by deriving a settings
* identifier and then associating the role of the given item to the items
* settings identifier.
* It then returns the derived settings identifier.
* In the case, where the settings identifier can't be derived, because the
* application can't be determined, this method throws an error. However it
* then also makes sure that once the app indicators "ready" signal emits,
* this classes "appIndicatorReady" signal emits as well, such that it and
* other methods can be called again to properly handle the item.
* 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
* item.
* @returns {string} The derived items settings identifier.
* @returns {string} The placeholder role.
*/
#handleAppIndicatorItem(indicatorContainer, role) {
const appIndicator = indicatorContainer.get_child()._indicator;
@ -111,74 +66,56 @@ export default class BoxOrderManager extends GObject.Object {
application = "dropbox-client";
}
// Derive the items settings identifier from the application name.
const itemSettingsId = `appindicator-kstatusnotifieritem-${application}`;
// Associate the role with the items settings identifier.
let roles = this.#appIndicatorItemSettingsIdToRolesMap.get(itemSettingsId);
// Associate the role with the application.
let roles = this.#appIndicatorItemApplicationRoleMap.get(application);
if (roles) {
// If the settings identifier already has an array of associated
// roles, just add the role to it, if needed.
// If the application already has an array of associated roles, just
// add the role to it, if needed.
if (!roles.includes(role)) {
roles.push(role);
}
} else {
// Otherwise create a new array.
this.#appIndicatorItemSettingsIdToRolesMap.set(itemSettingsId, [role]);
this.#appIndicatorItemApplicationRoleMap.set(application, [role]);
}
// Return the item settings identifier.
return itemSettingsId;
// Return the placeholder.
// A box order containing this placeholder can later be resolved to
// relevant roles using `#resolveAppIndicatorPlaceholders`.
return `appindicator-kstatusnotifieritem-${application}`;
}
/**
* Gets a resolved box order for the given top bar box, where all
* AppIndicator items got resolved using their roles, meaning they might be
* present multiple times or not at all depending on the roles stored.
* @param {string} box - The top bar box for which to get the resolved box order.
* Must be one of the following values:
* - "left"
* - "center"
* - "right"
* @returns {ResolvedBoxOrderItem[]} - The resolved box order.
* Takes a box order and replaces AppIndicator placeholder roles with
* actual roles.
* @param {string[]} - The box order of which to replace placeholder roles.
* @returns {string[]} - A box order with all placeholder roles
* resolved/replaced to/with actual roles.
*/
#getResolvedBoxOrder(box) {
let boxOrder = this.#getBoxOrder(box);
#resolveAppIndicatorPlaceholders(boxOrder) {
let resolvedBoxOrder = [];
for (const itemSettingsId of boxOrder) {
const resolvedBoxOrderItem = {
settingsId: itemSettingsId,
role: "",
};
// If the items settings identifier doesn't indicate that the item
// is an AppIndicator/KStatusNotifierItem item, then its identifier
// is the role and it can just be added to the resolved box order.
if (!itemSettingsId.startsWith("appindicator-kstatusnotifieritem-")) {
resolvedBoxOrderItem.role = resolvedBoxOrderItem.settingsId;
resolvedBoxOrder.push(resolvedBoxOrderItem);
for (const role of boxOrder) {
// If the role isn't a placeholder, just add it to the resolved box
// order.
if (!role.startsWith("appindicator-kstatusnotifieritem-")) {
resolvedBoxOrder.push(role);
continue;
}
// If the items settings identifier indicates otherwise, then handle
// the item specially.
/// If the role is a placeholder, replace it.
// First get the application this placeholder is associated with.
const application = role.replace("appindicator-kstatusnotifieritem-", "");
// Get the roles roles associated with the items settings id.
let roles = this.#appIndicatorItemSettingsIdToRolesMap.get(resolvedBoxOrderItem.settingsId);
// Then get the actual roles associated with this application.
let actualRoles = this.#appIndicatorItemApplicationRoleMap.get(application);
// If there are no roles associated, continue.
if (!roles) {
// If there are no actual roles, continue.
if (!actualRoles) {
continue;
}
// Otherwise create a new resolved box order item for each role and
// add it to the resolved box order.
for (const role of roles) {
const newResolvedBoxOrderItem = JSON.parse(JSON.stringify(resolvedBoxOrderItem));
newResolvedBoxOrderItem.role = role;
resolvedBoxOrder.push(newResolvedBoxOrderItem);
}
// Otherwise add the actual roles to the resolved box order.
resolvedBoxOrder.push(...actualRoles);
}
return resolvedBoxOrder;
@ -199,23 +136,24 @@ export default class BoxOrderManager extends GObject.Object {
}
/**
* Gets a valid box order for the given top bar box, where all AppIndicator
* items got resolved and where only items are included, which are in some
* GNOME Shell top bar box.
* 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 {ResolvedBoxOrderItem[]} - The valid box order.
* @returns {string[]} - The valid box order.
*/
getValidBoxOrder(box) {
createValidBoxOrder(box) {
// Get a resolved box order.
let resolvedBoxOrder = this.#getResolvedBoxOrder(box);
let boxOrder = this.#resolveAppIndicatorPlaceholders(this.#settings.get_strv(`${box}-box-order`));
// ToDo: simplify.
// Get the indicator containers (of the items) currently present in the
// GNOME Shell top bar.
// Gnome Shell top bar.
const indicatorContainers = [
Main.panel._leftBox.get_children(),
Main.panel._centerBox.get_children(),
@ -226,16 +164,16 @@ export default class BoxOrderManager extends GObject.Object {
// fast easy access.
const indicatorContainerSet = new Set(indicatorContainers);
// Go through the resolved box order and only add items to the valid box
// order, where their indicator is currently present in the GNOME Shell
// top bar.
// 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 item of resolvedBoxOrder) {
// Get the indicator container associated with the items role.
const associatedIndicatorContainer = Main.panel.statusArea[item.role]?.container;
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(item);
validBoxOrder.push(role);
}
}
@ -243,24 +181,24 @@ export default class BoxOrderManager extends GObject.Object {
}
/**
* This method saves all new items currently present in the GNOME Shell top
* bar to the settings.
* This method saves all new items currently present in the Gnome Shell top
* bar to the correct box orders.
*/
saveNewTopBarItems() {
// Only run, when the session mode is "user" or the parent session mode
// is "user".
if (Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
return;
}
// Get the box orders.
// Load the configured box orders from settings.
const boxOrders = {
left: this.#getBoxOrder("left"),
center: this.#getBoxOrder("center"),
right: this.#getBoxOrder("right"),
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
// 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) {
@ -268,7 +206,7 @@ export default class BoxOrderManager extends GObject.Object {
}
// Get the indicator containers (of the items) currently present in the
// GNOME Shell top bar boxes.
// Gnome Shell top bar boxes.
const boxIndicatorContainers = {
left: Main.panel._leftBox.get_children(),
center: Main.panel._centerBox.get_children(),
@ -278,8 +216,8 @@ export default class BoxOrderManager extends GObject.Object {
};
// This function goes through the indicator containers of the given box
// and adds new item settings identifiers to the given box order.
const addNewItemSettingsIdsToBoxOrder = (indicatorContainers, boxOrder, 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.
@ -288,46 +226,49 @@ export default class BoxOrderManager extends GObject.Object {
continue;
}
// Then get a settings identifier for the item.
let itemSettingsId;
// If the role indicates that the item is an
// AppIndicator/KStatusNotifierItem item, then handle it
// differently
// Handle an AppIndicator/KStatusNotifierItem item differently.
if (role.startsWith("appindicator-")) {
try {
itemSettingsId = this.#handleAppIndicatorItem(indicatorContainer, role);
role = this.#handleAppIndicatorItem(indicatorContainer, role);
} catch (e) {
if (e.message !== "Application can't be determined.") {
throw(e);
}
continue;
}
} else { // Otherwise just use the role as the settings identifier.
itemSettingsId = role;
}
// Add the items settings identifier to the box order, if it
// isn't in in one already.
if (!boxOrders.left.includes(itemSettingsId)
&& !boxOrders.center.includes(itemSettingsId)
&& !boxOrders.right.includes(itemSettingsId)) {
// 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(itemSettingsId);
boxOrder.unshift(role);
} else {
boxOrder.push(itemSettingsId);
boxOrder.push(role);
}
}
}
};
addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.left, boxOrders.left, "left");
addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.center, boxOrders.center, "center");
addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.right, boxOrders.right, "right");
addNewItemsToBoxOrder(boxIndicatorContainers.left, boxOrders.left, "left");
addNewItemsToBoxOrder(boxIndicatorContainers.center, boxOrders.center, "center");
addNewItemsToBoxOrder(boxIndicatorContainers.right, boxOrders.right, "right");
this.#saveBoxOrder("left", boxOrders.left);
this.#saveBoxOrder("center", boxOrders.center);
this.#saveBoxOrder("right", boxOrders.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");
}
}