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. // Initially handle new top bar items and order top bar boxes.
this.#handleNewItemsAndOrderTopBar(); this.#handleNewItemsAndOrderTopBar();
// Overwrite the `Panel._addToPanelBox` method with one handling new // Overwrite `Panel._addToPanelBox` method with one handling new items
// items. // and also handle AppIndicators getting ready, to handle new items.
this.#overwritePanelAddToPanelBox(); this.#overwritePanelAddToPanelBox();
// Handle AppIndicators getting ready, to handle new AppIndicator items.
this._boxOrderManager.connect("appIndicatorReady", () => { this._boxOrderManager.connect("appIndicatorReady", () => {
this.#handleNewItemsAndOrderTopBar(); this.#handleNewItemsAndOrderTopBar();
}); });
@ -96,7 +95,7 @@ export default class TopBarOrganizerExtension extends Extension {
} }
// Get the valid box order. // Get the valid box order.
const validBoxOrder = this._boxOrderManager.getValidBoxOrder(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;
@ -112,10 +111,10 @@ export default class TopBarOrganizerExtension extends Extension {
break; break;
} }
/// Go through the items of the validBoxOrder and order the GNOME Shell /// Go through the items (or rather their roles) of the validBoxOrder
/// top bar box accordingly. /// and order the panelBox accordingly.
for (let i = 0; i < validBoxOrder.length; i++) { 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. // Get the indicator container associated with the current role.
const associatedIndicatorContainer = Main.panel.statusArea[role].container; 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"; import * as Main from "resource:///org/gnome/shell/ui/main.js";
/** /**
* A resolved box order item containing the items role and settings identifier. * This class provides methods get, set and interact with box orders, while
* @typedef {Object} ResolvedBoxOrderItem * taking over the work of translating between what is stored in settings and
* @property {string} settingsId - The settings identifier of the item. * what is really useable by the other extension code.
* @property {string} role - The role of the item. * It's basically a heavy wrapper around the box orders stored in the settings.
*/
/**
* 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.
*/ */
export default class BoxOrderManager extends GObject.Object { export default class BoxOrderManager extends GObject.Object {
static { static {
@ -28,68 +20,31 @@ export default class BoxOrderManager extends GObject.Object {
} }
#appIndicatorReadyHandlerIdMap; #appIndicatorReadyHandlerIdMap;
#appIndicatorItemSettingsIdToRolesMap; #appIndicatorItemApplicationRoleMap;
#settings; #settings;
constructor(params = {}, settings) { constructor(params = {}, settings) {
super(params); super(params);
this.#appIndicatorReadyHandlerIdMap = new Map(); this.#appIndicatorReadyHandlerIdMap = new Map();
this.#appIndicatorItemSettingsIdToRolesMap = new Map(); this.#appIndicatorItemApplicationRoleMap = new Map();
this.#settings = settings; this.#settings = settings;
} }
/** /**
* Gets a box order for the given top bar box from settings. * Handles an AppIndicator/KStatusNotifierItem item by associating the role
* @param {string} box - The top bar box for which to get the box order. * of the given item with the application of the
* Must be one of the following values: * AppIndicator/KStatusNotifier item and returning a placeholder role.
* - "left" * In the case, where the application can't be determined, this method
* - "center" * throws an error. However it also makes sure that once the app indicators
* - "right" * "ready" signal emits, this classes "appIndicatorReady" signal emits as
* @returns {string[]} - The box order consisting of an array of item * well.
* 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.
* @param {string} indicatorContainer - The container of the indicator of the * @param {string} indicatorContainer - The container of the indicator of the
* AppIndicator/KStatusNotifierItem item. * AppIndicator/KStatusNotifierItem item.
* @param {string} role - The role of the AppIndicator/KStatusNotifierItem * @param {string} role - The role of the AppIndicator/KStatusNotifierItem
* item. * item.
* @returns {string} The derived items settings identifier. * @returns {string} The placeholder role.
*/ */
#handleAppIndicatorItem(indicatorContainer, role) { #handleAppIndicatorItem(indicatorContainer, role) {
const appIndicator = indicatorContainer.get_child()._indicator; const appIndicator = indicatorContainer.get_child()._indicator;
@ -111,74 +66,56 @@ export default class BoxOrderManager extends GObject.Object {
application = "dropbox-client"; application = "dropbox-client";
} }
// Derive the items settings identifier from the application name. // Associate the role with the application.
const itemSettingsId = `appindicator-kstatusnotifieritem-${application}`; let roles = this.#appIndicatorItemApplicationRoleMap.get(application);
// Associate the role with the items settings identifier.
let roles = this.#appIndicatorItemSettingsIdToRolesMap.get(itemSettingsId);
if (roles) { if (roles) {
// If the settings identifier already has an array of associated // If the application already has an array of associated roles, just
// roles, just add the role to it, if needed. // add the role to it, if needed.
if (!roles.includes(role)) { if (!roles.includes(role)) {
roles.push(role); roles.push(role);
} }
} else { } else {
// Otherwise create a new array. // Otherwise create a new array.
this.#appIndicatorItemSettingsIdToRolesMap.set(itemSettingsId, [role]); this.#appIndicatorItemApplicationRoleMap.set(application, [role]);
} }
// Return the item settings identifier. // Return the placeholder.
return itemSettingsId; // 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 * Takes a box order and replaces AppIndicator placeholder roles with
* AppIndicator items got resolved using their roles, meaning they might be * actual roles.
* present multiple times or not at all depending on the roles stored. * @param {string[]} - The box order of which to replace placeholder roles.
* @param {string} box - The top bar box for which to get the resolved box order. * @returns {string[]} - A box order with all placeholder roles
* Must be one of the following values: * resolved/replaced to/with actual roles.
* - "left"
* - "center"
* - "right"
* @returns {ResolvedBoxOrderItem[]} - The resolved box order.
*/ */
#getResolvedBoxOrder(box) { #resolveAppIndicatorPlaceholders(boxOrder) {
let boxOrder = this.#getBoxOrder(box);
let resolvedBoxOrder = []; let resolvedBoxOrder = [];
for (const itemSettingsId of boxOrder) { for (const role of boxOrder) {
const resolvedBoxOrderItem = { // If the role isn't a placeholder, just add it to the resolved box
settingsId: itemSettingsId, // order.
role: "", if (!role.startsWith("appindicator-kstatusnotifieritem-")) {
}; resolvedBoxOrder.push(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);
continue; continue;
} }
// If the items settings identifier indicates otherwise, then handle /// If the role is a placeholder, replace it.
// the item specially. // 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. // Then get the actual roles associated with this application.
let roles = this.#appIndicatorItemSettingsIdToRolesMap.get(resolvedBoxOrderItem.settingsId); let actualRoles = this.#appIndicatorItemApplicationRoleMap.get(application);
// If there are no roles associated, continue. // If there are no actual roles, continue.
if (!roles) { if (!actualRoles) {
continue; continue;
} }
// Otherwise create a new resolved box order item for each role and // Otherwise add the actual roles to the resolved box order.
// add it to the resolved box order. resolvedBoxOrder.push(...actualRoles);
for (const role of roles) {
const newResolvedBoxOrderItem = JSON.parse(JSON.stringify(resolvedBoxOrderItem));
newResolvedBoxOrderItem.role = role;
resolvedBoxOrder.push(newResolvedBoxOrderItem);
}
} }
return resolvedBoxOrder; 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 * This method returns a valid box order for the given top bar box.
* items got resolved and where only items are included, which are in some * This means it returns a box order, where only roles are included, which
* GNOME Shell top bar box. * 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. * @param {string} box - The top bar box to return the valid box order for.
* Must be one of the following values: * Must be one of the following values:
* - "left" * - "left"
* - "center" * - "center"
* - "right" * - "right"
* @returns {ResolvedBoxOrderItem[]} - The valid box order. * @returns {string[]} - The valid box order.
*/ */
getValidBoxOrder(box) { createValidBoxOrder(box) {
// Get a resolved box order. // Get a resolved box order.
let resolvedBoxOrder = this.#getResolvedBoxOrder(box); let boxOrder = this.#resolveAppIndicatorPlaceholders(this.#settings.get_strv(`${box}-box-order`));
// ToDo: simplify. // ToDo: simplify.
// Get the indicator containers (of the items) currently present in the // Get the indicator containers (of the items) currently present in the
// GNOME Shell top bar. // Gnome Shell top bar.
const indicatorContainers = [ const indicatorContainers = [
Main.panel._leftBox.get_children(), Main.panel._leftBox.get_children(),
Main.panel._centerBox.get_children(), Main.panel._centerBox.get_children(),
@ -226,16 +164,16 @@ export default class BoxOrderManager extends GObject.Object {
// fast easy access. // fast easy access.
const indicatorContainerSet = new Set(indicatorContainers); const indicatorContainerSet = new Set(indicatorContainers);
// Go through the resolved box order and only add items to the valid box // Go through the box order and only add items to the valid box order,
// order, where their indicator is currently present in the GNOME Shell // where their indicator is present in the Gnome Shell top bar
// top bar. // currently.
let validBoxOrder = []; let validBoxOrder = [];
for (const item of resolvedBoxOrder) { for (const role of boxOrder) {
// Get the indicator container associated with the items role. // Get the indicator container associated with the current role.
const associatedIndicatorContainer = Main.panel.statusArea[item.role]?.container; const associatedIndicatorContainer = Main.panel.statusArea[role]?.container;
if (indicatorContainerSet.has(associatedIndicatorContainer)) { if (indicatorContainerSet.has(associatedIndicatorContainer)) {
validBoxOrder.push(item); validBoxOrder.push(role);
} }
} }
@ -243,8 +181,8 @@ export default class BoxOrderManager extends GObject.Object {
} }
/** /**
* This method saves all new items currently present in the GNOME Shell top * This method saves all new items currently present in the Gnome Shell top
* bar to the settings. * bar to the correct box orders.
*/ */
saveNewTopBarItems() { saveNewTopBarItems() {
// Only run, when the session mode is "user" or the parent session mode // Only run, when the session mode is "user" or the parent session mode
@ -253,14 +191,14 @@ export default class BoxOrderManager extends GObject.Object {
return; return;
} }
// Get the box orders. // Load the configured box orders from settings.
const boxOrders = { const boxOrders = {
left: this.#getBoxOrder("left"), left: this.#settings.get_strv("left-box-order"),
center: this.#getBoxOrder("center"), center: this.#settings.get_strv("center-box-order"),
right: this.#getBoxOrder("right"), 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. // index them using their associated indicator container.
let indicatorContainerRoleMap = new Map(); let indicatorContainerRoleMap = new Map();
for (const role in Main.panel.statusArea) { 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 // Get the indicator containers (of the items) currently present in the
// GNOME Shell top bar boxes. // Gnome Shell top bar boxes.
const boxIndicatorContainers = { const boxIndicatorContainers = {
left: Main.panel._leftBox.get_children(), left: Main.panel._leftBox.get_children(),
center: Main.panel._centerBox.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 // This function goes through the indicator containers of the given box
// and adds new item settings identifiers to the given box order. // and adds roles of new items to the box order.
const addNewItemSettingsIdsToBoxOrder = (indicatorContainers, boxOrder, box) => { const addNewItemsToBoxOrder = (indicatorContainers, boxOrder, box) => {
for (const indicatorContainer of indicatorContainers) { for (const indicatorContainer of indicatorContainers) {
// First get the role associated with the current indicator // First get the role associated with the current indicator
// container. // container.
@ -288,46 +226,49 @@ export default class BoxOrderManager extends GObject.Object {
continue; continue;
} }
// Then get a settings identifier for the item. // Handle an AppIndicator/KStatusNotifierItem item differently.
let itemSettingsId;
// If the role indicates that the item is an
// AppIndicator/KStatusNotifierItem item, then handle it
// differently
if (role.startsWith("appindicator-")) { if (role.startsWith("appindicator-")) {
try { try {
itemSettingsId = this.#handleAppIndicatorItem(indicatorContainer, role); role = this.#handleAppIndicatorItem(indicatorContainer, role);
} catch (e) { } catch (e) {
if (e.message !== "Application can't be determined.") { if (e.message !== "Application can't be determined.") {
throw(e); throw(e);
} }
continue; continue;
} }
} else { // Otherwise just use the role as the settings identifier.
itemSettingsId = role;
} }
// Add the items settings identifier to the box order, if it // Add the role to the box order, if it isn't in in one already.
// isn't in in one already. if (!boxOrders.left.includes(role)
if (!boxOrders.left.includes(itemSettingsId) && !boxOrders.center.includes(role)
&& !boxOrders.center.includes(itemSettingsId) && !boxOrders.right.includes(role)) {
&& !boxOrders.right.includes(itemSettingsId)) {
if (box === "right") { if (box === "right") {
// Add the items to the beginning for this array, since // Add the items to the beginning for this array, since
// its RTL. // its RTL.
boxOrder.unshift(itemSettingsId); boxOrder.unshift(role);
} else { } else {
boxOrder.push(itemSettingsId); boxOrder.push(role);
} }
} }
} }
}; };
addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.left, boxOrders.left, "left"); addNewItemsToBoxOrder(boxIndicatorContainers.left, boxOrders.left, "left");
addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.center, boxOrders.center, "center"); addNewItemsToBoxOrder(boxIndicatorContainers.center, boxOrders.center, "center");
addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.right, boxOrders.right, "right"); addNewItemsToBoxOrder(boxIndicatorContainers.right, boxOrders.right, "right");
this.#saveBoxOrder("left", boxOrders.left); // This function saves the given box order to settings.
this.#saveBoxOrder("center", boxOrders.center); const saveBoxOrderToSettings = (boxOrder, box) => {
this.#saveBoxOrder("right", boxOrders.right); 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");
} }
} }