feat: almost complete.
This commit is contained in:
@@ -21,8 +21,7 @@
|
||||
IsEnabled="{Binding IsEnabled}"
|
||||
Value="{Binding Value}"
|
||||
Count="{Binding Count}"
|
||||
DefaultValue="{Binding DefaultValue }"
|
||||
Tooltips="{Binding Tooltips }" />
|
||||
DefaultValue="{Binding DefaultValue }" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border Grid.Column="1" VerticalAlignment="Top">
|
||||
@@ -71,41 +70,40 @@
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Content="Value" />
|
||||
<NumericUpDown
|
||||
Content="DefaultValue" />
|
||||
<Label
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Theme="{StaticResource TagLabel}"
|
||||
Classes="Large"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding DefaultValue}" />
|
||||
<Label
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Content="Value" />
|
||||
<NumericUpDown
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Maximum="100"
|
||||
Minimum="-1"
|
||||
Minimum="-10"
|
||||
Increment="0.1"
|
||||
VerticalAlignment="Center"
|
||||
Value="{Binding Value}" />
|
||||
<Label
|
||||
Grid.Row="5"
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Content="Count" />
|
||||
<NumericUpDown
|
||||
Grid.Row="5"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Maximum="100"
|
||||
Minimum="-1"
|
||||
Minimum="-10"
|
||||
Increment="1"
|
||||
VerticalAlignment="Center"
|
||||
Value="{Binding Count}" />
|
||||
<Label
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Content="DefaultValue" />
|
||||
<NumericUpDown
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Maximum="100"
|
||||
Minimum="-1"
|
||||
Increment="0.1"
|
||||
VerticalAlignment="Center"
|
||||
Value="{Binding DefaultValue}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
@@ -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<double> ValueProperty =
|
||||
AvaloniaProperty.Register<Rating, double>(nameof(Value), defaultBindingMode: BindingMode.TwoWay);
|
||||
@@ -43,18 +40,6 @@ public class Rating : TemplatedControl
|
||||
public static readonly StyledProperty<double> DefaultValueProperty =
|
||||
AvaloniaProperty.Register<Rating, double>(nameof(DefaultValue));
|
||||
|
||||
public static readonly StyledProperty<IList<string>> TooltipsProperty =
|
||||
AvaloniaProperty.Register<Rating, IList<string>>(nameof(Tooltips));
|
||||
|
||||
public static readonly StyledProperty<string> SelectedTooltipProperty =
|
||||
AvaloniaProperty.Register<Rating, string>(nameof(SelectedTooltip));
|
||||
|
||||
public string SelectedTooltip
|
||||
{
|
||||
get => GetValue(SelectedTooltipProperty);
|
||||
set => SetValue(SelectedTooltipProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<IDataTemplate?> ItemTemplateProperty =
|
||||
AvaloniaProperty.Register<Rating, IDataTemplate?>(nameof(ItemTemplate));
|
||||
|
||||
@@ -100,25 +85,19 @@ public class Rating : TemplatedControl
|
||||
set => SetValue(DefaultValueProperty, value);
|
||||
}
|
||||
|
||||
public IList<string> Tooltips
|
||||
{
|
||||
get => GetValue(TooltipsProperty);
|
||||
set => SetValue(TooltipsProperty, value);
|
||||
}
|
||||
|
||||
public IDataTemplate? ItemTemplate
|
||||
{
|
||||
get => GetValue(ItemTemplateProperty);
|
||||
set => SetValue(ItemTemplateProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DirectProperty<Rating, IList> ItemsProperty =
|
||||
AvaloniaProperty.RegisterDirect<Rating, IList>(
|
||||
public static readonly DirectProperty<Rating, AvaloniaList<RatingCharacter>> ItemsProperty =
|
||||
AvaloniaProperty.RegisterDirect<Rating, AvaloniaList<RatingCharacter>>(
|
||||
nameof(Items), o => o.Items);
|
||||
|
||||
private IList _items;
|
||||
private AvaloniaList<RatingCharacter> _items;
|
||||
|
||||
public IList Items
|
||||
public AvaloniaList<RatingCharacter> Items
|
||||
{
|
||||
get => _items;
|
||||
private set => SetAndRaise(ItemsProperty, ref _items, value);
|
||||
@@ -126,14 +105,14 @@ public class Rating : TemplatedControl
|
||||
|
||||
public Rating()
|
||||
{
|
||||
Items = new AvaloniaList<object>();
|
||||
Tooltips = new ObservableCollection<string>();
|
||||
Items = [];
|
||||
}
|
||||
|
||||
static Rating()
|
||||
{
|
||||
ValueProperty.Changed.AddClassHandler<Rating>((s, e) => s.OnValueChanged(e));
|
||||
CountProperty.Changed.AddClassHandler<Rating>((s, e) => s.OnCountChanged(e));
|
||||
AllowHalfProperty.Changed.AddClassHandler<Rating>((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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<bool> AllowHalfProperty = AvaloniaProperty.Register<RatingCharacter, bool>(
|
||||
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<Rating>().FirstOrDefault();
|
||||
parent?.UpdateItemsByValue(parent.Value);
|
||||
parent?.AdjustWidth(parent.Value);
|
||||
}
|
||||
|
||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||
{
|
||||
var parent = this.GetLogicalAncestors().OfType<Rating>().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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user