fix: same-slot reorder is a no-op; WM_DESTROY clears card drag state; fix card_meta.h ms-rounding comment
This commit is contained in:
@@ -76,6 +76,7 @@ bool SlotMap::remove(const std::string& id) {
|
||||
bool SlotMap::reorder(const std::string& id, int targetSlot) {
|
||||
if (slotOf(id) < 0) return false; // not mapped -> no mutation
|
||||
if (targetSlot < 0) targetSlot = 0;
|
||||
if (slotOf(id) == targetSlot) return false; // already there — true no-op
|
||||
|
||||
// Detach the moving id first so the occupancy test below sees the post-move world.
|
||||
remove(id);
|
||||
|
||||
@@ -2922,6 +2922,7 @@ void onMouseMove(int x, int y) {
|
||||
g_panel.dropBankId.clear();
|
||||
g_panel.cardGesture = CardGesture::None;
|
||||
g_panel.dragTargetSlot = -1;
|
||||
g_panel.dragPrimaryId.clear();
|
||||
invalidatePanel();
|
||||
|
||||
// Empty path list -> nothing draggable (all stale/missing); do not start a drag.
|
||||
@@ -3117,6 +3118,9 @@ WDL_DLGRET dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
stopAudition();
|
||||
g_panel.selection = Selection{};
|
||||
g_panel.dragArmed = g_panel.dragging = false;
|
||||
g_panel.cardGesture = CardGesture::None;
|
||||
g_panel.dragTargetSlot = -1;
|
||||
g_panel.dragPrimaryId.clear();
|
||||
g_panel.hovered = Hover{};
|
||||
g_panel.tooltipShown = false;
|
||||
g_panel.hwnd = nullptr;
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ struct MusicalLength {
|
||||
std::string formatBarsBeats(const MusicalLength& m);
|
||||
|
||||
// seconds.milliseconds from a wall-clock length (always derivable, meter-independent).
|
||||
// * "S.mmm" — integer seconds, a dot, zero-padded 3-digit milliseconds (floored).
|
||||
// * "S.mmm" — integer seconds, a dot, zero-padded 3-digit milliseconds (rounded to nearest ms).
|
||||
// e.g. 0.0 -> "0.000", 1.5 -> "1.500", 62.037 -> "62.037".
|
||||
// * negative length is clamped to "0.000" (a length is never negative; defensive).
|
||||
std::string formatSecondsMs(double lengthSeconds);
|
||||
|
||||
@@ -973,6 +973,28 @@ static void testReorderSampleRejectsUnknown() {
|
||||
CHECK(book.pool().slots.slotOf("id-r1") == 0); // unchanged
|
||||
}
|
||||
|
||||
// Same-slot reorder is a true no-op: returns false, no undo point triggered,
|
||||
// JSON byte-identical before/after (the invariant that blocks spurious dirty-state).
|
||||
static void testReorderSampleSameSlotIsNoOp() {
|
||||
BankBook book;
|
||||
CHECK(book.pool().index.add(sampleWith("a")) == AddResult::Added);
|
||||
CHECK(book.pool().index.add(sampleWith("b")) == AddResult::Added);
|
||||
CHECK(book.pool().index.add(sampleWith("c")) == AddResult::Added);
|
||||
book.reconcileSlots(); // a@0, b@1, c@2
|
||||
const std::string jsonBefore = book.serialize();
|
||||
|
||||
// Drop each card onto its own current slot — must return false every time.
|
||||
CHECK(!book.reorderSample("id-a", kPoolBankId, 0));
|
||||
CHECK(!book.reorderSample("id-b", kPoolBankId, 1));
|
||||
CHECK(!book.reorderSample("id-c", kPoolBankId, 2));
|
||||
|
||||
// Slots and JSON are byte-identical — no mutation occurred.
|
||||
CHECK(book.pool().slots.slotOf("id-a") == 0);
|
||||
CHECK(book.pool().slots.slotOf("id-b") == 1);
|
||||
CHECK(book.pool().slots.slotOf("id-c") == 2);
|
||||
CHECK(book.serialize() == jsonBefore);
|
||||
}
|
||||
|
||||
// --- BankBook L7: Alt-replace mutator ----------------------------------------
|
||||
|
||||
static void testReplaceSampleTakesSlotAndRemovesOccupant() {
|
||||
@@ -1090,6 +1112,7 @@ int main() {
|
||||
testMigrationDefaultsToInsertionOrderDense();
|
||||
testOrderedSampleIdsReconcilesLazily();
|
||||
testReorderSampleRejectsUnknown();
|
||||
testReorderSampleSameSlotIsNoOp();
|
||||
testReplaceSampleTakesSlotAndRemovesOccupant();
|
||||
testReplaceSampleNonDestructiveFileStays();
|
||||
testReplaceSampleRejectionsNoMutation();
|
||||
|
||||
Reference in New Issue
Block a user