Adding share functionality to Graphing Calculator (#601)
* Plumebd with data transfer * Getting mainpage to talk to getbitmap. moving share callbacks from mainpage to graphingcalculator * Trying to get bitmap from renderer. * work * Share worked * cleanups * Cleanups progressing * Share working, need loc for title string and user notification incase of a failure. Then add the equations key. * More cleanup, now using share icon image and resources for strings. Still need to do the graph equation key. * Change share to html based start. * Key working, with UL but going to try changing to table. * Fix a html formating error, generating a new UL for each equation. * Switched over to a table for equation key and have color block formating * Updates from PR feedback, using Graphing::IBitmap abstraction. * Update src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.h Fixed Co-Authored-By: Pepe Rivera <joseartrivera@gmail.com> * PR Updates. * Add variables to the graph key. * PR Updates.
This commit is contained in:
parent
46f11c7c72
commit
c1efa3d3e3
@ -3439,6 +3439,22 @@
|
||||
<value>Add Equation</value>
|
||||
<comment>Placeholder text for the equation input button</comment>
|
||||
</data>
|
||||
<data name="ShareActionErrorMessage" xml:space="preserve">
|
||||
<value>Unable to share at this time.</value>
|
||||
<comment>If there is an error in the sharing action will display a dialog with this text.</comment>
|
||||
</data>
|
||||
<data name="ShareActionErrorOk" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
<comment>Used on the dismiss button of the share action error dialog.</comment>
|
||||
</data>
|
||||
<data name="ShareActionTitle" xml:space="preserve">
|
||||
<value>Look what I graphed.</value>
|
||||
<comment>Sent as part of the shared content. The title for the share.</comment>
|
||||
</data>
|
||||
<data name="EmptyEquationString" xml:space="preserve">
|
||||
<value>Empty graph equation</value>
|
||||
<comment>When sharing and one of the equations has no content this will be shown in the graph key for that equation.</comment>
|
||||
</data>
|
||||
<data name="VaiablesHeader.Text" xml:space="preserve">
|
||||
<value>Variables</value>
|
||||
<comment>Header text for variables area</comment>
|
||||
|
@ -31,7 +31,7 @@
|
||||
Grid.Row="1"
|
||||
Grid.Column="0">
|
||||
|
||||
<graphControl:Grapher Name="GraphingControl"
|
||||
<graphControl:Grapher Name="GraphingControl" Grid.Row="0"
|
||||
Margin="4,7,4,4"
|
||||
EquationsSource="{x:Bind ViewModel.Equations, Mode=OneWay}"
|
||||
ForceProportionalAxes="True"
|
||||
@ -229,7 +229,12 @@
|
||||
<RowDefinition Height="3*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<local:EquationInputArea Grid.Row="0" Equations="{x:Bind ViewModel.Equations}"/>
|
||||
<StackPanel>
|
||||
<Button x:Name="Share" Click="OnShareClick">
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph=""/>
|
||||
</Button>
|
||||
<local:EquationInputArea Grid.Row="0" Equations="{x:Bind ViewModel.Equations}"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid x:Name="ButtonContainerGrid"
|
||||
Grid.Row="2"
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "CalcViewModel/Common/TraceLogger.h"
|
||||
#include "GraphingCalculator.xaml.h"
|
||||
#include "CalcViewModel/Common/KeyboardShortcutManager.h"
|
||||
#include "Controls/CalculationResult.h"
|
||||
@ -11,27 +12,38 @@ using namespace CalculatorApp::ViewModel;
|
||||
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::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::System;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Input;
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
using namespace Windows::UI::Xaml::Media::Imaging;
|
||||
using namespace Windows::UI::Popups;
|
||||
|
||||
|
||||
constexpr auto sc_ViewModelPropertyName = L"ViewModel";
|
||||
|
||||
|
||||
GraphingCalculator::GraphingCalculator()
|
||||
{
|
||||
Equation::RegisterDependencyProperties();
|
||||
Grapher::RegisterDependencyProperties();
|
||||
InitializeComponent();
|
||||
|
||||
DataTransferManager^ dataTransferManager = DataTransferManager::GetForCurrentView();
|
||||
|
||||
// Register the current control as a share source.
|
||||
m_dataRequestedToken = dataTransferManager->DataRequested += ref new TypedEventHandler<DataTransferManager^, DataRequestedEventArgs^>(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"<p><img src='graph.png'></p>";
|
||||
|
||||
auto equations = ViewModel->Equations;
|
||||
rawHtml += L"<p><table cellpadding=\"10\">";
|
||||
rawHtml += L"<col width=\"20\">";
|
||||
rawHtml += L"<row height=\"20\">";
|
||||
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"<tr>";
|
||||
|
||||
rawHtml += L"<td style=\"background-color:rgb(";
|
||||
rawHtml += color.R.ToString()->Data();
|
||||
rawHtml += L",";
|
||||
rawHtml += color.G.ToString()->Data();
|
||||
rawHtml += L",";
|
||||
rawHtml += color.B.ToString()->Data();
|
||||
rawHtml += L"); \">";
|
||||
rawHtml += L"</td>";
|
||||
rawHtml += L"<td>";
|
||||
rawHtml += expression;
|
||||
rawHtml += L"</td>";
|
||||
|
||||
rawHtml += L"</tr>";
|
||||
}
|
||||
rawHtml += L"</table></p>";
|
||||
|
||||
auto variables = ViewModel->Variables;
|
||||
rawHtml += L"<p><table cellpadding=\"10\">";
|
||||
rawHtml += L"<col width=\"20\">";
|
||||
rawHtml += L"<row height=\"20\">";
|
||||
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"<tr>";
|
||||
|
||||
rawHtml += L"<td>";
|
||||
rawHtml += name->Data();
|
||||
rawHtml += L"</td>";
|
||||
rawHtml += L"<td>";
|
||||
rawHtml += std::to_wstring(value);
|
||||
rawHtml += L"</td>";
|
||||
|
||||
rawHtml += L"</tr>";
|
||||
}
|
||||
}
|
||||
rawHtml += L"</table></p>";
|
||||
|
||||
|
||||
// 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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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<BYTE> byteVector = BitmapOut->GetData();
|
||||
auto arr = ref new Array<BYTE>(&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;
|
||||
}
|
||||
}
|
||||
|
@ -194,5 +194,8 @@ namespace GraphControl
|
||||
|
||||
const std::unique_ptr<Graphing::IMathSolver> m_solver;
|
||||
const std::shared_ptr<Graphing::IGraph> m_graph;
|
||||
|
||||
public:
|
||||
Windows::Storage::Streams::RandomAccessStreamReference^ GetGraphBitmapStream();
|
||||
};
|
||||
}
|
||||
|
11
src/GraphingInterfaces/IBitmap.h
Normal file
11
src/GraphingInterfaces/IBitmap.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Graphing
|
||||
{
|
||||
struct IBitmap
|
||||
{
|
||||
virtual const std::vector<BYTE>& GetData() const = 0;
|
||||
};
|
||||
}
|
@ -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<Graphing::IBitmap>& bitmapOut, bool& hasSomeMissingDataOut) = 0;
|
||||
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user