Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Calculator/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -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 acction 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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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="Segoe MDL2 Assets" Glyph="&#xE72D;"/>
</Button>
<local:EquationInputArea Grid.Row="0" Equations="{x:Bind ViewModel.Equations}"/>
</StackPanel>

<Grid x:Name="ButtonContainerGrid"
Grid.Row="2"
Expand Down
112 changes: 112 additions & 0 deletions src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "GraphingCalculator.xaml.h"
#include "CalcViewModel/Common/KeyboardShortcutManager.h"
#include "Controls/CalculationResult.h"
#include <Collection.h>

using namespace CalculatorApp;
using namespace CalculatorApp::Common;
Expand All @@ -25,13 +26,30 @@ using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Media::Imaging;

using namespace Windows::Foundation;
using namespace Windows::ApplicationModel::DataTransfer;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Xaml::Media;

using namespace Windows::UI::Core;

using namespace Windows::UI::Popups;

constexpr auto sc_ViewModelPropertyName = L"ViewModel";

using namespace Platform::Collections;


GraphingCalculator::GraphingCalculator()
{
Equation::RegisterDependencyProperties();
Grapher::RegisterDependencyProperties();
InitializeComponent();

DataTransferManager^ dataTransferManager = DataTransferManager::GetForCurrentView();

// Register the current control as a share source.
dataRequestedToken = dataTransferManager->DataRequested += ref new TypedEventHandler<DataTransferManager^, DataRequestedEventArgs^>(this, &GraphingCalculator::OnDataRequested);
}

void GraphingCalculator::GraphingCalculator_DataContextChanged(FrameworkElement^ sender, DataContextChangedEventArgs^ args)
Expand All @@ -55,6 +73,100 @@ 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.
if (TheGrapher != nullptr)
{
DataTransferManager::ShowShareUI();
}

}

// When share is invoked (by the user or programatically) the event handler we registered will be called to populate the datapackage with the
// data to be shared. We will request the current graph image from teh grapher as a stream that will pass to the share request.
void GraphingCalculator::OnDataRequested(DataTransferManager^ sender, DataRequestedEventArgs^ args)
{
try
{
// Get our title from the localized resources
auto resourceLoader = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView();
auto EmptyEquationString = resourceLoader->GetString(L"EmptyEquationString");

auto htmlResources = ref new Map<String^, RandomAccessStreamReference^>();
std::wstring rawHtml = L"<p><img src='graph.png'></p>";

auto equations = ViewModel->Equations;
rawHtml += L"<p><table cellpadding=\"10\">";
rawHtml += L"<col width=\"60\">";
rawHtml += L"<row height=\"60\">";
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"&#402;(&#215;)";
rawHtml += L"</td>";
rawHtml += L"<td>";
rawHtml += expression;
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);

htmlResources->Insert(ref new String(L"graph.png"), TheGrapher->GetGraphBitmapStream());

for (auto pair : htmlResources)
{
auto k = pair->Key;
auto v = pair->Value;
requestData->ResourceMap->Insert(k, v);
}
}
catch(...)
{
// Something went wrong, notify the user.
auto resourceLoader = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView();

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 = "OK";
errDialog->ShowAsync();
}
}

void GraphingCalculator::CommandInvokedHandler(Windows::UI::Popups::IUICommand^ command)
{

void GraphingCalculator::GraphVariablesUpdated(Object^, Object^)
{
m_viewModel->UpdateVariables(GraphingControl->Variables);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 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);
};

}
66 changes: 58 additions & 8 deletions src/GraphControl/Control/Grapher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,6 +19,14 @@ using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;

namespace MathSolverEngine::Graph::Renderer
{
struct IBitmap
{
virtual const std::vector<BYTE>& GetData() const = 0;
};
}

namespace
{
constexpr auto s_defaultStyleKey = L"GraphControl.Grapher";
Expand Down Expand Up @@ -49,7 +58,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() }
Expand Down Expand Up @@ -122,7 +131,7 @@ namespace GraphControl
{
s_equationsProperty = DependencyProperty::Register(
StringReference(s_propertyName_Equations),
EquationCollection::typeid ,
EquationCollection::typeid,
Grapher::typeid,
ref new PropertyMetadata(
nullptr,
Expand Down Expand Up @@ -555,24 +564,24 @@ 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);

e->Handled = true;
}

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)
{
Expand Down Expand Up @@ -618,7 +627,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)))
{
Expand All @@ -635,4 +644,45 @@ namespace GraphControl
}
}
}

RandomAccessStreamReference^ Grapher::GetGraphBitmapStream()
{
RandomAccessStreamReference^ outputStream;

if (m_renderMain != nullptr && m_graph != nullptr)
{
if (auto renderer = m_graph->GetRenderer())
{
std::shared_ptr < MathSolverEngine::Graph::Renderer::IBitmap> BitmapOut;
bool hasSomeMissingDataOut = false;

// Request the current graph image, we don't care if there is some missing data
if (SUCCEEDED(renderer->GetBitmap(BitmapOut, hasSomeMissingDataOut)))
{
// 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");
}
}
}

return outputStream;
}
}
3 changes: 3 additions & 0 deletions src/GraphControl/Control/Grapher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
};
}
7 changes: 7 additions & 0 deletions src/GraphingInterfaces/IGraphRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
struct ID2D1Factory;
struct ID2D1RenderTarget;

namespace MathSolverEngine::Graph::Renderer
{
struct IBitmap;
}

namespace Graphing::Renderer
{
struct IGraphRenderer : public NonCopyable, public NonMoveable
Expand All @@ -21,5 +26,7 @@ namespace Graphing::Renderer
virtual HRESULT ChangeRange(ChangeRangeAction action) = 0;
virtual HRESULT MoveRangeByRatio(double ratioX, double ratioY) = 0;
virtual HRESULT ResetRange() = 0;

virtual HRESULT GetBitmap(std::shared_ptr<MathSolverEngine::Graph::Renderer::IBitmap>& bitmapOut, bool& hasSomeMissingDataOut) = 0;
};
}