diff --git a/src/CalcViewModel/CalcViewModel.vcxproj b/src/CalcViewModel/CalcViewModel.vcxproj index be43a550..711c9383 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj +++ b/src/CalcViewModel/CalcViewModel.vcxproj @@ -331,6 +331,7 @@ + diff --git a/src/CalcViewModel/CalcViewModel.vcxproj.filters b/src/CalcViewModel/CalcViewModel.vcxproj.filters index 9e97ecb8..98235248 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj.filters +++ b/src/CalcViewModel/CalcViewModel.vcxproj.filters @@ -217,6 +217,9 @@ Common + + GraphingCalculator + diff --git a/src/CalcViewModel/Common/Utils.h b/src/CalcViewModel/Common/Utils.h index 4bdfbcf9..9e532870 100644 --- a/src/CalcViewModel/Common/Utils.h +++ b/src/CalcViewModel/Common/Utils.h @@ -10,6 +10,8 @@ // Utility macros to make Models easier to write // generates a member variable called m_ +#define SINGLE_ARG(...) __VA_ARGS__ + #define PROPERTY_R(t, n) \ property t n \ { \ @@ -72,25 +74,6 @@ private: \ public: -#define OBSERVABLE_PROPERTY_RW_ALWAYS_NOTIFY(t, n) \ - property t n \ - { \ - t get() \ - { \ - return m_##n; \ - } \ - void set(t value) \ - { \ - m_##n = value; \ - RaisePropertyChanged(L#n); \ - } \ - } \ - \ -private: \ - t m_##n; \ - \ -public: - #define OBSERVABLE_PROPERTY_RW(t, n) \ property t n \ { \ diff --git a/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp b/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp index b2eca554..94b97794 100644 --- a/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp +++ b/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp @@ -32,9 +32,9 @@ namespace CalculatorApp::ViewModel { } - EquationViewModel::EquationViewModel(GraphControl::Equation ^ equation) + EquationViewModel::EquationViewModel(Equation ^ equation, int functionLabelIndex, Windows::UI::Color color) : m_AnalysisErrorVisible{ false } - , m_FunctionLabelIndex{ 0 } + , m_FunctionLabelIndex{ functionLabelIndex } , m_KeyGraphFeaturesItems{ ref new Vector() } , m_resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView() } { @@ -44,18 +44,20 @@ namespace CalculatorApp::ViewModel } GraphEquation = equation; + LineColor = color; + IsLineEnabled = true; } - void EquationViewModel::PopulateKeyGraphFeatures() + void EquationViewModel::PopulateKeyGraphFeatures(KeyGraphFeaturesInfo ^ graphEquation) { - if (GraphEquation->AnalysisError != 0) + if (graphEquation->AnalysisError != 0) { AnalysisErrorVisible = true; - if (GraphEquation->AnalysisError == static_cast(AnalysisErrorType::AnalysisCouldNotBePerformed)) + if (graphEquation->AnalysisError == static_cast(AnalysisErrorType::AnalysisCouldNotBePerformed)) { AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisCouldNotBePerformed"); } - else if (GraphEquation->AnalysisError == static_cast(AnalysisErrorType::AnalysisNotSupported)) + else if (graphEquation->AnalysisError == static_cast(AnalysisErrorType::AnalysisNotSupported)) { AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisNotSupported"); } @@ -64,26 +66,23 @@ namespace CalculatorApp::ViewModel 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"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"InflectionPoints"), GraphEquation->InflectionPoints, m_resourceLoader->GetString(L"KGFInflectionPointsNone")); + m_resourceLoader->GetString(L"VerticalAsymptotes"), graphEquation->VerticalAsymptotes, m_resourceLoader->GetString(L"KGFVerticalAsymptotesNone")); AddKeyGraphFeature( - m_resourceLoader->GetString(L"VerticalAsymptotes"), GraphEquation->VerticalAsymptotes, m_resourceLoader->GetString(L"KGFVerticalAsymptotesNone")); + m_resourceLoader->GetString(L"HorizontalAsymptotes"), graphEquation->HorizontalAsymptotes, m_resourceLoader->GetString(L"KGFHorizontalAsymptotesNone")); 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(); - AddPeriodicityKeyGraphFeature(); - AddMonotoncityKeyGraphFeature(); - AddTooComplexKeyGraphFeature(); + m_resourceLoader->GetString(L"ObliqueAsymptotes"), graphEquation->ObliqueAsymptotes, m_resourceLoader->GetString(L"KGFObliqueAsymptotesNone")); + AddParityKeyGraphFeature(graphEquation); + AddPeriodicityKeyGraphFeature(graphEquation); + AddMonotoncityKeyGraphFeature(graphEquation); + AddTooComplexKeyGraphFeature(graphEquation); AnalysisErrorVisible = false; } @@ -125,11 +124,11 @@ namespace CalculatorApp::ViewModel KeyGraphFeaturesItems->Append(item); } - void EquationViewModel::AddParityKeyGraphFeature() + void EquationViewModel::AddParityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) { KeyGraphFeaturesItem ^ parityItem = ref new KeyGraphFeaturesItem(); parityItem->Title = m_resourceLoader->GetString(L"Parity"); - switch (GraphEquation->Parity) + switch (graphEquation->Parity) { case 0: parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown")); @@ -151,11 +150,11 @@ namespace CalculatorApp::ViewModel KeyGraphFeaturesItems->Append(parityItem); } - void EquationViewModel::AddPeriodicityKeyGraphFeature() + void EquationViewModel::AddPeriodicityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) { KeyGraphFeaturesItem ^ periodicityItem = ref new KeyGraphFeaturesItem(); periodicityItem->Title = m_resourceLoader->GetString(L"Periodicity"); - switch (GraphEquation->PeriodicityDirection) + switch (graphEquation->PeriodicityDirection) { case 0: // Periodicity is not supported or is too complex to calculate. @@ -163,14 +162,14 @@ namespace CalculatorApp::ViewModel // SetTooComplexFeaturesErrorProperty will set the too complex error when periodicity is supported and unknown return; case 1: - if (GraphEquation->PeriodicityExpression == L"") + if (graphEquation->PeriodicityExpression == L"") { periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityUnknown")); periodicityItem->IsText = true; } else { - periodicityItem->DisplayItems->Append(GraphEquation->PeriodicityExpression); + periodicityItem->DisplayItems->Append(graphEquation->PeriodicityExpression); periodicityItem->IsText = false; } break; @@ -186,13 +185,13 @@ namespace CalculatorApp::ViewModel KeyGraphFeaturesItems->Append(periodicityItem); } - void EquationViewModel::AddMonotoncityKeyGraphFeature() + void EquationViewModel::AddMonotoncityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) { KeyGraphFeaturesItem ^ monotonicityItem = ref new KeyGraphFeaturesItem(); monotonicityItem->Title = m_resourceLoader->GetString(L"Monotonicity"); - if (GraphEquation->Monotonicity->Size != 0) + if (graphEquation->Monotonicity->Size != 0) { - for (auto item : GraphEquation->Monotonicity) + for (auto item : graphEquation->Monotonicity) { GridDisplayItems ^ gridItem = ref new GridDisplayItems(); gridItem->Expression = item->Key; @@ -230,9 +229,9 @@ namespace CalculatorApp::ViewModel KeyGraphFeaturesItems->Append(monotonicityItem); } - void EquationViewModel::AddTooComplexKeyGraphFeature() + void EquationViewModel::AddTooComplexKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) { - if (GraphEquation->TooComplexFeatures <= 0) + if (graphEquation->TooComplexFeatures <= 0) { return; } @@ -240,55 +239,55 @@ namespace CalculatorApp::ViewModel Platform::String ^ separator = ref new String(LocalizationSettings::GetInstance().GetListSeparator().c_str()); wstring error; - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Domain) == KeyGraphFeaturesFlag::Domain) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Domain) == KeyGraphFeaturesFlag::Domain) { error.append((m_resourceLoader->GetString(L"Domain") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Range) == KeyGraphFeaturesFlag::Range) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Range) == KeyGraphFeaturesFlag::Range) { error.append((m_resourceLoader->GetString(L"Range") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Zeros) == KeyGraphFeaturesFlag::Zeros) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Zeros) == KeyGraphFeaturesFlag::Zeros) { error.append((m_resourceLoader->GetString(L"XIntercept") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::YIntercept) == KeyGraphFeaturesFlag::YIntercept) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::YIntercept) == KeyGraphFeaturesFlag::YIntercept) { error.append((m_resourceLoader->GetString(L"YIntercept") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Parity) == KeyGraphFeaturesFlag::Parity) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Parity) == KeyGraphFeaturesFlag::Parity) { error.append((m_resourceLoader->GetString(L"Parity") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Periodicity) == KeyGraphFeaturesFlag::Periodicity) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Periodicity) == KeyGraphFeaturesFlag::Periodicity) { error.append((m_resourceLoader->GetString(L"Periodicity") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Minima) == KeyGraphFeaturesFlag::Minima) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Minima) == KeyGraphFeaturesFlag::Minima) { error.append((m_resourceLoader->GetString(L"Minima") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Maxima) == KeyGraphFeaturesFlag::Maxima) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Maxima) == KeyGraphFeaturesFlag::Maxima) { error.append((m_resourceLoader->GetString(L"Maxima") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::InflectionPoints) == KeyGraphFeaturesFlag::InflectionPoints) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::InflectionPoints) == KeyGraphFeaturesFlag::InflectionPoints) { error.append((m_resourceLoader->GetString(L"InflectionPoints") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::VerticalAsymptotes) == KeyGraphFeaturesFlag::VerticalAsymptotes) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::VerticalAsymptotes) == KeyGraphFeaturesFlag::VerticalAsymptotes) { error.append((m_resourceLoader->GetString(L"VerticalAsymptotes") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::HorizontalAsymptotes) == KeyGraphFeaturesFlag::HorizontalAsymptotes) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::HorizontalAsymptotes) == KeyGraphFeaturesFlag::HorizontalAsymptotes) { error.append((m_resourceLoader->GetString(L"HorizontalAsymptotes") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::ObliqueAsymptotes) == KeyGraphFeaturesFlag::ObliqueAsymptotes) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::ObliqueAsymptotes) == KeyGraphFeaturesFlag::ObliqueAsymptotes) { error.append((m_resourceLoader->GetString(L"ObliqueAsymptotes") + separator + L" ")->Data()); } - if ((GraphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::MonotoneIntervals) == KeyGraphFeaturesFlag::MonotoneIntervals) + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::MonotoneIntervals) == KeyGraphFeaturesFlag::MonotoneIntervals) { error.append((m_resourceLoader->GetString(L"Monotonicity") + separator + L" ")->Data()); } @@ -300,5 +299,4 @@ namespace CalculatorApp::ViewModel KeyGraphFeaturesItems->Append(tooComplexItem); } - } diff --git a/src/CalcViewModel/GraphingCalculator/EquationViewModel.h b/src/CalcViewModel/GraphingCalculator/EquationViewModel.h index 299569b6..5ed0870d 100644 --- a/src/CalcViewModel/GraphingCalculator/EquationViewModel.h +++ b/src/CalcViewModel/GraphingCalculator/EquationViewModel.h @@ -5,6 +5,12 @@ #include "../Common/Utils.h" +namespace GraphControl +{ + ref class Equation; + ref class KeyGraphFeaturesInfo; +} + namespace CalculatorApp::ViewModel { public @@ -31,16 +37,15 @@ public OBSERVABLE_PROPERTY_RW(bool, IsText); }; - public ref class EquationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: - EquationViewModel(GraphControl::Equation ^ equation); + EquationViewModel(GraphControl::Equation ^ equation, int functionLabelIndex, Windows::UI::Color color); OBSERVABLE_OBJECT(); OBSERVABLE_PROPERTY_R(GraphControl::Equation ^, GraphEquation); - OBSERVABLE_PROPERTY_RW(int, FunctionLabelIndex); + OBSERVABLE_PROPERTY_R(int, FunctionLabelIndex); OBSERVABLE_PROPERTY_RW(bool, IsLastItemInList); property Platform::String ^ Expression @@ -59,15 +64,15 @@ public } } - property Windows::UI::Xaml::Media::SolidColorBrush ^ LineColor + property Windows::UI::Color LineColor { - Windows::UI::Xaml::Media::SolidColorBrush ^ get() + Windows::UI::Color get() { return GraphEquation->LineColor; } - void set(Windows::UI::Xaml::Media::SolidColorBrush ^ value) + void set(Windows::UI::Color value) { - if (GraphEquation->LineColor != value) + if (!Utils::AreColorsEqual(GraphEquation->LineColor, value)) { GraphEquation->LineColor = value; RaisePropertyChanged("LineColor"); @@ -96,7 +101,7 @@ public OBSERVABLE_PROPERTY_R(bool, AnalysisErrorVisible); OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, KeyGraphFeaturesItems) - void PopulateKeyGraphFeatures(); + void PopulateKeyGraphFeatures(GraphControl::KeyGraphFeaturesInfo ^ info); private: void AddKeyGraphFeature(Platform::String ^ title, Platform::String ^ expression, Platform::String ^ errorString); @@ -104,13 +109,12 @@ public Platform::String ^ title, Windows::Foundation::Collections::IVector ^ expressionVector, Platform::String ^ errorString); - void AddParityKeyGraphFeature(); - void AddPeriodicityKeyGraphFeature(); - void AddMonotoncityKeyGraphFeature(); - void AddTooComplexKeyGraphFeature(); + void AddParityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddPeriodicityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddMonotoncityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddTooComplexKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); Windows::Foundation::Collections::IObservableMap ^ m_Monotonicity; Windows::ApplicationModel::Resources::ResourceLoader ^ m_resourceLoader; }; - } diff --git a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp index 07b6fc13..974cf8f8 100644 --- a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp +++ b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp @@ -15,27 +15,30 @@ namespace CalculatorApp::ViewModel { GraphingCalculatorViewModel::GraphingCalculatorViewModel() : m_IsDecimalEnabled{ true } - , m_Equations{ ref new Vector< EquationViewModel^ >() } - , m_Variables{ ref new Vector< VariableViewModel^ >() } + , m_Equations{ ref new Vector() } + , m_Variables{ ref new Vector() } { } - void GraphingCalculatorViewModel::OnButtonPressed(Object^ parameter) + void GraphingCalculatorViewModel::OnButtonPressed(Object ^ parameter) { } - void GraphingCalculatorViewModel::UpdateVariables(IMap^ variables) + void GraphingCalculatorViewModel::UpdateVariables(IMap ^ variables) { Variables->Clear(); for (auto var : variables) { auto variable = ref new VariableViewModel(var->Key, var->Value); - variable->VariableUpdated += ref new EventHandler([this, variable](Object^ sender, VariableChangedEventArgs e) - { - VariableUpdated(variable, VariableChangedEventArgs{ e.variableName, e.newValue }); - }); + variable->VariableUpdated += ref new EventHandler([this, variable](Object ^ sender, VariableChangedEventArgs e) { + VariableUpdated(variable, VariableChangedEventArgs{ e.variableName, e.newValue }); + }); Variables->Append(variable); - } } + + void GraphingCalculatorViewModel::SetSelectedEquation(EquationViewModel ^ equation) + { + SelectedEquation = equation; + } } diff --git a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h index 18a60543..78e89479 100644 --- a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h +++ b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h @@ -5,66 +5,10 @@ #include "../Common/Utils.h" #include "EquationViewModel.h" +#include "VariableViewModel.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_CALLBACK(OnPropertyChanged); - - OBSERVABLE_PROPERTY_R(Platform::String ^, Name); - - // TODO: Consider removing this work around and manually set the textbox text. - OBSERVABLE_PROPERTY_RW_ALWAYS_NOTIFY(double, Value); - OBSERVABLE_PROPERTY_RW_ALWAYS_NOTIFY(double, Min); - OBSERVABLE_PROPERTY_RW_ALWAYS_NOTIFY(double, Step); - OBSERVABLE_PROPERTY_RW_ALWAYS_NOTIFY(double, Max); - OBSERVABLE_PROPERTY_RW(bool, SliderSettingsVisible); - - event Windows::Foundation::EventHandler ^ VariableUpdated; - - void SetValue(double value) - { - if (value < Min) - { - value = Min; - } - else if (value > Max) - { - value = Max; - } - - Value = value; - } - - private: - void OnPropertyChanged(Platform::String ^ propertyName) - { - if (propertyName == "Value") - { - VariableUpdated(this, VariableChangedEventArgs{ Name, Value }); - } - } - }; - [Windows::UI::Xaml::Data::Bindable] public ref class GraphingCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: @@ -74,6 +18,7 @@ public OBSERVABLE_PROPERTY_R(bool, IsDecimalEnabled); OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Equations); OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Variables); + OBSERVABLE_PROPERTY_R(EquationViewModel ^, SelectedEquation); COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed); @@ -81,6 +26,7 @@ public void UpdateVariables(Windows::Foundation::Collections::IMap ^ variables); + void SetSelectedEquation(EquationViewModel ^ equation); private: void OnButtonPressed(Platform::Object ^ parameter); }; diff --git a/src/CalcViewModel/GraphingCalculator/VariableViewModel.h b/src/CalcViewModel/GraphingCalculator/VariableViewModel.h new file mode 100644 index 00000000..d4dd699e --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/VariableViewModel.h @@ -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 ^ 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; + }; +} diff --git a/src/Calculator/Controls/EquationTextBox.cpp b/src/Calculator/Controls/EquationTextBox.cpp index 57f5a13e..ef3d7514 100644 --- a/src/Calculator/Controls/EquationTextBox.cpp +++ b/src/Calculator/Controls/EquationTextBox.cpp @@ -11,7 +11,6 @@ using namespace Platform; using namespace CalculatorApp; using namespace CalculatorApp::Common; using namespace CalculatorApp::Controls; -using namespace CalculatorApp::ViewModel; using namespace Windows::System; using namespace Windows::Foundation; using namespace Windows::ApplicationModel; diff --git a/src/Calculator/EquationStylePanelControl.xaml.cpp b/src/Calculator/EquationStylePanelControl.xaml.cpp index b964ebaf..e143d07a 100644 --- a/src/Calculator/EquationStylePanelControl.xaml.cpp +++ b/src/Calculator/EquationStylePanelControl.xaml.cpp @@ -32,11 +32,19 @@ void EquationStylePanelControl::SelectionChanged(Object ^ /*sender */, Selection { if (e->AddedItems->Size > 0) { - SelectedColor = static_cast(e->AddedItems->GetAt(0)); + auto brush = dynamic_cast(e->AddedItems->GetAt(0)); + if (brush == nullptr) + { + SelectedColor = Colors::Black; + } + else + { + SelectedColor = brush->Color; + } } } -void EquationStylePanelControl::OnSelectedColorPropertyChanged(SolidColorBrush ^ /*oldColor*/, SolidColorBrush ^ newColor) +void EquationStylePanelControl::OnSelectedColorPropertyChanged(Color /*oldColor*/, Color newColor) { SelectColor(newColor); } @@ -46,13 +54,8 @@ void EquationStylePanelControl::ColorChooserLoaded(Object ^ sender, RoutedEventA SelectColor(SelectedColor); } -void EquationStylePanelControl::SelectColor(SolidColorBrush ^ selectedColor) +void EquationStylePanelControl::SelectColor(Color selectedColor) { - if (selectedColor == nullptr) - { - return; - } - for (auto item : ColorChooser->Items->GetView()) { auto brush = static_cast(item); @@ -63,7 +66,7 @@ void EquationStylePanelControl::SelectColor(SolidColorBrush ^ selectedColor) continue; } - if (brush->Color == selectedColor->Color) + if (Utils::AreColorsEqual(brush->Color, selectedColor)) { gridViewItem->IsSelected = true; return; diff --git a/src/Calculator/EquationStylePanelControl.xaml.h b/src/Calculator/EquationStylePanelControl.xaml.h index 25539b83..b3c3aabb 100644 --- a/src/Calculator/EquationStylePanelControl.xaml.h +++ b/src/Calculator/EquationStylePanelControl.xaml.h @@ -14,13 +14,15 @@ namespace CalculatorApp EquationStylePanelControl(); DEPENDENCY_PROPERTY_OWNER(EquationStylePanelControl); - DEPENDENCY_PROPERTY_WITH_CALLBACK(Windows::UI::Xaml::Media::SolidColorBrush ^, SelectedColor); + DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(Windows::UI::Color, SelectedColor, Windows::UI::Colors::Black); DEPENDENCY_PROPERTY_WITH_DEFAULT(Windows::Foundation::Collections::IVector ^, AvailableColors, nullptr); private: void SelectionChanged(Platform::Object ^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs ^ e); - void OnSelectedColorPropertyChanged(Windows::UI::Xaml::Media::SolidColorBrush ^ oldValue, Windows::UI::Xaml::Media::SolidColorBrush ^ newValue); - void ColorChooserLoaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); - void SelectColor(Windows::UI::Xaml::Media::SolidColorBrush ^ selectedColor); + 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); }; } diff --git a/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml b/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml index 7c729a93..3f82bfaa 100644 --- a/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml +++ b/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml @@ -235,7 +235,7 @@ DataContextChanged="InputTextBox_DataContextChanged" EquationButtonClicked="EquationTextBox_EquationButtonClicked" EquationButtonContentIndex="{x:Bind FunctionLabelIndex, Mode=OneWay}" - EquationColor="{x:Bind LineColor, Mode=OneWay}" + EquationColor="{x:Bind local:EquationInputArea.ToSolidColorBrush(LineColor), Mode=OneWay}" EquationSubmitted="InputTextBox_Submitted" GotFocus="InputTextBox_GotFocus" HasError="{x:Bind GraphEquation.HasGraphError, Mode=OneWay}" diff --git a/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.cpp b/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.cpp index d39b4668..da084aa7 100644 --- a/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.cpp +++ b/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.cpp @@ -7,6 +7,7 @@ using namespace CalculatorApp; using namespace CalculatorApp::Common; +using namespace GraphControl; using namespace CalculatorApp::ViewModel; using namespace CalculatorApp::Controls; using namespace Platform; @@ -61,19 +62,14 @@ void EquationInputArea::OnEquationsPropertyChanged() void EquationInputArea::AddNewEquation() { - auto eq = ref new EquationViewModel(ref new Equation()); - eq->IsLastItemInList = true; - if (Equations->Size > 0) { Equations->GetAt(Equations->Size - 1)->IsLastItemInList = false; } m_lastLineColorIndex = (m_lastLineColorIndex + 1) % AvailableColors->Size; - - eq->LineColor = AvailableColors->GetAt(m_lastLineColorIndex); - eq->IsLineEnabled = true; - eq->FunctionLabelIndex = ++m_lastFunctionLabelIndex; + auto eq = ref new EquationViewModel(ref new Equation(), ++m_lastFunctionLabelIndex, AvailableColors->GetAt(m_lastLineColorIndex)->Color); + eq->IsLastItemInList = true; m_equationToFocus = eq; Equations->Append(eq); } @@ -192,8 +188,7 @@ void EquationInputArea::EquationTextBox_KeyGraphFeaturesButtonClicked(Object ^ s } auto eq = static_cast(tb->DataContext); - EquationVM = eq; - KeyGraphFeaturesRequested(EquationVM, ref new RoutedEventArgs()); + KeyGraphFeaturesRequested(this, eq); } void EquationInputArea::EquationTextBox_EquationButtonClicked(Object ^ sender, RoutedEventArgs ^ e) @@ -210,7 +205,7 @@ void EquationInputArea::InputTextBox_Loaded(Object ^ sender, RoutedEventArgs ^ e auto colorChooser = static_cast(tb->ColorChooserFlyout->Content); colorChooser->AvailableColors = AvailableColors; - if (m_equationToFocus!=nullptr && tb->DataContext == m_equationToFocus) + if (m_equationToFocus != nullptr && tb->DataContext == m_equationToFocus) { m_equationToFocus = nullptr; tb->FocusTextBox(); @@ -227,9 +222,7 @@ void EquationInputArea::InputTextBox_Loaded(Object ^ sender, RoutedEventArgs ^ e } } -void EquationInputArea::InputTextBox_DataContextChanged( - Windows::UI::Xaml::FrameworkElement ^ sender, - Windows::UI::Xaml::DataContextChangedEventArgs ^ args) +void EquationInputArea::InputTextBox_DataContextChanged(Windows::UI::Xaml::FrameworkElement ^ sender, Windows::UI::Xaml::DataContextChangedEventArgs ^ args) { auto tb = static_cast(sender); if (!tb->IsLoaded) @@ -237,7 +230,7 @@ void EquationInputArea::InputTextBox_DataContextChanged( return; } - FocusEquationIfNecessary(tb); + FocusEquationIfNecessary(tb); } void EquationInputArea::FocusEquationIfNecessary(CalculatorApp::Controls::EquationTextBox ^ textBox) @@ -296,18 +289,12 @@ void EquationInputArea::ReloadAvailableColors(bool isHighContrast) return; } - // Use a blank brush to clear out the color before setting it. This is needed because going - // from High Contrast White -> High Contrast Black, the high contrast colors seem to be equivalent, - // causing the change to not take place. - auto blankBrush = ref new SolidColorBrush(); - // Reassign colors for each equation m_lastLineColorIndex = -1; for (auto equationViewModel : Equations) { m_lastLineColorIndex = (m_lastLineColorIndex + 1) % AvailableColors->Size; - equationViewModel->LineColor = blankBrush; - equationViewModel->LineColor = AvailableColors->GetAt(m_lastLineColorIndex); + equationViewModel->LineColor = AvailableColors->GetAt(m_lastLineColorIndex)->Color; } } @@ -319,23 +306,35 @@ void EquationInputArea::TextBoxGotFocus(TextBox ^ sender, RoutedEventArgs ^ e) void EquationInputArea::SubmitTextbox(TextBox ^ sender) { auto variableViewModel = static_cast(sender->DataContext); - + double val; if (sender->Name == "ValueTextBox") { - variableViewModel->SetValue(validateDouble(sender->Text, variableViewModel->Value)); + val = validateDouble(sender->Text, variableViewModel->Value); + variableViewModel->Value = val; } else if (sender->Name == "MinTextBox") { - variableViewModel->Min = validateDouble(sender->Text, variableViewModel->Min); + val = validateDouble(sender->Text, variableViewModel->Min); + variableViewModel->Min = val; } else if (sender->Name == "MaxTextBox") { - variableViewModel->Max = validateDouble(sender->Text, variableViewModel->Max); + val = validateDouble(sender->Text, variableViewModel->Max); + variableViewModel->Max = val; } else if (sender->Name == "StepTextBox") { - variableViewModel->Step = validateDouble(sender->Text, variableViewModel->Step); + 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 ^) diff --git a/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.h b/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.h index 50e48638..78612d7c 100644 --- a/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.h +++ b/src/Calculator/Views/GraphingCalculator/EquationInputArea.xaml.h @@ -23,13 +23,15 @@ namespace CalculatorApp OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged); OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector ^, Equations); OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector ^, Variables); - OBSERVABLE_PROPERTY_RW_ALWAYS_NOTIFY(ViewModel::EquationViewModel ^, EquationVM); OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector ^, AvailableColors); - event Windows::UI::Xaml::RoutedEventHandler ^ KeyGraphFeaturesRequested; + event Windows::Foundation::EventHandler^ 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(); diff --git a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml index dd2aaae0..5b1c815c 100644 --- a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml +++ b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml @@ -480,7 +480,7 @@ Grid.RowSpan="2" Margin="0,4,0,0" KeyGraphFeaturesClosed="OnKeyGraphFeaturesClosed" - ViewModel="{x:Bind EquationInputAreaControl.EquationVM, Mode=OneWay}" + ViewModel="{x:Bind ViewModel.SelectedEquation, Mode=OneWay}" Visibility="{x:Bind IsKeyGraphFeaturesVisible, Mode=OneWay}" x:Load="{x:Bind IsKeyGraphFeaturesVisible, Mode=OneWay}"/> diff --git a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp index 182f1c94..690bc90d 100644 --- a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp +++ b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp @@ -53,8 +53,6 @@ DEPENDENCY_PROPERTY_INITIALIZATION(GraphingCalculator, IsSmallState); GraphingCalculator::GraphingCalculator() { - Equation::RegisterDependencyProperties(); - Grapher::RegisterDependencyProperties(); InitializeComponent(); DataTransferManager ^ dataTransferManager = DataTransferManager::GetForCurrentView(); @@ -219,7 +217,7 @@ void GraphingCalculator::OnDataRequested(DataTransferManager ^ sender, DataReque continue; } - auto color = equation->LineColor->Color; + auto color = equation->LineColor; hasEquations = true; expression = GraphingControl->ConvertToLinear(expression); @@ -380,12 +378,15 @@ void GraphingCalculator::GraphingControl_LosingFocus(UIElement ^ sender, LosingF } } -void GraphingCalculator::OnEquationKeyGraphFeaturesRequested(Object ^ sender, RoutedEventArgs ^ e) +void GraphingCalculator::OnEquationKeyGraphFeaturesRequested(Object ^ sender, EquationViewModel ^ equationViewModel) { - auto equationViewModel = static_cast(sender); - GraphingControl->AnalyzeEquation(equationViewModel->GraphEquation); - equationViewModel->PopulateKeyGraphFeatures(); - IsKeyGraphFeaturesVisible = true; + ViewModel->SetSelectedEquation(equationViewModel); + if (equationViewModel != nullptr) + { + auto keyGraphFeatureInfo = GraphingControl->AnalyzeEquation(equationViewModel->GraphEquation); + equationViewModel->PopulateKeyGraphFeatures(keyGraphFeatureInfo); + IsKeyGraphFeaturesVisible = true; + } } void GraphingCalculator::OnKeyGraphFeaturesClosed(Object ^ sender, RoutedEventArgs ^ e) diff --git a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h index cdf5564e..54b45db1 100644 --- a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h +++ b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h @@ -24,7 +24,7 @@ public ref class GraphingCalculator sealed : public Windows::UI::Xaml::Data::INo COMMAND_FOR_METHOD(ZoomOutButtonPressed, GraphingCalculator::OnZoomOutCommand); COMMAND_FOR_METHOD(ZoomInButtonPressed, GraphingCalculator::OnZoomInCommand); COMMAND_FOR_METHOD(ZoomResetButtonPressed, GraphingCalculator::OnZoomResetCommand); - OBSERVABLE_PROPERTY_RW(bool, IsKeyGraphFeaturesVisible); + OBSERVABLE_PROPERTY_R(bool, IsKeyGraphFeaturesVisible); DEPENDENCY_PROPERTY(bool, IsSmallState); property CalculatorApp::ViewModel::GraphingCalculatorViewModel^ ViewModel @@ -64,7 +64,7 @@ public ref class GraphingCalculator sealed : public Windows::UI::Xaml::Data::INo 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, Windows::UI::Xaml::RoutedEventArgs ^ e); + 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); diff --git a/src/Calculator/Views/GraphingCalculator/KeyGraphFeaturesPanel.xaml b/src/Calculator/Views/GraphingCalculator/KeyGraphFeaturesPanel.xaml index 0c3ffa75..11d18201 100644 --- a/src/Calculator/Views/GraphingCalculator/KeyGraphFeaturesPanel.xaml +++ b/src/Calculator/Views/GraphingCalculator/KeyGraphFeaturesPanel.xaml @@ -67,7 +67,7 @@ + Color="{TemplateBinding EquationColor}"/> @@ -330,8 +330,11 @@ DataContext="{x:Bind ViewModel, Mode=OneWay}" EquationButtonClicked="EquationButtonClicked" EquationButtonContentIndex="{x:Bind ViewModel.FunctionLabelIndex, Mode=OneWay}" - EquationColor="{x:Bind ViewModel.LineColor, Mode=OneWay}" - Loaded="EquationInputTextBox_Loaded"/> + Loaded="EquationInputTextBox_Loaded"> + + + + ::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_maximaProperty) - { - s_maximaProperty = DependencyProperty::Register( - EquationProperties::Maxima, - IObservableVector::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_domainProperty) - { - s_domainProperty = DependencyProperty::Register( - EquationProperties::Domain, - String::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_rangeProperty) - { - s_rangeProperty = DependencyProperty::Register( - EquationProperties::Range, - String::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_inflectionPointsProperty) - { - s_inflectionPointsProperty = DependencyProperty::Register( - EquationProperties::InflectionPoints, - IObservableVector::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_monotonicityProperty) - { - s_monotonicityProperty = DependencyProperty::Register( - EquationProperties::Monotonicity, - IObservableMap::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_verticalAsymptotesProperty) - { - s_verticalAsymptotesProperty = DependencyProperty::Register( - EquationProperties::VerticalAsymptotes, - IObservableVector::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_horizontalAsymptotesProperty) - { - s_horizontalAsymptotesProperty = DependencyProperty::Register( - EquationProperties::HorizontalAsymptotes, - IObservableVector::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_obliqueAsymptotesProperty) - { - s_obliqueAsymptotesProperty = DependencyProperty::Register( - EquationProperties::ObliqueAsymptotes, - IObservableVector::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - if (!s_tooComplexFeaturesProperty) - { - s_tooComplexFeaturesProperty = DependencyProperty::Register( - EquationProperties::TooComplexFeatures, - int ::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - - if (!s_analysisErrorProperty) - { - s_analysisErrorProperty = DependencyProperty::Register( - EquationProperties::AnalysisError, - int ::typeid, - Equation::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged))); - } - } - - void Equation::OnCustomDependencyPropertyChanged(DependencyObject ^ obj, DependencyPropertyChangedEventArgs ^ args) - { - if (auto eq = static_cast(obj)) - { - String ^ propertyName = nullptr; - if (args->Property == s_expressionProperty) - { - propertyName = EquationProperties::Expression; - } - else if (args->Property == s_lineColorProperty) - { - propertyName = EquationProperties::LineColor; - } - else if (args->Property == s_isLineEnabledProperty) - { - propertyName = EquationProperties::IsLineEnabled; - } - else if (args->Property == s_xInterceptProperty) - { - propertyName = EquationProperties::XIntercept; - } - else if (args->Property == s_yInterceptProperty) - { - propertyName = EquationProperties::YIntercept; - } - else if (args->Property == s_parityProperty) - { - propertyName = EquationProperties::Parity; - } - else if (args->Property == s_periodicityDirectionProperty) - { - propertyName = EquationProperties::PeriodicityDirection; - } - else if (args->Property == s_periodicityExpressionProperty) - { - propertyName = EquationProperties::PeriodicityExpression; - } - else if (args->Property == s_minimaProperty) - { - propertyName = EquationProperties::Minima; - } - else if (args->Property == s_maximaProperty) - { - propertyName = EquationProperties::Maxima; - } - else if (args->Property == s_domainProperty) - { - propertyName = EquationProperties::Domain; - } - else if (args->Property == s_rangeProperty) - { - propertyName = EquationProperties::Range; - } - else if (args->Property == s_inflectionPointsProperty) - { - propertyName = EquationProperties::InflectionPoints; - } - else if (args->Property == s_monotonicityProperty) - { - propertyName = EquationProperties::Monotonicity; - } - else if (args->Property == s_verticalAsymptotesProperty) - { - propertyName = EquationProperties::VerticalAsymptotes; - } - else if (args->Property == s_horizontalAsymptotesProperty) - { - propertyName = EquationProperties::HorizontalAsymptotes; - } - else if (args->Property == s_obliqueAsymptotesProperty) - { - propertyName = EquationProperties::ObliqueAsymptotes; - } - else if (args->Property == s_tooComplexFeaturesProperty) - { - propertyName = EquationProperties::TooComplexFeatures; - } - else if (args->Property == s_analysisErrorProperty) - { - propertyName = EquationProperties::AnalysisError; - } - else if (args->Property == s_hasGraphErrorProperty) - { - propertyName = EquationProperties::HasGraphError; - } - else if (args->Property == s_isValidatedProperty) - { - propertyName = EquationProperties::IsValidated; - } - - eq->PropertyChanged(eq, propertyName); - } - } - - wstring Equation::GetRequest() - { - wstringstream ss{}; - ss << GetRequestHeader() << GetExpression() << GetLineColor() << L""; - - return ss.str(); - } - - wstring Equation::GetRequestHeader() - { - 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">><") != wstring::npos || expr.find(L"><<") != wstring::npos || expr.find(L">≥<") != wstring::npos - || expr.find(L">≤<") != wstring::npos) - { - return L"plotIneq2D"s; - } - else if (expr.find(L">=<") != wstring::npos) - { - return L"plotEq2d"s; - } - else - { - return L"plot2d"s; - } - } - - 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; - } - - wstring Equation::GetLineColor() - { - return L""s; - } - - bool Equation::IsGraphableEquation() - { - return !Expression->IsEmpty() && IsLineEnabled && !HasGraphError; - } -} diff --git a/src/GraphControl/Control/Equation.h b/src/GraphControl/Control/Equation.h deleted file mode 100644 index 2fd32bde..00000000 --- a/src/GraphControl/Control/Equation.h +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace GraphControl -{ - namespace EquationProperties - { - extern Platform::String ^ Expression; - extern Platform::String ^ LineColor; - extern Platform::String ^ IsLineEnabled; - } - - ref class Equation; - delegate void PropertyChangedEventHandler(Equation ^ sender, Platform::String ^ propertyName); - - [Windows::UI::Xaml::Data::Bindable] public ref class Equation sealed : public Windows::UI::Xaml::DependencyObject - { - public: - - Equation() - { - } - - static void RegisterDependencyProperties(); - -#pragma region Platform::String ^ Expression DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ ExpressionProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_expressionProperty; - } - } - property Platform::String^ Expression - { - Platform::String^ get() - { - return static_cast(GetValue(s_expressionProperty)); - } - void set(Platform::String^ value) - { - SetValue(s_expressionProperty, value); - } - } -#pragma endregion - -#pragma region Windows::UI::Xaml::Media::SolidColorBrush ^ LineColor DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ LineColorProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_lineColorProperty; - } - } - property Windows::UI::Xaml::Media::SolidColorBrush^ LineColor - { - Windows::UI::Xaml::Media::SolidColorBrush^ get() - { - return static_cast(GetValue(s_lineColorProperty)); - } - void set(Windows::UI::Xaml::Media::SolidColorBrush^ value) - { - if (value == nullptr || LineColor == nullptr || (value->Color.A != LineColor->Color.A) || (value->Color.R != LineColor->Color.R) - || (value->Color.G != LineColor->Color.G) || (value->Color.B != LineColor->Color.B)) - SetValue(s_lineColorProperty, value); - } - } -#pragma endregion - -#pragma region bool IsLineEnabled DependencyProperty - static property Windows::UI::Xaml::DependencyProperty ^ IsLineEnabledProperty - { - Windows::UI::Xaml::DependencyProperty ^ get() - { - return s_isLineEnabledProperty; - } - } - property bool IsLineEnabled - { - bool get() - { - return static_cast(GetValue(s_isLineEnabledProperty)); - } - void set(bool value) - { - SetValue(s_isLineEnabledProperty, value); - } - } - -#pragma endregion - -#pragma region bool HasGraphError DependencyProperty - static property Windows::UI::Xaml::DependencyProperty ^ HasGraphErrorProperty { Windows::UI::Xaml::DependencyProperty ^ get() { return s_hasGraphErrorProperty; } } - - property bool HasGraphError - { - bool get() - { - return static_cast(GetValue(s_hasGraphErrorProperty)); - } - - internal: - void set(bool value) - { - SetValue(s_hasGraphErrorProperty, value); - } - } - -#pragma endregion - -#pragma region bool IsValidated DependencyProperty - static property Windows::UI::Xaml::DependencyProperty ^ IsValidatedProperty { Windows::UI::Xaml::DependencyProperty ^ get() { return s_isValidatedProperty; } } - - property bool IsValidated - { - bool get() - { - return static_cast(GetValue(s_isValidatedProperty)); - } - - internal: - void set(bool value) - { - SetValue(s_isValidatedProperty, value); - } - } - -#pragma endregion - -#pragma region Key Graph Features - - -#pragma region Platform::String ^ XIntercept DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ XInterceptProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_xInterceptProperty; - } - } - property Platform::String^ XIntercept - { - Platform::String^ get() - { - return static_cast(GetValue(s_xInterceptProperty)); - } - internal: void set(Platform::String^ value) - { - SetValue(s_xInterceptProperty, value); - } - } -#pragma endregion - -#pragma region Platform::String ^ YIntercept DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ YInterceptProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_yInterceptProperty; - } - } - property Platform::String^ YIntercept - { - Platform::String^ get() - { - return static_cast(GetValue(s_yInterceptProperty)); - } - internal: void set(Platform::String^ value) - { - SetValue(s_yInterceptProperty, value); - } - } -#pragma endregion - -#pragma region int Parity DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ ParityProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_parityProperty; - } - } - property int Parity - { - int get() - { - return static_cast(GetValue(s_parityProperty)); - } - internal: void set(int value) - { - SetValue(s_parityProperty, value); - } - } -#pragma endregion - -#pragma region int Periodicity DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ PeriodicityDirectionProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_periodicityDirectionProperty; - } - } - property int PeriodicityDirection - { - int get() - { - return static_cast(GetValue(s_periodicityDirectionProperty)); - } - internal: void set(int value) - { - SetValue(s_periodicityDirectionProperty, value); - } - } -#pragma endregion - - #pragma region Platform::String ^ PeriodicityExpression DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ PeriodicityExpressionProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_periodicityExpressionProperty; - } - } - property Platform::String ^ PeriodicityExpression - { - Platform::String ^ get() - { - return static_cast(GetValue(s_periodicityExpressionProperty)); - } - internal: void set(Platform::String ^ value) - { - SetValue(s_periodicityExpressionProperty, value); - } - } -#pragma endregion - -#pragma region Windows::Foundation::Collections::IVector < Platform::String ^ > ^ Minima DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ MinimaProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_minimaProperty; - } - } - property Windows::Foundation::Collections::IVector ^ Minima - { - Windows::Foundation::Collections::IVector ^ get() - { - return static_cast ^>(GetValue(s_minimaProperty)); - } - internal: void set(Windows::Foundation::Collections::IVector ^ value) - { - SetValue(s_minimaProperty, value); - } - } -#pragma endregion - -#pragma region Windows::Foundation::Collections::IVector < Platform::String ^ > ^ Maxima DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ MaximaProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_maximaProperty; - } - } - property Windows::Foundation::Collections::IVector ^ Maxima - { - Windows::Foundation::Collections::IVector ^ get() - { - return static_cast ^>(GetValue(s_maximaProperty)); - } - internal: void set(Windows::Foundation::Collections::IVector ^ value) - { - SetValue(s_maximaProperty, value); - } - } -#pragma endregion - -#pragma region Platform::String ^ Domain DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ DomainProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_domainProperty; - } - } - property Platform::String^ Domain - { - Platform::String^ get() - { - return static_cast(GetValue(s_domainProperty)); - } - internal: void set(Platform::String^ value) - { - SetValue(s_domainProperty, value); - } - } -#pragma endregion - -#pragma region Platform::String ^ Range DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ RangeProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_rangeProperty; - } - } - property Platform::String^ Range - { - Platform::String^ get() - { - return static_cast(GetValue(s_rangeProperty)); - } - internal: void set(Platform::String^ value) - { - SetValue(s_rangeProperty, value); - } - } -#pragma endregion - -#pragma region Windows::Foundation::Collections::IVector < Platform::String ^ > ^ InflectionPoints DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ InflectionPointsProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_inflectionPointsProperty; - } - } - property Windows::Foundation::Collections::IVector ^ InflectionPoints - { - Windows::Foundation::Collections::IVector ^ get() - { - return static_cast ^>(GetValue(s_inflectionPointsProperty)); - } - internal: void set(Windows::Foundation::Collections::IVector ^ value) - { - SetValue(s_inflectionPointsProperty, value); - } - } -#pragma endregion - -#pragma region Windows::Foundation::Collections::IObservableMap < Platform::String ^, Platform::String ^ > ^ Monotonicity DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ MonotonicityProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_monotonicityProperty; - } - } - property Windows::Foundation::Collections::IObservableMap ^ Monotonicity - { - Windows::Foundation::Collections::IObservableMap ^ get() - { - return static_cast ^>(GetValue(s_monotonicityProperty)); - } - internal: void set(Windows::Foundation::Collections::IObservableMap ^ value) - { - SetValue(s_monotonicityProperty, value); - } - } -#pragma endregion - -#pragma region Windows::Foundation::Collections::IVector < Platform::String ^ > ^ VerticalAsymptotes DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ VerticalAsymptotesProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_verticalAsymptotesProperty; - } - } - property Windows::Foundation::Collections::IVector ^ VerticalAsymptotes - { - Windows::Foundation::Collections::IVector ^ get() - { - return static_cast ^>(GetValue(s_verticalAsymptotesProperty)); - } - internal: void set(Windows::Foundation::Collections::IVector ^ value) - { - SetValue(s_verticalAsymptotesProperty, value); - } - } -#pragma endregion - -#pragma region Windows::Foundation::Collections::IVector < Platform::String ^ > ^ HorizontalAsymptotes DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ HorizontalAsymptotesProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_horizontalAsymptotesProperty; - } - } - property Windows::Foundation::Collections::IVector ^ HorizontalAsymptotes - { - Windows::Foundation::Collections::IVector ^ get() - { - return static_cast ^>(GetValue(s_horizontalAsymptotesProperty)); - } - internal: void set(Windows::Foundation::Collections::IVector ^ value) - { - SetValue(s_horizontalAsymptotesProperty, value); - } - } -#pragma endregion - -#pragma region Windows::Foundation::Collections::IVector < Platform::String ^ > ^ ObliqueAsymptotes DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ ObliqueAsymptotesProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_obliqueAsymptotesProperty; - } - } - property Windows::Foundation::Collections::IVector ^ ObliqueAsymptotes - { - Windows::Foundation::Collections::IVector ^ get() - { - return static_cast ^>(GetValue(s_obliqueAsymptotesProperty)); - } - internal: void set(Windows::Foundation::Collections::IVector ^ value) - { - SetValue(s_obliqueAsymptotesProperty, value); - } - } -#pragma endregion - -#pragma region int TooComplexFeatures DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ TooComplexFeaturesProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_tooComplexFeaturesProperty; - } - } - property int TooComplexFeatures - { - int get() - { - return static_cast(GetValue(s_tooComplexFeaturesProperty)); - } - internal: void set(int value) - { - SetValue(s_tooComplexFeaturesProperty, value); - } - } -#pragma endregion - -#pragma region int AnalysisError DependencyProperty - static property Windows::UI::Xaml::DependencyProperty ^ AnalysisErrorProperty - { - Windows::UI::Xaml::DependencyProperty ^ get() - { - return s_analysisErrorProperty; - } - } - property int AnalysisError - { - int get() - { - return static_cast(GetValue(s_analysisErrorProperty)); - } - internal: void set(int value) - { - SetValue(s_analysisErrorProperty, value); - } - } - -#pragma endregion - - internal : event PropertyChangedEventHandler ^ PropertyChanged; - - std::wstring GetRequest(); - bool IsGraphableEquation(); - - private: - static void OnCustomDependencyPropertyChanged(Windows::UI::Xaml::DependencyObject ^ obj, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args); - - std::wstring GetRequestHeader(); - std::wstring GetExpression(); - std::wstring GetLineColor(); - - private: - static Windows::UI::Xaml::DependencyProperty ^ s_expressionProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_lineColorProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_isLineEnabledProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_hasGraphErrorProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_isValidatedProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_xInterceptProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_yInterceptProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_parityProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_periodicityDirectionProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_periodicityExpressionProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_minimaProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_maximaProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_domainProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_rangeProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_inflectionPointsProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_monotonicityProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_verticalAsymptotesProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_horizontalAsymptotesProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_obliqueAsymptotesProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_tooComplexFeaturesProperty; - static Windows::UI::Xaml::DependencyProperty ^ s_analysisErrorProperty; - }; -} diff --git a/src/GraphControl/Control/Grapher.cpp b/src/GraphControl/Control/Grapher.cpp index 764bed93..78668859 100644 --- a/src/GraphControl/Control/Grapher.cpp +++ b/src/GraphControl/Control/Grapher.cpp @@ -24,16 +24,17 @@ 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); namespace { constexpr auto s_defaultStyleKey = L"GraphControl.Grapher"; constexpr auto s_templateKey_SwapChainPanel = L"GraphSurface"; - constexpr auto s_propertyName_Equations = L"Equations"; - constexpr auto s_propertyName_Variables = L"Variables"; - constexpr auto s_propertyName_ForceProportionalAxes = L"ForceProportionalAxes"; - constexpr auto s_X = L"x"; constexpr auto s_Y = L"y"; constexpr auto s_defaultFormatType = FormatType::MathML; @@ -52,24 +53,19 @@ namespace namespace GraphControl { - DependencyProperty ^ Grapher::s_equationsProperty; - DependencyProperty ^ Grapher::s_variablesProperty; - DependencyProperty ^ Grapher::s_forceProportionalAxesTemplateProperty; - 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->SetValue(EquationsProperty, ref new EquationCollection()); - this->SetValue(VariablesProperty, ref new Map()); - this->Loaded += ref new RoutedEventHandler(this, &Grapher::OnLoaded); this->Unloaded += ref new RoutedEventHandler(this, &Grapher::OnUnloaded); @@ -147,52 +143,6 @@ namespace GraphControl TryUpdateGraph(); } - void Grapher::RegisterDependencyProperties() - { - if (!s_equationsProperty) - { - s_equationsProperty = DependencyProperty::Register( - StringReference(s_propertyName_Equations), - EquationCollection::typeid, - Grapher::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged))); - } - - if (!s_variablesProperty) - { - s_variablesProperty = DependencyProperty::Register( - StringReference(s_propertyName_Variables), - IObservableMap::typeid, - Grapher::typeid, - ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged))); - } - - if (!s_forceProportionalAxesTemplateProperty) - { - s_forceProportionalAxesTemplateProperty = DependencyProperty::Register( - StringReference(s_propertyName_ForceProportionalAxes), - bool ::typeid, - Grapher::typeid, - ref new PropertyMetadata(true, ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged))); - } - } - - void Grapher::OnCustomDependencyPropertyChanged(DependencyObject ^ obj, DependencyPropertyChangedEventArgs ^ args) - { - auto self = static_cast(obj); - if (self) - { - if (args->Property == EquationsProperty) - { - self->OnEquationsChanged(args); - } - else if (args->Property == ForceProportionalAxesTemplateProperty) - { - self->OnForceProportionalAxesChanged(args); - } - } - } - void Grapher::OnDependencyPropertyChanged(DependencyObject ^ obj, DependencyProperty ^ p) { if (p == SolidColorBrush::ColorProperty) @@ -202,30 +152,36 @@ namespace GraphControl } } - void Grapher::OnEquationsChanged(DependencyPropertyChangedEventArgs ^ args) + void Grapher::OnEquationsPropertyChanged(EquationCollection ^ oldValue, EquationCollection ^ newValue) { - if (auto older = static_cast(args->OldValue)) + if (oldValue != nullptr) { if (m_tokenEquationChanged.Value != 0) { - older->EquationChanged -= m_tokenEquationChanged; + oldValue->EquationChanged -= m_tokenEquationChanged; m_tokenEquationChanged.Value = 0; } if (m_tokenEquationStyleChanged.Value != 0) { - older->EquationStyleChanged -= m_tokenEquationStyleChanged; + oldValue->EquationStyleChanged -= m_tokenEquationStyleChanged; m_tokenEquationStyleChanged.Value = 0; } + + if (m_tokenEquationLineEnabledChanged.Value != 0) + { + oldValue->EquationLineEnabledChanged -= m_tokenEquationLineEnabledChanged; + m_tokenEquationLineEnabledChanged.Value = 0; + } } - if (auto newer = static_cast(args->NewValue)) + if (newValue != nullptr) { - m_tokenEquationChanged = newer->EquationChanged += ref new EquationChangedEventHandler(this, &Grapher::OnEquationChanged); + m_tokenEquationChanged = newValue->EquationChanged += ref new EquationChangedEventHandler(this, &Grapher::OnEquationChanged); - m_tokenEquationStyleChanged = newer->EquationStyleChanged += ref new EquationChangedEventHandler(this, &Grapher::OnEquationStyleChanged); + m_tokenEquationStyleChanged = newValue->EquationStyleChanged += ref new EquationChangedEventHandler(this, &Grapher::OnEquationStyleChanged); - m_tokenEquationLineEnabledChanged = newer->EquationLineEnabledChanged += + m_tokenEquationLineEnabledChanged = newValue->EquationLineEnabledChanged += ref new EquationChangedEventHandler(this, &Grapher::OnEquationLineEnabledChanged); } @@ -268,8 +224,9 @@ namespace GraphControl PlotGraph(); } - void Grapher::AnalyzeEquation(Equation ^ equation) + KeyGraphFeaturesInfo ^ Grapher::AnalyzeEquation(Equation ^ equation) { + auto result = ref new KeyGraphFeaturesInfo(); if (auto graph = GetGraph(equation)) { if (auto analyzer = graph->GetAnalyzer()) @@ -281,38 +238,17 @@ namespace GraphControl (Graphing::Analyzer::NativeAnalysisType)Graphing::Analyzer::PerformAnalysisType::PerformAnalysisType_All)) { Graphing::IGraphFunctionAnalysisData functionAnalysisData = m_solver->Analyze(analyzer.get()); - { - equation->XIntercept = ref new String(functionAnalysisData.Zeros.c_str()); - equation->YIntercept = ref new String(functionAnalysisData.YIntercept.c_str()); - equation->Domain = ref new String(functionAnalysisData.Domain.c_str()); - equation->Range = ref new String(functionAnalysisData.Range.c_str()); - equation->Parity = functionAnalysisData.Parity; - equation->PeriodicityDirection = functionAnalysisData.PeriodicityDirection; - equation->PeriodicityExpression = ref new String(functionAnalysisData.PeriodicityExpression.c_str()); - equation->Minima = ConvertWStringVector(functionAnalysisData.Minima); - equation->Maxima = ConvertWStringVector(functionAnalysisData.Maxima); - equation->InflectionPoints = ConvertWStringVector(functionAnalysisData.InflectionPoints); - equation->Monotonicity = ConvertWStringIntMap(functionAnalysisData.MonotoneIntervals); - equation->VerticalAsymptotes = ConvertWStringVector(functionAnalysisData.VerticalAsymptotes); - equation->HorizontalAsymptotes = ConvertWStringVector(functionAnalysisData.HorizontalAsymptotes); - equation->ObliqueAsymptotes = ConvertWStringVector(functionAnalysisData.ObliqueAsymptotes); - equation->TooComplexFeatures = functionAnalysisData.TooComplexFeatures; - equation->AnalysisError = CalculatorApp::AnalysisErrorType::NoError; - - return; - } + return KeyGraphFeaturesInfo::Create(functionAnalysisData); } } else { - equation->AnalysisError = CalculatorApp::AnalysisErrorType::AnalysisNotSupported; - - return; + return KeyGraphFeaturesInfo::Create(CalculatorApp::AnalysisErrorType::AnalysisNotSupported); } } } - equation->AnalysisError = CalculatorApp::AnalysisErrorType::AnalysisCouldNotBePerformed; + return KeyGraphFeaturesInfo::Create(CalculatorApp::AnalysisErrorType::AnalysisCouldNotBePerformed); } void Grapher::PlotGraph() @@ -371,7 +307,7 @@ namespace GraphControl ss << L","; } - ss << eq->GetRequest(); + ss << eq->GetRequest()->Data(); } ss << s_getGraphClosingTags; @@ -463,13 +399,13 @@ namespace GraphControl } } - shared_ptr Grapher::GetGraph(GraphControl::Equation ^ equation) + shared_ptr Grapher::GetGraph(Equation ^ equation) { std::shared_ptr graph = m_solver->CreateGrapher(); wstringstream ss{}; ss << s_getGraphOpeningTags; - ss << equation->GetRequest(); + ss << equation->GetRequest()->Data(); ss << s_getGraphClosingTags; wstring request = ss.str(); @@ -485,30 +421,6 @@ namespace GraphControl return nullptr; } - IObservableVector ^ Grapher::ConvertWStringVector(vector inVector) - { - Vector ^ outVector = ref new Vector(); - - for (auto v : inVector) - { - outVector->Append(ref new String(v.c_str())); - } - - return outVector; - } - - IObservableMap ^ Grapher::ConvertWStringIntMap(map inMap) - { - Map ^ outMap = ref new Map(); - ; - for (auto m : inMap) - { - outMap->Insert(ref new String(m.first.c_str()), m.second.ToString()); - } - - return outMap; - } - void Grapher::UpdateVariables() { auto updatedVariables = ref new Map(); @@ -573,7 +485,7 @@ namespace GraphControl graphColors.reserve(validEqs.size()); for (Equation ^ eq : validEqs) { - auto lineColor = eq->LineColor->Color; + auto lineColor = eq->LineColor; graphColors.emplace_back(lineColor.R, lineColor.G, lineColor.B, lineColor.A); } options.SetGraphColors(graphColors); @@ -595,7 +507,7 @@ namespace GraphControl return validEqs; } - void Grapher::OnForceProportionalAxesChanged(DependencyPropertyChangedEventArgs ^ args) + void Grapher::OnForceProportionalAxesPropertyChanged(bool /*oldValue*/, bool /*newValue*/) { TryUpdateGraph(); } diff --git a/src/GraphControl/Control/Grapher.h b/src/GraphControl/Control/Grapher.h index ebe43eb4..f09f3e79 100644 --- a/src/GraphControl/Control/Grapher.h +++ b/src/GraphControl/Control/Grapher.h @@ -4,11 +4,14 @@ #pragma once #include "DirectX/RenderMain.h" -#include "Equation.h" -#include "EquationCollection.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 namespace GraphControl { @@ -28,71 +31,13 @@ public public: Grapher(); - static void RegisterDependencyProperties(); - -#pragma region GraphControl::EquationCollection ^ Equations DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ EquationsProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_equationsProperty; - } - } - - property GraphControl::EquationCollection^ Equations - { - GraphControl::EquationCollection^ get() - { - return static_cast< GraphControl::EquationCollection^ >(GetValue(s_equationsProperty)); - } - } -#pragma endregion - -#pragma region Windows::Foundation::Collections::IObservableMap < Platform::String ^, double> ^ Variables DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ VariablesProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_variablesProperty; - } - } - - property Windows::Foundation::Collections::IObservableMap^ Variables - { - Windows::Foundation::Collections::IObservableMap^ get() - { - return static_cast^>(GetValue(s_variablesProperty)); - } - - void set(Windows::Foundation::Collections::IObservableMap^ value) - { - SetValue(s_variablesProperty, value); - } - } -#pragma endregion - -#pragma region Windows::UI::Xaml::DataTemplate ^ ForceProportionalAxes DependencyProperty - static property Windows::UI::Xaml::DependencyProperty^ ForceProportionalAxesTemplateProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_forceProportionalAxesTemplateProperty; - } - } - - property bool ForceProportionalAxes - { - bool get() - { - return static_cast(GetValue(s_forceProportionalAxesTemplateProperty)); - } - void set(bool value) - { - SetValue(s_forceProportionalAxesTemplateProperty, value); - } - } -#pragma endregion - + DEPENDENCY_PROPERTY_OWNER(Grapher); + DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(bool, ForceProportionalAxes, true); + DEPENDENCY_PROPERTY_WITH_DEFAULT( + SINGLE_ARG(Windows::Foundation::Collections::IObservableMap ^), + Variables, + SINGLE_ARG(ref new Platform::Collections::Map())); + DEPENDENCY_PROPERTY_R_WITH_DEFAULT_AND_CALLBACK(GraphControl::EquationCollection ^, Equations, nullptr); // Pass active tracing turned on or off down to the renderer property bool ActiveTracing @@ -153,7 +98,7 @@ public void SetVariable(Platform::String ^ variableName, double newValue); Platform::String ^ ConvertToLinear(Platform::String ^ mmlString); void PlotGraph(); - void AnalyzeEquation(GraphControl::Equation ^ equation); + GraphControl::KeyGraphFeaturesInfo ^ AnalyzeEquation(GraphControl::Equation ^ equation); protected: #pragma region Control Overrides @@ -173,13 +118,13 @@ public void OnLoaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ args); void OnUnloaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ args); - static void OnCustomDependencyPropertyChanged(Windows::UI::Xaml::DependencyObject ^ obj, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args); + void OnForceProportionalAxesPropertyChanged(bool oldValue, bool newValue); + void OnEquationsPropertyChanged(EquationCollection ^ oldValue, EquationCollection ^ newValue); void OnDependencyPropertyChanged(Windows::UI::Xaml::DependencyObject ^ obj, Windows::UI::Xaml::DependencyProperty ^ p); - void OnEquationsChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args); - void OnEquationChanged(GraphControl::Equation ^ equation); - void OnEquationStyleChanged(GraphControl::Equation ^ equation); - void OnEquationLineEnabledChanged(GraphControl::Equation ^ equation); + void OnEquationChanged(Equation ^ equation); + void OnEquationStyleChanged(Equation ^ equation); + void OnEquationLineEnabledChanged(Equation ^ equation); bool TryUpdateGraph(); void TryPlotGraph(bool shouldRetry); void UpdateGraphOptions(Graphing::IGraphingOptions& options, const std::vector& validEqs); @@ -188,8 +133,6 @@ public std::shared_ptr GetGraph(GraphControl::Equation ^ equation); void UpdateVariables(); - void OnForceProportionalAxesChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args); - void OnBackgroundColorChanged(const Windows::UI::Color& color); void ScaleRange(double centerX, double centerY, double scale); @@ -222,8 +165,6 @@ public Windows::Foundation::EventRegistrationToken m_tokenEquationChanged; Windows::Foundation::EventRegistrationToken m_tokenEquationLineEnabledChanged; - static Windows::UI::Xaml::DependencyProperty ^ s_forceProportionalAxesTemplateProperty; - Windows::Foundation::EventRegistrationToken m_tokenBackgroundColorChanged; const std::unique_ptr m_solver; diff --git a/src/GraphControl/GraphControl.vcxproj b/src/GraphControl/GraphControl.vcxproj index 251ee35f..b3db6881 100644 --- a/src/GraphControl/GraphControl.vcxproj +++ b/src/GraphControl/GraphControl.vcxproj @@ -284,24 +284,27 @@ - - + + + + - + + Create Create diff --git a/src/GraphControl/GraphControl.vcxproj.filters b/src/GraphControl/GraphControl.vcxproj.filters index c0669912..99bf6dd9 100644 --- a/src/GraphControl/GraphControl.vcxproj.filters +++ b/src/GraphControl/GraphControl.vcxproj.filters @@ -10,6 +10,9 @@ {e8d91a71-6933-4fd8-b333-421085d13896} + + {0f768477-7ceb-42c4-a32a-cb024320dbc3} + @@ -19,9 +22,6 @@ DirectX - - Control - Control @@ -31,6 +31,10 @@ DirectX + + + Models + @@ -44,12 +48,6 @@ DirectX - - Control - - - Control - Control @@ -59,6 +57,14 @@ DirectX + + + + Models + + + Models + @@ -71,5 +77,7 @@ + + \ No newline at end of file diff --git a/src/GraphControl/Models/Equation.cpp b/src/GraphControl/Models/Equation.cpp new file mode 100644 index 00000000..3ca9313a --- /dev/null +++ b/src/GraphControl/Models/Equation.cpp @@ -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">><") != wstring::npos || expr.find(L"><<") != wstring::npos || expr.find(L">≥<") != wstring::npos + || expr.find(L">≤<") != wstring::npos) + { + ss << L"plotIneq2D"s; + } + else if (expr.find(L">=<") != wstring::npos) + { + ss << L"plotEq2d"; + } + else + { + ss << L"plot2d"; + } + ss << GetExpression() << L""; + + 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; + } +} diff --git a/src/GraphControl/Models/Equation.h b/src/GraphControl/Models/Equation.h new file mode 100644 index 00000000..cc404b7e --- /dev/null +++ b/src/GraphControl/Models/Equation.h @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +#include "Utils.h" +#include + +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; + }; +} diff --git a/src/GraphControl/Control/EquationCollection.h b/src/GraphControl/Models/EquationCollection.h similarity index 77% rename from src/GraphControl/Control/EquationCollection.h rename to src/GraphControl/Models/EquationCollection.h index dc920c6a..d1e1ba18 100644 --- a/src/GraphControl/Control/EquationCollection.h +++ b/src/GraphControl/Models/EquationCollection.h @@ -38,7 +38,7 @@ public { m_vector->Append(value); m_tokens.emplace_back( - value->PropertyChanged += ref new GraphControl::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged)); + value->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged)); } virtual void Clear() @@ -75,7 +75,8 @@ public { m_vector->InsertAt(index, value); m_tokens.insert( - m_tokens.begin() + index, value->PropertyChanged += ref new PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged)); + m_tokens.begin() + index, + value->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged)); } virtual void RemoveAt(unsigned int index) @@ -110,7 +111,8 @@ public m_tokens.resize(size); for (auto i = 0u; i < size; i++) { - m_tokens[i] = items[i]->PropertyChanged += ref new PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged); + m_tokens[i] = items[i]->PropertyChanged += + ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged); } m_vector->ReplaceAll(items); @@ -121,7 +123,8 @@ public m_vector->GetAt(index)->PropertyChanged -= m_tokens[index]; m_vector->SetAt(index, value); - m_tokens[index] = value->PropertyChanged += ref new PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged); + m_tokens[index] = value->PropertyChanged += + ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged); } #pragma endregion @@ -151,19 +154,21 @@ public event EquationChangedEventHandler ^ EquationLineEnabledChanged; private: - void OnEquationPropertyChanged(GraphControl::Equation ^ sender, Platform::String ^ propertyName) + void OnEquationPropertyChanged(Object^ sender, Windows::UI::Xaml::Data::PropertyChangedEventArgs ^ args) { - if (propertyName == EquationProperties::LineColor) + auto equation = static_cast(sender); + auto propertyName = args->PropertyName; + if (propertyName == GraphControl::Equation::LineColorPropertyName) { - EquationStyleChanged(sender); + EquationStyleChanged(equation); } - else if (propertyName == EquationProperties::Expression) + else if (propertyName == GraphControl::Equation::ExpressionPropertyName) { - EquationChanged(sender); + EquationChanged(equation); } - else if (propertyName == EquationProperties::IsLineEnabled) + else if (propertyName == GraphControl::Equation::IsLineEnabledPropertyName) { - EquationLineEnabledChanged(sender); + EquationLineEnabledChanged(equation); } } diff --git a/src/GraphControl/Models/KeyGraphFeaturesInfo.cpp b/src/GraphControl/Models/KeyGraphFeaturesInfo.cpp new file mode 100644 index 00000000..c7552246 --- /dev/null +++ b/src/GraphControl/Models/KeyGraphFeaturesInfo.cpp @@ -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 ^ KeyGraphFeaturesInfo::ConvertWStringVector(vector inVector) +{ + auto outVector = ref new Vector(); + + for (auto v : inVector) + { + outVector->Append(ref new String(v.c_str())); + } + + return outVector; +} + +IObservableMap ^ KeyGraphFeaturesInfo::ConvertWStringIntMap(map inMap) +{ + Map ^ outMap = ref new Map(); + ; + 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; +} diff --git a/src/GraphControl/Models/KeyGraphFeaturesInfo.h b/src/GraphControl/Models/KeyGraphFeaturesInfo.h new file mode 100644 index 00000000..2d389a0e --- /dev/null +++ b/src/GraphControl/Models/KeyGraphFeaturesInfo.h @@ -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 ^, Minima); + PROPERTY_R(Windows::Foundation::Collections::IVector ^, Maxima); + PROPERTY_R(Platform::String ^, Domain); + PROPERTY_R(Platform::String ^, Range); + PROPERTY_R(Windows::Foundation::Collections::IVector ^, InflectionPoints); + PROPERTY_R(SINGLE_ARG(Windows::Foundation::Collections::IObservableMap ^), Monotonicity); + PROPERTY_R(Windows::Foundation::Collections::IVector ^, VerticalAsymptotes); + PROPERTY_R(Windows::Foundation::Collections::IVector ^, HorizontalAsymptotes); + PROPERTY_R(Windows::Foundation::Collections::IVector ^, 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 ^ ConvertWStringVector(std::vector inVector); + static Windows::Foundation::Collections:: + IObservableMap ^ ConvertWStringIntMap(std::map inMap); + }; +} diff --git a/src/GraphControl/Utils.h b/src/GraphControl/Utils.h new file mode 100644 index 00000000..e29e4bde --- /dev/null +++ b/src/GraphControl/Utils.h @@ -0,0 +1,588 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +// Utility macros to make Models easier to write +// generates a member variable called m_ + +#define SINGLE_ARG(...) __VA_ARGS__ + +#define PROPERTY_R(t, n) \ + property t n \ + { \ + t get() \ + { \ + return m_##n; \ + } \ + \ + private: \ + void set(t value) \ + { \ + m_##n = value; \ + } \ + } \ + \ +private: \ + t m_##n; \ + \ +public: + +#define PROPERTY_RW(t, n) \ + property t n \ + { \ + t get() \ + { \ + return m_##n; \ + } \ + void set(t value) \ + { \ + m_##n = value; \ + } \ + } \ + \ +private: \ + t m_##n; \ + \ +public: + +#define OBSERVABLE_PROPERTY_R(t, n) \ + property t n \ + { \ + t get() \ + { \ + return m_##n; \ + } \ + \ + private: \ + void set(t value) \ + { \ + if (m_##n != value) \ + { \ + m_##n = value; \ + RaisePropertyChanged(L#n); \ + } \ + } \ + } \ + \ +private: \ + t m_##n; \ + \ +public: + +#define OBSERVABLE_PROPERTY_RW(t, n) \ + property t n \ + { \ + t get() \ + { \ + return m_##n; \ + } \ + void set(t value) \ + { \ + if (m_##n != value) \ + { \ + m_##n = value; \ + RaisePropertyChanged(L#n); \ + } \ + } \ + } \ + \ +private: \ + t m_##n; \ + \ +public: + +#define OBSERVABLE_NAMED_PROPERTY_R(t, n) \ + OBSERVABLE_PROPERTY_R(t, n) \ + internal: \ + static property Platform::String ^ n##PropertyName \ + { \ + Platform::String ^ get() { return Platform::StringReference(L#n); } \ + } \ + \ +public: + +#define OBSERVABLE_NAMED_PROPERTY_RW(t, n) \ + OBSERVABLE_PROPERTY_RW(t, n) \ + internal: \ + static property Platform::String ^ n##PropertyName \ + { \ + Platform::String ^ get() { return Platform::StringReference(L#n); } \ + } \ + \ +public: + +#define OBSERVABLE_PROPERTY_FIELD(n) m_##n + +// This variant of the observable object is for objects that don't want to react to property changes +#ifndef UNIT_TESTS +#define OBSERVABLE_OBJECT() \ + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; \ + internal: \ + void RaisePropertyChanged(Platform::String ^ p) \ + { \ + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(p)); \ + } \ + \ +public: +#else +#define OBSERVABLE_OBJECT() \ + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; \ + internal: \ + void RaisePropertyChanged(Platform::String ^ p) \ + { \ + } \ + \ +public: +#endif + +// The callback specified in the macro is a method in the class that will be called every time the object changes +// the callback is supposed to be have a single parameter of type Platform::String^ +#ifndef UNIT_TESTS +#define OBSERVABLE_OBJECT_CALLBACK(c) \ + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; \ + internal: \ + void RaisePropertyChanged(Platform::String ^ p) \ + { \ + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(p)); \ + c(p); \ + } \ + \ +public: +#else +#define OBSERVABLE_OBJECT_CALLBACK(c) \ + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; \ + internal: \ + void RaisePropertyChanged(Platform::String ^ p) \ + { \ + c(p); \ + } \ + \ +public: +#endif + +// The variable member generated by this macro should not be used in the class code, use the +// property getter instead. +#define COMMAND_FOR_METHOD(p, m) \ + property Windows::UI::Xaml::Input::ICommand^ p {\ + Windows::UI::Xaml::Input::ICommand^ get() {\ + if (!donotuse_##p) {\ + donotuse_##p = CalculatorApp::Common::MakeDelegate(this, &m);\ + } return donotuse_##p; }} private: Windows::UI::Xaml::Input::ICommand^ donotuse_##p; \ + \ +public: + +// Utilities for DependencyProperties +namespace Utils +{ + namespace Details + { + template + struct IsRefClass + { + static const bool value = __is_ref_class(T); + }; + + template + struct RemoveHat + { + typedef T type; + }; + + template + struct RemoveHat + { + typedef T type; + }; + + template + typename std::enable_if::value, T ^>::type MakeDefault() + { + return nullptr; + } + + template + typename std::enable_if::value, T>::type MakeDefault() + { + return T(); + } + + // There's a bug in Xaml in which custom enums are not recognized by the property system/binding + // therefore this template will determine that for enums the type to use to register the + // DependencyProperty is to be Object, for everything else it will use the type + // NOTE: If we are to find more types in which this is broken this template + // will be specialized for those types to return Object + template + struct TypeToUseForDependencyProperty + { + typedef typename std::conditional::value, Platform::Object, T>::type type; + }; + } + + const wchar_t LRE = 0x202a; // Left-to-Right Embedding + const wchar_t PDF = 0x202c; // Pop Directional Formatting + const wchar_t LRO = 0x202d; // Left-to-Right Override + + // Regular DependencyProperty + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyProperty( + _In_ const wchar_t* const name, + _In_ Windows::UI::Xaml::PropertyMetadata^ metadata) + { + typedef typename Details::RemoveHat::type OwnerType; + typedef typename Details::RemoveHat::type ThisPropertyType; + typedef typename Details::TypeToUseForDependencyProperty::type ThisDependencyPropertyType; + + static_assert(Details::IsRefClass::value, "The owner of a DependencyProperty must be a ref class"); + + return Windows::UI::Xaml::DependencyProperty::Register( + Platform::StringReference(name), + ThisDependencyPropertyType::typeid, // Work around bugs in Xaml by using the filtered type + OwnerType::typeid, + metadata); + } + + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyProperty(_In_ const wchar_t* const name) + { + typedef typename Details::RemoveHat::type ThisPropertyType; + + return RegisterDependencyProperty( + name, + ref new Windows::UI::Xaml::PropertyMetadata(Details::MakeDefault())); + } + + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyProperty(_In_ const wchar_t* const name, TType defaultValue) + { + return RegisterDependencyProperty( + name, + ref new Windows::UI::Xaml::PropertyMetadata(defaultValue)); + } + + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyWithCallback( + _In_ wchar_t const * const name, + TCallback callback) + { + typedef typename Details::RemoveHat::type ThisPropertyType; + return RegisterDependencyProperty( + name, + ref new Windows::UI::Xaml::PropertyMetadata( + Details::MakeDefault(), + ref new Windows::UI::Xaml::PropertyChangedCallback(callback))); + } + + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyWithCallback( + _In_ wchar_t const * const name, + TType defaultValue, + TCallback callback) + { + typedef typename Details::RemoveHat::type ThisPropertyType; + return RegisterDependencyProperty( + name, + ref new Windows::UI::Xaml::PropertyMetadata( + defaultValue, + ref new Windows::UI::Xaml::PropertyChangedCallback(callback))); + } + + // Attached DependencyProperty + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttached( + _In_ const wchar_t* const name, + _In_ Windows::UI::Xaml::PropertyMetadata^ metadata) + { + typedef typename Details::RemoveHat::type OwnerType; + typedef typename Details::RemoveHat::type ThisPropertyType; + typedef typename Details::TypeToUseForDependencyProperty::type ThisDependencyPropertyType; + + static_assert(Details::IsRefClass::value, "The owner of a DependencyProperty must be a ref class"); + + return Windows::UI::Xaml::DependencyProperty::RegisterAttached( + Platform::StringReference(name), + ThisDependencyPropertyType::typeid, // Work around bugs in Xaml by using the filtered type + OwnerType::typeid, + metadata); + } + + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttached(_In_ const wchar_t* const name) + { + typedef typename Details::RemoveHat::type ThisPropertyType; + return RegisterDependencyPropertyAttached( + name, + ref new Windows::UI::Xaml::PropertyMetadata(Details::MakeDefault())); + } + + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttached(_In_ const wchar_t* const name, TType defaultValue) + { + return RegisterDependencyPropertyAttached( + name, + ref new Windows::UI::Xaml::PropertyMetadata(defaultValue)); + } + + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttachedWithCallback( + _In_ wchar_t const * const name, + TCallback callback) + { + typedef typename Details::RemoveHat::type ThisPropertyType; + return RegisterDependencyPropertyAttached( + name, + ref new Windows::UI::Xaml::PropertyMetadata( + Details::MakeDefault(), + ref new Windows::UI::Xaml::PropertyChangedCallback(callback))); + } + + template + Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttachedWithCallback( + _In_ wchar_t const * const name, + TType defaultValue, + TCallback callback) + { + typedef typename Details::RemoveHat::type ThisPropertyType; + return RegisterDependencyPropertyAttached( + name, + ref new Windows::UI::Xaml::PropertyMetadata( + defaultValue, + ref new Windows::UI::Xaml::PropertyChangedCallback(callback))); + } + + template + void Swap(T *ref1, T *ref2) + { + T temp = *ref1; + *ref1 = *ref2; + *ref2 = temp; + } +} + +// This goes into the header to define the property, in the public: section of the class +#define DEPENDENCY_PROPERTY_OWNER(owner) \ +private: \ + typedef owner DependencyPropertiesOwner; \ + \ +public: + +// Normal DependencyProperty +#define DEPENDENCY_PROPERTY(type, name) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyProperty(L#name); \ + } \ + \ +public: + +#define DEPENDENCY_PROPERTY_WITH_CALLBACK(type, name) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyWithCallback(L#name, &On##name##PropertyChangedImpl); \ + } \ + static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject ^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args) \ + { \ + auto self = safe_cast(sender); \ + self->On##name##PropertyChanged(safe_cast(args->OldValue), safe_cast(args->NewValue)); \ + } \ + \ +public: +#define DEPENDENCY_PROPERTY_WITH_DEFAULT(type, name, defaultValue) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyProperty(L#name, defaultValue); \ + } \ + \ +public: + +#define DEPENDENCY_PROPERTY_WITH_CALLBACK(type, name) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyWithCallback(L#name, &On##name##PropertyChangedImpl); \ + } \ + static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject ^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args) \ + { \ + auto self = safe_cast(sender); \ + self->On##name##PropertyChanged(safe_cast(args->OldValue), safe_cast(args->NewValue)); \ + } \ + \ +public: + +#define DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(type, name, defaultValue) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyWithCallback(L#name, defaultValue, &On##name##PropertyChangedImpl); \ + } \ + static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject ^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args) \ + { \ + auto self = safe_cast(sender); \ + self->On##name##PropertyChanged(safe_cast(args->OldValue), safe_cast(args->NewValue)); \ + } \ + \ +public: + +#define DEPENDENCY_PROPERTY_R_WITH_DEFAULT_AND_CALLBACK(type, name, defaultValue) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + private: void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyWithCallback(L#name, defaultValue, &On##name##PropertyChangedImpl); \ + } \ + static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject ^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args) \ + { \ + auto self = safe_cast(sender); \ + self->On##name##PropertyChanged(safe_cast(args->OldValue), safe_cast(args->NewValue)); \ + } \ + \ +public: + +// This goes into the cpp to initialize the static variable +#define DEPENDENCY_PROPERTY_INITIALIZATION(owner, name) Windows::UI::Xaml::DependencyProperty ^ owner::s_##name##Property = owner::Initialize##name##Property(); diff --git a/src/GraphControl/pch.h b/src/GraphControl/pch.h index faf9c722..28613840 100644 --- a/src/GraphControl/pch.h +++ b/src/GraphControl/pch.h @@ -18,6 +18,10 @@ #include #include #include +#include +#include +#include + // DirectX headers #include