Repository: RetroAchievements/RAIntegration
Pull request #636 (2020-11-21 20:27:59)

hook up prefer decimal toggle


Patch:
diff --git a/src/ui/viewmodels/AssetEditorViewModel.cpp b/src/ui/viewmodels/AssetEditorViewModel.cpp
index b8b66667..54a91ea6 100644
--- a/src/ui/viewmodels/AssetEditorViewModel.cpp
+++ b/src/ui/viewmodels/AssetEditorViewModel.cpp
@@ -1,6 +1,7 @@
 #include "AssetEditorViewModel.hh"
 
 #include "services\AchievementRuntime.hh"
+#include "services\IConfiguration.hh"
 #include "services\ServiceLocator.hh"
 
 #include "ui\viewmodels\MessageBoxViewModel.hh"
@@ -18,6 +19,7 @@ const IntModelProperty AssetEditorViewModel::PointsProperty("AssetEditorViewMode
 const StringModelProperty AssetEditorViewModel::BadgeProperty("AssetEditorViewModel", "Badge", L"00000");
 const BoolModelProperty AssetEditorViewModel::PauseOnResetProperty("AssetEditorViewModel", "PauseOnReset", false);
 const BoolModelProperty AssetEditorViewModel::PauseOnTriggerProperty("AssetEditorViewModel", "PauseOnTrigger", false);
+const BoolModelProperty AssetEditorViewModel::DecimalPreferredProperty("AssetEditorViewModel", "DecimalPreferred", false);
 const BoolModelProperty AssetEditorViewModel::AssetLoadedProperty("AssetEditorViewModel", "AssetLoaded", false);
 
 AssetEditorViewModel::AssetEditorViewModel() noexcept
@@ -157,6 +159,20 @@ void AssetEditorViewModel::OnValueChanged(const BoolModelProperty::ChangeArgs& a
             pAchievement->SetPauseOnTrigger(args.tNewValue);
     }
 
+    if (args.Property == DecimalPreferredProperty)
+    {
+        auto& pConfiguration = ra::services::ServiceLocator::GetMutable<ra::services::IConfiguration>();
+        pConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, args.tNewValue);
+    }
+    else if (args.Property == IsVisibleProperty)
+    {
+        if (args.tNewValue)
+        {
+            const auto& pConfiguration = ra::services::ServiceLocator::Get<ra::services::IConfiguration>();
+            SetDecimalPreferred(pConfiguration.IsFeatureEnabled(ra::services::Feature::PreferDecimal));
+        }
+    }
+
     WindowViewModelBase::OnValueChanged(args);
 }
 
diff --git a/src/ui/viewmodels/AssetEditorViewModel.hh b/src/ui/viewmodels/AssetEditorViewModel.hh
index 7e1a0acc..47a358f8 100644
--- a/src/ui/viewmodels/AssetEditorViewModel.hh
+++ b/src/ui/viewmodels/AssetEditorViewModel.hh
@@ -178,6 +178,21 @@ public:
     /// </summary>
     void SetPauseOnTrigger(bool nValue) { SetValue(PauseOnTriggerProperty, nValue); }
 
+    /// <summary>
+    /// The <see cref="ModelProperty" /> for whether or not the condition values should be shown in decimal.
+    /// </summary>
+    static const BoolModelProperty DecimalPreferredProperty;
+
+    /// <summary>
+    /// Gets whether or not the condition values should be shown in decimal.
+    /// </summary>
+    bool IsDecimalPreferred() const { return GetValue(DecimalPreferredProperty); }
+
+    /// <summary>
+    /// Sets whether or not the condition values should be shown in decimal.
+    /// </summary>
+    void SetDecimalPreferred(bool nValue) { SetValue(DecimalPreferredProperty, nValue); }
+
     // ===== Functions =====
 
     void LoadAsset(ra::data::models::AssetModelBase* pAsset);
diff --git a/src/ui/viewmodels/TriggerConditionViewModel.cpp b/src/ui/viewmodels/TriggerConditionViewModel.cpp
index 46c1dfa7..d071ab56 100644
--- a/src/ui/viewmodels/TriggerConditionViewModel.cpp
+++ b/src/ui/viewmodels/TriggerConditionViewModel.cpp
@@ -5,6 +5,7 @@
 
 #include "data\context\GameContext.hh"
 
+#include "services\IConfiguration.hh"
 #include "services\ServiceLocator.hh"
 
 namespace ra {
@@ -241,15 +242,27 @@ void TriggerConditionViewModel::InitializeFrom(const rc_condition_t& pCondition)
 
 std::wstring TriggerConditionViewModel::GetTooltip(const IntModelProperty& nProperty) const
 {
-    if (nProperty == SourceValueProperty && GetSourceType() != TriggerOperandType::Value)
-        return GetAddressTooltip(GetSourceValue());
+    if (nProperty == SourceValueProperty)
+        return GetValueTooltip(GetSourceType(), GetSourceValue());
 
-    if (nProperty == TargetValueProperty && GetTargetType() != TriggerOperandType::Value)
-        return GetAddressTooltip(GetTargetValue());
+    if (nProperty == TargetValueProperty)
+        return GetValueTooltip(GetTargetType(), GetTargetValue());
 
     return L"";
 }
 
+std::wstring TriggerConditionViewModel::GetValueTooltip(TriggerOperandType nType, unsigned int nValue) const
+{
+    if (nType != TriggerOperandType::Value)
+        return GetAddressTooltip(nValue);
+
+    const auto& pConfiguration = ra::services::ServiceLocator::Get<ra::services::IConfiguration>();
+    if (pConfiguration.IsFeatureEnabled(ra::services::Feature::PreferDecimal))
+        return L"";
+
+    return std::to_wstring(nValue);
+}
+
 std::wstring TriggerConditionViewModel::GetAddressTooltip(unsigned nAddress) const
 {
     const auto& pGameContext = ra::services::ServiceLocator::Get<ra::data::context::GameContext>();
diff --git a/src/ui/viewmodels/TriggerConditionViewModel.hh b/src/ui/viewmodels/TriggerConditionViewModel.hh
index 7f19dd90..baf3fe85 100644
--- a/src/ui/viewmodels/TriggerConditionViewModel.hh
+++ b/src/ui/viewmodels/TriggerConditionViewModel.hh
@@ -114,6 +114,7 @@ public:
 private:
     void SerializeAppendOperand(std::string& sBuffer, TriggerOperandType nType, MemSize nSize, unsigned int nValue) const;
 
+    std::wstring GetValueTooltip(TriggerOperandType nType, unsigned nAddress) const;
     std::wstring GetAddressTooltip(unsigned nAddress) const;
 
     void SetOperand(const IntModelProperty& pTypeProperty, const IntModelProperty& pSizeProperty,
diff --git a/src/ui/win32/AssetEditorDialog.cpp b/src/ui/win32/AssetEditorDialog.cpp
index f8b71b95..70a17759 100644
--- a/src/ui/win32/AssetEditorDialog.cpp
+++ b/src/ui/win32/AssetEditorDialog.cpp
@@ -236,6 +236,12 @@ void AssetEditorDialog::BadgeNameBinding::UpdateSourceFromText(const std::wstrin
     }
 }
 
+void AssetEditorDialog::DecimalPreferredBinding::OnValueChanged()
+{
+    m_pOwner->m_bindConditions.RefreshColumn(4); // source value
+    m_pOwner->m_bindConditions.RefreshColumn(8); // target value
+}
+
 AssetEditorDialog::AssetEditorDialog(AssetEditorViewModel& vmAssetEditor)
     : DialogBase(vmAssetEditor),
       m_bindID(vmAssetEditor),
@@ -245,6 +251,7 @@ AssetEditorDialog::AssetEditorDialog(AssetEditorViewModel& vmAssetEditor)
       m_bindBadgeImage(vmAssetEditor),
       m_bindPoints(vmAssetEditor),
       m_bindPauseOnReset(vmAssetEditor),
+      m_bindDecimalPreferred(this, vmAssetEditor),
       m_bindPauseOnTrigger(vmAssetEditor),
       m_bindGroups(vmAssetEditor),
       m_bindConditions(vmAssetEditor.Trigger())
@@ -266,6 +273,7 @@ AssetEditorDialog::AssetEditorDialog(AssetEditorViewModel& vmAssetEditor)
 
     m_bindPauseOnReset.BindCheck(AssetEditorViewModel::PauseOnResetProperty);
     m_bindPauseOnTrigger.BindCheck(AssetEditorViewModel::PauseOnTriggerProperty);
+    m_bindDecimalPreferred.BindCheck(AssetEditorViewModel::DecimalPreferredProperty);
 
     auto pGroupColumn = std::make_unique<ra::ui::win32::bindings::GridTextColumnBinding>(
         TriggerViewModel::GroupViewModel::LabelProperty);
@@ -372,6 +380,7 @@ BOOL AssetEditorDialog::OnInitDialog()
 
     m_bindPauseOnReset.SetControl(*this, IDC_RA_CHK_ACH_PAUSE_ON_RESET);
     m_bindPauseOnTrigger.SetControl(*this, IDC_RA_CHK_ACH_PAUSE_ON_TRIGGER);
+    m_bindDecimalPreferred.SetControl(*this, IDC_RA_CHK_SHOW_DECIMALS);
 
     return DialogBase::OnInitDialog();
 }
diff --git a/src/ui/win32/AssetEditorDialog.hh b/src/ui/win32/AssetEditorDialog.hh
index 98a5763e..fce40646 100644
--- a/src/ui/win32/AssetEditorDialog.hh
+++ b/src/ui/win32/AssetEditorDialog.hh
@@ -54,6 +54,19 @@ private:
         void UpdateSourceFromText(const std::wstring& sText) override;
     };
 
+    class DecimalPreferredBinding : public ra::ui::win32::bindings::CheckBoxBinding
+    {
+    public:
+        DecimalPreferredBinding(AssetEditorDialog* pOwner, ViewModelBase& vmViewModel) noexcept
+            : ra::ui::win32::bindings::CheckBoxBinding(vmViewModel), m_pOwner(pOwner) {}
+
+    protected:
+        void OnValueChanged() override;
+
+    private:
+        AssetEditorDialog* m_pOwner;
+    };
+
     ra::ui::win32::bindings::NumericTextBoxBinding m_bindID;
     ra::ui::win32::bindings::TextBoxBinding m_bindName;
     ra::ui::win32::bindings::TextBoxBinding m_bindDescription;
@@ -63,6 +76,7 @@ private:
 
     ra::ui::win32::bindings::CheckBoxBinding m_bindPauseOnReset;
     ra::ui::win32::bindings::CheckBoxBinding m_bindPauseOnTrigger;
+    DecimalPreferredBinding m_bindDecimalPreferred;
 
     ra::ui::win32::bindings::GridBinding m_bindGroups;
     ra::ui::win32::bindings::GridBinding m_bindConditions;
diff --git a/src/ui/win32/bindings/CheckBoxBinding.hh b/src/ui/win32/bindings/CheckBoxBinding.hh
index f038afc3..a0edba9e 100644
--- a/src/ui/win32/bindings/CheckBoxBinding.hh
+++ b/src/ui/win32/bindings/CheckBoxBinding.hh
@@ -36,14 +36,18 @@ public:
         {
             const auto nState = Button_GetCheck(m_hWnd);
             SetValue(*m_pIsCheckedProperty, (nState == BST_CHECKED));
+            OnValueChanged();
         }
     }
 
 protected:
-    void OnViewModelBoolValueChanged(const BoolModelProperty::ChangeArgs& args) noexcept override
+    void OnViewModelBoolValueChanged(const BoolModelProperty::ChangeArgs& args) override
     {
         if (m_pIsCheckedProperty && *m_pIsCheckedProperty == args.Property)
+        {
             Button_SetCheck(m_hWnd, args.tNewValue ? BST_CHECKED : BST_UNCHECKED);
+            OnValueChanged();
+        }
     }
 
 private:
diff --git a/src/ui/win32/bindings/GridBinding.cpp b/src/ui/win32/bindings/GridBinding.cpp
index 4769ed40..ef671f44 100644
--- a/src/ui/win32/bindings/GridBinding.cpp
+++ b/src/ui/win32/bindings/GridBinding.cpp
@@ -50,14 +50,17 @@ void GridBinding::BindColumn(gsl::index nColumn, std::unique_ptr<GridColumnBindi
         UpdateLayout();
 
         if (m_vmItems)
-        {
-            DisableBinding();
-            UpdateItems(nColumn);
-            EnableBinding();
-        }
+            RefreshColumn(nColumn);
     }
 }
 
+void GridBinding::RefreshColumn(gsl::index nColumn)
+{
+    DisableBinding();
+    UpdateItems(nColumn);
+    EnableBinding();
+}
+
 void GridBinding::BindItems(ViewModelCollectionBase& vmItems)
 {
     if (m_vmItems != nullptr)
diff --git a/src/ui/win32/bindings/GridBinding.hh b/src/ui/win32/bindings/GridBinding.hh
index 9cab3942..c67a8c51 100644
--- a/src/ui/win32/bindings/GridBinding.hh
+++ b/src/ui/win32/bindings/GridBinding.hh
@@ -26,6 +26,7 @@ public:
     void SetHWND(DialogBase& pDialog, HWND hControl) override;
 
     void BindColumn(gsl::index nColumn, std::unique_ptr<GridColumnBinding> pColumnBinding);
+    void RefreshColumn(gsl::index nColumn);
 
     void BindItems(ViewModelCollectionBase& vmItems);
     ViewModelCollectionBase& GetItems() noexcept { return *m_vmItems; }


Test patch:
diff --git a/tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp b/tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp
index 0ddb9629..e7ebeb8e 100644
--- a/tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp
@@ -6,6 +6,7 @@
 #include "tests\data\DataAsserts.hh"
 
 #include "tests\mocks\MockClock.hh"
+#include "tests\mocks\MockConfiguration.hh"
 #include "tests\mocks\MockAchievementRuntime.hh"
 
 using namespace Microsoft::VisualStudio::CppUnitTestFramework;
@@ -41,6 +42,7 @@ private:
 
         ra::services::mocks::MockAchievementRuntime mockRuntime;
         ra::services::mocks::MockClock mockClock;
+        ra::services::mocks::MockConfiguration mockConfiguration;
     };
 
 
@@ -59,6 +61,7 @@ public:
         Assert::AreEqual(std::wstring(L"00000"), editor.GetBadge());
         Assert::IsFalse(editor.IsPauseOnReset());
         Assert::IsFalse(editor.IsPauseOnTrigger());
+        Assert::IsFalse(editor.IsDecimalPreferred());
         Assert::IsFalse(editor.IsAssetLoaded());
     }
 
@@ -471,6 +474,42 @@ public:
         editor.DoFrame();
         Assert::AreEqual(6U, pCondition->GetCurrentHits());
     }
+
+    TEST_METHOD(TestDecimalPreferredOnVisible)
+    {
+        AssetEditorViewModelHarness editor;
+        Assert::IsFalse(editor.IsDecimalPreferred());
+
+        editor.SetIsVisible(true);
+        Assert::IsFalse(editor.IsDecimalPreferred());
+
+        editor.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
+        Assert::IsFalse(editor.IsDecimalPreferred());
+
+        editor.SetIsVisible(false);
+        Assert::IsFalse(editor.IsDecimalPreferred());
+
+        editor.SetIsVisible(true);
+        Assert::IsTrue(editor.IsDecimalPreferred());
+
+        editor.SetIsVisible(false);
+        Assert::IsTrue(editor.IsDecimalPreferred());
+    }
+
+    TEST_METHOD(TestDecimalPreferredUpdatesConfiguration)
+    {
+        AssetEditorViewModelHarness editor;
+        Assert::IsFalse(editor.IsDecimalPreferred());
+        Assert::IsFalse(editor.mockConfiguration.IsFeatureEnabled(ra::services::Feature::PreferDecimal));
+
+        editor.SetDecimalPreferred(true);
+        Assert::IsTrue(editor.IsDecimalPreferred());
+        Assert::IsTrue(editor.mockConfiguration.IsFeatureEnabled(ra::services::Feature::PreferDecimal));
+
+        editor.SetDecimalPreferred(false);
+        Assert::IsFalse(editor.IsDecimalPreferred());
+        Assert::IsFalse(editor.mockConfiguration.IsFeatureEnabled(ra::services::Feature::PreferDecimal));
+    }
 };
 
 } // namespace tests
diff --git a/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp b/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
index b47428f8..a2dfbc48 100644
--- a/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
@@ -3,6 +3,7 @@
 #include "ui\viewmodels\TriggerConditionViewModel.hh"
 
 #include "tests\RA_UnitTestHelpers.h"
+#include "tests\mocks\MockConfiguration.hh"
 #include "tests\mocks\MockGameContext.hh"
 
 using namespace Microsoft::VisualStudio::CppUnitTestFramework;
@@ -115,6 +116,7 @@ private:
     {
     public:
         ra::data::context::mocks::MockGameContext mockGameContext;
+        ra::services::mocks::MockConfiguration mockConfiguration;
 
         void Parse(const std::string& sInput)
         {
@@ -233,6 +235,7 @@ public:
     {
         TriggerConditionViewModelHarness condition;
         condition.mockGameContext.SetCodeNote({ 8U }, L"This is a note.");
+        condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
         condition.Parse("0xH0008=3");
 
         Assert::AreEqual(std::wstring(L"0x0008\r\nThis is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
@@ -242,6 +245,7 @@ public:
     TEST_METHOD(TestTooltipAddressNoCodeNote)
     {
         TriggerConditionViewModelHarness condition;
+        condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
         condition.Parse("0xH0008=3");
 
         Assert::AreEqual(std::wstring(L"0x0008\r\n[No code note]"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
@@ -252,6 +256,7 @@ public:
     {
         TriggerConditionViewModelHarness condition;
         condition.mockGameContext.SetCodeNote({ 8U }, L"A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ");
+        condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
         condition.Parse("0xH0008=3");
 
         Assert::AreEqual(std::wstring(L"0x0008\r\nA\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nO\nP\nQ\nR\nS\nT\nU\n..."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
@@ -263,11 +268,35 @@ public:
         TriggerConditionViewModelHarness condition;
         condition.mockGameContext.SetCodeNote({ 8U }, L"This is a note.");
         condition.mockGameContext.SetCodeNote({ 9U }, L"This is another note.");
+        condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
         condition.Parse("0xH0008>d0xH0009");
 
         Assert::AreEqual(std::wstring(L"0x0008\r\nThis is a note."), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
         Assert::AreEqual(std::wstring(L"0x0009\r\nThis is another note."), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
     }
+
+    TEST_METHOD(TestTooltipValueDecimal)
+    {
+        TriggerConditionViewModelHarness condition;
+        condition.mockGameContext.SetCodeNote({ 99U }, L"This is a note.");
+        condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, true);
+        condition.Parse("255=99");
+
+        Assert::AreEqual(std::wstring(L""), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
+        Assert::AreEqual(std::wstring(L""), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
+    }
+
+    TEST_METHOD(TestTooltipValueHex)
+    {
+        TriggerConditionViewModelHarness condition;
+        condition.mockGameContext.SetCodeNote({ 99U }, L"This is a note.");
+        condition.mockConfiguration.SetFeatureEnabled(ra::services::Feature::PreferDecimal, false);
+        condition.Parse("255=99");
+
+        // in hex mode, the fields will show "0xFF" and "0x63", so display the decimal value in the tooltip
+        Assert::AreEqual(std::wstring(L"255"), condition.GetTooltip(TriggerConditionViewModel::SourceValueProperty));
+        Assert::AreEqual(std::wstring(L"99"), condition.GetTooltip(TriggerConditionViewModel::TargetValueProperty));
+    }
 };
 
 } // namespace tests