Skip to content

Commit 80015d2

Browse files
authored
Improve the Recall support (#2245)
* stash changes * stash changes * pass build * set snapshot * stash changes * update launch utils * stash changes * stash changes * renaming * DeflateUtils * stash changes * deserialization * restore session * connect ui * handle errors * rename CalcManagerHistoryToken * handle optional props * fix optional props * ensure error state * resolve a comment * resolve a comment
1 parent 9d7d3a6 commit 80015d2

19 files changed

+995
-673
lines changed

src/CalcViewModel/ApplicationViewModel.cpp

Lines changed: 20 additions & 485 deletions
Large diffs are not rendered by default.

src/CalcViewModel/ApplicationViewModel.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#pragma once
55

6+
#include "Snapshots.h"
67
#include "StandardCalculatorViewModel.h"
78
#include "DateCalculatorViewModel.h"
89
#include "GraphingCalculator/GraphingCalculatorViewModel.h"
@@ -12,19 +13,13 @@ namespace CalculatorApp
1213
{
1314
namespace ViewModel
1415
{
15-
struct ApplicationSnapshot
16-
{
17-
int SnapshotVersion;
18-
int Mode;
19-
std::optional<StandardCalculatorSnapshot> StandardCalc;
20-
};
21-
2216
[Windows::UI::Xaml::Data::Bindable] public ref class ApplicationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
2317
{
2418
public:
2519
ApplicationViewModel();
2620

2721
void Initialize(CalculatorApp::ViewModel::Common::ViewMode mode); // Use for first init, use deserialize for rehydration
22+
void RestoreFromSnapshot(CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot^ snapshot);
2823

2924
OBSERVABLE_OBJECT();
3025
OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel ^, CalculatorViewModel);
@@ -77,6 +72,11 @@ namespace CalculatorApp
7772
}
7873
}
7974

75+
property CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot ^ Snapshot
76+
{
77+
CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot ^ get();
78+
}
79+
8080
static property Platform::String ^ LaunchedLocalSettings
8181
{
8282
Platform::String ^ get()
@@ -103,9 +103,6 @@ namespace CalculatorApp
103103

104104
void ToggleAlwaysOnTop(float width, float height);
105105

106-
Windows::Data::Json::JsonObject ^ SaveApplicationSnapshot();
107-
bool TryRestoreFromSnapshot(Windows::Data::Json::JsonObject ^ jsonObject);
108-
109106
private:
110107
bool TryRecoverFromNavigationModeFailure();
111108

src/CalcViewModel/CalcViewModel.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@
338338
<ClInclude Include="HistoryViewModel.h" />
339339
<ClInclude Include="MemoryItemViewModel.h" />
340340
<ClInclude Include="pch.h" />
341+
<ClInclude Include="Snapshots.h" />
341342
<ClInclude Include="StandardCalculatorViewModel.h" />
342343
<ClInclude Include="targetver.h" />
343344
<ClInclude Include="UnitConverterViewModel.h" />
@@ -380,6 +381,7 @@
380381
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
381382
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
382383
</ClCompile>
384+
<ClCompile Include="Snapshots.cpp" />
383385
<ClCompile Include="StandardCalculatorViewModel.cpp" />
384386
<ClCompile Include="UnitConverterViewModel.cpp" />
385387
</ItemGroup>

src/CalcViewModel/CalcViewModel.vcxproj.filters

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<ClCompile Include="MemoryItemViewModel.cpp" />
9090
<ClCompile Include="StandardCalculatorViewModel.cpp" />
9191
<ClCompile Include="UnitConverterViewModel.cpp" />
92+
<ClCompile Include="Snapshots.cpp" />
9293
</ItemGroup>
9394
<ItemGroup>
9495
<ClInclude Include="pch.h" />
@@ -203,6 +204,7 @@
203204
<ClInclude Include="StandardCalculatorViewModel.h" />
204205
<ClInclude Include="targetver.h" />
205206
<ClInclude Include="UnitConverterViewModel.h" />
207+
<ClInclude Include="Snapshots.h" />
206208
</ItemGroup>
207209
<ItemGroup>
208210
<None Include="DataLoaders\DefaultFromToCurrency.json">

src/CalcViewModel/Common/TraceLogger.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,9 @@ namespace CalculatorApp
345345
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_RECALL_RESTORE), fields);
346346
}
347347

348-
void TraceLogger::LogRecallError(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String^ message)
348+
void TraceLogger::LogRecallError(Platform::String^ message)
349349
{
350350
auto fields = ref new LoggingFields();
351-
fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode));
352351
fields->AddString(StringReference(L"FunctionName"), L"Recall");
353352
fields->AddString(StringReference(L"Message"), message);
354353
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields);

src/CalcViewModel/Common/TraceLogger.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ namespace CalculatorApp::ViewModel::Common
8787
void LogPlatformExceptionInfo(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String ^ functionName, Platform::String ^ message, int hresult);
8888
void LogRecallSnapshot(CalculatorApp::ViewModel::Common::ViewMode mode);
8989
void LogRecallRestore(CalculatorApp::ViewModel::Common::ViewMode mode);
90-
void LogRecallError(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String ^ message);
90+
void LogRecallError(Platform::String ^ message);
9191

9292
internal:
9393
void LogPlatformException(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String ^ functionName, Platform::Exception ^ e);

src/CalcViewModel/Snapshots.cpp

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#include "pch.h"
5+
#include <cassert>
6+
#include <stdexcept>
7+
#include <vector>
8+
9+
#include "CalcManager/ExpressionCommand.h"
10+
#include "Snapshots.h"
11+
12+
namespace CalculatorApp::ViewModel::Snapshot
13+
{
14+
UnaryCommand::UnaryCommand()
15+
{
16+
}
17+
18+
UnaryCommand::UnaryCommand(std::vector<int> cmds)
19+
: m_cmds(std::move(cmds))
20+
{
21+
}
22+
23+
Windows::Foundation::Collections::IVectorView<int> ^ UnaryCommand::Commands::get()
24+
{
25+
return ref new Platform::Collections::VectorView<int>(m_cmds);
26+
}
27+
28+
void UnaryCommand::Commands::set(Windows::Foundation::Collections::IVectorView<int> ^ commands)
29+
{
30+
m_cmds.clear();
31+
for (auto cmd : commands)
32+
{
33+
m_cmds.push_back(cmd);
34+
}
35+
}
36+
37+
BinaryCommand::BinaryCommand()
38+
{
39+
Command = 0;
40+
}
41+
42+
BinaryCommand::BinaryCommand(int cmd)
43+
{
44+
Command = cmd;
45+
}
46+
47+
OperandCommand::OperandCommand()
48+
{
49+
IsNegative = false;
50+
IsDecimalPresent = false;
51+
IsSciFmt = false;
52+
}
53+
54+
OperandCommand::OperandCommand(bool isNegative, bool isDecimal, bool isSciFmt, std::vector<int> cmds)
55+
{
56+
IsNegative = isNegative;
57+
IsDecimalPresent = isDecimal;
58+
IsSciFmt = isSciFmt;
59+
m_cmds = std::move(cmds);
60+
}
61+
62+
Windows::Foundation::Collections::IVectorView<int> ^ OperandCommand::Commands::get()
63+
{
64+
return ref new Platform::Collections::VectorView<int>(m_cmds);
65+
}
66+
67+
void OperandCommand::Commands::set(Windows::Foundation::Collections::IVectorView<int> ^ commands)
68+
{
69+
m_cmds.clear();
70+
for (auto cmd : commands)
71+
{
72+
m_cmds.push_back(cmd);
73+
}
74+
}
75+
76+
Parentheses::Parentheses()
77+
{
78+
Command = 0;
79+
}
80+
81+
Parentheses::Parentheses(int cmd)
82+
{
83+
Command = cmd;
84+
}
85+
86+
ICalcManagerIExprCommand ^ CreateExprCommand(const IExpressionCommand* exprCmd) {
87+
switch (exprCmd->GetCommandType())
88+
{
89+
case CalculationManager::CommandType::UnaryCommand:
90+
{
91+
auto cmd = static_cast<const IUnaryCommand*>(exprCmd);
92+
std::vector<int> cmdlist;
93+
for (auto& subcmd : *cmd->GetCommands())
94+
{
95+
cmdlist.push_back(subcmd);
96+
}
97+
return ref new UnaryCommand(std::move(cmdlist));
98+
}
99+
case CalculationManager::CommandType::BinaryCommand:
100+
{
101+
auto cmd = static_cast<const IBinaryCommand*>(exprCmd);
102+
return ref new BinaryCommand(cmd->GetCommand());
103+
}
104+
case CalculationManager::CommandType::OperandCommand:
105+
{
106+
auto cmd = static_cast<const IOpndCommand*>(exprCmd);
107+
std::vector<int> cmdlist;
108+
for (auto& subcmd : *cmd->GetCommands())
109+
{
110+
cmdlist.push_back(subcmd);
111+
}
112+
return ref new OperandCommand(cmd->IsNegative(), cmd->IsDecimalPresent(), cmd->IsSciFmt(), std::move(cmdlist));
113+
}
114+
case CalculationManager::CommandType::Parentheses:
115+
{
116+
auto cmd = static_cast<const IParenthesisCommand*>(exprCmd);
117+
return ref new Parentheses(cmd->GetCommand());
118+
}
119+
default:
120+
throw std::logic_error{ "unhandled command type." };
121+
}
122+
}
123+
124+
CalcManagerToken::CalcManagerToken()
125+
{
126+
OpCodeName = ref new Platform::String();
127+
CommandIndex = 0;
128+
}
129+
130+
CalcManagerToken::CalcManagerToken(Platform::String ^ opCodeName, int cmdIndex)
131+
{
132+
assert(opCodeName != nullptr && "opCodeName is mandatory.");
133+
OpCodeName = opCodeName;
134+
CommandIndex = cmdIndex;
135+
}
136+
137+
CalcManagerHistoryItem::CalcManagerHistoryItem()
138+
{
139+
Tokens = ref new Platform::Collections::Vector<CalcManagerToken ^>();
140+
Commands = ref new Platform::Collections::Vector<ICalcManagerIExprCommand ^>();
141+
Expression = ref new Platform::String();
142+
Result = ref new Platform::String();
143+
}
144+
145+
CalcManagerHistoryItem::CalcManagerHistoryItem(const CalculationManager::HISTORYITEM& item)
146+
{
147+
Tokens = ref new Platform::Collections::Vector<CalcManagerToken ^>();
148+
assert(item.historyItemVector.spTokens != nullptr && "spTokens shall not be null.");
149+
for (auto& [opCode, cmdIdx] : *item.historyItemVector.spTokens)
150+
{
151+
Tokens->Append(ref new CalcManagerToken(ref new Platform::String(opCode.c_str()), cmdIdx));
152+
}
153+
Commands = ref new Platform::Collections::Vector<ICalcManagerIExprCommand ^>();
154+
assert(item.historyItemVector.spCommands != nullptr && "spCommands shall not be null.");
155+
for (auto& cmd : *item.historyItemVector.spCommands)
156+
{
157+
Commands->Append(CreateExprCommand(cmd.get()));
158+
}
159+
Expression = ref new Platform::String(item.historyItemVector.expression.c_str());
160+
Result = ref new Platform::String(item.historyItemVector.result.c_str());
161+
}
162+
163+
CalcManagerSnapshot::CalcManagerSnapshot()
164+
{
165+
HistoryItems = nullptr;
166+
}
167+
168+
CalcManagerSnapshot::CalcManagerSnapshot(const CalculationManager::CalculatorManager& calcMgr)
169+
{
170+
auto& items = calcMgr.GetHistoryItems();
171+
if (!items.empty())
172+
{
173+
HistoryItems = ref new Platform::Collections::Vector<CalcManagerHistoryItem ^>();
174+
for (auto& item : items)
175+
{
176+
HistoryItems->Append(ref new CalcManagerHistoryItem(*item));
177+
}
178+
}
179+
}
180+
181+
PrimaryDisplaySnapshot::PrimaryDisplaySnapshot()
182+
{
183+
DisplayValue = ref new Platform::String();
184+
IsError = false;
185+
}
186+
187+
PrimaryDisplaySnapshot::PrimaryDisplaySnapshot(Platform::String ^ display, bool isError)
188+
{
189+
assert(display != nullptr && "display is mandatory");
190+
DisplayValue = display;
191+
IsError = isError;
192+
}
193+
194+
ExpressionDisplaySnapshot::ExpressionDisplaySnapshot()
195+
{
196+
Tokens = ref new Platform::Collections::Vector<CalcManagerToken ^>();
197+
Commands = ref new Platform::Collections::Vector<ICalcManagerIExprCommand ^>();
198+
}
199+
200+
ExpressionDisplaySnapshot::ExpressionDisplaySnapshot(
201+
const std::vector<CalcHistoryToken>& tokens,
202+
const std::vector<std::shared_ptr<IExpressionCommand>>& commands)
203+
{
204+
Tokens = ref new Platform::Collections::Vector<CalcManagerToken ^>();
205+
for (auto& [opCode, cmdIdx] : tokens)
206+
{
207+
Tokens->Append(ref new CalcManagerToken(ref new Platform::String(opCode.c_str()), cmdIdx));
208+
}
209+
210+
Commands = ref new Platform::Collections::Vector<ICalcManagerIExprCommand ^>();
211+
for (auto& cmd : commands)
212+
{
213+
Commands->Append(CreateExprCommand(cmd.get()));
214+
}
215+
}
216+
217+
StandardCalculatorSnapshot::StandardCalculatorSnapshot()
218+
{
219+
CalcManager = ref new CalcManagerSnapshot();
220+
PrimaryDisplay = ref new PrimaryDisplaySnapshot();
221+
ExpressionDisplay = nullptr;
222+
DisplayCommands = ref new Platform::Collections::Vector<ICalcManagerIExprCommand ^>();
223+
}
224+
225+
std::vector<std::shared_ptr<CalculationManager::HISTORYITEM>> ToUnderlying(Windows::Foundation::Collections::IVector<CalcManagerHistoryItem ^> ^ items)
226+
{
227+
std::vector<std::shared_ptr<CalculationManager::HISTORYITEM>> result;
228+
for (CalcManagerHistoryItem ^ item : items)
229+
{
230+
CalculationManager::HISTORYITEMVECTOR nativeItem;
231+
nativeItem.spTokens = std::make_shared<std::vector<std::pair<std::wstring, int>>>();
232+
for (CalcManagerToken ^ token : item->Tokens)
233+
{
234+
nativeItem.spTokens->push_back(std::make_pair(token->OpCodeName->Data(), token->CommandIndex));
235+
}
236+
nativeItem.spCommands = std::make_shared<std::vector<std::shared_ptr<IExpressionCommand>>>(ToUnderlying(item->Commands));
237+
nativeItem.expression = item->Expression->Data();
238+
nativeItem.result = item->Result->Data();
239+
auto spItem = std::make_shared<CalculationManager::HISTORYITEM>(CalculationManager::HISTORYITEM{ std::move(nativeItem) });
240+
result.push_back(std::move(std::move(spItem)));
241+
}
242+
return result;
243+
}
244+
245+
std::vector<std::shared_ptr<IExpressionCommand>> ToUnderlying(Windows::Foundation::Collections::IVector<ICalcManagerIExprCommand ^> ^ commands)
246+
{
247+
std::vector<std::shared_ptr<IExpressionCommand>> result;
248+
for (ICalcManagerIExprCommand ^ cmdEntry : commands)
249+
{
250+
if (auto unary = dynamic_cast<UnaryCommand ^>(cmdEntry); unary != nullptr)
251+
{
252+
if (unary->m_cmds.size() == 1)
253+
{
254+
result.push_back(std::make_shared<CUnaryCommand>(unary->m_cmds[0]));
255+
}
256+
else if (unary->m_cmds.size() == 2)
257+
{
258+
result.push_back(std::make_shared<CUnaryCommand>(unary->m_cmds[0], unary->m_cmds[1]));
259+
}
260+
else
261+
{
262+
throw std::logic_error{ "ill-formed command." };
263+
}
264+
}
265+
else if (auto binary = dynamic_cast<BinaryCommand ^>(cmdEntry); binary != nullptr)
266+
{
267+
result.push_back(std::make_shared<CBinaryCommand>(binary->Command));
268+
}
269+
else if (auto paren = dynamic_cast<Parentheses ^>(cmdEntry); paren != nullptr)
270+
{
271+
result.push_back(std::make_shared<CParentheses>(paren->Command));
272+
}
273+
else if (auto operand = dynamic_cast<OperandCommand ^>(cmdEntry); operand != nullptr)
274+
{
275+
auto subcmds = std::make_shared<std::vector<int>>(operand->m_cmds);
276+
result.push_back(std::make_shared<COpndCommand>(std::move(subcmds), operand->IsNegative, operand->IsDecimalPresent, operand->IsSciFmt));
277+
}
278+
}
279+
return result;
280+
}
281+
} // namespace CalculatorApp::ViewModel

0 commit comments

Comments
 (0)