@ -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 ) ;
_d ecreaseButtonPressDispose ? . Dispose ( ) ;
_d ecreaseButtonReleaseDispose ? . Dispose ( ) ;
_ increaseButtonSubscription ? . Dispose ( ) ;
_ increaseButtonReleaseDispose ? . Dispose ( ) ;
_ pointerMovedDispose ? . Dispose ( ) ;
_ trackOnKeyDownDispose ? . Dispose ( ) ;
_d ecreaseButton = 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 ( _d ecreaseButton ! = 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 ) ;
}