mirror of
				https://gitlab.gnome.org/julianschacher/top-bar-organizer.git
				synced 2025-10-27 15:19:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			323 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // My annotated and cut down js/ui/panel.js from gnome-shell/48.2.
 | |
| // 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/48.2/js/ui/panel.js
 | |
| // On: 2025-06-08
 | |
| // License: This code is licensed under GPLv2.
 | |
| 
 | |
| // Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/48.2/js/ui/sessionMode.js
 | |
| // On: 2025-06-08
 | |
| // 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 AppMenuButton = [...]
 | |
| // const ActivitiesButton = [...]
 | |
| // const QuickSettings = [...]
 | |
| 
 | |
| const PANEL_ITEM_IMPLEMENTATIONS = {
 | |
|     'activities': ActivitiesButton,
 | |
|     'appMenu': AppMenuButton,
 | |
|     '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_47.rc_2024-09-12.js: connectObject instead of connect
 | |
|     // gets used, which shouldn't be relevant for this extension.
 | |
|     _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 (48.2, 2025-06-08) 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_47.rc_2024-09-12.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_47.rc_2024-09-12.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_47.rc_2024-09-12.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_47.rc_2024-09-12.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_47.rc_2024-09-12.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_47.rc_2024-09-12.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.
 | |
| });
 | 
