diff --git a/src/editor/debug/soundHooks.ts b/src/editor/debug/soundHooks.ts index 97346d49..85391802 100644 --- a/src/editor/debug/soundHooks.ts +++ b/src/editor/debug/soundHooks.ts @@ -39,6 +39,8 @@ export function soundHooks({ state, editor }: HookContext) { pickedClip: () => soundEditor.canvas.pickedClip()?.id ?? null, /** The middle of the clip strip at a second of the file, on screen. */ stripAt: (seconds: number) => soundEditor.canvas.stripAt(seconds), + /** The clips drawn faint over the wave, by name: all but the picked one. */ + faintClips: () => soundEditor.canvas.faintClips(), fadeInField: () => ({ ...centreOf(soundEditor.fadeInField), text: soundEditor.fadeInField.value }), fadeOutField: () => ({ ...centreOf(soundEditor.fadeOutField), text: soundEditor.fadeOutField.value }), /** The curve dropdowns show a picture of their curve; the tooltip names it. */ diff --git a/src/editor/sound/WaveCanvas.ts b/src/editor/sound/WaveCanvas.ts index 28dd5b27..43ffee08 100644 --- a/src/editor/sound/WaveCanvas.ts +++ b/src/editor/sound/WaveCanvas.ts @@ -505,6 +505,7 @@ export class WaveCanvas extends Widget { this.drawStrip(asset, wave); const span = this.shownClipSpan(); if (span) this.drawCut(wave, span, mid, half, room, reach); + this.drawOtherClips(asset, wave, mid, reach); this.drawScale(mid, room); if (this.playhead !== null) { const x = secondsToX(this.view, this.playhead); @@ -565,15 +566,14 @@ export class WaveCanvas extends Widget { /** A band per clip with its name, the picked one lit, and the one a drag spans. */ private drawStrip(asset: SoundAsset, wave: Wave): void { const c = Theme.colors; - const picked = - this.gesture?.kind === 'span' && this.gesture.moved ? this.gesture.clipId : this.pickedClip()?.id; + const picked = this.litClipId(); const names = new Map(clipsOf(asset).map((clip) => [clip.id, clip.name])); const placed: { text: string; x: number; room: number; lit: boolean }[] = []; for (const { id, span } of this.bands(asset, wave)) { const a = secondsToX(this.view, span.start); const b = secondsToX(this.view, span.end); if (b < 0 || a > this.areaWidth) continue; - const lit = id === (picked ?? ''); + const lit = id === picked; this.overlay .rect(a, RULER_HEIGHT + 2, Math.max(1, b - a), STRIP_HEIGHT - 4) .fill({ color: c.accent, alpha: lit ? 0.85 : 0.35 }) @@ -590,6 +590,41 @@ export class WaveCanvas extends Widget { this.syncStrip(placed); } + /** The clip the strip lights: the one a drag spans ('' for a new one), else the picked one. */ + private litClipId(): string { + const g = this.gesture?.kind === 'span' && this.gesture.moved ? this.gesture : null; + return (g ? g.clipId : this.pickedClip()?.id) ?? ''; + } + + /** The clips not picked, faint over the wave from edge to edge, as far as the file reaches. */ + private drawOtherClips(asset: SoundAsset, wave: Wave, mid: number, reach: number): void { + const c = Theme.colors; + const top = mid - reach; + for (const { span } of this.otherBands(asset, wave)) { + const a = secondsToX(this.view, span.start); + const b = secondsToX(this.view, span.end); + if (b < 0 || a > this.areaWidth) continue; + this.overlay + .rect(a, top, Math.max(1, b - a), 2 * reach) + .fill({ color: c.accent, alpha: 0.12 }) + .stroke({ width: 1, color: c.accent, alpha: 0.35, alignment: 1 }); + } + } + + /** The clips drawn faint over the wave, by name (test hook). */ + faintClips(): string[] { + const asset = this.asset(); + const wave = this.wave(); + if (!asset || !wave) return []; + const names = new Map(clipsOf(asset).map((clip) => [clip.id, clip.name])); + return this.otherBands(asset, wave).map(({ id }) => names.get(id) ?? id); + } + + private otherBands(asset: SoundAsset, wave: Wave): ClipBand[] { + const lit = this.litClipId(); + return this.bands(asset, wave).filter(({ id }) => id !== lit); + } + /** * Dark outside the stretch, a handle at each edge, and the ramp at each edge inside the file – all * as far up and down from the middle as the file reaches. diff --git a/tools/e2e/34-sound.mjs b/tools/e2e/34-sound.mjs index d5a79128..82c6eba6 100644 --- a/tools/e2e/34-sound.mjs +++ b/tools/e2e/34-sound.mjs @@ -2005,16 +2005,28 @@ export async function sound(t) { const beside = await json('__gm.soundTab.stripAt(0.5)'); await click(beside.x, beside.y); const pickedBeside = await picked(); + const faintBeside = await json('__gm.soundTab.faintClips()'); + await shot('34-sound-faint-clip'); const onBand = await json('__gm.soundTab.stripAt(0.2)'); await click(onBand.x, onBand.y); + const faintPicked = await json('__gm.soundTab.faintClips()'); check( - 'one undo takes the clip away, a redo brings it back; a click in the strip picks it, beside it the sound', + 'one undo takes the clip away, a redo brings it back; a click in the strip picks it, beside it the sound, the clip then faint over the wave', undoneClip.clips.length === 0 && undoneClip.picked === null && redoneClip.map((c) => c.id).join() === clipId && pickedBeside === null && + faintBeside.join() === 'Clip 1' && + faintPicked.length === 0 && (await picked()) === clipId, - JSON.stringify({ undoneClip, redoneClip, pickedBeside, picked: await picked() }), + JSON.stringify({ + undoneClip, + redoneClip, + pickedBeside, + faintBeside, + faintPicked, + picked: await picked(), + }), ); const inClip = await json('__gm.soundTab.at(0.2)'); await click(inClip.x, inClip.y);