diff --git a/host/src/main.cpp b/host/src/main.cpp index 657aa89..33e86dc 100644 --- a/host/src/main.cpp +++ b/host/src/main.cpp @@ -152,6 +152,9 @@ int run() bool show_overlay = true; double overlay_hidden_at = 0.0; coop::UiState ui; + // Persist the "Debug details" verbosity in the .ini. Register before the first + // begin_frame() below, which is when ImGui loads the .ini and replays our handler. + coop::register_ui_settings(ui); coop::FrameStats stats; // Frame-sync: the hook generation we last presented (so we wait for the next one). diff --git a/host/src/ui/app_chrome.cpp b/host/src/ui/app_chrome.cpp index daf51a7..e03cce8 100644 --- a/host/src/ui/app_chrome.cpp +++ b/host/src/ui/app_chrome.cpp @@ -2,14 +2,51 @@ #include #include +#include +#include #include "imgui.h" +#include "imgui_internal.h" // ImGuiSettingsHandler / AddSettingsHandler (custom .ini section) namespace coop { namespace { +// --- Custom .ini persistence for the UI switches --------------------------- +// We piggy-back on ImGui's .ini so the "Debug details" verbosity survives restarts +// without inventing a separate settings file. The section looks like: +// [CoopUI][State] +// DebugDetails=1 +// ImGui calls ReadOpenFn once per "[CoopUI][]" entry; we return the bound +// UiState* as the entry handle so ReadLineFn can write into it. +constexpr const char* kUiSettingsType = "CoopUI"; + +void* ui_settings_read_open(ImGuiContext*, ImGuiSettingsHandler* handler, const char* name) +{ + // Only one entry ("State"); hand back the UiState* so ReadLineFn can fill it. + return std::strcmp(name, "State") == 0 ? handler->UserData : nullptr; +} + +void ui_settings_read_line(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) +{ + auto* ui = static_cast(entry); + // Manual parse (avoids the sscanf CRT-secure deprecation for a single int key). + constexpr char kKey[] = "DebugDetails="; + if (std::strncmp(line, kKey, sizeof(kKey) - 1) == 0) + { + ui->debug_details = std::atoi(line + sizeof(kKey) - 1) != 0; + } +} + +void ui_settings_write_all(ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +{ + const auto* ui = static_cast(handler->UserData); + buf->appendf("[%s][State]\n", kUiSettingsType); + buf->appendf("DebugDetails=%d\n", ui->debug_details ? 1 : 0); + buf->append("\n"); +} + // Set by "View -> Reset layout"; true for the one frame in which panels re-apply // their default position/size, then cleared by apply_layout_end_frame(). bool g_layout_reset = false; @@ -22,6 +59,23 @@ int g_startup_force = 4; bool g_had_persisted_layout = false; } // namespace +void register_ui_settings(UiState& ui) +{ + // Idempotent: don't stack a second handler if this is somehow called twice. + if (ImGui::FindSettingsHandler(kUiSettingsType) != nullptr) + { + return; + } + ImGuiSettingsHandler handler; + handler.TypeName = kUiSettingsType; + handler.TypeHash = ImHashStr(kUiSettingsType); + handler.ReadOpenFn = ui_settings_read_open; + handler.ReadLineFn = ui_settings_read_line; + handler.WriteAllFn = ui_settings_write_all; + handler.UserData = &ui; + ImGui::AddSettingsHandler(&handler); // copied into the context +} + void request_layout_reset() { g_layout_reset = true; @@ -123,7 +177,10 @@ float draw_main_menu_bar(UiState& ui, const FrameStats& stats) ImGui::MenuItem("Audio mirror", nullptr, &ui.show_audio); ImGui::MenuItem("Log", nullptr, &ui.show_log); ImGui::Separator(); - ImGui::MenuItem("Debug details", nullptr, &ui.debug_details); + if (ImGui::MenuItem("Debug details", nullptr, &ui.debug_details)) + { + ImGui::MarkIniSettingsDirty(); // persist the new verbosity to coop_layout.ini + } if (ImGui::MenuItem("Reset layout")) { request_layout_reset(); diff --git a/host/src/ui/app_chrome.hpp b/host/src/ui/app_chrome.hpp index a98df49..e2208f6 100644 --- a/host/src/ui/app_chrome.hpp +++ b/host/src/ui/app_chrome.hpp @@ -22,6 +22,13 @@ struct UiState bool debug_details = false; // off = general status; on = full diagnostics }; +// Persist the verbosity-affecting UI switches (currently the "Debug details" toggle) +// into ImGui's .ini, alongside the window layout, so the operator's choice survives +// restarts. Registers a custom .ini settings handler bound to `ui`; call once after +// the ImGui context exists but before the first NewFrame (which loads the .ini), and +// keep `ui` alive for the program's lifetime (the handler stores a pointer to it). +void register_ui_settings(UiState& ui); + // The overlay panels, for the shared default layout below. enum class Panel {