#include "ImGuiContext.h" #include "../utils/Logger.h" #include "../utils/GLExt.h" #include "../utils/PathUtils.h" #include #include #include #include // Forward declare message handler from imgui_impl_win32.cpp extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); namespace UsdLayerManager { ImGuiContext::ImGuiContext() : m_hwnd(nullptr) , m_hdc(nullptr) , m_hglrc(nullptr) , m_shouldClose(false) , m_width(1280) , m_height(720) { } ImGuiContext::~ImGuiContext() { Shutdown(); } bool ImGuiContext::Initialize(const std::string& windowTitle, int width, int height) { m_width = width; m_height = height; LOG_INFO("Initializing ImGui context..."); // Create application window WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"UsdLayerManager", nullptr }; ::RegisterClassExW(&wc); m_hwnd = ::CreateWindowW( wc.lpszClassName, L"USD Layer Manager", WS_OVERLAPPEDWINDOW, 100, 100, m_width, m_height, nullptr, nullptr, wc.hInstance, nullptr ); if (!m_hwnd) { LOG_ERROR("Failed to create window"); return false; } // Store this pointer in window user data ::SetWindowLongPtr(m_hwnd, GWLP_USERDATA, reinterpret_cast(this)); // Initialize OpenGL if (!CreateDeviceWGL()) { LOG_ERROR("Failed to initialize OpenGL"); ::DestroyWindow(m_hwnd); ::UnregisterClassW(wc.lpszClassName, wc.hInstance); return false; } // Initialize OpenGL extensions if (!GL::InitExtensions()) { LOG_ERROR("Failed to initialize OpenGL extensions"); } // Show the window ::ShowWindow(m_hwnd, SW_SHOWDEFAULT); ::UpdateWindow(m_hwnd); // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); // DockingEnable only — NavEnableKeyboard is intentionally omitted for a DCC // viewport application that manages its own keyboard shortcuts. // With NavEnableKeyboard set, io.WantCaptureKeyboard becomes true whenever any // ImGui window is focused, which would block all viewport hotkeys (F, A, etc.). io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Store imgui.ini in %APPDATA%\UsdLayerManager\ so it doesn't litter the CWD. if (const char* appData = getenv("APPDATA")) { std::filesystem::path iniDir = std::filesystem::path(appData) / "UsdLayerManager"; std::filesystem::create_directories(iniDir); m_iniFilePath = (iniDir / "imgui.ini").string(); io.IniFilename = m_iniFilePath.c_str(); } // Load Inter font. // The build directory copies the font as Inter.ttc; the install step renames // it to Inter.ttf. Try .ttf first (install layout), fall back to .ttc (build). { ImFontConfig fontConfig; fontConfig.FontNo = 0; // select Regular face from the .ttc collection // Build exe-relative font paths so they work from both build and install dirs std::string fontPath0 = ResourcePath("resources/font/Inter.ttf"); std::string fontPath1 = ResourcePath("resources/font/Inter.ttc"); const char* tryPaths[] = { fontPath0.c_str(), fontPath1.c_str() }; bool loaded = false; for (const char* p : tryPaths) { ImFont* f = io.Fonts->AddFontFromFileTTF(p, 15.0f, &fontConfig); if (f) { LOG_INFO(std::string("Loaded Inter font from ") + p); loaded = true; break; } } if (!loaded) { LOG_WARNING("Could not load Inter font; using built-in default"); io.Fonts->AddFontDefault(); } } // Setup Dear ImGui style ImGui::StyleColorsDark(); // Setup Platform/Renderer backends ImGui_ImplWin32_Init(m_hwnd); ImGui_ImplOpenGL3_Init("#version 130"); LOG_INFO("ImGui context initialized successfully"); return true; } void ImGuiContext::Shutdown() { if (m_hwnd) { LOG_INFO("Shutting down ImGui context..."); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyPlatformWindows(); ImGui::DestroyContext(); CleanupDeviceWGL(); ::DestroyWindow(m_hwnd); ::UnregisterClassW(L"UsdLayerManager", ::GetModuleHandle(nullptr)); m_hwnd = nullptr; } } void ImGuiContext::NewFrame() { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); } void ImGuiContext::Render() { ImGui::Render(); glViewport(0, 0, m_width, m_height); glClearColor(0.45f, 0.55f, 0.60f, 1.00f); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); ::SwapBuffers(m_hdc); } bool ImGuiContext::ProcessEvents() { MSG msg; while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); if (msg.message == WM_QUIT) { m_shouldClose = true; } } return !m_shouldClose; } bool ImGuiContext::CreateDeviceWGL() { m_hdc = ::GetDC(m_hwnd); PIXELFORMATDESCRIPTOR pfd = {}; pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 32; pfd.cDepthBits = 24; pfd.cStencilBits = 8; int pixelFormat = ::ChoosePixelFormat(m_hdc, &pfd); if (pixelFormat == 0) { LOG_ERROR("ChoosePixelFormat failed"); return false; } if (!::SetPixelFormat(m_hdc, pixelFormat, &pfd)) { LOG_ERROR("SetPixelFormat failed"); return false; } m_hglrc = ::wglCreateContext(m_hdc); if (!m_hglrc) { LOG_ERROR("wglCreateContext failed"); return false; } if (!::wglMakeCurrent(m_hdc, m_hglrc)) { LOG_ERROR("wglMakeCurrent failed"); return false; } return true; } void ImGuiContext::CleanupDeviceWGL() { if (m_hglrc) { ::wglMakeCurrent(nullptr, nullptr); ::wglDeleteContext(m_hglrc); m_hglrc = nullptr; } if (m_hdc) { ::ReleaseDC(m_hwnd, m_hdc); m_hdc = nullptr; } } LRESULT WINAPI ImGuiContext::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) { return true; } ImGuiContext* context = reinterpret_cast(::GetWindowLongPtr(hWnd, GWLP_USERDATA)); switch (msg) { case WM_SIZE: if (context && wParam != SIZE_MINIMIZED) { context->m_width = LOWORD(lParam); context->m_height = HIWORD(lParam); } return 0; case WM_SYSCOMMAND: if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu return 0; break; case WM_DESTROY: ::PostQuitMessage(0); return 0; } return ::DefWindowProcW(hWnd, msg, wParam, lParam); } } // namespace UsdLayerManager