Plugins in data/plugins are loaded at startup and can add a tab with a read-only node board, and the demo plugin shows every asset as a node (#113)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2a390e52ff
commit
57a97ba94f
22 files changed
+1153
-57
No files matched your search
+38
-5
@@ -43,6 +43,7 @@ import type { PlayFrom } from './settings';
|
||||
import type { EditorState } from './state';
|
||||
import { TextureEditor } from './texture/TextureEditor';
|
||||
import { installFileImport } from './fileImport';
|
||||
import type { PluginRecord } from './plugins/loader';
|
||||
|
||||
export type CentreTab =
|
||||
| 'scene'
|
||||
@@ -56,6 +57,10 @@ export type CentreTab =
|
||||
| 'container'
|
||||
| 'list';
|
||||
|
||||
/** A tab a plugin added; the prefix keeps it apart from every built-in one. */
|
||||
export type PluginTabId = `plugin:${string}`;
|
||||
export type TabId = CentreTab | PluginTabId;
|
||||
|
||||
const TAB_ORDER = [
|
||||
'scene',
|
||||
'game',
|
||||
@@ -124,7 +129,7 @@ export class Editor implements PlayHost {
|
||||
readonly assets: AssetPanel;
|
||||
readonly inspector: Inspector;
|
||||
readonly console = new ConsolePanel();
|
||||
tab: CentreTab = 'scene';
|
||||
tab: TabId = 'scene';
|
||||
/** Frame rate, refreshed twice a second; null until the first measurement. */
|
||||
fps: number | null = null;
|
||||
private readonly play: PlaySession;
|
||||
@@ -136,8 +141,11 @@ export class Editor implements PlayHost {
|
||||
readonly consoleSplitter = new Splitter('y');
|
||||
private readonly workspace: HStack;
|
||||
private readonly centre: VStack;
|
||||
private readonly tabs: TabStrip<CentreTab>;
|
||||
private readonly tabs: TabStrip<TabId>;
|
||||
private readonly panes: Record<CentreTab, Widget>;
|
||||
private readonly pluginPanes = new Map<PluginTabId, Widget>();
|
||||
/** Every file in `data/plugins/` and what became of it; empty until the plugins are loaded. */
|
||||
plugins: PluginRecord[] = [];
|
||||
private readonly toolbar: Toolbar;
|
||||
/** Drawn-frame count at the last measurement, not a counter of its own. */
|
||||
private fpsFrames = 0;
|
||||
@@ -205,7 +213,10 @@ export class Editor implements PlayHost {
|
||||
this.leftColumn.add(this.hierarchy, 1);
|
||||
|
||||
this.centre = new VStack();
|
||||
this.tabs = new TabStrip(TAB_ORDER, TAB_LABELS, (tab) => this.showTab(tab));
|
||||
this.tabs = new TabStrip<TabId>(
|
||||
TAB_ORDER.map((tab) => ({ tab, label: TAB_LABELS[tab] })),
|
||||
(tab) => this.showTab(tab),
|
||||
);
|
||||
this.tabs.minHeight = Theme.controlHeight + 8;
|
||||
// Written out per tab rather than derived, because a `Record<CentreTab, …>` is what makes
|
||||
// adding a tab a type error everywhere it has to be handled instead of a silent omission.
|
||||
@@ -308,7 +319,28 @@ export class Editor implements PlayHost {
|
||||
state.on('mode', () => this.editorRun.invalidate());
|
||||
}
|
||||
|
||||
showTab(tab: CentreTab): void {
|
||||
/** Whether `id` names a tab already – a built-in one by its own id, a plugin's by the id it gave. */
|
||||
hasTab(id: string): boolean {
|
||||
return Object.hasOwn(TAB_LABELS, id) || this.pluginPanes.has(`plugin:${id}`);
|
||||
}
|
||||
|
||||
/** Appends a plugin's tab after the built-in ones; `hasTab` has said the id is free. */
|
||||
addPluginTab(id: string, label: string, content: Widget): void {
|
||||
const tab: PluginTabId = `plugin:${id}`;
|
||||
this.pluginPanes.set(tab, content);
|
||||
content.visible = false;
|
||||
this.centre.addChildAt(content, this.centre.getChildIndex(this.consoleSplitter));
|
||||
content.flex = 1;
|
||||
this.tabs.addTab(tab, label);
|
||||
this.centre.relayout();
|
||||
}
|
||||
|
||||
/** What a plugin put into its tab, for the check scripts. */
|
||||
pluginPane(id: string): Widget | undefined {
|
||||
return this.pluginPanes.get(`plugin:${id}`);
|
||||
}
|
||||
|
||||
showTab(tab: TabId): void {
|
||||
this.tab = tab;
|
||||
// The hierarchy lists what the visible tab edits; every other tab is about the scene.
|
||||
this.hierarchy.showObjectsOf(
|
||||
@@ -317,6 +349,7 @@ export class Editor implements PlayHost {
|
||||
: () => this.state.activeScene,
|
||||
);
|
||||
for (const key of TAB_ORDER) this.panes[key].visible = key === tab;
|
||||
for (const [key, pane] of this.pluginPanes) pane.visible = key === tab;
|
||||
this.tabs.setActive(tab);
|
||||
this.centre.relayout();
|
||||
ui().focus.validate();
|
||||
@@ -326,7 +359,7 @@ export class Editor implements PlayHost {
|
||||
* Global x-extent of the tab buttons in the row, from the widget box – not `getBounds()`, which
|
||||
* on an untoggled ghost tab sees nothing but the label (the background is not drawn at all).
|
||||
*/
|
||||
tabRow(): { shown: TabPlace<CentreTab>[]; hidden: CentreTab[] } {
|
||||
tabRow(): { shown: TabPlace<TabId>[]; hidden: TabId[] } {
|
||||
return { shown: this.tabs.places(), hidden: this.tabs.hidden() };
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user