// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" #include "GraphingCalculator.xaml.h" #include "CalcViewModel/Common/AppResourceProvider.h" #include "CalcViewModel/Common/TraceLogger.h" #include "CalcViewModel/Common/LocalizationSettings.h" #include "CalcViewModel/Common/LocalizationStringUtil.h" #include "Common/KeyboardShortcutManager.h" #include "CalcViewModel/Common/Automation/NarratorAnnouncement.h" #include "CalcViewModel/Common/Automation/NarratorNotifier.h" #include "Controls/CalculationResult.h" #include "CalcManager/NumberFormattingUtils.h" #include "Calculator/Controls/EquationTextBox.h" #include "Calculator/Views/GraphingCalculator/EquationInputArea.xaml.h" #include "CalcViewModel/Common/Utils.h" #include "GraphingSettings.xaml.h" using namespace CalculatorApp; using namespace CalculatorApp::Common; using namespace CalculatorApp::Common::Automation; using namespace CalculatorApp::Controls; using namespace CalculatorApp::ViewModel; using namespace CalcManager::NumberFormattingUtils; using namespace concurrency; using namespace GraphControl; using namespace Platform; using namespace Platform::Collections; using namespace std; using namespace std::chrono; using namespace Utils; using namespace Windows::ApplicationModel::DataTransfer; using namespace Windows::ApplicationModel::Resources; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::Storage; using namespace Windows::Storage::Streams; using namespace Windows::System; using namespace Windows::UI::Core; using namespace Windows::UI::Input; using namespace Windows::UI::ViewManagement; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Automation; using namespace Windows::UI::Xaml::Automation::Peers; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Controls::Primitives; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Media::Imaging; using namespace Windows::UI::Popups; using namespace Windows::UI::ViewManagement; constexpr auto sc_ViewModelPropertyName = L"ViewModel"; constexpr auto sc_IsGraphThemeMatchApp = L"IsGraphThemeMatchApp"; DEPENDENCY_PROPERTY_INITIALIZATION(GraphingCalculator, IsSmallState); DEPENDENCY_PROPERTY_INITIALIZATION(GraphingCalculator, GraphControlAutomationName); GraphingCalculator::GraphingCalculator() : m_accessibilitySettings{ ref new AccessibilitySettings() } { InitializeComponent(); DataTransferManager ^ dataTransferManager = DataTransferManager::GetForCurrentView(); // Register the current control as a share source. m_dataRequestedToken = dataTransferManager->DataRequested += ref new TypedEventHandler(this, &GraphingCalculator::OnDataRequested); // Request notifications when we should be showing the trace values GraphingControl->TracingChangedEvent += ref new TracingChangedEventHandler(this, &GraphingCalculator::OnShowTracePopupChanged); // And when the actual trace value changes GraphingControl->TracingValueChangedEvent += ref new TracingValueChangedEventHandler(this, &GraphingCalculator::OnTracePointChanged); // Update where the pointer value is (ie: where the user cursor from keyboard inputs moves the point to) GraphingControl->PointerValueChangedEvent += ref new PointerValueChangedEventHandler(this, &GraphingCalculator::OnPointerPointChanged); GraphingControl->UseCommaDecimalSeperator = LocalizationSettings::GetInstance().GetDecimalSeparator() == ','; // OemMinus and OemAdd aren't declared in the VirtualKey enum, we can't add this accelerator XAML-side auto virtualKey = ref new KeyboardAccelerator(); virtualKey->Key = (VirtualKey)189; // OemPlus key virtualKey->Modifiers = VirtualKeyModifiers::Control; ZoomOutButton->KeyboardAccelerators->Append(virtualKey); virtualKey = ref new KeyboardAccelerator(); virtualKey->Key = (VirtualKey)187; // OemAdd key virtualKey->Modifiers = VirtualKeyModifiers::Control; ZoomInButton->KeyboardAccelerators->Append(virtualKey); if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent(L"Windows.UI.Xaml.Media.ThemeShadow")) { SharedShadow->Receivers->Append(GraphingControl); } m_accessibilitySettings->HighContrastChanged += ref new TypedEventHandler(this, &GraphingCalculator::OnHighContrastChanged); m_uiSettings = ref new UISettings(); m_uiSettings->ColorValuesChanged += ref new TypedEventHandler(this, &GraphingCalculator::OnColorValuesChanged); ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; if (localSettings != nullptr && localSettings->Values->HasKey(StringReference(sc_IsGraphThemeMatchApp))) { auto isMatchAppLocalSetting = static_cast(localSettings->Values->Lookup(StringReference(sc_IsGraphThemeMatchApp))); if (isMatchAppLocalSetting) { IsMatchAppTheme = true; TraceLogger::GetInstance()->LogGraphTheme(L"IsMatchAppTheme"); } } else { IsMatchAppTheme = false; TraceLogger::GetInstance()->LogGraphTheme(L"IsAlwaysLightTheme"); } UpdateGraphTheme(); } void GraphingCalculator::OnShowTracePopupChanged(bool newValue) { if ((TraceValuePopup->Visibility == ::Visibility::Visible) != newValue) { TraceValuePopup->Visibility = newValue ? ::Visibility::Visible : ::Visibility::Collapsed; } } void GraphingCalculator::GraphingCalculator_DataContextChanged(FrameworkElement ^ sender, DataContextChangedEventArgs ^ args) { if (ViewModel != nullptr) { if (m_vectorChangedToken.Value != 0) { ViewModel->Equations->VectorChanged -= m_vectorChangedToken; m_vectorChangedToken.Value = 0; } if (m_variableUpdatedToken.Value != 0) { ViewModel->VariableUpdated -= m_variableUpdatedToken; m_variableUpdatedToken.Value = 0; } } ViewModel = dynamic_cast(args->NewValue); m_vectorChangedToken = ViewModel->Equations->VectorChanged += ref new VectorChangedEventHandler(this, &GraphingCalculator::OnEquationsVectorChanged); m_variableUpdatedToken = ViewModel->VariableUpdated += ref new EventHandler(this, &CalculatorApp::GraphingCalculator::OnVariableChanged); UpdateGraphAutomationName(); } void GraphingCalculator::OnEquationsVectorChanged(IObservableVector ^ sender, IVectorChangedEventArgs ^ event) { // If an item is already added to the graph, changing it should automatically trigger a graph update if (event->CollectionChange == ::CollectionChange::ItemChanged) { return; } // Do not plot the graph if we are removing an empty equation, just remove it if (event->CollectionChange == ::CollectionChange::ItemRemoved) { auto itemToRemove = GraphingControl->Equations->GetAt(event->Index); if (itemToRemove->Expression->IsEmpty()) { GraphingControl->Equations->RemoveAt(event->Index); if (GraphingControl->Equations->Size == 1 && GraphingControl->Equations->GetAt(0)->Expression->IsEmpty()) { IsManualAdjustment = false; } return; } } // Do not plot the graph if we are adding an empty equation, just add it if (event->CollectionChange == ::CollectionChange::ItemInserted) { auto itemToAdd = sender->GetAt(event->Index); if (itemToAdd->Expression->IsEmpty()) { GraphingControl->Equations->Append(itemToAdd->GraphEquation); return; } } // We are either adding or removing a valid equation, or resetting the collection. We will need to plot the graph GraphingControl->Equations->Clear(); for (auto equationViewModel : ViewModel->Equations) { GraphingControl->Equations->Append(equationViewModel->GraphEquation); } GraphingControl->PlotGraph(false); } wstringstream GraphingCalculator::FormatTraceValue(double min, double max, float pointValue) { wstringstream traceValueString; // Extract precision we will round to auto precision = static_cast(floor(log10(max - min)) - 3); // Determine if we want to show scientific notation instead if (precision <= -7 || precision >= 7) { traceValueString << scientific; } else { traceValueString << fixed; } // If we are rounding to a decimal place, set the precision if (precision < 0) { traceValueString << setprecision(::min(7, abs(precision))) << pointValue; } else { traceValueString << setprecision(0) << pointValue; } return traceValueString; } void GraphingCalculator::OnTracePointChanged(Point newPoint) { wstringstream traceValueString; double xAxisMin, xAxisMax, yAxisMin, yAxisMax; GraphingControl->GetDisplayRanges(&xAxisMin, &xAxisMax, &yAxisMin, &yAxisMax); traceValueString << "(" << FormatTraceValue(xAxisMin, xAxisMax, newPoint.X).str() << ", " << FormatTraceValue(yAxisMin, yAxisMax, newPoint.Y).str() << ")"; TraceValue->Text = ref new String(traceValueString.str().c_str()); auto peer = FrameworkElementAutomationPeer::FromElement(TraceValue); if (peer != nullptr) { peer->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged); } PositionGraphPopup(); } void CalculatorApp::GraphingCalculator::OnPointerPointChanged(Windows::Foundation::Point newPoint) { if (TracePointer != nullptr) { // Move the pointer glyph to where it is supposed to be. Canvas::SetLeft(TracePointer, newPoint.X); Canvas::SetTop(TracePointer, newPoint.Y); } } GraphingCalculatorViewModel ^ GraphingCalculator::ViewModel::get() { return m_viewModel; } void GraphingCalculator::ViewModel::set(GraphingCalculatorViewModel ^ vm) { if (m_viewModel != vm) { m_viewModel = vm; RaisePropertyChanged(StringReference(sc_ViewModelPropertyName)); } } void CalculatorApp::GraphingCalculator::OnShareClick(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { // Ask the OS to start a share action. DataTransferManager::ShowShareUI(); TraceLogger::GetInstance()->LogGraphButtonClicked(GraphButton::Share, GraphButtonValue::None); } // When share is invoked (by the user or programmatically) the event handler we registered will be called to populate the data package with the // data to be shared. We will request the current graph image from the grapher as a stream that will pass to the share request. void GraphingCalculator::OnDataRequested(DataTransferManager ^ sender, DataRequestedEventArgs ^ args) { auto resourceLoader = ResourceLoader::GetForCurrentView(); try { std::wstringstream rawHtml; std::wstringstream equationHtml; rawHtml << L"

" << resourceLoader->GetString(L"GraphImageAltText")->Data() << "

"; auto equations = ViewModel->Equations; bool hasEquations = false; if (equations->Size > 0) { equationHtml << L""; equationHtml << resourceLoader->GetString(L"EquationsShareHeader")->Data(); equationHtml << L""; equationHtml << L""; for (auto equation : equations) { auto expression = equation->Expression; if (expression->IsEmpty()) { continue; } auto color = equation->LineColor; hasEquations = true; expression = GraphingControl->ConvertToLinear(expression); std::wstringstream equationColorHtml; equationColorHtml << L"color:rgb(" << color.R.ToString()->Data() << L"," << color.G.ToString()->Data() << L"," << color.B.ToString()->Data() << L");"; equationHtml << L""; } equationHtml << L"
"; equationHtml << EscapeHtmlSpecialCharacters(expression)->Data(); equationHtml << L"
"; } if (hasEquations) { rawHtml << equationHtml.str(); } auto variables = ViewModel->Variables; if (variables->Size > 0) { auto localizedSeperator = LocalizationSettings::GetInstance().GetListSeparator() + L" "; rawHtml << L"
"; rawHtml << resourceLoader->GetString(L"VariablesShareHeader")->Data(); rawHtml << L"
"; for (unsigned i = 0; i < variables->Size; i++) { auto name = variables->GetAt(i)->Name; auto value = variables->GetAt(i)->Value; rawHtml << name->Data(); rawHtml << L"="; auto formattedValue = to_wstring(value); TrimTrailingZeros(formattedValue); rawHtml << formattedValue; if (variables->Size - 1 != i) { rawHtml << localizedSeperator; } } rawHtml << L"
"; } rawHtml << L"

"; // Shortcut to the request data auto requestData = args->Request->Data; DataPackage ^ dataPackage = ref new DataPackage(); auto html = HtmlFormatHelper::CreateHtmlFormat(ref new String(rawHtml.str().c_str())); requestData->Properties->Title = resourceLoader->GetString(L"ShareActionTitle"); requestData->SetHtmlFormat(html); auto bitmapStream = GraphingControl->GetGraphBitmapStream(); requestData->ResourceMap->Insert(ref new String(L"graph.png"), bitmapStream); requestData->SetBitmap(bitmapStream); // Set the thumbnail image (in case the share target can't handle HTML) requestData->Properties->Thumbnail = bitmapStream; } catch (Exception ^ ex) { TraceLogger::GetInstance()->LogPlatformException(ViewMode::Graphing, __FUNCTIONW__, ex); // Something went wrong, notify the user. auto errDialog = ref new ContentDialog(); errDialog->Content = resourceLoader->GetString(L"ShareActionErrorMessage"); errDialog->CloseButtonText = resourceLoader->GetString(L"ShareActionErrorOk"); errDialog->ShowAsync(); } } void GraphingCalculator::GraphingControl_VariablesUpdated(Object ^, Object ^) { m_viewModel->UpdateVariables(GraphingControl->Variables); } void GraphingCalculator::OnVariableChanged(Platform::Object ^ sender, VariableChangedEventArgs args) { GraphingControl->SetVariable(args.variableName, args.newValue); } void GraphingCalculator::OnZoomInCommand(Object ^ /* parameter */) { GraphingControl->ZoomFromCenter(zoomInScale); TraceLogger::GetInstance()->LogGraphButtonClicked(GraphButton::ZoomIn, GraphButtonValue::None); } void GraphingCalculator::OnZoomOutCommand(Object ^ /* parameter */) { GraphingControl->ZoomFromCenter(zoomOutScale); TraceLogger::GetInstance()->LogGraphButtonClicked(GraphButton::ZoomOut, GraphButtonValue::None); } String ^ GraphingCalculator::GetTracingLegend(Platform::IBox ^ isTracing) { auto resProvider = AppResourceProvider::GetInstance(); return isTracing != nullptr && isTracing->Value ? resProvider->GetResourceString(L"disableTracingButtonToolTip") : resProvider->GetResourceString(L"enableTracingButtonToolTip"); } void GraphingCalculator::GraphingControl_LostFocus(Object ^ sender, RoutedEventArgs ^ e) { // If the graph is losing focus while we are in active tracing we need to turn it off so we don't try to eat keys in other controls. if (GraphingControl->ActiveTracing) { if (ActiveTracing->Equals(FocusManager::GetFocusedElement()) && ActiveTracing->IsPressed) { m_ActiveTracingPointerCaptureLost = ActiveTracing->PointerCaptureLost += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &CalculatorApp::GraphingCalculator::ActiveTracing_PointerCaptureLost); } else { GraphingControl->ActiveTracing = false; OnShowTracePopupChanged(false); } } } void CalculatorApp::GraphingCalculator::ActiveTracing_PointerCaptureLost(Platform::Object ^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) { if (m_ActiveTracingPointerCaptureLost.Value != 0) { ActiveTracing->PointerCaptureLost -= m_ActiveTracingPointerCaptureLost; m_ActiveTracingPointerCaptureLost.Value = 0; } if (GraphingControl->ActiveTracing) { GraphingControl->ActiveTracing = false; OnShowTracePopupChanged(false); } } void GraphingCalculator::GraphingControl_LosingFocus(UIElement ^ sender, LosingFocusEventArgs ^ args) { auto newFocusElement = dynamic_cast(args->NewFocusedElement); if (newFocusElement == nullptr || newFocusElement->Name == nullptr) { // Because clicking on the swap chain panel will try to move focus to a control that can't actually take focus // we will get a null destination. So we are going to try and cancel that request. // If the destination is not in our application we will also get a null destination but the cancel will fail so it doesn't hurt to try. args->TryCancel(); } } void GraphingCalculator::OnEquationKeyGraphFeaturesRequested(Object ^ sender, EquationViewModel ^ equationViewModel) { ViewModel->SetSelectedEquation(equationViewModel); if (equationViewModel != nullptr) { auto keyGraphFeatureInfo = GraphingControl->AnalyzeEquation(equationViewModel->GraphEquation); equationViewModel->PopulateKeyGraphFeatures(keyGraphFeatureInfo); IsKeyGraphFeaturesVisible = true; equationViewModel->GraphEquation->IsSelected = true; } } void GraphingCalculator::OnKeyGraphFeaturesClosed(Object ^ sender, RoutedEventArgs ^ e) { IsKeyGraphFeaturesVisible = false; EquationInputAreaControl->FocusEquationTextBox(ViewModel->SelectedEquation); } Visibility GraphingCalculator::ShouldDisplayPanel(bool isSmallState, bool isEquationModeActivated, bool isGraphPanel) { return (!isSmallState || isEquationModeActivated ^ isGraphPanel) ? ::Visibility::Visible : ::Visibility::Collapsed; } Platform::String ^ GraphingCalculator::GetInfoForSwitchModeToggleButton(bool isChecked) { if (isChecked) { return AppResourceProvider::GetInstance()->GetResourceString(L"GraphSwitchToGraphMode"); } else { return AppResourceProvider::GetInstance()->GetResourceString(L"GraphSwitchToEquationMode"); } } void GraphingCalculator::SwitchModeToggleButton_Toggled(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { auto narratorNotifier = ref new NarratorNotifier(); String ^ announcementText; if (SwitchModeToggleButton->IsOn) { announcementText = AppResourceProvider::GetInstance()->GetResourceString(L"GraphSwitchedToEquationModeAnnouncement"); } else { announcementText = AppResourceProvider::GetInstance()->GetResourceString(L"GraphSwitchedToGraphModeAnnouncement"); } auto announcement = CalculatorAnnouncement::GetGraphModeChangedAnnouncement(announcementText); narratorNotifier->Announce(announcement); } void GraphingCalculator::PositionGraphPopup() { if (GraphingControl->TraceLocation.X + 15 + TraceValuePopup->ActualWidth >= GraphingControl->ActualWidth) { TraceValuePopupTransform->X = (int)GraphingControl->TraceLocation.X - 15 - TraceValuePopup->ActualWidth; } else { TraceValuePopupTransform->X = (int)GraphingControl->TraceLocation.X + 15; } if (GraphingControl->TraceLocation.Y >= 30) { TraceValuePopupTransform->Y = (int)GraphingControl->TraceLocation.Y - 30; } else { TraceValuePopupTransform->Y = (int)GraphingControl->TraceLocation.Y; } } void GraphingCalculator::TraceValuePopup_SizeChanged(Object ^ sender, SizeChangedEventArgs ^ e) { PositionGraphPopup(); } ::Visibility GraphingCalculator::ManageEditVariablesButtonVisibility(unsigned int numberOfVariables) { return numberOfVariables == 0 ? ::Visibility::Collapsed : ::Visibility::Visible; } void CalculatorApp::GraphingCalculator::ActiveTracing_Checked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { if (!m_cursorShadowInitialized) { this->FindName(L"TraceCanvas"); // add shadow to the trace pointer AddTracePointerShadow(); // hide the shadow in high contrast mode CursorShadow->Visibility = m_accessibilitySettings->HighContrast ? ::Visibility::Collapsed : ::Visibility::Visible; Canvas::SetLeft(TracePointer, TraceCanvas->ActualWidth / 2 + 40); Canvas::SetTop(TracePointer, TraceCanvas->ActualHeight / 2 - 40); m_cursorShadowInitialized = true; } FocusManager::TryFocusAsync(GraphingControl, ::FocusState::Programmatic); m_activeTracingKeyUpToken = Window::Current->CoreWindow->KeyUp += ref new TypedEventHandler( this, &CalculatorApp::GraphingCalculator::ActiveTracing_KeyUp); KeyboardShortcutManager::IgnoreEscape(false); TracePointer->Visibility = ::Visibility::Visible; TraceLogger::GetInstance()->LogGraphButtonClicked(GraphButton::ActiveTracingChecked, GraphButtonValue::None); } void CalculatorApp::GraphingCalculator::ActiveTracing_Unchecked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { if (m_ActiveTracingPointerCaptureLost.Value != 0) { ActiveTracing->PointerCaptureLost -= m_ActiveTracingPointerCaptureLost; m_ActiveTracingPointerCaptureLost.Value = 0; } if (m_activeTracingKeyUpToken.Value != 0) { Window::Current->CoreWindow->KeyUp -= m_activeTracingKeyUpToken; m_activeTracingKeyUpToken.Value = 0; } KeyboardShortcutManager::HonorEscape(); TracePointer->Visibility = ::Visibility::Collapsed; TraceLogger::GetInstance()->LogGraphButtonClicked(GraphButton::ActiveTracingUnchecked, GraphButtonValue::None); } void CalculatorApp::GraphingCalculator::ActiveTracing_KeyUp(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ args) { if (args->VirtualKey == VirtualKey::Escape) { GraphingControl->ActiveTracing = false; args->Handled = true; } } void GraphingCalculator::GraphSettingsButton_Click(Object ^ sender, RoutedEventArgs ^ e) { DisplayGraphSettings(); TraceLogger::GetInstance()->LogGraphButtonClicked(GraphButton::GraphSettings, GraphButtonValue::None); } void GraphingCalculator::DisplayGraphSettings() { if (m_graphSettings == nullptr) { m_graphSettings = ref new GraphingSettings(); m_graphSettings->GraphThemeSettingChanged += ref new EventHandler(this, &GraphingCalculator::OnGraphThemeSettingChanged); } m_graphSettings->IsMatchAppTheme = IsMatchAppTheme; m_graphSettings->SetGrapher(this->GraphingControl); auto flyoutGraphSettings = ref new Flyout(); flyoutGraphSettings->Content = m_graphSettings; flyoutGraphSettings->Closing += ref new TypedEventHandler(this, &GraphingCalculator::OnSettingsFlyout_Closing); auto options = ref new FlyoutShowOptions(); options->Placement = FlyoutPlacementMode::BottomEdgeAlignedRight; flyoutGraphSettings->ShowAt(GraphSettingsButton, options); } void CalculatorApp::GraphingCalculator::AddTracePointerShadow() { auto compositor = ::Hosting::ElementCompositionPreview::GetElementVisual(CursorPath)->Compositor; auto dropShadow = compositor->CreateDropShadow(); dropShadow->BlurRadius = 6; dropShadow->Opacity = 0.33f; dropShadow->Offset = ::Numerics::float3(2, 2, 0); dropShadow->Mask = CursorPath->GetAlphaMask(); auto shadowSpriteVisual = compositor->CreateSpriteVisual(); shadowSpriteVisual->Size = ::Numerics::float2(18, 18); shadowSpriteVisual->Shadow = dropShadow; ::Hosting::ElementCompositionPreview::SetElementChildVisual(CursorShadow, shadowSpriteVisual); } void GraphingCalculator::OnSettingsFlyout_Closing(FlyoutBase ^ sender, FlyoutBaseClosingEventArgs ^ args) { auto flyout = static_cast(sender); auto graphingSetting = static_cast(flyout->Content); } void GraphingCalculator::Canvas_SizeChanged(Object ^ /*sender*/, SizeChangedEventArgs ^ e) { // Initialize the pointer to the correct location to match initial value in GraphControl\DirectX\RenderMain.cpp if (TracePointer != nullptr) { Canvas::SetLeft(TracePointer, e->NewSize.Width / 2 + 40); Canvas::SetTop(TracePointer, e->NewSize.Height / 2 - 40); } } void GraphingCalculator::OnHighContrastChanged(AccessibilitySettings ^ sender, Object ^ /*args*/) { if (CursorShadow != nullptr) { CursorShadow->Visibility = sender->HighContrast ? ::Visibility::Collapsed : ::Visibility::Visible; } UpdateGraphTheme(); } void GraphingCalculator::OnEquationFormatRequested(Object ^ sender, MathRichEditBoxFormatRequest ^ e) { if (!e->OriginalText->IsEmpty()) { e->FormattedText = GraphingControl->FormatMathML(e->OriginalText); } } void GraphingCalculator::SetDefaultFocus() { if (IsSmallState) { SwitchModeToggleButton->Focus(::FocusState::Programmatic); } else { EquationInputAreaControl->Focus(::FocusState::Programmatic); } } void GraphingCalculator::GraphingControl_GraphViewChangedEvent(Object ^ sender, GraphViewChangedReason reason) { if (reason == GraphViewChangedReason::Manipulation) { IsManualAdjustment = true; } else { IsManualAdjustment = false; } UpdateGraphAutomationName(); auto announcement = CalculatorAnnouncement::GetGraphViewChangedAnnouncement(GraphControlAutomationName); auto peer = FrameworkElementAutomationPeer::FromElement(GraphingControl); if (peer != nullptr) { peer->RaiseNotificationEvent(announcement->Kind, announcement->Processing, announcement->Announcement, announcement->ActivityId); } } void GraphingCalculator::GraphingControl_GraphPlottedEvent(Object ^ sender, RoutedEventArgs ^ e) { UpdateGraphAutomationName(); } void GraphingCalculator::UpdateGraphAutomationName() { int numEquations = 0; double xAxisMin, xAxisMax, yAxisMin, yAxisMax; // Only count equations that are graphed for (auto equation : ViewModel->Equations) { if (equation->GraphEquation->IsValidated) { numEquations++; } } GraphingControl->GetDisplayRanges(&xAxisMin, &xAxisMax, &yAxisMin, &yAxisMax); GraphControlAutomationName = LocalizationStringUtil::GetLocalizedString( AppResourceProvider::GetInstance()->GetResourceString(L"graphAutomationName"), xAxisMin.ToString(), xAxisMax.ToString(), yAxisMin.ToString(), yAxisMax.ToString(), numEquations.ToString()); } void GraphingCalculator::GraphMenuFlyoutItem_Click(Object ^ sender, RoutedEventArgs ^ e) { auto dataPackage = ref new DataPackage(); dataPackage->RequestedOperation = ::DataPackageOperation::Copy; auto bitmapStream = GraphingControl->GetGraphBitmapStream(); dataPackage->SetBitmap(bitmapStream); ::Clipboard::SetContent(dataPackage); } void GraphingCalculator::OnVisualStateChanged(Object ^ sender, VisualStateChangedEventArgs ^ e) { TraceLogger::GetInstance()->LogVisualStateChanged(ViewMode::Graphing, e->NewState->Name, false); } void GraphingCalculator::OnColorValuesChanged(Windows::UI::ViewManagement::UISettings ^ sender, Platform::Object ^ args) { WeakReference weakThis(this); this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([weakThis]() { auto refThis = weakThis.Resolve(); if (refThis != nullptr && refThis->IsMatchAppTheme) { refThis->UpdateGraphTheme(); } })); } void GraphingCalculator::UpdateGraphTheme() { if (m_accessibilitySettings->HighContrast) { VisualStateManager::GoToState(this, L"GrapherHighContrast", true); return; } if (IsMatchAppTheme && Application::Current->RequestedTheme == ApplicationTheme::Dark) { VisualStateManager::GoToState(this, L"GrapherDarkTheme", true); } else { VisualStateManager::GoToState(this, L"GrapherLightTheme", true); } } void GraphingCalculator::OnGraphThemeSettingChanged(Object ^ sender, bool isMatchAppTheme) { if (IsMatchAppTheme == isMatchAppTheme) { return; } IsMatchAppTheme = isMatchAppTheme; WeakReference weakThis(this); this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([weakThis]() { auto refThis = weakThis.Resolve(); if (refThis != nullptr) { refThis->UpdateGraphTheme(); } })); } void GraphingCalculator::GraphViewButton_Click(Object ^ sender, RoutedEventArgs ^ e) { auto narratorNotifier = ref new NarratorNotifier(); String ^ announcementText; if (IsManualAdjustment) { announcementText = AppResourceProvider::GetInstance()->GetResourceString(L"GraphViewManualAdjustmentAnnouncement"); } else { GraphingControl->ResetGrid(); announcementText = AppResourceProvider::GetInstance()->GetResourceString(L"GraphViewAutomaticBestFitAnnouncement"); } auto announcement = CalculatorAnnouncement::GetGraphViewBestFitChangedAnnouncement(announcementText); narratorNotifier->Announce(announcement); TraceLogger::GetInstance()->LogGraphButtonClicked(GraphButton::GraphView, IsManualAdjustment ? GraphButtonValue::ManualAdjustment : GraphButtonValue::AutomaticBestFit); }