Persist the "Debug details" toggle in the layout .ini

The View-menu verbosity switch reset to off on every launch. Register a
custom ImGui settings handler (a [CoopUI][State] section in coop_layout.ini,
alongside the window layout) so the operator's choice survives restarts.
Toggling it marks settings dirty so ImGui's auto-save writes it back.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 23:59:03 +02:00
parent 7264cb2ef4
commit 699ca31d31
3 changed files with 68 additions and 1 deletions

View File

@@ -152,6 +152,9 @@ int run()
bool show_overlay = true; bool show_overlay = true;
double overlay_hidden_at = 0.0; double overlay_hidden_at = 0.0;
coop::UiState ui; 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; coop::FrameStats stats;
// Frame-sync: the hook generation we last presented (so we wait for the next one). // Frame-sync: the hook generation we last presented (so we wait for the next one).

View File

@@ -2,14 +2,51 @@
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <cstring>
#include "imgui.h" #include "imgui.h"
#include "imgui_internal.h" // ImGuiSettingsHandler / AddSettingsHandler (custom .ini section)
namespace coop namespace coop
{ {
namespace 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][<name>]" 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<UiState*>(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<const UiState*>(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 // 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(). // their default position/size, then cleared by apply_layout_end_frame().
bool g_layout_reset = false; bool g_layout_reset = false;
@@ -22,6 +59,23 @@ int g_startup_force = 4;
bool g_had_persisted_layout = false; bool g_had_persisted_layout = false;
} // namespace } // 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() void request_layout_reset()
{ {
g_layout_reset = true; 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("Audio mirror", nullptr, &ui.show_audio);
ImGui::MenuItem("Log", nullptr, &ui.show_log); ImGui::MenuItem("Log", nullptr, &ui.show_log);
ImGui::Separator(); 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")) if (ImGui::MenuItem("Reset layout"))
{ {
request_layout_reset(); request_layout_reset();

View File

@@ -22,6 +22,13 @@ struct UiState
bool debug_details = false; // off = general status; on = full diagnostics 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. // The overlay panels, for the shared default layout below.
enum class Panel enum class Panel
{ {