Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions scripts/run-gnome-shell.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ TOOLBOX="${1:-gnome-shell-devel}"

SHELL_ENV=(
SHELL_DEBUG=all
AURORA_DEVTOOLS=1
XDG_CURRENT_DESKTOP=GNOME
XDG_SESSION_TYPE=wayland
GSETTINGS_SCHEMA_DIR=/usr/share/glib-2.0/schemas
Expand All @@ -17,10 +18,6 @@ then
SHELL_ENV+=(XDG_DATA_DIRS=$XDG_DATA_DIRS:/usr/share/)
fi

if [[ -n "$AURORA_TRAY_DEBUG" ]]; then
SHELL_ENV+=(AURORA_TRAY_DEBUG=$AURORA_TRAY_DEBUG)
fi

echo "Running GNOME Shell in toolbox '$TOOLBOX'..."
toolbox --container $TOOLBOX run \
env "${SHELL_ENV[@]}" dbus-run-session gnome-shell "${SHELL_ARGS[@]}"
29 changes: 29 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import '@girs/gjs';

import type Gio from '@girs/gio-2.0';
import GLib from '@girs/glib-2.0';
import { Extension } from '@girs/gnome-shell/extensions/extension';

import type { Module } from './module.ts';
Expand All @@ -11,6 +12,7 @@ import { DefaultExtensionContext } from '~/core/context.ts';
import { ConsoleLogger, setGlobalLogger, logger } from '~/core/logger.ts';
import { GSettingsManager } from '~/core/settings.ts';
import { GnomeShellAdapter } from '~/core/adapters/shell.ts';
import { DevTool } from '~/modules/devTool/devTool.ts';

const LOG_PREFIX = 'AuroraShell';

Expand All @@ -22,6 +24,7 @@ const LOG_PREFIX = 'AuroraShell';
*/
export default class AuroraShellExtension extends Extension {
private _modules: Map<string, Module> = new Map();
private _devTool: DevTool | null = null;
private _settings: Gio.Settings | null = null;
private _context: ExtensionContext | null = null;

Expand All @@ -41,6 +44,7 @@ export default class AuroraShellExtension extends Extension {
initIcons(this.path);
this._initializeModules();
this._enableAllModules();
this._enableDevTool();
this._connectSettings();
}

Expand All @@ -62,6 +66,30 @@ export default class AuroraShellExtension extends Extension {
}
}

private _enableDevTool(): void {
if (GLib.getenv('AURORA_DEVTOOLS') !== '1' || !this._context) return;

try {
this._devTool = new DevTool(this._context);
this._devTool.enable();
} catch (e) {
logger.error(`Failed to enable DevTool: ${e}`, { prefix: LOG_PREFIX });
this._devTool = null;
}
}

private _disableDevTool(): void {
if (!this._devTool) return;

try {
this._devTool.disable();
} catch (e) {
logger.error(`Failed to disable DevTool: ${e}`, { prefix: LOG_PREFIX });
} finally {
this._devTool = null;
}
}

private _connectSettings(): void {
if (!this._settings) return;

Expand Down Expand Up @@ -104,6 +132,7 @@ export default class AuroraShellExtension extends Extension {
logger.log('Disabling extension', { prefix: LOG_PREFIX });

this._settings?.disconnectObject(this);
this._disableDevTool();

for (const [name, module] of this._modules) {
try {
Expand Down
182 changes: 182 additions & 0 deletions src/modules/devTool/devTool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import '@girs/gjs';

import St from '@girs/st-18';
import Clutter from '@girs/clutter-18';
import * as Main from '@girs/gnome-shell/ui/main';
import type { Button as PanelMenuButton } from '@girs/gnome-shell/ui/panelMenu';
import * as PanelMenu from '@girs/gnome-shell/ui/panelMenu';
import * as PopupMenu from '@girs/gnome-shell/ui/popupMenu';

import type { ExtensionContext } from '~/core/context.ts';
import { Module } from '~/module.ts';
import { loadIcon } from '~/shared/icons.ts';

import { TrayIconsDevTool } from './trayIconsDevTool.ts';

const DEVTOOL_ID = 'aurora-devtool';

type DevToolSection = {
key: string;
title: string;
iconName: string;
buildPanel(): St.Widget;
destroy(): void;
};

export class DevTool extends Module {
private _button: PanelMenu.Button | null = null;
private _menuOpenStateId = 0;
private _trayIconsTool: TrayIconsDevTool | null = null;
private _sections: DevToolSection[] = [];
private _activeSectionKey = 'tray-icons';

constructor(context: ExtensionContext) {
super(context);
}

override enable(): void {
this._button = new PanelMenu.Button(1.0, 'Aurora DevTool');
this._button.add_child(
new St.Icon({
gicon: loadIcon('applications-engineering-symbolic'),
icon_size: 16,
style_class: 'system-status-icon',
}),
);

this._trayIconsTool = new TrayIconsDevTool(() => this._rebuildMenu());
this._sections = [this._trayIconsTool];

const menu = this._getMenu();
if (!menu) return;
menu.setSourceAlignment(1.0);

this._menuOpenStateId = menu.connect('open-state-changed', (_menu, open) => {
if (open) this._rebuildMenu();
return undefined;
});

this._rebuildMenu();
Main.panel.addToStatusArea(DEVTOOL_ID, this._button as unknown as PanelMenuButton, 1, 'left');
}

override disable(): void {
for (const section of this._sections) {
section.destroy();
}
this._sections = [];
this._trayIconsTool = null;

if (this._menuOpenStateId && this._button) {
this._getMenu()?.disconnect(this._menuOpenStateId);
this._menuOpenStateId = 0;
}

(Main.panel.statusArea as Record<string, unknown>)[DEVTOOL_ID] = null;
this._button?.destroy();
this._button = null;
}

get trayIconsTool(): TrayIconsDevTool | null {
return this._trayIconsTool;
}

private _rebuildMenu(): void {
const menu = this._getMenu();
if (!menu) return;

menu.removeAll();
const section = new PopupMenu.PopupMenuSection();
section.box.add_child(this._buildPanel());
menu.addMenuItem(section);
}

private _buildPanel(): St.Widget {
const panel = new St.BoxLayout({
vertical: true,
style_class: 'aurora-devtool-panel',
});

panel.add_child(this._buildHeader());
panel.add_child(this._buildTabs());

const activeSection = this._activeSection();
if (activeSection) {
panel.add_child(activeSection.buildPanel());
}

return panel;
}

private _buildHeader(): St.Widget {
const header = new St.BoxLayout({
style_class: 'aurora-devtool-header',
});

header.add_child(
new St.Icon({
gicon: loadIcon('applications-engineering-symbolic'),
icon_size: 18,
style_class: 'aurora-devtool-header-icon',
}),
);
header.add_child(
new St.Label({
text: 'Aurora DevTool',
y_align: Clutter.ActorAlign.CENTER,
style_class: 'aurora-devtool-title',
}),
);

return header;
}

private _buildTabs(): St.Widget {
const tabs = new St.BoxLayout({
style_class: 'aurora-devtool-tabs',
});

for (const section of this._sections) {
const active = section.key === this._activeSectionKey;
const tabContent = new St.BoxLayout({
style_class: 'aurora-devtool-tab-content',
});
const tab = new St.Button({
child: tabContent,
style_class: active ? 'aurora-devtool-tab active' : 'aurora-devtool-tab',
can_focus: true,
x_expand: true,
accessible_name: section.title,
});

tabContent.add_child(
new St.Icon({
icon_name: section.iconName,
icon_size: 16,
style_class: 'aurora-devtool-tab-icon',
}),
);
tabContent.add_child(
new St.Label({
text: section.title,
y_align: Clutter.ActorAlign.CENTER,
}),
);
tab.connect('clicked', () => {
this._activeSectionKey = section.key;
this._rebuildMenu();
});
tabs.add_child(tab);
}

return tabs;
}

private _activeSection(): DevToolSection | null {
return this._sections.find((section) => section.key === this._activeSectionKey) ?? null;
}

private _getMenu(): PopupMenu.PopupMenu | null {
return (this._button?.menu as PopupMenu.PopupMenu | null | undefined) ?? null;
}
}
Loading
Loading