- Use UIElement.ManipulationDelta protected event handler instead of GestureRecognizer.

This commit is contained in:
Daniel Belcher 2019-04-14 20:21:50 -07:00
commit 1b92ba5fe4
2 changed files with 48 additions and 40 deletions

View file

@ -18,23 +18,31 @@ using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Media;
namespace GraphControl namespace
{ {
constexpr auto s_defaultStyleKey = L"GraphControl.Grapher"; constexpr auto s_defaultStyleKey = L"GraphControl.Grapher";
constexpr auto s_templateKey_SwapChainPanel = L"GraphSurface"; constexpr auto s_templateKey_SwapChainPanel = L"GraphSurface";
DependencyProperty^ Grapher::s_equationTemplateProperty;
constexpr auto s_propertyName_EquationTemplate = L"EquationTemplate"; constexpr auto s_propertyName_EquationTemplate = L"EquationTemplate";
DependencyProperty^ Grapher::s_equationsProperty;
constexpr auto s_propertyName_Equations = L"Equations"; constexpr auto s_propertyName_Equations = L"Equations";
DependencyProperty^ Grapher::s_equationsSourceProperty;
constexpr auto s_propertyName_EquationsSource = L"EquationsSource"; constexpr auto s_propertyName_EquationsSource = L"EquationsSource";
DependencyProperty^ Grapher::s_forceProportionalAxesTemplateProperty;
constexpr auto s_propertyName_ForceProportionalAxes = L"ForceProportionalAxes"; constexpr auto s_propertyName_ForceProportionalAxes = L"ForceProportionalAxes";
// Helper function for converting a pointer position to a position that the graphing engine will understand.
// posX/posY are the pointer position elements and width,height are the dimensions of the graph surface.
__inline pair<double, double> PointerPositionToGraphPosition(double posX, double posY, double width, double height)
{
return make_pair(2 * posX / width - 1, 1 - 2 * posY / height);
}
}
namespace GraphControl
{
DependencyProperty^ Grapher::s_equationTemplateProperty;
DependencyProperty^ Grapher::s_equationsProperty;
DependencyProperty^ Grapher::s_equationsSourceProperty;
DependencyProperty^ Grapher::s_forceProportionalAxesTemplateProperty;
Grapher::Grapher() Grapher::Grapher()
: m_solver{ IMathSolver::CreateMathSolver() } : m_solver{ IMathSolver::CreateMathSolver() }
, m_graph{ m_solver->CreateGrapher() } , m_graph{ m_solver->CreateGrapher() }
@ -48,14 +56,12 @@ namespace GraphControl
this->Loaded += ref new RoutedEventHandler(this, &Grapher::OnLoaded); this->Loaded += ref new RoutedEventHandler(this, &Grapher::OnLoaded);
this->Unloaded += ref new RoutedEventHandler(this, &Grapher::OnUnloaded); this->Unloaded += ref new RoutedEventHandler(this, &Grapher::OnUnloaded);
m_gestureRecognizer->GestureSettings = this->ManipulationMode =
GestureSettings::ManipulationTranslateX | ManipulationModes::TranslateX |
GestureSettings::ManipulationTranslateY | ManipulationModes::TranslateY |
GestureSettings::ManipulationTranslateInertia | ManipulationModes::TranslateInertia |
GestureSettings::ManipulationScale | ManipulationModes::Scale |
GestureSettings::ManipulationScaleInertia; ManipulationModes::ScaleInertia;
m_gestureRecognizer->ManipulationUpdated += ref new TypedEventHandler<GestureRecognizer^, ManipulationUpdatedEventArgs^>(this, &Grapher::OnGestureManipulationUpdated);
} }
void Grapher::OnLoaded(Object^ sender, RoutedEventArgs^ args) void Grapher::OnLoaded(Object^ sender, RoutedEventArgs^ args)
@ -416,8 +422,6 @@ namespace GraphControl
PointerPoint^ currPoint = e->GetCurrentPoint(/* relativeTo */ this); PointerPoint^ currPoint = e->GetCurrentPoint(/* relativeTo */ this);
m_renderMain->PointerLocation = currPoint->Position; m_renderMain->PointerLocation = currPoint->Position;
m_gestureRecognizer->ProcessMoveEvents(e->GetIntermediatePoints(/*relativeTo*/ this));
e->Handled = true; e->Handled = true;
} }
} }
@ -435,15 +439,28 @@ namespace GraphControl
{ {
PointerPoint^ currentPointer = args->GetCurrentPoint(/*relative to*/ this); PointerPoint^ currentPointer = args->GetCurrentPoint(/*relative to*/ this);
VirtualKeyModifiers vkmod = args->KeyModifiers; double delta = currentPointer->Properties->MouseWheelDelta;
m_gestureRecognizer->ProcessMouseWheelEvent(
currentPointer, // The maximum delta is 120 according to:
static_cast<bool>(vkmod & VirtualKeyModifiers::Shift), // https://docs.microsoft.com/en-us/uwp/api/windows.ui.input.pointerpointproperties.mousewheeldelta#Windows_UI_Input_PointerPointProperties_MouseWheelDelta
// Usually we would detect if Control key is pressed. Ctrl+MWHEELUP/DOWN is // Apply a dampening effect so that small mouse movements have a smoother zoom.
// considered a Scale manipulation. In this case, we want a basic MWHEELUP/DOWN constexpr double scrollDamper = 0.15;
// to trigger zoom, so we'll trick the gesture recognizer by always saying Ctrl double scale = 1.0 + (abs(delta) / WHEEL_DELTA) * scrollDamper;
// is pressed for the MouseWheelEvent.
true); // positive delta if wheel scrolled away from the user
if (delta >= 0)
{
scale = 1.0 / scale;
}
// 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);
ScaleRange(centerX, centerY, scale);
args->Handled = true;
} }
void Grapher::OnPointerPressed(PointerRoutedEventArgs^ e) void Grapher::OnPointerPressed(PointerRoutedEventArgs^ e)
@ -451,25 +468,20 @@ namespace GraphControl
// Set the pointer capture to the element being interacted with so that only it // Set the pointer capture to the element being interacted with so that only it
// will fire pointer-related events // will fire pointer-related events
CapturePointer(e->Pointer); CapturePointer(e->Pointer);
// Feed the current point into the gesture recognizer as a down event
m_gestureRecognizer->ProcessDownEvent(e->GetCurrentPoint(/*relativeTo*/ this));
} }
void Grapher::OnPointerReleased(PointerRoutedEventArgs^ e) void Grapher::OnPointerReleased(PointerRoutedEventArgs^ e)
{ {
// Feed the current point into the gesture recognizer as an up event
m_gestureRecognizer->ProcessUpEvent(e->GetCurrentPoint(/*relativeTo*/ this));
// Release the pointer // Release the pointer
ReleasePointerCapture(e->Pointer); ReleasePointerCapture(e->Pointer);
} }
void Grapher::OnPointerCanceled(PointerRoutedEventArgs^ e) void Grapher::OnPointerCanceled(PointerRoutedEventArgs^ e)
{ {
m_gestureRecognizer->CompleteGesture();
ReleasePointerCapture(e->Pointer); ReleasePointerCapture(e->Pointer);
} }
void Grapher::OnGestureManipulationUpdated(GestureRecognizer^ sender, ManipulationUpdatedEventArgs^ args) void Grapher::OnManipulationDelta(ManipulationDeltaRoutedEventArgs^ args)
{ {
if (m_graph) if (m_graph)
{ {
@ -478,7 +490,7 @@ namespace GraphControl
const double width = ActualWidth; const double width = ActualWidth;
const double height = ActualHeight; const double height = ActualHeight;
const auto& translation = args->Delta.Translation;; const auto& translation = args->Delta.Translation;
double translationX = translation.X; double translationX = translation.X;
double translationY = translation.Y; double translationY = translation.Y;
if (translationX != 0 || translation.Y != 0) if (translationX != 0 || translation.Y != 0)
@ -504,8 +516,7 @@ namespace GraphControl
// The graphing engine interprets x,y position between the range [-1, 1]. // The graphing engine interprets x,y position between the range [-1, 1].
// Translate the pointer position to the [-1, 1] bounds. // Translate the pointer position to the [-1, 1] bounds.
const auto& pos = args->Position; const auto& pos = args->Position;
double centerX = 2 * pos.X / width - 1; const auto[centerX, centerY] = PointerPositionToGraphPosition(pos.X, pos.Y, width, height);
double centerY = 1 - 2 * pos.Y / height;
if (FAILED(renderer->ScaleRange(centerX, centerY, scale))) if (FAILED(renderer->ScaleRange(centerX, centerY, scale)))
{ {

View file

@ -114,6 +114,7 @@ namespace GraphControl
void OnPointerPressed(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override; void OnPointerPressed(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override;
void OnPointerReleased(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override; void OnPointerReleased(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override;
void OnPointerCanceled(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override; void OnPointerCanceled(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override;
void OnManipulationDelta(Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs^ e) override;
#pragma endregion #pragma endregion
private: private:
@ -144,8 +145,6 @@ namespace GraphControl
void OnItemsAdded(int index, int count); void OnItemsAdded(int index, int count);
void OnItemsRemoved(int index, int count); void OnItemsRemoved(int index, int count);
void OnGestureManipulationUpdated(Windows::UI::Input::GestureRecognizer ^sender, Windows::UI::Input::ManipulationUpdatedEventArgs ^args);
private: private:
DX::RenderMain^ m_renderMain = nullptr; DX::RenderMain^ m_renderMain = nullptr;
@ -165,7 +164,5 @@ namespace GraphControl
const std::unique_ptr<Graphing::IMathSolver> m_solver; const std::unique_ptr<Graphing::IMathSolver> m_solver;
const std::shared_ptr<Graphing::IGraph> m_graph; const std::shared_ptr<Graphing::IGraph> m_graph;
Windows::UI::Input::GestureRecognizer^ m_gestureRecognizer = ref new Windows::UI::Input::GestureRecognizer();
}; };
} }