From e82259a70412df989239a831f1c1ca4aac17d980 Mon Sep 17 00:00:00 2001 From: fredzo Date: Wed, 14 Jan 2026 18:22:29 +0100 Subject: [PATCH 1/2] Fix for #297 : - Stored default value in Preference class, - Added ResetToDefault() method to Preference class, - Added Default buttons in PreferenceDialog for sections / categories / all preferences and individual preference, - If more than one preference is reset to default, shows an confirmation dialog. --- src/ngscopeclient/Preference.cpp | 18 ++++ src/ngscopeclient/Preference.h | 2 + src/ngscopeclient/PreferenceDialog.cpp | 119 +++++++++++++++++++++++++ src/ngscopeclient/PreferenceDialog.h | 7 ++ 4 files changed, 146 insertions(+) diff --git a/src/ngscopeclient/Preference.cpp b/src/ngscopeclient/Preference.cpp index dfe94dceb..189dca74b 100644 --- a/src/ngscopeclient/Preference.cpp +++ b/src/ngscopeclient/Preference.cpp @@ -212,6 +212,11 @@ bool Preference::HasUnit() return this->m_unit.GetType() != Unit::UNIT_COUNTS; } +void Preference::ResetToDefault() +{ + m_value = std::move(m_defaultValue); +} + Unit& Preference::GetUnit() { return this->m_unit; @@ -302,6 +307,8 @@ void Preference::MoveFrom(Preference& other) break; } } + // Copy default value + m_defaultValue = std::move(other.m_defaultValue); other.m_type = PreferenceType::None; } @@ -393,6 +400,7 @@ impl::PreferenceBuilder Preference::Int(std::string identifier, int64_t defaultV { Preference pref(PreferenceType::Int, std::move(identifier)); pref.Construct(defaultValue); + new (&pref.m_defaultValue) int64_t(std::move(defaultValue)); return impl::PreferenceBuilder{ std::move(pref) }; } @@ -401,6 +409,7 @@ impl::PreferenceBuilder Preference::Real(std::string identifier, double defaultV { Preference pref(PreferenceType::Real, std::move(identifier)); pref.Construct(defaultValue); + new (&pref.m_defaultValue) double(std::move(defaultValue)); return impl::PreferenceBuilder{ std::move(pref) }; } @@ -409,6 +418,7 @@ impl::PreferenceBuilder Preference::Bool(std::string identifier, bool defaultVal { Preference pref(PreferenceType::Boolean, std::move(identifier)); pref.Construct(defaultValue); + new (&pref.m_defaultValue) bool(std::move(defaultValue)); return impl::PreferenceBuilder{ std::move(pref) }; } @@ -417,6 +427,7 @@ impl::PreferenceBuilder Preference::String(std::string identifier, std::string d { Preference pref(PreferenceType::String, std::move(identifier)); pref.Construct(defaultValue); + new (&pref.m_defaultValue) std::string(std::move(defaultValue)); return impl::PreferenceBuilder{ std::move(pref) }; } @@ -429,6 +440,11 @@ impl::PreferenceBuilder Preference::Color(std::string identifier, const ImU32& d static_cast((defaultValue >> IM_COL32_G_SHIFT) & 0xff), static_cast((defaultValue >> IM_COL32_B_SHIFT) & 0xff), static_cast((defaultValue >> IM_COL32_A_SHIFT) & 0xff))); + new (&pref.m_defaultValue) impl::Color(std::move(impl::Color( + static_cast((defaultValue >> IM_COL32_R_SHIFT) & 0xff), + static_cast((defaultValue >> IM_COL32_G_SHIFT) & 0xff), + static_cast((defaultValue >> IM_COL32_B_SHIFT) & 0xff), + static_cast((defaultValue >> IM_COL32_A_SHIFT) & 0xff)))); return impl::PreferenceBuilder{ std::move(pref) }; } @@ -437,6 +453,7 @@ impl::PreferenceBuilder Preference::EnumRaw(std::string identifier, std::int64_t { Preference pref(PreferenceType::Enum, std::move(identifier)); pref.Construct(defaultValue); + new (&pref.m_defaultValue) std::int64_t(std::move(defaultValue)); return impl::PreferenceBuilder{ std::move(pref) }; } @@ -445,6 +462,7 @@ impl::PreferenceBuilder Preference::Font(std::string identifier, FontDescription { Preference pref(PreferenceType::Font, std::move(identifier)); pref.Construct(defaultValue); + new (&pref.m_defaultValue) FontDescription(std::move(defaultValue)); return impl::PreferenceBuilder{ std::move(pref) }; } diff --git a/src/ngscopeclient/Preference.h b/src/ngscopeclient/Preference.h index 3c6183952..f0c0e3d0f 100644 --- a/src/ngscopeclient/Preference.h +++ b/src/ngscopeclient/Preference.h @@ -146,6 +146,7 @@ class Preference std::string m_description; PreferenceType m_type; PreferenceValue m_value; + PreferenceValue m_defaultValue; bool m_isVisible{true}; Unit m_unit{Unit::UNIT_COUNTS}; bool m_hasValue{false}; @@ -204,6 +205,7 @@ class Preference void SetLabel(std::string label); void SetDescription(std::string description); bool HasUnit(); + void ResetToDefault(); Unit& GetUnit(); const EnumMapping& GetMapping() const; diff --git a/src/ngscopeclient/PreferenceDialog.cpp b/src/ngscopeclient/PreferenceDialog.cpp index 8942912a7..36ce41f79 100644 --- a/src/ngscopeclient/PreferenceDialog.cpp +++ b/src/ngscopeclient/PreferenceDialog.cpp @@ -110,6 +110,55 @@ void PreferenceDialog::FindFontFiles(const string& path) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Rendering +bool PreferenceDialog::DefaultButton(const std::string& label, const std::string& id, bool centered) +{ + float buttonWidth = ImGui::CalcTextSize(label.c_str()).x + ImGui::GetStyle().FramePadding.x * 2.0f; + float availWidth = ImGui::GetContentRegionAvail().x; + float cursorX = ImGui::GetCursorPosX(); + float xPos = centered ? (cursorX + (availWidth - buttonWidth)/2.0f) : (cursorX + availWidth - buttonWidth); + ImGui::SetCursorPosX(xPos); + ImGui::PushID(id.c_str()); + bool result = ImGui::Button(label.c_str()); + ImGui::PopID(); + ImGui::SameLine(cursorX); + return result; +} + +void PreferenceDialog::OpenConfirmDialog(const std::string& title, const std::string& message, const std::string& identifier) +{ + ImGui::OpenPopup((title + "###" + identifier).c_str()); + m_confirmDialogTitle = title; + m_confirmDialogMessage = message; +} + +bool PreferenceDialog::RenderConfirmDialog(const std::string& identifier) +{ + bool confirmed = false; + if (ImGui::BeginPopupModal((m_confirmDialogTitle + "###" + identifier).c_str(), nullptr,ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::TextWrapped("%s", m_confirmDialogMessage.c_str()); + ImGui::Separator(); + float buttonWidth = ImGui::GetFontSize()*6; + // OK button + if (ImGui::Button("OK", ImVec2(buttonWidth, 0))) + { + confirmed = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + // Cancel button + if (ImGui::Button("Cancel", ImVec2(buttonWidth, 0)) || ImGui::IsKeyPressed(ImGuiKey_Escape)) + { + ImGui::CloseCurrentPopup(); + } + // Default focus on cancel buttun + ImGui::SetItemDefaultFocus(); + ImGui::EndPopup(); + } + return confirmed; +} + + /** @brief Renders the dialog and handles UI events @@ -132,6 +181,14 @@ bool PreferenceDialog::DoRender() if(subCategory.IsVisible()) { ImGui::PushID(identifier.c_str()); + if(DefaultButton("Default section",identifier)) + { + OpenConfirmDialog("Reset to default","Reset all settings in this section to default?",identifier); + } + if(RenderConfirmDialog(identifier)) + { + ResetCategoryToDefault(subCategory); + } if(ImGui::CollapsingHeader(identifier.c_str())) ProcessCategory(subCategory); ImGui::PopID(); @@ -139,6 +196,24 @@ bool PreferenceDialog::DoRender() } } + ImGui::NewLine(); + if(DefaultButton("Default all Preferences","###resetAll",true)) + { + OpenConfirmDialog("Reset to default","Reset all settings to default?","resetAll"); + } + if(RenderConfirmDialog("resetAll")) + { + for(const auto& identifier: root.GetOrdering()) + { + auto& node = children[identifier]; + + if(node->IsCategory()) + { + auto& subCategory = node->AsCategory(); + ResetCategoryToDefault(subCategory); + } + } + } return true; } @@ -159,6 +234,14 @@ void PreferenceDialog::ProcessCategory(PreferenceCategory& cat) if(subCategory.IsVisible()) { + if(DefaultButton("Default category",identifier)) + { + OpenConfirmDialog("Reset to default","Reset all settings in this category to default?",identifier); + } + if(RenderConfirmDialog(identifier)) + { + ResetCategoryToDefault(subCategory); + } if(ImGui::TreeNode(identifier.c_str())) { ImGui::PushID(identifier.c_str()); @@ -175,6 +258,34 @@ void PreferenceDialog::ProcessCategory(PreferenceCategory& cat) } } +/** + @brief Reset all preferences in this category to default + */ +void PreferenceDialog::ResetCategoryToDefault(PreferenceCategory& cat) +{ + auto& children = cat.GetChildren(); + for(const auto& identifier: cat.GetOrdering()) + { + auto& node = children[identifier]; + + //Add child categories + if(node->IsCategory()) + { + auto& subCategory = node->AsCategory(); + ResetCategoryToDefault(subCategory); + } + + //Add preference widgets + if(node->IsPreference()) + { + auto& pref = node->AsPreference(); + pref.ResetToDefault(); + // Clear cache + m_preferenceTemporaries.erase(pref.GetIdentifier()); + } + } +} + /** @brief Run the UI for a single preference */ @@ -328,4 +439,12 @@ void PreferenceDialog::ProcessPreference(Preference& pref) } HelpMarker(pref.GetDescription()); + label = "Default###" + pref.GetIdentifier() + "default"; + ImGui::SameLine(); + if(ImGui::Button(label.c_str())) + { + pref.ResetToDefault(); + // Clear cache + m_preferenceTemporaries.erase(pref.GetIdentifier()); + } } diff --git a/src/ngscopeclient/PreferenceDialog.h b/src/ngscopeclient/PreferenceDialog.h index 5e403af08..c437af547 100644 --- a/src/ngscopeclient/PreferenceDialog.h +++ b/src/ngscopeclient/PreferenceDialog.h @@ -52,6 +52,10 @@ class PreferenceDialog : public Dialog protected: void ProcessCategory(PreferenceCategory& cat); void ProcessPreference(Preference& pref); + bool DefaultButton(const std::string& label, const std::string& id, bool centered = false); + void ResetCategoryToDefault(PreferenceCategory& cat); + void OpenConfirmDialog(const std::string& title, const std::string& message, const std::string& identifier); + bool RenderConfirmDialog(const std::string& identifier); PreferenceManager& m_prefs; @@ -59,6 +63,9 @@ class PreferenceDialog : public Dialog std::vector m_fontShortNames; std::map m_fontReverseMap; + std::string m_confirmDialogTitle; + std::string m_confirmDialogMessage; + void FindFontFiles(const std::string& path); //Temporary values for preferences that we're still configuring From 5c2592731294b15a5d6f23b517d2b86aa93b209f Mon Sep 17 00:00:00 2001 From: fredzo Date: Wed, 14 Jan 2026 19:19:55 +0100 Subject: [PATCH 2/2] Fixed CI compilation. --- src/ngscopeclient/Preference.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngscopeclient/Preference.cpp b/src/ngscopeclient/Preference.cpp index 189dca74b..8ebdce98d 100644 --- a/src/ngscopeclient/Preference.cpp +++ b/src/ngscopeclient/Preference.cpp @@ -440,11 +440,11 @@ impl::PreferenceBuilder Preference::Color(std::string identifier, const ImU32& d static_cast((defaultValue >> IM_COL32_G_SHIFT) & 0xff), static_cast((defaultValue >> IM_COL32_B_SHIFT) & 0xff), static_cast((defaultValue >> IM_COL32_A_SHIFT) & 0xff))); - new (&pref.m_defaultValue) impl::Color(std::move(impl::Color( + new (&pref.m_defaultValue) impl::Color( static_cast((defaultValue >> IM_COL32_R_SHIFT) & 0xff), static_cast((defaultValue >> IM_COL32_G_SHIFT) & 0xff), static_cast((defaultValue >> IM_COL32_B_SHIFT) & 0xff), - static_cast((defaultValue >> IM_COL32_A_SHIFT) & 0xff)))); + static_cast((defaultValue >> IM_COL32_A_SHIFT) & 0xff)); return impl::PreferenceBuilder{ std::move(pref) }; }