Skip to content

Commit c658d8b

Browse files
committed
feat(devtools): add DevTool module for enhanced tray icon management and debugging and others modules in future
1 parent c194cb1 commit c658d8b

9 files changed

Lines changed: 684 additions & 28 deletions

File tree

scripts/run-gnome-shell.sh

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ TOOLBOX="${1:-gnome-shell-devel}"
66

77
SHELL_ENV=(
88
SHELL_DEBUG=all
9+
AURORA_DEVTOOLS=1
910
XDG_CURRENT_DESKTOP=GNOME
1011
XDG_SESSION_TYPE=wayland
1112
GSETTINGS_SCHEMA_DIR=/usr/share/glib-2.0/schemas
@@ -17,10 +18,6 @@ then
1718
SHELL_ENV+=(XDG_DATA_DIRS=$XDG_DATA_DIRS:/usr/share/)
1819
fi
1920

20-
if [[ -n "$AURORA_TRAY_DEBUG" ]]; then
21-
SHELL_ENV+=(AURORA_TRAY_DEBUG=$AURORA_TRAY_DEBUG)
22-
fi
23-
2421
echo "Running GNOME Shell in toolbox '$TOOLBOX'..."
2522
toolbox --container $TOOLBOX run \
2623
env "${SHELL_ENV[@]}" dbus-run-session gnome-shell "${SHELL_ARGS[@]}"

src/extension.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import '@girs/gjs';
22

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

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

1517
const LOG_PREFIX = 'AuroraShell';
1618

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

@@ -41,6 +44,7 @@ export default class AuroraShellExtension extends Extension {
4144
initIcons(this.path);
4245
this._initializeModules();
4346
this._enableAllModules();
47+
this._enableDevTool();
4448
this._connectSettings();
4549
}
4650

@@ -62,6 +66,30 @@ export default class AuroraShellExtension extends Extension {
6266
}
6367
}
6468

69+
private _enableDevTool(): void {
70+
if (GLib.getenv('AURORA_DEVTOOLS') !== '1' || !this._context) return;
71+
72+
try {
73+
this._devTool = new DevTool(this._context);
74+
this._devTool.enable();
75+
} catch (e) {
76+
logger.error(`Failed to enable DevTool: ${e}`, { prefix: LOG_PREFIX });
77+
this._devTool = null;
78+
}
79+
}
80+
81+
private _disableDevTool(): void {
82+
if (!this._devTool) return;
83+
84+
try {
85+
this._devTool.disable();
86+
} catch (e) {
87+
logger.error(`Failed to disable DevTool: ${e}`, { prefix: LOG_PREFIX });
88+
} finally {
89+
this._devTool = null;
90+
}
91+
}
92+
6593
private _connectSettings(): void {
6694
if (!this._settings) return;
6795

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

106134
this._settings?.disconnectObject(this);
135+
this._disableDevTool();
107136

108137
for (const [name, module] of this._modules) {
109138
try {

src/modules/devTool/devTool.ts

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import '@girs/gjs';
2+
3+
import St from '@girs/st-18';
4+
import Clutter from '@girs/clutter-18';
5+
import * as Main from '@girs/gnome-shell/ui/main';
6+
import type { Button as PanelMenuButton } from '@girs/gnome-shell/ui/panelMenu';
7+
import * as PanelMenu from '@girs/gnome-shell/ui/panelMenu';
8+
import * as PopupMenu from '@girs/gnome-shell/ui/popupMenu';
9+
10+
import type { ExtensionContext } from '~/core/context.ts';
11+
import { Module } from '~/module.ts';
12+
import { loadIcon } from '~/shared/icons.ts';
13+
14+
import { TrayIconsDevTool } from './trayIconsDevTool.ts';
15+
16+
const DEVTOOL_ID = 'aurora-devtool';
17+
18+
type DevToolSection = {
19+
key: string;
20+
title: string;
21+
iconName: string;
22+
buildPanel(): St.Widget;
23+
destroy(): void;
24+
};
25+
26+
export class DevTool extends Module {
27+
private _button: PanelMenu.Button | null = null;
28+
private _menuOpenStateId = 0;
29+
private _trayIconsTool: TrayIconsDevTool | null = null;
30+
private _sections: DevToolSection[] = [];
31+
private _activeSectionKey = 'tray-icons';
32+
33+
constructor(context: ExtensionContext) {
34+
super(context);
35+
}
36+
37+
override enable(): void {
38+
this._button = new PanelMenu.Button(1.0, 'Aurora DevTool');
39+
this._button.add_child(
40+
new St.Icon({
41+
gicon: loadIcon('applications-engineering-symbolic'),
42+
icon_size: 16,
43+
style_class: 'system-status-icon',
44+
}),
45+
);
46+
47+
this._trayIconsTool = new TrayIconsDevTool(() => this._rebuildMenu());
48+
this._sections = [this._trayIconsTool];
49+
50+
const menu = this._getMenu();
51+
if (!menu) return;
52+
menu.setSourceAlignment(1.0);
53+
54+
this._menuOpenStateId = menu.connect('open-state-changed', (_menu, open) => {
55+
if (open) this._rebuildMenu();
56+
return undefined;
57+
});
58+
59+
this._rebuildMenu();
60+
Main.panel.addToStatusArea(DEVTOOL_ID, this._button as unknown as PanelMenuButton, 1, 'left');
61+
}
62+
63+
override disable(): void {
64+
for (const section of this._sections) {
65+
section.destroy();
66+
}
67+
this._sections = [];
68+
this._trayIconsTool = null;
69+
70+
if (this._menuOpenStateId && this._button) {
71+
this._getMenu()?.disconnect(this._menuOpenStateId);
72+
this._menuOpenStateId = 0;
73+
}
74+
75+
(Main.panel.statusArea as Record<string, unknown>)[DEVTOOL_ID] = null;
76+
this._button?.destroy();
77+
this._button = null;
78+
}
79+
80+
get trayIconsTool(): TrayIconsDevTool | null {
81+
return this._trayIconsTool;
82+
}
83+
84+
private _rebuildMenu(): void {
85+
const menu = this._getMenu();
86+
if (!menu) return;
87+
88+
menu.removeAll();
89+
const section = new PopupMenu.PopupMenuSection();
90+
section.box.add_child(this._buildPanel());
91+
menu.addMenuItem(section);
92+
}
93+
94+
private _buildPanel(): St.Widget {
95+
const panel = new St.BoxLayout({
96+
vertical: true,
97+
style_class: 'aurora-devtool-panel',
98+
});
99+
100+
panel.add_child(this._buildHeader());
101+
panel.add_child(this._buildTabs());
102+
103+
const activeSection = this._activeSection();
104+
if (activeSection) {
105+
panel.add_child(activeSection.buildPanel());
106+
}
107+
108+
return panel;
109+
}
110+
111+
private _buildHeader(): St.Widget {
112+
const header = new St.BoxLayout({
113+
style_class: 'aurora-devtool-header',
114+
});
115+
116+
header.add_child(
117+
new St.Icon({
118+
gicon: loadIcon('applications-engineering-symbolic'),
119+
icon_size: 18,
120+
style_class: 'aurora-devtool-header-icon',
121+
}),
122+
);
123+
header.add_child(
124+
new St.Label({
125+
text: 'Aurora DevTool',
126+
y_align: Clutter.ActorAlign.CENTER,
127+
style_class: 'aurora-devtool-title',
128+
}),
129+
);
130+
131+
return header;
132+
}
133+
134+
private _buildTabs(): St.Widget {
135+
const tabs = new St.BoxLayout({
136+
style_class: 'aurora-devtool-tabs',
137+
});
138+
139+
for (const section of this._sections) {
140+
const active = section.key === this._activeSectionKey;
141+
const tabContent = new St.BoxLayout({
142+
style_class: 'aurora-devtool-tab-content',
143+
});
144+
const tab = new St.Button({
145+
child: tabContent,
146+
style_class: active ? 'aurora-devtool-tab active' : 'aurora-devtool-tab',
147+
can_focus: true,
148+
x_expand: true,
149+
accessible_name: section.title,
150+
});
151+
152+
tabContent.add_child(
153+
new St.Icon({
154+
icon_name: section.iconName,
155+
icon_size: 16,
156+
style_class: 'aurora-devtool-tab-icon',
157+
}),
158+
);
159+
tabContent.add_child(
160+
new St.Label({
161+
text: section.title,
162+
y_align: Clutter.ActorAlign.CENTER,
163+
}),
164+
);
165+
tab.connect('clicked', () => {
166+
this._activeSectionKey = section.key;
167+
this._rebuildMenu();
168+
});
169+
tabs.add_child(tab);
170+
}
171+
172+
return tabs;
173+
}
174+
175+
private _activeSection(): DevToolSection | null {
176+
return this._sections.find((section) => section.key === this._activeSectionKey) ?? null;
177+
}
178+
179+
private _getMenu(): PopupMenu.PopupMenu | null {
180+
return (this._button?.menu as PopupMenu.PopupMenu | null | undefined) ?? null;
181+
}
182+
}

0 commit comments

Comments
 (0)