Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Refresh-Container usability #17496

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
20 changes: 18 additions & 2 deletions samples/ControlCatalog/Pages/RefreshContainerPage.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ControlCatalog.ViewModels"
xmlns:generic="clr-namespace:System.Collections.Generic;assembly=netstandard"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
Expand All @@ -11,12 +12,27 @@
<DockPanel HorizontalAlignment="Stretch"
Height="600"
VerticalAlignment="Top">
<Label DockPanel.Dock="Top">A control that supports pull to refresh</Label>
<StackPanel DockPanel.Dock="Top">
<Label>A control that supports pull to refresh on touch inputs</Label>
<StackPanel Orientation="Horizontal" Margin="3" Spacing="5">
<TextBlock VerticalAlignment="Center">Used pull direction:</TextBlock>
<ComboBox x:Name="PullDirections" SelectedIndex="0">
<ComboBox.ItemsSource>
<generic:List x:TypeArguments="PullDirection">
<PullDirection>TopToBottom</PullDirection>
<PullDirection>LeftToRight</PullDirection>
<PullDirection>RightToLeft</PullDirection>
<PullDirection>BottomToTop</PullDirection>
</generic:List>
</ComboBox.ItemsSource>
</ComboBox>
</StackPanel>
</StackPanel>
<RefreshContainer Name="Refresh"
DockPanel.Dock="Bottom"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
PullDirection="TopToBottom"
PullDirection="{Binding #PullDirections.SelectedItem}"
RefreshRequested="RefreshContainerPage_RefreshRequested"
Margin="5">
<ListBox HorizontalAlignment="Stretch"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public class ScrollGestureRecognizer : GestureRecognizer
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
private bool _isScrollInertiaEnabled;
private Vector? _offset;
private Size? _viewport;
private Size? _extent;
private readonly static int s_defaultScrollStartDistance = (int)((AvaloniaLocator.Current?.GetService<IPlatformSettings>()?.GetTapSize(PointerType.Touch).Height ?? 10) / 2);
private int _scrollStartDistance = s_defaultScrollStartDistance;

Expand Down Expand Up @@ -58,6 +61,30 @@ public class ScrollGestureRecognizer : GestureRecognizer
o => o.ScrollStartDistance, (o, v) => o.ScrollStartDistance = v,
unsetValue: s_defaultScrollStartDistance);

/// <summary>
/// Defines the <see cref="ScrollStartDistance"/> property.
/// </summary>
public static readonly DirectProperty<ScrollGestureRecognizer, Vector?> OffsetProperty =
AvaloniaProperty.RegisterDirect<ScrollGestureRecognizer, Vector?>(nameof(Offset),
o => o.Offset, (o, v) => o.Offset = v,
unsetValue: null);

/// <summary>
/// Defines the <see cref="ScrollStartDistance"/> property.
/// </summary>
public static readonly DirectProperty<ScrollGestureRecognizer, Size?> ExtentProperty =
AvaloniaProperty.RegisterDirect<ScrollGestureRecognizer, Size?>(nameof(Extent),
o => o.Extent, (o, v) => o.Extent = v,
unsetValue: null);

/// <summary>
/// Defines the <see cref="ScrollStartDistance"/> property.
/// </summary>
public static readonly DirectProperty<ScrollGestureRecognizer, Size?> ViewportProperty =
AvaloniaProperty.RegisterDirect<ScrollGestureRecognizer, Size?>(nameof(Viewport),
o => o.Viewport, (o, v) => o.Viewport = v,
unsetValue: null);

/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
Expand Down Expand Up @@ -94,6 +121,33 @@ public int ScrollStartDistance
set => SetAndRaise(ScrollStartDistanceProperty, ref _scrollStartDistance, value);
}

/// <summary>
/// Gets the extent of the scrollable content.
/// </summary>
public Size? Extent
{
get => _extent;
private set => SetAndRaise(ExtentProperty, ref _extent, value);
}

/// <summary>
/// Gets or sets the current scroll offset.
/// </summary>
public Vector? Offset
{
get => _offset;
private set => SetAndRaise(OffsetProperty, ref _offset, value);
}

/// <summary>
/// Gets the size of the viewport on the scrollable content.
/// </summary>
public Size? Viewport
{
get => _viewport;
private set => SetAndRaise(ViewportProperty, ref _viewport, value);
}

protected override void PointerPressed(PointerPressedEventArgs e)
{
if (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)
Expand All @@ -115,10 +169,34 @@ protected override void PointerMoved(PointerEventArgs e)
var rootPoint = e.GetPosition(_rootTarget);
if (!_scrolling)
{
if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance)
_scrolling = true;
if (CanVerticallyScroll && Math.Abs(_trackedRootPoint.Y - rootPoint.Y) > ScrollStartDistance)
_scrolling = true;
if (CanVerticallyScroll)
{
double delta = _trackedRootPoint.Y - rootPoint.Y;

if (Offset?.Y == 0 && delta < 0)
return;

if (Offset?.Y + Viewport?.Height - Extent?.Height == 0 && delta > 0)
return;

if (Math.Abs(delta) > ScrollStartDistance)
_scrolling = true;
}

if (CanHorizontallyScroll)
{
double delta = _trackedRootPoint.X - rootPoint.X;

if (Offset?.X == 0 && delta < 0)
return;

if (Offset?.X + Viewport?.Width - Extent?.Width == 0 && delta > 0)
return;

if (Math.Abs(delta) > ScrollStartDistance)
_scrolling = true;
}

if (_scrolling)
{
// Correct _trackedRootPoint with ScrollStartDistance, so scrolling does not start with a skip of ScrollStartDistance
Expand Down
21 changes: 12 additions & 9 deletions src/Avalonia.Controls/PullToRefresh/RefreshContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)

private void OnVisualizerSizeChanged(Rect obj)
{
if (_hasDefaultRefreshInfoProviderAdapter)
{
_refreshInfoProviderAdapter = new ScrollViewerIRefreshInfoProviderAdapter(PullDirection);
if (_hasDefaultRefreshInfoProviderAdapter && _refreshInfoProviderAdapter != null)
{
_refreshInfoProviderAdapter.UpdateVisualizerSize(obj.Size);
RaisePropertyChanged(RefreshInfoProviderAdapterProperty, null, _refreshInfoProviderAdapter);
}
}
Expand Down Expand Up @@ -231,12 +231,15 @@ private void OnPullDirectionChanged()
break;
}

if (_hasDefaultRefreshInfoProviderAdapter &&
_hasDefaultRefreshVisualizer &&
_refreshVisualizer.Bounds.Height == DefaultPullDimensionSize &&
_refreshVisualizer.Bounds.Width == DefaultPullDimensionSize)
{
_refreshInfoProviderAdapter = new ScrollViewerIRefreshInfoProviderAdapter(PullDirection);
if (_hasDefaultRefreshInfoProviderAdapter)
{
if (_hasDefaultRefreshVisualizer)
{
var size = new Size(_refreshVisualizer.Width, _refreshVisualizer.Height);
_refreshInfoProviderAdapter?.UpdateVisualizerSize(size);
}

_refreshInfoProviderAdapter?.UpdatePullDirection(PullDirection);
RaisePropertyChanged(RefreshInfoProviderAdapterProperty, null, _refreshInfoProviderAdapter);
}
}
Expand Down
24 changes: 22 additions & 2 deletions src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ internal class RefreshInfoProvider : Interactive
{
internal const double DefaultExecutionRatio = 0.8;

private readonly PullDirection _refreshPullDirection;
private readonly Size _refreshVisualizerSize;
private PullDirection _refreshPullDirection;
private Size _refreshVisualizerSize;

private readonly CompositionVisual? _visual;
private bool _isInteractingForRefresh;
Expand All @@ -30,6 +30,14 @@ internal class RefreshInfoProvider : Interactive
AvaloniaProperty.RegisterDirect<RefreshInfoProvider, double>(nameof(InteractionRatio),
s => s.InteractionRatio, (s, o) => s.InteractionRatio = o);

public static readonly DirectProperty<RefreshInfoProvider, PullDirection> PullDirectionProperty =
AvaloniaProperty.RegisterDirect<RefreshInfoProvider, PullDirection>(nameof(PullDirection),
s => s.PullDirection, (s, o) => s.PullDirection = o);

public static readonly DirectProperty<RefreshInfoProvider, Size> RefreshVisualizerSizeProperty =
AvaloniaProperty.RegisterDirect<RefreshInfoProvider, Size>(nameof(RefreshVisualizerSize),
s => s.RefreshVisualizerSize, (s, o) => s.RefreshVisualizerSize = o);

/// <summary>
/// Defines the <see cref="RefreshStarted"/> event.
/// </summary>
Expand Down Expand Up @@ -64,6 +72,18 @@ public double InteractionRatio
set => SetAndRaise(InteractionRatioProperty, ref _interactionRatio, value);
}

public Size RefreshVisualizerSize
{
get => _refreshVisualizerSize;
set => SetAndRaise(RefreshVisualizerSizeProperty, ref _refreshVisualizerSize, value);
}

public PullDirection PullDirection
{
get => _refreshPullDirection;
set => SetAndRaise(PullDirectionProperty, ref _refreshPullDirection, value);
}

public double ExecutionRatio
{
get => DefaultExecutionRatio;
Expand Down
Loading
Loading