Size notes by either end and pan before bar one in music tabs
A round of small fixes to the Pattern and Song tabs: what the ruler and a pan do in both, and a few things the tabs carried that were in the way. - A middle-drag can take bar 1 up to half the grid or lanes in. The time before it is drawn dark on the ruler, the grid and the lanes. Nothing lands there: a click draws no note, a drop shows no frame. - A press or drag on the ruler puts the start on the nearest grid line, no longer on the one before the pointer. - A new song has one track instead of four. The "+ Track" row under the heads is gone, a head's menu adds tracks above or below. A song still keeps at least one, so that menu is always there. - A note can be sized by its left end too: the start goes to the nearest grid line and the end stays put, never before tick 0. - An end of a note under the pointer lights up and shows a resize cursor, as on an item of the Song tab. Tests: - Unit: piano-roll, song-edit, song, song-play 52 tests. Two new cases for the left end and the pan, the song fixtures add their tracks themselves now. - e2e 64+65 on melektaus-atx: ALL PASSED - 50 checks (+2). e2e 65 adds its tracks through the head's menu and clicks the ruler three quarters into a bar, which only the nearest line passes. - Gate on melektaus-atx: 251 files, 3909 tests, CHECK EXIT 0. - A probe shot of the piano roll panned in: the time before bar 1 dark, the left end of a note lit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aa097bd076
commit
a3101c590c
15 files changed
+260
-89
No files matched your search
@@ -14,12 +14,14 @@ import {
|
||||
import {
|
||||
clampShift,
|
||||
draggedLength,
|
||||
draggedStart,
|
||||
drawnNote,
|
||||
duplicateOffset,
|
||||
moveDelta,
|
||||
placedCopies,
|
||||
resized,
|
||||
shifted,
|
||||
trimmed,
|
||||
withVelocity,
|
||||
} from '../../src/editor/pattern/rollEdit';
|
||||
import {
|
||||
@@ -48,10 +50,12 @@ const VIEW: RollView = { panX: 0, pxPerTick: 0.1, panY: -(127 - 60) * 10, rowHei
|
||||
const COMMON = { beats: 4, unit: 4 };
|
||||
|
||||
describe('time view of the piano roll (#333)', () => {
|
||||
it('zooms about the pointer and never shows time before 0', () => {
|
||||
it('zooms about the pointer and pans tick 0 at most half the width in', () => {
|
||||
const zoomed = zoomTime({ panX: -100, pxPerTick: 0.1 }, 2, 300);
|
||||
expect(xToTick(zoomed, 300)).toBeCloseTo(4000);
|
||||
expect(clampTimePan({ panX: 40, pxPerTick: 0.1 }).panX).toBe(0);
|
||||
expect(clampTimePan({ panX: 40, pxPerTick: 0.1 }, 600).panX).toBe(40);
|
||||
expect(clampTimePan({ panX: 400, pxPerTick: 0.1 }, 600).panX).toBe(300);
|
||||
expect(clampTimePan({ panX: -50, pxPerTick: 0.1 }, 600).panX).toBe(-50);
|
||||
expect(fitTime(7680, 768)).toEqual({ panX: 0, pxPerTick: 0.1 });
|
||||
});
|
||||
|
||||
@@ -117,11 +121,12 @@ describe('rows of the piano roll (#333)', () => {
|
||||
expect(centreRows(0, 127, 200).rowHeight).toBe(6);
|
||||
});
|
||||
|
||||
it('tells a note body from its right end, and takes the one drawn last', () => {
|
||||
it('tells a note body from its ends, and takes the one drawn last', () => {
|
||||
const notes = [note('a', 0, 480), note('b', 240, 480)];
|
||||
expect(noteAt(VIEW, notes, { x: 10, y: 5 })).toEqual({ id: 'a', part: 'body' });
|
||||
expect(noteAt(VIEW, notes, { x: 30, y: 5 })).toEqual({ id: 'b', part: 'body' });
|
||||
expect(noteAt(VIEW, notes, { x: 70, y: 5 })).toEqual({ id: 'b', part: 'end' });
|
||||
expect(noteAt(VIEW, notes, { x: 25, y: 5 })).toEqual({ id: 'b', part: 'start' });
|
||||
expect(noteAt(VIEW, notes, { x: 10, y: 15 })).toBeNull();
|
||||
// A note of a tick is still 4 px to the pointer.
|
||||
expect(noteAt(VIEW, [note('c', 1000, 1)], { x: 102, y: 5 })?.id).toBe('c');
|
||||
@@ -185,6 +190,21 @@ describe('editing in the piano roll (#333)', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('drags a start to the nearest grid line, its end kept, never up to the end or before 0', () => {
|
||||
const anchor = note('a', 240, 240);
|
||||
expect(draggedStart(anchor, 130, 120, false)).toBe(120);
|
||||
expect(draggedStart(anchor, 470, 120, false)).toBe(360);
|
||||
expect(draggedStart(anchor, -300, 120, false)).toBe(0);
|
||||
expect(draggedStart(anchor, 479.6, 120, true)).toBe(479);
|
||||
const notes = trimmed([anchor, note('b', 60, 60), note('c', 0, 60)], new Set(['a', 'b']), -120);
|
||||
expect(notes.map((n) => [n.tick, n.length])).toEqual([
|
||||
[120, 360],
|
||||
[0, 120],
|
||||
[0, 60],
|
||||
]);
|
||||
expect(trimmed([anchor], new Set(['a']), 999).map((n) => [n.tick, n.length])).toEqual([[479, 1]]);
|
||||
});
|
||||
|
||||
it('puts a duplicate right behind the picked notes, in whole grid steps', () => {
|
||||
const notes = [note('a', 0, 120), note('b', 240, 60), note('c', 960, 120)];
|
||||
expect(duplicateOffset(notes, new Set(['a', 'b']), 120)).toBe(360);
|
||||
|
||||
@@ -138,9 +138,9 @@ describe('where things are in the Song tab (#334)', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('scrolls no further than the tracks and the row that adds one', () => {
|
||||
it('scrolls no further than the tracks', () => {
|
||||
expect(clampTracks({ ...view, panY: 40 }, 3, 400).panY).toBe(0);
|
||||
expect(clampTracks({ ...view, panY: -500 }, 10, 400).panY).toBe(400 - 10 * TRACK_HEIGHT - 28);
|
||||
expect(clampTracks({ ...view, panY: -500 }, 10, 400).panY).toBe(400 - 10 * TRACK_HEIGHT);
|
||||
});
|
||||
|
||||
it('tells the parts of a head apart and reads a slider from left to right', () => {
|
||||
@@ -164,7 +164,7 @@ describe('where things are in the Song tab (#334)', () => {
|
||||
expect(trackAtY(zoomed, 10, 150)).toBe(trackAtY({ ...view, panY: -20 }, 10, 150));
|
||||
expect(zoomTracks(view, 100, 0).trackHeight).toBe(MAX_TRACK_HEIGHT);
|
||||
expect(zoomTracks(view, 0.01, 0).trackHeight).toBe(MIN_TRACK_HEIGHT);
|
||||
expect(clampTracks({ ...zoomed, panY: -9999 }, 10, 400).panY).toBe(400 - 10 * 2 * TRACK_HEIGHT - 28);
|
||||
expect(clampTracks({ ...zoomed, panY: -9999 }, 10, 400).panY).toBe(400 - 10 * 2 * TRACK_HEIGHT);
|
||||
expect(heldSongView({ panX: -5, pxPerTick: 0.3, panY: -10 })).toEqual({
|
||||
panX: -5,
|
||||
pxPerTick: 0.3,
|
||||
|
||||
Reference in new issue
Block a user