diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index a30d6ef..f99ff93 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -163,7 +163,14 @@ set(SWA_PATCHES_CXX_SOURCES "patches/video_patches.cpp" ) -set(SWA_UI_CXX_SOURCES +set(SWA_UI_CXX_SOURCES + "ui/reddog/windows/counter_window.cpp" + "ui/reddog/windows/exports_window.cpp" + "ui/reddog/windows/view_window.cpp" + "ui/reddog/windows/window_list.cpp" + "ui/reddog/reddog_controls.cpp" + "ui/reddog/reddog_manager.cpp" + "ui/reddog/reddog_window.cpp" "ui/achievement_menu.cpp" "ui/achievement_overlay.cpp" "ui/installer_wizard.cpp" @@ -453,6 +460,22 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/co BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/kbm.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/kbm.dds" ARRAY_NAME "g_kbm" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_NAME "g_select_fade" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_NAME "g_select_fill" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_close_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_close_1.dds" ARRAY_NAME "g_button_close_1") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_close_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_close_2.dds" ARRAY_NAME "g_button_close_2") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_minimum_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_minimum_1.dds" ARRAY_NAME "g_button_minimum_1") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_minimum_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_minimum_2.dds" ARRAY_NAME "g_button_minimum_2") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_pin_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_pin_1.dds" ARRAY_NAME "g_button_pin_1") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_pin_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_pin_2.dds" ARRAY_NAME "g_button_pin_2") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/checkbox_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/checkbox_1.dds" ARRAY_NAME "g_checkbox_1") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/checkbox_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/checkbox_2.dds" ARRAY_NAME "g_checkbox_2") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/debug_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/debug_icon.dds" ARRAY_NAME "g_debug_icon") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/common_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/common_icon.dds" ARRAY_NAME "g_common_icon") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor.bmp" ARRAY_NAME "g_mouse_cursor") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor_h.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor_h.bmp" ARRAY_NAME "g_mouse_cursor_h") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor_slant_l.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor_slant_l.bmp" ARRAY_NAME "g_mouse_cursor_slant_l") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor_slant_r.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor_slant_r.bmp" ARRAY_NAME "g_mouse_cursor_slant_r") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor_v.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor_v.bmp" ARRAY_NAME "g_mouse_cursor_v") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/title_bar.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/title_bar.dds" ARRAY_NAME "g_title_bar") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/arrow_circle.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/arrow_circle.dds" ARRAY_NAME "g_arrow_circle" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_NAME "g_install_001" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_002.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_002.dds" ARRAY_NAME "g_install_002" COMPRESSION_TYPE "zstd") diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 45dad00..a1ec929 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1186,9 +1187,16 @@ static void CreateImGuiBackend() AchievementMenu::Init(); AchievementOverlay::Init(); ButtonGuide::Init(); + InstallerWizard::Init(); MessageWindow::Init(); OptionsMenu::Init(); - InstallerWizard::Init(); + +#if !_DEBUG + if (Config::Debug) +#endif + { + Reddog::Manager::Init(); + } ImGui_ImplSDL2_InitForOther(GameWindow::s_pWindow); @@ -1943,82 +1951,58 @@ static double g_applicationValues[PROFILER_VALUE_COUNT]; static Profiler g_presentProfiler; static Profiler g_renderDirectorProfiler; -static bool g_profilerVisible; -static bool g_profilerWasToggled; - -static void DrawProfiler() +void Video::DrawCounter() { - bool toggleProfiler = SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_F1] != 0; + g_applicationValues[g_profilerValueIndex] = App::s_deltaTime * 1000.0; - if (!g_profilerWasToggled && toggleProfiler) - g_profilerVisible = !g_profilerVisible; + const double applicationAvg = std::accumulate(g_applicationValues, g_applicationValues + PROFILER_VALUE_COUNT, 0.0) / PROFILER_VALUE_COUNT; + double presentAvg = g_presentProfiler.UpdateAndReturnAverage(); + double renderDirectorAvg = g_renderDirectorProfiler.UpdateAndReturnAverage(); - g_profilerWasToggled = toggleProfiler; - - if (!g_profilerVisible) - return; - - ImFont* font = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); - float defaultScale = font->Scale; - font->Scale = ImGui::GetDefaultFont()->FontSize / font->FontSize; - ImGui::PushFont(font); - - if (ImGui::Begin("Profiler", &g_profilerVisible)) + if (ImPlot::BeginPlot("Frame Time")) { - g_applicationValues[g_profilerValueIndex] = App::s_deltaTime * 1000.0; - - const double applicationAvg = std::accumulate(g_applicationValues, g_applicationValues + PROFILER_VALUE_COUNT, 0.0) / PROFILER_VALUE_COUNT; - double presentAvg = g_presentProfiler.UpdateAndReturnAverage(); - double renderDirectorAvg = g_renderDirectorProfiler.UpdateAndReturnAverage(); - - if (ImPlot::BeginPlot("Frame Time")) - { - ImPlot::SetupAxisLimits(ImAxis_Y1, 0.0, 20.0); - ImPlot::SetupAxis(ImAxis_Y1, "ms", ImPlotAxisFlags_None); - ImPlot::PlotLine("Application", g_applicationValues, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); - ImPlot::PlotLine("Present", g_presentProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); - ImPlot::PlotLine("Render Director", g_renderDirectorProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); - ImPlot::EndPlot(); - } - - g_profilerValueIndex = (g_profilerValueIndex + 1) % PROFILER_VALUE_COUNT; - - ImGui::Text("Current Application: %g ms (%g FPS)", App::s_deltaTime * 1000.0, 1.0 / App::s_deltaTime); - ImGui::Text("Current Present: %g ms (%g FPS)", g_presentProfiler.value.load(), 1000.0 / g_presentProfiler.value.load()); - ImGui::Text("Current Render Director: %g ms (%g FPS)", g_renderDirectorProfiler.value.load(), 1000.0 / g_renderDirectorProfiler.value.load()); - ImGui::NewLine(); - - ImGui::Text("Average Application: %g ms (%g FPS)", applicationAvg, 1000.0 / applicationAvg); - ImGui::Text("Average Present: %g ms (%g FPS)", presentAvg, 1000.0 / presentAvg); - ImGui::Text("Average Render Director: %g ms (%g FPS)", renderDirectorAvg, 1000.0 / renderDirectorAvg); - ImGui::NewLine(); - - O1HeapDiagnostics diagnostics, physicalDiagnostics; - { - std::lock_guard lock(g_userHeap.mutex); - diagnostics = o1heapGetDiagnostics(g_userHeap.heap); - } - { - std::lock_guard lock(g_userHeap.physicalMutex); - physicalDiagnostics = o1heapGetDiagnostics(g_userHeap.physicalHeap); - } - - ImGui::Text("Heap Allocated: %d MB", int32_t(diagnostics.allocated / (1024 * 1024))); - ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024))); - ImGui::NewLine(); - - ImGui::Text("Present Wait: %s", g_capabilities.presentWait ? "Supported" : "Unsupported"); - ImGui::Text("Triangle Fan: %s", g_capabilities.triangleFan ? "Supported" : "Unsupported"); - ImGui::NewLine(); - - const char* sdlVideoDriver = SDL_GetCurrentVideoDriver(); - if (sdlVideoDriver != nullptr) - ImGui::Text("SDL Video Driver: %s", sdlVideoDriver); + ImPlot::SetupAxisLimits(ImAxis_Y1, 0.0, 20.0); + ImPlot::SetupAxis(ImAxis_Y1, "ms", ImPlotAxisFlags_None); + ImPlot::PlotLine("Application", g_applicationValues, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); + ImPlot::PlotLine("Present", g_presentProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); + ImPlot::PlotLine("Render Director", g_renderDirectorProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); + ImPlot::EndPlot(); } - ImGui::End(); - ImGui::PopFont(); - font->Scale = defaultScale; + g_profilerValueIndex = (g_profilerValueIndex + 1) % PROFILER_VALUE_COUNT; + + ImGui::Text("Current Application: %g ms (%g FPS)", App::s_deltaTime * 1000.0, 1.0 / App::s_deltaTime); + ImGui::Text("Current Present: %g ms (%g FPS)", g_presentProfiler.value.load(), 1000.0 / g_presentProfiler.value.load()); + ImGui::Text("Current Render Director: %g ms (%g FPS)", g_renderDirectorProfiler.value.load(), 1000.0 / g_renderDirectorProfiler.value.load()); + ImGui::NewLine(); + + ImGui::Text("Average Application: %g ms (%g FPS)", applicationAvg, 1000.0 / applicationAvg); + ImGui::Text("Average Present: %g ms (%g FPS)", presentAvg, 1000.0 / presentAvg); + ImGui::Text("Average Render Director: %g ms (%g FPS)", renderDirectorAvg, 1000.0 / renderDirectorAvg); + ImGui::NewLine(); + + O1HeapDiagnostics diagnostics, physicalDiagnostics; + { + std::lock_guard lock(g_userHeap.mutex); + diagnostics = o1heapGetDiagnostics(g_userHeap.heap); + } + { + std::lock_guard lock(g_userHeap.physicalMutex); + physicalDiagnostics = o1heapGetDiagnostics(g_userHeap.physicalHeap); + } + + ImGui::Text("Heap Allocated: %d MB", int32_t(diagnostics.allocated / (1024 * 1024))); + ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024))); + ImGui::NewLine(); + + auto capabilities = g_device->getCapabilities(); + ImGui::Text("Present Wait: %s", capabilities.presentWait ? "Supported" : "Unsupported"); + ImGui::Text("Triangle Fan: %s", capabilities.triangleFan ? "Supported" : "Unsupported"); + ImGui::NewLine(); + + const char* sdlVideoDriver = SDL_GetCurrentVideoDriver(); + if (sdlVideoDriver != nullptr) + ImGui::Text("SDL Video Driver: %s", sdlVideoDriver); } static void DrawImGui() @@ -2052,7 +2036,12 @@ static void DrawImGui() ButtonGuide::Draw(); Fader::Draw(); - DrawProfiler(); +#if !_DEBUG + if (Config::Debug) +#endif + { + Reddog::Manager::Draw(); + } ImGui::Render(); diff --git a/UnleashedRecomp/gpu/video.h b/UnleashedRecomp/gpu/video.h index d1472f9..0bd0cad 100644 --- a/UnleashedRecomp/gpu/video.h +++ b/UnleashedRecomp/gpu/video.h @@ -20,6 +20,7 @@ struct Video static void Present(); static void StartPipelinePrecompilation(); static void WaitForGPU(); + static void DrawCounter(); }; struct GuestSamplerState diff --git a/UnleashedRecomp/ui/game_window.cpp b/UnleashedRecomp/ui/game_window.cpp index 4460240..626a6a2 100644 --- a/UnleashedRecomp/ui/game_window.cpp +++ b/UnleashedRecomp/ui/game_window.cpp @@ -12,6 +12,11 @@ #pragma comment(lib, "dwmapi.lib") #endif +#include +#include +#include +#include +#include #include #include @@ -105,7 +110,7 @@ int Window_OnSDLEvent(void*, SDL_Event* event) GameWindow::s_isFocused = true; if (GameWindow::IsFullscreen()) - SDL_ShowCursor(GameWindow::s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE); + SDL_ShowCursor(GameWindow::s_isCursorVisible ? SDL_ENABLE : SDL_DISABLE); break; } @@ -146,7 +151,7 @@ int Window_OnSDLEvent(void*, SDL_Event* event) if (event->type == SDL_CONTROLLERBUTTONDOWN || event->type == SDL_CONTROLLERBUTTONUP || event->type == SDL_CONTROLLERAXISMOTION) { // Hide mouse cursor when controller input is detected. - SDL_ShowCursor(SDL_DISABLE); + SDL_ShowCursor(GameWindow::s_isCursorVisible ? SDL_ENABLE : SDL_DISABLE); } else if (event->type == SDL_MOUSEMOTION) { @@ -264,23 +269,23 @@ void GameWindow::Update() s_isChangingDisplay = false; } -SDL_Surface* GameWindow::GetIconSurface(void* pIconBmp, size_t iconSize) +SDL_Surface* GameWindow::CreateSurface(void* pBitmapData, size_t bitmapSize) { - auto rw = SDL_RWFromMem(pIconBmp, iconSize); + auto rw = SDL_RWFromMem(pBitmapData, bitmapSize); auto surface = SDL_LoadBMP_RW(rw, 1); if (!surface) - LOGF_ERROR("Failed to load icon: {}", SDL_GetError()); + LOGF_ERROR("Failed to create surface: {}", SDL_GetError()); return surface; } void GameWindow::SetIcon(void* pIconBmp, size_t iconSize) { - if (auto icon = GetIconSurface(pIconBmp, iconSize)) + if (auto surface = CreateSurface(pIconBmp, iconSize)) { - SDL_SetWindowIcon(s_pWindow, icon); - SDL_FreeSurface(icon); + SDL_SetWindowIcon(s_pWindow, surface); + SDL_FreeSurface(surface); } } @@ -296,6 +301,69 @@ void GameWindow::SetIcon(bool isNight) } } +void GameWindow::SetCursor(ECursorType cursorType) +{ + if (s_currentCursor == cursorType) + return; + + s_currentCursor = cursorType; + + uint8_t* pBitmapData; + size_t bitmapSize; + int x, y; + + switch (cursorType) + { + case ECursorType::DebugDefault: + pBitmapData = g_mouse_cursor; + bitmapSize = sizeof(g_mouse_cursor); + x = 0; + y = 0; + break; + + case ECursorType::DebugHorizontal: + pBitmapData = g_mouse_cursor_h; + bitmapSize = sizeof(g_mouse_cursor_h); + x = 15; + y = 15; + break; + + case ECursorType::DebugVertical: + pBitmapData = g_mouse_cursor_v; + bitmapSize = sizeof(g_mouse_cursor_v); + x = 15; + y = 15; + break; + + case ECursorType::DebugLeftDiagonal: + pBitmapData = g_mouse_cursor_slant_l; + bitmapSize = sizeof(g_mouse_cursor_slant_l); + x = 16; + y = 15; + break; + + case ECursorType::DebugRightDiagonal: + pBitmapData = g_mouse_cursor_slant_r; + bitmapSize = sizeof(g_mouse_cursor_slant_r); + x = 16; + y = 15; + break; + + default: + SDL_SetCursor(SDL_GetDefaultCursor()); + return; + } + + if (auto surface = CreateSurface(pBitmapData, bitmapSize)) + { + if (auto cursor = SDL_CreateColorCursor(surface, x, y)) + { + SDL_SetCursor(cursor); + SDL_FreeSurface(surface); + } + } +} + const char* GameWindow::GetTitle() { return Config::Language == ELanguage::Japanese @@ -338,7 +406,7 @@ bool GameWindow::SetFullscreen(bool isEnabled) if (isEnabled) { SDL_SetWindowFullscreen(s_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); - SDL_ShowCursor(s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE); + SDL_ShowCursor(s_isCursorVisible ? SDL_ENABLE : SDL_DISABLE); } else { @@ -352,13 +420,13 @@ bool GameWindow::SetFullscreen(bool isEnabled) return isEnabled; } -void GameWindow::SetFullscreenCursorVisibility(bool isVisible) +void GameWindow::SetCursorVisibility(bool isVisible) { - s_isFullscreenCursorVisible = isVisible; + s_isCursorVisible = isVisible; if (IsFullscreen()) { - SDL_ShowCursor(s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE); + SDL_ShowCursor(s_isCursorVisible ? SDL_ENABLE : SDL_DISABLE); } else { diff --git a/UnleashedRecomp/ui/game_window.h b/UnleashedRecomp/ui/game_window.h index 6bd703a..45facb5 100644 --- a/UnleashedRecomp/ui/game_window.h +++ b/UnleashedRecomp/ui/game_window.h @@ -9,6 +9,16 @@ #define MIN_WIDTH 640 #define MIN_HEIGHT 480 +enum class ECursorType +{ + Default, + DebugDefault, + DebugHorizontal, + DebugVertical, + DebugLeftDiagonal, + DebugRightDiagonal +}; + class GameWindow { public: @@ -22,18 +32,21 @@ public: static inline bool s_isFocused; static inline bool s_isIconNight; - static inline bool s_isFullscreenCursorVisible; + static inline bool s_isCursorVisible; static inline bool s_isChangingDisplay; - static SDL_Surface* GetIconSurface(void* pIconBmp, size_t iconSize); + static inline ECursorType s_currentCursor; + + static SDL_Surface* CreateSurface(void* pBitmapData, size_t bitmapSize); static void SetIcon(void* pIconBmp, size_t iconSize); static void SetIcon(bool isNight = false); + static void SetCursor(ECursorType cursorType = ECursorType::Default); static const char* GetTitle(); static void SetTitle(const char* title = nullptr); static void SetTitleBarColour(); static bool IsFullscreen(); static bool SetFullscreen(bool isEnabled); - static void SetFullscreenCursorVisibility(bool isVisible); + static void SetCursorVisibility(bool isVisible); static bool IsMaximised(); static EWindowState SetMaximised(bool isEnabled); static SDL_Rect GetDimensions(); diff --git a/UnleashedRecomp/ui/installer_wizard.cpp b/UnleashedRecomp/ui/installer_wizard.cpp index f6f7425..d804d5e 100644 --- a/UnleashedRecomp/ui/installer_wizard.cpp +++ b/UnleashedRecomp/ui/installer_wizard.cpp @@ -1557,7 +1557,7 @@ bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame) g_currentPage = g_firstPage; } - GameWindow::SetFullscreenCursorVisibility(true); + GameWindow::SetCursorVisibility(true); s_isVisible = true; while (s_isVisible) @@ -1569,7 +1569,7 @@ bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame) Video::Present(); } - GameWindow::SetFullscreenCursorVisibility(false); + GameWindow::SetCursorVisibility(false); NFD_Quit(); InstallerWizard::Shutdown(); diff --git a/UnleashedRecomp/ui/reddog/reddog_controls.cpp b/UnleashedRecomp/ui/reddog/reddog_controls.cpp new file mode 100644 index 0000000..32e1a39 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/reddog_controls.cpp @@ -0,0 +1,59 @@ +#include "reddog_controls.h" +#include + +#include +#include + +constexpr float CHECKBOX_SIZE = 16.0f; + +static std::unique_ptr g_upCheckbox1; +static std::unique_ptr g_upCheckbox2; + +void Reddog::InitControlsResources() +{ + g_upCheckbox1 = LoadTexture(g_checkbox_1, sizeof(g_checkbox_1)); + g_upCheckbox2 = LoadTexture(g_checkbox_2, sizeof(g_checkbox_2)); +} + +bool Reddog::Checkbox(const char* label, bool* v) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + + if (window->SkipItems) + return false; + + auto& ctx = *GImGui; + auto& style = ctx.Style; + auto id = window->GetID(label); + auto labelSize = ImGui::CalcTextSize(label, nullptr, true); + auto pos = window->DC.CursorPos; + + ImVec2 size = { CHECKBOX_SIZE, CHECKBOX_SIZE }; + ImRect checkBoundingBox(pos, ImVec2(pos.x + size.x, pos.y + size.y)); + ImRect totalBoundingBox(pos, ImVec2(pos.x + size.x + (labelSize.x > 0.0f ? style.ItemInnerSpacing.x + labelSize.x : 0.0f), pos.y + size.y)); + + ImGui::ItemSize(totalBoundingBox, style.FramePadding.y); + + if (!ImGui::ItemAdd(totalBoundingBox, id)) + return false; + + bool isHovered, isHeld; + bool isPressed = ImGui::ButtonBehavior(checkBoundingBox, id, &isHovered, &isHeld); + + if (isPressed) + { + *v = !*v; + ImGui::MarkItemEdited(id); + } + + auto texture = *v + ? g_upCheckbox2.get() + : g_upCheckbox1.get(); + + window->DrawList->AddImage(texture, checkBoundingBox.Min, checkBoundingBox.Max); + + if (labelSize.x > 0.0f) + ImGui::RenderText({ checkBoundingBox.Max.x + style.ItemInnerSpacing.x, checkBoundingBox.Min.y + style.FramePadding.y }, label); + + return isPressed; +} diff --git a/UnleashedRecomp/ui/reddog/reddog_controls.h b/UnleashedRecomp/ui/reddog/reddog_controls.h new file mode 100644 index 0000000..cc7a3bf --- /dev/null +++ b/UnleashedRecomp/ui/reddog/reddog_controls.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Reddog +{ + void InitControlsResources(); + bool Checkbox(const char* label, bool* v); +} diff --git a/UnleashedRecomp/ui/reddog/reddog_manager.cpp b/UnleashedRecomp/ui/reddog/reddog_manager.cpp new file mode 100644 index 0000000..27d7b53 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/reddog_manager.cpp @@ -0,0 +1,146 @@ +#include "reddog_manager.h" +#include +#include +#include +#include +#include + +#include + +static std::unique_ptr g_upDebugIcon; + +static ImVec2 g_debugIconPosOrig; +static ImVec2 g_debugIconPos = { 0, 0 }; + +static bool g_isDebugIconClicked = false; +static bool g_isDraggingDebugIcon = false; +static bool g_isReddogToggled = false; +static bool g_isWindowListVisible = false; + +void Reddog::Manager::Init() +{ + g_upDebugIcon = LoadTexture(g_debug_icon, sizeof(g_debug_icon)); + + Reddog::InitControlsResources(); + Reddog::InitWindowResources(); +} + +void Reddog::Manager::Draw() +{ + bool isReddogToggled = SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_F1] != 0; + + if (!g_isReddogToggled && isReddogToggled) + { + s_isVisible = !s_isVisible; + + if (!s_isVisible) + { + g_debugIconPos = { 0, 0 }; + GameWindow::SetCursor(ECursorType::Default); + GameWindow::SetCursorVisibility(false); + } + } + + g_isReddogToggled = isReddogToggled; + + if (!s_isVisible) + return; + + if (GameWindow::s_currentCursor == ECursorType::Default) + { + GameWindow::SetCursor(ECursorType::DebugDefault); + GameWindow::SetCursorVisibility(true); + } + + auto drawList = ImGui::GetForegroundDrawList(); + auto& res = ImGui::GetIO().DisplaySize; + + auto debugIconSize = Scale(32); + ImVec2 debugIconMin = g_debugIconPos; + ImVec2 debugIconMax = { g_debugIconPos.x + debugIconSize, g_debugIconPos.y + debugIconSize }; + + drawList->AddImage(g_upDebugIcon.get(), debugIconMin, debugIconMax); + + if (ImGui::IsMouseHoveringRect(debugIconMin, debugIconMax, false) && ImGui::IsMouseClicked(0)) + { + g_debugIconPosOrig = g_debugIconPos; + g_isDraggingDebugIcon = true; + } + + g_isDebugIconClicked = false; + + if (g_isDraggingDebugIcon && ImGui::IsMouseDown(0)) + { + auto& delta = ImGui::GetIO().MouseDelta; + g_debugIconPos.x += delta.x; + g_debugIconPos.y += delta.y; + } + else + { + if (g_isDraggingDebugIcon && + g_debugIconPos.x == g_debugIconPosOrig.x && + g_debugIconPos.y == g_debugIconPosOrig.y) + { + g_isDebugIconClicked = true; + g_isWindowListVisible = !g_isWindowListVisible; + } + + g_isDraggingDebugIcon = false; + } + + if (auto pWindowList = GetWindow(WINDOW_LIST_NAME)) + { + if (!g_isDebugIconClicked && !pWindowList->IsFocused) + pWindowList->IsVisible = g_isWindowListVisible = false; + + pWindowList->IsVisible = g_isWindowListVisible; + + if (pWindowList->ImWindow) + { + if (debugIconMin.x > res.x / 2) + { + pWindowList->ImWindow->Pos.x = debugIconMax.x - pWindowList->ImWindow->Size.x; + } + else + { + pWindowList->ImWindow->Pos.x = debugIconMin.x; + } + + if (debugIconMin.y > res.y / 2) + { + pWindowList->ImWindow->Pos.y = debugIconMin.y - pWindowList->ImWindow->Size.y; + } + else + { + pWindowList->ImWindow->Pos.y = debugIconMin.y + debugIconSize; + } + } + } + + for (auto& pWindow : Reddog::GetWindows()) + { + auto pTrueWindow = (Reddog::Window*)pWindow; + + if (!pTrueWindow->IsVisible) + continue; + + pWindow->Draw(); + } +} + +Reddog::Window* Reddog::Manager::GetWindow(const char* pName) +{ + for (auto pWindow : Reddog::GetWindows()) + { + if (strcmp(pWindow->GetName(), pName) == 0) + return (Reddog::Window*)pWindow; + } + + return nullptr; +} + +template +T* Reddog::Manager::GetWindow(const char* pName) +{ + return (T*)Reddog::Manager::GetWindow(pName); +} diff --git a/UnleashedRecomp/ui/reddog/reddog_manager.h b/UnleashedRecomp/ui/reddog/reddog_manager.h new file mode 100644 index 0000000..20c93b3 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/reddog_manager.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Reddog +{ + class Manager + { + public: + static inline bool s_isVisible = false; + + static void Init(); + static void Draw(); + + static Window* GetWindow(const char* pName); + + template + static T* GetWindow(const char* pName); + }; +} diff --git a/UnleashedRecomp/ui/reddog/reddog_window.cpp b/UnleashedRecomp/ui/reddog/reddog_window.cpp new file mode 100644 index 0000000..06c9c5d --- /dev/null +++ b/UnleashedRecomp/ui/reddog/reddog_window.cpp @@ -0,0 +1,364 @@ +#include "reddog_window.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr float MIN_WINDOW_SIZE = 21.0f; + +static std::unique_ptr g_upButtonClose1; +static std::unique_ptr g_upButtonClose2; +static std::unique_ptr g_upButtonMinimum1; +static std::unique_ptr g_upButtonMinimum2; +static std::unique_ptr g_upButtonPin1; +static std::unique_ptr g_upButtonPin2; +static std::unique_ptr g_upCommonIcon; +static std::unique_ptr g_upTitleBar; +static std::unique_ptr g_upWindowFrame; + +ImFont* g_font; +float g_defaultFontScale; + +ImGuiStyle g_defaultStyle; + +static std::vector& Reddog::GetWindows() +{ + static std::vector g_windows; + return g_windows; +} + +void Reddog::InitWindowResources() +{ + g_font = ImFontAtlasSnapshot::GetFont("micross.ttf"); + g_defaultFontScale = g_font->Scale; + + auto& style = ImGui::GetStyle(); + g_defaultStyle = style; + + g_upButtonClose1 = LoadTexture(g_button_close_1, sizeof(g_button_close_1)); + g_upButtonClose2 = LoadTexture(g_button_close_2, sizeof(g_button_close_2)); + g_upButtonMinimum1 = LoadTexture(g_button_minimum_1, sizeof(g_button_minimum_1)); + g_upButtonMinimum2 = LoadTexture(g_button_minimum_2, sizeof(g_button_minimum_2)); + g_upButtonPin1 = LoadTexture(g_button_pin_1, sizeof(g_button_pin_1)); + g_upButtonPin2 = LoadTexture(g_button_pin_2, sizeof(g_button_pin_2)); + g_upCommonIcon = LoadTexture(g_common_icon, sizeof(g_common_icon)); + g_upTitleBar = LoadTexture(g_title_bar, sizeof(g_title_bar)); +} + +void Reddog::Window::BeginStyle() +{ + g_font->Scale = ImGui::GetDefaultFont()->FontSize / g_font->FontSize; + ImGui::PushFont(g_font); + + UpdateStyle(); +} + +void Reddog::Window::UpdateStyle() +{ + auto& style = ImGui::GetStyle(); + + style.WindowMinSize = { 0, MIN_WINDOW_SIZE }; + + auto colBg = IsFocused + ? ImU32ToImVec4(ActiveColour) + : ImU32ToImVec4(InactiveColour); + + auto colBlack = ImVec4(0, 0, 0, 1); + auto colTrans = ImVec4(0, 0, 0, 0); + auto colWhite = ImVec4(1, 1, 1, 1); + + style.Colors[ImGuiCol_Border] = colTrans; + style.Colors[ImGuiCol_Button] = colTrans; + style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.8f, 0.788f, 0.741f, 1); + style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.722f, 0.714f, 0.667f, 1); + style.Colors[ImGuiCol_CheckMark] = colBlack; + style.Colors[ImGuiCol_ChildBg] = colBg; + style.Colors[ImGuiCol_FrameBg] = colBg; + style.Colors[ImGuiCol_PopupBg] = colBg; + style.Colors[ImGuiCol_ResizeGrip] = colTrans; + style.Colors[ImGuiCol_ResizeGripActive] = colTrans; + style.Colors[ImGuiCol_ResizeGripHovered] = colTrans; + style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.6f, 0.6f, 0.6f, 1); + style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(1, 0.6f, 0, 1); + style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(1, 0.6f, 0, 1); + style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(1, 0.6f, 0, 1); + style.Colors[ImGuiCol_SeparatorActive] = colTrans; + style.Colors[ImGuiCol_SeparatorHovered] = colTrans; + style.Colors[ImGuiCol_SliderGrab] = colWhite; + style.Colors[ImGuiCol_Tab] = ImVec4(0.467f, 0.467f, 0.467f, 1); + style.Colors[ImGuiCol_TabActive] = ImVec4(0.6f, 0.6f, 0.6f, 1); + style.Colors[ImGuiCol_TabHovered] = ImVec4(0.667f, 0.667f, 0.667f, 1); + style.Colors[ImGuiCol_Text] = colBlack; + style.Colors[ImGuiCol_WindowBg] = colBg; +} + +void Reddog::Window::EndStyle() +{ + ImGui::PopFont(); + g_font->Scale = g_defaultFontScale; + + ImGui::GetStyle() = g_defaultStyle; +} + +void Reddog::Window::CreateWindowGrips() +{ + if (!ImWindow || !IsFocused) + return; + + auto& io = ImGui::GetIO(); + + auto windowPos = ImWindow->Pos; + auto windowSize = ImWindow->Size; + + constexpr float resizeMargin = 5.0f; + + ImRect grips[6] = + { + // Corners + ImRect(windowPos.x, windowPos.y + windowSize.y - resizeMargin, windowPos.x + resizeMargin, windowPos.y + windowSize.y), // Bottom left + ImRect(windowPos.x + windowSize.x - resizeMargin, windowPos.y + windowSize.y - resizeMargin, windowPos.x + windowSize.x, windowPos.y + windowSize.y), // Bottom right + + // Edges + ImRect(windowPos.x + resizeMargin, windowPos.y, windowPos.x + windowSize.x - resizeMargin, windowPos.y + resizeMargin), // Top + ImRect(windowPos.x + resizeMargin, windowPos.y + windowSize.y - resizeMargin, windowPos.x + windowSize.x - resizeMargin, windowPos.y + windowSize.y), // Bottom + ImRect(windowPos.x, windowPos.y + resizeMargin, windowPos.x + resizeMargin, windowPos.y + windowSize.y - resizeMargin), // Left + ImRect(windowPos.x + windowSize.x - resizeMargin, windowPos.y + resizeMargin, windowPos.x + windowSize.x, windowPos.y + windowSize.y - resizeMargin) // Right + }; + + auto hoveredGrip = -1; + + for (int i = 0; i < IM_ARRAYSIZE(grips); i++) + { + auto& grip = grips[i]; + + if (ImGui::IsMouseHoveringRect(grip.Min, grip.Max)) + hoveredGrip = i; + } + + if (hoveredGrip < 0) + { + GameWindow::SetCursor(ECursorType::DebugDefault); + } + else + { + if (hoveredGrip == 0) + { + GameWindow::SetCursor(ECursorType::DebugRightDiagonal); + } + else if (hoveredGrip == 1) + { + GameWindow::SetCursor(ECursorType::DebugLeftDiagonal); + } + else if (hoveredGrip == 2 || hoveredGrip == 3) + { + GameWindow::SetCursor(ECursorType::DebugVertical); + } + else + { + GameWindow::SetCursor(ECursorType::DebugHorizontal); + } + } +} + +void Reddog::Window::DrawFrame() +{ + if (!ImWindow) + return; + + if ((Flags & eWindowFlags_NoResize) == 0) + CreateWindowGrips(); + + UpdateStyle(); + + auto drawList = ImGui::GetWindowDrawList(); + auto windowPos = ImWindow->Pos; + auto windowSize = ImWindow->Size; + auto isFocused = ImGui::IsWindowFocused(); + auto hasTitleBar = (Flags & eWindowFlags_NoTitleBar) == 0; + + float titleBarHeight = hasTitleBar ? 20.0f : 0.0f; + + ImVec2 windowMin = { windowPos.x, windowPos.y + titleBarHeight }; + ImVec2 windowMax = { windowPos.x + windowSize.x, windowPos.y + windowSize.y }; + + drawList->AddRectFilled(windowMin, windowMax, isFocused ? ActiveColour : InactiveColour); + drawList->AddLine(windowMin, { windowMax.x, windowMin.y }, IM_COL32(170, 170, 170, 255)); // Outer top line. + drawList->AddLine({ windowMin.x + 1, windowMin.y + 1 }, { windowMin.x + 1, windowMax.y }, IM_COL32(153, 153, 153, 255)); // Outer left line. + drawList->AddLine({ windowMin.x, windowMax.y - 2 }, { windowMax.x, windowMax.y - 2 }, IM_COL32(51, 51, 51, 255)); // Outer bottom line. + drawList->AddLine({ windowMax.x - 2, windowMin.y + 1 }, { windowMax.x - 2, windowMax.y }, IM_COL32(85, 85, 85, 255)); // Outer right line. + drawList->AddLine({ windowMin.x + 2, windowMin.y + 1 }, { windowMax.x - 2, windowMin.y + 1 }, IM_COL32(51, 51, 51, 255)); // Inner top line. + drawList->AddLine({ windowMin.x + 2, windowMin.y + 2 }, { windowMin.x + 2, windowMax.y - 2 }, IM_COL32(85, 85, 85, 255)); // Inner left line. + drawList->AddLine({ windowMin.x + 3, windowMax.y - 3 }, { windowMax.x - 2, windowMax.y - 3 }, IM_COL32(170, 170, 170, 255)); // Inner bottom line. + drawList->AddLine({ windowMax.x - 3, windowMin.y + 2 }, { windowMax.x - 3, windowMax.y - 3 }, IM_COL32(153, 153, 153, 255)); // Inner right line. + + // Close window if unfocused whilst unpinned (yes it works like this in SU preview). + if (!IsPinned && !isFocused && m_pIsVisible) + *m_pIsVisible = !*m_pIsVisible; + + if (hasTitleBar) + { + auto height = titleBarHeight; + auto left = PIXELS_TO_UV_COORDS(32, 32, 0, 0, 3, 32); + auto centre = PIXELS_TO_UV_COORDS(32, 32, 3, 0, 26, 32); + auto right = PIXELS_TO_UV_COORDS(32, 32, 29, 0, 3, 32); + + auto col = isFocused + ? IM_COL32(245, 245, 245, 255) + : IM_COL32(246, 226, 202, 255); + + drawList->AddImage(g_upTitleBar.get(), { windowPos.x, windowPos.y }, { windowPos.x + 3, windowPos.y + height }, GET_UV_COORDS(left), col); + drawList->AddImage(g_upTitleBar.get(), { windowPos.x + 3, windowPos.y }, { windowPos.x + windowSize.x - 3, windowPos.y + height }, GET_UV_COORDS(centre), col); + drawList->AddImage(g_upTitleBar.get(), { windowPos.x + windowSize.x - 3, windowPos.y }, { windowPos.x + windowSize.x, windowPos.y + height }, GET_UV_COORDS(right), col); + + auto cmnSize = 16.0f; + + ImVec2 cmnIconMin = { windowPos.x + 9, windowPos.y + 2 }; + ImVec2 cmnIconMax = { cmnIconMin.x + cmnSize, cmnIconMin.y + cmnSize }; + + drawList->AddImage(g_upCommonIcon.get(), cmnIconMin, cmnIconMax, { 0, 0 }, { 1, 1 }, IM_COL32(255, 255, 255, 255)); + drawList->AddText({ cmnIconMax.x + 5, windowPos.y + 4 }, IM_COL32(255, 255, 255, 255), Name); + + ImVec2 closeButtonMin = { windowPos.x + windowSize.x - cmnSize - 4, windowPos.y + 3 }; + ImVec2 closeButtonMax = { closeButtonMin.x + cmnSize, closeButtonMin.y + cmnSize }; + + ImGui::SetCursorScreenPos(closeButtonMin); + ImGui::InvisibleButton("##CloseButton", { cmnSize, cmnSize }); + bool isCloseButtonActive = ImGui::IsItemActive(); + + if (isCloseButtonActive) + { + drawList->AddImage(g_upButtonClose2.get(), closeButtonMin, closeButtonMax); + m_isCloseButtonPressed = true; + } + else + { + drawList->AddImage(g_upButtonClose1.get(), closeButtonMin, closeButtonMax); + + if (m_isCloseButtonPressed && m_pIsVisible) + { + *m_pIsVisible = !*m_pIsVisible; + m_isCloseButtonPressed = false; + } + } + + ImVec2 minimumButtonMin = { closeButtonMin.x - cmnSize - 4, windowPos.y + 3 }; + ImVec2 minimumButtonMax = { minimumButtonMin.x + cmnSize, minimumButtonMin.y + cmnSize }; + + ImGui::SetCursorScreenPos(minimumButtonMin); + ImGui::InvisibleButton("##MinimumButton", { cmnSize, cmnSize }); + bool isMinimumButtonActive = ImGui::IsItemActive(); + + if (isMinimumButtonActive) + { + drawList->AddImage(g_upButtonMinimum2.get(), minimumButtonMin, minimumButtonMax); + m_isMinimumButtonPressed = true; + } + else + { + drawList->AddImage(g_upButtonMinimum1.get(), minimumButtonMin, minimumButtonMax); + + if (m_isMinimumButtonPressed) + { + IsMinimum = !IsMinimum; + + if (IsMinimum) + { + m_preMinimumWindowSize = windowSize; + ImGui::SetWindowSize(ImWindow, { windowSize.x, MIN_WINDOW_SIZE }); + } + else + { + ImGui::SetWindowSize(ImWindow, { windowSize.x, m_preMinimumWindowSize.y }); + } + + m_isMinimumButtonPressed = false; + } + } + + ImVec2 pinButtonMin = { minimumButtonMin.x - cmnSize - 4, windowPos.y + 3 }; + ImVec2 pinButtonMax = { pinButtonMin.x + cmnSize, pinButtonMin.y + cmnSize }; + + ImGui::SetCursorScreenPos(pinButtonMin); + ImGui::InvisibleButton("##PinButton", { cmnSize, cmnSize }); + bool isPinButtonActive = ImGui::IsItemActive(); + + if (isPinButtonActive) + { + drawList->AddImage(g_upButtonPin2.get(), pinButtonMin, pinButtonMax); + m_isPinButtonPressed = true; + } + else + { + drawList->AddImage(g_upButtonPin1.get(), pinButtonMin, pinButtonMax); + + if (m_isPinButtonPressed) + { + IsPinned = !IsPinned; + m_isPinButtonPressed = false; + } + } + } +} + +void Reddog::Window::SetVisible(bool isVisible) +{ + IsVisible = m_isSetVisible = isVisible; +} + +bool Reddog::Window::Begin(bool* pIsVisible) +{ + if (pIsVisible) + { + m_pIsVisible = pIsVisible; + } + else + { + m_pIsVisible = &IsVisible; + } + + BeginStyle(); + + ImGui::SetNextWindowSizeConstraints({ m_minWidth, m_minHeight }, { m_maxWidth, m_maxHeight }); + + auto flags = REDDOG_IMGUI_FLAGS; + + if ((Flags & eWindowFlags_NoResize) != 0) + flags |= ImGuiWindowFlags_NoResize; + + bool result = ImGui::Begin(Name, m_pIsVisible, flags); + + if (result && *m_pIsVisible) + { + ImWindow = ImGui::GetCurrentWindow(); + IsFocused = ImGui::IsWindowFocused(); + + if (m_isSetVisible) + { + ImGui::SetWindowFocus(); + m_isSetVisible = false; + } + + DrawFrame(); + + return true; + } + + return false; +} + +void Reddog::Window::End() +{ + ImGui::End(); + EndStyle(); +} diff --git a/UnleashedRecomp/ui/reddog/reddog_window.h b/UnleashedRecomp/ui/reddog/reddog_window.h new file mode 100644 index 0000000..4fd9369 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/reddog_window.h @@ -0,0 +1,87 @@ +#pragma once + +#define REDDOG_IMGUI_FLAGS ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoBackground + +namespace Reddog +{ + enum EWindowFlags : uint32_t + { + eWindowFlags_None = 0, + eWindowFlags_NoTitleBar = 1 << 0, + eWindowFlags_NoResize = 1 << 1, + eWindowFlags_NoListEntry = 1 << 2 + }; + + class IWindow + { + public: + virtual ~IWindow() = default; + virtual const char* GetName() = 0; + virtual void Draw() = 0; + }; + + extern std::vector& GetWindows(); + + class Window : public IWindow + { + private: + bool* m_pIsVisible{ nullptr }; + + bool m_isSetVisible{}; + bool m_isCloseButtonPressed{}; + bool m_isMinimumButtonPressed{}; + bool m_isPinButtonPressed{}; + + ImVec2 m_preMinimumWindowSize{}; + + void BeginStyle(); + void UpdateStyle(); + void EndStyle(); + void CreateWindowGrips(); + void DrawFrame(); + + protected: + float m_minWidth{ 175.0f }; + float m_minHeight{}; + float m_maxWidth{ FLT_MAX }; + float m_maxHeight{ FLT_MAX }; + + public: + const char* Name{ "(null)" }; + EWindowFlags Flags{ eWindowFlags_None }; + bool IsFocused{ true }; + bool IsMinimum{}; + bool IsPinned{ true }; + bool IsVisible{}; + ImGuiWindow* ImWindow{ nullptr }; + ImU32 ActiveColour{ IM_COL32(232, 231, 255, 255) }; + ImU32 InactiveColour{ IM_COL32(193, 192, 181, 255) }; + + Window() + { + GetWindows().push_back(this); + } + + Window(const char* pName, EWindowFlags flags = eWindowFlags_None) : Name(pName), Flags(flags) + { + GetWindows().push_back(this); + } + + const char* GetName() override + { + return Name; + } + + void Draw() override + { + Begin(); + End(); + } + + void SetVisible(bool isVisible = true); + bool Begin(bool* pIsVisible = nullptr); + void End(); + }; + + void InitWindowResources(); +} diff --git a/UnleashedRecomp/ui/reddog/windows/counter_window.cpp b/UnleashedRecomp/ui/reddog/windows/counter_window.cpp new file mode 100644 index 0000000..fb67a5f --- /dev/null +++ b/UnleashedRecomp/ui/reddog/windows/counter_window.cpp @@ -0,0 +1,13 @@ +#include "counter_window.h" +#include + +static CounterWindow g_window; + +void CounterWindow::Draw() +{ + if (Begin()) + { + Video::DrawCounter(); + } + End(); +} diff --git a/UnleashedRecomp/ui/reddog/windows/counter_window.h b/UnleashedRecomp/ui/reddog/windows/counter_window.h new file mode 100644 index 0000000..5b702a7 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/windows/counter_window.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +class CounterWindow : public Reddog::Window +{ +public: + CounterWindow() : Window() + { + Name = "Counter"; + + m_minWidth = 215; + m_minHeight = 328; + } + + void Draw() override; +}; diff --git a/UnleashedRecomp/ui/reddog/windows/exports_window.cpp b/UnleashedRecomp/ui/reddog/windows/exports_window.cpp new file mode 100644 index 0000000..cda15a0 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/windows/exports_window.cpp @@ -0,0 +1,19 @@ +#include "exports_window.h" +#include +#include + +static ExportsWindow g_window{ "Exports" }; + +void ExportsWindow::Draw() +{ + if (Begin()) + { + ImGui::TextColored({ 1, 0, 0, 1 }, "For testing purposes only, use Hedge Mod Manager to toggle these."); + Reddog::Checkbox("Allow Cancelling Unleash", &Config::AllowCancellingUnleash.Value); + Reddog::Checkbox("Fix Unleash Out of Control Drain", &Config::FixUnleashOutOfControlDrain.Value); + Reddog::Checkbox("Homing Attack on Boost", &Config::HomingAttackOnBoost.Value); + Reddog::Checkbox("Save Score at Checkpoints", &Config::SaveScoreAtCheckpoints.Value); + Reddog::Checkbox("Skip Intro Logos", &Config::SkipIntroLogos.Value); + } + End(); +} diff --git a/UnleashedRecomp/ui/reddog/windows/exports_window.h b/UnleashedRecomp/ui/reddog/windows/exports_window.h new file mode 100644 index 0000000..db8ceab --- /dev/null +++ b/UnleashedRecomp/ui/reddog/windows/exports_window.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class ExportsWindow : public Reddog::Window +{ +public: + using Reddog::Window::Window; + + void Draw() override; +}; diff --git a/UnleashedRecomp/ui/reddog/windows/view_window.cpp b/UnleashedRecomp/ui/reddog/windows/view_window.cpp new file mode 100644 index 0000000..2ff9524 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/windows/view_window.cpp @@ -0,0 +1,14 @@ +#include "view_window.h" +#include +#include + +static ViewWindow g_window{ "View" }; + +void ViewWindow::Draw() +{ + if (Begin()) + { + Reddog::Checkbox("Render HUD", (bool*)g_memory.Translate(0x8328BB26)); + } + End(); +} diff --git a/UnleashedRecomp/ui/reddog/windows/view_window.h b/UnleashedRecomp/ui/reddog/windows/view_window.h new file mode 100644 index 0000000..2eb3ff2 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/windows/view_window.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class ViewWindow : public Reddog::Window +{ +public: + using Reddog::Window::Window; + + void Draw() override; +}; diff --git a/UnleashedRecomp/ui/reddog/windows/window_list.cpp b/UnleashedRecomp/ui/reddog/windows/window_list.cpp new file mode 100644 index 0000000..798dda2 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/windows/window_list.cpp @@ -0,0 +1,21 @@ +#include "window_list.h" + +static WindowList g_window; + +void WindowList::Draw() +{ + if (Begin()) + { + for (auto& pWindow : Reddog::GetWindows()) + { + auto pTrueWindow = (Reddog::Window*)pWindow; + + if ((pTrueWindow->Flags & Reddog::eWindowFlags_NoListEntry) != 0) + continue; + + if (ImGui::Button(pTrueWindow->Name, { 190, 26 })) + pTrueWindow->SetVisible(); + } + } + End(); +} diff --git a/UnleashedRecomp/ui/reddog/windows/window_list.h b/UnleashedRecomp/ui/reddog/windows/window_list.h new file mode 100644 index 0000000..4f9c05e --- /dev/null +++ b/UnleashedRecomp/ui/reddog/windows/window_list.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#define WINDOW_LIST_NAME "##WindowList" + +class WindowList : public Reddog::Window +{ +public: + WindowList() : Window() + { + Name = WINDOW_LIST_NAME; + Flags = (Reddog::EWindowFlags)(Reddog::eWindowFlags_NoTitleBar | Reddog::eWindowFlags_NoResize | Reddog::eWindowFlags_NoListEntry); + ActiveColour = InactiveColour = IM_COL32_WHITE; + + m_minWidth = m_minHeight = 0; + } + + void Draw() override; +}; diff --git a/UnleashedRecomp/user/config.h b/UnleashedRecomp/user/config.h index 852ef2b..409f3cc 100644 --- a/UnleashedRecomp/user/config.h +++ b/UnleashedRecomp/user/config.h @@ -596,6 +596,7 @@ public: CONFIG_DEFINE_LOCALISED("System", bool, ControlTutorial, true); CONFIG_DEFINE_LOCALISED("System", bool, AchievementNotifications, true); CONFIG_DEFINE_ENUM_LOCALISED("System", ETimeOfDayTransition, TimeOfDayTransition, ETimeOfDayTransition::Xbox); + CONFIG_DEFINE("System", bool, Debug, false); CONFIG_DEFINE_LOCALISED("Input", bool, InvertCameraX, false); CONFIG_DEFINE_LOCALISED("Input", bool, InvertCameraY, false); diff --git a/UnleashedRecompResources b/UnleashedRecompResources index 2817764..775f0bb 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit 2817764f5933ad3a5482535f7f529db3b1dc2cba +Subproject commit 775f0bb0262101653b68f5a2e42305e3a30c3347 diff --git a/tools/PowerRecomp b/tools/PowerRecomp index 7fb8af1..de28409 160000 --- a/tools/PowerRecomp +++ b/tools/PowerRecomp @@ -1 +1 @@ -Subproject commit 7fb8af1bad9ecb22d5fa9c1b72555cdf7c22db02 +Subproject commit de2840970ffc3161a4cb8743b10ddd4da93bdc9f