Switch to RichEdit math mode in EquationTextBox (#672)

* Use RichEdit Math Mode

* Add comment

Co-Authored-By: Stephanie Anderl <46726333+sanderl@users.noreply.github.com>
This commit is contained in:
Pepe Rivera 2019-09-23 11:51:08 -07:00 committed by Stephanie Anderl
parent 41fbcfe9c5
commit b2dd55a64f
15 changed files with 126 additions and 72 deletions

View File

@ -45,7 +45,7 @@
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.18362.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.18970.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

View File

@ -42,7 +42,7 @@
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.18970.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>

View File

@ -1628,6 +1628,7 @@
<Button x:Name="EquationButton"
MinWidth="44"
MinHeight="44"
VerticalAlignment="Stretch"
Background="{TemplateBinding EquationColor}"
Foreground="{StaticResource SystemChromeWhiteColor}"
Content="ƒₓ">
@ -1651,7 +1652,7 @@
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<RichEditBox x:Name="EquationTextBox"
MinHeight="42"
MinHeight="44"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Stretch"
Style="{StaticResource EquationTextBoxStyle}"

View File

@ -8,7 +8,7 @@
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.18362.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.18970.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
<!-- We want to manually control the MinVersion/MaxVersionTested in the manifest so turn of the replacement. -->
<AppxOSMinVersionReplaceManifestVersion>false</AppxOSMinVersionReplaceManifestVersion>

View File

@ -11,12 +11,14 @@ 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;
using namespace Windows::UI::Text;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Text;
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationColor);
DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, KeyGraphFeaturesContent);
@ -33,9 +35,11 @@ void EquationTextBox::OnApplyTemplate()
if (m_richEditBox != nullptr)
{
m_richEditBox->Loaded += ref new RoutedEventHandler(this, &EquationTextBox::OnRichEditBoxLoaded);
m_richEditBox->GotFocus += ref new RoutedEventHandler(this, &EquationTextBox::OnRichEditBoxGotFocus);
m_richEditBox->LostFocus += ref new RoutedEventHandler(this, &EquationTextBox::OnRichEditBoxLostFocus);
m_richEditBox->TextChanged += ref new RoutedEventHandler(this, &EquationTextBox::OnRichEditBoxTextChanged);
m_richEditBox->SelectionFlyout = nullptr;
}
if (m_equationButton != nullptr)
@ -94,6 +98,22 @@ void EquationTextBox::OnPointerCaptureLost(PointerRoutedEventArgs^ e)
UpdateCommonVisualState();
}
void EquationTextBox::OnKeyDown(KeyRoutedEventArgs ^ e)
{
if (e->Key == VirtualKey::Enter)
{
EquationSubmitted(this, ref new RoutedEventArgs());
}
}
void EquationTextBox::OnLostFocus(RoutedEventArgs ^ e)
{
if (!m_richEditBox->ContextFlyout->IsOpen)
{
EquationSubmitted(this, ref new RoutedEventArgs());
}
}
void EquationTextBox::OnColorFlyoutOpened(Object ^ sender, Object ^ e)
{
m_isColorChooserFlyoutOpen = true;
@ -107,6 +127,15 @@ void EquationTextBox::OnColorFlyoutClosed(Object^ sender, Object^ e)
UpdateCommonVisualState();
}
void EquationTextBox::OnRichEditBoxLoaded(Object ^ sender, RoutedEventArgs ^ e)
{
LimitedAccessFeatures::TryUnlockFeature(
"com.microsoft.windows.richeditmath",
"H6wflFFz3gkOsAHtG/D9Tg==",
"8wekyb3d8bbwe has registered their use of com.microsoft.windows.richeditmath with Microsoft and agrees to the terms of use.");
m_richEditBox->TextDocument->SetMathMode(::RichEditMathMode::MathOnly);
}
void EquationTextBox::OnRichEditBoxTextChanged(Object ^ sender, RoutedEventArgs ^ e)
{
UpdateDeleteButtonVisualState();
@ -120,8 +149,11 @@ void EquationTextBox::OnRichEditBoxGotFocus(Object^ sender, RoutedEventArgs^ e)
}
void EquationTextBox::OnRichEditBoxLostFocus(Object ^ sender, RoutedEventArgs ^ e)
{
if (!m_richEditBox->ContextFlyout->IsOpen)
{
m_isFocused = false;
}
UpdateCommonVisualState();
UpdateDeleteButtonVisualState();
}
@ -130,13 +162,12 @@ void EquationTextBox::OnDeleteButtonClicked(Object^ sender, RoutedEventArgs^ e)
{
if (m_richEditBox != nullptr)
{
m_richEditBox->TextDocument->SetText(::TextSetOptions::None, L"");
m_richEditBox->TextDocument->SetMath(L"");
}
}
void EquationTextBox::OnEquationButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
}
void EquationTextBox::OnRemoveButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
@ -155,7 +186,8 @@ void EquationTextBox::OnColorChooserButtonClicked(Object^ sender, RoutedEventArg
void EquationTextBox::OnFunctionButtonClicked(Object ^ sender, RoutedEventArgs ^ e)
{
auto equationViewModel = static_cast<EquationViewModel ^>(DataContext);
equationViewModel->KeyGraphFeaturesVisibility = (equationViewModel->KeyGraphFeaturesVisibility == ::Visibility::Collapsed) ? ::Visibility::Visible : ::Visibility::Collapsed;
equationViewModel->KeyGraphFeaturesVisibility =
(equationViewModel->KeyGraphFeaturesVisibility == ::Visibility::Collapsed) ? ::Visibility::Visible : ::Visibility::Collapsed;
UpdateCommonVisualState();
}
@ -191,14 +223,22 @@ void EquationTextBox::UpdateCommonVisualState()
VisualStateManager::GoToState(this, state, true);
}
Platform::String ^ EquationTextBox::GetEquationText()
{
String ^ text;
if (m_richEditBox != nullptr)
{
m_richEditBox->TextDocument->GetText(::TextGetOptions::NoHidden, &text);
// Clear formatting since the graph control doesn't work with bold/italic/underlines
ITextRange ^ range = m_richEditBox->TextDocument->GetRange(0, m_richEditBox->TextDocument->Selection->EndPosition);
if (range != nullptr)
{
range->CharacterFormat->Bold = FormatEffect::Off;
range->CharacterFormat->Italic = FormatEffect::Off;
range->CharacterFormat->Underline = UnderlineType::None;
}
m_richEditBox->TextDocument->GetMath(&text);
}
return text;
@ -208,13 +248,18 @@ void EquationTextBox::SetEquationText(Platform::String^ equationText)
{
if (m_richEditBox != nullptr)
{
m_richEditBox->TextDocument->SetText(::TextSetOptions::None, equationText);
m_richEditBox->TextDocument->SetMath(equationText);
}
}
bool EquationTextBox::ShouldDeleteButtonBeVisible()
{
return (!GetEquationText()->IsEmpty() && m_isFocused);
String ^ text;
if (m_richEditBox != nullptr)
{
m_richEditBox->TextDocument->GetMath(&text);
}
return (!text->IsEmpty() && m_isFocused);
}

View File

@ -22,6 +22,7 @@ namespace CalculatorApp
DEPENDENCY_PROPERTY(Windows::UI::Xaml::Controls::Flyout^, ColorChooserFlyout);
event Windows::UI::Xaml::RoutedEventHandler ^ RemoveButtonClicked;
event Windows::UI::Xaml::RoutedEventHandler ^ EquationSubmitted;
Platform::String^ GetEquationText();
void SetEquationText(Platform::String^ equationText);
@ -32,14 +33,18 @@ namespace CalculatorApp
virtual void OnPointerExited(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override;
virtual void OnPointerCanceled(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override;
virtual void OnPointerCaptureLost(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override;
virtual void OnKeyDown(Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e) override;
virtual void OnLostFocus(Windows::UI::Xaml::RoutedEventArgs^ e) override;
private:
void UpdateCommonVisualState();
void UpdateDeleteButtonVisualState();
bool ShouldDeleteButtonBeVisible();
void OnRichEditBoxLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnRichEditBoxGotFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnRichEditBoxLostFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnRichEditBoxLosingFocus(Windows::UI::Xaml::UIElement ^ sender, Windows::UI::Xaml::Input::LosingFocusEventArgs ^ e);
void OnRichEditBoxTextChanged(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnDeleteButtonClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);

View File

@ -45,8 +45,8 @@
Grid.Column="1"
Margin="0,0,3,0"
Style="{StaticResource EquationTextBoxStyle}"
EquationSubmitted="InputTextBox_Submitted"
GotFocus="InputTextBox_GotFocus"
KeyUp="InputTextBox_KeyUp"
LostFocus="InputTextBox_LostFocus"
RemoveButtonClicked="EquationTextBox_RemoveButtonClicked">
<controls:EquationTextBox.EquationColor>

View File

@ -78,22 +78,13 @@ void EquationInputArea::InputTextBox_GotFocus(Object^ sender, RoutedEventArgs^ e
void EquationInputArea::InputTextBox_LostFocus(Object^ sender, RoutedEventArgs^ e)
{
KeyboardShortcutManager::HonorShortcuts(true);
auto tb = static_cast<EquationTextBox^>(sender);
auto eq = static_cast<EquationViewModel^>(tb->DataContext);
tb->SetEquationText(eq->Expression);
}
void EquationInputArea::InputTextBox_KeyUp(Object^ sender, KeyRoutedEventArgs^ e)
{
if (e->Key == VirtualKey::Enter)
void EquationInputArea::InputTextBox_Submitted(Object ^ sender, RoutedEventArgs ^ e)
{
auto tb = static_cast<EquationTextBox^>(sender);
auto eq = static_cast<EquationViewModel^>(tb->DataContext);
eq->Expression = tb->GetEquationText();
e->Handled = true;
}
}
Color EquationInputArea::GetNextLineColor()

View File

@ -23,7 +23,7 @@ namespace CalculatorApp
void InputTextBox_GotFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void InputTextBox_LostFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void InputTextBox_KeyUp(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e);
void InputTextBox_Submitted(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e);
Windows::UI::Color GetNextLineColor();

View File

@ -7,7 +7,7 @@
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18970.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
<UnitTestPlatformVersion Condition="'$(UnitTestPlatformVersion)' == ''">15.0</UnitTestPlatformVersion>

View File

@ -274,7 +274,7 @@ namespace CalculatorUnitTests
void NavCategoryUnitTests::GetPosition()
{
// Position is the 1-based ordering of modes
vector<ViewMode> orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Programmer, ViewMode::Date, ViewMode::Graphing
vector<ViewMode> orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Programmer, ViewMode::Date, ViewMode::Graphing,
ViewMode::Currency, ViewMode::Volume, ViewMode::Length, ViewMode::Weight, ViewMode::Temperature,
ViewMode::Energy, ViewMode::Area, ViewMode::Speed, ViewMode::Time, ViewMode::Power,
ViewMode::Data, ViewMode::Pressure, ViewMode::Angle };

View File

@ -9,6 +9,9 @@ using namespace Windows::UI::Xaml;
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:";
DependencyProperty^ Equation::s_expressionProperty;
static constexpr auto s_propertyName_Expression = L"Expression";
@ -74,7 +77,7 @@ namespace GraphControl
ss << GetRequestHeader()
<< GetExpression()
<< GetLineColor()
<< L")";
<< L"</mfenced></mrow>";
return ss.str();
}
@ -82,19 +85,28 @@ namespace GraphControl
wstring Equation::GetRequestHeader()
{
wstring expr{ Expression->Data() };
if (expr.find(L"=") != wstring::npos)
if (expr.find(L">=<") != wstring::npos)
{
return L"plotEq2d("s;
return L"<mrow><mi>plotEq2d</mi><mfenced separators=\"\">"s;
}
else
{
return L"plot2d("s;
return L"<mrow><mi>plot2d</mi><mfenced separators=\"\">"s;
}
}
wstring Equation::GetExpression()
{
return Expression->Data();
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()

View File

@ -55,7 +55,7 @@ namespace GraphControl
: m_solver{ IMathSolver::CreateMathSolver() }
, m_graph{ m_solver->CreateGrapher() }
{
m_solver->ParsingOptions().SetFormatType(FormatType::Linear);
m_solver->ParsingOptions().SetFormatType(FormatType::MathML);
DefaultStyleKey = StringReference(s_defaultStyleKey);
@ -365,20 +365,20 @@ namespace GraphControl
if (!validEqs.empty())
{
wstringstream ss{};
ss << L"show2d(";
ss << L"<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><mi>show2d</mi><mfenced separators=\"\">";
int numValidEquations = 0;
for (Equation^ eq : validEqs)
{
if (numValidEquations++ > 0)
{
ss << L",";
ss << L"<mo>,</mo>";
}
ss << eq->GetRequest();
}
ss << L")";
ss << L"</mfenced></mrow></math>";
wstring request = ss.str();
unique_ptr<IExpression> graphExpression;

View File

@ -42,7 +42,7 @@
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.18970.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>

View File

@ -42,7 +42,7 @@
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.18970.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>