Improve error handling by displaying an error message (#1075)

* wire up error messages

* more errors

* fix crash on render error

* Always show copy and cut

* PR comments

* Fix spelling
This commit is contained in:
Pepe Rivera 2020-03-25 15:18:34 -07:00 committed by GitHub
parent 7b51b45906
commit fc19ddcbcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 582 additions and 32 deletions

View File

@ -11,6 +11,7 @@ using namespace Graphing;
using namespace Platform; using namespace Platform;
using namespace Platform::Collections; using namespace Platform::Collections;
using namespace std; using namespace std;
using namespace Windows::ApplicationModel::Resources;
using namespace Windows::UI; using namespace Windows::UI;
using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml;
using namespace Windows::Foundation::Collections; using namespace Windows::Foundation::Collections;
@ -36,7 +37,7 @@ namespace CalculatorApp::ViewModel
: m_AnalysisErrorVisible{ false } : m_AnalysisErrorVisible{ false }
, m_FunctionLabelIndex{ functionLabelIndex } , m_FunctionLabelIndex{ functionLabelIndex }
, m_KeyGraphFeaturesItems{ ref new Vector<KeyGraphFeaturesItem ^>() } , m_KeyGraphFeaturesItems{ ref new Vector<KeyGraphFeaturesItem ^>() }
, m_resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView() } , m_resourceLoader{ ::ResourceLoader::GetForCurrentView() }
{ {
if (equation == nullptr) if (equation == nullptr)
{ {
@ -72,11 +73,14 @@ namespace CalculatorApp::ViewModel
AddKeyGraphFeature(m_resourceLoader->GetString(L"YIntercept"), graphEquation->YIntercept, m_resourceLoader->GetString(L"KGFYInterceptNone")); 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"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"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"));
AddKeyGraphFeature( AddKeyGraphFeature(
m_resourceLoader->GetString(L"VerticalAsymptotes"), graphEquation->VerticalAsymptotes, m_resourceLoader->GetString(L"KGFVerticalAsymptotesNone")); m_resourceLoader->GetString(L"VerticalAsymptotes"), graphEquation->VerticalAsymptotes, m_resourceLoader->GetString(L"KGFVerticalAsymptotesNone"));
AddKeyGraphFeature( AddKeyGraphFeature(
m_resourceLoader->GetString(L"HorizontalAsymptotes"), graphEquation->HorizontalAsymptotes, m_resourceLoader->GetString(L"KGFHorizontalAsymptotesNone")); m_resourceLoader->GetString(L"HorizontalAsymptotes"),
graphEquation->HorizontalAsymptotes,
m_resourceLoader->GetString(L"KGFHorizontalAsymptotesNone"));
AddKeyGraphFeature( AddKeyGraphFeature(
m_resourceLoader->GetString(L"ObliqueAsymptotes"), graphEquation->ObliqueAsymptotes, m_resourceLoader->GetString(L"KGFObliqueAsymptotesNone")); m_resourceLoader->GetString(L"ObliqueAsymptotes"), graphEquation->ObliqueAsymptotes, m_resourceLoader->GetString(L"KGFObliqueAsymptotesNone"));
AddParityKeyGraphFeature(graphEquation); AddParityKeyGraphFeature(graphEquation);
@ -299,4 +303,154 @@ namespace CalculatorApp::ViewModel
KeyGraphFeaturesItems->Append(tooComplexItem); KeyGraphFeaturesItems->Append(tooComplexItem);
} }
String ^ EquationViewModel::EquationErrorText(ErrorType errorType, int errorCode)
{
auto resLoader = ResourceLoader::GetForCurrentView();
if (errorType == ::ErrorType::Evaluation)
{
switch (static_cast<EvaluationErrorCode>(errorCode))
{
case (EvaluationErrorCode::Overflow):
return resLoader->GetString(L"Overflow");
break;
case (EvaluationErrorCode::RequireRadiansMode):
return resLoader->GetString(L"RequireRadiansMode");
break;
case (EvaluationErrorCode::TooComplexToSolve):
return resLoader->GetString(L"TooComplexToSolve");
break;
case (EvaluationErrorCode::RequireDegreesMode):
return resLoader->GetString(L"RequireDegreesMode");
break;
case (EvaluationErrorCode::FactorialInvalidArgument):
case (EvaluationErrorCode::Factorial2InvalidArgument):
return resLoader->GetString(L"FactorialInvalidArgument");
break;
case (EvaluationErrorCode::FactorialCannotPerformOnLargeNumber):
return resLoader->GetString(L"FactorialCannotPerformOnLargeNumber");
break;
case (EvaluationErrorCode::ModuloCannotPerformOnFloat):
return resLoader->GetString(L"ModuloCannotPerformOnFloat");
break;
case (EvaluationErrorCode::EquationTooComplexToSolve):
case (EvaluationErrorCode::EquationTooComplexToSolveSymbolic):
case (EvaluationErrorCode::EquationTooComplexToPlot):
case (EvaluationErrorCode::InequalityTooComplexToSolve):
case (EvaluationErrorCode::GE_TooComplexToSolve):
return resLoader->GetString(L"TooComplexToSolve");
break;
case (EvaluationErrorCode::EquationHasNoSolution):
case (EvaluationErrorCode::InequalityHasNoSolution):
return resLoader->GetString(L"EquationHasNoSolution");
break;
case (EvaluationErrorCode::DivideByZero):
return resLoader->GetString(L"DivideByZero");
break;
case (EvaluationErrorCode::MutuallyExclusiveConditions):
return resLoader->GetString(L"MutuallyExclusiveConditions");
break;
case (EvaluationErrorCode::OutOfDomain):
return resLoader->GetString(L"OutOfDomain");
break;
case (EvaluationErrorCode::GE_NotSupported):
return resLoader->GetString(L"GE_NotSupported");
break;
default:
return resLoader->GetString(L"GeneralError");
break;
}
}
else if (errorType == ::ErrorType::Syntax)
{
switch (static_cast<SyntaxErrorCode>(errorCode))
{
case (SyntaxErrorCode::ParenthesisMismatch):
return resLoader->GetString(L"ParenthesisMismatch");
break;
case (SyntaxErrorCode::UnmatchedParenthesis):
return resLoader->GetString(L"UnmatchedParenthesis");
break;
case (SyntaxErrorCode::TooManyDecimalPoints):
return resLoader->GetString(L"TooManyDecimalPoints");
break;
case (SyntaxErrorCode::DecimalPointWithoutDigits):
return resLoader->GetString(L"DecimalPointWithoutDigits");
break;
case (SyntaxErrorCode::UnexpectedEndOfExpression):
return resLoader->GetString(L"UnexpectedEndOfExpression");
break;
case (SyntaxErrorCode::UnexpectedToken):
return resLoader->GetString(L"UnexpectedToken");
break;
case (SyntaxErrorCode::InvalidToken):
return resLoader->GetString(L"InvalidToken");
break;
case (SyntaxErrorCode::TooManyEquals):
return resLoader->GetString(L"TooManyEquals");
break;
case (SyntaxErrorCode::EqualWithoutGraphVariable):
return resLoader->GetString(L"EqualWithoutGraphVariable");
break;
case (SyntaxErrorCode::InvalidEquationSyntax):
case (SyntaxErrorCode::InvalidEquationFormat):
return resLoader->GetString(L"InvalidEquationSyntax");
break;
case (SyntaxErrorCode::EmptyExpression):
return resLoader->GetString(L"EmptyExpression");
break;
case (SyntaxErrorCode::EqualWithoutEquation):
return resLoader->GetString(L"EqualWithoutEquation");
break;
case (SyntaxErrorCode::ExpectParenthesisAfterFunctionName):
return resLoader->GetString(L"ExpectParenthesisAfterFunctionName");
break;
case (SyntaxErrorCode::IncorrectNumParameter):
return resLoader->GetString(L"IncorrectNumParameter");
break;
case (SyntaxErrorCode::InvalidVariableNameFormat):
return resLoader->GetString(L"InvalidVariableNameFormat");
break;
case (SyntaxErrorCode::BracketMismatch):
return resLoader->GetString(L"BracketMismatch");
break;
case (SyntaxErrorCode::UnmatchedBracket):
return resLoader->GetString(L"UnmatchedBracket");
break;
case (SyntaxErrorCode::CannotUseIInReal):
return resLoader->GetString(L"CannotUseIInReal");
break;
case (SyntaxErrorCode::InvalidNumberDigit):
return resLoader->GetString(L"InvalidNumberDigit");
break;
case (SyntaxErrorCode::InvalidNumberBase):
return resLoader->GetString(L"InvalidNumberBase");
break;
case (SyntaxErrorCode::InvalidVariableSpecification):
return resLoader->GetString(L"InvalidVariableSpecification");
break;
case (SyntaxErrorCode::ExpectingLogicalOperands):
case (SyntaxErrorCode::ExpectingScalarOperands):
return resLoader->GetString(L"ExpectingLogicalOperands");
break;
case (SyntaxErrorCode::CannotUseIndexVarInOpLimits):
return resLoader->GetString(L"CannotUseIndexVarInOpLimits");
break;
case (SyntaxErrorCode::CannotUseIndexVarInLimPoint):
return resLoader->GetString(L"Overflow");
break;
case (SyntaxErrorCode::CannotUseComplexInfinityInReal):
return resLoader->GetString(L"CannotUseComplexInfinityInReal");
break;
case (SyntaxErrorCode::CannotUseIInInequalitySolving):
return resLoader->GetString(L"CannotUseIInInequalitySolving");
break;
default:
return resLoader->GetString(L"GeneralError");
break;
}
}
return resLoader->GetString(L"GeneralError");
}
} }

View File

@ -103,6 +103,8 @@ public
void PopulateKeyGraphFeatures(GraphControl::KeyGraphFeaturesInfo ^ info); void PopulateKeyGraphFeatures(GraphControl::KeyGraphFeaturesInfo ^ info);
static Platform::String ^ EquationErrorText(GraphControl::ErrorType errorType, int errorCode);
private: private:
void AddKeyGraphFeature(Platform::String ^ title, Platform::String ^ expression, Platform::String ^ errorString); void AddKeyGraphFeature(Platform::String ^ title, Platform::String ^ expression, Platform::String ^ errorString);
void AddKeyGraphFeature( void AddKeyGraphFeature(

View File

@ -27,6 +27,7 @@ DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationButtonContentIndex);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, HasError); DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, HasError);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsAddEquationMode); DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsAddEquationMode);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, MathEquation); DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, MathEquation);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, ErrorText);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsEquationLineDisabled); DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsEquationLineDisabled);
EquationTextBox::EquationTextBox() EquationTextBox::EquationTextBox()
@ -432,12 +433,12 @@ void EquationTextBox::OnRichEditMenuOpened(Object ^ /*sender*/, Object ^ /*args*
if (m_richEditBox != nullptr && m_cutMenuItem != nullptr) if (m_richEditBox != nullptr && m_cutMenuItem != nullptr)
{ {
m_cutMenuItem->IsEnabled = m_richEditBox->TextDocument->CanCopy() && m_richEditBox->TextDocument->Selection->Length > 0; m_cutMenuItem->IsEnabled = m_richEditBox->TextDocument->CanCopy();
} }
if (m_richEditBox != nullptr && m_copyMenuItem != nullptr) if (m_richEditBox != nullptr && m_copyMenuItem != nullptr)
{ {
m_copyMenuItem->IsEnabled = m_richEditBox->TextDocument->CanCopy() && m_richEditBox->TextDocument->Selection->Length > 0; m_copyMenuItem->IsEnabled = m_richEditBox->TextDocument->CanCopy();
} }
if (m_richEditBox != nullptr && m_pasteMenuItem != nullptr) if (m_richEditBox != nullptr && m_pasteMenuItem != nullptr)

View File

@ -25,6 +25,7 @@ namespace CalculatorApp
DEPENDENCY_PROPERTY(Platform::String ^, MathEquation); DEPENDENCY_PROPERTY(Platform::String ^, MathEquation);
DEPENDENCY_PROPERTY_WITH_CALLBACK(bool, HasError); DEPENDENCY_PROPERTY_WITH_CALLBACK(bool, HasError);
DEPENDENCY_PROPERTY_WITH_CALLBACK(bool, IsAddEquationMode); DEPENDENCY_PROPERTY_WITH_CALLBACK(bool, IsAddEquationMode);
DEPENDENCY_PROPERTY(Platform::String ^, ErrorText);
DEPENDENCY_PROPERTY(bool, IsEquationLineDisabled); DEPENDENCY_PROPERTY(bool, IsEquationLineDisabled);
PROPERTY_R(bool, HasFocus); PROPERTY_R(bool, HasFocus);

View File

@ -4106,7 +4106,163 @@
<value>Unable to calculate the range for this function.</value> <value>Unable to calculate the range for this function.</value>
<comment>Error displayed when Range is not returned from the analyzer.</comment> <comment>Error displayed when Range is not returned from the analyzer.</comment>
</data> </data>
<data name="equationAnalysisBack.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve"> <data name="Overflow" xml:space="preserve">
<value>Overflow (the number is too large)</value>
<comment>Error that occurs during graphing when the number is too large. To see this error, assign a large number to variable a, then keep doing "a:=a*a" until it happens.</comment>
</data>
<data name="RequireRadiansMode" xml:space="preserve">
<value>Radians mode is required to graph this equation.</value>
<comment>Error that occurs during graphing when radians is required.</comment>
</data>
<data name="TooComplexToSolve" xml:space="preserve">
<value>This function is too complex to graph</value>
<comment>Error that occurs during graphing when the equation is too complex.</comment>
</data>
<data name="RequireDegreesMode" xml:space="preserve">
<value>Degrees mode is required to graph this function</value>
<comment>Error that occurs during graphing when degrees is required</comment>
</data>
<data name="FactorialInvalidArgument" xml:space="preserve">
<value>The factorial function has an invalid argument</value>
<comment>Error that occurs during graphing when a factorial function has an invalid argument.</comment>
</data>
<data name="FactorialCannotPerformOnLargeNumber" xml:space="preserve">
<value>The factorial function has an argument that is too large to graph</value>
<comment>Error that occurs during graphing when a factorial has a large n</comment>
</data>
<data name="ModuloCannotPerformOnFloat" xml:space="preserve">
<value>Modulo can only be used with whole numbers</value>
<comment>Error that occurs during graphing when modulo is used with a float.</comment>
</data>
<data name="EquationHasNoSolution" xml:space="preserve">
<value>The equation has no solution</value>
<comment>Error that occurs during graphing when the equation has no solution.</comment>
</data>
<data name="DivideByZero" xml:space="preserve">
<value>Cannot divide by zero</value>
<comment>Error that occurs during graphing when a divison by zero occurs.</comment>
</data>
<data name="MutuallyExclusiveConditions" xml:space="preserve">
<value>The equation contains logical conditions that are mutually exclusive</value>
<comment>Error that occurs during graphing when mutually exclusive conditions are used.</comment>
</data>
<data name="OutOfDomain" xml:space="preserve">
<value>Equation is out of domain</value>
<comment>Error that occurs during graphing when the equation is out of domain.</comment>
</data>
<data name="GE_NotSupported" xml:space="preserve">
<value>Graphing this equation is not supported</value>
<comment>Error that occurs during graphing when the equation is not supported.</comment>
</data>
<data name="ParenthesisMismatch" xml:space="preserve">
<value>The equation is missing an opening parenthesis</value>
<comment>Error that occurs during graphing when the equation is missing a (</comment>
</data>
<data name="UnmatchedParenthesis" xml:space="preserve">
<value>The equation is missing a closing parenthesis</value>
<comment>Error that occurs during graphing when the equation is missing a )</comment>
</data>
<data name="TooManyDecimalPoints" xml:space="preserve">
<value>There are too many decimal points in a number</value>
<comment>Error that occurs during graphing when a number has too many decimals. Ex: 1.2.3</comment>
</data>
<data name="DecimalPointWithoutDigits" xml:space="preserve">
<value>A decimal point is missing digits</value>
<comment>Error that occurs during graphing with a decimal point without digits</comment>
</data>
<data name="UnexpectedEndOfExpression" xml:space="preserve">
<value>Unexpected end of expression</value>
<comment>Error that occurs during graphing when the expression ends unexpectedly. Ex: 3-4*</comment>
</data>
<data name="UnexpectedToken" xml:space="preserve">
<value>Unexpected characters in the expression</value>
<comment>Error that occurs during graphing when there is an unexpected token.</comment>
</data>
<data name="InvalidToken" xml:space="preserve">
<value>Invalid characters in the expression</value>
<comment>Error that occurs during graphing when there is an invalid token.</comment>
</data>
<data name="TooManyEquals" xml:space="preserve">
<value>There are too many equal signs</value>
<comment>Error that occurs during graphing when there are too many equals.</comment>
</data>
<data name="EqualWithoutGraphVariable" xml:space="preserve">
<value>The function must contain at least one x or y variable</value>
<comment>Error that occurs during graphing when the equation is missing x or y.</comment>
</data>
<data name="InvalidEquationSyntax" xml:space="preserve">
<value>Invalid expression</value>
<comment>Error that occurs during graphing when an invalid syntax is used.</comment>
</data>
<data name="EmptyExpression" xml:space="preserve">
<value>The expression is empty</value>
<comment>Error that occurs during graphing when the expression is empty</comment>
</data>
<data name="EqualWithoutEquation" xml:space="preserve">
<value>Equal was used without an equation</value>
<comment>Error that occurs during graphing when equal is used without an equation. Ex: sin(x=y)</comment>
</data>
<data name="ExpectParenthesisAfterFunctionName" xml:space="preserve">
<value>Parenthesis missing after function name</value>
<comment>Error that occurs during graphing when parenthesis are missing after a function.</comment>
</data>
<data name="IncorrectNumParameter" xml:space="preserve">
<value>A mathematical operation has the incorrect number of parameters</value>
<comment>Error that occurs during graphing when a function has the wrong number of parameters</comment>
</data>
<data name="InvalidVariableNameFormat" xml:space="preserve">
<value>A variable name is invalid</value>
<comment>Error that occurs during graphing when a variable name is invalid.</comment>
</data>
<data name="BracketMismatch" xml:space="preserve">
<value>The equation is missing an opening bracket</value>
<comment>Error that occurs during graphing when a { is missing</comment>
</data>
<data name="UnmatchedBracket" xml:space="preserve">
<value>The equation is missing a closing bracket</value>
<comment>Error that occurs during graphing when a } is missing.</comment>
</data>
<data name="CannotUseIInReal" xml:space="preserve">
<value>"i" and "I" cannot be used as variable names</value>
<comment>Error that occurs during graphing when i or I is used.</comment>
</data>
<data name="GeneralError" xml:space="preserve">
<value>The equation could not be graphed</value>
<comment>General error that occurs during graphing.</comment>
</data>
<data name="InvalidNumberDigit" xml:space="preserve">
<value>The digit could not be resolved for the given base</value>
<comment>Error that occurs during graphing when trying to use bases incorrect. Ex: base(2,1020).</comment>
</data>
<data name="InvalidNumberBase" xml:space="preserve">
<value>The base must be greater than 2 and less than 36</value>
<comment>Error that occurs during graphing when the base is out of range.</comment>
</data>
<data name="InvalidVariableSpecification" xml:space="preserve">
<value>A mathematical operation requires one of its paramaters to be a variable</value>
<comment>Error that occurs during graphing when a function requires a variable in a particular position. Ex: 2nd argument of deriv.</comment>
</data>
<data name="ExpectingLogicalOperands" xml:space="preserve">
<value>Equation is mixing logical and scalar operands</value>
<comment>Error that occurs during graphing when operands are mixed. Such as true and 1.</comment>
</data>
<data name="CannotUseIndexVarInOpLimits" xml:space="preserve">
<value>x or y cannot be used in the upper or lower limits</value>
<comment>Error that occurs during graphing when x or y is used in integral upper limits.</comment>
</data>
<data name="CannotUseIndexVarInLimPoint" xml:space="preserve">
<value>x or y cannot be used in the limit point</value>
<comment>Error that occurs during graphing when x or y is used in the limit point.</comment>
</data>
<data name="CannotUseComplexInfinityInReal" xml:space="preserve">
<value>Cannot use complex infinity</value>
<comment>Error that occurs during graphing when complex infinity is used</comment>
</data>
<data name="CannotUseIInInequalitySolving" xml:space="preserve">
<value>Cannot use complex numbers in inequalities</value>
<comment>Error that occurs during graphing when complex numbers are used in inequalities.</comment>
</data>
<data name="equationAnalysisBack.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Back</value> <value>Back</value>
<comment>This is the tooltip for the back button in the equation analysis page in the graphing calculator</comment> <comment>This is the tooltip for the back button in the equation analysis page in the graphing calculator</comment>
</data> </data>

View File

@ -324,6 +324,7 @@
<Setter Target="EquationBoxBorder.BorderThickness" Value="1"/> <Setter Target="EquationBoxBorder.BorderThickness" Value="1"/>
<Setter Target="EquationBoxBorder.BorderBrush" Value="{ThemeResource EquationBoxErrorBorderBrush}"/> <Setter Target="EquationBoxBorder.BorderBrush" Value="{ThemeResource EquationBoxErrorBorderBrush}"/>
<Setter Target="EquationBoxBorder.Background" Value="{ThemeResource EquationBoxErrorBackgroundBrush}"/> <Setter Target="EquationBoxBorder.Background" Value="{ThemeResource EquationBoxErrorBackgroundBrush}"/>
<Setter Target="RemoveButton.Visibility" Value="Visible"/>
<Setter Target="ErrorIcon.Visibility" Value="Visible"/> <Setter Target="ErrorIcon.Visibility" Value="Visible"/>
</VisualState.Setters> </VisualState.Setters>
</VisualState> </VisualState>
@ -344,7 +345,7 @@
<Setter Target="ColorChooserButton.Visibility" Value="Collapsed"/> <Setter Target="ColorChooserButton.Visibility" Value="Collapsed"/>
<Setter Target="FunctionButton.Visibility" Value="Collapsed"/> <Setter Target="FunctionButton.Visibility" Value="Collapsed"/>
<Setter Target="RemoveButton.Visibility" Value="Visible"/> <Setter Target="RemoveButton.Visibility" Value="Visible"/>
<Setter Target="ErrorIcon.Visibility" Value="Collapsed"/> <Setter Target="ErrorIcon.Visibility" Value="Visible"/>
</VisualState.Setters> </VisualState.Setters>
</VisualState> </VisualState>
<VisualState x:Name="Disabled"> <VisualState x:Name="Disabled">
@ -568,6 +569,7 @@
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<controls:MathRichEditBox x:Name="MathRichEditBox" <controls:MathRichEditBox x:Name="MathRichEditBox"
x:Uid="mathRichEditBox" x:Uid="mathRichEditBox"
@ -698,7 +700,18 @@
</ResourceDictionary> </ResourceDictionary>
</ToggleButton.Resources> </ToggleButton.Resources>
</ToggleButton> </ToggleButton>
<Grid x:Name="RemoveButtonPanel" Grid.Column="3"> <FontIcon x:Name="ErrorIcon"
Grid.Column="3"
MinWidth="28"
VerticalAlignment="Stretch"
Foreground="{ThemeResource ButtonForeground}"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16"
AutomationProperties.AccessibilityView="Raw"
Glyph="&#xE7BA;"
ToolTipService.ToolTip="{TemplateBinding ErrorText}"
Visibility="Collapsed"/>
<Grid x:Name="RemoveButtonPanel" Grid.Column="4">
<Button x:Name="RemoveButton" <Button x:Name="RemoveButton"
x:Uid="removeButton" x:Uid="removeButton"
MinWidth="34" MinWidth="34"
@ -737,7 +750,7 @@
</Button> </Button>
</Grid> </Grid>
<Button x:Name="DeleteButton" <Button x:Name="DeleteButton"
Grid.Column="3" Grid.Column="4"
MinWidth="34" MinWidth="34"
Margin="1,2" Margin="1,2"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
@ -774,16 +787,6 @@
</ResourceDictionary> </ResourceDictionary>
</Button.Resources> </Button.Resources>
</Button> </Button>
<FontIcon x:Name="ErrorIcon"
Grid.Column="3"
MinWidth="28"
VerticalAlignment="Stretch"
Foreground="{ThemeResource ButtonForeground}"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16"
AutomationProperties.AccessibilityView="Raw"
Glyph="&#xE7BA;"
Visibility="Collapsed"/>
</Grid> </Grid>
</Border> </Border>
</Grid> </Grid>
@ -823,7 +826,8 @@
</ListView.ItemContainerStyle> </ListView.ItemContainerStyle>
<ListView.ItemTemplate> <ListView.ItemTemplate>
<DataTemplate x:DataType="vm:EquationViewModel"> <DataTemplate x:DataType="vm:EquationViewModel">
<controls:EquationTextBox x:Uid="EquationInputButton" <controls:EquationTextBox x:Name="EquationInputButton"
x:Uid="EquationInputButton"
Margin="1,0,1,2" Margin="1,0,1,2"
Style="{StaticResource EquationTextBoxStyle}" Style="{StaticResource EquationTextBoxStyle}"
DataContext="{x:Bind Mode=OneWay}" DataContext="{x:Bind Mode=OneWay}"
@ -833,6 +837,7 @@
EquationColor="{x:Bind local:EquationInputArea.ToSolidColorBrush(LineColor), Mode=OneWay}" EquationColor="{x:Bind local:EquationInputArea.ToSolidColorBrush(LineColor), Mode=OneWay}"
EquationFormatRequested="EquationTextBox_EquationFormatRequested" EquationFormatRequested="EquationTextBox_EquationFormatRequested"
EquationSubmitted="EquationTextBox_Submitted" EquationSubmitted="EquationTextBox_Submitted"
ErrorText="{x:Bind vm:EquationViewModel.EquationErrorText(GraphEquation.GraphErrorType, GraphEquation.GraphErrorCode), Mode=OneWay}"
GotFocus="EquationTextBox_GotFocus" GotFocus="EquationTextBox_GotFocus"
HasError="{x:Bind GraphEquation.HasGraphError, Mode=OneWay}" HasError="{x:Bind GraphEquation.HasGraphError, Mode=OneWay}"
IsAddEquationMode="{x:Bind IsLastItemInList, Mode=OneWay}" IsAddEquationMode="{x:Bind IsLastItemInList, Mode=OneWay}"

View File

@ -297,6 +297,8 @@ namespace GraphControl
{ {
optional<vector<shared_ptr<IEquation>>> initResult = nullopt; optional<vector<shared_ptr<IEquation>>> initResult = nullopt;
bool successful = false; bool successful = false;
m_errorCode = 0;
m_errorType = 0;
if (m_renderMain && m_graph != nullptr) if (m_renderMain && m_graph != nullptr)
{ {
@ -345,7 +347,7 @@ namespace GraphControl
parsableEquation += s_getGraphClosingTags; parsableEquation += s_getGraphClosingTags;
// Wire up the corresponding error to an error message in the UI at some point // Wire up the corresponding error to an error message in the UI at some point
if (!(expr = m_solver->ParseInput(parsableEquation))) if (!(expr = m_solver->ParseInput(parsableEquation, m_errorCode, m_errorType)))
{ {
co_return false; co_return false;
} }
@ -356,7 +358,7 @@ namespace GraphControl
request += s_getGraphClosingTags; request += s_getGraphClosingTags;
} }
if (graphExpression = m_solver->ParseInput(request)) if (graphExpression = m_solver->ParseInput(request, m_errorCode, m_errorType))
{ {
initResult = TryInitializeGraph(keepCurrentView, graphExpression.get()); initResult = TryInitializeGraph(keepCurrentView, graphExpression.get());
@ -386,8 +388,13 @@ namespace GraphControl
// If we failed to render then we have already lost the previous graph // If we failed to render then we have already lost the previous graph
shouldKeepPreviousGraph = false; shouldKeepPreviousGraph = false;
initResult = nullopt; initResult = nullopt;
m_solver->HRErrorToErrorInfo(m_renderMain->GetRenderError(), m_errorCode, m_errorType);
} }
} }
else
{
m_solver->HRErrorToErrorInfo(m_graph->GetInitializationError(), m_errorCode, m_errorType);
}
} }
if (initResult == nullopt) if (initResult == nullopt)
@ -398,7 +405,7 @@ namespace GraphControl
initResult = m_graph->TryInitialize(); initResult = m_graph->TryInitialize();
if (initResult != nullopt) if (initResult != nullopt)
{ {
UpdateGraphOptions(m_graph->GetOptions(), validEqs); UpdateGraphOptions(m_graph->GetOptions(), vector<Equation^>());
SetGraphArgs(m_graph); SetGraphArgs(m_graph);
m_renderMain->Graph = m_graph; m_renderMain->Graph = m_graph;
@ -431,6 +438,8 @@ namespace GraphControl
{ {
if (!eq->IsValidated) if (!eq->IsValidated)
{ {
eq->GraphErrorType = static_cast<ErrorType>(m_errorType);
eq->GraphErrorCode = m_errorCode;
eq->HasGraphError = true; eq->HasGraphError = true;
} }
} }
@ -457,7 +466,7 @@ namespace GraphControl
request += equation->GetRequest()->Data(); request += equation->GetRequest()->Data();
request += s_getGraphClosingTags; request += s_getGraphClosingTags;
if (unique_ptr<IExpression> graphExpression = m_solver->ParseInput(request)) if (unique_ptr<IExpression> graphExpression = m_solver->ParseInput(request, m_errorCode, m_errorType))
{ {
if (graph->TryInitialize(graphExpression.get())) if (graph->TryInitialize(graphExpression.get()))
{ {
@ -524,7 +533,7 @@ namespace GraphControl
m_graph->SetArgValue(variableName->Data(), newValue); m_graph->SetArgValue(variableName->Data(), newValue);
m_renderMain->GetCriticalSection().unlock(); m_renderMain->GetCriticalSection().unlock();
m_renderMain->RunRenderPassAsync(); m_renderMain->RunRenderPass();
}); });
ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::None); ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::None);
@ -549,7 +558,7 @@ namespace GraphControl
auto lineColor = eq->LineColor; auto lineColor = eq->LineColor;
graphColors.emplace_back(lineColor.R, lineColor.G, lineColor.B, lineColor.A); graphColors.emplace_back(lineColor.R, lineColor.G, lineColor.B, lineColor.A);
if (eq->IsSelected) if (eq->GraphedEquation != nullptr && !eq->HasGraphError && eq->IsSelected)
{ {
eq->GraphedEquation->TrySelectEquation(); eq->GraphedEquation->TrySelectEquation();
} }
@ -1001,7 +1010,7 @@ String ^ Grapher::ConvertToLinear(String ^ mmlString)
{ {
m_solver->FormatOptions().SetFormatType(FormatType::LinearInput); m_solver->FormatOptions().SetFormatType(FormatType::LinearInput);
auto expression = m_solver->ParseInput(mmlString->Data()); auto expression = m_solver->ParseInput(mmlString->Data(), m_errorCode, m_errorType);
auto linearExpression = m_solver->Serialize(expression.get()); auto linearExpression = m_solver->Serialize(expression.get());
m_solver->FormatOptions().SetFormatType(s_defaultFormatType); m_solver->FormatOptions().SetFormatType(s_defaultFormatType);
@ -1011,7 +1020,7 @@ String ^ Grapher::ConvertToLinear(String ^ mmlString)
String ^ Grapher::FormatMathML(String ^ mmlString) String ^ Grapher::FormatMathML(String ^ mmlString)
{ {
auto expression = m_solver->ParseInput(mmlString->Data()); auto expression = m_solver->ParseInput(mmlString->Data(), m_errorCode, m_errorType);
auto formattedExpression = m_solver->Serialize(expression.get()); auto formattedExpression = m_solver->Serialize(expression.get());
return ref new String(formattedExpression.c_str()); return ref new String(formattedExpression.c_str());
} }

View File

@ -335,6 +335,8 @@ public
bool m_Moving; bool m_Moving;
Windows::UI::Xaml::DispatcherTimer ^ m_TracingTrackingTimer; Windows::UI::Xaml::DispatcherTimer ^ m_TracingTrackingTimer;
Windows::UI::Core::CoreCursor ^ m_cachedCursor; Windows::UI::Core::CoreCursor ^ m_cachedCursor;
int m_errorType;
int m_errorCode;
public: public:
Windows::Storage::Streams::RandomAccessStreamReference ^ GetGraphBitmapStream(); Windows::Storage::Streams::RandomAccessStreamReference ^ GetGraphBitmapStream();

View File

@ -274,7 +274,9 @@ namespace GraphControl::DX
pRenderTarget->BeginDraw(); pRenderTarget->BeginDraw();
bool hasMissingData = false; bool hasMissingData = false;
successful = SUCCEEDED(renderer->DrawD2D1(pFactory, pRenderTarget, hasMissingData)); m_HResult = renderer->DrawD2D1(pFactory, pRenderTarget, hasMissingData);
successful = SUCCEEDED(m_HResult);
// We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device // We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
// is lost. It will be handled during the next call to Present. // is lost. It will be handled during the next call to Present.
@ -345,6 +347,11 @@ namespace GraphControl::DX
return successful; return successful;
} }
HRESULT RenderMain::GetRenderError()
{
return m_HResult;
}
void RenderMain::OnLoaded(Object ^ sender, RoutedEventArgs ^ e) void RenderMain::OnLoaded(Object ^ sender, RoutedEventArgs ^ e)
{ {
RunRenderPass(); RunRenderPass();

View File

@ -61,6 +61,8 @@ namespace GraphControl::DX
return m_isRenderPassSuccesful; return m_isRenderPassSuccesful;
} }
HRESULT GetRenderError();
// Indicates if we are in active tracing mode (the tracing box is being used and controlled through keyboard input) // Indicates if we are in active tracing mode (the tracing box is being used and controlled through keyboard input)
property bool ActiveTracing property bool ActiveTracing
{ {
@ -189,5 +191,7 @@ namespace GraphControl::DX
Windows::Foundation::IAsyncAction ^ m_renderPass = nullptr; Windows::Foundation::IAsyncAction ^ m_renderPass = nullptr;
bool m_isRenderPassSuccesful; bool m_isRenderPassSuccesful;
HRESULT m_HResult;
}; };
} }

View File

@ -7,6 +7,200 @@
namespace GraphControl namespace GraphControl
{ {
public enum class ErrorType
{
Evaluation,
Syntax,
Abort,
};
public enum class EvaluationErrorCode
{
// Number is too large. To see this error, assign a large number to variable a, then keep doing "a:=a*a" until it happens
Overflow = 2,
// doing trig in calculus and current mode is not Radians
RequireRadiansMode = 3,
// This is actually abort code generated as time limit reached.
TooComplexToSolve = 4,
// degree sign present and the current mode is not Degrees
RequireDegreesMode = 5,
// n! and n is not an non-negative integer or m/2
FactorialInvalidArgument = -1,
// n!! and n is not an non-negative integer or odd negative integers
Factorial2InvalidArgument = -2,
// n! and n is too big
FactorialCannotPerformOnLargeNumber = -3,
// x mod y, and one of them is not an integer
ModuloCannotPerformOnFloat = -5,
// below are equation solving specific errors
// example1: sin(x)+x=2 example2: x^4-x^3-8x=9
EquationTooComplexToSolveSymbolic = -7,
// example1: x-x=3 example2: x^2=-1
EquationHasNoSolution = -8,
// only used for numeric solve where the algorithm doesn't converge
EquationTooComplexToSolve = -9,
// used when equation cannot be ploted
EquationTooComplexToPlot = -10,
// example1: 1/0
DivideByZero = -15,
// the inequality is too complex for the engine to solve
InequalityTooComplexToSolve = -41,
// the inequality has no solution
InequalityHasNoSolution = -42,
// An exression contains logical conditions that are mutually exclusive
MutuallyExclusiveConditions = -43,
// Arbitrary Precision Evaluation input is out of domain
OutOfDomain = -101,
// Not supported.
GE_NotSupported = -503,
// General error for graphing engine
GE_GeneralError = -504,
// Failed to calculate
GE_TooComplexToSolve = -506,
};
public enum class SyntaxErrorCode
{
// found ) without matching (
ParenthesisMismatch = 1,
// found ( without matching )
UnmatchedParenthesis = 2,
// more than 1 decimal point in a number. Example: 7.3.2
TooManyDecimalPoints = 3,
// decimal point on its own without any digits surrounding it. Example: 3+.+4
DecimalPointWithoutDigits = 4,
// example: 3-4*
UnexpectedEndOfExpression = 5,
// example: 3-*4
UnexpectedToken = 6,
// example: [ (or many other special characters), another example: "3,5" (comma is invalid here)
InvalidToken = 7,
// example: solve(x+3=8=x)
TooManyEquals = 8,
// example: ploteq(4+83=9)
EqualWithoutGraphVariable = 10,
// <para>example: ploteq(x+y) (expecting "=" in equation ploting)</para>
// <para>example2: Solve(5*x+9) (expecting = in the equation solving)</para>
InvalidEquationSyntax = 11,
// there is nothing in the expression
EmptyExpression = 12,
// example: factor(x=3) (expecting solve(x=3)).
EqualWithoutEquation = 14,
// example: solve( (x=3)*2 )
InvalidEquationFormat = 15,
// This error only occurs when CasContext.ParsingOptions.AllowImplicitParentheses == false.
// example: sin a (expecting sin(a))
ExpectParenthesisAfterFunctionName = 25,
// example: root(a) (expecting 2 parameters)
IncorrectNumParameter = 26,
// exmaple: "x_", "x_@", "x__1"
InvalidVariableNameFormat = 32,
// found } without matching {
BracketMismatch = 34,
// found { without matching }
UnmatchedBracket = 35,
// syntax error in MathML format. Used only if CasContext.ParsingOptions.FormatType is MathML or MathMLNoWrapper
InvalidMathMLFormat = 40,
// The input has an unknown MathML entity. Used only if CasContext.ParsingOptions.FormatType is MathML or MathMLNoWrapper
UnknownMathMLEntity = 41,
// The input has an unknown MathML element. Used only if CasContext.ParsingOptions.FormatType is MathML or MathMLNoWrapper
UnknownMathMLElement = 42,
// "i" and "I" cannot be used as variable names in real number field
CannotUseIInReal = 48,
// General error
GeneralError = 52,
// used in parsing numbers with arbitrary bases. example: base(2, 1020), base(16, 1AG)
InvalidNumberDigit = 55,
// a valid number base must be an integer >=2 and &lt;=36
InvalidNumberBase = 56,
// some functions require a variable in certain argument position. e.g. 2nd argument of deriv, integral, limit, etc.
// this error code is used if the argument at the position is not a variable
InvalidVariableSpecification = 57,
// all operands of logical operators must be logical. example: "true and 1"
ExpectingLogicalOperands = 58,
// all operands of a non-logical operator must not be logical. example: "sin(true)"
ExpectingScalarOperands = 59,
// a list can contain logicals or scalars, but not both.
CannotMixLogicalScalarInList = 60,
// in definite integral, seriesSum and seriesProduct, the index variable is used in the lower/upper limits.
// example: integral(sin(x), x, 0, x)
CannotUseIndexVarInOpLimits = 61,
// in limit, the index variable is used in the limit point
// example: limit(sin(x), x, x-1)
CannotUseIndexVarInLimPoint = 62,
/// ComplexInfinity cannot be used in real number field
CannotUseComplexInfinityInReal = 72,
// complex numbers are not allowed in inequality solving
CannotUseIInInequalitySolving = 123,
// Indicate a bug in the MathRichEdit serializer
RichEditSerializationError = 201,
// can't initialize math zone in richedit, meaning it's the wrong version richedit dll, need reinstall
RichEditInitialization = 202,
// indicate bug in either richedit or richedit wrapper
RichEditInlineObjectStructure = 203,
// in a structure like integral, sum, product, one of the boxes is not filled
RichEditMissingArgument = 204,
// errors in richedit wrapper that are not specifically handled for
RichEditGeneralError = 210,
};
[Windows::UI::Xaml::Data::Bindable] public ref class Equation sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged [Windows::UI::Xaml::Data::Bindable] public ref class Equation sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{ {
public: public:
@ -18,6 +212,8 @@ namespace GraphControl
OBSERVABLE_NAMED_PROPERTY_RW(bool, IsValidated); OBSERVABLE_NAMED_PROPERTY_RW(bool, IsValidated);
OBSERVABLE_NAMED_PROPERTY_RW(bool, HasGraphError); OBSERVABLE_NAMED_PROPERTY_RW(bool, HasGraphError);
OBSERVABLE_NAMED_PROPERTY_RW(bool, IsSelected); OBSERVABLE_NAMED_PROPERTY_RW(bool, IsSelected);
OBSERVABLE_NAMED_PROPERTY_RW(ErrorType, GraphErrorType);
OBSERVABLE_NAMED_PROPERTY_RW(int, GraphErrorCode);
property Windows::UI::Color LineColor property Windows::UI::Color LineColor
{ {

View File

@ -29,6 +29,11 @@ namespace MockGraphingImpl
return std::nullopt; return std::nullopt;
} }
HRESULT GetInitializationError()
{
return S_OK;
}
virtual Graphing::IGraphingOptions& GetOptions() virtual Graphing::IGraphingOptions& GetOptions()
{ {
return m_graphingOptions; return m_graphingOptions;

View File

@ -115,7 +115,7 @@ namespace MockGraphingImpl
return m_formatOptions; return m_formatOptions;
} }
std::unique_ptr<Graphing::IExpression> ParseInput(const std::wstring& input) override std::unique_ptr<Graphing::IExpression> ParseInput(const std::wstring& input, int& errorCodeOut, int& errorTypeOut) override
{ {
if (input.empty()) if (input.empty())
{ {
@ -125,6 +125,10 @@ namespace MockGraphingImpl
return std::make_unique<MockExpression>(MockExpression{}); return std::make_unique<MockExpression>(MockExpression{});
} }
void HRErrorToErrorInfo(HRESULT hr, int& errorCodeOut, int& errorTypeOut)
{
}
std::shared_ptr<Graphing::IGraph> CreateGrapher(const Graphing::IExpression* expression) override; std::shared_ptr<Graphing::IGraph> CreateGrapher(const Graphing::IExpression* expression) override;
std::shared_ptr<Graphing::IGraph> CreateGrapher() override; std::shared_ptr<Graphing::IGraph> CreateGrapher() override;

View File

@ -18,6 +18,8 @@ namespace Graphing
virtual std::optional<std::vector<std::shared_ptr<IEquation>>> TryInitialize(const IExpression* graphingExp = nullptr) = 0; virtual std::optional<std::vector<std::shared_ptr<IEquation>>> TryInitialize(const IExpression* graphingExp = nullptr) = 0;
virtual HRESULT GetInitializationError() = 0;
virtual IGraphingOptions& GetOptions() = 0; virtual IGraphingOptions& GetOptions() = 0;
virtual std::vector<std::shared_ptr<Graphing::IVariable>> GetVariables() = 0; virtual std::vector<std::shared_ptr<Graphing::IVariable>> GetVariables() = 0;

View File

@ -64,9 +64,11 @@ namespace Graphing
virtual IEvalOptions& EvalOptions() = 0; virtual IEvalOptions& EvalOptions() = 0;
virtual IFormatOptions& FormatOptions() = 0; virtual IFormatOptions& FormatOptions() = 0;
virtual std::unique_ptr<IExpression> ParseInput(const std::wstring& input) = 0; virtual std::unique_ptr<IExpression> ParseInput(const std::wstring& input, int& errorCodeOut, int& errorTypeOut) = 0;
virtual std::shared_ptr<IGraph> CreateGrapher(const IExpression* expression) = 0;
virtual void HRErrorToErrorInfo(HRESULT hr, int& errorCodeOut, int& errorTypeOut) = 0;
virtual std::shared_ptr<IGraph> CreateGrapher(const IExpression* expression) = 0;
virtual std::shared_ptr<Graphing::IGraph> CreateGrapher() = 0; virtual std::shared_ptr<Graphing::IGraph> CreateGrapher() = 0;
virtual std::wstring Serialize(const IExpression* expression) = 0; virtual std::wstring Serialize(const IExpression* expression) = 0;