From 1a0087b5215c7e0501f911e70c7ebb557e72f408 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 11 Apr 2022 14:35:40 +0200 Subject: [PATCH 1/3] Don't multiply logical scroll by ScrollSize. `ScrollSize` is intended to be the amount scrolled when clicking the up/down line scroll buttons. The `logicalScrollItemSize` calculation should have already taken care of calculating this value for scroll gestures (this wasn't noticed before because the only place where it was checked was with `ItemsPresenter` which has a `ScrollSize.Height` of 1. --- src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 97fb4c3f43..a8bffcc842 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -389,7 +389,7 @@ namespace Avalonia.Controls.Presenters { var logicalUnits = delta.Y / logicalScrollItemSize.Y; delta = delta.WithY(delta.Y - logicalUnits * logicalScrollItemSize.Y); - dy = logicalUnits * scrollable!.ScrollSize.Height; + dy = logicalUnits; } else dy = delta.Y; @@ -407,7 +407,7 @@ namespace Avalonia.Controls.Presenters { var logicalUnits = delta.X / logicalScrollItemSize.X; delta = delta.WithX(delta.X - logicalUnits * logicalScrollItemSize.X); - dx = logicalUnits * scrollable!.ScrollSize.Width; + dx = logicalUnits; } else dx = delta.X; From 4deaa51ef172584ac99f01b4efe66e0670e55d17 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 11 Apr 2022 14:59:48 +0200 Subject: [PATCH 2/3] Return correct value for viewport. The `DateTimePickerPanel`'s viewport is __not__ the height of a single item ;) Scrolling is done in pixels, so it should be the size of the bounds. --- src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index a923c44d05..65347ab2e0 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -270,7 +270,7 @@ namespace Avalonia.Controls.Primitives public Size Extent => _extent; - public Size Viewport => new Size(0, ItemHeight); + public Size Viewport => Bounds.Size; public event EventHandler? ScrollInvalidated; From f87d6e4121a606f2db8516de5c4ad12f71ae435b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 11 Apr 2022 15:00:42 +0200 Subject: [PATCH 3/3] Snap date/time picker to value on scroll end. When a scroll gesture ends, snap the date/time picker offset to the nearest multiple of the item height. --- .../DateTimePickers/DateTimePickerPanel.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index 65347ab2e0..667f994a1d 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -1,7 +1,9 @@ using System; using System.Globalization; using System.Linq; +using Avalonia.Controls.Presenters; using Avalonia.Input; +using Avalonia.Input.GestureRecognizers; using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.VisualTree; @@ -60,6 +62,7 @@ namespace Avalonia.Controls.Primitives private Vector _offset; private bool _hasInit; private bool _suppressUpdateOffset; + private ScrollContentPresenter? _parentScroller; public DateTimePickerPanel() { @@ -255,6 +258,8 @@ namespace Avalonia.Controls.Primitives _suppressUpdateOffset = true; SelectedValue = (int)newSel * Increment + MinimumValue; _suppressUpdateOffset = false; + + System.Diagnostics.Debug.WriteLine($"Offset: {_offset} ItemHeight: {ItemHeight}"); } } @@ -341,6 +346,20 @@ namespace Avalonia.Controls.Primitives return finalSize; } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + _parentScroller = this.GetVisualParent() as ScrollContentPresenter; + _parentScroller?.AddHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + _parentScroller?.RemoveHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); + _parentScroller = null; + } + protected override void OnKeyDown(KeyEventArgs e) { switch (e.Key) @@ -554,5 +573,15 @@ namespace Avalonia.Controls.Primitives { ScrollInvalidated?.Invoke(this, e); } + + private void OnScrollGestureEnded(object? sender, ScrollGestureEndedEventArgs e) + { + var snapY = Math.Round(Offset.Y / ItemHeight) * ItemHeight; + + if (snapY != Offset.Y) + { + Offset = Offset.WithY(snapY); + } + } } }