Browse Source

Merge pull request #4219 from Mehigh17/feature/custom-ticks-slider

Snap thumb on custom ticks and add Ticks property on slider
pull/4221/head
Jumar Macato 6 years ago
committed by GitHub
parent
commit
3c098e4393
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      samples/ControlCatalog/Pages/SliderPage.xaml
  2. 66
      src/Avalonia.Controls/Slider.cs
  3. 12
      src/Avalonia.Controls/TickBar.cs
  4. 9
      src/Avalonia.Themes.Default/Slider.xaml
  5. 1
      src/Avalonia.Themes.Fluent/Slider.xaml

21
samples/ControlCatalog/Pages/SliderPage.xaml

@ -6,11 +6,22 @@
<TextBlock Classes="h2">A control that lets the user select from a range of values by moving a Thumb control along a Track.</TextBlock> <TextBlock Classes="h2">A control that lets the user select from a range of values by moving a Thumb control along a Track.</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="16"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="16">
<Slider Value="0" <StackPanel Orientation="Vertical"
Minimum="0" HorizontalAlignment="Center">
Maximum="100" <Slider Value="0"
TickFrequency="10" Minimum="0"
Width="300"/> Maximum="100"
TickFrequency="10"
Width="300" />
<Slider Name="CustomTickedSlider"
Value="0"
Minimum="0"
Maximum="100"
TickPlacement="BottomRight"
IsSnapToTickEnabled="True"
Ticks="0,20,25,40,75,100"
Width="300" />
</StackPanel>
<Slider Value="0" <Slider Value="0"
Minimum="0" Minimum="0"
Maximum="100" Maximum="100"

66
src/Avalonia.Controls/Slider.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Collections;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
@ -64,6 +65,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty<TickPlacement> TickPlacementProperty = public static readonly StyledProperty<TickPlacement> TickPlacementProperty =
AvaloniaProperty.Register<TickBar, TickPlacement>(nameof(TickPlacement), 0d); AvaloniaProperty.Register<TickBar, TickPlacement>(nameof(TickPlacement), 0d);
/// <summary>
/// Defines the <see cref="TicksProperty"/> property.
/// </summary>
public static readonly StyledProperty<AvaloniaList<double>> TicksProperty =
TickBar.TicksProperty.AddOwner<Slider>();
// Slider required parts // Slider required parts
private bool _isDragging = false; private bool _isDragging = false;
private Track _track; private Track _track;
@ -83,7 +90,8 @@ namespace Avalonia.Controls
PressedMixin.Attach<Slider>(); PressedMixin.Attach<Slider>();
OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal); OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal);
Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble); Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e), RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e),
RoutingStrategies.Bubble);
} }
/// <summary> /// <summary>
@ -94,6 +102,15 @@ namespace Avalonia.Controls
UpdatePseudoClasses(Orientation); UpdatePseudoClasses(Orientation);
} }
/// <summary>
/// Defines the ticks to be drawn on the tick bar.
/// </summary>
public AvaloniaList<double> Ticks
{
get => GetValue(TicksProperty);
set => SetValue(TicksProperty, value);
}
/// <summary> /// <summary>
/// Gets or sets the orientation of a <see cref="Slider"/>. /// Gets or sets the orientation of a <see cref="Slider"/>.
/// </summary> /// </summary>
@ -240,19 +257,50 @@ namespace Avalonia.Controls
/// <param name="value">Value that want to snap to closest Tick.</param> /// <param name="value">Value that want to snap to closest Tick.</param>
private double SnapToTick(double value) private double SnapToTick(double value)
{ {
var previous = Minimum; if (IsSnapToTickEnabled)
var next = Maximum;
if (TickFrequency > 0.0)
{ {
previous = Minimum + (Math.Round((value - Minimum) / TickFrequency) * TickFrequency); double previous = Minimum;
next = Math.Min(Maximum, previous + TickFrequency); double 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))
{
for (int i = 0; i < ticks.Count; i++)
{
double tick = ticks[i];
if (MathUtilities.AreClose(tick, value))
{
return value;
}
if (MathUtilities.LessThan(tick, value) && MathUtilities.GreaterThan(tick, previous))
{
previous = tick;
}
else if (MathUtilities.GreaterThan(tick, value) && MathUtilities.LessThan(tick, next))
{
next = tick;
}
}
}
else if (MathUtilities.GreaterThan(TickFrequency, 0.0))
{
previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency);
next = Math.Min(Maximum, previous + TickFrequency);
}
// Choose the closest value between previous and next. If tie, snap to 'next'.
value = MathUtilities.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous;
} }
// Choose the closest value between previous and next. If tie, snap to 'next'. return value;
return MathUtilities.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous;
} }
private void UpdatePseudoClasses(Orientation o) private void UpdatePseudoClasses(Orientation o)
{ {
PseudoClasses.Set(":vertical", o == Orientation.Vertical); PseudoClasses.Set(":vertical", o == Orientation.Vertical);

12
src/Avalonia.Controls/TickBar.cs

@ -1,8 +1,4 @@
using System; using Avalonia.Collections;
using System.Collections.Generic;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -135,15 +131,15 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Ticks"/> property. /// Defines the <see cref="Ticks"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<List<double>> TicksProperty = public static readonly StyledProperty<AvaloniaList<double>> TicksProperty =
AvaloniaProperty.Register<TickBar, List<double>>(nameof(Ticks)); AvaloniaProperty.Register<TickBar, AvaloniaList<double>>(nameof(Ticks));
/// <summary> /// <summary>
/// The Ticks property contains collection of value of type Double which /// The Ticks property contains collection of value of type Double which
/// are the logical positions use to draw the ticks. /// are the logical positions use to draw the ticks.
/// The property value is a <see cref="DoubleCollection" />. /// The property value is a <see cref="DoubleCollection" />.
/// </summary> /// </summary>
public List<double> Ticks public AvaloniaList<double> Ticks
{ {
get { return GetValue(TicksProperty); } get { return GetValue(TicksProperty); }
set { SetValue(TicksProperty, value); } set { SetValue(TicksProperty, value); }

9
src/Avalonia.Themes.Default/Slider.xaml

@ -87,7 +87,10 @@
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
</Style> </Style>
<Style Selector="Slider:disabled /template/ Grid#grid"> <Style Selector="Slider /template/ TickBar">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" /> <Setter Property="Ticks" Value="{TemplateBinding Ticks}" />
</Style> </Style>
<Style Selector="Slider:disabled /template/ Grid#grid">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
</Styles> </Styles>

1
src/Avalonia.Themes.Fluent/Slider.xaml

@ -182,6 +182,7 @@
<Style Selector="Slider /template/ TickBar"> <Style Selector="Slider /template/ TickBar">
<Setter Property="IsVisible" Value="False" /> <Setter Property="IsVisible" Value="False" />
<Setter Property="Ticks" Value="{TemplateBinding Ticks}" />
</Style> </Style>
<!-- TickBar Placement States --> <!-- TickBar Placement States -->

Loading…
Cancel
Save