Compare commits

..

No commits in common. "main" and "preferences_refactor" have entirely different histories.

8 changed files with 14 additions and 496 deletions

View file

@ -1,49 +0,0 @@
### Bug Description
<!--
Description of the issue you're experiencing.
-->
### Steps to Reproduce
<!--
Steps to reproduce the issue.
1. Open ...
2. Click on ...
...
-->
### What behavior did you observe?
<!--
What actually happened that was unexpected?
-->
### What behavior did you expect?
<!--
What did you expect to happen instead?
-->
### Further Information
<!--
If applicable: Screenshots and/or screencasts, which show the issue.
-->
<!--
If applicable: Logs, which show relevant information like error messages.
- For core extension issues, open the terminal and run: journalctl -f, then reproduce the issue and paste the relevant messages here in a code block (```).
- For extension preferences issues, open the terminal and run: journalctl -f -o cat /usr/bin/gjs, then reproduce the issue and paste the relevant messages here in a code block (```).
-->
### System Information
- Operating System: <!-- What operating system are you using? -->
- GNOME Version: <!-- What GNOME version are you running? Open a terminal and run: gnome-shell --version, then paste the output here. -->
Enabled Extensions:
<!--
Which extensions do you have enabled? Open a terminal and run: gnome-extensions list --enabled --details, then paste the output here in a code block (```).
-->

View file

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="PrefsBoxOrderItemOptionsDialog" parent="AdwDialog">
<!-- Same as the default-width of AdwPreferencesWindow.-->
<property name="content-width">640</property>
<child>
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar"></object>
</child>
<property name="content">
<object class="AdwPreferencesPage">
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwComboRow" id="visibility-row">
<property name="title">Visibility</property>
<property name="subtitle">Forcefully hide or show an item or just don't affect its visibility. This option applies every time the top bar gets reordered, an items visiblity might be influenced by other factors and this option might not work for every item.</property>
<property name="model">
<object class="GtkStringList">
<items>
<item>Default</item>
<item>Forcefully Hide</item>
<item>Forcefully Show</item>
</items>
</object>
</property>
<signal name="notify::selected-item" handler="onVisibilityRowSelectionChanged"/>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
</template>
</interface>

View file

@ -46,12 +46,6 @@
<attribute name="action">row.move-down</attribute>
</item>
</section>
<section>
<item>
<attribute name="label">Options</attribute>
<attribute name="action">row.options</attribute>
</item>
</section>
<section>
<item>
<attribute name="label">Forget</attribute>

View file

@ -1,321 +0,0 @@
// My annotated and cut down js/ui/panel.js from gnome-shell/49.0.
// All annotations are what I guessed, interpreted and copied while reading the
// code and comparing to other panel.js versions and might be wrong. They are
// prefixed with "Annotation:" to indicate that they're my comments, not
// comments that orginally existed.
// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/49.0/js/ui/panel.js
// On: 2025-10-03
// License: This code is licensed under GPLv2.
// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/49.0/js/ui/sessionMode.js
// On: 2025-10-03
// License: This code is licensed under GPLv2.
// I'm using the word "item" to refer to the thing, which gets added to the top
// (menu)bar / panel, where an item has a role/name and an indicator.
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this
// Extension.
import Clutter from 'gi://Clutter';
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this
// Extension.
import GObject from 'gi://GObject';
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this
// Extension.
import St from 'gi://St';
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this
// Extension.
import * as CtrlAltTab from './ctrlAltTab.js';
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this
// Extension.
import * as PopupMenu from './popupMenu.js';
import * as PanelMenu from './panelMenu.js';
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this
// Extension.
import * as Main from './main.js';
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this
// Extension.
import {DateMenuButton} from './dateMenu.js';
import {ATIndicator} from './status/accessibility.js';
import {InputSourceIndicator} from './status/keyboard.js';
import {DwellClickIndicator} from './status/dwellClick.js';
import {ScreenRecordingIndicator, ScreenSharingIndicator} from './status/remoteAccess.js';
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this
// Extension.
// Of note (for PANEL_ITEM_IMPLEMENTATIONS):
// const ActivitiesButton = [...]
// const QuickSettings = [...]
// Compared to panel_48.2_2025-06-08.js: AppMenuButton got removed.
// Compared to panel_48.2_2025-06-08.js: AppMenuButton got removed.
const PANEL_ITEM_IMPLEMENTATIONS = {
'activities': ActivitiesButton,
'quickSettings': QuickSettings,
'dateMenu': DateMenuButton,
'a11y': ATIndicator,
'keyboard': InputSourceIndicator,
'dwellClick': DwellClickIndicator,
'screenRecording': ScreenRecordingIndicator,
'screenSharing': ScreenSharingIndicator,
};
export const Panel = GObject.registerClass(
class Panel extends St.Widget {
// Annotation: Initializes the top (menu)bar / panel.
// Does relevant stuff like:
// - Defining this._leftBox, this._centerBox and this._rightBox.
// - Finally calling this._updatePanel().
// Compared to panel_48.2_2025-06-08.js: Nothing changed.
_init() {
super._init({
name: 'panel',
reactive: true,
});
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
this._sessionStyle = null;
this.statusArea = {};
this.menuManager = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({name: 'panelLeft'});
this.add_child(this._leftBox);
this._centerBox = new St.BoxLayout({name: 'panelCenter'});
this.add_child(this._centerBox);
this._rightBox = new St.BoxLayout({name: 'panelRight'});
this.add_child(this._rightBox);
this.connect('button-press-event', this._onButtonPress.bind(this));
this.connect('touch-event', this._onTouchEvent.bind(this));
Main.overview.connectObject('showing',
() => this.add_style_pseudo_class('overview'),
this);
Main.overview.connectObject('hiding',
() => this.remove_style_pseudo_class('overview'),
this);
Main.layoutManager.panelBox.add_child(this);
Main.ctrlAltTabManager.addGroup(this,
_('Top Bar'), 'shell-focus-top-bar-symbolic',
{sortGroup: CtrlAltTab.SortGroup.TOP});
Main.sessionMode.connectObject('updated',
this._updatePanel.bind(this),
this);
global.display.connectObject('workareas-changed',
() => this.queue_relayout(),
this);
this._updatePanel();
}
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for
// this Extension.
// Annotation: Gets called by this._init() to populate the top (menu)bar /
// panel initially.
//
// It does the following relevant stuff:
// - Calls this._hideIndicators()
// - Calls this._updateBox() for this._leftBox, this._centerBox and
// this._rightBox with panel.left, panel.center and panel.right to
// populate the boxes with items defined in panel.left, panel.center and
// panel.right.
//
// panel.left, panel.center and panel.right get set via the line let panel
// = Main.sessionMode.panel, which uses the panel of Mains (js/ui/main.js)
// instance of SessionMode (js/ui/sessionMode.js).
//
// And in js/ui/sessionMode.js (49.0, 2025-10-03) you have different modes
// with different panel configuration. For example the "user" mode with:
// panel: {
// left: ['activities'],
// center: ['dateMenu'],
// right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'quickSettings'],
// }
//
// This way this function populates the top (menu)bar / panel with the
// default stuff you see on a fresh Gnome.
//
// Compared to panel_48.2_2025-06-08.js: Nothing changed.
_updatePanel() {
let panel = Main.sessionMode.panel;
this._hideIndicators();
this._updateBox(panel.left, this._leftBox);
this._updateBox(panel.center, this._centerBox);
this._updateBox(panel.right, this._rightBox);
if (panel.left.includes('dateMenu'))
Main.messageTray.bannerAlignment = Clutter.ActorAlign.START;
else if (panel.right.includes('dateMenu'))
Main.messageTray.bannerAlignment = Clutter.ActorAlign.END;
// Default to center if there is no dateMenu
else
Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER;
if (this._sessionStyle)
this.remove_style_class_name(this._sessionStyle);
this._sessionStyle = Main.sessionMode.panelStyle;
if (this._sessionStyle)
this.add_style_class_name(this._sessionStyle);
}
// Annotation: This function hides all items, which are in the top (menu)bar
// panel and in PANEL_ITEM_IMPLEMENTATIONS.
//
// Compared to panel_48.2_2025-06-08.js: Nothing changed.
_hideIndicators() {
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
let indicator = this.statusArea[role];
if (!indicator)
continue;
indicator.container.hide();
}
}
// Annotation: This function takes a role (of an item) and returns a
// corresponding indicator, if either of two things are true:
// - The indicator is already in this.statusArea.
// Then it just returns the indicator by using this.statusArea.
// - The role is in PANEL_ITEM_IMPLEMENTATIONS.
// Then it creates a new indicator, adds it to this.statusArea and returns
// it.
//
// Compared to panel_48.2_2025-06-08.js: Nothing changed.
_ensureIndicator(role) {
let indicator = this.statusArea[role];
if (!indicator) {
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
return null;
}
indicator = new constructor(this);
this.statusArea[role] = indicator;
}
return indicator;
}
// Annotation: This function takes a list of items (or rather their roles)
// and adds the indicators of those items to a box (like this._leftBox)
// using this._ensureIndicator() to get the indicator corresponding to the
// given role.
// So only items with roles this._ensureIndicator() knows, get added.
//
// Compared to panel_48.2_2025-06-08.js: Nothing changed.
_updateBox(elements, box) {
let nChildren = box.get_n_children();
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let indicator = this._ensureIndicator(role);
if (indicator == null)
continue;
this._addToPanelBox(role, indicator, i + nChildren, box);
}
}
// Annotation: This function adds the given item to the specified top
// (menu)bar / panel box and connects to "destroy" and "menu-set" events.
//
// It takes the following arguments:
// - role: The name of the item to add.
// - indicator: The indicator of the item to add.
// - position: Where in the box to add the item.
// - box: The box to add the item to.
// Can be one of the following:
// - this._leftBox
// - this._centerBox
// - this._rightBox
//
// Compared to panel_48.2_2025-06-08.js: Nothing changed.
_addToPanelBox(role, indicator, position, box) {
let container = indicator.container;
container.show();
let parent = container.get_parent();
if (parent)
parent.remove_child(container);
box.insert_child_at_index(container, position);
this.statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', emitter => {
delete this.statusArea[role];
emitter.disconnect(destroyId);
});
indicator.connect('menu-set', this._onMenuSet.bind(this));
this._onMenuSet(indicator);
}
// Annotation: This function allows you to add an item to the top (menu)bar
// / panel.
// While per default it adds the item to the status area (the right box of
// the top bar), you can specify the box and add the item to any of the
// three boxes of the top bar.
// To add an item to the top bar, you need to give its role and indicator.
//
// This function takes the following arguments:
// - role: A name for the item to add.
// - indicator: The indicator for the item to add (must be an instance of
// PanelMenu.Button).
// - position: Where in the box to add the item.
// - box: The box to add the item to.
// Can be one of the following:
// - "left": referring to this._leftBox
// - "center": referring to this._centerBox
// - "right": referring to this._rightBox
// These boxes are what you see in the top bar as the left, right and
// center sections.
//
// Finally this function just calls this._addToPanelBox() for the actual
// work, so it basically just makes sure the input to this._addToPanelBox()
// is correct.
//
// Compared to panel_48.2_2025-06-08.js: Nothing changed.
addToStatusArea(role, indicator, position, box) {
if (this.statusArea[role])
throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`);
if (!(indicator instanceof PanelMenu.Button))
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
position ??= 0;
let boxes = {
left: this._leftBox,
center: this._centerBox,
right: this._rightBox,
};
let boxContainer = boxes[box] || this._rightBox;
this.statusArea[role] = indicator;
this._addToPanelBox(role, indicator, position, boxContainer);
return indicator;
}
// Of note:
// _onMenuSet(indicator) { [...] }
// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for
// this Extension.
});

View file

@ -121,7 +121,18 @@ export default class TopBarOrganizerExtension extends Extension {
const validBoxOrder = this._boxOrderManager.getValidBoxOrder(box);
// Get the relevant box of `Main.panel`.
let panelBox = (Main.panel as CustomPanel)[`_${box}Box`];
let panelBox;
switch (box) {
case "left":
panelBox = (Main.panel as CustomPanel)._leftBox;
break;
case "center":
panelBox = (Main.panel as CustomPanel)._centerBox;
break;
case "right":
panelBox = (Main.panel as CustomPanel)._rightBox;
break;
}
/// Go through the items of the validBoxOrder and order the GNOME Shell
/// top bar box accordingly.

View file

@ -2,8 +2,8 @@
"uuid": "top-bar-organizer@julian.gse.jsts.xyz",
"name": "Top Bar Organizer",
"description": "Organize the items of the top (menu)bar.",
"version": 15,
"shell-version": [ "45", "46", "47", "48", "49" ],
"version": 13,
"shell-version": [ "45", "46", "47", "48" ],
"settings-schema": "org.gnome.shell.extensions.top-bar-organizer",
"url": "https://gitlab.gnome.org/june/top-bar-organizer"
}

View file

@ -1,69 +0,0 @@
"use strict";
import GObject from "gi://GObject";
import Adw from "gi://Adw";
import GLib from "gi://GLib";
import type Gio from "gi://Gio";
import type Gtk from "gi://Gtk";
import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
export default class PrefsBoxOrderItemOptionsDialog extends Adw.Dialog {
static {
GObject.registerClass({
GTypeName: "PrefsBoxOrderItemOptionsDialog",
Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-box-order-item-options-dialog.ui", GLib.UriFlags.NONE),
InternalChildren: [
"visibility-row",
],
}, this);
}
declare _visibility_row: Adw.ComboRow;
#settings: Gio.Settings;
item: string;
constructor(params = {}, item: string) {
super(params);
// Associate `this` with an item.
this.item = item;
// Load the settings.
this.#settings = ExtensionPreferences.lookupByURL(import.meta.url)!.getSettings();
// Set the selected visibility row choice to the settings value.
const itemsToHide = new Set(this.#settings.get_strv("hide"));
const itemsToShow = new Set(this.#settings.get_strv("show"));
if (itemsToHide.has(this.item)) {
this._visibility_row.set_selected(1);
} else if (itemsToShow.has(this.item)) {
this._visibility_row.set_selected(2);
} else {
this._visibility_row.set_selected(0);
}
}
onVisibilityRowSelectionChanged(): void {
const visibility = (this._visibility_row.get_selected_item() as Gtk.StringObject).get_string();
const itemsToHide = new Set(this.#settings.get_strv("hide"));
const itemsToShow = new Set(this.#settings.get_strv("show"));
switch (visibility) {
case "Forcefully Hide":
itemsToHide.add(this.item)
itemsToShow.delete(this.item);
break;
case "Forcefully Show":
itemsToHide.delete(this.item)
itemsToShow.add(this.item);
break;
case "Default":
itemsToHide.delete(this.item)
itemsToShow.delete(this.item);
break;
}
this.#settings.set_strv("hide", Array.from(itemsToHide));
this.#settings.set_strv("show", Array.from(itemsToShow));
}
}

View file

@ -6,7 +6,6 @@ import GObject from "gi://GObject";
import Adw from "gi://Adw";
import GLib from "gi://GLib";
import PrefsBoxOrderItemOptionsDialog from "./PrefsBoxOrderItemOptionsDialog.js";
import type PrefsBoxOrderListBox from "./PrefsBoxOrderListBox.js";
export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
@ -26,15 +25,6 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
parentListBox.saveBoxOrderToSettings();
parentListBox.determineRowMoveActionEnable();
});
this.install_action("row.options", null, (self, _actionName, _param) => {
const itemOptionsDialog = new PrefsBoxOrderItemOptionsDialog({
// Get the title from self as the constructor of
// PrefsBoxOrderItemRow already processes the item name into a
// nice title.
title: (self as PrefsBoxOrderItemRow).get_title()
}, (self as PrefsBoxOrderItemRow).item);
itemOptionsDialog.present(self);
});
this.install_action("row.move-up", null, (self, _actionName, _param) => self.emit("move", "up"));
this.install_action("row.move-down", null, (self, _actionName, _param) => self.emit("move", "down"));
}