The tab from #378 shows a Compressor as an editor. The user wanted it to look like a device as well, after a brushed aluminium plug-in, with a meter of the gain reduction instead of the pictures. The tab stays and is switched to per node. - Hardware look is a switch under Settings, on by default for new and existing Compressors. Only the editor reads it. - The panel is 820x280 px and opens straight in a window of that size. The window has no sizing corner and no Dock. A pane says so through `fixedSize`, and `keptSize` keeps the window's middle when it changes to that size. - A Compressor in this look never sits in the tab row. Opening one floats it, and switching the look on moves its tab into the window. - The panel has chrome knobs (threshold and makeup large), a meter with a needle that moves like a VU's, an LCD with the name and the reading, a lever with a lamp for auto-gain, and LEDs for the level going out. Everything is drawn in code. - The Knob takes a size and a chrome look, and leaves out its caption when it has no name, so the panel engraves its own. - `autoGainOn` is now `switchOn`, because the new switch reads the same way. - Where parts sit, the meter's scale, the needle's speed, the LEDs and the plate's grain are in `hardwareView.ts`, apart from the drawing. Decided myself: - The meter's scale runs by the square root, so the first dB is about as wide as the last nine. It is red from 10 dB on. - The LEDs light one every 3 dB from -39 to 0 dB and fall by 20 dB a second. - Switching the look changes the graph's fingerprint, like any other setting of a node. The sound FX is built again once. Findings, fixed on the way: - Pixi's `arc` goes on from the last point of the path. The meter's scale had a line from the corner of the face until each arc got a `moveTo` to its own start. - A text put into a Graphics makes Pixi warn that only Containers take children. The LCD is a Container now. - The first browser check read the needle at one moment and missed the short sound. The panel keeps the most its needle and LEDs showed. Tests: - Unit: hardware-view (10 new), documents (1 new) - e2e 34 (melektaus-atx): ALL PASSED, 122 checks (4 new). Played, the needle reached 20.9 dB and 5 LEDs lit. - Gate (melektaus-atx): CHECK EXIT 0, 283 files, 4355 tests (11 new) - Counter-checks: a window that keeps its corner instead of its middle, and a linear meter scale, each turn exactly their test red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
128 lines
5.4 KiB
TypeScript
128 lines
5.4 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { closeDocument, neighbourOf, openDocument, replaceDocument } from '../../src/editor/documents';
|
||
import {
|
||
keptSize,
|
||
leastBox,
|
||
MIN_WINDOW_HEIGHT,
|
||
MIN_WINDOW_WIDTH,
|
||
onScreen,
|
||
openingBox,
|
||
sized,
|
||
} from '../../src/editor/floating';
|
||
import { tabRowLayout } from '../../src/editor/tabRow';
|
||
|
||
const row = (docs: string[], active: string | null) => ({ docs, active });
|
||
|
||
describe('open documents', () => {
|
||
it('puts a new document right of the one showing', () => {
|
||
expect(openDocument(row(['a', 'b', 'c'], 'a'), 'x')).toEqual(row(['a', 'x', 'b', 'c'], 'x'));
|
||
});
|
||
|
||
it('puts it last while a fixed tab is showing', () => {
|
||
expect(openDocument(row(['a', 'b'], null), 'x')).toEqual(row(['a', 'b', 'x'], 'x'));
|
||
expect(openDocument(row([], null), 'x')).toEqual(row(['x'], 'x'));
|
||
});
|
||
|
||
it('only brings an open document up – one tab per document', () => {
|
||
expect(openDocument(row(['a', 'b', 'c'], 'c'), 'a')).toEqual(row(['a', 'b', 'c'], 'a'));
|
||
});
|
||
|
||
it('shows the left neighbour after the one showing closes', () => {
|
||
expect(closeDocument(row(['a', 'b', 'c'], 'b'), 'b')).toEqual(row(['a', 'c'], 'a'));
|
||
});
|
||
|
||
it('shows the right one when the first closes, and nothing when the last goes', () => {
|
||
expect(closeDocument(row(['a', 'b'], 'a'), 'a')).toEqual(row(['b'], 'b'));
|
||
expect(closeDocument(row(['a'], 'a'), 'a')).toEqual(row([], null));
|
||
});
|
||
|
||
it('keeps what is showing when another tab closes', () => {
|
||
expect(closeDocument(row(['a', 'b', 'c'], 'c'), 'a')).toEqual(row(['b', 'c'], 'c'));
|
||
expect(closeDocument(row(['a', 'b'], null), 'b')).toEqual(row(['a'], null));
|
||
});
|
||
|
||
it('ignores a document that is not open', () => {
|
||
const before = row(['a'], 'a');
|
||
expect(closeDocument(before, 'x')).toBe(before);
|
||
});
|
||
|
||
it('lets a tab go on to another document in its place', () => {
|
||
expect(replaceDocument(row(['a', 'b', 'c'], 'b'), 'b', 'x')).toEqual(row(['a', 'x', 'c'], 'x'));
|
||
});
|
||
|
||
it('drops the other tab of a document it goes on to', () => {
|
||
expect(replaceDocument(row(['a', 'b', 'c'], 'a'), 'a', 'c')).toEqual(row(['c', 'b'], 'c'));
|
||
});
|
||
|
||
it('leaves the row alone for a tab that is not open', () => {
|
||
const before = row(['a'], 'a');
|
||
expect(replaceDocument(before, 'x', 'y')).toBe(before);
|
||
expect(replaceDocument(before, 'a', 'a')).toBe(before);
|
||
});
|
||
});
|
||
|
||
describe('what shows when a document goes to a window (#371)', () => {
|
||
const all = () => true;
|
||
|
||
it('picks the left neighbour, else the right one, else none', () => {
|
||
expect(neighbourOf(['a', 'c'], 1, all)).toBe('a');
|
||
expect(neighbourOf(['b', 'c'], 0, all)).toBe('b');
|
||
expect(neighbourOf([], 0, all)).toBeNull();
|
||
});
|
||
|
||
it('passes over documents whose kind is in a window', () => {
|
||
const tabs = (doc: string) => !doc.startsWith('fx');
|
||
expect(neighbourOf(['a', 'fx1', 'fx2', 'b'], 3, tabs)).toBe('a');
|
||
expect(neighbourOf(['fx1', 'fx2', 'b'], 1, tabs)).toBe('b');
|
||
expect(neighbourOf(['fx1', 'fx2'], 1, tabs)).toBeNull();
|
||
});
|
||
});
|
||
|
||
describe('a tab in a window (#371)', () => {
|
||
it('opens in the middle of the screen, every further window a step lower right', () => {
|
||
expect(openingBox(1280, 800, 0)).toEqual({ x: 320, y: 160, w: 640, h: 480 });
|
||
expect(openingBox(1280, 800, 2)).toEqual({ x: 368, y: 208, w: 640, h: 480 });
|
||
});
|
||
|
||
it('is held whole on the screen when it is moved past an edge', () => {
|
||
expect(onScreen({ x: 900, y: -40, w: 640, h: 480 }, 1280, 800)).toEqual({ x: 640, y: 0, w: 640, h: 480 });
|
||
expect(onScreen({ x: 100, y: 100, w: 640, h: 480 }, 500, 400)).toEqual({ x: 0, y: 0, w: 500, h: 400 });
|
||
});
|
||
|
||
it('is sized from its lower right corner, down to a floor and out to the screen’s edge', () => {
|
||
const box = { x: 600, y: 300, w: 400, h: 300 };
|
||
expect(sized(box, 100, 50, 1280, 800)).toEqual({ ...box, w: 500, h: 350 });
|
||
expect(sized(box, -500, -500, 1280, 800)).toEqual({ ...box, w: MIN_WINDOW_WIDTH, h: MIN_WINDOW_HEIGHT });
|
||
expect(sized(box, 900, 900, 1280, 800)).toEqual({ ...box, w: 680, h: 500 });
|
||
});
|
||
|
||
it('is held at the least its pane needs, but never larger than the screen (#378)', () => {
|
||
const least = leastBox(700.4, 450);
|
||
expect(least).toEqual({ w: 701, h: 450 });
|
||
expect(leastBox(100, 100)).toEqual({ w: MIN_WINDOW_WIDTH, h: MIN_WINDOW_HEIGHT });
|
||
const box = { x: 100, y: 100, w: 800, h: 600 };
|
||
expect(sized(box, -500, -500, 1280, 800, least)).toEqual({ ...box, w: 701, h: 450 });
|
||
expect(onScreen({ ...box, w: 400, h: 300 }, 1280, 800, least)).toEqual({ ...box, w: 701, h: 450 });
|
||
expect(onScreen({ ...box, w: 400, h: 300 }, 600, 400, least)).toEqual({ x: 0, y: 0, w: 600, h: 400 });
|
||
});
|
||
|
||
it('takes the size of a pane of one size around the middle it had, and keeps a box of that size (#379)', () => {
|
||
const box = { x: 100, y: 100, w: 800, h: 600 };
|
||
expect(keptSize(box, { w: 400, h: 200 })).toEqual({ x: 300, y: 300, w: 400, h: 200 });
|
||
const kept = { x: 7, y: 9, w: 400, h: 200 };
|
||
expect(keptSize(kept, { w: 400, h: 200 })).toBe(kept);
|
||
});
|
||
});
|
||
|
||
describe('tab row without an active tab', () => {
|
||
it('fills the row in order when a kind with no document is showing', () => {
|
||
const tabs = [
|
||
{ key: 'a', width: 50 },
|
||
{ key: 'b', width: 50 },
|
||
{ key: 'c', width: 50 },
|
||
];
|
||
const layout = tabRowLayout({ tabs, available: 132, gap: 2, overflowWidth: 28, active: null });
|
||
expect(layout).toEqual({ shown: ['a', 'b'], hidden: ['c'] });
|
||
});
|
||
});
|