From a5f43c7ee1b07d852f275f61500bf3dd7862a7ec Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 2 Jul 2020 14:49:20 +0200 Subject: [PATCH 1/3] Add ticks property on Slider This property will allow setting custom ticks on the slider without having to create a custom template for the TickBar. --- samples/ControlCatalog/Pages/SliderPage.xaml | 20 ++++++++++++++----- .../ControlCatalog/Pages/SliderPage.xaml.cs | 11 ++++++++++ src/Avalonia.Controls/Slider.cs | 19 +++++++++++++++++- src/Avalonia.Themes.Default/Slider.xaml | 9 ++++++--- src/Avalonia.Themes.Fluent/Slider.xaml | 1 + 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/samples/ControlCatalog/Pages/SliderPage.xaml b/samples/ControlCatalog/Pages/SliderPage.xaml index c6f5521e60..ca289e21fd 100644 --- a/samples/ControlCatalog/Pages/SliderPage.xaml +++ b/samples/ControlCatalog/Pages/SliderPage.xaml @@ -6,11 +6,21 @@ A control that lets the user select from a range of values by moving a Thumb control along a Track. - + + + + ("CustomTickedSlider"); + slider.Ticks = new List + { + 0d, + 5d, + 20d, + 50d, + 100d + }; } } } diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index fe1a4f5ac1..00a26a4c91 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Input; @@ -64,6 +65,12 @@ namespace Avalonia.Controls public static readonly StyledProperty TickPlacementProperty = AvaloniaProperty.Register(nameof(TickPlacement), 0d); + /// + /// Defines the property. + /// + public static readonly StyledProperty> TicksProperty = + TickBar.TicksProperty.AddOwner(); + // Slider required parts private bool _isDragging = false; private Track _track; @@ -83,7 +90,8 @@ namespace Avalonia.Controls PressedMixin.Attach(); OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal); Thumb.DragStartedEvent.AddClassHandler((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble); - Thumb.DragCompletedEvent.AddClassHandler((x, e) => x.OnThumbDragCompleted(e), RoutingStrategies.Bubble); + Thumb.DragCompletedEvent.AddClassHandler((x, e) => x.OnThumbDragCompleted(e), + RoutingStrategies.Bubble); } /// @@ -94,6 +102,15 @@ namespace Avalonia.Controls UpdatePseudoClasses(Orientation); } + /// + /// Defines the ticks to be drawn on the tick bar. + /// + public List Ticks + { + get => GetValue(TicksProperty); + set => SetValue(TicksProperty, value); + } + /// /// Gets or sets the orientation of a . /// diff --git a/src/Avalonia.Themes.Default/Slider.xaml b/src/Avalonia.Themes.Default/Slider.xaml index 1d48a946fc..ba4b1ee998 100644 --- a/src/Avalonia.Themes.Default/Slider.xaml +++ b/src/Avalonia.Themes.Default/Slider.xaml @@ -87,7 +87,10 @@ - + + diff --git a/src/Avalonia.Themes.Fluent/Slider.xaml b/src/Avalonia.Themes.Fluent/Slider.xaml index 539c448e0f..099c2000b8 100644 --- a/src/Avalonia.Themes.Fluent/Slider.xaml +++ b/src/Avalonia.Themes.Fluent/Slider.xaml @@ -182,6 +182,7 @@ From 6561cb739ad0ea3ae5292e3e17d84e75a01e0e94 Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 2 Jul 2020 14:50:17 +0200 Subject: [PATCH 2/3] Snap thumb on custom ticks when they are set --- src/Avalonia.Controls/Slider.cs | 47 +++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 00a26a4c91..b94f9bb7c9 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -257,19 +257,50 @@ namespace Avalonia.Controls /// Value that want to snap to closest Tick. private double SnapToTick(double value) { - var previous = Minimum; - var next = Maximum; - - if (TickFrequency > 0.0) + if (IsSnapToTickEnabled) { - previous = Minimum + (Math.Round((value - Minimum) / TickFrequency) * TickFrequency); - next = Math.Min(Maximum, previous + TickFrequency); + double previous = Minimum; + double next = Maximum; + + // This property is rarely set so let's try to avoid the GetValue + List 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 MathUtilities.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous; + return value; } + private void UpdatePseudoClasses(Orientation o) { PseudoClasses.Set(":vertical", o == Orientation.Vertical); From d8d9a1865dc4abe7466a43f14edbab7262222f7a Mon Sep 17 00:00:00 2001 From: Mihai Stan Date: Thu, 2 Jul 2020 16:34:28 +0200 Subject: [PATCH 3/3] Use AvaloniaList instead of List --- samples/ControlCatalog/Pages/SliderPage.xaml | 1 + samples/ControlCatalog/Pages/SliderPage.xaml.cs | 11 ----------- src/Avalonia.Controls/Slider.cs | 8 ++++---- src/Avalonia.Controls/TickBar.cs | 12 ++++-------- 4 files changed, 9 insertions(+), 23 deletions(-) diff --git a/samples/ControlCatalog/Pages/SliderPage.xaml b/samples/ControlCatalog/Pages/SliderPage.xaml index ca289e21fd..ea31ed0050 100644 --- a/samples/ControlCatalog/Pages/SliderPage.xaml +++ b/samples/ControlCatalog/Pages/SliderPage.xaml @@ -19,6 +19,7 @@ Maximum="100" TickPlacement="BottomRight" IsSnapToTickEnabled="True" + Ticks="0,20,25,40,75,100" Width="300" /> ("CustomTickedSlider"); - slider.Ticks = new List - { - 0d, - 5d, - 20d, - 50d, - 100d - }; } } } diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index b94f9bb7c9..293cbac82f 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using Avalonia.Collections; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Input; @@ -68,7 +68,7 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly StyledProperty> TicksProperty = + public static readonly StyledProperty> TicksProperty = TickBar.TicksProperty.AddOwner(); // Slider required parts @@ -105,7 +105,7 @@ namespace Avalonia.Controls /// /// Defines the ticks to be drawn on the tick bar. /// - public List Ticks + public AvaloniaList Ticks { get => GetValue(TicksProperty); set => SetValue(TicksProperty, value); @@ -263,7 +263,7 @@ namespace Avalonia.Controls double next = Maximum; // This property is rarely set so let's try to avoid the GetValue - List ticks = Ticks; + var ticks = Ticks; // If ticks collection is available, use it. // Note that ticks may be unsorted. diff --git a/src/Avalonia.Controls/TickBar.cs b/src/Avalonia.Controls/TickBar.cs index 16e063beb3..22145d8742 100644 --- a/src/Avalonia.Controls/TickBar.cs +++ b/src/Avalonia.Controls/TickBar.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using Avalonia.Controls.Primitives; -using Avalonia.Data; -using Avalonia.Data.Converters; +using Avalonia.Collections; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Utilities; @@ -135,15 +131,15 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly StyledProperty> TicksProperty = - AvaloniaProperty.Register>(nameof(Ticks)); + public static readonly StyledProperty> TicksProperty = + AvaloniaProperty.Register>(nameof(Ticks)); /// /// The Ticks property contains collection of value of type Double which /// are the logical positions use to draw the ticks. /// The property value is a . /// - public List Ticks + public AvaloniaList Ticks { get { return GetValue(TicksProperty); } set { SetValue(TicksProperty, value); }