From 5fb77dfde1260690af678a1b086288c073db89cd Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 2 Jul 2026 18:28:56 +0200 Subject: [PATCH] Settings: added io.ConfigIniSettingsAutoDiscardMonths, trimming tool in in Metrics->Settings + internal CleanupIniSettings(). (#9460) cc #437 #2564 --- docs/CHANGELOG.txt | 11 +++++--- imgui.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++++- imgui.h | 1 + imgui_internal.h | 15 ++++++++++ imgui_tables.cpp | 16 +++++++++++ 5 files changed, 107 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dba212f14..4647ca97f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -118,12 +118,15 @@ Other Changes: - Scale the NavCursor border thickness when using large values with `ScallAllSizes()`. - Settings: - Windows/Tables settings entries can now record the last used date in YYYYMMDD format, - allowing cleanup tools to run to e.g. delete entries that haven't been used in X months. - - Added bool io.ConfigIniSettingsSaveLastUsedDate to disable saving that info. + allowing tools to run to e.g. delete entries that haven't been used in X months. (#9460) + - Added bool io.ConfigIniSettingsSaveLastUsedDate to disable saving that info. (#9460) + - Added int io.ConfigIniSettingsAutoDiscardMonths to enable a mode where unused settings + are automatically discard after xx months. (#9460) + - Added a trimming tool under Metrics->Settings, along with a yet-unexposed function. - The current system date is fed through ImGuiPlatformIO::Platform_SessionDate, - which is automatically set by a call to time() done during context creation. + which is automatically set by a call to time() done during context creation. (#9460) - Added IMGUI_DISABLE_TIME_FUNCTIONS to disable setting platform_io.Platform_SessionDate. - A custom backend may still set it manually. + A custom backend may still set it manually. (#9460) - DrawList: - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) - Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText() diff --git a/imgui.cpp b/imgui.cpp index 844ca0342..f1729eb84 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1365,6 +1365,7 @@ static void AddWindowToSortBuffer(ImVector* out_sorted // Settings static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); +static void WindowSettingsHandler_Cleanup(ImGuiContext*, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args); static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); @@ -1684,6 +1685,7 @@ ImGuiIO::ImGuiIO() ConfigWindowsCopyContentsWithCtrlC = false; ConfigScrollbarScrollByPage = true; ConfigIniSettingsSaveLastUsedDate = true; + ConfigIniSettingsAutoDiscardMonths = 0; ConfigMemoryCompactTimer = 60.0f; ConfigDebugIsDebuggerPresent = false; ConfigDebugHighlightIdConflicts = true; @@ -4442,6 +4444,7 @@ void ImGui::Initialize() ini_handler.TypeName = "Window"; ini_handler.TypeHash = ImHashStr("Window"); ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; + ini_handler.CleanupFn = WindowSettingsHandler_Cleanup; ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; @@ -15692,6 +15695,19 @@ void ImGui::ClearIniSettings() handler.ClearAllFn(&g, &handler); } +void ImGui::CleanupIniSettings(ImGuiSettingsCleanupArgs* args) +{ + ImGuiContext& g = *GImGui; + if (g.PlatformIO.Platform_SessionDate == 0) + return; + ImGuiPackedDate discard_older_than_date_p = g.PlatformIO.Platform_SessionDate; + discard_older_than_date_p.SubtractMonths(args->DiscardOlderThanMonths); + args->_DiscardOlderThanDate = discard_older_than_date_p.Unpack(); + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + if (handler.CleanupFn != NULL) + handler.CleanupFn(&g, &handler, args); +} + void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) { size_t file_data_size = 0; @@ -15770,6 +15786,9 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) memcpy(buf, ini_data, ini_size); // Call post-read handlers + ImGuiSettingsCleanupArgs cleanup_args; + if (g.IO.ConfigIniSettingsAutoDiscardMonths > 0) + cleanup_args.DiscardOlderThanMonths = g.IO.ConfigIniSettingsAutoDiscardMonths; for (ImGuiSettingsHandler& handler : g.SettingsHandlers) if (handler.ApplyAllFn != NULL) handler.ApplyAllFn(&g, &handler); @@ -15866,6 +15885,18 @@ static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandl g.SettingsWindows.clear(); } +static void WindowSettingsHandler_Cleanup(ImGuiContext* ctx, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args) +{ + ImGuiContext& g = *ctx; + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + { + if (args->_DiscardOlderThanDate != 0 && settings->LastUsedDate.Unpack() < args->_DiscardOlderThanDate) + settings->WantDelete = true; + if (args->SetCurrentSessionDateToAll || (args->SetCurrentSessionDateWhenMissingDate && settings->LastUsedDate.IsValid() == false)) + settings->LastUsedDate = g.SessionDate; + } +} + static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { ImGuiID id = ImHashStr(name); @@ -15918,6 +15949,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl if (!settings) { settings = ImGui::CreateNewWindowSettings(window->Name); + settings->LastUsedDate = g.SessionDate; window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); @@ -16724,6 +16756,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; + ImGuiPlatformIO& platform_io = g.PlatformIO; ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; if (cfg->ShowDebugLog) ShowDebugLogWindow(&cfg->ShowDebugLog); @@ -17056,8 +17089,36 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("\"%s\"", g.IO.IniFilename); else TextUnformatted(""); - Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); + + int highlight_older_than_date = 0; + Text("SessionDate: %d", platform_io.Platform_SessionDate); + BeginDisabled(platform_io.Platform_SessionDate == 0); + Checkbox("Highlight Entries Older Than", &cfg->SettingsHighlightOldEntries); + SetNextItemWidth(GetFontSize() * 8); + SameLine(); + SliderInt("Months", &cfg->SettingsDiscardMonths, 1, 24); + if (cfg->SettingsHighlightOldEntries && cfg->SettingsDiscardMonths > 0) + { + ImGuiPackedDate cutoff_date = platform_io.Platform_SessionDate; + cutoff_date.SubtractMonths(cfg->SettingsDiscardMonths); + highlight_older_than_date = cutoff_date.Unpack(); + SameLine(); + ImGuiSettingsCleanupArgs cleanup_args; + cleanup_args.DiscardOlderThanMonths = cfg->SettingsDiscardMonths; + if (Button("Discard")) + CleanupIniSettings(&cleanup_args); + } + EndDisabled(); + Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); + + struct ScopedHighlightOlderThan + { + bool Highlight; + ScopedHighlightOlderThan(int cutoff_date, ImGuiPackedDate in_date) { Highlight = cutoff_date != 0 && in_date.Unpack() < cutoff_date; if (Highlight) PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); } + ~ScopedHighlightOlderThan() { if (Highlight) PopStyleColor(); } + }; + if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) { for (ImGuiSettingsHandler& handler : g.SettingsHandlers) @@ -17067,14 +17128,20 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) { for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + { + ScopedHighlightOlderThan scoped_highlight(highlight_older_than_date, settings->LastUsedDate); DebugNodeWindowSettings(settings); + } TreePop(); } if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) { for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + { + ScopedHighlightOlderThan scoped_highlight(highlight_older_than_date, settings->LastUsedDate); DebugNodeTableSettings(settings, NULL); + } TreePop(); } diff --git a/imgui.h b/imgui.h index d469e0fb8..59aba2875 100644 --- a/imgui.h +++ b/imgui.h @@ -2438,6 +2438,7 @@ struct ImGuiIO // Ini Settings bool ConfigIniSettingsSaveLastUsedDate;// = true // Enable loading/saving last used day (YYYYMMDD) in some .ini struct, making things easier to audit and allowing custom tools to cleanup old data. + int ConfigIniSettingsAutoDiscardMonths; // = 0 // [BETA] Set number of months after which unused .ini entries are discarded on load. Require platform_io.Platform_SessionDate to be set. For systems supporting the feature, .ini entries without a LastUsed field will always be discarded! Please report if you are using this. // Miscellaneous options // (you can visualize and interact with all options in 'Demo->Configuration') diff --git a/imgui_internal.h b/imgui_internal.h index c7e1a03b4..129981aa5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -98,6 +98,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type #elif defined(__GNUC__) @@ -845,7 +846,9 @@ struct ImGuiPackedDate ImGuiPackedDate() { Year = Month = Day = 0; } ImGuiPackedDate(int yyyymmdd) { Year = (ImU16)((yyyymmdd / 10000) - 2000); Month = (ImU16)((yyyymmdd / 100) % 100); Day = (ImU16)(yyyymmdd % 100); } // Pack + bool IsValid() { return (Year && Month && Day); } int Unpack() const { return (Year && Month && Day) ? ((Year + 2000) * 10000) + (Month * 100) + Day : 0; } // Unpack + void SubtractMonths(int m) { while (m > 0) { Year -= Month == 1; Month = (Month == 1) ? 12 : Month - 1; m--; } } // FIXME-OPT: Stupid but enough for what we do with it. }; // Helper: ImGuiStorage @@ -2043,6 +2046,14 @@ struct ImGuiWindowSettings char* GetName() { return (char*)(this + 1); } }; +struct ImGuiSettingsCleanupArgs +{ + int DiscardOlderThanMonths = 0; // Enable to discard entries older than XX months. + bool SetCurrentSessionDateToAll = false; // Enable to write current SessionDate to all supporting entries. // Let us know in #9460 if you use this. + bool SetCurrentSessionDateWhenMissingDate = false; // Enable to write current SessionDate to all supporting entries missing a date. // Let us know in #9460 if you use this. + int _DiscardOlderThanDate = 0; // [Internal] +}; + struct ImGuiSettingsHandler { const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' @@ -2053,6 +2064,7 @@ struct ImGuiSettingsHandler void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order) void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' + void (*CleanupFn) (ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiSettingsCleanupArgs* args);// Cleanup/patch settings void* UserData; ImGuiSettingsHandler() { memset((void*)this, 0, sizeof(*this)); } @@ -2166,6 +2178,8 @@ struct ImGuiMetricsConfig int ShowTablesRectsType = -1; int HighlightMonitorIdx = -1; ImGuiID HighlightViewportID = 0; + int SettingsDiscardMonths = 6; + bool SettingsHighlightOldEntries = false; bool ShowFontPreview = true; }; @@ -3410,6 +3424,7 @@ namespace ImGui IMGUI_API void MarkIniSettingsDirty(); IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); IMGUI_API void ClearIniSettings(); + IMGUI_API void CleanupIniSettings(ImGuiSettingsCleanupArgs* args); // [BETA] Expected to turn into a public API. Please report if you are using this! IMGUI_API void AddSettingsHandler(const ImGuiSettingsHandler* handler); IMGUI_API void RemoveSettingsHandler(const char* type_name); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 1ff368239..aa0b89efe 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -4126,6 +4126,21 @@ static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandle g.SettingsTables.clear(); } +static void TableSettingsHandler_Cleanup(ImGuiContext* ctx, ImGuiSettingsHandler*, ImGuiSettingsCleanupArgs* args) +{ + ImGuiContext& g = *ctx; + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + table->SettingsOffset = -1; + for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + { + if (args->_DiscardOlderThanDate != 0 && settings->LastUsedDate.Unpack() < args->_DiscardOlderThanDate) + settings->ID = 0; + if (args->SetCurrentSessionDateToAll || (args->SetCurrentSessionDateWhenMissingDate && settings->LastUsedDate.IsValid() == false)) + settings->LastUsedDate = g.SessionDate; + } +} + // Apply to existing windows (if any) static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { @@ -4234,6 +4249,7 @@ void ImGui::TableSettingsAddSettingsHandler() ini_handler.TypeName = "Table"; ini_handler.TypeHash = ImHashStr("Table"); ini_handler.ClearAllFn = TableSettingsHandler_ClearAll; + ini_handler.CleanupFn = TableSettingsHandler_Cleanup; ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen; ini_handler.ReadLineFn = TableSettingsHandler_ReadLine; ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll;