// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" #include "MathRichEditBox.h" using namespace Platform; using namespace CalculatorApp; using namespace CalculatorApp::Common; using namespace CalculatorApp::Controls; using namespace std; using namespace Windows::ApplicationModel; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Text; using namespace Windows::Foundation::Collections; using namespace Windows::System; DEPENDENCY_PROPERTY_INITIALIZATION(MathRichEditBox, MathText); // TODO remove when Windows 10 version 2004 SDK is adopted namespace Windows_2004_Prerelease { enum RichEditMathMode : int { NoMath, MathOnly, }; MIDL_INTERFACE("619c20f2-cb3b-4521-981f-2865b1b93f04") ITextDocument4 : public IInspectable { public: virtual HRESULT STDMETHODCALLTYPE SetMath(HSTRING value) = 0; virtual HRESULT STDMETHODCALLTYPE GetMath(HSTRING * value) = 0; virtual HRESULT STDMETHODCALLTYPE SetMathMode(RichEditMathMode mathMode) = 0; }; } MathRichEditBox::MathRichEditBox() { static LimitedAccessFeatureStatus m_lafResultStatus; String ^ packageName = Package::Current->Id->Name; if (packageName == L"Microsoft.WindowsCalculator.Dev") { m_lafResultStatus = LimitedAccessFeatures::TryUnlockFeature( "com.microsoft.windows.richeditmath", "BeDD/jxKhz/yfVNA11t4uA==", // Microsoft.WindowsCalculator.Dev "8wekyb3d8bbwe has registered their use of com.microsoft.windows.richeditmath with Microsoft and agrees to the terms of use.") ->Status; } else if (packageName == L"Microsoft.WindowsCalculator") { m_lafResultStatus = LimitedAccessFeatures::TryUnlockFeature( "com.microsoft.windows.richeditmath", "pfanNuxnzo+mAkBQ3N/rGQ==", // Microsoft.WindowsCalculator "8wekyb3d8bbwe has registered their use of com.microsoft.windows.richeditmath with Microsoft and agrees to the terms of use.") ->Status; } // TODO when Windows 10 version 2004 SDK is adopted, replace with: // TextDocument->SetMathMode(Windows::UI::Text::RichEditMathMode::MathOnly); Microsoft::WRL::ComPtr textDocument4; reinterpret_cast(this->TextDocument)->QueryInterface(IID_PPV_ARGS(&textDocument4)); auto hr = textDocument4->SetMathMode(Windows_2004_Prerelease::RichEditMathMode::MathOnly); if (FAILED(hr)) { throw Exception::CreateException(hr); } this->LosingFocus += ref new Windows::Foundation::TypedEventHandler( this, &CalculatorApp::Controls::MathRichEditBox::OnLosingFocus); this->KeyUp += ref new Windows::UI::Xaml::Input::KeyEventHandler(this, &CalculatorApp::Controls::MathRichEditBox::OnKeyUp); } String ^ MathRichEditBox::GetMathTextProperty() { // TODO when Windows 10 version 2004 SDK is adopted, replace with: // String ^ text; // this->TextDocument->GetMath(&text); // return text; Microsoft::WRL::ComPtr textDocument4; reinterpret_cast(this->TextDocument)->QueryInterface(IID_PPV_ARGS(&textDocument4)); HSTRING math; auto hr = textDocument4->GetMath(&math); if (FAILED(hr)) { throw Exception::CreateException(hr); } return reinterpret_cast(math); } void MathRichEditBox::SetMathTextProperty(String ^ newValue) { bool readOnlyState = this->IsReadOnly; this->IsReadOnly = false; // TODO when Windows 10 version 2004 SDK is adopted, replace with: // TextDocument->SetMath(newValue); Microsoft::WRL::ComPtr textDocument4; reinterpret_cast(this->TextDocument)->QueryInterface(IID_PPV_ARGS(&textDocument4)); auto hr = textDocument4->SetMath(reinterpret_cast(newValue)); if (FAILED(hr)) { throw Exception::CreateException(hr); } this->IsReadOnly = readOnlyState; } void CalculatorApp::Controls::MathRichEditBox::OnLosingFocus(Windows::UI::Xaml::UIElement ^ sender, Windows::UI::Xaml::Input::LosingFocusEventArgs ^ args) { if (!this->IsReadOnly) { SubmitEquation(EquationSubmissionSource::FOCUS_LOST); } } void CalculatorApp::Controls::MathRichEditBox::OnKeyUp(Platform::Object ^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e) { if (!this->IsReadOnly && e->Key == VirtualKey::Enter) { SubmitEquation(EquationSubmissionSource::ENTER_KEY); } } void MathRichEditBox::OnMathTextPropertyChanged(Platform::String ^ oldValue, Platform::String ^ newValue) { SetMathTextProperty(newValue); // Get the new math text directly from the TextBox since the textbox may have changed its formatting SetValue(MathTextProperty, GetMathTextProperty()); } void MathRichEditBox::InsertText(Platform::String ^ text, int cursorOffSet, int selectionLength) { // If the rich edit is empty, the math zone may not exist, and so selection (and thus the resulting text) will not be in a math zone. // If the rich edit has content already, then the mathzone will already be created due to mathonly mode being set and the selection will exist inside the // math zone. To handle this, we will force a math zone to be created in teh case of the text being empty and then replacing the text inside of the math // zone with the newly inserted text. if (GetMathTextProperty() == nullptr) { SetMathTextProperty("x"); TextDocument->Selection->StartPosition = 0; TextDocument->Selection->EndPosition = 1; } // insert the text in place of selection TextDocument->Selection->SetText(Windows::UI::Text::TextSetOptions::FormatRtf, text); // Move the cursor to the next logical place for users to enter text. TextDocument->Selection->StartPosition += cursorOffSet; TextDocument->Selection->EndPosition = TextDocument->Selection->StartPosition + selectionLength; } void MathRichEditBox::BackSpace() { // if anything is selected, just delete the selection. Note: EndPosition can be before start position. if (TextDocument->Selection->StartPosition != TextDocument->Selection->EndPosition) { TextDocument->Selection->SetText(Windows::UI::Text::TextSetOptions::None, L""); return; } // if we are at the start of the string, do nothing if (TextDocument->Selection->StartPosition == 0) { return; } // Select the previous group. TextDocument->Selection->EndPosition = TextDocument->Selection->StartPosition; TextDocument->Selection->StartPosition -= 1; // If the group contains anything complex, we want to give the user a chance to preview the deletion. // If it's a single character, then just delete it. Otherwise do nothing until the user triggers backspace again. auto text = TextDocument->Selection->Text; if (text->Length() == 1) { TextDocument->Selection->SetText(Windows::UI::Text::TextSetOptions::None, L""); } } void MathRichEditBox::SubmitEquation(EquationSubmissionSource source) { // Clear formatting since the graph control doesn't work with bold/underlines auto range = this->TextDocument->GetRange(0, this->TextDocument->Selection->EndPosition); if (range != nullptr) { range->CharacterFormat->Bold = FormatEffect::Off; range->CharacterFormat->Underline = UnderlineType::None; } auto newVal = GetMathTextProperty(); if (MathText != newVal) { // Request the final formatting of the text auto formatRequest = ref new MathRichEditBoxFormatRequest(newVal); FormatRequest(this, formatRequest); if (formatRequest->FormattedText != nullptr && !formatRequest->FormattedText->IsEmpty()) { newVal = formatRequest->FormattedText; } SetValue(MathTextProperty, newVal); EquationSubmitted(this, ref new MathRichEditBoxSubmission(true, source)); } else { EquationSubmitted(this, ref new MathRichEditBoxSubmission(false, source)); } }