From 8d1ff56e85c1095a296a2546c2652720392f0ca4 Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:54:15 +0800 Subject: [PATCH] feat: almost complete. --- demo/Ursa.Demo/Pages/RatingDemo.axaml | 40 +++-- src/Ursa/Controls/Rating/Rating.cs | 154 ++++++++------------ src/Ursa/Controls/Rating/RatingCharacter.cs | 35 ++++- 3 files changed, 108 insertions(+), 121 deletions(-) diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml b/demo/Ursa.Demo/Pages/RatingDemo.axaml index d7c9f7c..856958f 100644 --- a/demo/Ursa.Demo/Pages/RatingDemo.axaml +++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml @@ -21,8 +21,7 @@ IsEnabled="{Binding IsEnabled}" Value="{Binding Value}" Count="{Binding Count}" - DefaultValue="{Binding DefaultValue }" - Tooltips="{Binding Tooltips }" /> + DefaultValue="{Binding DefaultValue }" /> @@ -71,41 +70,40 @@ Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" - Content="Value" /> - + diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs index b1b819d..66a4edb 100644 --- a/src/Ursa/Controls/Rating/Rating.cs +++ b/src/Ursa/Controls/Rating/Rating.cs @@ -1,13 +1,10 @@ -using System.Collections; -using System.Collections.ObjectModel; -using Avalonia; +using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Data; -using Avalonia.Input; using Avalonia.Interactivity; namespace Ursa.Controls; @@ -20,7 +17,7 @@ public class Rating : TemplatedControl protected const string PC_Selected = ":selected"; private ItemsControl? _itemsControl; - private const double Tolerance = 0.0001; + private const double Tolerance = 0.00000001; public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register(nameof(Value), defaultBindingMode: BindingMode.TwoWay); @@ -43,18 +40,6 @@ public class Rating : TemplatedControl public static readonly StyledProperty DefaultValueProperty = AvaloniaProperty.Register(nameof(DefaultValue)); - public static readonly StyledProperty> TooltipsProperty = - AvaloniaProperty.Register>(nameof(Tooltips)); - - public static readonly StyledProperty SelectedTooltipProperty = - AvaloniaProperty.Register(nameof(SelectedTooltip)); - - public string SelectedTooltip - { - get => GetValue(SelectedTooltipProperty); - set => SetValue(SelectedTooltipProperty, value); - } - public static readonly StyledProperty ItemTemplateProperty = AvaloniaProperty.Register(nameof(ItemTemplate)); @@ -100,25 +85,19 @@ public class Rating : TemplatedControl set => SetValue(DefaultValueProperty, value); } - public IList Tooltips - { - get => GetValue(TooltipsProperty); - set => SetValue(TooltipsProperty, value); - } - public IDataTemplate? ItemTemplate { get => GetValue(ItemTemplateProperty); set => SetValue(ItemTemplateProperty, value); } - public static readonly DirectProperty ItemsProperty = - AvaloniaProperty.RegisterDirect( + public static readonly DirectProperty> ItemsProperty = + AvaloniaProperty.RegisterDirect>( nameof(Items), o => o.Items); - private IList _items; + private AvaloniaList _items; - public IList Items + public AvaloniaList Items { get => _items; private set => SetAndRaise(ItemsProperty, ref _items, value); @@ -126,14 +105,14 @@ public class Rating : TemplatedControl public Rating() { - Items = new AvaloniaList(); - Tooltips = new ObservableCollection(); + Items = []; } static Rating() { ValueProperty.Changed.AddClassHandler((s, e) => s.OnValueChanged(e)); CountProperty.Changed.AddClassHandler((s, e) => s.OnCountChanged(e)); + AllowHalfProperty.Changed.AddClassHandler((s, e) => s.OnAllowHalfChanged(e)); } private void OnValueChanged(AvaloniaPropertyChangedEventArgs e) @@ -169,6 +148,23 @@ public class Rating : TemplatedControl } } + UpdateItemsByValue(Value); + foreach (var item in Items) + { + item.AllowHalf = AllowHalf; + } + + AdjustWidth(Value); + } + + private void OnAllowHalfChanged(AvaloniaPropertyChangedEventArgs e) + { + if (e.NewValue is not bool newValue) return; + foreach (var item in Items) + { + item.AllowHalf = newValue; + } + UpdateItemsByValue(Value); AdjustWidth(Value); } @@ -183,113 +179,77 @@ public class Rating : TemplatedControl Items.Add(new RatingCharacter()); } - if (DefaultValue > Count) - { - SetCurrentValue(ValueProperty, Math.Max(Count, 0)); - } - else - { - SetCurrentValue(ValueProperty, DefaultValue); - } + SetCurrentValue(ValueProperty, DefaultValue); } protected override void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); + UpdateItemsByValue(Value); AdjustWidth(Value); } public void Preview(RatingCharacter o) { var index = Items.IndexOf(o); - var tooltipsCount = Tooltips.Count; - if (tooltipsCount > 0) - { - if (index < tooltipsCount) - { - SetCurrentValue(SelectedTooltipProperty, Tooltips[index]); - } - else - { - SetCurrentValue(SelectedTooltipProperty, string.Empty); - } - } - UpdateItemsByIndex(index); } - protected override void OnPointerExited(PointerEventArgs e) - { - UpdateItemsByValue(Value); - } - - public void PointerReleasedHandler(RatingCharacter o) + internal void PointerReleasedHandler(RatingCharacter o) { var index = Items.IndexOf(o); double newValue = index + 1; - if (o.IsHalf) + if (AllowHalf && o.IsHalf) { newValue = index + 0.5; } if (AllowClear && Math.Abs(Value - newValue) < Tolerance) { - UpdateItemsByValue(-1); SetCurrentValue(ValueProperty, 0); } else { - UpdateItemsByValue(newValue); SetCurrentValue(ValueProperty, newValue); } } - private void UpdateItemsByIndex(int index) - { - var isInt = Math.Abs(Value - Math.Floor(Value)) < Tolerance; - for (var i = 0; i <= index && i < Items.Count; i++) - { - if (Items[i] is RatingCharacter item) - { - item.Select(true); - item.IsHalf = !isInt && i == index; - } - } - - for (var i = index + 1; i >= 0 && i < Items.Count; i++) - { - if (Items[i] is RatingCharacter item) - { - item.Select(false); - item.IsHalf = false; - } - } - } - - private void UpdateItemsByValue(double newValue) + internal void UpdateItemsByValue(double newValue) { var index = (int)Math.Ceiling(newValue - 1); UpdateItemsByIndex(index); } - private void AdjustWidth(double newValue) + private void UpdateItemsByIndex(int index) + { + for (var i = 0; i < Items.Count; i++) + { + if (i == index) continue; + Items[i].Select(i < index); + Items[i].IsLast = false; + Items[i].IsHalf = false; + Items[i].Ratio = 1; + } + + if (index >= Items.Count || index < 0) return; + var ratio = Math.Abs(Value - Math.Floor(Value)); + var isInt = ratio < Tolerance; + Items[index].Select(true); + Items[index].IsLast = true; + Items[index].IsHalf = AllowHalf && !isInt; + Items[index].Ratio = AllowHalf ? ratio : 1; + } + + + internal void AdjustWidth(double newValue) { var ratio = Math.Abs(newValue - Math.Floor(newValue)); - foreach (var character in Items) + var isInt = ratio < Tolerance; + ratio = AllowHalf && !isInt ? ratio : 1; + foreach (var item in Items) { - if (character is RatingCharacter item) - { - if (item.IsHalf) - { - item.Ratio = ratio; - } - else - { - item.Ratio = 1; - } - - item.AdjustWidth(); - } + item.Ratio = item.IsLast ? ratio : 1; + item.AdjustWidth(); } } } \ No newline at end of file diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs index 4dea017..aae77a2 100644 --- a/src/Ursa/Controls/Rating/RatingCharacter.cs +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -1,7 +1,9 @@ -using Avalonia.Controls; +using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.LogicalTree; namespace Ursa.Controls; @@ -15,6 +17,18 @@ public class RatingCharacter : TemplatedControl private Control? _icon; + public static readonly StyledProperty AllowHalfProperty = AvaloniaProperty.Register( + nameof(AllowHalf)); + + + public bool AllowHalf + { + get => GetValue(AllowHalfProperty); + set => SetValue(AllowHalfProperty, value); + } + + internal bool IsLast { get; set; } + private bool _isHalf; internal bool IsHalf @@ -22,6 +36,7 @@ public class RatingCharacter : TemplatedControl get => _isHalf; set { + if (!AllowHalf) return; _isHalf = value; if (_icon is null) return; _icon.Width = value ? Bounds.Width * 0.5 : Bounds.Width; @@ -30,6 +45,12 @@ public class RatingCharacter : TemplatedControl internal double Ratio { get; set; } + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + AdjustWidth(); + } + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); @@ -44,22 +65,30 @@ public class RatingCharacter : TemplatedControl protected override void OnPointerMoved(PointerEventArgs e) { + if (!AllowHalf) return; var p = e.GetPosition(this); IsHalf = p.X < Bounds.Width * 0.5; } + protected override void OnPointerExited(PointerEventArgs e) + { + var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); + parent?.UpdateItemsByValue(parent.Value); + parent?.AdjustWidth(parent.Value); + } + protected override void OnPointerReleased(PointerReleasedEventArgs e) { var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); parent?.PointerReleasedHandler(this); } - public void Select(bool value) + internal void Select(bool value) { PseudoClasses.Set(PC_Selected, value); } - public void AdjustWidth() + internal void AdjustWidth() { if (_icon is not null) {