Lists add and delete items right in the list header and rows
The layer, frame and shape lists still had their add and delete buttons scattered below the list, each in its own layout. They now live in the shared SortableList, so every list works the same way. - SortableList gets an optional header with the title on the left and a + on the right, plus an optional x per row next to the eye. - The keepOne option hides the x on the last remaining row, so a pixel art image and a tilemap always keep one layer. - Pixel art layers and frames and tilemap layers use header, + and x. Only Merge down and Duplicate stay below the lists. - Vector art shapes get the x but no +. Add shape stays below the list and Delete shapes stays for a multi-selection. - The section headings in the pixel art column use the same style as the list titles and the inspector. Findings: - The wait loop added to check 47i for #196 only waited for the right half of the shape. If the texture landed between the two reads, the left half was still blue and the check failed. It now waits for both halves. Tests: - ./check.sh: 138 files, 2052 tests, exit 0. - GM_STEPS=39,40,47,48 (fresh): ALL PASSED, 156 checks (was 154). - GM_STEPS=47,48 (fresh, final): ALL PASSED, 86 checks. - Two new e2e checks: x on a shape (47) and x on a frame (48). The tilemap check in 40 now uses the x instead of Delete layer. - Counter-check: with the x not reporting to its owner, exactly the two new checks fail. - Check total 1266 in README and wiki is computed (1264 + 2). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5b9b57fd19
commit
ae0247d86f
11 files changed
+264
-100
No files matched your search
+28
-9
@@ -32,7 +32,7 @@ import { currentFontPicker } from './font/FontPicker';
|
||||
import { currentNodePalette } from './graph/NodePalette';
|
||||
import { currentSoundPicker } from './sound/SoundPicker';
|
||||
import { currentImagePicker } from './texture/ImagePicker';
|
||||
import { currentTilemapWidgets } from './panels/TilemapPanel';
|
||||
import { currentTilemapWidgets, paintedLayerId, paintedTilemap } from './panels/TilemapPanel';
|
||||
import { currentResizeDialog } from './pixelart/ResizeCanvasDialog';
|
||||
import { currentGradientBar, currentShapeList } from './inspector/shapeSection';
|
||||
import type { SnapToggle } from './graph/SnapToggle';
|
||||
@@ -514,6 +514,13 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
...Object.fromEntries(Object.entries(side.layerButtons).map(([k, b]) => [`layer:${k}`, b])),
|
||||
...Object.fromEntries(Object.entries(side.frameButtons).map(([k, b]) => [`frame:${k}`, b])),
|
||||
};
|
||||
// The plus in a list's header is no widget of its own.
|
||||
const list = name === 'layer:add' ? side.layers : name === 'frame:add' ? side.frames : null;
|
||||
if (list) {
|
||||
side.reveal(list);
|
||||
const at = list.addGlobal();
|
||||
return at ? { ...at, enabled: true, text: '+' } : null;
|
||||
}
|
||||
const widget = byName[name];
|
||||
if (!widget || !widget.visible) return null;
|
||||
if (widget !== editor.pixelArtEditor.resizeButton) side.reveal(widget);
|
||||
@@ -530,7 +537,7 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
},
|
||||
/**
|
||||
* The centre of a layer's or frame's row, scrolled into view, with the centre of its picture;
|
||||
* `eye` is the layer's switch.
|
||||
* `eye` is the layer's switch, `remove` the cross (null on the last one).
|
||||
*/
|
||||
layerRow: (layerId: string) => {
|
||||
const side = editor.pixelArtEditor.side;
|
||||
@@ -542,6 +549,7 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
y: row.y + row.h / 2,
|
||||
thumb: side.layers.thumbGlobal(layerId),
|
||||
eye: side.layers.eyeGlobal(layerId),
|
||||
remove: side.layers.removeGlobal(layerId),
|
||||
}
|
||||
: null;
|
||||
},
|
||||
@@ -550,7 +558,12 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
side.reveal(side.frames);
|
||||
const row = side.frames.rowGlobal(frameId);
|
||||
return row
|
||||
? { x: row.x + row.w / 2, y: row.y + row.h / 2, thumb: side.frames.thumbGlobal(frameId) }
|
||||
? {
|
||||
x: row.x + row.w / 2,
|
||||
y: row.y + row.h / 2,
|
||||
thumb: side.frames.thumbGlobal(frameId),
|
||||
remove: side.frames.removeGlobal(frameId),
|
||||
}
|
||||
: null;
|
||||
},
|
||||
/** The ids as the two lists show them, top first. */
|
||||
@@ -647,6 +660,8 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
*/
|
||||
shapeRows: () => currentShapeList()?.shownIds ?? null,
|
||||
shapeRowAt: (shapeId: string) => currentShapeList()?.rowGlobal(shapeId) ?? null,
|
||||
/** The cross on a shape's row. */
|
||||
shapeRemoveAt: (shapeId: string) => currentShapeList()?.removeGlobal(shapeId) ?? null,
|
||||
/** The Paint tool (#186): its button, the "Handles" menu, and which part it shows. */
|
||||
paintButton: () => centreOf(vectorArtEditor.paintButton),
|
||||
paintTarget: () => ({
|
||||
@@ -767,10 +782,8 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
const swatch = palette?.swatchGlobal(assetId);
|
||||
return palette && swatch ? { ...swatch, columns: palette.columns } : null;
|
||||
},
|
||||
addLayerAt: () => {
|
||||
const button = currentTilemapWidgets()?.addLayer;
|
||||
return button ? centreOf(button) : null;
|
||||
},
|
||||
/** The plus in the layer list's header. */
|
||||
addLayerAt: () => currentTilemapWidgets()?.layers.addGlobal() ?? null,
|
||||
/** The name field of the painted layer and the Show dropdown, with what they hold (#138). */
|
||||
layerNameAt: () => {
|
||||
const field = currentTilemapWidgets()?.layerName;
|
||||
@@ -795,9 +808,15 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
// `y` is the row's own line, so a check can tell whether it is inside the panel.
|
||||
return { id, y: name.y, name, visible: list.eyeGlobal(id) };
|
||||
},
|
||||
/** The cross on the painted layer's row; `enabled` is false where the row has none (the last layer). */
|
||||
removeLayerAt: () => {
|
||||
const button = currentTilemapWidgets()?.removeLayer;
|
||||
return button ? { ...centreOf(button), enabled: button.enabled } : null;
|
||||
const widgets = currentTilemapWidgets();
|
||||
const target = paintedTilemap(state);
|
||||
if (!widgets || !target) return null;
|
||||
const layerId = paintedLayerId(state, target.asset);
|
||||
const at = widgets.layers.removeGlobal(layerId);
|
||||
const row = widgets.layers.rowGlobal(layerId);
|
||||
return at ? { ...at, enabled: true } : row ? { x: row.x, y: row.y, enabled: false } : null;
|
||||
},
|
||||
/** Where a cell sits on screen – for a click that paints it or a pixel that proves it. */
|
||||
cellAt: (objectId: string, x: number, y: number) => viewport.cellScreen(objectId, x, y),
|
||||
|
||||
Reference in new issue
Block a user