From 091732aa944d4d7f0085513f40cf91c1007e85e6 Mon Sep 17 00:00:00 2001 From: Daniel Belcher Date: Wed, 10 Apr 2019 18:15:10 -0700 Subject: [PATCH 01/63] Feature/GraphingCalculator initial commit (#450) Initial PR for the feature/GraphingCalculator feature branch, part of #338. The feature incorporates a proprietary Microsoft-owned graphing engine to drive graphing experiences in the Windows Calculator app. Due to the private nature of the graphing engine, the source available in the public repo will make use of a mock graphing engine. See README.md for more details. This PR simply serves as a base for future feature development. As such, the PR will be immediately merged. Feedback on the content of this PR, and on the feature in general, is encouraged. If there is feedback related to the content of this specific PR, please leave comments on the PR page. We will address the comments in future PRs to the feature branch. --- .gitignore | 1 + README.md | 4 + src/CalcViewModel/ApplicationViewModel.cpp | 10 +- src/CalcViewModel/ApplicationViewModel.h | 2 + src/CalcViewModel/CalcViewModel.vcxproj | 4 + .../CalcViewModel.vcxproj.filters | 15 + .../Common/CalculatorButtonUser.h | 24 +- .../Common/KeyboardShortcutManager.cpp | 42 +- src/CalcViewModel/Common/NavCategory.cpp | 16 +- src/CalcViewModel/Common/NavCategory.h | 4 +- src/CalcViewModel/Common/Utils.cpp | 56 +- src/CalcViewModel/Common/Utils.h | 25 + .../GraphingCalculator/EquationViewModel.cpp | 12 + .../GraphingCalculator/EquationViewModel.h | 16 + .../GraphingCalculatorViewModel.cpp | 18 + .../GraphingCalculatorViewModel.h | 23 + src/Calculator.sln | 68 +- src/Calculator/Calculator.vcxproj | 20 + src/Calculator/Calculator.vcxproj.filters | 9 + src/Calculator/Package.appxmanifest | 4 +- src/Calculator/Resources/en-US/Resources.resw | 40 +- .../GraphingCalculator/EquationInputArea.xaml | 86 +++ .../EquationInputArea.xaml.cpp | 108 +++ .../EquationInputArea.xaml.h | 33 + .../GraphingCalculator.xaml | 242 +++++++ .../GraphingCalculator.xaml.cpp | 51 ++ .../GraphingCalculator.xaml.h | 28 + src/Calculator/Views/MainPage.xaml | 3 + src/Calculator/Views/MainPage.xaml.cpp | 38 +- src/Calculator/Views/MainPage.xaml.h | 29 +- .../NavCategoryUnitTests.cpp | 60 +- src/GraphControl/Control/Equation.cpp | 104 +++ src/GraphControl/Control/Equation.h | 80 +++ src/GraphControl/Control/EquationCollection.h | 176 +++++ src/GraphControl/Control/Grapher.cpp | 392 +++++++++++ src/GraphControl/Control/Grapher.h | 160 +++++ .../Control/InspectingDataSource.cpp | 242 +++++++ .../Control/InspectingDataSource.h | 62 ++ src/GraphControl/DirectX/DeviceResources.cpp | 659 ++++++++++++++++++ src/GraphControl/DirectX/DeviceResources.h | 108 +++ src/GraphControl/DirectX/DirectXHelper.h | 63 ++ .../DirectX/NearestPointRenderer.cpp | 66 ++ .../DirectX/NearestPointRenderer.h | 30 + src/GraphControl/DirectX/RenderMain.cpp | 339 +++++++++ src/GraphControl/DirectX/RenderMain.h | 102 +++ src/GraphControl/GraphControl.vcxproj | 346 +++++++++ src/GraphControl/GraphControl.vcxproj.filters | 68 ++ src/GraphControl/Themes/generic.xaml | 13 + src/GraphControl/pch.cpp | 1 + src/GraphControl/pch.h | 34 + src/GraphControl/winrtHeaders.h | 24 + src/GraphingInterfaces/Common.h | 96 +++ src/GraphingInterfaces/GraphingEnums.h | 387 ++++++++++ src/GraphingInterfaces/IGraph.h | 19 + src/GraphingInterfaces/IGraphRenderer.h | 20 + src/GraphingInterfaces/IGraphingOptions.h | 116 +++ src/GraphingInterfaces/IMathSolver.h | 45 ++ src/MockGraphingImpl/MockGraphingImpl.vcxproj | 297 ++++++++ .../MockGraphingImpl.vcxproj.filters | 47 ++ src/MockGraphingImpl/Mocks/MathSolver.cpp | 12 + src/MockGraphingImpl/Mocks/MathSolver.h | 68 ++ src/MockGraphingImpl/dllmain.cpp | 14 + src/MockGraphingImpl/pch.cpp | 1 + src/MockGraphingImpl/pch.h | 9 + src/MockGraphingImpl/targetver.h | 8 + 65 files changed, 5190 insertions(+), 109 deletions(-) create mode 100644 src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp create mode 100644 src/CalcViewModel/GraphingCalculator/EquationViewModel.h create mode 100644 src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp create mode 100644 src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h create mode 100644 src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml create mode 100644 src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.cpp create mode 100644 src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.h create mode 100644 src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml create mode 100644 src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp create mode 100644 src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h create mode 100644 src/GraphControl/Control/Equation.cpp create mode 100644 src/GraphControl/Control/Equation.h create mode 100644 src/GraphControl/Control/EquationCollection.h create mode 100644 src/GraphControl/Control/Grapher.cpp create mode 100644 src/GraphControl/Control/Grapher.h create mode 100644 src/GraphControl/Control/InspectingDataSource.cpp create mode 100644 src/GraphControl/Control/InspectingDataSource.h create mode 100644 src/GraphControl/DirectX/DeviceResources.cpp create mode 100644 src/GraphControl/DirectX/DeviceResources.h create mode 100644 src/GraphControl/DirectX/DirectXHelper.h create mode 100644 src/GraphControl/DirectX/NearestPointRenderer.cpp create mode 100644 src/GraphControl/DirectX/NearestPointRenderer.h create mode 100644 src/GraphControl/DirectX/RenderMain.cpp create mode 100644 src/GraphControl/DirectX/RenderMain.h create mode 100644 src/GraphControl/GraphControl.vcxproj create mode 100644 src/GraphControl/GraphControl.vcxproj.filters create mode 100644 src/GraphControl/Themes/generic.xaml create mode 100644 src/GraphControl/pch.cpp create mode 100644 src/GraphControl/pch.h create mode 100644 src/GraphControl/winrtHeaders.h create mode 100644 src/GraphingInterfaces/Common.h create mode 100644 src/GraphingInterfaces/GraphingEnums.h create mode 100644 src/GraphingInterfaces/IGraph.h create mode 100644 src/GraphingInterfaces/IGraphRenderer.h create mode 100644 src/GraphingInterfaces/IGraphingOptions.h create mode 100644 src/GraphingInterfaces/IMathSolver.h create mode 100644 src/MockGraphingImpl/MockGraphingImpl.vcxproj create mode 100644 src/MockGraphingImpl/MockGraphingImpl.vcxproj.filters create mode 100644 src/MockGraphingImpl/Mocks/MathSolver.cpp create mode 100644 src/MockGraphingImpl/Mocks/MathSolver.h create mode 100644 src/MockGraphingImpl/dllmain.cpp create mode 100644 src/MockGraphingImpl/pch.cpp create mode 100644 src/MockGraphingImpl/pch.h create mode 100644 src/MockGraphingImpl/targetver.h diff --git a/.gitignore b/.gitignore index 2100dfc..1088272 100644 --- a/.gitignore +++ b/.gitignore @@ -289,6 +289,7 @@ __pycache__/ # Calculator specific Generated Files/ +src/GraphControl/GraphingImplOverrides.props !/build/config/TRexDefs/** !src/Calculator/TemporaryKey.pfx !src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx \ No newline at end of file diff --git a/README.md b/README.md index d967b17..3da1f4a 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,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). + ## Data / Telemetry 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. diff --git a/src/CalcViewModel/ApplicationViewModel.cpp b/src/CalcViewModel/ApplicationViewModel.cpp index f99ce03..ecffca3 100644 --- a/src/CalcViewModel/ApplicationViewModel.cpp +++ b/src/CalcViewModel/ApplicationViewModel.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" @@ -42,6 +42,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)) { TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); diff --git a/src/CalcViewModel/ApplicationViewModel.h b/src/CalcViewModel/ApplicationViewModel.h index f13e00a..5c78739 100644 --- a/src/CalcViewModel/ApplicationViewModel.h +++ b/src/CalcViewModel/ApplicationViewModel.h @@ -5,6 +5,7 @@ #include "StandardCalculatorViewModel.h" #include "DateCalculatorViewModel.h" +#include "GraphingCalculator/GraphingCalculatorViewModel.h" #include "UnitConverterViewModel.h" namespace CalculatorApp @@ -22,6 +23,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_NAMED_PROPERTY_RW(Platform::String^, CategoryName); diff --git a/src/CalcViewModel/CalcViewModel.vcxproj b/src/CalcViewModel/CalcViewModel.vcxproj index e2e98ab..82c74f3 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj +++ b/src/CalcViewModel/CalcViewModel.vcxproj @@ -350,6 +350,8 @@ + + @@ -386,6 +388,8 @@ + + diff --git a/src/CalcViewModel/CalcViewModel.vcxproj.filters b/src/CalcViewModel/CalcViewModel.vcxproj.filters index d05aca0..1e85ec8 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj.filters +++ b/src/CalcViewModel/CalcViewModel.vcxproj.filters @@ -10,6 +10,9 @@ {0184f727-b8aa-4af8-a699-63f1b56e7853} + + {f7519cec-2ebd-432b-9d59-9647de131d50} + @@ -93,6 +96,12 @@ DataLoaders + + GraphingCalculator + + + GraphingCalculator + @@ -213,6 +222,12 @@ Common + + GraphingCalculator + + + GraphingCalculator + diff --git a/src/CalcViewModel/Common/CalculatorButtonUser.h b/src/CalcViewModel/Common/CalculatorButtonUser.h index cc6647d..cda4477 100644 --- a/src/CalcViewModel/Common/CalculatorButtonUser.h +++ b/src/CalcViewModel/Common/CalculatorButtonUser.h @@ -62,10 +62,6 @@ namespace CalculatorApp IsStandardMode = (int) CM::Command::ModeBasic, None = (int) CM::Command::CommandNULL, IsProgrammerMode = (int) CM::Command::ModeProgrammer, - DecButton = (int) CM::Command::CommandDec, - OctButton = (int) CM::Command::CommandOct, - HexButton = (int) CM::Command::CommandHex, - BinButton = (int) CM::Command::CommandBin, And = (int) CM::Command::CommandAnd, Ror = (int) CM::Command::CommandROR, Rol = (int) CM::Command::CommandROL, @@ -87,12 +83,21 @@ namespace CalculatorApp InvSinh = (int) CM::Command::CommandASINH, InvCosh = (int) CM::Command::CommandACOSH, InvTanh = (int) CM::Command::CommandATANH, - Qword = (int) CM::Command::CommandQword, - Dword = (int) CM::Command::CommandDword, - Word = (int) CM::Command::CommandWord, - Byte = (int) CM::Command::CommandByte, Cube = (int) CM::Command::CommandCUB, DMS = (int) CM::Command::CommandDMS, + Hyp = (int)CM::Command::CommandHYP, + HexButton = (int)CM::Command::CommandHex, + DecButton = (int)CM::Command::CommandDec, + OctButton = (int)CM::Command::CommandOct, + BinButton = (int)CM::Command::CommandBin, + Qword = (int)CM::Command::CommandQword, + Dword = (int)CM::Command::CommandDword, + Word = (int)CM::Command::CommandWord, + Byte = (int)CM::Command::CommandByte, + + Plot, + X, + Y, BINSTART = (int) CM::Command::CommandBINEDITSTART, BINPOS0 = (int) CM::Command::CommandBINPOS0, @@ -159,8 +164,7 @@ namespace CalculatorApp BINPOS61 = (int) CM::Command::CommandBINPOS61, BINPOS62 = (int) CM::Command::CommandBINPOS62, BINPOS63 = (int) CM::Command::CommandBINPOS63, - BINEND = (int) CM::Command::CommandBINEDITEND, - Hyp = (int) CM::Command::CommandHYP + BINEND = (int) CM::Command::CommandBINEDITEND }; // This contains list of functions whose usage we are tracelogging diff --git a/src/CalcViewModel/Common/KeyboardShortcutManager.cpp b/src/CalcViewModel/Common/KeyboardShortcutManager.cpp index d7b87c9..33d2697 100644 --- a/src/CalcViewModel/Common/KeyboardShortcutManager.cpp +++ b/src/CalcViewModel/Common/KeyboardShortcutManager.cpp @@ -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; @@ -70,7 +71,7 @@ namespace CalculatorApp } } - void LightUpButton(ButtonBase^ button) + winrt::fire_and_forget LightUpButton(ButtonBase^ button) { // If the button is a toggle button then we don't need // to change the UI of the button @@ -82,33 +83,15 @@ namespace CalculatorApp // The button will go into the visual Pressed state with this call VisualStateManager::GoToState(button, "Pressed", true); - // This timer will fire after lightUpTime and make the button - // go back to the normal state. - // This timer will only fire once after which it will be destroyed - auto timer = ref new DispatcherTimer(); - TimeSpan lightUpTime{}; - lightUpTime.Duration = 500000L; // Half second (in 100-ns units) - timer->Interval = lightUpTime; + winrt::apartment_context uiThreadContext; - WeakReference timerWeakReference(timer); - WeakReference buttonWeakReference(button); - timer->Tick += ref new EventHandler( - [buttonWeakReference, timerWeakReference](Object^, Object^) - { - auto button = buttonWeakReference.Resolve(); - if (button) - { - VisualStateManager::GoToState(button, "Normal", true); - } + co_await winrt::resume_background(); + co_await winrt::resume_after(500ms); - // Cancel the timer after we're done so it only fires once - auto timer = timerWeakReference.Resolve(); - if (timer) - { - timer->Stop(); - } - }); - timer->Start(); + co_await uiThreadContext; + + // Restore the normal state + VisualStateManager::GoToState(button, "Normal", true); } // Looks for the first button reference that it can resolve @@ -457,9 +440,8 @@ void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow^ sender, Cha wchar_t character = static_cast(args->KeyCode); auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character); - RunFirstEnabledButtonCommand(buttons); - LightUpButtons(buttons); + RunFirstEnabledButtonCommand(buttons); } } } @@ -613,8 +595,6 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^ { if (currentHonorShortcuts->second) { - 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 @@ -627,6 +607,8 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^ LightUpButtons(buttons); } } + + RunFirstEnabledButtonCommand(buttons); } } } diff --git a/src/CalcViewModel/Common/NavCategory.cpp b/src/CalcViewModel/Common/NavCategory.cpp index e5247cf..43049e8 100644 --- a/src/CalcViewModel/Common/NavCategory.cpp +++ b/src/CalcViewModel/Common/NavCategory.cpp @@ -46,14 +46,16 @@ 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 ^^^ // The order of items in this list determines the order of items in the menu. -static constexpr array s_categoryManifest = { +static constexpr array s_categoryManifest = { NavCategoryInitializer { ViewMode::Standard, STANDARD_ID, L"Standard", L"StandardMode", L"\uE8EF", CategoryGroupType::Calculator, MyVirtualKey::Number1, SUPPORTS_ALL }, NavCategoryInitializer { ViewMode::Scientific, SCIENTIFIC_ID, L"Scientific", L"ScientificMode", L"\uF196", CategoryGroupType::Calculator, MyVirtualKey::Number2, SUPPORTS_ALL }, NavCategoryInitializer { ViewMode::Programmer, PROGRAMMER_ID, L"Programmer", L"ProgrammerMode", L"\uECCE", CategoryGroupType::Calculator, MyVirtualKey::Number3, SUPPORTS_ALL }, NavCategoryInitializer { ViewMode::Date, DATE_ID, L"Date", L"DateCalculationMode", L"\uE787", CategoryGroupType::Calculator, MyVirtualKey::Number4, SUPPORTS_ALL }, + NavCategoryInitializer { ViewMode::Graphing, GRAPHING_ID, L"Graphing", L"GraphingCalculatorMode", L"\uF770", CategoryGroupType::Calculator, MyVirtualKey::Number5, SUPPORTS_ALL }, NavCategoryInitializer { ViewMode::Currency, CURRENCY_ID, L"Currency", L"CategoryName_Currency", L"\uEB0D", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, NavCategoryInitializer { ViewMode::Volume, VOLUME_ID, L"Volume", L"CategoryName_Volume", L"\uF1AA", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, NavCategoryInitializer { ViewMode::Length, LENGTH_ID, L"Length", L"CategoryName_Length", L"\uECC6", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, @@ -121,9 +123,15 @@ 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) diff --git a/src/CalcViewModel/Common/NavCategory.h b/src/CalcViewModel/Common/NavCategory.h index 33e6915..b61375c 100644 --- a/src/CalcViewModel/Common/NavCategory.h +++ b/src/CalcViewModel/Common/NavCategory.h @@ -43,7 +43,8 @@ namespace CalculatorApp Data = 13, Pressure = 14, Angle = 15, - Currency = 16 + Currency = 16, + Graphing = 17 }; public enum class CategoryGroupType @@ -163,6 +164,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); diff --git a/src/CalcViewModel/Common/Utils.cpp b/src/CalcViewModel/Common/Utils.cpp index 6aad5f5..b787c10 100644 --- a/src/CalcViewModel/Common/Utils.cpp +++ b/src/CalcViewModel/Common/Utils.cpp @@ -15,11 +15,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; @@ -68,7 +70,7 @@ int Utils::GetWindowId() return windowId; } -void Utils::RunOnUIThreadNonblocking(std::function&& function, _In_ CoreDispatcher^ currentDispatcher) +void Utils::RunOnUIThreadNonblocking(function&& function, _In_ CoreDispatcher^ currentDispatcher) { if (currentDispatcher != nullptr) { @@ -91,7 +93,7 @@ wstring Utils::RemoveUnwantedCharsFromWstring(wstring input, wchar_t* unwantedCh { for (unsigned int i = 0; i < size; ++i) { - input.erase(std::remove(input.begin(), input.end(), unwantedChars[i]), input.end()); + input.erase(remove(input.begin(), input.end(), unwantedChars[i]), input.end()); } return input; } @@ -225,3 +227,53 @@ task Utils::ReadFileFromFolder(IStorageFolder^ folder, String^ fileName 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()); +} + +bool operator==(const Color& color1, const Color& color2) +{ + return equal_to()(color1, color2); +} + +bool operator!=(const Color& color1, const Color& color2) +{ + return !(color1 == color2); +} diff --git a/src/CalcViewModel/Common/Utils.h b/src/CalcViewModel/Common/Utils.h index a6b773a..68c323a 100644 --- a/src/CalcViewModel/Common/Utils.h +++ b/src/CalcViewModel/Common/Utils.h @@ -6,6 +6,7 @@ #include "CalcManager/CalculatorVector.h" #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_ @@ -301,6 +302,15 @@ namespace Utils concurrency::task WriteFileToFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName, Platform::String^ contents, Windows::Storage::CreationCollisionOption collisionOption); concurrency::task 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); + + } // This goes into the header to define the property, in the public: section of the class @@ -421,3 +431,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 +{ +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); diff --git a/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp b/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp new file mode 100644 index 0000000..6946766 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp @@ -0,0 +1,12 @@ +#include "pch.h" +#include "EquationViewModel.h" + +using namespace Windows::UI; + +namespace CalculatorApp::ViewModel +{ + EquationViewModel::EquationViewModel() + : m_LineColor{ Colors::Transparent } + { + } +} diff --git a/src/CalcViewModel/GraphingCalculator/EquationViewModel.h b/src/CalcViewModel/GraphingCalculator/EquationViewModel.h new file mode 100644 index 0000000..0920caf --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/EquationViewModel.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../Common/Utils.h" + +namespace CalculatorApp::ViewModel +{ + public ref class EquationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + EquationViewModel(); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_RW(Platform::String^, Expression); + OBSERVABLE_PROPERTY_RW(Windows::UI::Color, LineColor); + }; +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp new file mode 100644 index 0000000..394c372 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp @@ -0,0 +1,18 @@ +#include "pch.h" +#include "GraphingCalculatorViewModel.h" + +using namespace CalculatorApp::ViewModel; +using namespace Platform::Collections; + +namespace CalculatorApp::ViewModel +{ + GraphingCalculatorViewModel::GraphingCalculatorViewModel() + : m_IsDecimalEnabled{ true } + , m_Equations{ ref new Vector< EquationViewModel^ >() } + { + } + + void GraphingCalculatorViewModel::OnButtonPressed(Object^ parameter) + { + } +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h new file mode 100644 index 0000000..326903a --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../Common/Utils.h" +#include "EquationViewModel.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); + + COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed); + + private: + void OnButtonPressed(Platform::Object^ parameter); + }; +} diff --git a/src/Calculator.sln b/src/Calculator.sln index 2d36a93..74fbf4a 100644 --- a/src/Calculator.sln +++ b/src/Calculator.sln @@ -16,6 +16,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcViewModel", "CalcViewMo EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorUnitTests", "CalculatorUnitTests\CalculatorUnitTests.vcxproj", "{D3BAED2C-4B07-4E1D-8807-9D6499450349}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MockGraphingImpl", "MockGraphingImpl\MockGraphingImpl.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 @@ -28,22 +32,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.ActiveCfg = Debug|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.Build.0 = Debug|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.Build.0 = Debug|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.ActiveCfg = Debug|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.Build.0 = Debug|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.ActiveCfg = Debug|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.Build.0 = Debug|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.ActiveCfg = Release|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.Build.0 = Release|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.ActiveCfg = Release|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.Build.0 = Release|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.ActiveCfg = Release|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.Build.0 = Release|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.ActiveCfg = Release|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.Build.0 = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.ActiveCfg = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Build.0 = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Deploy.0 = Debug|ARM @@ -68,6 +56,22 @@ Global {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.ActiveCfg = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Build.0 = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Deploy.0 = Release|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.ActiveCfg = Debug|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.Build.0 = Debug|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.Build.0 = Debug|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.ActiveCfg = Debug|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.Build.0 = Debug|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.ActiveCfg = Debug|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.Build.0 = Debug|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.ActiveCfg = Release|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.Build.0 = Release|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.ActiveCfg = Release|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.Build.0 = Release|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.ActiveCfg = Release|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.Build.0 = Release|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.ActiveCfg = Release|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.Build.0 = Release|Win32 {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.ActiveCfg = Debug|ARM {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.Build.0 = Debug|ARM {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM64.ActiveCfg = Debug|ARM64 @@ -100,6 +104,38 @@ Global {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.ActiveCfg = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Build.0 = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Deploy.0 = Release|Win32 + {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 diff --git a/src/Calculator/Calculator.vcxproj b/src/Calculator/Calculator.vcxproj index 4e19aab..6c40aa2 100644 --- a/src/Calculator/Calculator.vcxproj +++ b/src/Calculator/Calculator.vcxproj @@ -130,6 +130,9 @@ + + + /bigobj /await /std:c++17 @@ -281,6 +284,12 @@ Views\CalculatorStandardOperators.xaml + + Views\GraphingCalculator\EquationInputArea.xaml + + + Views\GraphingCalculator\GraphingCalculator.xaml + Views\HistoryList.xaml @@ -336,6 +345,8 @@ + + @@ -421,6 +432,12 @@ Views\CalculatorStandardOperators.xaml + + Views\GraphingCalculator\EquationInputArea.xaml + + + Views\GraphingCalculator\GraphingCalculator.xaml + Views\HistoryList.xaml @@ -829,6 +846,9 @@ {90e9761d-9262-4773-942d-caeae75d7140} + + {e727a92b-f149-492c-8117-c039a298719b} + diff --git a/src/Calculator/Calculator.vcxproj.filters b/src/Calculator/Calculator.vcxproj.filters index cc25a22..bc22d78 100644 --- a/src/Calculator/Calculator.vcxproj.filters +++ b/src/Calculator/Calculator.vcxproj.filters @@ -218,6 +218,9 @@ {0120c344-0bc0-4a1d-b82c-df7945f46189} + + {e23e2a6e-491b-4200-9bf7-d355a1ee695b} + @@ -480,6 +483,12 @@ Views + + Views\GraphingCalculator + + + Views\GraphingCalculator + diff --git a/src/Calculator/Package.appxmanifest b/src/Calculator/Package.appxmanifest index 93d1d7f..83bfc94 100644 --- a/src/Calculator/Package.appxmanifest +++ b/src/Calculator/Package.appxmanifest @@ -1,6 +1,6 @@ - + - + ms-resource:DevAppStoreName diff --git a/src/Calculator/Resources/en-US/Resources.resw b/src/Calculator/Resources/en-US/Resources.resw index a9f3682..9233a5a 100644 --- a/src/Calculator/Resources/en-US/Resources.resw +++ b/src/Calculator/Resources/en-US/Resources.resw @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Calculator + Graphing Calculator {@Appx_ShortDisplayName@}{StringCategory="Feature Title"} This is the title of the official application when published through Windows Store. @@ -1271,7 +1271,7 @@ Equals - Screen reader prompt for the invert button on the scientific operator keypad + Screen reader prompt for the equals button on the scientific operator keypad Inverse Function @@ -3379,4 +3379,40 @@ Microsoft Services Agreement Displayed on a link to the Microsoft Services Agreement in the about this app information + + Graphing + Name of the Graphing mode of the Calculator app. Displayed in the navigation menu. + + + = + {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. + + + Equals + Screen reader prompt for the equal button on the graphing calculator operator keypad + + + Enter + {Locked}This is the value from the VirtualKey enum that maps to this button + + + Plot + Screen reader prompt for the plot button on the graphing calculator operator keypad + + + X + {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. + + + Y + {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. + + + Equations + The text that shows as the header for the equation input area in Graphing Calculator mode. + + + ^ + {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. + diff --git a/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml b/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml new file mode 100644 index 0000000..5da0818 --- /dev/null +++ b/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + +