From 7eb15ee9a434330d0b77a0bf1245490474f34367 Mon Sep 17 00:00:00 2001 From: Joe Harvey Date: Thu, 13 Feb 2020 12:04:14 -0500 Subject: [PATCH 1/2] Added Scenario13 which will show you how to resize and move an InkStroke that has been selected using lasso tool --- Samples/SimpleInk/cpp/SampleConfiguration.cpp | 1 + Samples/SimpleInk/cpp/Scenario13.xaml.cpp | 373 ++++++++++++++++ Samples/SimpleInk/cpp/Scenario13.xaml.h | 82 ++++ Samples/SimpleInk/cpp/SimpleInk.vcxproj | 7 + .../SimpleInk/cpp/SimpleInk.vcxproj.filters | 3 + Samples/SimpleInk/cs/SampleConfiguration.cs | 1 + Samples/SimpleInk/cs/Scenario13.xaml.cs | 400 ++++++++++++++++++ Samples/SimpleInk/cs/SimpleInk.csproj | 6 + Samples/SimpleInk/shared/Scenario13.xaml | 78 ++++ SharedContent/cs/MainPage.xaml.cs | 1 + 10 files changed, 952 insertions(+) create mode 100644 Samples/SimpleInk/cpp/Scenario13.xaml.cpp create mode 100644 Samples/SimpleInk/cpp/Scenario13.xaml.h create mode 100644 Samples/SimpleInk/cs/Scenario13.xaml.cs create mode 100644 Samples/SimpleInk/shared/Scenario13.xaml diff --git a/Samples/SimpleInk/cpp/SampleConfiguration.cpp b/Samples/SimpleInk/cpp/SampleConfiguration.cpp index a63859aff1..a26d385c19 100644 --- a/Samples/SimpleInk/cpp/SampleConfiguration.cpp +++ b/Samples/SimpleInk/cpp/SampleConfiguration.cpp @@ -31,6 +31,7 @@ Platform::Array^ MainPage::scenariosInner = ref new Platform::Array + +#define _USE_MATH_DEFINES +#include + +using namespace SDKTemplate; + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Numerics; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Core; +using namespace Windows::UI::Input::Inking; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Shapes; + +Scenario13::Scenario13() +{ + InitializeComponent(); + LassoSelect = (Symbol)0xEF20; + + // Initialize the InkCanvas + inkCanvas->InkPresenter->InputDeviceTypes = CoreInputDeviceTypes::Mouse | CoreInputDeviceTypes::Pen; + + // Handlers to clear the selection when inking or erasing is detected + inkCanvas->InkPresenter->StrokeInput->StrokeStarted += ref new TypedEventHandler(this, &Scenario13::StrokeInput_StrokeStarted); + inkCanvas->InkPresenter->StrokeInput->StrokeEnded += ref new TypedEventHandler(this, &Scenario13::InkPresenter_StrokesErased); +} + +void Scenario13::StrokeInput_StrokeStarted(InkStrokeInput^ sender, PointerEventArgs^ args) +{ + ClearSelection(); + inkCanvas->InkPresenter->UnprocessedInput->PointerPressed -= pointerPressedToken; + inkCanvas->InkPresenter->UnprocessedInput->PointerMoved -= pointerMovedToken; + inkCanvas->InkPresenter->UnprocessedInput->PointerReleased -= pointerReleasedToken; +} + +void Scenario13::InkPresenter_StrokesErased(InkStrokeInput^ sender, PointerEventArgs^ args) +{ + ClearSelection(); + inkCanvas->InkPresenter->UnprocessedInput->PointerPressed -= pointerPressedToken; + inkCanvas->InkPresenter->UnprocessedInput->PointerMoved -= pointerMovedToken; + inkCanvas->InkPresenter->UnprocessedInput->PointerReleased -= pointerReleasedToken; +} + +void Scenario13::OnSizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + HelperFunctions::UpdateCanvasSize(RootGrid, outputGrid, inkCanvas); +} + +void Scenario13::UnprocessedInput_PointerPressed(InkUnprocessedInput^ sender, PointerEventArgs^ args) +{ + if (SelectedBoudningBoxContainsPosition(args->CurrentPoint->Position)) + { + return; + } + + lasso = ref new Polyline(); + lasso->Stroke = ref new SolidColorBrush(Windows::UI::Colors::Blue); + lasso->StrokeThickness = 1; + lasso->StrokeDashArray = ref new DoubleCollection(); + lasso->StrokeDashArray->Append(5.0); + lasso->StrokeDashArray->Append(2.0); + lasso->Points->Append(args->CurrentPoint->RawPosition); + selectionCanvas->Children->Append(lasso); + isBoundRect = true; +} + +void Scenario13::UnprocessedInput_PointerMoved(InkUnprocessedInput^ sender, PointerEventArgs^ args) +{ + if (isBoundRect) + { + lasso->Points->Append(args->CurrentPoint->RawPosition); + } +} + +void Scenario13::UnprocessedInput_PointerReleased(InkUnprocessedInput^ sender, PointerEventArgs^ args) +{ + lasso->Points->Append(args->CurrentPoint->RawPosition); + + boundingRect = inkCanvas->InkPresenter->StrokeContainer->SelectWithPolyLine(lasso->Points); + isBoundRect = false; + DrawBoundingRect(); +} + +void Scenario13::SelectionRectangle_ManipulationDelta(Object^ sender, Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs^ e) +{ + CompositeTransform^ transform = (CompositeTransform^) selectionRectangle->RenderTransform; + + switch (currentManipulationType) + { + case ManipulationTypes::Move: + MoveSelectedInkStrokes(e->Delta.Translation); + transform->TranslateX += e->Delta.Translation.X; + transform->TranslateY += e->Delta.Translation.Y; + + break; + + case ManipulationTypes::Size: + //TODO: Play with Scaling factor to change speed at which the "box" will grow. + auto scale = std::abs(1 + (float)e->Delta.Translation.X / 100); + + //TODO: need to stop scaling when the area gets too small, otherwise app will crash. Could be refined. + if (selectionRectangle->Width <= 10 && scale < 1.0) + return; + + transform->ScaleX *= scale; + transform->ScaleY *= scale; + + auto offset = selectionRectangle->ActualOffset; + auto center = new Windows::Foundation::Numerics::float2(offset.x + (float)transform->TranslateX, offset.y + (float)transform->TranslateY); + + ResizeSelectedInkStrokes(scale, *center); + break; + } +} + +void Scenario13::SelectionRectangle_ManipulationCompleted(Object^ sender, Windows::UI::Xaml::Input::ManipulationCompletedRoutedEventArgs^ args) +{ + currentManipulationType = ManipulationTypes::None; + + Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); + window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0); +} + +void Scenario13::SelectionRectangle_ManipulationStarted(Object^ sender, Windows::UI::Xaml::Input::ManipulationStartedRoutedEventArgs^ args) +{ + if (currentManipulationType != ManipulationTypes::None) + return; + + Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); + + if (window->PointerCursor->Type == CoreCursorType::SizeAll) + { + currentManipulationType = ManipulationTypes::Move; + } + else + { + currentManipulationType = ManipulationTypes::Size; + + switch (resizePointerCornor) + { + case RecCornor::TopLeft: + case RecCornor::BottomRight: + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); + break; + case RecCornor::TopRight: + case RecCornor::BottomLeft: + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); + break; + } + } +} + +void Scenario13::SelectionRectangle_PointerPressed(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) +{ + if (currentManipulationType != ManipulationTypes::None) + return; + + Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); + + if (window->PointerCursor->Type == CoreCursorType::SizeAll) + { + currentManipulationType = ManipulationTypes::Move; + } + else + { + currentManipulationType = ManipulationTypes::Size; + + switch (resizePointerCornor) + { + case RecCornor::TopLeft: + case RecCornor::BottomRight: + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); + break; + case RecCornor::TopRight: + case RecCornor::BottomLeft: + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); + break; + } + } +} + +void Scenario13::SelectionRectangle_PointerMoved(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) +{ + if (currentManipulationType != ManipulationTypes::None) + return; + + Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); + Point mousePos = args->GetCurrentPoint(selectionRectangle)->Position; + + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeAll, 0); + float offset = 5; + float recMinX, recMaxX, recMinY, recMaxY; + recMinX = 0; + recMinY = 0; + + recMaxX = (float)selectionRectangle->ActualWidth; + recMaxY = (float)selectionRectangle->ActualHeight; + + if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && + mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) + { + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); + resizePointerCornor = RecCornor::TopLeft; + return; + } + else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && + mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) + { + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); + resizePointerCornor = RecCornor::BottomRight; + return; + } + else if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && + mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) + { + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); + resizePointerCornor = RecCornor::BottomLeft; + return; + } + else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && + mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) + { + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); + resizePointerCornor = RecCornor::TopRight; + return; + } +} + +void Scenario13::SelectionRectangle_PointerExited(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) +{ + if (currentManipulationType != ManipulationTypes::None) + return; + + Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); + window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0); +} + +void Scenario13::ResizeSelectedInkStrokes(float scale, Windows::Foundation::Numerics::float2& center) +{ + + auto allStrokes = inkCanvas->InkPresenter->StrokeContainer->GetStrokes(); + + if (allStrokes == nullptr) + return; + + float3x2 matrixSacale = make_float3x2_scale(scale, center); + + for (auto stroke : allStrokes) + { + if (stroke->Selected) + { + stroke->PointTransform *= matrixSacale; + } + } +} + +void Scenario13::MoveSelectedInkStrokes(Point position) +{ + float3x2 matrix = make_float3x2_translation((float)position.X, (float)position.Y); + + auto allStrokes = inkCanvas->InkPresenter->StrokeContainer->GetStrokes(); + if (allStrokes == nullptr) + return; + + for (auto stroke : allStrokes) + { + if (stroke->Selected) + { + stroke->PointTransform *= matrix; + } + } + +} + +void Scenario13::DrawBoundingRect() +{ + selectionCanvas->Children->Clear(); + + if (boundingRect.Width <= 0 || boundingRect.Height <= 0) + { + return; + } + + selectionRectangle = ref new Rectangle(); + selectionRectangle->Name = "SelectedBox"; + selectionRectangle->Stroke = ref new SolidColorBrush(Windows::UI::Colors::Blue); + selectionRectangle->Fill = ref new SolidColorBrush(Windows::UI::Colors::Transparent); + selectionRectangle->StrokeThickness = 1; + selectionRectangle->StrokeDashArray = ref new DoubleCollection(); + selectionRectangle->ManipulationMode = Windows::UI::Xaml::Input::ManipulationModes::All; + selectionRectangle->RenderTransform = ref new CompositeTransform(); + selectionRectangle->StrokeDashArray->Append(5.0); + selectionRectangle->StrokeDashArray->Append(2.0); + selectionRectangle->Width = boundingRect.Width; + selectionRectangle->Height = boundingRect.Height; + + selectionCanvas->SetLeft(selectionRectangle, boundingRect.X); + selectionCanvas->SetTop(selectionRectangle, boundingRect.Y); + + + manipulationStartedToken = selectionRectangle->ManipulationStarted += ref new Windows::UI::Xaml::Input::ManipulationStartedEventHandler(this, &Scenario13::SelectionRectangle_ManipulationStarted); + manipulationCompletedToken = selectionRectangle->ManipulationCompleted += ref new Windows::UI::Xaml::Input::ManipulationCompletedEventHandler(this, &Scenario13::SelectionRectangle_ManipulationCompleted); + manipulationDeltaToken = selectionRectangle->ManipulationDelta += ref new Windows::UI::Xaml::Input::ManipulationDeltaEventHandler(this, &Scenario13::SelectionRectangle_ManipulationDelta); + pointerExitedRectangleToken = selectionRectangle->PointerExited += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &Scenario13::SelectionRectangle_PointerExited); + pointerMovedRectangleToken = selectionRectangle->PointerMoved += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &Scenario13::SelectionRectangle_PointerMoved); + pointerPressedRectangleToken = selectionRectangle->PointerPressed += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &Scenario13::SelectionRectangle_PointerPressed); + + selectionCanvas->Children->Append(selectionRectangle); +} + +void Scenario13::ToolButton_Lasso(Object^ sender, RoutedEventArgs^ e) +{ + // By default, pen barrel button or right mouse button is processed for inking + // Set the configuration to instead allow processing these input on the UI thread + inkCanvas->InkPresenter->InputProcessingConfiguration->RightDragAction = InkInputRightDragAction::LeaveUnprocessed; + + pointerPressedToken = inkCanvas->InkPresenter->UnprocessedInput->PointerPressed += ref new TypedEventHandler(this, &Scenario13::UnprocessedInput_PointerPressed); + pointerMovedToken = inkCanvas->InkPresenter->UnprocessedInput->PointerMoved += ref new TypedEventHandler(this, &Scenario13::UnprocessedInput_PointerMoved); + pointerReleasedToken = inkCanvas->InkPresenter->UnprocessedInput->PointerReleased += ref new TypedEventHandler(this, &Scenario13::UnprocessedInput_PointerReleased); +} + +void Scenario13::ClearDrawnBoundingRect() +{ + if (selectionCanvas->Children->Size > 0) + { + selectionCanvas->Children->Clear(); + + selectionRectangle->ManipulationStarted -= manipulationStartedToken; + selectionRectangle->ManipulationCompleted -= manipulationCompletedToken; + selectionRectangle->ManipulationDelta -= manipulationDeltaToken; + selectionRectangle->PointerExited -= pointerExitedRectangleToken; + selectionRectangle->PointerMoved -= pointerMovedRectangleToken; + selectionRectangle->PointerPressed -= pointerPressedRectangleToken; + + selectionRectangle = nullptr; + + boundingRect = Rect::Empty; + } +} + +void Scenario13::ClearSelection() +{ + auto strokes = inkCanvas->InkPresenter->StrokeContainer->GetStrokes(); + for (auto stroke : strokes) + { + stroke->Selected = false; + } + ClearDrawnBoundingRect(); +} + +bool Scenario13::SelectedBoudningBoxContainsPosition(Windows::Foundation::Point position) +{ + auto contains = !boundingRect.IsEmpty && boundingRect.Contains(position); + return contains; +} + diff --git a/Samples/SimpleInk/cpp/Scenario13.xaml.h b/Samples/SimpleInk/cpp/Scenario13.xaml.h new file mode 100644 index 0000000000..207eb2646c --- /dev/null +++ b/Samples/SimpleInk/cpp/Scenario13.xaml.h @@ -0,0 +1,82 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "Scenario13.g.h" +#include "MainPage.xaml.h" + +namespace SDKTemplate +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + [Windows::Foundation::Metadata::WebHostHidden] + public ref class Scenario13 sealed + { + internal: + enum class RecCornor { TopLeft, TopRight, BottomLeft, BottomRight }; + enum class ManipulationTypes { None, Move, Size }; + + public: + Scenario13(); + + property Windows::UI::Xaml::Controls::Symbol LassoSelect; + + private: + + void StrokeInput_StrokeStarted(Windows::UI::Input::Inking::InkStrokeInput^ sender, Windows::UI::Core::PointerEventArgs^ args); + void InkPresenter_StrokesErased(Windows::UI::Input::Inking::InkStrokeInput^ sender, Windows::UI::Core::PointerEventArgs^ args); + + void OnSizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void ToolButton_Lasso(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void MoveSelectedInkStrokes(Windows::Foundation::Point position); + void ResizeSelectedInkStrokes(float scale, Windows::Foundation::Numerics::float2& center); + void DrawBoundingRect(); + void ClearDrawnBoundingRect(); + void ClearSelection(); + + void UnprocessedInput_PointerPressed(Windows::UI::Input::Inking::InkUnprocessedInput^ sender, Windows::UI::Core::PointerEventArgs^ args); + void UnprocessedInput_PointerMoved(Windows::UI::Input::Inking::InkUnprocessedInput^ sender, Windows::UI::Core::PointerEventArgs^ args); + void UnprocessedInput_PointerReleased(Windows::UI::Input::Inking::InkUnprocessedInput^ sender, Windows::UI::Core::PointerEventArgs^ args); + + void SelectionRectangle_ManipulationStarted(Object^ sender, Windows::UI::Xaml::Input::ManipulationStartedRoutedEventArgs^ args); + void SelectionRectangle_ManipulationCompleted(Object^ sender, Windows::UI::Xaml::Input::ManipulationCompletedRoutedEventArgs ^ args); + void SelectionRectangle_ManipulationDelta(Object^ sender, Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs^ args); + + void SelectionRectangle_PointerExited(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args); + void SelectionRectangle_PointerMoved(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args); + void SelectionRectangle_PointerPressed(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args); + + bool SelectedBoudningBoxContainsPosition(Windows::Foundation::Point position); + + MainPage^ rootPage = MainPage::Current; + Windows::UI::Xaml::Shapes::Polyline^ lasso; + Windows::Foundation::Rect boundingRect; + Windows::UI::Xaml::Shapes::Rectangle^ selectionRectangle; + ManipulationTypes currentManipulationType; + RecCornor resizePointerCornor; + bool isBoundRect; + + Windows::Foundation::EventRegistrationToken pointerPressedToken; + Windows::Foundation::EventRegistrationToken pointerMovedToken; + Windows::Foundation::EventRegistrationToken pointerReleasedToken; + + Windows::Foundation::EventRegistrationToken manipulationStartedToken; + Windows::Foundation::EventRegistrationToken manipulationCompletedToken; + Windows::Foundation::EventRegistrationToken manipulationDeltaToken; + Windows::Foundation::EventRegistrationToken pointerExitedRectangleToken; + Windows::Foundation::EventRegistrationToken pointerMovedRectangleToken; + Windows::Foundation::EventRegistrationToken pointerPressedRectangleToken; + + + }; +} diff --git a/Samples/SimpleInk/cpp/SimpleInk.vcxproj b/Samples/SimpleInk/cpp/SimpleInk.vcxproj index 1ee7db00d3..a19556db6b 100644 --- a/Samples/SimpleInk/cpp/SimpleInk.vcxproj +++ b/Samples/SimpleInk/cpp/SimpleInk.vcxproj @@ -180,6 +180,9 @@ ..\shared\Scenario12.xaml + + + ..\shared\Scenario13.xaml @@ -204,6 +207,7 @@ + @@ -262,6 +266,9 @@ ..\shared\Scenario12.xaml + + + ..\shared\Scenario13.xaml diff --git a/Samples/SimpleInk/cpp/SimpleInk.vcxproj.filters b/Samples/SimpleInk/cpp/SimpleInk.vcxproj.filters index 1c69e5bfef..a400440334 100644 --- a/Samples/SimpleInk/cpp/SimpleInk.vcxproj.filters +++ b/Samples/SimpleInk/cpp/SimpleInk.vcxproj.filters @@ -30,6 +30,7 @@ + @@ -49,6 +50,7 @@ + @@ -70,6 +72,7 @@ + diff --git a/Samples/SimpleInk/cs/SampleConfiguration.cs b/Samples/SimpleInk/cs/SampleConfiguration.cs index f7c76524ff..44bafc1892 100644 --- a/Samples/SimpleInk/cs/SampleConfiguration.cs +++ b/Samples/SimpleInk/cs/SampleConfiguration.cs @@ -34,6 +34,7 @@ public partial class MainPage : Page new Scenario() { Title="Scenario 10", ClassType=typeof(Scenario10)}, new Scenario() { Title="Scenario 11", ClassType=typeof(Scenario11)}, new Scenario() { Title="Scenario 12", ClassType=typeof(Scenario12)}, + new Scenario() { Title="Scenario 13", ClassType=typeof(Scenario13)}, }; } diff --git a/Samples/SimpleInk/cs/Scenario13.xaml.cs b/Samples/SimpleInk/cs/Scenario13.xaml.cs new file mode 100644 index 0000000000..20c816264b --- /dev/null +++ b/Samples/SimpleInk/cs/Scenario13.xaml.cs @@ -0,0 +1,400 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Numerics; +using System.Threading; +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Core; +using Windows.UI.Input.Inking; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using Windows.UI.Xaml.Shapes; + +namespace SDKTemplate +{ + internal enum RecCornor + { + TopLeft, + TopRight, + BottomLeft, + BottomRight + } + + internal enum ManipulationTypes + { + None, + Move, + Size + } + + /// + /// This page shows the code to configure the InkToolbar. + /// + public sealed partial class Scenario13 : Page + { + private Polyline lasso; + private Rect boundingRect; + private bool isBoundRect; + + Symbol LassoSelect = (Symbol)0xEF20; + private Rectangle selectionRectangle; + private ManipulationTypes currentManipulationType; + private RecCornor resizePointerCornor; + + public Scenario13() + { + this.InitializeComponent(); + + // Initialize the InkCanvas + inkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Pen; + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + // Registering handlers to clear the selection when inking or erasing is detected + inkCanvas.InkPresenter.StrokeInput.StrokeStarted += StrokeInput_StrokeStarted; + inkCanvas.InkPresenter.StrokesErased += InkPresenter_StrokesErased; + } + + protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) + { + // Unregistering handlers to clear the selection when inking or erasing is detected + inkCanvas.InkPresenter.StrokeInput.StrokeStarted -= StrokeInput_StrokeStarted; + inkCanvas.InkPresenter.StrokesErased -= InkPresenter_StrokesErased; + } + + private void StrokeInput_StrokeStarted(InkStrokeInput sender, PointerEventArgs args) + { + ClearSelection(); + inkCanvas.InkPresenter.UnprocessedInput.PointerPressed -= UnprocessedInput_PointerPressed; + inkCanvas.InkPresenter.UnprocessedInput.PointerMoved -= UnprocessedInput_PointerMoved; + inkCanvas.InkPresenter.UnprocessedInput.PointerReleased -= UnprocessedInput_PointerReleased; + } + + private void InkPresenter_StrokesErased(InkPresenter sender, InkStrokesErasedEventArgs args) + { + ClearSelection(); + inkCanvas.InkPresenter.UnprocessedInput.PointerPressed -= UnprocessedInput_PointerPressed; + inkCanvas.InkPresenter.UnprocessedInput.PointerMoved -= UnprocessedInput_PointerMoved; + inkCanvas.InkPresenter.UnprocessedInput.PointerReleased -= UnprocessedInput_PointerReleased; + } + + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + HelperFunctions.UpdateCanvasSize(RootGrid, outputGrid, inkCanvas); + } + + private void UnprocessedInput_PointerPressed(InkUnprocessedInput sender, PointerEventArgs args) + { + if (SelectedBoudningBoxContainsPosition(args.CurrentPoint.Position)) + { + return; + } + + lasso = new Polyline() + { + Stroke = new SolidColorBrush(Windows.UI.Colors.Blue), + StrokeThickness = 1, + StrokeDashArray = new DoubleCollection() { 5, 2 }, + }; + + lasso.Points.Add(args.CurrentPoint.RawPosition); + selectionCanvas.Children.Add(lasso); + isBoundRect = true; + } + + private void UnprocessedInput_PointerMoved(InkUnprocessedInput sender, PointerEventArgs args) + { + args.Handled = true; + + if (isBoundRect) + { + lasso.Points.Add(args.CurrentPoint.RawPosition); + } + } + + private void UnprocessedInput_PointerReleased(InkUnprocessedInput sender, PointerEventArgs args) + { + lasso.Points.Add(args.CurrentPoint.RawPosition); + + boundingRect = inkCanvas.InkPresenter.StrokeContainer.SelectWithPolyLine(lasso.Points); + isBoundRect = false; + DrawBoundingRect(); + } + + private void DrawBoundingRect() + { + selectionCanvas.Children.Clear(); + if (boundingRect.Width <= 0 || boundingRect.Height <= 0) + { + return; + } + + selectionRectangle = new Rectangle() + { + Name = "SelectedBox", + Stroke = new SolidColorBrush(Windows.UI.Colors.Blue), + Fill = new SolidColorBrush(Windows.UI.Colors.Transparent), + StrokeThickness = 1, + StrokeDashArray = new DoubleCollection() { 5, 2 }, + ManipulationMode = ManipulationModes.All, + RenderTransform = new CompositeTransform(), + Width = boundingRect.Width, + Height = boundingRect.Height + }; + + Canvas.SetLeft(selectionRectangle, boundingRect.X); + Canvas.SetTop(selectionRectangle, boundingRect.Y); + selectionRectangle.ManipulationStarted += SelectionRectangle_ManipulationStarted; + selectionRectangle.ManipulationCompleted += SelectionRectangle_ManipulationCompleted; + selectionRectangle.ManipulationDelta += SelectionRectangle_ManipulationDelta; + selectionRectangle.PointerExited += SelectionRectangle_PointerExited; + selectionRectangle.PointerMoved += SelectionRectangle_PointerMoved; + selectionRectangle.PointerPressed += SelectionRectangle_PointerPressed; ; + + selectionCanvas.Children.Add(selectionRectangle); + + } + + private void SelectionRectangle_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) + { + e.Handled = true; + var transform = (selectionRectangle.RenderTransform as CompositeTransform); + + switch (currentManipulationType) + { + case ManipulationTypes.Move: + MoveSelectedInkStrokes(e.Delta.Translation); + transform.TranslateX += e.Delta.Translation.X; + transform.TranslateY += e.Delta.Translation.Y; + break; + + case ManipulationTypes.Size: + //TODO: Play with Scaling factor to change speed at which the "box" will grow. + var scale = Math.Abs(1 + (float)e.Delta.Translation.X / 100); + + //TODO: need to stop scaling when the area gets too small, otherwise app will crash. Could be refined. + if (selectionRectangle.Width <= 10 && scale < 1f) + return; + + transform.ScaleX *= scale; + transform.ScaleY *= scale; + + var offset = selectionRectangle.ActualOffset; + + ResizeSelectedInkStrokes(scale, new Vector2(offset.X + (float)transform.TranslateX, offset.Y + (float)transform.TranslateY)); + break; + } + } + + private void SelectionRectangle_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) + { + currentManipulationType = ManipulationTypes.None; + + + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0); + } + + private void SelectionRectangle_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) + { + if (currentManipulationType != ManipulationTypes.None) + return; + + if (Window.Current.CoreWindow.PointerCursor.Type == CoreCursorType.SizeAll) + { + currentManipulationType = ManipulationTypes.Move; + } + else + { + currentManipulationType = ManipulationTypes.Size; + + switch (resizePointerCornor) + { + case RecCornor.TopLeft: + case RecCornor.BottomRight: + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); + break; + case RecCornor.TopRight: + case RecCornor.BottomLeft: + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); + break; + } + } + + } + + private void SelectionRectangle_PointerPressed(object sender, PointerRoutedEventArgs e) + { + if (currentManipulationType != ManipulationTypes.None) + return; + + if (Window.Current.CoreWindow.PointerCursor.Type == CoreCursorType.SizeAll) + { + currentManipulationType = ManipulationTypes.Move; + } + else + { + currentManipulationType = ManipulationTypes.Size; + + switch (resizePointerCornor) + { + case RecCornor.TopLeft: + case RecCornor.BottomRight: + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); + break; + case RecCornor.TopRight: + case RecCornor.BottomLeft: + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); + break; + } + } + } + + private void SelectionRectangle_PointerMoved(object sender, PointerRoutedEventArgs e) + { + if (currentManipulationType != ManipulationTypes.None) + return; + + var mousePos = e.GetCurrentPoint(selectionRectangle).Position; + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeAll, 0); + var offset = 5; + float recMinX, recMaxX, recMinY, recMaxY; + recMinX = 0; + recMinY = 0; + + recMaxX = (float)selectionRectangle.ActualWidth; + recMaxY = (float)selectionRectangle.ActualHeight; + + if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && + mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); + resizePointerCornor = RecCornor.TopLeft; + return; + } + else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && + mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); + resizePointerCornor = RecCornor.BottomRight; + return; + } + else if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && + mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); + resizePointerCornor = RecCornor.BottomLeft; + return; + } + else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && + mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); + resizePointerCornor = RecCornor.TopRight; + return; + } + + Debug.WriteLine($"SelectionRectangle_ManipulationCompleted::{resizePointerCornor}"); + } + + private void SelectionRectangle_PointerExited(object sender, PointerRoutedEventArgs e) + { + if (currentManipulationType != ManipulationTypes.None) + return; + + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0); + } + + private void ToolButton_Lasso(object sender, RoutedEventArgs e) + { + // By default, pen barrel button or right mouse button is processed for inking + // Set the configuration to instead allow processing these input on the UI thread + inkCanvas.InkPresenter.InputProcessingConfiguration.RightDragAction = InkInputRightDragAction.LeaveUnprocessed; + + inkCanvas.InkPresenter.UnprocessedInput.PointerPressed += UnprocessedInput_PointerPressed; + inkCanvas.InkPresenter.UnprocessedInput.PointerMoved += UnprocessedInput_PointerMoved; + inkCanvas.InkPresenter.UnprocessedInput.PointerReleased += UnprocessedInput_PointerReleased; + } + + private void ClearDrawnBoundingRect() + { + + if (selectionCanvas.Children.Count > 0) + { + selectionCanvas.Children.Clear(); + selectionRectangle.ManipulationStarted -= SelectionRectangle_ManipulationStarted; + selectionRectangle.ManipulationCompleted -= SelectionRectangle_ManipulationCompleted; + selectionRectangle.ManipulationDelta -= SelectionRectangle_ManipulationDelta; + selectionRectangle.PointerExited -= SelectionRectangle_PointerExited; + selectionRectangle.PointerMoved -= SelectionRectangle_PointerMoved; + selectionRectangle.PointerPressed -= SelectionRectangle_PointerPressed; + + selectionRectangle = null; + boundingRect = Rect.Empty; + } + } + + private void ClearSelection() + { + var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes(); + foreach (var stroke in strokes) + { + stroke.Selected = false; + } + ClearDrawnBoundingRect(); + } + + private bool SelectedBoudningBoxContainsPosition(Point position) + { + var contains = !boundingRect.IsEmpty && RectHelper.Contains(boundingRect, position); + return contains; + } + + private void ResizeSelectedInkStrokes(float scale, Vector2 center) + { + + IEnumerable selectedStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Where(x => x.Selected); + if (selectedStrokes == null) + return; + + Matrix3x2 matrixSacale = Matrix3x2.CreateScale(scale, center); + + foreach (InkStroke inkStroke in selectedStrokes) + { + inkStroke.PointTransform *= matrixSacale; + } + } + + private void MoveSelectedInkStrokes(Point pos) + { + Matrix3x2 matrix = Matrix3x2.CreateTranslation((float)pos.X, (float)pos.Y); + + if (!matrix.IsIdentity) + { + IEnumerable selectedStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Where(x => x.Selected); + if (selectedStrokes == null) + return; + foreach (InkStroke stroke in selectedStrokes) + { + stroke.PointTransform *= matrix; + } + } + } + } +} \ No newline at end of file diff --git a/Samples/SimpleInk/cs/SimpleInk.csproj b/Samples/SimpleInk/cs/SimpleInk.csproj index f551038e77..7bd52dfdcc 100644 --- a/Samples/SimpleInk/cs/SimpleInk.csproj +++ b/Samples/SimpleInk/cs/SimpleInk.csproj @@ -107,6 +107,7 @@ Scenario1.xaml + Scenario2.xaml @@ -162,6 +163,11 @@ MSBuild:Compile Designer + + Scenario13.xaml + MSBuild:Compile + Designer + Scenario2.xaml MSBuild:Compile diff --git a/Samples/SimpleInk/shared/Scenario13.xaml b/Samples/SimpleInk/shared/Scenario13.xaml new file mode 100644 index 0000000000..e4c6e1987b --- /dev/null +++ b/Samples/SimpleInk/shared/Scenario13.xaml @@ -0,0 +1,78 @@ + + + + + + + + + + + + This scenario demonstrates selecting InkStrokes and Moving/Resizing them. + Select the Stroke selection tool, i.e. lasso, and select a drawn Stroke. + When you hover over the Bounding Box your mouse pointer will change to represent the available Manipulation Options. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SharedContent/cs/MainPage.xaml.cs b/SharedContent/cs/MainPage.xaml.cs index f75bbdd17d..4a6fd8787b 100644 --- a/SharedContent/cs/MainPage.xaml.cs +++ b/SharedContent/cs/MainPage.xaml.cs @@ -49,6 +49,7 @@ protected override void OnNavigatedTo(NavigationEventArgs e) { itemCollection.Add(new Scenario { Title = $"{i++}) {s.Title}", ClassType = s.ClassType }); } + ScenarioControl.ItemsSource = itemCollection; if (Window.Current.Bounds.Width < 640) From 4da5b1c098140f51222a4d081226e3449b770c97 Mon Sep 17 00:00:00 2001 From: Joe Harvey Date: Thu, 5 Mar 2020 11:47:00 -0500 Subject: [PATCH 2/2] -fixed peer review comments added comments, removed redundant code, changed order of methods. etc. --- Samples/SimpleInk/cpp/Scenario13.xaml.cpp | 227 ++++++++++++--------- Samples/SimpleInk/cpp/Scenario13.xaml.h | 41 +++- Samples/SimpleInk/cs/Scenario13.xaml.cs | 233 +++++++++++++--------- 3 files changed, 309 insertions(+), 192 deletions(-) diff --git a/Samples/SimpleInk/cpp/Scenario13.xaml.cpp b/Samples/SimpleInk/cpp/Scenario13.xaml.cpp index 79ce270f49..ba92609456 100644 --- a/Samples/SimpleInk/cpp/Scenario13.xaml.cpp +++ b/Samples/SimpleInk/cpp/Scenario13.xaml.cpp @@ -79,6 +79,9 @@ void Scenario13::UnprocessedInput_PointerPressed(InkUnprocessedInput^ sender, Po lasso->Points->Append(args->CurrentPoint->RawPosition); selectionCanvas->Children->Append(lasso); isBoundRect = true; + + // Prevent most handlers along the event route from handling the same event again. + args->Handled = true; } void Scenario13::UnprocessedInput_PointerMoved(InkUnprocessedInput^ sender, PointerEventArgs^ args) @@ -87,6 +90,9 @@ void Scenario13::UnprocessedInput_PointerMoved(InkUnprocessedInput^ sender, Poin { lasso->Points->Append(args->CurrentPoint->RawPosition); } + + // Prevent most handlers along the event route from handling the same event again. + args->Handled = true; } void Scenario13::UnprocessedInput_PointerReleased(InkUnprocessedInput^ sender, PointerEventArgs^ args) @@ -96,77 +102,87 @@ void Scenario13::UnprocessedInput_PointerReleased(InkUnprocessedInput^ sender, P boundingRect = inkCanvas->InkPresenter->StrokeContainer->SelectWithPolyLine(lasso->Points); isBoundRect = false; DrawBoundingRect(); + + // Prevent most handlers along the event route from handling the same event again. + args->Handled = true; +} + +/// +/// ManipulationStarted happens when the mouse is pressed. We use this method to store what Manipulation we want to perform. +/// If it was None, then we don’t want to do anything. We were not even in the Selected Box +/// If the cursor was a SizeAll, then we wanted to move the InkStrokes. +/// Otherwise we have one of the Diagonal cursors so we want to do a resize. +/// +/// +/// +void Scenario13::SelectionRectangle_ManipulationStarted(Object^ sender, Windows::UI::Xaml::Input::ManipulationStartedRoutedEventArgs^ args) +{ + if (currentManipulationType != ManipulationTypes::None) + return; + + Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); + + if (window->PointerCursor->Type == CoreCursorType::SizeAll) + { + currentManipulationType = ManipulationTypes::Move; + } + else + { + currentManipulationType = ManipulationTypes::Size; + } } +/// +/// This is the main process for the InkStoke Manipulation. +/// Here we use the stored currentManipualtiaonType to determine what type of Action we want to preform (Move or Size) +/// If its move, then we call the MoveSelectedInkStrokes method passing the pointers Delta Translation. We also apply that translation to the SelectedRectangle's Translation X and Y. +/// This will move both the InkStrokes and the Selected Box as the mouse pointer moves. +/// If its Size, then we first need to calculate a Scaling Factor. We derive this based on the Pointers Delta Transform X but reducing it by 100 to help match the pointers scroll speed +/// The Delta X can be a Positive or Negative number, so when we add this to 1 we get a reasonable value for a Scale Factor. Typically this will fall between .90 to 1.10 +/// we then check that this is a valid number, as scaling down can cause issues if we go too small or attempt to create Negative height/width. +/// and finally we apply this scale factor to the Selection Box and call the ResizeSelectedInkStrokes. +/// +/// +/// void Scenario13::SelectionRectangle_ManipulationDelta(Object^ sender, Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs^ e) { - CompositeTransform^ transform = (CompositeTransform^) selectionRectangle->RenderTransform; + CompositeTransform^ transform = (CompositeTransform^)selectionRectangle->RenderTransform; switch (currentManipulationType) { - case ManipulationTypes::Move: - MoveSelectedInkStrokes(e->Delta.Translation); - transform->TranslateX += e->Delta.Translation.X; - transform->TranslateY += e->Delta.Translation.Y; + case ManipulationTypes::Move: + MoveSelectedInkStrokes(e->Delta.Translation); + transform->TranslateX += e->Delta.Translation.X; + transform->TranslateY += e->Delta.Translation.Y; - break; + break; - case ManipulationTypes::Size: - //TODO: Play with Scaling factor to change speed at which the "box" will grow. - auto scale = std::abs(1 + (float)e->Delta.Translation.X / 100); + case ManipulationTypes::Size: + //Example Scaling factor used to determine the speed at which the box will grow/shrink. + auto scale = std::abs(1 + (float)e->Delta.Translation.X / 100); - //TODO: need to stop scaling when the area gets too small, otherwise app will crash. Could be refined. - if (selectionRectangle->Width <= 10 && scale < 1.0) - return; + //Restrict scaling to a Minimum value. this well prevent negative With amounts and app crashing. + if (selectionRectangle->Width <= 10 && scale < 1.0) + return; - transform->ScaleX *= scale; - transform->ScaleY *= scale; + transform->ScaleX *= scale; + transform->ScaleY *= scale; - auto offset = selectionRectangle->ActualOffset; - auto center = new Windows::Foundation::Numerics::float2(offset.x + (float)transform->TranslateX, offset.y + (float)transform->TranslateY); + auto offset = selectionRectangle->ActualOffset; + auto center = new Windows::Foundation::Numerics::float2(offset.x + (float)transform->TranslateX, offset.y + (float)transform->TranslateY); - ResizeSelectedInkStrokes(scale, *center); - break; + ResizeSelectedInkStrokes(scale, *center); + break; } } void Scenario13::SelectionRectangle_ManipulationCompleted(Object^ sender, Windows::UI::Xaml::Input::ManipulationCompletedRoutedEventArgs^ args) { currentManipulationType = ManipulationTypes::None; - Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0); } -void Scenario13::SelectionRectangle_ManipulationStarted(Object^ sender, Windows::UI::Xaml::Input::ManipulationStartedRoutedEventArgs^ args) -{ - if (currentManipulationType != ManipulationTypes::None) - return; - - Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); - - if (window->PointerCursor->Type == CoreCursorType::SizeAll) - { - currentManipulationType = ManipulationTypes::Move; - } - else - { - currentManipulationType = ManipulationTypes::Size; - - switch (resizePointerCornor) - { - case RecCornor::TopLeft: - case RecCornor::BottomRight: - window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); - break; - case RecCornor::TopRight: - case RecCornor::BottomLeft: - window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); - break; - } - } -} - void Scenario13::SelectionRectangle_PointerPressed(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) { if (currentManipulationType != ManipulationTypes::None) @@ -184,12 +200,12 @@ void Scenario13::SelectionRectangle_PointerPressed(Object^ sender, Windows::UI:: switch (resizePointerCornor) { - case RecCornor::TopLeft: - case RecCornor::BottomRight: + case RectangleCorner::TopLeft: + case RectangleCorner::BottomRight: window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); break; - case RecCornor::TopRight: - case RecCornor::BottomLeft: + case RectangleCorner::TopRight: + case RectangleCorner::BottomLeft: window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); break; } @@ -204,43 +220,7 @@ void Scenario13::SelectionRectangle_PointerMoved(Object^ sender, Windows::UI::Xa Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); Point mousePos = args->GetCurrentPoint(selectionRectangle)->Position; - window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeAll, 0); - float offset = 5; - float recMinX, recMaxX, recMinY, recMaxY; - recMinX = 0; - recMinY = 0; - - recMaxX = (float)selectionRectangle->ActualWidth; - recMaxY = (float)selectionRectangle->ActualHeight; - - if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && - mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) - { - window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); - resizePointerCornor = RecCornor::TopLeft; - return; - } - else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && - mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) - { - window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); - resizePointerCornor = RecCornor::BottomRight; - return; - } - else if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && - mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) - { - window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); - resizePointerCornor = RecCornor::BottomLeft; - return; - } - else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && - mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) - { - window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); - resizePointerCornor = RecCornor::TopRight; - return; - } + SetPointerCursorType(mousePos); } void Scenario13::SelectionRectangle_PointerExited(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args) @@ -252,7 +232,13 @@ void Scenario13::SelectionRectangle_PointerExited(Object^ sender, Windows::UI::X window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0); } -void Scenario13::ResizeSelectedInkStrokes(float scale, Windows::Foundation::Numerics::float2& center) +/// +/// The method will take all Strokes from the inkCanvas that have been "Selected" as per the link statement. +/// Then we will apply a Matrix3x2 transform, based on the supplied scale and the topLeft point of the bounding box. +/// +/// Scaling factor to apply to the selected points +/// the "X,Y" coordinates of the Selection Box. +void Scenario13::ResizeSelectedInkStrokes(float scale, Windows::Foundation::Numerics::float2& topLeft) { auto allStrokes = inkCanvas->InkPresenter->StrokeContainer->GetStrokes(); @@ -260,7 +246,7 @@ void Scenario13::ResizeSelectedInkStrokes(float scale, Windows::Foundation::Nume if (allStrokes == nullptr) return; - float3x2 matrixSacale = make_float3x2_scale(scale, center); + float3x2 matrixSacale = make_float3x2_scale(scale, topLeft); for (auto stroke : allStrokes) { @@ -314,13 +300,12 @@ void Scenario13::DrawBoundingRect() selectionCanvas->SetLeft(selectionRectangle, boundingRect.X); selectionCanvas->SetTop(selectionRectangle, boundingRect.Y); - manipulationStartedToken = selectionRectangle->ManipulationStarted += ref new Windows::UI::Xaml::Input::ManipulationStartedEventHandler(this, &Scenario13::SelectionRectangle_ManipulationStarted); - manipulationCompletedToken = selectionRectangle->ManipulationCompleted += ref new Windows::UI::Xaml::Input::ManipulationCompletedEventHandler(this, &Scenario13::SelectionRectangle_ManipulationCompleted); manipulationDeltaToken = selectionRectangle->ManipulationDelta += ref new Windows::UI::Xaml::Input::ManipulationDeltaEventHandler(this, &Scenario13::SelectionRectangle_ManipulationDelta); - pointerExitedRectangleToken = selectionRectangle->PointerExited += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &Scenario13::SelectionRectangle_PointerExited); - pointerMovedRectangleToken = selectionRectangle->PointerMoved += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &Scenario13::SelectionRectangle_PointerMoved); + manipulationCompletedToken = selectionRectangle->ManipulationCompleted += ref new Windows::UI::Xaml::Input::ManipulationCompletedEventHandler(this, &Scenario13::SelectionRectangle_ManipulationCompleted); pointerPressedRectangleToken = selectionRectangle->PointerPressed += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &Scenario13::SelectionRectangle_PointerPressed); + pointerMovedRectangleToken = selectionRectangle->PointerMoved += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &Scenario13::SelectionRectangle_PointerMoved); + pointerExitedRectangleToken = selectionRectangle->PointerExited += ref new Windows::UI::Xaml::Input::PointerEventHandler(this, &Scenario13::SelectionRectangle_PointerExited); selectionCanvas->Children->Append(selectionRectangle); } @@ -336,6 +321,9 @@ void Scenario13::ToolButton_Lasso(Object^ sender, RoutedEventArgs^ e) pointerReleasedToken = inkCanvas->InkPresenter->UnprocessedInput->PointerReleased += ref new TypedEventHandler(this, &Scenario13::UnprocessedInput_PointerReleased); } +/// +/// We want to make sure that whenever we clear the SelectionBox we also unhook all out event handlers. +/// void Scenario13::ClearDrawnBoundingRect() { if (selectionCanvas->Children->Size > 0) @@ -371,3 +359,52 @@ bool Scenario13::SelectedBoudningBoxContainsPosition(Windows::Foundation::Point return contains; } +/// +/// Take the current pointer position and determine what cursor you want to display. +/// If the cursor is in a corner within the distance of an OffSet then display a diagonal cursor +/// If it is not in a corner, but still inside the Bounding Box, then display the 4 arrow cursor +/// otherwise it is outside the box so reset to default. +/// +/// +void Scenario13::SetPointerCursorType(Point mousePos) +{ + Windows::UI::Core::CoreWindow^ window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeAll, 0); + float offset = 5; + float recMinX, recMaxX, recMinY, recMaxY; + recMinX = 0; + recMinY = 0; + + recMaxX = (float)selectionRectangle->ActualWidth; + recMaxY = (float)selectionRectangle->ActualHeight; + + if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && + mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) + { + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); + resizePointerCornor = RectangleCorner::TopLeft; + return; + } + else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && + mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) + { + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); + resizePointerCornor = RectangleCorner::BottomRight; + return; + } + else if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && + mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) + { + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); + resizePointerCornor = RectangleCorner::BottomLeft; + return; + } + else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && + mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) + { + window->PointerCursor = ref new CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); + resizePointerCornor = RectangleCorner::TopRight; + return; + } +} + diff --git a/Samples/SimpleInk/cpp/Scenario13.xaml.h b/Samples/SimpleInk/cpp/Scenario13.xaml.h index 207eb2646c..1c398c6523 100644 --- a/Samples/SimpleInk/cpp/Scenario13.xaml.h +++ b/Samples/SimpleInk/cpp/Scenario13.xaml.h @@ -23,8 +23,34 @@ namespace SDKTemplate public ref class Scenario13 sealed { internal: - enum class RecCornor { TopLeft, TopRight, BottomLeft, BottomRight }; - enum class ManipulationTypes { None, Move, Size }; + /// + /// An enum used to help determine what Pointer symbol we should use. + /// We can use this enum to store which corner of the Selected Boudning Box our pointer was in at the time pointer was pressed down + /// + enum RectangleCorner + { + //We will use a Diagonal a cursor of CoreCursorType.SizeNorthwestSoutheast + TopLeft, + //We will use a Diagonal a cursor of CoreCursorType.SizeNortheastSouthwest + TopRight, + //We will use a Diagonal a cursor of CoreCursorType.SizeNortheastSouthwest + BottomLeft, + //We will use a Diagonal a cursor of CoreCursorType.SizeNorthwestSoutheast + BottomRight + }; + + /// + /// An enum used to help determine what type of Manipulation we are doing to the selected InkStrokes + /// + enum ManipulationTypes + { + //No current Manipulation started - default or base condition + None, + //we are planning or doing a Move manipulation + Move, + //we are planning or doing a Resize manipulation + Size + }; public: Scenario13(); @@ -39,22 +65,23 @@ namespace SDKTemplate void OnSizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); void ToolButton_Lasso(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); void MoveSelectedInkStrokes(Windows::Foundation::Point position); - void ResizeSelectedInkStrokes(float scale, Windows::Foundation::Numerics::float2& center); + void ResizeSelectedInkStrokes(float scale, Windows::Foundation::Numerics::float2& topLeft); void DrawBoundingRect(); void ClearDrawnBoundingRect(); void ClearSelection(); + void SetPointerCursorType(Windows::Foundation::Point mousePos); void UnprocessedInput_PointerPressed(Windows::UI::Input::Inking::InkUnprocessedInput^ sender, Windows::UI::Core::PointerEventArgs^ args); void UnprocessedInput_PointerMoved(Windows::UI::Input::Inking::InkUnprocessedInput^ sender, Windows::UI::Core::PointerEventArgs^ args); void UnprocessedInput_PointerReleased(Windows::UI::Input::Inking::InkUnprocessedInput^ sender, Windows::UI::Core::PointerEventArgs^ args); void SelectionRectangle_ManipulationStarted(Object^ sender, Windows::UI::Xaml::Input::ManipulationStartedRoutedEventArgs^ args); - void SelectionRectangle_ManipulationCompleted(Object^ sender, Windows::UI::Xaml::Input::ManipulationCompletedRoutedEventArgs ^ args); void SelectionRectangle_ManipulationDelta(Object^ sender, Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs^ args); + void SelectionRectangle_ManipulationCompleted(Object^ sender, Windows::UI::Xaml::Input::ManipulationCompletedRoutedEventArgs ^ args); - void SelectionRectangle_PointerExited(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args); - void SelectionRectangle_PointerMoved(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args); void SelectionRectangle_PointerPressed(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args); + void SelectionRectangle_PointerMoved(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args); + void SelectionRectangle_PointerExited(Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ args); bool SelectedBoudningBoxContainsPosition(Windows::Foundation::Point position); @@ -63,7 +90,7 @@ namespace SDKTemplate Windows::Foundation::Rect boundingRect; Windows::UI::Xaml::Shapes::Rectangle^ selectionRectangle; ManipulationTypes currentManipulationType; - RecCornor resizePointerCornor; + RectangleCorner resizePointerCornor; bool isBoundRect; Windows::Foundation::EventRegistrationToken pointerPressedToken; diff --git a/Samples/SimpleInk/cs/Scenario13.xaml.cs b/Samples/SimpleInk/cs/Scenario13.xaml.cs index 20c816264b..beef6808c1 100644 --- a/Samples/SimpleInk/cs/Scenario13.xaml.cs +++ b/Samples/SimpleInk/cs/Scenario13.xaml.cs @@ -28,18 +28,33 @@ namespace SDKTemplate { - internal enum RecCornor + + /// + /// An enum used to help determine what Pointer symbol we should use. + /// We can use this enum to store which corner of the Selected Boudning Box our pointer was in at the time pointer was pressed down + /// + internal enum RectangleCorner { + //We will use a Diagonal a cursor of CoreCursorType.SizeNorthwestSoutheast TopLeft, + //We will use a Diagonal a cursor of CoreCursorType.SizeNortheastSouthwest TopRight, + //We will use a Diagonal a cursor of CoreCursorType.SizeNortheastSouthwest BottomLeft, + //We will use a Diagonal a cursor of CoreCursorType.SizeNorthwestSoutheast BottomRight } + /// + /// An enum used to help determine what type of Manipulation we are doing to the selected InkStrokes + /// internal enum ManipulationTypes { + //No current Manipulation started - default or base condition None, + //we are planning or doing a Move manipulation Move, + //we are planning or doing a Resize manipulation Size } @@ -55,7 +70,7 @@ public sealed partial class Scenario13 : Page Symbol LassoSelect = (Symbol)0xEF20; private Rectangle selectionRectangle; private ManipulationTypes currentManipulationType; - private RecCornor resizePointerCornor; + private RectangleCorner resizePointerCornor; public Scenario13() { @@ -77,6 +92,7 @@ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) // Unregistering handlers to clear the selection when inking or erasing is detected inkCanvas.InkPresenter.StrokeInput.StrokeStarted -= StrokeInput_StrokeStarted; inkCanvas.InkPresenter.StrokesErased -= InkPresenter_StrokesErased; + ClearSelection(); } private void StrokeInput_StrokeStarted(InkStrokeInput sender, PointerEventArgs args) @@ -117,16 +133,20 @@ private void UnprocessedInput_PointerPressed(InkUnprocessedInput sender, Pointer lasso.Points.Add(args.CurrentPoint.RawPosition); selectionCanvas.Children.Add(lasso); isBoundRect = true; + + // Prevent most handlers along the event route from handling the same event again. + args.Handled = true; } private void UnprocessedInput_PointerMoved(InkUnprocessedInput sender, PointerEventArgs args) { - args.Handled = true; - if (isBoundRect) { lasso.Points.Add(args.CurrentPoint.RawPosition); } + + // Prevent most handlers along the event route from handling the same event again. + args.Handled = true; } private void UnprocessedInput_PointerReleased(InkUnprocessedInput sender, PointerEventArgs args) @@ -136,6 +156,9 @@ private void UnprocessedInput_PointerReleased(InkUnprocessedInput sender, Pointe boundingRect = inkCanvas.InkPresenter.StrokeContainer.SelectWithPolyLine(lasso.Points); isBoundRect = false; DrawBoundingRect(); + + // Prevent most handlers along the event route from handling the same event again. + args.Handled = true; } private void DrawBoundingRect() @@ -161,17 +184,52 @@ private void DrawBoundingRect() Canvas.SetLeft(selectionRectangle, boundingRect.X); Canvas.SetTop(selectionRectangle, boundingRect.Y); + selectionRectangle.ManipulationStarted += SelectionRectangle_ManipulationStarted; - selectionRectangle.ManipulationCompleted += SelectionRectangle_ManipulationCompleted; selectionRectangle.ManipulationDelta += SelectionRectangle_ManipulationDelta; - selectionRectangle.PointerExited += SelectionRectangle_PointerExited; + selectionRectangle.ManipulationCompleted += SelectionRectangle_ManipulationCompleted; + selectionRectangle.PointerPressed += SelectionRectangle_PointerPressed; selectionRectangle.PointerMoved += SelectionRectangle_PointerMoved; - selectionRectangle.PointerPressed += SelectionRectangle_PointerPressed; ; + selectionRectangle.PointerExited += SelectionRectangle_PointerExited; selectionCanvas.Children.Add(selectionRectangle); + } + + /// + /// ManipulationStarted happens when the mouse is pressed. We use this method to store what Manipulation we want to perform. + /// If it was None, then we don’t want to do anything. We were not even in the Selected Box + /// If the cursor was a SizeAll, then we wanted to move the InkStrokes. + /// Otherwise we have one of the Diagonal cursors so we want to do a resize. + /// + /// + /// + private void SelectionRectangle_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) + { + if (currentManipulationType != ManipulationTypes.None) + return; + if (Window.Current.CoreWindow.PointerCursor.Type == CoreCursorType.SizeAll) + { + currentManipulationType = ManipulationTypes.Move; + } + else + { + currentManipulationType = ManipulationTypes.Size; + } } + /// + /// This is the main process for the InkStoke Manipulation. + /// Here we use the stored currentManipualtiaonType to determine what type of Action we want to preform (Move or Size) + /// If its move, then we call the MoveSelectedInkStrokes method passing the pointers Delta Translation. We also apply that translation to the SelectedRectangle's Translation X and Y. + /// This will move both the InkStrokes and the Selected Box as the mouse pointer moves. + /// If its Size, then we first need to calculate a Scaling Factor. We derive this based on the Pointers Delta Transform X but reducing it by 100 to help match the pointers scroll speed + /// The Delta X can be a Positive or Negative number, so when we add this to 1 we get a reasonable value for a Scale Factor. Typically this will fall between .90 to 1.10 + /// we then check that this is a valid number, as scaling down can cause issues if we go too small or attempt to create Negative height/width. + /// and finally we apply this scale factor to the Selection Box and call the ResizeSelectedInkStrokes. + /// + /// + /// private void SelectionRectangle_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) { e.Handled = true; @@ -186,59 +244,35 @@ private void SelectionRectangle_ManipulationDelta(object sender, ManipulationDel break; case ManipulationTypes.Size: - //TODO: Play with Scaling factor to change speed at which the "box" will grow. - var scale = Math.Abs(1 + (float)e.Delta.Translation.X / 100); - //TODO: need to stop scaling when the area gets too small, otherwise app will crash. Could be refined. - if (selectionRectangle.Width <= 10 && scale < 1f) + //Example Scaling factor used to determine the speed at which the box will grow/shrink. + var scalingFactor = Math.Abs(1 + (float)e.Delta.Translation.X / 100); + + //Restrict scaling to a Minimum value. this well prevent negative With amounts and app crashing. + if (selectionRectangle.Width <= 10 && scalingFactor < 1f) return; - transform.ScaleX *= scale; - transform.ScaleY *= scale; + transform.ScaleX *= scalingFactor; + transform.ScaleY *= scalingFactor; var offset = selectionRectangle.ActualOffset; - ResizeSelectedInkStrokes(scale, new Vector2(offset.X + (float)transform.TranslateX, offset.Y + (float)transform.TranslateY)); + ResizeSelectedInkStrokes(scalingFactor, new Vector2(offset.X + (float)transform.TranslateX, offset.Y + (float)transform.TranslateY)); break; } } + /// + /// When we are done resizing/moving the InkStrokes, we reset the mouse pointer and clear the currentManipulationType placeholder. + /// + /// + /// private void SelectionRectangle_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) { currentManipulationType = ManipulationTypes.None; - - Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0); } - private void SelectionRectangle_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) - { - if (currentManipulationType != ManipulationTypes.None) - return; - - if (Window.Current.CoreWindow.PointerCursor.Type == CoreCursorType.SizeAll) - { - currentManipulationType = ManipulationTypes.Move; - } - else - { - currentManipulationType = ManipulationTypes.Size; - - switch (resizePointerCornor) - { - case RecCornor.TopLeft: - case RecCornor.BottomRight: - Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); - break; - case RecCornor.TopRight: - case RecCornor.BottomLeft: - Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); - break; - } - } - - } - private void SelectionRectangle_PointerPressed(object sender, PointerRoutedEventArgs e) { if (currentManipulationType != ManipulationTypes.None) @@ -254,12 +288,12 @@ private void SelectionRectangle_PointerPressed(object sender, PointerRoutedEvent switch (resizePointerCornor) { - case RecCornor.TopLeft: - case RecCornor.BottomRight: + case RectangleCorner.TopLeft: + case RectangleCorner.BottomRight: Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); break; - case RecCornor.TopRight: - case RecCornor.BottomLeft: + case RectangleCorner.TopRight: + case RectangleCorner.BottomLeft: Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); break; } @@ -272,46 +306,8 @@ private void SelectionRectangle_PointerMoved(object sender, PointerRoutedEventAr return; var mousePos = e.GetCurrentPoint(selectionRectangle).Position; - Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeAll, 0); - var offset = 5; - float recMinX, recMaxX, recMinY, recMaxY; - recMinX = 0; - recMinY = 0; - - recMaxX = (float)selectionRectangle.ActualWidth; - recMaxY = (float)selectionRectangle.ActualHeight; - - if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && - mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) - { - Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); - resizePointerCornor = RecCornor.TopLeft; - return; - } - else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && - mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) - { - Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); - resizePointerCornor = RecCornor.BottomRight; - return; - } - else if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && - mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) - { - Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); - resizePointerCornor = RecCornor.BottomLeft; - return; - } - else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && - mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) - { - Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); - resizePointerCornor = RecCornor.TopRight; - return; - } - - Debug.WriteLine($"SelectionRectangle_ManipulationCompleted::{resizePointerCornor}"); - } + SetPointerCursorType(mousePos); + } private void SelectionRectangle_PointerExited(object sender, PointerRoutedEventArgs e) { @@ -332,6 +328,9 @@ private void ToolButton_Lasso(object sender, RoutedEventArgs e) inkCanvas.InkPresenter.UnprocessedInput.PointerReleased += UnprocessedInput_PointerReleased; } + /// + /// We want to make sure that whenever we clear the SelectionBox we also unhook all out event handlers. + /// private void ClearDrawnBoundingRect() { @@ -366,14 +365,20 @@ private bool SelectedBoudningBoxContainsPosition(Point position) return contains; } - private void ResizeSelectedInkStrokes(float scale, Vector2 center) + /// + /// The method will take all Strokes from the inkCanvas that have been "Selected" as per the link statement. + /// Then we will apply a Matrix3x2 transform, based on the supplied scale and the topLeft point of the bounding box. + /// + /// Scaling factor to apply to the selected points + /// the "X,Y" coordinates of the Selection Box. + private void ResizeSelectedInkStrokes(float scale, Vector2 topLeft) { IEnumerable selectedStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Where(x => x.Selected); if (selectedStrokes == null) return; - Matrix3x2 matrixSacale = Matrix3x2.CreateScale(scale, center); + Matrix3x2 matrixSacale = Matrix3x2.CreateScale(scale, topLeft); foreach (InkStroke inkStroke in selectedStrokes) { @@ -396,5 +401,53 @@ private void MoveSelectedInkStrokes(Point pos) } } } + + /// + /// Take the current pointer position and determine what cursor you want to display. + /// If the cursor is in a corner within the distance of an OffSet then display a diagonal cursor + /// If it is not in a corner, but still inside the Bounding Box, then display the 4 arrow cursor + /// otherwise it is outside the box so reset to default. + /// + /// + private void SetPointerCursorType(Point mousePos) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeAll, 0); + var offset = 5; + float recMinX, recMaxX, recMinY, recMaxY; + recMinX = 0; + recMinY = 0; + + recMaxX = (float)selectionRectangle.ActualWidth; + recMaxY = (float)selectionRectangle.ActualHeight; + + if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && + mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); + resizePointerCornor = RectangleCorner.TopLeft; + return; + } + else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && + mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNorthwestSoutheast, 0); + resizePointerCornor = RectangleCorner.BottomRight; + return; + } + else if (mousePos.X >= recMinX - offset && mousePos.X <= recMinX + offset && + mousePos.Y >= recMaxY - offset && mousePos.Y <= recMaxY + offset) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); + resizePointerCornor = RectangleCorner.BottomLeft; + return; + } + else if (mousePos.X >= recMaxX - offset && mousePos.X <= recMaxX + offset && + mousePos.Y >= recMinY - offset && mousePos.Y <= recMinY + offset) + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.SizeNortheastSouthwest, 0); + resizePointerCornor = RectangleCorner.TopRight; + return; + } + } } } \ No newline at end of file