Merge pull request #899 from EriWong/mergeFeature

Merge graphing calculator into master
This commit is contained in:
Matt Cooley 2020-01-07 14:56:30 -08:00 committed by GitHub
commit 4ac76c66da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
128 changed files with 15944 additions and 927 deletions

View File

@ -51,6 +51,10 @@ We also welcome [issues submitted on GitHub](https://github.com/Microsoft/calcul
## Roadmap
For information regarding Windows Calculator plans and release schedule, please see the [Windows Calculator Roadmap](docs/Roadmap.md).
### Graphing Mode
Adding graphing calculator functionality [is on the project roadmap](https://github.com/Microsoft/calculator/issues/338) and we hope that this project can create a great end-user experience around graphing. To that end, the UI from the official in-box Windows Calculator is currently part of this repository, although the proprietary Microsoft-built graphing engine, which also drives graphing in Microsoft Mathematics and OneNote, is not. Community members can still be involved in the creation of the UI, however developer builds will not have graphing functionality due to the use of a [mock implementation of the engine](/src/MockGraphingImpl) built on top of a
[common graphing API](/src/GraphingInterfaces).
## Diagnostic Data
This project collects usage data and sends it to Microsoft to help improve our products and services.
Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkId=521839) to learn more.

View File

@ -260,17 +260,19 @@ Steps:
4. While in the Menu: Check the About Page
*Expected: Everything in the about page fits into its window*
5. For Scientific Mode: At a Larger Scale
*Expected: All buttons are present and the up arrow is grayed out.*
*Expected: All buttons are present and the 2nd button is grayed out.*
6. For Scientific Mode: At a Smaller Scale
*Expected: All buttons are present and the up arrow is able to be toggled.*
*Expected: All buttons are present and the 2nd button is able to be toggled.*
7. For Programmer Mode: At a Any Scale
*Expected: All buttons are present and the up arrow is able to be toggled.*
*Expected: All buttons are present and the 2nd button is able to be toggled.*
8. For Converter Mode: While Scaling
*Expected: The number pad and input areas move around each other gracefully.*
9. Changing Language: Open Settings app > Time & language > Region & language > Add a language > Select a Right to Left (RTL) language such as Hebrew > Install the associated files> Set it to the system default.
10. Set the system number format preference: Open a Run dialog (WIN + R) > type intl.cpl > Enter > In the format dropdown list > Select Hebrew > Apply.
11. Initiating the change: Package has completed installing > Sign out > Sign in. (This change to the app may also require a reinstallation of the build)
12. Repeat Steps 2-6 again in a (RTL) language.
9. For Graphing Mode: While Scaling
*Expected: The number pad, graph area, and input areas move around each other gracefully.*
10. Changing Language: Open Settings app > Time & language > Region & language > Add a language > Select a Right to Left (RTL) language such as Hebrew > Install the associated files> Set it to the system default.
11. Set the system number format preference: Open a Run dialog (WIN + R) > type intl.cpl > Enter > In the format dropdown list > Select Hebrew > Apply.
12. Initiating the change: Package has completed installing > Sign out > Sign in. (This change to the app may also require a reinstallation of the build)
13. Repeat Steps 2-6 again in a (RTL) language.
*Expected: No elements fall out of intended boundaries.*
@ -302,11 +304,60 @@ Verify the following:
11. "Bin" Binary:
*Expected: A B C D E F 2 3 4 5 6 7 8 9 are inactive. A maximum of 64 characters can be entered.*
**Graphing Mode Test: Verify Graphing mode functions**
Steps:
1. Launch the "Calculator" app
2. Navigate to "Graphing" Calculator
3. Enter a function of x in the input field <br>
*Expected: Function is plotted in the graph area. Line color matches the colored square next to the input field*
4. Select the "+" button below the function input and enter more functions in the fields that appear <br>
*Expected: All functions are plotted in the graph area and match the colors of the input field squares*
5. Select the colored square for any function <br>
*Expected: Visibility of the function in the graph is toggled off/on*
6. Select the "Zoom In", "Zoom Out", and "Reset View' buttons in the graph area <br>
*Expected: Both X and Y axes zoom in, out, and revert to default settings, respectively*
7. Select the Trace button, then click + drag the graph until the red square is near a graphed function<br>
*Expected: Closest (X, Y) coordinates of the function to the red square are displayed with a black dot to indicate the location*
8. Enter "y=mx+b" into a function input field, then select "Variables" button <br>
*Expected: y=x+1 function is plotted in the graph, "Variables" modal window shows two variables "m" and "b" with values set to 1.*
9. Adjust the value, minimum, maximum, and step for each variable <br>
*Expected: y=mx+b graph adjusts to the new values for m and b, step size changes the increments of the slider for each value*
10. Share the graph via OneNote, Outlook/Mail, Twitter, and Feedback Hub <br>
*Expected: Modifiable message that contains an image of the graph customized for the chosen application opens*
11. Verify Key Graph Features tab shows the correct information for the following functions: <br>
*(Note: IP = Inflection Points, VA = Vertical Asymptotes, HA = Horizontal Asymptotes, OA = Oblique Asymptotes)* <br>
a. **y=x** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈⁆; X/Y Intercepts: (0)/(0); Max: none; Min: none; IP: none; VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: (-∞, ∞) Increasing* <br>
b. **y=1/x** <br>
*Expected: Domain: ⁅𝑥≠0⁆; Range: ⁅y∈\{0}⁆; X/Y Intercepts: ø/ø; Max: none; Min: none; IP: none; VA: x=0; HA: y=0; OA: none; Parity: Odd; Monotonicity: (0, ∞) Decreasing, (-∞, 0) Increasing* <br>
c. **y=x^2** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈{0, ∞)⁆; X/Y Intercepts: (0)/(0); Max: none; Min: (0,0); IP: none; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: (0, ∞) Increasing, (-∞, 0) Decreasing* <br>
d. **y=x^3** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈⁆; X/Y Intercepts: (0)/(0); Max: none; Min: none; IP: (0,0); VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: (-∞, ∞) Increasing* <br>
e. **y=e^x** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈(0, ∞)⁆; X/Y Intercepts: ø/(1); Max: none; Min: none; IP: none; VA: none; HA: y=0; OA: none; Parity: none; Monotonicity: (-∞, ∞) Increasing* <br>
f. **y=ln(x)** <br>
*Expected: Domain: ⁅𝑥>0⁆; Range: ⁅y∈⁆; X/Y Intercepts: (1)/ø; Max: none; Min: none; IP: none; VA: x=0; HA: none; OA: none; Parity: none; Monotonicity: (0, ∞) Increasing* <br>
g. **y=sin(x)** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅𝑦∈[1,1]⁆; X/Y Intercepts: (⁅𝜋n1,n1∈⁆)/(0); Max: ⁅(2𝜋n1+𝜋/2,1),n1∈⁆; Min: ⁅(2𝜋n1+3𝜋/2,1),n1∈⁆; IP: ⁅(𝜋n1,0),n1∈⁆; VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: ⁅(2𝜋n1+𝜋/2,2𝜋n1+3𝜋/2),n1∈⁆ Decreasing; ⁅(2𝜋n1+3𝜋/2,2𝜋n1+5𝜋/2),n1∈⁆ Increasing; Period: 2𝜋* <br>
h. **y=cos(x)** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅𝑦∈[1,1]⁆; X/Y Intercepts: (⁅𝜋n1+𝜋/2,n1∈⁆)/(1); Max: ⁅(2𝜋n1,1),n1∈⁆; Min: ⁅(2𝜋n1+𝜋,-1),n1∈⁆; IP: ⁅(𝜋n1+𝜋/2,0),n1∈⁆; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: ⁅(2𝜋n1+𝜋,2𝜋n1+2𝜋),n1∈⁆ Increasing, ⁅(2𝜋n1,2𝜋n1+𝜋),n1∈⁆ Decreasing; Period: 2𝜋* <br>
i. **y=tan(x)** <br>
*Expected: Domain: ⁅x≠𝜋n1+𝜋/2,∀n1∈⁆; Range: ⁅𝑦∈ℝ⁆; X/Y Intercepts: (x=𝜋n1, n1 ∈ℤ)/(0); Max: none; Min: none; IP: x=𝜋n1, n1 ∈ℤ; VA: x=𝜋n1+𝜋/2, n1∈; HA: none; OA: none; Parity: Odd; Monotonicity: ⁅(𝜋n1+𝜋/2,𝜋n1+3𝜋/2),n1∈⁆ Increasing; Period: 𝜋* <br>
j. **y=sqrt(25-x^2)** <br>
*Expected: Domain: ⁅x∈[-5,5]⁆; Range: ⁅𝑦∈[0,5]⁆; X/Y Intercepts: (5),(-5)/(5); Max: (0,5); Min: (-5,0) and (5,0); IP: none; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: (0,5) Decreasing, (-5,0) Increasing* <br>
k. **y=(-3x^2+2)/(x-1)** <br>
*Expected: Domain: ⁅x≠1⁆; Range: ⁅𝑦∈(-∞, -2√3 - 6}U{2√3 -6,∞⁆; X/Y Intercepts: (-√6/3),(√6/3)/(-2); Max: ⁅(√3/3+1,-2√36)⁆; Min: ⁅(√3/3+1,2√36)⁆; IP: none; VA: x=1; HA: none; OA: y=-3x-3; Parity: none; Monotonicity: (√3/3+1,∞) Decreasing, (1,√3/3+1,) Increasing(-√3/3+1,1), Increasing, (-∞,-√3/3+1) Decreasing* <br>
l. **y=sin(sin(x))** ("too complex" error test) <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: Unable to calculate range for this function; X/Y Intercepts: none; Max: none; Min: none; IP: none; VA: none; HA: none; OA: none; Parity: odd; Monotonicity: Unable to determine the monotonicity of the function* <br>
*These features are too complex for Calculator to calculate: Range, X Intercept, Period, Minima, Maxima, Inflection Points, Monotonicity*
m. **y=mx+b** <br>
*Expected: Analysis is not supported for this function*
**Date Calculation Test: Verify dates can be calculated.**
Steps:
1. Launch the "Calculator" app.
2. Navigate to "Date Calculation" Calculator.
1. Launch the "Calculator" app
2. Navigate to "Date Calculation" Calculator
3. With "Difference between dates" Selected
Change the various date input fields
*Expected: From and To reflect dates input respectively.*
@ -332,80 +383,88 @@ Steps:
1. Launch the "Calculator" app.
For All Applicable Modes verify the following (note: only 11-15 and 20 work in Always-on-Top mode):
2. Press **Alt +1** to Enter "Standard" mode
2. Press **Alt +1** to enter "Standard" mode
*Expected: Move to "Standard" screen.*
3. Press **Alt +2** to Enter "Scientific" mode
3. Press **Alt +2** to enter "Scientific" mode
*Expected: Move to "Scientific" screen.*
4. Press **Alt +3** to Enter "Programmer" mode
4. Press **Alt +3** to enter "Programmer" mode
*Expected: Move to "Programming" screen.*
5. Press **Alt +4** to Enter "Date Calculation" mode
5. Press **Alt +4** to enter "Date Calculation" mode
*Expected: Move to "Date Calculation" screen.*
6. Press **Ctrl +M** to Store in Memory
7. Press **Ctrl +P** to Add to Active Memory
8. Press **Ctrl +Q** to Subtract form Active Memory
9. Press **Ctrl +R** to Recall from Memory
10. Press **Ctrl +L** to Clear from Memory
11. Press **Delete** to Clear Current Input 'CE'
12. Press **Esc** to Full Clear Input 'C'
13. Press **F9** to Toggle '±'
14. Press **R** to Select '1/x'
15. Press **@** to Select '√'
16. Press **Ctrl + H** to Toggle History Panel
6 Press **Alt +5** to enter "Graphing" mode
*Expected: Move to "Graphing" screen.*
7. Press **Ctrl +M** to Store in Memory
8. Press **Ctrl +P** to Add to Active Memory
9. Press **Ctrl +Q** to Subtract form Active Memory
10. Press **Ctrl +R** to Recall from Memory
11. Press **Ctrl +L** to Clear from Memory
12. Press **Delete** to Clear Current Input 'CE'
13. Press **Esc** to Full Clear Input 'C'
14. Press **F9** to Toggle '±'
15. Press **R** to Select '1/x'
16. Press **@** to Select '√'
17. Press **Ctrl + H** to Toggle History Panel
*Expected: Function when in small scale window.*
17. Press **Up arrow** to Move up History Panel
18. Press **Up arrow** to Move up History Panel
*Expected: Function when in small scale window.*
18. Press **Down arrow** to Move Down History Panel
19. Press **Down arrow** to Move Down History Panel
*Expected: Function when in small scale window.*
19. Press **Ctrl + Shift + D** to Clear History Panel
20. Press **Ctrl + Shift + D** to Clear History Panel
*Expected: Function when in small scale window.*
20. Press **Spacebar** to Repeat Last Input
21. Press **Spacebar** to Repeat Last Input
Verify the following in Scientific Mode
21. Press **F3** to Select 'DEG'
22. Press **F4** to Select 'RAD'
23. Press **F5** to Select 'GRAD'
24. Press **Ctrl +G** to Select '10ˣ'
25. Press **Ctrl +Y** to Select 'y√x'
26. Press **Shift +O** to Select 'sin-1'
27. Press **Shift + S** to Select 'cos-1'
28. Press **Shift +T** to Select 'tan-1'
29. Press **Ctrl +O** to Select 'Cosh'
30. Press **Ctrl +S** to Select 'Sinh'
31. Press **Ctrl +T** to Select 'Tanh'
32. Press **D** to Select 'Mod'
33. Press **L** to Select 'log'
34. Press **M** to Select 'dms'
35. Press **N** to Select 'ln'
36. Press **Ctrl +N** to Select 'ex'
37. Press **O** to Select 'Cos'
38. Press **P** to Select 'π'
39. Press **Q** to Select 'x²'
40. Press **S** to Select 'Sin'
41. Press **T** to Select 'Tan'
42. Press **V** to Toggle 'F-E'
43. Press **X** to Select 'Exp'
44. Press **Y** or **^** to Select 'xʸ'
45. Press **#** to Select 'x³'
46. Press **!** to Select 'n!'
22. Press **F3** to Select 'DEG'
23. Press **F4** to Select 'RAD'
24. Press **F5** to Select 'GRAD'
25. Press **Ctrl +G** to Select '10ˣ'
26. Press **Ctrl +Y** to Select 'y√x'
27. Press **Shift +O** to Select 'sin-1'
28. Press **Shift + S** to Select 'cos-1'
29. Press **Shift +T** to Select 'tan-1'
30. Press **Ctrl +O** to Select 'Cosh'
31. Press **Ctrl +S** to Select 'Sinh'
32. Press **Ctrl +T** to Select 'Tanh'
33. Press **D** to Select 'Mod'
34. Press **L** to Select 'log'
35. Press **M** to Select 'dms'
36. Press **N** to Select 'ln'
37. Press **Ctrl +N** to Select 'ex'
38. Press **O** to Select 'Cos'
39. Press **P** to Select 'π'
40. Press **Q** to Select 'x²'
41. Press **S** to Select 'Sin'
42. Press **T** to Select 'Tan'
43. Press **V** to Toggle 'F-E'
44. Press **X** to Select 'Exp'
45. Press **Y** or **^** to Select 'xʸ'
46. Press **#** to Select 'x³'
47. Press **!** to Select 'n!'
Verify the following in Programmer Mode
47. Press **F2** to Select 'DWORD'
48. Press **F3** to Select 'WORD'
49. Press **F4** to Select 'BYTE'
50. Press **F5** to Select 'HEX'
51. Press **F6** to Select 'DEC'
52. Press **F7** to Select 'OCT'
53. Press **F8** to Select 'BIN'
54. Press **F12** to Select 'QWORD'
55. Press **A-F** to Input in HEX
56. Press **J** to Select 'RoL'
57. Press **K** to Select 'RoR'
58. Press **<** to Select 'Lsh'
59. Press **>** to Select 'Rsh'
60. Press **%** to Select 'Mod'
61. Press **|** to Select 'Or'
62. Press **~** to Select 'Not'
63. Press **&** to Select 'And'
48. Press **F2** to Select 'DWORD'
49. Press **F3** to Select 'WORD'
50. Press **F4** to Select 'BYTE'
51. Press **F5** to Select 'HEX'
52. Press **F6** to Select 'DEC'
53. Press **F7** to Select 'OCT'
54. Press **F8** to Select 'BIN'
55. Press **F12** to Select 'QWORD'
56. Press **A-F** to Input in HEX
57. Press **J** to Select 'RoL'
58. Press **K** to Select 'RoR'
59. Press **<** to Select 'Lsh'
60. Press **>** to Select 'Rsh'
61. Press **%** to Select 'Mod'
62. Press **|** to Select 'Or'
63. Press **~** to Select 'Not'
64. Press **&** to Select 'And'
Verify the following in Graphing Mode
65. Press **x** to Select 'x'
66. Press **y** to Select 'y'
67. Press **Ctrl +[Numpad+]** to Select 'Zoom In'
68. Press **Ctrl +[Numpad-]** to Select 'Zoom Out'
## Localization Tests

View File

@ -132,29 +132,9 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
<GenerateProjectSpecificOutputFolder>true</GenerateProjectSpecificOutputFolder>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>

View File

@ -0,0 +1,156 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include <string>
#include <vector>
#include "winerror_cross_platform.h"
#include "Ratpack/CalcErr.h"
#include <stdexcept> // for std::out_of_range
#include "sal_cross_platform.h" // for SAL
template <typename TType>
class CalculatorVector
{
public:
ResultCode GetAt(_In_opt_ unsigned int index, _Out_ TType* item)
{
try
{
*item = m_vector.at(index);
}
catch (const std::out_of_range& /*ex*/)
{
return E_BOUNDS;
}
return S_OK;
}
ResultCode GetSize(_Out_ unsigned int* size)
{
*size = static_cast<unsigned>(m_vector.size());
return S_OK;
}
ResultCode SetAt(_In_ unsigned int index, _In_opt_ TType item)
{
try
{
m_vector[index] = item;
}
catch (const std::out_of_range& /*ex*/)
{
return E_BOUNDS;
}
return S_OK;
}
ResultCode RemoveAt(_In_ unsigned int index)
{
if (index < m_vector.size())
{
m_vector.erase(m_vector.begin() + index);
}
else
{
return E_BOUNDS;
}
return S_OK;
}
ResultCode InsertAt(_In_ unsigned int index, _In_ TType item)
{
try
{
auto iter = m_vector.begin() + index;
m_vector.insert(iter, item);
}
catch (const std::bad_alloc& /*ex*/)
{
return E_OUTOFMEMORY;
}
return S_OK;
}
ResultCode Truncate(_In_ unsigned int index)
{
if (index < m_vector.size())
{
auto startIter = m_vector.begin() + index;
m_vector.erase(startIter, m_vector.end());
}
else
{
return E_BOUNDS;
}
return S_OK;
}
ResultCode Append(_In_opt_ TType item)
{
try
{
m_vector.push_back(item);
}
catch (const std::bad_alloc& /*ex*/)
{
return E_OUTOFMEMORY;
}
return S_OK;
}
ResultCode RemoveAtEnd()
{
m_vector.erase(--(m_vector.end()));
return S_OK;
}
ResultCode Clear()
{
m_vector.clear();
return S_OK;
}
ResultCode GetString(_Out_ std::wstring* expression)
{
unsigned int nTokens = 0;
ResultCode hr = this->GetSize(&nTokens);
if (SUCCEEDED(hr))
{
std::pair<std::wstring, int> currentPair;
for (unsigned int i = 0; i < nTokens; i++)
{
hr = this->GetAt(i, &currentPair);
if (SUCCEEDED(hr))
{
expression->append(currentPair.first);
if (i != (nTokens - 1))
{
expression->append(L" ");
}
}
}
std::wstring expressionSuffix{};
hr = GetExpressionSuffix(&expressionSuffix);
if (SUCCEEDED(hr))
{
expression->append(expressionSuffix);
}
}
return hr;
}
ResultCode GetExpressionSuffix(_Out_ std::wstring* suffix)
{
*suffix = L" =";
return S_OK;
}
private:
std::vector<TType> m_vector;
};

View File

@ -43,6 +43,7 @@ namespace
ApplicationViewModel::ApplicationViewModel()
: m_CalculatorViewModel(nullptr)
, m_DateCalcViewModel(nullptr)
, m_GraphingCalcViewModel(nullptr)
, m_ConverterViewModel(nullptr)
, m_PreviousMode(ViewMode::None)
, m_mode(ViewMode::None)
@ -132,6 +133,13 @@ void ApplicationViewModel::OnModeChanged()
}
m_CalculatorViewModel->SetCalculatorType(m_mode);
}
else if (NavCategory::IsGraphingCalculatorViewMode(m_mode))
{
if (!m_GraphingCalcViewModel)
{
m_GraphingCalcViewModel = ref new GraphingCalculatorViewModel();
}
}
else if (NavCategory::IsDateCalculatorViewMode(m_mode))
{
if (!m_DateCalcViewModel)
@ -182,7 +190,7 @@ void ApplicationViewModel::OnCopyCommand(Object ^ parameter)
{
DateCalcViewModel->OnCopyCommand(parameter);
}
else
else if (NavCategory::IsCalculatorViewMode(m_mode))
{
CalculatorViewModel->OnCopyCommand(parameter);
}

View File

@ -5,6 +5,7 @@
#include "StandardCalculatorViewModel.h"
#include "DateCalculatorViewModel.h"
#include "GraphingCalculator/GraphingCalculatorViewModel.h"
#include "UnitConverterViewModel.h"
namespace CalculatorApp
@ -21,6 +22,7 @@ namespace CalculatorApp
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel ^, CalculatorViewModel);
OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel ^, DateCalcViewModel);
OBSERVABLE_PROPERTY_RW(GraphingCalculatorViewModel ^, GraphingCalcViewModel);
OBSERVABLE_PROPERTY_RW(UnitConverterViewModel ^, ConverterViewModel);
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode);
OBSERVABLE_PROPERTY_R(bool, IsAlwaysOnTop);

View File

@ -122,29 +122,9 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
<GenerateProjectSpecificOutputFolder>true</GenerateProjectSpecificOutputFolder>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -343,6 +323,11 @@
<ClInclude Include="DataLoaders\UnitConverterDataConstants.h" />
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h" />
<ClInclude Include="DateCalculatorViewModel.h" />
<ClInclude Include="GraphingCalculatorEnums.h" />
<ClInclude Include="GraphingCalculator\EquationViewModel.h" />
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h" />
<ClInclude Include="GraphingCalculator\VariableViewModel.h" />
<ClInclude Include="GraphingCalculator\GraphingSettingsViewModel.h" />
<ClInclude Include="HistoryItemViewModel.h" />
<ClInclude Include="HistoryViewModel.h" />
<ClInclude Include="MemoryItemViewModel.h" />
@ -372,6 +357,9 @@
<ClCompile Include="DataLoaders\CurrencyHttpClient.cpp" />
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp" />
<ClCompile Include="DateCalculatorViewModel.cpp" />
<ClCompile Include="GraphingCalculator\EquationViewModel.cpp" />
<ClCompile Include="GraphingCalculator\GraphingCalculatorViewModel.cpp" />
<ClCompile Include="GraphingCalculator\GraphingSettingsViewModel.cpp" />
<ClCompile Include="HistoryItemViewModel.cpp" />
<ClCompile Include="HistoryViewModel.cpp" />
<ClCompile Include="MemoryItemViewModel.cpp" />
@ -392,6 +380,9 @@
<ProjectReference Include="..\CalcManager\CalcManager.vcxproj">
<Project>{311e866d-8b93-4609-a691-265941fee101}</Project>
</ProjectReference>
<ProjectReference Include="..\GraphControl\GraphControl.vcxproj">
<Project>{e727a92b-f149-492c-8117-c039a298719b}</Project>
</ProjectReference>
</ItemGroup>
<ItemDefinitionGroup Condition="!Exists('DataLoaders\DataLoaderConstants.h')">
<ClCompile>

View File

@ -10,6 +10,9 @@
<Filter Include="DataLoaders">
<UniqueIdentifier>{0184f727-b8aa-4af8-a699-63f1b56e7853}</UniqueIdentifier>
</Filter>
<Filter Include="GraphingCalculator">
<UniqueIdentifier>{cf7dca32-9727-4f98-83c3-1c0ca7dd1e0c}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
@ -71,9 +74,18 @@
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp">
<Filter>DataLoaders</Filter>
</ClCompile>
<ClCompile Include="GraphingCalculator\EquationViewModel.cpp">
<Filter>GraphingCalculator</Filter>
</ClCompile>
<ClCompile Include="GraphingCalculator\GraphingCalculatorViewModel.cpp">
<Filter>GraphingCalculator</Filter>
</ClCompile>
<ClCompile Include="Common\Automation\NarratorAnnouncement.cpp">
<Filter>Common\Automation</Filter>
</ClCompile>
<ClCompile Include="GraphingCalculator\GraphingSettingsViewModel.cpp">
<Filter>GraphingCalculator</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@ -172,6 +184,21 @@
<ClInclude Include="Common\NumberBase.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\EquationViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculatorEnums.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\VariableViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\GraphingSettingsViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="Common\DelegateCommand.h">
<Filter>Common</Filter>
</ClInclude>

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "NarratorAnnouncement.h"
// Declaration of the INarratorAnnouncementHost interface.
// This interface exists to hide the concrete announcement host
// being used. Depending on the version of the OS the app is running on,
// the app may need a host that uses LiveRegionChanged or RaiseNotification.
namespace CalculatorApp::Common::Automation
{
public
interface class INarratorAnnouncementHost
{
public:
// Is the host available on this OS.
bool IsHostAvailable();
// Make a new instance of a concrete host.
INarratorAnnouncementHost ^ MakeHost();
// Make an announcement using the concrete host's preferred method.
void Announce(NarratorAnnouncement ^ announcement);
};
}

View File

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "LiveRegionHost.h"
using namespace CalculatorApp::Common::Automation;
using namespace Windows::UI::Xaml::Automation;
using namespace Windows::UI::Xaml::Automation::Peers;
using namespace Windows::UI::Xaml::Controls;
LiveRegionHost::LiveRegionHost()
: m_host(nullptr)
{
}
bool LiveRegionHost::IsHostAvailable()
{
// LiveRegion is always available.
return true;
}
INarratorAnnouncementHost ^ LiveRegionHost::MakeHost()
{
return ref new LiveRegionHost();
}
void LiveRegionHost::Announce(NarratorAnnouncement ^ announcement)
{
if (m_host == nullptr)
{
m_host = ref new TextBlock();
AutomationProperties::SetLiveSetting(m_host, AutomationLiveSetting::Assertive);
}
AutomationProperties::SetName(m_host, announcement->Announcement);
AutomationPeer ^ peer = FrameworkElementAutomationPeer::FromElement(m_host);
if (peer != nullptr)
{
peer->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged);
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "INarratorAnnouncementHost.h"
// Declaration of the LiveRegionHost class.
// This class announces NarratorAnnouncements using the LiveRegionChanged event.
// This event is unreliable and should be deprecated in favor of the new
// RaiseNotification API in RS3.
namespace CalculatorApp::Common::Automation
{
// This class exists so that the app can run on RS2 and use LiveRegions
// to host notifications on those builds.
// When the app switches to min version RS3, this class can be removed
// and the app will switch to using the Notification API.
// TODO - MSFT 12735088
public
ref class LiveRegionHost sealed : public INarratorAnnouncementHost
{
public:
LiveRegionHost();
virtual bool IsHostAvailable();
virtual INarratorAnnouncementHost ^ MakeHost();
virtual void Announce(NarratorAnnouncement ^ announcement);
private:
Windows::UI::Xaml::UIElement ^ m_host;
};
}

View File

@ -23,6 +23,7 @@ namespace CalculatorApp::Common::Automation
StringReference DisplayCopied(L"DisplayCopied");
StringReference OpenParenthesisCountChanged(L"OpenParenthesisCountChanged");
StringReference NoParenthesisAdded(L"NoParenthesisAdded");
StringReference GraphModeChanged(L"GraphModeChanged");
}
}
@ -140,3 +141,12 @@ NarratorAnnouncement ^ CalculatorAnnouncement::GetNoRightParenthesisAddedAnnounc
AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::ImportantMostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetGraphModeChangedAnnouncement(Platform::String ^ announcement)
{
return ref new NarratorAnnouncement(
announcement,
CalculatorActivityIds::GraphModeChanged,
AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::ImportantMostRecent);
}

View File

@ -66,5 +66,8 @@ public
static NarratorAnnouncement ^ GetOpenParenthesisCountChangedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetNoRightParenthesisAddedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetGraphModeChangedAnnouncement(Platform::String ^ announcement);
};
}

View File

@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "NarratorAnnouncementHostFactory.h"
#include "NotificationHost.h"
#include "LiveRegionHost.h"
using namespace CalculatorApp::Common::Automation;
using namespace std;
INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::s_hostProducer;
vector<INarratorAnnouncementHost ^> NarratorAnnouncementHostFactory::s_hosts;
// This static variable is used only to call the initialization function, to initialize the other static variables.
int NarratorAnnouncementHostFactory::s_init = NarratorAnnouncementHostFactory::Initialize();
int NarratorAnnouncementHostFactory::Initialize()
{
RegisterHosts();
NarratorAnnouncementHostFactory::s_hostProducer = GetHostProducer();
return 0;
}
// For now, there are two type of announcement hosts.
// We'd prefer to use Notification if it's available and fall back to LiveRegion
// if not. The availability of the host depends on the version of the OS the app is running on.
// When the app switches to min version RS3, the LiveRegionHost can be removed and we will always
// use NotificationHost.
// TODO - MSFT 12735088
void NarratorAnnouncementHostFactory::RegisterHosts()
{
// The host that will be used is the first available host,
// therefore, order of hosts is important here.
NarratorAnnouncementHostFactory::s_hosts = { ref new NotificationHost(), ref new LiveRegionHost() };
}
INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::GetHostProducer()
{
for (INarratorAnnouncementHost ^ host : NarratorAnnouncementHostFactory::s_hosts)
{
if (host->IsHostAvailable())
{
return host;
}
}
assert(false && L"No suitable AnnouncementHost was found.");
return nullptr;
}
INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::MakeHost()
{
if (NarratorAnnouncementHostFactory::s_hostProducer == nullptr)
{
assert(false && L"No host producer has been assigned.");
return nullptr;
}
return NarratorAnnouncementHostFactory::s_hostProducer->MakeHost();
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "INarratorAnnouncementHost.h"
// Declaration of the NarratorAnnouncementHostFactory class.
// This class exists to hide the construction of a concrete INarratorAnnouncementHost.
// Depending on the version of the OS the app is running on, the factory will return
// an announcement host appropriate for that version.
namespace CalculatorApp::Common::Automation
{
class NarratorAnnouncementHostFactory
{
public:
static INarratorAnnouncementHost ^ MakeHost();
private:
NarratorAnnouncementHostFactory()
{
}
static int Initialize();
static void RegisterHosts();
static INarratorAnnouncementHost ^ GetHostProducer();
private:
static int s_init;
static INarratorAnnouncementHost ^ s_hostProducer;
static std::vector<INarratorAnnouncementHost ^> s_hosts;
};
}

View File

@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "NotificationHost.h"
using namespace CalculatorApp::Common::Automation;
using namespace Windows::Foundation::Metadata;
using namespace Windows::UI::Xaml::Automation;
using namespace Windows::UI::Xaml::Automation::Peers;
using namespace Windows::UI::Xaml::Controls;
NotificationHost::NotificationHost()
: m_host(nullptr)
{
}
bool NotificationHost::IsHostAvailable()
{
return ApiInformation::IsMethodPresent(L"Windows.UI.Xaml.Automation.Peers.AutomationPeer", L"RaiseNotificationEvent");
}
INarratorAnnouncementHost ^ NotificationHost::MakeHost()
{
return ref new NotificationHost();
}
void NotificationHost::Announce(NarratorAnnouncement ^ announcement)
{
if (m_host == nullptr)
{
m_host = ref new TextBlock();
}
auto peer = FrameworkElementAutomationPeer::FromElement(m_host);
if (peer != nullptr)
{
peer->RaiseNotificationEvent(
GetWindowsNotificationKind(announcement->Kind),
GetWindowsNotificationProcessing(announcement->Processing),
announcement->Announcement,
announcement->ActivityId);
}
}
StandardPeers::AutomationNotificationKind NotificationHost::GetWindowsNotificationKind(CustomPeers::AutomationNotificationKind customKindType)
{
switch (customKindType)
{
case CustomPeers::AutomationNotificationKind::ItemAdded:
return StandardPeers::AutomationNotificationKind::ItemAdded;
case CustomPeers::AutomationNotificationKind::ItemRemoved:
return StandardPeers::AutomationNotificationKind::ItemRemoved;
case CustomPeers::AutomationNotificationKind::ActionCompleted:
return StandardPeers::AutomationNotificationKind::ActionCompleted;
case CustomPeers::AutomationNotificationKind::ActionAborted:
return StandardPeers::AutomationNotificationKind::ActionAborted;
case CustomPeers::AutomationNotificationKind::Other:
return StandardPeers::AutomationNotificationKind::Other;
default:
assert(false && L"Unexpected AutomationNotificationKind");
}
return StandardPeers::AutomationNotificationKind::Other;
}
StandardPeers::AutomationNotificationProcessing
NotificationHost::GetWindowsNotificationProcessing(CustomPeers::AutomationNotificationProcessing customProcessingType)
{
switch (customProcessingType)
{
case CustomPeers::AutomationNotificationProcessing::ImportantAll:
return StandardPeers::AutomationNotificationProcessing::ImportantAll;
case CustomPeers::AutomationNotificationProcessing::ImportantMostRecent:
return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent;
case CustomPeers::AutomationNotificationProcessing::All:
return StandardPeers::AutomationNotificationProcessing::All;
case CustomPeers::AutomationNotificationProcessing::MostRecent:
return StandardPeers::AutomationNotificationProcessing::MostRecent;
case CustomPeers::AutomationNotificationProcessing::CurrentThenMostRecent:
return StandardPeers::AutomationNotificationProcessing::CurrentThenMostRecent;
default:
assert(false && L"Unexpected AutomationNotificationProcessing");
}
return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent;
}

View File

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "INarratorAnnouncementHost.h"
// Declaration of the NotificationHost class.
// This class announces NarratorAnnouncements using the RaiseNotification API
// available in RS3.
namespace CalculatorApp::Common::Automation
{
public
ref class NotificationHost sealed : public INarratorAnnouncementHost
{
public:
NotificationHost();
virtual bool IsHostAvailable();
virtual INarratorAnnouncementHost ^ MakeHost();
virtual void Announce(NarratorAnnouncement ^ announcement);
private:
static Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind
GetWindowsNotificationKind(CalculatorApp::Common::Automation::AutomationNotificationKind customKindType);
static Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing
GetWindowsNotificationProcessing(CalculatorApp::Common::Automation::AutomationNotificationProcessing customProcessingType);
private:
Windows::UI::Xaml::UIElement ^ m_host;
};
}

View File

@ -194,6 +194,14 @@ public
MemoryRecall = (int)CM::Command::CommandRECALL,
MemoryClear = (int)CM::Command::CommandMCLEAR,
BitflipButton = 1000,
FullKeypadButton = 1001
FullKeypadButton = 1001,
// Buttons used in graphing calculator
LessThan,
LessThanOrEqualTo,
GreaterThan,
GreaterThanOrEqualTo,
X,
Y
};
}

View File

@ -5,6 +5,7 @@
#include "NavCategory.h"
#include "AppResourceProvider.h"
#include "Common/LocalizationStringUtil.h"
#include <initializer_list>
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
@ -22,12 +23,6 @@ static constexpr bool SUPPORTS_ALL = true;
static constexpr bool SUPPORTS_NEGATIVE = true;
static constexpr bool POSITIVE_ONLY = false;
// The order of items in this list determines the order of groups in the menu.
static constexpr array<const NavCategoryGroupInitializer, 2> s_categoryGroupManifest = {
NavCategoryGroupInitializer{ CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText" },
NavCategoryGroupInitializer{ CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" }
};
// vvv THESE CONSTANTS SHOULD NEVER CHANGE vvv
static constexpr int STANDARD_ID = 0;
static constexpr int SCIENTIFIC_ID = 1;
@ -46,17 +41,84 @@ static constexpr int DATA_ID = 13;
static constexpr int PRESSURE_ID = 14;
static constexpr int ANGLE_ID = 15;
static constexpr int CURRENCY_ID = 16;
static constexpr int GRAPHING_ID = 17;
// ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^
wchar_t* towchar_t(int number)
{
auto wstr = to_wstring(number);
return _wcsdup(wstr.c_str());
}
extern "C"
{
WINADVAPI LSTATUS APIENTRY RegGetValueW(
_In_ HKEY hkey,
_In_opt_ LPCWSTR lpSubKey,
_In_opt_ LPCWSTR lpValue,
_In_ DWORD dwFlags,
_Out_opt_ LPDWORD pdwType,
_When_(
(dwFlags & 0x7F) == RRF_RT_REG_SZ || (dwFlags & 0x7F) == RRF_RT_REG_EXPAND_SZ || (dwFlags & 0x7F) == (RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ)
|| *pdwType == REG_SZ || *pdwType == REG_EXPAND_SZ,
_Post_z_) _When_((dwFlags & 0x7F) == RRF_RT_REG_MULTI_SZ || *pdwType == REG_MULTI_SZ, _Post_ _NullNull_terminated_)
_Out_writes_bytes_to_opt_(*pcbData, *pcbData) PVOID pvData,
_Inout_opt_ LPDWORD pcbData);
}
bool IsGraphingModeAvailable()
{
static bool supportGraph = Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath");
return supportGraph;
}
Box<bool> ^ _isGraphingModeEnabledCached = nullptr;
bool IsGraphingModeEnabled()
{
if (!IsGraphingModeAvailable())
{
return false;
}
if (_isGraphingModeEnabledCached != nullptr)
{
return _isGraphingModeEnabledCached->Value;
}
DWORD allowGraphingCalculator{ 0 };
DWORD bufferSize{ sizeof(allowGraphingCalculator) };
// Make sure to call RegGetValueW only on Windows 10 1903+
if (RegGetValueW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Calculator",
L"AllowGraphingCalculator",
RRF_RT_REG_DWORD | RRF_RT_REG_BINARY,
nullptr,
reinterpret_cast<LPBYTE>(&allowGraphingCalculator),
&bufferSize)
== ERROR_SUCCESS)
{
_isGraphingModeEnabledCached = allowGraphingCalculator != 0;
}
else
{
_isGraphingModeEnabledCached = true;
}
return _isGraphingModeEnabledCached->Value;
}
// The order of items in this list determines the order of items in the menu.
static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = { NavCategoryInitializer{ ViewMode::Standard,
static const list<NavCategoryInitializer> s_categoryManifest = [] {
auto res = list<NavCategoryInitializer>{ NavCategoryInitializer{ ViewMode::Standard,
STANDARD_ID,
L"Standard",
L"StandardMode",
L"\uE8EF",
CategoryGroupType::Calculator,
MyVirtualKey::Number1,
SUPPORTS_ALL },
L"1",
SUPPORTS_ALL,
true },
NavCategoryInitializer{ ViewMode::Scientific,
SCIENTIFIC_ID,
L"Scientific",
@ -64,23 +126,49 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF196",
CategoryGroupType::Calculator,
MyVirtualKey::Number2,
SUPPORTS_ALL },
NavCategoryInitializer{ ViewMode::Programmer,
L"2",
SUPPORTS_ALL,
true } };
int currentIndex = 3;
bool supportGraphingCalculator = IsGraphingModeAvailable();
if (supportGraphingCalculator)
{
const bool isEnabled = IsGraphingModeEnabled();
res.push_back(NavCategoryInitializer{ ViewMode::Graphing,
GRAPHING_ID,
L"Graphing",
L"GraphingCalculatorMode",
L"\uF770",
CategoryGroupType::Calculator,
MyVirtualKey::Number3,
L"3",
SUPPORTS_ALL,
isEnabled });
++currentIndex;
}
res.insert(
res.end(),
{ NavCategoryInitializer{ ViewMode::Programmer,
PROGRAMMER_ID,
L"Programmer",
L"ProgrammerMode",
L"\uECCE",
CategoryGroupType::Calculator,
MyVirtualKey::Number3,
SUPPORTS_ALL },
supportGraphingCalculator ? MyVirtualKey::Number4 : MyVirtualKey::Number3,
towchar_t(currentIndex++),
SUPPORTS_ALL,
true },
NavCategoryInitializer{ ViewMode::Date,
DATE_ID,
L"Date",
L"DateCalculationMode",
L"\uE787",
CategoryGroupType::Calculator,
MyVirtualKey::Number4,
SUPPORTS_ALL },
supportGraphingCalculator ? MyVirtualKey::Number5 : MyVirtualKey::Number4,
towchar_t(currentIndex++),
SUPPORTS_ALL,
true },
NavCategoryInitializer{ ViewMode::Currency,
CURRENCY_ID,
L"Currency",
@ -88,7 +176,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uEB0D",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Volume,
VOLUME_ID,
L"Volume",
@ -96,7 +186,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF1AA",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Length,
LENGTH_ID,
L"Length",
@ -104,7 +196,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uECC6",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Weight,
WEIGHT_ID,
L"Weight and Mass",
@ -112,7 +206,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF4C1",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Temperature,
TEMPERATURE_ID,
L"Temperature",
@ -120,7 +216,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uE7A3",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE },
nullptr,
SUPPORTS_NEGATIVE,
true },
NavCategoryInitializer{ ViewMode::Energy,
ENERGY_ID,
L"Energy",
@ -128,7 +226,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uECAD",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Area,
AREA_ID,
L"Area",
@ -136,7 +236,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uE809",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Speed,
SPEED_ID,
L"Speed",
@ -144,7 +246,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uEADA",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Time,
TIME_ID,
L"Time",
@ -152,7 +256,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uE917",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Power,
POWER_ID,
L"Power",
@ -160,7 +266,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uE945",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE },
nullptr,
SUPPORTS_NEGATIVE,
true },
NavCategoryInitializer{ ViewMode::Data,
DATA_ID,
L"Data",
@ -168,7 +276,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF20F",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Pressure,
PRESSURE_ID,
L"Pressure",
@ -176,7 +286,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uEC4A",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Angle,
ANGLE_ID,
L"Angle",
@ -184,7 +296,11 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF515",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE } };
nullptr,
SUPPORTS_NEGATIVE,
true } });
return res;
}();
// This function should only be used when storing the mode to app data.
int NavCategory::Serialize(ViewMode mode)
@ -211,6 +327,14 @@ ViewMode NavCategory::Deserialize(Platform::Object ^ obj)
if (iter != s_categoryManifest.end())
{
if (iter->viewMode == ViewMode::Graphing)
{
// check if the user is allowed to use this feature
if (!IsGraphingModeEnabled())
{
return ViewMode::None;
}
}
return iter->viewMode;
}
}
@ -228,9 +352,13 @@ bool NavCategory::IsValidViewMode(ViewMode mode)
bool NavCategory::IsCalculatorViewMode(ViewMode mode)
{
// Historically, Date Calculator is not a Calculator mode
// even though it is in the Calculator category.
return !IsDateCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
// Historically, Calculator modes are Standard, Scientific, and Programmer.
return !IsDateCalculatorViewMode(mode) && !IsGraphingCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
}
bool NavCategory::IsGraphingCalculatorViewMode(ViewMode mode)
{
return mode == ViewMode::Graphing;
}
bool NavCategory::IsDateCalculatorViewMode(ViewMode mode)
@ -389,10 +517,12 @@ NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupIniti
categoryName,
categoryAutomationName,
StringReference(categoryInitializer.glyph),
resProvider->GetResourceString(nameResourceKey + "AccessKey"),
categoryInitializer.accessKey != nullptr ? ref new String(categoryInitializer.accessKey)
: resProvider->GetResourceString(nameResourceKey + "AccessKey"),
groupMode,
categoryInitializer.viewMode,
categoryInitializer.supportsNegative));
categoryInitializer.supportsNegative,
categoryInitializer.isEnabled));
}
}
}
@ -407,29 +537,12 @@ IObservableVector<NavCategoryGroup ^> ^ NavCategoryGroup::CreateMenuOptions()
NavCategoryGroup ^ NavCategoryGroup::CreateCalculatorCategory()
{
return ref new NavCategoryGroup(s_categoryGroupManifest.at(0));
return ref new NavCategoryGroup(
NavCategoryGroupInitializer{ CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText" });
}
NavCategoryGroup ^ NavCategoryGroup::CreateConverterCategory()
{
return ref new NavCategoryGroup(s_categoryGroupManifest.at(1));
}
vector<NavCategoryInitializer> NavCategoryGroup::GetInitializerCategoryGroup(CategoryGroupType groupType)
{
vector<NavCategoryInitializer> initializers{};
copy_if(begin(s_categoryManifest), end(s_categoryManifest), back_inserter(initializers), [groupType](const NavCategoryInitializer& initializer) {
return initializer.groupType == groupType;
});
return initializers;
}
String ^ NavCategoryGroup::GetHeaderResourceKey(CategoryGroupType type)
{
auto iter = find_if(begin(s_categoryGroupManifest), end(s_categoryGroupManifest), [type](const NavCategoryGroupInitializer& initializer) {
return initializer.type == type;
});
return (iter != s_categoryGroupManifest.end()) ? StringReference(iter->headerResourceKey) : nullptr;
return ref new NavCategoryGroup(
NavCategoryGroupInitializer{ CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" });
}

View File

@ -44,7 +44,8 @@ namespace CalculatorApp
Data = 13,
Pressure = 14,
Angle = 15,
Currency = 16
Currency = 16,
Graphing = 17
};
public
@ -66,7 +67,9 @@ namespace CalculatorApp
wchar_t const* glyph,
CategoryGroupType group,
MyVirtualKey vKey,
bool categorySupportsNegative)
wchar_t const* aKey,
bool categorySupportsNegative,
bool enabled)
: viewMode(mode)
, serializationId(id)
, friendlyName(name)
@ -74,7 +77,9 @@ namespace CalculatorApp
, glyph(glyph)
, groupType(group)
, virtualKey(vKey)
, accessKey(aKey)
, supportsNegative(categorySupportsNegative)
, isEnabled(enabled)
{
}
@ -85,7 +90,9 @@ namespace CalculatorApp
const wchar_t* const glyph;
const CategoryGroupType groupType;
const MyVirtualKey virtualKey;
const wchar_t* const accessKey;
const bool supportsNegative;
const bool isEnabled;
};
private
@ -109,45 +116,17 @@ namespace CalculatorApp
{
public:
OBSERVABLE_OBJECT();
PROPERTY_R(Platform::String ^, Name);
PROPERTY_R(Platform::String ^, AutomationName);
PROPERTY_R(Platform::String ^, Glyph);
PROPERTY_R(ViewMode, Mode);
PROPERTY_R(Platform::String ^, AccessKey);
PROPERTY_R(bool, SupportsNegative);
PROPERTY_R(bool, IsEnabled);
property Platform::String
^ Name { Platform::String ^ get() { return m_name; } }
^ AutomationId { Platform::String ^ get() { return m_Mode.ToString(); } }
property Platform::String
^ AutomationName { Platform::String ^ get() { return m_automationName; } }
property Platform::String
^ Glyph { Platform::String ^ get() { return m_glyph; } }
property int Position
{
int get()
{
return m_position;
}
}
property ViewMode Mode
{
ViewMode get()
{
return m_viewMode;
}
}
property Platform::String
^ AutomationId { Platform::String ^ get() { return m_viewMode.ToString(); } }
property Platform::String
^ AccessKey { Platform::String ^ get() { return m_accessKey; } }
property bool SupportsNegative
{
bool get()
{
return m_supportsNegative;
}
}
// For saving/restoring last mode used.
static int Serialize(ViewMode mode);
@ -156,6 +135,7 @@ namespace CalculatorApp
static bool IsValidViewMode(ViewMode mode);
static bool IsCalculatorViewMode(ViewMode mode);
static bool IsGraphingCalculatorViewMode(ViewMode mode);
static bool IsDateCalculatorViewMode(ViewMode mode);
static bool IsConverterViewMode(ViewMode mode);
@ -178,16 +158,17 @@ namespace CalculatorApp
Platform::String ^ accessKey,
Platform::String ^ mode,
ViewMode viewMode,
bool supportsNegative)
: m_name(name)
, m_automationName(automationName)
, m_glyph(glyph)
, m_accessKey(accessKey)
, m_mode(mode)
, m_viewMode(viewMode)
, m_supportsNegative(supportsNegative)
bool supportsNegative,
bool isEnabled)
: m_Name(name)
, m_AutomationName(automationName)
, m_Glyph(glyph)
, m_AccessKey(accessKey)
, m_modeString(mode)
, m_Mode(viewMode)
, m_SupportsNegative(supportsNegative)
, m_IsEnabled(isEnabled)
{
m_position = NavCategory::GetPosition(m_viewMode);
}
static std::vector<MyVirtualKey> GetCategoryAcceleratorKeys();
@ -195,14 +176,7 @@ namespace CalculatorApp
private:
static bool IsModeInCategoryGroup(ViewMode mode, CategoryGroupType groupType);
ViewMode m_viewMode;
Platform::String ^ m_name;
Platform::String ^ m_automationName;
Platform::String ^ m_glyph;
Platform::String ^ m_accessKey;
Platform::String ^ m_mode;
int m_position;
bool m_supportsNegative;
Platform::String ^ m_modeString;
};
[Windows::UI::Xaml::Data::Bindable] public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
@ -216,15 +190,11 @@ namespace CalculatorApp
static Windows::Foundation::Collections::IObservableVector<NavCategoryGroup ^> ^ CreateMenuOptions();
static Platform::String ^ GetHeaderResourceKey(CategoryGroupType type);
internal : static NavCategoryGroup ^ CreateCalculatorCategory();
static NavCategoryGroup ^ CreateConverterCategory();
private:
NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer);
static std::vector<NavCategoryInitializer> GetInitializerCategoryGroup(CategoryGroupType groupType);
};
}
}

View File

@ -14,11 +14,13 @@
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace concurrency;
using namespace Graphing::Renderer;
using namespace Platform;
using namespace std;
using namespace Utils;
using namespace Windows::ApplicationModel::Resources;
using namespace Windows::Storage::Streams;
using namespace Windows::UI;
using namespace Windows::UI::Core;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
@ -196,3 +198,118 @@ task<String ^> Utils::ReadFileFromFolder(IStorageFolder ^ folder, String ^ fileN
String ^ contents = co_await FileIO::ReadTextAsync(file);
co_return contents;
}
bool Utils::AreColorsEqual(const Color& color1, const Color& color2)
{
return ((color1.A == color2.A)
&& (color1.R == color2.R)
&& (color1.G == color2.G)
&& (color1.B == color2.B));
}
String^ Utils::Trim(String^ value)
{
if (!value)
{
return nullptr;
}
wstring trimmed = value->Data();
Trim(trimmed);
return ref new String(trimmed.c_str());
}
void Utils::Trim(wstring& value)
{
TrimFront(value);
TrimBack(value);
}
void Utils::TrimFront(wstring& value)
{
value.erase(value.begin(), find_if(value.cbegin(), value.cend(), [](int ch){
return !isspace(ch);
}));
}
void Utils::TrimBack(wstring& value)
{
value.erase(find_if(value.crbegin(), value.crend(), [](int ch) {
return !isspace(ch);
}).base(), value.end());
}
String^ Utils::EscapeHtmlSpecialCharacters(String^ originalString, shared_ptr<vector<wchar_t>> specialCharacters)
{
// Construct a default special characters if not provided.
if (specialCharacters == nullptr)
{
specialCharacters = make_shared<vector<wchar_t>>();
specialCharacters->push_back(L'&');
specialCharacters->push_back(L'\"');
specialCharacters->push_back(L'\'');
specialCharacters->push_back(L'<');
specialCharacters->push_back(L'>');
}
bool replaceCharacters = false;
const wchar_t* pCh;
String^ replacementString = nullptr;
// First step is scanning the string for special characters.
// If there isn't any special character, we simply return the original string
for (pCh = originalString->Data(); *pCh; pCh++)
{
if (std::find(specialCharacters->begin(), specialCharacters->end(), *pCh) != specialCharacters->end())
{
replaceCharacters = true;
break;
}
}
if (replaceCharacters)
{
// If we indeed find a special character, we step back one character (the special
// character), and we create a new string where we replace those characters one by one
pCh--;
wstringstream buffer;
buffer << wstring(originalString->Data(), pCh);
for (; *pCh; pCh++)
{
switch (*pCh)
{
case L'&':
buffer << L"&amp;";
break;
case L'\"':
buffer << L"&quot;";
break;
case L'\'':
buffer << L"&apos;";
break;
case L'<':
buffer << L"&lt;";
break;
case L'>':
buffer << L"&gt;";
break;
default:
buffer << *pCh;
}
}
replacementString = ref new String(buffer.str().c_str());
}
return replaceCharacters ? replacementString : originalString;
}
bool operator==(const Color& color1, const Color& color2)
{
return equal_to<Color>()(color1, color2);
}
bool operator!=(const Color& color1, const Color& color2)
{
return !(color1 == color2);
}

View File

@ -5,9 +5,13 @@
#include "CalcManager/ExpressionCommandInterface.h"
#include "DelegateCommand.h"
#include "GraphingInterfaces/GraphingEnums.h"
// Utility macros to make Models easier to write
// generates a member variable called m_<n>
#define SINGLE_ARG(...) __VA_ARGS__
#define PROPERTY_R(t, n) \
property t n \
{ \
@ -405,12 +409,18 @@ namespace Utils
Windows::Foundation::DateTime GetUniversalSystemTime();
bool IsDateTimeOlderThan(Windows::Foundation::DateTime dateTime, const long long duration);
concurrency::task<void> WriteFileToFolder(
Windows::Storage::IStorageFolder ^ folder,
Platform::String ^ fileName,
Platform::String ^ contents,
Windows::Storage::CreationCollisionOption collisionOption);
concurrency::task<void> WriteFileToFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName, Platform::String^ contents, Windows::Storage::CreationCollisionOption collisionOption);
concurrency::task<Platform::String^> ReadFileFromFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName);
bool AreColorsEqual(const Windows::UI::Color& color1, const Windows::UI::Color& color2);
Platform::String^ Trim(Platform::String^ value);
void Trim(std::wstring& value);
void TrimFront(std::wstring& value);
void TrimBack(std::wstring& value);
Platform::String ^ EscapeHtmlSpecialCharacters(Platform::String ^ originalString, std::shared_ptr<std::vector<wchar_t>> specialCharacters = nullptr);
}
// This goes into the header to define the property, in the public: section of the class
@ -707,3 +717,18 @@ namespace CalculatorApp
return to;
}
}
// There's no standard definition of equality for Windows::UI::Color structs.
// Define a template specialization for std::equal_to.
template<>
class std::equal_to<Windows::UI::Color>
{
public:
bool operator()(const Windows::UI::Color& color1, const Windows::UI::Color& color2)
{
return Utils::AreColorsEqual(color1, color2);
}
};
bool operator==(const Windows::UI::Color& color1, const Windows::UI::Color& color2);
bool operator!=(const Windows::UI::Color& color1, const Windows::UI::Color& color2);

View File

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once

View File

@ -0,0 +1,302 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "EquationViewModel.h"
#include "CalcViewModel\Common\LocalizationSettings.h"
#include "CalcViewModel\GraphingCalculatorEnums.h"
using namespace CalculatorApp::Common;
using namespace Graphing;
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::UI;
using namespace Windows::UI::Xaml;
using namespace Windows::Foundation::Collections;
using namespace GraphControl;
namespace CalculatorApp::ViewModel
{
GridDisplayItems::GridDisplayItems()
: m_Expression{ "" }
, m_Direction{ "" }
{
}
KeyGraphFeaturesItem::KeyGraphFeaturesItem()
: m_Title{ "" }
, m_DisplayItems{ ref new Vector<String ^>() }
, m_GridItems{ ref new Vector<GridDisplayItems ^>() }
, m_IsText{ false }
{
}
EquationViewModel::EquationViewModel(Equation ^ equation, int functionLabelIndex, Windows::UI::Color color)
: m_AnalysisErrorVisible{ false }
, m_FunctionLabelIndex{ functionLabelIndex }
, m_KeyGraphFeaturesItems{ ref new Vector<KeyGraphFeaturesItem ^>() }
, m_resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView() }
{
if (equation == nullptr)
{
throw ref new InvalidArgumentException(L"Equation cannot be null");
}
GraphEquation = equation;
LineColor = color;
IsLineEnabled = true;
}
void EquationViewModel::PopulateKeyGraphFeatures(KeyGraphFeaturesInfo ^ graphEquation)
{
if (graphEquation->AnalysisError != 0)
{
AnalysisErrorVisible = true;
if (graphEquation->AnalysisError == static_cast<int>(AnalysisErrorType::AnalysisCouldNotBePerformed))
{
AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisCouldNotBePerformed");
}
else if (graphEquation->AnalysisError == static_cast<int>(AnalysisErrorType::AnalysisNotSupported))
{
AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisNotSupported");
}
return;
}
KeyGraphFeaturesItems->Clear();
AddKeyGraphFeature(m_resourceLoader->GetString(L"Domain"), graphEquation->Domain, m_resourceLoader->GetString(L"KGFDomainNone"));
AddKeyGraphFeature(m_resourceLoader->GetString(L"Range"), graphEquation->Range, m_resourceLoader->GetString(L"KGFRangeNone"));
AddKeyGraphFeature(m_resourceLoader->GetString(L"XIntercept"), graphEquation->XIntercept, m_resourceLoader->GetString(L"KGFXInterceptNone"));
AddKeyGraphFeature(m_resourceLoader->GetString(L"YIntercept"), graphEquation->YIntercept, m_resourceLoader->GetString(L"KGFYInterceptNone"));
AddKeyGraphFeature(m_resourceLoader->GetString(L"Minima"), graphEquation->Minima, m_resourceLoader->GetString(L"KGFMinimaNone"));
AddKeyGraphFeature(m_resourceLoader->GetString(L"Maxima"), graphEquation->Maxima, m_resourceLoader->GetString(L"KGFMaximaNone"));
AddKeyGraphFeature(m_resourceLoader->GetString(L"InflectionPoints"), graphEquation->InflectionPoints, m_resourceLoader->GetString(L"KGFInflectionPointsNone"));
AddKeyGraphFeature(
m_resourceLoader->GetString(L"VerticalAsymptotes"), graphEquation->VerticalAsymptotes, m_resourceLoader->GetString(L"KGFVerticalAsymptotesNone"));
AddKeyGraphFeature(
m_resourceLoader->GetString(L"HorizontalAsymptotes"), graphEquation->HorizontalAsymptotes, m_resourceLoader->GetString(L"KGFHorizontalAsymptotesNone"));
AddKeyGraphFeature(
m_resourceLoader->GetString(L"ObliqueAsymptotes"), graphEquation->ObliqueAsymptotes, m_resourceLoader->GetString(L"KGFObliqueAsymptotesNone"));
AddParityKeyGraphFeature(graphEquation);
AddPeriodicityKeyGraphFeature(graphEquation);
AddMonotoncityKeyGraphFeature(graphEquation);
AddTooComplexKeyGraphFeature(graphEquation);
AnalysisErrorVisible = false;
}
void EquationViewModel::AddKeyGraphFeature(String ^ title, String ^ expression, String ^ errorString)
{
KeyGraphFeaturesItem ^ item = ref new KeyGraphFeaturesItem();
item->Title = title;
if (expression != L"")
{
item->DisplayItems->Append(expression);
item->IsText = false;
}
else
{
item->DisplayItems->Append(errorString);
item->IsText = true;
}
KeyGraphFeaturesItems->Append(item);
}
void EquationViewModel::AddKeyGraphFeature(String ^ title, IVector<String ^> ^ expressionVector, String ^ errorString)
{
KeyGraphFeaturesItem ^ item = ref new KeyGraphFeaturesItem();
item->Title = title;
if (expressionVector->Size != 0)
{
for (auto expression : expressionVector)
{
item->DisplayItems->Append(expression);
}
item->IsText = false;
}
else
{
item->DisplayItems->Append(errorString);
item->IsText = true;
}
KeyGraphFeaturesItems->Append(item);
}
void EquationViewModel::AddParityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation)
{
KeyGraphFeaturesItem ^ parityItem = ref new KeyGraphFeaturesItem();
parityItem->Title = m_resourceLoader->GetString(L"Parity");
switch (graphEquation->Parity)
{
case 0:
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown"));
break;
case 1:
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityOdd"));
break;
case 2:
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityEven"));
break;
case 3:
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityNeither"));
break;
default:
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown"));
}
parityItem->IsText = true;
KeyGraphFeaturesItems->Append(parityItem);
}
void EquationViewModel::AddPeriodicityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation)
{
KeyGraphFeaturesItem ^ periodicityItem = ref new KeyGraphFeaturesItem();
periodicityItem->Title = m_resourceLoader->GetString(L"Periodicity");
switch (graphEquation->PeriodicityDirection)
{
case 0:
// Periodicity is not supported or is too complex to calculate.
// Return out of this function without adding periodicity to KeyGraphFeatureItems.
// SetTooComplexFeaturesErrorProperty will set the too complex error when periodicity is supported and unknown
return;
case 1:
if (graphEquation->PeriodicityExpression == L"")
{
periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityUnknown"));
periodicityItem->IsText = true;
}
else
{
periodicityItem->DisplayItems->Append(graphEquation->PeriodicityExpression);
periodicityItem->IsText = false;
}
break;
case 2:
periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityNotPeriodic"));
periodicityItem->IsText = false;
break;
default:
periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityError"));
periodicityItem->IsText = true;
}
KeyGraphFeaturesItems->Append(periodicityItem);
}
void EquationViewModel::AddMonotoncityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation)
{
KeyGraphFeaturesItem ^ monotonicityItem = ref new KeyGraphFeaturesItem();
monotonicityItem->Title = m_resourceLoader->GetString(L"Monotonicity");
if (graphEquation->Monotonicity->Size != 0)
{
for (auto item : graphEquation->Monotonicity)
{
GridDisplayItems ^ gridItem = ref new GridDisplayItems();
gridItem->Expression = item->Key;
auto monotonicityType = item->Value->Data();
switch (*monotonicityType)
{
case '0':
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityUnknown");
break;
case '1':
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityIncreasing");
break;
case '2':
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityDecreasing");
break;
case '3':
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityConstant");
break;
default:
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityError");
break;
}
monotonicityItem->GridItems->Append(gridItem);
}
monotonicityItem->IsText = false;
}
else
{
monotonicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFMonotonicityError"));
monotonicityItem->IsText = true;
}
KeyGraphFeaturesItems->Append(monotonicityItem);
}
void EquationViewModel::AddTooComplexKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation)
{
if (graphEquation->TooComplexFeatures <= 0)
{
return;
}
Platform::String ^ separator = ref new String(LocalizationSettings::GetInstance().GetListSeparator().c_str());
wstring error;
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Domain) == KeyGraphFeaturesFlag::Domain)
{
error.append((m_resourceLoader->GetString(L"Domain") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Range) == KeyGraphFeaturesFlag::Range)
{
error.append((m_resourceLoader->GetString(L"Range") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Zeros) == KeyGraphFeaturesFlag::Zeros)
{
error.append((m_resourceLoader->GetString(L"XIntercept") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::YIntercept) == KeyGraphFeaturesFlag::YIntercept)
{
error.append((m_resourceLoader->GetString(L"YIntercept") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Parity) == KeyGraphFeaturesFlag::Parity)
{
error.append((m_resourceLoader->GetString(L"Parity") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Periodicity) == KeyGraphFeaturesFlag::Periodicity)
{
error.append((m_resourceLoader->GetString(L"Periodicity") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Minima) == KeyGraphFeaturesFlag::Minima)
{
error.append((m_resourceLoader->GetString(L"Minima") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Maxima) == KeyGraphFeaturesFlag::Maxima)
{
error.append((m_resourceLoader->GetString(L"Maxima") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::InflectionPoints) == KeyGraphFeaturesFlag::InflectionPoints)
{
error.append((m_resourceLoader->GetString(L"InflectionPoints") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::VerticalAsymptotes) == KeyGraphFeaturesFlag::VerticalAsymptotes)
{
error.append((m_resourceLoader->GetString(L"VerticalAsymptotes") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::HorizontalAsymptotes) == KeyGraphFeaturesFlag::HorizontalAsymptotes)
{
error.append((m_resourceLoader->GetString(L"HorizontalAsymptotes") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::ObliqueAsymptotes) == KeyGraphFeaturesFlag::ObliqueAsymptotes)
{
error.append((m_resourceLoader->GetString(L"ObliqueAsymptotes") + separator + L" ")->Data());
}
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::MonotoneIntervals) == KeyGraphFeaturesFlag::MonotoneIntervals)
{
error.append((m_resourceLoader->GetString(L"Monotonicity") + separator + L" ")->Data());
}
KeyGraphFeaturesItem ^ tooComplexItem = ref new KeyGraphFeaturesItem();
tooComplexItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFTooComplexFeaturesError"));
tooComplexItem->DisplayItems->Append(ref new String(error.substr(0, (error.length() - (separator->Length() + 1))).c_str()));
tooComplexItem->IsText = true;
KeyGraphFeaturesItems->Append(tooComplexItem);
}
}

View File

@ -0,0 +1,120 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "../Common/Utils.h"
namespace GraphControl
{
ref class Equation;
ref class KeyGraphFeaturesInfo;
}
namespace CalculatorApp::ViewModel
{
public
ref class GridDisplayItems sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
GridDisplayItems();
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_RW(Platform::String ^, Expression);
OBSERVABLE_PROPERTY_RW(Platform::String ^, Direction);
};
public
ref class KeyGraphFeaturesItem sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
KeyGraphFeaturesItem();
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_RW(Platform::String ^, Title);
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector<Platform::String ^> ^, DisplayItems);
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector<GridDisplayItems ^> ^, GridItems);
OBSERVABLE_PROPERTY_RW(bool, IsText);
};
public
ref class EquationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
EquationViewModel(GraphControl::Equation ^ equation, int functionLabelIndex, Windows::UI::Color color);
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_R(GraphControl::Equation ^, GraphEquation);
OBSERVABLE_PROPERTY_R(int, FunctionLabelIndex);
OBSERVABLE_PROPERTY_RW(bool, IsLastItemInList);
property Platform::String ^ Expression
{
Platform::String ^ get()
{
return GraphEquation->Expression;
}
void set(Platform::String ^ value)
{
if (GraphEquation->Expression != value)
{
GraphEquation->Expression = value;
RaisePropertyChanged("Expression");
}
}
}
property Windows::UI::Color LineColor
{
Windows::UI::Color get()
{
return GraphEquation->LineColor;
}
void set(Windows::UI::Color value)
{
if (!Utils::AreColorsEqual(GraphEquation->LineColor, value))
{
GraphEquation->LineColor = value;
RaisePropertyChanged("LineColor");
}
}
}
property bool IsLineEnabled
{
bool get()
{
return GraphEquation->IsLineEnabled;
}
void set(bool value)
{
if (GraphEquation->IsLineEnabled != value)
{
GraphEquation->IsLineEnabled = value;
RaisePropertyChanged("IsLineEnabled");
}
}
}
// Key Graph Features
OBSERVABLE_PROPERTY_R(Platform::String ^, AnalysisErrorString);
OBSERVABLE_PROPERTY_R(bool, AnalysisErrorVisible);
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<CalculatorApp::ViewModel::KeyGraphFeaturesItem ^> ^, KeyGraphFeaturesItems)
void PopulateKeyGraphFeatures(GraphControl::KeyGraphFeaturesInfo ^ info);
private:
void AddKeyGraphFeature(Platform::String ^ title, Platform::String ^ expression, Platform::String ^ errorString);
void AddKeyGraphFeature(
Platform::String ^ title,
Windows::Foundation::Collections::IVector<Platform::String ^> ^ expressionVector,
Platform::String ^ errorString);
void AddParityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info);
void AddPeriodicityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info);
void AddMonotoncityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info);
void AddTooComplexKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info);
Windows::Foundation::Collections::IObservableMap<Platform::String ^, Platform::String ^> ^ m_Monotonicity;
Windows::ApplicationModel::Resources::ResourceLoader ^ m_resourceLoader;
};
}

View File

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "GraphingCalculatorViewModel.h"
using namespace CalculatorApp::ViewModel;
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml::Data;
namespace CalculatorApp::ViewModel
{
GraphingCalculatorViewModel::GraphingCalculatorViewModel()
: m_IsDecimalEnabled{ true }
, m_Equations{ ref new Vector<EquationViewModel ^>() }
, m_Variables{ ref new Vector<VariableViewModel ^>() }
{
}
void GraphingCalculatorViewModel::OnButtonPressed(Object ^ parameter)
{
}
void GraphingCalculatorViewModel::UpdateVariables(IMap<String ^, double> ^ variables)
{
Variables->Clear();
for (auto var : variables)
{
auto variable = ref new VariableViewModel(var->Key, var->Value);
variable->VariableUpdated += ref new EventHandler<VariableChangedEventArgs>([this, variable](Object ^ sender, VariableChangedEventArgs e) {
VariableUpdated(variable, VariableChangedEventArgs{ e.variableName, e.newValue });
});
Variables->Append(variable);
}
}
void GraphingCalculatorViewModel::SetSelectedEquation(EquationViewModel ^ equation)
{
SelectedEquation = equation;
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "../Common/Utils.h"
#include "EquationViewModel.h"
#include "VariableViewModel.h"
namespace CalculatorApp::ViewModel
{
[Windows::UI::Xaml::Data::Bindable] public ref class GraphingCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
GraphingCalculatorViewModel();
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_R(bool, IsDecimalEnabled);
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<EquationViewModel ^> ^, Equations);
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<VariableViewModel ^> ^, Variables);
OBSERVABLE_PROPERTY_R(EquationViewModel ^, SelectedEquation);
COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed);
event Windows::Foundation::EventHandler<VariableChangedEventArgs> ^ VariableUpdated;
void UpdateVariables(Windows::Foundation::Collections::IMap<Platform::String ^, double> ^ variables);
void SetSelectedEquation(EquationViewModel ^ equation);
private:
void OnButtonPressed(Platform::Object ^ parameter);
};
}

View File

@ -0,0 +1,149 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "GraphingSettingsViewModel.h"
#include <CalcManager\NumberFormattingUtils.h>
using namespace CalculatorApp::ViewModel;
using namespace CalcManager::NumberFormattingUtils;
using namespace GraphControl;
using namespace std;
using namespace Platform;
GraphingSettingsViewModel::GraphingSettingsViewModel()
: m_XMinValue(0)
, m_XMaxValue(0)
, m_YMinValue(0)
, m_YMaxValue(0)
, m_XMinError(false)
, m_XMaxError(false)
, m_YMinError(false)
, m_YMaxError(false)
, m_dontUpdateDisplayRange(false)
, m_XIsMinLastChanged(true)
, m_YIsMinLastChanged(true)
{
}
void GraphingSettingsViewModel::SetGrapher(Grapher ^ grapher)
{
if (grapher != nullptr)
{
if (grapher->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Invalid)
{
grapher->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Radians;
}
}
Graph = grapher;
InitRanges();
RaisePropertyChanged(L"TrigUnit");
}
void GraphingSettingsViewModel::InitRanges()
{
double xMin = 0, xMax = 0, yMin = 0, yMax = 0;
if (m_Graph != nullptr)
{
m_Graph->GetDisplayRanges(&xMin, &xMax, &yMin, &yMax);
}
m_dontUpdateDisplayRange = true;
m_XMinValue = xMin;
m_XMaxValue = xMax;
m_YMinValue = yMin;
m_YMaxValue = yMax;
auto valueStr = to_wstring(m_XMinValue);
TrimTrailingZeros(valueStr);
m_XMin = ref new String(valueStr.c_str());
valueStr = to_wstring(m_XMaxValue);
TrimTrailingZeros(valueStr);
m_XMax = ref new String(valueStr.c_str());
valueStr = to_wstring(m_YMinValue);
TrimTrailingZeros(valueStr);
m_YMin = ref new String(valueStr.c_str());
valueStr = to_wstring(m_YMaxValue);
TrimTrailingZeros(valueStr);
m_YMax = ref new String(valueStr.c_str());
m_dontUpdateDisplayRange = false;
}
void GraphingSettingsViewModel::UpdateDisplayRange(bool XValuesModified)
{
if (m_Graph == nullptr || m_dontUpdateDisplayRange || HasError())
{
return;
}
if (m_Graph->ForceProportionalAxes)
{
// If ForceProportionalAxes is set, the graph will try to automatically adjust ranges to remain proportional.
// but without a logic to choose which values can be modified or not.
// To solve this problem, we calculate the new ranges here, taking care to not modify the current axis and
// modifying only the least recently updated value of the other axis.
if (XValuesModified)
{
if (m_YIsMinLastChanged)
{
auto yMaxValue = m_YMinValue + (m_XMaxValue - m_XMinValue) * m_Graph->ActualHeight / m_Graph->ActualWidth;
if (m_YMaxValue != yMaxValue)
{
m_YMaxValue = yMaxValue;
auto valueStr = to_wstring(m_YMaxValue);
TrimTrailingZeros(valueStr);
m_YMax = ref new String(valueStr.c_str());
RaisePropertyChanged("YMax");
}
}
else
{
auto yMinValue = m_YMaxValue - (m_XMaxValue - m_XMinValue) * m_Graph->ActualHeight / m_Graph->ActualWidth;
if (m_YMinValue != yMinValue)
{
m_YMinValue = yMinValue;
auto valueStr = to_wstring(m_YMinValue);
TrimTrailingZeros(valueStr);
m_YMin = ref new String(valueStr.c_str());
RaisePropertyChanged("YMin");
}
}
}
else
{
if (m_XIsMinLastChanged)
{
auto xMaxValue = m_XMinValue + (m_YMaxValue - m_YMinValue) * m_Graph->ActualWidth / m_Graph->ActualHeight;
if (m_XMaxValue != xMaxValue)
{
m_XMaxValue = xMaxValue;
auto valueStr = to_wstring(m_XMaxValue);
TrimTrailingZeros(valueStr);
m_XMax = ref new String(valueStr.c_str());
RaisePropertyChanged("XMax");
}
}
else
{
auto xMinValue = m_XMaxValue - (m_YMaxValue - m_YMinValue) * m_Graph->ActualWidth / m_Graph->ActualHeight;
if (m_XMinValue != xMinValue)
{
m_XMinValue = xMinValue;
auto valueStr = to_wstring(m_XMinValue);
TrimTrailingZeros(valueStr);
m_XMin = ref new String(valueStr.c_str());
RaisePropertyChanged("XMin");
}
}
}
}
m_Graph->SetDisplayRanges(m_XMinValue, m_XMaxValue, m_YMinValue, m_YMaxValue);
}
bool GraphingSettingsViewModel::HasError()
{
return m_XMinError || m_YMinError || m_XMaxError || m_YMaxError || XError || YError;
}

View File

@ -0,0 +1,293 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "../Common/Utils.h"
namespace CalculatorApp::ViewModel
{
#pragma once
[Windows::UI::Xaml::Data::Bindable] public ref class GraphingSettingsViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_R(bool, YMinError);
OBSERVABLE_PROPERTY_R(bool, XMinError);
OBSERVABLE_PROPERTY_R(bool, XMaxError);
OBSERVABLE_PROPERTY_R(bool, YMaxError);
OBSERVABLE_PROPERTY_R(GraphControl::Grapher ^, Graph);
GraphingSettingsViewModel();
property bool XError
{
bool get()
{
return !m_XMinError && !m_XMaxError && m_XMinValue >= m_XMaxValue;
}
}
property bool YError
{
bool get()
{
return !m_YMinError && !m_YMaxError && m_YMinValue >= m_YMaxValue;
}
}
property Platform::String ^ XMin
{
Platform::String ^ get()
{
return m_XMin;
}
void set(Platform::String ^ value)
{
if (m_XMin == value)
{
return;
}
m_XMin = value;
m_XIsMinLastChanged = true;
if (m_Graph != nullptr)
{
try
{
size_t sz;
auto number = std::stod(value->Data(), &sz);
if (value->Length() == sz)
{
m_Graph->XAxisMin = m_XMinValue = number;
XMinError = false;
}
else
{
XMinError = true;
}
}
catch (...)
{
XMinError = true;
}
}
RaisePropertyChanged("XError");
RaisePropertyChanged("XMin");
UpdateDisplayRange(true);
}
}
property Platform::String ^ XMax
{
Platform::String ^ get()
{
return m_XMax;
}
void set(Platform::String ^ value)
{
if (m_XMax == value)
{
return;
}
m_XMax = value;
m_XIsMinLastChanged = false;
if (m_Graph != nullptr)
{
try
{
size_t sz;
auto number = std::stod(value->Data(), &sz);
if (value->Length() == sz)
{
m_Graph->XAxisMax = m_XMaxValue = number;
XMaxError = false;
}
else
{
XMaxError = true;
}
}
catch (...)
{
XMaxError = true;
}
}
RaisePropertyChanged("XError");
RaisePropertyChanged("XMax");
UpdateDisplayRange(true);
}
}
property Platform::String ^ YMin
{
Platform::String ^ get()
{
return m_YMin;
}
void set(Platform::String ^ value)
{
if (m_YMin == value)
{
return;
}
m_YMin = value;
m_YIsMinLastChanged = true;
if (m_Graph != nullptr)
{
try
{
size_t sz;
auto number = std::stod(value->Data(), &sz);
if (value->Length() == sz)
{
m_Graph->YAxisMin = m_YMinValue = number;
YMinError = false;
}
else
{
YMinError = true;
}
}
catch (...)
{
YMinError = true;
}
}
RaisePropertyChanged("YError");
RaisePropertyChanged("YMin");
UpdateDisplayRange(false);
}
}
property Platform::String ^ YMax
{
Platform::String ^ get()
{
return m_YMax;
}
void set(Platform::String ^ value)
{
if (m_YMax == value)
{
return;
}
m_YMax = value;
m_YIsMinLastChanged = false;
if (m_Graph != nullptr)
{
try
{
size_t sz;
auto number = std::stod(value->Data(), &sz);
if (value->Length() == sz)
{
m_Graph->YAxisMax = m_YMaxValue = number;
YMaxError = false;
}
else
{
YMaxError = true;
}
}
catch (...)
{
YMaxError = true;
}
}
RaisePropertyChanged("YError");
RaisePropertyChanged("YMax");
UpdateDisplayRange(false);
}
}
property int TrigUnit
{
int get()
{
return m_Graph == nullptr ? (int)Graphing::EvalTrigUnitMode::Invalid : m_Graph->TrigUnitMode;
}
void set(int value)
{
if (m_Graph == nullptr)
{
return;
}
m_Graph->TrigUnitMode = value;
RaisePropertyChanged(L"TrigUnit");
}
}
property bool TrigModeRadians
{
bool get()
{
return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Radians;
}
void set(bool value)
{
if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Radians)
{
m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Radians;
RaisePropertyChanged(L"TrigModeRadians");
RaisePropertyChanged(L"TrigModeDegrees");
RaisePropertyChanged(L"TrigModeGradians");
}
}
}
property bool TrigModeDegrees
{
bool get()
{
return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Degrees;
}
void set(bool value)
{
if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Degrees)
{
m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Degrees;
RaisePropertyChanged(L"TrigModeDegrees");
RaisePropertyChanged(L"TrigModeRadians");
RaisePropertyChanged(L"TrigModeGradians");
}
}
}
property bool TrigModeGradians
{
bool get()
{
return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Grads;
}
void set(bool value)
{
if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Grads)
{
m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Grads;
RaisePropertyChanged(L"TrigModeGradians");
RaisePropertyChanged(L"TrigModeDegrees");
RaisePropertyChanged(L"TrigModeRadians");
}
}
}
public:
void UpdateDisplayRange(bool XValuesModified);
public:
void SetGrapher(GraphControl::Grapher ^ grapher);
void InitRanges();
bool HasError();
private:
Platform::String ^ m_XMin;
Platform::String ^ m_XMax;
Platform::String ^ m_YMin;
Platform::String ^ m_YMax;
double m_XMinValue;
double m_XMaxValue;
double m_YMinValue;
double m_YMaxValue;
bool m_dontUpdateDisplayRange;
bool m_XIsMinLastChanged;
bool m_YIsMinLastChanged;
};
}

View File

@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "../Common/Utils.h"
#include "EquationViewModel.h"
namespace CalculatorApp::ViewModel
{
public
value struct VariableChangedEventArgs sealed
{
Platform::String ^ variableName;
double newValue;
};
[Windows::UI::Xaml::Data::Bindable] public ref class VariableViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
VariableViewModel(Platform::String ^ name, double value)
: m_Name(name)
, m_Value(value)
, m_SliderSettingsVisible(false)
, m_Min(0.0)
, m_Step(0.1)
, m_Max(2.0)
{
}
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_R(Platform::String ^, Name);
OBSERVABLE_PROPERTY_RW(double, Min);
OBSERVABLE_PROPERTY_RW(double, Step);
OBSERVABLE_PROPERTY_RW(double, Max);
OBSERVABLE_PROPERTY_RW(bool, SliderSettingsVisible);
event Windows::Foundation::EventHandler<VariableChangedEventArgs> ^ VariableUpdated;
property double Value
{
double get()
{
return m_Value;
}
void set(double value)
{
if (value < Min)
{
value = Min;
}
else if (value > Max)
{
value = Max;
}
if (Value != value)
{
m_Value = value;
VariableUpdated(this, VariableChangedEventArgs{ Name, value });
RaisePropertyChanged(L"Value");
}
}
}
private:
double m_Value;
};
}

View File

@ -0,0 +1,28 @@
#pragma once
namespace CalculatorApp
{
enum KeyGraphFeaturesFlag
{
Domain = 1,
Range = 2,
Parity = 4,
Periodicity = 8,
Zeros = 16,
YIntercept = 32,
Minima = 64,
Maxima = 128,
InflectionPoints = 256,
VerticalAsymptotes = 512,
HorizontalAsymptotes = 1024,
ObliqueAsymptotes = 2048,
MonotoneIntervals = 4096
};
enum AnalysisErrorType
{
NoError,
AnalysisCouldNotBePerformed,
AnalysisNotSupported
};
}

View File

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "ViewState.h"
namespace CalculatorApp
{
namespace ViewState
{
Platform::StringReference Snap(L"Snap");
Platform::StringReference DockedView(L"DockedView");
bool IsValidViewState(Platform::String ^ viewState)
{
return viewState->Equals(ViewState::Snap) || viewState->Equals(ViewState::DockedView);
}
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
namespace CalculatorApp
{
namespace ViewState
{
extern Platform::StringReference Snap;
extern Platform::StringReference DockedView;
bool IsValidViewState(Platform::String ^ viewState);
}
}

View File

@ -40,6 +40,7 @@
#include "winrt/Windows.Globalization.DateTimeFormatting.h"
#include "winrt/Windows.System.UserProfile.h"
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.Foundation.Metadata.h"
// The following namespaces exist as a convenience to resolve
// ambiguity for Windows types in the Windows::UI::Xaml::Automation::Peers

View File

@ -21,6 +21,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CalculatorUITests", "Calcul
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CalculatorUITestFramework", "CalculatorUITestFramework\CalculatorUITestFramework.csproj", "{96454213-94AF-457D-9DF9-B14F80E7770F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GraphingImpl", "GraphingImpl\GraphingImpl.vcxproj", "{52E03A58-B378-4F50-8BFB-F659FB85E790}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GraphControl", "GraphControl\GraphControl.vcxproj", "{E727A92B-F149-492C-8117-C039A298719B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@ -137,6 +141,38 @@ Global
{96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x64.Build.0 = Release|Any CPU
{96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x86.ActiveCfg = Release|Any CPU
{96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x86.Build.0 = Release|Any CPU
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM.ActiveCfg = Debug|ARM
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM.Build.0 = Debug|ARM
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM64.ActiveCfg = Debug|ARM64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM64.Build.0 = Debug|ARM64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x64.ActiveCfg = Debug|x64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x64.Build.0 = Debug|x64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x86.ActiveCfg = Debug|Win32
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x86.Build.0 = Debug|Win32
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM.ActiveCfg = Release|ARM
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM.Build.0 = Release|ARM
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM64.ActiveCfg = Release|ARM64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM64.Build.0 = Release|ARM64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x64.ActiveCfg = Release|x64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x64.Build.0 = Release|x64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x86.ActiveCfg = Release|Win32
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x86.Build.0 = Release|Win32
{E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM.ActiveCfg = Debug|ARM
{E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM.Build.0 = Debug|ARM
{E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM64.Build.0 = Debug|ARM64
{E727A92B-F149-492C-8117-C039A298719B}.Debug|x64.ActiveCfg = Debug|x64
{E727A92B-F149-492C-8117-C039A298719B}.Debug|x64.Build.0 = Debug|x64
{E727A92B-F149-492C-8117-C039A298719B}.Debug|x86.ActiveCfg = Debug|Win32
{E727A92B-F149-492C-8117-C039A298719B}.Debug|x86.Build.0 = Debug|Win32
{E727A92B-F149-492C-8117-C039A298719B}.Release|ARM.ActiveCfg = Release|ARM
{E727A92B-F149-492C-8117-C039A298719B}.Release|ARM.Build.0 = Release|ARM
{E727A92B-F149-492C-8117-C039A298719B}.Release|ARM64.ActiveCfg = Release|ARM64
{E727A92B-F149-492C-8117-C039A298719B}.Release|ARM64.Build.0 = Release|ARM64
{E727A92B-F149-492C-8117-C039A298719B}.Release|x64.ActiveCfg = Release|x64
{E727A92B-F149-492C-8117-C039A298719B}.Release|x64.Build.0 = Release|x64
{E727A92B-F149-492C-8117-C039A298719B}.Release|x86.ActiveCfg = Release|Win32
{E727A92B-F149-492C-8117-C039A298719B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -3,7 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="using:CalculatorApp.Controls"
xmlns:common="using:CalculatorApp.Common"
xmlns:converters="using:CalculatorApp.Converters"
xmlns:local="using:CalculatorApp">
<Application.Resources>
@ -28,8 +27,12 @@
Opacity="0.4"
Color="{ThemeResource SystemAltMediumLowColor}"/>
<SolidColorBrush x:Key="AppControlPageTextBaseMediumHighBrush" Color="{StaticResource SystemBaseMediumHighColor}"/>
<RevealBackgroundBrush x:Key="AppControlHoverButtonFaceBrush" Color="#18FFFFFF" FallbackColor="#18FFFFFF"/>
<RevealBackgroundBrush x:Key="AppControlPressedButtonFaceBrush" Color="#30FFFFFF" FallbackColor="#30FFFFFF"/>
<RevealBackgroundBrush x:Key="AppControlHoverButtonFaceBrush"
FallbackColor="#18FFFFFF"
Color="#18FFFFFF"/>
<RevealBackgroundBrush x:Key="AppControlPressedButtonFaceBrush"
FallbackColor="#30FFFFFF"
Color="#30FFFFFF"/>
<SolidColorBrush x:Key="AppControlTransparentAccentColorBrush" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="AppControlPageTextBaseHighColorBrush" Color="{StaticResource SystemBaseHighColor}"/>
<SolidColorBrush x:Key="OperatorPanelScrollButtonBackgroundBrush" Color="{ThemeResource OperatorPanelScrollButtonBackgroundColor}"/>
@ -44,6 +47,7 @@
Opacity="0.9"
Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="AppControlForegroundAccentBrush" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="AppControlPageTextRedColorBrush" Color="Red"/>
<RevealBorderBrush x:Key="AppControlForegroundTransparentRevealBorderBrush"
FallbackColor="Transparent"
@ -62,7 +66,24 @@
FallbackColor="{ThemeResource SystemChromeMediumColor}"
TintColor="{ThemeResource SystemChromeLowColor}"
TintOpacity="0.7"/>
<ApplicationTheme x:Key="CalcApplicationTheme">Dark</ApplicationTheme>
<SolidColorBrush x:Key="AppChromeAcrylicOperatorFlyoutBackgroundBrush" Color="#FF2F2F2F"/>
<SolidColorBrush x:Key="AppControlTransparentButtonBackgroundBrush" Color="Transparent"/>
<SolidColorBrush x:Key="EquationBrush1" Color="#FFE81123"/>
<SolidColorBrush x:Key="EquationBrush2" Color="#FFE3008C"/>
<SolidColorBrush x:Key="EquationBrush3" Color="#FFB31564"/>
<SolidColorBrush x:Key="EquationBrush4" Color="#FF107C10"/>
<SolidColorBrush x:Key="EquationBrush5" Color="#FF00CC6A"/>
<SolidColorBrush x:Key="EquationBrush6" Color="#FF008055"/>
<SolidColorBrush x:Key="EquationBrush7" Color="#FF58595B"/>
<SolidColorBrush x:Key="EquationBrush8" Color="#FF0063B1"/>
<SolidColorBrush x:Key="EquationBrush9" Color="#FF00B7C3"/>
<SolidColorBrush x:Key="EquationBrush10" Color="#FF6600CC"/>
<SolidColorBrush x:Key="EquationBrush11" Color="#FFFFB900"/>
<SolidColorBrush x:Key="EquationBrush12" Color="#FFF7630C"/>
<SolidColorBrush x:Key="EquationBrush13" Color="#FF8E562E"/>
<SolidColorBrush x:Key="EquationBrush14" Color="#FF000000"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Thickness x:Key="HighContrastThicknessTop">0,0,0,0</Thickness>
@ -80,11 +101,16 @@
Opacity="0.4"
Color="{ThemeResource SystemAltMediumLowColor}"/>
<SolidColorBrush x:Key="AppControlPageTextBaseMediumHighBrush" Color="{StaticResource SystemBaseMediumHighColor}"/>
<RevealBackgroundBrush x:Key="AppControlHoverButtonFaceBrush" Color="#17000000" FallbackColor="#17000000"/>
<RevealBackgroundBrush x:Key="AppControlPressedButtonFaceBrush" Color="#30000000" FallbackColor="#30000000"/>
<RevealBackgroundBrush x:Key="AppControlHoverButtonFaceBrush"
FallbackColor="#17000000"
Color="#17000000"/>
<RevealBackgroundBrush x:Key="AppControlPressedButtonFaceBrush"
FallbackColor="#30000000"
Color="#30000000"/>
<SolidColorBrush x:Key="AppControlTransparentAccentColorBrush" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="AppControlPageTextBaseHighColorBrush" Color="{StaticResource SystemBaseHighColor}"/>
<SolidColorBrush x:Key="OperatorPanelScrollButtonBackgroundBrush" Color="{ThemeResource OperatorPanelScrollButtonBackgroundColor}"/>
<SolidColorBrush x:Key="AppControlForegroundAccentBrush" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="AppControlPageTextRedColorBrush" Color="Red"/>
<SolidColorBrush x:Key="AppControlHighlightCalcButtonBrush"
Opacity="0.4"
@ -119,6 +145,22 @@
FallbackColor="{ThemeResource SystemChromeMediumColor}"
TintColor="{ThemeResource SystemChromeLowColor}"
TintOpacity="0.8"/>
<ApplicationTheme x:Key="CalcApplicationTheme">Light</ApplicationTheme>
<SolidColorBrush x:Key="AppControlTransparentButtonBackgroundBrush" Color="Transparent"/>
<SolidColorBrush x:Key="EquationBrush1" Color="#FFE81123"/>
<SolidColorBrush x:Key="EquationBrush2" Color="#FFE3008C"/>
<SolidColorBrush x:Key="EquationBrush3" Color="#FFB31564"/>
<SolidColorBrush x:Key="EquationBrush4" Color="#FF107C10"/>
<SolidColorBrush x:Key="EquationBrush5" Color="#FF00CC6A"/>
<SolidColorBrush x:Key="EquationBrush6" Color="#FF008055"/>
<SolidColorBrush x:Key="EquationBrush7" Color="#FF58595B"/>
<SolidColorBrush x:Key="EquationBrush8" Color="#FF0063B1"/>
<SolidColorBrush x:Key="EquationBrush9" Color="#FF00B7C3"/>
<SolidColorBrush x:Key="EquationBrush10" Color="#FF6600CC"/>
<SolidColorBrush x:Key="EquationBrush11" Color="#FFFFB900"/>
<SolidColorBrush x:Key="EquationBrush12" Color="#FFF7630C"/>
<SolidColorBrush x:Key="EquationBrush13" Color="#FF8E562E"/>
<SolidColorBrush x:Key="EquationBrush14" Color="#FF000000"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Thickness x:Key="HighContrastThicknessTop">0,1,0,0</Thickness>
@ -136,6 +178,7 @@
<SolidColorBrush x:Key="AppControlTransparentAccentColorBrush" Color="Transparent"/>
<SolidColorBrush x:Key="AppControlPageTextBaseHighColorBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>
<SolidColorBrush x:Key="OperatorPanelScrollButtonBackgroundBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
<SolidColorBrush x:Key="AppControlForegroundAccentBrush" Color="{ThemeResource SystemColorButtonTextColor}"/>
<SolidColorBrush x:Key="AppControlPageTextRedColorBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>
<SolidColorBrush x:Key="AppControlForegroundTransparentRevealBorderBrush" Color="{ThemeResource SystemColorButtonTextColor}"/>
<SolidColorBrush x:Key="AppControlHighlightAccentRevealBackgroundBrush" Color="{ThemeResource SystemColorHighlightColor}"/>
@ -146,6 +189,12 @@
<SolidColorBrush x:Key="AppControlHighlightCalcButtonBrush" Color="{ThemeResource SystemColorWindowColor}"/>
<SolidColorBrush x:Key="AppControlHighlightCalcButtonToggledBrush" Color="{ThemeResource SystemColorHighlightColor}"/>
<SolidColorBrush x:Key="AppControlHighlightCalcButtonHoverBrush" Color="{ThemeResource SystemColorHighlightColor}"/>
<SolidColorBrush x:Key="AppControlTransparentButtonBackgroundBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
<ApplicationTheme x:Key="CalcApplicationTheme">Dark</ApplicationTheme>
<SolidColorBrush x:Key="EquationBrush1" Color="{ThemeResource SystemColorGrayTextColor}"/>
<SolidColorBrush x:Key="EquationBrush2" Color="{ThemeResource SystemColorHighlightColor}"/>
<SolidColorBrush x:Key="EquationBrush3" Color="{ThemeResource SystemColorHotlightColor}"/>
<SolidColorBrush x:Key="EquationBrush4" Color="{ThemeResource SystemColorWindowTextColor}"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
@ -251,7 +300,6 @@
<VisualState x:Name="KeyBoardEntry"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
@ -394,7 +442,6 @@
<VisualState x:Name="KeyBoardEntry"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
@ -562,6 +609,30 @@
</Setter>
</Style>
<Style x:Key="OperatorPanelButtonSmallStyle"
BasedOn="{StaticResource OperatorPanelButtonStyle}"
TargetType="Controls:OperatorPanelButton">
<Setter Property="FontSize" Value="{StaticResource OperatorPanelFontSizeSmall}"/>
<Setter Property="GlyphFontSize" Value="{StaticResource OperatorPanelGlyphFontSizeSmall}"/>
<Setter Property="ChevronFontSize" Value="{StaticResource OperatorPanelChevronFontSizeSmall}"/>
</Style>
<Style x:Key="OperatorPanelButtonMediumStyle"
BasedOn="{StaticResource OperatorPanelButtonStyle}"
TargetType="Controls:OperatorPanelButton">
<Setter Property="FontSize" Value="{StaticResource OperatorPanelFontSizeMedium}"/>
<Setter Property="GlyphFontSize" Value="{StaticResource OperatorPanelGlyphFontSizeMedium}"/>
<Setter Property="ChevronFontSize" Value="{StaticResource OperatorPanelChevronFontSizeMedium}"/>
</Style>
<Style x:Key="OperatorPanelButtonLargeStyle"
BasedOn="{StaticResource OperatorPanelButtonStyle}"
TargetType="Controls:OperatorPanelButton">
<Setter Property="FontSize" Value="{StaticResource OperatorPanelFontSizeLarge}"/>
<Setter Property="GlyphFontSize" Value="{StaticResource OperatorPanelGlyphFontSizeLarge}"/>
<Setter Property="ChevronFontSize" Value="{StaticResource OperatorPanelChevronFontSizeLarge}"/>
</Style>
<Style x:Key="OperatorPanelButtonStyle" TargetType="Controls:OperatorPanelButton">
<Setter Property="FontSize" Value="{StaticResource CaptionFontSize}"/>
<Setter Property="GlyphFontSize" Value="{StaticResource CaptionFontSize}"/>
@ -643,7 +714,6 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
@ -663,8 +733,7 @@
FontFamily="{StaticResource CalculatorFontFamily}"
FontSize="{TemplateBinding GlyphFontSize}"
Glyph="{TemplateBinding Glyph}"/>
<TextBlock x:Name="trigTextBlock"
VerticalAlignment="Center"
<TextBlock VerticalAlignment="Center"
FontSize="{TemplateBinding FontSize}"
Text="{TemplateBinding Text}"/>
<FontIcon Margin="8,4,0,0"
@ -692,6 +761,7 @@
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="FontSize" Value="{StaticResource CaptionFontSize}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
@ -721,7 +791,6 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
@ -731,7 +800,7 @@
BorderBrush="{ThemeResource AppControlForegroundTransparentRevealBorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
FontSize="{TemplateBinding FontSize}"
FontWeight="SemiBold"
FontWeight="{TemplateBinding FontWeight}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
@ -779,7 +848,6 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
@ -885,7 +953,6 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="SelectionBorder" BorderThickness="0,0,0,2"/>
<ContentPresenter x:Name="ContentPresenter"
@ -986,7 +1053,6 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
@ -1042,7 +1108,6 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
@ -1223,7 +1288,6 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
@ -1300,7 +1364,6 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
@ -1543,6 +1606,162 @@
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MathRichEditBoxStyle" TargetType="Controls:MathRichEditBox">
<Setter Property="Foreground" Value="{ThemeResource TextControlForeground}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ContentLinkForegroundColor" Value="{ThemeResource ContentLinkForegroundColor}"/>
<Setter Property="ContentLinkBackgroundColor" Value="{ThemeResource ContentLinkBackgroundColor}"/>
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextControlSelectionHighlightColor}"/>
<Setter Property="BorderBrush" Value="{ThemeResource TextControlBorderBrush}"/>
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}"/>
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/>
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False"/>
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
<Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}"/>
<Setter Property="ContextFlyout" Value="{StaticResource TextControlCommandBarContextFlyout}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Controls:MathRichEditBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlHeaderForegroundDisabled}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundDisabled}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushDisabled}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlPlaceholderForegroundDisabled}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushPointerOver}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlPlaceholderForegroundPointerOver}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundPointerOver}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlPlaceholderForegroundFocused}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushFocused}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundFocused}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="HeaderContentPresenter"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="4"
Margin="{ThemeResource RichEditBoxTopHeaderMargin}"
VerticalAlignment="Top"
Foreground="{ThemeResource TextControlHeaderForeground}"
FontWeight="Normal"
x:DeferLoadStrategy="Lazy"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
TextWrapping="Wrap"
Visibility="Collapsed"/>
<Border x:Name="BorderElement"
Grid.Row="1"
Grid.RowSpan="1"
Grid.Column="0"
Grid.ColumnSpan="4"
MinWidth="32"
MinHeight="16"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Control.IsTemplateFocusTarget="True"
CornerRadius="{TemplateBinding CornerRadius}"/>
<ScrollViewer x:Name="ContentElement"
Grid.Row="1"
Grid.Column="0"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsTabStop="False"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
ZoomMode="Disabled"/>
<TextBlock x:Name="PlaceholderTextContentPresenter"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="4"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
Foreground="{ThemeResource TextControlPlaceholderForeground}"
IsHitTestVisible="False"
Text="{TemplateBinding PlaceholderText}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}"/>
<ContentPresenter x:Name="DescriptionPresenter"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="4"
Foreground="{ThemeResource SystemControlDescriptionTextForegroundBrush}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding Description}"
x:Load="False"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

Binary file not shown.

View File

@ -130,6 +130,9 @@
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup>
<Import Project="$(SolutionDir)\GraphControl\GraphingImplOverrides.props" Condition="Exists('$(SolutionDir)\GraphControl\GraphingImplOverrides.props')" />
</ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<AdditionalOptions>/bigobj /await /std:c++17 /utf-8</AdditionalOptions>
@ -222,6 +225,9 @@
<PropertyGroup>
<AppVersion Condition="'$(AppVersion)' == ''">0.0.0.0</AppVersion>
</PropertyGroup>
<PropertyGroup>
<GenerateProjectSpecificOutputFolder>true</GenerateProjectSpecificOutputFolder>
</PropertyGroup>
<ItemGroup>
<ResourceCompile Include="Calculator.rc" PreprocessorDefinitions="%(PreprocessorDefinitions);APP_VERSION_MAJOR=$(AppVersion.Split(`.`)[0]);APP_VERSION_MINOR=$(AppVersion.Split(`.`)[1]);APP_VERSION_BUILD=$(AppVersion.Split(`.`)[2]);APP_VERSION_REVISION=$(AppVersion.Split(`.`)[3])" />
</ItemGroup>
@ -234,6 +240,7 @@
<ClInclude Include="Common\ValidatingConverters.h" />
<ClInclude Include="Common\ViewState.h" />
<ClInclude Include="Controls\CalculationResultAutomationPeer.h" />
<ClInclude Include="Controls\MathRichEditBox.h" />
<ClInclude Include="Controls\OverflowTextBlockAutomationPeer.h" />
<ClInclude Include="Common\AlwaysSelectedCollectionView.h" />
<ClInclude Include="Controls\CalculationResult.h" />
@ -251,10 +258,16 @@
<ClInclude Include="Controls\HorizontalNoOverflowStackPanel.h" />
<ClInclude Include="Controls\OperatorPanelListView.h" />
<ClInclude Include="Controls\OperatorPanelButton.h" />
<ClInclude Include="Controls\EquationTextBox.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="App.xaml.h">
<DependentUpon>App.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="EquationStylePanelControl.xaml.h">
<DependentUpon>EquationStylePanelControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="TemplateSelectors\KeyGraphFeaturesTemplateSelector.h" />
<ClInclude Include="Utils\VisualTree.h" />
<ClInclude Include="Views\Calculator.xaml.h">
<DependentUpon>Views\Calculator.xaml</DependentUpon>
</ClInclude>
@ -279,6 +292,21 @@
<ClInclude Include="Views\CalculatorStandardOperators.xaml.h">
<DependentUpon>Views\CalculatorStandardOperators.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Views\GraphingCalculator\EquationInputArea.xaml.h">
<DependentUpon>Views\GraphingCalculator\EquationInputArea.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Views\GraphingCalculator\GraphingCalculator.xaml.h">
<DependentUpon>Views\GraphingCalculator\GraphingCalculator.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Views\GraphingCalculator\GraphingSettings.xaml.h">
<DependentUpon>Views\GraphingCalculator\GraphingSettings.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml.h">
<DependentUpon>Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Views\GraphingCalculator\GraphingNumPad.xaml.h">
<DependentUpon>Views\GraphingCalculator\GraphingNumPad.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Views\HistoryList.xaml.h">
<DependentUpon>Views\HistoryList.xaml</DependentUpon>
</ClInclude>
@ -318,6 +346,9 @@
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="AboutFlyout.xaml" />
<Page Include="EquationStylePanelControl.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="Views\Calculator.xaml">
<SubType>Designer</SubType>
</Page>
@ -335,6 +366,11 @@
</Page>
<Page Include="Views\CalculatorStandardOperators.xaml" />
<Page Include="Views\DelighterUnitStyles.xaml" />
<Page Include="Views\GraphingCalculator\EquationInputArea.xaml" />
<Page Include="Views\GraphingCalculator\GraphingCalculator.xaml" />
<Page Include="Views\GraphingCalculator\GraphingSettings.xaml" />
<Page Include="Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml" />
<Page Include="Views\GraphingCalculator\GraphingNumPad.xaml" />
<Page Include="Views\HistoryList.xaml" />
<Page Include="Views\MainPage.xaml" />
<Page Include="Views\Memory.xaml" />
@ -368,6 +404,7 @@
<ClCompile Include="Common\KeyboardShortcutManager.cpp" />
<ClCompile Include="Common\ViewState.cpp" />
<ClCompile Include="Controls\CalculationResultAutomationPeer.cpp" />
<ClCompile Include="Controls\MathRichEditBox.cpp" />
<ClCompile Include="Controls\OverflowTextBlockAutomationPeer.cpp" />
<ClCompile Include="Controls\CalculationResult.cpp" />
<ClCompile Include="Controls\CalculatorButton.cpp" />
@ -384,6 +421,7 @@
<ClCompile Include="Controls\HorizontalNoOverflowStackPanel.cpp" />
<ClCompile Include="Controls\OperatorPanelListView.cpp" />
<ClCompile Include="Controls\OperatorPanelButton.cpp" />
<ClCompile Include="Controls\EquationTextBox.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
@ -394,6 +432,11 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="EquationStylePanelControl.xaml.cpp">
<DependentUpon>EquationStylePanelControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TemplateSelectors\KeyGraphFeaturesTemplateSelector.cpp" />
<ClCompile Include="Utils\VisualTree.cpp" />
<ClCompile Include="Views\Calculator.xaml.cpp">
<DependentUpon>Views\Calculator.xaml</DependentUpon>
</ClCompile>
@ -418,6 +461,21 @@
<ClCompile Include="Views\CalculatorStandardOperators.xaml.cpp">
<DependentUpon>Views\CalculatorStandardOperators.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Views\GraphingCalculator\EquationInputArea.xaml.cpp">
<DependentUpon>Views\GraphingCalculator\EquationInputArea.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Views\GraphingCalculator\GraphingCalculator.xaml.cpp">
<DependentUpon>Views\GraphingCalculator\GraphingCalculator.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Views\GraphingCalculator\GraphingSettings.xaml.cpp">
<DependentUpon>Views\GraphingCalculator\GraphingSettings.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml.cpp">
<DependentUpon>Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Views\GraphingCalculator\GraphingNumPad.xaml.cpp">
<DependentUpon>Views\GraphingCalculator\GraphingNumPad.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Views\HistoryList.xaml.cpp">
<DependentUpon>Views\HistoryList.xaml</DependentUpon>
</ClCompile>
@ -811,6 +869,9 @@
<ProjectReference Include="..\CalcViewModel\CalcViewModel.vcxproj">
<Project>{90e9761d-9262-4773-942d-caeae75d7140}</Project>
</ProjectReference>
<ProjectReference Include="..\GraphControl\GraphControl.vcxproj">
<Project>{e727a92b-f149-492c-8117-c039a298719b}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<!-- Override MSBuild target to workaround VS bug with building using framework packages -->

View File

@ -218,6 +218,12 @@
<Filter Include="Views\StateTriggers">
<UniqueIdentifier>{0120c344-0bc0-4a1d-b82c-df7945f46189}</UniqueIdentifier>
</Filter>
<Filter Include="Views\GraphingCalculator">
<UniqueIdentifier>{e23e2a6e-491b-4200-9bf7-d355a1ee695b}</UniqueIdentifier>
</Filter>
<Filter Include="Utils">
<UniqueIdentifier>{b491a249-26b8-4814-9f50-2c3a57018c56}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml" />
@ -305,6 +311,19 @@
<ClCompile Include="Controls\OperatorPanelButton.cpp">
<Filter>Controls</Filter>
</ClCompile>
<ClCompile Include="Views\GraphingCalculator\EquationInputArea.xaml.cpp" />
<ClCompile Include="Views\GraphingCalculator\GraphingCalculator.xaml.cpp" />
<ClCompile Include="Controls\EquationTextBox.cpp">
<Filter>Controls</Filter>
</ClCompile>
<ClCompile Include="Controls\MathRichEditBox.cpp" />
<ClCompile Include="Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml.cpp">
<Filter>Views\GraphingCalculator</Filter>
</ClCompile>
<ClCompile Include="TemplateSelectors\KeyGraphFeaturesTemplateSelector.cpp" />
<ClCompile Include="Utils\VisualTree.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="Common\KeyboardShortcutManager.cpp">
<Filter>Common</Filter>
</ClCompile>
@ -396,6 +415,19 @@
<ClInclude Include="Controls\OperatorPanelButton.h">
<Filter>Controls</Filter>
</ClInclude>
<ClInclude Include="Views\GraphingCalculator\EquationInputArea.xaml.h" />
<ClInclude Include="Views\GraphingCalculator\GraphingCalculator.xaml.h" />
<ClInclude Include="Controls\EquationTextBox.h">
<Filter>Controls</Filter>
</ClInclude>
<ClInclude Include="Controls\MathRichEditBox.h" />
<ClInclude Include="Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml.h">
<Filter>Views\GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="TemplateSelectors\KeyGraphFeaturesTemplateSelector.h" />
<ClInclude Include="Utils\VisualTree.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="Common\KeyboardShortcutManager.h">
<Filter>Common</Filter>
</ClInclude>
@ -470,6 +502,24 @@
<Page Include="Views\TitleBar.xaml">
<Filter>Views</Filter>
</Page>
<Page Include="Views\GraphingCalculator\EquationInputArea.xaml">
<Filter>Views\GraphingCalculator</Filter>
</Page>
<Page Include="Views\GraphingCalculator\GraphingCalculator.xaml">
<Filter>Views\GraphingCalculator</Filter>
</Page>
<Page Include="Views\TitleBar.xaml">
<Filter>Views</Filter>
</Page>
<Page Include="Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml">
<Filter>Views\GraphingCalculator</Filter>
</Page>
<Page Include="Views\GraphingCalculator\GraphingSettings.xaml">
<Filter>Views\GraphingCalculator</Filter>
</Page>
<Page Include="Views\GraphingCalculator\GraphingNumPad.xaml">
<Filter>Views\GraphingCalculator</Filter>
</Page>
<Page Include="$(MSBuildThisFileDirectory)DensityStyles\Compact.xaml" />
</ItemGroup>
<ItemGroup>
@ -1516,4 +1566,8 @@
<ItemGroup>
<ResourceCompile Include="Calculator.rc" />
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="$(GraphingImplDll)" />
<CopyFileToFolders Include="$(GraphingEngineDll)" />
</ItemGroup>
</Project>

View File

@ -10,6 +10,7 @@
using namespace Concurrency;
using namespace Platform;
using namespace std;
using namespace std::chrono;
using namespace Windows::ApplicationModel::Resources;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
@ -43,10 +44,15 @@ static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlS
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyInverseChordsForButtons;
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlInverseChordsForButtons;
static multimap<int, bool> s_ShiftKeyPressed;
static multimap<int, bool> s_ControlKeyPressed;
static multimap<int, bool> s_ShiftButtonChecked;
static multimap<int, bool> s_IsDropDownOpen;
static map<int, bool> s_ShiftKeyPressed;
static map<int, bool> s_ControlKeyPressed;
static map<int, bool> s_ShiftButtonChecked;
static map<int, bool> s_IsDropDownOpen;
static map<int, bool> s_ignoreNextEscape;
static map<int, bool> s_keepIgnoringEscape;
static map<int, bool> s_fHonorShortcuts;
static map<int, Flyout ^> s_AboutFlyout;
static reader_writer_lock s_keyboardShortcutMapLock;
@ -157,12 +163,6 @@ namespace CalculatorApp
}
}
static multimap<int, bool> s_ignoreNextEscape;
static multimap<int, bool> s_keepIgnoringEscape;
static multimap<int, bool> s_fHonorShortcuts;
static multimap<int, bool> s_fHandledEnter;
static multimap<int, Flyout ^> s_AboutFlyout;
void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce)
{
// Writer lock for the static maps
@ -172,14 +172,12 @@ void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce)
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
{
s_ignoreNextEscape.erase(viewId);
s_ignoreNextEscape.insert(std::make_pair(viewId, true));
s_ignoreNextEscape[viewId] = true;
}
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
{
s_keepIgnoringEscape.erase(viewId);
s_keepIgnoringEscape.insert(std::make_pair(viewId, !onlyOnce));
s_keepIgnoringEscape[viewId] = !onlyOnce;
}
}
@ -192,14 +190,12 @@ void KeyboardShortcutManager::HonorEscape()
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
{
s_ignoreNextEscape.erase(viewId);
s_ignoreNextEscape.insert(std::make_pair(viewId, false));
s_ignoreNextEscape[viewId] = false;
}
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
{
s_keepIgnoringEscape.erase(viewId);
s_keepIgnoringEscape.insert(std::make_pair(viewId, false));
s_keepIgnoringEscape[viewId] = false;
}
}
@ -430,7 +426,6 @@ void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow ^ sender, Ch
{
wchar_t character = static_cast<wchar_t>(args->KeyCode);
auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character);
RunFirstEnabledButtonCommand(buttons);
LightUpButtons(buttons);
@ -474,8 +469,8 @@ const std::multimap<MyVirtualKey, WeakReference>& GetCurrentKeyDictionary(MyVirt
}
else
{
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
auto iterViewMap = s_VirtualKeyInverseChordsForButtons.find(viewId);
if (iterViewMap != s_VirtualKeyInverseChordsForButtons.end())
{
for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator)
{
@ -558,8 +553,7 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow ^ sender, KeyEventArgs
if (currControlKeyPressed != s_ControlKeyPressed.end())
{
s_ControlKeyPressed.erase(viewId);
s_ControlKeyPressed.insert(std::make_pair(viewId, true));
s_ControlKeyPressed[viewId] = true;
}
return;
}
@ -572,26 +566,24 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow ^ sender, KeyEventArgs
if (currShiftKeyPressed != s_ShiftKeyPressed.end())
{
s_ShiftKeyPressed.erase(viewId);
s_ShiftKeyPressed.insert(std::make_pair(viewId, true));
s_ShiftKeyPressed[viewId] = true;
}
return;
}
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key));
auto buttons = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId);
if (currentHonorShortcuts != s_fHonorShortcuts.end())
{
if (currentHonorShortcuts->second)
{
const auto myVirtualKey = static_cast<MyVirtualKey>(key);
const auto& lookupMap = GetCurrentKeyDictionary(myVirtualKey);
auto buttons = lookupMap.equal_range(myVirtualKey);
RunFirstEnabledButtonCommand(buttons);
// Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V
// is pressed. When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open. Ctrl+Insert is
// equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V
auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId);
if (currentIsDropDownOpen != s_IsDropDownOpen.end() && !currentIsDropDownOpen->second)
{
// Do not Light Up Buttons when Ctrl+C, Ctrl+V, Ctrl+Insert or Shift+Insert is pressed
@ -620,8 +612,7 @@ void KeyboardShortcutManager::OnKeyUpHandler(CoreWindow ^ sender, KeyEventArgs ^
if (currentShiftKeyPressed != s_ShiftKeyPressed.end())
{
s_ShiftKeyPressed.erase(viewId);
s_ShiftKeyPressed.insert(std::make_pair(viewId, false));
s_ShiftKeyPressed[viewId] = false;
}
}
else if (key == VirtualKey::Control)
@ -633,8 +624,7 @@ void KeyboardShortcutManager::OnKeyUpHandler(CoreWindow ^ sender, KeyEventArgs ^
if (currControlKeyPressed != s_ControlKeyPressed.end())
{
s_ControlKeyPressed.erase(viewId);
s_ControlKeyPressed.insert(std::make_pair(viewId, false));
s_ControlKeyPressed[viewId] = false;
}
}
}
@ -712,8 +702,7 @@ void KeyboardShortcutManager::ShiftButtonChecked(bool checked)
if (s_ShiftButtonChecked.find(viewId) != s_ShiftButtonChecked.end())
{
s_ShiftButtonChecked.erase(viewId);
s_ShiftButtonChecked.insert(std::make_pair(viewId, checked));
s_ShiftButtonChecked[viewId] = checked;
}
}
@ -723,8 +712,7 @@ void KeyboardShortcutManager::UpdateDropDownState(bool isOpen)
if (s_IsDropDownOpen.find(viewId) != s_IsDropDownOpen.end())
{
s_IsDropDownOpen.erase(viewId);
s_IsDropDownOpen.insert(std::make_pair(viewId, isOpen));
s_IsDropDownOpen[viewId] = isOpen;
}
}
@ -734,8 +722,7 @@ void KeyboardShortcutManager::UpdateDropDownState(Flyout ^ aboutPageFlyout)
if (s_AboutFlyout.find(viewId) != s_AboutFlyout.end())
{
s_AboutFlyout.erase(viewId);
s_AboutFlyout.insert(std::make_pair(viewId, aboutPageFlyout));
s_AboutFlyout[viewId] = aboutPageFlyout;
}
}
@ -748,19 +735,7 @@ void KeyboardShortcutManager::HonorShortcuts(bool allow)
if (s_fHonorShortcuts.find(viewId) != s_fHonorShortcuts.end())
{
s_fHonorShortcuts.erase(viewId);
s_fHonorShortcuts.insert(std::make_pair(viewId, allow));
}
}
void KeyboardShortcutManager::HandledEnter(bool ishandled)
{
int viewId = Utils::GetWindowId();
if (s_fHandledEnter.find(viewId) != s_fHandledEnter.end())
{
s_fHandledEnter.erase(viewId);
s_fHandledEnter.insert(std::make_pair(viewId, ishandled));
s_fHonorShortcuts[viewId] = allow;
}
}
@ -812,15 +787,14 @@ void KeyboardShortcutManager::RegisterNewAppViewId()
s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
}
s_ShiftKeyPressed.insert(std::make_pair(appViewId, false));
s_ControlKeyPressed.insert(std::make_pair(appViewId, false));
s_ShiftButtonChecked.insert(std::make_pair(appViewId, false));
s_IsDropDownOpen.insert(std::make_pair(appViewId, false));
s_ignoreNextEscape.insert(std::make_pair(appViewId, false));
s_keepIgnoringEscape.insert(std::make_pair(appViewId, false));
s_fHonorShortcuts.insert(std::make_pair(appViewId, true));
s_fHandledEnter.insert(std::make_pair(appViewId, true));
s_AboutFlyout.insert(std::make_pair(appViewId, nullptr));
s_ShiftKeyPressed[appViewId] = false;
s_ControlKeyPressed[appViewId] = false;
s_ShiftButtonChecked[appViewId] = false;
s_IsDropDownOpen[appViewId] = false;
s_ignoreNextEscape[appViewId] = false;
s_keepIgnoringEscape[appViewId] = false;
s_fHonorShortcuts[appViewId] = true;
s_AboutFlyout[appViewId] = nullptr;
}
void KeyboardShortcutManager::OnWindowClosed(int viewId)
@ -845,6 +819,5 @@ void KeyboardShortcutManager::OnWindowClosed(int viewId)
s_ignoreNextEscape.erase(viewId);
s_keepIgnoringEscape.erase(viewId);
s_fHonorShortcuts.erase(viewId);
s_fHandledEnter.erase(viewId);
s_AboutFlyout.erase(viewId);
}

View File

@ -43,7 +43,6 @@ namespace CalculatorApp
static void IgnoreEscape(bool onlyOnce);
static void HonorEscape();
static void HonorShortcuts(bool allow);
static void HandledEnter(bool ishandled);
static void UpdateDropDownState(bool);
static void ShiftButtonChecked(bool checked);
static void UpdateDropDownState(Windows::UI::Xaml::Controls::Flyout ^ aboutPageFlyout);

View File

@ -0,0 +1,383 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "CalcViewModel/Common/AppResourceProvider.h"
#include "CalcViewModel/Common/LocalizationStringUtil.h"
#include "EquationTextBox.h"
using namespace std;
using namespace Platform;
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace CalculatorApp::Controls;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::ApplicationModel;
using namespace Windows::UI::Text;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Automation;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Controls::Primitives;
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationColor);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, ColorChooserFlyout);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationButtonContentIndex);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, HasError);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsAddEquationMode);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, MathEquation);
EquationTextBox::EquationTextBox()
{
}
void EquationTextBox::OnApplyTemplate()
{
m_equationButton = dynamic_cast<ToggleButton ^>(GetTemplateChild("EquationButton"));
m_richEditBox = dynamic_cast<MathRichEditBox ^>(GetTemplateChild("MathRichEditBox"));
m_deleteButton = dynamic_cast<Button ^>(GetTemplateChild("DeleteButton"));
m_removeButton = dynamic_cast<Button ^>(GetTemplateChild("RemoveButton"));
m_functionButton = dynamic_cast<Button ^>(GetTemplateChild("FunctionButton"));
m_colorChooserButton = dynamic_cast<ToggleButton ^>(GetTemplateChild("ColorChooserButton"));
m_richEditContextMenu = dynamic_cast<MenuFlyout ^>(GetTemplateChild("MathRichEditContextMenu"));
m_kgfEquationMenuItem = dynamic_cast<MenuFlyoutItem ^>(GetTemplateChild("FunctionAnalysisMenuItem"));
m_removeMenuItem = dynamic_cast<MenuFlyoutItem ^>(GetTemplateChild("RemoveFunctionMenuItem"));
m_colorChooserMenuItem = dynamic_cast<MenuFlyoutItem ^>(GetTemplateChild("ChangeFunctionStyleMenuItem"));
auto resProvider = AppResourceProvider::GetInstance();
if (m_richEditBox != nullptr)
{
m_richEditBox->GotFocus += ref new RoutedEventHandler(this, &EquationTextBox::OnRichEditBoxGotFocus);
m_richEditBox->LostFocus += ref new RoutedEventHandler(this, &EquationTextBox::OnRichEditBoxLostFocus);
m_richEditBox->SelectionFlyout = nullptr;
m_richEditBox->EquationSubmitted +=
ref new EventHandler<MathRichEditBoxSubmission ^>(this, &EquationTextBox::OnEquationSubmitted);
}
if (m_equationButton != nullptr)
{
m_equationButton->Click += ref new RoutedEventHandler(this, &EquationTextBox::OnEquationButtonClicked);
auto toolTip = ref new ToolTip();
auto equationButtonMessage = m_equationButton->IsChecked->Value ? resProvider->GetResourceString(L"showEquationButtonToolTip")
: resProvider->GetResourceString(L"hideEquationButtonToolTip");
toolTip->Content = equationButtonMessage;
ToolTipService::SetToolTip(m_equationButton, toolTip);
AutomationProperties::SetName(m_equationButton, equationButtonMessage);
}
if (m_richEditContextMenu != nullptr)
{
m_richEditContextMenu->Opening += ref new EventHandler<Platform::Object ^>(this, &EquationTextBox::OnRichEditMenuOpening);
}
if (m_deleteButton != nullptr)
{
m_deleteButton->Click += ref new RoutedEventHandler(this, &EquationTextBox::OnDeleteButtonClicked);
}
if (m_removeButton != nullptr)
{
m_removeButton->Click += ref new RoutedEventHandler(this, &EquationTextBox::OnRemoveButtonClicked);
}
if (m_removeMenuItem != nullptr)
{
m_removeMenuItem->Text = resProvider->GetResourceString(L"removeMenuItem");
m_removeMenuItem->Click += ref new RoutedEventHandler(this, &EquationTextBox::OnRemoveButtonClicked);
}
if (m_colorChooserButton != nullptr)
{
m_colorChooserButton->Click += ref new RoutedEventHandler(this, &EquationTextBox::OnColorChooserButtonClicked);
}
if (m_colorChooserMenuItem != nullptr)
{
m_colorChooserMenuItem->Text = resProvider->GetResourceString(L"colorChooserMenuItem");
m_colorChooserMenuItem->Click += ref new RoutedEventHandler(this, &EquationTextBox::OnColorChooserButtonClicked);
}
if (m_functionButton != nullptr)
{
m_functionButton->Click += ref new RoutedEventHandler(this, &EquationTextBox::OnFunctionButtonClicked);
m_functionButton->IsEnabled = false;
}
if (m_kgfEquationMenuItem != nullptr)
{
m_kgfEquationMenuItem->Text = resProvider->GetResourceString(L"functionAnalysisMenuItem");
m_kgfEquationMenuItem->Click += ref new RoutedEventHandler(this, &EquationTextBox::OnFunctionButtonClicked);
}
if (ColorChooserFlyout != nullptr)
{
ColorChooserFlyout->Opened += ref new EventHandler<Object ^>(this, &EquationTextBox::OnColorFlyoutOpened);
ColorChooserFlyout->Closed += ref new EventHandler<Object ^>(this, &EquationTextBox::OnColorFlyoutClosed);
}
UpdateCommonVisualState();
UpdateButtonsVisualState();
}
void EquationTextBox::OnPointerEntered(PointerRoutedEventArgs ^ e)
{
m_isPointerOver = true;
UpdateCommonVisualState();
}
void EquationTextBox::OnPointerExited(PointerRoutedEventArgs ^ e)
{
m_isPointerOver = false;
UpdateCommonVisualState();
}
void EquationTextBox::OnPointerCanceled(PointerRoutedEventArgs ^ e)
{
m_isPointerOver = false;
UpdateCommonVisualState();
}
void EquationTextBox::OnPointerCaptureLost(PointerRoutedEventArgs ^ e)
{
m_isPointerOver = false;
UpdateCommonVisualState();
}
void EquationTextBox::OnColorFlyoutOpened(Object ^ sender, Object ^ e)
{
m_isColorChooserFlyoutOpen = true;
UpdateCommonVisualState();
}
void EquationTextBox::OnColorFlyoutClosed(Object ^ sender, Object ^ e)
{
m_colorChooserButton->IsChecked = false;
m_isColorChooserFlyoutOpen = false;
UpdateCommonVisualState();
}
void EquationTextBox::OnRichEditBoxGotFocus(Object ^ sender, RoutedEventArgs ^ e)
{
m_HasFocus = true;
UpdateCommonVisualState();
UpdateButtonsVisualState();
}
void EquationTextBox::OnRichEditBoxLostFocus(Object ^ sender, RoutedEventArgs ^ e)
{
if (!m_richEditBox->ContextFlyout->IsOpen)
{
m_HasFocus = false;
}
UpdateCommonVisualState();
UpdateButtonsVisualState();
}
void EquationTextBox::OnDeleteButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
if (m_richEditBox != nullptr)
{
m_richEditBox->MathText = L"";
if (m_functionButton)
{
m_functionButton->IsEnabled = false;
}
}
}
void EquationTextBox::OnEquationButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
EquationButtonClicked(this, ref new RoutedEventArgs());
auto toolTip = ref new ToolTip();
auto resProvider = AppResourceProvider::GetInstance();
auto equationButtonMessage = m_equationButton->IsChecked->Value ? resProvider->GetResourceString(L"showEquationButtonToolTip")
: resProvider->GetResourceString(L"hideEquationButtonToolTip");
toolTip->Content = equationButtonMessage;
ToolTipService::SetToolTip(m_equationButton, toolTip);
AutomationProperties::SetName(m_equationButton, equationButtonMessage);
}
void EquationTextBox::OnRemoveButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
if (IsAddEquationMode)
{
// Don't remove the last equation
return;
}
if (m_richEditBox != nullptr)
{
m_richEditBox->MathText = L"";
}
RemoveButtonClicked(this, ref new RoutedEventArgs());
if (m_functionButton)
{
m_functionButton->IsEnabled = false;
}
if (m_equationButton)
{
m_equationButton->IsChecked = false;
}
VisualStateManager::GoToState(this, "Normal", true);
}
void EquationTextBox::OnColorChooserButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
if (ColorChooserFlyout != nullptr && m_richEditBox != nullptr)
{
ColorChooserFlyout->ShowAt(m_richEditBox);
}
}
void EquationTextBox::OnFunctionButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
KeyGraphFeaturesButtonClicked(this, ref new RoutedEventArgs());
}
void EquationTextBox::UpdateButtonsVisualState()
{
String ^ state;
if (IsAddEquationMode)
{
state = "ButtonHideRemove";
}
else if (RichEditHasContent())
{
state = "ButtonVisible";
}
else
{
state = "ButtonCollapsed";
}
VisualStateManager::GoToState(this, state, true);
}
void EquationTextBox::UpdateCommonVisualState()
{
String ^ state = nullptr;
if (m_HasFocus && HasError)
{
state = "FocusedError";
}
else if (m_HasFocus)
{
state = "Focused";
}
else if (HasError && (m_isPointerOver || m_isColorChooserFlyoutOpen))
{
state = "PointerOverError";
}
else if (m_isPointerOver || m_isColorChooserFlyoutOpen)
{
state = "PointerOver";
}
else if (HasError)
{
state = "Error";
}
else if (IsAddEquationMode)
{
state = "AddEquation";
}
else
{
state = "Normal";
}
VisualStateManager::GoToState(this, state, false);
}
void EquationTextBox::OnHasErrorPropertyChanged(bool, bool)
{
UpdateCommonVisualState();
}
Platform::String ^ EquationTextBox::GetEquationText()
{
String ^ text;
if (m_richEditBox != nullptr)
{
// Clear formatting since the graph control doesn't work with bold/underlines
ITextRange ^ range = m_richEditBox->TextDocument->GetRange(0, m_richEditBox->TextDocument->Selection->EndPosition);
if (range != nullptr)
{
range->CharacterFormat->Bold = FormatEffect::Off;
range->CharacterFormat->Underline = UnderlineType::None;
}
text = m_richEditBox->MathText;
}
return text;
}
void EquationTextBox::SetEquationText(Platform::String ^ equationText)
{
if (m_richEditBox != nullptr)
{
m_richEditBox->MathText = equationText;
}
}
bool EquationTextBox::RichEditHasContent()
{
String ^ text;
if (m_richEditBox != nullptr)
{
text = m_richEditBox->MathText;
}
return !text->IsEmpty() && m_HasFocus;
}
void EquationTextBox::OnRichEditMenuOpening(Object ^ /*sender*/, Object ^ /*args*/)
{
if (m_kgfEquationMenuItem != nullptr)
{
m_kgfEquationMenuItem->IsEnabled = RichEditHasContent();
}
if (m_colorChooserMenuItem != nullptr)
{
m_colorChooserMenuItem->IsEnabled = !HasError;
}
}
void EquationTextBox::OnIsAddEquationModePropertyChanged(bool /*oldValue*/, bool /*newValue*/)
{
UpdateCommonVisualState();
UpdateButtonsVisualState();
}
void EquationTextBox::FocusTextBox()
{
if (m_richEditBox != nullptr)
{
FocusManager::TryFocusAsync(m_richEditBox, ::FocusState::Programmatic);
}
}
void EquationTextBox::OnEquationSubmitted(Platform::Object ^ sender, MathRichEditBoxSubmission ^ args)
{
if (args->HasTextChanged)
{
if (m_functionButton && m_richEditBox->MathText != L"")
{
m_functionButton->IsEnabled = true;
}
}
EquationSubmitted(this, args);
}

View File

@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "CalcViewModel/Common/Utils.h"
#include "CalcViewModel/GraphingCalculator/EquationViewModel.h"
#include "Calculator/Controls/MathRichEditBox.h"
namespace CalculatorApp
{
namespace Controls
{
public
ref class EquationTextBox sealed : public Windows::UI::Xaml::Controls::Control
{
public:
EquationTextBox();
DEPENDENCY_PROPERTY_OWNER(EquationTextBox);
DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::SolidColorBrush ^, EquationColor);
DEPENDENCY_PROPERTY(Windows::UI::Xaml::Controls::Flyout ^, ColorChooserFlyout);
DEPENDENCY_PROPERTY(Platform::String ^, EquationButtonContentIndex);
DEPENDENCY_PROPERTY(Platform::String ^, MathEquation);
DEPENDENCY_PROPERTY_WITH_CALLBACK(bool, HasError);
DEPENDENCY_PROPERTY_WITH_CALLBACK(bool, IsAddEquationMode);
PROPERTY_R(bool, HasFocus);
event Windows::UI::Xaml::RoutedEventHandler ^ RemoveButtonClicked;
event Windows::UI::Xaml::RoutedEventHandler ^ KeyGraphFeaturesButtonClicked;
event Windows::Foundation::EventHandler<MathRichEditBoxSubmission ^> ^ EquationSubmitted;
event Windows::UI::Xaml::RoutedEventHandler ^ EquationButtonClicked;
Platform::String ^ GetEquationText();
void SetEquationText(Platform::String ^ equationText);
void FocusTextBox();
protected:
virtual void OnApplyTemplate() override;
virtual void OnPointerEntered(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
virtual void OnPointerExited(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
virtual void OnPointerCanceled(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
virtual void OnPointerCaptureLost(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnIsAddEquationModePropertyChanged(bool oldValue, bool newValue);
private:
void UpdateCommonVisualState();
void UpdateButtonsVisualState();
bool RichEditHasContent();
void OnRichEditBoxGotFocus(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void OnRichEditBoxLostFocus(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void OnDeleteButtonClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnEquationButtonClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnRemoveButtonClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnColorChooserButtonClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnFunctionButtonClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnRichEditMenuOpening(Platform::Object ^ sender, Platform::Object ^ args);
void OnColorFlyoutOpened(Platform::Object ^ sender, Platform::Object ^ e);
void OnColorFlyoutClosed(Platform::Object ^ sender, Platform::Object ^ e);
void OnHasErrorPropertyChanged(bool oldValue, bool newValue);
CalculatorApp::Controls::MathRichEditBox ^ m_richEditBox;
Windows::UI::Xaml::Controls::Primitives::ToggleButton ^ m_equationButton;
Windows::UI::Xaml::Controls::Button ^ m_deleteButton;
Windows::UI::Xaml::Controls::Button ^ m_removeButton;
Windows::UI::Xaml::Controls::Button ^ m_functionButton;
Windows::UI::Xaml::Controls::Primitives::ToggleButton ^ m_colorChooserButton;
Windows::UI::Xaml::Controls::MenuFlyout^ m_richEditContextMenu;
Windows::UI::Xaml::Controls::MenuFlyoutItem^ m_kgfEquationMenuItem;
Windows::UI::Xaml::Controls::MenuFlyoutItem^ m_removeMenuItem;
Windows::UI::Xaml::Controls::MenuFlyoutItem^ m_colorChooserMenuItem;
bool m_isPointerOver;
bool m_isColorChooserFlyoutOpen;
void OnEquationSubmitted(Platform::Object ^ sender, CalculatorApp::Controls::MathRichEditBoxSubmission ^ args);
};
}
}

View File

@ -0,0 +1,200 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "MathRichEditBox.h"
using namespace Platform;
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace CalculatorApp::Controls;
using namespace std;
using namespace Windows::ApplicationModel;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::Foundation::Collections;
using namespace Windows::System;
DEPENDENCY_PROPERTY_INITIALIZATION(MathRichEditBox, MathText);
// TODO remove when Windows 10 version 2004 SDK is adopted
namespace Windows_2004_Prerelease
{
enum RichEditMathMode : int
{
NoMath,
MathOnly,
};
MIDL_INTERFACE("619c20f2-cb3b-4521-981f-2865b1b93f04")
ITextDocument4 : public IInspectable
{
public:
virtual HRESULT STDMETHODCALLTYPE SetMath(HSTRING value) = 0;
virtual HRESULT STDMETHODCALLTYPE GetMath(HSTRING * value) = 0;
virtual HRESULT STDMETHODCALLTYPE SetMathMode(RichEditMathMode mathMode) = 0;
};
}
MathRichEditBox::MathRichEditBox()
{
static LimitedAccessFeatureStatus m_lafResultStatus;
String ^ packageName = Package::Current->Id->Name;
if (packageName == L"Microsoft.WindowsCalculator.Dev")
{
m_lafResultStatus = LimitedAccessFeatures::TryUnlockFeature(
"com.microsoft.windows.richeditmath",
"BeDD/jxKhz/yfVNA11t4uA==", // Microsoft.WindowsCalculator.Dev
"8wekyb3d8bbwe has registered their use of com.microsoft.windows.richeditmath with Microsoft and agrees to the terms of use.")
->Status;
}
else if (packageName == L"Microsoft.WindowsCalculator")
{
m_lafResultStatus = LimitedAccessFeatures::TryUnlockFeature(
"com.microsoft.windows.richeditmath",
"pfanNuxnzo+mAkBQ3N/rGQ==", // Microsoft.WindowsCalculator
"8wekyb3d8bbwe has registered their use of com.microsoft.windows.richeditmath with Microsoft and agrees to the terms of use.")
->Status;
}
else if (packageName == L"Microsoft.WindowsCalculator.Graphing")
{
m_lafResultStatus = LimitedAccessFeatures::TryUnlockFeature(
"com.microsoft.windows.richeditmath",
"H6wflFFz3gkOsAHtG/D9Tg==", // Microsoft.WindowsCalculator.Graphing
"8wekyb3d8bbwe has registered their use of com.microsoft.windows.richeditmath with Microsoft and agrees to the terms of use.")
->Status;
}
// TODO when Windows 10 version 2004 SDK is adopted, replace with:
// TextDocument->SetMathMode(Windows::UI::Text::RichEditMathMode::MathOnly);
Microsoft::WRL::ComPtr<Windows_2004_Prerelease::ITextDocument4> textDocument4;
reinterpret_cast<IInspectable*>(this->TextDocument)->QueryInterface(IID_PPV_ARGS(&textDocument4));
auto hr = textDocument4->SetMathMode(Windows_2004_Prerelease::RichEditMathMode::MathOnly);
if (FAILED(hr))
{
throw Exception::CreateException(hr);
}
this->LosingFocus += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Xaml::UIElement ^, Windows::UI::Xaml::Input::LosingFocusEventArgs ^>(
this, &CalculatorApp::Controls::MathRichEditBox::OnLosingFocus);
this->KeyUp += ref new Windows::UI::Xaml::Input::KeyEventHandler(this, &CalculatorApp::Controls::MathRichEditBox::OnKeyUp);
}
String ^ MathRichEditBox::GetMathTextProperty()
{
// TODO when Windows 10 version 2004 SDK is adopted, replace with:
// String ^ text;
// this->TextDocument->GetMath(&text);
// return text;
Microsoft::WRL::ComPtr<Windows_2004_Prerelease::ITextDocument4> textDocument4;
reinterpret_cast<IInspectable*>(this->TextDocument)->QueryInterface(IID_PPV_ARGS(&textDocument4));
HSTRING math;
auto hr = textDocument4->GetMath(&math);
if (FAILED(hr))
{
throw Exception::CreateException(hr);
}
return reinterpret_cast<String ^>(math);
}
void MathRichEditBox::SetMathTextProperty(String ^ newValue)
{
bool readOnlyState = this->IsReadOnly;
this->IsReadOnly = false;
// TODO when Windows 10 version 2004 SDK is adopted, replace with:
// TextDocument->SetMath(newValue);
Microsoft::WRL::ComPtr<Windows_2004_Prerelease::ITextDocument4> textDocument4;
reinterpret_cast<IInspectable*>(this->TextDocument)->QueryInterface(IID_PPV_ARGS(&textDocument4));
auto hr = textDocument4->SetMath(reinterpret_cast<HSTRING>(newValue));
if (FAILED(hr))
{
throw Exception::CreateException(hr);
}
this->IsReadOnly = readOnlyState;
}
void CalculatorApp::Controls::MathRichEditBox::OnLosingFocus(Windows::UI::Xaml::UIElement ^ sender, Windows::UI::Xaml::Input::LosingFocusEventArgs ^ args)
{
SubmitEquation(EquationSubmissionSource::FOCUS_LOST);
}
void CalculatorApp::Controls::MathRichEditBox::OnKeyUp(Platform::Object ^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e)
{
if (e->Key == VirtualKey::Enter)
{
SubmitEquation(EquationSubmissionSource::ENTER_KEY);
}
}
void MathRichEditBox::OnMathTextPropertyChanged(Platform::String ^ oldValue, Platform::String ^ newValue)
{
SetMathTextProperty(newValue);
SetValue(MathTextProperty, newValue);
}
void MathRichEditBox::InsertText(Platform::String ^ text, int cursorOffSet, int selectionLength)
{
// If the rich edit is empty, the math zone may not exist, and so selection (and thus the resulting text) will not be in a math zone.
// If the rich edit has content already, then the mathzone will already be created due to mathonly mode being set and the selection will exist inside the
// math zone. To handle this, we will force a math zone to be created in teh case of the text being empty and then replacing the text inside of the math
// zone with the newly inserted text.
if (GetMathTextProperty() == nullptr)
{
SetMathTextProperty("<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mi>x</mi></math>");
TextDocument->Selection->StartPosition = 0;
TextDocument->Selection->EndPosition = 1;
}
// insert the text in place of selection
TextDocument->Selection->SetText(Windows::UI::Text::TextSetOptions::FormatRtf, text);
// Move the cursor to the next logical place for users to enter text.
TextDocument->Selection->StartPosition += cursorOffSet;
TextDocument->Selection->EndPosition = TextDocument->Selection->StartPosition + selectionLength;
}
void MathRichEditBox::BackSpace()
{
// if anything is selected, just delete the selection. Note: EndPosition can be before start position.
if (TextDocument->Selection->StartPosition != TextDocument->Selection->EndPosition)
{
TextDocument->Selection->SetText(Windows::UI::Text::TextSetOptions::None, L"");
return;
}
// if we are at the start of the string, do nothing
if (TextDocument->Selection->StartPosition == 0)
{
return;
}
// Select the previous group.
TextDocument->Selection->EndPosition = TextDocument->Selection->StartPosition;
TextDocument->Selection->StartPosition -= 1;
// If the group contains anything complex, we want to give the user a chance to preview the deletion.
// If it's a single character, then just delete it. Otherwise do nothing until the user triggers backspace again.
auto text = TextDocument->Selection->Text;
if (text->Length() == 1)
{
TextDocument->Selection->SetText(Windows::UI::Text::TextSetOptions::None, L"");
}
}
void MathRichEditBox::SubmitEquation(EquationSubmissionSource source)
{
auto newVal = GetMathTextProperty();
if (MathText != newVal)
{
SetValue(MathTextProperty, newVal);
EquationSubmitted(this, ref new MathRichEditBoxSubmission(true, source));
}
else
{
EquationSubmitted(this, ref new MathRichEditBoxSubmission(false, source));
}
}

View File

@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "CalcViewModel/Common/Utils.h"
namespace CalculatorApp
{
namespace Controls
{
public
enum class EquationSubmissionSource
{
FOCUS_LOST,
ENTER_KEY,
PROGRAMMATIC
};
public
ref class MathRichEditBoxSubmission sealed
{
public:
PROPERTY_R(bool, HasTextChanged);
PROPERTY_R(EquationSubmissionSource, Source);
public:
MathRichEditBoxSubmission(bool hasTextChanged, EquationSubmissionSource source)
: m_HasTextChanged(hasTextChanged)
, m_Source(source)
{
}
};
public
ref class MathRichEditBox sealed : Windows::UI::Xaml::Controls::RichEditBox
{
public:
MathRichEditBox();
DEPENDENCY_PROPERTY_OWNER(MathRichEditBox);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(Platform::String ^, MathText, L"");
event Windows::Foundation::EventHandler<MathRichEditBoxSubmission^> ^ EquationSubmitted;
void OnMathTextPropertyChanged(Platform::String ^ oldValue, Platform::String ^ newValue);
void InsertText(Platform::String ^ text, int cursorOffSet, int selectionLength);
void SubmitEquation(EquationSubmissionSource source);
void BackSpace();
private:
Platform::String ^ GetMathTextProperty();
void SetMathTextProperty(Platform::String ^ newValue);
void OnLosingFocus(Windows::UI::Xaml::UIElement ^ sender, Windows::UI::Xaml::Input::LosingFocusEventArgs ^ args);
void OnKeyUp(Platform::Object ^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e);
};
}
}

View File

@ -0,0 +1,91 @@
<UserControl x:Class="CalculatorApp.EquationStylePanelControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:CalculatorApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<GridView x:Name="ColorChooser"
ItemsSource="{x:Bind AvailableColors}"
Loaded="ColorChooserLoaded"
SelectionChanged="SelectionChanged"
SingleSelectionFollowsFocus="False">
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="MinHeight" Value="52"/>
<Setter Property="MinWidth" Value="52"/>
<Setter Property="Height" Value="52"/>
<Setter Property="Width" Value="52"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="ItemContent.Margin" Value="-2,-2,-2,-2"/>
<Setter Target="ItemBorder.Margin" Value="2,2,2,2"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="ItemContent.Margin" Value="-2,-2,-2,-2"/>
<Setter Target="ItemBorder.Stroke" Value="{ThemeResource InkToolbarFlyoutItemBorderPressedThemeBrush}"/>
<Setter Target="ItemBorder.Margin" Value="2,2,2,2"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Target="ItemContent.Margin" Value="2,2,2,2"/>
<Setter Target="ItemBorder.Stroke" Value="{ThemeResource InkToolbarFlyoutItemBorderSelectedThemeBrush}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse x:Name="ItemBorder"
Margin="6,6,6,6"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
Stroke="Transparent"
StrokeThickness="2"
UseLayoutRounding="false"/>
<ContentPresenter x:Name="ItemContent" UseLayoutRounding="false"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GridView.ItemContainerStyle>
<GridView.Header>
<TextBlock x:Uid="LineColorText" Margin="8,0,8,8"/>
</GridView.Header>
<GridView.ItemTemplate>
<DataTemplate x:DataType="Brush">
<Ellipse Margin="8,8,8,8"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="{x:Bind}"
StrokeThickness="0"
UseLayoutRounding="false"/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid MaximumRowsOrColumns="7" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</UserControl>

View File

@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "EquationStylePanelControl.xaml.h"
using namespace CalculatorApp;
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
using namespace Windows::UI::Xaml::Shapes;
DEPENDENCY_PROPERTY_INITIALIZATION(EquationStylePanelControl, SelectedColor);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationStylePanelControl, AvailableColors);
EquationStylePanelControl::EquationStylePanelControl()
{
InitializeComponent();
}
void EquationStylePanelControl::SelectionChanged(Object ^ /*sender */, SelectionChangedEventArgs ^ e)
{
if (e->AddedItems->Size > 0)
{
auto brush = dynamic_cast<SolidColorBrush ^>(e->AddedItems->GetAt(0));
if (brush == nullptr)
{
SelectedColor = Colors::Black;
}
else
{
SelectedColor = brush->Color;
}
}
}
void EquationStylePanelControl::OnSelectedColorPropertyChanged(Color /*oldColor*/, Color newColor)
{
SelectColor(newColor);
}
void EquationStylePanelControl::ColorChooserLoaded(Object ^ sender, RoutedEventArgs ^ e)
{
SelectColor(SelectedColor);
}
void EquationStylePanelControl::SelectColor(Color selectedColor)
{
for (auto item : ColorChooser->Items->GetView())
{
auto brush = static_cast<SolidColorBrush ^>(item);
auto gridViewItem = dynamic_cast<GridViewItem ^>(ColorChooser->ContainerFromItem(brush));
if (!gridViewItem)
{
continue;
}
if (Utils::AreColorsEqual(brush->Color, selectedColor))
{
gridViewItem->IsSelected = true;
return;
}
else
{
gridViewItem->IsSelected = false;
}
}
}

View File

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "EquationStylePanelControl.g.h"
#include "CalcViewModel/Common/Utils.h"
namespace CalculatorApp
{
[Windows::Foundation::Metadata::WebHostHidden] public ref class EquationStylePanelControl sealed
{
public:
EquationStylePanelControl();
DEPENDENCY_PROPERTY_OWNER(EquationStylePanelControl);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(Windows::UI::Color, SelectedColor, Windows::UI::Colors::Black);
DEPENDENCY_PROPERTY_WITH_DEFAULT(Windows::Foundation::Collections::IVector<Windows::UI::Xaml::Media::SolidColorBrush ^> ^, AvailableColors, nullptr);
private:
void SelectionChanged(Platform::Object ^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs ^ e);
void OnSelectedColorPropertyChanged(Windows::UI::Color oldColor, Windows::UI::Color newColor);
void ColorChooserLoaded(
Platform::Object ^ sender,
Windows::UI::Xaml::RoutedEventArgs ^ e);
void SelectColor(Windows::UI::Color selectedColor);
};
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" IgnorableNamespaces="uap uap5 mp">
<Identity Name="Microsoft.WindowsCalculator.Dev" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="0.0.0.0" />
<Identity Name="Microsoft.WindowsCalculator.Dev" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="0.0.1.0" />
<mp:PhoneIdentity PhoneProductId="b58171c6-c70c-4266-a2e8-8f9c994f4456" PhonePublisherId="95d94207-0c7c-47ed-82db-d75c81153c35" />
<Properties>
<DisplayName>ms-resource:DevAppStoreName</DisplayName>

View File

@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AppName" xml:space="preserve">
<value>Calculator</value>
<value>Graphing Calculator</value>
<comment>{@Appx_ShortDisplayName@}{StringCategory="Feature Title"} This is the title of the official application when published through Windows Store.</comment>
</data>
<data name="DevAppName" xml:space="preserve">
@ -1279,7 +1279,7 @@
</data>
<data name="equalButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Equals</value>
<comment>Screen reader prompt for the invert button on the scientific operator keypad</comment>
<comment>Screen reader prompt for the equals button on the scientific operator keypad</comment>
</data>
<data name="shiftButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Inverse Function</value>
@ -3163,10 +3163,6 @@
<value>AB</value>
<comment>AccessKey for the About button. {StringCategory="Accelerator"}</comment>
</data>
<data name="DateCalculationModeAccessKey" xml:space="preserve">
<value>4</value>
<comment>AccessKey for the Date Calculation mode navbar item. {StringCategory="Accelerator"}</comment>
</data>
<data name="HistoryButton.AccessKey" xml:space="preserve">
<value>I</value>
<comment>Access key for the History button. {StringCategory="Accelerator"}</comment>
@ -3179,18 +3175,6 @@
<value>H</value>
<comment>Access key for the Hamburger button. {StringCategory="Accelerator"}</comment>
</data>
<data name="ProgrammerModeAccessKey" xml:space="preserve">
<value>3</value>
<comment>AccessKey for the Programmer mode navbar item. {StringCategory="Accelerator"}</comment>
</data>
<data name="ScientificModeAccessKey" xml:space="preserve">
<value>2</value>
<comment>AccessKey for the Scientific mode navbar item. {StringCategory="Accelerator"}</comment>
</data>
<data name="StandardModeAccessKey" xml:space="preserve">
<value>1</value>
<comment>AccessKey for the Standard mode navbar item. {StringCategory="Accelerator"}</comment>
</data>
<data name="CategoryName_AngleAccessKey" xml:space="preserve">
<value>AN</value>
<comment>AccessKey for the angle converter navbar item. {StringCategory="Accelerator"}</comment>
@ -3551,6 +3535,10 @@
<value>Function</value>
<comment>Displayed on the button that contains a flyout for the general functions in scientific mode.</comment>
</data>
<data name="inequalityButton.Text" xml:space="preserve">
<value>Inequalities</value>
<comment>Displayed on the button that contains a flyout for the inequality functions.</comment>
</data>
<data name="bitwiseButton.Text" xml:space="preserve">
<value>Bitwise</value>
<comment>Displayed on the button that contains a flyout for the bitwise functions in programmer mode.</comment>
@ -3851,4 +3839,423 @@
<value>most significant bit</value>
<comment>Used to describe the last bit of a binary number. Used in bit flip</comment>
</data>
<data name="GraphingCalculatorModeText" xml:space="preserve">
<value>Graphing</value>
<comment>Name of the Graphing mode of the Calculator app. Displayed in the navigation menu.</comment>
</data>
<data name="graphingEqualButton.[using:CalculatorApp.Common]KeyboardShortcutManager.Character" xml:space="preserve">
<value>=</value>
<comment>{Locked}This is the character that should trigger this button. Note that it is a character and not a key, so it does not come from the Windows::System::VirtualKey enum.</comment>
</data>
<data name="graphingEqualButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Equals</value>
<comment>Screen reader prompt for the equal button on the graphing calculator operator keypad</comment>
</data>
<data name="plotButton.[using:CalculatorApp.Common]KeyboardShortcutManager.VirtualKey" xml:space="preserve">
<value>Enter</value>
<comment>{Locked}This is the value from the VirtualKey enum that maps to this button</comment>
</data>
<data name="plotButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Plot</value>
<comment>Screen reader prompt for the plot button on the graphing calculator operator keypad</comment>
</data>
<data name="xButton.[using:CalculatorApp.Common]KeyboardShortcutManager.VirtualKey" xml:space="preserve">
<value>X</value>
<comment>{Locked}This is the value that comes from the VirtualKey enum that represents the button. This value is not localized and must be one value that comes from the Windows::System::VirtualKey enum.</comment>
</data>
<data name="yButton.[using:CalculatorApp.Common]KeyboardShortcutManager.VirtualKey" xml:space="preserve">
<value>Y</value>
<comment>{Locked}This is the value that comes from the VirtualKey enum that represents the button. This value is not localized and must be one value that comes from the Windows::System::VirtualKey enum.</comment>
</data>
<data name="graphingPowerButton.[using:CalculatorApp.Common]KeyboardShortcutManager.Character" xml:space="preserve">
<value>^</value>
<comment>{Locked}This is the character that should trigger this button. Note that it is a character and not a key, so it does not come from the Windows::System::VirtualKey enum.</comment>
</data>
<data name="zoomResetButton.[using:CalculatorApp.Common]KeyboardShortcutManager.VirtualKeyControlChord" xml:space="preserve">
<value>Home</value>
<comment>{Locked}This is the shortcut for the zoom reset button.</comment>
</data>
<data name="zoomResetButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Reset View</value>
<comment>This is the tool tip automation name for the Calculator zoom reset button.</comment>
</data>
<data name="zoomResetButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Reset View</value>
<comment>Screen reader prompt for the reset zoom button.</comment>
</data>
<data name="zoomInButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Zoom In</value>
<comment>This is the tool tip automation name for the Calculator zoom in button.</comment>
</data>
<data name="zoomInButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Zoom In</value>
<comment>Screen reader prompt for the zoom in button.</comment>
</data>
<data name="zoomOutButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Zoom Out</value>
<comment>This is the tool tip automation name for the Calculator zoom out button.</comment>
</data>
<data name="zoomOutButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Zoom Out</value>
<comment>Screen reader prompt for the zoom out button.</comment>
</data>
<data name="EquationTextBoxAddPanel.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Add Equation</value>
<comment>Placeholder text for the equation input button</comment>
</data>
<data name="ShareActionErrorMessage" xml:space="preserve">
<value>Unable to share at this time.</value>
<comment>If there is an error in the sharing action will display a dialog with this text.</comment>
</data>
<data name="ShareActionErrorOk" xml:space="preserve">
<value>OK</value>
<comment>Used on the dismiss button of the share action error dialog.</comment>
</data>
<data name="ShareActionTitle" xml:space="preserve">
<value>Look what I graphed with Windows Calculator</value>
<comment>Sent as part of the shared content. The title for the share.</comment>
</data>
<data name="EquationsShareHeader" xml:space="preserve">
<value>Equations</value>
<comment>Header that appears over the equations section when sharing</comment>
</data>
<data name="VariablesShareHeader" xml:space="preserve">
<value>Variables</value>
<comment>Header that appears over the variables section when sharing</comment>
</data>
<data name="GraphImageAltText" xml:space="preserve">
<value>Image of a graph with equations</value>
<comment>Alt text for the graph image when output via Share</comment>
</data>
<data name="VaiablesHeader.Text" xml:space="preserve">
<value>Variables</value>
<comment>Header text for variables area</comment>
</data>
<data name="StepTextBlock.Text" xml:space="preserve">
<value>Step</value>
<comment>Label text for the step text box</comment>
</data>
<data name="MinTextBlock.Text" xml:space="preserve">
<value>Min</value>
<comment>Label text for the min text box</comment>
</data>
<data name="MaxTextBlock.Text" xml:space="preserve">
<value>Max</value>
<comment>Label text for the max text box</comment>
</data>
<data name="LineColorText.Text" xml:space="preserve">
<value>Line Color</value>
<comment>Label for the Line Color section of the style picker</comment>
</data>
<data name="KeyGraphFeaturesLabel.Text" xml:space="preserve">
<value>Key Graph Features</value>
<comment>Title for KeyGraphFeatures Control</comment>
</data>
<data name="KGFHorizontalAsymptotesNone" xml:space="preserve">
<value>The function does not have any horizontal asymptotes.</value>
<comment>Message displayed when the graph does not have any horizontal asymptotes</comment>
</data>
<data name="KGFInflectionPointsNone" xml:space="preserve">
<value>The function does not have any inflection points.</value>
<comment>Message displayed when the graph does not have any inflection points</comment>
</data>
<data name="KGFMaximaNone" xml:space="preserve">
<value>The function does not have any maxima points.</value>
<comment>Message displayed when the graph does not have any maxima</comment>
</data>
<data name="KGFMinimaNone" xml:space="preserve">
<value>The function does not have any minima points.</value>
<comment>Message displayed when the graph does not have any minima</comment>
</data>
<data name="KGFMonotonicityConstant" xml:space="preserve">
<value>Constant</value>
<comment>String describing constant monotonicity of a function</comment>
</data>
<data name="KGFMonotonicityDecreasing" xml:space="preserve">
<value>Decreasing</value>
<comment>String describing decreasing monotonicity of a function</comment>
</data>
<data name="KGFMonotonicityError" xml:space="preserve">
<value>Unable to determine the monotonicity of the function.</value>
<comment>Error displayed when monotonicity cannot be determined</comment>
</data>
<data name="KGFMonotonicityIncreasing" xml:space="preserve">
<value>Increasing</value>
<comment>String describing increasing monotonicity of a function</comment>
</data>
<data name="KGFMonotonicityUnknown" xml:space="preserve">
<value>The monotonicity of the function is unknown.</value>
<comment>Error displayed when monotonicity is unknown</comment>
</data>
<data name="KGFObliqueAsymptotesNone" xml:space="preserve">
<value>The function does not have any oblique aysmptotes.</value>
<comment>Message displayed when the graph does not have any oblique asymptotes</comment>
</data>
<data name="KGFParityError" xml:space="preserve">
<value>Unable to determine the parity of the function.</value>
<comment>Error displayed when parity is cannot be determined</comment>
</data>
<data name="KGFParityEven" xml:space="preserve">
<value>The function is even.</value>
<comment>Message displayed with the function parity is even</comment>
</data>
<data name="KGFParityNeither" xml:space="preserve">
<value>The function is neither even nor odd.</value>
<comment>Message displayed with the function parity is neither even nor odd</comment>
</data>
<data name="KGFParityOdd" xml:space="preserve">
<value>The function is odd.</value>
<comment>Message displayed with the function parity is odd</comment>
</data>
<data name="KGFParityUnknown" xml:space="preserve">
<value>The function parity is unknown.</value>
<comment>Error displayed when parity is unknown</comment>
</data>
<data name="KGFPeriodicityError" xml:space="preserve">
<value>Periodicity is not supported for this function.</value>
<comment>Error displayed when periodicity is not supported</comment>
</data>
<data name="KGFPeriodicityNotPeriodic" xml:space="preserve">
<value>The function is not periodic.</value>
<comment>Message displayed with the function periodicity is not periodic</comment>
</data>
<data name="KGFPeriodicityUnknown" xml:space="preserve">
<value>The function periodicity is unknown.</value>
<comment>Message displayed with the function periodicity is unknown</comment>
</data>
<data name="KGFTooComplexFeaturesError" xml:space="preserve">
<value>These features are too complex for Calculator to calculate:</value>
<comment>Error displayed when analysis features cannot be calculated</comment>
</data>
<data name="KGFVerticalAsymptotesNone" xml:space="preserve">
<value>The function does not have any vertical asymptotes.</value>
<comment>Message displayed when the graph does not have any vertical asymptotes</comment>
</data>
<data name="KGFXInterceptNone" xml:space="preserve">
<value>The function does not have any x-intercepts.</value>
<comment>Message displayed when the graph does not have any x-intercepts</comment>
</data>
<data name="KGFYInterceptNone" xml:space="preserve">
<value>The function does not have any y-intercepts.</value>
<comment>Message displayed when the graph does not have any y-intercepts</comment>
</data>
<data name="Domain" xml:space="preserve">
<value>Domain</value>
<comment>Title for KeyGraphFeatures Domain Property</comment>
</data>
<data name="HorizontalAsymptotes" xml:space="preserve">
<value>Horizontal Asymptotes</value>
<comment>Title for KeyGraphFeatures Horizontal Aysmptotes Property</comment>
</data>
<data name="InflectionPoints" xml:space="preserve">
<value>Inflection Points</value>
<comment>Title for KeyGraphFeatures Inflection Points Property</comment>
</data>
<data name="KGFAnalysisNotSupported" xml:space="preserve">
<value>Analysis is not supported for this function.</value>
<comment>Error displayed when graph analysis is not supported or had an error.</comment>
</data>
<data name="Maxima" xml:space="preserve">
<value>Maxima</value>
<comment>Title for KeyGraphFeatures Maxima Property</comment>
</data>
<data name="Minima" xml:space="preserve">
<value>Minima</value>
<comment>Title for KeyGraphFeatures Minima Property</comment>
</data>
<data name="Monotonicity" xml:space="preserve">
<value>Monotonicity</value>
<comment>Title for KeyGraphFeatures Monotonicity Property</comment>
</data>
<data name="ObliqueAsymptotes" xml:space="preserve">
<value>Oblique Asymptotes</value>
<comment>Title for KeyGraphFeatures Oblique Asymptotes Property</comment>
</data>
<data name="Parity" xml:space="preserve">
<value>Parity</value>
<comment>Title for KeyGraphFeatures Parity Property</comment>
</data>
<data name="Periodicity" xml:space="preserve">
<value>Period</value>
<comment>Title for KeyGraphFeatures Periodicity Property</comment>
</data>
<data name="Range" xml:space="preserve">
<value>Range</value>
<comment>Title for KeyGraphFeatures Range Property</comment>
</data>
<data name="VerticalAsymptotes" xml:space="preserve">
<value>Vertical Asymptotes</value>
<comment>Title for KeyGraphFeatures Vertical Asymptotes Property</comment>
</data>
<data name="XIntercept" xml:space="preserve">
<value>X-Intercept</value>
<comment>Title for KeyGraphFeatures XIntercept Property</comment>
</data>
<data name="YIntercept" xml:space="preserve">
<value>Y-Intercept</value>
<comment>Title for KeyGraphFeatures YIntercept Property</comment>
</data>
<data name="KGFAnalysisCouldNotBePerformed" xml:space="preserve">
<value>Analysis could not be performed for the function.</value>
</data>
<data name="KGFDomainNone" xml:space="preserve">
<value>Unable to calculate the domain for this function.</value>
<comment>Error displayed when Domain is not returned from the analyzer.</comment>
</data>
<data name="KGFRangeNone" xml:space="preserve">
<value>Unable to calculate the range for this function.</value>
<comment>Error displayed when Range is not returned from the analyzer.</comment>
</data>
<data name="equationAnalysisBack.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Back</value>
<comment>This is the tooltip for the back button in the equation analysis page in the graphing calculator</comment>
</data>
<data name="equationAnalysisBack.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Back</value>
<comment>This is the automation name for the back button in the equation analysis page in the graphing calculator</comment>
</data>
<data name="functionAnalysisButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Analyze equation</value>
<comment>This is the tooltip for the analyze equation button</comment>
</data>
<data name="functionAnalysisButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Analyze equation</value>
<comment>This is the automation name for the analyze equation button</comment>
</data>
<data name="functionAnalysisMenuItem" xml:space="preserve">
<value>Analyze equation</value>
<comment>This is the text for the for the analyze equation context menu command</comment>
</data>
<data name="removeButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Remove equation</value>
<comment>This is the tooltip for the graphing calculator remove equation buttons</comment>
</data>
<data name="removeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Remove equation</value>
<comment>This is the automation name for the graphing calculator remove equation buttons</comment>
</data>
<data name="removeMenuItem" xml:space="preserve">
<value>Remove equation</value>
<comment>This is the text for the for the remove equation context menu command</comment>
</data>
<data name="shareButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Share</value>
<comment>This is the automation name for the graphing calculator share button.</comment>
</data>
<data name="shareButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Share</value>
<comment>This is the tooltip for the graphing calculator share button.</comment>
</data>
<data name="colorChooserButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Change equation style</value>
<comment>This is the tooltip for the graphing calculator equation style button</comment>
</data>
<data name="colorChooserButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Change equation style</value>
<comment>This is the automation name for the graphing calculator equation style button</comment>
</data>
<data name="colorChooserMenuItem" xml:space="preserve">
<value>Change equation style</value>
<comment>This is the text for the for the equation style context menu command</comment>
</data>
<data name="showEquationButtonToolTip" xml:space="preserve">
<value>Show</value>
<comment>This is the tooltip/automation name shown when visibility is set to hidden in the graphing calculator</comment>
</data>
<data name="hideEquationButtonToolTip" xml:space="preserve">
<value>Hide</value>
<comment>This is the tooltip/automation name shown when visibility is set to visible in the graphing calculator</comment>
</data>
<data name="disableTracingButtonToolTip" xml:space="preserve">
<value>Stop tracing</value>
<comment>This is the tooltip/automation name for the graphing calculator stop tracing button</comment>
</data>
<data name="enableTracingButtonToolTip" xml:space="preserve">
<value>Start tracing</value>
<comment>This is the tooltip/automation name for the graphing calculator start tracing button</comment>
</data>
<data name="sliderOptionsButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Configure slider</value>
<comment>This is the tooltip text for the slider options button in Graphing Calculator</comment>
</data>
<data name="sliderOptionsButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Configure slider</value>
<comment>This is the automation name text for the slider options button in Graphing Calculator</comment>
</data>
<data name="GraphSwitchToEquationMode" xml:space="preserve">
<value>Switch to equation mode</value>
<comment>Used in Graphing Calculator to switch the view to the equation mode</comment>
</data>
<data name="GraphSwitchToGraphMode" xml:space="preserve">
<value>Switch to graph mode</value>
<comment>Used in Graphing Calculator to switch the view to the graph mode</comment>
</data>
<data name="SwitchModeToggleButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Switch to equation mode</value>
<comment>Used in Graphing Calculator to switch the view to the equation mode</comment>
</data>
<data name="GraphSwitchedToEquationModeAnnouncement" xml:space="preserve">
<value>Current mode is equation mode</value>
<comment>Announcement used in Graphing Calculator when switching to the equation mode</comment>
</data>
<data name="GraphSwitchedToGraphModeAnnouncement" xml:space="preserve">
<value>Current mode is graph mode</value>
<comment>Announcement used in Graphing Calculator when switching to the graph mode</comment>
</data>
<data name="GridHeading.Text" xml:space="preserve">
<value>Grid</value>
<comment>Heading for grid extents on the settings </comment>
</data>
<data name="TrigModeDegrees.Content" xml:space="preserve">
<value>Degrees</value>
<comment>Degrees mode on settings page</comment>
</data>
<data name="TrigModeGradians.Content" xml:space="preserve">
<value>Gradians</value>
<comment>Gradian mode on settings page</comment>
</data>
<data name="TrigModeRadians.Content" xml:space="preserve">
<value>Radians</value>
<comment>Radians mode on settings page</comment>
</data>
<data name="UnitsHeading.Text" xml:space="preserve">
<value>Units</value>
<comment>Heading for Unit's on the settings</comment>
</data>
<data name="GraphSettingsXMax.Header" xml:space="preserve">
<value>X-Max</value>
<comment>X maximum value header</comment>
</data>
<data name="GraphSettingsXMin.Header" xml:space="preserve">
<value>X-Min</value>
<comment>X minimum value header</comment>
</data>
<data name="GraphSettingsYMax.Header" xml:space="preserve">
<value>Y-Max</value>
<comment>Y Maximum value header</comment>
</data>
<data name="GraphSettingsYMin.Header" xml:space="preserve">
<value>Y-Min</value>
<comment>Y minimum value header</comment>
</data>
<data name="equationMathRichEditBox.PlaceholderText" xml:space="preserve">
<value>Enter an equation</value>
<comment>Used in the Graphing Calculator to indicate to users that they can enter an equation in the textbox</comment>
</data>
<data name="graphSettingsButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Grid options</value>
<comment>This is the tooltip text for the grid options button in Graphing Calculator</comment>
</data>
<data name="graphSettingsButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Grid options</value>
<comment>This is the automation name text for the grid options button in Graphing Calculator</comment>
</data>
<data name="GraphOptionsHeading.Text" xml:space="preserve">
<value>Graph Options</value>
<comment>Heading for the Graph Options flyout in Graphing mode.</comment>
</data>
<data name="mathRichEditBox.PlaceholderText" xml:space="preserve">
<value>Enter an equation</value>
<comment>this is the placeholder text used by the textbox to enter an equation</comment>
</data>
</root>

View File

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "KeyGraphFeaturesTemplateSelector.h"
#include "CalcViewModel/GraphingCalculator/EquationViewModel.h"
using namespace CalculatorApp::ViewModel;
using namespace CalculatorApp::TemplateSelectors;
using namespace Platform;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
DataTemplate ^ KeyGraphFeaturesTemplateSelector::SelectTemplateCore(Object ^ item)
{
auto kgfItem = static_cast<KeyGraphFeaturesItem ^>(item);
if (!kgfItem->IsText)
{
if (kgfItem->DisplayItems->Size != 0)
{
return RichEditTemplate;
}
else if (kgfItem->GridItems->Size != 0)
{
return GridTemplate;
}
}
return TextBlockTemplate;
}
DataTemplate ^ KeyGraphFeaturesTemplateSelector::SelectTemplateCore(Object ^ item, DependencyObject ^ container)
{
return SelectTemplateCore(item);
}

View File

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
namespace CalculatorApp
{
namespace TemplateSelectors
{
public
ref class KeyGraphFeaturesTemplateSelector sealed : Windows::UI::Xaml::Controls::DataTemplateSelector
{
public:
KeyGraphFeaturesTemplateSelector()
{
}
property Windows::UI::Xaml::DataTemplate ^ RichEditTemplate;
property Windows::UI::Xaml::DataTemplate ^ GridTemplate;
property Windows::UI::Xaml::DataTemplate ^ TextBlockTemplate;
Windows::UI::Xaml::DataTemplate ^ SelectTemplateCore(Platform::Object ^ item) override;
Windows::UI::Xaml::DataTemplate ^ SelectTemplateCore(Platform::Object ^ item, Windows::UI::Xaml::DependencyObject ^ container) override;
};
}
}

View File

@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "VisualTree.h"
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Platform;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Media;
using namespace Calculator::Utils;
FrameworkElement ^ VisualTree::FindDescendantByName(DependencyObject ^ element, String ^ name)
{
if (element == nullptr || name == nullptr || name->Length() == 0)
{
return nullptr;
}
auto frameworkElement = dynamic_cast<FrameworkElement ^>(element);
auto nsame = frameworkElement->Name->Data();
nsame = nsame;
if (frameworkElement != nullptr && name->Equals(frameworkElement->Name))
{
return frameworkElement;
}
auto childCount = VisualTreeHelper::GetChildrenCount(element);
for (int i = 0; i < childCount; i++)
{
auto result = FindDescendantByName(VisualTreeHelper::GetChild(element, i), name);
if (result != nullptr)
{
return result;
}
}
return nullptr;
}
DependencyObject ^ VisualTree::FindDescendant(DependencyObject ^ element, TypeName typeName)
{
DependencyObject ^ retValue = nullptr;
auto childrenCount = VisualTreeHelper::GetChildrenCount(element);
for (auto i = 0; i < childrenCount; i++)
{
auto child = VisualTreeHelper::GetChild(element, i);
if (child->GetType() == typeName)
{
retValue = child;
break;
}
retValue = FindDescendant(child, typeName);
if (retValue != nullptr)
{
break;
}
}
return retValue;
}
FrameworkElement ^ VisualTree::FindAscendantByName(DependencyObject ^ element, String ^ name)
{
if (element == nullptr || name == nullptr || name->Length() == 0)
{
return nullptr;
}
auto parent = VisualTreeHelper::GetParent(element);
if (parent == nullptr)
{
return nullptr;
}
auto frameworkElement = dynamic_cast<FrameworkElement ^>(parent);
if (frameworkElement != nullptr && name->Equals(frameworkElement->Name))
{
return frameworkElement;
}
return FindAscendantByName(parent, name);
}
Object ^ VisualTree::FindAscendant(DependencyObject ^ element, TypeName typeName)
{
auto parent = VisualTreeHelper::GetParent(element);
if (parent == nullptr)
{
return nullptr;
}
if (parent->GetType() == typeName)
{
return parent;
}
return FindAscendant(parent, typeName);
}

View File

@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Light C++/CX port of Microsoft.Toolkit.Uwp.UI.Extensions.VisualTree from the Windows Community toolkit
// Original version here:
// https://raw.githubusercontent.com/windows-toolkit/WindowsCommunityToolkit/master/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs
namespace Calculator::Utils
{
/// <summary>
/// Defines a collection of extensions methods for UI.
/// </summary>
ref class VisualTree sealed
{
/// <summary>
/// Find descendant <see cref="Windows::UI::Xaml::FrameworkElement ^"/> control using its name.
/// </summary>
/// <param name="element">Parent element.</param>
/// <param name="name">Name of the control to find</param>
/// <returns>Descendant control or null if not found.</returns>
internal : static Windows::UI::Xaml::FrameworkElement ^ FindDescendantByName(Windows::UI::Xaml::DependencyObject ^ element, Platform::String ^ name);
/// <summary>
/// Find first descendant control of a specified type.
/// </summary>
/// <param name="element">Parent element.</param>
/// <param name="type">Type of descendant.</param>
/// <returns>Descendant control or null if not found.</returns>
static Windows::UI::Xaml::DependencyObject
^ FindDescendant(Windows::UI::Xaml::DependencyObject ^ element, Windows::UI::Xaml::Interop::TypeName typeName);
/// <summary>
/// Find visual ascendant <see cref="Windows::UI::Xaml::FrameworkElement ^"/> control using its name.
/// </summary>
/// <param name="element">Parent element.</param>
/// <param name="name">Name of the control to find</param>
/// <returns>Descendant control or null if not found.</returns>
static Windows::UI::Xaml::FrameworkElement ^ FindAscendantByName(Windows::UI::Xaml::DependencyObject ^ element, Platform::String ^ name);
/// <summary>
/// Find first visual ascendant control of a specified type.
/// </summary>
/// <param name="element">Child element.</param>
/// <param name="type">Type of ascendant to look for.</param>
/// <returns>Ascendant control or null if not found.</returns>
static Platform::Object ^ FindAscendant(Windows::UI::Xaml::DependencyObject ^ element, Windows::UI::Xaml::Interop::TypeName type);
};
}

View File

@ -13,7 +13,10 @@
#include "Controls/OverflowTextBlock.h"
#include "Controls/OperatorPanelListView.h"
#include "Controls/OperatorPanelButton.h"
#include "Controls/EquationTextBox.h"
#include "Controls/MathRichEditBox.h"
#include "CalcViewModel/HistoryViewModel.h"
#include "TemplateSelectors/KeyGraphFeaturesTemplateSelector.h"
#include "Views/CalculatorProgrammerDisplayPanel.xaml.h"
#include "Views/CalculatorProgrammerOperators.xaml.h"
#include "Views/CalculatorScientificAngleButtons.xaml.h"

View File

@ -115,13 +115,8 @@
<Setter Target="OperatorPanelRow.MinHeight" Value="{StaticResource OperatorPanelButtonRowSizeLarge}"/>
<Setter Target="BitwiseButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeLarge}"/>
<Setter Target="BitwiseButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeLarge}"/>
<Setter Target="BitwiseButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeLarge}"/>
<Setter Target="BitShiftButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeLarge}"/>
<Setter Target="BitShiftButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeLarge}"/>
<Setter Target="BitShiftButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeLarge}"/>
<Setter Target="BitwiseButton.Style" Value="{StaticResource OperatorPanelButtonLargeStyle}"/>
<Setter Target="BitShiftButton.Style" Value="{StaticResource OperatorPanelButtonLargeStyle}"/>
<Setter Target="BitwiseGrid.MinWidth" Value="387"/>
<Setter Target="BitwiseGrid.MinHeight" Value="192"/>
@ -172,13 +167,8 @@
<Setter Target="OperatorPanelRow.MinHeight" Value="{StaticResource OperatorPanelButtonRowSizeMedium}"/>
<Setter Target="BitwiseButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeMedium}"/>
<Setter Target="BitwiseButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeMedium}"/>
<Setter Target="BitwiseButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeMedium}"/>
<Setter Target="BitShiftButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeMedium}"/>
<Setter Target="BitShiftButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeMedium}"/>
<Setter Target="BitShiftButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeMedium}"/>
<Setter Target="BitwiseButton.Style" Value="{StaticResource OperatorPanelButtonMediumStyle}"/>
<Setter Target="BitShiftButton.Style" Value="{StaticResource OperatorPanelButtonMediumStyle}"/>
<Setter Target="BitwiseGrid.MinWidth" Value="416"/>
<Setter Target="BitwiseGrid.MinHeight" Value="144"/>
@ -227,13 +217,8 @@
<Setter Target="OperatorPanelRow.MinHeight" Value="{StaticResource OperatorPanelButtonRowSizeSmall}"/>
<Setter Target="BitwiseButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeSmall}"/>
<Setter Target="BitwiseButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeSmall}"/>
<Setter Target="BitwiseButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeSmall}"/>
<Setter Target="BitShiftButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeSmall}"/>
<Setter Target="BitShiftButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeSmall}"/>
<Setter Target="BitShiftButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeSmall}"/>
<Setter Target="BitwiseButton.Style" Value="{StaticResource OperatorPanelButtonSmallStyle}"/>
<Setter Target="BitShiftButton.Style" Value="{StaticResource OperatorPanelButtonSmallStyle}"/>
<Setter Target="BitwiseGrid.MinWidth" Value="194"/>
<Setter Target="BitwiseGrid.MinHeight" Value="96"/>

View File

@ -177,14 +177,8 @@
<Setter Target="OperatorPanelRow.MinHeight" Value="{StaticResource OperatorPanelButtonRowSizeLarge}"/>
<Setter Target="TrigButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeLarge}"/>
<Setter Target="TrigButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeLarge}"/>
<Setter Target="TrigButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeLarge}"/>
<Setter Target="FuncButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeLarge}"/>
<Setter Target="FuncButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeLarge}"/>
<Setter Target="FuncButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeLarge}"/>
<Setter Target="TrigButton.Style" Value="{StaticResource OperatorPanelButtonLargeStyle}"/>
<Setter Target="FuncButton.Style" Value="{StaticResource OperatorPanelButtonLargeStyle}"/>
<Setter Target="TrigGrid.MinWidth" Value="516"/>
<Setter Target="TrigGrid.MinHeight" Value="192"/>
@ -273,13 +267,8 @@
<Setter Target="OperatorPanelRow.MinHeight" Value="{StaticResource OperatorPanelButtonRowSizeMedium}"/>
<Setter Target="TrigButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeMedium}"/>
<Setter Target="TrigButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeMedium}"/>
<Setter Target="TrigButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeMedium}"/>
<Setter Target="FuncButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeMedium}"/>
<Setter Target="FuncButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeMedium}"/>
<Setter Target="FuncButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeMedium}"/>
<Setter Target="TrigButton.Style" Value="{StaticResource OperatorPanelButtonMediumStyle}"/>
<Setter Target="FuncButton.Style" Value="{StaticResource OperatorPanelButtonMediumStyle}"/>
<Setter Target="TrigGrid.MinWidth" Value="480"/>
<Setter Target="TrigGrid.MinHeight" Value="144"/>
@ -366,13 +355,8 @@
<Setter Target="OperatorPanelRow.MinHeight" Value="{ThemeResource OperatorPanelButtonRowSizeSmall}"/>
<Setter Target="TrigButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeSmall}"/>
<Setter Target="TrigButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeSmall}"/>
<Setter Target="TrigButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeSmall}"/>
<Setter Target="FuncButton.ChevronFontSize" Value="{ThemeResource OperatorPanelChevronFontSizeSmall}"/>
<Setter Target="FuncButton.GlyphFontSize" Value="{ThemeResource OperatorPanelGlyphFontSizeSmall}"/>
<Setter Target="FuncButton.FontSize" Value="{ThemeResource OperatorPanelFontSizeSmall}"/>
<Setter Target="TrigButton.Style" Value="{StaticResource OperatorPanelButtonSmallStyle}"/>
<Setter Target="FuncButton.Style" Value="{StaticResource OperatorPanelButtonSmallStyle}"/>
<Setter Target="TrigGrid.MinWidth" Value="258"/>
<Setter Target="TrigGrid.MinHeight" Value="96"/>

View File

@ -0,0 +1,821 @@
<UserControl x:Class="CalculatorApp.EquationInputArea"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)"
xmlns:controls="using:CalculatorApp.Controls"
xmlns:converters="using:CalculatorApp.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:CalculatorApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
xmlns:vm="using:CalculatorApp.ViewModel"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<converters:BooleanToVisibilityNegationConverter x:Name="BooleanToVisibilityNegationConverter"/>
<DataTemplate x:Key="VariableDataTemplate" x:DataType="vm:VariableViewModel">
<Grid>
<Grid.Resources>
<ResourceDictionary>
<Style x:Key="VariableTextBoxStyle" TargetType="TextBox">
<Setter Property="Margin" Value="10,0,0,0"/>
<Setter Property="Padding" Value="2,6,2,2"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="InputScope" Value="Number"/>
<Setter Property="MaxWidth" Value="46"/>
<Setter Property="MinWidth" Value="30"/>
</Style>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="White"/>
<SolidColorBrush x:Key="SliderLegendBrush" Color="#B2ffffff"/>
<Style x:Key="VariableContainerStyle" TargetType="Border">
<Setter Property="Background" Value="#34000000"/>
</Style>
<Style x:Key="ThemedVariableTextBoxStyle"
BasedOn="{StaticResource VariableTextBoxStyle}"
TargetType="TextBox">
<Setter Property="BorderBrush" Value="#50ffffff"/>
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="Black"/>
<SolidColorBrush x:Key="SliderLegendBrush" Color="#B2000000"/>
<Style x:Key="VariableContainerStyle" TargetType="Border">
<Setter Property="Background" Value="#34ffffff"/>
</Style>
<Style x:Key="ThemedVariableTextBoxStyle"
BasedOn="{StaticResource VariableTextBoxStyle}"
TargetType="TextBox">
<Setter Property="BorderBrush" Value="#70000000"/>
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="{StaticResource SystemColorButtonFaceColor}"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="{StaticResource SystemColorButtonFaceColor}"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="{StaticResource SystemColorButtonTextColor}"/>
<SolidColorBrush x:Key="VariablesBackgroundBrush" Color="{StaticResource SystemColorBackgroundColor}"/>
<SolidColorBrush x:Key="SliderLegendBrush" Color="{StaticResource SystemColorWindowTextColor}"/>
<Style x:Key="VariableContainerStyle" TargetType="Border">
<Setter Property="Background" Value="{StaticResource SystemColorWindowColor}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{StaticResource SystemColorWindowTextColor}"/>
</Style>
<Style x:Key="ThemedVariableTextBoxStyle" TargetType="TextBox">
<Setter Property="InputScope" Value="Number"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="InputScope" Value="Number"/>
<Setter Property="MaxWidth" Value="80"/>
<Setter Property="MinWidth" Value="40"/>
</Style>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Grid.Resources>
<Border Margin="0,3" Style="{ThemeResource VariableContainerStyle}">
<StackPanel Padding="12,0" DataContext="{x:Bind}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock MinWidth="10"
Margin="0,4,0,0"
VerticalAlignment="Center"
FontWeight="SemiBold"
Text="{x:Bind Name}"/>
<TextBox x:Name="ValueTextBox"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource ThemedVariableTextBoxStyle}"
GotFocus="TextBoxGotFocus"
KeyDown="TextBoxKeyDown"
LosingFocus="TextBoxLosingFocus"
Text="{x:Bind Value, Mode=OneWay}"/>
<ToggleButton x:Uid="sliderOptionsButton"
Grid.Column="2"
Margin="0,0,-8,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Background="Transparent"
FontFamily="{StaticResource SymbolThemeFontFamily}"
Content="&#xE713;"
IsChecked="{x:Bind SliderSettingsVisible, Mode=TwoWay}">
<ToggleButton.Resources>
<SolidColorBrush x:Key="ToggleButtonBackgroundPressed" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundChecked" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundCheckedPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrushCheckedPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrushPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundCheckedPressed" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonForegroundPointerOver" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundPressed" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundChecked" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundCheckedPressed" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundCheckedPointerOver" Color="{ThemeResource SystemAccentColor}"/>
<x:Double x:Key="TextControlThemeMinWidth">32</x:Double>
</ToggleButton.Resources>
</ToggleButton>
</Grid>
<Grid Visibility="{x:Bind SliderSettingsVisible, Converter={StaticResource BooleanToVisibilityNegationConverter}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center"
Foreground="{ThemeResource SliderLegendBrush}"
Text="{x:Bind Min, Mode=OneWay}"/>
<Slider Grid.Column="1"
Margin="8,0"
VerticalAlignment="Center"
Maximum="{x:Bind Max, Mode=TwoWay}"
Minimum="{x:Bind Min, Mode=TwoWay}"
StepFrequency="{x:Bind Step, Mode=TwoWay}"
Value="{x:Bind Value, Mode=TwoWay}"/>
<TextBlock Grid.Column="2"
VerticalAlignment="Center"
Foreground="{ThemeResource SliderLegendBrush}"
Text="{x:Bind Max, Mode=OneWay}"/>
</Grid>
<Grid Grid.Row="1"
Padding="0,6,0,12"
HorizontalAlignment="Stretch"
Visibility="{x:Bind SliderSettingsVisible, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Uid="MinTextBlock"
Margin="0,4,0,0"
VerticalAlignment="Center"/>
<TextBox x:Name="MinTextBox"
Grid.Column="1"
Style="{StaticResource ThemedVariableTextBoxStyle}"
GotFocus="TextBoxGotFocus"
KeyDown="TextBoxKeyDown"
LosingFocus="TextBoxLosingFocus"
Text="{x:Bind Min, Mode=OneWay}"/>
</Grid>
<Grid Grid.Column="1"
Margin="4,0"
HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Uid="StepTextBlock"
Margin="0,4,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<TextBox x:Name="StepTextBox"
Grid.Column="1"
Style="{StaticResource ThemedVariableTextBoxStyle}"
GotFocus="TextBoxGotFocus"
KeyDown="TextBoxKeyDown"
LosingFocus="TextBoxLosingFocus"
Text="{x:Bind Step, Mode=OneWay}"/>
</Grid>
<Grid Grid.Column="2" HorizontalAlignment="Right">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Uid="MaxTextBlock"
Margin="0,4,0,0"
VerticalAlignment="Center"/>
<TextBox x:Name="MaxTextBox"
Grid.Column="1"
Style="{StaticResource ThemedVariableTextBoxStyle}"
GotFocus="TextBoxGotFocus"
KeyDown="TextBoxKeyDown"
LosingFocus="TextBoxLosingFocus"
Text="{x:Bind Max, Mode=OneWay}"/>
</Grid>
</Grid>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
<Style x:Key="EquationTextBoxStyle" TargetType="controls:EquationTextBox">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="Foreground" Value="{ThemeResource TextBoxForegroundThemeBrush}"/>
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Typography.StylisticSet20" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:EquationTextBox">
<Grid>
<Grid.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<Visibility x:Key="ColorRectangleVisibility">Collapsed</Visibility>
<SolidColorBrush x:Key="EquationTextBoxBorderBrush" Color="Transparent"/>
<SolidColorBrush x:Key="EquationTextBoxBorderBrushPointerOver" Color="{Binding EquationColor.Color, RelativeSource={RelativeSource TemplatedParent}}"/>
<SolidColorBrush x:Key="EquationTextBoxBorderBrushFocused" Color="{Binding EquationColor.Color, RelativeSource={RelativeSource TemplatedParent}}"/>
<SolidColorBrush x:Key="EquationTextBoxBorderBrushDisabled" Color="{Binding EquationColor.Color, RelativeSource={RelativeSource TemplatedParent}}"/>
<SolidColorBrush x:Key="EquationBoxAddBackgroundBrush" Color="#9d000000"/>
<SolidColorBrush x:Key="EquationBoxErrorBackgroundBrush" Color="#33EB5757"/>
<SolidColorBrush x:Key="EquationBoxErrorBorderBrush" Color="#FFEB5757"/>
<Thickness x:Key="EquationTextBoxBorderThickness">0,1,1,1</Thickness>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Visibility x:Key="ColorRectangleVisibility">Collapsed</Visibility>
<SolidColorBrush x:Key="EquationTextBoxBorderBrush" Color="Transparent"/>
<SolidColorBrush x:Key="EquationTextBoxBorderBrushPointerOver" Color="{Binding EquationColor.Color, RelativeSource={RelativeSource TemplatedParent}}"/>
<SolidColorBrush x:Key="EquationTextBoxBorderBrushFocused" Color="{Binding EquationColor.Color, RelativeSource={RelativeSource TemplatedParent}}"/>
<SolidColorBrush x:Key="EquationTextBoxBorderBrushDisabled" Color="{Binding EquationColor.Color, RelativeSource={RelativeSource TemplatedParent}}"/>
<SolidColorBrush x:Key="EquationBoxAddBackgroundBrush" Color="#D0FFFFFF"/>
<SolidColorBrush x:Key="EquationBoxErrorBackgroundBrush" Color="#33EB5757"/>
<SolidColorBrush x:Key="EquationBoxErrorBorderBrush" Color="#FFEB5757"/>
<Thickness x:Key="EquationTextBoxBorderThickness">0,1,1,1</Thickness>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Visibility x:Key="ColorRectangleVisibility">Visible</Visibility>
<StaticResource x:Key="EquationTextBoxBorderBrush" ResourceKey="TextControlBorderBrush"/>
<StaticResource x:Key="EquationTextBoxBorderBrushPointerOver" ResourceKey="TextControlBorderBrushPointerOver"/>
<StaticResource x:Key="EquationTextBoxBorderBrushFocused" ResourceKey="TextControlBorderBrushFocused"/>
<StaticResource x:Key="EquationTextBoxBorderBrushDisabled" ResourceKey="TextControlBorderBrushDisabled"/>
<SolidColorBrush x:Key="EquationBoxAddBackgroundBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
<SolidColorBrush x:Key="EquationBoxErrorBackgroundBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
<SolidColorBrush x:Key="EquationBoxErrorBorderBrush" Color="{ThemeResource SystemColorButtonTextColor}"/>
<Thickness x:Key="EquationTextBoxBorderThickness">1</Thickness>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="44"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Target="MathRichEditBox.PlaceholderText" Value=""/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="AddEquation">
<VisualState.Setters>
<Setter Target="EquationBoxBorder.Background" Value="{ThemeResource EquationBoxAddBackgroundBrush}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Error">
<VisualState.Setters>
<Setter Target="MathRichEditBox.PlaceholderText" Value=""/>
<Setter Target="EquationBoxBorder.BorderThickness" Value="1"/>
<Setter Target="EquationBoxBorder.BorderBrush" Value="{ThemeResource EquationBoxErrorBorderBrush}"/>
<Setter Target="EquationBoxBorder.Background" Value="{ThemeResource EquationBoxErrorBackgroundBrush}"/>
<Setter Target="ErrorIcon.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="EquationBoxBorder.BorderBrush" Value="{ThemeResource EquationTextBoxBorderBrushPointerOver}"/>
<Setter Target="ColorChooserButton.Visibility" Value="Visible"/>
<Setter Target="FunctionButton.Visibility" Value="Visible"/>
<Setter Target="RemoveButton.Visibility" Value="Visible"/>
<Setter Target="ErrorIcon.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOverError">
<VisualState.Setters>
<Setter Target="EquationBoxBorder.BorderBrush" Value="{ThemeResource EquationBoxErrorBorderBrush}"/>
<Setter Target="EquationBoxBorder.BorderThickness" Value="1"/>
<Setter Target="EquationBoxBorder.Background" Value="{ThemeResource EquationBoxErrorBackgroundBrush}"/>
<Setter Target="ColorChooserButton.Visibility" Value="Collapsed"/>
<Setter Target="FunctionButton.Visibility" Value="Collapsed"/>
<Setter Target="RemoveButton.Visibility" Value="Visible"/>
<Setter Target="ErrorIcon.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="EquationBoxBorder.Background" Value="{ThemeResource TextBoxDisabledBackgroundThemeBrush}"/>
<Setter Target="EquationBoxBorder.BorderBrush" Value="{ThemeResource EquationTextBoxBorderBrushPointerOver}"/>
<Setter Target="EquationTextBox.Background" Value="{ThemeResource AppControlTransparentButtonBackgroundBrush}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Target="EquationBoxBorder.BorderBrush" Value="{ThemeResource EquationTextBoxBorderBrushFocused}"/>
<Setter Target="EquationBoxBorder.Background" Value="{ThemeResource TextBoxBackgroundThemeBrush}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="FocusedError">
<VisualState.Setters>
<Setter Target="EquationBoxBorder.BorderBrush" Value="{ThemeResource EquationBoxErrorBorderBrush}"/>
<Setter Target="EquationBoxBorder.BorderThickness" Value="1"/>
<Setter Target="EquationBoxBorder.Background" Value="{ThemeResource TextBoxBackgroundThemeBrush}"/>
<Setter Target="ErrorIcon.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
<VisualState.Setters>
<Setter Target="DeleteButton.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="ButtonHideRemove">
<VisualState.Setters>
<Setter Target="RemoveButtonPanel.Visibility" Value="Collapsed"/>
<Setter Target="RemoveFunctionMenuItem.IsEnabled" Value="False"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="ButtonCollapsed"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle Grid.Column="0"
Width="10"
Margin="0,0,2,0"
Fill="{TemplateBinding EquationColor}"
Visibility="{ThemeResource ColorRectangleVisibility}"/>
<ToggleButton x:Name="EquationButton"
Grid.Column="1"
MinWidth="44"
MinHeight="44"
VerticalAlignment="Stretch">
<ToggleButton.Content>
<StackPanel x:Name="FunctionNumberLabel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent"
Orientation="Horizontal">
<FontIcon FontFamily="{StaticResource CalculatorFontFamily}" Glyph="&#xF893;"/>
<TextBlock Margin="-5,19,0,0"
FontSize="11"
Text="{TemplateBinding EquationButtonContentIndex}"/>
</StackPanel>
</ToggleButton.Content>
<ToggleButton.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<x:Double x:Key="EquationButtonOverlayPointerOverOpacity">0.3</x:Double>
<x:Double x:Key="EquationButtonOverlayPressedOpacity">0.5</x:Double>
<SolidColorBrush x:Key="EquationButtonOverlayBackgroundBrush" Color="White"/>
<SolidColorBrush x:Key="ToggleButtonBackground" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrush" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonForeground" Color="White"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundPointerOver" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrushPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundPressed" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrushPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="EquationButtonHideLineForegroundBrush"
Opacity="0.6"
Color="{StaticResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="EquationButtonHideLineBackgroundBrush"
Opacity="0.4"
Color="#FFFFFF"/>
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonBorderBrushChecked" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonForegroundChecked" ResourceKey="EquationButtonHideLineForegroundBrush"/>
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPointerOver" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonForegroundCheckedPointerOver" ResourceKey="EquationButtonHideLineForegroundBrush"/>
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPressed" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonForegroundCheckedPressed" ResourceKey="EquationButtonHideLineForegroundBrush"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<x:Double x:Key="EquationButtonOverlayPointerOverOpacity">0.2</x:Double>
<x:Double x:Key="EquationButtonOverlayPressedOpacity">0.4</x:Double>
<SolidColorBrush x:Key="EquationButtonOverlayBackgroundBrush" Color="Black"/>
<SolidColorBrush x:Key="ToggleButtonBackground" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrush" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonForeground" Color="White"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundPointerOver" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrushPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundPressed" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrushPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="EquationButtonHideLineForegroundBrush" Color="{StaticResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="EquationButtonHideLineBackgroundBrush"
Opacity="0.4"
Color="#000000"/>
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonBorderBrushChecked" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonForegroundChecked" ResourceKey="EquationButtonHideLineForegroundBrush"/>
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPointerOver" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonForegroundCheckedPointerOver" ResourceKey="EquationButtonHideLineForegroundBrush"/>
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPressed" ResourceKey="EquationButtonHideLineBackgroundBrush"/>
<StaticResource x:Key="ToggleButtonForegroundCheckedPressed" ResourceKey="EquationButtonHideLineForegroundBrush"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<x:Double x:Key="EquationButtonOverlayPointerOverOpacity">0</x:Double>
<x:Double x:Key="EquationButtonOverlayPressedOpacity">0</x:Double>
<SolidColorBrush x:Key="EquationButtonOverlayBackgroundBrush" Color="Transparent"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ToggleButton.Resources>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid x:Name="RootGrid"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="1">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonBackground}"/>
<Setter Target="RootGrid.BorderBrush" Value="{ThemeResource ToggleButtonBorderBrush}"/>
<Setter Target="Overlay.Opacity" Value="0.0"/>
<Setter Target="ContentPresenter.Visibility" Value="Visible"/>
<Setter Target="ShowHideIcon.Visibility" Value="Collapsed"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonBackgroundPointerOver}"/>
<Setter Target="RootGrid.BorderBrush" Value="{ThemeResource ToggleButtonBorderBrushPointerOver}"/>
<Setter Target="ShowHideIcon.Foreground" Value="{ThemeResource ToggleButtonForegroundPointerOver}"/>
<Setter Target="Overlay.Opacity" Value="{StaticResource EquationButtonOverlayPointerOverOpacity}"/>
<Setter Target="ContentPresenter.Visibility" Value="Collapsed"/>
<Setter Target="ShowHideIcon.Visibility" Value="Visible"/>
<Setter Target="ShowHideIcon.Glyph" Value="&#xF6AC;"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonBackgroundPressed}"/>
<Setter Target="RootGrid.BorderBrush" Value="{ThemeResource ToggleButtonBorderBrush}"/>
<Setter Target="Overlay.Opacity" Value="{StaticResource EquationButtonOverlayPressedOpacity}"/>
<Setter Target="ShowHideIcon.Foreground" Value="{ThemeResource ToggleButtonForegroundPressed}"/>
<Setter Target="ContentPresenter.Visibility" Value="Collapsed"/>
<Setter Target="ShowHideIcon.Visibility" Value="Visible"/>
<Setter Target="ShowHideIcon.Glyph" Value="&#xF6AC;"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonBackgroundChecked}"/>
<Setter Target="RootGrid.BorderBrush" Value="{ThemeResource ToggleButtonBorderBrushChecked}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundChecked}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="CheckedPointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonBackgroundCheckedPointerOver}"/>
<Setter Target="RootGrid.BorderBrush" Value="{ThemeResource ToggleButtonBorderBrushCheckedPointerOver}"/>
<Setter Target="ShowHideIcon.Foreground" Value="{ThemeResource ToggleButtonForegroundCheckedPointerOver}"/>
<Setter Target="Overlay.Opacity" Value="{StaticResource EquationButtonOverlayPointerOverOpacity}"/>
<Setter Target="ContentPresenter.Visibility" Value="Collapsed"/>
<Setter Target="ShowHideIcon.Visibility" Value="Visible"/>
<Setter Target="ShowHideIcon.Glyph" Value="&#xE890;"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="CheckedPressed">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonBackgroundCheckedPressed}"/>
<Setter Target="RootGrid.BorderBrush" Value="{ThemeResource ToggleButtonBorderBrushCheckedPressed}"/>
<Setter Target="ShowHideIcon.Foreground" Value="{ThemeResource ToggleButtonForegroundCheckedPressed}"/>
<Setter Target="Overlay.Opacity" Value="{StaticResource EquationButtonOverlayPressedOpacity}"/>
<Setter Target="ContentPresenter.Visibility" Value="Collapsed"/>
<Setter Target="ShowHideIcon.Visibility" Value="Visible"/>
<Setter Target="ShowHideIcon.Glyph" Value="&#xE890;"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="Overlay"
Fill="{ThemeResource EquationButtonOverlayBackgroundBrush}"
Opacity="0"
IsHitTestVisible="False"/>
<ContentPresenter x:Name="ContentPresenter"
AutomationProperties.AccessibilityView="Raw"
IsHitTestVisible="False"/>
<FontIcon x:Name="ShowHideIcon"
FontFamily="{StaticResource CalculatorFontFamily}"
Visibility="Collapsed"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Border x:Name="EquationBoxBorder"
Grid.Column="2"
Background="{ThemeResource TextControlBackground}"
BorderBrush="{ThemeResource EquationTextBoxBorderBrush}"
BorderThickness="{ThemeResource EquationTextBoxBorderThickness}"
contract7Present:BackgroundSizing="OuterBorderEdge">
<Grid contract7Present:Margin="{TemplateBinding BorderThickness}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:MathRichEditBox x:Name="MathRichEditBox"
x:Uid="mathRichEditBox"
MinHeight="44"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Stretch"
Style="{StaticResource MathRichEditBoxStyle}"
BorderThickness="0"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
AcceptsReturn="false"
InputScope="Text"
MathText="{Binding MathEquation, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
MaxLength="2048"
TextWrapping="NoWrap">
<controls:MathRichEditBox.ContextFlyout>
<MenuFlyout x:Name="MathRichEditContextMenu">
<MenuFlyoutItem x:Name="FunctionAnalysisMenuItem">
<MenuFlyoutItem.Icon>
<FontIcon FontFamily="{StaticResource CalculatorFontFamily}" Glyph="&#xE3B5;"/>
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem x:Name="ChangeFunctionStyleMenuItem">
<MenuFlyoutItem.Icon>
<FontIcon FontFamily="{StaticResource CalculatorFontFamily}" Glyph="&#xE790;"/>
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem x:Name="RemoveFunctionMenuItem">
<MenuFlyoutItem.Icon>
<FontIcon FontFamily="{StaticResource CalculatorFontFamily}" Glyph="&#xECC9;"/>
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
</MenuFlyout>
</controls:MathRichEditBox.ContextFlyout>
</controls:MathRichEditBox>
<Button x:Name="FunctionButton"
x:Uid="functionAnalysisButton"
Grid.Column="1"
MinWidth="34"
Margin="1,2"
VerticalAlignment="Stretch"
Style="{ThemeResource ButtonRevealStyle}"
FontFamily="{StaticResource CalculatorFontFamily}"
AutomationProperties.AccessibilityView="Raw"
Content="&#xE3B5;"
IsTabStop="False"
Visibility="Collapsed">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="ButtonForegroundPressed" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ButtonRevealBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ButtonRevealBackgroundPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ButtonRevealBackground" Color="Transparent"/>
<SolidColorBrush x:Key="ButtonRevealBackgroundDisabled" Color="Transparent"/>
<RevealBorderBrush x:Key="ButtonRevealBorderBrush"
Opacity="0.33"
FallbackColor="Transparent"
TargetTheme="{ThemeResource CalcApplicationTheme}"
Color="Transparent"/>
<RevealBorderBrush x:Key="ButtonRevealBorderBrushPointerOver"
Opacity="0.33"
FallbackColor="{StaticResource SystemBaseMediumLowColor}"
TargetTheme="{ThemeResource CalcApplicationTheme}"
Color="{StaticResource SystemRevealBaseMediumLowColor}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast"/>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
</Button>
<ToggleButton x:Name="ColorChooserButton"
x:Uid="colorChooserButton"
Grid.Column="2"
MinWidth="34"
Margin="1,2"
VerticalAlignment="Stretch"
Style="{ThemeResource ToggleButtonRevealStyle}"
FontFamily="{StaticResource CalculatorFontFamily}"
AutomationProperties.AccessibilityView="Raw"
Content="&#xE790;"
IsTabStop="False"
Visibility="Collapsed">
<ToggleButton.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Name="ToggleButtonForegroundPressed" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Name="ToggleButtonForegroundChecked" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Name="ToggleButtonForegroundPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Name="ToggleButtonRevealBackground" Color="Transparent"/>
<SolidColorBrush x:Name="ToggleButtonRevealBackgroundChecked" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Name="ToggleButtonRevealBackgroundCheckedPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Name="ToggleButtonRevealBackgroundCheckedPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Name="ToggleButtonRevealBackgroundPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Name="ToggleButtonRevealBackgroundPointerOver" Color="{ThemeResource AppControlTransparentButtonBackgroundBrush}"/>
<RevealBorderBrush x:Key="ToggleButtonRevealBorderBrush"
Opacity="0.33"
FallbackColor="Transparent"
TargetTheme="{ThemeResource CalcApplicationTheme}"
Color="Transparent"/>
<RevealBorderBrush x:Key="ToggleButtonRevealBorderBrushPointerOver"
Opacity="0.33"
FallbackColor="{StaticResource SystemBaseMediumLowColor}"
TargetTheme="{ThemeResource CalcApplicationTheme}"
Color="{StaticResource SystemRevealBaseMediumLowColor}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast"/>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ToggleButton.Resources>
</ToggleButton>
<Grid x:Name="RemoveButtonPanel" Grid.Column="3">
<Button x:Name="RemoveButton"
x:Uid="removeButton"
MinWidth="34"
Margin="1,2"
VerticalAlignment="Stretch"
Style="{StaticResource ButtonRevealStyle}"
FontFamily="{StaticResource CalculatorFontFamily}"
AutomationProperties.AccessibilityView="Raw"
Content="&#xECC9;"
IsTabStop="False"
Visibility="Collapsed">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="ButtonForegroundPressed" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ButtonRevealBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ButtonRevealBackgroundPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ButtonRevealBackground" Color="Transparent"/>
<RevealBorderBrush x:Key="ButtonRevealBorderBrush"
Opacity="0.33"
FallbackColor="Transparent"
TargetTheme="{ThemeResource CalcApplicationTheme}"
Color="Transparent"/>
<RevealBorderBrush x:Key="ButtonRevealBorderBrushPointerOver"
Opacity="0.33"
FallbackColor="{StaticResource SystemBaseMediumLowColor}"
TargetTheme="{ThemeResource CalcApplicationTheme}"
Color="{StaticResource SystemRevealBaseMediumLowColor}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast"/>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
</Button>
</Grid>
<Button x:Name="DeleteButton"
Grid.Column="3"
MinWidth="34"
Margin="1,2"
VerticalAlignment="Stretch"
Style="{StaticResource ButtonRevealStyle}"
Foreground="{ThemeResource TextControlForegroundFocused}"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
AutomationProperties.AccessibilityView="Raw"
Content="&#xE10A;"
IsTabStop="False"
Visibility="Collapsed">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="ButtonForegroundPressed" Color="{ThemeResource SystemChromeWhiteColor}"/>
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ButtonRevealBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ButtonRevealBackgroundPressed" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=EquationColor.Color}"/>
<SolidColorBrush x:Key="ButtonRevealBackground" Color="Transparent"/>
<RevealBorderBrush x:Key="ButtonRevealBorderBrush"
Opacity="0.33"
FallbackColor="Transparent"
TargetTheme="{ThemeResource CalcApplicationTheme}"
Color="Transparent"/>
<RevealBorderBrush x:Key="ButtonRevealBorderBrushPointerOver"
Opacity="0.33"
FallbackColor="{StaticResource SystemBaseMediumLowColor}"
TargetTheme="{ThemeResource CalcApplicationTheme}"
Color="{StaticResource SystemRevealBaseMediumLowColor}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast"/>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
</Button>
<FontIcon x:Name="ErrorIcon"
Grid.Column="3"
MinWidth="28"
VerticalAlignment="Stretch"
Foreground="{ThemeResource ButtonForeground}"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16"
AutomationProperties.AccessibilityView="Raw"
Glyph="&#xE7BA;"
Visibility="Collapsed"/>
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<mux:ItemsRepeaterScrollHost>
<ScrollViewer>
<StackPanel>
<mux:ItemsRepeater x:Name="EquationInputList"
ItemsSource="{x:Bind Equations}"
TabFocusNavigation="Local">
<mux:ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="vm:EquationViewModel">
<controls:EquationTextBox x:Uid="EquationInputButton"
Margin="1,0,1,2"
Style="{StaticResource EquationTextBoxStyle}"
DataContext="{x:Bind Mode=OneWay}"
DataContextChanged="EquationTextBox_DataContextChanged"
EquationButtonClicked="EquationTextBox_EquationButtonClicked"
EquationButtonContentIndex="{x:Bind FunctionLabelIndex, Mode=OneWay}"
EquationColor="{x:Bind local:EquationInputArea.ToSolidColorBrush(LineColor), Mode=OneWay}"
EquationSubmitted="EquationTextBox_Submitted"
GotFocus="EquationTextBox_GotFocus"
HasError="{x:Bind GraphEquation.HasGraphError, Mode=OneWay}"
IsAddEquationMode="{x:Bind IsLastItemInList, Mode=OneWay}"
KeyGraphFeaturesButtonClicked="EquationTextBox_KeyGraphFeaturesButtonClicked"
Loaded="EquationTextBox_Loaded"
LostFocus="EquationTextBox_LostFocus"
MathEquation="{x:Bind Expression, Mode=TwoWay}"
RemoveButtonClicked="EquationTextBox_RemoveButtonClicked">
<controls:EquationTextBox.ColorChooserFlyout>
<Flyout x:Name="ColorChooserFlyout"
x:Uid="ColorChooserFlyout"
Placement="Bottom">
<local:EquationStylePanelControl SelectedColor="{x:Bind LineColor, Mode=TwoWay}"/>
</Flyout>
</controls:EquationTextBox.ColorChooserFlyout>
</controls:EquationTextBox>
</DataTemplate>
</mux:ItemsRepeater.ItemTemplate>
</mux:ItemsRepeater>
<StackPanel Margin="12" Visibility="{x:Bind local:EquationInputArea.ManageEditVariablesButtonVisibility(Variables.Size), Mode=OneWay}">
<TextBlock x:Uid="VaiablesHeader"
Margin="0,6"
FontWeight="SemiBold"/>
<mux:ItemsRepeater x:Name="VariableListView"
ItemTemplate="{StaticResource VariableDataTemplate}"
ItemsSource="{x:Bind Variables, Mode=OneWay}"
TabFocusNavigation="Local">
<mux:ItemsRepeater.Transitions>
<TransitionCollection>
<AddDeleteThemeTransition/>
</TransitionCollection>
</mux:ItemsRepeater.Transitions>
</mux:ItemsRepeater>
</StackPanel>
</StackPanel>
</ScrollViewer>
</mux:ItemsRepeaterScrollHost>
</UserControl>

View File

@ -0,0 +1,378 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "EquationInputArea.xaml.h"
#include "Utils/VisualTree.h"
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace GraphControl;
using namespace CalculatorApp::ViewModel;
using namespace CalculatorApp::Controls;
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::Foundation;
using namespace Windows::System;
using namespace Windows::UI;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Input;
using namespace GraphControl;
using namespace Calculator::Utils;
namespace
{
inline constexpr auto maxEquationSize = 14;
inline constexpr std::array<int, 14> colorAssignmentMapping = { 0, 3, 7, 10, 1, 4, 8, 11, 2, 5, 9, 12, 6, 13 };
StringReference EquationsPropertyName(L"Equations");
}
EquationInputArea::EquationInputArea()
: m_lastLineColorIndex{ -1 }
, m_AvailableColors{ ref new Vector<SolidColorBrush ^>() }
, m_accessibilitySettings{ ref new AccessibilitySettings() }
, m_equationToFocus{ nullptr }
{
m_accessibilitySettings->HighContrastChanged +=
ref new TypedEventHandler<AccessibilitySettings ^, Object ^>(this, &EquationInputArea::OnHighContrastChanged);
ReloadAvailableColors(m_accessibilitySettings->HighContrast);
InitializeComponent();
}
void EquationInputArea::OnPropertyChanged(String ^ propertyName)
{
if (propertyName == EquationsPropertyName)
{
OnEquationsPropertyChanged();
}
}
void EquationInputArea::OnEquationsPropertyChanged()
{
if (Equations != nullptr && Equations->Size == 0)
{
AddNewEquation();
}
}
void EquationInputArea::AddNewEquation()
{
if (Equations->Size > 0)
{
Equations->GetAt(Equations->Size - 1)->IsLastItemInList = false;
}
// Cap equations at 14
if (Equations->Size >= maxEquationSize)
{
return;
}
m_lastLineColorIndex = (m_lastLineColorIndex + 1) % AvailableColors->Size;
int colorIndex;
if (m_accessibilitySettings->HighContrast)
{
colorIndex = m_lastLineColorIndex;
}
else
{
colorIndex = colorAssignmentMapping[m_lastLineColorIndex];
}
auto eq = ref new EquationViewModel(ref new Equation(), ++m_lastFunctionLabelIndex, AvailableColors->GetAt(colorIndex)->Color);
eq->IsLastItemInList = true;
m_equationToFocus = eq;
Equations->Append(eq);
}
void EquationInputArea::EquationTextBox_GotFocus(Object ^ sender, RoutedEventArgs ^ e)
{
KeyboardShortcutManager::HonorShortcuts(false);
}
void EquationInputArea::EquationTextBox_LostFocus(Object ^ sender, RoutedEventArgs ^ e)
{
KeyboardShortcutManager::HonorShortcuts(true);
}
void EquationInputArea::EquationTextBox_Submitted(Object ^ sender, MathRichEditBoxSubmission ^ submission)
{
auto tb = static_cast<EquationTextBox ^>(sender);
if (tb == nullptr)
{
return;
}
auto eq = static_cast<EquationViewModel ^>(tb->DataContext);
if (eq == nullptr)
{
return;
}
if (submission->Source == EquationSubmissionSource::ENTER_KEY
|| (submission->Source == EquationSubmissionSource::FOCUS_LOST && submission->HasTextChanged && eq->Expression != nullptr
&& eq->Expression->Length() > 0))
{
unsigned int index = 0;
if (Equations->IndexOf(eq, &index))
{
if (index == Equations->Size - 1)
{
// If it's the last equation of the list
AddNewEquation();
}
else
{
if (submission->Source == EquationSubmissionSource::ENTER_KEY)
{
auto nextEquation = Equations->GetAt(index + 1);
FocusEquationTextBox(nextEquation);
}
}
}
}
}
void EquationInputArea::FocusEquationTextBox(EquationViewModel ^ equation)
{
unsigned int index;
if (!Equations->IndexOf(equation, &index) || index < 0)
{
return;
}
auto container = EquationInputList->TryGetElement(index);
if (container == nullptr)
{
return;
}
auto equationTextBox = dynamic_cast<EquationTextBox ^>(container);
if (equationTextBox != nullptr)
{
equationTextBox->FocusTextBox();
}
else
{
auto equationInput = VisualTree::FindDescendantByName(container, "EquationInputButton");
if (equationInput == nullptr)
{
return;
}
equationTextBox = dynamic_cast<EquationTextBox ^>(equationInput);
if (equationTextBox != nullptr)
{
equationTextBox->FocusTextBox();
}
}
}
void EquationInputArea::EquationTextBox_RemoveButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
auto tb = static_cast<EquationTextBox ^>(sender);
auto eq = static_cast<EquationViewModel ^>(tb->DataContext);
unsigned int index;
if (Equations->IndexOf(eq, &index))
{
if (eq->FunctionLabelIndex == m_lastFunctionLabelIndex)
{
m_lastFunctionLabelIndex--;
}
if (index == Equations->Size - 1 && Equations->Size > 1)
{
Equations->GetAt(Equations->Size - 2)->IsLastItemInList = true;
}
Equations->RemoveAt(index);
}
}
void EquationInputArea::EquationTextBox_KeyGraphFeaturesButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
auto tb = static_cast<EquationTextBox ^>(sender);
auto eq = static_cast<EquationViewModel ^>(tb->DataContext);
KeyGraphFeaturesRequested(this, eq);
}
void EquationInputArea::EquationTextBox_EquationButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
auto tb = static_cast<EquationTextBox ^>(sender);
auto eq = static_cast<EquationViewModel ^>(tb->DataContext);
eq->IsLineEnabled = !eq->IsLineEnabled;
}
void EquationInputArea::EquationTextBox_Loaded(Object ^ sender, RoutedEventArgs ^ e)
{
auto tb = static_cast<EquationTextBox ^>(sender);
auto colorChooser = static_cast<EquationStylePanelControl ^>(tb->ColorChooserFlyout->Content);
colorChooser->AvailableColors = AvailableColors;
if (m_equationToFocus != nullptr && tb->DataContext == m_equationToFocus)
{
auto copyEquationToFocus = m_equationToFocus;
m_equationToFocus = nullptr;
tb->FocusTextBox();
unsigned int index;
if (Equations->IndexOf(copyEquationToFocus, &index))
{
auto container = EquationInputList->TryGetElement(index);
if (container != nullptr)
{
container->StartBringIntoView();
}
}
}
}
void EquationInputArea::EquationTextBox_DataContextChanged(Windows::UI::Xaml::FrameworkElement ^ sender, Windows::UI::Xaml::DataContextChangedEventArgs ^ args)
{
auto tb = static_cast<EquationTextBox ^>(sender);
if (!tb->IsLoaded)
{
return;
}
FocusEquationIfNecessary(tb);
}
void EquationInputArea::FocusEquationIfNecessary(CalculatorApp::Controls::EquationTextBox ^ textBox)
{
if (m_equationToFocus != nullptr && textBox->DataContext == m_equationToFocus)
{
m_equationToFocus = nullptr;
textBox->FocusTextBox();
unsigned int index;
if (Equations->IndexOf(m_equationToFocus, &index))
{
auto container = EquationInputList->TryGetElement(index);
if (container != nullptr)
{
container->StartBringIntoView();
}
}
}
}
void EquationInputArea::OnHighContrastChanged(AccessibilitySettings ^ sender, Object ^ args)
{
ReloadAvailableColors(sender->HighContrast);
}
void EquationInputArea::ReloadAvailableColors(bool isHighContrast)
{
m_AvailableColors->Clear();
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush1")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush2")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush3")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush4")));
// If this is not high contrast, we have all 16 colors, otherwise we will restrict this to a subset of high contrast colors
if (!isHighContrast)
{
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush5")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush6")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush7")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush8")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush9")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush10")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush11")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush12")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush13")));
m_AvailableColors->Append(safe_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"EquationBrush14")));
}
// If there are no equations to reload, quit early
if (Equations == nullptr || Equations->Size == 0)
{
return;
}
// Reassign colors for each equation
m_lastLineColorIndex = -1;
for (auto equationViewModel : Equations)
{
m_lastLineColorIndex = (m_lastLineColorIndex + 1) % AvailableColors->Size;
equationViewModel->LineColor = AvailableColors->GetAt(m_lastLineColorIndex)->Color;
}
}
void EquationInputArea::TextBoxGotFocus(TextBox ^ sender, RoutedEventArgs ^ e)
{
sender->SelectAll();
}
void EquationInputArea::SubmitTextbox(TextBox ^ sender)
{
auto variableViewModel = static_cast<VariableViewModel ^>(sender->DataContext);
double val;
if (sender->Name == "ValueTextBox")
{
val = validateDouble(sender->Text, variableViewModel->Value);
variableViewModel->Value = val;
}
else if (sender->Name == "MinTextBox")
{
val = validateDouble(sender->Text, variableViewModel->Min);
variableViewModel->Min = val;
}
else if (sender->Name == "MaxTextBox")
{
val = validateDouble(sender->Text, variableViewModel->Max);
variableViewModel->Max = val;
}
else if (sender->Name == "StepTextBox")
{
val = validateDouble(sender->Text, variableViewModel->Step);
variableViewModel->Step = val;
}
else
{
return;
}
wostringstream oss;
oss << std::noshowpoint << val;
sender->Text = ref new String(oss.str().c_str());
}
void EquationInputArea::TextBoxLosingFocus(TextBox ^ sender, LosingFocusEventArgs ^)
{
SubmitTextbox(sender);
}
void EquationInputArea::TextBoxKeyDown(TextBox ^ sender, KeyRoutedEventArgs ^ e)
{
if (e->Key == ::VirtualKey::Enter)
{
SubmitTextbox(sender);
}
}
double EquationInputArea::validateDouble(String ^ value, double defaultValue)
{
try
{
return stod(value->Data());
}
catch (...)
{
return defaultValue;
}
}
::Visibility EquationInputArea::ManageEditVariablesButtonVisibility(unsigned int numberOfVariables)
{
return numberOfVariables == 0 ? ::Visibility::Collapsed : ::Visibility::Visible;
}

View File

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "Views/GraphingCalculator/EquationInputArea.g.h"
#include "CalcViewModel/Common/Utils.h"
#include "CalcViewModel/GraphingCalculator/EquationViewModel.h"
#include "CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h"
#include "EquationStylePanelControl.xaml.h"
#include "Common/KeyboardShortcutManager.h"
#include "Controls/EquationTextBox.h"
#include "Converters/BooleanNegationConverter.h"
#include "Controls/MathRichEditBox.h"
namespace CalculatorApp
{
public ref class EquationInputArea sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
EquationInputArea();
OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged);
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector<ViewModel::EquationViewModel ^> ^, Equations);
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector<ViewModel::VariableViewModel ^> ^, Variables);
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector<Windows::UI::Xaml::Media::SolidColorBrush ^> ^, AvailableColors);
event Windows::Foundation::EventHandler<ViewModel::EquationViewModel^>^ KeyGraphFeaturesRequested;
public:
static Windows::UI::Xaml::Visibility ManageEditVariablesButtonVisibility(unsigned int numberOfVariables);
static Windows::UI::Xaml::Media::SolidColorBrush
^ ToSolidColorBrush(Windows::UI::Color color) { return ref new Windows::UI::Xaml::Media::SolidColorBrush(color); }
private:
void OnPropertyChanged(Platform::String^ propertyName);
void OnEquationsPropertyChanged();
void AddNewEquation();
void EquationTextBox_GotFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void EquationTextBox_LostFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void EquationTextBox_Submitted(Platform::Object ^ sender, CalculatorApp::Controls::MathRichEditBoxSubmission ^ e);
void OnHighContrastChanged(Windows::UI::ViewManagement::AccessibilitySettings ^ sender, Platform::Object ^ args);
void ReloadAvailableColors(bool isHighContrast);
void FocusEquationTextBox(ViewModel::EquationViewModel ^ equation);
void EquationTextBox_RemoveButtonClicked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void EquationTextBox_KeyGraphFeaturesButtonClicked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void EquationTextBox_EquationButtonClicked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void EquationTextBox_Loaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void EquationTextBox_DataContextChanged(Windows::UI::Xaml::FrameworkElement ^ sender, Windows::UI::Xaml::DataContextChangedEventArgs ^ args);
void FocusEquationIfNecessary(_In_ CalculatorApp::Controls::EquationTextBox ^ textBox);
double validateDouble(Platform::String ^ value, double defaultValue);
void TextBoxGotFocus(Windows::UI::Xaml::Controls::TextBox ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void TextBoxLosingFocus(Windows::UI::Xaml::Controls::TextBox ^ textbox, Windows::UI::Xaml::Input::LosingFocusEventArgs ^ args);
void TextBoxKeyDown(Windows::UI::Xaml::Controls::TextBox ^ textbox, Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e);
void SubmitTextbox(Windows::UI::Xaml::Controls::TextBox ^ textbox);
Windows::UI::ViewManagement::AccessibilitySettings ^ m_accessibilitySettings;
int m_lastLineColorIndex;
int m_lastFunctionLabelIndex;
ViewModel::EquationViewModel ^ m_equationToFocus;
};
}

View File

@ -0,0 +1,553 @@
<UserControl x:Class="CalculatorApp.GraphingCalculator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)"
xmlns:converters="using:CalculatorApp.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:graphControl="using:GraphControl"
xmlns:local="using:CalculatorApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="Control"
DataContextChanged="GraphingCalculator_DataContextChanged"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="GraphToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{ThemeResource AppControlForegroundTransparentRevealBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Margin" Value="-1"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="FontSize" Value="{StaticResource CaptionFontSize}"/>
<Setter Property="FontWeight" Value="Normal"/>
</Style>
<Style x:Key="GraphButtonStyle" TargetType="Button">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{ThemeResource AppControlForegroundTransparentRevealBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Margin" Value="-1"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="FontSize" Value="{StaticResource CaptionFontSize}"/>
<Setter Property="FontWeight" Value="Normal"/>
</Style>
<Style x:Key="GraphRepeatButtonStyle" TargetType="RepeatButton">
<Setter Property="Background" Value="{ThemeResource AppControlTransparentButtonBackgroundBrush}"/>
<Setter Property="Foreground" Value="{ThemeResource RepeatButtonForeground}"/>
<Setter Property="BorderBrush" Value="{ThemeResource AppControlForegroundTransparentRevealBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Margin" Value="-1"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Delay" Value="500"/>
<Setter Property="Interval" Value="40"/>
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
<Setter Property="FocusVisualMargin" Value="-3"/>
</Style>
<Style x:Key="SwitchModeToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundTransparentBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource AppControlForegroundTransparentRevealBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="{ThemeResource ButtonPadding}"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
<Setter Property="FocusVisualMargin" Value="-3"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid x:Name="RootGrid"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver"/>
<Setter Target="RootGrid.Background" Value="{ThemeResource AppControlHoverButtonFaceBrush}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"/>
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed"/>
<Setter Target="RootGrid.Background" Value="{ThemeResource AppControlPressedButtonFaceBrush}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"/>
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundDisabled}"/>
<Setter Target="ContentPresenter.Opacity" Value="0.5"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource SystemControlDisabledBaseLowBrush}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushChecked}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource SystemAccentColor}"/>
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedPointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver"/>
<Setter Target="RootGrid.Background" Value="{ThemeResource AppControlHoverButtonFaceBrush}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource SystemAccentColor}"/>
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedPressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed"/>
<Setter Target="RootGrid.Background" Value="{ThemeResource AppControlPressedButtonFaceBrush}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource SystemAccentColor}"/>
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedDisabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundDisabled}"/>
<Setter Target="ContentPresenter.Opacity" Value="0.5"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource SystemAccentColor}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Indeterminate">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminate}"/>
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminate}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminate}"/>
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminatePointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver"/>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminatePointerOver}"/>
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminatePointerOver}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminatePointerOver}"/>
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminatePressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed"/>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminatePressed}"/>
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminatePressed}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminatePressed}"/>
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminateDisabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminateDisabled}"/>
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminateDisabled}"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminateDisabled}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
CornerRadius="{TemplateBinding CornerRadius}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<converters:BooleanToVisibilityNegationConverter x:Name="BooleanToVisibilityNegationConverter"/>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<Style x:Key="ThemedSwitchModeToggleButtonStyle"
BasedOn="{StaticResource SwitchModeToggleButtonStyle}"
TargetType="ToggleButton"/>
<Style x:Key="GraphControlCommandPanel" TargetType="Border">
<Setter Property="Background" Value="{StaticResource SystemControlAcrylicElementBrush}"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="CornerRadius" Value="4"/>
</Style>
<Style x:Key="ThemedGraphRepeatButtonStyle"
BasedOn="{StaticResource GraphRepeatButtonStyle}"
TargetType="RepeatButton"/>
<Style x:Key="ThemedGraphButtonStyle"
BasedOn="{StaticResource GraphButtonStyle}"
TargetType="Button"/>
<Style x:Key="ThemedGraphToggleButtonStyle"
BasedOn="{StaticResource GraphToggleButtonStyle}"
TargetType="ToggleButton"/>
<Style x:Key="GraphTooltipStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="#e0e0e0"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Background" Value="{ThemeResource SystemControlAcrylicElementBrush}"/>
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Style x:Key="ThemedSwitchModeToggleButtonStyle"
BasedOn="{StaticResource SwitchModeToggleButtonStyle}"
TargetType="ToggleButton"/>
<Style x:Key="GraphControlCommandPanel" TargetType="Border">
<Setter Property="Background" Value="{StaticResource SystemControlAcrylicElementBrush}"/>
<Setter Property="BorderBrush" Value="#e0e0e0"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="4"/>
</Style>
<Style x:Key="ThemedGraphRepeatButtonStyle"
BasedOn="{StaticResource GraphRepeatButtonStyle}"
TargetType="RepeatButton"/>
<Style x:Key="ThemedGraphButtonStyle"
BasedOn="{StaticResource GraphButtonStyle}"
TargetType="Button"/>
<Style x:Key="ThemedGraphToggleButtonStyle"
BasedOn="{StaticResource GraphToggleButtonStyle}"
TargetType="ToggleButton"/>
<Style x:Key="GraphTooltipStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="#e0e0e0"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Background" Value="{ThemeResource SystemControlAcrylicElementBrush}"/>
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Style x:Key="ThemedSwitchModeToggleButtonStyle" TargetType="ToggleButton"/>
<Style x:Key="GraphControlCommandPanel" TargetType="Border"/>
<Style x:Key="ThemedGraphRepeatButtonStyle" TargetType="RepeatButton">
<Setter Property="Margin" Value="1"/>
</Style>
<Style x:Key="ThemedGraphButtonStyle" TargetType="Button">
<Setter Property="Margin" Value="1"/>
</Style>
<Style x:Key="ThemedGraphToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Margin" Value="1"/>
</Style>
<Style x:Key="GraphTooltipStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="{ThemeResource ToolTipBorderBrush}"/>
<Setter Property="BorderThickness" Value="{ThemeResource ToolTipBorderThemeThickness}"/>
<Setter Property="Background" Value="{ThemeResource ToolTipBackground}"/>
</Style>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="RootGrid">
<Grid.RowDefinitions>
<RowDefinition x:Name="RowHamburger" Height="{StaticResource HamburgerHeightGridLength}"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"
MinWidth="300"
MaxWidth="420"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="ColumnsState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="800"/>
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="SmallState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Control.IsSmallState" Value="True"/>
<Setter Target="LeftGrid.(Grid.ColumnSpan)" Value="2"/>
<Setter Target="RightGrid.(Grid.ColumnSpan)" Value="2"/>
<Setter Target="RightGrid.(Grid.Column)" Value="0"/>
<Setter Target="SwitchModeToggleButton.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- Top panel -->
<Grid Grid.ColumnSpan="2">
<ToggleButton x:Name="SwitchModeToggleButton"
x:Uid="SwitchModeToggleButton"
Margin="0,2,4,2"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Style="{ThemeResource ThemedSwitchModeToggleButtonStyle}"
AutomationProperties.AutomationId="SwitchModeToggleButton"
AutomationProperties.Name="{x:Bind local:GraphingCalculator.GetInfoForSwitchModeToggleButton(SwitchModeToggleButton.IsChecked.Value), Mode=OneWay}"
Checked="SwitchModeToggleButton_Checked"
ToolTipService.ToolTip="{x:Bind local:GraphingCalculator.GetInfoForSwitchModeToggleButton(SwitchModeToggleButton.IsChecked.Value), Mode=OneWay}"
Unchecked="SwitchModeToggleButton_Checked"
Visibility="Collapsed">
<FontIcon FontFamily="{StaticResource CalculatorFontFamily}" Glyph="&#xF770;"/>
</ToggleButton>
</Grid>
<!-- Left portion of the screen -->
<Grid x:Name="LeftGrid"
Grid.Row="1"
Padding="0,4,0,0"
Visibility="{x:Bind ShouldDisplayPanel(IsSmallState, SwitchModeToggleButton.IsChecked.Value, x:True), Mode=OneWay}">
<Grid.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#10000000"/>
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="#20000000"/>
<SolidColorBrush x:Key="RepeatButtonBackgroundPointerOver" Color="#10000000"/>
<SolidColorBrush x:Key="RepeatButtonBackgroundPressed" Color="#20000000"/>
<CornerRadius x:Key="TopButtonCornerRadius">4,4,0,0</CornerRadius>
<CornerRadius x:Key="BottomButtonCornerRadius">0,0,4,4</CornerRadius>
<CornerRadius x:Key="LeftButtonCornerRadius">4,0,0,4</CornerRadius>
<CornerRadius x:Key="RightButtonCornerRadius">0,4,4,0</CornerRadius>
<Style x:Key="ThemedGrapherStyle" TargetType="graphControl:Grapher">
<Setter Property="AxesColor" Value="Black"/>
<Setter Property="GraphBackground" Value="White"/>
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<CornerRadius x:Key="TopButtonCornerRadius">4,4,0,0</CornerRadius>
<CornerRadius x:Key="BottomButtonCornerRadius">0,0,4,4</CornerRadius>
<CornerRadius x:Key="LeftButtonCornerRadius">4,0,0,4</CornerRadius>
<CornerRadius x:Key="RightButtonCornerRadius">0,4,4,0</CornerRadius>
<Style x:Key="ThemedGrapherStyle" TargetType="graphControl:Grapher">
<Setter Property="AxesColor" Value="White"/>
<Setter Property="GraphBackground" Value="Black"/>
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<CornerRadius x:Key="TopButtonCornerRadius">0</CornerRadius>
<CornerRadius x:Key="BottomButtonCornerRadius">0</CornerRadius>
<CornerRadius x:Key="LeftButtonCornerRadius">0</CornerRadius>
<CornerRadius x:Key="RightButtonCornerRadius">0</CornerRadius>
<Style x:Key="ThemedGrapherStyle" TargetType="graphControl:Grapher">
<Setter Property="AxesColor" Value="{ThemeResource SystemColorWindowTextColor}"/>
<Setter Property="GraphBackground" Value="{ThemeResource SystemColorWindowColor}"/>
</Style>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Grid.Resources>
<graphControl:Grapher Name="GraphingControl"
Style="{ThemeResource ThemedGrapherStyle}"
ForceProportionalAxes="True"
LosingFocus="GraphingControl_LosingFocus"
LostFocus="GraphingControl_LostFocus"
RequestedTheme="Light"
UseSystemFocusVisuals="True"
VariablesUpdated="GraphingControl_VariablesUpdated"/>
<Border MinHeight="36"
Margin="0,12,12,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Style="{ThemeResource GraphControlCommandPanel}">
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="ActiveTracing"
MinWidth="40"
Margin="0,-1"
Style="{ThemeResource ThemedGraphToggleButtonStyle}"
contract7Present:CornerRadius="{ThemeResource LeftButtonCornerRadius}"
AutomationProperties.Name="{x:Bind local:GraphingCalculator.GetTracingLegend(GraphingControl.ActiveTracing), Mode=OneWay}"
Checked="ActiveTracing_Checked"
IsChecked="{x:Bind GraphingControl.ActiveTracing, Mode=TwoWay}"
Unchecked="ActiveTracing_Unchecked">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind ActiveTracing.(AutomationProperties.Name), Mode=OneWay}"/>
</ToolTipService.ToolTip>
<FontIcon FontFamily="{StaticResource CalculatorFontFamily}"
FontSize="18"
Glyph="&#xE3B3;"/>
</ToggleButton>
<Button x:Name="Share"
x:Uid="shareButton"
MinWidth="40"
Style="{ThemeResource ThemedGraphButtonStyle}"
Click="OnShareClick">
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="18"
Glyph="&#xE72D;"/>
</Button>
<Button x:Name="GraphSettingsButton"
x:Uid="graphSettingsButton"
MinWidth="40"
Style="{ThemeResource ThemedGraphButtonStyle}"
contract7Present:CornerRadius="{ThemeResource RightButtonCornerRadius}"
Click="GraphSettingsButton_Click"
RequestedTheme="Light">
<FontIcon FontFamily="{StaticResource CalculatorFontFamily}"
FontSize="18"
Glyph="&#xE3B4;"/>
</Button>
</StackPanel>
</Border>
<Border x:Name="TracePointer"
Width="16"
Height="18"
Margin="48,50,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Visibility="Collapsed">
<FontIcon Foreground="Red"
FontFamily="{StaticResource CalculatorFontFamily}"
FontSize="18"
Glyph="&#xE3B3;"/>
</Border>
<Border MinWidth="36"
Margin="0,0,12,12"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Style="{ThemeResource GraphControlCommandPanel}">
<StackPanel Orientation="Vertical">
<RepeatButton x:Name="ZoomInButton"
x:Uid="zoomInButton"
MinHeight="40"
HorizontalAlignment="Stretch"
Style="{ThemeResource ThemedGraphRepeatButtonStyle}"
FontFamily="{StaticResource SymbolThemeFontFamily}"
contract7Present:CornerRadius="{ThemeResource TopButtonCornerRadius}"
AutomationProperties.AutomationId="zoomInButton"
Command="{x:Bind ZoomInButtonPressed, Mode=OneTime}">
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="14"
Glyph="&#xE710;"/>
<RepeatButton.KeyboardAccelerators>
<KeyboardAccelerator Key="Add" Modifiers="Control"/>
</RepeatButton.KeyboardAccelerators>
</RepeatButton>
<RepeatButton x:Name="ZoomOutButton"
x:Uid="zoomOutButton"
MinHeight="40"
HorizontalAlignment="Stretch"
Style="{ThemeResource ThemedGraphRepeatButtonStyle}"
FontFamily="{StaticResource SymbolThemeFontFamily}"
AutomationProperties.AutomationId="zoomOutButton"
Command="{x:Bind ZoomOutButtonPressed, Mode=OneTime}">
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="14"
Glyph="&#xE738;"/>
<RepeatButton.KeyboardAccelerators>
<KeyboardAccelerator Key="Subtract" Modifiers="Control"/>
</RepeatButton.KeyboardAccelerators>
</RepeatButton>
<Button x:Uid="zoomResetButton"
MinHeight="40"
Style="{ThemeResource ThemedGraphButtonStyle}"
contract7Present:CornerRadius="{ThemeResource BottomButtonCornerRadius}"
AutomationProperties.AutomationId="zoomResetButton"
Command="{x:Bind ZoomResetButtonPressed, Mode=OneTime}">
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="14"
Glyph="&#xE895;"/>
</Button>
</StackPanel>
</Border>
<Border x:Name="TraceValuePopup"
Padding="{ThemeResource ToolTipBorderThemePadding}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Style="{ThemeResource GraphTooltipStyle}"
IsHitTestVisible="False"
SizeChanged="TraceValuePopup_SizeChanged"
Visibility="Collapsed">
<Border.RenderTransform>
<TranslateTransform x:Name="TraceValuePopupTransform"/>
</Border.RenderTransform>
<TextBlock x:Name="TraceValue"
Foreground="{ThemeResource ToolTipForeground}"
FontSize="{ThemeResource ToolTipContentThemeFontSize}"
AutomationProperties.LiveSetting="Polite"/>
</Border>
</Grid>
<!-- Right portion of the screen -->
<Grid x:Name="RightGrid"
Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="1"
Visibility="{x:Bind local:GraphingCalculator.ShouldDisplayPanel(IsSmallState, SwitchModeToggleButton.IsChecked.Value, x:False), Mode=OneWay}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="2.2*" MaxHeight="400"/>
</Grid.RowDefinitions>
<!-- Ideally the KeyGraphFeaturesPanel should be a frame so that navigation to and from the panel could be handled nicely -->
<local:KeyGraphFeaturesPanel x:Name="KeyGraphFeaturesControl"
Grid.RowSpan="2"
Margin="0,4,0,0"
KeyGraphFeaturesClosed="OnKeyGraphFeaturesClosed"
ViewModel="{x:Bind ViewModel.SelectedEquation, Mode=OneWay}"
Visibility="{x:Bind IsKeyGraphFeaturesVisible, Mode=OneWay}"
x:Load="{x:Bind IsKeyGraphFeaturesVisible, Mode=OneWay}"/>
<!-- This control should be within a grid that limits the hight to keep the sticky footer functionality from breaking -->
<local:EquationInputArea x:Name="EquationInputAreaControl"
Margin="0,4,0,0"
Equations="{x:Bind ViewModel.Equations}"
KeyGraphFeaturesRequested="OnEquationKeyGraphFeaturesRequested"
Variables="{x:Bind ViewModel.Variables}"
Visibility="{x:Bind IsKeyGraphFeaturesVisible, Converter={StaticResource BooleanToVisibilityNegationConverter}, Mode=OneWay}"/>
<local:GraphingNumPad x:Name="GraphingNumPad"
Grid.Row="1"
Margin="2,0,2,2"
Visibility="{x:Bind IsKeyGraphFeaturesVisible, Converter={StaticResource BooleanToVisibilityNegationConverter}, Mode=OneWay}"/>
</Grid>
</Grid>
</UserControl>

View File

@ -0,0 +1,545 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "GraphingCalculator.xaml.h"
#include "CalcViewModel/Common/AppResourceProvider.h"
#include "CalcViewModel/Common/TraceLogger.h"
#include "CalcViewModel/Common/LocalizationSettings.h"
#include "CalcViewModel/Common/LocalizationStringUtil.h"
#include "Common/KeyboardShortcutManager.h"
#include "CalcViewModel/Common/Automation/NarratorAnnouncement.h"
#include "CalcViewModel/Common/Automation/NarratorNotifier.h"
#include "Controls/CalculationResult.h"
#include "CalcManager/NumberFormattingUtils.h"
#include "Calculator/Controls/EquationTextBox.h"
#include "Calculator/Views/GraphingCalculator/EquationInputArea.xaml.h"
#include "CalcViewModel/Common/Utils.h"
#include "GraphingSettings.xaml.h"
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace CalculatorApp::Common::Automation;
using namespace CalculatorApp::Controls;
using namespace CalculatorApp::ViewModel;
using namespace CalcManager::NumberFormattingUtils;
using namespace concurrency;
using namespace GraphControl;
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace std::chrono;
using namespace Utils;
using namespace Windows::ApplicationModel::DataTransfer;
using namespace Windows::ApplicationModel::Resources;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Storage::Streams;
using namespace Windows::System;
using namespace Windows::UI::Core;
using namespace Windows::UI::Input;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Automation;
using namespace Windows::UI::Xaml::Automation::Peers;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Media::Imaging;
using namespace Windows::UI::Popups;
constexpr auto sc_ViewModelPropertyName = L"ViewModel";
DEPENDENCY_PROPERTY_INITIALIZATION(GraphingCalculator, IsSmallState);
GraphingCalculator::GraphingCalculator()
{
InitializeComponent();
DataTransferManager ^ dataTransferManager = DataTransferManager::GetForCurrentView();
// Register the current control as a share source.
m_dataRequestedToken = dataTransferManager->DataRequested +=
ref new TypedEventHandler<DataTransferManager ^, DataRequestedEventArgs ^>(this, &GraphingCalculator::OnDataRequested);
// Request notifications when we should be showing the trace values
GraphingControl->TracingChangedEvent += ref new TracingChangedEventHandler(this, &GraphingCalculator::OnShowTracePopupChanged);
// And when the actual trace value changes
GraphingControl->TracingValueChangedEvent += ref new TracingValueChangedEventHandler(this, &GraphingCalculator::OnTracePointChanged);
// Update where the pointer value is (ie: where the user cursor from keyboard inputs moves the point to)
GraphingControl->PointerValueChangedEvent += ref new PointerValueChangedEventHandler(this, &GraphingCalculator::OnPointerPointChanged);
// OemMinus and OemAdd aren't declared in the VirtualKey enum, we can't add this accelerator XAML-side
auto virtualKey = ref new KeyboardAccelerator();
virtualKey->Key = (VirtualKey)189; // OemPlus key
virtualKey->Modifiers = VirtualKeyModifiers::Control;
ZoomOutButton->KeyboardAccelerators->Append(virtualKey);
virtualKey = ref new KeyboardAccelerator();
virtualKey->Key = (VirtualKey)187; // OemAdd key
virtualKey->Modifiers = VirtualKeyModifiers::Control;
ZoomInButton->KeyboardAccelerators->Append(virtualKey);
}
void GraphingCalculator::OnShowTracePopupChanged(bool newValue)
{
if ((TraceValuePopup->Visibility == ::Visibility::Visible) != newValue)
{
TraceValuePopup->Visibility = newValue ? ::Visibility::Visible : ::Visibility::Collapsed;
}
}
void GraphingCalculator::GraphingCalculator_DataContextChanged(FrameworkElement ^ sender, DataContextChangedEventArgs ^ args)
{
if (ViewModel != nullptr)
{
if (m_vectorChangedToken.Value != 0)
{
ViewModel->Equations->VectorChanged -= m_vectorChangedToken;
m_vectorChangedToken.Value = 0;
}
if (m_variableUpdatedToken.Value != 0)
{
ViewModel->VariableUpdated -= m_variableUpdatedToken;
m_variableUpdatedToken.Value = 0;
}
}
ViewModel = dynamic_cast<GraphingCalculatorViewModel ^>(args->NewValue);
m_vectorChangedToken = ViewModel->Equations->VectorChanged +=
ref new VectorChangedEventHandler<EquationViewModel ^>(this, &GraphingCalculator::OnEquationsVectorChanged);
m_variableUpdatedToken = ViewModel->VariableUpdated +=
ref new EventHandler<VariableChangedEventArgs>(this, &CalculatorApp::GraphingCalculator::OnVariableChanged);
}
void GraphingCalculator::OnEquationsVectorChanged(IObservableVector<EquationViewModel ^> ^ sender, IVectorChangedEventArgs ^ event)
{
// If an item is already added to the graph, changing it should automatically trigger a graph update
if (event->CollectionChange == ::CollectionChange::ItemChanged)
{
return;
}
// Do not plot the graph if we are removing an empty equation, just remove it
if (event->CollectionChange == ::CollectionChange::ItemRemoved)
{
auto itemToRemove = GraphingControl->Equations->GetAt(event->Index);
if (itemToRemove->Expression->IsEmpty())
{
GraphingControl->Equations->RemoveAt(event->Index);
return;
}
}
// Do not plot the graph if we are adding an empty equation, just add it
if (event->CollectionChange == ::CollectionChange::ItemInserted)
{
auto itemToAdd = sender->GetAt(event->Index);
if (itemToAdd->Expression->IsEmpty())
{
GraphingControl->Equations->Append(itemToAdd->GraphEquation);
return;
}
}
// We are either adding or removing a valid equation, or resetting the collection. We will need to plot the graph
GraphingControl->Equations->Clear();
for (auto equationViewModel : ViewModel->Equations)
{
GraphingControl->Equations->Append(equationViewModel->GraphEquation);
}
GraphingControl->PlotGraph(false);
}
void GraphingCalculator::OnTracePointChanged(Point newPoint)
{
wstringstream traceValueString;
// TODO: The below precision should ideally be dynamic based on the current scale of the graph.
traceValueString << "(" << fixed << setprecision(1) << newPoint.X << ", " << fixed << setprecision(1) << newPoint.Y << ")";
TraceValue->Text = ref new String(traceValueString.str().c_str());
auto peer = FrameworkElementAutomationPeer::FromElement(TraceValue);
if (peer != nullptr)
{
peer->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged);
}
PositionGraphPopup();
}
void CalculatorApp::GraphingCalculator::OnPointerPointChanged(Windows::Foundation::Point newPoint)
{
// Move the pointer glyph to where it is supposed to be.
// because the glyph is centered and has some spacing, to get the point to properly line up with the glyph, move the x point over 2 px
TracePointer->Margin = Thickness(newPoint.X - 2, newPoint.Y, 0, 0);
}
GraphingCalculatorViewModel ^ GraphingCalculator::ViewModel::get()
{
return m_viewModel;
}
void GraphingCalculator::ViewModel::set(GraphingCalculatorViewModel ^ vm)
{
if (m_viewModel != vm)
{
m_viewModel = vm;
RaisePropertyChanged(StringReference(sc_ViewModelPropertyName));
}
}
void CalculatorApp::GraphingCalculator::OnShareClick(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
{
// Ask the OS to start a share action.
DataTransferManager::ShowShareUI();
}
// When share is invoked (by the user or programmatically) the event handler we registered will be called to populate the data package with the
// data to be shared. We will request the current graph image from the grapher as a stream that will pass to the share request.
void GraphingCalculator::OnDataRequested(DataTransferManager ^ sender, DataRequestedEventArgs ^ args)
{
auto resourceLoader = ResourceLoader::GetForCurrentView();
try
{
std::wstringstream rawHtml;
std::wstringstream equationHtml;
rawHtml << L"<p><img src='graph.png' width='600' alt='" << resourceLoader->GetString(L"GraphImageAltText")->Data() << "'></p>";
auto equations = ViewModel->Equations;
bool hasEquations = false;
if (equations->Size > 0)
{
equationHtml << L"<span style=\"color: rgb(68, 114, 196); font-style: bold; font-size : 13pt;\">";
equationHtml << resourceLoader->GetString(L"EquationsShareHeader")->Data();
equationHtml << L"</span>";
equationHtml << L"<table cellpadding=\"0\" cellspacing=\"0\" >";
for (auto equation : equations)
{
auto expression = equation->Expression;
if (expression->IsEmpty())
{
continue;
}
auto color = equation->LineColor;
hasEquations = true;
expression = GraphingControl->ConvertToLinear(expression);
std::wstringstream equationColorHtml;
equationColorHtml << L"color:rgb(" << color.R.ToString()->Data() << L"," << color.G.ToString()->Data() << L"," << color.B.ToString()->Data()
<< L");";
equationHtml << L"<tr style=\"margin: 0pt 0pt 0pt 0pt; padding: 0pt 0pt 0pt 0pt; \"><td><span style=\"font-size: 22pt; line-height: 0;" << equationColorHtml.str()
<< L"\">&#x25A0;</span></td><td><div style=\"margin: 4pt 0pt 0pt 6pt;\">";
equationHtml << EscapeHtmlSpecialCharacters(expression)->Data();
equationHtml << L"</div></td>";
}
equationHtml << L"</table>";
}
if (hasEquations)
{
rawHtml << equationHtml.str();
}
auto variables = ViewModel->Variables;
if (variables->Size > 0)
{
auto localizedSeperator = LocalizationSettings::GetInstance().GetListSeparator() + L" ";
rawHtml << L"<br><span style=\"color: rgb(68, 114, 196); font-style: bold; font-size: 13pt;\">";
rawHtml << resourceLoader->GetString(L"VariablesShareHeader")->Data();
rawHtml << L"</span><br><div style=\"margin: 4pt 0pt 0pt 0pt;\">";
for (unsigned i = 0; i < variables->Size; i++)
{
auto name = variables->GetAt(i)->Name;
auto value = variables->GetAt(i)->Value;
rawHtml << name->Data();
rawHtml << L"=";
auto formattedValue = to_wstring(value);
TrimTrailingZeros(formattedValue);
rawHtml << formattedValue;
if (variables->Size - 1 != i)
{
rawHtml << localizedSeperator;
}
}
rawHtml << L"</div>";
}
rawHtml << L"<br><br>";
// Shortcut to the request data
auto requestData = args->Request->Data;
DataPackage ^ dataPackage = ref new DataPackage();
auto html = HtmlFormatHelper::CreateHtmlFormat(ref new String(rawHtml.str().c_str()));
requestData->Properties->Title = resourceLoader->GetString(L"ShareActionTitle");
requestData->SetHtmlFormat(html);
auto bitmapStream = GraphingControl->GetGraphBitmapStream();
requestData->ResourceMap->Insert(ref new String(L"graph.png"), bitmapStream);
// Set the thumbnail image (in case the share target can't handle HTML)
requestData->Properties->Thumbnail = bitmapStream;
}
catch (Exception ^ ex)
{
TraceLogger::GetInstance()->LogPlatformException(ViewMode::Graphing, __FUNCTIONW__, ex);
// Something went wrong, notify the user.
auto errDialog = ref new ContentDialog();
errDialog->Content = resourceLoader->GetString(L"ShareActionErrorMessage");
errDialog->CloseButtonText = resourceLoader->GetString(L"ShareActionErrorOk");
errDialog->ShowAsync();
}
}
void GraphingCalculator::GraphingControl_VariablesUpdated(Object ^, Object ^)
{
m_viewModel->UpdateVariables(GraphingControl->Variables);
}
void GraphingCalculator::OnVariableChanged(Platform::Object ^ sender, VariableChangedEventArgs args)
{
GraphingControl->SetVariable(args.variableName, args.newValue);
}
void GraphingCalculator::OnZoomInCommand(Object ^ /* parameter */)
{
GraphingControl->ZoomFromCenter(zoomInScale);
}
void GraphingCalculator::OnZoomOutCommand(Object ^ /* parameter */)
{
GraphingControl->ZoomFromCenter(zoomOutScale);
}
void GraphingCalculator::OnZoomResetCommand(Object ^ /* parameter */)
{
GraphingControl->ResetGrid();
}
String ^ GraphingCalculator::GetTracingLegend(Platform::IBox<bool> ^ isTracing)
{
auto resProvider = AppResourceProvider::GetInstance();
return isTracing != nullptr && isTracing->Value ? resProvider->GetResourceString(L"disableTracingButtonToolTip")
: resProvider->GetResourceString(L"enableTracingButtonToolTip");
}
void GraphingCalculator::GraphingControl_LostFocus(Object ^ sender, RoutedEventArgs ^ e)
{
// If the graph is losing focus while we are in active tracing we need to turn it off so we don't try to eat keys in other controls.
if (GraphingControl->ActiveTracing)
{
if (ActiveTracing->Equals(FocusManager::GetFocusedElement()) && ActiveTracing->IsPressed)
{
m_ActiveTracingPointerCaptureLost = ActiveTracing->PointerCaptureLost +=
ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &CalculatorApp::GraphingCalculator::ActiveTracing_PointerCaptureLost);
}
else
{
GraphingControl->ActiveTracing = false;
OnShowTracePopupChanged(false);
}
}
}
void CalculatorApp::GraphingCalculator::ActiveTracing_PointerCaptureLost(Platform::Object ^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e)
{
if (m_ActiveTracingPointerCaptureLost.Value != 0)
{
ActiveTracing->PointerCaptureLost -= m_ActiveTracingPointerCaptureLost;
m_ActiveTracingPointerCaptureLost.Value = 0;
}
if (GraphingControl->ActiveTracing)
{
GraphingControl->ActiveTracing = false;
OnShowTracePopupChanged(false);
}
}
void GraphingCalculator::GraphingControl_LosingFocus(UIElement ^ sender, LosingFocusEventArgs ^ args)
{
auto newFocusElement = dynamic_cast<FrameworkElement ^>(args->NewFocusedElement);
if (newFocusElement == nullptr || newFocusElement->Name == nullptr)
{
// Because clicking on the swap chain panel will try to move focus to a control that can't actually take focus
// we will get a null destination. So we are going to try and cancel that request.
// If the destination is not in our application we will also get a null destination but the cancel will fail so it doesn't hurt to try.
args->TryCancel();
}
}
void GraphingCalculator::OnEquationKeyGraphFeaturesRequested(Object ^ sender, EquationViewModel ^ equationViewModel)
{
ViewModel->SetSelectedEquation(equationViewModel);
if (equationViewModel != nullptr)
{
auto keyGraphFeatureInfo = GraphingControl->AnalyzeEquation(equationViewModel->GraphEquation);
equationViewModel->PopulateKeyGraphFeatures(keyGraphFeatureInfo);
IsKeyGraphFeaturesVisible = true;
}
}
void GraphingCalculator::OnKeyGraphFeaturesClosed(Object ^ sender, RoutedEventArgs ^ e)
{
IsKeyGraphFeaturesVisible = false;
}
Visibility GraphingCalculator::ShouldDisplayPanel(bool isSmallState, bool isEquationModeActivated, bool isGraphPanel)
{
return (!isSmallState || isEquationModeActivated ^ isGraphPanel) ? ::Visibility::Visible : ::Visibility::Collapsed;
}
Platform::String ^ GraphingCalculator::GetInfoForSwitchModeToggleButton(bool isChecked)
{
if (isChecked)
{
return AppResourceProvider::GetInstance()->GetResourceString(L"GraphSwitchToGraphMode");
}
else
{
return AppResourceProvider::GetInstance()->GetResourceString(L"GraphSwitchToEquationMode");
}
}
void GraphingCalculator::SwitchModeToggleButton_Checked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
{
auto narratorNotifier = ref new NarratorNotifier();
String ^ announcementText;
if (SwitchModeToggleButton->IsChecked->Value)
{
announcementText = AppResourceProvider::GetInstance()->GetResourceString(L"GraphSwitchedToEquationModeAnnouncement");
}
else
{
announcementText = AppResourceProvider::GetInstance()->GetResourceString(L"GraphSwitchedToGraphModeAnnouncement");
}
auto announcement = CalculatorAnnouncement::GetGraphModeChangedAnnouncement(announcementText);
narratorNotifier->Announce(announcement);
}
void GraphingCalculator::PositionGraphPopup()
{
if (GraphingControl->TraceLocation.X + 15 + TraceValuePopup->ActualWidth >= GraphingControl->ActualWidth)
{
TraceValuePopupTransform->X = (int)GraphingControl->TraceLocation.X - 15 - TraceValuePopup->ActualWidth;
}
else
{
TraceValuePopupTransform->X = (int)GraphingControl->TraceLocation.X + 15;
}
if (GraphingControl->TraceLocation.Y >= 30)
{
TraceValuePopupTransform->Y = (int)GraphingControl->TraceLocation.Y - 30;
}
else
{
TraceValuePopupTransform->Y = (int)GraphingControl->TraceLocation.Y;
}
}
void GraphingCalculator::TraceValuePopup_SizeChanged(Object ^ sender, SizeChangedEventArgs ^ e)
{
PositionGraphPopup();
}
::Visibility GraphingCalculator::ManageEditVariablesButtonVisibility(unsigned int numberOfVariables)
{
return numberOfVariables == 0 ? ::Visibility::Collapsed : ::Visibility::Visible;
}
void CalculatorApp::GraphingCalculator::ActiveTracing_Checked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
{
FocusManager::TryFocusAsync(GraphingControl, ::FocusState::Programmatic);
m_activeTracingKeyUpToken = Window::Current->CoreWindow->KeyUp +=
ref new TypedEventHandler<Windows::UI::Core::CoreWindow ^, Windows::UI::Core::KeyEventArgs ^>(
this, &CalculatorApp::GraphingCalculator::ActiveTracing_KeyUp);
KeyboardShortcutManager::IgnoreEscape(false);
TracePointer->Visibility = ::Visibility::Visible;
}
void CalculatorApp::GraphingCalculator::ActiveTracing_Unchecked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e)
{
if (m_ActiveTracingPointerCaptureLost.Value != 0)
{
ActiveTracing->PointerCaptureLost -= m_ActiveTracingPointerCaptureLost;
m_ActiveTracingPointerCaptureLost.Value = 0;
}
if (m_activeTracingKeyUpToken.Value != 0)
{
Window::Current->CoreWindow->KeyUp -= m_activeTracingKeyUpToken;
m_activeTracingKeyUpToken.Value = 0;
}
KeyboardShortcutManager::HonorEscape();
TracePointer->Visibility = ::Visibility::Collapsed;
}
void CalculatorApp::GraphingCalculator::ActiveTracing_KeyUp(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ args)
{
if (args->VirtualKey == VirtualKey::Escape)
{
GraphingControl->ActiveTracing = false;
args->Handled = true;
}
}
void GraphingCalculator::GraphSettingsButton_Click(Object ^ sender, RoutedEventArgs ^ e)
{
DisplayGraphSettings();
}
void GraphingCalculator::DisplayGraphSettings()
{
auto graphSettings = ref new GraphingSettings();
graphSettings->SetGrapher(this->GraphingControl);
auto flyoutGraphSettings = ref new Flyout();
flyoutGraphSettings->Content = graphSettings;
flyoutGraphSettings->Closing += ref new TypedEventHandler<FlyoutBase ^, FlyoutBaseClosingEventArgs ^>(this, &GraphingCalculator::OnSettingsFlyout_Closing);
flyoutGraphSettings->ShowAt(GraphSettingsButton);
}
void GraphingCalculator::OnSettingsFlyout_Closing(FlyoutBase ^ sender, FlyoutBaseClosingEventArgs ^ args)
{
auto flyout = static_cast<Flyout ^>(sender);
auto graphingSetting = static_cast<GraphingSettings ^>(flyout->Content);
args->Cancel = graphingSetting->CanBeClose();
}

View File

@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "Views\GraphingCalculator\GraphingCalculator.g.h"
#include "CalcViewModel\GraphingCalculator\GraphingCalculatorViewModel.h"
#include "Views\NumberPad.xaml.h"
#include "Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml.h"
#include "Views\GraphingCalculator\GraphingNumPad.xaml.h"
#include "Views\GraphingCalculator\GraphingSettings.xaml.h"
namespace CalculatorApp
{
constexpr double zoomInScale = 1 / 1.0625;
constexpr double zoomOutScale = 1.0625;
public ref class GraphingCalculator sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
GraphingCalculator();
OBSERVABLE_OBJECT();
DEPENDENCY_PROPERTY_OWNER(GraphingCalculator);
COMMAND_FOR_METHOD(ZoomOutButtonPressed, GraphingCalculator::OnZoomOutCommand);
COMMAND_FOR_METHOD(ZoomInButtonPressed, GraphingCalculator::OnZoomInCommand);
COMMAND_FOR_METHOD(ZoomResetButtonPressed, GraphingCalculator::OnZoomResetCommand);
OBSERVABLE_PROPERTY_R(bool, IsKeyGraphFeaturesVisible);
DEPENDENCY_PROPERTY(bool, IsSmallState);
property CalculatorApp::ViewModel::GraphingCalculatorViewModel^ ViewModel
{
CalculatorApp::ViewModel::GraphingCalculatorViewModel^ get();
void set(CalculatorApp::ViewModel::GraphingCalculatorViewModel^ vm);
}
static Windows::UI::Xaml::Visibility ShouldDisplayPanel(bool isSmallState, bool isEquationModeActivated, bool isGraphPanel);
static Platform::String ^ GetInfoForSwitchModeToggleButton(bool isChecked);
static Windows::UI::Xaml::Visibility ManageEditVariablesButtonVisibility(unsigned int numberOfVariables);
static Platform::String ^ GetTracingLegend(Platform::IBox<bool> ^ isTracing);
private:
void GraphingCalculator_DataContextChanged(Windows::UI::Xaml::FrameworkElement ^ sender, Windows::UI::Xaml::DataContextChangedEventArgs ^ args);
void OnVariableChanged(Platform::Object ^ sender, CalculatorApp::ViewModel::VariableChangedEventArgs args);
void OnEquationsVectorChanged(
Windows::Foundation::Collections::IObservableVector<CalculatorApp::ViewModel::EquationViewModel ^> ^ sender,
Windows::Foundation::Collections::IVectorChangedEventArgs ^ event);
void OnZoomInCommand(Object ^ parameter);
void OnZoomOutCommand(Object ^ parameter);
void OnZoomResetCommand(Object ^ parameter);
double validateDouble(Platform::String ^ value, double defaultValue);
void OnShareClick(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void OnShowTracePopupChanged(bool newValue);
void OnTracePointChanged(Windows::Foundation::Point newPoint);
void OnPointerPointChanged(Windows::Foundation::Point newPoint);
private:
void OnDataRequested(
Windows::ApplicationModel::DataTransfer::DataTransferManager ^ sender,
Windows::ApplicationModel::DataTransfer::DataRequestedEventArgs ^ e);
void TextBoxGotFocus(Windows::UI::Xaml::Controls::TextBox ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void GraphingControl_LostFocus(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void GraphingControl_LosingFocus(Windows::UI::Xaml::UIElement ^ sender, Windows::UI::Xaml::Input::LosingFocusEventArgs ^ args);
void GraphingControl_VariablesUpdated(Platform::Object ^ sender, Object ^ args);
void OnEquationKeyGraphFeaturesRequested(Platform::Object ^ sender, CalculatorApp::ViewModel::EquationViewModel ^ e);
void OnKeyGraphFeaturesClosed(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void SwitchModeToggleButton_Checked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void TraceValuePopup_SizeChanged(Platform::Object ^ sender, Windows::UI::Xaml::SizeChangedEventArgs ^ e);
void PositionGraphPopup();
void ActiveTracing_Checked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void ActiveTracing_Unchecked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void ActiveTracing_KeyUp(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ args);
void ActiveTracing_PointerCaptureLost(Platform::Object ^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e);
void GraphSettingsButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void DisplayGraphSettings();
private:
Windows::Foundation::EventRegistrationToken m_dataRequestedToken;
Windows::Foundation::EventRegistrationToken m_vectorChangedToken;
Windows::Foundation::EventRegistrationToken m_variableUpdatedToken;
Windows::Foundation::EventRegistrationToken m_activeTracingKeyUpToken;
Windows::Foundation::EventRegistrationToken m_ActiveTracingPointerCaptureLost;
CalculatorApp::ViewModel::GraphingCalculatorViewModel ^ m_viewModel;
void
OnSettingsFlyout_Closing(Windows::UI::Xaml::Controls::Primitives::FlyoutBase ^ sender, Windows::UI::Xaml::Controls::Primitives::FlyoutBaseClosingEventArgs ^ args);
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,257 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "GraphingNumPad.xaml.h"
#include "Views/NumberPad.xaml.h"
#include "Controls/CalculatorButton.h"
#include "CalcViewModel/Common/LocalizationSettings.h"
#include "Controls/MathRichEditBox.h"
using namespace CalculatorApp;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
// Dictionary of the enum of the button clicked mapped to an object with the string to enter into the rich edit, and the start and end of the selection after text has been entered.
static const std::unordered_map<NumbersAndOperatorsEnum, std::tuple<Platform::String ^, int, int>> buttonOutput = {
{ NumbersAndOperatorsEnum::Sin, { L"sin()", 4, 0 } },
{ NumbersAndOperatorsEnum::Cos, { L"cos()", 4, 0 } },
{ NumbersAndOperatorsEnum::Tan, { L"tan()", 4, 0 } },
{ NumbersAndOperatorsEnum::Sec, { L"sec()", 4, 0 } },
{ NumbersAndOperatorsEnum::Csc, { L"csc()", 4, 0 } },
{ NumbersAndOperatorsEnum::Cot, { L"cot()", 4, 0 } },
{ NumbersAndOperatorsEnum::InvSin, { L"arcsin()", 7, 0 } },
{ NumbersAndOperatorsEnum::InvCos, { L"arccos()", 7, 0 } },
{ NumbersAndOperatorsEnum::InvTan, { L"arctan()", 7, 0 } },
{ NumbersAndOperatorsEnum::InvSec, { L"arcsec()", 7, 0 } },
{ NumbersAndOperatorsEnum::InvCsc, { L"arccsc()", 7, 0 } },
{ NumbersAndOperatorsEnum::InvCot, { L"arccot()", 7, 0 } },
{ NumbersAndOperatorsEnum::Sinh, { L"sinh()", 5, 0 } },
{ NumbersAndOperatorsEnum::Cosh, { L"cosh()", 5, 0 } },
{ NumbersAndOperatorsEnum::Tanh, { L"tanh()", 5, 0 } },
{ NumbersAndOperatorsEnum::Sech, { L"sech()", 5, 0 } },
{ NumbersAndOperatorsEnum::Csch, { L"csch()", 5, 0 } },
{ NumbersAndOperatorsEnum::Coth, { L"coth()", 5, 0 } },
{ NumbersAndOperatorsEnum::InvSinh, { L"arcsinh()", 8, 0 } },
{ NumbersAndOperatorsEnum::InvCosh, { L"arccosh()", 8, 0 } },
{ NumbersAndOperatorsEnum::InvTanh, { L"arctanh()", 8, 0 } },
{ NumbersAndOperatorsEnum::InvSech, { L"arcsech()", 8, 0 } },
{ NumbersAndOperatorsEnum::InvCsch, { L"arccsch()", 8, 0 } },
{ NumbersAndOperatorsEnum::InvCoth, { L"arccoth()", 8, 0 } },
{ NumbersAndOperatorsEnum::Abs, { L"abs()", 4, 0 } },
{ NumbersAndOperatorsEnum::Floor, { L"floor()", 6, 0 } },
{ NumbersAndOperatorsEnum::Ceil, { L"ceiling()", 8, 0 } },
{ NumbersAndOperatorsEnum::Pi, { L"\u03C0", 1, 0 } },
{ NumbersAndOperatorsEnum::Euler, { L"e", 1, 0 } },
{ NumbersAndOperatorsEnum::XPower2, { L"^2", 2, 0 } },
{ NumbersAndOperatorsEnum::Cube, { L"^3", 2, 0 } },
{ NumbersAndOperatorsEnum::XPowerY, { L"^", 1, 0 } },
{ NumbersAndOperatorsEnum::TenPowerX, { L"10^", 3, 0 } },
{ NumbersAndOperatorsEnum::LogBase10, { L"log()", 4, 0 } },
{ NumbersAndOperatorsEnum::LogBaseE, { L"ln()", 3, 0 } },
{ NumbersAndOperatorsEnum::Sqrt, { L"sqrt()", 5, 0 } },
{ NumbersAndOperatorsEnum::CubeRoot, { L"cbrt()", 5, 0 } },
{ NumbersAndOperatorsEnum::YRootX, { L"root(x,n)", 7, 1 } },
{ NumbersAndOperatorsEnum::TwoPowerX, { L"2^", 2, 0 } },
{ NumbersAndOperatorsEnum::LogBaseX, { L"log(b, x)", 4, 1 } },
{ NumbersAndOperatorsEnum::EPowerX, { L"e^", 4, 0 } },
{ NumbersAndOperatorsEnum::Abs, { L"abs()", 4, 0 } },
{ NumbersAndOperatorsEnum::X, { L"x", 1, 0 } },
{ NumbersAndOperatorsEnum::Y, { L"y", 1, 0 } },
{ NumbersAndOperatorsEnum::OpenParenthesis, { L"(", 1, 0 } },
{ NumbersAndOperatorsEnum::CloseParenthesis, { L")", 1, 0 } },
{ NumbersAndOperatorsEnum::Equals, { L"=", 1, 0 } },
{ NumbersAndOperatorsEnum::Divide, { L"/", 1, 0 } },
{ NumbersAndOperatorsEnum::Multiply, { L"*", 1, 0 } },
{ NumbersAndOperatorsEnum::Subtract, { L"-", 1, 0 } },
{ NumbersAndOperatorsEnum::Add, { L"+", 1, 0 } },
{ NumbersAndOperatorsEnum::Invert, { L"1/", 2, 0 } },
{ NumbersAndOperatorsEnum::Negate, { L"-", 1, 0 } },
{ NumbersAndOperatorsEnum::GreaterThan, { L">", 1, 0 } },
{ NumbersAndOperatorsEnum::GreaterThanOrEqualTo, { L"\u2265", 1, 0 } },
{ NumbersAndOperatorsEnum::LessThan, { L"<", 1, 0 } },
{ NumbersAndOperatorsEnum::LessThanOrEqualTo, { L"\u2264", 1, 0 } },
{ NumbersAndOperatorsEnum::Zero, { L"0", 1, 0 } },
{ NumbersAndOperatorsEnum::One, { L"1", 1, 0 } },
{ NumbersAndOperatorsEnum::Two, { L"2", 1, 0 } },
{ NumbersAndOperatorsEnum::Three, { L"3", 1, 0 } },
{ NumbersAndOperatorsEnum::Four, { L"4", 1, 0 } },
{ NumbersAndOperatorsEnum::Five, { L"5", 1, 0 } },
{ NumbersAndOperatorsEnum::Six, { L"6", 1, 0 } },
{ NumbersAndOperatorsEnum::Seven, { L"7", 1, 0 } },
{ NumbersAndOperatorsEnum::Eight, { L"8", 1, 0 } },
{ NumbersAndOperatorsEnum::Nine, { L"9", 1, 0 } },
{ NumbersAndOperatorsEnum::Decimal, { L".", 1, 0 } },
};
GraphingNumPad::GraphingNumPad()
{
InitializeComponent();
const auto& localizationSettings = CalculatorApp::Common::LocalizationSettings::GetInstance();
DecimalSeparatorButton->Content = localizationSettings.GetDecimalSeparator();
Num0Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('0');
Num1Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('1');
Num2Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('2');
Num3Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('3');
Num4Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('4');
Num5Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('5');
Num6Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('6');
Num7Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('7');
Num8Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('8');
Num9Button->Content = localizationSettings.GetDigitSymbolFromEnUsDigit('9');
}
void GraphingNumPad::ShiftButton_Check(_In_ Platform::Object ^ /*sender*/, _In_ RoutedEventArgs ^ /*e*/)
{
SetOperatorRowVisibility();
}
void GraphingNumPad::ShiftButton_Uncheck(_In_ Platform::Object ^ sender, _In_ RoutedEventArgs ^ /*e*/)
{
ShiftButton->IsChecked = false;
SetOperatorRowVisibility();
GraphingNumPad::Button_Clicked(sender, nullptr);
}
void GraphingNumPad::TrigFlyoutShift_Toggle(_In_ Platform::Object ^ /*sender*/, _In_ RoutedEventArgs ^ /*e*/)
{
SetTrigRowVisibility();
}
void GraphingNumPad::TrigFlyoutHyp_Toggle(_In_ Platform::Object ^ /*sender*/, _In_ RoutedEventArgs ^ /*e*/)
{
SetTrigRowVisibility();
}
void GraphingNumPad::FlyoutButton_Clicked(_In_ Platform::Object ^ sender, _In_ RoutedEventArgs ^ /*e*/)
{
this->HypButton->IsChecked = false;
this->TrigShiftButton->IsChecked = false;
this->Trigflyout->Hide();
this->FuncFlyout->Hide();
this->InequalityFlyout->Hide();
GraphingNumPad::Button_Clicked(sender, nullptr);
}
void GraphingNumPad::ShiftButton_IsEnabledChanged(_In_ Platform::Object ^ /*sender*/, _In_ DependencyPropertyChangedEventArgs ^ /*e*/)
{
SetOperatorRowVisibility();
}
void GraphingNumPad::SetTrigRowVisibility()
{
bool isShiftChecked = TrigShiftButton->IsChecked->Value;
bool isHypeChecked = HypButton->IsChecked->Value;
InverseHyperbolicTrigFunctions->Visibility = ::Visibility::Collapsed;
InverseTrigFunctions->Visibility = ::Visibility::Collapsed;
HyperbolicTrigFunctions->Visibility = ::Visibility::Collapsed;
TrigFunctions->Visibility = ::Visibility::Collapsed;
if (isShiftChecked && isHypeChecked)
{
InverseHyperbolicTrigFunctions->Visibility = ::Visibility::Visible;
}
else if (isShiftChecked && !isHypeChecked)
{
InverseTrigFunctions->Visibility = ::Visibility::Visible;
}
else if (!isShiftChecked && isHypeChecked)
{
HyperbolicTrigFunctions->Visibility = ::Visibility::Visible;
}
else
{
TrigFunctions->Visibility = ::Visibility::Visible;
}
}
void GraphingNumPad::SetOperatorRowVisibility()
{
::Visibility rowVis, invRowVis;
if (ShiftButton->IsChecked->Value)
{
rowVis = ::Visibility::Collapsed;
invRowVis = ::Visibility::Visible;
}
else
{
rowVis = ::Visibility::Visible;
invRowVis = ::Visibility::Collapsed;
}
Row1->Visibility = rowVis;
InvRow1->Visibility = invRowVis;
}
void GraphingNumPad::Button_Clicked(Platform::Object ^ sender, DependencyPropertyChangedEventArgs ^ /*e*/)
{
auto mathRichEdit = GetActiveRichEdit();
auto button = dynamic_cast<CalculatorApp::Controls::CalculatorButton ^>(sender);
if (mathRichEdit != nullptr && sender != nullptr)
{
auto id = button->ButtonId;
auto output = buttonOutput.find(id);
mathRichEdit->InsertText(std::get<0>(output->second), std::get<1>(output->second), std::get<2>(output->second));
}
}
void GraphingNumPad::SubmitButton_Clicked(Platform::Object ^ /*sender*/, RoutedEventArgs ^ /*e*/)
{
auto mathRichEdit = GetActiveRichEdit();
if (mathRichEdit != nullptr)
{
mathRichEdit->SubmitEquation(CalculatorApp::Controls::EquationSubmissionSource::ENTER_KEY);
}
}
void GraphingNumPad::ClearButton_Clicked(Platform::Object ^ /*sender*/, RoutedEventArgs ^ /*e*/)
{
auto mathRichEdit = GetActiveRichEdit();
if (mathRichEdit != nullptr)
{
mathRichEdit->MathText = L"<math xmlns=\"http://www.w3.org/1998/Math/MathML\"></math>";
mathRichEdit->SubmitEquation(CalculatorApp::Controls::EquationSubmissionSource::PROGRAMMATIC);
}
}
void GraphingNumPad::BackSpaceButton_Clicked(Platform::Object ^ /*sender*/, RoutedEventArgs ^ /*e*/)
{
auto mathRichEdit = GetActiveRichEdit();
if (mathRichEdit != nullptr)
{
mathRichEdit->BackSpace();
}
}
// To avoid focus moving when the space between buttons is clicked, handle click events that make it through the keypad.
void GraphingNumPad::GraphingNumPad_PointerPressed(Platform::Object ^ /*sender*/, PointerRoutedEventArgs ^ e)
{
e->Handled = true;
}
CalculatorApp::Controls::MathRichEditBox ^ GraphingNumPad::GetActiveRichEdit()
{
return dynamic_cast<Controls::MathRichEditBox ^>(FocusManager::GetFocusedElement());
}
// Adding event because the ShowMode property is ignored in xaml.
void GraphingNumPad::Flyout_Opening(Platform::Object ^ sender, Platform::Object ^ /*e*/)
{
auto flyout = dynamic_cast<Flyout ^>(sender);
if (flyout)
{
flyout->ShowMode = FlyoutShowMode::Transient;
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "Views\GraphingCalculator\GraphingNumPad.g.h"
#include "CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h"
#include "Views/GraphingCalculator/EquationInputArea.xaml.h"
#include "CalcViewModel/Common/CalculatorButtonUser.h"
namespace CalculatorApp
{
[Windows::Foundation::Metadata::WebHostHidden]
public ref class GraphingNumPad sealed
{
public:
GraphingNumPad();
private:
void ShiftButton_Check(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e);
void ShiftButton_Uncheck(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e);
void TrigFlyoutShift_Toggle(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e);
void TrigFlyoutHyp_Toggle(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e);
void FlyoutButton_Clicked(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e);
void ShiftButton_IsEnabledChanged(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ e);
void SetOperatorRowVisibility();
void SetTrigRowVisibility();
void Button_Clicked(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ e);
void SubmitButton_Clicked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void ClearButton_Clicked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void BackSpaceButton_Clicked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
void GraphingNumPad_PointerPressed(Platform::Object ^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e);
Controls::MathRichEditBox^ GetActiveRichEdit();
void Flyout_Opening(Platform::Object ^ sender, Platform::Object ^ e);
};
}

View File

@ -0,0 +1,172 @@
<UserControl x:Class="CalculatorApp.GraphingSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:CalculatorApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="GraphSettingsErrorBackgroundBrush" Color="#33EB5757"/>
<SolidColorBrush x:Key="GraphSettingsErrorBorderBrush" Color="#FFEB5757"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="GraphSettingsErrorBackgroundBrush" Color="#33EB5757"/>
<SolidColorBrush x:Key="GraphSettingsErrorBorderBrush" Color="#FFEB5757"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="GraphSettingsErrorBackgroundBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
<SolidColorBrush x:Key="GraphSettingsErrorBorderBrush" Color="Red"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Style x:Key="TrigUnitsRadioButtonStyle" TargetType="RadioButton">
<Setter Property="MinHeight" Value="38"/>
<Setter Property="MinWidth" Value="90"/>
<Setter Property="Padding" Value="4,0"/>
<Setter Property="FocusVisualMargin" Value="0"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid Name="LayoutRoot" Background="{ThemeResource ToggleButtonBackground}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="LayoutRoot.Background" Value="{ThemeResource ToggleButtonBackgroundPointerOver}"/>
<Setter Target="AccessibilityBorder.BorderBrush" Value="{ThemeResource ToggleButtonBorderBrushPointerOver}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="LayoutRoot.Background" Value="{ThemeResource ToggleButtonBackgroundPressed}"/>
<Setter Target="AccessibilityBorder.BorderBrush" Value="{ThemeResource ToggleButtonBorderBrushPressed}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Target="SelectedBackgroundRectangle.Opacity" Value="1"/>
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundChecked}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked"/>
<VisualState x:Name="Indeterminate"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Name="AccessibilityBorder"
BorderBrush="{ThemeResource ToggleButtonBorderBrush}"
BorderThickness="1"/>
<Rectangle Name="SelectedBackgroundRectangle"
Fill="{ThemeResource ToggleButtonBackgroundChecked}"
Opacity="0"/>
<ContentPresenter Name="ContentPresenter"
Grid.Column="1"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{TemplateBinding Foreground}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SubTitleTextBoxStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
<Style x:Key="ErrorTextBoxStyle" TargetType="TextBox">
<Setter Property="BorderBrush" Value="{ThemeResource GraphSettingsErrorBorderBrush}"/>
<Setter Property="Background" Value="{ThemeResource GraphSettingsErrorBackgroundBrush}"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<StackPanel Margin="4,0,4,4">
<TextBlock x:Name="GraphOptionsHeading"
x:Uid="GraphOptionsHeading"
FontSize="22"
AutomationProperties.HeadingLevel="Level1"/>
<TextBlock x:Name="GridHeading"
x:Uid="GridHeading"
Margin="0,12,0,6"
Style="{StaticResource SubTitleTextBoxStyle}"
AutomationProperties.HeadingLevel="Level2"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SettingsXMin"
x:Uid="GraphSettingsXMin"
MaxWidth="160"
Style="{x:Bind SelectTextBoxStyle(ViewModel.XError, ViewModel.XMinError), Mode=OneWay}"
PreviewKeyDown="GridSettingsTextBox_PreviewKeyDown"
Text="{x:Bind ViewModel.XMin, Mode=TwoWay}"/>
<TextBox x:Name="SettingsXMax"
x:Uid="GraphSettingsXMax"
Grid.Column="2"
MaxWidth="160"
Style="{x:Bind SelectTextBoxStyle(ViewModel.XError, ViewModel.XMaxError), Mode=OneWay}"
PreviewKeyDown="GridSettingsTextBox_PreviewKeyDown"
Text="{x:Bind ViewModel.XMax, Mode=TwoWay}"/>
<TextBox x:Name="SettingsYMin"
x:Uid="GraphSettingsYMin"
Grid.Row="2"
MaxWidth="160"
Style="{x:Bind SelectTextBoxStyle(ViewModel.YError, ViewModel.YMinError), Mode=OneWay}"
PreviewKeyDown="GridSettingsTextBox_PreviewKeyDown"
Text="{x:Bind ViewModel.YMin, Mode=TwoWay}"/>
<TextBox x:Name="SettingsYMax"
x:Uid="GraphSettingsYMax"
Grid.Row="2"
Grid.Column="2"
MaxWidth="160"
Style="{x:Bind SelectTextBoxStyle(ViewModel.YError, ViewModel.YMaxError), Mode=OneWay}"
PreviewKeyDown="GridSettingsTextBox_PreviewKeyDown"
Text="{x:Bind ViewModel.YMax, Mode=TwoWay}"/>
</Grid>
<TextBlock x:Name="UnitsHeading"
x:Uid="UnitsHeading"
Margin="0,16,0,6"
Style="{StaticResource SubTitleTextBoxStyle}"
AutomationProperties.HeadingLevel="Level2"/>
<StackPanel Orientation="Horizontal">
<RadioButton x:Name="Radians"
x:Uid="TrigModeRadians"
Style="{StaticResource TrigUnitsRadioButtonStyle}"
IsChecked="{x:Bind ViewModel.TrigModeRadians, Mode=TwoWay}"/>
<RadioButton x:Name="Degrees"
x:Uid="TrigModeDegrees"
Margin="1,0"
Style="{StaticResource TrigUnitsRadioButtonStyle}"
IsChecked="{x:Bind ViewModel.TrigModeDegrees, Mode=TwoWay}"/>
<RadioButton x:Name="Gradians"
x:Uid="TrigModeGradians"
Style="{StaticResource TrigUnitsRadioButtonStyle}"
IsChecked="{x:Bind ViewModel.TrigModeGradians, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,94 @@
#include "pch.h"
#include "GraphingSettings.xaml.h"
#include "CalcViewModel\Common\AppResourceProvider.cpp"
using namespace std;
using namespace Graphing;
using namespace GraphControl;
using namespace CalculatorApp;
using namespace CalculatorApp::ViewModel;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::System;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
GraphingSettings::GraphingSettings()
: m_ViewModel(ref new GraphingSettingsViewModel())
{
InitializeComponent();
}
void GraphingSettings::SetGrapher(Grapher ^ grapher)
{
m_ViewModel->SetGrapher(grapher);
}
Style ^ GraphingSettings::SelectTextBoxStyle(bool incorrectRange, bool error)
{
if (incorrectRange || error)
{
return static_cast<::Style ^>(this->Resources->Lookup(L"ErrorTextBoxStyle"));
}
else
{
return nullptr;
}
}
void GraphingSettings::GridSettingsTextBox_PreviewKeyDown(Platform::Object ^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e)
{
if (e->Key == VirtualKey::Enter)
{
if (!FocusManager::TryMoveFocusAsync(FocusNavigationDirection::Next))
{
FocusManager::TryMoveFocusAsync(FocusNavigationDirection::Previous);
}
e->Handled = true;
}
}
bool GraphingSettings::CanBeClose()
{
auto focusedElement = FocusManager::GetFocusedElement();
// Move focus so we are sure all values are in sync with the VM
if (focusedElement != nullptr)
{
if (focusedElement->Equals(SettingsXMin))
{
auto textbox = static_cast<TextBox ^>(focusedElement);
ViewModel->XMin = textbox->Text;
}
else if (focusedElement->Equals(SettingsXMax))
{
auto textbox = static_cast<TextBox ^>(focusedElement);
ViewModel->XMax = textbox->Text;
}
else if (focusedElement->Equals(SettingsYMin))
{
auto textbox = static_cast<TextBox ^>(focusedElement);
ViewModel->YMin = textbox->Text;
}
else if (focusedElement->Equals(SettingsYMax))
{
auto textbox = static_cast<TextBox ^>(focusedElement);
ViewModel->YMax = textbox->Text;
}
}
return ViewModel != nullptr && ViewModel->HasError();
}
void GraphingSettings::RefreshRanges()
{
ViewModel->InitRanges();
}

View File

@ -0,0 +1,28 @@
//
// MyUserControl.xaml.h
// Declaration of the MyUserControl class
//
#pragma once
#include "CalcViewModel/Common/Utils.h"
#include "CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h"
#include "Views\GraphingCalculator\GraphingSettings.g.h"
#include <CalcViewModel\GraphingCalculator\GraphingCalculatorViewModel.h>
namespace CalculatorApp
{
[Windows::Foundation::Metadata::WebHostHidden] public ref class GraphingSettings sealed
{
public:
GraphingSettings();
PROPERTY_R(CalculatorApp::ViewModel::GraphingSettingsViewModel ^, ViewModel);
Windows::UI::Xaml::Style ^ SelectTextBoxStyle(bool incorrectRange, bool error);
void SetGrapher(GraphControl::Grapher ^ grapher);
bool CanBeClose();
void RefreshRanges();
private:
void GridSettingsTextBox_PreviewKeyDown(Platform::Object ^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e);
};
}

View File

@ -0,0 +1,219 @@
<UserControl x:Class="CalculatorApp.KeyGraphFeaturesPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CalculatorApp.Controls"
xmlns:converters="using:CalculatorApp.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:CalculatorApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ts="using:CalculatorApp.TemplateSelectors"
xmlns:vm="using:CalculatorApp.ViewModel"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<!-- Can't be #ffffff due to a bug in RichEditBox considering it as the default value -->
<!-- and replacing it by the system value (#000000) when dark theme is used -->
<SolidColorBrush x:Key="TitleMathRichEditBoxForegroundBrush" Color="#feffff"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="TitleMathRichEditBoxForegroundBrush" Color="Black"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="TitleMathRichEditBoxForegroundBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Style x:Key="KGF_RichEditBoxStyle" TargetType="controls:MathRichEditBox">
<Setter Property="FontSize" Value="14"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="UseSystemFocusVisuals" Value="True"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Typography.StylisticSet20" Value="True"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:MathRichEditBox">
<ScrollViewer x:Name="ContentElement"
Grid.Row="1"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsTabStop="False"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
ZoomMode="Disabled"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Name="KGF_TitleTextBlockStyle" TargetType="TextBlock">
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="TextWrapping" Value="WrapWholeWords"/>
<Setter Property="AutomationProperties.HeadingLevel" Value="Level2"/>
</Style>
<Style x:Name="KGF_TextBlockStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="IsTextSelectionEnabled" Value="True"/>
<Setter Property="TextWrapping" Value="WrapWholeWords"/>
</Style>
<Style x:Name="KGF_ListViewItemContainerStyle" TargetType="ListViewItem">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="AutomationProperties.AccessibilityView" Value="Raw"/>
<Setter Property="Padding" Value="0,10"/>
</Style>
<DataTemplate x:Key="KGFRichEditDataTemplate" x:DataType="vm:KeyGraphFeaturesItem">
<StackPanel>
<TextBlock x:Name="TitleTextBlock"
Style="{StaticResource KGF_TitleTextBlockStyle}"
Text="{x:Bind Title, Mode=OneWay}"/>
<ItemsControl ItemsSource="{x:Bind DisplayItems, Mode=OneWay}" UseSystemFocusVisuals="True">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="x:String">
<controls:MathRichEditBox Style="{StaticResource KGF_RichEditBoxStyle}" MathText="{x:Bind}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="KGFGridDataTemplate" x:DataType="vm:KeyGraphFeaturesItem">
<StackPanel>
<TextBlock x:Name="TitleTextBlock"
Style="{StaticResource KGF_TitleTextBlockStyle}"
Text="{x:Bind Title, Mode=OneWay}"/>
<ItemsControl ItemsSource="{x:Bind GridItems, Mode=OneWay}" UseSystemFocusVisuals="True">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:GridDisplayItems">
<Grid VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="64"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:MathRichEditBox Style="{StaticResource KGF_RichEditBoxStyle}" MathText="{x:Bind Expression}"/>
<TextBlock Grid.Column="1"
Margin="0,-2,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource KGF_TextBlockStyle}"
Text="{x:Bind Direction}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="KGFTextBlockDataTemplate" x:DataType="vm:KeyGraphFeaturesItem">
<StackPanel>
<TextBlock x:Name="TitleTextBlock"
Style="{StaticResource KGF_TitleTextBlockStyle}"
Text="{x:Bind Title, Mode=OneWay}"/>
<ItemsControl ItemsSource="{x:Bind DisplayItems, Mode=OneWay}" UseSystemFocusVisuals="True">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Style="{StaticResource KGF_TextBlockStyle}" Text="{x:Bind}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
<ts:KeyGraphFeaturesTemplateSelector x:Key="KGFTemplateSelector"
GridTemplate="{StaticResource KGFGridDataTemplate}"
RichEditTemplate="{StaticResource KGFRichEditDataTemplate}"
TextBlockTemplate="{StaticResource KGFTextBlockDataTemplate}"/>
<converters:BooleanToVisibilityNegationConverter x:Name="BooleanToVisibilityNegationConverter"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="Transparent"/>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Margin="0,0,1,0" Background="{ThemeResource TextControlBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="BackButton"
x:Uid="equationAnalysisBack"
MinWidth="44"
VerticalAlignment="Stretch"
Style="{StaticResource ButtonRevealStyle}"
Background="{x:Bind local:KeyGraphFeaturesPanel.ToSolidColorBrush(ViewModel.LineColor), Mode=OneWay}"
Foreground="{ThemeResource SystemChromeWhiteColor}"
Click="BackButton_Click">
<StackPanel Margin="5,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<FontIcon Margin="0,0,6,0"
VerticalAlignment="Center"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16"
Glyph="&#xE72B;"/>
<FontIcon FontFamily="{StaticResource CalculatorFontFamily}" Glyph="&#xF893;"/>
<TextBlock Margin="-5,19,0,0"
FontSize="11"
Text="{x:Bind ViewModel.FunctionLabelIndex}"/>
</StackPanel>
</Button>
<controls:MathRichEditBox Grid.Column="1"
Padding="10,0,6,0"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Style="{StaticResource KGF_RichEditBoxStyle}"
Foreground="{ThemeResource TitleMathRichEditBoxForegroundBrush}"
MathText="{x:Bind ViewModel.Expression, Mode=OneWay}"/>
</Grid>
<TextBlock x:Uid="KeyGraphFeaturesLabel"
Grid.Row="1"
Margin="12,12,10,0"
Style="{StaticResource KGF_TitleTextBlockStyle}"
FontSize="18"
AutomationProperties.HeadingLevel="Level1"/>
<ListView x:Name="KeyGraphFeaturesListView"
Grid.Row="2"
Padding="12,10,10,12"
IsItemClickEnabled="False"
ItemContainerStyle="{StaticResource KGF_ListViewItemContainerStyle}"
ItemTemplateSelector="{StaticResource KGFTemplateSelector}"
ItemsSource="{x:Bind ViewModel.KeyGraphFeaturesItems}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Auto"
SelectionMode="None"
Visibility="{x:Bind ViewModel.AnalysisErrorVisible, Converter={StaticResource BooleanToVisibilityNegationConverter}, Mode=OneWay}"/>
<TextBlock x:Name="AnalysisErrorTextBlock"
Grid.Row="2"
Margin="12,10,10,0"
Style="{StaticResource KGF_TextBlockStyle}"
FontWeight="Normal"
IsTextSelectionEnabled="False"
Text="{x:Bind ViewModel.AnalysisErrorString, Mode=OneWay}"
TextWrapping="Wrap"
Visibility="{x:Bind ViewModel.AnalysisErrorVisible, Mode=OneWay}"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "KeyGraphFeaturesPanel.xaml.h"
#include "Controls/MathRichEditBox.h"
#include "CalcViewModel/GraphingCalculatorEnums.h"
using namespace CalculatorApp;
using namespace CalculatorApp::ViewModel;
using namespace Platform;
using namespace Windows::UI;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
KeyGraphFeaturesPanel::KeyGraphFeaturesPanel()
{
InitializeComponent();
this->Loaded += ref new RoutedEventHandler(this, &KeyGraphFeaturesPanel::KeyGraphFeaturesPanel_Loaded);
}
void KeyGraphFeaturesPanel::KeyGraphFeaturesPanel_Loaded(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e)
{
BackButton->Focus(::FocusState::Programmatic);
}
void KeyGraphFeaturesPanel::BackButton_Click(Object ^ sender, RoutedEventArgs ^ e)
{
KeyGraphFeaturesClosed(this, ref new RoutedEventArgs());
}

View File

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "Views\GraphingCalculator\KeyGraphFeaturesPanel.g.h"
#include "CalcViewModel\GraphingCalculator\EquationViewModel.h"
#include "Controls/MathRichEditBox.h"
#include "Controls/EquationTextBox.h"
#include "TemplateSelectors/KeyGraphFeaturesTemplateSelector.h"
namespace CalculatorApp
{
public
ref class KeyGraphFeaturesPanel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
KeyGraphFeaturesPanel();
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_RW(CalculatorApp::ViewModel::EquationViewModel ^, ViewModel);
public:
event Windows::UI::Xaml::RoutedEventHandler ^ KeyGraphFeaturesClosed;
static Windows::UI::Xaml::Media::SolidColorBrush
^ ToSolidColorBrush(Windows::UI::Color color) { return ref new Windows::UI::Xaml::Media::SolidColorBrush(color); }
private:
void KeyGraphFeaturesPanel_Loaded(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e);
void BackButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
private:
CalculatorApp::ViewModel::EquationViewModel ^ m_viewModel;
};
}

View File

@ -86,8 +86,9 @@
Command="{x:Bind Model.PasteCommand}"/>
</StackPanel>
<local:TitleBar Grid.Row="0" IsAlwaysOnTopMode="{x:Bind Model.IsAlwaysOnTop, Mode=OneWay}"
AlwaysOnTopClick="TitleBarAlwaysOnTopButtonClick"/>
<local:TitleBar Grid.Row="0"
AlwaysOnTopClick="TitleBarAlwaysOnTopButtonClick"
IsAlwaysOnTopMode="{x:Bind Model.IsAlwaysOnTop, Mode=OneWay}"/>
<Grid Grid.Row="1">
<Border x:Name="CalcHolder">
@ -96,6 +97,9 @@
<Border x:Name="DateCalcHolder">
<!-- PLACEHOLDER!!!! This is where the date calculator goes when it is delay loaded -->
</Border>
<Border x:Name="GraphingCalcHolder">
<!-- PLACEHOLDER!!!! This is where the graphing calculator goes when it is delay loaded -->
</Border>
<Border x:Name="ConverterHolder">
<!-- PLACEHOLDER!!!! This is where the converter goes when it is delay loaded -->
</Border>
@ -108,7 +112,6 @@
DataContext="{x:Bind Model}"
ExpandedModeThresholdWidth="Infinity"
IsBackButtonVisible="Collapsed"
IsEnabled="{x:Bind Model.IsAlwaysOnTop, Converter={StaticResource BooleanNegationConverter}, Mode=OneWay}"
IsPaneToggleButtonVisible="{x:Bind Model.IsAlwaysOnTop, Converter={StaticResource BooleanNegationConverter}, Mode=OneWay}"
IsSettingsVisible="False"
ItemInvoked="OnNavItemInvoked"
@ -126,9 +129,9 @@
<StackPanel HorizontalAlignment="Stretch" Orientation="Vertical">
<muxc:NavigationViewList x:Name="NavFooter"
Width="{x:Bind NavView.OpenPaneLength, Mode=Oneway}"
x:Load="False"
IsItemClickEnabled="True"
ItemClick="OnAboutButtonClick">
ItemClick="OnAboutButtonClick"
x:Load="False">
<muxc:NavigationViewList.ItemContainerTransitions>
<!-- Remove EntranceThemeTransition which is a default transition for NavigationViewList -->
<TransitionCollection/>
@ -180,7 +183,7 @@
Content="&#xEE49;"
Visibility="{x:Bind Model.DisplayNormalAlwaysOnTopOption, Mode=OneWay}">
<Button.KeyboardAccelerators>
<KeyboardAccelerator Modifiers="Menu" Key="Up"/>
<KeyboardAccelerator Key="Up" Modifiers="Menu"/>
</Button.KeyboardAccelerators>
</Button>
</Grid>

View File

@ -168,6 +168,10 @@ void MainPage::OnAppPropertyChanged(_In_ Platform::Object ^ sender, _In_ Windows
}
EnsureDateCalculator();
}
else if (newValue == ViewMode::Graphing)
{
EnsureGraphingCalculator();
}
else if (NavCategory::IsConverterViewMode(newValue))
{
if (m_model->CalculatorViewModel)
@ -198,6 +202,7 @@ void MainPage::ShowHideControls(ViewMode mode)
{
auto isCalcViewMode = NavCategory::IsCalculatorViewMode(mode);
auto isDateCalcViewMode = NavCategory::IsDateCalculatorViewMode(mode);
auto isGraphingCalcViewMode = NavCategory::IsGraphingCalculatorViewMode(mode);
auto isConverterViewMode = NavCategory::IsConverterViewMode(mode);
if (m_calculator)
@ -212,6 +217,12 @@ void MainPage::ShowHideControls(ViewMode mode)
m_dateCalculator->IsEnabled = isDateCalcViewMode;
}
if (m_graphingCalculator)
{
m_graphingCalculator->Visibility = BooleanToVisibilityConverter::Convert(isGraphingCalcViewMode);
m_graphingCalculator->IsEnabled = isGraphingCalcViewMode;
}
if (m_converter)
{
m_converter->Visibility = BooleanToVisibilityConverter::Convert(isConverterViewMode);
@ -239,7 +250,7 @@ void MainPage::UpdatePanelViewState()
void MainPage::OnPageLoaded(_In_ Object ^, _In_ RoutedEventArgs ^ args)
{
if (!m_converter && !m_calculator && !m_dateCalculator)
if (!m_converter && !m_calculator && !m_dateCalculator && !m_graphingCalculator)
{
// We have just launched into our default mode (standard calc) so ensure calc is loaded
EnsureCalculator();
@ -284,6 +295,10 @@ void MainPage::SetDefaultFocus()
{
m_dateCalculator->SetDefaultFocus();
}
if (m_graphingCalculator != nullptr && m_graphingCalculator->Visibility == ::Visibility::Visible)
{
FocusManager::TryFocusAsync(m_graphingCalculator, ::FocusState::Programmatic);
}
if (m_converter != nullptr && m_converter->Visibility == ::Visibility::Visible)
{
m_converter->SetDefaultFocus();
@ -345,6 +360,18 @@ void MainPage::EnsureDateCalculator()
}
}
void MainPage::EnsureGraphingCalculator()
{
if (!m_graphingCalculator)
{
m_graphingCalculator = ref new GraphingCalculator();
m_graphingCalculator->Name = L"GraphingCalculator";
m_graphingCalculator->DataContext = m_model->GraphingCalcViewModel;
GraphingCalcHolder->Child = m_graphingCalculator;
}
}
void MainPage::EnsureConverter()
{
if (!m_converter)
@ -473,6 +500,7 @@ MUXC::NavigationViewItem ^ MainPage::CreateNavViewItemFromCategory(NavCategory ^
item->Content = category->Name;
item->AccessKey = category->AccessKey;
item->IsEnabled = category->IsEnabled;
item->Style = static_cast<Windows::UI::Xaml::Style ^>(Resources->Lookup(L"NavViewItemStyle"));
AutomationProperties::SetName(item, category->AutomationName);
@ -517,7 +545,7 @@ void MainPage::SetHeaderAutomationName()
else
{
String ^ full;
if (NavCategory::IsCalculatorViewMode(mode))
if (NavCategory::IsCalculatorViewMode(mode) || NavCategory::IsGraphingCalculatorViewMode(mode))
{
full = resProvider->GetResourceString(L"HeaderAutomationName_Calculator");
}

View File

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
@ -6,6 +6,7 @@
#include "Views/Calculator.xaml.h"
#include "Views/MainPage.g.h"
#include "Views/DateCalculator.xaml.h"
#include "Views/GraphingCalculator/GraphingCalculator.xaml.h"
#include "Views/UnitConverter.xaml.h"
#include "CalcViewModel/ApplicationViewModel.h"
@ -71,13 +72,15 @@ public
void App_Suspending(Object ^ sender, Windows::ApplicationModel::SuspendingEventArgs ^ e);
void EnsureCalculator();
void EnsureConverter();
void EnsureDateCalculator();
void EnsureGraphingCalculator();
void EnsureConverter();
void ShowAboutPage();
void AnnounceCategoryName();
CalculatorApp::Calculator ^ m_calculator;
GraphingCalculator^ m_graphingCalculator;
CalculatorApp::UnitConverter ^ m_converter;
CalculatorApp::DateCalculator ^ m_dateCalculator;
Windows::Foundation::EventRegistrationToken m_windowSizeEventToken;

View File

@ -27,6 +27,8 @@
#include <concrt.h>
#include <regex>
#include <string>
#include <tuple>
#include <iomanip>
// C++\WinRT Headers
#include "winrt/base.h"

View File

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "AsyncHelper.h"
#include <chrono>
#include <thread>
using namespace std;
using namespace concurrency;
using namespace Platform;
using namespace CalculatorApp;
using namespace Windows::UI::Core;
using namespace Windows::ApplicationModel::Core;
task<void> AsyncHelper::RunOnUIThreadAsync(function<void()>&& action)
{
auto callback = ref new DispatchedHandler([action]() { action(); });
return create_task(CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, callback));
}
void AsyncHelper::RunOnUIThread(function<void()>&& action, DWORD timeout)
{
task<void> waitTask = RunOnUIThreadAsync([action]() { action(); });
WaitForTask<void>(waitTask, timeout);
}
void AsyncHelper::Delay(DWORD milliseconds)
{
thread timer(bind(CalculatorApp::AsyncHelper::Sleep, milliseconds));
timer.join();
}
void AsyncHelper::Sleep(DWORD milliseconds)
{
this_thread::sleep_for(chrono::milliseconds(milliseconds));
}

View File

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include <wrl/wrappers/corewrappers.h>
namespace CalculatorApp
{
class AsyncHelper
{
public:
static concurrency::task<void> RunOnUIThreadAsync(std::function<void()>&& action);
static void RunOnUIThread(std::function<void()>&& action, DWORD timeout = INFINITE);
static void Delay(DWORD milliseconds);
template <typename T>
static void RunOnUIThread(std::function<concurrency::task<T>()>&& action, DWORD timeout = INFINITE)
{
concurrency::task<T> t;
concurrency::task<void> uiTask =
RunOnUIThreadAsync([&t, action]() { t = action(); }).then([&t]() { t.wait(); }, concurrency::task_continuation_context::use_arbitrary());
WaitForTask<void>(uiTask, timeout);
}
template <typename T>
static bool WaitForTask(concurrency::task<T>& t, DWORD timeout = INFINITE)
{
Microsoft::WRL::Wrappers::Event event(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS));
if (!event.IsValid())
{
throw std::bad_alloc();
}
Platform::Exception ^ ex;
t.then(
[&event, &ex](concurrency::task<T> prevTask) {
try
{
prevTask.get();
}
catch (Platform::Exception ^ e)
{
ex = e;
}
if (event.IsValid())
{
SetEvent(event.Get());
}
},
concurrency::task_continuation_context::use_arbitrary());
DWORD waitResult; // = STATUS_PENDING;
waitResult = WaitForSingleObjectEx(event.Get(), timeout, true);
event.Close();
if (ex != nullptr)
{
throw ex;
}
if (waitResult == WAIT_FAILED)
{
throw ref new Platform::Exception(-1, L"Error in waiting for task completion: " + waitResult.ToString());
}
return waitResult == WAIT_OBJECT_0;
}
private:
static void Sleep(DWORD milliseconds);
};
}

Binary file not shown.

View File

@ -103,8 +103,6 @@ namespace CalculatorUnitTests
{
constexpr auto sc_Language_EN = L"en-US";
const UCM::Category CURRENCY_CATEGORY = { NavCategory::Serialize(ViewMode::Currency), L"Currency", false /*supportsNegative*/ };
unique_ptr<CurrencyDataLoader> MakeLoaderWithResults(String ^ staticResponse, String ^ allRatiosResponse)
{
auto client = make_unique<MockCurrencyHttpClientWithResult>(staticResponse, allRatiosResponse);
@ -382,7 +380,11 @@ TEST_METHOD(Load_Success_LoadedFromWeb)
}
;
TEST_CLASS(CurrencyConverterUnitTests){ const UCM::Unit GetUnit(const vector<UCM::Unit>& unitList, const wstring& target){
TEST_CLASS(CurrencyConverterUnitTests){
const UCM::Category CURRENCY_CATEGORY = { NavCategory::Serialize(ViewMode::Currency), L"Currency", false /*supportsNegative*/ };
const UCM::Unit GetUnit(const vector<UCM::Unit>& unitList, const wstring& target){
return *find_if(begin(unitList), end(unitList), [&target](const UCM::Unit& u) { return u.abbreviation == target; });
}

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include <CppUnitTest.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace CalculatorUnitTests
{
BEGIN_TEST_MODULE_ATTRIBUTE()
TEST_MODULE_ATTRIBUTE(L"APPX:CertificateFileName", L"CalculatorUnitTests.cer:TrustedPeople")
END_TEST_MODULE_ATTRIBUTE()
TEST_MODULE_INITIALIZE(ModuleSetup)
{
}
TEST_MODULE_CLEANUP(ModuleCleanup)
{
}
}

View File

@ -97,7 +97,7 @@ namespace CalculatorUnitTests
// Boundary testing
VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box<int>(-1)));
VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box<int>(17)));
VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box<int>(18)));
}
void NavCategoryUnitTests::IsValidViewMode_AllValid()
@ -106,6 +106,10 @@ namespace CalculatorUnitTests
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Scientific));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Programmer));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Date));
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Graphing));
}
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Currency));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Volume));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Length));
@ -125,9 +129,18 @@ namespace CalculatorUnitTests
{
VERIFY_IS_FALSE(NavCategory::IsValidViewMode(ViewMode::None));
// There are 17 total options so int 17 should be the first invalid
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
// There are 18 total options so int 18 should be the first invalid
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(17)));
VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast<ViewMode>(18)));
}
else
{
// There are 17 total options when graphing calculator is not present, so int 17 should be the first invalid
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(16)));
VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast<ViewMode>(17)));
}
// Also verify the lower bound
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(0)));
@ -141,6 +154,10 @@ namespace CalculatorUnitTests
VERIFY_IS_TRUE(NavCategory::IsCalculatorViewMode(ViewMode::Programmer));
VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Date));
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Graphing));
}
VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Currency));
VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Volume));
@ -164,6 +181,10 @@ namespace CalculatorUnitTests
VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Programmer));
VERIFY_IS_TRUE(NavCategory::IsDateCalculatorViewMode(ViewMode::Date));
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Graphing));
}
VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Currency));
VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Volume));
@ -185,8 +206,11 @@ namespace CalculatorUnitTests
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Standard));
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Scientific));
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Programmer));
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Date));
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Graphing));
}
VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Currency));
VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Volume));
@ -209,6 +233,10 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(StringReference(L"Scientific"), NavCategory::GetFriendlyName(ViewMode::Scientific));
VERIFY_ARE_EQUAL(StringReference(L"Programmer"), NavCategory::GetFriendlyName(ViewMode::Programmer));
VERIFY_ARE_EQUAL(StringReference(L"Date"), NavCategory::GetFriendlyName(ViewMode::Date));
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
VERIFY_ARE_EQUAL(StringReference(L"Graphing"), NavCategory::GetFriendlyName(ViewMode::Graphing));
}
VERIFY_ARE_EQUAL(StringReference(L"Currency"), NavCategory::GetFriendlyName(ViewMode::Currency));
VERIFY_ARE_EQUAL(StringReference(L"Volume"), NavCategory::GetFriendlyName(ViewMode::Volume));
VERIFY_ARE_EQUAL(StringReference(L"Length"), NavCategory::GetFriendlyName(ViewMode::Length));
@ -232,6 +260,10 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Scientific));
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Programmer));
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Date));
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Graphing));
}
VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Currency));
VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Volume));
@ -251,12 +283,23 @@ namespace CalculatorUnitTests
void NavCategoryUnitTests::GetIndex()
{
// Index is the 0-based ordering of modes
vector<ViewMode> orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency,
ViewMode::Volume, ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy,
ViewMode::Area, ViewMode::Speed, ViewMode::Time, ViewMode::Power, ViewMode::Data,
ViewMode::Pressure, ViewMode::Angle };
vector<ViewMode> orderedModes;
for (size_t index = 0; index < orderedModes.size(); index++)
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Graphing, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency,
ViewMode::Volume, ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area,
ViewMode::Speed, ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle };
}
else
{
orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency, ViewMode::Volume,
ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area, ViewMode::Speed,
ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle };
}
auto orderedModesSize = size(orderedModes);
for (size_t index = 0; index < orderedModesSize; index++)
{
ViewMode mode = orderedModes[index];
VERIFY_ARE_EQUAL(index, (size_t)NavCategory::GetIndex(mode));
@ -268,12 +311,23 @@ namespace CalculatorUnitTests
void NavCategoryUnitTests::GetPosition()
{
// Position is the 1-based ordering of modes
vector<ViewMode> orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency,
ViewMode::Volume, ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy,
ViewMode::Area, ViewMode::Speed, ViewMode::Time, ViewMode::Power, ViewMode::Data,
ViewMode::Pressure, ViewMode::Angle };
vector<ViewMode> orderedModes;
for (size_t pos = 1; pos <= orderedModes.size(); pos++)
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Graphing, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency,
ViewMode::Volume, ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area,
ViewMode::Speed, ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle };
}
else
{
orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency, ViewMode::Volume,
ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area, ViewMode::Speed,
ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle };
}
auto orderedModesSize = size(orderedModes);
for (size_t pos = 1; pos <= orderedModesSize; pos++)
{
ViewMode mode = orderedModes[pos - 1];
VERIFY_ARE_EQUAL(pos, (size_t)NavCategory::GetPosition(mode));
@ -295,9 +349,17 @@ namespace CalculatorUnitTests
{
VERIFY_ARE_EQUAL(0, NavCategory::GetIndexInGroup(ViewMode::Standard, CategoryGroupType::Calculator));
VERIFY_ARE_EQUAL(1, NavCategory::GetIndexInGroup(ViewMode::Scientific, CategoryGroupType::Calculator));
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
VERIFY_ARE_EQUAL(2, NavCategory::GetIndexInGroup(ViewMode::Graphing, CategoryGroupType::Calculator));
VERIFY_ARE_EQUAL(3, NavCategory::GetIndexInGroup(ViewMode::Programmer, CategoryGroupType::Calculator));
VERIFY_ARE_EQUAL(4, NavCategory::GetIndexInGroup(ViewMode::Date, CategoryGroupType::Calculator));
}
else
{
VERIFY_ARE_EQUAL(2, NavCategory::GetIndexInGroup(ViewMode::Programmer, CategoryGroupType::Calculator));
VERIFY_ARE_EQUAL(3, NavCategory::GetIndexInGroup(ViewMode::Date, CategoryGroupType::Calculator));
}
VERIFY_ARE_EQUAL(0, NavCategory::GetIndexInGroup(ViewMode::Currency, CategoryGroupType::Converter));
VERIFY_ARE_EQUAL(1, NavCategory::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter));
VERIFY_ARE_EQUAL(2, NavCategory::GetIndexInGroup(ViewMode::Length, CategoryGroupType::Converter));
@ -320,7 +382,17 @@ namespace CalculatorUnitTests
{
VERIFY_ARE_EQUAL(ViewMode::Standard, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number1));
VERIFY_ARE_EQUAL(ViewMode::Scientific, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number2));
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
VERIFY_ARE_EQUAL(ViewMode::Graphing, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number3));
VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number4));
VERIFY_ARE_EQUAL(ViewMode::Date, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number5));
}
else
{
VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number3));
VERIFY_ARE_EQUAL(ViewMode::Date, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number4));
}
}
TEST_CLASS(NavCategoryGroupUnitTests)
@ -329,14 +401,13 @@ namespace CalculatorUnitTests
TEST_METHOD(CreateNavCategoryGroup);
private:
void ValidateNavCategory(IObservableVector<NavCategory ^> ^ categories, unsigned int index, ViewMode expectedMode, int expectedPosition)
void ValidateNavCategory(IObservableVector<NavCategory ^> ^ categories, unsigned int index, ViewMode expectedMode)
{
VERIFY_IS_LESS_THAN(0u, categories->Size);
VERIFY_IS_GREATER_THAN(categories->Size, index);
NavCategory ^ category = categories->GetAt(index);
VERIFY_ARE_EQUAL(expectedMode, category->Mode);
VERIFY_ARE_EQUAL(expectedPosition, category->Position);
}
};
@ -350,29 +421,39 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, calculatorGroup->GroupType);
IObservableVector<NavCategory ^> ^ calculatorCategories = calculatorGroup->Categories;
ValidateNavCategory(calculatorCategories, 0u, ViewMode::Standard);
ValidateNavCategory(calculatorCategories, 1u, ViewMode::Scientific);
if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"))
{
ValidateNavCategory(calculatorCategories, 2u, ViewMode::Graphing);
ValidateNavCategory(calculatorCategories, 3u, ViewMode::Programmer);
ValidateNavCategory(calculatorCategories, 4u, ViewMode::Date);
VERIFY_ARE_EQUAL(5, calculatorCategories->Size);
}
else
{
ValidateNavCategory(calculatorCategories, 2u, ViewMode::Programmer);
ValidateNavCategory(calculatorCategories, 3u, ViewMode::Date);
VERIFY_ARE_EQUAL(4, calculatorCategories->Size);
ValidateNavCategory(calculatorCategories, 0u, ViewMode::Standard, 1);
ValidateNavCategory(calculatorCategories, 1u, ViewMode::Scientific, 2);
ValidateNavCategory(calculatorCategories, 2u, ViewMode::Programmer, 3);
ValidateNavCategory(calculatorCategories, 3u, ViewMode::Date, 4);
}
NavCategoryGroup ^ converterGroup = menuOptions->GetAt(1);
VERIFY_ARE_EQUAL(CategoryGroupType::Converter, converterGroup->GroupType);
IObservableVector<NavCategory ^> ^ converterCategories = converterGroup->Categories;
VERIFY_ARE_EQUAL(13, converterCategories->Size);
ValidateNavCategory(converterCategories, 0u, ViewMode::Currency, 5);
ValidateNavCategory(converterCategories, 1u, ViewMode::Volume, 6);
ValidateNavCategory(converterCategories, 2u, ViewMode::Length, 7);
ValidateNavCategory(converterCategories, 3u, ViewMode::Weight, 8);
ValidateNavCategory(converterCategories, 4u, ViewMode::Temperature, 9);
ValidateNavCategory(converterCategories, 5u, ViewMode::Energy, 10);
ValidateNavCategory(converterCategories, 6u, ViewMode::Area, 11);
ValidateNavCategory(converterCategories, 7u, ViewMode::Speed, 12);
ValidateNavCategory(converterCategories, 8u, ViewMode::Time, 13);
ValidateNavCategory(converterCategories, 9u, ViewMode::Power, 14);
ValidateNavCategory(converterCategories, 10u, ViewMode::Data, 15);
ValidateNavCategory(converterCategories, 11u, ViewMode::Pressure, 16);
ValidateNavCategory(converterCategories, 12u, ViewMode::Angle, 17);
ValidateNavCategory(converterCategories, 0u, ViewMode::Currency);
ValidateNavCategory(converterCategories, 1u, ViewMode::Volume);
ValidateNavCategory(converterCategories, 2u, ViewMode::Length);
ValidateNavCategory(converterCategories, 3u, ViewMode::Weight);
ValidateNavCategory(converterCategories, 4u, ViewMode::Temperature);
ValidateNavCategory(converterCategories, 5u, ViewMode::Energy);
ValidateNavCategory(converterCategories, 6u, ViewMode::Area);
ValidateNavCategory(converterCategories, 7u, ViewMode::Speed);
ValidateNavCategory(converterCategories, 8u, ViewMode::Time);
ValidateNavCategory(converterCategories, 9u, ViewMode::Power);
ValidateNavCategory(converterCategories, 10u, ViewMode::Data);
ValidateNavCategory(converterCategories, 11u, ViewMode::Pressure);
ValidateNavCategory(converterCategories, 12u, ViewMode::Angle);
}
}

View File

@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by CalculatorUnitTests_VS.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -0,0 +1,906 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Grapher.h"
#include "IBitmap.h"
#include "../../CalcViewModel/GraphingCalculatorEnums.h"
using namespace Graphing;
using namespace GraphControl;
using namespace GraphControl::DX;
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::Devices::Input;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Storage::Streams;
using namespace Windows::System;
using namespace Windows::UI;
using namespace Windows::UI::Core;
using namespace Windows::UI::Input;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace GraphControl;
DEPENDENCY_PROPERTY_INITIALIZATION(Grapher, ForceProportionalAxes);
DEPENDENCY_PROPERTY_INITIALIZATION(Grapher, Variables);
DEPENDENCY_PROPERTY_INITIALIZATION(Grapher, Equations);
DEPENDENCY_PROPERTY_INITIALIZATION(Grapher, AxesColor);
DEPENDENCY_PROPERTY_INITIALIZATION(Grapher, GraphBackground);
namespace
{
constexpr auto s_defaultStyleKey = L"GraphControl.Grapher";
constexpr auto s_templateKey_SwapChainPanel = L"GraphSurface";
constexpr auto s_X = L"x";
constexpr auto s_Y = L"y";
constexpr auto s_defaultFormatType = FormatType::MathML;
constexpr auto s_getGraphOpeningTags = L"<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><mi>show2d</mi><mfenced separators=\"\">";
constexpr auto s_getGraphClosingTags = L"</mfenced></mrow></math>";
// Helper function for converting a pointer position to a position that the graphing engine will understand.
// posX/posY are the pointer position elements and width,height are the dimensions of the graph surface.
// The graphing engine interprets x,y position between the range [-1, 1].
// Translate the pointer position to the [-1, 1] bounds.
__inline pair<double, double> PointerPositionToGraphPosition(double posX, double posY, double width, double height)
{
return make_pair((2 * posX / width - 1), (1 - 2 * posY / height));
}
}
namespace GraphControl
{
Grapher::Grapher()
: m_solver{ IMathSolver::CreateMathSolver() }
, m_graph{ m_solver->CreateGrapher() }
, m_Moving{ false }
{
Equations = ref new EquationCollection();
m_solver->ParsingOptions().SetFormatType(s_defaultFormatType);
m_solver->FormatOptions().SetFormatType(s_defaultFormatType);
m_solver->FormatOptions().SetMathMLPrefix(wstring(L"mml"));
DefaultStyleKey = StringReference(s_defaultStyleKey);
this->ManipulationMode = ManipulationModes::TranslateX | ManipulationModes::TranslateY | ManipulationModes::TranslateInertia | ManipulationModes::Scale
| ManipulationModes::ScaleInertia;
auto cw = CoreWindow::GetForCurrentThread();
cw->KeyDown += ref new TypedEventHandler<CoreWindow ^, KeyEventArgs ^>(this, &Grapher::OnCoreKeyDown);
cw->KeyUp += ref new TypedEventHandler<CoreWindow ^, KeyEventArgs ^>(this, &Grapher::OnCoreKeyUp);
auto& formatOptions = m_solver->FormatOptions();
}
void Grapher::ZoomFromCenter(double scale)
{
ScaleRange(0, 0, scale);
}
void Grapher::ScaleRange(double centerX, double centerY, double scale)
{
if (m_graph != nullptr && m_renderMain != nullptr)
{
if (auto renderer = m_graph->GetRenderer())
{
if (SUCCEEDED(renderer->ScaleRange(centerX, centerY, scale)))
{
m_renderMain->RunRenderPass();
}
}
}
}
void Grapher::ResetGrid()
{
if (m_graph != nullptr && m_renderMain != nullptr)
{
if (auto renderer = m_graph->GetRenderer())
{
if (SUCCEEDED(renderer->ResetRange()))
{
m_renderMain->RunRenderPass();
}
}
}
}
void Grapher::OnApplyTemplate()
{
auto swapChainPanel = dynamic_cast<SwapChainPanel ^>(GetTemplateChild(StringReference(s_templateKey_SwapChainPanel)));
if (swapChainPanel)
{
swapChainPanel->AllowFocusOnInteraction = true;
m_renderMain = ref new RenderMain(swapChainPanel);
m_renderMain->BackgroundColor = GraphBackground;
}
TryUpdateGraph(false);
}
void Grapher::OnEquationsPropertyChanged(EquationCollection ^ oldValue, EquationCollection ^ newValue)
{
if (oldValue != nullptr)
{
if (m_tokenEquationChanged.Value != 0)
{
oldValue->EquationChanged -= m_tokenEquationChanged;
m_tokenEquationChanged.Value = 0;
}
if (m_tokenEquationStyleChanged.Value != 0)
{
oldValue->EquationStyleChanged -= m_tokenEquationStyleChanged;
m_tokenEquationStyleChanged.Value = 0;
}
if (m_tokenEquationLineEnabledChanged.Value != 0)
{
oldValue->EquationLineEnabledChanged -= m_tokenEquationLineEnabledChanged;
m_tokenEquationLineEnabledChanged.Value = 0;
}
}
if (newValue != nullptr)
{
m_tokenEquationChanged = newValue->EquationChanged += ref new EquationChangedEventHandler(this, &Grapher::OnEquationChanged);
m_tokenEquationStyleChanged = newValue->EquationStyleChanged += ref new EquationChangedEventHandler(this, &Grapher::OnEquationStyleChanged);
m_tokenEquationLineEnabledChanged = newValue->EquationLineEnabledChanged +=
ref new EquationChangedEventHandler(this, &Grapher::OnEquationLineEnabledChanged);
}
PlotGraph(false);
}
void Grapher::OnEquationChanged(Equation ^ equation)
{
// If the equation was previously valid, we should try to graph again in the event of the failure
bool shouldRetry = equation->IsValidated;
// Reset these properties if the equation is requesting to be graphed again
equation->HasGraphError = false;
equation->IsValidated = false;
TryPlotGraph(false, shouldRetry);
}
void Grapher::OnEquationStyleChanged(Equation ^)
{
if (m_graph)
{
UpdateGraphOptions(m_graph->GetOptions(), GetGraphableEquations());
}
if (m_renderMain)
{
m_renderMain->RunRenderPass();
}
}
void Grapher::OnEquationLineEnabledChanged(Equation ^ equation)
{
// If the equation is in an error state or is empty, it should not be graphed anyway.
if (equation->HasGraphError || equation->Expression->IsEmpty())
{
return;
}
PlotGraph(true);
}
KeyGraphFeaturesInfo ^ Grapher::AnalyzeEquation(Equation ^ equation)
{
auto result = ref new KeyGraphFeaturesInfo();
if (auto graph = GetGraph(equation))
{
if (auto analyzer = graph->GetAnalyzer())
{
if (analyzer->CanFunctionAnalysisBePerformed())
{
if (S_OK
== analyzer->PerformFunctionAnalysis(
(Graphing::Analyzer::NativeAnalysisType)Graphing::Analyzer::PerformAnalysisType::PerformAnalysisType_All))
{
Graphing::IGraphFunctionAnalysisData functionAnalysisData = m_solver->Analyze(analyzer.get());
return KeyGraphFeaturesInfo::Create(functionAnalysisData);
}
}
else
{
return KeyGraphFeaturesInfo::Create(CalculatorApp::AnalysisErrorType::AnalysisNotSupported);
}
}
}
return KeyGraphFeaturesInfo::Create(CalculatorApp::AnalysisErrorType::AnalysisCouldNotBePerformed);
}
void Grapher::PlotGraph(bool keepCurrentView)
{
TryPlotGraph(keepCurrentView, false);
}
void Grapher::TryPlotGraph(bool keepCurrentView, bool shouldRetry)
{
if (TryUpdateGraph(keepCurrentView))
{
SetEquationsAsValid();
}
else
{
SetEquationErrors();
// If we failed to plot the graph, try again after the bad equations are flagged.
if (shouldRetry)
{
TryUpdateGraph(keepCurrentView);
}
}
}
bool Grapher::TryUpdateGraph(bool keepCurrentView)
{
optional<vector<shared_ptr<IEquation>>> initResult = nullopt;
bool successful = false;
if (m_renderMain && m_graph != nullptr)
{
unique_ptr<IExpression> graphExpression;
wstring request;
auto validEqs = GetGraphableEquations();
// Will be set to true if the previous graph should be kept in the event of an error
bool shouldKeepPreviousGraph = false;
if (!validEqs.empty())
{
wstringstream ss{};
ss << s_getGraphOpeningTags;
int numValidEquations = 0;
for (Equation ^ eq : validEqs)
{
if (eq->IsValidated)
{
shouldKeepPreviousGraph = true;
}
if (numValidEquations++ > 0)
{
ss << L"<mo>,</mo>";
}
ss << eq->GetRequest()->Data();
}
ss << s_getGraphClosingTags;
request = ss.str();
}
if (graphExpression = m_solver->ParseInput(request))
{
initResult = TryInitializeGraph(keepCurrentView, graphExpression.get());
if (initResult != nullopt)
{
UpdateGraphOptions(m_graph->GetOptions(), validEqs);
SetGraphArgs();
m_renderMain->Graph = m_graph;
// It is possible that the render fails, in that case fall through to explicit empty initialization
if (m_renderMain->RunRenderPass())
{
UpdateVariables();
successful = true;
}
else
{
// If we failed to render then we have already lost the previous graph
shouldKeepPreviousGraph = false;
initResult = nullopt;
}
}
}
if (initResult == nullopt)
{
// Do not re-initialize the graph to empty if there are still valid equations graphed
if (!shouldKeepPreviousGraph)
{
initResult = TryInitializeGraph(keepCurrentView, graphExpression.get());
if (initResult != nullopt)
{
UpdateGraphOptions(m_graph->GetOptions(), validEqs);
SetGraphArgs();
m_renderMain->Graph = m_graph;
m_renderMain->RunRenderPass();
UpdateVariables();
// Initializing an empty graph is only a success if there were no equations to graph.
successful = (validEqs.size() == 0);
}
}
}
}
// Return true if we were able to graph and render all graphable equations
return successful;
}
void Grapher::SetEquationsAsValid()
{
for (Equation ^ eq : GetGraphableEquations())
{
eq->IsValidated = true;
}
}
void Grapher::SetEquationErrors()
{
for (Equation ^ eq : GetGraphableEquations())
{
if (!eq->IsValidated)
{
eq->HasGraphError = true;
}
}
}
void Grapher::SetGraphArgs()
{
if (m_graph)
{
for (auto variable : Variables)
{
m_graph->SetArgValue(variable->Key->Data(), variable->Value);
}
}
}
shared_ptr<IGraph> Grapher::GetGraph(Equation ^ equation)
{
shared_ptr<Graphing::IGraph> graph = m_solver->CreateGrapher();
wstringstream ss{};
ss << s_getGraphOpeningTags;
ss << equation->GetRequest()->Data();
ss << s_getGraphClosingTags;
wstring request = ss.str();
unique_ptr<IExpression> graphExpression;
if (graphExpression = m_solver->ParseInput(request))
{
if (graph->TryInitialize(graphExpression.get()))
{
return graph;
}
}
return nullptr;
}
void Grapher::UpdateVariables()
{
auto updatedVariables = ref new Map<String ^, double>();
if (m_graph)
{
auto graphVariables = m_graph->GetVariables();
for (auto graphVar : graphVariables)
{
if (graphVar->GetVariableName() != s_X && graphVar->GetVariableName() != s_Y)
{
auto key = ref new String(graphVar->GetVariableName().data());
double value = 1.0;
if (Variables->HasKey(key))
{
value = Variables->Lookup(key);
}
updatedVariables->Insert(key, value);
}
}
}
Variables = updatedVariables;
VariablesUpdated(this, Variables);
}
void Grapher::SetVariable(Platform::String ^ variableName, double newValue)
{
if (Variables->HasKey(variableName))
{
if (Variables->Lookup(variableName) == newValue)
{
return;
}
Variables->Remove(variableName);
}
Variables->Insert(variableName, newValue);
if (m_graph)
{
m_graph->SetArgValue(variableName->Data(), newValue);
if (m_renderMain)
{
m_renderMain->RunRenderPass();
}
}
}
void Grapher::UpdateGraphOptions(IGraphingOptions& options, const vector<Equation ^>& validEqs)
{
options.SetForceProportional(ForceProportionalAxes);
if (!options.GetAllowKeyGraphFeaturesForFunctionsWithParameters())
{
options.SetAllowKeyGraphFeaturesForFunctionsWithParameters(true);
}
if (!validEqs.empty())
{
vector<Graphing::Color> graphColors;
graphColors.reserve(validEqs.size());
for (Equation ^ eq : validEqs)
{
auto lineColor = eq->LineColor;
graphColors.emplace_back(lineColor.R, lineColor.G, lineColor.B, lineColor.A);
}
options.SetGraphColors(graphColors);
}
}
vector<Equation ^> Grapher::GetGraphableEquations()
{
vector<Equation ^> validEqs;
for (Equation ^ eq : Equations)
{
if (eq->IsGraphableEquation())
{
validEqs.push_back(eq);
}
}
return validEqs;
}
void Grapher::OnForceProportionalAxesPropertyChanged(bool /*oldValue*/, bool newValue)
{
m_calculatedForceProportional = newValue;
TryUpdateGraph(false);
}
void Grapher::OnPointerEntered(PointerRoutedEventArgs ^ e)
{
if (m_renderMain)
{
OnPointerMoved(e);
m_renderMain->DrawNearestPoint = true;
e->Handled = true;
}
}
void Grapher::UpdateTracingChanged()
{
if (m_renderMain->Tracing)
{
TracingChangedEvent(true);
TracingValueChangedEvent(m_renderMain->TraceValue);
}
else
{
TracingChangedEvent(false);
}
}
void Grapher::OnPointerMoved(PointerRoutedEventArgs ^ e)
{
if (m_renderMain)
{
PointerPoint ^ currPoint = e->GetCurrentPoint(/* relativeTo */ this);
m_renderMain->PointerLocation = currPoint->Position;
UpdateTracingChanged();
e->Handled = true;
}
}
void Grapher::OnPointerExited(PointerRoutedEventArgs ^ e)
{
if (m_renderMain)
{
m_renderMain->DrawNearestPoint = false;
TracingChangedEvent(false);
e->Handled = true;
}
}
void Grapher::OnPointerWheelChanged(PointerRoutedEventArgs ^ e)
{
PointerPoint ^ currentPointer = e->GetCurrentPoint(/*relative to*/ this);
double delta = currentPointer->Properties->MouseWheelDelta;
// The maximum delta is 120 according to:
// https://docs.microsoft.com/en-us/uwp/api/windows.ui.input.pointerpointproperties.mousewheeldelta#Windows_UI_Input_PointerPointProperties_MouseWheelDelta
// Apply a dampening effect so that small mouse movements have a smoother zoom.
constexpr double scrollDamper = 0.15;
double scale = 1.0 + (abs(delta) / WHEEL_DELTA) * scrollDamper;
// positive delta if wheel scrolled away from the user
if (delta >= 0)
{
scale = 1.0 / scale;
}
// For scaling, the graphing engine interprets x,y position between the range [-1, 1].
// Translate the pointer position to the [-1, 1] bounds.
const auto& pos = currentPointer->Position;
const auto [centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, ActualWidth, ActualHeight);
ScaleRange(centerX, centerY, scale);
e->Handled = true;
}
void Grapher::OnPointerPressed(PointerRoutedEventArgs ^ e)
{
// Set the pointer capture to the element being interacted with so that only it
// will fire pointer-related events
CapturePointer(e->Pointer);
}
void Grapher::OnPointerReleased(PointerRoutedEventArgs ^ e)
{
ReleasePointerCapture(e->Pointer);
}
void Grapher::OnPointerCanceled(PointerRoutedEventArgs ^ e)
{
ReleasePointerCapture(e->Pointer);
}
void Grapher::OnManipulationDelta(ManipulationDeltaRoutedEventArgs ^ e)
{
if (m_renderMain != nullptr && m_graph != nullptr)
{
if (auto renderer = m_graph->GetRenderer())
{
// Only call for a render pass if we actually scaled or translated.
bool needsRenderPass = false;
const double width = ActualWidth;
const double height = ActualHeight;
const auto& translation = e->Delta.Translation;
double translationX = translation.X;
double translationY = translation.Y;
if (translationX != 0 || translationY != 0)
{
// The graphing engine pans the graph according to a ratio for x and y.
// A value of +1 means move a half screen in the positive direction for the given axis.
// Convert the manipulation's translation values to ratios for the engine.
translationX /= -width;
translationY /= height;
if (FAILED(renderer->MoveRangeByRatio(translationX, translationY)))
{
return;
}
needsRenderPass = true;
}
if (double scale = e->Delta.Scale; scale != 1.0)
{
// The graphing engine interprets scale amounts as the inverse of the value retrieved
// from the ManipulationUpdatedEventArgs. Invert the scale amount for the engine.
scale = 1.0 / scale;
// Convert from PointerPosition to graph position.
const auto& pos = e->Position;
const auto [centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, width, height);
if (FAILED(renderer->ScaleRange(centerX, centerY, scale)))
{
return;
}
needsRenderPass = true;
}
if (needsRenderPass)
{
m_renderMain->RunRenderPass();
}
}
}
}
RandomAccessStreamReference ^ Grapher::GetGraphBitmapStream()
{
RandomAccessStreamReference ^ outputStream;
if (m_renderMain != nullptr && m_graph != nullptr)
{
if (auto renderer = m_graph->GetRenderer())
{
shared_ptr<Graphing::IBitmap> BitmapOut;
bool hasSomeMissingDataOut = false;
HRESULT hr = E_FAIL;
hr = renderer->GetBitmap(BitmapOut, hasSomeMissingDataOut);
if (SUCCEEDED(hr))
{
// Get the raw date
vector<BYTE> byteVector = BitmapOut->GetData();
auto arr = ref new Array<BYTE>(&byteVector[0], (unsigned int)byteVector.size());
// create a memory stream wrapper
InMemoryRandomAccessStream ^ stream = ref new InMemoryRandomAccessStream();
// Get a writer to transfer the data
auto writer = ref new DataWriter(stream->GetOutputStreamAt(0));
// write the data
writer->WriteBytes(arr);
writer->StoreAsync()->GetResults();
// Get a reference stream to return;
outputStream = RandomAccessStreamReference::CreateFromStream(stream);
}
else
{
OutputDebugString(L"Grapher::GetGraphBitmapStream() unable to get graph image from renderer\r\n");
winrt::throw_hresult(hr);
}
}
}
return outputStream;
}
}
void Grapher::OnCoreKeyUp(CoreWindow ^ sender, KeyEventArgs ^ e)
{
// We don't want to react to keyboard input unless the graph control has the focus.
// NOTE: you can't select the graph control from the mouse for focus but you can tab to it.
GraphControl::Grapher ^ gcHasFocus = dynamic_cast<GraphControl::Grapher ^>(FocusManager::GetFocusedElement());
if (gcHasFocus == nullptr || gcHasFocus != this)
{
// Not a graphingCalculator control so we don't want the input.
return;
}
switch (e->VirtualKey)
{
case VirtualKey::Left:
case VirtualKey::Right:
case VirtualKey::Down:
case VirtualKey::Up:
case VirtualKey::Shift:
{
HandleKey(false, e->VirtualKey);
}
break;
}
}
void Grapher::OnCoreKeyDown(CoreWindow ^ sender, KeyEventArgs ^ e)
{
// We don't want to react to any keys when we are not in the graph control
GraphControl::Grapher ^ gcHasFocus = dynamic_cast<GraphControl::Grapher ^>(FocusManager::GetFocusedElement());
if (gcHasFocus == nullptr || gcHasFocus != this)
{
// Not a graphingCalculator control so we don't want the input.
return;
}
switch (e->VirtualKey)
{
case VirtualKey::Left:
case VirtualKey::Right:
case VirtualKey::Down:
case VirtualKey::Up:
case VirtualKey::Shift:
{
HandleKey(true, e->VirtualKey);
}
break;
}
}
void Grapher::HandleKey(bool keyDown, VirtualKey key)
{
int pressedKeys = 0;
if (key == VirtualKey::Left)
{
m_KeysPressed[KeysPressedSlots::Left] = keyDown;
if (keyDown)
{
pressedKeys++;
}
}
if (key == VirtualKey::Right)
{
m_KeysPressed[KeysPressedSlots::Right] = keyDown;
if (keyDown)
{
pressedKeys++;
}
}
if (key == VirtualKey::Up)
{
m_KeysPressed[KeysPressedSlots::Up] = keyDown;
if (keyDown)
{
pressedKeys++;
}
}
if (key == VirtualKey::Down)
{
m_KeysPressed[KeysPressedSlots::Down] = keyDown;
if (keyDown)
{
pressedKeys++;
}
}
if (key == VirtualKey::Shift)
{
m_KeysPressed[KeysPressedSlots::Accelerator] = keyDown;
}
if (pressedKeys > 0 && !m_Moving)
{
m_Moving = true;
// Key(s) we care about, so ensure we are ticking our timer (and that we have one to tick)
if (m_TracingTrackingTimer == nullptr)
{
m_TracingTrackingTimer = ref new DispatcherTimer();
m_TracingTrackingTimer->Tick += ref new EventHandler<Object ^>(this, &Grapher::HandleTracingMovementTick);
TimeSpan ts;
ts.Duration = 100000; // .1 second
m_TracingTrackingTimer->Interval = ts;
auto i = m_TracingTrackingTimer->Interval;
}
m_TracingTrackingTimer->Start();
}
}
void Grapher::HandleTracingMovementTick(Object ^ sender, Object ^ e)
{
int delta = 5;
int liveKeys = 0;
if (m_KeysPressed[KeysPressedSlots::Accelerator])
{
delta = 1;
}
auto curPos = ActiveTraceCursorPosition;
if (m_KeysPressed[KeysPressedSlots::Left])
{
liveKeys++;
curPos.X -= delta;
if (curPos.X < 0)
{
curPos.X = 0;
}
}
if (m_KeysPressed[KeysPressedSlots::Right])
{
liveKeys++;
curPos.X += delta;
if (curPos.X > ActualWidth - delta)
{
curPos.X = (float)ActualWidth - delta; // TODO change this to deal with size of cursor
}
}
if (m_KeysPressed[KeysPressedSlots::Up])
{
liveKeys++;
curPos.Y -= delta;
if (curPos.Y < 0)
{
curPos.Y = 0;
}
}
if (m_KeysPressed[KeysPressedSlots::Down])
{
liveKeys++;
curPos.Y += delta;
if (curPos.Y > ActualHeight - delta)
{
curPos.Y = (float)ActualHeight - delta; // TODO change this to deal with size of cursor
}
}
if (liveKeys == 0)
{
m_Moving = false;
// Non of the keys we care about are being hit any longer so shut down our timer
m_TracingTrackingTimer->Stop();
}
else
{
ActiveTraceCursorPosition = curPos;
PointerValueChangedEvent(curPos);
}
}
String ^ Grapher::ConvertToLinear(String ^ mmlString)
{
m_solver->FormatOptions().SetFormatType(FormatType::LinearInput);
auto expression = m_solver->ParseInput(mmlString->Data());
auto linearExpression = m_solver->Serialize(expression.get());
m_solver->FormatOptions().SetFormatType(s_defaultFormatType);
return ref new String(linearExpression.c_str());
}
void Grapher::OnAxesColorPropertyChanged(Windows::UI::Color /*oldValue*/, Windows::UI::Color newValue)
{
if (m_graph)
{
auto axesColor = Graphing::Color(newValue.R, newValue.G, newValue.B, newValue.A);
m_graph->GetOptions().SetAxisColor(axesColor);
m_graph->GetOptions().SetFontColor(axesColor);
}
}
void Grapher::OnGraphBackgroundPropertyChanged(Windows::UI::Color /*oldValue*/, Windows::UI::Color newValue)
{
if (m_renderMain)
{
m_renderMain->BackgroundColor = newValue;
}
if (m_graph)
{
auto color = Graphing::Color(newValue.R, newValue.G, newValue.B, newValue.A);
m_graph->GetOptions().SetBackColor(color);
m_graph->GetOptions().SetBoxColor(color);
}
}
optional<vector<shared_ptr<Graphing::IEquation>>> Grapher::TryInitializeGraph(bool keepCurrentView, const IExpression* graphingExp)
{
if (keepCurrentView)
{
double xMin, xMax, yMin, yMax;
m_graph->GetRenderer()->GetDisplayRanges(xMin, xMax, yMin, yMax);
auto initResult = m_graph->TryInitialize(graphingExp);
m_graph->GetRenderer()->SetDisplayRanges(xMin, xMax, yMin, yMax);
return initResult;
}
else
{
return m_graph->TryInitialize(graphingExp);
}
}

View File

@ -0,0 +1,331 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "DirectX/RenderMain.h"
#include "../Models/Equation.h"
#include "../Models/EquationCollection.h"
#include "../Utils.h"
#include "IGraphAnalyzer.h"
#include "IMathSolver.h"
#include "Common.h"
#include "Models/KeyGraphFeaturesInfo.h"
#include <ppltasks.h>
namespace GraphControl
{
public
delegate void TracingChangedEventHandler(bool newValue);
public
delegate void TracingValueChangedEventHandler(Windows::Foundation::Point value);
public
delegate void PointerValueChangedEventHandler(Windows::Foundation::Point value);
[Windows::UI::Xaml::Markup::ContentPropertyAttribute(Name = L"Equations")] public ref class Grapher sealed
: public Windows::UI::Xaml::Controls::Control,
public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
event TracingValueChangedEventHandler ^ TracingValueChangedEvent;
event PointerValueChangedEventHandler ^ PointerValueChangedEvent;
event TracingChangedEventHandler ^ TracingChangedEvent;
virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged;
public:
Grapher();
DEPENDENCY_PROPERTY_OWNER(Grapher);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(bool, ForceProportionalAxes, true);
DEPENDENCY_PROPERTY_WITH_DEFAULT(
SINGLE_ARG(Windows::Foundation::Collections::IObservableMap<Platform::String ^, double> ^),
Variables,
SINGLE_ARG(ref new Platform::Collections::Map<Platform::String ^, double>()));
DEPENDENCY_PROPERTY_R_WITH_DEFAULT_AND_CALLBACK(GraphControl::EquationCollection ^, Equations, nullptr);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(Windows::UI::Color, AxesColor, Windows::UI::Colors::Transparent);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(Windows::UI::Color, GraphBackground, Windows::UI::Colors::Transparent);
// Pass active tracing turned on or off down to the renderer
property bool ActiveTracing
{
bool get()
{
return m_renderMain != nullptr && m_renderMain->ActiveTracing;
}
void set(bool value)
{
if (m_renderMain != nullptr && m_renderMain->ActiveTracing != value)
{
m_renderMain->ActiveTracing = value;
UpdateTracingChanged();
PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(L"ActiveTracing"));
}
}
}
void ZoomFromCenter(double scale);
void ResetGrid();
property Windows::Foundation::Point TraceValue
{
Windows::Foundation::Point get()
{
return m_renderMain->TraceValue;
}
}
property Windows::Foundation::Point TraceLocation
{
Windows::Foundation::Point get()
{
return m_renderMain->TraceLocation;
}
}
property Windows::Foundation::Point ActiveTraceCursorPosition
{
Windows::Foundation::Point get()
{
return m_renderMain->ActiveTraceCursorPosition;
}
void set(Windows::Foundation::Point newValue)
{
if (m_renderMain->ActiveTraceCursorPosition != newValue)
{
m_renderMain->ActiveTraceCursorPosition = newValue;
UpdateTracingChanged();
}
}
}
event Windows::Foundation::EventHandler<Windows::Foundation::Collections::IMap<Platform::String ^, double> ^> ^ VariablesUpdated;
void SetVariable(Platform::String ^ variableName, double newValue);
Platform::String ^ ConvertToLinear(Platform::String ^ mmlString);
/// <summary>
/// Draw the graph. Call this method if you add or modify an equation.
/// </summary>
/// <param name="keepCurrentView">Force the graph control to not pan or zoom to adapt the view.</param>
void PlotGraph(bool keepCurrentView);
GraphControl::KeyGraphFeaturesInfo ^ AnalyzeEquation(GraphControl::Equation ^ equation);
// We can't use the EvalTrigUnitMode enum directly in as the property type because it comes from another module which doesn't expose
// it as a public enum class. So the compiler doesn't recognize it as a valid type for the ABI boundary.
property int TrigUnitMode
{
void set(int value)
{
if (value != (int)m_solver->EvalOptions().GetTrigUnitMode())
{
m_solver->EvalOptions().SetTrigUnitMode((Graphing::EvalTrigUnitMode)value);
PlotGraph(true);
}
}
int get()
{
return (int)m_solver->EvalOptions().GetTrigUnitMode();
}
}
property double XAxisMin
{
double get()
{
return m_graph->GetOptions().GetDefaultXRange().first;
}
void set(double value)
{
std::pair<double, double> newValue(value, XAxisMax);
if (m_graph != nullptr)
{
m_graph->GetOptions().SetDefaultXRange(newValue);
if (m_renderMain != nullptr)
{
m_renderMain->RunRenderPass();
}
}
}
}
property double XAxisMax
{
double get()
{
return m_graph->GetOptions().GetDefaultXRange().second;
}
void set(double value)
{
std::pair<double, double> newValue(XAxisMin, value);
if (m_graph != nullptr)
{
m_graph->GetOptions().SetDefaultXRange(newValue);
if (m_renderMain != nullptr)
{
m_renderMain->RunRenderPass();
}
}
}
}
property double YAxisMin
{
double get()
{
return m_graph->GetOptions().GetDefaultXRange().first;
}
void set(double value)
{
std::pair<double, double> newValue(value, YAxisMax);
if (m_graph != nullptr)
{
m_graph->GetOptions().SetDefaultYRange(newValue);
if (m_renderMain != nullptr)
{
m_renderMain->RunRenderPass();
}
}
}
}
property double YAxisMax
{
double get()
{
return m_graph->GetOptions().GetDefaultXRange().second;
}
void set(double value)
{
std::pair<double, double> newValue(YAxisMin, value);
if (m_graph != nullptr)
{
m_graph->GetOptions().SetDefaultYRange(newValue);
if (m_renderMain != nullptr)
{
m_renderMain->RunRenderPass();
}
}
}
}
void GetDisplayRanges(double* xMin, double* xMax, double* yMin, double* yMax)
{
try
{
if (m_graph != nullptr)
{
if (auto render = m_graph->GetRenderer())
{
render->GetDisplayRanges(*xMin, *xMax, *yMin, *yMax);
}
}
}
catch (const std::exception&)
{
OutputDebugString(L"GetDisplayRanges failed\r\n");
}
}
void SetDisplayRanges(double xMin, double xMax, double yMin, double yMax)
{
try
{
if (auto render = m_graph->GetRenderer())
{
render->SetDisplayRanges(xMin, xMax, yMin, yMax);
if (m_renderMain)
{
m_renderMain->RunRenderPass();
}
}
}
catch (const std::exception&)
{
OutputDebugString(L"SetDisplayRanges failed\r\n");
}
}
protected:
#pragma region Control Overrides
void OnApplyTemplate() override;
void OnPointerEntered(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerMoved(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerExited(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerWheelChanged(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerPressed(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerReleased(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerCanceled(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnManipulationDelta(Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs ^ e) override;
#pragma endregion
private:
void OnForceProportionalAxesPropertyChanged(bool oldValue, bool newValue);
void OnEquationsPropertyChanged(EquationCollection ^ oldValue, EquationCollection ^ newValue);
void OnAxesColorPropertyChanged(Windows::UI::Color oldValue, Windows::UI::Color newValue);
void OnGraphBackgroundPropertyChanged(Windows::UI::Color oldValue, Windows::UI::Color newValue);
void OnEquationChanged(Equation ^ equation);
void OnEquationStyleChanged(Equation ^ equation);
void OnEquationLineEnabledChanged(Equation ^ equation);
bool TryUpdateGraph(bool keepCurrentView);
void TryPlotGraph(bool keepCurrentView, bool shouldRetry);
void UpdateGraphOptions(Graphing::IGraphingOptions& options, const std::vector<Equation ^>& validEqs);
std::vector<Equation ^> GetGraphableEquations();
void SetGraphArgs();
std::shared_ptr<Graphing::IGraph> GetGraph(GraphControl::Equation ^ equation);
void UpdateVariables();
void ScaleRange(double centerX, double centerY, double scale);
void OnCoreKeyDown(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ e);
void OnCoreKeyUp(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ e);
void UpdateTracingChanged();
void HandleTracingMovementTick(Object ^ sender, Object ^ e);
void HandleKey(bool keyDown, Windows::System::VirtualKey key);
void SetEquationsAsValid();
void SetEquationErrors();
std::optional<std::vector<std::shared_ptr<Graphing::IEquation>>> TryInitializeGraph(bool keepCurrentView, _In_ const Graphing::IExpression* graphingExp = nullptr);
private:
DX::RenderMain ^ m_renderMain = nullptr;
static Windows::UI::Xaml::DependencyProperty ^ s_equationTemplateProperty;
static Windows::UI::Xaml::DependencyProperty ^ s_equationsSourceProperty;
Windows::Foundation::EventRegistrationToken m_tokenDataSourceChanged;
static Windows::UI::Xaml::DependencyProperty ^ s_equationsProperty;
static Windows::UI::Xaml::DependencyProperty ^ s_variablesProperty;
Windows::Foundation::EventRegistrationToken m_tokenEquationsChanged;
Windows::Foundation::EventRegistrationToken m_tokenEquationStyleChanged;
Windows::Foundation::EventRegistrationToken m_tokenEquationChanged;
Windows::Foundation::EventRegistrationToken m_tokenEquationLineEnabledChanged;
Windows::Foundation::EventRegistrationToken m_tokenBackgroundColorChanged;
const std::unique_ptr<Graphing::IMathSolver> m_solver;
const std::shared_ptr<Graphing::IGraph> m_graph;
bool m_calculatedForceProportional = false;
bool m_tracingTracking;
enum KeysPressedSlots
{
Left,
Right,
Down,
Up,
Accelerator
};
bool m_KeysPressed[5];
bool m_Moving;
Windows::UI::Xaml::DispatcherTimer ^ m_TracingTrackingTimer;
public:
Windows::Storage::Streams::RandomAccessStreamReference ^ GetGraphBitmapStream();
};
}

View File

@ -0,0 +1,662 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "DeviceResources.h"
#include "DirectXHelper.h"
using namespace D2D1;
using namespace DirectX;
using namespace Microsoft::WRL;
using namespace std;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml::Controls;
using namespace Platform;
namespace DisplayMetrics
{
// High resolution displays can require a lot of GPU and battery power to render.
// High resolution phones, for example, may suffer from poor battery life if
// games attempt to render at 60 frames per second at full fidelity.
// The decision to render at full fidelity across all platforms and form factors
// should be deliberate.
static constexpr bool SupportHighResolutions = false;
// The default thresholds that define a "high resolution" display. If the thresholds
// are exceeded and SupportHighResolutions is false, the dimensions will be scaled
// by 50%.
static constexpr float DpiThreshold = 192.0f; // 200% of standard desktop display.
static constexpr float WidthThreshold = 1920.0f; // 1080p width.
static constexpr float HeightThreshold = 1080.0f; // 1080p height.
};
// Constants used to calculate screen rotations.
namespace ScreenRotation
{
// 0-degree Z-rotation
static constexpr XMFLOAT4X4 Rotation0(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 90-degree Z-rotation
static constexpr XMFLOAT4X4 Rotation90(
0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 180-degree Z-rotation
static constexpr XMFLOAT4X4 Rotation180(
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 270-degree Z-rotation
static constexpr XMFLOAT4X4 Rotation270(
0.0f, -1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
};
namespace GraphControl::DX
{
// Constructor for DeviceResources.
DeviceResources::DeviceResources(SwapChainPanel^ panel) :
m_screenViewport(),
m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1),
m_d3dRenderTargetSize(),
m_outputSize(),
m_logicalSize(),
m_nativeOrientation(DisplayOrientations::None),
m_currentOrientation(DisplayOrientations::None),
m_dpi(-1.0f),
m_effectiveDpi(-1.0f),
m_compositionScaleX(1.0f),
m_compositionScaleY(1.0f),
m_deviceNotify(nullptr)
{
CreateDeviceIndependentResources();
CreateDeviceResources();
SetSwapChainPanel(panel);
}
// Configures resources that don't depend on the Direct3D device.
void DeviceResources::CreateDeviceIndependentResources()
{
// Initialize Direct2D resources.
D2D1_FACTORY_OPTIONS options;
ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
#if defined(_DEBUG)
// If the project is in a debug build, enable Direct2D debugging via SDK Layers.
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
// Initialize the Direct2D Factory.
DX::ThrowIfFailed(
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory3),
&options,
&m_d2dFactory
)
);
// Initialize the DirectWrite Factory.
DX::ThrowIfFailed(
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory3),
&m_dwriteFactory
)
);
// Initialize the Windows Imaging Component (WIC) Factory.
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory2,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicFactory)
)
);
}
// Configures the Direct3D device, and stores handles to it and the device context.
void DeviceResources::CreateDeviceResources()
{
// This flag adds support for surfaces with a different color channel ordering
// than the API default. It is required for compatibility with Direct2D.
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#if defined(_DEBUG)
if (DX::SdkLayersAvailable())
{
// If the project is in a debug build, enable debugging via SDK Layers with this flag.
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
}
#endif
// This array defines the set of DirectX hardware feature levels this app will support.
// Note the ordering should be preserved.
// Don't forget to declare your application's minimum required feature level in its
// description. All applications are assumed to support 9.1 unless otherwise stated.
static constexpr UINT featureLevelsSize = 9;
static constexpr std::array<D3D_FEATURE_LEVEL, featureLevelsSize> featureLevels =
{
D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
// Create the Direct3D 11 API device object and a corresponding context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
HRESULT hr = D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver.
0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
creationFlags, // Set debug and Direct2D compatibility flags.
&featureLevels[0], // List of feature levels this app can support.
featureLevelsSize, // Size of the list above.
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
&device, // Returns the Direct3D device created.
&m_d3dFeatureLevel, // Returns feature level of device created.
&context // Returns the device immediate context.
);
if (FAILED(hr))
{
// If the initialization fails, fall back to the WARP device.
// For more information on WARP, see:
// https://go.microsoft.com/fwlink/?LinkId=286690
DX::ThrowIfFailed(
D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device.
0,
creationFlags,
&featureLevels[0],
featureLevelsSize,
D3D11_SDK_VERSION,
&device,
&m_d3dFeatureLevel,
&context
)
);
}
// Store pointers to the Direct3D 11.3 API device and immediate context.
DX::ThrowIfFailed(
device.As(&m_d3dDevice)
);
DX::ThrowIfFailed(
context.As(&m_d3dContext)
);
// Create the Direct2D device object and a corresponding context.
ComPtr<IDXGIDevice3> dxgiDevice;
DX::ThrowIfFailed(
m_d3dDevice.As(&dxgiDevice)
);
DX::ThrowIfFailed(
m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
);
DX::ThrowIfFailed(
m_d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&m_d2dContext
)
);
}
// These resources need to be recreated every time the window size is changed.
void DeviceResources::CreateWindowSizeDependentResources()
{
// Clear the previous window size specific context.
static constexpr std::array<ID3D11RenderTargetView*, 1> nullViews = { nullptr };
m_d3dContext->OMSetRenderTargets(static_cast<UINT>(nullViews.size()), &nullViews[0], nullptr);
m_d3dRenderTargetView = nullptr;
m_d2dContext->SetTarget(nullptr);
m_d2dTargetBitmap = nullptr;
m_d3dDepthStencilView = nullptr;
m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr);
UpdateRenderTargetSize();
m_d3dRenderTargetSize.Width = m_outputSize.Width;
m_d3dRenderTargetSize.Height = m_outputSize.Height;
if (m_swapChain != nullptr)
{
// If the swap chain already exists, resize it.
HRESULT hr = m_swapChain->ResizeBuffers(
2, // Double-buffered swap chain.
lround(m_d3dRenderTargetSize.Width),
lround(m_d3dRenderTargetSize.Height),
DXGI_FORMAT_B8G8R8A8_UNORM,
0
);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
// If the device was removed for any reason, a new device and swap chain will need to be created.
HandleDeviceLost();
// Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
// and correctly set up the new device.
return;
}
else
{
DX::ThrowIfFailed(hr);
}
}
else
{
// Otherwise, create a new one using the same adapter as the existing Direct3D device.
DXGI_SCALING scaling = DisplayMetrics::SupportHighResolutions ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window.
swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height);
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use _FLIP_ SwapEffects.
swapChainDesc.Flags = 0;
swapChainDesc.Scaling = scaling;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
// This sequence obtains the DXGI factory that was used to create the Direct3D device above.
ComPtr<IDXGIDevice3> dxgiDevice;
DX::ThrowIfFailed(
m_d3dDevice.As(&dxgiDevice)
);
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(
dxgiDevice->GetAdapter(&dxgiAdapter)
);
ComPtr<IDXGIFactory4> dxgiFactory;
DX::ThrowIfFailed(
dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
);
// When using XAML interop, the swap chain must be created for composition.
ComPtr<IDXGISwapChain1> swapChain;
DX::ThrowIfFailed(
dxgiFactory->CreateSwapChainForComposition(
m_d3dDevice.Get(),
&swapChainDesc,
nullptr,
&swapChain
)
);
DX::ThrowIfFailed(
swapChain.As(&m_swapChain)
);
// Associate swap chain with SwapChainPanel
// UI changes will need to be dispatched back to the UI thread
m_swapChainPanel->Dispatcher->RunAsync(CoreDispatcherPriority::High, ref new DispatchedHandler([=]()
{
// Get backing native interface for SwapChainPanel
ComPtr<ISwapChainPanelNative> panelNative;
DX::ThrowIfFailed(
reinterpret_cast<IUnknown*>(m_swapChainPanel)->QueryInterface(IID_PPV_ARGS(&panelNative))
);
DX::ThrowIfFailed(
panelNative->SetSwapChain(m_swapChain.Get())
);
}, CallbackContext::Any));
// Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
// ensures that the application will only render after each VSync, minimizing power consumption.
DX::ThrowIfFailed(
dxgiDevice->SetMaximumFrameLatency(1)
);
}
// Set the proper orientation for the swap chain, and generate 2D and
// 3D matrix transformations for rendering to the rotated swap chain.
// Note the rotation angle for the 2D and 3D transforms are different.
// This is due to the difference in coordinate spaces. Additionally,
// the 3D matrix is specified explicitly to avoid rounding errors.
m_orientationTransform2D = Matrix3x2F::Identity();
m_orientationTransform3D = ScreenRotation::Rotation0;
// Setup inverse scale on the swap chain
DXGI_MATRIX_3X2_F inverseScale = { 0 };
inverseScale._11 = 1.0f / m_effectiveCompositionScaleX;
inverseScale._22 = 1.0f / m_effectiveCompositionScaleY;
ComPtr<IDXGISwapChain2> spSwapChain2;
DX::ThrowIfFailed(
m_swapChain.As<IDXGISwapChain2>(&spSwapChain2)
);
DX::ThrowIfFailed(
spSwapChain2->SetMatrixTransform(&inverseScale)
);
// Create a render target view of the swap chain back buffer.
ComPtr<ID3D11Texture2D1> backBuffer;
DX::ThrowIfFailed(
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
);
DX::ThrowIfFailed(
m_d3dDevice->CreateRenderTargetView1(
backBuffer.Get(),
nullptr,
&m_d3dRenderTargetView
)
);
// Create a depth stencil view for use with 3D rendering if needed.
CD3D11_TEXTURE2D_DESC1 depthStencilDesc(
DXGI_FORMAT_D24_UNORM_S8_UINT,
lround(m_d3dRenderTargetSize.Width),
lround(m_d3dRenderTargetSize.Height),
1, // This depth stencil view has only one texture.
1, // Use a single mipmap level.
D3D11_BIND_DEPTH_STENCIL
);
ComPtr<ID3D11Texture2D1> depthStencil;
DX::ThrowIfFailed(
m_d3dDevice->CreateTexture2D1(
&depthStencilDesc,
nullptr,
&depthStencil
)
);
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
DX::ThrowIfFailed(
m_d3dDevice->CreateDepthStencilView(
depthStencil.Get(),
&depthStencilViewDesc,
&m_d3dDepthStencilView
)
);
// Set the 3D rendering viewport to target the entire window.
m_screenViewport = CD3D11_VIEWPORT(
0.0f,
0.0f,
m_d3dRenderTargetSize.Width,
m_d3dRenderTargetSize.Height
);
m_d3dContext->RSSetViewports(1, &m_screenViewport);
// Create a Direct2D target bitmap associated with the
// swap chain back buffer and set it as the current target.
D2D1_BITMAP_PROPERTIES1 bitmapProperties =
D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
m_dpi,
m_dpi
);
ComPtr<IDXGISurface2> dxgiBackBuffer;
DX::ThrowIfFailed(
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
);
DX::ThrowIfFailed(
m_d2dContext->CreateBitmapFromDxgiSurface(
dxgiBackBuffer.Get(),
&bitmapProperties,
&m_d2dTargetBitmap
)
);
m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
m_d2dContext->SetDpi(m_effectiveDpi, m_effectiveDpi);
// Grayscale text anti-aliasing is recommended for all Windows Store apps.
m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
}
// Determine the dimensions of the render target and whether it will be scaled down.
void DeviceResources::UpdateRenderTargetSize()
{
m_effectiveDpi = m_dpi;
m_effectiveCompositionScaleX = m_compositionScaleX;
m_effectiveCompositionScaleY = m_compositionScaleY;
// To improve battery life on high resolution devices, render to a smaller render target
// and allow the GPU to scale the output when it is presented.
if (!DisplayMetrics::SupportHighResolutions && m_dpi > DisplayMetrics::DpiThreshold)
{
float width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_dpi);
float height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_dpi);
// When the device is in portrait orientation, height > width. Compare the
// larger dimension against the width threshold and the smaller dimension
// against the height threshold.
if (max(width, height) > DisplayMetrics::WidthThreshold && min(width, height) > DisplayMetrics::HeightThreshold)
{
// To scale the app we change the effective DPI. Logical size does not change.
m_effectiveDpi /= 2.0f;
m_effectiveCompositionScaleX /= 2.0f;
m_effectiveCompositionScaleY /= 2.0f;
}
}
// Calculate the necessary render target size in pixels.
m_outputSize.Width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_effectiveDpi);
m_outputSize.Height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_effectiveDpi);
// Prevent zero size DirectX content from being created.
m_outputSize.Width = max(m_outputSize.Width, 1.0f);
m_outputSize.Height = max(m_outputSize.Height, 1.0f);
}
// This method is called when the XAML control is created (or re-created).
void DeviceResources::SetSwapChainPanel(SwapChainPanel^ panel)
{
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
m_swapChainPanel = panel;
m_logicalSize = Windows::Foundation::Size(static_cast<float>(panel->ActualWidth), static_cast<float>(panel->ActualHeight));
m_nativeOrientation = currentDisplayInformation->NativeOrientation;
m_currentOrientation = currentDisplayInformation->CurrentOrientation;
m_compositionScaleX = panel->CompositionScaleX;
m_compositionScaleY = panel->CompositionScaleY;
m_dpi = currentDisplayInformation->LogicalDpi;
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
}
// This method is called in the event handler for the SizeChanged event.
void DeviceResources::SetLogicalSize(Windows::Foundation::Size logicalSize)
{
if (m_logicalSize != logicalSize)
{
m_logicalSize = logicalSize;
CreateWindowSizeDependentResources();
}
}
// This method is called in the event handler for the DpiChanged event.
void DeviceResources::SetDpi(float dpi)
{
if (dpi != m_dpi)
{
m_dpi = dpi;
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
}
}
// This method is called in the event handler for the OrientationChanged event.
void DeviceResources::SetCurrentOrientation(DisplayOrientations currentOrientation)
{
if (m_currentOrientation != currentOrientation)
{
m_currentOrientation = currentOrientation;
CreateWindowSizeDependentResources();
}
}
// This method is called in the event handler for the CompositionScaleChanged event.
void DeviceResources::SetCompositionScale(float compositionScaleX, float compositionScaleY)
{
if (m_compositionScaleX != compositionScaleX ||
m_compositionScaleY != compositionScaleY)
{
m_compositionScaleX = compositionScaleX;
m_compositionScaleY = compositionScaleY;
CreateWindowSizeDependentResources();
}
}
// This method is called in the event handler for the DisplayContentsInvalidated event.
void DeviceResources::ValidateDevice()
{
// The D3D Device is no longer valid if the default adapter changed since the device
// was created or if the device has been removed.
// First, get the information for the default adapter from when the device was created.
ComPtr<IDXGIDevice3> dxgiDevice;
DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));
ComPtr<IDXGIAdapter> deviceAdapter;
DX::ThrowIfFailed(dxgiDevice->GetAdapter(&deviceAdapter));
ComPtr<IDXGIFactory2> deviceFactory;
DX::ThrowIfFailed(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory)));
ComPtr<IDXGIAdapter1> previousDefaultAdapter;
DX::ThrowIfFailed(deviceFactory->EnumAdapters1(0, &previousDefaultAdapter));
DXGI_ADAPTER_DESC1 previousDesc;
DX::ThrowIfFailed(previousDefaultAdapter->GetDesc1(&previousDesc));
// Next, get the information for the current default adapter.
ComPtr<IDXGIFactory4> currentFactory;
DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&currentFactory)));
ComPtr<IDXGIAdapter1> currentDefaultAdapter;
DX::ThrowIfFailed(currentFactory->EnumAdapters1(0, &currentDefaultAdapter));
DXGI_ADAPTER_DESC1 currentDesc;
DX::ThrowIfFailed(currentDefaultAdapter->GetDesc1(&currentDesc));
// If the adapter LUIDs don't match, or if the device reports that it has been removed,
// a new D3D device must be created.
if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart ||
previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart ||
FAILED(m_d3dDevice->GetDeviceRemovedReason()))
{
// Release references to resources related to the old device.
dxgiDevice = nullptr;
deviceAdapter = nullptr;
deviceFactory = nullptr;
previousDefaultAdapter = nullptr;
// Create a new device and swap chain.
HandleDeviceLost();
}
}
// Recreate all device resources and set them back to the current state.
void DeviceResources::HandleDeviceLost()
{
m_swapChain = nullptr;
if (m_deviceNotify != nullptr)
{
m_deviceNotify->OnDeviceLost();
}
CreateDeviceResources();
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
if (m_deviceNotify != nullptr)
{
m_deviceNotify->OnDeviceRestored();
}
}
// Register our DeviceNotify to be informed on device lost and creation.
void DeviceResources::RegisterDeviceNotify(DX::IDeviceNotify^ deviceNotify)
{
m_deviceNotify = deviceNotify;
}
// Call this method when the app suspends. It provides a hint to the driver that the app
// is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
void DeviceResources::Trim()
{
ComPtr<IDXGIDevice3> dxgiDevice;
m_d3dDevice.As(&dxgiDevice);
dxgiDevice->Trim();
}
// Present the contents of the swap chain to the screen.
void DeviceResources::Present()
{
// The first argument instructs DXGI to block until VSync, putting the application
// to sleep until the next VSync. This ensures we don't waste any cycles rendering
// frames that will never be displayed to the screen.
DXGI_PRESENT_PARAMETERS parameters = { 0 };
HRESULT hr = m_swapChain->Present1(1, 0, &parameters);
// Discard the contents of the render target.
// This is a valid operation only when the existing contents will be entirely
// overwritten. If dirty or scroll rects are used, this call should be modified.
m_d3dContext->DiscardView1(m_d3dRenderTargetView.Get(), nullptr, 0);
// Discard the contents of the depth stencil.
m_d3dContext->DiscardView1(m_d3dDepthStencilView.Get(), nullptr, 0);
// If the device was removed either by a disconnection or a driver upgrade, we
// must recreate all device resources.
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
HandleDeviceLost();
}
else
{
DX::ThrowIfFailed(hr);
}
}
}

View File

@ -0,0 +1,111 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
// Modified from the default template for Xaml and Direct3D 11 apps.
namespace GraphControl::DX
{
// Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created.
interface class IDeviceNotify
{
virtual void OnDeviceLost();
virtual void OnDeviceRestored();
};
// Controls all the DirectX device resources.
class DeviceResources
{
public:
DeviceResources(Windows::UI::Xaml::Controls::SwapChainPanel^ panel);
void SetSwapChainPanel(Windows::UI::Xaml::Controls::SwapChainPanel^ panel);
void SetLogicalSize(Windows::Foundation::Size logicalSize);
void SetCurrentOrientation(Windows::Graphics::Display::DisplayOrientations currentOrientation);
void SetDpi(float dpi);
void SetCompositionScale(float compositionScaleX, float compositionScaleY);
void ValidateDevice();
void HandleDeviceLost();
void RegisterDeviceNotify(IDeviceNotify^ deviceNotify);
void Trim();
void Present();
// The size of the render target, in pixels.
Windows::Foundation::Size GetOutputSize() const { return m_outputSize; }
// The size of the render target, in dips.
Windows::Foundation::Size GetLogicalSize() const { return m_logicalSize; }
float GetDpi() const { return m_effectiveDpi; }
// D3D Accessors.
ID3D11Device3* GetD3DDevice() const { return m_d3dDevice.Get(); }
ID3D11DeviceContext3* GetD3DDeviceContext() const { return m_d3dContext.Get(); }
IDXGISwapChain3* GetSwapChain() const { return m_swapChain.Get(); }
D3D_FEATURE_LEVEL GetDeviceFeatureLevel() const { return m_d3dFeatureLevel; }
ID3D11RenderTargetView1* GetBackBufferRenderTargetView() const { return m_d3dRenderTargetView.Get(); }
ID3D11DepthStencilView* GetDepthStencilView() const { return m_d3dDepthStencilView.Get(); }
D3D11_VIEWPORT GetScreenViewport() const { return m_screenViewport; }
DirectX::XMFLOAT4X4 GetOrientationTransform3D() const { return m_orientationTransform3D; }
// D2D Accessors.
ID2D1Factory3* GetD2DFactory() const { return m_d2dFactory.Get(); }
ID2D1Device2* GetD2DDevice() const { return m_d2dDevice.Get(); }
ID2D1DeviceContext2* GetD2DDeviceContext() const { return m_d2dContext.Get(); }
ID2D1Bitmap1* GetD2DTargetBitmap() const { return m_d2dTargetBitmap.Get(); }
IDWriteFactory3* GetDWriteFactory() const { return m_dwriteFactory.Get(); }
IWICImagingFactory2* GetWicImagingFactory() const { return m_wicFactory.Get(); }
D2D1::Matrix3x2F GetOrientationTransform2D() const { return m_orientationTransform2D; }
private:
void CreateDeviceIndependentResources();
void CreateDeviceResources();
void CreateWindowSizeDependentResources();
void UpdateRenderTargetSize();
// Direct3D objects.
Microsoft::WRL::ComPtr<ID3D11Device3> m_d3dDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext3> m_d3dContext;
Microsoft::WRL::ComPtr<IDXGISwapChain3> m_swapChain;
// Direct3D rendering objects. Required for 3D.
Microsoft::WRL::ComPtr<ID3D11RenderTargetView1> m_d3dRenderTargetView;
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_d3dDepthStencilView;
D3D11_VIEWPORT m_screenViewport;
// Direct2D drawing components.
Microsoft::WRL::ComPtr<ID2D1Factory3> m_d2dFactory;
Microsoft::WRL::ComPtr<ID2D1Device2> m_d2dDevice;
Microsoft::WRL::ComPtr<ID2D1DeviceContext2> m_d2dContext;
Microsoft::WRL::ComPtr<ID2D1Bitmap1> m_d2dTargetBitmap;
// DirectWrite drawing components.
Microsoft::WRL::ComPtr<IDWriteFactory3> m_dwriteFactory;
Microsoft::WRL::ComPtr<IWICImagingFactory2> m_wicFactory;
// Cached reference to the XAML panel.
Windows::UI::Xaml::Controls::SwapChainPanel^ m_swapChainPanel;
// Cached device properties.
D3D_FEATURE_LEVEL m_d3dFeatureLevel;
Windows::Foundation::Size m_d3dRenderTargetSize;
Windows::Foundation::Size m_outputSize;
Windows::Foundation::Size m_logicalSize;
Windows::Graphics::Display::DisplayOrientations m_nativeOrientation;
Windows::Graphics::Display::DisplayOrientations m_currentOrientation;
float m_dpi;
float m_compositionScaleX;
float m_compositionScaleY;
// Variables that take into account whether the app supports high resolution screens or not.
float m_effectiveDpi;
float m_effectiveCompositionScaleX;
float m_effectiveCompositionScaleY;
// Transforms used for display orientation.
D2D1::Matrix3x2F m_orientationTransform2D;
DirectX::XMFLOAT4X4 m_orientationTransform3D;
// The IDeviceNotify can be held directly as it owns the DeviceResources.
IDeviceNotify^ m_deviceNotify;
};
}

View File

@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
// Taken from the default template for Xaml and Direct3D 11 apps.
namespace GraphControl::DX
{
inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
// Set a breakpoint on this line to catch Win32 API errors.
throw Platform::Exception::CreateException(hr);
}
}
// Function that reads from a binary file asynchronously.
inline Concurrency::task<std::vector<byte>> ReadDataAsync(const std::wstring& filename)
{
using namespace Windows::Storage;
using namespace Concurrency;
auto folder = Windows::ApplicationModel::Package::Current->InstalledLocation;
return create_task(folder->GetFileAsync(Platform::StringReference(filename.c_str()))).then([] (StorageFile^ file)
{
return FileIO::ReadBufferAsync(file);
}).then([] (Streams::IBuffer^ fileBuffer) -> std::vector<byte>
{
std::vector<byte> returnBuffer;
returnBuffer.resize(fileBuffer->Length);
Streams::DataReader::FromBuffer(fileBuffer)->ReadBytes(Platform::ArrayReference<byte>(returnBuffer.data(), fileBuffer->Length));
return returnBuffer;
});
}
// Converts a length in device-independent pixels (DIPs) to a length in physical pixels.
inline float ConvertDipsToPixels(float dips, float dpi)
{
static const float dipsPerInch = 96.0f;
return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer.
}
#if defined(_DEBUG)
// Check for SDK Layer support.
inline bool SdkLayersAvailable()
{
HRESULT hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device.
0,
D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers.
nullptr, // Any feature level will do.
0,
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
nullptr, // No need to keep the D3D device reference.
nullptr, // No need to know the feature level.
nullptr // No need to keep the D3D device context reference.
);
return SUCCEEDED(hr);
}
#endif
}

View File

@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "NearestPointRenderer.h"
#include "DirectXHelper.h"
using namespace D2D1;
using namespace GraphControl::DX;
using namespace std;
using namespace Windows::Foundation;
namespace
{
const ColorF c_DefaultPointColor = ColorF::Black;
constexpr float c_NearestPointRadius = 3;
}
NearestPointRenderer::NearestPointRenderer(DeviceResources* deviceResources)
: m_deviceResources{ deviceResources }
, m_color{ c_DefaultPointColor }
, m_ellipse{ D2D1_POINT_2F{ 0, 0 }, c_NearestPointRadius, c_NearestPointRadius }
{
CreateDeviceDependentResources();
}
void NearestPointRenderer::CreateDeviceDependentResources()
{
CreateBrush();
}
void NearestPointRenderer::ReleaseDeviceDependentResources()
{
m_brush.Reset();
}
void NearestPointRenderer::Render(const Point& location)
{
if (ID2D1DeviceContext* context = m_deviceResources->GetD2DDeviceContext())
{
m_ellipse.point.x = location.X;
m_ellipse.point.y = location.Y;
context->BeginDraw();
context->FillEllipse(m_ellipse, m_brush.Get());
// Ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
// is lost. It will be handled during the next call to Present.
HRESULT hr = context->EndDraw();
if (hr != D2DERR_RECREATE_TARGET)
{
ThrowIfFailed(hr);
}
}
}
void NearestPointRenderer::SetColor(const ColorF& color)
{
m_color = color;
CreateBrush();
}
void NearestPointRenderer::CreateBrush()
{
m_brush.Reset();
ThrowIfFailed(
m_deviceResources->GetD2DDeviceContext()->CreateSolidColorBrush(m_color, &m_brush)
);
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
namespace GraphControl::DX
{
class DeviceResources;
class NearestPointRenderer
{
public:
NearestPointRenderer(DeviceResources* deviceResources);
void CreateDeviceDependentResources();
void ReleaseDeviceDependentResources();
void Render(const Windows::Foundation::Point& location);
void SetColor(const D2D1::ColorF& color);
private:
void CreateBrush();
private:
DeviceResources* const m_deviceResources;
D2D1::ColorF m_color;
D2D1_ELLIPSE m_ellipse;
// Resources related to rendering.
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> m_brush;
};
}

View File

@ -0,0 +1,386 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "RenderMain.h"
#include "DirectXHelper.h"
using namespace Concurrency;
using namespace Graphing;
using namespace Platform;
using namespace std;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace Windows::System::Threading;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
namespace
{
constexpr unsigned int s_RedChannelIndex = 0;
constexpr unsigned int s_GreenChannelIndex = 1;
constexpr unsigned int s_BlueChannelIndex = 2;
constexpr unsigned int s_AlphaChannelIndex = 3;
constexpr float s_MaxChannelValue = 255.0f;
constexpr float nearestPointRadius = 3;
}
namespace GraphControl::DX
{
RenderMain::RenderMain(SwapChainPanel ^ panel)
: m_deviceResources{ panel }
, m_nearestPointRenderer{ &m_deviceResources }
, m_backgroundColor{ {} }
, m_swapChainPanel{ panel }
, m_TraceValue(Point(0, 0))
, m_TraceLocation(Point(0, 0))
, m_Tracing(false)
{
// Register to be notified if the Device is lost or recreated
m_deviceResources.RegisterDeviceNotify(this);
RegisterEventHandlers();
m_drawActiveTracing = false;
m_activeTracingPointerLocation.X = 50;
m_activeTracingPointerLocation.Y = 50;
}
RenderMain::~RenderMain()
{
UnregisterEventHandlers();
}
void RenderMain::Graph::set(shared_ptr<IGraph> graph)
{
m_graph = move(graph);
if (m_graph)
{
if (auto renderer = m_graph->GetRenderer())
{
float dpi = m_deviceResources.GetDpi();
renderer->SetDpi(dpi, dpi);
renderer->SetGraphSize(static_cast<unsigned int>(m_swapChainPanel->ActualWidth), static_cast<unsigned int>(m_swapChainPanel->ActualHeight));
}
}
}
void RenderMain::BackgroundColor::set(Windows::UI::Color backgroundColor)
{
m_backgroundColor[s_RedChannelIndex] = static_cast<float>(backgroundColor.R) / s_MaxChannelValue;
m_backgroundColor[s_GreenChannelIndex] = static_cast<float>(backgroundColor.G) / s_MaxChannelValue;
m_backgroundColor[s_BlueChannelIndex] = static_cast<float>(backgroundColor.B) / s_MaxChannelValue;
m_backgroundColor[s_AlphaChannelIndex] = static_cast<float>(backgroundColor.A) / s_MaxChannelValue;
RunRenderPass();
}
void RenderMain::DrawNearestPoint::set(bool value)
{
if (m_drawNearestPoint != value)
{
m_drawNearestPoint = value;
if (!m_drawNearestPoint)
{
m_Tracing = false;
}
RunRenderPass();
}
}
void RenderMain::PointerLocation::set(Point location)
{
if (m_pointerLocation != location)
{
m_pointerLocation = location;
RunRenderPass();
}
}
void RenderMain::ActiveTracing::set(bool value)
{
if (m_drawActiveTracing != value)
{
m_drawActiveTracing = value;
RunRenderPass();
}
}
bool RenderMain::ActiveTracing::get()
{
return m_drawActiveTracing;
}
// Updates application state when the window size changes (e.g. device orientation change)
void RenderMain::CreateWindowSizeDependentResources()
{
// TODO: Replace this with the sizedependent initialization of your app's content.
RunRenderPass();
}
bool RenderMain::RunRenderPass()
{
bool succesful = Render();
if (succesful)
{
m_deviceResources.Present();
}
return succesful;
}
// Renders the current frame according to the current application state.
// Returns true if the frame was rendered and is ready to be displayed.
bool RenderMain::Render()
{
bool successful = true;
// Must call BeginDraw before any draw commands.
ID2D1Factory3* pFactory = m_deviceResources.GetD2DFactory();
ID2D1DeviceContext* pRenderTarget = m_deviceResources.GetD2DDeviceContext();
auto context = m_deviceResources.GetD3DDeviceContext();
// Clear the back buffer and set the background color.
context->ClearRenderTargetView(m_deviceResources.GetBackBufferRenderTargetView(), m_backgroundColor);
if (m_graph)
{
if (auto renderer = m_graph->GetRenderer())
{
pRenderTarget->BeginDraw();
bool hasMissingData = false;
successful = SUCCEEDED(renderer->DrawD2D1(pFactory, pRenderTarget, hasMissingData));
// We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
// is lost. It will be handled during the next call to Present.
HRESULT endDraw = pRenderTarget->EndDraw();
if (endDraw != D2DERR_RECREATE_TARGET)
{
DX::ThrowIfFailed(endDraw);
}
if (successful)
{
if (m_drawNearestPoint || m_drawActiveTracing)
{
Point trackPoint = m_pointerLocation;
if (m_drawActiveTracing)
{
// Active tracing takes over for draw nearest point input from the mouse pointer.
trackPoint = m_activeTracingPointerLocation;
}
int formulaId = -1;
float nearestPointLocationX, nearestPointLocationY;
float nearestPointValueX, nearestPointValueY;
if (renderer->GetClosePointData(
trackPoint.X, trackPoint.Y, formulaId, nearestPointLocationX, nearestPointLocationY, nearestPointValueX, nearestPointValueY)
== S_OK)
{
if (!isnan(nearestPointLocationX) && !isnan(nearestPointLocationY))
{
auto lineColors = m_graph->GetOptions().GetGraphColors();
if (formulaId >= 0 && static_cast<unsigned int>(formulaId) < lineColors.size())
{
auto dotColor = lineColors[formulaId];
m_nearestPointRenderer.SetColor(D2D1::ColorF(dotColor.R * 65536 + dotColor.G * 256 + dotColor.B, 1.0));
}
m_TraceLocation = Point(nearestPointLocationX, nearestPointLocationY);
m_nearestPointRenderer.Render(m_TraceLocation);
m_Tracing = true;
m_TraceLocation = Point(nearestPointLocationX, nearestPointLocationY);
m_TraceValue = Point(nearestPointValueX, nearestPointValueY);
}
else
{
m_Tracing = false;
}
}
else
{
m_Tracing = false;
}
}
}
}
}
return successful;
}
void RenderMain::OnLoaded(Object ^ sender, RoutedEventArgs ^ e)
{
RunRenderPass();
}
void RenderMain::RegisterEventHandlers()
{
UnregisterEventHandlers();
// Register event handlers for control lifecycle.
m_coreWindow = Agile<CoreWindow>(Window::Current->CoreWindow);
if (m_coreWindow != nullptr)
{
m_tokenVisibilityChanged = m_coreWindow->VisibilityChanged +=
ref new TypedEventHandler<CoreWindow ^, VisibilityChangedEventArgs ^>(this, &RenderMain::OnVisibilityChanged);
}
m_displayInformation = DisplayInformation::GetForCurrentView();
if (m_displayInformation != nullptr)
{
m_tokenDpiChanged = m_displayInformation->DpiChanged += ref new TypedEventHandler<DisplayInformation ^, Object ^>(this, &RenderMain::OnDpiChanged);
m_tokenOrientationChanged = m_displayInformation->OrientationChanged +=
ref new TypedEventHandler<DisplayInformation ^, Object ^>(this, &RenderMain::OnOrientationChanged);
}
m_tokenDisplayContentsInvalidated = DisplayInformation::DisplayContentsInvalidated +=
ref new TypedEventHandler<DisplayInformation ^, Object ^>(this, &RenderMain::OnDisplayContentsInvalidated);
if (m_swapChainPanel != nullptr)
{
m_tokenLoaded = m_swapChainPanel->Loaded += ref new RoutedEventHandler(this, &RenderMain::OnLoaded);
m_tokenCompositionScaleChanged = m_swapChainPanel->CompositionScaleChanged +=
ref new TypedEventHandler<SwapChainPanel ^, Object ^>(this, &RenderMain::OnCompositionScaleChanged);
m_tokenSizeChanged = m_swapChainPanel->SizeChanged += ref new SizeChangedEventHandler(this, &RenderMain::OnSizeChanged);
}
}
void RenderMain::UnregisterEventHandlers()
{
if (m_coreWindow != nullptr)
{
if (m_tokenVisibilityChanged.Value != 0)
{
m_coreWindow->VisibilityChanged -= m_tokenVisibilityChanged;
m_tokenVisibilityChanged.Value = 0;
}
m_coreWindow = nullptr;
}
if (m_displayInformation != nullptr)
{
if (m_tokenDpiChanged.Value != 0)
{
m_displayInformation->DpiChanged -= m_tokenDpiChanged;
m_tokenDpiChanged.Value = 0;
}
if (m_tokenOrientationChanged.Value != 0)
{
m_displayInformation->OrientationChanged -= m_tokenOrientationChanged;
m_tokenOrientationChanged.Value = 0;
}
m_displayInformation = nullptr;
}
if (m_tokenDisplayContentsInvalidated.Value != 0)
{
DisplayInformation::DisplayContentsInvalidated -= m_tokenDisplayContentsInvalidated;
m_tokenDisplayContentsInvalidated.Value = 0;
}
if (m_swapChainPanel != nullptr)
{
if (m_tokenLoaded.Value != 0)
{
m_swapChainPanel->Loaded -= m_tokenLoaded;
m_tokenLoaded.Value = 0;
}
if (m_tokenCompositionScaleChanged.Value != 0)
{
m_swapChainPanel->CompositionScaleChanged -= m_tokenCompositionScaleChanged;
m_tokenCompositionScaleChanged.Value = 0;
}
if (m_tokenSizeChanged.Value != 0)
{
m_swapChainPanel->SizeChanged -= m_tokenSizeChanged;
m_tokenSizeChanged.Value = 0;
}
}
}
void RenderMain::OnVisibilityChanged(CoreWindow ^ sender, VisibilityChangedEventArgs ^ args)
{
if (args->Visible)
{
RunRenderPass();
}
}
void RenderMain::OnDpiChanged(DisplayInformation ^ sender, Object ^ args)
{
// Note: The value for LogicalDpi retrieved here may not match the effective DPI of the app
// if it is being scaled for high resolution devices. Once the DPI is set on DeviceResources,
// you should always retrieve it using the GetDpi method.
// See DeviceResources.cpp for more details.
m_deviceResources.SetDpi(sender->LogicalDpi);
if (m_graph)
{
if (auto renderer = m_graph->GetRenderer())
{
float dpi = m_deviceResources.GetDpi();
renderer->SetDpi(dpi, dpi);
}
}
CreateWindowSizeDependentResources();
}
void RenderMain::OnOrientationChanged(DisplayInformation ^ sender, Object ^ args)
{
m_deviceResources.SetCurrentOrientation(sender->CurrentOrientation);
CreateWindowSizeDependentResources();
}
void RenderMain::OnDisplayContentsInvalidated(DisplayInformation ^ sender, Object ^ args)
{
m_deviceResources.ValidateDevice();
}
void RenderMain::OnCompositionScaleChanged(SwapChainPanel ^ sender, Object ^ args)
{
m_deviceResources.SetCompositionScale(sender->CompositionScaleX, sender->CompositionScaleY);
CreateWindowSizeDependentResources();
}
void RenderMain::OnSizeChanged(Object ^ sender, SizeChangedEventArgs ^ e)
{
m_deviceResources.SetLogicalSize(e->NewSize);
if (m_graph)
{
if (auto renderer = m_graph->GetRenderer())
{
const auto& newSize = e->NewSize;
renderer->SetGraphSize(static_cast<unsigned int>(newSize.Width), static_cast<unsigned int>(newSize.Height));
}
}
CreateWindowSizeDependentResources();
}
// Notifies renderers that device resources need to be released.
void RenderMain::OnDeviceLost()
{
m_nearestPointRenderer.ReleaseDeviceDependentResources();
}
// Notifies renderers that device resources may now be recreated.
void RenderMain::OnDeviceRestored()
{
m_nearestPointRenderer.CreateDeviceDependentResources();
}
}

View File

@ -0,0 +1,166 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
// Taken from the default template for Xaml and Direct3D 11 apps.
#include "DeviceResources.h"
#include "NearestPointRenderer.h"
#include "IGraph.h"
// Renders Direct2D and 3D content on the screen.
namespace GraphControl::DX
{
ref class RenderMain sealed : public IDeviceNotify
{
public:
virtual ~RenderMain();
// IDeviceNotify
virtual void OnDeviceLost();
virtual void OnDeviceRestored();
internal : RenderMain(Windows::UI::Xaml::Controls::SwapChainPanel ^ panel);
property std::shared_ptr<Graphing::IGraph> Graph
{
void set(std::shared_ptr<Graphing::IGraph> graph);
}
property Windows::UI::Color BackgroundColor
{
void set(Windows::UI::Color color);
}
property bool DrawNearestPoint
{
void set(bool value);
}
property Windows::Foundation::Point PointerLocation
{
void set(Windows::Foundation::Point location);
}
void CreateWindowSizeDependentResources();
bool RunRenderPass();
// Indicates if we are in active tracing mode (the tracing box is being used and controlled through keyboard input)
property bool ActiveTracing
{
bool get();
void set(bool value);
}
property Windows::Foundation::Point ActiveTraceCursorPosition
{
Windows::Foundation::Point get()
{
return m_activeTracingPointerLocation;
}
void set(Windows::Foundation::Point newValue)
{
if (m_activeTracingPointerLocation != newValue)
{
m_activeTracingPointerLocation = newValue;
RunRenderPass();
}
}
}
property Windows::Foundation::Point TraceValue
{
Windows::Foundation::Point get()
{
return m_TraceValue;
}
}
property Windows::Foundation::Point TraceLocation
{
Windows::Foundation::Point get()
{
return m_TraceLocation;
}
}
// Any time we should be showing the tracing popup (either active or passive tracing)
property bool Tracing
{
bool get()
{
return m_Tracing;
}
}
private:
bool Render();
// Loaded/Unloaded
void OnLoaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
// Dependent event registration
void RegisterEventHandlers();
void UnregisterEventHandlers();
// Window event handlers.
void OnVisibilityChanged(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::VisibilityChangedEventArgs ^ args);
// DisplayInformation event handlers.
void OnDpiChanged(Windows::Graphics::Display::DisplayInformation ^ sender, Platform::Object ^ args);
void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation ^ sender, Platform::Object ^ args);
void OnDisplayContentsInvalidated(Windows::Graphics::Display::DisplayInformation ^ sender, Platform::Object ^ args);
// Other event handlers.
void OnCompositionScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel ^ sender, Object ^ args);
void OnSizeChanged(Platform::Object ^ sender, Windows::UI::Xaml::SizeChangedEventArgs ^ e);
private:
DX::DeviceResources m_deviceResources;
NearestPointRenderer m_nearestPointRenderer;
// Cached Graph object with Renderer property.
std::shared_ptr<Graphing::IGraph> m_graph = nullptr;
// Track current input pointer position.
bool m_drawNearestPoint = false;
Windows::Foundation::Point m_pointerLocation;
// Track current active tracing pointer position.
bool m_drawActiveTracing = false;
Windows::Foundation::Point m_activeTracingPointerLocation;
float m_backgroundColor[4];
// The SwapChainPanel^ surface.
Windows::UI::Xaml::Controls::SwapChainPanel ^ m_swapChainPanel = nullptr;
Windows::Foundation::EventRegistrationToken m_tokenLoaded;
Windows::Foundation::EventRegistrationToken m_tokenCompositionScaleChanged;
Windows::Foundation::EventRegistrationToken m_tokenSizeChanged;
// Cached references to event notifiers.
Platform::Agile<Windows::UI::Core::CoreWindow> m_coreWindow = nullptr;
Windows::Foundation::EventRegistrationToken m_tokenVisibilityChanged;
Windows::Graphics::Display::DisplayInformation ^ m_displayInformation = nullptr;
Windows::Foundation::EventRegistrationToken m_tokenDpiChanged;
Windows::Foundation::EventRegistrationToken m_tokenOrientationChanged;
Windows::Foundation::EventRegistrationToken m_tokenDisplayContentsInvalidated;
// Track our independent input on a background worker thread.
Windows::Foundation::IAsyncAction ^ m_inputLoopWorker = nullptr;
Windows::UI::Core::CoreIndependentInputSource ^ m_coreInput = nullptr;
// What is the current trace value
Windows::Foundation::Point m_TraceValue;
// And where is it located on screen
Windows::Foundation::Point m_TraceLocation;
// Are we currently showing the tracing value
bool m_Tracing;
};
}

View File

@ -0,0 +1,327 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{e727a92b-f149-492c-8117-c039a298719b}</ProjectGuid>
<Keyword>WindowsRuntimeComponent</Keyword>
<RootNamespace>GraphControl</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.18362.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
<GenerateProjectSpecificOutputFolder>true</GenerateProjectSpecificOutputFolder>
</PropertyGroup>
<PropertyGroup>
<GraphingInterfaceDir>$([MSBuild]::ValueOrDefault($(GraphingInterfaceDir), '$(SolutionDir)\GraphingInterfaces\'))</GraphingInterfaceDir>
</PropertyGroup>
<ImportGroup Condition="Exists('GraphingImplOverrides.props')">
<Import Project="GraphingImplOverrides.props" />
</ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await /d2CoroOptsWorkaround %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await /d2CoroOptsWorkaround %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await /d2CoroOptsWorkaround %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Control\Grapher.h" />
<ClInclude Include="DirectX\DeviceResources.h" />
<ClInclude Include="DirectX\DirectXHelper.h" />
<ClInclude Include="DirectX\NearestPointRenderer.h" />
<ClInclude Include="DirectX\RenderMain.h" />
<ClInclude Include="Models\Equation.h" />
<ClInclude Include="Models\EquationCollection.h" />
<ClInclude Include="Models\KeyGraphFeaturesInfo.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="winrtHeaders.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Control\Grapher.cpp" />
<ClCompile Include="DirectX\DeviceResources.cpp" />
<ClCompile Include="DirectX\NearestPointRenderer.cpp" />
<ClCompile Include="DirectX\RenderMain.cpp" />
<ClCompile Include="Models\Equation.cpp" />
<ClCompile Include="Models\KeyGraphFeaturesInfo.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Page Include="Themes\generic.xaml" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::ValueOrDefault($(UseMockGraphingImpl), true))">
<ProjectReference Include="$(SolutionDir)\GraphingImpl\GraphingImpl.vcxproj">
<Project>{52E03A58-B378-4F50-8BFB-F659FB85E790}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="DirectX">
<UniqueIdentifier>{0d550f5f-db67-4160-8648-397c9bdc0307}</UniqueIdentifier>
</Filter>
<Filter Include="Themes">
<UniqueIdentifier>{3d424f3b-ba30-440b-ac2b-8a2740506153}</UniqueIdentifier>
</Filter>
<Filter Include="Control">
<UniqueIdentifier>{e8d91a71-6933-4fd8-b333-421085d13896}</UniqueIdentifier>
</Filter>
<Filter Include="Models">
<UniqueIdentifier>{0f768477-7ceb-42c4-a32a-cb024320dbc3}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="DirectX\DeviceResources.cpp">
<Filter>DirectX</Filter>
</ClCompile>
<ClCompile Include="DirectX\RenderMain.cpp">
<Filter>DirectX</Filter>
</ClCompile>
<ClCompile Include="Control\Grapher.cpp">
<Filter>Control</Filter>
</ClCompile>
<ClCompile Include="DirectX\NearestPointRenderer.cpp">
<Filter>DirectX</Filter>
</ClCompile>
<ClCompile Include="Models\KeyGraphFeaturesInfo.cpp" />
<ClCompile Include="Models\Equation.cpp">
<Filter>Models</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="DirectX\DeviceResources.h">
<Filter>DirectX</Filter>
</ClInclude>
<ClInclude Include="DirectX\DirectXHelper.h">
<Filter>DirectX</Filter>
</ClInclude>
<ClInclude Include="DirectX\RenderMain.h">
<Filter>DirectX</Filter>
</ClInclude>
<ClInclude Include="winrtHeaders.h" />
<ClInclude Include="Control\Grapher.h">
<Filter>Control</Filter>
</ClInclude>
<ClInclude Include="DirectX\NearestPointRenderer.h">
<Filter>DirectX</Filter>
</ClInclude>
<ClInclude Include="Utils.h" />
<ClInclude Include="Models\KeyGraphFeaturesInfo.h" />
<ClInclude Include="Models\Equation.h">
<Filter>Models</Filter>
</ClInclude>
<ClInclude Include="Models\EquationCollection.h">
<Filter>Models</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Page Include="Themes\generic.xaml">
<Filter>Themes</Filter>
</Page>
</ItemGroup>
<ItemGroup>
<None Include="$(ProjectDir)\GraphingImplOverrides.props" />
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="$(GraphingImplDll)" />
<CopyFileToFolders Include="$(GraphingEngineDll)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Equation.h"
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Media;
namespace GraphControl
{
// Remove mml: formatting specific to RichEditBox control, which is not understood by the graph engine.
static constexpr wstring_view s_mathPrefix = L"mml:";
Equation::Equation()
{
}
String ^ Equation::GetRequest()
{
wstringstream ss;
wstring expr{ Expression->Data() };
// Check for unicode characters of less than, less than or equal to, greater than and greater than or equal to.
if (expr.find(L">&#x3E;<") != wstring::npos || expr.find(L">&#x3C;<") != wstring::npos || expr.find(L">&#x2265;<") != wstring::npos
|| expr.find(L">&#x2264;<") != wstring::npos)
{
ss << L"<mrow><mi>plotIneq2D</mi><mfenced separators=\"\">"s;
}
else if (expr.find(L">=<") != wstring::npos)
{
ss << L"<mrow><mi>plotEq2d</mi><mfenced separators=\"\">";
}
else
{
ss << L"<mrow><mi>plot2d</mi><mfenced separators=\"\">";
}
ss << GetExpression() << L"</mfenced></mrow>";
return ref new String(ss.str().c_str());
}
wstring Equation::GetExpression()
{
wstring mathML = Expression->Data();
size_t mathPrefix = 0;
while ((mathPrefix = mathML.find(s_mathPrefix, mathPrefix)) != std::string::npos)
{
mathML.replace(mathPrefix, s_mathPrefix.length(), L"");
mathPrefix += s_mathPrefix.length();
}
return mathML;
}
Color Equation::LineColor::get()
{
return m_LineColor;
}
void Equation::LineColor::set(Color value)
{
if (m_LineColor.R != value.R || m_LineColor.G != value.G || m_LineColor.B != value.B || m_LineColor.A != value.A)
{
m_LineColor = value;
RaisePropertyChanged(L"LineColor");
}
}
Platform::String ^ Equation::LineColorPropertyName::get()
{
return Platform::StringReference(L"LineColor");
}
bool Equation::IsGraphableEquation()
{
return !Expression->IsEmpty() && IsLineEnabled && !HasGraphError;
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "Utils.h"
#include <string>
namespace GraphControl
{
[Windows::UI::Xaml::Data::Bindable] public ref class Equation sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
Equation();
OBSERVABLE_OBJECT();
OBSERVABLE_NAMED_PROPERTY_RW(Platform::String ^, Expression);
OBSERVABLE_NAMED_PROPERTY_RW(bool, IsLineEnabled);
OBSERVABLE_NAMED_PROPERTY_RW(bool, IsValidated);
OBSERVABLE_NAMED_PROPERTY_RW(bool, HasGraphError);
property Windows::UI::Color LineColor
{
Windows::UI::Color get();
void set(Windows::UI::Color value);
}
static property Platform::String
^ LineColorPropertyName { Platform::String ^ get(); }
public : Platform::String
^ GetRequest();
bool IsGraphableEquation();
private:
std::wstring GetExpression();
private:
Windows::UI::Color m_LineColor;
};
}

View File

@ -0,0 +1,179 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "Equation.h"
namespace GraphControl
{
delegate void EquationChangedEventHandler(Equation ^ sender);
delegate void VisibilityChangedEventHandler(Equation ^ sender);
public
ref class EquationCollection sealed : public Windows::Foundation::Collections::IObservableVector<GraphControl::Equation ^>
{
public:
virtual ~EquationCollection()
{
}
#pragma region IIterable
virtual Windows::Foundation::Collections::IIterator< GraphControl::Equation^ >^ First()
{
return m_vector->First();
}
#pragma endregion
#pragma region IVector
virtual property unsigned int Size
{
unsigned int get()
{
return m_vector->Size;
}
}
virtual void Append(GraphControl::Equation ^ value)
{
m_vector->Append(value);
m_tokens.emplace_back(
value->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged));
}
virtual void Clear()
{
auto numEqs = m_vector->Size;
for (auto i = 0u; i < numEqs; i++)
{
m_vector->GetAt(i)->PropertyChanged -= m_tokens[i];
}
m_vector->Clear();
m_tokens.clear();
}
virtual GraphControl::Equation
^ GetAt(unsigned int index) { return m_vector->GetAt(index); }
virtual unsigned int GetMany(unsigned int startIndex, Platform::WriteOnlyArray<GraphControl::Equation ^> ^ items)
{
return m_vector->GetMany(startIndex, items);
}
virtual Windows::Foundation::Collections::IVectorView< GraphControl::Equation^ >^ GetView()
{
return m_vector->GetView();
}
virtual Platform::Boolean IndexOf(GraphControl::Equation^ value, unsigned int *index)
{
return m_vector->IndexOf(value, index);
}
virtual void InsertAt(unsigned int index, GraphControl::Equation ^ value)
{
m_vector->InsertAt(index, value);
m_tokens.insert(
m_tokens.begin() + index,
value->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged));
}
virtual void RemoveAt(unsigned int index)
{
m_vector->GetAt(index)->PropertyChanged -= m_tokens[index];
m_vector->RemoveAt(index);
m_tokens.erase(m_tokens.begin() + index);
}
virtual void RemoveAtEnd()
{
auto size = m_vector->Size;
if (size > 0)
{
m_vector->GetAt(size - 1)->PropertyChanged -= *m_tokens.rbegin();
m_tokens.erase(m_tokens.end() - 1);
}
m_vector->RemoveAtEnd();
}
virtual void ReplaceAll(const Platform::Array<GraphControl::Equation ^> ^ items)
{
auto size = m_vector->Size;
for (auto i = 0u; i < size; i++)
{
m_vector->GetAt(i)->PropertyChanged -= m_tokens[i];
}
size = items->Length;
m_tokens.resize(size);
for (auto i = 0u; i < size; i++)
{
m_tokens[i] = items[i]->PropertyChanged +=
ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged);
}
m_vector->ReplaceAll(items);
}
virtual void SetAt(unsigned int index, GraphControl::Equation ^ value)
{
m_vector->GetAt(index)->PropertyChanged -= m_tokens[index];
m_vector->SetAt(index, value);
m_tokens[index] = value->PropertyChanged +=
ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged);
}
#pragma endregion
#pragma region IObservableVector
virtual event Windows::Foundation::Collections::VectorChangedEventHandler< GraphControl::Equation^ >^ VectorChanged
{
Windows::Foundation::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler< GraphControl::Equation^ >^ handler)
{
return m_vector->VectorChanged += handler;
}
void remove(Windows::Foundation::EventRegistrationToken token)
{
m_vector->VectorChanged -= token;
}
}
#pragma endregion
internal:
EquationCollection() :
m_vector(ref new Platform::Collections::Vector< GraphControl::Equation^ >())
{
}
event EquationChangedEventHandler ^ EquationChanged;
event EquationChangedEventHandler ^ EquationStyleChanged;
event EquationChangedEventHandler ^ EquationLineEnabledChanged;
private:
void OnEquationPropertyChanged(Object^ sender, Windows::UI::Xaml::Data::PropertyChangedEventArgs ^ args)
{
auto equation = static_cast<Equation ^>(sender);
auto propertyName = args->PropertyName;
if (propertyName == GraphControl::Equation::LineColorPropertyName)
{
EquationStyleChanged(equation);
}
else if (propertyName == GraphControl::Equation::ExpressionPropertyName)
{
EquationChanged(equation);
}
else if (propertyName == GraphControl::Equation::IsLineEnabledPropertyName)
{
EquationLineEnabledChanged(equation);
}
}
private:
Platform::Collections::Vector<GraphControl::Equation ^> ^ m_vector;
std::vector<Windows::Foundation::EventRegistrationToken> m_tokens;
};
}

View File

@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "KeyGraphFeaturesInfo.h"
#include "../../CalcViewModel/GraphingCalculatorEnums.h"
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Media;
using namespace GraphControl;
using namespace Graphing;
IObservableVector<String ^> ^ KeyGraphFeaturesInfo::ConvertWStringVector(vector<wstring> inVector)
{
auto outVector = ref new Vector<String ^>();
for (auto v : inVector)
{
outVector->Append(ref new String(v.c_str()));
}
return outVector;
}
IObservableMap<String ^, String ^> ^ KeyGraphFeaturesInfo::ConvertWStringIntMap(map<wstring, int> inMap)
{
Map<String ^, String ^> ^ outMap = ref new Map<String ^, String ^>();
;
for (auto m : inMap)
{
outMap->Insert(ref new String(m.first.c_str()), m.second.ToString());
}
return outMap;
}
KeyGraphFeaturesInfo ^ KeyGraphFeaturesInfo::Create(IGraphFunctionAnalysisData data)
{
auto res = ref new KeyGraphFeaturesInfo();
res->XIntercept = ref new String(data.Zeros.c_str());
res->YIntercept = ref new String(data.YIntercept.c_str());
res->Domain = ref new String(data.Domain.c_str());
res->Range = ref new String(data.Range.c_str());
res->Parity = data.Parity;
res->PeriodicityDirection = data.PeriodicityDirection;
res->PeriodicityExpression = ref new String(data.PeriodicityExpression.c_str());
res->Minima = ConvertWStringVector(data.Minima);
res->Maxima = ConvertWStringVector(data.Maxima);
res->InflectionPoints = ConvertWStringVector(data.InflectionPoints);
res->Monotonicity = ConvertWStringIntMap(data.MonotoneIntervals);
res->VerticalAsymptotes = ConvertWStringVector(data.VerticalAsymptotes);
res->HorizontalAsymptotes = ConvertWStringVector(data.HorizontalAsymptotes);
res->ObliqueAsymptotes = ConvertWStringVector(data.ObliqueAsymptotes);
res->TooComplexFeatures = data.TooComplexFeatures;
res->AnalysisError = CalculatorApp::AnalysisErrorType::NoError;
return res;
}
KeyGraphFeaturesInfo ^ KeyGraphFeaturesInfo::Create(CalculatorApp::AnalysisErrorType type)
{
auto res = ref new KeyGraphFeaturesInfo();
res->AnalysisError = type;
return res;
}

View File

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "Utils.h"
namespace Graphing
{
struct IGraphFunctionAnalysisData;
}
namespace CalculatorApp
{
enum AnalysisErrorType;
}
namespace GraphControl
{
public
ref class KeyGraphFeaturesInfo sealed
{
public:
PROPERTY_R(Platform::String ^, XIntercept);
PROPERTY_R(Platform::String ^, YIntercept);
PROPERTY_R(int, Parity);
PROPERTY_R(int, PeriodicityDirection);
PROPERTY_R(Platform::String ^, PeriodicityExpression);
PROPERTY_R(Windows::Foundation::Collections::IVector<Platform::String ^> ^, Minima);
PROPERTY_R(Windows::Foundation::Collections::IVector<Platform::String ^> ^, Maxima);
PROPERTY_R(Platform::String ^, Domain);
PROPERTY_R(Platform::String ^, Range);
PROPERTY_R(Windows::Foundation::Collections::IVector<Platform::String ^> ^, InflectionPoints);
PROPERTY_R(SINGLE_ARG(Windows::Foundation::Collections::IObservableMap<Platform::String ^, Platform::String ^> ^), Monotonicity);
PROPERTY_R(Windows::Foundation::Collections::IVector<Platform::String ^> ^, VerticalAsymptotes);
PROPERTY_R(Windows::Foundation::Collections::IVector<Platform::String ^> ^, HorizontalAsymptotes);
PROPERTY_R(Windows::Foundation::Collections::IVector<Platform::String ^> ^, ObliqueAsymptotes);
PROPERTY_R(int, TooComplexFeatures);
PROPERTY_R(int, AnalysisError);
internal:
static KeyGraphFeaturesInfo ^ Create(Graphing::IGraphFunctionAnalysisData data);
static KeyGraphFeaturesInfo ^ Create(CalculatorApp::AnalysisErrorType type);
private:
static Windows::Foundation::Collections::IObservableVector<Platform::String ^> ^ ConvertWStringVector(std::vector<std::wstring> inVector);
static Windows::Foundation::Collections::
IObservableMap<Platform::String ^, Platform::String ^> ^ ConvertWStringIntMap(std::map<std::wstring, int> inMap);
};
}

View File

@ -0,0 +1,13 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:GraphControl">
<Style TargetType="local:Grapher">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Grapher">
<SwapChainPanel x:Name="GraphSurface"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

Some files were not shown because too many files have changed in this diff Show More