mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
rawinput: modifiers (key combinations) (#813)
## Link to GitHub Issue or related Pull Request, if one exists Fixes #635 ## Description of change Adds optional **button modifiers**, allowing a binding to require one or more "modifier" buttons to be held before it activates. - New "Modifiers" controller page in the overlay config to bind the Modifier 1–4 source buttons. - Each binding gains a 4-bit modifier_mask (Modifier 1-4). It is persisted as an optional modifiers XML attribute on button nodes and controller-preset entries. The attribute is optional and defaults to 0, so existing config files and presets load unchanged and the feature is off unless the user opts in. - The button Edit properties popup gains a "Modifiers" dropdown to pick which modifiers a binding requires. Doesn't apply to MIDI though. - Input evaluation skips a binding whose required modifiers are not held, falling through to its alternatives; velocity reporting uses the same gated path. - Controller-preset templates gain a "Modifiers" group for importing and exporting. ## Testing
This commit is contained in:
@@ -85,6 +85,7 @@ namespace overlay::windows {
|
||||
static const std::vector<std::pair<const char *, ControllerPage>> CONTROLLER_PAGE_GROUPS = {
|
||||
{ "Buttons", ControllerPage::CONTROLLER_PAGE_BUTTONS },
|
||||
{ "Keypads", ControllerPage::CONTROLLER_PAGE_KEYPADS },
|
||||
{ "Modifiers", ControllerPage::CONTROLLER_PAGE_MODIFIERS },
|
||||
{ "Analogs", ControllerPage::CONTROLLER_PAGE_ANALOGS },
|
||||
{ "Overlay", ControllerPage::CONTROLLER_PAGE_OVERLAY },
|
||||
{ "Lights", ControllerPage::CONTROLLER_PAGE_LIGHTS },
|
||||
@@ -616,6 +617,21 @@ namespace overlay::windows {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ControllerPage::CONTROLLER_PAGE_MODIFIERS: {
|
||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "Button Modifiers");
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped(
|
||||
"Bind modifier buttons below, then open Edit properties on any button binding "
|
||||
"(except MIDI) to select the required modifiers. This allows you to configure "
|
||||
"button combinations.\n\n"
|
||||
"For example, if you bind the Start button as Modifier 1, and then bind "
|
||||
"the A button for Service Coin and set it to require Modifier 1, you press "
|
||||
"Start + A to trigger Service Coin.");
|
||||
ImGui::TextUnformatted("");
|
||||
this->build_buttons(
|
||||
"Modifier", games::get_buttons_modifiers(this->games_selected_name));
|
||||
break;
|
||||
}
|
||||
case ControllerPage::CONTROLLER_PAGE_ANALOGS: {
|
||||
|
||||
// help text for binding analog, if the game has one
|
||||
@@ -1256,7 +1272,8 @@ namespace overlay::windows {
|
||||
if (open_edit) {
|
||||
ImGui::OpenPopup(edit_name.c_str());
|
||||
}
|
||||
edit_button_popup(edit_name, button_display, button, button_velocity, alt_index);
|
||||
edit_button_popup(
|
||||
edit_name, button_display, button, button_velocity, alt_index, name != "Modifier");
|
||||
|
||||
// clean up
|
||||
ImGui::PopID();
|
||||
@@ -1275,6 +1292,7 @@ namespace overlay::windows {
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setInvert(false);
|
||||
button->setModifierMask(0);
|
||||
button->setLastState(GameAPI::Buttons::BUTTON_NOT_PRESSED);
|
||||
button->setLastVelocity(0);
|
||||
button->setTemporary(false);
|
||||
@@ -1370,6 +1388,7 @@ namespace overlay::windows {
|
||||
button->setDeviceIdentifier(device->name);
|
||||
button->setVKey(static_cast<unsigned short>(i));
|
||||
button->setAnalogType(BAT_NONE);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button, alt_index - 1);
|
||||
ImGui::CloseCurrentPopup();
|
||||
@@ -1402,6 +1421,7 @@ namespace overlay::windows {
|
||||
button->setDeviceIdentifier(device->name);
|
||||
button->setVKey(vkey);
|
||||
button->setAnalogType(BAT_NONE);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button, alt_index - 1);
|
||||
ImGui::CloseCurrentPopup();
|
||||
@@ -1537,6 +1557,7 @@ namespace overlay::windows {
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button,
|
||||
alt_index - 1);
|
||||
@@ -1582,6 +1603,7 @@ namespace overlay::windows {
|
||||
button->setDebounceUp(0.0);
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
// same idea as setMidiVKey - keep velocity threshold consistent
|
||||
button->setVelocityThreshold(
|
||||
device->midiInfo->v2_velocity_threshold[button->getVKey()]);
|
||||
@@ -1609,6 +1631,7 @@ namespace overlay::windows {
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button,
|
||||
alt_index - 1);
|
||||
@@ -1636,6 +1659,7 @@ namespace overlay::windows {
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button,
|
||||
alt_index - 1);
|
||||
@@ -1663,6 +1687,7 @@ namespace overlay::windows {
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button,
|
||||
alt_index - 1);
|
||||
@@ -1689,6 +1714,7 @@ namespace overlay::windows {
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button,
|
||||
alt_index - 1);
|
||||
@@ -1710,6 +1736,7 @@ namespace overlay::windows {
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button,
|
||||
alt_index - 1);
|
||||
@@ -1740,6 +1767,7 @@ namespace overlay::windows {
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button,
|
||||
alt_index - 1);
|
||||
@@ -1816,6 +1844,7 @@ namespace overlay::windows {
|
||||
if (RI_MGR->XINPUT_MGR->get_any_button_pressed(xinput)) {
|
||||
button->setDeviceIdentifier(xinput::get_device_desc(xinput.player));
|
||||
button->setVKey(static_cast<uint16_t>(xinput.button));
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button, alt_index - 1);
|
||||
inc_buttons_many_index(button_it_max);
|
||||
@@ -1872,6 +1901,7 @@ namespace overlay::windows {
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setModifierMask(0);
|
||||
::Config::getInstance().updateBinding(
|
||||
games_list[games_selected], *button,
|
||||
alt_index - 1);
|
||||
@@ -1890,27 +1920,103 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::build_modifier_picker(Button &button) {
|
||||
bool changed = false;
|
||||
auto modifier_mask = button.getModifierMask();
|
||||
|
||||
// collect live states and build the closed dropdown summary
|
||||
uint8_t pressed_modifier_mask = 0;
|
||||
std::string modifier_preview;
|
||||
auto *modifier_buttons = games::get_buttons_modifiers(this->games_selected_name);
|
||||
for (uint8_t index = 0; index < games::ModifierButtons::Size; index++) {
|
||||
const uint8_t modifier_bit = UINT8_C(1) << index;
|
||||
if (modifier_buttons && index < modifier_buttons->size() &&
|
||||
GameAPI::Buttons::getState(RI_MGR, modifier_buttons->at(index))) {
|
||||
pressed_modifier_mask |= modifier_bit;
|
||||
}
|
||||
if ((modifier_mask & modifier_bit) != 0) {
|
||||
modifier_preview += modifier_preview.empty() ? "Mod " : "+";
|
||||
modifier_preview += std::to_string(index + 1);
|
||||
}
|
||||
}
|
||||
if (modifier_preview.empty()) {
|
||||
modifier_preview = "None";
|
||||
}
|
||||
|
||||
// color the combo when all requirements are held, but keep its label unchanged
|
||||
const bool all_modifiers_pressed = modifier_mask != 0 &&
|
||||
(pressed_modifier_mask & modifier_mask) == modifier_mask;
|
||||
if (all_modifiers_pressed) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, TEXT_COLOR_GREEN);
|
||||
}
|
||||
const bool combo_open = ImGui::BeginCombo("##Modifiers", modifier_preview.c_str());
|
||||
if (all_modifiers_pressed) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// update requirements while highlighting modifier sources that are currently pressed
|
||||
if (combo_open) {
|
||||
for (uint8_t index = 0; index < games::ModifierButtons::Size; index++) {
|
||||
const uint8_t modifier_bit = UINT8_C(1) << index;
|
||||
bool required = (modifier_mask & modifier_bit) != 0;
|
||||
const auto label = fmt::format("Modifier {}", index + 1);
|
||||
const bool modifier_pressed = (pressed_modifier_mask & modifier_bit) != 0;
|
||||
if (modifier_pressed) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, TEXT_COLOR_GREEN);
|
||||
}
|
||||
const bool requirement_changed = ImGui::Checkbox(label.c_str(), &required);
|
||||
if (modifier_pressed) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (requirement_changed) {
|
||||
modifier_mask ^= modifier_bit;
|
||||
button.setModifierMask(modifier_mask);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::TextUnformatted("Modifiers");
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void Config::edit_button_popup(
|
||||
const std::string &edit_name,
|
||||
const std::string &button_display,
|
||||
Button *button,
|
||||
const float button_velocity,
|
||||
const int alt_index) {
|
||||
const int alt_index,
|
||||
const bool allow_modifiers) {
|
||||
|
||||
const auto button_state = GameAPI::Buttons::getState(RI_MGR, *button, false);
|
||||
if (ImGui::BeginPopupModal(edit_name.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
bool dirty = false;
|
||||
auto device = RI_MGR->devices_get(button->getDeviceIdentifier());
|
||||
|
||||
auto switch_device = [&](const std::string &device_identifier) {
|
||||
button->setDeviceIdentifier(device_identifier);
|
||||
button->setAnalogType(ButtonAnalogType::BAT_NONE);
|
||||
button->setDebounceUp(0.0);
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setVKey(0);
|
||||
button->setInvert(false);
|
||||
button->setModifierMask(0);
|
||||
dirty = true;
|
||||
};
|
||||
|
||||
// binding
|
||||
ImGui::Text("Binding");
|
||||
ImGui::Separator();
|
||||
|
||||
// combo for devices
|
||||
std::string device_desc = (device != nullptr) ? device->desc : "Empty (Naive)";
|
||||
if (ImGui::BeginCombo("Device Identifier", device_desc.c_str())) {
|
||||
if (ImGui::Selectable("Empty (Naive)", button->isNaive())) {
|
||||
button->setDeviceIdentifier("");
|
||||
dirty = true;
|
||||
if (ImGui::Selectable("Empty (Naive)", button->isNaive()) && !button->isNaive()) {
|
||||
switch_device("");
|
||||
}
|
||||
if (button->isNaive()) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
@@ -1918,17 +2024,8 @@ namespace overlay::windows {
|
||||
for (auto &device : RI_MGR->devices_get()) {
|
||||
bool selected = button->getDeviceIdentifier() == device.name.c_str();
|
||||
const auto device_desc = fmt::format("{}##{}", device.desc, device.name);
|
||||
if (ImGui::Selectable(device_desc.c_str(), selected)) {
|
||||
button->setDeviceIdentifier(device.name);
|
||||
// reset controls when switching devices
|
||||
button->setAnalogType(ButtonAnalogType::BAT_NONE);
|
||||
button->setDebounceUp(0.0);
|
||||
button->setDebounceDown(0.0);
|
||||
button->setBatThreshold(0);
|
||||
button->setVelocityThreshold(0);
|
||||
button->setVKey(0);
|
||||
button->setInvert(false);
|
||||
dirty = true;
|
||||
if (ImGui::Selectable(device_desc.c_str(), selected) && !selected) {
|
||||
switch_device(device.name);
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
@@ -1936,6 +2033,7 @@ namespace overlay::windows {
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
device = RI_MGR->devices_get(button->getDeviceIdentifier());
|
||||
|
||||
// analog type (only for HID)
|
||||
const auto bat = button->getAnalogType();
|
||||
@@ -2227,21 +2325,22 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
// modifiers
|
||||
{
|
||||
const bool is_midi = device != nullptr && device->type == rawinput::MIDI;
|
||||
if (allow_modifiers && button->isValid() && !is_midi) {
|
||||
dirty |= build_modifier_picker(*button);
|
||||
}
|
||||
}
|
||||
|
||||
// preview
|
||||
if (!button_display.empty()) {
|
||||
ImGui::TextUnformatted("\nPreview");
|
||||
ImGui::Separator();
|
||||
if (button_state == GameAPI::Buttons::State::BUTTON_PRESSED) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 0.7f, 0.f, 1.f));
|
||||
}
|
||||
// this doesn't account for utf-8 but whatever
|
||||
if (button_display.size() >= 40) {
|
||||
ImGui::Text("%.37s...", button_display.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(button_display.c_str());
|
||||
} else {
|
||||
ImGui::TextUnformatted(button_display.c_str());
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, TEXT_COLOR_GREEN);
|
||||
}
|
||||
ImGui::TextTruncated(button_display, ImGui::GetContentRegionAvail().x);
|
||||
ImGui::TextUnformatted("\n");
|
||||
if (button_state == GameAPI::Buttons::State::BUTTON_PRESSED) {
|
||||
ImGui::PopStyleColor();
|
||||
@@ -2250,8 +2349,10 @@ namespace overlay::windows {
|
||||
ImGui::TextUnformatted("");
|
||||
}
|
||||
|
||||
|
||||
// options
|
||||
ImGui::Text("Options");
|
||||
ImGui::TextUnformatted("Options");
|
||||
ImGui::Separator();
|
||||
|
||||
// check for debounce
|
||||
if (button->getDebounceUp() || button->getDebounceDown()
|
||||
@@ -2370,6 +2471,7 @@ namespace overlay::windows {
|
||||
ImGui::ProgressBar(button_velocity);
|
||||
} else {
|
||||
ImGui::Text("State");
|
||||
ImGui::Separator();
|
||||
ImGui::ProgressBar(button_velocity);
|
||||
}
|
||||
|
||||
@@ -5490,6 +5592,12 @@ namespace overlay::windows {
|
||||
tmpl.buttons.emplace_back(btn);
|
||||
}
|
||||
}
|
||||
auto *modifier_buttons = games::get_buttons_modifiers(this->games_selected_name);
|
||||
if (modifier_buttons) {
|
||||
for (auto &modifier : *modifier_buttons) {
|
||||
tmpl.modifier_buttons.emplace_back(modifier);
|
||||
}
|
||||
}
|
||||
|
||||
// capture keypad buttons
|
||||
auto *keypad_buttons = games::get_buttons_keypads(this->games_selected_name);
|
||||
@@ -5545,6 +5653,17 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
// clear all modifier buttons
|
||||
auto *modifier_buttons = games::get_buttons_modifiers(this->games_selected_name);
|
||||
if (modifier_buttons) {
|
||||
for (auto &modifier : *modifier_buttons) {
|
||||
for (int ai = (int)modifier.getAlternatives().size() - 1; ai >= 0; ai--) {
|
||||
clear_button(&modifier.getAlternatives()[ai], ai + 1);
|
||||
}
|
||||
clear_button(&modifier, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// clear all keypad buttons
|
||||
auto *keypad_buttons = games::get_buttons_keypads(this->games_selected_name);
|
||||
if (keypad_buttons) {
|
||||
@@ -5638,6 +5757,7 @@ namespace overlay::windows {
|
||||
btn.setDebounceDown(entry->debounce_down);
|
||||
btn.setBatThreshold(entry->bat_threshold);
|
||||
btn.setVelocityThreshold(entry->velocity_threshold);
|
||||
btn.setModifierMask(entry->modifier_mask);
|
||||
::Config::getInstance().updateBinding(game, btn, -1);
|
||||
} else {
|
||||
Button alt_btn(btn.getName());
|
||||
@@ -5649,6 +5769,7 @@ namespace overlay::windows {
|
||||
alt_btn.setDebounceDown(entry->debounce_down);
|
||||
alt_btn.setBatThreshold(entry->bat_threshold);
|
||||
alt_btn.setVelocityThreshold(entry->velocity_threshold);
|
||||
alt_btn.setModifierMask(entry->modifier_mask);
|
||||
alt_btn.setTemporary(true);
|
||||
btn.getAlternatives().push_back(alt_btn);
|
||||
::Config::getInstance().updateBinding(
|
||||
@@ -5667,6 +5788,11 @@ namespace overlay::windows {
|
||||
if (this->apply_buttons) {
|
||||
apply_buttons(tmpl.buttons, games::get_buttons(this->games_selected_name));
|
||||
}
|
||||
if (this->apply_modifiers) {
|
||||
apply_buttons(
|
||||
tmpl.modifier_buttons,
|
||||
games::get_buttons_modifiers(this->games_selected_name));
|
||||
}
|
||||
if (this->apply_keypads) {
|
||||
apply_buttons(tmpl.keypad_buttons, games::get_buttons_keypads(this->games_selected_name));
|
||||
}
|
||||
@@ -5908,7 +6034,7 @@ namespace overlay::windows {
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Clears all game button, analog, and light bindings.");
|
||||
"Clears all game button, modifier, keypad, analog, and light bindings.");
|
||||
if (this->all_cleared) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted("Done.");
|
||||
@@ -5923,6 +6049,8 @@ namespace overlay::windows {
|
||||
bool selection_changed = false;
|
||||
selection_changed |= ImGui::Checkbox("Buttons", &this->apply_buttons);
|
||||
ImGui::SameLine();
|
||||
selection_changed |= ImGui::Checkbox("Modifiers", &this->apply_modifiers);
|
||||
ImGui::SameLine();
|
||||
selection_changed |= ImGui::Checkbox("Keypads", &this->apply_keypads);
|
||||
ImGui::SameLine();
|
||||
selection_changed |= ImGui::Checkbox("Analogs", &this->apply_analogs);
|
||||
@@ -5932,7 +6060,7 @@ namespace overlay::windows {
|
||||
std::fill(this->template_is_applied.begin(), this->template_is_applied.end(), false);
|
||||
}
|
||||
|
||||
if (!this->apply_buttons && !this->apply_keypads &&
|
||||
if (!this->apply_buttons && !this->apply_modifiers && !this->apply_keypads &&
|
||||
!this->apply_analogs && !this->apply_lights) {
|
||||
ImGui::TextUnformatted("\nYou must select at least one group to apply.\n\n");
|
||||
}
|
||||
@@ -5972,7 +6100,8 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
if (!apply_buttons && !apply_keypads && !apply_analogs && !apply_lights) {
|
||||
if (!apply_buttons && !apply_modifiers && !apply_keypads &&
|
||||
!apply_analogs && !apply_lights) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
@@ -6141,7 +6270,8 @@ namespace overlay::windows {
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (!apply_buttons && !apply_keypads && !apply_analogs && !apply_lights) {
|
||||
if (!apply_buttons && !apply_modifiers && !apply_keypads &&
|
||||
!apply_analogs && !apply_lights) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
@@ -6220,12 +6350,14 @@ namespace overlay::windows {
|
||||
ImGui::TextUnformatted("Pick which groups to save:");
|
||||
ImGui::Checkbox("Buttons", &this->save_buttons);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Modifiers", &this->save_modifiers);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Keypads", &this->save_keypads);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Analogs", &this->save_analogs);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Lights", &this->save_lights);
|
||||
if (!this->save_analogs && !this->save_keypads &&
|
||||
if (!this->save_buttons && !this->save_modifiers && !this->save_keypads &&
|
||||
!this->save_analogs && !this->save_lights) {
|
||||
ImGui::TextUnformatted("\nYou must select at least one group to save.\n\n");
|
||||
}
|
||||
@@ -6277,7 +6409,8 @@ namespace overlay::windows {
|
||||
|
||||
ImGui::BeginDisabled(
|
||||
!all_labels_set ||
|
||||
(!this->save_buttons && !this->save_keypads && !this->save_analogs && !this->save_lights));
|
||||
(!this->save_buttons && !this->save_modifiers && !this->save_keypads &&
|
||||
!this->save_analogs && !this->save_lights));
|
||||
|
||||
if (ImGui::Button("Save")) {
|
||||
// replace device IDs with labels in the template
|
||||
@@ -6286,7 +6419,8 @@ namespace overlay::windows {
|
||||
template_save_sources[si], template_save_labels[si]);
|
||||
}
|
||||
if (save_user_template(template_pending_save,
|
||||
this->save_buttons, this->save_keypads, this->save_analogs, this->save_lights)) {
|
||||
this->save_buttons, this->save_modifiers, this->save_keypads,
|
||||
this->save_analogs, this->save_lights)) {
|
||||
template_save_name[0] = '\0';
|
||||
templates_cache_dirty = true;
|
||||
}
|
||||
@@ -6368,7 +6502,7 @@ namespace overlay::windows {
|
||||
t.rename_source(template_save_sources[si], template_save_labels[si]);
|
||||
}
|
||||
}
|
||||
save_user_template(t, true, true, true, true);
|
||||
save_user_template(t, true, true, true, true, true);
|
||||
templates_cache_dirty = true;
|
||||
template_save_sources.clear();
|
||||
template_save_labels.clear();
|
||||
|
||||
Reference in New Issue
Block a user