Before this change, the pchs for CalcViewModel and Calculator project referenced project headers. If those project headers (or any of their dependencies) were to change, then the pch would be recompiled, slowing local build times. By removing references to project headers, the pch will be compiled once and is resilient to changes in the project. Now that project headers are explicit about their dependencies, when there is a change to a project header only the translation units referencing the modified header will need to be rebuilt. - Manually tested by ensuring Calculator project builds locally. @Microsoft/calculator-team
425 lines
18 KiB
C++
425 lines
18 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
#pragma once
|
|
|
|
#include "CalcManager/UnitConverter.h"
|
|
#include "Common/Utils.h"
|
|
#include "Common/NetworkManager.h"
|
|
#include "Common/Automation/NarratorAnnouncement.h"
|
|
#include "Common/ConversionResultTaskHelper.h"
|
|
#include "Common/CalculatorButtonUser.h"
|
|
#include "Common/NavCategory.h"
|
|
|
|
namespace CalculatorApp
|
|
{
|
|
namespace ViewModel
|
|
{
|
|
[Windows::UI::Xaml::Data::Bindable]
|
|
public ref class Category sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
|
{
|
|
internal:
|
|
Category(const UnitConversionManager::Category& category) :
|
|
m_original(category)
|
|
{ }
|
|
|
|
public:
|
|
OBSERVABLE_OBJECT();
|
|
|
|
property Platform::String^ Name
|
|
{
|
|
Platform::String^ get() { return ref new Platform::String(m_original.name.c_str()); }
|
|
}
|
|
|
|
property Windows::UI::Xaml::Visibility NegateVisibility
|
|
{
|
|
Windows::UI::Xaml::Visibility get()
|
|
{
|
|
return m_original.supportsNegative
|
|
? Windows::UI::Xaml::Visibility::Visible
|
|
: Windows::UI::Xaml::Visibility::Collapsed;
|
|
}
|
|
}
|
|
|
|
internal:
|
|
const UnitConversionManager::Category& GetModelCategory() const { return m_original; }
|
|
|
|
private:
|
|
const UnitConversionManager::Category m_original;
|
|
};
|
|
|
|
[Windows::UI::Xaml::Data::Bindable]
|
|
public ref class Unit sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
|
{
|
|
internal:
|
|
Unit(const UnitConversionManager::Unit& unit) :
|
|
m_original(unit)
|
|
{ }
|
|
|
|
public:
|
|
OBSERVABLE_OBJECT();
|
|
|
|
property Platform::String^ Name
|
|
{
|
|
Platform::String^ get() { return ref new Platform::String(m_original.name.c_str()); }
|
|
}
|
|
|
|
property Platform::String^ AccessibleName
|
|
{
|
|
Platform::String^ get() { return ref new Platform::String(m_original.accessibleName.c_str()); }
|
|
}
|
|
|
|
property Platform::String^ Abbreviation
|
|
{
|
|
Platform::String^ get() { return ref new Platform::String(m_original.abbreviation.c_str()); }
|
|
}
|
|
|
|
// This method is used to return the desired autonamtion name for default unit in UnitConveter combo box.
|
|
Platform::String^ ToString() override
|
|
{
|
|
return AccessibleName;
|
|
}
|
|
|
|
internal:
|
|
const UnitConversionManager::Unit& GetModelUnit() const { return m_original; }
|
|
|
|
private:
|
|
const UnitConversionManager::Unit m_original;
|
|
};
|
|
|
|
[Windows::UI::Xaml::Data::Bindable]
|
|
public ref class SupplementaryResult sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
|
{
|
|
internal:
|
|
SupplementaryResult(Platform::String^ value, Unit^ unit) :
|
|
m_Value(value),
|
|
m_Unit(unit)
|
|
{}
|
|
|
|
bool IsWhimsical() const
|
|
{
|
|
return m_Unit->GetModelUnit().isWhimsical;
|
|
}
|
|
|
|
Platform::String^ GetLocalizedAutomationName();
|
|
|
|
public:
|
|
OBSERVABLE_OBJECT();
|
|
|
|
OBSERVABLE_PROPERTY_R(Platform::String^, Value);
|
|
OBSERVABLE_PROPERTY_R(CalculatorApp::ViewModel::Unit^, Unit);
|
|
};
|
|
|
|
interface class IActivatable
|
|
{
|
|
virtual property bool IsActive;
|
|
};
|
|
|
|
template<typename TActivatable>
|
|
ref class Activatable sealed : public IActivatable
|
|
{
|
|
private:
|
|
TActivatable m_activatable;
|
|
|
|
public:
|
|
Activatable(TActivatable activatable) : m_activatable(activatable)
|
|
{ }
|
|
|
|
virtual property bool IsActive
|
|
{
|
|
bool get() { return m_activatable->IsActive; }
|
|
void set(bool value) { m_activatable->IsActive = value; }
|
|
}
|
|
};
|
|
|
|
template<typename TActivatable>
|
|
IActivatable^ AsActivatable(TActivatable activatable)
|
|
{
|
|
return ref new Activatable<TActivatable>(activatable);
|
|
}
|
|
|
|
namespace UnitConverterViewModelProperties
|
|
{
|
|
extern Platform::StringReference CurrentCategory;
|
|
extern Platform::StringReference Unit1;
|
|
extern Platform::StringReference Unit2;
|
|
extern Platform::StringReference Value1Active;
|
|
extern Platform::StringReference Value2Active;
|
|
extern Platform::StringReference SupplementaryVisibility;
|
|
extern Platform::StringReference SupplementaryResults;
|
|
extern Platform::StringReference CurrencySymbol1;
|
|
extern Platform::StringReference CurrencySymbol2;
|
|
extern Platform::StringReference CurrencySymbolVisibility;
|
|
extern Platform::StringReference CurrencyRatioEquality;
|
|
extern Platform::StringReference NetworkBehavior;
|
|
extern Platform::StringReference CurrencyDataLoadFailed;
|
|
extern Platform::StringReference CurrencyDataIsWeekOld;
|
|
extern Platform::StringReference IsCurrencyLoadingVisible;
|
|
}
|
|
|
|
[Windows::UI::Xaml::Data::Bindable]
|
|
public ref class UnitConverterViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
|
{
|
|
internal:
|
|
UnitConverterViewModel(const std::shared_ptr<UnitConversionManager::IUnitConverter>& model);
|
|
|
|
public:
|
|
OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged);
|
|
|
|
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<Category^>^, Categories);
|
|
OBSERVABLE_PROPERTY_RW(Category^, CurrentCategory);
|
|
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, Mode);
|
|
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<Unit^>^, Units);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencySymbol1);
|
|
OBSERVABLE_PROPERTY_RW(Unit^, Unit1);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, Value1);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencySymbol2);
|
|
OBSERVABLE_PROPERTY_RW(Unit^, Unit2);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, Value2);
|
|
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<SupplementaryResult^>^, SupplementaryResults);
|
|
OBSERVABLE_PROPERTY_RW(bool, Value1Active);
|
|
OBSERVABLE_PROPERTY_RW(bool, Value2Active);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, Value1AutomationName);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, Value2AutomationName);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, Unit1AutomationName);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, Unit2AutomationName);
|
|
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement^, Announcement);
|
|
OBSERVABLE_PROPERTY_RW(bool, IsDecimalEnabled);
|
|
OBSERVABLE_PROPERTY_RW(bool, IsDropDownOpen);
|
|
OBSERVABLE_PROPERTY_RW(bool, IsDropDownEnabled);
|
|
OBSERVABLE_PROPERTY_RW(bool, IsCurrencyLoadingVisible);
|
|
OBSERVABLE_PROPERTY_RW(bool, IsCurrencyCurrentCategory);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyRatioEquality);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyRatioEqualityAutomationName);
|
|
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyTimestamp);
|
|
OBSERVABLE_PROPERTY_RW(CalculatorApp::NetworkAccessBehavior, NetworkBehavior);
|
|
OBSERVABLE_PROPERTY_RW(bool, CurrencyDataLoadFailed);
|
|
OBSERVABLE_PROPERTY_RW(bool, CurrencyDataIsWeekOld);
|
|
|
|
property Windows::UI::Xaml::Visibility SupplementaryVisibility
|
|
{
|
|
Windows::UI::Xaml::Visibility get()
|
|
{
|
|
return SupplementaryResults->Size > 0
|
|
? Windows::UI::Xaml::Visibility::Visible
|
|
: Windows::UI::Xaml::Visibility::Collapsed;
|
|
}
|
|
}
|
|
|
|
property Windows::UI::Xaml::Visibility CurrencySymbolVisibility
|
|
{
|
|
Windows::UI::Xaml::Visibility get()
|
|
{
|
|
return (CurrencySymbol1->IsEmpty() || CurrencySymbol2->IsEmpty())
|
|
? Windows::UI::Xaml::Visibility::Collapsed
|
|
: Windows::UI::Xaml::Visibility::Visible;
|
|
}
|
|
}
|
|
|
|
COMMAND_FOR_METHOD(CategoryChanged, UnitConverterViewModel::OnCategoryChanged);
|
|
COMMAND_FOR_METHOD(UnitChanged, UnitConverterViewModel::OnUnitChanged);
|
|
COMMAND_FOR_METHOD(SwitchActive, UnitConverterViewModel::OnSwitchActive);
|
|
COMMAND_FOR_METHOD(ButtonPressed, UnitConverterViewModel::OnButtonPressed);
|
|
COMMAND_FOR_METHOD(CopyCommand, UnitConverterViewModel::OnCopyCommand);
|
|
COMMAND_FOR_METHOD(PasteCommand, UnitConverterViewModel::OnPasteCommand);
|
|
|
|
void AnnounceConversionResult();
|
|
|
|
internal:
|
|
void ResetView();
|
|
void PopulateData();
|
|
NumbersAndOperatorsEnum MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate);
|
|
void DisplayPasteError();
|
|
void OnValueActivated(IActivatable^ control);
|
|
void OnPaste(Platform::String^ stringToPaste, CalculatorApp::Common::ViewMode mode);
|
|
|
|
void OnCopyCommand(Platform::Object^ parameter);
|
|
void OnPasteCommand(Platform::Object^ parameter);
|
|
|
|
Platform::String^ GetLocalizedAutomationName(_In_ Platform::String^ displayvalue, _In_ Platform::String^ unitname, _In_ Platform::String^ format);
|
|
Platform::String^ GetLocalizedConversionResultStringFormat(_In_ Platform::String^ fromValue, _In_ Platform::String^ fromUnit, _In_ Platform::String^ toValue, _In_ Platform::String^ toUnit);
|
|
void UpdateValue1AutomationName();
|
|
void UpdateValue2AutomationName();
|
|
Platform::String^ Serialize();
|
|
void Deserialize(Platform::String^ state);
|
|
|
|
//Saving And Restoring User Prefernces of Category and Associated-Units across Sessions.
|
|
void SaveUserPreferences();
|
|
void RestoreUserPreferences();
|
|
|
|
void OnCurrencyDataLoadFinished(bool didLoad);
|
|
void OnCurrencyTimestampUpdated(_In_ const std::wstring& timestamp, bool isWeekOld);
|
|
void RefreshCurrencyRatios();
|
|
void OnNetworkBehaviorChanged(_In_ CalculatorApp::NetworkAccessBehavior newBehavior);
|
|
|
|
const std::wstring& GetValueFromUnlocalized() const
|
|
{
|
|
return m_valueFromUnlocalized;
|
|
}
|
|
const std::wstring& GetValueToUnlocalized() const
|
|
{
|
|
return m_valueToUnlocalized;
|
|
}
|
|
|
|
// used by UnitConverterVMCallback
|
|
void UpdateDisplay(const std::wstring& from, const std::wstring& to);
|
|
void UpdateSupplementaryResults(const std::vector<std::tuple<std::wstring, UnitConversionManager::Unit>>& suggestedValues);
|
|
void OnMaxDigitsReached();
|
|
|
|
void BuildUnitList(const std::vector<UnitConversionManager::Unit>& modelUnitList);
|
|
Unit^ FindUnitInList(UnitConversionManager::Unit target);
|
|
void SetSelectedUnits();
|
|
|
|
private:
|
|
void InitializeView();
|
|
void OnPropertyChanged(Platform::String^ prop);
|
|
void OnCategoryChanged(Platform::Object^ unused);
|
|
void OnUnitChanged(Platform::Object^ unused);
|
|
void OnSwitchActive(Platform::Object^ unused);
|
|
UnitConversionManager::Command CommandFromButtonId(CalculatorApp::NumbersAndOperatorsEnum button);
|
|
void SupplementaryResultsTimerTick(Windows::System::Threading::ThreadPoolTimer^ timer);
|
|
void SupplementaryResultsTimerCancel(Windows::System::Threading::ThreadPoolTimer^ timer);
|
|
void RefreshSupplementaryResults();
|
|
void UpdateInputBlocked(_In_ const std::wstring& currencyInput);
|
|
bool UnitsAreValid();
|
|
|
|
void OnButtonPressed(Platform::Object^ parameter);
|
|
Platform::String^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings);
|
|
|
|
void StartConversionResultTimer();
|
|
|
|
std::shared_ptr<UnitConversionManager::IUnitConverter> m_model;
|
|
wchar_t m_decimalSeparator;
|
|
|
|
enum class ConversionParameter
|
|
{
|
|
Source,
|
|
Target
|
|
} m_value1cp;
|
|
property Platform::String^ ValueFrom
|
|
{
|
|
Platform::String^ get() { return m_value1cp == ConversionParameter::Source ? Value1 : Value2; }
|
|
void set(Platform::String^ value) { m_value1cp == ConversionParameter::Source ? Value1 = value : Value2 = value; }
|
|
}
|
|
property Unit^ UnitFrom
|
|
{
|
|
Unit^ get() { return m_value1cp == ConversionParameter::Source ? Unit1 : Unit2; }
|
|
void set(Unit^ value) { m_value1cp == ConversionParameter::Source ? Unit1 = value : Unit2 = value; }
|
|
}
|
|
property Platform::String^ ValueTo
|
|
{
|
|
Platform::String^ get() { return m_value1cp == ConversionParameter::Target ? Value1 : Value2; }
|
|
void set(Platform::String^ value) { m_value1cp == ConversionParameter::Target ? Value1 = value : Value2 = value; }
|
|
}
|
|
property Unit^ UnitTo
|
|
{
|
|
Unit^ get() { return m_value1cp == ConversionParameter::Target ? Unit1 : Unit2; }
|
|
void set(Unit^ value) { m_value1cp == ConversionParameter::Target ? Unit1 = value : Unit2 = value; }
|
|
}
|
|
void SwitchConversionParameters()
|
|
{
|
|
m_value1cp = m_value1cp == ConversionParameter::Source ? ConversionParameter::Target : ConversionParameter::Source;
|
|
}
|
|
|
|
private:
|
|
bool m_isInputBlocked;
|
|
Windows::System::Threading::ThreadPoolTimer^ m_supplementaryResultsTimer;
|
|
bool m_resettingTimer;
|
|
std::vector<std::tuple<std::wstring, UnitConversionManager::Unit>> m_cachedSuggestedValues;
|
|
std::mutex m_cacheMutex;
|
|
Windows::Globalization::NumberFormatting::DecimalFormatter^ m_decimalFormatter;
|
|
Windows::Globalization::NumberFormatting::CurrencyFormatter^ m_currencyFormatter;
|
|
int m_currencyMaxFractionDigits;
|
|
std::wstring m_valueFromUnlocalized;
|
|
std::wstring m_valueToUnlocalized;
|
|
bool m_relocalizeStringOnSwitch;
|
|
// For Saving the User Preferences only if the Unit convertyer ViewModel is initialised for the first time
|
|
bool m_IsFirstTime;
|
|
|
|
Platform::String^ m_localizedValueFromFormat;
|
|
Platform::String^ m_localizedValueFromDecimalFormat;
|
|
Platform::String^ m_localizedValueToFormat;
|
|
Platform::String^ m_localizedConversionResultFormat;
|
|
Platform::String^ m_localizedInputUnitName;
|
|
Platform::String^ m_localizedOutputUnitName;
|
|
|
|
bool m_isValue1Updating;
|
|
bool m_isValue2Updating;
|
|
std::wstring m_lastAnnouncedFrom;
|
|
std::wstring m_lastAnnouncedTo;
|
|
Platform::String^ m_lastAnnouncedConversionResult;
|
|
|
|
bool m_isCurrencyDataLoaded;
|
|
|
|
std::unique_ptr<CalculatorApp::Common::ConversionResultTaskHelper> m_conversionResultTaskHelper;
|
|
};
|
|
|
|
class UnitConverterVMCallback : public UnitConversionManager::IUnitConverterVMCallback
|
|
{
|
|
public:
|
|
UnitConverterVMCallback(UnitConverterViewModel^ viewModel) : m_viewModel(viewModel)
|
|
{}
|
|
|
|
void DisplayCallback(const std::wstring& from, const std::wstring& to) override
|
|
{
|
|
m_viewModel->UpdateDisplay(from, to);
|
|
}
|
|
|
|
void SuggestedValueCallback(
|
|
const std::vector<std::tuple<std::wstring, UnitConversionManager::Unit>>& suggestedValues) override
|
|
{
|
|
m_viewModel->UpdateSupplementaryResults(suggestedValues);
|
|
}
|
|
|
|
void MaxDigitsReached()
|
|
{
|
|
m_viewModel->OnMaxDigitsReached();
|
|
}
|
|
|
|
private:
|
|
UnitConverterViewModel^ m_viewModel;
|
|
};
|
|
|
|
class ViewModelCurrencyCallback : public UnitConversionManager::IViewModelCurrencyCallback
|
|
{
|
|
public:
|
|
ViewModelCurrencyCallback(UnitConverterViewModel^ viewModel) : m_viewModel(viewModel)
|
|
{}
|
|
|
|
void CurrencyDataLoadFinished(bool didLoad) override
|
|
{
|
|
m_viewModel->OnCurrencyDataLoadFinished(didLoad);
|
|
}
|
|
|
|
void CurrencySymbolsCallback(const std::wstring& symbol1, const std::wstring& symbol2) override
|
|
{
|
|
Platform::String^ sym1 = Platform::StringReference(symbol1.c_str());
|
|
Platform::String^ sym2 = Platform::StringReference(symbol2.c_str());
|
|
|
|
bool value1Active = m_viewModel->Value1Active;
|
|
m_viewModel->CurrencySymbol1 = value1Active ? sym1 : sym2;
|
|
m_viewModel->CurrencySymbol2 = value1Active ? sym2 : sym1;
|
|
}
|
|
|
|
void CurrencyRatiosCallback(_In_ const std::wstring& ratioEquality, _In_ const std::wstring& accRatioEquality) override
|
|
{
|
|
m_viewModel->CurrencyRatioEquality = ref new Platform::String(ratioEquality.c_str());
|
|
m_viewModel->CurrencyRatioEqualityAutomationName = ref new Platform::String(accRatioEquality.c_str());
|
|
}
|
|
|
|
void CurrencyTimestampCallback(_In_ const std::wstring& timestamp, bool isWeekOld) override
|
|
{
|
|
m_viewModel->OnCurrencyTimestampUpdated(timestamp, isWeekOld);
|
|
}
|
|
|
|
void NetworkBehaviorChanged(_In_ int newBehavior) override
|
|
{
|
|
m_viewModel->OnNetworkBehaviorChanged(static_cast<CalculatorApp::NetworkAccessBehavior>(newBehavior));
|
|
}
|
|
|
|
private:
|
|
UnitConverterViewModel^ m_viewModel;
|
|
};
|
|
}
|
|
}
|