overlay: "reset all" buttons for buttons/overlay/analog tabs (#578)

## Link to GitHub Issue or related Pull Request, if one exists
#576 

## Description of change
Add `Reset All` buttons to these tabs which clears out bindings to none
/ default.

For keypads, introduce a special `Use Preset` button which lets users
pick between numpad and top row number keys.

Update UI-visible string used for vKeys, especially the numpad ones
(e.g., `.` is now `Numpad .` to distinguish from the regular `.`
period).

Breaking change: if the user never had `P1 Keypad 00` set, the default
will change from `Enter` to `Numpad -` since both enter keys trigger the
same. This will affect a small number of popn/ddr players. They can just
change it back.

## Testing
*how was the code tested?*
This commit is contained in:
bicarus
2026-03-17 18:47:54 -07:00
committed by GitHub
parent a9f0e86aa3
commit 97d65b137a
6 changed files with 185 additions and 28 deletions
+145 -8
View File
@@ -590,14 +590,75 @@ namespace overlay::windows {
}
void Config::build_buttons(const std::string &name, std::vector<Button> *buttons, int min, int max) {
ImGui::AlignTextToFramePadding();
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "%s Buttons", name.c_str());
std::string reset_button_str = "Reset all";
if (name == "Keypad") {
reset_button_str = "Use Preset";
}
const float clear_w = ImGui::CalcTextSize(reset_button_str.c_str()).x
+ ImGui::GetStyle().FramePadding.x * 2;
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - clear_w);
std::string reset_str = "Reset all " + name + " button bindings";
// clear all
ImGui::PushID(name.c_str());
if (ImGui::Button(reset_button_str.c_str())) {
ImGui::OpenPopup(reset_str.c_str());
}
if (ImGui::BeginPopupModal(reset_str.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextUnformatted("Are you sure? This can't be undone.");
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (name == "Keypad") {
if (ImGui::Button("Use Numpad")) {
for (auto &button : *buttons) {
reset_button_to_default(&button, button.getVKeyDefault());
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Use Top Row")) {
for (auto &button : *buttons) {
reset_button_to_default(&button, get_keypad_top_row(button));
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Remove All")) {
for (auto &button : *buttons) {
reset_button_to_default(&button, 0xFF);
}
ImGui::CloseCurrentPopup();
}
} else {
if (ImGui::Button("Yes")) {
for (auto &button : *buttons) {
reset_button_to_default(&button, button.getVKeyDefault());
}
ImGui::CloseCurrentPopup();
}
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::PopID();
ImGui::Separator();
if (ImGui::BeginTable("ButtonsTable", 3, ImGuiTableFlags_Resizable)) {
// longest column is probably "Toggle Virtual Keypad P1" in Overlay tab
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, overlay::apply_scaling(220));
ImGui::TableSetupColumn("Binding", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, overlay::apply_scaling(180));
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, overlay::apply_scaling(140));
// check if empty
if (!buttons || buttons->empty()) {
@@ -651,7 +712,14 @@ namespace overlay::windows {
// get button info
ImGui::PushID(button);
const auto button_name = button->getName();
auto button_name = button->getName();
if (button_name == "Service") {
button_name = "Insert Service Coin";
} else if (button_name == "Test") {
button_name = "Enter Test Menu";
} else if (button_name == "Coin Mech") {
button_name = "Activate Coin Mech";
}
const auto button_display = button->getDisplayString(RI_MGR.get());
const auto button_velocity = GameAPI::Buttons::getVelocity(RI_MGR, *button);
@@ -899,9 +967,13 @@ namespace overlay::windows {
ImGui::PopID();
}
void Config::clear_button(Button *button, const int alt_index) {
void Config::clear_button(Button *button, const int alt_index, std::optional<unsigned short> vKey_default) {
button->setDeviceIdentifier("");
button->setVKey(0xFF);
if (vKey_default.has_value()) {
button->setVKey(vKey_default.value());
} else {
button->setVKey(0xFF);
}
button->setAnalogType(BAT_NONE);
button->setDebounceUp(0.0);
button->setDebounceDown(0.0);
@@ -915,6 +987,41 @@ namespace overlay::windows {
alt_index - 1);
}
void Config::reset_button_to_default(Button *button, unsigned short vKey_default) {
// delete all alternatives
int alt_index = 1; // 0 is primary
for (auto &alt : button->getAlternatives()) {
clear_button(&alt, alt_index);
alt_index++;
}
// reset primary button to default
clear_button(button, 0, vKey_default);
}
unsigned int Config::get_keypad_top_row(const Button &button) {
static const std::unordered_map<std::string, unsigned short> keypad_top_row_defaults = {
{"P1 Keypad 0", 0x30}, // 0 (top row number key)
{"P1 Keypad 1", 0x31},
{"P1 Keypad 2", 0x32},
{"P1 Keypad 3", 0x33},
{"P1 Keypad 4", 0x34},
{"P1 Keypad 5", 0x35},
{"P1 Keypad 6", 0x36},
{"P1 Keypad 7", 0x37},
{"P1 Keypad 8", 0x38},
{"P1 Keypad 9", 0x39},
{"P1 Keypad 00", VK_OEM_MINUS},
{"P1 Keypad Decimal", VK_OEM_PLUS},
{"P1 Keypad Insert Card", VK_BACK}};
for (const auto &[key, value] : keypad_top_row_defaults) {
if (button.getName() == key) {
return value;
}
}
return 0xFF;
}
void Config::bind_button_popup(
const std::string &bind_name, Button *button, const int button_it_max, const int alt_index) {
@@ -1872,7 +1979,40 @@ namespace overlay::windows {
}
void Config::build_analogs(const std::string &name, std::vector<Analog> *analogs) {
ImGui::AlignTextToFramePadding();
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Analogs");
const float clear_w = ImGui::CalcTextSize("Clear All").x
+ ImGui::GetStyle().FramePadding.x * 2;
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - clear_w);
// clear all
if (ImGui::Button("Clear All")) {
ImGui::OpenPopup("Clear all analog bindings");
}
if (ImGui::BeginPopupModal("Clear all analog bindings", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextUnformatted("Are you sure? This can't be undone.");
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button("Yes")) {
for (auto &analog : *analogs) {
// explicitly not checking for analog.isSet() here
// since it doesn't account for a binding with valid device but invalid index
analog.clearBindings();
analog.setLastState(0.f);
::Config::getInstance().updateBinding(games_list[games_selected], analog);
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::Separator();
if (ImGui::BeginTable("AnalogsTable", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
@@ -2419,9 +2559,6 @@ namespace overlay::windows {
ImGui::OpenPopup("Clear all light bindings");
}
ImGui::EndDisabled();
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
ImGui::SetTooltip("Unbind all lights.");
}
if (ImGui::BeginPopupModal("Clear all light bindings", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextUnformatted("Are you sure? This can't be undone.");
ImGui::Spacing();
+3 -1
View File
@@ -124,7 +124,9 @@ namespace overlay::windows {
Button *button,
const float button_velocity,
const int alt_index);
void clear_button(Button *button, const int alt_index);
void clear_button(Button *button, const int alt_index, std::optional<unsigned short> vKey_default = std::nullopt);
void reset_button_to_default(Button *button, unsigned short vKey_default);
unsigned int get_keypad_top_row(const Button &button);
void build_analogs(const std::string &name, std::vector<Analog> *analogs);
void edit_analog_popup(Analog &analog);