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();
|
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();
|
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)
|
shared_ptr<HISTORYITEM> const& CalculatorManager::GetHistoryItem(_In_ unsigned int uIdx)
|
||||||
{
|
{
|
||||||
return m_pHistory->GetHistoryItem(uIdx);
|
return m_pHistory->GetHistoryItem(uIdx);
|
||||||
|
|
|
@ -107,8 +107,9 @@ namespace CalculationManager
|
||||||
void UpdateMaxIntDigits();
|
void UpdateMaxIntDigits();
|
||||||
wchar_t DecimalSeparator();
|
wchar_t DecimalSeparator();
|
||||||
|
|
||||||
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems();
|
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems() const;
|
||||||
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems(_In_ CalculatorMode mode);
|
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);
|
std::shared_ptr<HISTORYITEM> const& GetHistoryItem(_In_ unsigned int uIdx);
|
||||||
bool RemoveHistoryItem(_In_ unsigned int uIdx);
|
bool RemoveHistoryItem(_In_ unsigned int uIdx);
|
||||||
void ClearHistory();
|
void ClearHistory();
|
||||||
|
|
|
@ -18,7 +18,6 @@ using namespace CalculatorApp::ViewModel;
|
||||||
using namespace CalculationManager;
|
using namespace CalculationManager;
|
||||||
using namespace Platform;
|
using namespace Platform;
|
||||||
using namespace Platform::Collections;
|
using namespace Platform::Collections;
|
||||||
using namespace std;
|
|
||||||
using namespace Windows::System;
|
using namespace Windows::System;
|
||||||
using namespace Windows::Storage;
|
using namespace Windows::Storage;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
@ -38,6 +37,439 @@ namespace
|
||||||
{
|
{
|
||||||
StringReference CategoriesPropertyName(L"Categories");
|
StringReference CategoriesPropertyName(L"Categories");
|
||||||
StringReference ClearMemoryVisibilityPropertyName(L"ClearMemoryVisibility");
|
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()
|
ApplicationViewModel::ApplicationViewModel()
|
||||||
|
@ -151,9 +583,9 @@ void ApplicationViewModel::OnModeChanged()
|
||||||
{
|
{
|
||||||
if (!m_ConverterViewModel)
|
if (!m_ConverterViewModel)
|
||||||
{
|
{
|
||||||
auto dataLoader = make_shared<UnitConverterDataLoader>(ref new GeographicRegion());
|
auto dataLoader = std::make_shared<UnitConverterDataLoader>(ref new GeographicRegion());
|
||||||
auto currencyDataLoader = make_shared<CurrencyDataLoader>(make_unique<CurrencyHttpClient>());
|
auto currencyDataLoader = std::make_shared<CurrencyDataLoader>(std::make_unique<CurrencyHttpClient>());
|
||||||
m_ConverterViewModel = ref new UnitConverterViewModel(make_shared<UnitConversionManager::UnitConverter>(dataLoader, currencyDataLoader));
|
m_ConverterViewModel = ref new UnitConverterViewModel(std::make_shared<UnitConversionManager::UnitConverter>(dataLoader, currencyDataLoader));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ConverterViewModel->Mode = m_mode;
|
m_ConverterViewModel->Mode = m_mode;
|
||||||
|
@ -272,3 +704,47 @@ void ApplicationViewModel::SetDisplayNormalAlwaysOnTopOption()
|
||||||
DisplayNormalAlwaysOnTopOption =
|
DisplayNormalAlwaysOnTopOption =
|
||||||
m_mode == ViewMode::Standard && ApplicationView::GetForCurrentView()->IsViewModeSupported(ApplicationViewMode::CompactOverlay) && !IsAlwaysOnTop;
|
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
|
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
|
[Windows::UI::Xaml::Data::Bindable] public ref class ApplicationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -96,6 +103,9 @@ namespace CalculatorApp
|
||||||
|
|
||||||
void ToggleAlwaysOnTop(float width, float height);
|
void ToggleAlwaysOnTop(float width, float height);
|
||||||
|
|
||||||
|
Windows::Data::Json::JsonObject ^ SaveApplicationSnapshot();
|
||||||
|
bool TryRestoreFromSnapshot(Windows::Data::Json::JsonObject ^ jsonObject);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool TryRecoverFromNavigationModeFailure();
|
bool TryRecoverFromNavigationModeFailure();
|
||||||
|
|
||||||
|
|
|
@ -1782,3 +1782,34 @@ void StandardCalculatorViewModel::SetBitshiftRadioButtonCheckedAnnouncement(Plat
|
||||||
{
|
{
|
||||||
Announcement = CalculatorAnnouncement::GetBitShiftRadioButtonCheckedAnnouncement(announcement);
|
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;
|
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
|
[Windows::UI::Xaml::Data::Bindable] public ref class StandardCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -294,6 +318,9 @@ namespace CalculatorApp
|
||||||
{
|
{
|
||||||
return m_CurrentAngleType;
|
return m_CurrentAngleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StandardCalculatorSnapshot GetStandardCalculatorSnapshot() const;
|
||||||
|
void SetStandardCalculatorSnapshot(const StandardCalculatorSnapshot& state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetMemorizedNumbers(const std::vector<std::wstring>& memorizedNumbers);
|
void SetMemorizedNumbers(const std::vector<std::wstring>& memorizedNumbers);
|
||||||
|
|
|
@ -45,8 +45,7 @@ namespace CalculatorApp
|
||||||
if (string.IsNullOrEmpty(activity.ActivityId) ||
|
if (string.IsNullOrEmpty(activity.ActivityId) ||
|
||||||
activity.ActivationUri == null ||
|
activity.ActivationUri == null ||
|
||||||
activity.ActivationUri.AbsolutePath != "/snapshot" ||
|
activity.ActivationUri.AbsolutePath != "/snapshot" ||
|
||||||
string.IsNullOrEmpty(activity.ActivationUri.Query) ||
|
string.IsNullOrEmpty(activity.ActivationUri.Query))
|
||||||
activity.ContentInfo == null)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,9 +67,7 @@ namespace CalculatorApp
|
||||||
var channel = UserActivityChannel.GetDefault();
|
var channel = UserActivityChannel.GetDefault();
|
||||||
var activity = await channel.GetOrCreateUserActivityAsync($"{Guid.NewGuid()}");
|
var activity = await channel.GetOrCreateUserActivityAsync($"{Guid.NewGuid()}");
|
||||||
activity.ActivationUri = new Uri($"ms-calculator:///snapshot?activityId={activity.ActivityId}");
|
activity.ActivationUri = new Uri($"ms-calculator:///snapshot?activityId={activity.ActivityId}");
|
||||||
|
activity.ContentInfo = UserActivityContentInfo.FromJson(Model.SaveApplicationSnapshot().Stringify());
|
||||||
var snapshot = "{}"; // TODO: serialize the current snapshot into a JSON representation string.
|
|
||||||
activity.ContentInfo = UserActivityContentInfo.FromJson(snapshot);
|
|
||||||
|
|
||||||
var resProvider = AppResourceProvider.GetInstance();
|
var resProvider = AppResourceProvider.GetInstance();
|
||||||
activity.VisualElements.DisplayText =
|
activity.VisualElements.DisplayText =
|
||||||
|
@ -176,9 +174,27 @@ namespace CalculatorApp
|
||||||
}
|
}
|
||||||
else
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue