mirror of
https://gitlab.gnome.org/julianschacher/top-bar-organizer.git
synced 2025-10-27 15:19:09 +00:00
201 lines
8.4 KiB
TypeScript
201 lines
8.4 KiB
TypeScript
"use strict";
|
|
|
|
import St from "gi://St"
|
|
import type Gio from "gi://Gio"
|
|
|
|
import * as Main from "resource:///org/gnome/shell/ui/main.js";
|
|
import * as Panel from "resource:///org/gnome/shell/ui/panel.js";
|
|
import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
|
|
|
|
import BoxOrderManager from "./extensionModules/BoxOrderManager.js";
|
|
import type { Box } from "./extensionModules/BoxOrderManager.js";
|
|
|
|
export interface CustomPanel extends Panel.Panel {
|
|
_leftBox: St.BoxLayout;
|
|
_centerBox: St.BoxLayout;
|
|
_rightBox: St.BoxLayout;
|
|
}
|
|
|
|
export default class TopBarOrganizerExtension extends Extension {
|
|
_settings!: Gio.Settings;
|
|
_boxOrderManager!: BoxOrderManager;
|
|
_settingsHandlerIds!: number[];
|
|
|
|
enable(): void {
|
|
this._settings = this.getSettings();
|
|
|
|
this._boxOrderManager = new BoxOrderManager({}, this._settings);
|
|
|
|
/// Stuff to do on startup(extension enable).
|
|
// Initially handle new top bar items and order top bar boxes.
|
|
this.#handleNewItemsAndOrderTopBar();
|
|
|
|
// Overwrite the `Panel._addToPanelBox` method with one handling new
|
|
// items.
|
|
this.#overwritePanelAddToPanelBox();
|
|
// Handle AppIndicators getting ready, to handle new AppIndicator items.
|
|
this._boxOrderManager.connect("appIndicatorReady", () => {
|
|
this.#handleNewItemsAndOrderTopBar();
|
|
});
|
|
|
|
// Handle changes of settings.
|
|
this._settingsHandlerIds = [];
|
|
const addSettingsChangeHandler = (settingsName: string) => {
|
|
const handlerId = this._settings.connect(`changed::${settingsName}`, () => {
|
|
this.#handleNewItemsAndOrderTopBar();
|
|
});
|
|
this._settingsHandlerIds.push(handlerId);
|
|
};
|
|
addSettingsChangeHandler("left-box-order");
|
|
addSettingsChangeHandler("center-box-order");
|
|
addSettingsChangeHandler("right-box-order");
|
|
addSettingsChangeHandler("hide");
|
|
addSettingsChangeHandler("show");
|
|
}
|
|
|
|
disable(): void {
|
|
// Revert the overwrite of `Panel._addToPanelBox`.
|
|
// @ts-ignore
|
|
Panel.Panel.prototype._addToPanelBox = Panel.Panel.prototype._originalAddToPanelBox;
|
|
// Set `Panel._originalAddToPanelBox` to `undefined`.
|
|
// @ts-ignore
|
|
Panel.Panel.prototype._originalAddToPanelBox = undefined;
|
|
|
|
// Disconnect signals.
|
|
for (const handlerId of this._settingsHandlerIds) {
|
|
this._settings.disconnect(handlerId);
|
|
}
|
|
this._boxOrderManager.disconnectSignals();
|
|
|
|
// @ts-ignore
|
|
this._settings = null;
|
|
// @ts-ignore
|
|
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(): void {
|
|
// Add the original `Panel._addToPanelBox` method as
|
|
// `Panel._originalAddToPanelBox`.
|
|
// @ts-ignore
|
|
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.
|
|
// @ts-ignore
|
|
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 {Box} box - The box to order.
|
|
*/
|
|
#orderTopBarItems(box: Box): void {
|
|
// Only run, when the session mode is "user" or the parent session mode
|
|
// is "user".
|
|
if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
|
|
return;
|
|
}
|
|
|
|
// Get the valid box order.
|
|
const validBoxOrder = this._boxOrderManager.getValidBoxOrder(box);
|
|
|
|
// Get the relevant box of `Main.panel`.
|
|
let panelBox = (Main.panel as CustomPanel)[`_${box}Box`];
|
|
|
|
/// Go through the items of the validBoxOrder and order the GNOME Shell
|
|
/// top bar box accordingly.
|
|
for (let i = 0; i < validBoxOrder.length; i++) {
|
|
const item = validBoxOrder[i];
|
|
// Get the indicator container associated with the current role.
|
|
const associatedIndicatorContainer = (Main.panel.statusArea as any)[item.role]?.container;
|
|
if (!(associatedIndicatorContainer instanceof St.Bin)) {
|
|
// TODO: maybe add logging
|
|
continue;
|
|
}
|
|
|
|
// Save whether or not the indicator container is visible.
|
|
const isVisible = associatedIndicatorContainer.visible;
|
|
|
|
const parent = associatedIndicatorContainer.get_parent();
|
|
if (parent !== null) {
|
|
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);
|
|
}
|
|
|
|
// Hide the indicator container...
|
|
// - ...if it wasn't visible before and the hide property of the
|
|
// item is "default".
|
|
// - if the hide property of the item is "hide".
|
|
// In all other cases have the item show.
|
|
// An e.g. screen recording indicator still wouldn't show tho, since
|
|
// this here acts on the indicator container, but a screen recording
|
|
// indicator is hidden on the indicator level.
|
|
if ((!isVisible && item.hide === "default") ||
|
|
item.hide === "hide") {
|
|
associatedIndicatorContainer.hide();
|
|
}
|
|
}
|
|
// 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(): void {
|
|
// Only run, when the session mode is "user" or the parent session mode
|
|
// is "user".
|
|
if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
|
|
return;
|
|
}
|
|
|
|
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.
|
|
}
|
|
}
|