|
|
|
@ -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); |
|
|
|
} |
|
|
|
|
|
|
|
|