#include "imgui_layer.hpp" #include #include #include #include #include "coop/dpi.hpp" #include "coop/tool_paths.hpp" #include "ui/app_chrome.hpp" #include "util/utf8.hpp" namespace coop { ImGuiLayer::~ImGuiLayer() { if (initialized_) { ImGui_ImplDX11_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); initialized_ = false; } } bool ImGuiLayer::init(HWND hwnd, ID3D11Device* device, ID3D11DeviceContext* context) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); // Persist the panel layout next to the executable so window positions/sizes survive // restarts. CWD is unreliable when Steam launches us under the donor appid, so anchor // to the exe directory. If a layout already exists, tell the chrome not to force the // computed default on startup (which would clobber the restored positions). const std::wstring ini_w = exe_directory() + L"coop_layout.ini"; const bool had_layout = GetFileAttributesW(ini_w.c_str()) != INVALID_FILE_ATTRIBUTES; // ImGui treats IniFilename as UTF-8 (it converts to wide for _wfopen), so narrow to UTF-8, not // the system ANSI code page. ini_path_ = narrow(ini_w); io.IniFilename = ini_path_.c_str(); set_layout_persisted(had_layout); if (!ImGui_ImplWin32_Init(hwnd)) { return false; } if (!ImGui_ImplDX11_Init(device, context)) { return false; } initialized_ = true; // Scale the overlay (font + style) to the monitor the window opened on. The process is // per-monitor-DPI-aware (set in wWinMain), so GetDpiForWindow returns that monitor's real DPI. apply_dpi(GetDpiForWindow(hwnd)); return true; } void ImGuiLayer::apply_dpi(unsigned dpi) { const float scale = dpi_scale_from(dpi); ImGuiIO& io = ImGui::GetIO(); // Rebuild the font atlas at the DPI-scaled pixel size so ImGui's built-in default font is // rasterized crisp at the target size, rather than bitmap-stretched the way io.FontGlobalScale // would leave it. scaled_font_px() scales the one named base-size constant. io.Fonts->Clear(); ImFontConfig cfg; cfg.SizePixels = scaled_font_px(dpi); io.Fonts->AddFontDefault(&cfg); // Reset to the base dark theme, then scale spacing/padding/border sizes to match. Resetting // first keeps repeated DPI changes from compounding (ScaleAllSizes multiplies the style in place). ImGui::StyleColorsDark(); ImGui::GetStyle().ScaleAllSizes(scale); // Drop the DX11 backend's cached font texture so it rebuilds from the new atlas next frame. Before // the first frame nothing is built yet, so this is a harmless no-op during init(). if (initialized_) { ImGui_ImplDX11_InvalidateDeviceObjects(); } dpi_scale_ = scale; } void ImGuiLayer::set_dpi(unsigned dpi) { if (!initialized_ || dpi_scale_from(dpi) == dpi_scale_) { return; // not up yet, or the scale didn't actually change -- skip a needless atlas rebuild } apply_dpi(dpi); } void ImGuiLayer::begin_frame() { ImGui_ImplDX11_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); } void ImGuiLayer::end_frame() { ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); } } // namespace coop