2026-02-02 12:24:50 -08:00
|
|
|
#include "ui/ui_manager.hpp"
|
|
|
|
|
#include "core/window.hpp"
|
|
|
|
|
#include "core/application.hpp"
|
|
|
|
|
#include "core/logger.hpp"
|
|
|
|
|
#include "auth/auth_handler.hpp"
|
|
|
|
|
#include "game/game_handler.hpp"
|
2026-02-21 19:41:21 -08:00
|
|
|
#include "rendering/vk_context.hpp"
|
2026-02-02 12:24:50 -08:00
|
|
|
#include <imgui.h>
|
|
|
|
|
#include <imgui_impl_sdl2.h>
|
2026-02-21 19:41:21 -08:00
|
|
|
#include <imgui_impl_vulkan.h>
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace ui {
|
|
|
|
|
|
|
|
|
|
UIManager::UIManager() {
|
|
|
|
|
// Create screen instances
|
|
|
|
|
authScreen = std::make_unique<AuthScreen>();
|
|
|
|
|
realmScreen = std::make_unique<RealmScreen>();
|
2026-02-05 14:13:48 -08:00
|
|
|
characterCreateScreen = std::make_unique<CharacterCreateScreen>();
|
2026-02-02 12:24:50 -08:00
|
|
|
characterScreen = std::make_unique<CharacterScreen>();
|
|
|
|
|
gameScreen = std::make_unique<GameScreen>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UIManager::~UIManager() = default;
|
|
|
|
|
|
|
|
|
|
bool UIManager::initialize(core::Window* win) {
|
|
|
|
|
window = win;
|
|
|
|
|
LOG_INFO("Initializing UI manager");
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
auto* vkCtx = window->getVkContext();
|
|
|
|
|
if (!vkCtx) {
|
|
|
|
|
LOG_ERROR("No Vulkan context available for ImGui initialization");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
// Initialize ImGui
|
|
|
|
|
IMGUI_CHECKVERSION();
|
|
|
|
|
ImGui::CreateContext();
|
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
|
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
|
|
|
|
|
|
|
|
|
// Setup ImGui style
|
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
|
|
|
|
|
|
// Customize style for better WoW feel
|
|
|
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
|
|
|
style.WindowRounding = 6.0f;
|
|
|
|
|
style.FrameRounding = 4.0f;
|
|
|
|
|
style.GrabRounding = 4.0f;
|
|
|
|
|
style.WindowBorderSize = 1.0f;
|
|
|
|
|
style.FrameBorderSize = 1.0f;
|
|
|
|
|
|
|
|
|
|
// WoW-inspired colors
|
|
|
|
|
ImVec4* colors = style.Colors;
|
|
|
|
|
colors[ImGuiCol_WindowBg] = ImVec4(0.08f, 0.08f, 0.12f, 0.94f);
|
|
|
|
|
colors[ImGuiCol_TitleBg] = ImVec4(0.10f, 0.10f, 0.15f, 1.00f);
|
|
|
|
|
colors[ImGuiCol_TitleBgActive] = ImVec4(0.15f, 0.15f, 0.25f, 1.00f);
|
|
|
|
|
colors[ImGuiCol_Button] = ImVec4(0.20f, 0.25f, 0.40f, 1.00f);
|
|
|
|
|
colors[ImGuiCol_ButtonHovered] = ImVec4(0.25f, 0.30f, 0.50f, 1.00f);
|
|
|
|
|
colors[ImGuiCol_ButtonActive] = ImVec4(0.15f, 0.20f, 0.35f, 1.00f);
|
|
|
|
|
colors[ImGuiCol_Header] = ImVec4(0.20f, 0.25f, 0.40f, 0.55f);
|
|
|
|
|
colors[ImGuiCol_HeaderHovered] = ImVec4(0.25f, 0.30f, 0.50f, 0.80f);
|
|
|
|
|
colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.25f, 0.45f, 1.00f);
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Initialize ImGui for SDL2 + Vulkan
|
|
|
|
|
ImGui_ImplSDL2_InitForVulkan(window->getSDLWindow());
|
|
|
|
|
|
|
|
|
|
ImGui_ImplVulkan_InitInfo initInfo{};
|
|
|
|
|
initInfo.ApiVersion = VK_API_VERSION_1_1;
|
|
|
|
|
initInfo.Instance = vkCtx->getInstance();
|
|
|
|
|
initInfo.PhysicalDevice = vkCtx->getPhysicalDevice();
|
|
|
|
|
initInfo.Device = vkCtx->getDevice();
|
|
|
|
|
initInfo.QueueFamily = vkCtx->getGraphicsQueueFamily();
|
|
|
|
|
initInfo.Queue = vkCtx->getGraphicsQueue();
|
|
|
|
|
initInfo.DescriptorPool = vkCtx->getImGuiDescriptorPool();
|
|
|
|
|
initInfo.MinImageCount = 2;
|
|
|
|
|
initInfo.ImageCount = vkCtx->getSwapchainImageCount();
|
|
|
|
|
initInfo.PipelineInfoMain.RenderPass = vkCtx->getImGuiRenderPass();
|
2026-02-22 02:59:24 -08:00
|
|
|
initInfo.PipelineInfoMain.MSAASamples = vkCtx->getMsaaSamples();
|
2026-02-21 19:41:21 -08:00
|
|
|
|
|
|
|
|
ImGui_ImplVulkan_Init(&initInfo);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
imguiInitialized = true;
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
LOG_INFO("UI manager initialized successfully (Vulkan)");
|
2026-02-02 12:24:50 -08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UIManager::shutdown() {
|
|
|
|
|
if (imguiInitialized) {
|
2026-02-21 19:41:21 -08:00
|
|
|
auto* vkCtx = window ? window->getVkContext() : nullptr;
|
|
|
|
|
if (vkCtx) {
|
|
|
|
|
vkDeviceWaitIdle(vkCtx->getDevice());
|
|
|
|
|
}
|
|
|
|
|
ImGui_ImplVulkan_Shutdown();
|
2026-02-02 12:24:50 -08:00
|
|
|
ImGui_ImplSDL2_Shutdown();
|
|
|
|
|
ImGui::DestroyContext();
|
|
|
|
|
imguiInitialized = false;
|
|
|
|
|
}
|
|
|
|
|
LOG_INFO("UI manager shutdown");
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-07 11:43:37 -08:00
|
|
|
void UIManager::update([[maybe_unused]] float deltaTime) {
|
2026-02-02 12:24:50 -08:00
|
|
|
if (!imguiInitialized) return;
|
|
|
|
|
|
|
|
|
|
// Start ImGui frame
|
2026-02-21 19:41:21 -08:00
|
|
|
ImGui_ImplVulkan_NewFrame();
|
2026-02-02 12:24:50 -08:00
|
|
|
ImGui_ImplSDL2_NewFrame();
|
|
|
|
|
ImGui::NewFrame();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UIManager::render(core::AppState appState, auth::AuthHandler* authHandler, game::GameHandler* gameHandler) {
|
|
|
|
|
if (!imguiInitialized) return;
|
|
|
|
|
|
|
|
|
|
// Render appropriate screen based on application state
|
|
|
|
|
switch (appState) {
|
|
|
|
|
case core::AppState::AUTHENTICATION:
|
|
|
|
|
if (authHandler) {
|
|
|
|
|
authScreen->render(*authHandler);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case core::AppState::REALM_SELECTION:
|
2026-02-05 15:44:42 -08:00
|
|
|
authScreen->stopLoginMusic();
|
2026-02-02 12:24:50 -08:00
|
|
|
if (authHandler) {
|
|
|
|
|
realmScreen->render(*authHandler);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2026-02-05 14:13:48 -08:00
|
|
|
case core::AppState::CHARACTER_CREATION:
|
2026-02-05 15:44:42 -08:00
|
|
|
authScreen->stopLoginMusic();
|
2026-02-05 14:13:48 -08:00
|
|
|
if (gameHandler) {
|
|
|
|
|
characterCreateScreen->render(*gameHandler);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2026-02-02 12:24:50 -08:00
|
|
|
case core::AppState::CHARACTER_SELECTION:
|
2026-02-05 15:44:42 -08:00
|
|
|
authScreen->stopLoginMusic();
|
2026-02-02 12:24:50 -08:00
|
|
|
if (gameHandler) {
|
|
|
|
|
characterScreen->render(*gameHandler);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case core::AppState::IN_GAME:
|
2026-02-05 15:44:42 -08:00
|
|
|
authScreen->stopLoginMusic();
|
2026-02-02 12:24:50 -08:00
|
|
|
if (gameHandler) {
|
|
|
|
|
gameScreen->render(*gameHandler);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case core::AppState::DISCONNECTED:
|
2026-02-05 15:44:42 -08:00
|
|
|
authScreen->stopLoginMusic();
|
2026-02-02 12:24:50 -08:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(400, 150), ImGuiCond_Always);
|
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x * 0.5f - 200,
|
|
|
|
|
ImGui::GetIO().DisplaySize.y * 0.5f - 75),
|
|
|
|
|
ImGuiCond_Always);
|
|
|
|
|
ImGui::Begin("Disconnected", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize);
|
|
|
|
|
ImGui::TextWrapped("You have been disconnected from the server.");
|
|
|
|
|
ImGui::Spacing();
|
|
|
|
|
if (ImGui::Button("Return to Login", ImVec2(-1, 0))) {
|
|
|
|
|
// Will be handled by application
|
|
|
|
|
}
|
|
|
|
|
ImGui::End();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 19:41:21 -08:00
|
|
|
// Finalize ImGui draw data (actual rendering happens in the command buffer)
|
2026-02-02 12:24:50 -08:00
|
|
|
ImGui::Render();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UIManager::processEvent(const SDL_Event& event) {
|
|
|
|
|
if (imguiInitialized) {
|
|
|
|
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace ui
|
|
|
|
|
} // namespace wowee
|