Trim needless comments and fix the picker width
Remove comments that only restate the code, keeping the section banners and the notes that carry a reason, a measured fact or an external contract. ButtonOptions.width claimed to be a fixed width but only set minWidth, so the Graph and Texture pickers grew with the name they showed. It is now what it promises, with a separate minWidth for the buttons that have two forms. Narrow buttons shrink their padding, otherwise a 24px icon button had 8px left for its glyph and truncate dropped it entirely. __gm.labelsDropped() guards that class: the overflow guard only saw text that was too wide, never a label truncated to nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f16801aef3
commit
4d84231b9f
115 files changed
+707
-1564
No files matched your search
@@ -37,13 +37,8 @@ interface PortAnchor {
|
||||
}
|
||||
|
||||
/**
|
||||
* The Graph tab's canvas: the node graph of one behavior asset. Which behavior that is comes
|
||||
* from the outside (`show`), so the canvas never has an opinion about the selection – the
|
||||
* GraphEditor around it does.
|
||||
*
|
||||
* Interactions: drag nodes, drag from port to port to connect, right-click the canvas for
|
||||
* the node palette, right-click a node or port for delete/disconnect, wheel to zoom, drag
|
||||
* the empty canvas to pan. All edits go through commands (undoable).
|
||||
* The node graph of one behavior asset. Which behavior that is comes from the outside (`show`),
|
||||
* so the canvas never has an opinion about the selection – the GraphEditor around it does.
|
||||
*/
|
||||
export class GraphCanvas extends Widget {
|
||||
private readonly bg = new Graphics();
|
||||
@@ -94,19 +89,17 @@ export class GraphCanvas extends Widget {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// Only the highlight of the selected node depends on the selection; which graph is shown
|
||||
// is the editor's decision.
|
||||
// Only the highlight of the selected node depends on the selection.
|
||||
state.on('selection', () => this.redraw());
|
||||
state.on('project', () => this.sync());
|
||||
state.on('mode', () => this.redraw());
|
||||
}
|
||||
|
||||
/** The behavior whose graph is shown, if any. */
|
||||
get currentAssetId(): string | null {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
/** Shows a behavior's graph. Switching refits the view, since the next graph is a new subject. */
|
||||
/** Switching refits the view, since the next graph is a new subject. */
|
||||
show(assetId: string | null): void {
|
||||
if (this.assetId === assetId) return;
|
||||
this.assetId = assetId;
|
||||
@@ -115,16 +108,12 @@ export class GraphCanvas extends Widget {
|
||||
this.fitView();
|
||||
}
|
||||
|
||||
/** Recentres the graph in the view – the "reset view" the header offers. */
|
||||
fit(): void {
|
||||
this.needsFit = true;
|
||||
this.fitView();
|
||||
}
|
||||
|
||||
/**
|
||||
* The header's "Add node": the same palette the right-click opens, dropped in the middle of
|
||||
* the view, since a button press says nothing about where the node should go.
|
||||
*/
|
||||
/** Drops the node in the middle of the view, since a button press names no position. */
|
||||
addNodeFrom(anchor: Widget): void {
|
||||
if (!this.assetId || this.state.mode !== 'edit') return;
|
||||
const centre = this.screenToGraph(this.w / 2, this.h / 2);
|
||||
@@ -148,8 +137,7 @@ export class GraphCanvas extends Widget {
|
||||
private sync(): void {
|
||||
const asset = this.asset();
|
||||
if (!asset) {
|
||||
// A behavior that was deleted (or undone away) must not linger as a graph of a thing
|
||||
// that no longer exists, so the canvas drops it rather than waiting to be told.
|
||||
// A behavior that was deleted (or undone away) must not linger as a graph.
|
||||
this.assetId = null;
|
||||
for (const view of this.views.values()) view.destroy({ children: true });
|
||||
this.views.clear();
|
||||
@@ -393,7 +381,6 @@ export class GraphCanvas extends Widget {
|
||||
return;
|
||||
}
|
||||
const playing = this.state.mode === 'play';
|
||||
// The header names the behavior; the hint only says what the canvas itself is up to.
|
||||
this.hint.text = asset.graph.nodes.length === 0 ? 'Right-click to add a node' : playing ? 'live' : '';
|
||||
|
||||
const trace = playing ? this.traceProvider(asset.id) : null;
|
||||
@@ -447,8 +434,7 @@ export class GraphCanvas extends Widget {
|
||||
this.grid.clear();
|
||||
const step = GRID_STEP * this.zoom;
|
||||
if (step < MIN_GRID_STEP) return;
|
||||
// `pan` is where the graph origin sits on screen – but the graph has no origin worth
|
||||
// marking, so no axis lines.
|
||||
// `pan` is where the graph origin sits on screen, but no axis lines: it marks nothing.
|
||||
drawGrid(this.grid, {
|
||||
originX: this.pan.x,
|
||||
originY: this.pan.y,
|
||||
@@ -479,7 +465,7 @@ export class GraphCanvas extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
/** Horizontal S-curve between two points, as used by every node editor. */
|
||||
/** Horizontal S-curve between two points. */
|
||||
function bezier(g: Graphics, x1: number, y1: number, x2: number, y2: number): void {
|
||||
const dx = Math.max(40, Math.abs(x2 - x1) * 0.5);
|
||||
g.moveTo(x1, y1).bezierCurveTo(x1 + dx, y1, x2 - dx, y2, x2, y2);
|
||||
|
||||
@@ -9,12 +9,8 @@ import { GraphCanvas } from './GraphCanvas';
|
||||
const INFO_WIDTH = 150;
|
||||
|
||||
/**
|
||||
* The Graph tab: a picker for which behavior is being worked on, the actions that act on the
|
||||
* whole graph, and the canvas the nodes are drawn on.
|
||||
*
|
||||
* It follows the selection – selecting a behavior asset, a placed behavior or one of its nodes
|
||||
* shows that graph – but it keeps showing the last behavior when the selection moves elsewhere.
|
||||
* Otherwise clicking a sprite to check its position would empty the graph you are editing.
|
||||
* The Graph tab. It follows the selection, but keeps showing the last behavior when the
|
||||
* selection moves elsewhere – clicking a sprite must not empty the graph being edited.
|
||||
*/
|
||||
export class GraphEditor extends VStack {
|
||||
/** The canvas itself: the live redraw while playing and the check scripts reach for it. */
|
||||
@@ -38,8 +34,7 @@ export class GraphEditor extends VStack {
|
||||
header.add(new Button('Fit', { onClick: () => this.canvas.fit() }));
|
||||
header.add(new Spacer(), 1);
|
||||
this.info = new Label('', { style: 'small', align: 'right' });
|
||||
// Reserved rather than measured: the label is built empty, and a stack sizes it once from
|
||||
// the text it had at the time – so without a floor the readout is clipped from then on.
|
||||
// Reserved, not measured: a stack sizes the label once, from the empty text it was built with.
|
||||
this.info.minWidth = INFO_WIDTH;
|
||||
header.add(this.info);
|
||||
|
||||
@@ -63,6 +58,11 @@ export class GraphEditor extends VStack {
|
||||
return this.canvas.currentAssetId;
|
||||
}
|
||||
|
||||
/** For the check scripts: the picker's width must not follow the name it happens to show. */
|
||||
get pickerWidth(): number {
|
||||
return this.picker.w;
|
||||
}
|
||||
|
||||
private followSelection(): void {
|
||||
const id = this.state.selectedBehaviorId;
|
||||
// Null merely means "the selection is about something else"; the tab keeps its subject.
|
||||
@@ -93,8 +93,7 @@ export class GraphEditor extends VStack {
|
||||
}
|
||||
|
||||
private refresh(): void {
|
||||
// A deleted behavior leaves the canvas empty; rather than showing nothing at all the tab
|
||||
// falls back to whatever behavior is left, the way it starts out on the first one.
|
||||
// A deleted behavior leaves the canvas empty; fall back to whatever behavior is left.
|
||||
if (!this.asset()) this.canvas.show(this.behaviors()[0]?.id ?? null);
|
||||
const asset = this.asset();
|
||||
this.picker.setText(asset ? asset.name : 'No behavior');
|
||||
|
||||
@@ -20,11 +20,7 @@ import type { PropertyDef } from '../../core/properties/PropertySchema';
|
||||
import { formatNumber, Theme, textStyle, type TextStyleToken, truncate, Widget } from '../../ui';
|
||||
import { keyLabel } from '../inspector/fieldFactory';
|
||||
|
||||
/**
|
||||
* Node geometry in graph units – what the node lays itself out, draws and hit-tests with.
|
||||
* Exported as the single source of these numbers: edge anchors have to land exactly on the
|
||||
* ports drawn here.
|
||||
*/
|
||||
/** Node geometry in graph units; exported because edge anchors must land exactly on these ports. */
|
||||
export const NODE_WIDTH = 176;
|
||||
export const TITLE_HEIGHT = 24;
|
||||
export const ROW_HEIGHT = 20;
|
||||
@@ -72,11 +68,7 @@ export interface PortHit {
|
||||
isOutput: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws one graph node: a title bar coloured by category, a line per setting and a row per
|
||||
* port pair (inputs left, outputs right). Everything is derived from the NodeDef; the view
|
||||
* never interprets what a node does. Positioned in graph coordinates by the GraphCanvas.
|
||||
*/
|
||||
/** Draws one graph node entirely from its NodeDef; positioned in graph coordinates by GraphCanvas. */
|
||||
export class NodeView extends Widget {
|
||||
private readonly box = new Graphics();
|
||||
private readonly ports = new Graphics();
|
||||
@@ -103,7 +95,7 @@ export class NodeView extends Widget {
|
||||
this.hitArea = new Rectangle(-PORT_HIT_RADIUS, 0, w + PORT_HIT_RADIUS * 2, h);
|
||||
}
|
||||
|
||||
/** Re-reads params and connections; rebuilds the text lines. `project` resolves asset references (Spawn, Is asset). */
|
||||
/** `project` resolves asset references (Spawn, Is asset). */
|
||||
sync(node: GraphNode, graph: Graph, asset: BehaviorAsset | null, project: Project | null): void {
|
||||
this.paramLines = this.def.params.map(
|
||||
(p) => `${p.label}: ${formatParam(p, node.params[p.key] ?? p.default, asset, project)}`,
|
||||
@@ -147,7 +139,6 @@ export class NodeView extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
/** Which port (if any) sits under a local point. */
|
||||
portAt(localX: number, localY: number): PortHit | null {
|
||||
for (const { port, isOutput, x, y } of this.portCentres()) {
|
||||
if (Math.hypot(localX - x, localY - y) <= PORT_HIT_RADIUS) return { port, isOutput };
|
||||
@@ -264,8 +255,7 @@ function formatParam(
|
||||
}
|
||||
|
||||
function formatValue(value: Value): string {
|
||||
// Whole numbers stay bare to keep nodes narrow; fractions get the same precision as the
|
||||
// field that edits them, so a node never rounds harder than the inspector.
|
||||
// Whole numbers stay bare to keep nodes narrow; fractions match the field that edits them.
|
||||
if (typeof value === 'number') return Number.isInteger(value) ? String(value) : formatNumber(value);
|
||||
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
||||
return asString(value);
|
||||
|
||||
Reference in new issue
Block a user