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>
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
/**
|
|
* The magnet in the tab headers (#194): one icon toggle for every tab that snaps, graph canvas and
|
|
* vector art alike. Driven through the button's own `onClick`, the path a click takes.
|
|
*/
|
|
import { beforeAll, describe, expect, it, vi } from 'vitest';
|
|
import { CanvasTextMetrics } from 'pixi.js';
|
|
import { SnapToggle } from '../../src/editor/graph/SnapToggle';
|
|
import { Theme } from '../../src/ui';
|
|
|
|
// Node has no DOMParser to read the file with; what the magnet looks like is the icon tests' job.
|
|
vi.mock('../../src/ui/svg', () => ({ drawSvg: () => undefined, tintSvg: (svg: string) => svg }));
|
|
|
|
beforeAll(() => {
|
|
CanvasTextMetrics.measureText = (() => ({ width: 10, height: 10, lines: [], lineWidths: [] })) as never;
|
|
});
|
|
|
|
function target() {
|
|
const calls: boolean[] = [];
|
|
return { calls, setSnapping: (on: boolean) => calls.push(on) };
|
|
}
|
|
|
|
describe('SnapToggle', () => {
|
|
it('is a square icon button without a caption', () => {
|
|
const snap = new SnapToggle(() => target());
|
|
expect(snap.widgets).toEqual([snap.button]);
|
|
expect(snap.button.measure().w).toBe(Theme.controlHeight);
|
|
expect(snap.button.text).toBe('');
|
|
});
|
|
|
|
it('starts off and switches the target on and off again, lit while on', () => {
|
|
const canvas = target();
|
|
const snap = new SnapToggle(() => canvas);
|
|
expect(snap.snapping).toBe(false);
|
|
expect(snap.button.toggled).toBe(false);
|
|
|
|
snap.button.onClick?.();
|
|
expect(snap.snapping).toBe(true);
|
|
expect(snap.button.toggled).toBe(true);
|
|
|
|
snap.button.onClick?.();
|
|
expect(snap.snapping).toBe(false);
|
|
expect(snap.button.toggled).toBe(false);
|
|
expect(canvas.calls).toEqual([true, false]);
|
|
});
|
|
|
|
it('asks for the target at the click, so a canvas mounted later is the one switched', () => {
|
|
let canvas = target();
|
|
const snap = new SnapToggle(() => canvas);
|
|
const later = target();
|
|
canvas = later;
|
|
snap.set(true);
|
|
expect(later.calls).toEqual([true]);
|
|
});
|
|
});
|