Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use different formatter for different currency #1432

Merged
merged 14 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/CalcManager/UnitConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static constexpr uint32_t OPTIMALDIGITSALLOWED = 7U;
static constexpr wchar_t LEFTESCAPECHAR = L'{';
static constexpr wchar_t RIGHTESCAPECHAR = L'}';

static const double OPTIMALDECIMALALLOWED = 1e-6; // pow(10, -1 * (OPTIMALDIGITSALLOWED - 1));
static const double OPTIMALDECIMALALLOWED = 1e-6; // pow(10, -1 * (OPTIMALDIGITSALLOWED - 1));
static const double MINIMUMDECIMALALLOWED = 1e-14; // pow(10, -1 * (MAXIMUMDIGITSALLOWED - 1));

unordered_map<wchar_t, wstring> quoteConversions;
Expand Down Expand Up @@ -149,6 +149,11 @@ void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType
return;
}

if (m_fromType != fromType)
{
m_switchedActive = true;
}

m_fromType = fromType;
m_toType = toType;
Calculate();
Expand Down Expand Up @@ -191,6 +196,11 @@ void UnitConverter::SwitchActive(const wstring& newValue)
}
}

bool UnitConversionManager::UnitConverter::IsSwitchedActive() const
{
return m_switchedActive;
}

xuhongxu96 marked this conversation as resolved.
Show resolved Hide resolved
wstring UnitConverter::CategoryToString(const Category& c, wstring_view delimiter)
{
return Quote(std::to_wstring(c.id))
Expand Down
2 changes: 2 additions & 0 deletions src/CalcManager/UnitConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ namespace UnitConversionManager
virtual Category GetCurrentCategory() = 0;
virtual void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) = 0;
virtual void SwitchActive(const std::wstring& newValue) = 0;
virtual bool IsSwitchedActive() const = 0;
virtual std::wstring SaveUserPreferences() = 0;
virtual void RestoreUserPreferences(_In_ std::wstring_view userPreferences) = 0;
virtual void SendCommand(Command command) = 0;
Expand All @@ -246,6 +247,7 @@ namespace UnitConversionManager
Category GetCurrentCategory() override;
void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) override;
void SwitchActive(const std::wstring& newValue) override;
bool IsSwitchedActive() const override;
std::wstring SaveUserPreferences() override;
void RestoreUserPreferences(std::wstring_view userPreference) override;
void SendCommand(Command command) override;
Expand Down
128 changes: 100 additions & 28 deletions src/CalcViewModel/UnitConverterViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ UnitConverterViewModel::UnitConverterViewModel(const shared_ptr<UCM::IUnitConver
m_currencyFormatter->IsGrouped = true;
m_currencyFormatter->Mode = CurrencyFormatterMode::UseCurrencyCode;
m_currencyFormatter->ApplyRoundingForCurrency(RoundingAlgorithm::RoundHalfDown);
m_currencyMaxFractionDigits = m_currencyFormatter->FractionDigits;

auto resourceLoader = AppResourceProvider::GetInstance();
m_localizedValueFromFormat = resourceLoader->GetResourceString(UnitConverterResourceKeys::ValueFromFormat);
Expand Down Expand Up @@ -228,7 +227,9 @@ void UnitConverterViewModel::OnUnitChanged(Object ^ parameter)
return;
}

UpdateCurrencyFormatter();
m_model->SetCurrentUnitTypes(UnitFrom->GetModelUnit(), UnitTo->GetModelUnit());

if (m_supplementaryResultsTimer != nullptr)
{
// End timer to show results immediately
Expand All @@ -246,7 +247,7 @@ void UnitConverterViewModel::OnSwitchActive(Platform::Object ^ unused)
if (m_relocalizeStringOnSwitch)
{
// clean up any ill-formed strings that were in progress before the switch
ValueFrom = ConvertToLocalizedString(m_valueFromUnlocalized, false);
ValueFrom = ConvertToLocalizedString(m_valueFromUnlocalized, false, CurrencyFormatterParameterFrom);
}

SwitchConversionParameters();
Expand All @@ -269,9 +270,11 @@ void UnitConverterViewModel::OnSwitchActive(Platform::Object ^ unused)

m_isInputBlocked = false;
m_model->SwitchActive(m_valueFromUnlocalized);

UpdateIsDecimalEnabled();
}

String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings)
String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings, CurrencyFormatterParameter cfp)
{
Platform::String ^ result;

Expand All @@ -280,10 +283,33 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st
return result;
}

CurrencyFormatter ^ currencyFormatter;

switch (cfp)
{
case CurrencyFormatterParameter::ForValue1:
currencyFormatter = m_currencyFormatter1;
break;
case CurrencyFormatterParameter::ForValue2:
currencyFormatter = m_currencyFormatter2;
break;
default:
currencyFormatter = m_currencyFormatter;
break;
}

// If unit hasn't been set, currencyFormatter1/2 is nullptr. Fallback to default.
if (currencyFormatter == nullptr)
{
currencyFormatter = m_currencyFormatter;
}

int lastCurrencyFractionDigits = currencyFormatter->FractionDigits;

m_decimalFormatter->IsDecimalPointAlwaysDisplayed = false;
m_decimalFormatter->FractionDigits = 0;
m_currencyFormatter->IsDecimalPointAlwaysDisplayed = false;
m_currencyFormatter->FractionDigits = 0;
currencyFormatter->IsDecimalPointAlwaysDisplayed = false;
currencyFormatter->FractionDigits = 0;

wstring::size_type posOfE = stringToLocalize.find(L'e');
if (posOfE != wstring::npos)
Expand All @@ -293,7 +319,8 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st
std::wstring significandStr(stringToLocalize.substr(0, posOfE));
std::wstring exponentStr(stringToLocalize.substr(posOfSign + 1, stringToLocalize.length() - posOfSign));

result += ConvertToLocalizedString(significandStr, allowPartialStrings) + "e" + signOfE + ConvertToLocalizedString(exponentStr, allowPartialStrings);
result += ConvertToLocalizedString(significandStr, allowPartialStrings, cfp) + "e" + signOfE
xuhongxu96 marked this conversation as resolved.
Show resolved Hide resolved
+ ConvertToLocalizedString(exponentStr, allowPartialStrings, cfp);
}
else
{
Expand All @@ -304,26 +331,26 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st

if (hasDecimal)
{
if (allowPartialStrings)
if (allowPartialStrings && lastCurrencyFractionDigits > 0)
{
// allow "in progress" strings, like "3." that occur during the composition of
// a final number. Without this, when typing the three characters in "3.2"
// you don't see the decimal point when typing it, you only see it once you've finally
// typed a post-decimal digit.

m_decimalFormatter->IsDecimalPointAlwaysDisplayed = true;
m_currencyFormatter->IsDecimalPointAlwaysDisplayed = true;
currencyFormatter->IsDecimalPointAlwaysDisplayed = true;
}

// force post-decimal digits so that trailing zeroes entered by the user aren't suddenly cut off.
m_decimalFormatter->FractionDigits = static_cast<int>(stringToLocalize.length() - (posOfDecimal + 1));
m_currencyFormatter->FractionDigits = m_currencyMaxFractionDigits;
currencyFormatter->FractionDigits = lastCurrencyFractionDigits;
}

if (IsCurrencyCurrentCategory)
{
wstring currencyResult = m_currencyFormatter->Format(stod(stringToLocalize))->Data();
wstring currencyCode = m_currencyFormatter->Currency->Data();
wstring currencyResult = currencyFormatter->Format(stod(stringToLocalize))->Data();
xuhongxu96 marked this conversation as resolved.
Show resolved Hide resolved
wstring currencyCode = currencyFormatter->Currency->Data();

// CurrencyFormatter always includes LangCode or Symbol. Make it include LangCode
// because this includes a non-breaking space. Remove the LangCode.
Expand Down Expand Up @@ -381,6 +408,10 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st
}
result = L"-" + result;
}

// restore the original fraction digits
currencyFormatter->FractionDigits = lastCurrencyFractionDigits;

return result;
}

Expand All @@ -394,9 +425,9 @@ void UnitConverterViewModel::DisplayPasteError()

void UnitConverterViewModel::UpdateDisplay(const wstring& from, const wstring& to)
{
String ^ fromStr = this->ConvertToLocalizedString(from, true);
String ^ fromStr = this->ConvertToLocalizedString(from, true, CurrencyFormatterParameterFrom);
UpdateInputBlocked(from);
String ^ toStr = this->ConvertToLocalizedString(to, true);
String ^ toStr = this->ConvertToLocalizedString(to, true, CurrencyFormatterParameterTo);

bool updatedValueFrom = ValueFrom != fromStr;
bool updatedValueTo = ValueTo != toStr;
Expand Down Expand Up @@ -473,14 +504,14 @@ void UnitConverterViewModel::OnButtonPressed(Platform::Object ^ parameter)
}

static constexpr UCM::Command OPERANDS[] = { UCM::Command::Zero, UCM::Command::One, UCM::Command::Two, UCM::Command::Three, UCM::Command::Four,
UCM::Command::Five, UCM::Command::Six, UCM::Command::Seven, UCM::Command::Eight, UCM::Command::Nine };
if (m_isInputBlocked &&
command != UCM::Command::Clear &&
command != UCM::Command::Backspace)
{
return;
}
m_model->SendCommand(command);
UCM::Command::Five, UCM::Command::Six, UCM::Command::Seven, UCM::Command::Eight, UCM::Command::Nine };

// input should be allowed if user just switches active, because we will clear values in such cases
if (m_isInputBlocked && !m_model->IsSwitchedActive() && command != UCM::Command::Clear && command != UCM::Command::Backspace)
{
return;
}
m_model->SendCommand(command);
xuhongxu96 marked this conversation as resolved.
Show resolved Hide resolved

TraceLogger::GetInstance()->LogConverterInputReceived(Mode);
}
Expand Down Expand Up @@ -755,8 +786,8 @@ void UnitConverterViewModel::RefreshSupplementaryResults()

for (tuple<wstring, UCM::Unit> suggestedValue : m_cachedSuggestedValues)
{
SupplementaryResult ^ result =
ref new SupplementaryResult(this->ConvertToLocalizedString(get<0>(suggestedValue), false), ref new Unit(get<1>(suggestedValue)));
SupplementaryResult ^ result = ref new SupplementaryResult(
this->ConvertToLocalizedString(get<0>(suggestedValue), false, CurrencyFormatterParameter::Default), ref new Unit(get<1>(suggestedValue)));
if (result->IsWhimsical())
{
whimsicals.push_back(result);
Expand Down Expand Up @@ -803,10 +834,46 @@ void UnitConverterViewModel::UpdateInputBlocked(_In_ const wstring& currencyInpu
m_isInputBlocked = false;
if (posOfDecimal != wstring::npos && IsCurrencyCurrentCategory)
{
m_isInputBlocked = (posOfDecimal + static_cast<size_t>(m_currencyMaxFractionDigits) + 1 == currencyInput.length());
m_isInputBlocked = (posOfDecimal + static_cast<size_t>(CurrencyFormatterFrom->FractionDigits) + 1 == currencyInput.length());
}
}

std::wstring TruncateFractionDigits(const std::wstring& n, int digitCount)
{
auto i = n.find('.');
if (i == std::wstring::npos)
return n;
size_t actualDigitCount = n.size() - i - 1;
return n.substr(0, n.size() - (actualDigitCount - digitCount));
}

void UnitConverterViewModel::UpdateCurrencyFormatter()
joseartrivera marked this conversation as resolved.
Show resolved Hide resolved
{
if (!IsCurrencyCurrentCategory || m_Unit1->Abbreviation->IsEmpty() || m_Unit2->Abbreviation->IsEmpty())
return;

m_currencyFormatter1 = ref new CurrencyFormatter(m_Unit1->Abbreviation);
m_currencyFormatter1->IsGrouped = true;
m_currencyFormatter1->Mode = CurrencyFormatterMode::UseCurrencyCode;
m_currencyFormatter1->ApplyRoundingForCurrency(RoundingAlgorithm::RoundHalfDown);

m_currencyFormatter2 = ref new CurrencyFormatter(m_Unit2->Abbreviation);
m_currencyFormatter2->IsGrouped = true;
m_currencyFormatter2->Mode = CurrencyFormatterMode::UseCurrencyCode;
m_currencyFormatter2->ApplyRoundingForCurrency(RoundingAlgorithm::RoundHalfDown);

UpdateIsDecimalEnabled();

OnPaste(ref new String(TruncateFractionDigits(m_valueFromUnlocalized, CurrencyFormatterFrom->FractionDigits).data()));
}

void UnitConverterViewModel::UpdateIsDecimalEnabled()
{
if (!IsCurrencyCurrentCategory || CurrencyFormatterFrom == nullptr)
return;
IsDecimalEnabled = CurrencyFormatterFrom->FractionDigits > 0;
}

NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate)
{
static_assert(NumbersAndOperatorsEnum::Zero < NumbersAndOperatorsEnum::One, "NumbersAndOperatorsEnum order is invalid");
Expand Down Expand Up @@ -934,14 +1001,19 @@ void UnitConverterViewModel::OnPaste(String ^ stringToPaste)
}
}

String ^ UnitConverterViewModel::GetLocalizedAutomationName(_In_ String ^ displayvalue, _In_ String ^ unitname, _In_ String ^ format)
String
^ UnitConverterViewModel::GetLocalizedAutomationName(
_In_ String ^ displayvalue,
_In_ String ^ unitname,
_In_ String ^ format,
_In_ CurrencyFormatterParameter cfp)
{
String ^ valueToLocalize = displayvalue;
if (displayvalue == ValueFrom && Utils::IsLastCharacterTarget(m_valueFromUnlocalized, m_decimalSeparator))
{
// Need to compute a second localized value for the automation
// name that does not include the decimal separator.
displayvalue = ConvertToLocalizedString(m_valueFromUnlocalized, false /*allowTrailingDecimal*/);
displayvalue = ConvertToLocalizedString(m_valueFromUnlocalized, false /*allowTrailingDecimal*/, cfp);
format = m_localizedValueFromDecimalFormat;
}

Expand All @@ -962,15 +1034,15 @@ void UnitConverterViewModel::UpdateValue1AutomationName()
{
if (Unit1)
{
Value1AutomationName = GetLocalizedAutomationName(Value1, Unit1->AccessibleName, m_localizedValueFromFormat);
Value1AutomationName = GetLocalizedAutomationName(Value1, Unit1->AccessibleName, m_localizedValueFromFormat, CurrencyFormatterParameter::ForValue1);
}
}

void UnitConverterViewModel::UpdateValue2AutomationName()
{
if (Unit2)
{
Value2AutomationName = GetLocalizedAutomationName(Value2, Unit2->AccessibleName, m_localizedValueToFormat);
Value2AutomationName = GetLocalizedAutomationName(Value2, Unit2->AccessibleName, m_localizedValueToFormat, CurrencyFormatterParameter::ForValue1);
}
}

Expand Down
48 changes: 45 additions & 3 deletions src/CalcViewModel/UnitConverterViewModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,19 @@ namespace CalculatorApp
void OnCopyCommand(Platform::Object ^ parameter);
void OnPasteCommand(Platform::Object ^ parameter);

enum class CurrencyFormatterParameter
{
Default,
ForValue1,
ForValue2,
};

Platform::String
^ GetLocalizedAutomationName(_In_ Platform::String ^ displayvalue, _In_ Platform::String ^ unitname, _In_ Platform::String ^ format);
^ GetLocalizedAutomationName(
_In_ Platform::String ^ displayvalue,
_In_ Platform::String ^ unitname,
_In_ Platform::String ^ format,
_In_ CurrencyFormatterParameter cfp);
Platform::String
^ GetLocalizedConversionResultStringFormat(
_In_ Platform::String ^ fromValue,
Expand Down Expand Up @@ -276,11 +287,13 @@ namespace CalculatorApp
void SupplementaryResultsTimerCancel(Windows::System::Threading::ThreadPoolTimer ^ timer);
void RefreshSupplementaryResults();
void UpdateInputBlocked(_In_ const std::wstring& currencyInput);
void UpdateCurrencyFormatter();
void UpdateIsDecimalEnabled();
bool UnitsAreValid();
void ResetCategory();

void OnButtonPressed(Platform::Object ^ parameter);
Platform::String ^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings);
Platform::String ^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings, CurrencyFormatterParameter cfp);

std::shared_ptr<UnitConversionManager::IUnitConverter> m_model;
wchar_t m_decimalSeparator;
Expand All @@ -290,6 +303,34 @@ namespace CalculatorApp
Source,
Target
} m_value1cp;
property CurrencyFormatterParameter CurrencyFormatterParameterFrom
{
CurrencyFormatterParameter get()
{
return m_value1cp == ConversionParameter::Source ? CurrencyFormatterParameter::ForValue1 : CurrencyFormatterParameter::ForValue2;
}
}
property CurrencyFormatterParameter CurrencyFormatterParameterTo
{
CurrencyFormatterParameter get()
{
return m_value1cp == ConversionParameter::Target ? CurrencyFormatterParameter::ForValue1 : CurrencyFormatterParameter::ForValue2;
}
}
property Windows::Globalization::NumberFormatting::CurrencyFormatter^ CurrencyFormatterFrom
{
Windows::Globalization::NumberFormatting::CurrencyFormatter^ get()
{
return m_value1cp == ConversionParameter::Source ? m_currencyFormatter1 : m_currencyFormatter2;
}
}
property Windows::Globalization::NumberFormatting::CurrencyFormatter^ CurrencyFormatterTo
{
Windows::Globalization::NumberFormatting::CurrencyFormatter^ get()
{
return m_value1cp == ConversionParameter::Target ? m_currencyFormatter1 : m_currencyFormatter2;
}
}
property Platform::String^ ValueFrom
{
Platform::String^ get() { return m_value1cp == ConversionParameter::Source ? Value1 : Value2; }
Expand Down Expand Up @@ -323,7 +364,8 @@ namespace CalculatorApp
std::mutex m_cacheMutex;
Windows::Globalization::NumberFormatting::DecimalFormatter ^ m_decimalFormatter;
Windows::Globalization::NumberFormatting::CurrencyFormatter ^ m_currencyFormatter;
int m_currencyMaxFractionDigits;
Windows::Globalization::NumberFormatting::CurrencyFormatter ^ m_currencyFormatter1;
Windows::Globalization::NumberFormatting::CurrencyFormatter ^ m_currencyFormatter2;
std::wstring m_valueFromUnlocalized;
std::wstring m_valueToUnlocalized;
bool m_relocalizeStringOnSwitch;
Expand Down
Loading