Add a Container tab and propagate template edits
The Container tab reuses SceneViewport against any object holder, so a container template is edited exactly like a scene. Right-click no longer changes the selection, which previously made nesting impossible: opening the menu on a container loaded it, and "Place in container" then targeted itself. Template edits (move, rename, override) now reach already-placed instances per slot, through a shared templateSync used by both the apply path and the plain object commands. Copies the user has touched keep their own value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6bedc948be
commit
9d93020bd8
35 files changed
+1452
-363
No files matched your search
@@ -3,6 +3,7 @@ import { centreY } from '../math';
|
||||
import { Theme, textStyle } from '../Theme';
|
||||
import { type Size, Widget } from '../Widget';
|
||||
import { ListRow } from './ListRow';
|
||||
import type { Popup } from './Popup';
|
||||
|
||||
export interface ListItem {
|
||||
id: string;
|
||||
@@ -46,9 +47,12 @@ export class ListView extends Widget {
|
||||
private rows: ListRow[] = [];
|
||||
private selectedId: string | null = null;
|
||||
private highlighted = new Set<string>();
|
||||
/** Row an open context menu acts on – drawn like a selected row, but only until it closes. */
|
||||
private contextId: string | null = null;
|
||||
onSelect?: (id: string | null, modifiers?: PickModifiers) => void;
|
||||
onActivate?: (id: string) => void;
|
||||
onContextMenu?: (id: string, globalX: number, globalY: number) => void;
|
||||
/** Returning the opened menu keeps its row marked for as long as it is up. */
|
||||
onContextMenu?: (id: string, globalX: number, globalY: number) => Popup | void;
|
||||
/** Called when a dragged row is released over another row (`targetId`) or over empty space (null). */
|
||||
onDrop?: (draggedId: string, targetId: string | null) => void;
|
||||
/**
|
||||
@@ -94,6 +98,7 @@ export class ListView extends Widget {
|
||||
});
|
||||
this.addChild(this.dropMarker); // keep the marker above the rows
|
||||
if (this.selectedId && !items.some((i) => i.id === this.selectedId)) this.selectedId = null;
|
||||
if (this.contextId && !items.some((i) => i.id === this.contextId)) this.contextId = null;
|
||||
this.layout();
|
||||
this.redraw();
|
||||
}
|
||||
@@ -106,6 +111,12 @@ export class ListView extends Widget {
|
||||
if (!silent) this.onSelect?.(id);
|
||||
}
|
||||
|
||||
private setContext(id: string | null): void {
|
||||
if (id === this.contextId) return;
|
||||
this.contextId = id;
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
/** Rows drawn as selected in addition to the selected one (the owner's multi-selection). */
|
||||
highlight(ids: Iterable<string>): void {
|
||||
this.highlighted = new Set(ids);
|
||||
@@ -153,7 +164,11 @@ export class ListView extends Widget {
|
||||
|
||||
override redraw(): void {
|
||||
for (const row of this.rows) {
|
||||
row.setSelected(row.item.id === this.selectedId || this.highlighted.has(row.item.id), this.focused);
|
||||
const id = row.item.id;
|
||||
row.setSelected(
|
||||
id === this.selectedId || id === this.contextId || this.highlighted.has(id),
|
||||
this.focused,
|
||||
);
|
||||
}
|
||||
this.emptyLabel.text = this.items.length === 0 ? this.emptyText : '';
|
||||
this.emptyLabel.position.set(Theme.padding, centreY(this.rowHeight, this.emptyLabel.height));
|
||||
@@ -169,9 +184,11 @@ export class ListView extends Widget {
|
||||
if (e.button === 0 && row.hitsChevron(row.toLocal(e.global).x)) {
|
||||
this.onToggle?.(item.id, !item.expanded);
|
||||
} else if (e.button === 2) {
|
||||
// A right-click inside a multi-selection keeps it (the menu acts on all of it).
|
||||
if (!this.highlighted.has(item.id)) this.select(item.id);
|
||||
this.onContextMenu?.(item.id, e.global.x, e.global.y);
|
||||
// Marked, not selected: selecting also loads the row into the editor, which would swap the
|
||||
// very tab out from under a menu opened to place something into it.
|
||||
const menu = this.onContextMenu?.(item.id, e.global.x, e.global.y);
|
||||
this.setContext(menu ? item.id : null);
|
||||
if (menu) menu.onClose = () => this.setContext(null);
|
||||
} else if (modifiers.ctrl || modifiers.shift) {
|
||||
this.onSelect?.(item.id, modifiers);
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user