mirror of
https://github.com/Microsoft/calculator.git
synced 2025-08-19 21:03:11 -07:00
Merged PR 10741448: [Recall] Snapshot saving and restoring
####What According to PM [Spec](https://microsoft.sharepoint-df.com/:w:/t/PAXEssentialExperiences421/EcpP5tGRtFdIsRrP84ueRfUBjb6tfayxWtF9ujvJuNx6Dg?e=AeRzVf), saving and restoring Calculator snapshot when required. The current snapshot supports to: - Restore the calculator mode. - Restore the current calculation (display value and expression). - Restore the history of calculations (either in Standard mode or Scientific mode) shown at the time the snapshot was taken. - Restore the current calculation error state, if applicable. ####How - Added `SnapshotHelper` to help save and restore snapshots. - Besides the existing snapshot information from view models, added an extra field `SnapshotVersion` in `ApplicationSnapshot` for backward compatibility. #### Note Unit tests will be added in a separate PR. Related work items: #50701758
This commit is contained in:
parent
04a1842061
commit
c4be28fbfc
8 changed files with 584 additions and 15 deletions
|
@ -477,16 +477,25 @@ namespace CalculationManager
|
|||
}
|
||||
}
|
||||
|
||||
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems()
|
||||
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems() const
|
||||
{
|
||||
return m_pHistory->GetHistory();
|
||||
}
|
||||
|
||||
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems(_In_ CalculatorMode mode)
|
||||
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems(_In_ CalculatorMode mode) const
|
||||
{
|
||||
return (mode == CalculatorMode::Standard) ? m_pStdHistory->GetHistory() : m_pSciHistory->GetHistory();
|
||||
}
|
||||
|
||||
void CalculatorManager::SetHistoryItems(_In_ std::vector<std::shared_ptr<HISTORYITEM>> const& historyItems)
|
||||
{
|
||||
for (auto const& historyItem : historyItems)
|
||||
{
|
||||
auto index = m_pHistory->AddItem(historyItem);
|
||||
OnHistoryItemAdded(index);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<HISTORYITEM> const& CalculatorManager::GetHistoryItem(_In_ unsigned int uIdx)
|
||||
{
|
||||
return m_pHistory->GetHistoryItem(uIdx);
|
||||
|
|
|
@ -107,8 +107,9 @@ namespace CalculationManager
|
|||
void UpdateMaxIntDigits();
|
||||
wchar_t DecimalSeparator();
|
||||
|
||||
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems();
|
||||
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems(_In_ CalculatorMode mode);
|
||||
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems() const;
|
||||
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems(_In_ CalculatorMode mode) const;
|
||||
void SetHistoryItems(_In_ std::vector<std::shared_ptr<HISTORYITEM>> const& historyItems);
|
||||
std::shared_ptr<HISTORYITEM> const& GetHistoryItem(_In_ unsigned int uIdx);
|
||||
bool RemoveHistoryItem(_In_ unsigned int uIdx);
|
||||
void ClearHistory();
|
||||
|
|
|
@ -18,7 +18,6 @@ using namespace CalculatorApp::ViewModel;
|
|||
using namespace CalculationManager;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::System;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Utils;
|
||||
|
@ -38,6 +37,439 @@ namespace
|
|||
{
|
||||
StringReference CategoriesPropertyName(L"Categories");
|
||||
StringReference ClearMemoryVisibilityPropertyName(L"ClearMemoryVisibility");
|
||||
|
||||
struct SnapshotHelper
|
||||
{
|
||||
static constexpr int SnapshotVersion = 0;
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const ApplicationSnapshot& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
jsonObject->SetNamedValue(L"SnapshotVersion", Windows::Data::Json::JsonValue::CreateNumberValue(value.SnapshotVersion));
|
||||
jsonObject->SetNamedValue(L"Mode", Windows::Data::Json::JsonValue::CreateNumberValue(value.Mode));
|
||||
if (value.StandardCalc.has_value())
|
||||
{
|
||||
jsonObject->SetNamedValue(L"StandardCalculatorSnapshot", SaveSnapshotToJson(*value.StandardCalc));
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const StandardCalculatorSnapshot& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
jsonObject->SetNamedValue(L"CalculatorManagerSnapshot", SaveSnapshotToJson(value.CalcManager));
|
||||
jsonObject->SetNamedValue(L"PrimaryDisplay", SaveSnapshotToJson(value.PrimaryDisplay));
|
||||
if (value.ExpressionDisplay.has_value())
|
||||
{
|
||||
jsonObject->SetNamedValue(L"ExpressionDisplay", SaveSnapshotToJson(*value.ExpressionDisplay));
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const PrimaryDisplaySnapshot& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
jsonObject->SetNamedValue(L"DisplayValue", Windows::Data::Json::JsonValue::CreateStringValue(value.DisplayValue));
|
||||
jsonObject->SetNamedValue(L"IsError", Windows::Data::Json::JsonValue::CreateBooleanValue(value.IsError));
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const ExpressionDisplaySnapshot& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
auto tokensJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
for (const auto& token : value.Tokens)
|
||||
{
|
||||
auto tokenJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
tokenJsonArray->Append(Windows::Data::Json::JsonValue::CreateStringValue(ref new Platform::String(token.first.c_str())));
|
||||
tokenJsonArray->Append(Windows::Data::Json::JsonValue::CreateNumberValue(token.second));
|
||||
tokensJsonArray->Append(tokenJsonArray);
|
||||
}
|
||||
jsonObject->SetNamedValue(L"Tokens", tokensJsonArray);
|
||||
|
||||
auto commandsJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
for (const auto& command : value.Commands)
|
||||
{
|
||||
commandsJsonArray->Append(SaveSnapshotToJson(command));
|
||||
}
|
||||
jsonObject->SetNamedValue(L"Commands", commandsJsonArray);
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CalculatorManagerSnapshot& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
if (value.HistoryItems.has_value())
|
||||
{
|
||||
auto historyJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
for (const auto& item : *value.HistoryItems)
|
||||
{
|
||||
historyJsonArray->Append(SaveSnapshotToJson(*item));
|
||||
}
|
||||
jsonObject->SetNamedValue(L"HistoryItems", historyJsonArray);
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CalculationManager::HISTORYITEM& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
jsonObject->SetNamedValue(L"Expression", Windows::Data::Json::JsonValue::CreateStringValue(ref new Platform::String(value.historyItemVector.expression.c_str())));
|
||||
jsonObject->SetNamedValue(L"Result", Windows::Data::Json::JsonValue::CreateStringValue(ref new Platform::String(value.historyItemVector.result.c_str())));
|
||||
|
||||
auto tokensJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
for (const auto& token : *value.historyItemVector.spTokens)
|
||||
{
|
||||
auto tokenJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
tokenJsonArray->Append(Windows::Data::Json::JsonValue::CreateStringValue(ref new Platform::String(token.first.c_str())));
|
||||
tokenJsonArray->Append(Windows::Data::Json::JsonValue::CreateNumberValue(token.second));
|
||||
tokensJsonArray->Append(tokenJsonArray);
|
||||
}
|
||||
jsonObject->SetNamedValue(L"Tokens", tokensJsonArray);
|
||||
|
||||
auto commandsJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
for (const auto& command : *value.historyItemVector.spCommands)
|
||||
{
|
||||
commandsJsonArray->Append(SaveSnapshotToJson(command));
|
||||
}
|
||||
jsonObject->SetNamedValue(L"Commands", commandsJsonArray);
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const std::shared_ptr<IExpressionCommand>& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
auto opndCommand = dynamic_cast<COpndCommand*>(value.get());
|
||||
if (opndCommand != nullptr)
|
||||
{
|
||||
jsonObject = SaveSnapshotToJson(*opndCommand);
|
||||
}
|
||||
auto unaryCommand = dynamic_cast<CUnaryCommand*>(value.get());
|
||||
if (unaryCommand != nullptr)
|
||||
{
|
||||
jsonObject = SaveSnapshotToJson(*unaryCommand);
|
||||
}
|
||||
auto binaryCommand = dynamic_cast<CBinaryCommand*>(value.get());
|
||||
if (binaryCommand != nullptr)
|
||||
{
|
||||
jsonObject = SaveSnapshotToJson(*binaryCommand);
|
||||
}
|
||||
auto parenthesesCommand = dynamic_cast<CParentheses*>(value.get());
|
||||
if (parenthesesCommand != nullptr)
|
||||
{
|
||||
jsonObject = SaveSnapshotToJson(*parenthesesCommand);
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const COpndCommand& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
jsonObject->SetNamedValue(L"CommandType", Windows::Data::Json::JsonValue::CreateNumberValue(static_cast<double>(value.GetCommandType())));
|
||||
jsonObject->SetNamedValue(L"IsNegative", Windows::Data::Json::JsonValue::CreateBooleanValue(value.IsNegative()));
|
||||
jsonObject->SetNamedValue(L"IsDecimalPresent", Windows::Data::Json::JsonValue::CreateBooleanValue(value.IsDecimalPresent()));
|
||||
jsonObject->SetNamedValue(L"IsSciFmt", Windows::Data::Json::JsonValue::CreateBooleanValue(value.IsSciFmt()));
|
||||
auto commandsJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
for (const auto& command : *value.GetCommands())
|
||||
{
|
||||
commandsJsonArray->Append(Windows::Data::Json::JsonValue::CreateNumberValue(command));
|
||||
}
|
||||
jsonObject->SetNamedValue(L"Commands", commandsJsonArray);
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CUnaryCommand& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
jsonObject->SetNamedValue(L"CommandType", Windows::Data::Json::JsonValue::CreateNumberValue(static_cast<double>(value.GetCommandType())));
|
||||
auto commandsJsonArray = ref new Windows::Data::Json::JsonArray();
|
||||
for (const auto& command : *value.GetCommands())
|
||||
{
|
||||
commandsJsonArray->Append(Windows::Data::Json::JsonValue::CreateNumberValue(command));
|
||||
}
|
||||
jsonObject->SetNamedValue(L"Commands", commandsJsonArray);
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CBinaryCommand& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
jsonObject->SetNamedValue(L"CommandType", Windows::Data::Json::JsonValue::CreateNumberValue(static_cast<double>(value.GetCommandType())));
|
||||
jsonObject->SetNamedValue(L"Command", Windows::Data::Json::JsonValue::CreateNumberValue(value.GetCommand()));
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CParentheses& value)
|
||||
{
|
||||
auto jsonObject = ref new Windows::Data::Json::JsonObject();
|
||||
jsonObject->SetNamedValue(L"CommandType", Windows::Data::Json::JsonValue::CreateNumberValue(static_cast<double>(value.GetCommandType())));
|
||||
jsonObject->SetNamedValue(L"Command", Windows::Data::Json::JsonValue::CreateNumberValue(value.GetCommand()));
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<ApplicationSnapshot>& value)
|
||||
{
|
||||
ApplicationSnapshot applicationSnapshot;
|
||||
applicationSnapshot.SnapshotVersion = static_cast<int>(jsonObject->GetNamedNumber(L"SnapshotVersion"));
|
||||
if (applicationSnapshot.SnapshotVersion > SnapshotVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
applicationSnapshot.Mode = static_cast<int>(jsonObject->GetNamedNumber(L"Mode"));
|
||||
if (jsonObject->HasKey(L"StandardCalculatorSnapshot"))
|
||||
{
|
||||
std::optional<StandardCalculatorSnapshot> standardCalculatorSnapshot;
|
||||
RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"StandardCalculatorSnapshot"), standardCalculatorSnapshot);
|
||||
if (standardCalculatorSnapshot.has_value())
|
||||
{
|
||||
applicationSnapshot.StandardCalc = std::move(*standardCalculatorSnapshot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
value = std::move(applicationSnapshot);
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<StandardCalculatorSnapshot>& value)
|
||||
{
|
||||
StandardCalculatorSnapshot standardCalculatorSnapshot;
|
||||
std::optional<CalculatorManagerSnapshot> calcManagerSnapshot;
|
||||
RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"CalculatorManagerSnapshot"), calcManagerSnapshot);
|
||||
if (calcManagerSnapshot.has_value())
|
||||
{
|
||||
standardCalculatorSnapshot.CalcManager = std::move(*calcManagerSnapshot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::optional<PrimaryDisplaySnapshot> primaryDisplaySnapshot;
|
||||
RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"PrimaryDisplay"), primaryDisplaySnapshot);
|
||||
if (primaryDisplaySnapshot.has_value())
|
||||
{
|
||||
standardCalculatorSnapshot.PrimaryDisplay = std::move(*primaryDisplaySnapshot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (jsonObject->HasKey(L"ExpressionDisplay"))
|
||||
{
|
||||
std::optional<ExpressionDisplaySnapshot> expressionDisplaySnapshot;
|
||||
RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"ExpressionDisplay"), expressionDisplaySnapshot);
|
||||
if (expressionDisplaySnapshot.has_value())
|
||||
{
|
||||
standardCalculatorSnapshot.ExpressionDisplay = std::move(*expressionDisplaySnapshot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
value = std::move(standardCalculatorSnapshot);
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<PrimaryDisplaySnapshot>& value)
|
||||
{
|
||||
value = PrimaryDisplaySnapshot{ jsonObject->GetNamedString(L"DisplayValue"), jsonObject->GetNamedBoolean(L"IsError") };
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<ExpressionDisplaySnapshot>& value)
|
||||
{
|
||||
ExpressionDisplaySnapshot expressionDisplaySnapshot;
|
||||
expressionDisplaySnapshot.Tokens = RestoreExpressionTokensFromJsonArray(jsonObject->GetNamedArray(L"Tokens"));
|
||||
if (expressionDisplaySnapshot.Tokens.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
expressionDisplaySnapshot.Commands = RestoreExpressionCommandsFromJsonArray(jsonObject->GetNamedArray(L"Commands"));
|
||||
if (expressionDisplaySnapshot.Commands.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
value = std::move(expressionDisplaySnapshot);
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<CalculatorManagerSnapshot>& value)
|
||||
{
|
||||
CalculatorManagerSnapshot calcManagerSnapshot;
|
||||
if (jsonObject->HasKey(L"HistoryItems"))
|
||||
{
|
||||
std::vector<std::shared_ptr<CalculationManager::HISTORYITEM>> historyItems;
|
||||
auto historyJsonArray = jsonObject->GetNamedArray(L"HistoryItems");
|
||||
for (uint32_t i = 0; i < historyJsonArray->Size; ++i)
|
||||
{
|
||||
std::optional<CalculationManager::HISTORYITEM> historyItem;
|
||||
RestoreJsonToSnapshot(historyJsonArray->GetObjectAt(i), historyItem);
|
||||
if (historyItem.has_value())
|
||||
{
|
||||
historyItems.push_back(std::make_shared<CalculationManager::HISTORYITEM>(*historyItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
calcManagerSnapshot.HistoryItems = std::move(historyItems);
|
||||
}
|
||||
value = std::move(calcManagerSnapshot);
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<CalculationManager::HISTORYITEM>& value)
|
||||
{
|
||||
CalculationManager::HISTORYITEM historyItem;
|
||||
historyItem.historyItemVector.expression = std::wstring(jsonObject->GetNamedString(L"Expression")->Data());
|
||||
historyItem.historyItemVector.result = std::wstring(jsonObject->GetNamedString(L"Result")->Data());
|
||||
historyItem.historyItemVector.spTokens =
|
||||
std::make_shared<std::vector<std::pair<std::wstring, int>>>(RestoreExpressionTokensFromJsonArray(jsonObject->GetNamedArray(L"Tokens")));
|
||||
if (historyItem.historyItemVector.spTokens->empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
historyItem.historyItemVector.spCommands = std::make_shared<std::vector<std::shared_ptr<IExpressionCommand>>>(
|
||||
RestoreExpressionCommandsFromJsonArray(jsonObject->GetNamedArray(L"Commands")));
|
||||
if (historyItem.historyItemVector.spCommands->empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
value = std::move(historyItem);
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<std::shared_ptr<IExpressionCommand>>& value)
|
||||
{
|
||||
auto commandType = static_cast<CalculationManager::CommandType>(jsonObject->GetNamedNumber(L"CommandType"));
|
||||
switch (commandType)
|
||||
{
|
||||
case CalculationManager::CommandType::OperandCommand:
|
||||
{
|
||||
std::optional<COpndCommand> opndCommand;
|
||||
RestoreJsonToSnapshot(jsonObject, opndCommand);
|
||||
if (opndCommand.has_value())
|
||||
{
|
||||
value = std::make_shared<COpndCommand>(*opndCommand);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CalculationManager::CommandType::UnaryCommand:
|
||||
{
|
||||
std::optional<CUnaryCommand> unaryCommand;
|
||||
RestoreJsonToSnapshot(jsonObject, unaryCommand);
|
||||
if (unaryCommand.has_value())
|
||||
{
|
||||
value = std::make_shared<CUnaryCommand>(*unaryCommand);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CalculationManager::CommandType::BinaryCommand:
|
||||
{
|
||||
std::optional<CBinaryCommand> binaryCommand;
|
||||
RestoreJsonToSnapshot(jsonObject, binaryCommand);
|
||||
if (binaryCommand.has_value())
|
||||
{
|
||||
value = std::make_shared<CBinaryCommand>(*binaryCommand);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CalculationManager::CommandType::Parentheses:
|
||||
{
|
||||
std::optional<CParentheses> parenthesesCommand;
|
||||
RestoreJsonToSnapshot(jsonObject, parenthesesCommand);
|
||||
if (parenthesesCommand.has_value())
|
||||
{
|
||||
value = std::make_shared<CParentheses>(*parenthesesCommand);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error{ "c8cba597-dfec-447a-bd1c-e78a9ffaad95" };
|
||||
}
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<COpndCommand>& value)
|
||||
{
|
||||
auto isNegative = jsonObject->GetNamedBoolean(L"IsNegative");
|
||||
auto isDecimalPresent = jsonObject->GetNamedBoolean(L"IsDecimalPresent");
|
||||
auto isSciFmt = jsonObject->GetNamedBoolean(L"IsSciFmt");
|
||||
std::vector<int> commands;
|
||||
auto commandsJsonArray = jsonObject->GetNamedArray(L"Commands");
|
||||
for (uint32_t i = 0; i < commandsJsonArray->Size; ++i)
|
||||
{
|
||||
commands.push_back(static_cast<int>(commandsJsonArray->GetNumberAt(i)));
|
||||
}
|
||||
value = COpndCommand(std::make_shared<std::vector<int>>(std::move(commands)), isNegative, isDecimalPresent, isSciFmt);
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<CUnaryCommand>& value)
|
||||
{
|
||||
std::vector<int> commands;
|
||||
auto commandsJsonArray = jsonObject->GetNamedArray(L"Commands");
|
||||
if (commandsJsonArray->Size == 1)
|
||||
{
|
||||
value = CUnaryCommand(static_cast<int>(commandsJsonArray->GetNumberAt(0)));
|
||||
}
|
||||
else if (commandsJsonArray->Size == 2)
|
||||
{
|
||||
value = CUnaryCommand(static_cast<int>(commandsJsonArray->GetNumberAt(0)), static_cast<int>(commandsJsonArray->GetNumberAt(1)));
|
||||
}
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<CBinaryCommand>& value)
|
||||
{
|
||||
value = CBinaryCommand(static_cast<int>(jsonObject->GetNamedNumber(L"Command")));
|
||||
}
|
||||
|
||||
static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional<CParentheses>& value)
|
||||
{
|
||||
value = CParentheses(static_cast<int>(jsonObject->GetNamedNumber(L"Command")));
|
||||
}
|
||||
|
||||
static std::vector<std::pair<std::wstring, int>> RestoreExpressionTokensFromJsonArray(Windows::Data::Json::JsonArray ^ jsonArray)
|
||||
{
|
||||
std::vector<std::pair<std::wstring, int>> tokens;
|
||||
for (uint32_t i = 0; i < jsonArray->Size; ++i)
|
||||
{
|
||||
auto tokenJsonArray = jsonArray->GetArrayAt(i);
|
||||
if (tokenJsonArray->Size == 2 && tokenJsonArray->GetAt(0)->ValueType == Windows::Data::Json::JsonValueType::String
|
||||
&& tokenJsonArray->GetAt(1)->ValueType == Windows::Data::Json::JsonValueType::Number)
|
||||
{
|
||||
tokens.emplace_back(std::wstring(tokenJsonArray->GetAt(0)->GetString()->Data()), static_cast<int>(tokenJsonArray->GetAt(1)->GetNumber()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
static std::vector<std::shared_ptr<IExpressionCommand>> RestoreExpressionCommandsFromJsonArray(Windows::Data::Json::JsonArray ^ jsonArray)
|
||||
{
|
||||
std::vector<std::shared_ptr<IExpressionCommand>> commands;
|
||||
for (uint32_t i = 0; i < jsonArray->Size; ++i)
|
||||
{
|
||||
std::optional<std::shared_ptr<IExpressionCommand>> command;
|
||||
RestoreJsonToSnapshot(jsonArray->GetObjectAt(i), command);
|
||||
if (command.has_value())
|
||||
{
|
||||
commands.push_back(*command);
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
static bool IsJsonParsingException(Platform::COMException ^ e)
|
||||
{
|
||||
return e->HResult == WEB_E_JSON_VALUE_NOT_FOUND || e->HResult == E_ILLEGAL_METHOD_CALL;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ApplicationViewModel::ApplicationViewModel()
|
||||
|
@ -151,9 +583,9 @@ void ApplicationViewModel::OnModeChanged()
|
|||
{
|
||||
if (!m_ConverterViewModel)
|
||||
{
|
||||
auto dataLoader = make_shared<UnitConverterDataLoader>(ref new GeographicRegion());
|
||||
auto currencyDataLoader = make_shared<CurrencyDataLoader>(make_unique<CurrencyHttpClient>());
|
||||
m_ConverterViewModel = ref new UnitConverterViewModel(make_shared<UnitConversionManager::UnitConverter>(dataLoader, currencyDataLoader));
|
||||
auto dataLoader = std::make_shared<UnitConverterDataLoader>(ref new GeographicRegion());
|
||||
auto currencyDataLoader = std::make_shared<CurrencyDataLoader>(std::make_unique<CurrencyHttpClient>());
|
||||
m_ConverterViewModel = ref new UnitConverterViewModel(std::make_shared<UnitConversionManager::UnitConverter>(dataLoader, currencyDataLoader));
|
||||
}
|
||||
|
||||
m_ConverterViewModel->Mode = m_mode;
|
||||
|
@ -272,3 +704,47 @@ void ApplicationViewModel::SetDisplayNormalAlwaysOnTopOption()
|
|||
DisplayNormalAlwaysOnTopOption =
|
||||
m_mode == ViewMode::Standard && ApplicationView::GetForCurrentView()->IsViewModeSupported(ApplicationViewMode::CompactOverlay) && !IsAlwaysOnTop;
|
||||
}
|
||||
|
||||
Windows::Data::Json::JsonObject ^ ApplicationViewModel::SaveApplicationSnapshot()
|
||||
{
|
||||
ApplicationSnapshot applicationSnapshot;
|
||||
applicationSnapshot.SnapshotVersion = SnapshotHelper::SnapshotVersion;
|
||||
applicationSnapshot.Mode = static_cast<int>(Mode);
|
||||
if (NavCategory::IsCalculatorViewMode(m_mode) && m_CalculatorViewModel != nullptr)
|
||||
{
|
||||
applicationSnapshot.StandardCalc = m_CalculatorViewModel->GetStandardCalculatorSnapshot();
|
||||
}
|
||||
return SnapshotHelper::SaveSnapshotToJson(applicationSnapshot);
|
||||
}
|
||||
|
||||
bool ApplicationViewModel::TryRestoreFromSnapshot(Windows::Data::Json::JsonObject ^ jsonObject)
|
||||
{
|
||||
std::optional<ApplicationSnapshot> applicationSnapshot;
|
||||
try
|
||||
{
|
||||
SnapshotHelper::RestoreJsonToSnapshot(jsonObject, applicationSnapshot);
|
||||
}
|
||||
catch (Platform::COMException ^ e)
|
||||
{
|
||||
if (SnapshotHelper::IsJsonParsingException(e))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
if (applicationSnapshot.has_value())
|
||||
{
|
||||
Mode = static_cast<ViewMode>(applicationSnapshot->Mode);
|
||||
if (applicationSnapshot->StandardCalc.has_value())
|
||||
{
|
||||
if (m_CalculatorViewModel == nullptr)
|
||||
{
|
||||
m_CalculatorViewModel = ref new StandardCalculatorViewModel();
|
||||
}
|
||||
m_CalculatorViewModel->SetStandardCalculatorSnapshot(applicationSnapshot->StandardCalc.value());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,13 @@ namespace CalculatorApp
|
|||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
struct ApplicationSnapshot
|
||||
{
|
||||
int SnapshotVersion;
|
||||
int Mode;
|
||||
std::optional<StandardCalculatorSnapshot> StandardCalc;
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable] public ref class ApplicationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
|
@ -96,6 +103,9 @@ namespace CalculatorApp
|
|||
|
||||
void ToggleAlwaysOnTop(float width, float height);
|
||||
|
||||
Windows::Data::Json::JsonObject ^ SaveApplicationSnapshot();
|
||||
bool TryRestoreFromSnapshot(Windows::Data::Json::JsonObject ^ jsonObject);
|
||||
|
||||
private:
|
||||
bool TryRecoverFromNavigationModeFailure();
|
||||
|
||||
|
|
|
@ -1782,3 +1782,34 @@ void StandardCalculatorViewModel::SetBitshiftRadioButtonCheckedAnnouncement(Plat
|
|||
{
|
||||
Announcement = CalculatorAnnouncement::GetBitShiftRadioButtonCheckedAnnouncement(announcement);
|
||||
}
|
||||
|
||||
StandardCalculatorSnapshot StandardCalculatorViewModel::GetStandardCalculatorSnapshot() const
|
||||
{
|
||||
StandardCalculatorSnapshot snapshot;
|
||||
auto historyItems = m_standardCalculatorManager.GetHistoryItems();
|
||||
if (!historyItems.empty())
|
||||
{
|
||||
snapshot.CalcManager.HistoryItems = std::move(historyItems);
|
||||
}
|
||||
snapshot.PrimaryDisplay = PrimaryDisplaySnapshot{ m_DisplayValue, m_IsInError };
|
||||
if (!m_tokens->empty() && !m_commands->empty())
|
||||
{
|
||||
snapshot.ExpressionDisplay = { *m_tokens, *m_commands };
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void StandardCalculatorViewModel::SetStandardCalculatorSnapshot(const StandardCalculatorSnapshot& snapshot)
|
||||
{
|
||||
if (snapshot.CalcManager.HistoryItems.has_value())
|
||||
{
|
||||
m_standardCalculatorManager.SetHistoryItems(snapshot.CalcManager.HistoryItems.value());
|
||||
}
|
||||
SetPrimaryDisplay(snapshot.PrimaryDisplay.DisplayValue, snapshot.PrimaryDisplay.IsError);
|
||||
if (snapshot.ExpressionDisplay.has_value())
|
||||
{
|
||||
SetExpressionDisplay(
|
||||
std::make_shared<std::vector<std::pair<std::wstring, int>>>(snapshot.ExpressionDisplay->Tokens),
|
||||
std::make_shared<std::vector<std::shared_ptr<IExpressionCommand>>>(snapshot.ExpressionDisplay->Commands));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,30 @@ namespace CalculatorApp
|
|||
bool canSendNegate;
|
||||
};
|
||||
|
||||
struct CalculatorManagerSnapshot
|
||||
{
|
||||
std::optional<std::vector<std::shared_ptr<CalculationManager::HISTORYITEM>>> HistoryItems;
|
||||
};
|
||||
|
||||
struct PrimaryDisplaySnapshot
|
||||
{
|
||||
Platform::String ^ DisplayValue;
|
||||
bool IsError = false;
|
||||
};
|
||||
|
||||
struct ExpressionDisplaySnapshot
|
||||
{
|
||||
std::vector<std::pair<std::wstring, int>> Tokens;
|
||||
std::vector<std::shared_ptr<IExpressionCommand>> Commands;
|
||||
};
|
||||
|
||||
struct StandardCalculatorSnapshot
|
||||
{
|
||||
CalculatorManagerSnapshot CalcManager;
|
||||
PrimaryDisplaySnapshot PrimaryDisplay;
|
||||
std::optional<ExpressionDisplaySnapshot> ExpressionDisplay;
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable] public ref class StandardCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
|
@ -294,6 +318,9 @@ namespace CalculatorApp
|
|||
{
|
||||
return m_CurrentAngleType;
|
||||
}
|
||||
|
||||
StandardCalculatorSnapshot GetStandardCalculatorSnapshot() const;
|
||||
void SetStandardCalculatorSnapshot(const StandardCalculatorSnapshot& state);
|
||||
|
||||
private:
|
||||
void SetMemorizedNumbers(const std::vector<std::wstring>& memorizedNumbers);
|
||||
|
|
|
@ -45,8 +45,7 @@ namespace CalculatorApp
|
|||
if (string.IsNullOrEmpty(activity.ActivityId) ||
|
||||
activity.ActivationUri == null ||
|
||||
activity.ActivationUri.AbsolutePath != "/snapshot" ||
|
||||
string.IsNullOrEmpty(activity.ActivationUri.Query) ||
|
||||
activity.ContentInfo == null)
|
||||
string.IsNullOrEmpty(activity.ActivationUri.Query))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -67,9 +67,7 @@ namespace CalculatorApp
|
|||
var channel = UserActivityChannel.GetDefault();
|
||||
var activity = await channel.GetOrCreateUserActivityAsync($"{Guid.NewGuid()}");
|
||||
activity.ActivationUri = new Uri($"ms-calculator:///snapshot?activityId={activity.ActivityId}");
|
||||
|
||||
var snapshot = "{}"; // TODO: serialize the current snapshot into a JSON representation string.
|
||||
activity.ContentInfo = UserActivityContentInfo.FromJson(snapshot);
|
||||
activity.ContentInfo = UserActivityContentInfo.FromJson(Model.SaveApplicationSnapshot().Stringify());
|
||||
|
||||
var resProvider = AppResourceProvider.GetInstance();
|
||||
activity.VisualElements.DisplayText =
|
||||
|
@ -176,9 +174,27 @@ namespace CalculatorApp
|
|||
}
|
||||
else
|
||||
{
|
||||
if (JsonObject.TryParse(activity.ContentInfo.ToJson(), out var jsonModel))
|
||||
if (JsonObject.TryParse(activity.ToJson(), out var activityJson))
|
||||
{
|
||||
// TODO: try restore the model from jsonModel
|
||||
try
|
||||
{
|
||||
// Work around for bug https://microsoft.visualstudio.com/DefaultCollection/OS/_workitems/edit/48931227 where ContentInfo can't be directly accessed.
|
||||
var contentJson = activityJson.GetNamedObject("contentInfo");
|
||||
if (Model.TryRestoreFromSnapshot(contentJson))
|
||||
{
|
||||
SelectNavigationItemByModel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: show error dialog
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// TODO: show error dialog
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue