Browse Source

Merge pull request #5100 from AvaloniaUI/slider-smalllargechange-keyboard

Add keyboard navigation to slider
pull/5414/head
Dan Walmsley 5 years ago
committed by GitHub
parent
commit
e6d9e23b9a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 111
      src/Avalonia.Controls/Slider.cs

111
src/Avalonia.Controls/Slider.cs

@ -11,7 +11,6 @@ using Avalonia.Utilities;
namespace Avalonia.Controls
{
/// <summary>
/// Enum which describes how to position the ticks in a <see cref="Slider"/>.
/// </summary>
@ -84,6 +83,9 @@ namespace Avalonia.Controls
private IDisposable _increaseButtonSubscription;
private IDisposable _increaseButtonReleaseDispose;
private IDisposable _pointerMovedDispose;
private IDisposable _trackOnKeyDownDispose;
private const double Tolerance = 0.0001;
/// <summary>
/// Initializes static members of the <see cref="Slider"/> class.
@ -95,7 +97,7 @@ namespace Avalonia.Controls
Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e),
RoutingStrategies.Bubble);
ValueProperty.OverrideMetadata<Slider>(new DirectPropertyMetadata<double>(enableDataValidation: true));
}
@ -157,13 +159,14 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_decreaseButtonPressDispose?.Dispose();
_decreaseButtonReleaseDispose?.Dispose();
_increaseButtonSubscription?.Dispose();
_increaseButtonReleaseDispose?.Dispose();
_pointerMovedDispose?.Dispose();
_trackOnKeyDownDispose?.Dispose();
_decreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton");
_track = e.NameScope.Find<Track>("PART_Track");
_increaseButton = e.NameScope.Find<Button>("PART_IncreaseButton");
@ -171,6 +174,7 @@ namespace Avalonia.Controls
if (_track != null)
{
_track.IsThumbDragHandled = true;
_trackOnKeyDownDispose = _track.AddDisposableHandler(KeyDownEvent, TrackOnKeyDown);
}
if (_decreaseButton != null)
@ -188,6 +192,94 @@ namespace Avalonia.Controls
_pointerMovedDispose = this.AddDisposableHandler(PointerMovedEvent, TrackMoved, RoutingStrategies.Tunnel);
}
private void TrackOnKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyModifiers != KeyModifiers.None) return;
switch (e.Key)
{
case Key.Left:
MoveToNextTick(-SmallChange);
break;
case Key.Right:
MoveToNextTick(SmallChange);
break;
case Key.PageUp:
MoveToNextTick(-LargeChange);
break;
case Key.PageDown:
MoveToNextTick(LargeChange);
break;
case Key.Home:
Value = Minimum;
break;
case Key.End:
Value = Maximum;
break;
}
}
private void MoveToNextTick(double direction)
{
if (direction == 0.0) return;
var value = Value;
// Find the next value by snapping
var next = SnapToTick(Math.Max(Minimum, Math.Min(Maximum, value + direction)));
var greaterThan = direction > 0; //search for the next tick greater than value?
// If the snapping brought us back to value, find the next tick point
if (Math.Abs(next - value) < Tolerance
&& !(greaterThan && Math.Abs(value - Maximum) < Tolerance) // Stop if searching up if already at Max
&& !(!greaterThan && Math.Abs(value - Minimum) < Tolerance)) // Stop if searching down if already at Min
{
var ticks = Ticks;
// If ticks collection is available, use it.
// Note that ticks may be unsorted.
if (ticks != null && ticks.Count > 0)
{
foreach (var tick in ticks)
{
// Find the smallest tick greater than value or the largest tick less than value
if (greaterThan && MathUtilities.GreaterThan(tick, value) &&
(MathUtilities.LessThan(tick, next) || Math.Abs(next - value) < Tolerance)
|| !greaterThan && MathUtilities.LessThan(tick, value) &&
(MathUtilities.GreaterThan(tick, next) || Math.Abs(next - value) < Tolerance))
{
next = tick;
}
}
}
else if (MathUtilities.GreaterThan(TickFrequency, 0.0))
{
// Find the current tick we are at
var tickNumber = Math.Round((value - Minimum) / TickFrequency);
if (greaterThan)
tickNumber += 1.0;
else
tickNumber -= 1.0;
next = Minimum + tickNumber * TickFrequency;
}
}
// Update if we've found a better value
if (Math.Abs(next - value) > Tolerance)
{
Value = next;
}
}
private void TrackMoved(object sender, PointerEventArgs e)
{
if (_isDragging)
@ -272,19 +364,18 @@ namespace Avalonia.Controls
{
if (IsSnapToTickEnabled)
{
double previous = Minimum;
double next = Maximum;
var previous = Minimum;
var next = Maximum;
// This property is rarely set so let's try to avoid the GetValue
var ticks = Ticks;
// If ticks collection is available, use it.
// Note that ticks may be unsorted.
if ((ticks != null) && (ticks.Count > 0))
if (ticks != null && ticks.Count > 0)
{
for (int i = 0; i < ticks.Count; i++)
foreach (var tick in ticks)
{
double tick = ticks[i];
if (MathUtilities.AreClose(tick, value))
{
return value;
@ -302,7 +393,7 @@ namespace Avalonia.Controls
}
else if (MathUtilities.GreaterThan(TickFrequency, 0.0))
{
previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency);
previous = Minimum + Math.Round((value - Minimum) / TickFrequency) * TickFrequency;
next = Math.Min(Maximum, previous + TickFrequency);
}

Loading…
Cancel
Save