From 43bcbb158c64a4813511c400a54b39e2b74c070d Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Fri, 12 Dec 2025 08:55:57 -0800 Subject: [PATCH] Load last saved GUI configuration on first load. Also save theme to the configuration. PiperOrigin-RevId: 843710929 Change-Id: I7ce9ce0f026d2cb5f79addf6c16c9b540bf11954 --- src/experimental/platform/imgui_widgets.h | 30 +++++++++++++++++++---- src/experimental/studio/app.cc | 11 +++++++-- src/experimental/studio/app.h | 1 + 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/experimental/platform/imgui_widgets.h b/src/experimental/platform/imgui_widgets.h index 9694f178..498fe0a6 100644 --- a/src/experimental/platform/imgui_widgets.h +++ b/src/experimental/platform/imgui_widgets.h @@ -47,9 +47,12 @@ static constexpr const char ICON_FA_SUN[] = "\xEF\x86\x85"; static constexpr const char ICON_FA_TACHOMETER[] = "\xEF\x83\xA4"; static constexpr const char ICON_FA_UNDO[] = "\xEF\x83\xA2"; - using KeyValues = std::unordered_map; +// This is a workaround to fix compilation on gcc <= 12 and clang <= 16 +template +struct dependent_false : std::false_type {}; + // Appends key/value pairs to an Ini file. void AppendIniSection(std::string& ini, const std::string& section, const KeyValues& key_values); @@ -58,6 +61,27 @@ void AppendIniSection(std::string& ini, const std::string& section, KeyValues ReadIniSection(const std::string& contents, const std::string& section); +template +T ReadIniValue(const KeyValues& key_values, const std::string& key, T def) { + auto iter = key_values.find(key); + if (iter == key_values.end()) { + return def; + } + if constexpr (std::is_same_v) { + return std::stoi(iter->second); + } else if constexpr (std::is_same_v) { + return std::stof(iter->second); + } else if constexpr (std::is_same_v) { + return std::stod(iter->second); + } else if constexpr (std::is_same_v) { + return iter->second; + } else if constexpr (std::is_enum_v) { + return static_cast(std::stoi(iter->second)); + } else { + static_assert(dependent_false::value, "Unsupported type"); + } +} + // Helper class for setting ImGui style options; automatically resets the // styles when going out of scope. struct ScopedStyle { @@ -179,10 +203,6 @@ struct ImGuiOpts { const char* format = std::is_floating_point_v ? "%.3g" : "%d"; }; -// This is a workaround to fix compilation on gcc <= 12 and clang <= 16 -template -struct dependent_false : std::false_type {}; - // A compile-time wrapper around ImGui::InputScalarN. This is useful because // MuJoCo uses an `mjtNum` type which is an alias for float or double. // diff --git a/src/experimental/studio/app.cc b/src/experimental/studio/app.cc index cd2b2678..3f9a1ddc 100644 --- a/src/experimental/studio/app.cc +++ b/src/experimental/studio/app.cc @@ -742,7 +742,7 @@ void App::LoadSettings() { if (!ini_path_.empty()) { std::string settings = platform::LoadText(ini_path_); if (!settings.empty()) { - ui_.FromDict(platform::ReadIniSection(settings, "[Simulate][Data]")); + ui_.FromDict(platform::ReadIniSection(settings, "[Studio][UX]")); ImGui::LoadIniSettingsFromMemory(settings.data(), settings.size()); } } @@ -751,7 +751,7 @@ void App::LoadSettings() { void App::SaveSettings() { if (!ini_path_.empty()) { std::string settings = ImGui::SaveIniSettingsToMemory(); - platform::AppendIniSection(settings, "[Simulate][Data]", ui_.ToDict()); + platform::AppendIniSection(settings, "[Studio][UX]", ui_.ToDict()); platform::SaveText(settings, ini_path_); } } @@ -912,6 +912,10 @@ void App::BuildGui() { } ImGuiIO& io = ImGui::GetIO(); + if (tmp_.first_frame) { + LoadSettings(); + tmp_.first_frame = false; + } if (io.WantSaveIniSettings) { SaveSettings(); io.WantSaveIniSettings = false; @@ -922,6 +926,7 @@ void App::SetupTheme(platform::GuiTheme theme) { if (!tmp_.style_editor) { platform::SetupTheme(theme); ui_.theme = theme; + ImGui::GetIO().WantSaveIniSettings = true; } } @@ -1747,11 +1752,13 @@ std::vector App::GetCameraNames() { App::UiState::Dict App::UiState::ToDict() const { return { + {"theme", std::to_string(static_cast(theme))}, }; } void App::UiState::FromDict(const Dict& dict) { *this = UiState(); + theme = ReadIniValue(dict, "theme", theme); } int App::LoadAssetCallback(const char* path, void* user_data, diff --git a/src/experimental/studio/app.h b/src/experimental/studio/app.h index 720e380b..38ec34b9 100644 --- a/src/experimental/studio/app.h +++ b/src/experimental/studio/app.h @@ -90,6 +90,7 @@ class App { // UI state that is transient and only needed while the application runs struct UiTempState { bool should_exit = false; + bool first_frame = true; // Windows. bool help = false;