Modern Menu (#183)
* Add modern menu. Add and include LUS cvars .cmake file for macro usage in modern menu. * Un-ignore new .cmake * Copy new Resolution Editor stuff from 2ship (incomplete). Fix fullscreen checkbox. Finish changes for new UIWidgets. Cleanup ShipUtils. * Remove duplicate asm-differ entry from .gitmodules. * Re-implement Freecam options. * Cleanup freecam character follow buttons. Partially re-implement Multiplayer window as enhancement sidebar (needs text input widget to finish). Cleanup disables. --------- Co-authored-by: MegaMech <MegaMech@users.noreply.github.com>
This commit is contained in:
parent
301bbd3cd9
commit
695879c4cb
|
|
@ -4,9 +4,6 @@
|
||||||
[submodule "tools/decomp-permuter"]
|
[submodule "tools/decomp-permuter"]
|
||||||
path = tools/decomp-permuter
|
path = tools/decomp-permuter
|
||||||
url = https://github.com/simonlindholm/decomp-permuter
|
url = https://github.com/simonlindholm/decomp-permuter
|
||||||
[submodule "tools/asm-differ"]
|
|
||||||
path = tools/asm-differ
|
|
||||||
url = https://github.com/simonlindholm/asm-differ
|
|
||||||
[submodule "doxygen-awesome-css"]
|
[submodule "doxygen-awesome-css"]
|
||||||
path = doxygen-awesome-css
|
path = doxygen-awesome-css
|
||||||
url = https://github.com/jothepro/doxygen-awesome-css.git
|
url = https://github.com/jothepro/doxygen-awesome-css.git
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Spaghetti
|
||||||
# Add a custom module path to locate additional CMake modules
|
# Add a custom module path to locate additional CMake modules
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
||||||
include(FindFontconfig)
|
include(FindFontconfig)
|
||||||
|
include(CMake/lus-cvars.cmake)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
include(libultraship/cmake/automate-vcpkg.cmake)
|
include(libultraship/cmake/automate-vcpkg.cmake)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# kept for future reference
|
||||||
|
#set(CVAR_VSYNC_ENABLED "${CVAR_PREFIX_SETTING}.VsyncEnabled")
|
||||||
|
include("libultraship/cmake/cvars.cmake")
|
||||||
|
|
@ -18,6 +18,9 @@
|
||||||
#include "resource/importers/UnkActorSpawnDataFactory.h"
|
#include "resource/importers/UnkActorSpawnDataFactory.h"
|
||||||
#include "resource/importers/ArrayFactory.h"
|
#include "resource/importers/ArrayFactory.h"
|
||||||
#include <Fast3D/Fast3dWindow.h>
|
#include <Fast3D/Fast3dWindow.h>
|
||||||
|
#include <Fonts.h>
|
||||||
|
#include "window/gui/resource/Font.h"
|
||||||
|
#include "window/gui/resource/FontFactory.h"
|
||||||
|
|
||||||
#include <Fast3D/gfx_pc.h>
|
#include <Fast3D/gfx_pc.h>
|
||||||
#include <Fast3D/gfx_rendering_api.h>
|
#include <Fast3D/gfx_rendering_api.h>
|
||||||
|
|
@ -139,6 +142,14 @@ GameEngine::GameEngine() {
|
||||||
loader->RegisterResourceFactory(std::make_shared<MK64::ResourceFactoryBinaryUnkActorSpawnDataV0>(),
|
loader->RegisterResourceFactory(std::make_shared<MK64::ResourceFactoryBinaryUnkActorSpawnDataV0>(),
|
||||||
RESOURCE_FORMAT_BINARY, "UnkSpawnData",
|
RESOURCE_FORMAT_BINARY, "UnkSpawnData",
|
||||||
static_cast<uint32_t>(MK64::ResourceType::UnkSpawnData), 0);
|
static_cast<uint32_t>(MK64::ResourceType::UnkSpawnData), 0);
|
||||||
|
|
||||||
|
fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf");
|
||||||
|
fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf");
|
||||||
|
fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf");
|
||||||
|
fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf");
|
||||||
|
fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf");
|
||||||
|
fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf");
|
||||||
|
ImGui::GetIO().FontDefault = fontMono;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameEngine::Create() {
|
void GameEngine::Create() {
|
||||||
|
|
@ -296,6 +307,37 @@ uint8_t GameEngine::GetBankIdByName(const std::string& name) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImFont* GameEngine::CreateFontWithSize(float size, std::string fontPath) {
|
||||||
|
auto mImGuiIo = &ImGui::GetIO();
|
||||||
|
ImFont* font;
|
||||||
|
if (fontPath == "") {
|
||||||
|
ImFontConfig fontCfg = ImFontConfig();
|
||||||
|
fontCfg.OversampleH = fontCfg.OversampleV = 1;
|
||||||
|
fontCfg.PixelSnapH = true;
|
||||||
|
fontCfg.SizePixels = size;
|
||||||
|
font = mImGuiIo->Fonts->AddFontDefault(&fontCfg);
|
||||||
|
} else {
|
||||||
|
auto initData = std::make_shared<Ship::ResourceInitData>();
|
||||||
|
initData->Format = RESOURCE_FORMAT_BINARY;
|
||||||
|
initData->Type = static_cast<uint32_t>(RESOURCE_TYPE_FONT);
|
||||||
|
initData->ResourceVersion = 0;
|
||||||
|
initData->Path = fontPath;
|
||||||
|
std::shared_ptr<Ship::Font> fontData = std::static_pointer_cast<Ship::Font>(
|
||||||
|
Ship::Context::GetInstance()->GetResourceManager()->LoadResource(fontPath, false, initData));
|
||||||
|
font = mImGuiIo->Fonts->AddFontFromMemoryTTF(fontData->Data, fontData->DataSize, size);
|
||||||
|
}
|
||||||
|
// FontAwesome fonts need to have their sizes reduced by 2.0f/3.0f in order to align correctly
|
||||||
|
float iconFontSize = size * 2.0f / 3.0f;
|
||||||
|
static const ImWchar sIconsRanges[] = { ICON_MIN_FA, ICON_MAX_16_FA, 0 };
|
||||||
|
ImFontConfig iconsConfig;
|
||||||
|
iconsConfig.MergeMode = true;
|
||||||
|
iconsConfig.PixelSnapH = true;
|
||||||
|
iconsConfig.GlyphMinAdvanceX = iconFontSize;
|
||||||
|
mImGuiIo->Fonts->AddFontFromMemoryCompressedBase85TTF(fontawesome_compressed_data_base85, iconFontSize,
|
||||||
|
&iconsConfig, sIconsRanges);
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
// End
|
// End
|
||||||
|
|
||||||
extern "C" uint32_t GameEngine_GetSampleRate() {
|
extern "C" uint32_t GameEngine_GetSampleRate() {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,13 @@ class GameEngine {
|
||||||
std::vector<std::string> sequenceTable;
|
std::vector<std::string> sequenceTable;
|
||||||
std::vector<AudioSequenceData*> audioSequenceTable;
|
std::vector<AudioSequenceData*> audioSequenceTable;
|
||||||
|
|
||||||
|
ImFont* fontStandard;
|
||||||
|
ImFont* fontStandardLarger;
|
||||||
|
ImFont* fontStandardLargest;
|
||||||
|
ImFont* fontMono;
|
||||||
|
ImFont* fontMonoLarger;
|
||||||
|
ImFont* fontMonoLargest;
|
||||||
|
|
||||||
std::unordered_map<std::string, uint8_t> bankMapTable;
|
std::unordered_map<std::string, uint8_t> bankMapTable;
|
||||||
GameEngine();
|
GameEngine();
|
||||||
static void Create();
|
static void Create();
|
||||||
|
|
@ -54,6 +61,8 @@ class GameEngine {
|
||||||
uint32_t OTRGetGameRenderHeight();
|
uint32_t OTRGetGameRenderHeight();
|
||||||
uint32_t OTRCalculateCenterOfAreaFromRightEdge(int32_t center);
|
uint32_t OTRCalculateCenterOfAreaFromRightEdge(int32_t center);
|
||||||
uint32_t OTRCalculateCenterOfAreaFromLeftEdge(int32_t center);
|
uint32_t OTRCalculateCenterOfAreaFromLeftEdge(int32_t center);
|
||||||
|
private:
|
||||||
|
ImFont* CreateFontWithSize(float size, std::string fontPath = "");
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "ShipUtils.h"
|
||||||
|
#include <libultraship/libultraship.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "macros.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr f32 fourByThree = 4.0f / 3.0f;
|
||||||
|
|
||||||
|
extern "C" bool Ship_IsCStringEmpty(const char* str) {
|
||||||
|
return str == NULL || str[0] == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build vertex coordinates for a quad command
|
||||||
|
// In order of top left, top right, bottom left, then bottom right
|
||||||
|
// Supports flipping the texture horizontally
|
||||||
|
extern "C" void Ship_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH) {
|
||||||
|
vtxList[0].v.ob[0] = xStart;
|
||||||
|
vtxList[0].v.ob[1] = yStart;
|
||||||
|
vtxList[0].v.tc[0] = (flippedH ? width : 0) << 5;
|
||||||
|
vtxList[0].v.tc[1] = 0 << 5;
|
||||||
|
|
||||||
|
vtxList[1].v.ob[0] = xStart + width;
|
||||||
|
vtxList[1].v.ob[1] = yStart;
|
||||||
|
vtxList[1].v.tc[0] = (flippedH ? width * 2 : width) << 5;
|
||||||
|
vtxList[1].v.tc[1] = 0 << 5;
|
||||||
|
|
||||||
|
vtxList[2].v.ob[0] = xStart;
|
||||||
|
vtxList[2].v.ob[1] = yStart + height;
|
||||||
|
vtxList[2].v.tc[0] = (flippedH ? width : 0) << 5;
|
||||||
|
vtxList[2].v.tc[1] = height << 5;
|
||||||
|
|
||||||
|
vtxList[3].v.ob[0] = xStart + width;
|
||||||
|
vtxList[3].v.ob[1] = yStart + height;
|
||||||
|
vtxList[3].v.tc[0] = (flippedH ? width * 2 : width) << 5;
|
||||||
|
vtxList[3].v.tc[1] = height << 5;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef SHIP_UTILS_H
|
||||||
|
#define SHIP_UTILS_H
|
||||||
|
|
||||||
|
#include <libultraship/libultraship.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
void LoadGuiTextures();
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool Ship_IsCStringEmpty(const char* str);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // SHIP_UTILS_H
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
#include "FreecamWindow.h"
|
#include "FreecamWindow.h"
|
||||||
|
#include "port/ui/PortMenu.h"
|
||||||
#include "UIWidgets.h"
|
#include "UIWidgets.h"
|
||||||
#include "libultraship/src/Context.h"
|
#include "libultraship/src/Context.h"
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
#include <map>
|
||||||
#include <libultraship/libultraship.h>
|
#include <libultraship/libultraship.h>
|
||||||
#include "spdlog/spdlog.h"
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
#include "spdlog/formatter.h"
|
||||||
#include <common_structs.h>
|
#include <common_structs.h>
|
||||||
#include <defines.h>
|
#include <defines.h>
|
||||||
#include "enhancements/freecam/freecam_engine.h"
|
#include "enhancements/freecam/freecam_engine.h"
|
||||||
|
|
@ -30,80 +33,166 @@ extern u32 gFreecamControllerType;
|
||||||
void freecam_get_player_from_character(s32 characterId);
|
void freecam_get_player_from_character(s32 characterId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameUI {
|
||||||
|
extern std::shared_ptr<PortMenu> mPortMenu;
|
||||||
namespace Freecam {
|
namespace Freecam {
|
||||||
FreecamWindow::~FreecamWindow() {
|
|
||||||
SPDLOG_TRACE("destruct freecam window");
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreecamWindow::InitElement() {
|
|
||||||
}
|
|
||||||
|
|
||||||
static s32 sReadyUpBool = false;
|
static s32 sReadyUpBool = false;
|
||||||
|
|
||||||
float dampMin = 0.970;
|
int32_t controllerType = 0;
|
||||||
float dampMax = 1.0f;
|
std::unordered_map<int32_t, const char*> controlType = {{ 0, "Mouse/Keyboard" }, { 1, "Controller" }};
|
||||||
float minSpeed = 1.0;
|
|
||||||
float maxSpeed = 30.0f;
|
|
||||||
float minSpeedMultiplier = 1.5f;
|
|
||||||
float maxSpeedMultiplier = 15.0f;
|
|
||||||
float minFreecamRotateFactor = 0.0f;
|
|
||||||
float maxFreecamRotateFactor = 1.0f;
|
|
||||||
float minFreecamFollowFactor = 0.0f;
|
|
||||||
float maxFreecamFollowFactor = 1.0f;
|
|
||||||
|
|
||||||
uint32_t focusPlayer;
|
uint32_t focusPlayer;
|
||||||
|
|
||||||
void FreecamWindow::DrawElement() {
|
bool IsPlayerValid(const char* string) {
|
||||||
const float framerate = ImGui::GetIO().Framerate;
|
return string != NULL && (strncmp(string, "\xA1\xBC\xA1\xBC\xA1\xBC\xA1\xBC", 8) != 0);
|
||||||
const float deltatime = ImGui::GetIO().DeltaTime;
|
}
|
||||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
|
||||||
|
|
||||||
ImGui::Text("Controller mode is not configured yet.");
|
void RegisterFreecamWidgets() {
|
||||||
|
mPortMenu->AddSidebarEntry("Enhancements", "Freecam", 2);
|
||||||
|
WidgetPath path = { "Enhancements", "Freecam", SECTION_COLUMN_1 };
|
||||||
|
//const float framerate = ImGui::GetIO().Framerate;
|
||||||
|
//const float deltatime = ImGui::GetIO().DeltaTime;
|
||||||
|
//ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
||||||
|
|
||||||
const char* items[] = { "Mouse/Keyboard", "Controller" };
|
//mPortMenu->AddWidget(path, "Controller mode is not configured yet.", WIDGET_TEXT);
|
||||||
static int current_item = 0;
|
|
||||||
if (ImGui::Combo("Dropdown", ¤t_item, items, IM_ARRAYSIZE(items))) {
|
|
||||||
gFreecamControllerType = current_item;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Text("Move: W,A,S,D\nUp: Space, Down: Shift\nFaster: Ctrl\nLook: Right-mouse button\nTarget Player Mode: F, Next: M, Previous: N");
|
//static int current_item = 0;
|
||||||
ImGui::Spacing();
|
mPortMenu->AddWidget(path, "Control Type", WIDGET_COMBOBOX)
|
||||||
UIWidgets::CVarCheckbox("Enable Flycam", "gFreecam", { .tooltip = "Allows you to fly around the course" });
|
.ValuePointer(&controllerType)
|
||||||
|
.Callback([](WidgetInfo& info) { gFreecamControllerType = (uint32_t)*std::get<int32_t*>(info.valuePointer); })
|
||||||
|
.Options(UIWidgets::ComboboxOptions().ComboMap(controlType));
|
||||||
|
|
||||||
ImGui::Spacing();
|
mPortMenu->AddWidget(path, "Move: W,A,S,D\nUp: Space, Down: Shift\nFaster: Ctrl\nLook: Right-mouse button\nTarget Player Mode: F, Next: M, Previous: N", WIDGET_TEXT);
|
||||||
|
mPortMenu->AddWidget(path, "Enable Flycam", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gFreecam")
|
||||||
|
.Options(UIWidgets::CheckboxOptions({{ .tooltip = "Allows you to fly around the course"}}));
|
||||||
|
|
||||||
if (ImGui::SliderScalar("Camera Damping", ImGuiDataType_Float, &gDampValue, &dampMin, &dampMax, "%f")) {};
|
mPortMenu->AddWidget(path, "Camera Damping", WIDGET_SLIDER_FLOAT)
|
||||||
if (ImGui::SliderScalar("Camera Speed", ImGuiDataType_Float, &gFreecamSpeed, &minSpeed, &maxSpeed, "%f")) {};
|
.ValuePointer(&gDampValue)
|
||||||
if (ImGui::SliderScalar("Camera Speed Multiplier", ImGuiDataType_Float, &gFreecamSpeedMultiplier,
|
.Options(UIWidgets::FloatSliderOptions().Min(0.970f).Max(1.0f).Step(0.001f).Format("%.3f"));
|
||||||
&minSpeedMultiplier, &maxSpeedMultiplier, "%f")) {};
|
mPortMenu->AddWidget(path, "Camera Speed", WIDGET_SLIDER_FLOAT)
|
||||||
if (ImGui::SliderScalar("Camera Rotation Smoothing", ImGuiDataType_Float, &gFreecamRotateSmoothingFactor,
|
.ValuePointer(&gFreecamSpeed)
|
||||||
&minFreecamRotateFactor, &maxFreecamRotateFactor, "%f")) {};
|
.Options(UIWidgets::FloatSliderOptions().Min(1.0f).Max(30.0f).Step(0.1f).Format("%.1f"));
|
||||||
if (ImGui::SliderScalar("Follow Factor", ImGuiDataType_Float, &gFreecamFollowFactor,
|
mPortMenu->AddWidget(path, "Camera Speed Multiplier", WIDGET_SLIDER_FLOAT)
|
||||||
&minFreecamFollowFactor, &maxFreecamFollowFactor, "%f")) {};
|
.ValuePointer(&gFreecamSpeedMultiplier)
|
||||||
|
.Options(UIWidgets::FloatSliderOptions().Min(1.5f).Max(15.0f).Step(0.1f).Format("%.1f"));
|
||||||
|
mPortMenu->AddWidget(path, "Camera Rotation Smoothing", WIDGET_SLIDER_FLOAT)
|
||||||
|
.ValuePointer(&gFreecamRotateSmoothingFactor)
|
||||||
|
.Options(UIWidgets::FloatSliderOptions().Min(0.0f).Max(1.0f).Step(0.01f).Format("%.2f"));
|
||||||
|
mPortMenu->AddWidget(path, "Follow Factor", WIDGET_SLIDER_FLOAT)
|
||||||
|
.ValuePointer(&gFreecamFollowFactor)
|
||||||
|
.Options(UIWidgets::FloatSliderOptions().Min(0.0f).Max(1.0f).Step(0.01f).Format("%.2f"));
|
||||||
|
|
||||||
ImGui::Spacing();
|
mPortMenu->AddWidget(path, "Target Player", WIDGET_TEXT);
|
||||||
|
mPortMenu->AddWidget(path, "None", WIDGET_BUTTON)
|
||||||
|
.Callback([](WidgetInfo& info) { fTargetPlayer = false; });
|
||||||
|
|
||||||
ImGui::Text("Target Player");
|
mPortMenu->AddWidget(path, "Player 1", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
if (ImGui::Button("None")) {
|
info.isHidden = !IsPlayerValid(D_800E76A8[0]);
|
||||||
fTargetPlayer = false;
|
if (!info.isHidden) {
|
||||||
}
|
info.name = D_800E76A8[0];
|
||||||
|
}
|
||||||
for (size_t i = 0; i < NUM_PLAYERS; i++) {
|
})
|
||||||
// These are euc-jp characters that look sort of like a hyphen -
|
.Options(UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))
|
||||||
if (D_800E76A8[i] == NULL || D_800E76A8[i] == "\xA1\xBC\xA1\xBC\xA1\xBC\xA1\xBC") {
|
.Callback([](WidgetInfo& info) {
|
||||||
break;
|
freecam_get_player_from_character(0);
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button(D_800E76A8[i])) {
|
|
||||||
freecam_get_player_from_character(i);
|
|
||||||
fTargetPlayer = true;
|
fTargetPlayer = true;
|
||||||
}
|
});
|
||||||
}
|
mPortMenu->AddWidget(path, "Player 2", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
ImGui::PopStyleColor();
|
info.isHidden = !IsPlayerValid(D_800E76A8[1]);
|
||||||
|
if (!info.isHidden) {
|
||||||
|
info.name = D_800E76A8[1];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))
|
||||||
|
.SameLine(true)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
freecam_get_player_from_character(1);
|
||||||
|
fTargetPlayer = true;
|
||||||
|
});
|
||||||
|
mPortMenu->AddWidget(path, "Player 3", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
info.isHidden = !IsPlayerValid(D_800E76A8[2]);
|
||||||
|
if (!info.isHidden) {
|
||||||
|
info.name = D_800E76A8[2];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))
|
||||||
|
.SameLine(true)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
freecam_get_player_from_character(2);
|
||||||
|
fTargetPlayer = true;
|
||||||
|
});
|
||||||
|
mPortMenu->AddWidget(path, "Player 4", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
info.isHidden = !IsPlayerValid(D_800E76A8[3]);
|
||||||
|
if (!info.isHidden) {
|
||||||
|
info.name = D_800E76A8[3];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))
|
||||||
|
.SameLine(true)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
freecam_get_player_from_character(3);
|
||||||
|
fTargetPlayer = true;
|
||||||
|
});
|
||||||
|
mPortMenu->AddWidget(path, "Player 5", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
info.isHidden = !IsPlayerValid(D_800E76A8[4]);
|
||||||
|
if (!info.isHidden) {
|
||||||
|
info.name = D_800E76A8[4];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
freecam_get_player_from_character(4);
|
||||||
|
fTargetPlayer = true;
|
||||||
|
});
|
||||||
|
mPortMenu->AddWidget(path, "Player 6", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
info.isHidden = !IsPlayerValid(D_800E76A8[5]);
|
||||||
|
if (!info.isHidden) {
|
||||||
|
info.name = D_800E76A8[5];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))
|
||||||
|
.SameLine(true)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
freecam_get_player_from_character(5);
|
||||||
|
fTargetPlayer = true;
|
||||||
|
});
|
||||||
|
mPortMenu->AddWidget(path, "Player 7", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
info.isHidden = !IsPlayerValid(D_800E76A8[6]);
|
||||||
|
if (!info.isHidden) {
|
||||||
|
info.name = D_800E76A8[6];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))
|
||||||
|
.SameLine(true)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
freecam_get_player_from_character(6);
|
||||||
|
fTargetPlayer = true;
|
||||||
|
});
|
||||||
|
mPortMenu->AddWidget(path, "Player 8", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
info.isHidden = !IsPlayerValid(D_800E76A8[7]);
|
||||||
|
if (!info.isHidden) {
|
||||||
|
info.name = D_800E76A8[7];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))
|
||||||
|
.SameLine(true)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
freecam_get_player_from_character(7);
|
||||||
|
fTargetPlayer = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreecamWindow::UpdateElement() {
|
static RegisterMenuInitFunc initFunc(RegisterFreecamWidgets);
|
||||||
}
|
|
||||||
} // namespace Freecam
|
} // namespace Freecam
|
||||||
|
} // namespace GameUI
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,4 @@
|
||||||
#include <libultraship/libultraship.h>
|
#include <libultraship/libultraship.h>
|
||||||
|
|
||||||
namespace Freecam {
|
namespace Freecam {
|
||||||
class FreecamWindow : public Ship::GuiWindow {
|
|
||||||
public:
|
|
||||||
using Ship::GuiWindow::GuiWindow;
|
|
||||||
~FreecamWindow();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void InitElement() override;
|
|
||||||
void DrawElement() override;
|
|
||||||
void UpdateElement() override;
|
|
||||||
};
|
|
||||||
} // namespace Freecam
|
} // namespace Freecam
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include <libultraship/libultraship.h>
|
#include <libultraship/libultraship.h>
|
||||||
#include <Fast3D/gfx_pc.h>
|
#include <Fast3D/gfx_pc.h>
|
||||||
#include "port/Engine.h"
|
#include "port/Engine.h"
|
||||||
|
#include "PortMenu.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
extern s32 gGamestateNext;
|
extern s32 gGamestateNext;
|
||||||
|
|
@ -24,26 +25,23 @@ extern "C" {
|
||||||
|
|
||||||
namespace GameUI {
|
namespace GameUI {
|
||||||
std::shared_ptr<GameMenuBar> mGameMenuBar;
|
std::shared_ptr<GameMenuBar> mGameMenuBar;
|
||||||
|
std::shared_ptr<PortMenu> mPortMenu;
|
||||||
std::shared_ptr<Ship::GuiWindow> mConsoleWindow;
|
std::shared_ptr<Ship::GuiWindow> mConsoleWindow;
|
||||||
std::shared_ptr<Ship::GuiWindow> mStatsWindow;
|
std::shared_ptr<Ship::GuiWindow> mStatsWindow;
|
||||||
std::shared_ptr<Ship::GuiWindow> mInputEditorWindow;
|
std::shared_ptr<Ship::GuiWindow> mInputEditorWindow;
|
||||||
std::shared_ptr<Ship::GuiWindow> mGfxDebuggerWindow;
|
std::shared_ptr<Ship::GuiWindow> mGfxDebuggerWindow;
|
||||||
std::shared_ptr<Ship::GuiWindow> mGameInfoWindow;
|
std::shared_ptr<Ship::GuiWindow> mGameInfoWindow;
|
||||||
std::shared_ptr<Ship::GuiWindow> mMultiplayerWindow;
|
std::shared_ptr<Ship::GuiWindow> mMultiplayerWindow;
|
||||||
std::shared_ptr<Ship::GuiWindow> mFreecamWindow;
|
|
||||||
std::shared_ptr<AdvancedResolutionSettings::AdvancedResolutionSettingsWindow> mAdvancedResolutionSettingsWindow;
|
|
||||||
|
|
||||||
void SetupGuiElements() {
|
void SetupGuiElements() {
|
||||||
auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui();
|
auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui();
|
||||||
|
|
||||||
auto& style = ImGui::GetStyle();
|
|
||||||
style.FramePadding = ImVec2(4.0f, 6.0f);
|
|
||||||
style.ItemSpacing = ImVec2(8.0f, 6.0f);
|
|
||||||
style.Colors[ImGuiCol_MenuBarBg] = UIWidgets::Colors::DarkGray;
|
|
||||||
|
|
||||||
mGameMenuBar = std::make_shared<GameMenuBar>("gOpenMenuBar", CVarGetInteger("gOpenMenuBar", 0));
|
mGameMenuBar = std::make_shared<GameMenuBar>("gOpenMenuBar", CVarGetInteger("gOpenMenuBar", 0));
|
||||||
gui->SetMenuBar(mGameMenuBar);
|
gui->SetMenuBar(mGameMenuBar);
|
||||||
|
|
||||||
|
mPortMenu = std::make_shared<PortMenu>("gOpenMenu", "Port Menu");
|
||||||
|
gui->SetMenu(mPortMenu);
|
||||||
|
|
||||||
mMultiplayerWindow = gui->GetGuiWindow("Multiplayer");
|
mMultiplayerWindow = gui->GetGuiWindow("Multiplayer");
|
||||||
if (mMultiplayerWindow == nullptr) {
|
if (mMultiplayerWindow == nullptr) {
|
||||||
SPDLOG_ERROR("Could not find multiplayer window");
|
SPDLOG_ERROR("Could not find multiplayer window");
|
||||||
|
|
@ -75,27 +73,11 @@ void SetupGuiElements() {
|
||||||
SPDLOG_ERROR("Could not find input GfxDebuggerWindow");
|
SPDLOG_ERROR("Could not find input GfxDebuggerWindow");
|
||||||
}
|
}
|
||||||
|
|
||||||
mMultiplayerWindow = std::make_shared<Multiplayer::MultiplayerWindow>("gMultiplayerWindowEnabled", "Multiplayer");
|
mGameInfoWindow = std::make_shared<GameInfo::GameInfoWindow>("gGameInfoEnabled", "Game Info");
|
||||||
gui->AddGuiWindow(mMultiplayerWindow);
|
|
||||||
|
|
||||||
mFreecamWindow = gui->GetGuiWindow("FreecamWindow");
|
|
||||||
if (mFreecamWindow == nullptr) {
|
|
||||||
SPDLOG_ERROR("Could not find input FreecamWindow");
|
|
||||||
}
|
|
||||||
|
|
||||||
mFreecamWindow = std::make_shared<Freecam::FreecamWindow>("gFreecamEnabled", "Freecam");
|
|
||||||
gui->AddGuiWindow(mFreecamWindow);
|
|
||||||
|
|
||||||
mGameInfoWindow = std::make_shared<GameInfo::GameInfoWindow>("gGameInfoEnabled", "Game info");
|
|
||||||
gui->AddGuiWindow(mGameInfoWindow);
|
gui->AddGuiWindow(mGameInfoWindow);
|
||||||
|
|
||||||
mAdvancedResolutionSettingsWindow = std::make_shared<AdvancedResolutionSettings::AdvancedResolutionSettingsWindow>(
|
|
||||||
"gAdvancedResolutionEditorEnabled", "Advanced Resolution Settings");
|
|
||||||
gui->AddGuiWindow(mAdvancedResolutionSettingsWindow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Destroy() {
|
void Destroy() {
|
||||||
mAdvancedResolutionSettingsWindow = nullptr;
|
|
||||||
mGameInfoWindow = nullptr;
|
mGameInfoWindow = nullptr;
|
||||||
mConsoleWindow = nullptr;
|
mConsoleWindow = nullptr;
|
||||||
mStatsWindow = nullptr;
|
mStatsWindow = nullptr;
|
||||||
|
|
@ -125,294 +107,294 @@ static const char* filters[3] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
void DrawSettingsMenu() {
|
void DrawSettingsMenu() {
|
||||||
if (UIWidgets::BeginMenu("Settings")) {
|
// if (UIWidgets::BeginMenu("Settings")) {
|
||||||
if (UIWidgets::BeginMenu("Audio")) {
|
// if (UIWidgets::BeginMenu("Audio")) {
|
||||||
UIWidgets::CVarSliderFloat("Master Volume", "gGameMasterVolume", 0.0f, 1.0f, 1.0f, {
|
// UIWidgets::CVarSliderFloat("Master Volume", "gGameMasterVolume", 0.0f, 1.0f, 1.0f, {
|
||||||
.format = "%.0f%%",
|
// .format = "%.0f%%",
|
||||||
.isPercentage = true,
|
// .isPercentage = true,
|
||||||
});
|
// });
|
||||||
if (UIWidgets::CVarSliderFloat("Main Music Volume", "gMainMusicVolume", 0.0f, 1.0f, 1.0f,
|
// if (UIWidgets::CVarSliderFloat("Main Music Volume", "gMainMusicVolume", 0.0f, 1.0f, 1.0f,
|
||||||
{
|
// {
|
||||||
.format = "%.0f%%",
|
// .format = "%.0f%%",
|
||||||
.isPercentage = true,
|
// .isPercentage = true,
|
||||||
})) {
|
// })) {
|
||||||
audio_set_player_volume(SEQ_PLAYER_LEVEL, CVarGetFloat("gMainMusicVolume", 1.0f));
|
// audio_set_player_volume(SEQ_PLAYER_LEVEL, CVarGetFloat("gMainMusicVolume", 1.0f));
|
||||||
}
|
// }
|
||||||
if (UIWidgets::CVarSliderFloat("Sound Effects Volume", "gSFXMusicVolume",
|
// if (UIWidgets::CVarSliderFloat("Sound Effects Volume", "gSFXMusicVolume",
|
||||||
0.0f, 1.0f, 1.0f, {
|
// 0.0f, 1.0f, 1.0f, {
|
||||||
.format = "%.0f%%",
|
// .format = "%.0f%%",
|
||||||
.isPercentage = true,
|
// .isPercentage = true,
|
||||||
})) {
|
// })) {
|
||||||
audio_set_player_volume(SEQ_PLAYER_SFX, CVarGetFloat("gSFXMusicVolume", 1.0f));
|
// audio_set_player_volume(SEQ_PLAYER_SFX, CVarGetFloat("gSFXMusicVolume", 1.0f));
|
||||||
}
|
// }
|
||||||
if (UIWidgets::CVarSliderFloat("Environment Volume", "gEnvironmentVolume",
|
// if (UIWidgets::CVarSliderFloat("Environment Volume", "gEnvironmentVolume",
|
||||||
0.0f, 1.0f, 1.0f, {
|
// 0.0f, 1.0f, 1.0f, {
|
||||||
.format = "%.0f%%",
|
// .format = "%.0f%%",
|
||||||
.isPercentage = true,
|
// .isPercentage = true,
|
||||||
})) {
|
// })) {
|
||||||
audio_set_player_volume(SEQ_PLAYER_ENV, CVarGetFloat("gEnvironmentVolume", 1.0f));
|
// audio_set_player_volume(SEQ_PLAYER_ENV, CVarGetFloat("gEnvironmentVolume", 1.0f));
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
static std::unordered_map<Ship::AudioBackend, const char*> audioBackendNames = {
|
// static std::unordered_map<Ship::AudioBackend, const char*> audioBackendNames = {
|
||||||
{ Ship::AudioBackend::WASAPI, "Windows Audio Session API" },
|
// { Ship::AudioBackend::WASAPI, "Windows Audio Session API" },
|
||||||
{ Ship::AudioBackend::SDL, "SDL" },
|
// { Ship::AudioBackend::SDL, "SDL" },
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
ImGui::Text("Audio API (Needs reload)");
|
// ImGui::Text("Audio API (Needs reload)");
|
||||||
auto currentAudioBackend = Ship::Context::GetInstance()->GetAudio()->GetCurrentAudioBackend();
|
// auto currentAudioBackend = Ship::Context::GetInstance()->GetAudio()->GetCurrentAudioBackend();
|
||||||
|
//
|
||||||
if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) {
|
// if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) {
|
||||||
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
// UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||||
}
|
// }
|
||||||
if (ImGui::BeginCombo("##AApi", audioBackendNames[currentAudioBackend])) {
|
// if (ImGui::BeginCombo("##AApi", audioBackendNames[currentAudioBackend])) {
|
||||||
for (uint8_t i = 0; i <
|
// for (uint8_t i = 0; i <
|
||||||
Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size(); i++) {
|
// Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size(); i++) {
|
||||||
auto backend = Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->data()[i];
|
// auto backend = Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->data()[i];
|
||||||
if (ImGui::Selectable(audioBackendNames[backend], backend == currentAudioBackend)) {
|
// if (ImGui::Selectable(audioBackendNames[backend], backend == currentAudioBackend)) {
|
||||||
Ship::Context::GetInstance()->GetAudio()->SetCurrentAudioBackend(backend);
|
// Ship::Context::GetInstance()->GetAudio()->SetCurrentAudioBackend(backend);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
ImGui::EndCombo();
|
// ImGui::EndCombo();
|
||||||
}
|
// }
|
||||||
if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) {
|
// if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) {
|
||||||
UIWidgets::ReEnableComponent("");
|
// UIWidgets::ReEnableComponent("");
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
ImGui::EndMenu();
|
// ImGui::EndMenu();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
UIWidgets::Spacer(0);
|
// UIWidgets::Spacer(0);
|
||||||
|
//
|
||||||
if (UIWidgets::BeginMenu("Controller")) {
|
// if (UIWidgets::BeginMenu("Controller")) {
|
||||||
UIWidgets::WindowButton("Controller Mapping", "gInputEditorWindow", GameUI::mInputEditorWindow);
|
// UIWidgets::WindowButton("Controller Mapping", "gInputEditorWindow", GameUI::mInputEditorWindow);
|
||||||
|
//
|
||||||
UIWidgets::Spacer(0);
|
// UIWidgets::Spacer(0);
|
||||||
|
//
|
||||||
#ifndef __SWITCH__
|
//#ifndef __SWITCH__
|
||||||
UIWidgets::CVarCheckbox(
|
// UIWidgets::CVarCheckbox(
|
||||||
"Menubar Controller Navigation", "gControlNav",
|
// "Menubar Controller Navigation", "gControlNav",
|
||||||
{ .tooltip = "Allows controller navigation of the SOH menu bar (Settings, Enhancements,...)\nCAUTION: "
|
// { .tooltip = "Allows controller navigation of the SOH menu bar (Settings, Enhancements,...)\nCAUTION: "
|
||||||
"This will disable game inputs while the menubar is visible.\n\nD-pad to move between "
|
// "This will disable game inputs while the menubar is visible.\n\nD-pad to move between "
|
||||||
"items, A to select, and X to grab focus on the menu bar" });
|
// "items, A to select, and X to grab focus on the menu bar" });
|
||||||
#endif
|
//#endif
|
||||||
UIWidgets::CVarCheckbox("Show Inputs", "gInputEnabled",
|
// UIWidgets::CVarCheckbox("Show Inputs", "gInputEnabled",
|
||||||
{ .tooltip = "Shows currently pressed inputs on the bottom right of the screen" });
|
// { .tooltip = "Shows currently pressed inputs on the bottom right of the screen" });
|
||||||
if (CVarGetInteger("gInputEnabled", 0)) {
|
// if (CVarGetInteger("gInputEnabled", 0)) {
|
||||||
UIWidgets::CVarSliderFloat(
|
// UIWidgets::CVarSliderFloat(
|
||||||
"Input Scale", "gInputScale", 1.0f, 3.0f, 1.0f,
|
// "Input Scale", "gInputScale", 1.0f, 3.0f, 1.0f,
|
||||||
{
|
// {
|
||||||
.tooltip = "Sets the on screen size of the displayed inputs from the Show Inputs setting",
|
// .tooltip = "Sets the on screen size of the displayed inputs from the Show Inputs setting",
|
||||||
.format = "%.1fx",
|
// .format = "%.1fx",
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
ImGui::EndMenu();
|
// ImGui::EndMenu();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
ImGui::EndMenu();
|
// ImGui::EndMenu();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
ImGui::SetCursorPosY(0.0f);
|
// ImGui::SetCursorPosY(0.0f);
|
||||||
if (UIWidgets::BeginMenu("Graphics")) {
|
// if (UIWidgets::BeginMenu("Graphics")) {
|
||||||
UIWidgets::WindowButton("Resolution Editor", "gAdvancedResolutionEditorEnabled",
|
// UIWidgets::WindowButton("Resolution Editor", "gAdvancedResolutionEditorEnabled",
|
||||||
GameUI::mAdvancedResolutionSettingsWindow);
|
// GameUI::mAdvancedResolutionSettingsWindow);
|
||||||
|
//
|
||||||
UIWidgets::Spacer(0);
|
// UIWidgets::Spacer(0);
|
||||||
|
//
|
||||||
// Previously was running every frame, and nothing was setting it? Maybe a bad copy/paste?
|
// // Previously was running every frame, and nothing was setting it? Maybe a bad copy/paste?
|
||||||
// Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1));
|
// // Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1));
|
||||||
// UIWidgets::Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but
|
// // UIWidgets::Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but
|
||||||
// effective form of anti-aliasing");
|
// // effective form of anti-aliasing");
|
||||||
#ifndef __WIIU__
|
//#ifndef __WIIU__
|
||||||
if (UIWidgets::CVarSliderInt(
|
// if (UIWidgets::CVarSliderInt(
|
||||||
"MSAA: %d", "gMSAAValue", 1, 8, 1,
|
// "MSAA: %d", "gMSAAValue", 1, 8, 1,
|
||||||
{ .tooltip =
|
// { .tooltip =
|
||||||
"Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel" })) {
|
// "Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel" })) {
|
||||||
Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1));
|
// Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1));
|
||||||
}
|
// }
|
||||||
#endif
|
//#endif
|
||||||
|
//
|
||||||
{ // FPS Slider
|
// { // FPS Slider
|
||||||
const int minFps = 30;
|
// const int minFps = 30;
|
||||||
static int maxFps;
|
// static int maxFps;
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() ==
|
// if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() ==
|
||||||
Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
// Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
||||||
maxFps = 360;
|
// maxFps = 360;
|
||||||
} else {
|
// } else {
|
||||||
maxFps = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
|
// maxFps = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
|
||||||
}
|
// }
|
||||||
int currentFps = 0;
|
// int currentFps = 0;
|
||||||
#ifdef __WIIU__
|
//#ifdef __WIIU__
|
||||||
UIWidgets::Spacer(0);
|
// UIWidgets::Spacer(0);
|
||||||
// only support divisors of 60 on the Wii U
|
// // only support divisors of 60 on the Wii U
|
||||||
if (currentFps > 60) {
|
// if (currentFps > 60) {
|
||||||
currentFps = 60;
|
// currentFps = 60;
|
||||||
} else {
|
// } else {
|
||||||
currentFps = 60 / (60 / currentFps);
|
// currentFps = 60 / (60 / currentFps);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
int fpsSlider = 1;
|
// int fpsSlider = 1;
|
||||||
if (currentFps == 30) {
|
// if (currentFps == 30) {
|
||||||
ImGui::Text("FPS: Original (30)");
|
// ImGui::Text("FPS: Original (30)");
|
||||||
} else {
|
// } else {
|
||||||
ImGui::Text("FPS: %d", currentFps);
|
// ImGui::Text("FPS: %d", currentFps);
|
||||||
if (currentFps == 30) {
|
// if (currentFps == 30) {
|
||||||
fpsSlider = 2;
|
// fpsSlider = 2;
|
||||||
} else { // currentFps == 60
|
// } else { // currentFps == 60
|
||||||
fpsSlider = 3;
|
// fpsSlider = 3;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (CVarGetInteger("gMatchRefreshRate", 0)) {
|
// if (CVarGetInteger("gMatchRefreshRate", 0)) {
|
||||||
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
// UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (ImGui::Button(" - ##WiiUFPS")) {
|
// if (ImGui::Button(" - ##WiiUFPS")) {
|
||||||
fpsSlider--;
|
// fpsSlider--;
|
||||||
}
|
// }
|
||||||
ImGui::SameLine();
|
// ImGui::SameLine();
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f);
|
// ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f);
|
||||||
|
//
|
||||||
UIWidgets::Spacer(0);
|
// UIWidgets::Spacer(0);
|
||||||
|
//
|
||||||
ImGui::PushItemWidth(std::min((ImGui::GetContentRegionAvail().x - 60.0f), 260.0f));
|
// ImGui::PushItemWidth(std::min((ImGui::GetContentRegionAvail().x - 60.0f), 260.0f));
|
||||||
ImGui::SliderInt("##WiiUFPSSlider", &fpsSlider, 1, 3, "", ImGuiSliderFlags_AlwaysClamp);
|
// ImGui::SliderInt("##WiiUFPSSlider", &fpsSlider, 1, 3, "", ImGuiSliderFlags_AlwaysClamp);
|
||||||
ImGui::PopItemWidth();
|
// ImGui::PopItemWidth();
|
||||||
|
//
|
||||||
ImGui::SameLine();
|
// ImGui::SameLine();
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f);
|
// ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f);
|
||||||
if (ImGui::Button(" + ##WiiUFPS")) {
|
// if (ImGui::Button(" + ##WiiUFPS")) {
|
||||||
fpsSlider++;
|
// fpsSlider++;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (CVarGetInteger("gMatchRefreshRate", 0)) {
|
// if (CVarGetInteger("gMatchRefreshRate", 0)) {
|
||||||
UIWidgets::ReEnableComponent("");
|
// UIWidgets::ReEnableComponent("");
|
||||||
}
|
// }
|
||||||
if (fpsSlider > 3) {
|
// if (fpsSlider > 3) {
|
||||||
fpsSlider = 3;
|
// fpsSlider = 3;
|
||||||
} else if (fpsSlider < 1) {
|
// } else if (fpsSlider < 1) {
|
||||||
fpsSlider = 1;
|
// fpsSlider = 1;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (fpsSlider == 1) {
|
// if (fpsSlider == 1) {
|
||||||
currentFps = 20;
|
// currentFps = 20;
|
||||||
} else if (fpsSlider == 2) {
|
// } else if (fpsSlider == 2) {
|
||||||
currentFps = 30;
|
// currentFps = 30;
|
||||||
} else if (fpsSlider == 3) {
|
// } else if (fpsSlider == 3) {
|
||||||
currentFps = 60;
|
// currentFps = 60;
|
||||||
}
|
// }
|
||||||
CVarSetInteger("gInterpolationFPS", currentFps);
|
// CVarSetInteger("gInterpolationFPS", currentFps);
|
||||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
// Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
||||||
#else
|
//#else
|
||||||
bool matchingRefreshRate =
|
// bool matchingRefreshRate =
|
||||||
CVarGetInteger("gMatchRefreshRate", 0) &&
|
// CVarGetInteger("gMatchRefreshRate", 0) &&
|
||||||
Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() != Ship::WindowBackend::FAST3D_DXGI_DX11;
|
// Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() != Ship::WindowBackend::FAST3D_DXGI_DX11;
|
||||||
UIWidgets::CVarSliderInt((currentFps == 20) ? "FPS: Original (20)" : "FPS: %d", "gInterpolationFPS", minFps,
|
// UIWidgets::CVarSliderInt((currentFps == 20) ? "FPS: Original (20)" : "FPS: %d", "gInterpolationFPS", minFps,
|
||||||
maxFps, 1, { .disabled = matchingRefreshRate });
|
// maxFps, 1, { .disabled = matchingRefreshRate });
|
||||||
#endif
|
//#endif
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() ==
|
// if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() ==
|
||||||
Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
// Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
||||||
UIWidgets::Tooltip(
|
// UIWidgets::Tooltip(
|
||||||
"Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. "
|
// "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. "
|
||||||
"This is purely "
|
// "This is purely "
|
||||||
"visual and does not impact game logic, execution of glitches etc.\n\n"
|
// "visual and does not impact game logic, execution of glitches etc.\n\n"
|
||||||
"A higher target FPS than your monitor's refresh rate will waste resources, and might give a worse "
|
// "A higher target FPS than your monitor's refresh rate will waste resources, and might give a worse "
|
||||||
"result.");
|
// "result.");
|
||||||
} else {
|
// } else {
|
||||||
UIWidgets::Tooltip(
|
// UIWidgets::Tooltip(
|
||||||
"Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is purely "
|
// "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is purely "
|
||||||
"visual and does not impact game logic, execution of glitches etc.");
|
// "visual and does not impact game logic, execution of glitches etc.");
|
||||||
}
|
// }
|
||||||
} // END FPS Slider
|
// } // END FPS Slider
|
||||||
|
//
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
// if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
||||||
UIWidgets::Spacer(0);
|
// UIWidgets::Spacer(0);
|
||||||
if (ImGui::Button("Match Refresh Rate")) {
|
// if (ImGui::Button("Match Refresh Rate")) {
|
||||||
int hz = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
|
// int hz = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
|
||||||
if (hz >= 30 && hz <= 360) {
|
// if (hz >= 30 && hz <= 360) {
|
||||||
CVarSetInteger("gInterpolationFPS", hz);
|
// CVarSetInteger("gInterpolationFPS", hz);
|
||||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
// Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Match Refresh Rate", "gMatchRefreshRate", true, false);
|
// UIWidgets::PaddedEnhancementCheckbox("Match Refresh Rate", "gMatchRefreshRate", true, false);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
UIWidgets::Tooltip("Matches interpolation value to the current game's window refresh rate");
|
// UIWidgets::Tooltip("Matches interpolation value to the current game's window refresh rate");
|
||||||
|
//
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
// if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
||||||
UIWidgets::PaddedEnhancementSliderInt(
|
// UIWidgets::PaddedEnhancementSliderInt(
|
||||||
CVarGetInteger("gExtraLatencyThreshold", 0) == 0 ? "Jitter fix: Off" : "Jitter fix: >= %d FPS",
|
// CVarGetInteger("gExtraLatencyThreshold", 0) == 0 ? "Jitter fix: Off" : "Jitter fix: >= %d FPS",
|
||||||
"##ExtraLatencyThreshold", "gExtraLatencyThreshold", 0, 360, "", 0, true, true, false);
|
// "##ExtraLatencyThreshold", "gExtraLatencyThreshold", 0, 360, "", 0, true, true, false);
|
||||||
UIWidgets::Tooltip("When Interpolation FPS setting is at least this threshold, add one frame of input lag "
|
// UIWidgets::Tooltip("When Interpolation FPS setting is at least this threshold, add one frame of input lag "
|
||||||
"(e.g. 16.6 ms for 60 FPS) in order to avoid jitter. This setting allows the CPU to "
|
// "(e.g. 16.6 ms for 60 FPS) in order to avoid jitter. This setting allows the CPU to "
|
||||||
"work on one frame while GPU works on the previous frame.\nThis setting should be used "
|
// "work on one frame while GPU works on the previous frame.\nThis setting should be used "
|
||||||
"when your computer is too slow to do CPU + GPU work in time.");
|
// "when your computer is too slow to do CPU + GPU work in time.");
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
// UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
||||||
|
//
|
||||||
static std::unordered_map<Ship::WindowBackend, const char*> windowBackendNames = {
|
// static std::unordered_map<Ship::WindowBackend, const char*> windowBackendNames = {
|
||||||
{ Ship::WindowBackend::FAST3D_DXGI_DX11, "DirectX" },
|
// { Ship::WindowBackend::FAST3D_DXGI_DX11, "DirectX" },
|
||||||
{ Ship::WindowBackend::FAST3D_SDL_OPENGL, "OpenGL" },
|
// { Ship::WindowBackend::FAST3D_SDL_OPENGL, "OpenGL" },
|
||||||
{ Ship::WindowBackend::FAST3D_SDL_METAL, "Metal" },
|
// { Ship::WindowBackend::FAST3D_SDL_METAL, "Metal" },
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
ImGui::Text("Renderer API (Needs reload)");
|
// ImGui::Text("Renderer API (Needs reload)");
|
||||||
Ship::WindowBackend runningWindowBackend = Ship::Context::GetInstance()->GetWindow()->GetWindowBackend();
|
// Ship::WindowBackend runningWindowBackend = Ship::Context::GetInstance()->GetWindow()->GetWindowBackend();
|
||||||
Ship::WindowBackend configWindowBackend;
|
// Ship::WindowBackend configWindowBackend;
|
||||||
int configWindowBackendId = Ship::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1);
|
// int configWindowBackendId = Ship::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1);
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->IsAvailableWindowBackend(configWindowBackendId)) {
|
// if (Ship::Context::GetInstance()->GetWindow()->IsAvailableWindowBackend(configWindowBackendId)) {
|
||||||
configWindowBackend = static_cast<Ship::WindowBackend>(configWindowBackendId);
|
// configWindowBackend = static_cast<Ship::WindowBackend>(configWindowBackendId);
|
||||||
} else {
|
// } else {
|
||||||
configWindowBackend = runningWindowBackend;
|
// configWindowBackend = runningWindowBackend;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size() <= 1) {
|
// if (Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size() <= 1) {
|
||||||
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
// UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||||
}
|
// }
|
||||||
if (ImGui::BeginCombo("##RApi", windowBackendNames[configWindowBackend])) {
|
// if (ImGui::BeginCombo("##RApi", windowBackendNames[configWindowBackend])) {
|
||||||
for (size_t i = 0; i < Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size();
|
// for (size_t i = 0; i < Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size();
|
||||||
i++) {
|
// i++) {
|
||||||
auto backend = Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->data()[i];
|
// auto backend = Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->data()[i];
|
||||||
if (ImGui::Selectable(windowBackendNames[backend], backend == configWindowBackend)) {
|
// if (ImGui::Selectable(windowBackendNames[backend], backend == configWindowBackend)) {
|
||||||
Ship::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id", static_cast<int>(backend));
|
// Ship::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id", static_cast<int>(backend));
|
||||||
Ship::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name",
|
// Ship::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name",
|
||||||
windowBackendNames[backend]);
|
// windowBackendNames[backend]);
|
||||||
Ship::Context::GetInstance()->GetConfig()->Save();
|
// Ship::Context::GetInstance()->GetConfig()->Save();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
ImGui::EndCombo();
|
// ImGui::EndCombo();
|
||||||
}
|
// }
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size() <= 1) {
|
// if (Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size() <= 1) {
|
||||||
UIWidgets::ReEnableComponent("");
|
// UIWidgets::ReEnableComponent("");
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync()) {
|
// if (Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync()) {
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Enable Vsync", "gVsyncEnabled", true, false);
|
// UIWidgets::PaddedEnhancementCheckbox("Enable Vsync", "gVsyncEnabled", true, false);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen()) {
|
// if (Ship::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen()) {
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Windowed fullscreen", "gSdlWindowedFullscreen", true, false);
|
// UIWidgets::PaddedEnhancementCheckbox("Windowed fullscreen", "gSdlWindowedFullscreen", true, false);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (Ship::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports()) {
|
// if (Ship::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports()) {
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Allow multi-windows", "gEnableMultiViewports", true, false, false, "",
|
// UIWidgets::PaddedEnhancementCheckbox("Allow multi-windows", "gEnableMultiViewports", true, false, false, "",
|
||||||
UIWidgets::CheckboxGraphics::Cross, true);
|
// UIWidgets::CheckboxGraphics::Cross, true);
|
||||||
UIWidgets::Tooltip("Allows windows to be able to be dragged off of the main game window. Requires a reload "
|
// UIWidgets::Tooltip("Allows windows to be able to be dragged off of the main game window. Requires a reload "
|
||||||
"to take effect.");
|
// "to take effect.");
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// If more filters are added to LUS, make sure to add them to the filters list here
|
// // If more filters are added to LUS, make sure to add them to the filters list here
|
||||||
ImGui::Text("Texture Filter (Needs reload)");
|
// ImGui::Text("Texture Filter (Needs reload)");
|
||||||
|
//
|
||||||
UIWidgets::EnhancementCombobox("gTextureFilter", filters, 0);
|
// UIWidgets::EnhancementCombobox("gTextureFilter", filters, 0);
|
||||||
|
//
|
||||||
UIWidgets::Spacer(0);
|
// UIWidgets::Spacer(0);
|
||||||
|
//
|
||||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->DrawSettings();
|
// Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->DrawSettings();
|
||||||
|
//
|
||||||
ImGui::EndMenu();
|
// ImGui::EndMenu();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawMenuBarIcon() {
|
void DrawMenuBarIcon() {
|
||||||
|
|
@ -442,56 +424,15 @@ void DrawMenuBarIcon() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawGameMenu() {
|
void DrawGameMenu() {
|
||||||
if (UIWidgets::BeginMenu("Spaghetti")) {
|
|
||||||
if (UIWidgets::MenuItem("Reset",
|
|
||||||
#ifdef __APPLE__
|
|
||||||
"Command-R"
|
|
||||||
#else
|
|
||||||
"Ctrl+R"
|
|
||||||
#endif
|
|
||||||
)) {
|
|
||||||
gGamestateNext = MAIN_MENU_FROM_QUIT;
|
|
||||||
if (CVarGetInteger("gEnableDebugMode", 0) == true) {
|
|
||||||
gMenuSelection = START_MENU;
|
|
||||||
} else {
|
|
||||||
gMenuSelection = LOGO_INTRO_MENU;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if !defined(__SWITCH__) && !defined(__WIIU__)
|
|
||||||
|
|
||||||
if (UIWidgets::MenuItem("Toggle Fullscreen", "F11")) {
|
|
||||||
Ship::Context::GetInstance()->GetWindow()->ToggleFullscreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UIWidgets::MenuItem("Quit")) {
|
|
||||||
Ship::Context::GetInstance()->GetWindow()->Close();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawEnhancementsMenu() {
|
void DrawEnhancementsMenu() {
|
||||||
if (UIWidgets::BeginMenu("Enhancements")) {
|
if (UIWidgets::BeginMenu("Enhancements")) {
|
||||||
|
|
||||||
UIWidgets::WindowButton("Multiplayer", "gMultiplayerWindowEnabled", GameUI::mMultiplayerWindow,
|
|
||||||
{ .tooltip = "Shows the multiplayer window" });
|
|
||||||
|
|
||||||
if (UIWidgets::BeginMenu("Gameplay")) {
|
if (UIWidgets::BeginMenu("Gameplay")) {
|
||||||
UIWidgets::WindowButton("Freecam", "gFreecam", GameUI::mFreecamWindow,
|
|
||||||
{ .tooltip = "Allows you to fly around the course" });
|
|
||||||
UIWidgets::CVarCheckbox("No multiplayer feature cuts", "gMultiplayerNoFeatureCuts",
|
|
||||||
{ .tooltip = "Allows full train and jumbotron in multiplayer, etc." });
|
|
||||||
UIWidgets::CVarCheckbox("General Improvements", "gImprovements", { .tooltip = "General improvements to the game experience." });
|
|
||||||
UIWidgets::CVarCheckbox(
|
|
||||||
"No Level of Detail (LOD)", "gDisableLod",
|
|
||||||
{ .tooltip = "Disable Level of Detail (LOD) to avoid models using lower poly versions at a distance" });
|
|
||||||
|
|
||||||
|
|
||||||
UIWidgets::CVarCheckbox("Disable Culling", "gNoCulling", { .tooltip = "Disable original culling of mk64" });
|
|
||||||
UIWidgets::CVarSliderFloat(
|
|
||||||
"Far Frustrum", "gFarFrustrum", 0.0f, 10000.0f, 10000.0f,
|
|
||||||
{ .tooltip = "Say how Far the Frustrum are when 'Disable Culling' are enable", .step = 10.0f });
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,16 +441,7 @@ void DrawEnhancementsMenu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawCheatsMenu() {
|
void DrawCheatsMenu() {
|
||||||
if (UIWidgets::BeginMenu("Cheats")) {
|
|
||||||
UIWidgets::CVarCheckbox("Moon Jump", "gEnableMoonJump");
|
|
||||||
UIWidgets::CVarCheckbox("Enable Custom CC", "gEnableCustomCC");
|
|
||||||
UIWidgets::CVarSliderFloat("Custom CC", "gCustomCC", 0.0, 1000.0, 150.0, { .step = 10.0 });
|
|
||||||
UIWidgets::CVarCheckbox("Disable Wall Collision", "gNoWallColision", { .tooltip = "Disable wall collision." });
|
|
||||||
UIWidgets::CVarSliderFloat(
|
|
||||||
"Min Height", "gMinHeight", -50.0f, 50.0f, 0.0f,
|
|
||||||
{ .tooltip = "When Disable Wall Collision are enable what is the minimal height you can get." });
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* debugInfoPages[6] = {
|
const char* debugInfoPages[6] = {
|
||||||
|
|
@ -517,31 +449,7 @@ const char* debugInfoPages[6] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
void DrawDebugMenu() {
|
void DrawDebugMenu() {
|
||||||
if (UIWidgets::BeginMenu("Developer")) {
|
|
||||||
UIWidgets::WindowButton(
|
|
||||||
"Gfx Debugger", "gGfxDebuggerEnabled", GameUI::mGfxDebuggerWindow,
|
|
||||||
{ .tooltip =
|
|
||||||
"Enables the Gfx Debugger window, allowing you to input commands, type help for some examples" });
|
|
||||||
|
|
||||||
UIWidgets::CVarCheckbox("Debug mode", "gEnableDebugMode", { .tooltip = "Enable debug mode" });
|
|
||||||
|
|
||||||
UIWidgets::CVarCheckbox("Render Collision", "gRenderCollisionMesh",
|
|
||||||
{ .tooltip = "Renders the collision mesh instead of the course mesh" });
|
|
||||||
|
|
||||||
UIWidgets::Spacer(0);
|
|
||||||
|
|
||||||
UIWidgets::WindowButton("GameInfo", "gGameInfoEnabled", GameUI::mGameInfoWindow,
|
|
||||||
{ .tooltip = "Shows the game info window, contains player and actor information" });
|
|
||||||
|
|
||||||
UIWidgets::WindowButton(
|
|
||||||
"Stats", "gStatsEnabled", GameUI::mStatsWindow,
|
|
||||||
{ .tooltip = "Shows the stats window, with your FPS and frametimes, and the OS you're playing on" });
|
|
||||||
UIWidgets::WindowButton(
|
|
||||||
"Console", "gConsoleEnabled", GameUI::mConsoleWindow,
|
|
||||||
{ .tooltip = "Enables the console window, allowing you to input commands, type help for some examples" });
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameMenuBar::DrawElement() {
|
void GameMenuBar::DrawElement() {
|
||||||
|
|
@ -552,19 +460,19 @@ void GameMenuBar::DrawElement() {
|
||||||
|
|
||||||
ImGui::SetCursorPosY(0.0f);
|
ImGui::SetCursorPosY(0.0f);
|
||||||
|
|
||||||
DrawSettingsMenu();
|
//DrawSettingsMenu();
|
||||||
|
|
||||||
ImGui::SetCursorPosY(0.0f);
|
ImGui::SetCursorPosY(0.0f);
|
||||||
|
|
||||||
DrawEnhancementsMenu();
|
//DrawEnhancementsMenu();
|
||||||
|
|
||||||
ImGui::SetCursorPosY(0.0f);
|
ImGui::SetCursorPosY(0.0f);
|
||||||
|
|
||||||
DrawCheatsMenu();
|
//DrawCheatsMenu();
|
||||||
|
|
||||||
ImGui::SetCursorPosY(0.0f);
|
ImGui::SetCursorPosY(0.0f);
|
||||||
|
|
||||||
DrawDebugMenu();
|
//DrawDebugMenu();
|
||||||
|
|
||||||
ImGui::EndMenuBar();
|
ImGui::EndMenuBar();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,801 @@
|
||||||
|
#include "Menu.h"
|
||||||
|
#include "UIWidgets.h"
|
||||||
|
#include "port/Engine.h"
|
||||||
|
#include "window/gui/GuiMenuBar.h"
|
||||||
|
#include "window/gui/GuiElement.h"
|
||||||
|
#include <variant>
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
#include <variant>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern s32 gGamestateNext;
|
||||||
|
extern s32 gMenuSelection;
|
||||||
|
#include "audio/external.h"
|
||||||
|
#include "defines.h"
|
||||||
|
}
|
||||||
|
std::vector<ImVec2> windowTypeSizes = { {} };
|
||||||
|
|
||||||
|
extern std::unordered_map<s16, const char*> warpPointSceneList;
|
||||||
|
extern void Warp();
|
||||||
|
|
||||||
|
namespace BenGui {}
|
||||||
|
|
||||||
|
namespace Ship {
|
||||||
|
std::string disabledTempTooltip;
|
||||||
|
const char* disabledTooltip;
|
||||||
|
bool disabledValue = false;
|
||||||
|
|
||||||
|
bool operator==(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
|
||||||
|
return l.r == r.r && l.g == r.g && l.b == r.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
|
||||||
|
return l.r == r.r && l.g == r.g && l.b == r.b && l.a == r.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
|
||||||
|
return (l.r < r.r && l.g <= r.g && l.b <= r.b) || (l.r <= r.r && l.g < r.g && l.b <= r.b) ||
|
||||||
|
(l.r <= r.r && l.g <= r.g && l.b < r.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
|
||||||
|
return (l.r < r.r && l.g <= r.g && l.b <= r.b && l.a <= r.a) ||
|
||||||
|
(l.r <= r.r && l.g < r.g && l.b <= r.b && l.a <= r.a) ||
|
||||||
|
(l.r <= r.r && l.g <= r.g && l.b < r.b && l.a <= r.a) ||
|
||||||
|
(l.r <= r.r && l.g <= r.g && l.b <= r.b && l.a < r.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
|
||||||
|
return (l.r > r.r && l.g >= r.g && l.b >= r.b) || (l.r >= r.r && l.g > r.g && l.b >= r.b) ||
|
||||||
|
(l.r >= r.r && l.g >= r.g && l.b > r.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
|
||||||
|
return (l.r > r.r && l.g >= r.g && l.b >= r.b && l.a >= r.a) ||
|
||||||
|
(l.r >= r.r && l.g > r.g && l.b >= r.b && l.a >= r.a) ||
|
||||||
|
(l.r >= r.r && l.g >= r.g && l.b > r.b && l.a >= r.a) ||
|
||||||
|
(l.r >= r.r && l.g >= r.g && l.b >= r.b && l.a > r.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetVectorIndexOf(std::vector<std::string>& vector, std::string value) {
|
||||||
|
return std::distance(vector.begin(), std::find(vector.begin(), vector.end(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::InsertSidebarSearch() {
|
||||||
|
menuEntries["Settings"].sidebars.emplace("Search", searchSidebarEntry);
|
||||||
|
uint32_t curIndex = 0;
|
||||||
|
if (!Ship_IsCStringEmpty(CVarGetString(menuEntries["Settings"].sidebarCvar, ""))) {
|
||||||
|
curIndex = GetVectorIndexOf(menuEntries["Settings"].sidebarOrder,
|
||||||
|
CVarGetString(menuEntries["Settings"].sidebarCvar, ""));
|
||||||
|
}
|
||||||
|
menuEntries["Settings"].sidebarOrder.insert(menuEntries["Settings"].sidebarOrder.begin() + searchSidebarIndex,
|
||||||
|
"Search");
|
||||||
|
if (curIndex > searchSidebarIndex) {
|
||||||
|
CVarSetString(menuEntries["Settings"].sidebarCvar, menuEntries["Settings"].sidebarOrder.at(curIndex).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::RemoveSidebarSearch() {
|
||||||
|
uint32_t curIndex =
|
||||||
|
GetVectorIndexOf(menuEntries["Settings"].sidebarOrder, CVarGetString(menuEntries["Settings"].sidebarCvar, ""));
|
||||||
|
menuEntries["Settings"].sidebars.erase("Search");
|
||||||
|
std::erase_if(menuEntries["Settings"].sidebarOrder, [](std::string& name) { return name == "Search"; });
|
||||||
|
if (curIndex > searchSidebarIndex) {
|
||||||
|
curIndex--;
|
||||||
|
} else if (curIndex >= menuEntries["Settings"].sidebarOrder.size()) {
|
||||||
|
curIndex = menuEntries["Settings"].sidebarOrder.size() - 1;
|
||||||
|
}
|
||||||
|
CVarSetString(menuEntries["Settings"].sidebarCvar, menuEntries["Settings"].sidebarOrder.at(curIndex).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::UpdateWindowBackendObjects() {
|
||||||
|
Ship::WindowBackend runningWindowBackend = Ship::Context::GetInstance()->GetWindow()->GetWindowBackend();
|
||||||
|
int32_t configWindowBackendId = Ship::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1);
|
||||||
|
if (Ship::Context::GetInstance()->GetWindow()->IsAvailableWindowBackend(configWindowBackendId)) {
|
||||||
|
configWindowBackend = static_cast<Ship::WindowBackend>(configWindowBackendId);
|
||||||
|
} else {
|
||||||
|
configWindowBackend = runningWindowBackend;
|
||||||
|
}
|
||||||
|
|
||||||
|
availableWindowBackends = Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends();
|
||||||
|
for (auto& backend : *availableWindowBackends) {
|
||||||
|
availableWindowBackendsMap[backend] = windowBackendsMap.at(backend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu::Menu(const std::string& cVar, const std::string& name, uint8_t searchSidebarIndex_,
|
||||||
|
UIWidgets::Colors defaultThemeIndex_)
|
||||||
|
: GuiWindow(cVar, name), searchSidebarIndex(searchSidebarIndex_), defaultThemeIndex(defaultThemeIndex_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::InitElement() {
|
||||||
|
popped = CVarGetInteger("gSettings.Menu.Popout", 0);
|
||||||
|
poppedSize.x = CVarGetInteger("gSettings.Menu.PoppedWidth", 1280);
|
||||||
|
poppedSize.y = CVarGetInteger("gSettings.Menu.PoppedHeight", 800);
|
||||||
|
poppedPos.x = CVarGetInteger("gSettings.Menu.PoppedPos.x", 0);
|
||||||
|
poppedPos.y = CVarGetInteger("gSettings.Menu.PoppedPos.y", 0);
|
||||||
|
|
||||||
|
UpdateWindowBackendObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::UpdateElement() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModernMenuSidebarEntry(std::string label) {
|
||||||
|
ImGuiContext& g = *GImGui;
|
||||||
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
ImVec2 pos = window->DC.CursorPos;
|
||||||
|
const ImGuiID sidebarId = window->GetID(std::string(label + "##Sidebar").c_str());
|
||||||
|
ImVec2 labelSize = ImGui::CalcTextSize(label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true);
|
||||||
|
pos.y += style.FramePadding.y;
|
||||||
|
pos.x = window->WorkRect.GetCenter().x - labelSize.x / 2;
|
||||||
|
ImRect bb = { pos - style.FramePadding, pos + labelSize + style.FramePadding };
|
||||||
|
ImGui::ItemSize(bb, style.FramePadding.y);
|
||||||
|
ImGui::ItemAdd(bb, sidebarId);
|
||||||
|
bool hovered, held;
|
||||||
|
bool pressed = ImGui::ButtonBehavior(bb, sidebarId, &hovered, &held);
|
||||||
|
if (pressed) {
|
||||||
|
ImGui::MarkItemEdited(sidebarId);
|
||||||
|
}
|
||||||
|
window->DrawList->AddRectFilled(pos - style.FramePadding, pos + labelSize + style.FramePadding,
|
||||||
|
ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive
|
||||||
|
: hovered ? ImGuiCol_ButtonHovered
|
||||||
|
: ImGuiCol_Button),
|
||||||
|
3.0f);
|
||||||
|
UIWidgets::RenderText(pos, label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true);
|
||||||
|
return pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModernMenuHeaderEntry(std::string label) {
|
||||||
|
ImGuiContext& g = *GImGui;
|
||||||
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
ImVec2 pos = window->DC.CursorPos;
|
||||||
|
const ImGuiID headerId = window->GetID(std::string(label + "##Header").c_str());
|
||||||
|
ImVec2 labelSize = ImGui::CalcTextSize(label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true);
|
||||||
|
ImRect bb = { pos, pos + labelSize + style.FramePadding * 2 };
|
||||||
|
ImGui::ItemSize(bb, style.FramePadding.y);
|
||||||
|
ImGui::ItemAdd(bb, headerId);
|
||||||
|
bool hovered, held;
|
||||||
|
bool pressed = ImGui::ButtonBehavior(bb, headerId, &hovered, &held);
|
||||||
|
window->DrawList->AddRectFilled(bb.Min, bb.Max,
|
||||||
|
ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive
|
||||||
|
: hovered ? ImGuiCol_ButtonHovered
|
||||||
|
: ImGuiCol_Button),
|
||||||
|
3.0f);
|
||||||
|
pos += style.FramePadding;
|
||||||
|
UIWidgets::RenderText(pos, label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true);
|
||||||
|
return pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Menu::DrawSearchResults(std::string& menuSearchText) {
|
||||||
|
auto menuThemeIndex = static_cast<UIWidgets::Colors>(CVarGetInteger("gSettings.Menu.Theme", defaultThemeIndex));
|
||||||
|
ImGui::BeginChild("Search Results");
|
||||||
|
int searchCount = 0;
|
||||||
|
for (auto& menuLabel : menuOrder) {
|
||||||
|
auto& menuEntry = menuEntries.at(menuLabel);
|
||||||
|
for (auto& sidebarLabel : menuEntry.sidebarOrder) {
|
||||||
|
auto& sidebar = menuEntry.sidebars[sidebarLabel];
|
||||||
|
for (int i = 0; i < sidebar.columnWidgets.size(); i++) {
|
||||||
|
auto& column = sidebar.columnWidgets.at(i);
|
||||||
|
for (auto& info : column) {
|
||||||
|
if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR || info.type == WIDGET_SEPARATOR_TEXT ||
|
||||||
|
info.isHidden) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const char* tooltip = info.options->tooltip;
|
||||||
|
std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : "");
|
||||||
|
std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower);
|
||||||
|
menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '),
|
||||||
|
menuSearchText.end());
|
||||||
|
std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower);
|
||||||
|
widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end());
|
||||||
|
if (widgetStr.find(menuSearchText) != std::string::npos) {
|
||||||
|
MenuDrawItem(info, 90 / sidebar.columnCount, menuThemeIndex);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray));
|
||||||
|
std::string origin = fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1);
|
||||||
|
ImGui::Text("%s", origin.c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
searchCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return searchCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::AddMenuEntry(std::string entryName, const char* entryCvar) {
|
||||||
|
menuEntries.emplace(entryName, MainMenuEntry{ entryName, entryCvar });
|
||||||
|
menuOrder.push_back(entryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<uint32_t, disabledInfo>& Menu::GetDisabledMap() {
|
||||||
|
return disabledMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors menuThemeIndex) {
|
||||||
|
disabledTempTooltip = "This setting is disabled because: \n\n";
|
||||||
|
disabledValue = false;
|
||||||
|
disabledTooltip = " ";
|
||||||
|
|
||||||
|
if (widget.preFunc != nullptr) {
|
||||||
|
widget.ResetDisables();
|
||||||
|
widget.preFunc(widget);
|
||||||
|
if (widget.isHidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!widget.activeDisables.empty()) {
|
||||||
|
widget.options->disabled = true;
|
||||||
|
for (auto option : widget.activeDisables) {
|
||||||
|
disabledTempTooltip += std::string("- ") + disabledMap.at(option).reason + std::string("\n");
|
||||||
|
}
|
||||||
|
widget.options->disabledTooltip = disabledTempTooltip.c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.sameLine) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (widget.type) {
|
||||||
|
case WIDGET_CHECKBOX: {
|
||||||
|
bool* pointer = std::get<bool*>(widget.valuePointer);
|
||||||
|
if (pointer == nullptr) {
|
||||||
|
SPDLOG_ERROR("Checkbox Widget requires a value pointer, currently nullptr");
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::CheckboxOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::Checkbox(UIWidgets::WrappedText(widget.name.c_str(), width).c_str(), pointer,
|
||||||
|
*options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_CVAR_CHECKBOX: {
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::CheckboxOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::CVarCheckbox(UIWidgets::WrappedText(widget.name.c_str(), width).c_str(), widget.cVar,
|
||||||
|
*options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
case WIDGET_AUDIO_BACKEND: {
|
||||||
|
auto currentAudioBackend = Ship::Context::GetInstance()->GetAudio()->GetCurrentAudioBackend();
|
||||||
|
UIWidgets::ComboboxOptions options = {};
|
||||||
|
options.color = menuThemeIndex;
|
||||||
|
options.tooltip = "Sets the audio API used by the game. Requires a relaunch to take effect.";
|
||||||
|
options.disabled = Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1;
|
||||||
|
options.disabledTooltip = "Only one audio API is available on this platform.";
|
||||||
|
if (UIWidgets::Combobox("Audio API", ¤tAudioBackend, audioBackendsMap, options)) {
|
||||||
|
Ship::Context::GetInstance()->GetAudio()->SetCurrentAudioBackend(currentAudioBackend);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_VIDEO_BACKEND: {
|
||||||
|
UIWidgets::ComboboxOptions options = {};
|
||||||
|
options.color = menuThemeIndex;
|
||||||
|
options.tooltip = "Sets the renderer API used by the game.";
|
||||||
|
options.disabled = availableWindowBackends->size() <= 1;
|
||||||
|
options.disabledTooltip = "Only one renderer API is available on this platform.";
|
||||||
|
if (UIWidgets::Combobox("Renderer API (Needs reload)", &configWindowBackend, availableWindowBackendsMap,
|
||||||
|
options)) {
|
||||||
|
Ship::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id",
|
||||||
|
(int32_t)(configWindowBackend));
|
||||||
|
Ship::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name",
|
||||||
|
windowBackendsMap.at(configWindowBackend));
|
||||||
|
Ship::Context::GetInstance()->GetConfig()->Save();
|
||||||
|
UpdateWindowBackendObjects();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_SEPARATOR: {
|
||||||
|
ImGui::Separator();
|
||||||
|
} break;
|
||||||
|
case WIDGET_SEPARATOR_TEXT: {
|
||||||
|
if (widget.options->color != UIWidgets::Colors::NoColor) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(widget.options->color));
|
||||||
|
}
|
||||||
|
ImGui::SeparatorText(widget.name.c_str());
|
||||||
|
if (widget.options->color != UIWidgets::Colors::NoColor) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_TEXT: {
|
||||||
|
if (widget.options->color != UIWidgets::Colors::NoColor) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(widget.options->color));
|
||||||
|
}
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::TextWrapped("%s", widget.name.c_str());
|
||||||
|
if (widget.options->color != UIWidgets::Colors::NoColor) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_COMBOBOX: {
|
||||||
|
int32_t* pointer = std::get<int32_t*>(widget.valuePointer);
|
||||||
|
if (pointer == nullptr) {
|
||||||
|
SPDLOG_ERROR("Combobox Widget requires a value pointer, currently nullptr");
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::ComboboxOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::Combobox(widget.name.c_str(), pointer, options->comboMap, *options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
case WIDGET_CVAR_COMBOBOX: {
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::ComboboxOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::CVarCombobox(widget.name.c_str(), widget.cVar, options->comboMap, *options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_SLIDER_INT: {
|
||||||
|
int32_t* pointer = std::get<int32_t*>(widget.valuePointer);
|
||||||
|
if (pointer == nullptr) {
|
||||||
|
SPDLOG_ERROR("int32 Slider Widget requires a value pointer, currently nullptr");
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::IntSliderOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::SliderInt(widget.name.c_str(), pointer, *options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
case WIDGET_CVAR_SLIDER_INT: {
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::IntSliderOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::CVarSliderInt(widget.name.c_str(), widget.cVar, *options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
case WIDGET_SLIDER_FLOAT: {
|
||||||
|
float* pointer = std::get<float*>(widget.valuePointer);
|
||||||
|
|
||||||
|
if (pointer == nullptr) {
|
||||||
|
SPDLOG_ERROR("float Slider Widget requires a value pointer, currently nullptr");
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::FloatSliderOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::SliderFloat(widget.name.c_str(), pointer, *options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_CVAR_SLIDER_FLOAT: {
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::FloatSliderOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::CVarSliderFloat(widget.name.c_str(), widget.cVar, *options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_BUTTON: {
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::ButtonOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
if (UIWidgets::Button(widget.name.c_str(), *options)) {
|
||||||
|
if (widget.callback != nullptr) {
|
||||||
|
widget.callback(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_CUSTOM: {
|
||||||
|
if (widget.customFunction != nullptr) {
|
||||||
|
widget.customFunction(widget);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_WINDOW_BUTTON: {
|
||||||
|
if (widget.windowName == nullptr || widget.windowName[0] == '\0') {
|
||||||
|
std::string msg =
|
||||||
|
fmt::format("Error drawing window contents for {}: windowName not defined", widget.name);
|
||||||
|
SPDLOG_ERROR(msg.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto window = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow(widget.windowName);
|
||||||
|
if (!window) {
|
||||||
|
std::string msg =
|
||||||
|
fmt::format("Error drawing window contents: windowName {} does not exist", widget.windowName);
|
||||||
|
SPDLOG_ERROR(msg.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto options = std::static_pointer_cast<UIWidgets::ButtonOptions>(widget.options);
|
||||||
|
options->color = menuThemeIndex;
|
||||||
|
UIWidgets::WindowButton(widget.name.c_str(), widget.cVar, window, *options);
|
||||||
|
if (!window->IsVisible()) {
|
||||||
|
window->DrawElement();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case WIDGET_SEARCH: {
|
||||||
|
UIWidgets::PushStyleButton(menuThemeIndex);
|
||||||
|
if (ImGui::Button("Clear")) {
|
||||||
|
menuSearch.Clear();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (CVarGetInteger("gSettings.Menu.SearchAutofocus", 0) &&
|
||||||
|
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() &&
|
||||||
|
!ImGui::IsMouseClicked(0)) {
|
||||||
|
ImGui::SetKeyboardFocusHere(0);
|
||||||
|
}
|
||||||
|
UIWidgets::PushStyleCombobox(menuThemeIndex);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Border, UIWidgets::ColorValues.at(menuThemeIndex));
|
||||||
|
menuSearch.Draw();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
UIWidgets::PopStyleCombobox();
|
||||||
|
UIWidgets::PopStyleButton();
|
||||||
|
std::string menuSearchText(menuSearch.InputBuf);
|
||||||
|
|
||||||
|
if (menuSearchText == "") {
|
||||||
|
ImGui::Text("Start typing to see results.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DrawSearchResults(menuSearchText);
|
||||||
|
ImGui::EndChild();
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (widget.postFunc != nullptr) {
|
||||||
|
widget.postFunc(widget);
|
||||||
|
}
|
||||||
|
} catch (const std::bad_variant_access& e) {
|
||||||
|
SPDLOG_ERROR("Failed to draw menu item \"{}\" due to: {}", widget.name, e.what());
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::Draw() {
|
||||||
|
if (!IsVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DrawElement();
|
||||||
|
// Sync up the IsVisible flag if it was changed by ImGui
|
||||||
|
SyncVisibilityConsoleVariable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::DrawElement() {
|
||||||
|
for (auto& [reason, info] : disabledMap) {
|
||||||
|
info.active = info.evaluation(info);
|
||||||
|
}
|
||||||
|
auto menuThemeIndex = static_cast<UIWidgets::Colors>(CVarGetInteger("gSettings.Menu.Theme", defaultThemeIndex));
|
||||||
|
|
||||||
|
windowHeight = ImGui::GetMainViewport()->WorkSize.y;
|
||||||
|
windowWidth = ImGui::GetMainViewport()->WorkSize.x;
|
||||||
|
auto windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
|
||||||
|
bool popout = CVarGetInteger("gSettings.Menu.Popout", 0) && allowPopout;
|
||||||
|
if (popout) {
|
||||||
|
windowFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDocking;
|
||||||
|
}
|
||||||
|
if (popout != popped) {
|
||||||
|
if (popout) {
|
||||||
|
windowHeight = poppedSize.y;
|
||||||
|
windowWidth = poppedSize.x;
|
||||||
|
ImGui::SetNextWindowSize({ static_cast<float>(windowWidth), static_cast<float>(windowHeight) },
|
||||||
|
ImGuiCond_Always);
|
||||||
|
ImGui::SetNextWindowPos(poppedPos, ImGuiCond_Always);
|
||||||
|
} else if (popped) {
|
||||||
|
CVarSetFloat("gSettings.Menu.PoppedWidth", poppedSize.x);
|
||||||
|
CVarSetFloat("gSettings.Menu.PoppedHeight", poppedSize.y);
|
||||||
|
CVarSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popped = popout;
|
||||||
|
auto windowCond = ImGuiCond_Always;
|
||||||
|
if (!popout) {
|
||||||
|
ImGui::SetNextWindowSize({ static_cast<float>(windowWidth), static_cast<float>(windowHeight) }, windowCond);
|
||||||
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), windowCond, { 0.5f, 0.5f });
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
}
|
||||||
|
if (!ImGui::Begin("Main Menu", NULL, windowFlags)) {
|
||||||
|
if (!popout) {
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (popped != popout) {
|
||||||
|
if (!popout) {
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
}
|
||||||
|
CVarSetInteger("gSettings.Menu.Popout", popped);
|
||||||
|
CVarSetFloat("gSettings.Menu.PoppedWidth", poppedSize.x);
|
||||||
|
CVarSetFloat("gSettings.Menu.PoppedHeight", poppedSize.y);
|
||||||
|
CVarSetFloat("gSettings.Menu.PoppedPos.x", poppedSize.x);
|
||||||
|
CVarSetFloat("gSettings.Menu.PoppedPos.y", poppedSize.y);
|
||||||
|
CVarSave();
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ImGui::PushFont(GameEngine::Instance->fontStandardLargest);
|
||||||
|
ImGuiContext& g = *GImGui;
|
||||||
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
windowHeight = window->WorkRect.GetHeight();
|
||||||
|
windowWidth = window->WorkRect.GetWidth();
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f));
|
||||||
|
const char* headerCvar = "gSettings.Menu.ActiveHeader";
|
||||||
|
std::string headerIndex = CVarGetString(headerCvar, "Settings");
|
||||||
|
ImVec2 pos = window->DC.CursorPos;
|
||||||
|
float centerX = pos.x + windowWidth / 2 - (style.ItemSpacing.x * (menuEntries.size() + 1));
|
||||||
|
std::vector<ImVec2> headerSizes;
|
||||||
|
float headerWidth = style.ItemSpacing.x;
|
||||||
|
bool headerSearch = !CVarGetInteger("gSettings.Menu.SidebarSearch", 0);
|
||||||
|
if (headerSearch) {
|
||||||
|
headerWidth += 200.0f + style.ItemSpacing.x + style.FramePadding.x;
|
||||||
|
}
|
||||||
|
for (auto& label : menuOrder) {
|
||||||
|
ImVec2 size = ImGui::CalcTextSize(label.c_str());
|
||||||
|
headerSizes.push_back(size);
|
||||||
|
headerWidth += size.x + style.FramePadding.x * 2;
|
||||||
|
if (label == headerIndex) {
|
||||||
|
headerWidth += style.ItemSpacing.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImVec2 menuSize = { std::fminf(1280, windowWidth), std::fminf(800, windowHeight) };
|
||||||
|
pos += window->WorkRect.GetSize() / 2 - menuSize / 2;
|
||||||
|
ImGui::SetNextWindowPos(pos);
|
||||||
|
ImGui::BeginChild("Menu Block", menuSize,
|
||||||
|
ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize,
|
||||||
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, SidebarEntry>* sidebar;
|
||||||
|
float headerHeight = headerSizes.at(0).y + style.FramePadding.y * 2;
|
||||||
|
ImVec2 buttonSize = ImGui::CalcTextSize(ICON_FA_TIMES_CIRCLE) + style.FramePadding * 2;
|
||||||
|
bool scrollbar = false;
|
||||||
|
if (headerWidth > menuSize.x - buttonSize.x * 3 - style.ItemSpacing.x * 3) {
|
||||||
|
headerHeight += style.ScrollbarSize;
|
||||||
|
scrollbar = true;
|
||||||
|
}
|
||||||
|
UIWidgets::ButtonOptions options = {};
|
||||||
|
options.size = UIWidgets::Sizes::Inline;
|
||||||
|
options.tooltip = "Close Menu (Esc)";
|
||||||
|
if (UIWidgets::Button(ICON_FA_TIMES_CIRCLE, options)) {
|
||||||
|
ToggleVisibility();
|
||||||
|
|
||||||
|
// Update gamepad navigation after close based on if other menus are still visible
|
||||||
|
auto mImGuiIo = &ImGui::GetIO();
|
||||||
|
if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) &&
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetMenuOrMenubarVisible()) {
|
||||||
|
mImGuiIo->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||||
|
} else {
|
||||||
|
mImGuiIo->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextWindowSizeConstraints({ 0, headerHeight }, { headerWidth, headerHeight });
|
||||||
|
ImVec2 headerSelSize = { menuSize.x - buttonSize.x * 3 - style.ItemSpacing.x * 3, headerHeight };
|
||||||
|
if (scrollbar) {
|
||||||
|
headerSelSize.y += style.ScrollbarSize;
|
||||||
|
}
|
||||||
|
bool autoFocus = CVarGetInteger("gSettings.Menu.SearchAutofocus", 0);
|
||||||
|
ImGui::BeginChild("Header Selection", headerSelSize,
|
||||||
|
ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize,
|
||||||
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
|
uint8_t curIndex = 0;
|
||||||
|
for (auto& label : menuOrder) {
|
||||||
|
if (curIndex != 0) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
auto& entry = menuEntries.at(label);
|
||||||
|
std::string nextIndex = label;
|
||||||
|
UIWidgets::PushStyleButton(menuThemeIndex);
|
||||||
|
if (headerIndex != label) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 });
|
||||||
|
}
|
||||||
|
if (ModernMenuHeaderEntry(entry.label)) {
|
||||||
|
if (headerSearch) {
|
||||||
|
menuSearch.Clear();
|
||||||
|
}
|
||||||
|
CVarSetString(headerCvar, label.c_str());
|
||||||
|
CVarSave();
|
||||||
|
nextIndex = label;
|
||||||
|
}
|
||||||
|
if (headerIndex != label) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
UIWidgets::PopStyleButton();
|
||||||
|
if (headerIndex == label) {
|
||||||
|
sidebar = &entry.sidebars;
|
||||||
|
}
|
||||||
|
if (nextIndex != label) {
|
||||||
|
headerIndex = nextIndex;
|
||||||
|
}
|
||||||
|
curIndex++;
|
||||||
|
}
|
||||||
|
std::string menuSearchText = "";
|
||||||
|
if (headerSearch) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (autoFocus && ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() &&
|
||||||
|
!ImGui::IsMouseClicked(0)) {
|
||||||
|
ImGui::SetKeyboardFocusHere(0);
|
||||||
|
}
|
||||||
|
auto color = UIWidgets::ColorValues.at(menuThemeIndex);
|
||||||
|
color.w = 0.2f;
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, color);
|
||||||
|
menuSearch.Draw("##search", 200.0f);
|
||||||
|
menuSearchText = menuSearch.InputBuf;
|
||||||
|
menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), menuSearchText.end());
|
||||||
|
if (menuSearchText.length() < 1) {
|
||||||
|
ImGui::SameLine(headerWidth - 200.0f + style.ItemSpacing.x);
|
||||||
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search...");
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
ImGui::SameLine(menuSize.x - (buttonSize.x * 2) - style.ItemSpacing.x);
|
||||||
|
UIWidgets::ButtonOptions options2 = {};
|
||||||
|
options2.color = UIWidgets::Colors::Red;
|
||||||
|
options2.size = UIWidgets::Sizes::Inline;
|
||||||
|
options2.tooltip = "Reset"
|
||||||
|
#ifdef __APPLE__
|
||||||
|
" (Command-R)"
|
||||||
|
#elif !defined(__SWITCH__) && !defined(__WIIU__)
|
||||||
|
" (Ctrl+R)"
|
||||||
|
#else
|
||||||
|
""
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
if (UIWidgets::Button(ICON_FA_UNDO, options2)) {
|
||||||
|
ProcessReset();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
UIWidgets::ButtonOptions options3 = {};
|
||||||
|
options3.color = UIWidgets::Colors::Red;
|
||||||
|
options3.size = UIWidgets::Sizes::Inline;
|
||||||
|
options3.tooltip = "Quit 2S2H";
|
||||||
|
if (UIWidgets::Button(ICON_FA_POWER_OFF, options3)) {
|
||||||
|
if (!popped) {
|
||||||
|
ToggleVisibility();
|
||||||
|
}
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->Close();
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
pos.y += headerHeight + style.ItemSpacing.y;
|
||||||
|
pos.x = centerX - menuSize.x / 2 + (style.ItemSpacing.x * (menuEntries.size() + 1));
|
||||||
|
window->DrawList->AddRectFilled(pos, pos + ImVec2{ menuSize.x, 4 }, ImGui::GetColorU32({ 255, 255, 255, 255 }),
|
||||||
|
true, style.WindowRounding);
|
||||||
|
pos.y += style.ItemSpacing.y;
|
||||||
|
float sectionHeight = menuSize.y - headerHeight - 4 - style.ItemSpacing.y * 2;
|
||||||
|
float columnHeight = sectionHeight - style.ItemSpacing.y * 4;
|
||||||
|
ImGui::SetNextWindowPos(pos + style.ItemSpacing * 2);
|
||||||
|
float sidebarWidth = 200 - style.ItemSpacing.x;
|
||||||
|
|
||||||
|
const char* sidebarCvar = menuEntries.at(headerIndex).sidebarCvar;
|
||||||
|
|
||||||
|
std::string sectionIndex = CVarGetString(sidebarCvar, "");
|
||||||
|
if (!sidebar->contains(sectionIndex)) {
|
||||||
|
sectionIndex = sidebar->begin()->first;
|
||||||
|
}
|
||||||
|
float sectionCenterX = pos.x + (sidebarWidth / 2);
|
||||||
|
float topY = pos.y;
|
||||||
|
ImGui::SetNextWindowSizeConstraints({ sidebarWidth, 0 }, { sidebarWidth, columnHeight });
|
||||||
|
ImGui::BeginChild((menuEntries.at(headerIndex).label + " Section").c_str(), { sidebarWidth, columnHeight * 3 },
|
||||||
|
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoTitleBar);
|
||||||
|
for (auto& sidebarLabel : menuEntries.at(headerIndex).sidebarOrder) {
|
||||||
|
std::string nextIndex = "";
|
||||||
|
UIWidgets::PushStyleButton(menuThemeIndex);
|
||||||
|
if (sectionIndex != sidebarLabel) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 });
|
||||||
|
}
|
||||||
|
if (ModernMenuSidebarEntry(sidebarLabel)) {
|
||||||
|
if (headerSearch) {
|
||||||
|
menuSearch.Clear();
|
||||||
|
}
|
||||||
|
CVarSetString(sidebarCvar, sidebarLabel.c_str());
|
||||||
|
CVarSave();
|
||||||
|
nextIndex = sidebarLabel;
|
||||||
|
}
|
||||||
|
if (sectionIndex != sidebarLabel) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
UIWidgets::PopStyleButton();
|
||||||
|
if (nextIndex != "") {
|
||||||
|
sectionIndex = nextIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
ImGui::PushFont(GameEngine::Instance->fontMonoLarger);
|
||||||
|
pos = ImVec2{ sectionCenterX + (sidebarWidth / 2), topY } + style.ItemSpacing * 2;
|
||||||
|
window->DrawList->AddRectFilled(pos, pos + ImVec2{ 4, sectionHeight - style.FramePadding.y * 2 },
|
||||||
|
ImGui::GetColorU32({ 255, 255, 255, 255 }), true, style.WindowRounding);
|
||||||
|
pos.x += 4 + style.ItemSpacing.x;
|
||||||
|
ImGui::SetNextWindowPos(pos + style.ItemSpacing);
|
||||||
|
float sectionWidth = menuSize.x - sidebarWidth - 4 - style.ItemSpacing.x * 4;
|
||||||
|
std::string sectionMenuId = sectionIndex + " Settings";
|
||||||
|
int columns = sidebar->at(sectionIndex).columnCount;
|
||||||
|
size_t columnFuncs = sidebar->at(sectionIndex).columnWidgets.size();
|
||||||
|
if (windowWidth < 800) {
|
||||||
|
columns = 1;
|
||||||
|
}
|
||||||
|
float columnWidth = (sectionWidth - style.ItemSpacing.x * columns) / columns;
|
||||||
|
bool useColumns = columns > 1;
|
||||||
|
if (!useColumns || (headerSearch && menuSearchText.length() > 0)) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextWindowSizeConstraints({ sectionWidth, 0 }, { sectionWidth, columnHeight });
|
||||||
|
ImGui::BeginChild(sectionMenuId.c_str(), { sectionWidth, windowHeight * 4 }, ImGuiChildFlags_AutoResizeY,
|
||||||
|
ImGuiWindowFlags_NoTitleBar);
|
||||||
|
}
|
||||||
|
if (headerSearch && menuSearchText.length() > 0) {
|
||||||
|
uint32_t searchCount = DrawSearchResults(menuSearchText);
|
||||||
|
if (searchCount == 0) {
|
||||||
|
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize("No results found").x) / 2);
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f);
|
||||||
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "No results found");
|
||||||
|
}
|
||||||
|
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize("Clear Search").x) / 2 - 10.0f);
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f);
|
||||||
|
UIWidgets::ButtonOptions clearBtnOpts = {};
|
||||||
|
clearBtnOpts.size = UIWidgets::Sizes::Inline;
|
||||||
|
if (UIWidgets::Button("Clear Search", clearBtnOpts)) {
|
||||||
|
menuSearch.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
} else {
|
||||||
|
std::string menuLabel = menuEntries.at(headerIndex).label;
|
||||||
|
if (MenuInit::GetUpdateFuncs().contains(menuLabel)) {
|
||||||
|
if (MenuInit::GetUpdateFuncs()[menuLabel].contains(sectionIndex)) {
|
||||||
|
for (auto& updateFunc : MenuInit::GetUpdateFuncs()[menuLabel][sectionIndex]) {
|
||||||
|
updateFunc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < columnFuncs; i++) {
|
||||||
|
std::string sectionId = fmt::format("{} Column {}", sectionMenuId, i);
|
||||||
|
if (useColumns) {
|
||||||
|
ImGui::SetNextWindowSizeConstraints({ columnWidth, 0 }, { columnWidth, columnHeight });
|
||||||
|
ImGui::BeginChild(sectionId.c_str(), { columnWidth, windowHeight * 4 }, ImGuiChildFlags_AutoResizeY,
|
||||||
|
ImGuiWindowFlags_NoTitleBar);
|
||||||
|
}
|
||||||
|
// for (auto& entryName : sidebar->at(sectionIndex).sidebarOrder) {
|
||||||
|
for (auto& entry : sidebar->at(sectionIndex).columnWidgets.at(i)) {
|
||||||
|
MenuDrawItem(entry, 90 / sidebar->at(sectionIndex).columnCount, menuThemeIndex);
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
if (useColumns) {
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
if (i < columns - 1) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!useColumns || menuSearchText.length() > 0) {
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
if (!popout) {
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
if (popout) {
|
||||||
|
poppedSize = ImGui::GetWindowSize();
|
||||||
|
poppedPos = ImGui::GetWindowPos();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
} // namespace Ship
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef MENU_H
|
||||||
|
#define MENU_H
|
||||||
|
|
||||||
|
#include <libultraship/libultraship.h>
|
||||||
|
#include "UIWidgets.h"
|
||||||
|
#include "graphic/Fast3D/gfx_rendering_api.h"
|
||||||
|
#include "MenuTypes.h"
|
||||||
|
|
||||||
|
namespace Ship {
|
||||||
|
uint32_t GetVectorIndexOf(std::vector<std::string>& vector, std::string value);
|
||||||
|
class Menu : public GuiWindow {
|
||||||
|
public:
|
||||||
|
using GuiWindow::GuiWindow;
|
||||||
|
|
||||||
|
Menu(const std::string& cVar, const std::string& name, uint8_t searchSidebarIndex_ = 0,
|
||||||
|
UIWidgets::Colors menuThemeIndex_ = UIWidgets::Colors::LightBlue);
|
||||||
|
virtual ~Menu() {}
|
||||||
|
|
||||||
|
void InitElement() override;
|
||||||
|
void DrawElement() override;
|
||||||
|
void UpdateElement() override;
|
||||||
|
void Draw() override;
|
||||||
|
void InsertSidebarSearch();
|
||||||
|
void RemoveSidebarSearch();
|
||||||
|
void UpdateWindowBackendObjects();
|
||||||
|
|
||||||
|
void MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors menuThemeIndex);
|
||||||
|
void AddMenuEntry(std::string entryName, const char* entryCvar);
|
||||||
|
std::unordered_map<uint32_t, disabledInfo>& GetDisabledMap();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ImVec2 mOriginalSize;
|
||||||
|
std::string mName;
|
||||||
|
uint32_t mWindowFlags;
|
||||||
|
std::unordered_map<std::string, MainMenuEntry> menuEntries;
|
||||||
|
std::vector<std::string> menuOrder;
|
||||||
|
uint32_t DrawSearchResults(std::string& menuSearchText);
|
||||||
|
ImGuiTextFilter menuSearch;
|
||||||
|
uint8_t searchSidebarIndex;
|
||||||
|
UIWidgets::Colors defaultThemeIndex;
|
||||||
|
std::shared_ptr<std::vector<Ship::WindowBackend>> availableWindowBackends;
|
||||||
|
std::unordered_map<Ship::WindowBackend, const char*> availableWindowBackendsMap;
|
||||||
|
Ship::WindowBackend configWindowBackend;
|
||||||
|
|
||||||
|
std::unordered_map<uint32_t, disabledInfo> disabledMap;
|
||||||
|
std::vector<disabledInfo> disabledVector;
|
||||||
|
const SidebarEntry searchSidebarEntry = {
|
||||||
|
.columnCount = 1,
|
||||||
|
.columnWidgets = { { { .name = "Sidebar Search",
|
||||||
|
.type = WIDGET_SEARCH,
|
||||||
|
.options = std::make_shared<UIWidgets::WidgetOptions>(UIWidgets::WidgetOptions{}.Tooltip(
|
||||||
|
"Searches all menus for the given text, including tooltips.")) } } }
|
||||||
|
};
|
||||||
|
virtual void ProcessReset() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool allowPopout = true; // PortNote: should be set to false on small screen ports
|
||||||
|
bool popped;
|
||||||
|
ImVec2 poppedSize;
|
||||||
|
ImVec2 poppedPos;
|
||||||
|
float windowHeight;
|
||||||
|
float windowWidth;
|
||||||
|
};
|
||||||
|
} // namespace Ship
|
||||||
|
|
||||||
|
#endif // MENU_H
|
||||||
|
|
@ -0,0 +1,303 @@
|
||||||
|
#ifndef MENUTYPES_H
|
||||||
|
#define MENUTYPES_H
|
||||||
|
|
||||||
|
#include <libultraship/libultraship.h>
|
||||||
|
#include "UIWidgets.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DISABLE_FOR_FREE_CAM_ON,
|
||||||
|
DISABLE_FOR_FREE_CAM_OFF,
|
||||||
|
DISABLE_FOR_DEBUG_MODE_OFF,
|
||||||
|
DISABLE_FOR_NO_VSYNC,
|
||||||
|
DISABLE_FOR_NO_WINDOWED_FULLSCREEN,
|
||||||
|
DISABLE_FOR_NO_MULTI_VIEWPORT,
|
||||||
|
DISABLE_FOR_NOT_DIRECTX,
|
||||||
|
DISABLE_FOR_DIRECTX,
|
||||||
|
DISABLE_FOR_MATCH_REFRESH_RATE_ON,
|
||||||
|
DISABLE_FOR_ADVANCED_RESOLUTION_ON,
|
||||||
|
DISABLE_FOR_VERTICAL_RES_TOGGLE_ON,
|
||||||
|
DISABLE_FOR_LOW_RES_MODE_ON,
|
||||||
|
//DISABLE_FOR_MULTIPLAYER_CONNECTED,
|
||||||
|
} DisableOption;
|
||||||
|
|
||||||
|
struct WidgetInfo;
|
||||||
|
struct disabledInfo;
|
||||||
|
using VoidFunc = void (*)();
|
||||||
|
using DisableInfoFunc = bool (*)(disabledInfo&);
|
||||||
|
using DisableVec = std::vector<DisableOption>;
|
||||||
|
using WidgetFunc = void (*)(WidgetInfo&);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WIDGET_CHECKBOX,
|
||||||
|
WIDGET_COMBOBOX,
|
||||||
|
WIDGET_SLIDER_INT,
|
||||||
|
WIDGET_SLIDER_FLOAT,
|
||||||
|
WIDGET_CVAR_CHECKBOX,
|
||||||
|
WIDGET_CVAR_COMBOBOX,
|
||||||
|
WIDGET_CVAR_SLIDER_INT,
|
||||||
|
WIDGET_CVAR_SLIDER_FLOAT,
|
||||||
|
WIDGET_BUTTON,
|
||||||
|
WIDGET_COLOR_24, // color picker without alpha
|
||||||
|
WIDGET_COLOR_32, // color picker with alpha
|
||||||
|
WIDGET_SEARCH,
|
||||||
|
WIDGET_SEPARATOR,
|
||||||
|
WIDGET_SEPARATOR_TEXT,
|
||||||
|
WIDGET_TEXT,
|
||||||
|
WIDGET_WINDOW_BUTTON,
|
||||||
|
WIDGET_AUDIO_BACKEND, // needed for special operations that can't be handled easily with the normal combobox widget
|
||||||
|
WIDGET_VIDEO_BACKEND, // same as above
|
||||||
|
WIDGET_CUSTOM,
|
||||||
|
} WidgetType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SECTION_COLUMN_1,
|
||||||
|
SECTION_COLUMN_2,
|
||||||
|
SECTION_COLUMN_3,
|
||||||
|
} SectionColumns;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MOTION_BLUR_DYNAMIC,
|
||||||
|
MOTION_BLUR_ALWAYS_OFF,
|
||||||
|
MOTION_BLUR_ALWAYS_ON,
|
||||||
|
} MotionBlurOption;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DEBUG_LOG_TRACE,
|
||||||
|
DEBUG_LOG_DEBUG,
|
||||||
|
DEBUG_LOG_INFO,
|
||||||
|
DEBUG_LOG_WARN,
|
||||||
|
DEBUG_LOG_ERROR,
|
||||||
|
DEBUG_LOG_CRITICAL,
|
||||||
|
DEBUG_LOG_OFF,
|
||||||
|
} DebugLogOption;
|
||||||
|
|
||||||
|
// holds the widget values for a widget, contains all CVar types available from LUS. int32_t is used for boolean
|
||||||
|
// evaluation
|
||||||
|
using CVarVariant = std::variant<int32_t, const char*, float, Color_RGBA8, Color_RGB8>;
|
||||||
|
using OptionsVariant =
|
||||||
|
std::variant<UIWidgets::ButtonOptions, UIWidgets::CheckboxOptions, UIWidgets::ComboboxOptions,
|
||||||
|
UIWidgets::FloatSliderOptions, UIWidgets::IntSliderOptions, UIWidgets::WidgetOptions>;
|
||||||
|
|
||||||
|
// All the info needed for display and search of all widgets in the menu.
|
||||||
|
// `name` is the label displayed,
|
||||||
|
// `cVar` is the string representation of the CVar used to store the widget value
|
||||||
|
// `tooltip` is what is displayed when hovering (except when disabled, more on that later)
|
||||||
|
// `type` is the WidgetType for the widget, which is what determines how the information is used in the draw func
|
||||||
|
// `options` is a variant that holds the UIWidgetsOptions struct for the widget type
|
||||||
|
// blank objects need to be initialized with specific typing matching the expected Options struct for the widget
|
||||||
|
// `callback` is a lambda used for running code on widget change. may need `BenGui::GetMenu()` for specific menu actions
|
||||||
|
// `preFunc` is a lambda called before drawing code starts. It can be used to determine a widget's status,
|
||||||
|
// whether disabled or hidden, as well as update pointers for non-CVar widget types.
|
||||||
|
// `postFunc` is a lambda called after all drawing code is finished, for reacting to states other than
|
||||||
|
// widgets having been changed, like holding buttons.
|
||||||
|
// All three lambdas accept a `widgetInfo` reference in case it needs information on the widget for these operations
|
||||||
|
// `activeDisables` is a vector of DisableOptions for specifying what reasons a widget is disabled, which are displayed
|
||||||
|
// in the disabledTooltip for the widget. Can display multiple reasons. Handling the reasons is done in `preFunc`.
|
||||||
|
// It is recommended to utilize `disabledInfo`/`DisableReason` to list out all reasons for disabling and isHidden so
|
||||||
|
// the info can be shown.
|
||||||
|
// `windowName` is what is displayed and searched for `windowButton` type and window interactions
|
||||||
|
// `isHidden` just prevents the widget from being drawn under whatever circumstances you specify in the `preFunc`
|
||||||
|
// `sameLine` allows for specifying that the widget should be on the same line as the previous widget
|
||||||
|
struct WidgetInfo {
|
||||||
|
std::string name; // Used by all widgets
|
||||||
|
const char* cVar; // Used by all widgets except
|
||||||
|
WidgetType type;
|
||||||
|
std::shared_ptr<UIWidgets::WidgetOptions> options;
|
||||||
|
std::variant<bool*, int32_t*, float*> valuePointer;
|
||||||
|
WidgetFunc callback = nullptr;
|
||||||
|
WidgetFunc preFunc = nullptr;
|
||||||
|
WidgetFunc postFunc = nullptr;
|
||||||
|
WidgetFunc customFunction = nullptr;
|
||||||
|
DisableVec activeDisables = {};
|
||||||
|
const char* windowName = "";
|
||||||
|
bool isHidden = false;
|
||||||
|
bool sameLine = false;
|
||||||
|
|
||||||
|
WidgetInfo& CVar(const char* cVar_) {
|
||||||
|
cVar = cVar_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetInfo& Options(OptionsVariant options_) {
|
||||||
|
switch (type) {
|
||||||
|
case WIDGET_AUDIO_BACKEND:
|
||||||
|
case WIDGET_VIDEO_BACKEND:
|
||||||
|
case WIDGET_COMBOBOX:
|
||||||
|
case WIDGET_CVAR_COMBOBOX:
|
||||||
|
options = std::make_shared<UIWidgets::ComboboxOptions>(std::get<UIWidgets::ComboboxOptions>(options_));
|
||||||
|
break;
|
||||||
|
case WIDGET_CHECKBOX:
|
||||||
|
case WIDGET_CVAR_CHECKBOX:
|
||||||
|
options = std::make_shared<UIWidgets::CheckboxOptions>(std::get<UIWidgets::CheckboxOptions>(options_));
|
||||||
|
break;
|
||||||
|
case WIDGET_SLIDER_FLOAT:
|
||||||
|
case WIDGET_CVAR_SLIDER_FLOAT:
|
||||||
|
options =
|
||||||
|
std::make_shared<UIWidgets::FloatSliderOptions>(std::get<UIWidgets::FloatSliderOptions>(options_));
|
||||||
|
break;
|
||||||
|
case WIDGET_SLIDER_INT:
|
||||||
|
case WIDGET_CVAR_SLIDER_INT:
|
||||||
|
options =
|
||||||
|
std::make_shared<UIWidgets::IntSliderOptions>(std::get<UIWidgets::IntSliderOptions>(options_));
|
||||||
|
break;
|
||||||
|
case WIDGET_BUTTON:
|
||||||
|
case WIDGET_WINDOW_BUTTON:
|
||||||
|
options = std::make_shared<UIWidgets::ButtonOptions>(std::get<UIWidgets::ButtonOptions>(options_));
|
||||||
|
break;
|
||||||
|
case WIDGET_CUSTOM:
|
||||||
|
case WIDGET_TEXT:
|
||||||
|
case WIDGET_SEPARATOR_TEXT:
|
||||||
|
case WIDGET_SEPARATOR:
|
||||||
|
default:
|
||||||
|
options = std::make_shared<UIWidgets::WidgetOptions>(std::get<UIWidgets::WidgetOptions>(options_));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
void ResetDisables() {
|
||||||
|
isHidden = false;
|
||||||
|
options->disabled = false;
|
||||||
|
options->disabledTooltip = "";
|
||||||
|
activeDisables.clear();
|
||||||
|
}
|
||||||
|
WidgetInfo& Options(std::shared_ptr<UIWidgets::WidgetOptions> options_) {
|
||||||
|
options = options_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetInfo& Callback(WidgetFunc callback_) {
|
||||||
|
callback = callback_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetInfo& PreFunc(WidgetFunc preFunc_) {
|
||||||
|
preFunc = preFunc_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetInfo& PostFunc(WidgetFunc postFunc_) {
|
||||||
|
postFunc = postFunc_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetInfo& WindowName(const char* windowName_) {
|
||||||
|
windowName = windowName_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetInfo& ValuePointer(std::variant<bool*, int32_t*, float*> valuePointer_) {
|
||||||
|
valuePointer = valuePointer_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetInfo& SameLine(bool sameLine_) {
|
||||||
|
sameLine = sameLine_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetInfo& CustomFunction(WidgetFunc customFunction_) {
|
||||||
|
customFunction = customFunction_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WidgetPath {
|
||||||
|
std::string sectionName;
|
||||||
|
std::string sidebarName;
|
||||||
|
SectionColumns column;
|
||||||
|
};
|
||||||
|
|
||||||
|
// `disabledInfo` holds information on reasons for hiding or disabling a widget, as well as an evaluation lambda that
|
||||||
|
// is run once per frame to update its status (this is done to prevent dozens of redundant CVarGets in each frame loop)
|
||||||
|
// `evaluation` returns a bool which can be determined by whatever code you want that changes its status
|
||||||
|
// `reason` is the text displayed in the disabledTooltip when a widget is disabled by a particular DisableReason
|
||||||
|
// `active` is what's referenced when determining disabled status for a widget that uses this This can also be used to
|
||||||
|
// hold reasons to hide widgets so that their evaluations are also only run once per frame
|
||||||
|
struct disabledInfo {
|
||||||
|
DisableInfoFunc evaluation;
|
||||||
|
const char* reason;
|
||||||
|
bool active = false;
|
||||||
|
int32_t value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// struct Sidebar {
|
||||||
|
// //std::unordered_map<std::string, SidebarEntry> entries;
|
||||||
|
// uint32_t columnCount;
|
||||||
|
// std::vector<std::vector<WidgetInfo>> columnWidgets;
|
||||||
|
//
|
||||||
|
// void Insert(std::string entryName, WidgetInfo& entry, int32_t index = -1) {
|
||||||
|
// if (index == -1 || index >= entryOrder.size()) {
|
||||||
|
// entryOrder.push_back(entryName);
|
||||||
|
// } else {
|
||||||
|
// entryOrder.insert(entryOrder.begin() + index, entryName);
|
||||||
|
// }
|
||||||
|
// columnWidgets[entryName].push_back({entry});
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void Erase(std::string entryName) {
|
||||||
|
// if (columnWidgets.contains(entryName)) {
|
||||||
|
// columnWidgets.erase(entryName);
|
||||||
|
// }
|
||||||
|
// std::erase_if(entryOrder, [entryName](std::string name) { return name == entryName; });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// Contains the name displayed in the sidebar (label), the number of columns to use in drawing (columnCount; for visual
|
||||||
|
// separation, 1-3), and nested vectors of the widgets, grouped by column (columnWidgets). The number of widget vectors
|
||||||
|
// added to the column groups does not need to match the specified columnCount, e.g. you can have one vector added to
|
||||||
|
// the sidebar, but still separate the window into 3 columns and display only in the first column
|
||||||
|
struct SidebarEntry {
|
||||||
|
uint32_t columnCount;
|
||||||
|
std::vector<std::vector<WidgetInfo>> columnWidgets;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Contains entries for what's listed in the header at the top, including the name displayed on the top bar (label),
|
||||||
|
// a vector of the SidebarEntries for that header entry, and the name of the cvar used to track what sidebar entry is
|
||||||
|
// the last viewed for that header.
|
||||||
|
struct MainMenuEntry {
|
||||||
|
std::string label;
|
||||||
|
const char* sidebarCvar;
|
||||||
|
std::unordered_map<std::string, SidebarEntry> sidebars = {};
|
||||||
|
std::vector<std::string> sidebarOrder = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::unordered_map<Ship::AudioBackend, const char*> audioBackendsMap = {
|
||||||
|
{ Ship::AudioBackend::WASAPI, "Windows Audio Session API" },
|
||||||
|
{ Ship::AudioBackend::SDL, "SDL" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::unordered_map<Ship::WindowBackend, const char*> windowBackendsMap = {
|
||||||
|
{ Ship::WindowBackend::FAST3D_DXGI_DX11, "DirectX" },
|
||||||
|
{ Ship::WindowBackend::FAST3D_SDL_OPENGL, "OpenGL" },
|
||||||
|
{ Ship::WindowBackend::FAST3D_SDL_METAL, "Metal" },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MenuInit {
|
||||||
|
static std::vector<std::function<void()>>& GetInitFuncs() {
|
||||||
|
static std::vector<std::function<void()>> menuInitFuncs;
|
||||||
|
return menuInitFuncs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, std::unordered_map<std::string, std::vector<std::function<void()>>>>&
|
||||||
|
GetUpdateFuncs() {
|
||||||
|
static std::unordered_map<std::string, std::unordered_map<std::string, std::vector<std::function<void()>>>>
|
||||||
|
menuUpdateFuncs;
|
||||||
|
return menuUpdateFuncs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitAll() {
|
||||||
|
auto& menuInitFuncs = MenuInit::GetInitFuncs();
|
||||||
|
for (const auto& initFunc : menuInitFuncs) {
|
||||||
|
initFunc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RegisterMenuInitFunc {
|
||||||
|
RegisterMenuInitFunc(std::function<void()> initFunc) {
|
||||||
|
auto& menuInitFuncs = MenuInit::GetInitFuncs();
|
||||||
|
|
||||||
|
menuInitFuncs.push_back(initFunc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RegisterMenuUpdateFunc {
|
||||||
|
RegisterMenuUpdateFunc(std::function<void()> updateFunc, std::string sectionName, std::string sidebarName) {
|
||||||
|
auto& menuUpdateFuncs = MenuInit::GetUpdateFuncs();
|
||||||
|
|
||||||
|
menuUpdateFuncs[sectionName][sidebarName].push_back(updateFunc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MENUTYPES_H
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "MultiplayerWindow.h"
|
#include "MultiplayerWindow.h"
|
||||||
#include "UIWidgets.h"
|
#include "UIWidgets.h"
|
||||||
|
#include "PortMenu.h"
|
||||||
#include "libultraship/src/Context.h"
|
#include "libultraship/src/Context.h"
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
@ -9,7 +10,7 @@
|
||||||
#include <defines.h>
|
#include <defines.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "../../../src/main.h"
|
#include "main.h"
|
||||||
#include "networking/networking.h"
|
#include "networking/networking.h"
|
||||||
#include "actor_types.h"
|
#include "actor_types.h"
|
||||||
#include "code_800029B0.h"
|
#include "code_800029B0.h"
|
||||||
|
|
@ -21,135 +22,145 @@ extern Camera cameras[];
|
||||||
extern UnkStruct_800DC5EC* D_800DC5EC;
|
extern UnkStruct_800DC5EC* D_800DC5EC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameUI {
|
||||||
|
extern std::shared_ptr<PortMenu> mPortMenu;
|
||||||
namespace Multiplayer {
|
namespace Multiplayer {
|
||||||
MultiplayerWindow::~MultiplayerWindow() {
|
|
||||||
SPDLOG_TRACE("destruct multiplayer window");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiplayerWindow::InitElement() {
|
|
||||||
}
|
|
||||||
|
|
||||||
static s32 sConnectedBool = false;
|
static s32 sConnectedBool = false;
|
||||||
static s32 sReadyUpBool = false;
|
static s32 sReadyUpBool = false;
|
||||||
static s32 sCharacter = 0;
|
static s32 sCharacter = 0;
|
||||||
static s32 sCup = 0;
|
static s32 sCup = 0;
|
||||||
|
|
||||||
void MultiplayerWindow::DrawElement() {
|
void DrawColumn1(WidgetInfo& info) {
|
||||||
const float framerate = ImGui::GetIO().Framerate;
|
if (gNetwork.isConnected) {
|
||||||
const float deltatime = ImGui::GetIO().DeltaTime;
|
ImGui::BeginDisabled();
|
||||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
|
||||||
|
|
||||||
if (!gNetwork.isConnected) {
|
|
||||||
|
|
||||||
static char username[NETWORK_USERNAME_LENGTH] = "TestUser";
|
|
||||||
|
|
||||||
ImGui::Text("Username:");
|
|
||||||
ImGui::InputText("##edit", username, IM_ARRAYSIZE(username));
|
|
||||||
|
|
||||||
static char address[32] = "127.0.0.1:64010";
|
|
||||||
static char addressCopy[32];
|
|
||||||
strncpy(addressCopy, address, sizeof(addressCopy));
|
|
||||||
addressCopy[sizeof(addressCopy) - 1] = '\0'; // Ensure null termination
|
|
||||||
|
|
||||||
ImGui::Text("Host:");
|
|
||||||
ImGui::InputText("##edit3", addressCopy, IM_ARRAYSIZE(addressCopy));
|
|
||||||
|
|
||||||
if (ImGui::Button("Connect!")) {
|
|
||||||
char* ip = NULL;
|
|
||||||
uint16_t port = 0;
|
|
||||||
|
|
||||||
char* token = strtok(addressCopy, ":");
|
|
||||||
ip = token;
|
|
||||||
|
|
||||||
token = strtok(NULL, ":");
|
|
||||||
if (token != NULL) {
|
|
||||||
port = (uint16_t) atoi(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
gNetwork.enabled = true;
|
|
||||||
ConnectToServer(ip, port, username);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Text("Select Character:");
|
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < NUM_PLAYERS; i++) {
|
|
||||||
// These are euc-jp characters that look sort of like a hyphen -
|
|
||||||
if (D_800E76A8[i] == NULL || D_800E76A8[i] == "\xA1\xBC\xA1\xBC\xA1\xBC\xA1\xBC") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button(D_800E76A8[i])) {
|
|
||||||
network_character_vote(i);
|
|
||||||
sCharacter = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Text("-- %s --", D_800E76A8[sCharacter]);
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Text("Course Vote:");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < NUM_CUPS - 1; i++) {
|
|
||||||
if (ImGui::Button(gCupNames[i])) {
|
|
||||||
network_cup_vote(i);
|
|
||||||
sCup = i;
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
}
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Text("-- %s --", gCupNames[sCup]);
|
|
||||||
|
|
||||||
if (ImGui::Button("test", ImVec2(-1, 0))) {
|
|
||||||
// Add disconnect logic here
|
|
||||||
cameras[0].playerId = 2;
|
|
||||||
D_800DC5EC->player = &gPlayers[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < NETWORK_MAX_PLAYERS; i++) {
|
|
||||||
ImGui::Text("Slot %d: char: %d, hasAuthority: %d", clients[i].slot, clients[i].character,
|
|
||||||
gPlayers[i].nHasAuthority);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stick UI to the bottom of the panel */
|
|
||||||
|
|
||||||
float windowHeight = ImGui::GetWindowHeight();
|
|
||||||
|
|
||||||
// Move the cursor to a position relative to the bottom of the window
|
|
||||||
ImGui::SetCursorPosY(windowHeight -
|
|
||||||
ImGui::GetFrameHeightWithSpacing() * 2); // Adjust Y position for two buttons
|
|
||||||
|
|
||||||
char buttonLabel[32];
|
|
||||||
|
|
||||||
if (sReadyUpBool) {
|
|
||||||
strcpy(buttonLabel, "Lets Go!");
|
|
||||||
} else {
|
|
||||||
strcpy(buttonLabel, "Ready Up!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// First button (Ready Up / Let's Go)
|
|
||||||
if (ImGui::Button(buttonLabel, ImVec2(-1, 0))) {
|
|
||||||
sReadyUpBool = !sReadyUpBool;
|
|
||||||
networking_ready_up(sReadyUpBool);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add some vertical spacing between the buttons
|
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
// Second button (Disconnect)
|
|
||||||
if (ImGui::Button("Disconnect", ImVec2(-1, 0))) {
|
|
||||||
// Add disconnect logic here
|
|
||||||
networking_disconnect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopStyleColor();
|
static char username[NETWORK_USERNAME_LENGTH] = "TestUser";
|
||||||
|
|
||||||
|
ImGui::Text("Username:");
|
||||||
|
ImGui::InputText("##edit", username, IM_ARRAYSIZE(username));
|
||||||
|
|
||||||
|
static char address[32] = "127.0.0.1:64010";
|
||||||
|
static char addressCopy[32];
|
||||||
|
strncpy(addressCopy, address, sizeof(addressCopy));
|
||||||
|
addressCopy[sizeof(addressCopy) - 1] = '\0'; // Ensure null termination
|
||||||
|
|
||||||
|
ImGui::Text("Host:");
|
||||||
|
ImGui::InputText("##edit3", addressCopy, IM_ARRAYSIZE(addressCopy));
|
||||||
|
|
||||||
|
if (ImGui::Button("Connect!")) {
|
||||||
|
char* ip = NULL;
|
||||||
|
uint16_t port = 0;
|
||||||
|
|
||||||
|
char* token = strtok(addressCopy, ":");
|
||||||
|
ip = token;
|
||||||
|
|
||||||
|
token = strtok(NULL, ":");
|
||||||
|
if (token != NULL) {
|
||||||
|
port = (uint16_t) atoi(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
gNetwork.enabled = true;
|
||||||
|
ConnectToServer(ip, port, username);
|
||||||
|
}
|
||||||
|
if (gNetwork.isConnected) {
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiplayerWindow::UpdateElement() {
|
void DrawColumn2(WidgetInfo& info) {
|
||||||
|
if (!gNetwork.isConnected) {
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Select Character:");
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NUM_PLAYERS; i++) {
|
||||||
|
// These are euc-jp characters that look sort of like a hyphen -
|
||||||
|
if (D_800E76A8[i] == NULL || D_800E76A8[i] == "\xA1\xBC\xA1\xBC\xA1\xBC\xA1\xBC") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(D_800E76A8[i])) {
|
||||||
|
network_character_vote(i);
|
||||||
|
sCharacter = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Text("-- %s --", D_800E76A8[sCharacter]);
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Text("Course Vote:");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NUM_CUPS - 1; i++) {
|
||||||
|
if (ImGui::Button(gCupNames[i])) {
|
||||||
|
network_cup_vote(i);
|
||||||
|
sCup = i;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Text("-- %s --", gCupNames[sCup]);
|
||||||
|
|
||||||
|
if (ImGui::Button("test", ImVec2(-1, 0))) {
|
||||||
|
// Add disconnect logic here
|
||||||
|
cameras[0].playerId = 2;
|
||||||
|
D_800DC5EC->player = &gPlayers[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NETWORK_MAX_PLAYERS; i++) {
|
||||||
|
ImGui::Text("Slot %d: char: %d, hasAuthority: %d", clients[i].slot, clients[i].character,
|
||||||
|
gPlayers[i].nHasAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stick UI to the bottom of the panel */
|
||||||
|
|
||||||
|
float windowHeight = ImGui::GetWindowHeight();
|
||||||
|
|
||||||
|
// Move the cursor to a position relative to the bottom of the window
|
||||||
|
ImGui::SetCursorPosY(windowHeight -
|
||||||
|
ImGui::GetFrameHeightWithSpacing() * 2); // Adjust Y position for two buttons
|
||||||
|
|
||||||
|
char buttonLabel[32];
|
||||||
|
|
||||||
|
if (sReadyUpBool) {
|
||||||
|
strcpy(buttonLabel, "Lets Go!");
|
||||||
|
} else {
|
||||||
|
strcpy(buttonLabel, "Ready Up!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// First button (Ready Up / Let's Go)
|
||||||
|
if (ImGui::Button(buttonLabel, ImVec2(-1, 0))) {
|
||||||
|
sReadyUpBool = !sReadyUpBool;
|
||||||
|
networking_ready_up(sReadyUpBool);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some vertical spacing between the buttons
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
// Second button (Disconnect)
|
||||||
|
if (ImGui::Button("Disconnect", ImVec2(-1, 0))) {
|
||||||
|
// Add disconnect logic here
|
||||||
|
networking_disconnect();
|
||||||
|
}
|
||||||
|
if (!gNetwork.isConnected) {
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RegisterMultiplayerWidgets() {
|
||||||
|
mPortMenu->AddSidebarEntry("Enhancements", "Multiplayer", 2);
|
||||||
|
WidgetPath path = { "Enhancements", "Multiplayer", SECTION_COLUMN_1 };
|
||||||
|
mPortMenu->AddWidget(path, "Multi Custom 1", WIDGET_CUSTOM)
|
||||||
|
.CustomFunction(DrawColumn1);
|
||||||
|
path.column = SECTION_COLUMN_2;
|
||||||
|
mPortMenu->AddWidget(path, "Multi Custom 2", WIDGET_CUSTOM)
|
||||||
|
.CustomFunction(DrawColumn2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RegisterMenuInitFunc initFunc(RegisterMultiplayerWidgets);
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
} // namespace GameUI
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,4 @@
|
||||||
#include <libultraship/libultraship.h>
|
#include <libultraship/libultraship.h>
|
||||||
|
|
||||||
namespace Multiplayer {
|
namespace Multiplayer {
|
||||||
class MultiplayerWindow : public Ship::GuiWindow {
|
|
||||||
public:
|
|
||||||
using Ship::GuiWindow::GuiWindow;
|
|
||||||
~MultiplayerWindow();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void InitElement() override;
|
|
||||||
void DrawElement() override;
|
|
||||||
void UpdateElement() override;
|
|
||||||
};
|
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,497 @@
|
||||||
|
#include "PortMenu.h"
|
||||||
|
#include "UIWidgets.h"
|
||||||
|
#include "port/Game.h"
|
||||||
|
#include "window/gui/GuiMenuBar.h"
|
||||||
|
#include "window/gui/GuiElement.h"
|
||||||
|
#include <variant>
|
||||||
|
#include "StringHelper.h"
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
#include <variant>
|
||||||
|
#include <tuple>
|
||||||
|
#include "ResolutionEditor.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern s32 gGamestateNext;
|
||||||
|
extern s32 gMenuSelection;
|
||||||
|
#include "audio/external.h"
|
||||||
|
#include "defines.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GameUI {
|
||||||
|
extern std::shared_ptr<PortMenu> mPortMenu;
|
||||||
|
|
||||||
|
using namespace UIWidgets;
|
||||||
|
|
||||||
|
void PortMenu::AddSidebarEntry(std::string sectionName, std::string sidebarName, uint32_t columnCount) {
|
||||||
|
assert(!sectionName.empty());
|
||||||
|
assert(!sidebarName.empty());
|
||||||
|
menuEntries.at(sectionName).sidebars.emplace(sidebarName, SidebarEntry{ .columnCount = columnCount });
|
||||||
|
menuEntries.at(sectionName).sidebarOrder.push_back(sidebarName);
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetInfo& PortMenu::AddWidget(WidgetPath& pathInfo, std::string widgetName, WidgetType widgetType) {
|
||||||
|
assert(!widgetName.empty()); // Must be unique
|
||||||
|
assert(menuEntries.contains(pathInfo.sectionName)); // Section/header must already exist
|
||||||
|
assert(menuEntries.at(pathInfo.sectionName).sidebars.contains(pathInfo.sidebarName)); // Sidebar must already exist
|
||||||
|
std::unordered_map<std::string, SidebarEntry>& sidebar = menuEntries.at(pathInfo.sectionName).sidebars;
|
||||||
|
uint8_t column = pathInfo.column;
|
||||||
|
if (sidebar.contains(pathInfo.sidebarName)) {
|
||||||
|
while (sidebar.at(pathInfo.sidebarName).columnWidgets.size() < column + 1) {
|
||||||
|
sidebar.at(pathInfo.sidebarName).columnWidgets.push_back({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SidebarEntry& entry = sidebar.at(pathInfo.sidebarName);
|
||||||
|
entry.columnWidgets.at(column).push_back({ .name = widgetName, .type = widgetType });
|
||||||
|
WidgetInfo& widget = entry.columnWidgets.at(column).back();
|
||||||
|
switch (widgetType) {
|
||||||
|
case WIDGET_CHECKBOX:
|
||||||
|
case WIDGET_CVAR_CHECKBOX:
|
||||||
|
widget.options = std::make_shared<CheckboxOptions>();
|
||||||
|
break;
|
||||||
|
case WIDGET_SLIDER_FLOAT:
|
||||||
|
case WIDGET_CVAR_SLIDER_FLOAT:
|
||||||
|
widget.options = std::make_shared<FloatSliderOptions>();
|
||||||
|
break;
|
||||||
|
case WIDGET_SLIDER_INT:
|
||||||
|
case WIDGET_CVAR_SLIDER_INT:
|
||||||
|
widget.options = std::make_shared<IntSliderOptions>();
|
||||||
|
break;
|
||||||
|
case WIDGET_COMBOBOX:
|
||||||
|
case WIDGET_CVAR_COMBOBOX:
|
||||||
|
case WIDGET_AUDIO_BACKEND:
|
||||||
|
case WIDGET_VIDEO_BACKEND:
|
||||||
|
widget.options = std::make_shared<ComboboxOptions>();
|
||||||
|
break;
|
||||||
|
case WIDGET_BUTTON:
|
||||||
|
widget.options = std::make_shared<ButtonOptions>();
|
||||||
|
break;
|
||||||
|
case WIDGET_WINDOW_BUTTON:
|
||||||
|
widget.options = std::make_shared<ButtonOptions>(ButtonOptions{ .size = Sizes::Inline });
|
||||||
|
break;
|
||||||
|
case WIDGET_COLOR_24:
|
||||||
|
case WIDGET_COLOR_32:
|
||||||
|
break;
|
||||||
|
case WIDGET_SEARCH:
|
||||||
|
case WIDGET_SEPARATOR:
|
||||||
|
case WIDGET_SEPARATOR_TEXT:
|
||||||
|
case WIDGET_TEXT:
|
||||||
|
default:
|
||||||
|
widget.options = std::make_shared<WidgetOptions>();
|
||||||
|
}
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortMenu::AddSettings() {
|
||||||
|
// Add Settings menu
|
||||||
|
AddMenuEntry("Settings", "gSettings.Menu.SettingsSidebarSection");
|
||||||
|
// General Settings
|
||||||
|
AddSidebarEntry("Settings", "General", 3);
|
||||||
|
WidgetPath path = { "Settings", "General", SECTION_COLUMN_1 };
|
||||||
|
AddWidget(path, "Menu Theme", WIDGET_CVAR_COMBOBOX)
|
||||||
|
.CVar("gSettings.Menu.Theme")
|
||||||
|
.Options(ComboboxOptions()
|
||||||
|
.Tooltip("Changes the Theme of the Menu Widgets.")
|
||||||
|
.ComboMap(menuThemeOptions)
|
||||||
|
.DefaultIndex(Colors::LightBlue));
|
||||||
|
#if not defined(__SWITCH__) and not defined(__WIIU__)
|
||||||
|
AddWidget(path, "Menu Controller Navigation", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar(CVAR_IMGUI_CONTROLLER_NAV)
|
||||||
|
.Options(CheckboxOptions().Tooltip(
|
||||||
|
"Allows controller navigation of the Spaghetti menu (Settings, Enhancements,...)\nCAUTION: "
|
||||||
|
"This will disable game inputs while the menu is visible.\n\nD-pad to move between "
|
||||||
|
"items, A to select, B to move up in scope."));
|
||||||
|
AddWidget(path, "Cursor Always Visible", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gSettings.CursorVisibility")
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->SetForceCursorVisibility(
|
||||||
|
CVarGetInteger("gSettings.CursorVisibility", 0));
|
||||||
|
})
|
||||||
|
.Options(CheckboxOptions().Tooltip("Makes the cursor always visible, even in full screen."));
|
||||||
|
#endif
|
||||||
|
AddWidget(path, "Search In Sidebar", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gSettings.Menu.SidebarSearch")
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
if (CVarGetInteger("gSettings.Menu.SidebarSearch", 0)) {
|
||||||
|
mPortMenu->InsertSidebarSearch();
|
||||||
|
} else {
|
||||||
|
mPortMenu->RemoveSidebarSearch();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(CheckboxOptions().Tooltip(
|
||||||
|
"Displays the Search menu as a sidebar entry in Settings instead of in the header."));
|
||||||
|
AddWidget(path, "Search Input Autofocus", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gSettings.Menu.SearchAutofocus")
|
||||||
|
.Options(CheckboxOptions().Tooltip(
|
||||||
|
"Search input box gets autofocus when visible. Does not affect using other widgets."));
|
||||||
|
AddWidget(path, "Alt Assets Tab hotkey", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gEnhancements.Mods.AlternateAssetsHotkey")
|
||||||
|
.Options(
|
||||||
|
CheckboxOptions().Tooltip("Allows pressing the Tab key to toggle alternate assets").DefaultValue(true));
|
||||||
|
AddWidget(path, "Open App Files Folder", WIDGET_BUTTON)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
std::string filesPath = Ship::Context::GetInstance()->GetAppDirectoryPath();
|
||||||
|
SDL_OpenURL(std::string("file:///" + std::filesystem::absolute(filesPath).string()).c_str());
|
||||||
|
})
|
||||||
|
.Options(ButtonOptions().Tooltip("Opens the folder that contains the save and mods folders, etc."));
|
||||||
|
|
||||||
|
// Audio Settings
|
||||||
|
path.sidebarName = "Audio";
|
||||||
|
AddSidebarEntry("Settings", "Audio", 3);
|
||||||
|
AddWidget(path, "Master Volume: %.0f%%", WIDGET_CVAR_SLIDER_FLOAT)
|
||||||
|
.CVar("gGameMasterVolume")
|
||||||
|
.Options(FloatSliderOptions()
|
||||||
|
.Tooltip("Adjust the overall sound volume.")
|
||||||
|
.ShowButtons(false)
|
||||||
|
.Format("")
|
||||||
|
.IsPercentage());
|
||||||
|
AddWidget(path, "Main Music Volume: %.0f%%", WIDGET_CVAR_SLIDER_FLOAT)
|
||||||
|
.CVar("gMainMusicVolume")
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
audio_set_player_volume(SEQ_PLAYER_LEVEL, CVarGetFloat("gMainMusicVolume", 1.0f));
|
||||||
|
})
|
||||||
|
.Options(FloatSliderOptions()
|
||||||
|
.Tooltip("Adjust the background music volume.")
|
||||||
|
.ShowButtons(false)
|
||||||
|
.Format("")
|
||||||
|
.IsPercentage());
|
||||||
|
AddWidget(path, "Sound Effects Volume: %.0f%%", WIDGET_CVAR_SLIDER_FLOAT)
|
||||||
|
.CVar("gSFXMusicVolume")
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
audio_set_player_volume(SEQ_PLAYER_SFX, CVarGetFloat("gSFXMusicVolume", 1.0f));
|
||||||
|
})
|
||||||
|
.Options(
|
||||||
|
FloatSliderOptions().Tooltip("Adjust the sound effects volume.").ShowButtons(false).Format("").IsPercentage());
|
||||||
|
AddWidget(path, "Sound Effects Volume: %.0f%%", WIDGET_CVAR_SLIDER_FLOAT)
|
||||||
|
.CVar("gEnvironmentVolume")
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
audio_set_player_volume(SEQ_PLAYER_ENV, CVarGetFloat("gEnvironmentVolume", 1.0f));
|
||||||
|
})
|
||||||
|
.Options(FloatSliderOptions()
|
||||||
|
.Tooltip("Adjust the environment volume.")
|
||||||
|
.ShowButtons(false)
|
||||||
|
.Format("")
|
||||||
|
.IsPercentage());
|
||||||
|
AddWidget(path, "Audio API", WIDGET_AUDIO_BACKEND);
|
||||||
|
|
||||||
|
// Graphics Settings
|
||||||
|
static int32_t maxFps;
|
||||||
|
static bool isFullscreen = false;
|
||||||
|
const char* tooltip = "";
|
||||||
|
if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
||||||
|
maxFps = 360;
|
||||||
|
tooltip = "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is "
|
||||||
|
"purely visual and does not impact game logic, execution of glitches etc.\n\nA higher target "
|
||||||
|
"FPS than your monitor's refresh rate will waste resources, and might give a worse result.";
|
||||||
|
} else {
|
||||||
|
maxFps = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
|
||||||
|
tooltip = "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is "
|
||||||
|
"purely visual and does not impact game logic, execution of glitches etc.";
|
||||||
|
}
|
||||||
|
path.sidebarName = "Graphics";
|
||||||
|
AddSidebarEntry("Settings", "Graphics", 3);
|
||||||
|
AddWidget(path, "Toggle Fullscreen", WIDGET_CHECKBOX)
|
||||||
|
.ValuePointer(&isFullscreen)
|
||||||
|
.PreFunc([](WidgetInfo& info) { isFullscreen = Ship::Context::GetInstance()->GetWindow()->IsFullscreen(); })
|
||||||
|
.Callback([](WidgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->ToggleFullscreen(); })
|
||||||
|
.Options(CheckboxOptions().Tooltip("Toggles Fullscreen On/Off."));
|
||||||
|
#ifndef __APPLE__
|
||||||
|
AddWidget(path, "Internal Resolution: %.0f%%", WIDGET_CVAR_SLIDER_FLOAT)
|
||||||
|
.CVar(CVAR_INTERNAL_RESOLUTION)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(
|
||||||
|
CVarGetFloat(CVAR_INTERNAL_RESOLUTION, 1));
|
||||||
|
})
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
if (mPortMenu->disabledMap.at(DISABLE_FOR_ADVANCED_RESOLUTION_ON).active &&
|
||||||
|
mPortMenu->disabledMap.at(DISABLE_FOR_VERTICAL_RES_TOGGLE_ON).active) {
|
||||||
|
info.activeDisables.push_back(DISABLE_FOR_ADVANCED_RESOLUTION_ON);
|
||||||
|
info.activeDisables.push_back(DISABLE_FOR_VERTICAL_RES_TOGGLE_ON);
|
||||||
|
} else if (mPortMenu->disabledMap.at(DISABLE_FOR_LOW_RES_MODE_ON).active) {
|
||||||
|
info.activeDisables.push_back(DISABLE_FOR_LOW_RES_MODE_ON);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Options(
|
||||||
|
FloatSliderOptions()
|
||||||
|
.Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective "
|
||||||
|
"form of anti-aliasing.")
|
||||||
|
.ShowButtons(false)
|
||||||
|
.IsPercentage()
|
||||||
|
.Format("")
|
||||||
|
.Min(0.5f)
|
||||||
|
.Max(2.0f));
|
||||||
|
#endif
|
||||||
|
#ifndef __WIIU__
|
||||||
|
AddWidget(path, "Anti-aliasing (MSAA): %d", WIDGET_CVAR_SLIDER_INT)
|
||||||
|
.CVar(CVAR_MSAA_VALUE)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger(CVAR_MSAA_VALUE, 1));
|
||||||
|
})
|
||||||
|
.Options(
|
||||||
|
IntSliderOptions()
|
||||||
|
.Tooltip("Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x, to smooth the edges of "
|
||||||
|
"rendered geometry.\n"
|
||||||
|
"Higher sample count will result in smoother edges on models, but may reduce performance.")
|
||||||
|
.Min(1)
|
||||||
|
.Max(8)
|
||||||
|
.DefaultValue(1));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AddWidget(path, "Current FPS: %d", WIDGET_CVAR_SLIDER_INT)
|
||||||
|
.CVar("gInterpolationFPS")
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
int32_t defaultValue = std::static_pointer_cast<IntSliderOptions>(info.options)->defaultValue;
|
||||||
|
if (CVarGetInteger(info.cVar, defaultValue) == defaultValue) {
|
||||||
|
info.name = "Current FPS: Original (%d)";
|
||||||
|
} else {
|
||||||
|
info.name = "Current FPS: %d";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
if (mPortMenu->disabledMap.at(DISABLE_FOR_MATCH_REFRESH_RATE_ON).active)
|
||||||
|
info.activeDisables.push_back(DISABLE_FOR_MATCH_REFRESH_RATE_ON);
|
||||||
|
})
|
||||||
|
.Options(IntSliderOptions().Tooltip(tooltip).Min(20).Max(maxFps).DefaultValue(20));
|
||||||
|
AddWidget(path, "Match Refresh Rate", WIDGET_BUTTON)
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
int hz = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
|
||||||
|
if (hz >= 20 && hz <= 360) {
|
||||||
|
CVarSetInteger("gInterpolationFPS", hz);
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.PreFunc([](WidgetInfo& info) { info.isHidden = mPortMenu->disabledMap.at(DISABLE_FOR_NOT_DIRECTX).active; })
|
||||||
|
.Options(ButtonOptions().Tooltip("Matches interpolation value to the current game's window refresh rate."));
|
||||||
|
AddWidget(path, "Match Refresh Rate", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gMatchRefreshRate")
|
||||||
|
.PreFunc([](WidgetInfo& info) { info.isHidden = mPortMenu->disabledMap.at(DISABLE_FOR_DIRECTX).active; })
|
||||||
|
.Options(CheckboxOptions().Tooltip("Matches interpolation value to the current game's window refresh rate."));
|
||||||
|
AddWidget(path, "Jitter fix : >= % d FPS", WIDGET_CVAR_SLIDER_INT)
|
||||||
|
.CVar("gExtraLatencyThreshold")
|
||||||
|
.PreFunc([](WidgetInfo& info) { info.isHidden = mPortMenu->disabledMap.at(DISABLE_FOR_NOT_DIRECTX).active; })
|
||||||
|
.Options(IntSliderOptions()
|
||||||
|
.Tooltip("When Interpolation FPS setting is at least this threshold, add one frame of input "
|
||||||
|
"lag (e.g. 16.6 ms for 60 FPS) in order to avoid jitter. This setting allows the "
|
||||||
|
"CPU to work on one frame while GPU works on the previous frame.\nThis setting "
|
||||||
|
"should be used when your computer is too slow to do CPU + GPU work in time.")
|
||||||
|
.Min(0)
|
||||||
|
.Max(360)
|
||||||
|
.DefaultValue(80));
|
||||||
|
AddWidget(path, "Renderer API (Needs reload)", WIDGET_VIDEO_BACKEND);
|
||||||
|
AddWidget(path, "Enable Vsync", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar(CVAR_VSYNC_ENABLED)
|
||||||
|
.PreFunc([](WidgetInfo& info) { info.isHidden = mPortMenu->disabledMap.at(DISABLE_FOR_NO_VSYNC).active; })
|
||||||
|
.Options(CheckboxOptions().Tooltip("Enables Vsync."));
|
||||||
|
AddWidget(path, "Windowed Fullscreen", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar(CVAR_SDL_WINDOWED_FULLSCREEN)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
info.isHidden = mPortMenu->disabledMap.at(DISABLE_FOR_NO_WINDOWED_FULLSCREEN).active;
|
||||||
|
})
|
||||||
|
.Options(CheckboxOptions().Tooltip("Enables Windowed Fullscreen Mode."));
|
||||||
|
AddWidget(path, "Allow multi-windows", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar(CVAR_ENABLE_MULTI_VIEWPORTS)
|
||||||
|
.PreFunc(
|
||||||
|
[](WidgetInfo& info) { info.isHidden = mPortMenu->disabledMap.at(DISABLE_FOR_NO_MULTI_VIEWPORT).active; })
|
||||||
|
.Options(CheckboxOptions().Tooltip(
|
||||||
|
"Allows multiple windows to be opened at once. Requires a reload to take effect."));
|
||||||
|
AddWidget(path, "Texture Filter (Needs reload)", WIDGET_CVAR_COMBOBOX)
|
||||||
|
.CVar(CVAR_TEXTURE_FILTER)
|
||||||
|
.Options(ComboboxOptions().Tooltip("Sets the applied Texture Filtering.").ComboMap(textureFilteringMap));
|
||||||
|
|
||||||
|
path.sidebarName = "Controls";
|
||||||
|
AddSidebarEntry("Settings", "Controls", 1);
|
||||||
|
AddWidget(path,
|
||||||
|
"This interface can be a little daunting. Please bear with us as we work to improve the experience "
|
||||||
|
"and address some known issues.\n"
|
||||||
|
"\n"
|
||||||
|
"At first glance, you may notice several input devices displayed below the 'Clear All' button. "
|
||||||
|
"Some of these might be other controllers connected to your computer, while others may be "
|
||||||
|
"duplicated controllers (a known issue). We recommend clicking on the box with the " ICON_FA_EYE
|
||||||
|
" icon and the name of any disconnected or unused controllers to hide their inputs. Make sure the "
|
||||||
|
"target controller remains visible.\n"
|
||||||
|
"\n"
|
||||||
|
"If you encounter issues connecting your controller or registering inputs, try closing Steam or "
|
||||||
|
"any other external input software. Alternatively, test a different controller to determine if "
|
||||||
|
"it's a compatibility issue.\n",
|
||||||
|
WIDGET_TEXT);
|
||||||
|
AddWidget(path, "Bindings", WIDGET_SEPARATOR_TEXT);
|
||||||
|
AddWidget(path, "Popout Bindings Window", WIDGET_WINDOW_BUTTON)
|
||||||
|
.CVar(CVAR_CONTROLLER_CONFIGURATION_WINDOW_OPEN)
|
||||||
|
.WindowName("Input Editor")
|
||||||
|
.Options(ButtonOptions().Tooltip("Enables the separate Bindings Window.").Size(Sizes::Inline));
|
||||||
|
}
|
||||||
|
int32_t motionBlurStrength;
|
||||||
|
|
||||||
|
void PortMenu::AddEnhancements() {
|
||||||
|
AddMenuEntry("Enhancements", "gSettings.Menu.EnhancementsSidebarSection");
|
||||||
|
WidgetPath path = { "Enhancements", "General", SECTION_COLUMN_1 };
|
||||||
|
AddSidebarEntry("Enhancements", "General", 3);
|
||||||
|
//UIWidgets::WindowButton("Multiplayer", "gMultiplayerWindowEnabled", GameUI::mMultiplayerWindow,
|
||||||
|
// { .tooltip = "Shows the multiplayer window" });
|
||||||
|
// UIWidgets::WindowButton("Freecam", "gFreecam", GameUI::mFreecamWindow,
|
||||||
|
// { .tooltip = "Allows you to fly around the course" });
|
||||||
|
AddWidget(path, "No multiplayer feature cuts", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gMultiplayerNoFeatureCuts")
|
||||||
|
.Options(CheckboxOptions().Tooltip("Allows full train and jumbotron in multiplayer, etc."));
|
||||||
|
AddWidget(path, "General Improvements", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gImprovements").Options(CheckboxOptions().Tooltip("General improvements to the game experience."));
|
||||||
|
AddWidget(path, "No Level of Detail (LOD)", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gDisableLod")
|
||||||
|
.Options(CheckboxOptions().Tooltip("Disable Level of Detail (LOD) to avoid models using lower poly versions at a distance"));
|
||||||
|
AddWidget(path, "Disable Culling", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gNoCulling").Options(CheckboxOptions().Tooltip("Disable original culling of mk64"));
|
||||||
|
AddWidget(path, "Far Frustrum", WIDGET_CVAR_SLIDER_FLOAT)
|
||||||
|
.CVar("gFarFrustrum")
|
||||||
|
.Options(FloatSliderOptions().Min(0.0f).Max(10000.0f).DefaultValue(10000.0f)
|
||||||
|
.Tooltip("Say how Far the Frustrum are when 'Disable Culling' are enable").Step(10.0f));
|
||||||
|
|
||||||
|
path = { "Enhancements", "Cheats", SECTION_COLUMN_1 };
|
||||||
|
AddSidebarEntry("Enhancements", "Cheats", 3);
|
||||||
|
AddWidget(path, "Moon Jump", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gEnableMoonJump");
|
||||||
|
AddWidget(path, "Enable Custom CC", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gEnableCustomCC");
|
||||||
|
AddWidget(path, "Custom CC", WIDGET_CVAR_SLIDER_FLOAT)
|
||||||
|
.CVar("gCustomCC")
|
||||||
|
.Options(FloatSliderOptions().Min(0.0f).Max(1000.0f).DefaultValue(150.0f).Step(10.0f));
|
||||||
|
AddWidget(path, "Disable Wall Collision", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gNoWallColision")
|
||||||
|
.Options(CheckboxOptions().Tooltip("Disable wall collision."));
|
||||||
|
AddWidget(path, "Min Height", WIDGET_CVAR_SLIDER_FLOAT)
|
||||||
|
.CVar("gMinHeight")
|
||||||
|
.Options(FloatSliderOptions().Min(-50.0f).Max(50.0f).DefaultValue(0.0f)
|
||||||
|
.Tooltip("When Disable Wall Collision are enable what is the minimal height you can get."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortMenu::AddDevTools() {
|
||||||
|
AddMenuEntry("Developer", "gSettings.Menu.DevToolsSidebarSection");
|
||||||
|
AddSidebarEntry("Developer", "General", 3);
|
||||||
|
WidgetPath path = { "Developer", "General", SECTION_COLUMN_1 };
|
||||||
|
AddWidget(path, "Popout Menu", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gSettings.Menu.Popout")
|
||||||
|
.Options(CheckboxOptions().Tooltip("Changes the menu display from overlay to windowed."));
|
||||||
|
AddWidget(path, "Debug Mode", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gEnableDebugMode")
|
||||||
|
.Options(CheckboxOptions().Tooltip("Enables Debug Mode."));
|
||||||
|
AddWidget(path, "Render Collision", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar("gRenderCollisionMesh")
|
||||||
|
.Options(CheckboxOptions().Tooltip("Renders the collision mesh instead of the course mesh"));
|
||||||
|
|
||||||
|
path = { "Developer", "Gfx Debugger", SECTION_COLUMN_1 };
|
||||||
|
AddSidebarEntry("Developer", "Gfx Debugger", 1);
|
||||||
|
AddWidget(path, "Popout Gfx Debugger", WIDGET_WINDOW_BUTTON)
|
||||||
|
.CVar("gGfxDebuggerEnabled")
|
||||||
|
.Options(ButtonOptions().Tooltip(
|
||||||
|
"Enables the Gfx Debugger window, allowing you to input commands. Type help for some examples"))
|
||||||
|
.WindowName("GfxDebuggerWindow");
|
||||||
|
|
||||||
|
path = { "Developer", "Game Info", SECTION_COLUMN_1 };
|
||||||
|
AddSidebarEntry("Developer", "Game Info", 1);
|
||||||
|
AddWidget(path, "Popout Game Info", WIDGET_WINDOW_BUTTON)
|
||||||
|
.CVar("gGameInfoEnabled")
|
||||||
|
.Options(ButtonOptions().Tooltip(
|
||||||
|
"Shows the game info window, contains player and actor information"))
|
||||||
|
.WindowName("GfxDebuggerWindow");
|
||||||
|
|
||||||
|
path = { "Developer", "Stats", SECTION_COLUMN_1 };
|
||||||
|
AddSidebarEntry("Developer", "Stats", 1);
|
||||||
|
AddWidget(path, "Popout Stats", WIDGET_WINDOW_BUTTON)
|
||||||
|
.CVar("gStatsEnabled")
|
||||||
|
.Options(ButtonOptions().Tooltip("Shows the stats window, with your FPS and frametimes, and the OS you're playing on"))
|
||||||
|
.WindowName("Stats");
|
||||||
|
|
||||||
|
path = { "Developer", "Console", SECTION_COLUMN_1 };
|
||||||
|
AddSidebarEntry("Developer", "Console", 1);
|
||||||
|
AddWidget(path, "Popout Console", WIDGET_WINDOW_BUTTON)
|
||||||
|
.CVar("gConsoleEnabled")
|
||||||
|
.Options(ButtonOptions().Tooltip(
|
||||||
|
"Enables the console window, allowing you to input commands. Type help for some examples"))
|
||||||
|
.WindowName("Console");
|
||||||
|
}
|
||||||
|
|
||||||
|
PortMenu::PortMenu(const std::string& consoleVariable, const std::string& name)
|
||||||
|
: Menu(consoleVariable, name, 0, UIWidgets::Colors::LightBlue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//bool CheckNetworkConnected(disabledInfo& info) {
|
||||||
|
// return gNetwork.isConnected;
|
||||||
|
//}
|
||||||
|
|
||||||
|
void PortMenu::InitElement() {
|
||||||
|
Ship::Menu::InitElement();
|
||||||
|
AddSettings();
|
||||||
|
AddEnhancements();
|
||||||
|
AddDevTools();
|
||||||
|
|
||||||
|
if (CVarGetInteger("gSettings.Menu.SidebarSearch", 0)) {
|
||||||
|
InsertSidebarSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& initFunc : MenuInit::GetInitFuncs()) {
|
||||||
|
initFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
disabledMap = {
|
||||||
|
{ DISABLE_FOR_FREE_CAM_ON,
|
||||||
|
{ [](disabledInfo& info) -> bool { return CVarGetInteger("gFreecam", 0); }, "Free Cam is Enabled" } },
|
||||||
|
{ DISABLE_FOR_FREE_CAM_OFF,
|
||||||
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger("gFreecam", 0); }, "Free Cam is Disabled" } },
|
||||||
|
{ DISABLE_FOR_DEBUG_MODE_OFF,
|
||||||
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnableDebugMode", 0); },
|
||||||
|
"Debug Mode is Disabled" } },
|
||||||
|
{ DISABLE_FOR_NO_VSYNC,
|
||||||
|
{ [](disabledInfo& info) -> bool {
|
||||||
|
return !Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync();
|
||||||
|
},
|
||||||
|
"Disabling VSync not supported" } },
|
||||||
|
{ DISABLE_FOR_NO_WINDOWED_FULLSCREEN,
|
||||||
|
{ [](disabledInfo& info) -> bool {
|
||||||
|
return !Ship::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen();
|
||||||
|
},
|
||||||
|
"Windowed Fullscreen not supported" } },
|
||||||
|
{ DISABLE_FOR_NO_MULTI_VIEWPORT,
|
||||||
|
{ [](disabledInfo& info) -> bool {
|
||||||
|
return !Ship::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports();
|
||||||
|
},
|
||||||
|
"Multi-viewports not supported" } },
|
||||||
|
{ DISABLE_FOR_NOT_DIRECTX,
|
||||||
|
{ [](disabledInfo& info) -> bool {
|
||||||
|
return Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() !=
|
||||||
|
Ship::WindowBackend::FAST3D_DXGI_DX11;
|
||||||
|
},
|
||||||
|
"Available Only on DirectX" } },
|
||||||
|
{ DISABLE_FOR_DIRECTX,
|
||||||
|
{ [](disabledInfo& info) -> bool {
|
||||||
|
return Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() ==
|
||||||
|
Ship::WindowBackend::FAST3D_DXGI_DX11;
|
||||||
|
},
|
||||||
|
"Not Available on DirectX" } },
|
||||||
|
{ DISABLE_FOR_MATCH_REFRESH_RATE_ON,
|
||||||
|
{ [](disabledInfo& info) -> bool { return CVarGetInteger("gMatchRefreshRate", 0); },
|
||||||
|
"Match Refresh Rate is Enabled" } },
|
||||||
|
{ DISABLE_FOR_ADVANCED_RESOLUTION_ON,
|
||||||
|
{ [](disabledInfo& info) -> bool { return CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0); },
|
||||||
|
"Advanced Resolution Enabled" } },
|
||||||
|
{ DISABLE_FOR_VERTICAL_RES_TOGGLE_ON,
|
||||||
|
{ [](disabledInfo& info) -> bool {
|
||||||
|
return CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0);
|
||||||
|
},
|
||||||
|
"Vertical Resolution Toggle Enabled" } },
|
||||||
|
{ DISABLE_FOR_LOW_RES_MODE_ON,
|
||||||
|
{ [](disabledInfo& info) -> bool { return CVarGetInteger(CVAR_LOW_RES_MODE, 0); }, "N64 Mode Enabled" } },
|
||||||
|
//{ DISABLE_FOR_MULTIPLAYER_CONNECTED,
|
||||||
|
// { CheckNetworkConnected, "Multiplayer Connected"}},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortMenu::UpdateElement() {
|
||||||
|
Ship::Menu::UpdateElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortMenu::Draw() {
|
||||||
|
Ship::Menu::Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortMenu::DrawElement() {
|
||||||
|
Ship::Menu::DrawElement();
|
||||||
|
}
|
||||||
|
} // namespace BenGui
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
#ifndef PORTMENU_H
|
||||||
|
#define PORTMENU_H
|
||||||
|
|
||||||
|
#include <libultraship/libultraship.h>
|
||||||
|
#include "UIWidgets.h"
|
||||||
|
#include "Menu.h"
|
||||||
|
#include "graphic/Fast3D/gfx_rendering_api.h"
|
||||||
|
|
||||||
|
namespace GameUI {
|
||||||
|
|
||||||
|
static const std::unordered_map<int32_t, const char*> menuThemeOptions = {
|
||||||
|
{ UIWidgets::Colors::Red, "Red" },
|
||||||
|
{ UIWidgets::Colors::DarkRed, "Dark Red" },
|
||||||
|
{ UIWidgets::Colors::Orange, "Orange" },
|
||||||
|
{ UIWidgets::Colors::Green, "Green" },
|
||||||
|
{ UIWidgets::Colors::DarkGreen, "Dark Green" },
|
||||||
|
{ UIWidgets::Colors::LightBlue, "Light Blue" },
|
||||||
|
{ UIWidgets::Colors::Blue, "Blue" },
|
||||||
|
{ UIWidgets::Colors::DarkBlue, "Dark Blue" },
|
||||||
|
{ UIWidgets::Colors::Indigo, "Indigo" },
|
||||||
|
{ UIWidgets::Colors::Violet, "Violet" },
|
||||||
|
{ UIWidgets::Colors::Purple, "Purple" },
|
||||||
|
{ UIWidgets::Colors::Brown, "Brown" },
|
||||||
|
{ UIWidgets::Colors::Gray, "Gray" },
|
||||||
|
{ UIWidgets::Colors::DarkGray, "Dark Gray" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::unordered_map<int32_t, const char*> textureFilteringMap = {
|
||||||
|
{ FILTER_THREE_POINT, "Three-Point" },
|
||||||
|
{ FILTER_LINEAR, "Linear" },
|
||||||
|
{ FILTER_NONE, "None" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::unordered_map<int32_t, const char*> motionBlurOptions = {
|
||||||
|
{ MOTION_BLUR_DYNAMIC, "Dynamic (default)" },
|
||||||
|
{ MOTION_BLUR_ALWAYS_OFF, "Always Off" },
|
||||||
|
{ MOTION_BLUR_ALWAYS_ON, "Always On" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::unordered_map<int32_t, const char*> logLevels = {
|
||||||
|
{ DEBUG_LOG_TRACE, "Trace" }, { DEBUG_LOG_DEBUG, "Debug" }, { DEBUG_LOG_INFO, "Info" },
|
||||||
|
{ DEBUG_LOG_WARN, "Warn" }, { DEBUG_LOG_ERROR, "Error" }, { DEBUG_LOG_CRITICAL, "Critical" },
|
||||||
|
{ DEBUG_LOG_OFF, "Off" },
|
||||||
|
};
|
||||||
|
|
||||||
|
class PortMenu : public Ship::Menu {
|
||||||
|
public:
|
||||||
|
PortMenu(const std::string& consoleVariable, const std::string& name);
|
||||||
|
~PortMenu() {}
|
||||||
|
|
||||||
|
void InitElement() override;
|
||||||
|
void DrawElement() override;
|
||||||
|
void UpdateElement() override;
|
||||||
|
void Draw() override;
|
||||||
|
|
||||||
|
void AddSidebarEntry(std::string sectionName, std::string sidbarName, uint32_t columnCount);
|
||||||
|
WidgetInfo& AddWidget(WidgetPath& pathInfo, std::string widgetName, WidgetType widgetType);
|
||||||
|
void AddSettings();
|
||||||
|
void AddEnhancements();
|
||||||
|
void AddDevTools();
|
||||||
|
};
|
||||||
|
} // namespace BenGui
|
||||||
|
|
||||||
|
#endif // PORTMENU_H
|
||||||
|
|
@ -1,422 +1,542 @@
|
||||||
#include "ResolutionEditor.h"
|
#include "ResolutionEditor.h"
|
||||||
#include "UIWidgets.h"
|
|
||||||
#include "libultraship/src/Context.h"
|
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <libultraship/libultraship.h>
|
#include <libultraship/libultraship.h>
|
||||||
|
|
||||||
|
#include "UIWidgets.h"
|
||||||
#include <graphic/Fast3D/gfx_pc.h>
|
#include <graphic/Fast3D/gfx_pc.h>
|
||||||
|
#include "port/Engine.h"
|
||||||
|
#include "PortMenu.h"
|
||||||
|
|
||||||
/* Console Variables are grouped under gAdvancedResolution. (e.g. "gAdvancedResolution.Enabled")
|
/* Console Variables are grouped under gAdvancedResolution. (e.g. CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled")
|
||||||
|
|
||||||
The following CVars are used in Libultraship and can be edited here:
|
The following cvars are used in Libultraship and can be edited here:
|
||||||
- Enabled - Turns Advanced Resolution Mode on.
|
- Enabled - Turns Advanced Resolution Mode on.
|
||||||
- AspectRatioX, AspectRatioY - Aspect ratio controls. To toggle off, set either to zero.
|
- AspectRatioX, AspectRatioY - Aspect ratio controls. To toggle off, set either to zero.
|
||||||
- VerticalPixelCount, VerticalResolutionToggle - Resolution controls.
|
- VerticalPixelCount, VerticalResolutionToggle - Resolution controls.
|
||||||
- PixelPerfectMode, IntegerScale.Factor - Pixel Perfect Mode a.k.a. integer scaling controls.
|
- PixelPerfectMode, IntegerScale.Factor - Pixel Perfect Mode a.k.a. integer scaling controls.
|
||||||
(Waiting on a second PR merge on LUS for this to fully function.):
|
|
||||||
- IntegerScale.FitAutomatically - Automatic resizing for Pixel Perfect Mode.
|
- IntegerScale.FitAutomatically - Automatic resizing for Pixel Perfect Mode.
|
||||||
- IntegerScale.NeverExceedBounds - Prevents manual resizing from exceeding screen bounds.
|
- IntegerScale.NeverExceedBounds - Prevents manual resizing from exceeding screen bounds.
|
||||||
|
|
||||||
The following CVars are also implemented in LUS for niche use cases:
|
The following cvars are also implemented in LUS for niche use cases:
|
||||||
- IgnoreAspectCorrection - Stretch framebuffer to fill screen.
|
- IgnoreAspectCorrection - Stretch framebuffer to fill screen.
|
||||||
This is something of a power-user setting for niche setups that most people won't need or care about,
|
This is something of a power-user setting for niche setups that most people won't need or care about,
|
||||||
but may be useful if playing the Switch/Wii U ports on a 4:3 television.
|
but may be useful if playing the Switch/Wii U ports on a 4:3 television.
|
||||||
- IntegerScale.ExceedBoundsBy - Offset the max screen bounds, usually by +1.
|
- IntegerScale.ExceedBoundsBy - Offset the max screen bounds, usually by +1.
|
||||||
This isn't that useful at the moment.
|
This isn't that useful at the moment, so it's unused here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace AdvancedResolutionSettings {
|
namespace GameUI {
|
||||||
|
extern std::shared_ptr<PortMenu> mPortMenu;
|
||||||
enum setting { UPDATE_aspectRatioX, UPDATE_aspectRatioY, UPDATE_verticalPixelCount };
|
enum setting { UPDATE_aspectRatioX, UPDATE_aspectRatioY, UPDATE_verticalPixelCount };
|
||||||
|
|
||||||
const char* aspectRatioPresetLabels[] = {
|
std::unordered_map<int32_t, const char*> aspectRatioPresetLabels = { { 0, "Off" },
|
||||||
"Off", "Custom", "Original (4:3)", "Widescreen (16:9)", "Nintendo 3DS (5:3)", "16:10 (8:5)", "Ultrawide (21:9)"
|
{ 1, "Custom" },
|
||||||
};
|
{ 2, "Original (4:3)" },
|
||||||
const float aspectRatioPresetsX[] = { 0.0f, 12.0f, 4.0f, 16.0f, 5.0f, 16.0f, 21.0f };
|
{ 3, "Widescreen (16:9)" },
|
||||||
|
{ 4, "Nintendo 3DS (5:3)" },
|
||||||
|
{ 5, "16:10 (8:5)" },
|
||||||
|
{ 6, "Ultrawide (21:9)" } };
|
||||||
|
const float aspectRatioPresetsX[] = { 0.0f, 16.0f, 4.0f, 16.0f, 5.0f, 16.0f, 21.0f };
|
||||||
const float aspectRatioPresetsY[] = { 0.0f, 9.0f, 3.0f, 9.0f, 3.0f, 10.0f, 9.0f };
|
const float aspectRatioPresetsY[] = { 0.0f, 9.0f, 3.0f, 9.0f, 3.0f, 10.0f, 9.0f };
|
||||||
const int default_aspectRatio = 1; // Default combo list option
|
const int default_aspectRatio = 1; // Default combo list option
|
||||||
|
|
||||||
const char* pixelCountPresetLabels[] = { "Custom", "Native N64 (240p)", "2x (480p)", "3x (720p)", "4x (960p)",
|
const char* pixelCountPresetLabels[] = { "Custom", "Native N64 (240p)", "2x (480p)", "3x (720p)", "4x (960p)",
|
||||||
"5x (1200p)", "6x (1440p)", "Full HD (1080p)", "4K (2160p)" };
|
"5x (1200p)", "6x (1440p)", "Full HD (1080p)", "4K (2160p)" };
|
||||||
const int pixelCountPresets[] = { 480, 240, 480, 720, 960, 1200, 1440, 1080, 2160, 480 };
|
const int pixelCountPresets[] = { 480, 240, 480, 720, 960, 1200, 1440, 1080, 2160 };
|
||||||
const int default_pixelCount = 0; // Default combo list option
|
const int default_pixelCount = 0; // Default combo list option
|
||||||
|
|
||||||
const uint32_t minVerticalPixelCount = 240; // see: Ship::AdvancedResolution()
|
// Resolution clamp values as hardcoded in LUS::Gui::ApplyResolutionChanges()
|
||||||
const uint32_t maxVerticalPixelCount = 4320;
|
const uint32_t minVerticalPixelCount = SCREEN_HEIGHT;
|
||||||
|
const uint32_t maxVerticalPixelCount = 4320; // 18x native, or 8K TV resolution
|
||||||
|
|
||||||
const unsigned short default_maxIntegerScaleFactor = 6; // Default size of Integer scale factor slider.
|
const unsigned short default_maxIntegerScaleFactor = 6; // Default size of Integer scale factor slider.
|
||||||
|
|
||||||
const float enhancementSpacerHeight = 19.0f;
|
enum messageType { MESSAGE_ERROR, MESSAGE_WARNING, MESSAGE_QUESTION, MESSAGE_INFO, MESSAGE_GRAY_75 };
|
||||||
// This will need to be determined more intelligently when Hi-DPI UI support is added.
|
const ImVec4 messageColor[]{
|
||||||
|
{ 0.85f, 0.0f, 0.0f, 1.0f }, // MESSAGE_ERROR
|
||||||
|
{ 0.85f, 0.85f, 0.0f, 1.0f }, // MESSAGE_WARNING
|
||||||
|
{ 0.0f, 0.85f, 0.85f, 1.0f }, // MESSAGE_QUESTION
|
||||||
|
{ 0.0f, 0.85f, 0.55f, 1.0f }, // MESSAGE_INFO
|
||||||
|
{ 0.75f, 0.75f, 0.75f, 1.0f } // MESSAGE_GRAY_75
|
||||||
|
};
|
||||||
|
static const float enhancementSpacerHeight = 19.0f;
|
||||||
|
// Initialise update flags.
|
||||||
|
static bool update[3];
|
||||||
|
|
||||||
void AdvancedResolutionSettingsWindow::InitElement() {
|
// Initialise integer scale bounds.
|
||||||
}
|
static short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get
|
||||||
|
// overridden depending on viewport res
|
||||||
|
|
||||||
void AdvancedResolutionSettingsWindow::DrawElement() {
|
static short integerScale_maximumBounds = 1; // can change when window is resized
|
||||||
ImGui::SetNextWindowSize(ImVec2(497, 532), ImGuiCond_FirstUseEver);
|
|
||||||
if (ImGui::Begin("Advanced Resolution Settings", &mIsVisible)) {
|
|
||||||
// Initialise update flags.
|
|
||||||
bool update[sizeof(setting)];
|
|
||||||
for (unsigned short i = 0; i < sizeof(setting); i++)
|
|
||||||
update[i] = false;
|
|
||||||
static short updateCountdown = 0;
|
|
||||||
short countdownStartingValue = CVarGetInteger("gInterpolationFPS", 30) / 2; // half of a second, in frames.
|
|
||||||
|
|
||||||
// Initialise integer scale bounds.
|
// Combo List defaults
|
||||||
short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get
|
static int32_t item_aspectRatio;
|
||||||
// overridden depending on viewport res
|
static int32_t item_pixelCount;
|
||||||
|
// Stored Values for non-UIWidgets elements
|
||||||
|
static float aspectRatioX;
|
||||||
|
static float aspectRatioY;
|
||||||
|
static int32_t verticalPixelCount;
|
||||||
|
// Additional settings
|
||||||
|
static bool showHorizontalResField;
|
||||||
|
static int32_t horizontalPixelCount;
|
||||||
|
// Disabling flags
|
||||||
|
static bool disabled_everything;
|
||||||
|
static bool disabled_pixelCount;
|
||||||
|
|
||||||
short integerScale_maximumBounds = 1; // can change when window is resized
|
using namespace UIWidgets;
|
||||||
// This is mostly just for UX purposes, as Fit Automatically logic is part of LUS.
|
|
||||||
if (((float) gfx_current_game_window_viewport.width / gfx_current_game_window_viewport.height) >
|
|
||||||
((float) gfx_current_dimensions.width / gfx_current_dimensions.height)) {
|
|
||||||
// Scale to window height
|
|
||||||
integerScale_maximumBounds = gfx_current_game_window_viewport.height / gfx_current_dimensions.height;
|
|
||||||
} else {
|
|
||||||
// Scale to window width
|
|
||||||
integerScale_maximumBounds = gfx_current_game_window_viewport.width / gfx_current_dimensions.width;
|
|
||||||
}
|
|
||||||
// Lower-clamping maximum bounds value to 1 is no-longer necessary as that's accounted for in LUS.
|
|
||||||
// Letting it go below 1 in this Editor will even allow for checking if screen bounds are being exceeded.
|
|
||||||
if (default_maxIntegerScaleFactor < integerScale_maximumBounds) {
|
|
||||||
max_integerScaleFactor =
|
|
||||||
integerScale_maximumBounds + CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stored Values for non-UIWidgets elements
|
|
||||||
static float aspectRatioX = CVarGetFloat("gAdvancedResolution.AspectRatioX", 16.0f);
|
|
||||||
static float aspectRatioY = CVarGetFloat("gAdvancedResolution.AspectRatioY", 9.0f);
|
|
||||||
static int verticalPixelCount = CVarGetInteger("gAdvancedResolution.VerticalPixelCount", 480);
|
|
||||||
// Combo List defaults
|
|
||||||
static int item_aspectRatio = default_aspectRatio;
|
|
||||||
static int item_pixelCount = default_pixelCount;
|
|
||||||
// Additional settings
|
|
||||||
static bool showHorizontalResField = false;
|
|
||||||
static int horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
|
||||||
|
|
||||||
|
void RegisterResolutionWidgets() {
|
||||||
|
WidgetPath path = { "Settings", "Graphics", SECTION_COLUMN_2 };
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
ImGui::Text("Note: these settings may behave incorrectly on Apple Retina Displays.");
|
mPortMenu
|
||||||
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
->AddWidget(path, ICON_FA_INFO_CIRCLE " These settings may behave incorrectly on Retina displays.", WIDGET_TEXT)
|
||||||
|
.Options(WidgetOptions().Color(Colors::Green));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The original resolution slider (for convenience)
|
// Resolution visualiser
|
||||||
if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f,
|
mPortMenu->AddWidget(path, "Viewport dimensions: {} x {}", WIDGET_TEXT).PreFunc([](WidgetInfo& info) {
|
||||||
"", 1.0f, true, true,
|
info.name = fmt::format("Viewport dimensions: {} x {}", gfx_current_game_window_viewport.width,
|
||||||
(CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0) &&
|
gfx_current_game_window_viewport.height);
|
||||||
CVarGetInteger("gAdvancedResolution.Enabled", 0)) ||
|
});
|
||||||
CVarGetInteger("gLowResMode", 0))) {
|
mPortMenu->AddWidget(path, "Internal resolution: {} x {}", WIDGET_TEXT).PreFunc([](WidgetInfo& info) {
|
||||||
Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1));
|
info.name =
|
||||||
}
|
fmt::format("Internal resolution: {} x {}", gfx_current_dimensions.width, gfx_current_dimensions.height);
|
||||||
UIWidgets::Tooltip("Multiplies your output resolution by the value entered, as a more intensive but effective "
|
});
|
||||||
"form of anti-aliasing"); // Description pulled from SohMenuBar.cpp
|
|
||||||
|
|
||||||
// N64 Mode toggle (again for convenience)
|
// UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
||||||
// UIWidgets::PaddedEnhancementCheckbox("(Enhancements>Graphics) N64 Mode", "gLowResMode", false, false, false,
|
// Activator
|
||||||
// "", UIWidgets::CheckboxGraphics::Cross, false);
|
mPortMenu->AddWidget(path, "Enable advanced settings.", WIDGET_CVAR_CHECKBOX)
|
||||||
|
.CVar(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled");
|
||||||
|
/*UIWidgets::PaddedEnhancementCheckbox(, , false, false,
|
||||||
|
false, "", UIWidgets::CheckboxGraphics::Cross, false);*/
|
||||||
|
// Error/Warning display
|
||||||
|
mPortMenu
|
||||||
|
->AddWidget(path, ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring.",
|
||||||
|
WIDGET_TEXT)
|
||||||
|
.PreFunc(
|
||||||
|
[](WidgetInfo& info) { info.isHidden = !(!CVarGetInteger(CVAR_LOW_RES_MODE, 0) && IsDroppingFrames()); })
|
||||||
|
.Options(WidgetOptions().Color(Colors::Orange));
|
||||||
|
mPortMenu->AddWidget(path, ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings.", WIDGET_TEXT)
|
||||||
|
.PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_LOW_RES_MODE, 0); })
|
||||||
|
.Options(WidgetOptions().Color(Colors::LightBlue));
|
||||||
|
mPortMenu->AddWidget(path, "Click to disable N64 mode", WIDGET_BUTTON)
|
||||||
|
.PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_LOW_RES_MODE, 0); })
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
CVarSetInteger(CVAR_LOW_RES_MODE, 0);
|
||||||
|
CVarSave();
|
||||||
|
});
|
||||||
|
|
||||||
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
// if (disabled_everything) { // Hide aspect ratio controls.
|
||||||
// Activator
|
// UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Enable advanced settings.", "gAdvancedResolution.Enabled", false, false,
|
// }
|
||||||
false, "", UIWidgets::CheckboxGraphics::Cross, false);
|
|
||||||
// Error/Warning display
|
// Aspect Ratio
|
||||||
if (!CVarGetInteger("gLowResMode", 0)) {
|
mPortMenu->AddWidget(path, "AspectSep", WIDGET_SEPARATOR);
|
||||||
if (IsDroppingFrames()) { // Significant frame drop warning
|
mPortMenu->AddWidget(path, "Force aspect ratio:", WIDGET_TEXT);
|
||||||
ImGui::TextColored({ 0.85f, 0.85f, 0.0f, 1.0f },
|
mPortMenu->AddWidget(path, "(Select \"Off\" to disable.)", WIDGET_TEXT)
|
||||||
ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring.");
|
.SameLine(true)
|
||||||
UIWidgets::Spacer(2);
|
.Options(WidgetOptions().Color(Colors::Gray));
|
||||||
} else { // No warnings
|
// Presets
|
||||||
UIWidgets::Spacer(enhancementSpacerHeight);
|
mPortMenu->AddWidget(path, "Aspect Ratio", WIDGET_COMBOBOX)
|
||||||
|
.ValuePointer(&item_aspectRatio)
|
||||||
|
.PreFunc([](WidgetInfo& info) {
|
||||||
|
info.isHidden = !mPortMenu->GetDisabledMap().at(DISABLE_FOR_ADVANCED_RESOLUTION_ON).active;
|
||||||
|
})
|
||||||
|
.Callback([](WidgetInfo& info) {
|
||||||
|
if (item_aspectRatio != default_aspectRatio) { // don't change anything if "Custom" is selected.
|
||||||
|
aspectRatioX = aspectRatioPresetsX[item_aspectRatio];
|
||||||
|
aspectRatioY = aspectRatioPresetsY[item_aspectRatio];
|
||||||
|
|
||||||
|
if (showHorizontalResField) {
|
||||||
|
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioX);
|
||||||
|
CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioY);
|
||||||
}
|
}
|
||||||
} else { // N64 Mode warning
|
CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", item_aspectRatio);
|
||||||
ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f },
|
CVarSave();
|
||||||
ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings.");
|
})
|
||||||
ImGui::SameLine();
|
.Options(ComboboxOptions().ComboMap(aspectRatioPresetLabels));
|
||||||
if (ImGui::Button("Click to disable")) {
|
mPortMenu->AddWidget(path, "AspectRationCustom", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) {
|
||||||
CVarSetInteger("gLowResMode", (int) false);
|
// Hide aspect ratio input fields if using one of the presets.
|
||||||
CVarSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Resolution visualiser
|
|
||||||
ImGui::Text("Viewport dimensions: %d x %d", gfx_current_game_window_viewport.width,
|
|
||||||
gfx_current_game_window_viewport.height);
|
|
||||||
ImGui::Text("Internal resolution: %d x %d", gfx_current_dimensions.width, gfx_current_dimensions.height);
|
|
||||||
|
|
||||||
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
|
||||||
|
|
||||||
// Aspect Ratio
|
|
||||||
ImGui::Text("Force aspect ratio:");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::TextColored({ 0.75f, 0.75f, 0.75f, 1.0f }, "(Select \"Off\" to disable.)");
|
|
||||||
if (ImGui::Combo(" ", &item_aspectRatio, aspectRatioPresetLabels,
|
|
||||||
IM_ARRAYSIZE(aspectRatioPresetLabels)) &&
|
|
||||||
item_aspectRatio != default_aspectRatio) { // don't change anything if "Custom" is selected.
|
|
||||||
aspectRatioX = aspectRatioPresetsX[item_aspectRatio];
|
|
||||||
aspectRatioY = aspectRatioPresetsY[item_aspectRatio];
|
|
||||||
update[UPDATE_aspectRatioX] = true;
|
|
||||||
update[UPDATE_aspectRatioY] = true;
|
|
||||||
|
|
||||||
if (showHorizontalResField) {
|
|
||||||
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) {
|
if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) {
|
||||||
// Declaring the Y input interaction in particular as a variable beforehand
|
// Declare input interaction bools outside of IF statement to prevent Y field from disappearing.
|
||||||
// will prevent a bug where the Y field would disappear when modifying X.
|
const bool input_X = ImGui::InputFloat("X", &aspectRatioX, 0.1f, 1.0f, "%.3f");
|
||||||
bool inputX = ImGui::InputFloat("X", &aspectRatioX, 0.1f, 1.0f, "%.3f");
|
const bool input_Y = ImGui::InputFloat("Y", &aspectRatioY, 0.1f, 1.0f, "%.3f");
|
||||||
bool inputY = ImGui::InputFloat("Y", &aspectRatioY, 0.1f, 1.0f, "%.3f");
|
if (input_X || input_Y) {
|
||||||
if (inputX || inputY) {
|
|
||||||
item_aspectRatio = default_aspectRatio;
|
item_aspectRatio = default_aspectRatio;
|
||||||
update[UPDATE_aspectRatioX] = true;
|
update[UPDATE_aspectRatioX] = true;
|
||||||
update[UPDATE_aspectRatioY] = true;
|
update[UPDATE_aspectRatioY] = true;
|
||||||
}
|
}
|
||||||
} else if (showHorizontalResField) { // Show calculated aspect ratio
|
} else if (showHorizontalResField) { // Show calculated aspect ratio
|
||||||
if (item_aspectRatio) {
|
if (item_aspectRatio) {
|
||||||
UIWidgets::Spacer(2);
|
ImGui::Dummy({ 0, 2 });
|
||||||
float resolvedAspectRatio = (float) gfx_current_dimensions.height / gfx_current_dimensions.width;
|
const float resolvedAspectRatio = (float)gfx_current_dimensions.width / gfx_current_dimensions.height;
|
||||||
ImGui::Text("Aspect ratio: %.4f", resolvedAspectRatio);
|
ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio);
|
||||||
} else {
|
|
||||||
UIWidgets::Spacer(enhancementSpacerHeight);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
//
|
||||||
|
// if (disabled_everything) { // Hide aspect ratio controls.
|
||||||
|
// UIWidgets::ReEnableComponent("disabledTooltipText");
|
||||||
|
// }
|
||||||
|
// UIWidgets::Spacer(0);
|
||||||
|
//
|
||||||
|
// // Vertical Resolution
|
||||||
|
// UIWidgets::PaddedEnhancementCheckbox("Set fixed vertical resolution (disables Resolution slider)",
|
||||||
|
// CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", true,
|
||||||
|
// false, disabled_everything, "", UIWidgets::CheckboxGraphics::Cross,
|
||||||
|
// false);
|
||||||
|
// UIWidgets::Tooltip(
|
||||||
|
// "Override the resolution scale slider and use the settings below, irrespective of window size.");
|
||||||
|
// if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls.
|
||||||
|
// UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||||
|
// }
|
||||||
|
// if (ImGui::Combo("Pixel Count Presets", &item_pixelCount, pixelCountPresetLabels,
|
||||||
|
// IM_ARRAYSIZE(pixelCountPresetLabels)) &&
|
||||||
|
// item_pixelCount != default_pixelCount) { // don't change anything if "Custom" is selected.
|
||||||
|
// verticalPixelCount = pixelCountPresets[item_pixelCount];
|
||||||
|
//
|
||||||
|
// if (showHorizontalResField) {
|
||||||
|
// horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", verticalPixelCount);
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", item_pixelCount);
|
||||||
|
// CVarSave();
|
||||||
|
// }
|
||||||
|
// // Horizontal Resolution, if visibility is enabled for it.
|
||||||
|
// if (showHorizontalResField) {
|
||||||
|
// // Only show the field if Aspect Ratio is being enforced.
|
||||||
|
// if ((aspectRatioX > 0.0f) && (aspectRatioY > 0.0f)) {
|
||||||
|
// // So basically we're "faking" this one by setting aspectRatioX instead.
|
||||||
|
// if (ImGui::InputInt("Horiz. Pixel Count", &horizontalPixelCount, 8, 320)) {
|
||||||
|
// item_aspectRatio = default_aspectRatio;
|
||||||
|
// if (horizontalPixelCount < SCREEN_WIDTH) {
|
||||||
|
// horizontalPixelCount = SCREEN_WIDTH;
|
||||||
|
// }
|
||||||
|
// aspectRatioX = horizontalPixelCount;
|
||||||
|
// aspectRatioY = verticalPixelCount;
|
||||||
|
// update[UPDATE_aspectRatioX] = true;
|
||||||
|
// update[UPDATE_aspectRatioY] = true;
|
||||||
|
// }
|
||||||
|
// } else { // Display a notice instead.
|
||||||
|
// ImGui::TextColored(messageColor[MESSAGE_QUESTION],
|
||||||
|
// ICON_FA_QUESTION_CIRCLE " \"Force aspect ratio\" required.");
|
||||||
|
// // ImGui::Text(" ");
|
||||||
|
// ImGui::SameLine();
|
||||||
|
// if (ImGui::Button("Click to resolve")) {
|
||||||
|
// item_aspectRatio = default_aspectRatio; // Set it to Custom
|
||||||
|
// aspectRatioX = aspectRatioPresetsX[2]; // but use the 4:3 defaults
|
||||||
|
// aspectRatioY = aspectRatioPresetsY[2];
|
||||||
|
// update[UPDATE_aspectRatioX] = true;
|
||||||
|
// update[UPDATE_aspectRatioY] = true;
|
||||||
|
// horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // Vertical Resolution part 2
|
||||||
|
// if (ImGui::InputInt("Vertical Pixel Count", &verticalPixelCount, 8, 240)) {
|
||||||
|
// item_pixelCount = default_pixelCount;
|
||||||
|
// update[UPDATE_verticalPixelCount] = true;
|
||||||
|
//
|
||||||
|
// // Account for the natural instinct to enter horizontal first.
|
||||||
|
// // Ignore vertical resolutions that are below the lower clamp constant.
|
||||||
|
// if (showHorizontalResField && !(verticalPixelCount < minVerticalPixelCount)) {
|
||||||
|
// item_aspectRatio = default_aspectRatio;
|
||||||
|
// aspectRatioX = horizontalPixelCount;
|
||||||
|
// aspectRatioY = verticalPixelCount;
|
||||||
|
// update[UPDATE_aspectRatioX] = true;
|
||||||
|
// update[UPDATE_aspectRatioY] = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls.
|
||||||
|
// UIWidgets::ReEnableComponent("disabledTooltipText");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// UIWidgets::Spacer(0);
|
||||||
|
//
|
||||||
|
// // Integer scaling settings group (Pixel-perfect Mode)
|
||||||
|
// static const ImGuiTreeNodeFlags IntegerScalingResolvedImGuiFlag =
|
||||||
|
// CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) ? ImGuiTreeNodeFlags_DefaultOpen
|
||||||
|
// : ImGuiTreeNodeFlags_None;
|
||||||
|
// if (ImGui::CollapsingHeader("Integer Scaling Settings", IntegerScalingResolvedImGuiFlag)) {
|
||||||
|
// const bool disabled_pixelPerfectMode =
|
||||||
|
// !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || disabled_everything;
|
||||||
|
// // Pixel-perfect Mode
|
||||||
|
// UIWidgets::PaddedEnhancementCheckbox("Pixel-perfect Mode", CVAR_PREFIX_ADVANCED_RESOLUTION
|
||||||
|
// ".PixelPerfectMode", true,
|
||||||
|
// true, disabled_pixelCount || disabled_everything, "",
|
||||||
|
// UIWidgets::CheckboxGraphics::Cross, false);
|
||||||
|
// UIWidgets::Tooltip("Don't scale image to fill window.");
|
||||||
|
// if (disabled_pixelCount && CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0)) {
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0);
|
||||||
|
// CVarSave();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Integer Scaling
|
||||||
|
// UIWidgets::EnhancementSliderInt(
|
||||||
|
// "Integer scale factor: %d", "##ARSIntScale", CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor",
|
||||||
|
// 1, max_integerScaleFactor, "%d", 1, true, disabled_pixelPerfectMode ||
|
||||||
|
// CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0));
|
||||||
|
// UIWidgets::Tooltip("Integer scales the image. Only available in pixel-perfect mode.");
|
||||||
|
// // Display warning if size is being clamped or if framebuffer is larger than viewport.
|
||||||
|
// if (!disabled_pixelPerfectMode &&
|
||||||
|
// (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.NeverExceedBounds", 1) &&
|
||||||
|
// CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor", 1) >
|
||||||
|
// integerScale_maximumBounds)) {
|
||||||
|
// ImGui::SameLine();
|
||||||
|
// ImGui::TextColored(messageColor[MESSAGE_WARNING], ICON_FA_EXCLAMATION_TRIANGLE " Window exceeded.");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// UIWidgets::PaddedEnhancementCheckbox(
|
||||||
|
// "Automatically scale image to fit viewport", CVAR_PREFIX_ADVANCED_RESOLUTION
|
||||||
|
// ".IntegerScale.FitAutomatically", true, true, disabled_pixelPerfectMode, "",
|
||||||
|
// UIWidgets::CheckboxGraphics::Cross, false);
|
||||||
|
// UIWidgets::Tooltip("Automatically sets scale factor to fit window. Only available in pixel-perfect
|
||||||
|
// mode."); if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0)) {
|
||||||
|
// // This is just here to update the value shown on the slider.
|
||||||
|
// // The function in LUS to handle this setting will ignore IntegerScaleFactor while active.
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.Factor", integerScale_maximumBounds);
|
||||||
|
// // CVarSave();
|
||||||
|
// }
|
||||||
|
// } // End of integer scaling settings
|
||||||
|
//
|
||||||
|
// UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
||||||
|
//
|
||||||
|
// // Collapsible panel for additional settings
|
||||||
|
// if (ImGui::CollapsingHeader("Additional Settings")) {
|
||||||
|
// UIWidgets::Spacer(0);
|
||||||
|
//
|
||||||
|
//#if defined(__SWITCH__) || defined(__WIIU__)
|
||||||
|
// // Disable aspect correction, stretching the framebuffer to fill the viewport.
|
||||||
|
// // This option is only really needed on systems limited to 16:9 TV resolutions, such as consoles.
|
||||||
|
// // The associated cvar is still functional on PC platforms if you want to use it though.
|
||||||
|
// UIWidgets::PaddedEnhancementCheckbox("Disable aspect correction and stretch the output image.\n"
|
||||||
|
// "(Might be useful for 4:3 televisions!)\n"
|
||||||
|
// "Not available in Pixel Perfect Mode.",
|
||||||
|
// CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", false,
|
||||||
|
// true, CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION
|
||||||
|
// ".PixelPerfectMode", 0) ||
|
||||||
|
// disabled_everything,
|
||||||
|
// "", UIWidgets::CheckboxGraphics::Cross, false);
|
||||||
|
//#else
|
||||||
|
// if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", 0)) {
|
||||||
|
// // This setting is intentionally not exposed on PC platforms,
|
||||||
|
// // but may be accidentally activated for varying reasons.
|
||||||
|
// // Having this button should hopefully prevent support headaches.
|
||||||
|
// ImGui::TextColored(messageColor[MESSAGE_QUESTION], ICON_FA_QUESTION_CIRCLE
|
||||||
|
// " If the image is stretched and you don't know why, click this.");
|
||||||
|
// if (ImGui::Button("Click to reenable aspect correction.")) {
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IgnoreAspectCorrection", 0);
|
||||||
|
// CVarSave();
|
||||||
|
// }
|
||||||
|
// UIWidgets::Spacer(2);
|
||||||
|
// }
|
||||||
|
//#endif
|
||||||
|
//
|
||||||
|
// // A requested addition; an alternative way of displaying the resolution field.
|
||||||
|
// if (ImGui::Checkbox("Show a horizontal resolution field, instead of aspect ratio.",
|
||||||
|
// &showHorizontalResField)) {
|
||||||
|
// if (!showHorizontalResField && (aspectRatioX > 0.0f)) { // when turning this setting off
|
||||||
|
// // Refresh relevant values
|
||||||
|
// aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
|
||||||
|
// horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
||||||
|
// } else { // when turning this setting on
|
||||||
|
// item_aspectRatio = default_aspectRatio;
|
||||||
|
// if (aspectRatioX > 0.0f) {
|
||||||
|
// // Refresh relevant values in the opposite order
|
||||||
|
// horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
||||||
|
// aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// update[UPDATE_aspectRatioX] = true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Beginning of Integer Scaling additional settings.
|
||||||
|
// {
|
||||||
|
// // UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
||||||
|
//
|
||||||
|
// // Integer Scaling - Never Exceed Bounds.
|
||||||
|
// const bool disabled_neverExceedBounds =
|
||||||
|
// !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) ||
|
||||||
|
// CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.FitAutomatically", 0) ||
|
||||||
|
// disabled_everything;
|
||||||
|
// const bool checkbox_neverExceedBounds =
|
||||||
|
// UIWidgets::PaddedEnhancementCheckbox("Prevent integer scaling from exceeding screen bounds.\n"
|
||||||
|
// "(Makes screen bounds take priority over specified
|
||||||
|
// factor.)", CVAR_PREFIX_ADVANCED_RESOLUTION
|
||||||
|
// ".IntegerScale.NeverExceedBounds", true, false,
|
||||||
|
// disabled_neverExceedBounds, "",
|
||||||
|
// UIWidgets::CheckboxGraphics::Cross, true);
|
||||||
|
// UIWidgets::Tooltip(
|
||||||
|
// "Prevents integer scaling factor from exceeding screen bounds.\n\n"
|
||||||
|
// "Enabled: Will clamp the scaling factor and display a gentle warning in the resolution editor.\n"
|
||||||
|
// "Disabled: Will allow scaling to exceed screen bounds, for users who want to crop overscan.\n\n"
|
||||||
|
// " " ICON_FA_INFO_CIRCLE
|
||||||
|
// " Please note that exceeding screen bounds may show a scroll bar on-screen.");
|
||||||
|
//
|
||||||
|
// // Initialise the (currently unused) "Exceed Bounds By" cvar if it's been changed.
|
||||||
|
// if (checkbox_neverExceedBounds &&
|
||||||
|
// CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) {
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0);
|
||||||
|
// CVarSave();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Integer Scaling - Exceed Bounds By 1x/Offset.
|
||||||
|
// // A popular feature in some retro frontends/upscalers, sometimes called "crop overscan" or "1080p
|
||||||
|
// 5x".
|
||||||
|
// /*
|
||||||
|
// UIWidgets::PaddedEnhancementCheckbox("Allow integer scale factor to go +1 above maximum screen
|
||||||
|
// bounds.", CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", false, false,
|
||||||
|
// !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".PixelPerfectMode", 0) || disabled_everything, "",
|
||||||
|
// UIWidgets::CheckboxGraphics::Cross, false);
|
||||||
|
// */
|
||||||
|
// // It does actually function as expected, but exceeding the bottom of the screen shows a scroll bar.
|
||||||
|
// // I've ended up commenting this one out because of the scroll bar, and for simplicity.
|
||||||
|
//
|
||||||
|
// // Display an info message about the scroll bar.
|
||||||
|
// if (!CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.NeverExceedBounds", 1) ||
|
||||||
|
// CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) {
|
||||||
|
// if (disabled_neverExceedBounds) { // Dim this help text accordingly
|
||||||
|
// UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||||
|
// }
|
||||||
|
// ImGui::TextColored(messageColor[MESSAGE_INFO],
|
||||||
|
// " " ICON_FA_INFO_CIRCLE
|
||||||
|
// " A scroll bar may become visible if screen bounds are exceeded.");
|
||||||
|
// if (disabled_neverExceedBounds) { // Dim this help text accordingly
|
||||||
|
// UIWidgets::ReEnableComponent("disabledTooltipText");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Another support helper button, to disable the unused "Exceed Bounds By" cvar.
|
||||||
|
// // (Remove this button if uncommenting the checkbox.)
|
||||||
|
// if (CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0)) {
|
||||||
|
// if (ImGui::Button("Click to reset a console variable that may be causing this.")) {
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0);
|
||||||
|
// CVarSave();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// ImGui::Text(" ");
|
||||||
|
// }
|
||||||
|
// // UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
||||||
|
// } // End of Integer Scaling additional settings.
|
||||||
|
//
|
||||||
|
// } // End of additional settings
|
||||||
|
//
|
||||||
|
// // Clamp and update the cvars that don't use UIWidgets
|
||||||
|
// if (update[UPDATE_aspectRatioX] || update[UPDATE_aspectRatioY] || update[UPDATE_verticalPixelCount]) {
|
||||||
|
// if (update[UPDATE_aspectRatioX]) {
|
||||||
|
// if (aspectRatioX < 0.0f) {
|
||||||
|
// aspectRatioX = 0.0f;
|
||||||
|
// }
|
||||||
|
// CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioX);
|
||||||
|
// }
|
||||||
|
// if (update[UPDATE_aspectRatioY]) {
|
||||||
|
// if (aspectRatioY < 0.0f) {
|
||||||
|
// aspectRatioY = 0.0f;
|
||||||
|
// }
|
||||||
|
// CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioY);
|
||||||
|
// }
|
||||||
|
// if (update[UPDATE_verticalPixelCount]) {
|
||||||
|
// // There's a upper and lower clamp on the Libultraship side too,
|
||||||
|
// // so clamping it here is entirely visual, so the vertical resolution field reflects it.
|
||||||
|
// if (verticalPixelCount < minVerticalPixelCount) {
|
||||||
|
// verticalPixelCount = minVerticalPixelCount;
|
||||||
|
// }
|
||||||
|
// if (verticalPixelCount > maxVerticalPixelCount) {
|
||||||
|
// verticalPixelCount = maxVerticalPixelCount;
|
||||||
|
// }
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", verticalPixelCount);
|
||||||
|
// }
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", item_aspectRatio);
|
||||||
|
// CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", item_pixelCount);
|
||||||
|
// CVarSave();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
UIWidgets::Spacer(0);
|
void UpdateResolutionVars() {
|
||||||
// Vertical Resolution
|
// Clamp and update the cvars that don't use UIWidgets
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Set fixed vertical resolution (disables Resolution slider)",
|
if (update[UPDATE_aspectRatioX] || update[UPDATE_aspectRatioY] || update[UPDATE_verticalPixelCount]) {
|
||||||
"gAdvancedResolution.VerticalResolutionToggle", true, false, false, "",
|
if (update[UPDATE_aspectRatioX]) {
|
||||||
UIWidgets::CheckboxGraphics::Cross, false);
|
if (aspectRatioX < 0.0f) {
|
||||||
UIWidgets::Tooltip(
|
aspectRatioX = 0.0f;
|
||||||
"Override the resolution scale slider and use the settings below, irrespective of window size.");
|
|
||||||
if (ImGui::Combo("Pixel Count Presets", &item_pixelCount, pixelCountPresetLabels,
|
|
||||||
IM_ARRAYSIZE(pixelCountPresetLabels)) &&
|
|
||||||
item_pixelCount != default_pixelCount) { // don't change anything if "Custom" is selected.
|
|
||||||
verticalPixelCount = pixelCountPresets[item_pixelCount];
|
|
||||||
update[UPDATE_verticalPixelCount] = true;
|
|
||||||
|
|
||||||
if (showHorizontalResField) {
|
|
||||||
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
|
||||||
}
|
}
|
||||||
|
CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioX);
|
||||||
}
|
}
|
||||||
// Horizontal Resolution, if visibility is enabled for it.
|
if (update[UPDATE_aspectRatioY]) {
|
||||||
if (showHorizontalResField) {
|
if (aspectRatioY < 0.0f) {
|
||||||
// Only show the field if Aspect Ratio is being enforced.
|
aspectRatioY = 0.0f;
|
||||||
if ((aspectRatioX > 0.0f) && (aspectRatioY > 0.0f)) {
|
|
||||||
// So basically we're "faking" this one by setting aspectRatioX instead.
|
|
||||||
if (ImGui::InputInt("Horiz. Pixel Count", &horizontalPixelCount, 8, 320)) {
|
|
||||||
item_aspectRatio = default_aspectRatio;
|
|
||||||
if (horizontalPixelCount < (minVerticalPixelCount / 3.0f) * 4.0f) {
|
|
||||||
horizontalPixelCount = (minVerticalPixelCount / 3.0f) * 4.0f;
|
|
||||||
}
|
|
||||||
aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
|
|
||||||
update[UPDATE_aspectRatioX] = true;
|
|
||||||
}
|
|
||||||
} else { // Display a notice instead.
|
|
||||||
ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f },
|
|
||||||
ICON_FA_QUESTION_CIRCLE " \"Force aspect ratio\" required.");
|
|
||||||
// ImGui::Text(" ");
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Click to resolve")) {
|
|
||||||
item_aspectRatio = default_aspectRatio; // Set it to Custom
|
|
||||||
aspectRatioX = aspectRatioPresetsX[2]; // but use the 4:3 defaults
|
|
||||||
aspectRatioY = aspectRatioPresetsY[2];
|
|
||||||
update[UPDATE_aspectRatioX] = true;
|
|
||||||
update[UPDATE_aspectRatioY] = true;
|
|
||||||
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
CVarSetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioY);
|
||||||
}
|
}
|
||||||
// Vertical Resolution part 2
|
if (update[UPDATE_verticalPixelCount]) {
|
||||||
if (ImGui::InputInt("Vertical Pixel Count", &verticalPixelCount, 8, 240)) {
|
// There's a upper and lower clamp on the Libultraship side too,
|
||||||
item_pixelCount = default_pixelCount;
|
// so clamping it here is entirely visual, so the vertical resolution field reflects it.
|
||||||
update[UPDATE_verticalPixelCount] = true;
|
if (verticalPixelCount < minVerticalPixelCount) {
|
||||||
|
verticalPixelCount = minVerticalPixelCount;
|
||||||
// Account for the natural instinct to enter horizontal first.
|
|
||||||
// Ignore vertical resolutions that are below the lower clamp constant.
|
|
||||||
if (showHorizontalResField && !(verticalPixelCount < minVerticalPixelCount)) {
|
|
||||||
aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
|
|
||||||
update[UPDATE_aspectRatioX] = true;
|
|
||||||
}
|
}
|
||||||
|
if (verticalPixelCount > maxVerticalPixelCount) {
|
||||||
|
verticalPixelCount = maxVerticalPixelCount;
|
||||||
|
}
|
||||||
|
CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", verticalPixelCount);
|
||||||
}
|
}
|
||||||
|
CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", item_aspectRatio);
|
||||||
UIWidgets::Spacer(0);
|
CVarSetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", item_pixelCount);
|
||||||
// UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
CVarSave();
|
||||||
|
|
||||||
// Integer scaling settings group
|
|
||||||
if (ImGui::CollapsingHeader("Integer Scaling Settings")) {
|
|
||||||
// Pixel-perfect Mode
|
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Pixel-perfect Mode", "gAdvancedResolution.PixelPerfectMode", true,
|
|
||||||
true,
|
|
||||||
!CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0), "",
|
|
||||||
UIWidgets::CheckboxGraphics::Cross, false);
|
|
||||||
UIWidgets::Tooltip("Don't scale image to fill window.");
|
|
||||||
if (!CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0)) {
|
|
||||||
CVarSetInteger("gAdvancedResolution.PixelPerfectMode", (int) false);
|
|
||||||
CVarSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer Scaling
|
|
||||||
UIWidgets::EnhancementSliderInt("Integer scale factor: %d", "##ARSIntScale",
|
|
||||||
"gAdvancedResolution.IntegerScale.Factor", 1, max_integerScaleFactor, "%d",
|
|
||||||
1, true,
|
|
||||||
!CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) ||
|
|
||||||
CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0));
|
|
||||||
UIWidgets::Tooltip("Integer scales the image. Only available in pixel-perfect mode.");
|
|
||||||
// Display warning if size is being clamped or if framebuffer is larger than viewport.
|
|
||||||
if (CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) &&
|
|
||||||
(CVarGetInteger("gAdvancedResolution.IntegerScale.NeverExceedBounds", 1) &&
|
|
||||||
CVarGetInteger("gAdvancedResolution.IntegerScale.Factor", 1) > integerScale_maximumBounds)) {
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::TextColored({ 0.85f, 0.85f, 0.0f, 1.0f }, ICON_FA_EXCLAMATION_TRIANGLE " Window exceeded.");
|
|
||||||
}
|
|
||||||
|
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Automatically scale image to fit viewport",
|
|
||||||
"gAdvancedResolution.IntegerScale.FitAutomatically", true, true,
|
|
||||||
!CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0), "",
|
|
||||||
UIWidgets::CheckboxGraphics::Cross, false);
|
|
||||||
UIWidgets::Tooltip("Automatically sets scale factor to fit window. Only available in pixel-perfect mode.");
|
|
||||||
if (CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0)) {
|
|
||||||
// This is just here to update the value shown on the slider.
|
|
||||||
// The function in LUS to handle this setting will ignore IntegerScaleFactor while active.
|
|
||||||
CVarSetInteger("gAdvancedResolution.IntegerScale.Factor", integerScale_maximumBounds);
|
|
||||||
// CVarSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
|
|
||||||
|
|
||||||
// Collapsible panel for additional settings
|
|
||||||
if (ImGui::CollapsingHeader("Additional Settings")) {
|
|
||||||
UIWidgets::Spacer(0);
|
|
||||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
|
||||||
// Disable aspect correction, stretching the framebuffer to fill the viewport.
|
|
||||||
// This option is only really needed on systems limited to 16:9 TV resolutions, such as consoles.
|
|
||||||
// The associated CVar is still functional on PC platforms if you want to use it though.
|
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Disable aspect correction and stretch the output image.\n"
|
|
||||||
"(Might be useful for 4:3 televisions!)\n"
|
|
||||||
"Not available in Pixel Perfect Mode.",
|
|
||||||
"gAdvancedResolution.IgnoreAspectCorrection", false, true,
|
|
||||||
CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0), "",
|
|
||||||
UIWidgets::CheckboxGraphics::Cross, false);
|
|
||||||
#else
|
|
||||||
if (CVarGetInteger("gAdvancedResolution.IgnoreAspectCorrection", 0)) {
|
|
||||||
// This setting is intentionally not exposed on PC platforms,
|
|
||||||
// but may be accidentally activated for varying reasons.
|
|
||||||
// Having this button should hopefully prevent support headaches.
|
|
||||||
ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f }, ICON_FA_QUESTION_CIRCLE
|
|
||||||
" If the image is stretched and you don't know why, click this.");
|
|
||||||
if (ImGui::Button("Click to reenable aspect correction.")) {
|
|
||||||
CVarSetInteger("gAdvancedResolution.IgnoreAspectCorrection", (int) false);
|
|
||||||
CVarSave();
|
|
||||||
}
|
|
||||||
UIWidgets::Spacer(2);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ImGui::Checkbox("Show a horizontal resolution field.", &showHorizontalResField)) {
|
|
||||||
if (!showHorizontalResField && (aspectRatioX > 0.0f)) { // when turning this setting off
|
|
||||||
// Refresh relevant values
|
|
||||||
aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
|
|
||||||
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
|
||||||
} else { // when turning this setting on
|
|
||||||
item_aspectRatio = default_aspectRatio;
|
|
||||||
if (aspectRatioX > 0.0f) {
|
|
||||||
// Refresh relevant values in the opposite order
|
|
||||||
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
|
||||||
aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
update[UPDATE_aspectRatioX] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIWidgets::PaddedEnhancementCheckbox(
|
|
||||||
"Don't allow integer scaling to exceed screen bounds.\n"
|
|
||||||
"(Makes screen bounds take priority over specified factor.)",
|
|
||||||
"gAdvancedResolution.IntegerScale.NeverExceedBounds", true, false,
|
|
||||||
!CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) ||
|
|
||||||
CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0),
|
|
||||||
"", UIWidgets::CheckboxGraphics::Cross, true);
|
|
||||||
|
|
||||||
if (!CVarGetInteger("gAdvancedResolution.IntegerScale.NeverExceedBounds", 1) ||
|
|
||||||
CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
|
|
||||||
ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f },
|
|
||||||
" " ICON_FA_QUESTION_CIRCLE
|
|
||||||
" A scroll bar may become visible if screen bounds are exceeded.");
|
|
||||||
// Another helpful button for an unused CVar.
|
|
||||||
if (CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
|
|
||||||
if (ImGui::Button("Click to reset an unused CVar that may be causing this.")) {
|
|
||||||
CVarSetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0);
|
|
||||||
CVarSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
UIWidgets::Spacer(enhancementSpacerHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// I've ended up dummying this one out because it doesn't function in a satisfactory way.
|
|
||||||
// Consider this idea on the table, but I don't deem it an important enough feature to push for.
|
|
||||||
/*
|
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Allow integer scale factor to go 1x above maximum screen bounds.",
|
|
||||||
"gAdvancedResolution.IntegerScale.ExceedBoundsBy", false, false,
|
|
||||||
!CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0), "",
|
|
||||||
UIWidgets::CheckboxGraphics::Cross, false);
|
|
||||||
if (CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
|
|
||||||
ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f },
|
|
||||||
" " ICON_FA_QUESTION_CIRCLE
|
|
||||||
" A scroll bar may become visible if screen bounds are exceeded.");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
} // end of Additional Settings
|
|
||||||
|
|
||||||
// Clamp and update the CVars that don't use UIWidgets
|
|
||||||
if (IsBoolArrayTrue(update)) {
|
|
||||||
if (update[UPDATE_aspectRatioX]) {
|
|
||||||
if (aspectRatioX < 0.0f) {
|
|
||||||
aspectRatioX = 0.0f;
|
|
||||||
}
|
|
||||||
CVarSetFloat("gAdvancedResolution.AspectRatioX", aspectRatioX);
|
|
||||||
}
|
|
||||||
if (update[UPDATE_aspectRatioY]) {
|
|
||||||
if (aspectRatioY < 0.0f) {
|
|
||||||
aspectRatioY = 0.0f;
|
|
||||||
}
|
|
||||||
CVarSetFloat("gAdvancedResolution.AspectRatioY", aspectRatioY);
|
|
||||||
}
|
|
||||||
if (update[UPDATE_verticalPixelCount]) {
|
|
||||||
// There's a upper and lower clamp on the Libultraship side too,
|
|
||||||
// so clamping it here is purely visual, so the vertical resolution field reflects it.
|
|
||||||
if (verticalPixelCount < minVerticalPixelCount) {
|
|
||||||
verticalPixelCount = minVerticalPixelCount;
|
|
||||||
}
|
|
||||||
if (verticalPixelCount > maxVerticalPixelCount) {
|
|
||||||
verticalPixelCount = maxVerticalPixelCount;
|
|
||||||
}
|
|
||||||
CVarSetInteger("gAdvancedResolution.VerticalPixelCount", verticalPixelCount);
|
|
||||||
}
|
|
||||||
// Delay saving this set of CVars by a predetermined length of time, in frames.
|
|
||||||
updateCountdown = countdownStartingValue;
|
|
||||||
}
|
|
||||||
if (updateCountdown > 0) {
|
|
||||||
updateCountdown--;
|
|
||||||
} else {
|
|
||||||
CVarSave();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
// Initialise update flags.
|
||||||
|
for (uint8_t i = 0; i < sizeof(update); i++) {
|
||||||
|
update[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise integer scale bounds.
|
||||||
|
short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get
|
||||||
|
// overridden depending on viewport res
|
||||||
|
|
||||||
|
short integerScale_maximumBounds = 1; // can change when window is resized
|
||||||
|
// This is mostly just for UX purposes, as Fit Automatically logic is part of LUS.
|
||||||
|
if (((float)gfx_current_game_window_viewport.width / gfx_current_game_window_viewport.height) >
|
||||||
|
((float)gfx_current_dimensions.width / gfx_current_dimensions.height)) {
|
||||||
|
// Scale to window height
|
||||||
|
integerScale_maximumBounds = gfx_current_game_window_viewport.height / gfx_current_dimensions.height;
|
||||||
|
} else {
|
||||||
|
// Scale to window width
|
||||||
|
integerScale_maximumBounds = gfx_current_game_window_viewport.width / gfx_current_dimensions.width;
|
||||||
|
}
|
||||||
|
// Lower-clamping maximum bounds value to 1 is no-longer necessary as that's accounted for in LUS.
|
||||||
|
// Letting it go below 1 in this Editor will even allow for checking if screen bounds are being exceeded.
|
||||||
|
if (default_maxIntegerScaleFactor < integerScale_maximumBounds) {
|
||||||
|
max_integerScaleFactor = integerScale_maximumBounds +
|
||||||
|
CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".IntegerScale.ExceedBoundsBy", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combo List defaults
|
||||||
|
item_aspectRatio = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.AspectRatio", 3);
|
||||||
|
item_pixelCount = CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".UIComboItem.PixelCount", default_pixelCount);
|
||||||
|
// Stored Values for non-UIWidgets elements
|
||||||
|
aspectRatioX = CVarGetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioX", aspectRatioPresetsX[item_aspectRatio]);
|
||||||
|
aspectRatioY = CVarGetFloat(CVAR_PREFIX_ADVANCED_RESOLUTION ".AspectRatioY", aspectRatioPresetsY[item_aspectRatio]);
|
||||||
|
verticalPixelCount =
|
||||||
|
CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalPixelCount", pixelCountPresets[item_pixelCount]);
|
||||||
|
// Additional settings
|
||||||
|
showHorizontalResField = false;
|
||||||
|
horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
|
||||||
|
// Disabling flags
|
||||||
|
disabled_everything = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".Enabled", 0);
|
||||||
|
disabled_pixelCount = !CVarGetInteger(CVAR_PREFIX_ADVANCED_RESOLUTION ".VerticalResolutionToggle", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvancedResolutionSettingsWindow::UpdateElement() {
|
bool IsDroppingFrames() {
|
||||||
}
|
|
||||||
|
|
||||||
bool AdvancedResolutionSettingsWindow::IsDroppingFrames() {
|
|
||||||
// a rather imprecise way of checking for frame drops.
|
// a rather imprecise way of checking for frame drops.
|
||||||
// but it's mostly there to inform the player of large drops.
|
// but it's mostly there to inform the player of large drops.
|
||||||
const short targetFPS = CVarGetInteger("gInterpolationFPS", 30);
|
const short targetFPS = CVarGetInteger("gInterpolationFPS", 20);
|
||||||
const float threshold = targetFPS / 20.0f + 4.1f;
|
const float threshold = targetFPS / 20.0f + 4.1f;
|
||||||
return ImGui::GetIO().Framerate < targetFPS - threshold;
|
return ImGui::GetIO().Framerate < targetFPS - threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdvancedResolutionSettingsWindow::IsBoolArrayTrue(bool* foo) {
|
static RegisterMenuUpdateFunc updateFunc(UpdateResolutionVars, "Settings", "Graphics");
|
||||||
for (unsigned short i = 0; i < sizeof(&foo); i++)
|
static RegisterMenuInitFunc initFunc(RegisterResolutionWidgets);
|
||||||
if (&foo[i])
|
|
||||||
return true;
|
} // namespace BenGui
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // namespace AdvancedResolutionSettings
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,12 @@
|
||||||
#pragma once
|
#ifndef RESOLUTIONEDITOR_H
|
||||||
|
#define RESOLUTIONEDITOR_H
|
||||||
|
|
||||||
#include <libultraship/libultraship.h>
|
#include <libultraship/libultraship.h>
|
||||||
|
|
||||||
namespace AdvancedResolutionSettings {
|
namespace GameUI {
|
||||||
class AdvancedResolutionSettingsWindow : public Ship::GuiWindow {
|
bool IsDroppingFrames();
|
||||||
private:
|
void RegisterResolutionWidgets();
|
||||||
bool IsDroppingFrames();
|
void UpdateResolutionVars();
|
||||||
|
} // namespace BenGui
|
||||||
|
|
||||||
protected:
|
#endif // RESOLUTIONEDITOR_H
|
||||||
bool IsBoolArrayTrue(bool*);
|
|
||||||
|
|
||||||
public:
|
|
||||||
using Ship::GuiWindow::GuiWindow;
|
|
||||||
|
|
||||||
void InitElement() override;
|
|
||||||
void DrawElement() override;
|
|
||||||
void UpdateElement() override;
|
|
||||||
};
|
|
||||||
} // namespace AdvancedResolutionSettings
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,231 +1,711 @@
|
||||||
#pragma once
|
#ifndef UIWidgets_hpp
|
||||||
|
#define UIWidgets_hpp
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <cstdint>
|
#include <stdint.h>
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <libultraship/libultraship.h>
|
#include <libultraship/libultraship.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "port/ShipUtils.h"
|
||||||
|
|
||||||
namespace UIWidgets {
|
namespace UIWidgets {
|
||||||
|
|
||||||
struct TextFilters {
|
using SectionFunc = void(*)();
|
||||||
static int FilterNumbers(ImGuiInputTextCallbackData* data) {
|
|
||||||
if (data->EventChar < 256 && strchr("1234567890", (char) data->EventChar)) {
|
struct TextFilters {
|
||||||
return 0;
|
static int FilterNumbers(ImGuiInputTextCallbackData* data) {
|
||||||
|
if (data->EventChar < 256 && strchr("1234567890", (char)data->EventChar)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
|
static int FilterAlphaNum(ImGuiInputTextCallbackData* data) {
|
||||||
|
const char* alphanum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWYZ0123456789";
|
||||||
|
if (data->EventChar < 256 && strchr(alphanum, (char)data->EventChar)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string WrappedText(const char* text, unsigned int charactersPerLine = 60);
|
||||||
|
std::string WrappedText(const std::string& text, unsigned int charactersPerLine = 60);
|
||||||
|
void Tooltip(const char* text);
|
||||||
|
|
||||||
|
// mostly in order for colors usable by the menu without custom text color
|
||||||
|
enum Colors {
|
||||||
|
Red,
|
||||||
|
DarkRed,
|
||||||
|
Orange,
|
||||||
|
Green,
|
||||||
|
DarkGreen,
|
||||||
|
LightBlue,
|
||||||
|
Blue,
|
||||||
|
DarkBlue,
|
||||||
|
Indigo,
|
||||||
|
Violet,
|
||||||
|
Purple,
|
||||||
|
Brown,
|
||||||
|
Gray,
|
||||||
|
DarkGray,
|
||||||
|
// not suitable for menu theme use
|
||||||
|
Pink,
|
||||||
|
Yellow,
|
||||||
|
Cyan,
|
||||||
|
Black,
|
||||||
|
LightGray,
|
||||||
|
White,
|
||||||
|
NoColor
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::unordered_map<Colors, ImVec4> ColorValues = {
|
||||||
|
{ Colors::Pink, ImVec4(0.87f, 0.3f, 0.87f, 1.0f) },
|
||||||
|
{ Colors::Red, ImVec4(0.55f, 0.0f, 0.0f, 1.0f) },
|
||||||
|
{ Colors::DarkRed, ImVec4(0.3f, 0.0f, 0.0f, 1.0f) },
|
||||||
|
{ Colors::Orange, ImVec4(0.85f, 0.55f, 0.0f, 1.0f) },
|
||||||
|
{ Colors::Yellow, ImVec4(0.95f, 0.95f, 0.0f, 1.0f) },
|
||||||
|
{ Colors::Green, ImVec4(0.0f, 0.55f, 0.0f, 1.0f) },
|
||||||
|
{ Colors::DarkGreen, ImVec4(0.0f, 0.3f, 0.0f, 1.0f) },
|
||||||
|
{ Colors::Cyan, ImVec4(0.0f, 0.9f, 0.9f, 1.0f) },
|
||||||
|
{ Colors::LightBlue, ImVec4(0.0f, 0.24f, 0.8f, 1.0f) },
|
||||||
|
{ Colors::Blue, ImVec4(0.08f, 0.03f, 0.65f, 1.0f) },
|
||||||
|
{ Colors::DarkBlue, ImVec4(0.03f, 0.0f, 0.5f, 1.0f) },
|
||||||
|
{ Colors::Indigo, ImVec4(0.35f, 0.0f, 0.87f, 1.0f) },
|
||||||
|
{ Colors::Violet, ImVec4(0.5f, 0.0f, 0.9f, 1.0f) },
|
||||||
|
{ Colors::Purple, ImVec4(0.31f, 0.0f, 0.67f, 1.0f) },
|
||||||
|
{ Colors::Brown, ImVec4(0.37f, 0.18f, 0.0f, 1.0f) },
|
||||||
|
{ Colors::LightGray, ImVec4(0.75f, 0.75f, 0.75f, 1.0f) },
|
||||||
|
{ Colors::Gray, ImVec4(0.45f, 0.45f, 0.45f, 1.0f) },
|
||||||
|
{ Colors::DarkGray, ImVec4(0.15f, 0.15f, 0.15f, 1.0f) },
|
||||||
|
{ Colors::Black, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)},
|
||||||
|
{ Colors::White, ImVec4(1.0f, 1.0f, 1.0f, 1.0f) },
|
||||||
|
{ Colors::NoColor, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)},
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Sizes {
|
||||||
|
const ImVec2 Inline = ImVec2(0.0f, 0.0f);
|
||||||
|
const ImVec2 Fill = ImVec2(-1.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int FilterAlphaNum(ImGuiInputTextCallbackData* data) {
|
enum LabelPosition {
|
||||||
const char* alphanum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWYZ0123456789";
|
Near,
|
||||||
if (data->EventChar < 256 && strchr(alphanum, (char) data->EventChar)) {
|
Far,
|
||||||
return 0;
|
Above,
|
||||||
|
None,
|
||||||
|
Within,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ComponentAlignment {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WidgetOptions{
|
||||||
|
const char* tooltip = "";
|
||||||
|
bool disabled = false;
|
||||||
|
const char* disabledTooltip = "";
|
||||||
|
Colors color = Colors::NoColor;
|
||||||
|
|
||||||
|
WidgetOptions& Color(Colors color_) {
|
||||||
|
color = color = color_;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
return 1;
|
WidgetOptions& Tooltip(const char* tooltip_) {
|
||||||
|
tooltip = tooltip_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
WidgetOptions& Disabled(bool disabled_) {
|
||||||
|
disabled = disabled_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ButtonOptions : WidgetOptions {
|
||||||
|
ImVec2 size = Sizes::Fill;
|
||||||
|
Colors color = Colors::Gray;
|
||||||
|
|
||||||
|
ButtonOptions& Size(ImVec2 size_) {
|
||||||
|
size = size_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ButtonOptions& Tooltip(const char* tooltip_) {
|
||||||
|
WidgetOptions::tooltip = tooltip_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ButtonOptions& Color(Colors color_) {
|
||||||
|
WidgetOptions::color = color = color_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CheckboxOptions : WidgetOptions {
|
||||||
|
bool defaultValue = false; // Only applicable to CVarCheckbox
|
||||||
|
ComponentAlignment alignment = ComponentAlignment::Left;
|
||||||
|
LabelPosition labelPosition = LabelPosition::Near;
|
||||||
|
Colors color = WidgetOptions::color = Colors::LightBlue;
|
||||||
|
|
||||||
|
CheckboxOptions& DefaultValue(bool defaultValue_) {
|
||||||
|
defaultValue = defaultValue_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
CheckboxOptions& ComponentAlignment(ComponentAlignment alignment_) {
|
||||||
|
alignment = alignment_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
CheckboxOptions& LabelPosition(LabelPosition labelPosition_) {
|
||||||
|
labelPosition = labelPosition_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
CheckboxOptions& Tooltip(const char* tooltip_) {
|
||||||
|
WidgetOptions::tooltip = tooltip_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
CheckboxOptions& Color(Colors color_) {
|
||||||
|
WidgetOptions::color = color = color_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ComboboxOptions : WidgetOptions {
|
||||||
|
std::unordered_map<int32_t, const char*> comboMap = {};
|
||||||
|
uint32_t defaultIndex = 0; // Only applicable to CVarCombobox
|
||||||
|
ComponentAlignment alignment = ComponentAlignment::Left;
|
||||||
|
LabelPosition labelPosition = LabelPosition::Above;
|
||||||
|
ImGuiComboFlags flags = 0;
|
||||||
|
Colors color = Colors::LightBlue;
|
||||||
|
|
||||||
|
ComboboxOptions& ComboMap(std::unordered_map<int32_t, const char*> comboMap_) {
|
||||||
|
comboMap = comboMap_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ComboboxOptions& DefaultIndex(uint32_t defaultIndex_) {
|
||||||
|
defaultIndex = defaultIndex_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ComboboxOptions& ComponentAlignment(ComponentAlignment alignment_) {
|
||||||
|
alignment = alignment_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ComboboxOptions& LabelPosition(LabelPosition labelPosition_) {
|
||||||
|
labelPosition = labelPosition_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ComboboxOptions& Tooltip(const char* tooltip_) {
|
||||||
|
WidgetOptions::tooltip = tooltip_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ComboboxOptions& Color(Colors color_) {
|
||||||
|
WidgetOptions::color = color = color_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntSliderOptions : WidgetOptions {
|
||||||
|
bool showButtons = true;
|
||||||
|
const char* format = "%d";
|
||||||
|
int32_t step = 1;
|
||||||
|
int32_t min = 1;
|
||||||
|
int32_t max = 10;
|
||||||
|
int32_t defaultValue = 1;
|
||||||
|
ComponentAlignment alignment = ComponentAlignment::Left;
|
||||||
|
LabelPosition labelPosition = LabelPosition::Above;
|
||||||
|
Colors color = Colors::Gray;
|
||||||
|
ImGuiSliderFlags flags = 0;
|
||||||
|
|
||||||
|
IntSliderOptions& ShowButtons(bool showButtons_) {
|
||||||
|
showButtons = showButtons_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& Format(const char* format_) {
|
||||||
|
format = format_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& Step(int32_t step_) {
|
||||||
|
step = step_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& Min(int32_t min_) {
|
||||||
|
min = min_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& Max(int32_t max_) {
|
||||||
|
max = max_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& DefaultValue(int32_t defaultValue_) {
|
||||||
|
defaultValue = defaultValue_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& ComponentAlignment(ComponentAlignment alignment_) {
|
||||||
|
alignment = alignment_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& LabelPosition(LabelPosition labelPosition_) {
|
||||||
|
labelPosition = labelPosition_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& Tooltip(const char* tooltip_) {
|
||||||
|
WidgetOptions::tooltip = tooltip_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
IntSliderOptions& Color(Colors color_) {
|
||||||
|
WidgetOptions::color = color = color_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FloatSliderOptions : WidgetOptions {
|
||||||
|
bool showButtons = true;
|
||||||
|
const char* format = "%f";
|
||||||
|
float step = 0.01f;
|
||||||
|
float min = 0.01f;
|
||||||
|
float max = 10.0f;
|
||||||
|
float defaultValue = 1.0f;
|
||||||
|
bool isPercentage = false; // Multiplies visual value by 100
|
||||||
|
ComponentAlignment alignment = ComponentAlignment::Left;
|
||||||
|
LabelPosition labelPosition = LabelPosition::Above;
|
||||||
|
Colors color = Colors::Gray;
|
||||||
|
ImGuiSliderFlags flags = 0;
|
||||||
|
|
||||||
|
FloatSliderOptions& ShowButtons(bool showButtons_) {
|
||||||
|
showButtons = showButtons_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& Format(const char* format_) {
|
||||||
|
format = format_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& Step(float step_) {
|
||||||
|
step = step_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& Min(float min_) {
|
||||||
|
min = min_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& Max(float max_) {
|
||||||
|
max = max_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& DefaultValue(float defaultValue_) {
|
||||||
|
defaultValue = defaultValue_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& ComponentAlignment(ComponentAlignment alignment_) {
|
||||||
|
alignment = alignment_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& LabelPosition(LabelPosition labelPosition_) {
|
||||||
|
labelPosition = labelPosition_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& IsPercentage(bool isPercentage_ = true) {
|
||||||
|
isPercentage = isPercentage_;
|
||||||
|
format = "%.0f";
|
||||||
|
min = 0.0f;
|
||||||
|
max = 1.0f;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& Tooltip(const char* tooltip_) {
|
||||||
|
WidgetOptions::tooltip = tooltip_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FloatSliderOptions& Color(Colors color_) {
|
||||||
|
WidgetOptions::color = color = color_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void PushStyleMenu(const ImVec4& color);
|
||||||
|
void PushStyleMenu(Colors color = Colors::LightBlue);
|
||||||
|
void PopStyleMenu();
|
||||||
|
bool BeginMenu(const char* label, Colors color = Colors::LightBlue);
|
||||||
|
|
||||||
|
void PushStyleMenuItem(const ImVec4& color);
|
||||||
|
void PushStyleMenuItem(Colors color = Colors::LightBlue);
|
||||||
|
void PopStyleMenuItem();
|
||||||
|
bool MenuItem(const char* label, const char* shortcut = NULL, Colors color = Colors::LightBlue);
|
||||||
|
|
||||||
|
void PushStyleButton(const ImVec4& color);
|
||||||
|
void PushStyleButton(Colors color = Colors::Gray);
|
||||||
|
void PopStyleButton();
|
||||||
|
bool Button(const char* label, const ButtonOptions& options = {});
|
||||||
|
bool WindowButton(const char* label, const char* cvarName, std::shared_ptr<Ship::GuiWindow> windowPtr, const ButtonOptions& options = {});
|
||||||
|
|
||||||
|
void PushStyleCheckbox(const ImVec4& color);
|
||||||
|
void PushStyleCheckbox(Colors color = Colors::LightBlue);
|
||||||
|
void PopStyleCheckbox();
|
||||||
|
void RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash);
|
||||||
|
bool Checkbox(const char* label, bool* v, const CheckboxOptions& options = {});
|
||||||
|
bool CVarCheckbox(const char* label, const char* cvarName, const CheckboxOptions& options = {});
|
||||||
|
|
||||||
|
void PushStyleCombobox(const ImVec4& color);
|
||||||
|
void PushStyleCombobox(Colors color = Colors::LightBlue);
|
||||||
|
void PopStyleCombobox();
|
||||||
|
|
||||||
|
/*using ComboVariant = std::variant<const std::unordered_map<int32_t, const char*>&, const std::vector<const char*>&>;
|
||||||
|
|
||||||
|
bool Combobox(const char* label, int32_t* value, ComboVariant comboSource, const ComboboxOptions& options = {}) {
|
||||||
|
bool dirty = false;
|
||||||
|
float startX = ImGui::GetCursorPosX();
|
||||||
|
std::string invisibleLabelStr = "##" + std::string(label);
|
||||||
|
const char* invisibleLabel = invisibleLabelStr.c_str();
|
||||||
|
ImGui::PushID(label);
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
ImGui::BeginDisabled(options.disabled);
|
||||||
|
PushStyleCombobox(options.color);
|
||||||
|
if (options.alignment == ComponentAlignment::Left) {
|
||||||
|
if (options.labelPosition == LabelPosition::Above) {
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x - ImGui::GetStyle().ItemSpacing.x * 2);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
||||||
|
ImGui::SetNextItemWidth(ImGui::CalcTextSize(comboMap.at(*value)).x + ImGui::GetStyle().FramePadding.x * 4 + ImGui::GetStyle().ItemSpacing.x);
|
||||||
|
}
|
||||||
|
} else if (options.alignment == ComponentAlignment::Right) {
|
||||||
|
if (options.labelPosition == LabelPosition::Above) {
|
||||||
|
ImGui::NewLine();
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SameLine(ImGui::CalcTextSize(label).x + ImGui::GetStyle().ItemSpacing.x * 2);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
||||||
|
float width = ImGui::CalcTextSize(comboMap.at(*value)).x + ImGui::GetStyle().FramePadding.x * 4;
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - width);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::BeginCombo(invisibleLabel, comboMap.at(*value), options.flags)) {
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f, 10.0f));
|
||||||
|
for (const auto& pair : comboMap) {
|
||||||
|
if (strlen(pair.second) > 1) {
|
||||||
|
if (ImGui::Selectable(pair.second, pair.first == *value)) {
|
||||||
|
*value = pair.first;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
if (options.alignment == ComponentAlignment::Left) {
|
||||||
|
if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far) {
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
}
|
||||||
|
} else if (options.alignment == ComponentAlignment::Right) {
|
||||||
|
if (options.labelPosition == LabelPosition::Near || options.labelPosition == LabelPosition::Far) {
|
||||||
|
ImGui::SameLine(startX);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PopStyleCombobox();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::EndGroup();
|
||||||
|
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)) {
|
||||||
|
ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str());
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
return dirty;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// MARK: - Enums
|
bool CVarCombobox(const char* label, const char* cvarName, ComboVariant comboSource, const ComboboxOptions& options = {}) {
|
||||||
|
bool dirty = false;
|
||||||
|
int32_t value = CVarGetInteger(cvarName, options.defaultIndex);
|
||||||
|
if (Combobox(label, &value, comboSource, options)) {
|
||||||
|
CVarSetInteger(cvarName, value);
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
||||||
|
ShipInit::Init(cvarName);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
return dirty;
|
||||||
|
}*/
|
||||||
|
|
||||||
enum class CheckboxGraphics { Cross, Checkmark, None };
|
template <typename T>
|
||||||
constexpr float maxSliderWidth = 260.0f;
|
bool Combobox(const char* label, T* value, const std::unordered_map<T, const char*>& comboMap, const ComboboxOptions& options = {}) {
|
||||||
#ifdef __SWITCH__
|
bool dirty = false;
|
||||||
constexpr float sliderButtonWidth = 42.0f;
|
float startX = ImGui::GetCursorPosX();
|
||||||
#elif defined(__WIIU__)
|
std::string invisibleLabelStr = "##" + std::string(label);
|
||||||
constexpr float sliderButtonWidth = 60.0f;
|
const char* invisibleLabel = invisibleLabelStr.c_str();
|
||||||
#else
|
ImGui::PushID(label);
|
||||||
constexpr float sliderButtonWidth = 30.0f;
|
ImGui::BeginGroup();
|
||||||
#endif
|
ImGui::BeginDisabled(options.disabled);
|
||||||
|
PushStyleCombobox(options.color);
|
||||||
|
if (options.alignment == ComponentAlignment::Left) {
|
||||||
|
if (options.labelPosition == LabelPosition::Above) {
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x - ImGui::GetStyle().ItemSpacing.x * 2);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
||||||
|
ImGui::SetNextItemWidth(ImGui::CalcTextSize(comboMap.at(*value)).x + ImGui::GetStyle().FramePadding.x * 4 + ImGui::GetStyle().ItemSpacing.x);
|
||||||
|
}
|
||||||
|
} else if (options.alignment == ComponentAlignment::Right) {
|
||||||
|
if (options.labelPosition == LabelPosition::Above) {
|
||||||
|
ImGui::NewLine();
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SameLine(ImGui::CalcTextSize(label).x + ImGui::GetStyle().ItemSpacing.x * 2);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
||||||
|
float width = ImGui::CalcTextSize(comboMap.at(*value)).x + ImGui::GetStyle().FramePadding.x * 4;
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - width);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::BeginCombo(invisibleLabel, comboMap.at(*value), options.flags)) {
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f, 10.0f));
|
||||||
|
for (const auto& pair : comboMap) {
|
||||||
|
if (strlen(pair.second) > 1) {
|
||||||
|
if (ImGui::Selectable(pair.second, pair.first == *value)) {
|
||||||
|
*value = pair.first;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
if (options.alignment == ComponentAlignment::Left) {
|
||||||
|
if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far) {
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
}
|
||||||
|
} else if (options.alignment == ComponentAlignment::Right) {
|
||||||
|
if (options.labelPosition == LabelPosition::Near || options.labelPosition == LabelPosition::Far) {
|
||||||
|
ImGui::SameLine(startX);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PopStyleCombobox();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::EndGroup();
|
||||||
|
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)) {
|
||||||
|
ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str());
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
char* WrappedText(const char* text, unsigned int charactersPerLine = 60);
|
template <typename T = size_t>
|
||||||
char* WrappedText(const std::string& text, unsigned int charactersPerLine);
|
bool Combobox(const char* label, T* value, const std::vector<const char*>& comboVector, const ComboboxOptions& options = {}) {
|
||||||
|
bool dirty = false;
|
||||||
|
float startX = ImGui::GetCursorPosX();
|
||||||
|
size_t currentValueIndex = static_cast<size_t>(*value);
|
||||||
|
std::string invisibleLabelStr = "##" + std::string(label);
|
||||||
|
const char* invisibleLabel = invisibleLabelStr.c_str();
|
||||||
|
ImGui::PushID(label);
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
ImGui::BeginDisabled(options.disabled);
|
||||||
|
PushStyleCombobox(options.color);
|
||||||
|
if (options.alignment == ComponentAlignment::Left) {
|
||||||
|
if (options.labelPosition == LabelPosition::Above) {
|
||||||
|
ImGui::Text(label);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x - ImGui::GetStyle().ItemSpacing.x * 2);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
||||||
|
ImGui::SetNextItemWidth(ImGui::CalcTextSize(comboVector.at(currentValueIndex)).x + ImGui::GetStyle().FramePadding.x * 4 + ImGui::GetStyle().ItemSpacing.x);
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SameLine(ImGui::CalcTextSize(label).x + ImGui::GetStyle().ItemSpacing.x * 2);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
||||||
|
float width = ImGui::CalcTextSize(comboVector.at(currentValueIndex)).x + ImGui::GetStyle().FramePadding.x * 4;
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - width);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::BeginCombo(invisibleLabel, comboVector.at(currentValueIndex), options.flags)) {
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f, 10.0f));
|
||||||
|
for (size_t i = 0; i < comboVector.size(); ++i) {
|
||||||
|
auto newValue = static_cast<T>(i);
|
||||||
|
if (strlen(comboVector.at(i)) > 1) {
|
||||||
|
if (ImGui::Selectable(comboVector.at(i), newValue == *value)) {
|
||||||
|
*value = newValue;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else if (options.alignment == ComponentAlignment::Right) {
|
||||||
|
if (options.labelPosition == LabelPosition::Near || options.labelPosition == LabelPosition::Far) {
|
||||||
|
ImGui::SameLine(startX);
|
||||||
|
ImGui::Text(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PopStyleCombobox();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::EndGroup();
|
||||||
|
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)) {
|
||||||
|
ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str());
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
void SetLastItemHoverText(const std::string& text);
|
template <typename T = size_t, size_t N>
|
||||||
void SetLastItemHoverText(const char* text);
|
bool Combobox(const char* label, T* value, const char* (&comboArray)[N], const ComboboxOptions& options = {}) {
|
||||||
|
bool dirty = false;
|
||||||
|
float startX = ImGui::GetCursorPosX();
|
||||||
|
size_t currentValueIndex = static_cast<size_t>(*value);
|
||||||
|
if (currentValueIndex >= N) {
|
||||||
|
currentValueIndex = 0;
|
||||||
|
}
|
||||||
|
std::string invisibleLabelStr = "##" + std::string(label);
|
||||||
|
const char* invisibleLabel = invisibleLabelStr.c_str();
|
||||||
|
ImGui::PushID(label);
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
ImGui::BeginDisabled(options.disabled);
|
||||||
|
PushStyleCombobox(options.color);
|
||||||
|
if (options.alignment == ComponentAlignment::Left) {
|
||||||
|
if (options.labelPosition == LabelPosition::Above) {
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x - ImGui::GetStyle().ItemSpacing.x * 2);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
||||||
|
ImGui::SetNextItemWidth(ImGui::CalcTextSize(comboArray[currentValueIndex]).x + ImGui::GetStyle().FramePadding.x * 4 + ImGui::GetStyle().ItemSpacing.x);
|
||||||
|
}
|
||||||
|
} else if (options.alignment == ComponentAlignment::Right) {
|
||||||
|
if (options.labelPosition == LabelPosition::Above) {
|
||||||
|
ImGui::NewLine();
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SameLine(ImGui::CalcTextSize(label).x + ImGui::GetStyle().ItemSpacing.x * 2);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far || options.labelPosition == LabelPosition::None) {
|
||||||
|
float width = ImGui::CalcTextSize(comboArray[currentValueIndex]).x + ImGui::GetStyle().FramePadding.x * 4;
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - width);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::BeginCombo(invisibleLabel, comboArray[currentValueIndex], options.flags)) {
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f, 10.0f));
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
auto newValue = static_cast<T>(i);
|
||||||
|
if (strlen(comboArray[i]) > 1) {
|
||||||
|
if (ImGui::Selectable(comboArray[i], newValue == *value)) {
|
||||||
|
*value = newValue;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
if (options.alignment == ComponentAlignment::Left) {
|
||||||
|
if (options.labelPosition == LabelPosition::Near) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
} else if (options.labelPosition == LabelPosition::Far) {
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(label).x);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
}
|
||||||
|
} else if (options.alignment == ComponentAlignment::Right) {
|
||||||
|
if (options.labelPosition == LabelPosition::Near || options.labelPosition == LabelPosition::Far) {
|
||||||
|
ImGui::SameLine(startX);
|
||||||
|
ImGui::Text("%s", label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PopStyleCombobox();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::EndGroup();
|
||||||
|
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)) {
|
||||||
|
ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str());
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
void InsertHelpHoverText(const std::string& text);
|
template <typename T = int32_t>
|
||||||
void InsertHelpHoverText(const char* text);
|
bool CVarCombobox(const char* label, const char* cvarName, const std::unordered_map<T, const char*>& comboMap, const ComboboxOptions& options = {}) {
|
||||||
|
bool dirty = false;
|
||||||
|
int32_t value = CVarGetInteger(cvarName, options.defaultIndex);
|
||||||
|
if (Combobox<T>(label, &value, comboMap, options)) {
|
||||||
|
CVarSetInteger(cvarName, value);
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
void Tooltip(const char* text);
|
template <typename T = int32_t>
|
||||||
void Spacer(float height);
|
bool CVarCombobox(const char* label, const char* cvarName, const std::vector<const char*>& comboVector, const ComboboxOptions& options = {}) {
|
||||||
void PaddedSeparator(bool padTop = true, bool padBottom = true, float extraVerticalTopPadding = 0.0f,
|
bool dirty = false;
|
||||||
float extraVerticalBottomPadding = 0.0f);
|
int32_t value = CVarGetInteger(cvarName, options.defaultIndex);
|
||||||
|
if (Combobox<T>(label, &value, comboVector, options)) {
|
||||||
|
CVarSetInteger(cvarName, value);
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
void RenderCross(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz);
|
template <typename T = int32_t, size_t N>
|
||||||
bool CustomCheckbox(const char* label, bool* v, bool disabled, CheckboxGraphics disabledGraphic);
|
bool CVarCombobox(const char* label, const char* cvarName, const char* (&comboArray)[N], const ComboboxOptions& options = {}) {
|
||||||
|
bool dirty = false;
|
||||||
|
int32_t value = CVarGetInteger(cvarName, options.defaultIndex);
|
||||||
|
if (Combobox<T>(label, &value, comboArray, options)) {
|
||||||
|
CVarSetInteger(cvarName, value);
|
||||||
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
void ReEnableComponent(const char* disabledTooltipText);
|
void PushStyleSlider(Colors color = Colors::LightBlue);
|
||||||
void DisableComponent(const float alpha);
|
void PopStyleSlider();
|
||||||
|
bool SliderInt(const char* label, int32_t* value, const IntSliderOptions& options = {});
|
||||||
|
bool CVarSliderInt(const char* label, const char* cvarName, const IntSliderOptions& options = {});
|
||||||
|
bool SliderFloat(const char* label, float* value, const FloatSliderOptions& options = {});
|
||||||
|
bool CVarSliderFloat(const char* label, const char* cvarName, const FloatSliderOptions& options = {});
|
||||||
|
bool CVarColorPicker(const char* label, const char* cvarName, Color_RGBA8 defaultColor);
|
||||||
|
void DrawFlagArray32(const std::string& name, uint32_t& flags);
|
||||||
|
void DrawFlagArray16(const std::string& name, uint16_t& flags);
|
||||||
|
void DrawFlagArray8(const std::string& name, uint8_t& flags);
|
||||||
|
void DrawFlagArray8Mask(const std::string& name, uint8_t& flags);
|
||||||
|
}
|
||||||
|
|
||||||
bool EnhancementCheckbox(const char* text, const char* cvarName, bool disabled = false,
|
#endif /* UIWidgets_hpp */
|
||||||
const char* disabledTooltipText = "",
|
|
||||||
CheckboxGraphics disabledGraphic = CheckboxGraphics::Cross, bool defaultValue = false);
|
|
||||||
bool PaddedEnhancementCheckbox(const char* text, const char* cvarName, bool padTop = true, bool padBottom = true,
|
|
||||||
bool disabled = false, const char* disabledTooltipText = "",
|
|
||||||
CheckboxGraphics disabledGraphic = CheckboxGraphics::Cross, bool defaultValue = false);
|
|
||||||
|
|
||||||
bool EnhancementCombobox(const char* cvarName, std::span<const char*, std::dynamic_extent> comboArray,
|
|
||||||
uint8_t defaultIndex, bool disabled = false, const char* disabledTooltipText = "",
|
|
||||||
uint8_t disabledValue = -1);
|
|
||||||
bool LabeledRightAlignedEnhancementCombobox(const char* label, const char* cvarName,
|
|
||||||
std::span<const char*, std::dynamic_extent> comboArray,
|
|
||||||
uint8_t defaultIndex, bool disabled = false,
|
|
||||||
const char* disabledTooltipText = "", uint8_t disabledValue = -1);
|
|
||||||
|
|
||||||
void PaddedText(const char* text, bool padTop = true, bool padBottom = true);
|
|
||||||
|
|
||||||
bool EnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format,
|
|
||||||
int defaultValue = 0, bool PlusMinusButton = true, bool disabled = false,
|
|
||||||
const char* disabledTooltipText = "");
|
|
||||||
bool PaddedEnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max,
|
|
||||||
const char* format, int defaultValue = 0, bool PlusMinusButton = true,
|
|
||||||
bool padTop = true, bool padBottom = true, bool disabled = false,
|
|
||||||
const char* disabledTooltipText = "");
|
|
||||||
bool EnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max,
|
|
||||||
const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton = true,
|
|
||||||
bool disabled = false, const char* disabledTooltipText = "");
|
|
||||||
bool PaddedEnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max,
|
|
||||||
const char* format, float defaultValue, bool isPercentage,
|
|
||||||
bool PlusMinusButton = true, bool padTop = true, bool padBottom = true,
|
|
||||||
bool disabled = false, const char* disabledTooltipText = "");
|
|
||||||
|
|
||||||
bool EnhancementRadioButton(const char* text, const char* cvarName, int id);
|
|
||||||
|
|
||||||
bool DrawResetColorButton(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha);
|
|
||||||
bool DrawRandomizeColorButton(const char* cvarName, ImVec4* colors);
|
|
||||||
void DrawLockColorCheckbox(const char* cvarName);
|
|
||||||
void RainbowColor(const char* cvarName, ImVec4* colors);
|
|
||||||
|
|
||||||
void LoadPickersColors(ImVec4& ColorArray, const char* cvarname, const ImVec4& default_colors, bool has_alpha);
|
|
||||||
bool EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors,
|
|
||||||
bool allow_rainbow = true, bool has_alpha = false, bool TitleSameLine = false);
|
|
||||||
|
|
||||||
void DrawFlagArray32(const std::string& name, uint32_t& flags);
|
|
||||||
void DrawFlagArray16(const std::string& name, uint16_t& flags);
|
|
||||||
void DrawFlagArray8(const std::string& name, uint8_t& flags);
|
|
||||||
|
|
||||||
// V2
|
|
||||||
namespace Colors {
|
|
||||||
const ImVec4 White = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
||||||
const ImVec4 Gray = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);
|
|
||||||
const ImVec4 DarkGray = ImVec4(0.1f, 0.1f, 0.1f, 1.0f);
|
|
||||||
const ImVec4 Indigo = ImVec4(0.24f, 0.31f, 0.71f, 1.0f);
|
|
||||||
const ImVec4 Red = ImVec4(0.5f, 0.0f, 0.0f, 1.0f);
|
|
||||||
const ImVec4 DarkRed = ImVec4(0.3f, 0.0f, 0.0f, 1.0f);
|
|
||||||
const ImVec4 LightGreen = ImVec4(0.0f, 0.7f, 0.0f, 1.0f);
|
|
||||||
const ImVec4 Green = ImVec4(0.0f, 0.5f, 0.0f, 1.0f);
|
|
||||||
const ImVec4 DarkGreen = ImVec4(0.0f, 0.3f, 0.0f, 1.0f);
|
|
||||||
const ImVec4 Yellow = ImVec4(1.0f, 0.627f, 0.0f, 1.0f);
|
|
||||||
}; // namespace Colors
|
|
||||||
|
|
||||||
namespace Sizes {
|
|
||||||
const ImVec2 Inline = ImVec2(0.0f, 0.0f);
|
|
||||||
const ImVec2 Fill = ImVec2(-1.0f, 0.0f);
|
|
||||||
} // namespace Sizes
|
|
||||||
|
|
||||||
enum LabelPosition {
|
|
||||||
Near,
|
|
||||||
Far,
|
|
||||||
Above,
|
|
||||||
None,
|
|
||||||
Within,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ComponentAlignment {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
};
|
|
||||||
|
|
||||||
void PushStyleMenu(const ImVec4& color = Colors::Indigo);
|
|
||||||
void PopStyleMenu();
|
|
||||||
bool BeginMenu(const char* label, const ImVec4& color = Colors::Indigo);
|
|
||||||
|
|
||||||
void PushStyleMenuItem(const ImVec4& color = Colors::Indigo);
|
|
||||||
void PopStyleMenuItem();
|
|
||||||
bool MenuItem(const char* label, const char* shortcut = NULL, const ImVec4& color = Colors::Indigo);
|
|
||||||
|
|
||||||
struct ButtonOptions {
|
|
||||||
const ImVec4 color = Colors::Gray;
|
|
||||||
const ImVec2 size = Sizes::Fill;
|
|
||||||
const char* tooltip = "";
|
|
||||||
bool disabled = false;
|
|
||||||
const char* disabledTooltip = "";
|
|
||||||
};
|
|
||||||
|
|
||||||
void PushStyleButton(const ImVec4& color = Colors::Gray);
|
|
||||||
void PopStyleButton();
|
|
||||||
bool Button(const char* label, const ButtonOptions& options = {});
|
|
||||||
bool WindowButton(const char* label, const char* cvarName, std::shared_ptr<Ship::GuiWindow> windowPtr,
|
|
||||||
const ButtonOptions& options = {});
|
|
||||||
|
|
||||||
struct CheckboxOptions {
|
|
||||||
const ImVec4 color = Colors::Indigo;
|
|
||||||
const char* tooltip = "";
|
|
||||||
bool disabled = false;
|
|
||||||
const char* disabledTooltip = "";
|
|
||||||
bool defaultValue = false; // Only applicable to CVarCheckbox
|
|
||||||
ComponentAlignment alignment = ComponentAlignment::Left;
|
|
||||||
LabelPosition labelPosition = LabelPosition::Near;
|
|
||||||
};
|
|
||||||
|
|
||||||
void PushStyleCheckbox(const ImVec4& color = Colors::Indigo);
|
|
||||||
void PopStyleCheckbox();
|
|
||||||
bool Checkbox(const char* label, bool* v, const CheckboxOptions& options = {});
|
|
||||||
bool CVarCheckbox(const char* label, const char* cvarName, const CheckboxOptions& options = {});
|
|
||||||
|
|
||||||
struct ComboboxOptions {
|
|
||||||
const ImVec4 color = Colors::Indigo;
|
|
||||||
const char* tooltip = "";
|
|
||||||
bool disabled = false;
|
|
||||||
const char* disabledTooltip = "";
|
|
||||||
uint32_t defaultIndex = 0; // Only applicable to CVarCombobox
|
|
||||||
ComponentAlignment alignment = ComponentAlignment::Left;
|
|
||||||
LabelPosition labelPosition = LabelPosition::Above;
|
|
||||||
ImGuiComboFlags flags = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
void PushStyleCombobox(const ImVec4& color = Colors::Indigo);
|
|
||||||
void PopStyleCombobox();
|
|
||||||
bool Combobox(const char* label, uint8_t* value, std::span<const char*, std::dynamic_extent> comboArray,
|
|
||||||
const ComboboxOptions& options = {});
|
|
||||||
bool CVarCombobox(const char* label, const char* cvarName, std::span<const char*, std::dynamic_extent> comboArray,
|
|
||||||
const ComboboxOptions& options = {});
|
|
||||||
|
|
||||||
struct IntSliderOptions {
|
|
||||||
const ImVec4 color = Colors::Gray;
|
|
||||||
const char* tooltip = "";
|
|
||||||
bool disabled = false;
|
|
||||||
const char* disabledTooltip = "";
|
|
||||||
bool showButtons = true;
|
|
||||||
ImGuiSliderFlags flags = 0;
|
|
||||||
const char* format = "%d";
|
|
||||||
const uint32_t step = 1;
|
|
||||||
ComponentAlignment alignment = ComponentAlignment::Left;
|
|
||||||
LabelPosition labelPosition = LabelPosition::Above;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FloatSliderOptions {
|
|
||||||
const ImVec4 color = Colors::Gray;
|
|
||||||
const char* tooltip = "";
|
|
||||||
bool disabled = false;
|
|
||||||
const char* disabledTooltip = "";
|
|
||||||
bool showButtons = true;
|
|
||||||
ImGuiSliderFlags flags = 0;
|
|
||||||
const char* format = "%f";
|
|
||||||
const float step = 0.01f;
|
|
||||||
bool isPercentage = false; // Multiplies visual value by 100
|
|
||||||
ComponentAlignment alignment = ComponentAlignment::Left;
|
|
||||||
LabelPosition labelPosition = LabelPosition::Above;
|
|
||||||
};
|
|
||||||
|
|
||||||
void PushStyleSlider(const ImVec4& color = Colors::Indigo);
|
|
||||||
void PopStyleSlider();
|
|
||||||
bool SliderInt(const char* label, int32_t* value, int32_t min, int32_t max, const IntSliderOptions& options = {});
|
|
||||||
bool CVarSliderInt(const char* label, const char* cvarName, int32_t min, int32_t max, const int32_t defaultValue,
|
|
||||||
const IntSliderOptions& options = {});
|
|
||||||
bool SliderFloat(const char* label, float* value, float min, float max, const FloatSliderOptions& options = {});
|
|
||||||
bool CVarSliderFloat(const char* label, const char* cvarName, float min, float max, const float defaultValue,
|
|
||||||
const FloatSliderOptions& options = {});
|
|
||||||
} // namespace UIWidgets
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue