diff --git a/src/Calculator/Resources/en-US/Resources.resw b/src/Calculator/Resources/en-US/Resources.resw index e7d703c..2502d4a 100644 --- a/src/Calculator/Resources/en-US/Resources.resw +++ b/src/Calculator/Resources/en-US/Resources.resw @@ -3439,6 +3439,22 @@ Add Equation Placeholder text for the equation input button + + Unable to share at this time. + If there is an error in the sharing action will display a dialog with this text. + + + OK + Used on the dismiss button of the share action error dialog. + + + Look what I graphed. + Sent as part of the shared content. The title for the share. + + + Empty graph equation + When sharing and one of the equations has no content this will be shown in the graph key for that equation. + Variables Header text for variables area diff --git a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml index bd0ec8d..1c7e9aa 100644 --- a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml +++ b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml @@ -31,7 +31,7 @@ Grid.Row="1" Grid.Column="0"> - - + + + + DataRequested += ref new TypedEventHandler(this, &GraphingCalculator::OnDataRequested); } void GraphingCalculator::GraphingCalculator_DataContextChanged(FrameworkElement^ sender, DataContextChangedEventArgs^ args) @@ -55,6 +67,118 @@ void GraphingCalculator::ViewModel::set(GraphingCalculatorViewModel^ vm) } } +void CalculatorApp::GraphingCalculator::OnShareClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) +{ + // Ask the OS to start a share action. + DataTransferManager::ShowShareUI(); +} + +// 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 = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(); + try + { + // Get our title from the localized resources + auto EmptyEquationString = resourceLoader->GetString(L"EmptyEquationString"); + + std::wstring rawHtml = L"

"; + + auto equations = ViewModel->Equations; + rawHtml += L"

"; + rawHtml += L""; + rawHtml += L""; + for (unsigned i = 0; i < equations->Size; i++) + { + auto expression = equations->GetAt(i)->Expression->Data(); + auto color = equations->GetAt(i)->LineColor; + + if (equations->GetAt(i)->Expression->Length() == 0) + { + expression = EmptyEquationString->Data(); + } + + rawHtml += L""; + + rawHtml += L""; + rawHtml += L""; + + rawHtml += L""; + } + rawHtml += L"
Data(); + rawHtml += L","; + rawHtml += color.G.ToString()->Data(); + rawHtml += L","; + rawHtml += color.B.ToString()->Data(); + rawHtml += L"); \">"; + rawHtml += L""; + rawHtml += expression; + rawHtml += L"

"; + + auto variables = ViewModel->Variables; + rawHtml += L"

"; + rawHtml += L""; + rawHtml += L""; + for (unsigned i = 0; i < variables->Size; i++) + { + auto name = variables->GetAt(i)->Name; + auto value = variables->GetAt(i)->Value; + + if (name->Length() >= 0) + { + rawHtml += L""; + + rawHtml += L""; + rawHtml += L""; + + rawHtml += L""; + } + } + rawHtml += L"
"; + rawHtml += name->Data(); + rawHtml += L""; + rawHtml += std::to_wstring(value); + 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.c_str())); + + auto titleString = resourceLoader->GetString(L"ShareActionTitle"); + requestData->Properties->Title = titleString; + + requestData->SetHtmlFormat(html); + + auto bitmapStream = GraphingControl->GetGraphBitmapStream(); + + requestData->ResourceMap->Insert(ref new String(L"graph.png"), bitmapStream); + + // Set the thumbnail image (in case the share target can't handle HTML) + requestData->Properties->Thumbnail = bitmapStream; + + // And the bitmap (in case the share target can't handle HTML) + requestData->SetBitmap(bitmapStream); + } + catch(Exception ^ ex) + { + TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, ex); + + // Something went wrong, notify the user. + auto errorTitleString = resourceLoader->GetString(L"ShareActionErrorMessage"); + auto errorOkString = resourceLoader->GetString(L"ShareActionErrorOk"); + auto errDialog = ref new Windows::UI::Xaml::Controls::ContentDialog(); + + errDialog->Content = errorTitleString; + errDialog->CloseButtonText = errorOkString; + errDialog->ShowAsync(); + } +} + void GraphingCalculator::GraphVariablesUpdated(Object^, Object^) { m_viewModel->UpdateVariables(GraphingControl->Variables); diff --git a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h index 8faef86..4212ea3 100644 --- a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h +++ b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h @@ -33,6 +33,15 @@ namespace CalculatorApp CalculatorApp::ViewModel::GraphingCalculatorViewModel^ m_viewModel; + void OnShareClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + + private: + Windows::Foundation::EventRegistrationToken m_dataRequestedToken; + + void OnDataRequested(Windows::ApplicationModel::DataTransfer::DataTransferManager^ sender, Windows::ApplicationModel::DataTransfer::DataRequestedEventArgs^ e); + void CommandInvokedHandler(Windows::UI::Popups::IUICommand^ command); + void TextBoxGotFocus(Windows::UI::Xaml::Controls::TextBox^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); }; + } diff --git a/src/GraphControl/Control/Grapher.cpp b/src/GraphControl/Control/Grapher.cpp index 643b504..66f2375 100644 --- a/src/GraphControl/Control/Grapher.cpp +++ b/src/GraphControl/Control/Grapher.cpp @@ -10,6 +10,7 @@ using namespace std; using namespace Windows::Devices::Input; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; +using namespace Windows::Storage::Streams; using namespace Windows::System; using namespace Windows::UI; using namespace Windows::UI::Input; @@ -49,7 +50,7 @@ namespace GraphControl DependencyProperty^ Grapher::s_equationsSourceProperty; DependencyProperty^ Grapher::s_variablesProperty; DependencyProperty^ Grapher::s_forceProportionalAxesTemplateProperty; - + Grapher::Grapher() : m_solver{ IMathSolver::CreateMathSolver() } , m_graph{ m_solver->CreateGrapher() } @@ -122,7 +123,7 @@ namespace GraphControl { s_equationsProperty = DependencyProperty::Register( StringReference(s_propertyName_Equations), - EquationCollection::typeid , + EquationCollection::typeid, Grapher::typeid, ref new PropertyMetadata( nullptr, @@ -555,7 +556,7 @@ namespace GraphControl // For scaling, the graphing engine interprets x,y position between the range [-1, 1]. // Translate the pointer position to the [-1, 1] bounds. const auto& pos = currentPointer->Position; - const auto[centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, ActualWidth, ActualHeight); + const auto [centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, ActualWidth, ActualHeight); ScaleRange(centerX, centerY, scale); @@ -563,16 +564,16 @@ namespace GraphControl } void Grapher::OnPointerPressed(PointerRoutedEventArgs^ e) - { + { // Set the pointer capture to the element being interacted with so that only it // will fire pointer-related events CapturePointer(e->Pointer); - } + } void Grapher::OnPointerReleased(PointerRoutedEventArgs^ e) - { + { ReleasePointerCapture(e->Pointer); - } + } void Grapher::OnPointerCanceled(PointerRoutedEventArgs^ e) { @@ -618,7 +619,7 @@ namespace GraphControl // Convert from PointerPosition to graph position. const auto& pos = e->Position; - const auto[centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, width, height); + const auto [centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, width, height); if (FAILED(renderer->ScaleRange(centerX, centerY, scale))) { @@ -635,4 +636,47 @@ namespace GraphControl } } } + + + RandomAccessStreamReference^ Grapher::GetGraphBitmapStream() + { + RandomAccessStreamReference^ outputStream; + + if (m_renderMain != nullptr && m_graph != nullptr) + { + if (auto renderer = m_graph->GetRenderer()) + { + std::shared_ptr < Graphing::IBitmap> BitmapOut; + bool hasSomeMissingDataOut = false; + HRESULT hr = E_FAIL; + hr = renderer->GetBitmap(BitmapOut, hasSomeMissingDataOut); + if (SUCCEEDED(hr)) + { + // Get the raw date + std::vector byteVector = BitmapOut->GetData(); + auto arr = ref new Array(&byteVector[0], (unsigned int)byteVector.size()); + + // create a memory stream wrapper + InMemoryRandomAccessStream^ stream = ref new InMemoryRandomAccessStream(); + + // Get a writer to transfer the data + auto writer = ref new DataWriter(stream->GetOutputStreamAt(0)); + + // write the data + writer->WriteBytes(arr); + writer->StoreAsync()->GetResults(); + + // Get a reference stream to return; + outputStream = RandomAccessStreamReference::CreateFromStream(stream); + } + else + { + OutputDebugString(L"Grapher::GetGraphBitmapStream() unable to get graph image from renderer\r\n"); + winrt::throw_hresult(hr); + } + } + } + + return outputStream; + } } diff --git a/src/GraphControl/Control/Grapher.h b/src/GraphControl/Control/Grapher.h index 8997995..2353e7d 100644 --- a/src/GraphControl/Control/Grapher.h +++ b/src/GraphControl/Control/Grapher.h @@ -194,5 +194,8 @@ namespace GraphControl const std::unique_ptr m_solver; const std::shared_ptr m_graph; + + public: + Windows::Storage::Streams::RandomAccessStreamReference^ GetGraphBitmapStream(); }; } diff --git a/src/GraphingInterfaces/IBitmap.h b/src/GraphingInterfaces/IBitmap.h new file mode 100644 index 0000000..cda8f5b --- /dev/null +++ b/src/GraphingInterfaces/IBitmap.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace Graphing +{ + struct IBitmap + { + virtual const std::vector& GetData() const = 0; + }; +} diff --git a/src/GraphingInterfaces/IGraphRenderer.h b/src/GraphingInterfaces/IGraphRenderer.h index ce72d86..7f28f11 100644 --- a/src/GraphingInterfaces/IGraphRenderer.h +++ b/src/GraphingInterfaces/IGraphRenderer.h @@ -1,25 +1,29 @@ #pragma once #include "Common.h" +#include "IBitmap.h" struct ID2D1Factory; struct ID2D1RenderTarget; namespace Graphing::Renderer { - struct IGraphRenderer : public NonCopyable, public NonMoveable - { - virtual ~IGraphRenderer() = default; + struct IGraphRenderer : public NonCopyable, public NonMoveable + { + virtual ~IGraphRenderer() = default; - virtual HRESULT SetGraphSize(unsigned int width, unsigned int height) = 0; - virtual HRESULT SetDpi(float dpiX, float dpiY) = 0; + virtual HRESULT SetGraphSize(unsigned int width, unsigned int height) = 0; + virtual HRESULT SetDpi(float dpiX, float dpiY) = 0; - virtual HRESULT DrawD2D1(ID2D1Factory* pDirect2dFactory, ID2D1RenderTarget* pRenderTarget, bool& hasSomeMissingDataOut) = 0; - virtual HRESULT GetClosePointData(float inScreenPointX, float inScreenPointY, int& formulaIdOut, float& xScreenPointOut, float& yScreenPointOut, float& xValueOut, float& yValueOut) = 0; - - virtual HRESULT ScaleRange(double centerX, double centerY, double scale) = 0; - virtual HRESULT ChangeRange(ChangeRangeAction action) = 0; - virtual HRESULT MoveRangeByRatio(double ratioX, double ratioY) = 0; - virtual HRESULT ResetRange() = 0; - }; + virtual HRESULT DrawD2D1(ID2D1Factory* pDirect2dFactory, ID2D1RenderTarget* pRenderTarget, bool& hasSomeMissingDataOut) = 0; + virtual HRESULT GetClosePointData(float inScreenPointX, float inScreenPointY, int& formulaIdOut, float& xScreenPointOut, float& yScreenPointOut, float& xValueOut, float& yValueOut) = 0; + + virtual HRESULT ScaleRange(double centerX, double centerY, double scale) = 0; + virtual HRESULT ChangeRange(ChangeRangeAction action) = 0; + virtual HRESULT MoveRangeByRatio(double ratioX, double ratioY) = 0; + virtual HRESULT ResetRange() = 0; + + virtual HRESULT GetBitmap(std::shared_ptr& bitmapOut, bool& hasSomeMissingDataOut) = 0; + + }; }