S5 Tier-1: zoned keymap editor + performance-map playback/persistence
Zone editor in the IPlugView LICE surface, zoned resolution built off-thread into the LoadedInstrument keymap with Tier-0 fallback, performance map in VST3 component state with v1 back-compat. Pure resolve/build/serialize + geometry with CTest coverage.
This commit is contained in:
@@ -198,6 +198,118 @@ static void testSampleRowHitTestMatchesDrawnRows() {
|
||||
}
|
||||
}
|
||||
|
||||
// --- keymap editor (S5 Tier-1 UI) --------------------------------------------
|
||||
|
||||
static void testKeymapLayoutSplitsCanvas() {
|
||||
const KeymapEditorLayout L = layoutKeymapEditor(600, 300);
|
||||
// The left sample list and right zone panel partition the canvas with no overlap and
|
||||
// no gap: the list's right edge is the panel's left edge.
|
||||
CHECK(L.sampleList.left == L.base.canvas.left);
|
||||
CHECK(L.sampleList.right == L.zonePanel.left);
|
||||
CHECK(L.zonePanel.right == L.base.canvas.right);
|
||||
CHECK(L.sampleList.top == L.base.canvas.top);
|
||||
CHECK(L.zonePanel.top == L.base.canvas.top);
|
||||
CHECK(L.sampleList.bottom == L.base.canvas.bottom);
|
||||
CHECK(L.zonePanel.bottom == L.base.canvas.bottom);
|
||||
CHECK(L.sampleList.width() > 0 && L.zonePanel.width() > 0);
|
||||
// Add-Zone button caps the panel; zone rows stack below it.
|
||||
CHECK(L.addZoneButton.top == L.zonePanel.top);
|
||||
CHECK(L.addZoneButton.left == L.zonePanel.left && L.addZoneButton.right == L.zonePanel.right);
|
||||
CHECK(L.zoneRowArea.top == L.addZoneButton.bottom);
|
||||
CHECK(L.zoneRowArea.bottom == L.zonePanel.bottom);
|
||||
}
|
||||
|
||||
static void checkNoInversion(const KeymapEditorLayout& L) {
|
||||
CHECK(L.sampleList.right >= L.sampleList.left);
|
||||
CHECK(L.zonePanel.right >= L.zonePanel.left);
|
||||
CHECK(L.addZoneButton.right >= L.addZoneButton.left);
|
||||
CHECK(L.addZoneButton.bottom >= L.addZoneButton.top);
|
||||
CHECK(L.zoneRowArea.right >= L.zoneRowArea.left);
|
||||
CHECK(L.zoneRowArea.bottom >= L.zoneRowArea.top);
|
||||
// Regions stay within the client area.
|
||||
CHECK(L.zonePanel.right <= L.base.canvas.right);
|
||||
}
|
||||
|
||||
static void testKeymapLayoutTinyAndZeroNoInversion() {
|
||||
checkNoInversion(layoutKeymapEditor(30, 30));
|
||||
checkNoInversion(layoutKeymapEditor(0, 0));
|
||||
// A click anywhere on a zero layout hits no zone and no Add button.
|
||||
const KeymapEditorLayout Z = layoutKeymapEditor(0, 0);
|
||||
CHECK(zoneHitTest(Z, 3, 0, 0).zoneIndex == -1);
|
||||
CHECK(!addZoneHitTest(Z, 0, 0));
|
||||
}
|
||||
|
||||
static void testKeymapSampleRowInLeftColumn() {
|
||||
const KeymapEditorLayout L = layoutKeymapEditor(600, 300);
|
||||
const Rect r0 = keymapSampleRowRect(L, 0);
|
||||
// Rows live in the LEFT column (not the full canvas width).
|
||||
CHECK(r0.left == L.sampleList.left && r0.right == L.sampleList.right);
|
||||
CHECK(r0.right < L.base.canvas.right); // strictly left of the zone panel
|
||||
CHECK(r0.top == L.sampleList.top && r0.height() == kSampleRowHeight);
|
||||
// Hit-test maps a left-column click to the row and rejects a click in the zone panel.
|
||||
const int midY = (r0.top + r0.bottom) / 2;
|
||||
CHECK(keymapSampleRowHitTest(L, 3, r0.left + 2, midY) == 0);
|
||||
CHECK(keymapSampleRowHitTest(L, 3, L.zonePanel.left + 2, midY) == -1);
|
||||
}
|
||||
|
||||
static void testAddZoneHitTest() {
|
||||
const KeymapEditorLayout L = layoutKeymapEditor(600, 300);
|
||||
const int cx = (L.addZoneButton.left + L.addZoneButton.right) / 2;
|
||||
const int cy = (L.addZoneButton.top + L.addZoneButton.bottom) / 2;
|
||||
CHECK(addZoneHitTest(L, cx, cy));
|
||||
// A click in the zone-row area below the button is NOT the Add button.
|
||||
CHECK(!addZoneHitTest(L, cx, L.zoneRowArea.top + 2));
|
||||
// A click in the left list is NOT the Add button.
|
||||
CHECK(!addZoneHitTest(L, L.sampleList.left + 2, L.sampleList.top + 2));
|
||||
}
|
||||
|
||||
static void testZoneRowStacksAndSelects() {
|
||||
const KeymapEditorLayout L = layoutKeymapEditor(600, 300);
|
||||
const Rect z0 = zoneRowRect(L, 0);
|
||||
const Rect z1 = zoneRowRect(L, 1);
|
||||
CHECK(z0.top == L.zoneRowArea.top && z0.height() == kZoneRowHeight);
|
||||
CHECK(z1.top == z0.bottom); // stacked, no gap
|
||||
CHECK(z0.left == L.zoneRowArea.left && z0.right == L.zoneRowArea.right);
|
||||
// A click on the LABEL area (left part of a zone row) selects the zone with no field.
|
||||
const int labelX = z0.left + 2; // far left = label, not a control
|
||||
const int midY = (z0.top + z0.bottom) / 2;
|
||||
const ZoneHit h = zoneHitTest(L, 2, labelX, midY);
|
||||
CHECK(h.zoneIndex == 0 && h.field == ZoneField::kZoneNone);
|
||||
}
|
||||
|
||||
static void testZoneRowControlsMapToFields() {
|
||||
const KeymapEditorLayout L = layoutKeymapEditor(600, 300);
|
||||
const Rect row = zoneRowRect(L, 0);
|
||||
const int midY = (row.top + row.bottom) / 2;
|
||||
// The seven controls occupy the rightmost 7*kZoneCtrlWidth px, left-to-right:
|
||||
// low-, low+, high-, high+, root-, root+, delete.
|
||||
const int block = row.right - 7 * kZoneCtrlWidth;
|
||||
const ZoneField expected[7] = {
|
||||
ZoneField::kLowDown, ZoneField::kLowUp, ZoneField::kHighDown,
|
||||
ZoneField::kHighUp, ZoneField::kRootDown, ZoneField::kRootUp,
|
||||
ZoneField::kDelete,
|
||||
};
|
||||
for (int s = 0; s < 7; ++s) {
|
||||
const int x = block + s * kZoneCtrlWidth + kZoneCtrlWidth / 2; // center of slot s
|
||||
const ZoneHit h = zoneHitTest(L, 1, x, midY);
|
||||
CHECK(h.zoneIndex == 0);
|
||||
CHECK(h.zoneIndex == 0 && h.field == expected[s]);
|
||||
}
|
||||
}
|
||||
|
||||
static void testZoneHitTestMisses() {
|
||||
const KeymapEditorLayout L = layoutKeymapEditor(600, 300);
|
||||
const Rect row = zoneRowRect(L, 0);
|
||||
const int midY = (row.top + row.bottom) / 2;
|
||||
// Zero zones -> always miss.
|
||||
CHECK(zoneHitTest(L, 0, row.left + 2, midY).zoneIndex == -1);
|
||||
// Below the last zone row -> miss.
|
||||
const Rect last = zoneRowRect(L, 2);
|
||||
CHECK(zoneHitTest(L, 3, row.left + 2, last.bottom + 1).zoneIndex == -1);
|
||||
// Left of the zone panel (in the sample list) -> miss.
|
||||
CHECK(zoneHitTest(L, 3, L.sampleList.left + 2, midY).zoneIndex == -1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
testContainsHalfOpen();
|
||||
testContainsDegenerate();
|
||||
@@ -212,6 +324,13 @@ int main() {
|
||||
testSampleRowHitTestMapsClickToRow();
|
||||
testSampleRowHitTestMisses();
|
||||
testSampleRowHitTestMatchesDrawnRows();
|
||||
testKeymapLayoutSplitsCanvas();
|
||||
testKeymapLayoutTinyAndZeroNoInversion();
|
||||
testKeymapSampleRowInLeftColumn();
|
||||
testAddZoneHitTest();
|
||||
testZoneRowStacksAndSelects();
|
||||
testZoneRowControlsMapToFields();
|
||||
testZoneHitTestMisses();
|
||||
|
||||
if (g_fail == 0) std::printf("editor_geometry: all tests passed\n");
|
||||
return g_fail != 0;
|
||||
|
||||
Reference in New Issue
Block a user