Edit a Parametric EQ in a tab of its own (#370)
The EQ's editor floated over the editor as a window since #367. The user wants it as a tab for now. Whether any tab can float and dock again is weighed apart from this. - A double-click on the node asks for the tab through `EditorState.openEq`, as `openValues` does for a Variables tab. - The tab is a document `eq:<asset>:<node>`, one per EQ node. A second double-click brings the open one up. - Its name is the asset's, with `EQ 2`, `EQ 3` when the asset has more than one. The icon is the sound FX's. - The tab closes with its node, its asset, or a new project, through the same `syncDocuments` as every document. - `EqWindow` becomes `EqEditor`, a `VStack` with a `TabHeader`. The curve takes the room of the tab, 320 x 160 at least. - The window's title bar, dragging and Close button are gone. - Delete in the EQ's tab removes the picked band. It never falls through to the selection, which is still the EQ's node. - The hint below the curve is shorter, it was cut off at 1280 px. Tests: - e2e 34 (melektaus-atx): ALL PASSED, 96 checks, 2 rewritten. The tab opens on a double-click and goes with its node. - Gate (melektaus-atx): CHECK EXIT 0, 274 files, 4231 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
db15422372
commit
9ab1bbc507
6 files changed
+150
-129
No files matched your search
+13
-9
@@ -523,6 +523,7 @@ export async function sound(t) {
|
||||
// #367: a Parametric EQ behind the gain. A double-click on the node opens its editor, one on the
|
||||
// curve adds a peak there, a drag moves it as one undo step, the wheel narrows it, and the
|
||||
// voice passes one filter for it.
|
||||
// #370: the editor is a tab of its own, and goes with its node.
|
||||
await mutate(`
|
||||
const graph = __gm.state.project.assets.find((a) => a.id === ${JSON.stringify(fxId)}).graph;
|
||||
graph.nodes.push(${JSON.stringify(node('fx-eq', 'SoundFxParametricEq', -240, 0))});
|
||||
@@ -534,20 +535,23 @@ export async function sound(t) {
|
||||
const eqNode = await json("__gm.soundfx.nodeAt('fx-eq')");
|
||||
await t.doubleClick(eqNode.x, eqNode.y);
|
||||
const eqShown = await json('__gm.eq.shown()');
|
||||
const eqDocs = await json('__gm.documents()');
|
||||
const eqPeakAt = await json('__gm.eq.pointAt(1000, 6)');
|
||||
await t.doubleClick(eqPeakAt.x, eqPeakAt.y);
|
||||
const eqAdded = await json('{ bands: __gm.eq.bands(), fields: __gm.eq.fields() }');
|
||||
const bandNear = (band, hz, db) =>
|
||||
band && Math.abs(Math.log2(band.frequency / hz)) < 0.05 && Math.abs(band.gain - db) < 0.5;
|
||||
check(
|
||||
'a double-click opens the Parametric EQ’s editor, and one on its curve adds a peak there',
|
||||
'a double-click opens the Parametric EQ in a tab, and one on its curve adds a peak there',
|
||||
eqShown?.nodeId === 'fx-eq' &&
|
||||
eqDocs.active === `eq:${fxId}:fx-eq` &&
|
||||
eqDocs.docs.includes(`asset:${fxId}`) &&
|
||||
eqAdded.bands.length === 1 &&
|
||||
eqAdded.bands[0].type === 'peaking' &&
|
||||
bandNear(eqAdded.bands[0], 1000, 6) &&
|
||||
eqAdded.fields.type === 'Peaking' &&
|
||||
eqAdded.fields.slope === null,
|
||||
JSON.stringify({ eqShown, eqAdded }),
|
||||
JSON.stringify({ eqShown, eqDocs, eqAdded }),
|
||||
);
|
||||
const eqHandle = await json('__gm.eq.handleAt(0)');
|
||||
const eqTo = await json('__gm.eq.pointAt(4000, -6)');
|
||||
@@ -568,23 +572,23 @@ export async function sound(t) {
|
||||
JSON.stringify({ eqDragged, eqUndone, eqWheeled }),
|
||||
);
|
||||
await shot('34-parametric-eq');
|
||||
await evaluate(`__gm.openDocument(${JSON.stringify(`asset:${fxId}`)})`);
|
||||
await evaluate('__gm.sound.stopAll()');
|
||||
const eqBefore = await stats();
|
||||
await click(fxPlayAt.x, fxPlayAt.y);
|
||||
const eqHeard = await waitFor(stats, (s) => s.started > eqBefore.started);
|
||||
await evaluate('__gm.sound.stopAll()');
|
||||
const eqClose = await json('__gm.eq.closeAt()');
|
||||
await click(eqClose.x, eqClose.y);
|
||||
check(
|
||||
'the voice passes the gain and the band, and the editor closes by its button',
|
||||
eqHeard.last?.stages === 2 && (await json('__gm.eq.shown()')) === null,
|
||||
JSON.stringify(eqHeard.last),
|
||||
);
|
||||
await mutate(`
|
||||
const graph = __gm.state.project.assets.find((a) => a.id === ${JSON.stringify(fxId)}).graph;
|
||||
graph.nodes = graph.nodes.filter((n) => n.id !== 'fx-eq');
|
||||
graph.edges = graph.edges.filter((e) => !['fx-e10', 'fx-e11'].includes(e.id));
|
||||
graph.edges.push(${JSON.stringify(edge('fx-e3', 'fx-gain', 'out', 'fx-out', 'in'))});`);
|
||||
const eqDocsAfter = await json('__gm.documents()');
|
||||
check(
|
||||
'the voice passes the gain and the band, and the EQ’s tab goes with its node',
|
||||
eqHeard.last?.stages === 2 && !eqDocsAfter.docs.some((doc) => doc.startsWith('eq:')),
|
||||
JSON.stringify({ last: eqHeard.last, eqDocsAfter }),
|
||||
);
|
||||
await showTab('scene');
|
||||
await mutate(`
|
||||
const graph = __gm.state.project.assets.find((a) => a.id === 'sn-logic').graph;
|
||||
|
||||
Reference in new issue
Block a user