Add controller presets management (#581)

## Link to GitHub Issue or related Pull Request, if one exists
Implements #579.

## Description of change

### Controller Presets

Add a new "Presets" tab to spicecfg that allows users to save, load, and
manage controller binding presets.

#### Save Presets
- Capture all current button, analog, and light bindings as a preset
- "Assign Labels" dialog prompts the user to name each source device
(e.g. "Player 1", "Player 2") before saving
- Device IDs are replaced with these labels, making presets portable
across different machines
- Presets are stored in `%APPDATA%/spice2x/spicetools_presets.xml`

#### Load / Apply Presets
- Preset list shows name, type (Built-in / User), and binding counts
- Apply dialog maps each preset source label to a connected device or
keyboard
- Bindings are applied per-source, supporting multi-device setups (e.g.
two PHOENIXWAN controllers)

#### Edit / Delete Presets
- Rename source labels in saved presets (propagates to all bindings)
- Delete user presets

#### Built-in Presets
- PHOENIXWAN preset for Beatmania IIDX: P1/P2 buttons (1-7, Start,
EFFECT, VEFX), turntable analogs, and button lights

## Testing

* [x] Open spicecfg, go to Buttons tab, verify Presets section loads
* [x] Verify PHOENIXWAN builtin preset appears for Beatmania IIDX
* [x] Save a new preset: confirm "Assign Labels" dialog appears with
device descriptions as defaults
* [x] Apply a preset: confirm source column shows labels, popup does not
shrink
* [x] Edit labels on a saved preset: confirm rename propagates to all
bindings
* [x] Hover over a device source tooltip: confirm vKey shows as number
This commit is contained in:
ichijyo-hotaru
2026-03-21 08:46:17 +09:00
committed by GitHub
parent 458a1495c8
commit b31ddb1ffc
10 changed files with 1778 additions and 1 deletions
+25
View File
@@ -8,6 +8,7 @@
#include "rawinput/device.h"
#include "external/imgui/imgui_filebrowser.h"
#include "patch_manager.h"
#include "controller_presets.h"
namespace overlay::windows {
@@ -23,6 +24,7 @@ namespace overlay::windows {
CONFIG_TAB_OPTIONS,
CONFIG_TAB_ADVANCED,
CONFIG_TAB_DEV,
CONFIG_TAB_PRESETS,
CONFIG_TAB_SEARCH,
};
@@ -93,6 +95,22 @@ namespace overlay::windows {
ImGui::FileBrowser keypads_card_select_browser[2];
char keypads_card_number[2][18] {};
// presets tab
std::vector<ControllerTemplate> templates_cache;
bool templates_cache_dirty = true;
int templates_selected = -1;
char template_save_name[256] = {};
// per-source target mapping: source index -> selected target device index
std::vector<int> template_target_selection;
// deferred popup flags (to avoid ImGui ID stack mismatch inside tables)
bool template_apply_open = false;
bool template_edit_open = false;
bool template_save_labels_open = false;
bool template_delete_open = false;
ControllerTemplate template_pending_save;
std::vector<std::string> template_save_sources;
std::vector<std::string> template_save_labels;
// patches tab
std::unique_ptr<PatchManager> patch_manager;
@@ -146,6 +164,13 @@ namespace overlay::windows {
std::vector<MatchEntry> &matched,
std::vector<MatchEntry> &unmatched);
void build_presets();
void clear_all_bindings();
void apply_template_source(const ControllerTemplate &tmpl,
const std::string &source_filter,
const std::string &target_device);
ControllerTemplate capture_current_bindings(const std::string &name);
void build_cards();
std::string build_option_value_picker_title(const OptionDefinition& option);
void build_option_value_picker(Option& option);