mirror of
https://github.com/izzy2lost/2ship2harkinian-Android.git
synced 2026-06-19 01:20:08 -07:00
397 lines
18 KiB
C++
397 lines
18 KiB
C++
#include "UIWidgets.hpp"
|
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
#include <ImGui/imgui_internal.h>
|
|
#include <libultraship/libultraship.h>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <libultraship/libultra/types.h>
|
|
|
|
namespace UIWidgets {
|
|
// Automatically adds newlines to break up text longer than a specified number of characters
|
|
// Manually included newlines will still be respected and reset the line length
|
|
// If line is midword when it hits the limit, text should break at the last encountered space
|
|
char* WrappedText(const char* text, unsigned int charactersPerLine) {
|
|
std::string newText(text);
|
|
const size_t tipLength = newText.length();
|
|
int lastSpace = -1;
|
|
int currentLineLength = 0;
|
|
for (unsigned int currentCharacter = 0; currentCharacter < tipLength; currentCharacter++) {
|
|
if (newText[currentCharacter] == '\n') {
|
|
currentLineLength = 0;
|
|
lastSpace = -1;
|
|
continue;
|
|
} else if (newText[currentCharacter] == ' ') {
|
|
lastSpace = currentCharacter;
|
|
}
|
|
|
|
if ((currentLineLength >= charactersPerLine) && (lastSpace >= 0)) {
|
|
newText[lastSpace] = '\n';
|
|
currentLineLength = currentCharacter - lastSpace - 1;
|
|
lastSpace = -1;
|
|
}
|
|
currentLineLength++;
|
|
}
|
|
|
|
return strdup(newText.c_str());
|
|
}
|
|
|
|
char* WrappedText(const std::string& text, unsigned int charactersPerLine) {
|
|
return WrappedText(text.c_str(), charactersPerLine);
|
|
}
|
|
|
|
void Tooltip(const char* text) {
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::SetTooltip("%s", WrappedText(text));
|
|
}
|
|
}
|
|
|
|
void PushStyleMenu(const ImVec4& color) {
|
|
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 0.5f));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIWidgets::Colors::DarkGray);
|
|
ImGui::PushStyleColor(ImGuiCol_Border, UIWidgets::Colors::DarkGray);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 15.0f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 3.0f);
|
|
}
|
|
|
|
void PopStyleMenu() {
|
|
ImGui::PopStyleVar(2);
|
|
ImGui::PopStyleColor(4);
|
|
}
|
|
|
|
bool BeginMenu(const char* label, const ImVec4& color) {
|
|
bool dirty = false;
|
|
PushStyleMenu(color);
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(200.0f, 0.0f), ImVec2(FLT_MAX, FLT_MAX));
|
|
if (ImGui::BeginMenu(label)) {
|
|
dirty = true;
|
|
}
|
|
PopStyleMenu();
|
|
return dirty;
|
|
}
|
|
|
|
void PushStyleMenuItem(const ImVec4& color) {
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20.0f, 15.0f));
|
|
}
|
|
|
|
void PopStyleMenuItem() {
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::PopStyleColor(1);
|
|
}
|
|
|
|
bool MenuItem(const char* label, const char* shortcut, const ImVec4& color) {
|
|
bool dirty = false;
|
|
PushStyleMenuItem(color);
|
|
if (ImGui::MenuItem(label, shortcut)) {
|
|
dirty = true;
|
|
}
|
|
PopStyleMenuItem();
|
|
return dirty;
|
|
}
|
|
|
|
void PushStyleButton(const ImVec4& color) {
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x, color.y, color.z, 0.8f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(color.x, color.y, color.z, 0.6f));
|
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 5.0f);
|
|
}
|
|
|
|
void PopStyleButton() {
|
|
ImGui::PopStyleVar(3);
|
|
ImGui::PopStyleColor(4);
|
|
}
|
|
|
|
bool Button(const char* label, const ButtonOptions& options) {
|
|
ImGui::BeginDisabled(options.disabled);
|
|
PushStyleButton(options.color);
|
|
bool dirty = ImGui::Button(label, options.size);
|
|
PopStyleButton();
|
|
ImGui::EndDisabled();
|
|
if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(options.disabledTooltip, "") != 0) {
|
|
ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip));
|
|
} else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(options.tooltip, "") != 0) {
|
|
ImGui::SetTooltip("%s", WrappedText(options.tooltip));
|
|
}
|
|
return dirty;
|
|
}
|
|
|
|
bool WindowButton(const char* label, const char* cvarName, std::shared_ptr<LUS::GuiWindow> windowPtr, const ButtonOptions& options) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
|
|
std::string buttonText = label;
|
|
bool dirty = false;
|
|
if (CVarGetInteger(cvarName, 0)) {
|
|
buttonText = ICON_FA_WINDOW_CLOSE " " + buttonText;
|
|
} else {
|
|
buttonText = ICON_FA_EXTERNAL_LINK_SQUARE " " + buttonText;
|
|
}
|
|
if (Button(buttonText.c_str(), options)) {
|
|
windowPtr->ToggleVisibility();
|
|
dirty = true;
|
|
}
|
|
ImGui::PopStyleVar();
|
|
return dirty;
|
|
}
|
|
|
|
void PushStyleCheckbox(const ImVec4& color) {
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 0.8f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 0.6f));
|
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f));
|
|
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.0f, 1.0f, 1.0f, 0.7f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 5.0f);
|
|
}
|
|
|
|
void PopStyleCheckbox() {
|
|
ImGui::PopStyleVar(3);
|
|
ImGui::PopStyleColor(5);
|
|
}
|
|
|
|
bool Checkbox(const char* label, bool* value, const CheckboxOptions& options) {
|
|
ImGui::PushID(label);
|
|
bool dirty = false;
|
|
float startX = ImGui::GetCursorPosX();
|
|
std::string invisibleLabelStr = "##" + std::string(label);
|
|
const char* invisibleLabel = invisibleLabelStr.c_str();
|
|
ImGui::BeginDisabled(options.disabled);
|
|
PushStyleCheckbox(options.color);
|
|
if (options.alignment == ComponentAlignment::Right) {
|
|
if (options.labelPosition == LabelPosition::Near || options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::GetStyle().FramePadding.x * 2 - ImGui::GetStyle().ItemSpacing.x);
|
|
} else if (options.labelPosition == LabelPosition::Above) {
|
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
|
ImGui::Text(label);
|
|
ImGui::NewLine();
|
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::GetStyle().FramePadding.x * 2 - ImGui::GetStyle().ItemSpacing.x);
|
|
}
|
|
} else if (options.alignment == ComponentAlignment::Left) {
|
|
if (options.labelPosition == LabelPosition::Above) {
|
|
ImGui::Text(label);
|
|
}
|
|
}
|
|
dirty = ImGui::Checkbox(invisibleLabel, value);
|
|
if (options.alignment == ComponentAlignment::Right) {
|
|
if (options.labelPosition == LabelPosition::Near) {
|
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x - ImGui::GetStyle().FramePadding.x * 2 - ImGui::GetStyle().ItemSpacing.x * 2);
|
|
ImGui::Text(label);
|
|
} else if (options.labelPosition == LabelPosition::Far) {
|
|
ImGui::SameLine();
|
|
ImGui::SetCursorPosX(startX);
|
|
ImGui::Text(label);
|
|
}
|
|
} else if (options.alignment == ComponentAlignment::Left) {
|
|
if (options.labelPosition == LabelPosition::Near) {
|
|
ImGui::SameLine();
|
|
ImGui::Text(label);
|
|
} else if (options.labelPosition == LabelPosition::Far) {
|
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
|
ImGui::Text(label);
|
|
}
|
|
}
|
|
PopStyleCheckbox();
|
|
ImGui::EndDisabled();
|
|
if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(options.disabledTooltip, "") != 0) {
|
|
ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip));
|
|
} else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(options.tooltip, "") != 0) {
|
|
ImGui::SetTooltip("%s", WrappedText(options.tooltip));
|
|
}
|
|
ImGui::PopID();
|
|
return dirty;
|
|
}
|
|
|
|
bool CVarCheckbox(const char* label, const char* cvarName, const CheckboxOptions& options) {
|
|
bool dirty = false;
|
|
bool value = (bool)CVarGetInteger(cvarName, options.defaultValue);
|
|
if (Checkbox(label, &value, options)) {
|
|
CVarSetInteger(cvarName, value);
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
return dirty;
|
|
}
|
|
|
|
void PushStyleCombobox(const ImVec4& color) {
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 0.8f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 0.6f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 0.6f));
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x, color.y, color.z, 0.8f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 0.5f));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 0.6f));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(color.x, color.y, color.z, 0.6f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 3.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 0.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f));
|
|
}
|
|
|
|
void PopStyleCombobox() {
|
|
ImGui::PopStyleVar(4);
|
|
ImGui::PopStyleColor(9);
|
|
}
|
|
|
|
void PushStyleSlider(const ImVec4& color) {
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(color.x, color.y, color.z, 1.0f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(1.0, 1.0, 1.0, 0.4f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(1.0, 1.0, 1.0, 0.5f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_GrabRounding, 3.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
|
|
}
|
|
|
|
void PopStyleSlider() {
|
|
ImGui::PopStyleVar(4);
|
|
ImGui::PopStyleColor(6);
|
|
}
|
|
|
|
bool SliderInt(const char* label, int32_t* value, int32_t min, int32_t max, const IntSliderOptions& options) {
|
|
bool dirty = false;
|
|
std::string invisibleLabelStr = "##" + std::string(label);
|
|
const char* invisibleLabel = invisibleLabelStr.c_str();
|
|
ImGui::PushID(label);
|
|
ImGui::BeginGroup();
|
|
ImGui::BeginDisabled(options.disabled);
|
|
PushStyleSlider(options.color);
|
|
if (options.alignment == ComponentAlignment::Left) {
|
|
if (options.labelPosition == LabelPosition::Above) {
|
|
ImGui::Text(label, *value);
|
|
}
|
|
} else if (options.alignment == ComponentAlignment::Right) {
|
|
if (options.labelPosition == LabelPosition::Above) {
|
|
ImGui::NewLine();
|
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
|
ImGui::Text(label, *value);
|
|
}
|
|
}
|
|
if (options.showButtons) {
|
|
if (Button("-", { .color = options.color, .size = Sizes::Inline }) && *value > min) {
|
|
*value -= options.step;
|
|
if (*value < min) *value = min;
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
ImGui::SameLine(0, 3.0f);
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("+").x + 20.0f + 3.0f));
|
|
} else {
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
}
|
|
if (ImGui::SliderScalar(invisibleLabel, ImGuiDataType_S32, value, &min, &max, options.format, options.flags)) {
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
if (options.showButtons) {
|
|
ImGui::SameLine(0, 3.0f);
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
if (Button("+", { .color = options.color, .size = Sizes::Inline }) && *value < max) {
|
|
*value += options.step;
|
|
if (*value > max) *value = max;
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
}
|
|
PopStyleSlider();
|
|
ImGui::EndDisabled();
|
|
ImGui::EndGroup();
|
|
if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(options.disabledTooltip, "") != 0) {
|
|
ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip));
|
|
} else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(options.tooltip, "") != 0) {
|
|
ImGui::SetTooltip("%s", WrappedText(options.tooltip));
|
|
}
|
|
ImGui::PopID();
|
|
return dirty;
|
|
}
|
|
|
|
bool CVarSliderInt(const char* label, const char* cvarName, int32_t min, int32_t max, const int32_t defaultValue, const IntSliderOptions& options) {
|
|
bool dirty = false;
|
|
int32_t value = CVarGetInteger(cvarName, defaultValue);
|
|
if (SliderInt(label, &value, min, max, options)) {
|
|
CVarSetInteger(cvarName, value);
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
return dirty;
|
|
}
|
|
|
|
bool SliderFloat(const char* label, float* value, float min, float max, const FloatSliderOptions& options) {
|
|
bool dirty = false;
|
|
std::string invisibleLabelStr = "##" + std::string(label);
|
|
const char* invisibleLabel = invisibleLabelStr.c_str();
|
|
float valueToDisplay = options.isPercentage ? *value * 100.0f : *value;
|
|
float maxToDisplay = options.isPercentage ? max * 100.0f : max;
|
|
float minToDisplay = options.isPercentage ? min * 100.0f : min;
|
|
ImGui::PushID(label);
|
|
ImGui::BeginGroup();
|
|
ImGui::BeginDisabled(options.disabled);
|
|
PushStyleSlider(options.color);
|
|
if (options.alignment == ComponentAlignment::Left) {
|
|
if (options.labelPosition == LabelPosition::Above) {
|
|
ImGui::Text(label, valueToDisplay);
|
|
}
|
|
} else if (options.alignment == ComponentAlignment::Right) {
|
|
if (options.labelPosition == LabelPosition::Above) {
|
|
ImGui::NewLine();
|
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
|
ImGui::Text(label, valueToDisplay);
|
|
}
|
|
}
|
|
if (options.showButtons) {
|
|
if (Button("-", { .color = options.color, .size = Sizes::Inline }) && *value > min) {
|
|
*value -= options.step;
|
|
if (*value < min) *value = min;
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
ImGui::SameLine(0, 3.0f);
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("+").x + 20.0f + 3.0f));
|
|
} else {
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
}
|
|
if (ImGui::SliderScalar(invisibleLabel, ImGuiDataType_Float, &valueToDisplay, &minToDisplay, &maxToDisplay, options.format, options.flags)) {
|
|
*value = options.isPercentage ? valueToDisplay / 100.0f : valueToDisplay;
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
if (options.showButtons) {
|
|
ImGui::SameLine(0, 3.0f);
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
if (Button("+", { .color = options.color, .size = Sizes::Inline }) && *value < max) {
|
|
*value += options.step;
|
|
if (*value > max) *value = max;
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
}
|
|
PopStyleSlider();
|
|
ImGui::EndDisabled();
|
|
ImGui::EndGroup();
|
|
if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(options.disabledTooltip, "") != 0) {
|
|
ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip));
|
|
} else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(options.tooltip, "") != 0) {
|
|
ImGui::SetTooltip("%s", WrappedText(options.tooltip));
|
|
}
|
|
ImGui::PopID();
|
|
return dirty;
|
|
}
|
|
|
|
bool CVarSliderFloat(const char* label, const char* cvarName, float min, float max, const float defaultValue, const FloatSliderOptions& options) {
|
|
bool dirty = false;
|
|
float value = CVarGetFloat(cvarName, defaultValue);
|
|
if (SliderFloat(label, &value, min, max, options)) {
|
|
CVarSetFloat(cvarName, value);
|
|
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
dirty = true;
|
|
}
|
|
return dirty;
|
|
}
|
|
}
|