Skip to content

Commit

Permalink
New Scroller test code for mouse-based panning and mouse-based consta…
Browse files Browse the repository at this point in the history
…nt-velocity-panning (#1472)

* Adding test code for mouse-based panning and mouse-based constant-velocity-panning.

* Added missing IncludeInWindowsAppx tag.
  • Loading branch information
RBrid committed Oct 22, 2019
1 parent 24e624d commit f647d63
Show file tree
Hide file tree
Showing 5 changed files with 326 additions and 1 deletion.
80 changes: 80 additions & 0 deletions dev/Scroller/TestUI/ScrollerMousePanningPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
<local:TestPage
x:Class="MUXControlsTestApp.ScrollerMousePanningPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MUXControlsTestApp"
xmlns:controlsPrimitives="using:Microsoft.UI.Xaml.Controls.Primitives"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<controlsPrimitives:Scroller
x:Name="scroller" Width="500" Height="600"
ZoomMode="Enabled" Margin="4" Background="ForestGreen"
VerticalAlignment="Top">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image x:Name="img00" Source="Assets/ingredient7.png"/>
<Rectangle x:Name="rect01" Width="500" Height="700" Grid.Column="1">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Blue" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Red" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<StackPanel x:Name="sp10" Grid.Row="1" MinWidth="500">
<TextBox FontSize="20" Text="TextBox0" Margin="4"/>
<TextBox FontSize="20" Text="TextBox1" Margin="4"/>
<TextBox FontSize="20" Text="TextBox2" Margin="4"/>
<TextBox FontSize="20" Text="TextBox3" Margin="4"/>
<TextBox FontSize="20" Text="TextBox4" Margin="4"/>
<TextBox FontSize="20" Text="TextBox5" Margin="4"/>
</StackPanel>
<Canvas x:Name="cnv11" Grid.Row="1" Grid.Column="1" Width="500" Height="400">
<Rectangle x:Name="rect" Width="50" Height="50" Canvas.Left="100" Canvas.Top="100">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Blue" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Red" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Canvas>
</Grid>
</controlsPrimitives:Scroller>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Events" Margin="2"/>
<Button x:Name="btnClearEvents" Content="Clear" HorizontalAlignment="Stretch" Margin="2" Grid.Row="1"/>
<ListBox x:Name="lstEvents" HorizontalAlignment="Stretch" Margin="2" Grid.Row="2"/>
</Grid>
<FontIcon
x:Name="fiConstantVelocityPanCenter"
Glyph="&#xEA3A;"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Visibility="Collapsed"
IsHitTestVisible="False"/>
</Grid>
</local:TestPage>
235 changes: 235 additions & 0 deletions dev/Scroller/TestUI/ScrollerMousePanningPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Microsoft.UI.Xaml.Controls;
using System;
using System.Numerics;
using Windows.Foundation;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;

namespace MUXControlsTestApp
{
public sealed partial class ScrollerMousePanningPage : TestPage
{
private bool _isMiddleButtonPressed = false;
private bool _isInConstantVelocityPan = false;
private Point _preConstantVelocityPanPosition;
private bool _isInMousePan = false;
private Point _preMousePanPosition;
private double _preMousePanHorizontalOffset = 0.0;
private double _preMousePanVerticalOffset = 0.0;

public ScrollerMousePanningPage()
{
InitializeComponent();

btnClearEvents.Click += BtnClearEvents_Click;
scroller.AddHandler(UIElement.PointerPressedEvent, new PointerEventHandler(Scroller_PointerPressed), true);
scroller.AddHandler(UIElement.PointerReleasedEvent, new PointerEventHandler(Scroller_PointerReleased), true);
scroller.AddHandler(UIElement.PointerMovedEvent, new PointerEventHandler(Scroller_PointerMoved), true);
scroller.AddHandler(UIElement.PointerCanceledEvent, new PointerEventHandler(Scroller_PointerCanceled), true);
scroller.AddHandler(UIElement.PointerCaptureLostEvent, new PointerEventHandler(Scroller_PointerCaptureLost), true);
}

private void Scroller_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse &&
e.KeyModifiers == Windows.System.VirtualKeyModifiers.None)
{
if (_isMiddleButtonPressed || _isInConstantVelocityPan)
{
(sender as UIElement).ReleasePointerCapture(e.Pointer);
StopConstantVelocityPan();
return;
}

if (!e.Handled)
{
PointerPointProperties ppp = e.GetCurrentPoint(null).Properties;
Point position = e.GetCurrentPoint(scroller).Position;

if (ppp.IsLeftButtonPressed)
{
if (e.OriginalSource == rect)
{
// 'rect' element is meant to be dragged with the mouse using its
// left button, so do not initiate a mouse pan.
return;
}

if ((sender as UIElement).CapturePointer(e.Pointer))
{
Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(
Windows.UI.Core.CoreCursorType.Hand, 0);
_isInMousePan = true;
_preMousePanPosition = position;
_preMousePanHorizontalOffset = scroller.HorizontalOffset;
_preMousePanVerticalOffset = scroller.VerticalOffset;
LogEvent("Pointer capture initiated @ position " + _preMousePanPosition.ToString());
}
else
{
LogEvent("Pointer capture failed");
}
}
else if (ppp.IsMiddleButtonPressed)
{
if ((sender as UIElement).CapturePointer(e.Pointer))
{
Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(
Windows.UI.Core.CoreCursorType.SizeAll, 0);
_isMiddleButtonPressed = true;
_preConstantVelocityPanPosition = position;
fiConstantVelocityPanCenter.Visibility = Visibility.Visible;
fiConstantVelocityPanCenter.Margin = new Thickness(
position.X - 6,
position.Y - 6,
4, 4);
LogEvent("Middle button pressed @ position " + _preConstantVelocityPanPosition.ToString());
}
else
{
LogEvent("Pointer capture failed");
}
}
}
}
}

private void Scroller_PointerReleased(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
{
PointerPointProperties ppp = e.GetCurrentPoint(null).Properties;
Point position = e.GetCurrentPoint(scroller).Position;

if (_isInMousePan && !ppp.IsLeftButtonPressed)
{
(sender as UIElement).ReleasePointerCapture(e.Pointer);
StopMousePan();
LogEvent("Pointer capture released @ position " + position.ToString());
}
else if (_isMiddleButtonPressed && !ppp.IsMiddleButtonPressed)
{
_isMiddleButtonPressed = false;
LogEvent("Middle button released @ position " + position.ToString());
if (Math.Abs(position.X - _preConstantVelocityPanPosition.X) >= 4.0 ||
Math.Abs(position.Y - _preConstantVelocityPanPosition.Y) >= 4.0)
{
if (_isInConstantVelocityPan)
{
(sender as UIElement).ReleasePointerCapture(e.Pointer);
StopConstantVelocityPan();
}
}
else if (!_isInConstantVelocityPan)
{
// Unfortunately mouse capture is automatically lost and apparently cannot
// be regained. Xaml framework should allow retention of mouse capture
// beyond the PointerReleased event to correctly support middle-button-based panning.
StartConstantVelocityPan();
}
}
}
}

private void Scroller_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
{
Point position = e.GetCurrentPoint(scroller).Position;

if (_isInMousePan)
{
scroller.ScrollTo(
_preMousePanPosition.X - position.X + _preMousePanHorizontalOffset,
_preMousePanPosition.Y - position.Y + _preMousePanVerticalOffset,
new ScrollOptions(AnimationMode.Disabled));
}
else
{
if (_isMiddleButtonPressed)
{
if (Math.Abs(position.X - _preConstantVelocityPanPosition.X) >= 4.0 ||
Math.Abs(position.Y - _preConstantVelocityPanPosition.Y) >= 4.0)
{
if (!_isInConstantVelocityPan)
{
LogEvent("Pointer with pressed middle button moved to position " + position.ToString());
StartConstantVelocityPan();
}
}
}

if (_isInConstantVelocityPan)
{
Vector2 offsetsVelocity = new Vector2(
(float)(position.X - _preConstantVelocityPanPosition.X) / 2.0f,
(float)(position.Y - _preConstantVelocityPanPosition.Y) / 2.0f);

scroller.ScrollFrom(offsetsVelocity, inertiaDecayRate: Vector2.Zero);
}
}
}
}

private void Scroller_PointerCanceled(object sender, PointerRoutedEventArgs e)
{
LogEvent("Pointer canceled - id " + e.Pointer.PointerId);
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
{
if (_isInMousePan)
{
StopMousePan();
}
else if (_isMiddleButtonPressed || _isInConstantVelocityPan)
{
StopConstantVelocityPan();
}
}
}

private void Scroller_PointerCaptureLost(object sender, PointerRoutedEventArgs e)
{
LogEvent("Pointer capture lost - id " + e.Pointer.PointerId);
}

private void StopMousePan()
{
Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(
Windows.UI.Core.CoreCursorType.Arrow, 0);
_isInMousePan = false;
_preMousePanHorizontalOffset = 0.0;
_preMousePanVerticalOffset = 0.0;
}

private void StartConstantVelocityPan()
{
_isInConstantVelocityPan = true;
LogEvent("Constant velocity pan started");
}

private void StopConstantVelocityPan()
{
Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(
Windows.UI.Core.CoreCursorType.Arrow, 0);
_isInConstantVelocityPan = false;
_isMiddleButtonPressed = false;
fiConstantVelocityPanCenter.Visibility = Visibility.Collapsed;
scroller.ScrollBy(0, 0);
LogEvent("Constant velocity pan stopped");
}

private void BtnClearEvents_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
lstEvents.Items.Clear();
}

private void LogEvent(string log)
{
lstEvents.Items.Insert(0, log);
}
}
}
1 change: 1 addition & 0 deletions dev/Scroller/TestUI/ScrollerPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<Button x:Name="navigateToCompositionScrollControllers" AutomationProperties.Name="navigateToCompositionScrollControllers" Margin="2" HorizontalAlignment="Stretch">Scroller with composition scroll controllers</Button>
<Button x:Name="navigateToBiDirectionalScrollController" AutomationProperties.Name="navigateToBiDirectionalScrollController" Margin="2" HorizontalAlignment="Stretch">Scroller with composition bi-directional scroll controller</Button>
<Button x:Name="navigateToLeakDetection" AutomationProperties.Name="navigateToLeakDetection" Margin="2" HorizontalAlignment="Stretch">Detect Scroller Memory Leak</Button>
<Button x:Name="navigateToMousePanning" AutomationProperties.Name="navigateToMousePanning" Margin="2" HorizontalAlignment="Stretch">Mouse-based panning</Button>
</StackPanel>

<StackPanel Grid.Column="1">
Expand Down
1 change: 1 addition & 0 deletions dev/Scroller/TestUI/ScrollerPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public ScrollerPage()
navigateToCompositionScrollControllers.Click += delegate { Frame.NavigateWithoutAnimation(typeof(ScrollerWithCompositionScrollControllersPage), 0); };
navigateToBiDirectionalScrollController.Click += delegate { Frame.NavigateWithoutAnimation(typeof(ScrollerWithBiDirectionalScrollControllerPage), 0); };
navigateToLeakDetection.Click += delegate { Frame.NavigateWithoutAnimation(typeof(ScrollerLeakDetectionPage), 0); };
navigateToMousePanning.Click += delegate { Frame.NavigateWithoutAnimation(typeof(ScrollerMousePanningPage), 0); };

try
{
Expand Down
10 changes: 9 additions & 1 deletion dev/Scroller/TestUI/Scroller_TestUI.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<Compile Include="$(MSBuildThisFileDirectory)ScrollerBringIntoViewPage.xaml.cs">
<DependentUpon>ScrollerBringIntoViewPage.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)ScrollerMousePanningPage.xaml.cs">
<DependentUpon>ScrollerMousePanningPage.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)ScrollerSnapPointsPage.xaml.cs">
<DependentUpon>ScrollerSnapPointsPage.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -66,19 +69,24 @@
<IncludeInWindowsAppx>false</IncludeInWindowsAppx>
</Page>
<Page Include="$(MSBuildThisFileDirectory)ScrollerBringIntoViewPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<IncludeInWindowsAppx>false</IncludeInWindowsAppx>
</Page>
<Page Include="$(MSBuildThisFileDirectory)ScrollerMousePanningPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<IncludeInWindowsAppx>false</IncludeInWindowsAppx>
</Page>
<Page Include="$(MSBuildThisFileDirectory)ScrollerSnapPointsPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<IncludeInWindowsAppx>false</IncludeInWindowsAppx>
</Page>
<Page Include="$(MSBuildThisFileDirectory)ScrollerStackPanelAnchoringPage.xaml">
<IncludeInWindowsAppx>false</IncludeInWindowsAppx>
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<IncludeInWindowsAppx>false</IncludeInWindowsAppx>
</Page>
<Page Include="$(MSBuildThisFileDirectory)ScrollerRepeaterAnchoringPage.xaml">
<SubType>Designer</SubType>
Expand Down

0 comments on commit f647d63

Please sign in to comment.