Snap is a magnet button in every graph and vector art tab (#194)
game-maker / gate (push) Successful in 2m13s
game-maker / suite (push) Successful in 7m33s

The snap toggle was a checkbox with the word "Snap" next to Fit, and
the vector art tab had its own "Grid" button for the same idea. Both
now share one square icon button, so snapping looks and works the
same wherever it exists.

- SnapToggle is a square toggle button with a magnet icon and no
  text. It lights up blue when snapping is on, like the other toggle
  buttons.
- The magnet is drawn with cubic curves instead of arcs, because the
  Pixi SVG parser draws arcs as jagged edges.
- The vector art tab drops its Grid button and uses the same
  SnapToggle. It still snaps to 8 px, the graph tabs to 24 px.
- SnapToggle accepts anything with setSnapping(on). The vector art
  canvas method is renamed from setSnap to setSnapping for that.
- Every tab exposes the button as __gm.<tab>.snapButton(). Modules
  06, 26 and 36 click through that hook instead of the label "Snap"
  plus 30 px.

Tests:
- ./check.sh: 129 files, 1903 tests (was 128 / 1897)
- 3 new tests in tests/editor/snap-toggle.test.ts, 3 in the icon tests
- GM_STEPS=1-6,26,36,47 fresh: ALL PASSED, 346 checks
- GM_STEPS=47: ALL PASSED, 32 checks (was 29)
- Counter-checks: toggle not switching its target fails 2 unit
  tests, the canvas ignoring the switch fails module 47
- Check total 1188 in README and wiki is computed (1184 + 4), the
  full run is left to CI

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-21 06:25:56 +02:00
1 parent ad62a0f525
commit 90627ccf47
17 files changed
+181 -43

No files matched your search

+13
View File
@@ -33,6 +33,7 @@ import { currentSoundPicker } from './sound/SoundPicker';
import { currentImagePicker } from './texture/ImagePicker';
import { currentTilemapWidgets } from './panels/TilemapPanel';
import { currentShapeList } from './inspector/shapeSection';
import type { SnapToggle } from './graph/SnapToggle';
import type { EditorState, TilemapTool } from './state';
import { rectOf, textProbe } from './textProbe';
@@ -56,6 +57,13 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
const p = widget.getGlobalPosition();
return { x: p.x + widget.w / 2, y: p.y + widget.h / 2 };
};
/** A tab's magnet (#194): where to click it, how big it is drawn, and whether it is on. */
const snapButton = (snap: SnapToggle) => ({
...centreOf(snap.button),
w: snap.button.w,
h: snap.button.h,
toggled: snap.button.toggled,
});
const { eachText, locate, locateAll } = textProbe(app);
/** Fill colour of a visible text, as `#rrggbb` – what tells an override label from a plain one. */
const textColor = (text: string): string | null => {
@@ -338,6 +346,7 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
// drawing area.
center: () => centreOf(graphEditor.canvas),
snapping: () => graphEditor.canvas.snapEnabled,
snapButton: () => snapButton(graphEditor.snap),
zoom: () => graphEditor.canvas.zoomLevel,
crumbs: () => graphEditor.breadcrumb.crumbsDrawn(),
},
@@ -349,6 +358,7 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
canvasRect: () => editor.stateMachineEditor.canvas.canvasRect(),
center: () => centreOf(editor.stateMachineEditor.canvas),
snapping: () => editor.stateMachineEditor.canvas.snapEnabled,
snapButton: () => snapButton(editor.stateMachineEditor.snap),
/** Where a state's hidden pin sits; it cannot be grabbed – `portGrabbable` is the check. */
portAt: (nodeId: string, port: string, isOutput: boolean) =>
editor.stateMachineEditor.canvas.portGlobal(nodeId, port, isOutput),
@@ -372,6 +382,7 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
particles: {
current: () => editor.particlesEditor.currentAssetId,
info: () => editor.particlesEditor.shownInfo,
snapButton: () => snapButton(editor.particlesEditor.snap),
nodeAt: (nodeId: string) => editor.particlesEditor.canvas.nodeGlobal(nodeId),
nodeLabel: (nodeId: string) => editor.particlesEditor.canvas.nodeLabel(nodeId),
nodeRect: (nodeId: string) => editor.particlesEditor.canvas.nodeRect(nodeId),
@@ -405,6 +416,7 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
emptySpot: () => editor.shaderEditor.canvas.emptySpotGlobal(),
center: () => centreOf(editor.shaderEditor.canvas),
snapping: () => editor.shaderEditor.canvas.snapEnabled,
snapButton: () => snapButton(editor.shaderEditor.snap),
},
/** The console panel: its lines as kept, its drawn height, and the divider above it. */
console: {
@@ -456,6 +468,7 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
rect: () => vectorArtEditor.canvasRect(),
tool: () => vectorArtEditor.canvas.currentTool,
snapping: () => vectorArtEditor.canvas.snapping,
snapButton: () => snapButton(vectorArtEditor.snap),
/** Whether the pen has a line open that the next click would add a point to. */
drawing: () => vectorArtEditor.canvas.drawing,
/** The shapes as the model holds them, so a check can read points and handles exactly. */