ab934d0818
Sets io.IniFilename to %APPDATA%\UsdLayerManager\imgui.ini right after CreateContext so ImGui never touches the working directory for layout persistence. The directory is created on first run via create_directories. m_iniFilePath on ImGuiContext holds the string so the pointer stays valid for the lifetime of the context. Also removes imgui.ini from version control and adds it to .gitignore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
266 lines
7.4 KiB
C++
266 lines
7.4 KiB
C++
#include "ImGuiContext.h"
|
|
#include "../utils/Logger.h"
|
|
#include "../utils/GLExt.h"
|
|
#include "../utils/PathUtils.h"
|
|
#include <imgui.h>
|
|
#include <imgui_impl_win32.h>
|
|
#include <imgui_impl_opengl3.h>
|
|
#include <filesystem>
|
|
|
|
// 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<LONG_PTR>(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<ImGuiContext*>(::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
|