diff --git a/src/CalcManager/CalculatorManager.cpp b/src/CalcManager/CalculatorManager.cpp index a5e2f18c..c32a027d 100644 --- a/src/CalcManager/CalculatorManager.cpp +++ b/src/CalcManager/CalculatorManager.cpp @@ -477,16 +477,25 @@ namespace CalculationManager } } - vector> const& CalculatorManager::GetHistoryItems() + vector> const& CalculatorManager::GetHistoryItems() const { return m_pHistory->GetHistory(); } - vector> const& CalculatorManager::GetHistoryItems(_In_ CalculatorMode mode) + vector> const& CalculatorManager::GetHistoryItems(_In_ CalculatorMode mode) const { return (mode == CalculatorMode::Standard) ? m_pStdHistory->GetHistory() : m_pSciHistory->GetHistory(); } + void CalculatorManager::SetHistoryItems(_In_ std::vector> const& historyItems) + { + for (auto const& historyItem : historyItems) + { + auto index = m_pHistory->AddItem(historyItem); + OnHistoryItemAdded(index); + } + } + shared_ptr const& CalculatorManager::GetHistoryItem(_In_ unsigned int uIdx) { return m_pHistory->GetHistoryItem(uIdx); diff --git a/src/CalcManager/CalculatorManager.h b/src/CalcManager/CalculatorManager.h index 88a8457b..5db70b65 100644 --- a/src/CalcManager/CalculatorManager.h +++ b/src/CalcManager/CalculatorManager.h @@ -107,8 +107,9 @@ namespace CalculationManager void UpdateMaxIntDigits(); wchar_t DecimalSeparator(); - std::vector> const& GetHistoryItems(); - std::vector> const& GetHistoryItems(_In_ CalculatorMode mode); + std::vector> const& GetHistoryItems() const; + std::vector> const& GetHistoryItems(_In_ CalculatorMode mode) const; + void SetHistoryItems(_In_ std::vector> const& historyItems); std::shared_ptr const& GetHistoryItem(_In_ unsigned int uIdx); bool RemoveHistoryItem(_In_ unsigned int uIdx); void ClearHistory(); diff --git a/src/CalcViewModel/ApplicationViewModel.cpp b/src/CalcViewModel/ApplicationViewModel.cpp index a5de2285..16358f97 100644 --- a/src/CalcViewModel/ApplicationViewModel.cpp +++ b/src/CalcViewModel/ApplicationViewModel.cpp @@ -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& value) + { + auto jsonObject = ref new Windows::Data::Json::JsonObject(); + auto opndCommand = dynamic_cast(value.get()); + if (opndCommand != nullptr) + { + jsonObject = SaveSnapshotToJson(*opndCommand); + } + auto unaryCommand = dynamic_cast(value.get()); + if (unaryCommand != nullptr) + { + jsonObject = SaveSnapshotToJson(*unaryCommand); + } + auto binaryCommand = dynamic_cast(value.get()); + if (binaryCommand != nullptr) + { + jsonObject = SaveSnapshotToJson(*binaryCommand); + } + auto parenthesesCommand = dynamic_cast(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(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(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(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(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& value) + { + ApplicationSnapshot applicationSnapshot; + applicationSnapshot.SnapshotVersion = static_cast(jsonObject->GetNamedNumber(L"SnapshotVersion")); + if (applicationSnapshot.SnapshotVersion > SnapshotVersion) + { + return; + } + applicationSnapshot.Mode = static_cast(jsonObject->GetNamedNumber(L"Mode")); + if (jsonObject->HasKey(L"StandardCalculatorSnapshot")) + { + std::optional 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& value) + { + StandardCalculatorSnapshot standardCalculatorSnapshot; + std::optional calcManagerSnapshot; + RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"CalculatorManagerSnapshot"), calcManagerSnapshot); + if (calcManagerSnapshot.has_value()) + { + standardCalculatorSnapshot.CalcManager = std::move(*calcManagerSnapshot); + } + else + { + return; + } + std::optional 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; + 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& value) + { + value = PrimaryDisplaySnapshot{ jsonObject->GetNamedString(L"DisplayValue"), jsonObject->GetNamedBoolean(L"IsError") }; + } + + static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& 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& value) + { + CalculatorManagerSnapshot calcManagerSnapshot; + if (jsonObject->HasKey(L"HistoryItems")) + { + std::vector> historyItems; + auto historyJsonArray = jsonObject->GetNamedArray(L"HistoryItems"); + for (uint32_t i = 0; i < historyJsonArray->Size; ++i) + { + std::optional historyItem; + RestoreJsonToSnapshot(historyJsonArray->GetObjectAt(i), historyItem); + if (historyItem.has_value()) + { + historyItems.push_back(std::make_shared(*historyItem)); + } + else + { + return; + } + } + calcManagerSnapshot.HistoryItems = std::move(historyItems); + } + value = std::move(calcManagerSnapshot); + } + + static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& 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>>(RestoreExpressionTokensFromJsonArray(jsonObject->GetNamedArray(L"Tokens"))); + if (historyItem.historyItemVector.spTokens->empty()) + { + return; + } + historyItem.historyItemVector.spCommands = std::make_shared>>( + 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>& value) + { + auto commandType = static_cast(jsonObject->GetNamedNumber(L"CommandType")); + switch (commandType) + { + case CalculationManager::CommandType::OperandCommand: + { + std::optional opndCommand; + RestoreJsonToSnapshot(jsonObject, opndCommand); + if (opndCommand.has_value()) + { + value = std::make_shared(*opndCommand); + } + break; + } + case CalculationManager::CommandType::UnaryCommand: + { + std::optional unaryCommand; + RestoreJsonToSnapshot(jsonObject, unaryCommand); + if (unaryCommand.has_value()) + { + value = std::make_shared(*unaryCommand); + } + break; + } + case CalculationManager::CommandType::BinaryCommand: + { + std::optional binaryCommand; + RestoreJsonToSnapshot(jsonObject, binaryCommand); + if (binaryCommand.has_value()) + { + value = std::make_shared(*binaryCommand); + } + break; + } + case CalculationManager::CommandType::Parentheses: + { + std::optional parenthesesCommand; + RestoreJsonToSnapshot(jsonObject, parenthesesCommand); + if (parenthesesCommand.has_value()) + { + value = std::make_shared(*parenthesesCommand); + } + break; + } + default: + throw std::logic_error{ "c8cba597-dfec-447a-bd1c-e78a9ffaad95" }; + } + } + + static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) + { + auto isNegative = jsonObject->GetNamedBoolean(L"IsNegative"); + auto isDecimalPresent = jsonObject->GetNamedBoolean(L"IsDecimalPresent"); + auto isSciFmt = jsonObject->GetNamedBoolean(L"IsSciFmt"); + std::vector commands; + auto commandsJsonArray = jsonObject->GetNamedArray(L"Commands"); + for (uint32_t i = 0; i < commandsJsonArray->Size; ++i) + { + commands.push_back(static_cast(commandsJsonArray->GetNumberAt(i))); + } + value = COpndCommand(std::make_shared>(std::move(commands)), isNegative, isDecimalPresent, isSciFmt); + } + + static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) + { + std::vector commands; + auto commandsJsonArray = jsonObject->GetNamedArray(L"Commands"); + if (commandsJsonArray->Size == 1) + { + value = CUnaryCommand(static_cast(commandsJsonArray->GetNumberAt(0))); + } + else if (commandsJsonArray->Size == 2) + { + value = CUnaryCommand(static_cast(commandsJsonArray->GetNumberAt(0)), static_cast(commandsJsonArray->GetNumberAt(1))); + } + } + + static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) + { + value = CBinaryCommand(static_cast(jsonObject->GetNamedNumber(L"Command"))); + } + + static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) + { + value = CParentheses(static_cast(jsonObject->GetNamedNumber(L"Command"))); + } + + static std::vector> RestoreExpressionTokensFromJsonArray(Windows::Data::Json::JsonArray ^ jsonArray) + { + std::vector> 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(tokenJsonArray->GetAt(1)->GetNumber())); + } + else + { + return {}; + } + } + return tokens; + } + + static std::vector> RestoreExpressionCommandsFromJsonArray(Windows::Data::Json::JsonArray ^ jsonArray) + { + std::vector> commands; + for (uint32_t i = 0; i < jsonArray->Size; ++i) + { + std::optional> 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(ref new GeographicRegion()); - auto currencyDataLoader = make_shared(make_unique()); - m_ConverterViewModel = ref new UnitConverterViewModel(make_shared(dataLoader, currencyDataLoader)); + auto dataLoader = std::make_shared(ref new GeographicRegion()); + auto currencyDataLoader = std::make_shared(std::make_unique()); + m_ConverterViewModel = ref new UnitConverterViewModel(std::make_shared(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(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; + try + { + SnapshotHelper::RestoreJsonToSnapshot(jsonObject, applicationSnapshot); + } + catch (Platform::COMException ^ e) + { + if (SnapshotHelper::IsJsonParsingException(e)) + { + return false; + } + throw; + } + + if (applicationSnapshot.has_value()) + { + Mode = static_cast(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; +} diff --git a/src/CalcViewModel/ApplicationViewModel.h b/src/CalcViewModel/ApplicationViewModel.h index ab26c35f..542144fa 100644 --- a/src/CalcViewModel/ApplicationViewModel.h +++ b/src/CalcViewModel/ApplicationViewModel.h @@ -12,6 +12,13 @@ namespace CalculatorApp { namespace ViewModel { + struct ApplicationSnapshot + { + int SnapshotVersion; + int Mode; + std::optional 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(); diff --git a/src/CalcViewModel/StandardCalculatorViewModel.cpp b/src/CalcViewModel/StandardCalculatorViewModel.cpp index 22554ca6..e1a0a12c 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.cpp +++ b/src/CalcViewModel/StandardCalculatorViewModel.cpp @@ -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>>(snapshot.ExpressionDisplay->Tokens), + std::make_shared>>(snapshot.ExpressionDisplay->Commands)); + } +} diff --git a/src/CalcViewModel/StandardCalculatorViewModel.h b/src/CalcViewModel/StandardCalculatorViewModel.h index 0af82478..dd5c102c 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.h +++ b/src/CalcViewModel/StandardCalculatorViewModel.h @@ -33,6 +33,30 @@ namespace CalculatorApp bool canSendNegate; }; + struct CalculatorManagerSnapshot + { + std::optional>> HistoryItems; + }; + + struct PrimaryDisplaySnapshot + { + Platform::String ^ DisplayValue; + bool IsError = false; + }; + + struct ExpressionDisplaySnapshot + { + std::vector> Tokens; + std::vector> Commands; + }; + + struct StandardCalculatorSnapshot + { + CalculatorManagerSnapshot CalcManager; + PrimaryDisplaySnapshot PrimaryDisplay; + std::optional 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& memorizedNumbers); diff --git a/src/Calculator/Common/LaunchArguments.cs b/src/Calculator/Common/LaunchArguments.cs index 80d4cc26..5ba2359a 100644 --- a/src/Calculator/Common/LaunchArguments.cs +++ b/src/Calculator/Common/LaunchArguments.cs @@ -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; } diff --git a/src/Calculator/Views/MainPage.xaml.cs b/src/Calculator/Views/MainPage.xaml.cs index c774e7e4..ba82deaf 100644 --- a/src/Calculator/Views/MainPage.xaml.cs +++ b/src/Calculator/Views/MainPage.xaml.cs @@ -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 {