Pokered Save Editor 2: a redesigned Pokémon editor

The General, DV/EV, and Moves tabs are rebuilt into one visual language — and a long-standing display bug that showed the wrong types is fixed.

The Pokémon detail editor in Pokered Save Editor 2 was the last major area still carrying its 2020-era layout. This day brought all three of its tabs — General, DV/EV, and Moves — into the same visual language as the rest of the app: grouped panels of zebra-striped rows, connected segmented controls instead of loose buttons and overflow menus, and consistent sizing.

The bug under the redesign

The General-tab work ended on a real, long-standing bug: the type dropdowns displayed the wrong type for every Pokémon — a Charizard reading as Ghost/Fighting instead of Fire/Flying — and “reset to default” appeared to do nothing. A diagnostic test proved the stored data was correct all along; the combos just rendered it wrong.

The cause was an off-by-one. The type list has a “no type” placeholder at row 0, so a type stored at index i lives at model row i + 1 — but the lookup returned i:

// TypesModel::valToIndex — "-----" placeholder sits at row 0
- ret = i;        // selects one row early → Fire shows as Ghost, Flying as Fighting
+ ret = i + 1;    // account for the placeholder

The first regression test for it was tautological — it compared the result against the same source the method reads, so it passed regardless. It was rewritten to assert the literal type IDs (Fire = 20, Flying = 2), which is the difference between a test that guards behaviour and one that just agrees with the code.

A binding instead of a toggle

The DV/EV tab’s “Future Shiny” control highlights a small but useful QML pattern. Shiny status is derived from DVs, so the selection has to mirror live data — dragging the DV sliders should flip it on its own. A checkable toggle can’t do that (a click would break the binding), so each segment’s active state is a plain binding to the data, and clicking performs the action that changes the data:

SegSel {
    active: boxData.isShiny          // a binding, not a checkable state
    onClicked: boxData.makeShiny()   // changes data → re-drives `active`
}

One source of truth, no drift.

Closing the loop on data safety

The day finished with a data-integrity pass: destructive editor actions — re-roll, reset, max out, evolve, and the bulk move operations — now ask for confirmation through a shared dialog, while harmless per-field actions don’t. A status badge was added to the Pokémon preview, and the level field was widened so “100” fits.

References


← All updates