mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-22 14:23:44 -07:00
Add error state to InputString, and corresponding members and builders to InputOptions.
Implement saving and loading of preset files.
This commit is contained in:
parent
a71e02650d
commit
75fbb46297
3 changed files with 144 additions and 27 deletions
|
@ -2,12 +2,16 @@
|
|||
#include <variant>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <libultraship/bridge.h>
|
||||
#include <libultraship/libultraship.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include "soh/SohGui/MenuTypes.h"
|
||||
#include "soh/SohGui/SohMenu.h"
|
||||
#include "soh/SohGui/SohGui.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace SohGui {
|
||||
extern std::shared_ptr<SohMenu> mSohMenu;
|
||||
}
|
||||
|
@ -108,26 +112,13 @@ enum PresetSection {
|
|||
};
|
||||
|
||||
struct PresetInfo {
|
||||
std::unordered_map<std::string, CVarVariant> settings;
|
||||
std::unordered_map<std::string, CVarVariant> enhancements;
|
||||
std::unordered_map<std::string, CVarVariant> audio;
|
||||
std::unordered_map<std::string, CVarVariant> cosmetics;
|
||||
std::unordered_map<std::string, CVarVariant> rando;
|
||||
std::unordered_map<std::string, CVarVariant> trackers;
|
||||
nlohmann::json presetValues;
|
||||
|
||||
bool applySettings = true, applyEnhancements = true, applyAudio = true, applyCosmetics = true, applyRando = true, applyTrackers = true;
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, PresetInfo> presets = {
|
||||
{ "Vanilla Plus", {
|
||||
{},
|
||||
{{"DpadEquips", 1}},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
}}
|
||||
};
|
||||
static std::map<std::string, PresetInfo> presets;
|
||||
static std::string presetFolder;
|
||||
|
||||
void DrawSectionCheck(const std::string& name, bool empty, bool* pointer, std::string section) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
|
@ -147,11 +138,95 @@ void DrawSectionCheck(const std::string& name, bool empty, bool* pointer, std::s
|
|||
}
|
||||
}
|
||||
|
||||
void LoadPresets() {
|
||||
if (!fs::exists(presetFolder)) {
|
||||
return;
|
||||
}
|
||||
for (auto const& preset : fs::directory_iterator(presetFolder)) {
|
||||
std::ifstream ifs(preset.path());
|
||||
|
||||
auto json = nlohmann::json::parse(ifs);
|
||||
try {
|
||||
if (!json.contains("presetName")) {
|
||||
spdlog::error(fmt::format("Attempted to load file {} as a preset, but was not a preset file.", preset.path().filename().string()));
|
||||
return;
|
||||
}
|
||||
presets[json["presetName"]].presetValues = json;
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
void SavePreset(std::string& presetName) {
|
||||
std::string folderPath = Ship::Context::GetInstance()->GetPathRelativeToAppDirectory("presets");
|
||||
if (!fs::exists(folderPath)) {
|
||||
fs::create_directory(folderPath);
|
||||
}
|
||||
presets[presetName].presetValues["presetName"] = presetName;
|
||||
std::ofstream file(fmt::format("{}/{}.json", folderPath, presetName));
|
||||
file << presets[presetName].presetValues.dump(4);
|
||||
}
|
||||
|
||||
static std::string newPresetName;
|
||||
static bool newPresetSettings = true, newPresetEnhancements = true, newPresetAudio = true, newPresetCosmetics = true, newPresetRando = true, newPresetTrackers = true;
|
||||
|
||||
void PresetsCustomWidget(WidgetInfo& info) {
|
||||
UIWidgets::PushStyleTabs(THEME_COLOR);
|
||||
ImGui::PushFont(OTRGlobals::Instance->fontMonoLargest);
|
||||
if (UIWidgets::Button("New Preset", UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) {
|
||||
ImGui::OpenPopup("newPreset");
|
||||
}
|
||||
ImGui::SetNextWindowSize({400, 400});
|
||||
if (ImGui::BeginPopup("newPreset", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoTitleBar)) {
|
||||
bool nameExists = presets.contains(newPresetName);
|
||||
UIWidgets::InputString("Preset Name", &newPresetName, UIWidgets::InputOptions().Color(THEME_COLOR).Size({200, 40})
|
||||
.ComponentAlignment(UIWidgets::ComponentAlignments::Right).LabelPosition(UIWidgets::LabelPositions::Near).ErrorText("Preset name already exists").HasError(nameExists));
|
||||
nameExists = presets.contains(newPresetName);
|
||||
bool noneSelected = !newPresetSettings && !newPresetEnhancements && !newPresetAudio && !newPresetCosmetics && !newPresetRando && !newPresetTrackers;
|
||||
const char* disabledTooltip = (newPresetName.empty() ? "Preset name is empty" : (noneSelected ? "No sections selected" : "Preset name already exists"));
|
||||
UIWidgets::Checkbox("Save Settings", &newPresetSettings, UIWidgets::CheckboxOptions().Color(THEME_COLOR).Padding({6.0f, 6.0f}));
|
||||
UIWidgets::Checkbox("Save Enhancements", &newPresetEnhancements, UIWidgets::CheckboxOptions().Color(THEME_COLOR).Padding({6.0f, 6.0f}));
|
||||
UIWidgets::Checkbox("Save Audio", &newPresetAudio, UIWidgets::CheckboxOptions().Color(THEME_COLOR).Padding({6.0f, 6.0f}));
|
||||
UIWidgets::Checkbox("Save Cosmetics", &newPresetCosmetics, UIWidgets::CheckboxOptions().Color(THEME_COLOR).Padding({6.0f, 6.0f}));
|
||||
UIWidgets::Checkbox("Save Rando Settings", &newPresetRando, UIWidgets::CheckboxOptions().Color(THEME_COLOR).Padding({6.0f, 6.0f}));
|
||||
UIWidgets::Checkbox("Save Trackers", &newPresetTrackers, UIWidgets::CheckboxOptions().Color(THEME_COLOR).Padding({6.0f, 6.0f}));
|
||||
if (UIWidgets::Button("Save", UIWidgets::ButtonOptions({.disabled = nameExists || noneSelected || newPresetName.empty(), .disabledTooltip = disabledTooltip})
|
||||
.Padding({6.0f, 6.0f}).Color(THEME_COLOR))) {
|
||||
presets[newPresetName] = {};
|
||||
auto config = Ship::Context::GetInstance()->GetConfig()->GetNestedJson();
|
||||
if (newPresetSettings) {
|
||||
presets[newPresetName].presetValues["settings"] = config["CVars"][CVAR_PREFIX_SETTING];
|
||||
presets[newPresetName].presetValues["windows"] = config["CVars"][CVAR_PREFIX_WINDOW];
|
||||
}
|
||||
if (newPresetEnhancements) {
|
||||
presets[newPresetName].presetValues["enhancements"] = config["CVars"][CVAR_PREFIX_ENHANCEMENT];
|
||||
presets[newPresetName].presetValues["randoEnhancements"] = config["CVars"][CVAR_PREFIX_RANDOMIZER_ENHANCEMENT];
|
||||
}
|
||||
if (newPresetAudio) {
|
||||
presets[newPresetName].presetValues["audio"] = config["CVars"][CVAR_PREFIX_AUDIO];
|
||||
}
|
||||
if (newPresetCosmetics) {
|
||||
presets[newPresetName].presetValues["cosmetics"] = config["CVars"][CVAR_PREFIX_COSMETIC];
|
||||
}
|
||||
if (newPresetRando) {
|
||||
presets[newPresetName].presetValues["rando"] = config["CVars"][CVAR_PREFIX_RANDOMIZER_SETTING];
|
||||
}
|
||||
if (newPresetTrackers) {
|
||||
presets[newPresetName].presetValues["trackers"] = config["CVars"][CVAR_PREFIX_TRACKER];
|
||||
}
|
||||
SavePreset(newPresetName);
|
||||
newPresetName = "";
|
||||
newPresetSettings = newPresetEnhancements = newPresetAudio = newPresetCosmetics = newPresetRando = newPresetTrackers = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (UIWidgets::Button("Cancel", UIWidgets::ButtonOptions().Padding({6.0f, 6.0f}).Color(THEME_COLOR))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
UIWidgets::PushStyleTabs(THEME_COLOR);
|
||||
if (ImGui::BeginTable("PresetWidgetTable", 9)) {
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 200);
|
||||
ImGui::TableSetupColumn("Settings");
|
||||
ImGui::TableSetupColumn("Enhancements");
|
||||
ImGui::TableSetupColumn("Audio");
|
||||
|
@ -183,33 +258,46 @@ void PresetsCustomWidget(WidgetInfo& info) {
|
|||
UIWidgets::Tooltip("Trackers");
|
||||
UIWidgets::PopStyleButton();
|
||||
|
||||
if (presets.empty()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("No presets found.");
|
||||
ImGui::EndTable();
|
||||
UIWidgets::PopStyleTabs();
|
||||
ImGui::PopFont();
|
||||
return;
|
||||
}
|
||||
for (auto& [name, info] : presets) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
DrawSectionCheck(name, info.settings.empty(), &info.applySettings, "settings");
|
||||
DrawSectionCheck(name, !info.presetValues.contains("settings"), &info.applySettings, "settings");
|
||||
ImGui::TableNextColumn();
|
||||
DrawSectionCheck(name, info.enhancements.empty(), &info.applyEnhancements, "enhancements");
|
||||
DrawSectionCheck(name, !info.presetValues.contains("enhancements"), &info.applyEnhancements, "enhancements");
|
||||
ImGui::TableNextColumn();
|
||||
DrawSectionCheck(name, info.audio.empty(), &info.applyAudio, "audio");
|
||||
DrawSectionCheck(name, !info.presetValues.contains("audio"), &info.applyAudio, "audio");
|
||||
ImGui::TableNextColumn();
|
||||
DrawSectionCheck(name, info.cosmetics.empty(), &info.applyCosmetics, "cosmetics");
|
||||
DrawSectionCheck(name, !info.presetValues.contains("cosmetics"), &info.applyCosmetics, "cosmetics");
|
||||
ImGui::TableNextColumn();
|
||||
DrawSectionCheck(name, info.rando.empty(), &info.applyRando, "rando");
|
||||
DrawSectionCheck(name, !info.presetValues.contains("rando"), &info.applyRando, "rando");
|
||||
ImGui::TableNextColumn();
|
||||
DrawSectionCheck(name, info.trackers.empty(), &info.applyTrackers, "trackers");
|
||||
DrawSectionCheck(name, !info.presetValues.contains("trackers"), &info.applyTrackers, "trackers");
|
||||
ImGui::TableNextColumn();
|
||||
UIWidgets::PushStyleButton(THEME_COLOR);
|
||||
if (UIWidgets::Button(("Apply##" + name).c_str(), UIWidgets::ButtonOptions().Padding({6.0f, 6.0f}))) {
|
||||
|
||||
if (info.applySettings && info.presetValues.contains("gSettings")) {
|
||||
}
|
||||
}
|
||||
UIWidgets::PopStyleButton();
|
||||
ImGui::TableNextColumn();
|
||||
UIWidgets::PushStyleButton(THEME_COLOR);
|
||||
if (UIWidgets::Button(("Delete##" + name).c_str(), UIWidgets::ButtonOptions().Padding({6.0f, 6.0f}))) {
|
||||
presets.erase(name);
|
||||
UIWidgets::PopStyleButton();
|
||||
break;
|
||||
}
|
||||
UIWidgets::PopStyleButton();
|
||||
}
|
||||
|
@ -225,6 +313,8 @@ void RegisterPresetsWidgets() {
|
|||
WidgetPath path = { "Settings", "Presets", SECTION_COLUMN_1 };
|
||||
SohGui::mSohMenu->AddWidget(path, "PresetsWidget", WIDGET_CUSTOM)
|
||||
.CustomFunction(PresetsCustomWidget);
|
||||
presetFolder = Ship::Context::GetInstance()->GetPathRelativeToAppDirectory("presets");
|
||||
LoadPresets();
|
||||
}
|
||||
|
||||
//static RegisterMenuUpdateFunc updateFunc(UpdateResolutionVars, "Settings", "General");
|
||||
|
|
|
@ -318,6 +318,7 @@ bool Checkbox(const char* _label, bool* value, const CheckboxOptions& options) {
|
|||
|
||||
ImGui::ItemSize(total_bb, style.FramePadding.y);
|
||||
if (!ImGui::ItemAdd(total_bb, id)) {
|
||||
PopStyleCheckbox();
|
||||
ImGui::EndDisabled();
|
||||
return false;
|
||||
}
|
||||
|
@ -333,7 +334,7 @@ bool Checkbox(const char* _label, bool* value, const CheckboxOptions& options) {
|
|||
checkPos.y += label_size.y + (style.ItemInnerSpacing.y * 2.0f);
|
||||
} else {
|
||||
// Center with checkbox automatically
|
||||
labelPos.y += ImGui::CalcTextSize("g").y / 8;
|
||||
labelPos.y += ImGui::GetStyle().FramePadding.y;
|
||||
}
|
||||
if (options.alignment == ComponentAlignments::Right) {
|
||||
checkPos.x = total_bb.Max.x - square_sz;
|
||||
|
@ -762,6 +763,9 @@ bool InputString(const char* label, std::string* value, const InputOptions& opti
|
|||
ImGui::BeginGroup();
|
||||
ImGui::BeginDisabled(options.disabled);
|
||||
PushStyleInput(options.color);
|
||||
if (options.hasError) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ColorValues.at(Colors::Red));
|
||||
}
|
||||
float width = (options.size == ImVec2(0, 0)) ? ImGui::GetContentRegionAvail().x : options.size.x;
|
||||
if (options.alignment == ComponentAlignments::Left) {
|
||||
if (options.labelPosition == LabelPositions::Above) {
|
||||
|
@ -787,10 +791,16 @@ bool InputString(const char* label, std::string* value, const InputOptions& opti
|
|||
ImGui::SameLine(17.0f);
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "%s", options.placeholder.c_str());
|
||||
}
|
||||
if (options.hasError) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
PopStyleInput();
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndGroup();
|
||||
if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) &&
|
||||
if (options.hasError&& ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) &&
|
||||
!Ship_IsCStringEmpty(options.errorText)) {
|
||||
ImGui::SetTooltip("%s", WrappedText(options.errorText).c_str());
|
||||
} else if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) &&
|
||||
!Ship_IsCStringEmpty(options.disabledTooltip)) {
|
||||
ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str());
|
||||
} else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) {
|
||||
|
|
|
@ -425,6 +425,8 @@ struct InputOptions : WidgetOptions {
|
|||
std::string defaultValue = "";
|
||||
bool secret = false;
|
||||
ImGuiInputFlags addedFlags = 0;
|
||||
bool hasError = false;
|
||||
const char* errorText = "";
|
||||
|
||||
InputOptions& Tooltip(const char* tooltip_) {
|
||||
WidgetOptions::tooltip = tooltip_;
|
||||
|
@ -459,6 +461,11 @@ struct InputOptions : WidgetOptions {
|
|||
return *this;
|
||||
}
|
||||
|
||||
InputOptions& ComponentAlignment(ComponentAlignments alignment_) {
|
||||
alignment = alignment_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
InputOptions& DefaultValue(std::string defaultValue_) {
|
||||
defaultValue = defaultValue_;
|
||||
return *this;
|
||||
|
@ -468,6 +475,16 @@ struct InputOptions : WidgetOptions {
|
|||
secret = secret_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
InputOptions& HasError(bool error_ = false) {
|
||||
hasError = error_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
InputOptions& ErrorText(const char* errorText_) {
|
||||
errorText = errorText_;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
void PushStyleMenu(const ImVec4& color);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue