Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
Loading...
Searching...
No Matches
2026-06-09 — Session Log

Pokémon-storage drag & drop (reorder + cross-pane transfer)

Two new Q_INVOKABLEs on PokemonStorageModel (dragReorder, dragTransfer in mvc/pokemonstoragemodel.cpp/.h) plus the QML edit in fragments/screens/pokemon/PokemonBoxView.qml. The storage grid now supports drag-to-reorder within a pane and drag-to-transfer between the two panes (drop-to-commit, insert at the drop slot, drag-threshold so a plain click still opens the editor, group moves via the existing checkboxes, dashed drop-slot placeholder). Built clean (kit + repo build/); ran tst_storage_model, tst_models, tst_pokemonbox, tst_move_select_model, tst_roundtrip — all green (byte-fidelity roundtrip included). No new QML files. Convention: ../../reference/ui-patterns.md → "Drag & drop reordering + cross-pane transfer".

Drag & drop — iterated in-app

(1) drop now commits — an internal MouseArea drag never auto-fires DropArea.onDropped; we drive Drag.active manually and call Drag.drop() on release (was the "drag does nothing, no error" bug). (2) indicator is now an insertion caret — a dashed vertical bar in the gap before the hovered cell (overlay, no reflow; the full-cell box and any live entry-shuffle were rejected); the "+" slot's caret marks "after the last mon, before New". (3) footer bulk-action bar removed (PokemonPane.qml) — moves are drag now. (4) checkbox selection has scoped persistence (the rule): survives only the detail-editor round-trip, clears on box-switch and on leaving the screen. Mechanics: delegate CheckBox binds checked: itemChecked (+ onToggled) so per-mon checks restore across the model reset the editor-close triggers; switchBox clears the outgoing box; Pokemon.qml's Component.onDestruction clears both models (StackView push/pop lifecycle). (5) per-cell delete button is a round chip Button with real hover/press states — dark chip + red times at rest → fills red w/ white glyph on hover → darkens on press, bottom-right, hover-or-checked, keyed off a HoverHandler so reaching for it doesn't hide it. deleteMon(index, group) — group(checked) deletes the whole checked set, else just that mon.

Bug (intermittent crash): use-after-free — the box was being GC'd, dragging its mons down

Create/open a stored Pokémon's editor, back out, re-open → use-after-free. Actual root cause found: after the earlier per-mon ownership fix the crash persisted (now at data() line 153 !mon->isBoxMon() — a virtual call crashing while the scalar species read just above it succeeded: textbook freed-object/clobbered-vtable). The freeing agent is the box, not the mon: PokemonStorageModel::getCurBox()/getBox() are public slots (QML-callable like Q_INVOKABLE), and the new-mon path calls theModel.getCurBox().pokemonNew() from QML — handing the parentless PokemonStorageBox to JS ownership. When QML GC'd the box, ~PokemonStorageBox() deleteLater()'d every mon in it, so all the box's mons dangled regardless of their own CppOwnership. (The party path getBox()->party never went through Storage::boxAt's wrap — unprotected.) Fix: QQmlEngine::setObjectOwnership(this, CppOwnership) in the PokemonStorageBox ctor (covers all storage boxes + the party via PlayerPokemon inheritance). Also realised the commented-out TODO in PokemonBox/PokemonMove ctors: every PokemonBox/PokemonParty/PokemonMove now sets CppOwnership in its ctor (static — no engine needed), so mons/moves are self-protecting from birth. Containers still free them via deleteLater() → no leak/double-free. Mirrors the DB::qmlProtect precedent. Tests green on the repo build (tst_pokemonbox/tst_storage_model/tst_move_select_model/ tst_models + byte-fidelity tst_roundtrip/tst_verbs). See ../../reference/fix-patterns.md / qt-patterns.md.

Stale-binary gotcha (the two failed retests)

The first two in-app retests still crashed because they ran a stale savefile.dll — the editor is launched from the Qt Creator kit dir projects/build/Desktop_Qt_6_11_0_llvm_mingw_64_bit-Debug/, NOT the repo-root build/ the automated loop uses. (Tell: the crash showed PokemonBox::toData() at the old line; after the ctor edits it moved.) PokeredSaveEditor.exe links savefile.dll via its import lib, so editing the DLL body does NOT relink the exe (exe mtime stays put) — rebuild the kit dir's savefile.dll and just re-run the exe (it loads the new DLL). Lesson: always rebuild the kit dir for in-app testing, not just build/. Verify by the DLL timestamp, not the exe.