mirror of
https://gitlab.gnome.org/julianschacher/top-bar-organizer.git
synced 2025-10-27 15:19:09 +00:00
Compare commits
1 Commits
114e1335d1
...
9a6474b947
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a6474b947 |
@ -6,7 +6,7 @@ insert_final_newline = true
|
|||||||
indent_style = space
|
indent_style = space
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
[*.{js,json}]
|
[*.{js,ts,json}]
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/node_modules/
|
/node_modules/
|
||||||
|
/dist/
|
||||||
top-bar-organizer@julian.gse.jsts.xyz.shell-extension.zip
|
top-bar-organizer@julian.gse.jsts.xyz.shell-extension.zip
|
||||||
|
|||||||
4
ambient.d.ts
vendored
Normal file
4
ambient.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import "@girs/gjs"
|
||||||
|
import "@girs/gjs/dom"
|
||||||
|
import "@girs/gnome-shell/ambient"
|
||||||
|
import "@girs/gnome-shell/extensions/global"
|
||||||
1002
package-lock.json
generated
1002
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
|||||||
"name": "top-bar-organizer",
|
"name": "top-bar-organizer",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "A Gnome Shell Extension for organizing your Gnome Shell top bar.",
|
"description": "A Gnome Shell Extension for organizing your Gnome Shell top bar.",
|
||||||
|
"type": "module",
|
||||||
"directories": {
|
"directories": {
|
||||||
"doc": "docs"
|
"doc": "docs"
|
||||||
},
|
},
|
||||||
@ -12,6 +13,12 @@
|
|||||||
"author": "June",
|
"author": "June",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.50.0"
|
"eslint": "^8.57.1",
|
||||||
|
"eslint-plugin-jsdoc": "^50.7.1",
|
||||||
|
"typescript": "^5.8.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@girs/gjs": "^4.0.0-beta.23",
|
||||||
|
"@girs/gnome-shell": "^48.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,11 @@ set -e
|
|||||||
|
|
||||||
REAL_BASE_DIR=$( dirname $( readlink -f "$0" ))
|
REAL_BASE_DIR=$( dirname $( readlink -f "$0" ))
|
||||||
|
|
||||||
gnome-extensions pack "$REAL_BASE_DIR/src" \
|
rm -rf "$REAL_BASE_DIR/dist"
|
||||||
|
cd "$REAL_BASE_DIR"
|
||||||
|
npx tsc
|
||||||
|
cp "$REAL_BASE_DIR/src/metadata.json" "$REAL_BASE_DIR/dist/metadata.json"
|
||||||
|
gnome-extensions pack "$REAL_BASE_DIR/dist" \
|
||||||
--force \
|
--force \
|
||||||
--extra-source extensionModules \
|
--extra-source extensionModules \
|
||||||
--extra-source prefsModules \
|
--extra-source prefsModules \
|
||||||
|
|||||||
@ -1,13 +1,27 @@
|
|||||||
"use strict";
|
"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 Main from "resource:///org/gnome/shell/ui/main.js";
|
||||||
import * as Panel from "resource:///org/gnome/shell/ui/panel.js";
|
import * as Panel from "resource:///org/gnome/shell/ui/panel.js";
|
||||||
import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
|
import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
|
||||||
|
|
||||||
import BoxOrderManager from "./extensionModules/BoxOrderManager.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 {
|
export default class TopBarOrganizerExtension extends Extension {
|
||||||
enable() {
|
_settings!: Gio.Settings;
|
||||||
|
_boxOrderManager!: BoxOrderManager;
|
||||||
|
_settingsHandlerIds!: number[];
|
||||||
|
|
||||||
|
enable(): void {
|
||||||
this._settings = this.getSettings();
|
this._settings = this.getSettings();
|
||||||
|
|
||||||
this._boxOrderManager = new BoxOrderManager({}, this._settings);
|
this._boxOrderManager = new BoxOrderManager({}, this._settings);
|
||||||
@ -26,7 +40,7 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
|
|
||||||
// Handle changes of settings.
|
// Handle changes of settings.
|
||||||
this._settingsHandlerIds = [];
|
this._settingsHandlerIds = [];
|
||||||
const addSettingsChangeHandler = (settingsName) => {
|
const addSettingsChangeHandler = (settingsName: string) => {
|
||||||
const handlerId = this._settings.connect(`changed::${settingsName}`, () => {
|
const handlerId = this._settings.connect(`changed::${settingsName}`, () => {
|
||||||
this.#handleNewItemsAndOrderTopBar();
|
this.#handleNewItemsAndOrderTopBar();
|
||||||
});
|
});
|
||||||
@ -39,10 +53,12 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
addSettingsChangeHandler("show");
|
addSettingsChangeHandler("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
disable() {
|
disable(): void {
|
||||||
// Revert the overwrite of `Panel._addToPanelBox`.
|
// Revert the overwrite of `Panel._addToPanelBox`.
|
||||||
|
// @ts-ignore
|
||||||
Panel.Panel.prototype._addToPanelBox = Panel.Panel.prototype._originalAddToPanelBox;
|
Panel.Panel.prototype._addToPanelBox = Panel.Panel.prototype._originalAddToPanelBox;
|
||||||
// Set `Panel._originalAddToPanelBox` to `undefined`.
|
// Set `Panel._originalAddToPanelBox` to `undefined`.
|
||||||
|
// @ts-ignore
|
||||||
Panel.Panel.prototype._originalAddToPanelBox = undefined;
|
Panel.Panel.prototype._originalAddToPanelBox = undefined;
|
||||||
|
|
||||||
// Disconnect signals.
|
// Disconnect signals.
|
||||||
@ -51,7 +67,9 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
}
|
}
|
||||||
this._boxOrderManager.disconnectSignals();
|
this._boxOrderManager.disconnectSignals();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
this._settings = null;
|
this._settings = null;
|
||||||
|
// @ts-ignore
|
||||||
this._boxOrderManager = null;
|
this._boxOrderManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,9 +81,10 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
* Overwrite `Panel._addToPanelBox` with a custom method, which simply calls
|
* Overwrite `Panel._addToPanelBox` with a custom method, which simply calls
|
||||||
* the original one and handles new items and orders the top bar afterwards.
|
* the original one and handles new items and orders the top bar afterwards.
|
||||||
*/
|
*/
|
||||||
#overwritePanelAddToPanelBox() {
|
#overwritePanelAddToPanelBox(): void {
|
||||||
// Add the original `Panel._addToPanelBox` method as
|
// Add the original `Panel._addToPanelBox` method as
|
||||||
// `Panel._originalAddToPanelBox`.
|
// `Panel._originalAddToPanelBox`.
|
||||||
|
// @ts-ignore
|
||||||
Panel.Panel.prototype._originalAddToPanelBox = Panel.Panel.prototype._addToPanelBox;
|
Panel.Panel.prototype._originalAddToPanelBox = Panel.Panel.prototype._addToPanelBox;
|
||||||
|
|
||||||
const handleNewItemsAndOrderTopBar = () => {
|
const handleNewItemsAndOrderTopBar = () => {
|
||||||
@ -76,6 +95,7 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
Panel.Panel.prototype._addToPanelBox = function(role, indicator, position, box) {
|
Panel.Panel.prototype._addToPanelBox = function(role, indicator, position, box) {
|
||||||
// Simply call the original `_addToPanelBox` and order the top bar
|
// Simply call the original `_addToPanelBox` and order the top bar
|
||||||
// and handle new items afterwards.
|
// and handle new items afterwards.
|
||||||
|
// @ts-ignore
|
||||||
this._originalAddToPanelBox(role, indicator, position, box);
|
this._originalAddToPanelBox(role, indicator, position, box);
|
||||||
handleNewItemsAndOrderTopBar();
|
handleNewItemsAndOrderTopBar();
|
||||||
};
|
};
|
||||||
@ -88,9 +108,9 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
/**
|
/**
|
||||||
* 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.
|
||||||
* @param {string} box - The box to order.
|
* @param {Box} box - The box to order.
|
||||||
*/
|
*/
|
||||||
#orderTopBarItems(box) {
|
#orderTopBarItems(box: Box): void {
|
||||||
// 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
|
||||||
// is "user".
|
// is "user".
|
||||||
if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
|
if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
|
||||||
@ -104,13 +124,13 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
let panelBox;
|
let panelBox;
|
||||||
switch (box) {
|
switch (box) {
|
||||||
case "left":
|
case "left":
|
||||||
panelBox = Main.panel._leftBox;
|
panelBox = (Main.panel as CustomPanel)._leftBox;
|
||||||
break;
|
break;
|
||||||
case "center":
|
case "center":
|
||||||
panelBox = Main.panel._centerBox;
|
panelBox = (Main.panel as CustomPanel)._centerBox;
|
||||||
break;
|
break;
|
||||||
case "right":
|
case "right":
|
||||||
panelBox = Main.panel._rightBox;
|
panelBox = (Main.panel as CustomPanel)._rightBox;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,12 +139,19 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
for (let i = 0; i < validBoxOrder.length; i++) {
|
for (let i = 0; i < validBoxOrder.length; i++) {
|
||||||
const item = validBoxOrder[i];
|
const item = 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[item.role].container;
|
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.
|
// Save whether or not the indicator container is visible.
|
||||||
const isVisible = associatedIndicatorContainer.visible;
|
const isVisible = associatedIndicatorContainer.visible;
|
||||||
|
|
||||||
associatedIndicatorContainer.get_parent().remove_child(associatedIndicatorContainer);
|
const parent = associatedIndicatorContainer.get_parent();
|
||||||
|
if (parent !== null) {
|
||||||
|
parent.remove_child(associatedIndicatorContainer);
|
||||||
|
}
|
||||||
if (box === "right") {
|
if (box === "right") {
|
||||||
// If the target panel box is the right panel box, insert the
|
// If the target panel box is the right panel box, insert the
|
||||||
// indicator container at index `-1`, which just adds it to the
|
// indicator container at index `-1`, which just adds it to the
|
||||||
@ -165,7 +192,7 @@ export default class TopBarOrganizerExtension extends Extension {
|
|||||||
* This method handles all new items currently present in the top bar and
|
* This method handles all new items currently present in the top bar and
|
||||||
* orders the items of all top bar boxes.
|
* orders the items of all top bar boxes.
|
||||||
*/
|
*/
|
||||||
#handleNewItemsAndOrderTopBar() {
|
#handleNewItemsAndOrderTopBar(): void {
|
||||||
// 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
|
||||||
// is "user".
|
// is "user".
|
||||||
if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
|
if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
|
||||||
@ -1,18 +1,24 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import GObject from "gi://GObject";
|
import GObject from "gi://GObject";
|
||||||
|
import St from "gi://St";
|
||||||
|
import type Gio from "gi://Gio";
|
||||||
|
|
||||||
import * as Main from "resource:///org/gnome/shell/ui/main.js";
|
import * as Main from "resource:///org/gnome/shell/ui/main.js";
|
||||||
|
|
||||||
|
import type { CustomPanel } from "../extension.js"
|
||||||
|
|
||||||
|
export type Box = "left" | "center" | "right";
|
||||||
|
type Hide = "hide" | "show" | "default";
|
||||||
/**
|
/**
|
||||||
* A resolved box order item containing the items role, settings identifier and
|
* A resolved box order item containing the items role, settings identifier and
|
||||||
* additional information.
|
* additional information.
|
||||||
* @typedef {Object} ResolvedBoxOrderItem
|
|
||||||
* @property {string} settingsId - The settings identifier of the item.
|
|
||||||
* @property {string} role - The role of the item.
|
|
||||||
* @property {string} hide - Whether the item should be (forcefully) hidden
|
|
||||||
* (hide), shown (show) or just be left as is (default).
|
|
||||||
*/
|
*/
|
||||||
|
interface ResolvedBoxOrderItem {
|
||||||
|
settingsId: string // The settings identifier of the item.
|
||||||
|
role: string // The role of the item.
|
||||||
|
hide: Hide // Whether the item should be (forcefully) hidden, (forcefully) shown or just be left as is.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides an interfaces to the box orders stored in settings.
|
* This class provides an interfaces to the box orders stored in settings.
|
||||||
@ -30,12 +36,15 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#appIndicatorReadyHandlerIdMap;
|
// Can't have type guarantees here, since this is working with types from
|
||||||
#appIndicatorItemSettingsIdToRolesMap;
|
// the KStatusNotifier/AppIndicator extension.
|
||||||
#taskUpUltraLiteItemRoles;
|
#appIndicatorReadyHandlerIdMap: Map<any, any>;
|
||||||
#settings;
|
#appIndicatorItemSettingsIdToRolesMap: Map<string, string[]>;
|
||||||
|
#taskUpUltraLiteItemRoles: string[];
|
||||||
|
#settings: Gio.Settings;
|
||||||
|
|
||||||
constructor(params = {}, settings) {
|
constructor(params = {}, settings: Gio.Settings) {
|
||||||
|
// @ts-ignore Params should be passed, see: https://gjs.guide/guides/gobject/subclassing.html#subclassing-gobject
|
||||||
super(params);
|
super(params);
|
||||||
|
|
||||||
this.#appIndicatorReadyHandlerIdMap = new Map();
|
this.#appIndicatorReadyHandlerIdMap = new Map();
|
||||||
@ -47,30 +56,22 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a box order for the given top bar box from 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.
|
* @param {Box} 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
|
* @returns {string[]} - The box order consisting of an array of item
|
||||||
* settings identifiers.
|
* settings identifiers.
|
||||||
*/
|
*/
|
||||||
#getBoxOrder(box) {
|
#getBoxOrder(box: Box): string[] {
|
||||||
return this.#settings.get_strv(`${box}-box-order`);
|
return this.#settings.get_strv(`${box}-box-order`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the given box order to settings, making sure to only save a changed
|
* Save the given box order to settings, making sure to only save a changed
|
||||||
* box order, to avoid loops when listening on settings changes.
|
* box order, to avoid loops when listening on settings changes.
|
||||||
* @param {string} box - The top bar box for which to save the box order.
|
* @param {Box} 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
|
* @param {string[]} boxOrder - The box order to save. Must be an array of
|
||||||
* item settings identifiers.
|
* item settings identifiers.
|
||||||
*/
|
*/
|
||||||
#saveBoxOrder(box, boxOrder) {
|
#saveBoxOrder(box: Box, boxOrder: string[]): void {
|
||||||
const currentBoxOrder = this.#getBoxOrder(box);
|
const currentBoxOrder = this.#getBoxOrder(box);
|
||||||
|
|
||||||
// Only save the given box order to settings, if it is different, to
|
// Only save the given box order to settings, if it is different, to
|
||||||
@ -90,14 +91,18 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
* then also makes sure that once the app indicators "ready" signal emits,
|
* then also makes sure that once the app indicators "ready" signal emits,
|
||||||
* this classes "appIndicatorReady" signal emits as well, such that it and
|
* this classes "appIndicatorReady" signal emits as well, such that it and
|
||||||
* other methods can be called again to properly handle the item.
|
* other methods can be called again to properly handle the item.
|
||||||
* @param {string} indicatorContainer - The container of the indicator of the
|
* @param {St.Bin} 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 derived items settings identifier.
|
||||||
*/
|
*/
|
||||||
#handleAppIndicatorItem(indicatorContainer, role) {
|
#handleAppIndicatorItem(indicatorContainer: St.Bin, role: string): string {
|
||||||
const appIndicator = indicatorContainer.get_child()._indicator;
|
// Since this is working with types from the
|
||||||
|
// AppIndicator/KStatusNotifierItem extension, we loose a bunch of type
|
||||||
|
// safety here.
|
||||||
|
// https://github.com/ubuntu/gnome-shell-extension-appindicator
|
||||||
|
const appIndicator = (indicatorContainer.get_child() as any)._indicator;
|
||||||
let application = appIndicator.id;
|
let application = appIndicator.id;
|
||||||
|
|
||||||
if (!application && this.#appIndicatorReadyHandlerIdMap) {
|
if (!application && this.#appIndicatorReadyHandlerIdMap) {
|
||||||
@ -146,7 +151,7 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
* @param {string} role - The role of the Task Up UltraLite item.
|
* @param {string} role - The role of the Task Up UltraLite item.
|
||||||
* @returns {string} The settings identifier to use.
|
* @returns {string} The settings identifier to use.
|
||||||
*/
|
*/
|
||||||
#handleTaskUpUltraLiteItem(role) {
|
#handleTaskUpUltraLiteItem(role: string): string {
|
||||||
const roles = this.#taskUpUltraLiteItemRoles;
|
const roles = this.#taskUpUltraLiteItemRoles;
|
||||||
|
|
||||||
if (!roles.includes(role)) {
|
if (!roles.includes(role)) {
|
||||||
@ -162,14 +167,10 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
* meaning they might be present multiple times or not at all depending on
|
* meaning they might be present multiple times or not at all depending on
|
||||||
* the roles stored.
|
* the roles stored.
|
||||||
* The items of the box order also have additional information stored.
|
* The items of the box order also have additional information stored.
|
||||||
* @param {string} box - The top bar box for which to get the resolved box order.
|
* @param {Box} 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.
|
* @returns {ResolvedBoxOrderItem[]} - The resolved box order.
|
||||||
*/
|
*/
|
||||||
#getResolvedBoxOrder(box) {
|
#getResolvedBoxOrder(box: Box): ResolvedBoxOrderItem[] {
|
||||||
let boxOrder = this.#getBoxOrder(box);
|
let boxOrder = this.#getBoxOrder(box);
|
||||||
|
|
||||||
const itemsToHide = this.#settings.get_strv("hide");
|
const itemsToHide = this.#settings.get_strv("hide");
|
||||||
@ -207,15 +208,15 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
// the item specially.
|
// the item specially.
|
||||||
|
|
||||||
// Get the roles associated with the items settings id.
|
// Get the roles associated with the items settings id.
|
||||||
let roles = [];
|
let roles: string[] = [];
|
||||||
if (itemSettingsId.startsWith("appindicator-kstatusnotifieritem-")) {
|
if (itemSettingsId.startsWith("appindicator-kstatusnotifieritem-")) {
|
||||||
roles = this.#appIndicatorItemSettingsIdToRolesMap.get(resolvedBoxOrderItem.settingsId);
|
roles = this.#appIndicatorItemSettingsIdToRolesMap.get(resolvedBoxOrderItem.settingsId) ?? [];
|
||||||
} else if (itemSettingsId === "item-role-group-task-up-ultralite") {
|
} else if (itemSettingsId === "item-role-group-task-up-ultralite") {
|
||||||
roles = this.#taskUpUltraLiteItemRoles;
|
roles = this.#taskUpUltraLiteItemRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are no roles associated, continue.
|
// If there are no roles associated, continue.
|
||||||
if (!roles) {
|
if (roles.length === 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,12 +237,13 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
* This is typically used before nulling an instance of this class to make
|
* This is typically used before nulling an instance of this class to make
|
||||||
* sure all signals are disconnected.
|
* sure all signals are disconnected.
|
||||||
*/
|
*/
|
||||||
disconnectSignals() {
|
disconnectSignals(): void {
|
||||||
for (const [handlerId, appIndicator] of this.#appIndicatorReadyHandlerIdMap) {
|
for (const [handlerId, appIndicator] of this.#appIndicatorReadyHandlerIdMap) {
|
||||||
if (handlerId && appIndicator?.signalHandlerIsConnected(handlerId)) {
|
if (handlerId && appIndicator?.signalHandlerIsConnected(handlerId)) {
|
||||||
appIndicator.disconnect(handlerId);
|
appIndicator.disconnect(handlerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// @ts-ignore
|
||||||
this.#appIndicatorReadyHandlerIdMap = null;
|
this.#appIndicatorReadyHandlerIdMap = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,25 +252,23 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
* and Task Up UltraLite items got resolved and where only items are
|
* and Task Up UltraLite items got resolved and where only items are
|
||||||
* included, which are in some GNOME Shell top bar box.
|
* included, which are in some GNOME Shell top bar box.
|
||||||
* The items of the box order also have additional information stored.
|
* The items of the box order also have additional information stored.
|
||||||
* @param {string} box - The top bar box to return the valid box order for.
|
* @param {Box} 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 {ResolvedBoxOrderItem[]} - The valid box order.
|
||||||
*/
|
*/
|
||||||
getValidBoxOrder(box) {
|
getValidBoxOrder(box: Box): ResolvedBoxOrderItem[] {
|
||||||
// Get a resolved box order.
|
// Get a resolved box order.
|
||||||
let resolvedBoxOrder = this.#getResolvedBoxOrder(box);
|
let resolvedBoxOrder = this.#getResolvedBoxOrder(box);
|
||||||
|
|
||||||
// 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.
|
||||||
|
// They should be St.Bins (see link), so ensure that using a filter.
|
||||||
|
// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/48.2/js/ui/panelMenu.js?ref_type=tags#L21
|
||||||
const indicatorContainers = [
|
const indicatorContainers = [
|
||||||
Main.panel._leftBox.get_children(),
|
(Main.panel as CustomPanel)._leftBox.get_children(),
|
||||||
Main.panel._centerBox.get_children(),
|
(Main.panel as CustomPanel)._centerBox.get_children(),
|
||||||
Main.panel._rightBox.get_children(),
|
(Main.panel as CustomPanel)._rightBox.get_children(),
|
||||||
].flat();
|
].flat().filter(ic => ic instanceof St.Bin);
|
||||||
|
|
||||||
// Create an indicator containers set from the indicator containers for
|
// Create an indicator containers set from the indicator containers for
|
||||||
// fast easy access.
|
// fast easy access.
|
||||||
@ -277,10 +277,13 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
// Go through the resolved box order and only add items to the valid box
|
// 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
|
// order, where their indicator is currently present in the GNOME Shell
|
||||||
// top bar.
|
// top bar.
|
||||||
let validBoxOrder = [];
|
let validBoxOrder: ResolvedBoxOrderItem[] = [];
|
||||||
for (const item of resolvedBoxOrder) {
|
for (const item of resolvedBoxOrder) {
|
||||||
// Get the indicator container associated with the items role.
|
const associatedIndicatorContainer = (Main.panel.statusArea as any)[item.role]?.container;
|
||||||
const associatedIndicatorContainer = Main.panel.statusArea[item.role]?.container;
|
if (!(associatedIndicatorContainer instanceof St.Bin)) {
|
||||||
|
// TODO: maybe add logging
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (indicatorContainerSet.has(associatedIndicatorContainer)) {
|
if (indicatorContainerSet.has(associatedIndicatorContainer)) {
|
||||||
validBoxOrder.push(item);
|
validBoxOrder.push(item);
|
||||||
@ -294,7 +297,7 @@ 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 settings.
|
||||||
*/
|
*/
|
||||||
saveNewTopBarItems() {
|
saveNewTopBarItems(): void {
|
||||||
// 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
|
||||||
// is "user".
|
// is "user".
|
||||||
if (Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
|
if (Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") {
|
||||||
@ -310,24 +313,31 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
|
|
||||||
// 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<St.Bin, string>();
|
||||||
for (const role in Main.panel.statusArea) {
|
for (const role in (Main.panel.statusArea as any)) {
|
||||||
indicatorContainerRoleMap.set(Main.panel.statusArea[role].container, role);
|
const associatedIndicatorContainer = (Main.panel.statusArea as any)[role]?.container;
|
||||||
|
if (!(associatedIndicatorContainer instanceof St.Bin)) {
|
||||||
|
// TODO: maybe add logging
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
indicatorContainerRoleMap.set(associatedIndicatorContainer, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
|
// They should be St.Bins (see link), so ensure that using a filter.
|
||||||
|
// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/48.2/js/ui/panelMenu.js?ref_type=tags#L21
|
||||||
const boxIndicatorContainers = {
|
const boxIndicatorContainers = {
|
||||||
left: Main.panel._leftBox.get_children(),
|
left: (Main.panel as CustomPanel)._leftBox.get_children().filter(ic => ic instanceof St.Bin),
|
||||||
center: Main.panel._centerBox.get_children(),
|
center: (Main.panel as CustomPanel)._centerBox.get_children().filter(ic => ic instanceof St.Bin),
|
||||||
// Reverse this array, since the items in the left and center box
|
// Reverse this array, since the items in the left and center box
|
||||||
// are logically LTR, while the items in the right box are RTL.
|
// are logically LTR, while the items in the right box are RTL.
|
||||||
right: Main.panel._rightBox.get_children().reverse(),
|
right: (Main.panel as CustomPanel)._rightBox.get_children().filter(ic => ic instanceof St.Bin).reverse(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 new item settings identifiers to the given box order.
|
||||||
const addNewItemSettingsIdsToBoxOrder = (indicatorContainers, boxOrder, box) => {
|
const addNewItemSettingsIdsToBoxOrder = (indicatorContainers: St.Bin[], boxOrder: string[], box: 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.
|
||||||
@ -345,6 +355,9 @@ export default class BoxOrderManager extends GObject.Object {
|
|||||||
try {
|
try {
|
||||||
itemSettingsId = this.#handleAppIndicatorItem(indicatorContainer, role);
|
itemSettingsId = this.#handleAppIndicatorItem(indicatorContainer, role);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (!(e instanceof Error)) {
|
||||||
|
throw(e);
|
||||||
|
}
|
||||||
if (e.message !== "Application can't be determined.") {
|
if (e.message !== "Application can't be determined.") {
|
||||||
throw(e);
|
throw(e);
|
||||||
}
|
}
|
||||||
@ -13,7 +13,7 @@ export default class TopBarOrganizerPreferences extends ExtensionPreferences {
|
|||||||
provider.load_from_path(this.metadata.dir.get_path() + "/css/prefs.css");
|
provider.load_from_path(this.metadata.dir.get_path() + "/css/prefs.css");
|
||||||
const defaultGdkDisplay = Gdk.Display.get_default();
|
const defaultGdkDisplay = Gdk.Display.get_default();
|
||||||
Gtk.StyleContext.add_provider_for_display(
|
Gtk.StyleContext.add_provider_for_display(
|
||||||
defaultGdkDisplay,
|
(defaultGdkDisplay as Gdk.Display),
|
||||||
provider,
|
provider,
|
||||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||||
);
|
);
|
||||||
@ -22,7 +22,7 @@ export default class TopBarOrganizerPreferences extends ExtensionPreferences {
|
|||||||
|
|
||||||
prefsPage.connect("destroy", () => {
|
prefsPage.connect("destroy", () => {
|
||||||
Gtk.StyleContext.remove_provider_for_display(
|
Gtk.StyleContext.remove_provider_for_display(
|
||||||
defaultGdkDisplay,
|
(defaultGdkDisplay as Gdk.Display),
|
||||||
provider
|
provider
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -6,6 +6,8 @@ import GObject from "gi://GObject";
|
|||||||
import Adw from "gi://Adw";
|
import Adw from "gi://Adw";
|
||||||
import GLib from "gi://GLib";
|
import GLib from "gi://GLib";
|
||||||
|
|
||||||
|
import type PrefsBoxOrderListBox from "./PrefsBoxOrderListBox.js";
|
||||||
|
|
||||||
export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
|
export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
|
||||||
static {
|
static {
|
||||||
GObject.registerClass({
|
GObject.registerClass({
|
||||||
@ -18,8 +20,8 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
|
|||||||
},
|
},
|
||||||
}, this);
|
}, this);
|
||||||
this.install_action("row.forget", null, (self, _actionName, _param) => {
|
this.install_action("row.forget", null, (self, _actionName, _param) => {
|
||||||
const parentListBox = self.get_parent();
|
const parentListBox = self.get_parent() as PrefsBoxOrderListBox;
|
||||||
parentListBox.removeRow(self);
|
parentListBox.removeRow(self as PrefsBoxOrderItemRow);
|
||||||
parentListBox.saveBoxOrderToSettings();
|
parentListBox.saveBoxOrderToSettings();
|
||||||
parentListBox.determineRowMoveActionEnable();
|
parentListBox.determineRowMoveActionEnable();
|
||||||
});
|
});
|
||||||
@ -27,39 +29,32 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
|
|||||||
this.install_action("row.move-down", null, (self, _actionName, _param) => self.emit("move", "down"));
|
this.install_action("row.move-down", null, (self, _actionName, _param) => self.emit("move", "down"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#drag_starting_point_x;
|
item: string;
|
||||||
#drag_starting_point_y;
|
#drag_starting_point_x?: number;
|
||||||
|
#drag_starting_point_y?: number;
|
||||||
|
|
||||||
constructor(params = {}, item) {
|
constructor(params = {}, item: string) {
|
||||||
super(params);
|
super(params);
|
||||||
|
|
||||||
this.#associateItem(item);
|
// Associate `this` with an item.
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Associate `this` with an item.
|
|
||||||
* @param {String} item
|
|
||||||
*/
|
|
||||||
#associateItem(item) {
|
|
||||||
this.item = item;
|
this.item = item;
|
||||||
|
if (this.item.startsWith("appindicator-kstatusnotifieritem-")) {
|
||||||
if (item.startsWith("appindicator-kstatusnotifieritem-")) {
|
|
||||||
// Set the title to something nicer, if the associated item is an
|
// Set the title to something nicer, if the associated item is an
|
||||||
// AppIndicator/KStatusNotifierItem item.
|
// AppIndicator/KStatusNotifierItem item.
|
||||||
this.set_title(item.replace("appindicator-kstatusnotifieritem-", ""));
|
this.set_title(this.item.replace("appindicator-kstatusnotifieritem-", ""));
|
||||||
} else if (item === "item-role-group-task-up-ultralite") {
|
} else if (this.item === "item-role-group-task-up-ultralite") {
|
||||||
// Set the title to something nicer, if the item in question is the
|
// Set the title to something nicer, if the item in question is the
|
||||||
// Task Up UltraLite item role group.
|
// Task Up UltraLite item role group.
|
||||||
this.set_title("Task Up UltraLite Items");
|
this.set_title("Task Up UltraLite Items");
|
||||||
} else {
|
} else {
|
||||||
// Otherwise just set it to `item`.
|
// Otherwise just set it to `item`.
|
||||||
this.set_title(item);
|
this.set_title(this.item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDragPrepare(_source, x, y) {
|
onDragPrepare(_source: Gtk.DragSource, x: number, y: number): Gdk.ContentProvider {
|
||||||
const value = new GObject.Value();
|
const value = new GObject.Value();
|
||||||
value.init(PrefsBoxOrderItemRow);
|
value.init(PrefsBoxOrderItemRow.$gtype);
|
||||||
value.set_object(this);
|
value.set_object(this);
|
||||||
|
|
||||||
this.#drag_starting_point_x = x;
|
this.#drag_starting_point_x = x;
|
||||||
@ -67,7 +62,7 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
|
|||||||
return Gdk.ContentProvider.new_for_value(value);
|
return Gdk.ContentProvider.new_for_value(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDragBegin(_source, drag) {
|
onDragBegin(_source: Gtk.DragSource, drag: Gdk.Drag): void {
|
||||||
let dragWidget = new Gtk.ListBox();
|
let dragWidget = new Gtk.ListBox();
|
||||||
let allocation = this.get_allocation();
|
let allocation = this.get_allocation();
|
||||||
dragWidget.set_size_request(allocation.width, allocation.height);
|
dragWidget.set_size_request(allocation.width, allocation.height);
|
||||||
@ -78,20 +73,32 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
|
|||||||
|
|
||||||
let currentDragIcon = Gtk.DragIcon.get_for_drag(drag);
|
let currentDragIcon = Gtk.DragIcon.get_for_drag(drag);
|
||||||
currentDragIcon.set_child(dragWidget);
|
currentDragIcon.set_child(dragWidget);
|
||||||
drag.set_hotspot(this.#drag_starting_point_x, this.#drag_starting_point_y);
|
// Even tho this should always be the case, ensure the values for the hotspot aren't undefined.
|
||||||
|
if (typeof this.#drag_starting_point_x !== "undefined" &&
|
||||||
|
typeof this.#drag_starting_point_y !== "undefined") {
|
||||||
|
drag.set_hotspot(this.#drag_starting_point_x, this.#drag_starting_point_y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle a new drop on `this` properly.
|
// Handle a new drop on `this` properly.
|
||||||
// `value` is the thing getting dropped.
|
// `value` is the thing getting dropped.
|
||||||
onDrop(_target, value, _x, _y) {
|
onDrop(_target: Gtk.DropTarget, value: any, _x: number, _y: number): boolean {
|
||||||
|
// According to the type annotations of Gtk.DropTarget, value is of type
|
||||||
|
// GObject.Value, so ensure the one we work with is of type
|
||||||
|
// PrefsBoxOrderItemRow.
|
||||||
|
if (!(value instanceof PrefsBoxOrderItemRow)) {
|
||||||
|
// TODO: maybe add logging
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If `this` got dropped onto itself, do nothing.
|
// If `this` got dropped onto itself, do nothing.
|
||||||
if (value === this) {
|
if (value === this) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the GtkListBoxes of `this` and the drop value.
|
// Get the GtkListBoxes of `this` and the drop value.
|
||||||
const ownListBox = this.get_parent();
|
const ownListBox = this.get_parent() as PrefsBoxOrderListBox;
|
||||||
const valueListBox = value.get_parent();
|
const valueListBox = value.get_parent() as PrefsBoxOrderListBox;
|
||||||
|
|
||||||
// Get the position of `this` and the drop value.
|
// Get the position of `this` and the drop value.
|
||||||
const ownPosition = this.get_index();
|
const ownPosition = this.get_index();
|
||||||
@ -3,6 +3,7 @@
|
|||||||
import Gtk from "gi://Gtk";
|
import Gtk from "gi://Gtk";
|
||||||
import GObject from "gi://GObject";
|
import GObject from "gi://GObject";
|
||||||
import GLib from "gi://GLib";
|
import GLib from "gi://GLib";
|
||||||
|
import type Gio from "gi://Gio";
|
||||||
|
|
||||||
import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
|
import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
|
||||||
|
|
||||||
@ -25,14 +26,15 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
|
|||||||
},
|
},
|
||||||
Signals: {
|
Signals: {
|
||||||
"row-move": {
|
"row-move": {
|
||||||
param_types: [PrefsBoxOrderItemRow, GObject.TYPE_STRING],
|
param_types: [PrefsBoxOrderItemRow.$gtype, GObject.TYPE_STRING],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#settings;
|
_boxOrder!: string;
|
||||||
#rowSignalHandlerIds = new Map();
|
#settings: Gio.Settings;
|
||||||
|
#rowSignalHandlerIds = new Map<PrefsBoxOrderItemRow, number[]>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
@ -41,18 +43,18 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
|
|||||||
super(params);
|
super(params);
|
||||||
|
|
||||||
// Load the settings.
|
// Load the settings.
|
||||||
this.#settings = ExtensionPreferences.lookupByURL(import.meta.url).getSettings();
|
this.#settings = ExtensionPreferences.lookupByURL(import.meta.url)!.getSettings();
|
||||||
|
|
||||||
// Add a placeholder widget for the case, where no GtkListBoxRows are
|
// Add a placeholder widget for the case, where no GtkListBoxRows are
|
||||||
// present.
|
// present.
|
||||||
this.set_placeholder(new PrefsBoxOrderListEmptyPlaceholder());
|
this.set_placeholder(new PrefsBoxOrderListEmptyPlaceholder());
|
||||||
}
|
}
|
||||||
|
|
||||||
get boxOrder() {
|
get boxOrder(): string {
|
||||||
return this._boxOrder;
|
return this._boxOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
set boxOrder(value) {
|
set boxOrder(value: string) {
|
||||||
this._boxOrder = value;
|
this._boxOrder = value;
|
||||||
|
|
||||||
// Get the actual box order for the given box order name from settings.
|
// Get the actual box order for the given box order name from settings.
|
||||||
@ -73,10 +75,10 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
|
|||||||
* position.
|
* position.
|
||||||
* Also handles stuff like connecting signals.
|
* Also handles stuff like connecting signals.
|
||||||
*/
|
*/
|
||||||
insertRow(row, position) {
|
insertRow(row: PrefsBoxOrderItemRow, position: number): void {
|
||||||
this.insert(row, position);
|
this.insert(row, position);
|
||||||
|
|
||||||
const signalHandlerIds = [];
|
const signalHandlerIds: number[] = [];
|
||||||
signalHandlerIds.push(row.connect("move", (row, direction) => {
|
signalHandlerIds.push(row.connect("move", (row, direction) => {
|
||||||
this.emit("row-move", row, direction);
|
this.emit("row-move", row, direction);
|
||||||
}));
|
}));
|
||||||
@ -88,8 +90,8 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
|
|||||||
* Removes the given PrefsBoxOrderItemRow from this list box.
|
* Removes the given PrefsBoxOrderItemRow from this list box.
|
||||||
* Also handles stuff like disconnecting signals.
|
* Also handles stuff like disconnecting signals.
|
||||||
*/
|
*/
|
||||||
removeRow(row) {
|
removeRow(row: PrefsBoxOrderItemRow): void {
|
||||||
const signalHandlerIds = this.#rowSignalHandlerIds.get(row);
|
const signalHandlerIds = this.#rowSignalHandlerIds.get(row) ?? [];
|
||||||
|
|
||||||
for (const id of signalHandlerIds) {
|
for (const id of signalHandlerIds) {
|
||||||
row.disconnect(id);
|
row.disconnect(id);
|
||||||
@ -102,11 +104,11 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
|
|||||||
* Saves the box order represented by `this` (and its
|
* Saves the box order represented by `this` (and its
|
||||||
* `PrefsBoxOrderItemRows`) to settings.
|
* `PrefsBoxOrderItemRows`) to settings.
|
||||||
*/
|
*/
|
||||||
saveBoxOrderToSettings() {
|
saveBoxOrderToSettings(): void {
|
||||||
let currentBoxOrder = [];
|
let currentBoxOrder: string[] = [];
|
||||||
for (let potentialPrefsBoxOrderItemRow of this) {
|
for (let potentialPrefsBoxOrderItemRow of this) {
|
||||||
// Only process PrefsBoxOrderItemRows.
|
// Only process PrefsBoxOrderItemRows.
|
||||||
if (potentialPrefsBoxOrderItemRow.constructor.$gtype.name !== "PrefsBoxOrderItemRow") {
|
if (!(potentialPrefsBoxOrderItemRow instanceof PrefsBoxOrderItemRow)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,10 +122,10 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
|
|||||||
* Determines whether or not each move action of each PrefsBoxOrderItemRow
|
* Determines whether or not each move action of each PrefsBoxOrderItemRow
|
||||||
* should be enabled or disabled.
|
* should be enabled or disabled.
|
||||||
*/
|
*/
|
||||||
determineRowMoveActionEnable() {
|
determineRowMoveActionEnable(): void {
|
||||||
for (let potentialPrefsBoxOrderItemRow of this) {
|
for (let potentialPrefsBoxOrderItemRow of this) {
|
||||||
// Only process PrefsBoxOrderItemRows.
|
// Only process PrefsBoxOrderItemRows.
|
||||||
if (potentialPrefsBoxOrderItemRow.constructor.$gtype.name !== "PrefsBoxOrderItemRow") {
|
if (!(potentialPrefsBoxOrderItemRow instanceof PrefsBoxOrderItemRow)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4,6 +4,9 @@ import Gtk from "gi://Gtk";
|
|||||||
import GObject from "gi://GObject";
|
import GObject from "gi://GObject";
|
||||||
import GLib from "gi://GLib";
|
import GLib from "gi://GLib";
|
||||||
|
|
||||||
|
import PrefsBoxOrderItemRow from "./PrefsBoxOrderItemRow.js";
|
||||||
|
import type PrefsBoxOrderListBox from "./PrefsBoxOrderListBox.js";
|
||||||
|
|
||||||
export default class PrefsBoxOrderListEmptyPlaceholder extends Gtk.Box {
|
export default class PrefsBoxOrderListEmptyPlaceholder extends Gtk.Box {
|
||||||
static {
|
static {
|
||||||
GObject.registerClass({
|
GObject.registerClass({
|
||||||
@ -14,10 +17,18 @@ export default class PrefsBoxOrderListEmptyPlaceholder extends Gtk.Box {
|
|||||||
|
|
||||||
// Handle a new drop on `this` properly.
|
// Handle a new drop on `this` properly.
|
||||||
// `value` is the thing getting dropped.
|
// `value` is the thing getting dropped.
|
||||||
onDrop(_target, value, _x, _y) {
|
onDrop(_target: Gtk.DropTarget, value: any, _x: number, _y: number): boolean {
|
||||||
|
// According to the type annotations of Gtk.DropTarget, value is of type
|
||||||
|
// GObject.Value, so ensure the one we work with is of type
|
||||||
|
// PrefsBoxOrderItemRow.
|
||||||
|
if (!(value instanceof PrefsBoxOrderItemRow)) {
|
||||||
|
// TODO: maybe add logging
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the GtkListBoxes of `this` and the drop value.
|
// Get the GtkListBoxes of `this` and the drop value.
|
||||||
const ownListBox = this.get_parent();
|
const ownListBox = this.get_parent() as PrefsBoxOrderListBox;
|
||||||
const valueListBox = value.get_parent();
|
const valueListBox = value.get_parent() as PrefsBoxOrderListBox;
|
||||||
|
|
||||||
// Remove the drop value from its list box.
|
// Remove the drop value from its list box.
|
||||||
valueListBox.removeRow(value);
|
valueListBox.removeRow(value);
|
||||||
@ -1,5 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import Gdk from "gi://Gdk";
|
||||||
import Gtk from "gi://Gtk";
|
import Gtk from "gi://Gtk";
|
||||||
import GObject from "gi://GObject";
|
import GObject from "gi://GObject";
|
||||||
import Adw from "gi://Adw";
|
import Adw from "gi://Adw";
|
||||||
@ -7,6 +8,7 @@ import GLib from "gi://GLib";
|
|||||||
|
|
||||||
import ScrollManager from "./ScrollManager.js";
|
import ScrollManager from "./ScrollManager.js";
|
||||||
import PrefsBoxOrderListEmptyPlaceholder from "./PrefsBoxOrderListEmptyPlaceholder.js";
|
import PrefsBoxOrderListEmptyPlaceholder from "./PrefsBoxOrderListEmptyPlaceholder.js";
|
||||||
|
import type PrefsBoxOrderItemRow from "./PrefsBoxOrderItemRow.js";
|
||||||
|
|
||||||
// Imports to make UI file work.
|
// Imports to make UI file work.
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
@ -25,6 +27,11 @@ export default class PrefsPage extends Adw.PreferencesPage {
|
|||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_dndEnded?: boolean;
|
||||||
|
_left_box_order_list_box!: PrefsBoxOrderListBox;
|
||||||
|
_center_box_order_list_box!: PrefsBoxOrderListBox;
|
||||||
|
_right_box_order_list_box!: PrefsBoxOrderListBox;
|
||||||
|
|
||||||
constructor(params = {}) {
|
constructor(params = {}) {
|
||||||
super(params);
|
super(params);
|
||||||
|
|
||||||
@ -37,23 +44,27 @@ export default class PrefsPage extends Adw.PreferencesPage {
|
|||||||
* operation is in progress and the user has their cursor either in the
|
* operation is in progress and the user has their cursor either in the
|
||||||
* upper or lower 10% of this widget respectively.
|
* upper or lower 10% of this widget respectively.
|
||||||
*/
|
*/
|
||||||
#setupDNDScroll() {
|
#setupDNDScroll(): void {
|
||||||
// Pass `this.get_first_child()` to the ScrollManager, since this
|
// Pass `this.get_first_child()` to the ScrollManager, since this
|
||||||
// `PrefsPage` extends an `Adw.PreferencesPage` and the first child of
|
// `PrefsPage` extends an `Adw.PreferencesPage` and the first child of
|
||||||
// an `Adw.PreferencesPage` is the built-in `Gtk.ScrolledWindow`.
|
// an `Adw.PreferencesPage` is the built-in `Gtk.ScrolledWindow`.
|
||||||
const scrollManager = new ScrollManager(this.get_first_child());
|
const scrollManager = new ScrollManager(this.get_first_child() as Gtk.ScrolledWindow);
|
||||||
|
|
||||||
/// Setup GtkDropControllerMotion event controller and make use of its
|
/// Setup GtkDropControllerMotion event controller and make use of its
|
||||||
/// events.
|
/// events.
|
||||||
let controller = new Gtk.DropControllerMotion();
|
let controller = new Gtk.DropControllerMotion();
|
||||||
|
|
||||||
// Scroll, when the pointer is in the right places.
|
// Make sure scrolling stops, when DND operation ends.
|
||||||
|
this._dndEnded = true;
|
||||||
|
|
||||||
|
// Scroll, when the pointer is in the right places and a DND operation
|
||||||
|
// is properly set up (this._dndEnded is false).
|
||||||
controller.connect("motion", (_, _x, y) => {
|
controller.connect("motion", (_, _x, y) => {
|
||||||
if (y <= this.get_allocated_height() * 0.1) {
|
if ((y <= this.get_allocated_height() * 0.1) && !this._dndEnded) {
|
||||||
// If the pointer is currently in the upper ten percent of this
|
// If the pointer is currently in the upper ten percent of this
|
||||||
// widget, then scroll up.
|
// widget, then scroll up.
|
||||||
scrollManager.startScrollUp();
|
scrollManager.startScrollUp();
|
||||||
} else if (y >= this.get_allocated_height() * 0.9) {
|
} else if ((y >= this.get_allocated_height() * 0.9) && !this._dndEnded) {
|
||||||
// If the pointer is currently in the lower ten percent of this
|
// If the pointer is currently in the lower ten percent of this
|
||||||
// widget, then scroll down.
|
// widget, then scroll down.
|
||||||
scrollManager.startScrollDown();
|
scrollManager.startScrollDown();
|
||||||
@ -63,11 +74,9 @@ export default class PrefsPage extends Adw.PreferencesPage {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure scrolling stops, when DND operation ends.
|
|
||||||
this._dndEnded = true;
|
|
||||||
const stopScrollAllAtDNDEnd = () => {
|
const stopScrollAllAtDNDEnd = () => {
|
||||||
scrollManager.stopScrollAll();
|
|
||||||
this._dndEnded = true;
|
this._dndEnded = true;
|
||||||
|
scrollManager.stopScrollAll();
|
||||||
};
|
};
|
||||||
controller.connect("leave", () => {
|
controller.connect("leave", () => {
|
||||||
stopScrollAllAtDNDEnd();
|
stopScrollAllAtDNDEnd();
|
||||||
@ -76,7 +85,14 @@ export default class PrefsPage extends Adw.PreferencesPage {
|
|||||||
// Make use of `this._dndEnded` to setup stopScrollAtDNDEnd only
|
// Make use of `this._dndEnded` to setup stopScrollAtDNDEnd only
|
||||||
// once per DND operation.
|
// once per DND operation.
|
||||||
if (this._dndEnded) {
|
if (this._dndEnded) {
|
||||||
let drag = controller.get_drop().get_drag();
|
const drag = controller.get_drop()?.get_drag() ?? null;
|
||||||
|
// Ensure we have a Gdk.Drag.
|
||||||
|
// If this is not the case for whatever reason, then don't start
|
||||||
|
// DND scrolling and just return.
|
||||||
|
if (!(drag instanceof Gdk.Drag)) {
|
||||||
|
// TODO: maybe add logging
|
||||||
|
return;
|
||||||
|
}
|
||||||
drag.connect("drop-performed", () => {
|
drag.connect("drop-performed", () => {
|
||||||
stopScrollAllAtDNDEnd();
|
stopScrollAllAtDNDEnd();
|
||||||
});
|
});
|
||||||
@ -93,7 +109,7 @@ export default class PrefsPage extends Adw.PreferencesPage {
|
|||||||
this.add_controller(controller);
|
this.add_controller(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRowMove(listBox, row, direction) {
|
onRowMove(listBox: PrefsBoxOrderListBox, row: PrefsBoxOrderItemRow, direction: string): void {
|
||||||
const rowPosition = row.get_index();
|
const rowPosition = row.get_index();
|
||||||
|
|
||||||
if (direction === "up") { // If the direction of the move is up.
|
if (direction === "up") { // If the direction of the move is up.
|
||||||
@ -1,23 +1,21 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import GLib from "gi://GLib";
|
import GLib from "gi://GLib";
|
||||||
|
import type Gtk from "gi://Gtk";
|
||||||
|
|
||||||
export default class ScrollManager {
|
export default class ScrollManager {
|
||||||
#gtkScrolledWindow;
|
#gtkScrolledWindow: Gtk.ScrolledWindow;
|
||||||
#scrollUp;
|
#scrollUp: boolean;
|
||||||
#scrollDown;
|
#scrollDown: boolean;
|
||||||
|
|
||||||
/**
|
constructor(gtkScrolledWindow: Gtk.ScrolledWindow) {
|
||||||
* @param {Gtk.ScrolledWindow} gtkScrolledWindow
|
|
||||||
*/
|
|
||||||
constructor(gtkScrolledWindow) {
|
|
||||||
this.#gtkScrolledWindow = gtkScrolledWindow;
|
this.#gtkScrolledWindow = gtkScrolledWindow;
|
||||||
|
|
||||||
this.#scrollUp = false;
|
this.#scrollUp = false;
|
||||||
this.#scrollDown = false;
|
this.#scrollDown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
startScrollUp() {
|
startScrollUp(): void {
|
||||||
// If the scroll up is already started, don't do anything.
|
// If the scroll up is already started, don't do anything.
|
||||||
if (this.#scrollUp) {
|
if (this.#scrollUp) {
|
||||||
return;
|
return;
|
||||||
@ -44,7 +42,7 @@ export default class ScrollManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
startScrollDown() {
|
startScrollDown(): void {
|
||||||
// If the scroll down is already started, don't do anything.
|
// If the scroll down is already started, don't do anything.
|
||||||
if (this.#scrollDown) {
|
if (this.#scrollDown) {
|
||||||
return;
|
return;
|
||||||
@ -74,15 +72,15 @@ export default class ScrollManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stopScrollUp() {
|
stopScrollUp(): void {
|
||||||
this.#scrollUp = false;
|
this.#scrollUp = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stopScrollDown() {
|
stopScrollDown(): void {
|
||||||
this.#scrollDown = false;
|
this.#scrollDown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stopScrollAll() {
|
stopScrollAll(): void {
|
||||||
this.stopScrollUp();
|
this.stopScrollUp();
|
||||||
this.stopScrollDown();
|
this.stopScrollDown();
|
||||||
}
|
}
|
||||||
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"sourceMap": false,
|
||||||
|
"strict": true,
|
||||||
|
// To preserve imports for UI files.
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"target": "es2022",
|
||||||
|
"lib": [
|
||||||
|
"ES2022"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"ambient.d.ts",
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"src/extension.ts",
|
||||||
|
"src/prefs.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user