Add variable editing (#581)

This commit is contained in:
Pepe Rivera 2019-07-24 11:23:33 -07:00 committed by GitHub
parent a418777f02
commit 46f11c7c72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 519 additions and 6 deletions

View File

@ -10,6 +10,7 @@
// Utility macros to make Models easier to write // Utility macros to make Models easier to write
// generates a member variable called m_<n> // generates a member variable called m_<n>
#define PROPERTY_R(t, n) \ #define PROPERTY_R(t, n) \
property t n \ property t n \
{ \ { \
@ -72,6 +73,25 @@ private:
\ \
public: 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) \ #define OBSERVABLE_PROPERTY_RW(t, n) \
property t n \ property t n \
{ \ { \

View File

@ -2,17 +2,37 @@
#include "GraphingCalculatorViewModel.h" #include "GraphingCalculatorViewModel.h"
using namespace CalculatorApp::ViewModel; using namespace CalculatorApp::ViewModel;
using namespace Platform;
using namespace Platform::Collections; using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml::Data;
namespace CalculatorApp::ViewModel namespace CalculatorApp::ViewModel
{ {
GraphingCalculatorViewModel::GraphingCalculatorViewModel() GraphingCalculatorViewModel::GraphingCalculatorViewModel()
: m_IsDecimalEnabled{ true } : m_IsDecimalEnabled{ true }
, m_Equations{ ref new Vector< EquationViewModel^ >() } , m_Equations{ ref new Vector< EquationViewModel^ >() }
, m_Variables{ ref new Vector< VariableViewModel^ >() }
{ {
} }
void GraphingCalculatorViewModel::OnButtonPressed(Object^ parameter) void GraphingCalculatorViewModel::OnButtonPressed(Object^ parameter)
{ {
} }
void GraphingCalculatorViewModel::UpdateVariables(IMap<String^, double>^ variables)
{
Variables->Clear();
for (auto var : variables)
{
auto variable = ref new VariableViewModel(var->Key, var->Value);
variable->VariableUpdated += ref new EventHandler<VariableChangedEventArgs>([this, variable](Object^ sender, VariableChangedEventArgs e)
{
VariableUpdated(variable, VariableChangedEventArgs{ e.variableName, e.newValue });
});
Variables->Append(variable);
}
}
} }

View File

@ -5,6 +5,62 @@
namespace CalculatorApp::ViewModel 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<VariableChangedEventArgs>^ 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] [Windows::UI::Xaml::Data::Bindable]
public ref class GraphingCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged public ref class GraphingCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{ {
@ -14,9 +70,13 @@ namespace CalculatorApp::ViewModel
OBSERVABLE_OBJECT(); OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_R(bool, IsDecimalEnabled); OBSERVABLE_PROPERTY_R(bool, IsDecimalEnabled);
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector< EquationViewModel^ >^, Equations); OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector< EquationViewModel^ >^, Equations);
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector< VariableViewModel^ >^, Variables);
COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed); COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed);
event Windows::Foundation::EventHandler<VariableChangedEventArgs>^ VariableUpdated;
void UpdateVariables(Windows::Foundation::Collections::IMap<Platform::String^, double>^ variables);
private: private:
void OnButtonPressed(Platform::Object^ parameter); void OnButtonPressed(Platform::Object^ parameter);
}; };

View File

@ -1725,6 +1725,16 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<Style x:Key="VariableTextBoxStyle" TargetType="TextBox">
<Setter Property="Margin" Value="12,4,4,4"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="AcceptsReturn" Value="False"/>
<Setter Property="InputScope" Value="Number"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@ -3439,4 +3439,20 @@
<value>Add Equation</value> <value>Add Equation</value>
<comment>Placeholder text for the equation input button</comment> <comment>Placeholder text for the equation input button</comment>
</data> </data>
<data name="VaiablesHeader.Text" xml:space="preserve">
<value>Variables</value>
<comment>Header text for variables area</comment>
</data>
<data name="StepTextBlock.Text" xml:space="preserve">
<value>Step</value>
<comment>Label text for the step text box</comment>
</data>
<data name="MinTextBlock.Text" xml:space="preserve">
<value>Min</value>
<comment>Label text for the min text box</comment>
</data>
<data name="MaxTextBlock.Text" xml:space="preserve">
<value>Max</value>
<comment>Label text for the max text box</comment>
</data>
</root> </root>

View File

@ -2,6 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CalculatorApp.Controls" xmlns:controls="using:CalculatorApp.Controls"
xmlns:converters="using:CalculatorApp.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:graphControl="using:GraphControl" xmlns:graphControl="using:GraphControl"
xmlns:local="using:CalculatorApp" xmlns:local="using:CalculatorApp"
@ -10,6 +11,11 @@
DataContextChanged="GraphingCalculator_DataContextChanged" DataContextChanged="GraphingCalculator_DataContextChanged"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Name="BooleanToVisibilityConverter"/>
<converters:BooleanToVisibilityNegationConverter x:Name="BooleanToVisibilityNegationConverter"/>
</UserControl.Resources>
<Grid x:Name="RootGrid"> <Grid x:Name="RootGrid">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition x:Name="RowHamburger" Height="{StaticResource HamburgerHeightGridLength}"/> <RowDefinition x:Name="RowHamburger" Height="{StaticResource HamburgerHeightGridLength}"/>
@ -25,11 +31,12 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="0"> Grid.Column="0">
<graphControl:Grapher Grid.Row="0" <graphControl:Grapher Name="GraphingControl"
Margin="4,7,4,4" Margin="4,7,4,4"
EquationsSource="{x:Bind ViewModel.Equations, Mode=OneWay}" EquationsSource="{x:Bind ViewModel.Equations, Mode=OneWay}"
ForceProportionalAxes="True" ForceProportionalAxes="True"
UseSystemFocusVisuals="True"> UseSystemFocusVisuals="True"
VariablesUpdated="GraphVariablesUpdated">
<graphControl:Grapher.Background> <graphControl:Grapher.Background>
<SolidColorBrush Color="White"/> <SolidColorBrush Color="White"/>
</graphControl:Grapher.Background> </graphControl:Grapher.Background>
@ -39,6 +46,175 @@
</DataTemplate> </DataTemplate>
</graphControl:Grapher.EquationTemplate> </graphControl:Grapher.EquationTemplate>
</graphControl:Grapher> </graphControl:Grapher>
<!-- Temporary button until the final UI is created -->
<Button Margin="12,0,0,12"
VerticalAlignment="Bottom"
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="18"
Content="&#xE70F;"
RequestedTheme="Light">
<Button.Flyout>
<Flyout Placement="TopEdgeAlignedRight">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="RequestedTheme" Value="Default"/>
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="Margin" Value="12,0,0,0"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<ListView x:Name="VariableListView"
MinWidth="300"
ItemsSource="{x:Bind ViewModel.Variables}"
SelectionMode="None">
<ListView.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="White"/>
<x:Double x:Key="TextControlThemeMinWidth">32</x:Double>
<x:Double x:Key="TextControlThemeMinHeight">16</x:Double>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="Black"/>
<x:Double x:Key="TextControlThemeMinWidth">32</x:Double>
<x:Double x:Key="TextControlThemeMinHeight">16</x:Double>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="{ThemeResource SystemColorButtonFaceColor}"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="{StaticResource SystemColorButtonFaceColor}"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="{ThemeResource SystemColorButtonTextColor}"/>
<x:Double x:Key="TextControlThemeMinWidth">32</x:Double>
<x:Double x:Key="TextControlThemeMinHeight">16</x:Double>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ListView.Resources>
<ListView.Header>
<TextBlock x:Uid="VaiablesHeader" Margin="0,0,0,12"/>
</ListView.Header>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0,0,0,12"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:VariableViewModel">
<StackPanel>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Padding="0,0,0,8"
VerticalAlignment="Bottom"
Text="{x:Bind Name}"/>
<TextBox x:Name="ValueTextBox"
Grid.Column="1"
HorizontalAlignment="Left"
Style="{StaticResource VariableTextBoxStyle}"
GotFocus="TextBoxGotFocus"
KeyDown="TextBoxKeyDown"
LosingFocus="TextBoxLosingFocus"
Text="{x:Bind Value, Mode=OneWay}"/>
<ToggleButton Grid.Column="2"
HorizontalAlignment="Right"
Background="Transparent"
FontFamily="{StaticResource SymbolThemeFontFamily}"
Content="&#xE713;"
IsChecked="{x:Bind SliderSettingsVisible, Mode=TwoWay}">
<ToggleButton.Resources>
<SolidColorBrush x:Key="ToggleButtonBackgroundPressed" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundChecked" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundCheckedPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrushCheckedPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBorderBrushPointerOver" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonBackgroundCheckedPressed" Color="Transparent"/>
<SolidColorBrush x:Key="ToggleButtonForegroundPointerOver" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundPressed" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundChecked" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundCheckedPressed" Color="{ThemeResource SystemAccentColor}"/>
<SolidColorBrush x:Key="ToggleButtonForegroundCheckedPointerOver" Color="{ThemeResource SystemAccentColor}"/>
<x:Double x:Key="TextControlThemeMinWidth">32</x:Double>
</ToggleButton.Resources>
</ToggleButton>
</Grid>
<Slider MinHeight="38"
Maximum="{x:Bind Max, Mode=TwoWay}"
Minimum="{x:Bind Min, Mode=TwoWay}"
StepFrequency="{x:Bind Step, Mode=TwoWay}"
Visibility="{x:Bind SliderSettingsVisible, Converter={StaticResource BooleanToVisibilityNegationConverter}, Mode=OneWay}"
Value="{x:Bind Value, Mode=TwoWay}"/>
<Grid Grid.Row="1"
MinHeight="38"
HorizontalAlignment="Stretch"
Visibility="{x:Bind SliderSettingsVisible, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock x:Uid="MinTextBlock"
Padding="0,0,0,8"
VerticalAlignment="Bottom"
FontWeight="SemiBold"/>
<TextBox x:Name="MinTextBox"
MaxWidth="96"
Style="{StaticResource VariableTextBoxStyle}"
GotFocus="TextBoxGotFocus"
KeyDown="TextBoxKeyDown"
LosingFocus="TextBoxLosingFocus"
Text="{x:Bind Min, Mode=OneWay}"/>
</StackPanel>
<StackPanel Grid.Column="1"
HorizontalAlignment="Center"
Orientation="Horizontal">
<TextBlock x:Uid="StepTextBlock"
Padding="0,0,0,8"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
FontWeight="SemiBold"/>
<TextBox x:Name="StepTextBox"
MaxWidth="96"
HorizontalAlignment="Center"
Style="{StaticResource VariableTextBoxStyle}"
GotFocus="TextBoxGotFocus"
KeyDown="TextBoxKeyDown"
LosingFocus="TextBoxLosingFocus"
Text="{x:Bind Step, Mode=OneWay}"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal">
<TextBlock x:Uid="MaxTextBlock"
Padding="0,0,0,8"
VerticalAlignment="Bottom"
FontWeight="SemiBold"/>
<TextBox x:Name="MaxTextBox"
MaxWidth="96"
Style="{StaticResource VariableTextBoxStyle}"
GotFocus="TextBoxGotFocus"
KeyDown="TextBoxKeyDown"
LosingFocus="TextBoxLosingFocus"
Text="{x:Bind Max, Mode=OneWay}"/>
</StackPanel>
</Grid>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Flyout>
</Button.Flyout>
</Button>
</Grid> </Grid>
<!-- Right portion of the screen --> <!-- Right portion of the screen -->
@ -50,12 +226,13 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="5*"/> <RowDefinition Height="5*"/>
<RowDefinition Height="3*"/> <RowDefinition Height="3*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<local:EquationInputArea Grid.Row="0" Equations="{x:Bind ViewModel.Equations}"/> <local:EquationInputArea Grid.Row="0" Equations="{x:Bind ViewModel.Equations}"/>
<Grid x:Name="ButtonContainerGrid" <Grid x:Name="ButtonContainerGrid"
Grid.Row="1" Grid.Row="2"
Margin="3,0,3,3" Margin="3,0,3,3"
UseLayoutRounding="False"> UseLayoutRounding="False">
<Grid.RowDefinitions> <Grid.RowDefinitions>

View File

@ -11,13 +11,16 @@ using namespace CalculatorApp::ViewModel;
using namespace concurrency; using namespace concurrency;
using namespace GraphControl; using namespace GraphControl;
using namespace Platform; using namespace Platform;
using namespace std;
using namespace std::chrono; using namespace std::chrono;
using namespace Utils; using namespace Utils;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections; using namespace Windows::Foundation::Collections;
using namespace Windows::Storage::Streams; using namespace Windows::Storage::Streams;
using namespace Windows::System; using namespace Windows::System;
using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Media::Imaging; using namespace Windows::UI::Xaml::Media::Imaging;
@ -34,6 +37,8 @@ GraphingCalculator::GraphingCalculator()
void GraphingCalculator::GraphingCalculator_DataContextChanged(FrameworkElement^ sender, DataContextChangedEventArgs^ args) void GraphingCalculator::GraphingCalculator_DataContextChanged(FrameworkElement^ sender, DataContextChangedEventArgs^ args)
{ {
ViewModel = dynamic_cast<GraphingCalculatorViewModel^>(args->NewValue); ViewModel = dynamic_cast<GraphingCalculatorViewModel^>(args->NewValue);
ViewModel->VariableUpdated += ref new EventHandler<VariableChangedEventArgs>(this, &CalculatorApp::GraphingCalculator::OnVariableChanged);
} }
GraphingCalculatorViewModel^ GraphingCalculator::ViewModel::get() GraphingCalculatorViewModel^ GraphingCalculator::ViewModel::get()
@ -49,3 +54,67 @@ void GraphingCalculator::ViewModel::set(GraphingCalculatorViewModel^ vm)
RaisePropertyChanged(StringReference(sc_ViewModelPropertyName)); RaisePropertyChanged(StringReference(sc_ViewModelPropertyName));
} }
} }
void GraphingCalculator::GraphVariablesUpdated(Object^, Object^)
{
m_viewModel->UpdateVariables(GraphingControl->Variables);
}
void GraphingCalculator::OnVariableChanged(Platform::Object^ sender, VariableChangedEventArgs args)
{
GraphingControl->SetVariable(args.variableName, args.newValue);
}
void GraphingCalculator::SubmitTextbox(TextBox^ sender)
{
auto variableViewModel = static_cast<VariableViewModel^>(sender->DataContext);
if (sender->Name == "ValueTextBox")
{
variableViewModel->SetValue(validateDouble(sender->Text, variableViewModel->Value));
}
else if (sender->Name == "MinTextBox")
{
variableViewModel->Min = validateDouble(sender->Text, variableViewModel->Min);
}
else if (sender->Name == "MaxTextBox")
{
variableViewModel->Step = validateDouble(sender->Text, variableViewModel->Step);
}
else if (sender->Name == "StepTextBox")
{
variableViewModel->Max = validateDouble(sender->Text, variableViewModel->Max);
}
}
void GraphingCalculator::TextBoxLosingFocus(TextBox^ sender, LosingFocusEventArgs^)
{
SubmitTextbox(sender);
}
void GraphingCalculator::TextBoxKeyDown(TextBox^ sender, KeyRoutedEventArgs^ e)
{
if (e->Key == ::VirtualKey::Enter)
{
SubmitTextbox(sender);
}
}
double GraphingCalculator::validateDouble(String^ value, double defaultValue)
{
try
{
return stod(value->Data());
}
catch (...)
{
return defaultValue;
}
}
void GraphingCalculator::TextBoxGotFocus(TextBox^ sender, RoutedEventArgs^ e)
{
sender->SelectAll();
}

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "Views\GraphingCalculator\GraphingCalculator.g.h" #include "Views\GraphingCalculator\GraphingCalculator.g.h"
#include "CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h" #include "CalcViewModel\GraphingCalculator\GraphingCalculatorViewModel.h"
#include "Views\NumberPad.xaml.h" #include "Views\NumberPad.xaml.h"
namespace CalculatorApp namespace CalculatorApp
@ -22,7 +22,17 @@ namespace CalculatorApp
private: private:
void GraphingCalculator_DataContextChanged(Windows::UI::Xaml::FrameworkElement^ sender, Windows::UI::Xaml::DataContextChangedEventArgs^ args); void GraphingCalculator_DataContextChanged(Windows::UI::Xaml::FrameworkElement^ sender, Windows::UI::Xaml::DataContextChangedEventArgs^ args);
private: void GraphVariablesUpdated(Platform::Object^ sender, Object^ args);
void OnVariableChanged(Platform::Object^ sender, CalculatorApp::ViewModel::VariableChangedEventArgs args);
void TextBoxLosingFocus(Windows::UI::Xaml::Controls::TextBox^ textbox, Windows::UI::Xaml::Input::LosingFocusEventArgs^ args);
void TextBoxKeyDown(Windows::UI::Xaml::Controls::TextBox^ textbox, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e);
void SubmitTextbox(Windows::UI::Xaml::Controls::TextBox^ textbox);
double validateDouble(Platform::String^ value, double defaultValue);
CalculatorApp::ViewModel::GraphingCalculatorViewModel^ m_viewModel; CalculatorApp::ViewModel::GraphingCalculatorViewModel^ m_viewModel;
void TextBoxGotFocus(Windows::UI::Xaml::Controls::TextBox^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
}; };
} }

View File

@ -26,8 +26,12 @@ namespace
constexpr auto s_propertyName_EquationTemplate = L"EquationTemplate"; constexpr auto s_propertyName_EquationTemplate = L"EquationTemplate";
constexpr auto s_propertyName_Equations = L"Equations"; constexpr auto s_propertyName_Equations = L"Equations";
constexpr auto s_propertyName_EquationsSource = L"EquationsSource"; constexpr auto s_propertyName_EquationsSource = L"EquationsSource";
constexpr auto s_propertyName_Variables = L"Variables";
constexpr auto s_propertyName_ForceProportionalAxes = L"ForceProportionalAxes"; constexpr auto s_propertyName_ForceProportionalAxes = L"ForceProportionalAxes";
constexpr auto s_X = L"x";
constexpr auto s_Y = L"y";
// Helper function for converting a pointer position to a position that the graphing engine will understand. // Helper function for converting a pointer position to a position that the graphing engine will understand.
// posX/posY are the pointer position elements and width,height are the dimensions of the graph surface. // posX/posY are the pointer position elements and width,height are the dimensions of the graph surface.
// The graphing engine interprets x,y position between the range [-1, 1]. // The graphing engine interprets x,y position between the range [-1, 1].
@ -43,6 +47,7 @@ namespace GraphControl
DependencyProperty^ Grapher::s_equationTemplateProperty; DependencyProperty^ Grapher::s_equationTemplateProperty;
DependencyProperty^ Grapher::s_equationsProperty; DependencyProperty^ Grapher::s_equationsProperty;
DependencyProperty^ Grapher::s_equationsSourceProperty; DependencyProperty^ Grapher::s_equationsSourceProperty;
DependencyProperty^ Grapher::s_variablesProperty;
DependencyProperty^ Grapher::s_forceProportionalAxesTemplateProperty; DependencyProperty^ Grapher::s_forceProportionalAxesTemplateProperty;
Grapher::Grapher() Grapher::Grapher()
@ -54,6 +59,7 @@ namespace GraphControl
DefaultStyleKey = StringReference(s_defaultStyleKey); DefaultStyleKey = StringReference(s_defaultStyleKey);
this->SetValue(EquationsProperty, ref new EquationCollection()); this->SetValue(EquationsProperty, ref new EquationCollection());
this->SetValue(VariablesProperty, ref new Map<String^, double>());
this->Loaded += ref new RoutedEventHandler(this, &Grapher::OnLoaded); this->Loaded += ref new RoutedEventHandler(this, &Grapher::OnLoaded);
this->Unloaded += ref new RoutedEventHandler(this, &Grapher::OnUnloaded); this->Unloaded += ref new RoutedEventHandler(this, &Grapher::OnUnloaded);
@ -145,6 +151,17 @@ namespace GraphControl
ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged))); ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged)));
} }
if (!s_variablesProperty)
{
s_variablesProperty = DependencyProperty::Register(
StringReference(s_propertyName_Variables),
IObservableMap<String^, double>::typeid,
Grapher::typeid,
ref new PropertyMetadata(
nullptr,
ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged)));
}
if (!s_forceProportionalAxesTemplateProperty) if (!s_forceProportionalAxesTemplateProperty)
{ {
s_forceProportionalAxesTemplateProperty = DependencyProperty::Register( s_forceProportionalAxesTemplateProperty = DependencyProperty::Register(
@ -350,8 +367,11 @@ namespace GraphControl
if (m_graph->TryInitialize(graphExpression.get())) if (m_graph->TryInitialize(graphExpression.get()))
{ {
UpdateGraphOptions(m_graph->GetOptions(), validEqs); UpdateGraphOptions(m_graph->GetOptions(), validEqs);
SetGraphArgs();
m_renderMain->Graph = m_graph; m_renderMain->Graph = m_graph;
UpdateVariables();
} }
} }
} }
@ -360,13 +380,80 @@ namespace GraphControl
if (m_graph->TryInitialize()) if (m_graph->TryInitialize())
{ {
UpdateGraphOptions(m_graph->GetOptions(), validEqs); UpdateGraphOptions(m_graph->GetOptions(), validEqs);
SetGraphArgs();
m_renderMain->Graph = m_graph; m_renderMain->Graph = m_graph;
UpdateVariables();
} }
} }
} }
} }
void Grapher::SetGraphArgs()
{
if (m_graph)
{
for (auto variable : Variables)
{
m_graph->SetArgValue(variable->Key->Data(), variable->Value);
}
}
}
void Grapher::UpdateVariables()
{
auto updatedVariables = ref new Map<String^, double>();
if (m_graph)
{
auto graphVariables = m_graph->GetVariables();
for (auto graphVar : graphVariables)
{
if (graphVar->GetVariableName() != s_X && graphVar->GetVariableName() != s_Y)
{
auto key = ref new String(graphVar->GetVariableName().data());
double value = 1.0;
if (Variables->HasKey(key))
{
value = Variables->Lookup(key);
}
updatedVariables->Insert(key, value);
}
}
}
Variables = updatedVariables;
VariablesUpdated(this, Variables);
}
void Grapher::SetVariable(Platform::String^ variableName, double newValue)
{
if (Variables->HasKey(variableName))
{
if (Variables->Lookup(variableName) == newValue)
{
return;
}
Variables->Remove(variableName);
}
Variables->Insert(variableName, newValue);
if (m_graph)
{
m_graph->SetArgValue(variableName->Data(), newValue);
if (m_renderMain)
{
m_renderMain->RunRenderPass();
}
}
}
void Grapher::UpdateGraphOptions(IGraphingOptions& options, const vector<Equation^>& validEqs) void Grapher::UpdateGraphOptions(IGraphingOptions& options, const vector<Equation^>& validEqs)
{ {
options.SetForceProportional(ForceProportionalAxes); options.SetForceProportional(ForceProportionalAxes);

View File

@ -5,6 +5,7 @@
#include "Equation.h" #include "Equation.h"
#include "EquationCollection.h" #include "EquationCollection.h"
#include "IMathSolver.h" #include "IMathSolver.h"
#include "Common.h"
namespace GraphControl namespace GraphControl
{ {
@ -78,6 +79,29 @@ namespace GraphControl
} }
#pragma endregion #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<Platform::String^, double>^ Variables
{
Windows::Foundation::Collections::IObservableMap<Platform::String^, double>^ get()
{
return static_cast<Windows::Foundation::Collections::IObservableMap<Platform::String^, double>^>(GetValue(s_variablesProperty));
}
void set(Windows::Foundation::Collections::IObservableMap<Platform::String^, double>^ value)
{
SetValue(s_variablesProperty, value);
}
}
#pragma endregion
#pragma region Windows::UI::Xaml::DataTemplate^ ForceProportionalAxes DependencyProperty #pragma region Windows::UI::Xaml::DataTemplate^ ForceProportionalAxes DependencyProperty
static property Windows::UI::Xaml::DependencyProperty^ ForceProportionalAxesTemplateProperty static property Windows::UI::Xaml::DependencyProperty^ ForceProportionalAxesTemplateProperty
{ {
@ -100,6 +124,10 @@ namespace GraphControl
} }
#pragma endregion #pragma endregion
event Windows::Foundation::EventHandler<Windows::Foundation::Collections::IMap<Platform::String^, double>^>^ VariablesUpdated;
void SetVariable(Platform::String^ variableName, double newValue);
protected: protected:
#pragma region Control Overrides #pragma region Control Overrides
void OnApplyTemplate() override; void OnApplyTemplate() override;
@ -127,12 +155,14 @@ namespace GraphControl
void OnDataSourceChanged(GraphControl::InspectingDataSource^ sender, GraphControl::DataSourceChangedEventArgs args); void OnDataSourceChanged(GraphControl::InspectingDataSource^ sender, GraphControl::DataSourceChangedEventArgs args);
void OnEquationsChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args); void OnEquationsChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args);
void OnEquationsVectorChanged(Windows::Foundation::Collections::IObservableVector<GraphControl::Equation ^> ^sender, Windows::Foundation::Collections::IVectorChangedEventArgs ^event); void OnEquationsVectorChanged(Windows::Foundation::Collections::IObservableVector<GraphControl::Equation ^> ^sender, Windows::Foundation::Collections::IVectorChangedEventArgs^ event);
void OnEquationChanged(); void OnEquationChanged();
void UpdateGraph(); void UpdateGraph();
void UpdateGraphOptions(Graphing::IGraphingOptions& options, const std::vector<Equation^>& validEqs); void UpdateGraphOptions(Graphing::IGraphingOptions& options, const std::vector<Equation^>& validEqs);
std::vector<Equation^> GetValidEquations(); std::vector<Equation^> GetValidEquations();
void SetGraphArgs();
void UpdateVariables();
void OnForceProportionalAxesChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args); void OnForceProportionalAxesChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args);
@ -154,6 +184,7 @@ namespace GraphControl
Windows::Foundation::EventRegistrationToken m_tokenDataSourceChanged; Windows::Foundation::EventRegistrationToken m_tokenDataSourceChanged;
static Windows::UI::Xaml::DependencyProperty^ s_equationsProperty; static Windows::UI::Xaml::DependencyProperty^ s_equationsProperty;
static Windows::UI::Xaml::DependencyProperty^ s_variablesProperty;
Windows::Foundation::EventRegistrationToken m_tokenEquationsChanged; Windows::Foundation::EventRegistrationToken m_tokenEquationsChanged;
Windows::Foundation::EventRegistrationToken m_tokenEquationChanged; Windows::Foundation::EventRegistrationToken m_tokenEquationChanged;

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <string>
#ifndef GRAPHINGAPI #ifndef GRAPHINGAPI
#ifdef GRAPHING_ENGINE_IMPL #ifdef GRAPHING_ENGINE_IMPL
@ -38,6 +39,14 @@ namespace Graphing
virtual bool IsEmptySet() const = 0; virtual bool IsEmptySet() const = 0;
}; };
struct IVariable
{
virtual ~IVariable() = default;
virtual int GetVariableID() const = 0;
virtual const std::wstring& GetVariableName() = 0;
};
struct IExpressible struct IExpressible
{ {
virtual ~IExpressible() = default; virtual ~IExpressible() = default;

View File

@ -16,6 +16,10 @@ namespace Graphing
virtual IGraphingOptions& GetOptions() = 0; virtual IGraphingOptions& GetOptions() = 0;
virtual std::vector<std::shared_ptr<IVariable>> GetVariables() = 0;
virtual void SetArgValue(std::wstring variableName, double value) = 0;
virtual std::shared_ptr< Renderer::IGraphRenderer > GetRenderer() const = 0; virtual std::shared_ptr< Renderer::IGraphRenderer > GetRenderer() const = 0;
virtual bool TryResetSelection() = 0; virtual bool TryResetSelection() = 0;