Multi-Platform Package Manager for Stable Diffusion
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
4.5 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Media;
using Avalonia.VisualTree;
using FluentIcons.Common;
using FluentIcons.FluentAvalonia;
using SpacedGridControl.Avalonia;
using StabilityMatrix.Avalonia.Styles;
namespace StabilityMatrix.Avalonia.Controls;
public class StarsRating : TemplatedControl
{
private SymbolIcon? StarFilledIcon => Resources["StarFilledIcon"] as SymbolIcon;
private ItemsControl? itemsControl;
private IEnumerable<SymbolIcon> StarItems => itemsControl!.ItemsSource!.Cast<SymbolIcon>();
public static readonly StyledProperty<bool> IsEditableProperty = AvaloniaProperty.Register<
StarsRating,
bool
>("IsEditable");
public bool IsEditable
{
get => GetValue(IsEditableProperty);
set => SetValue(IsEditableProperty, value);
}
public static readonly StyledProperty<int> MaximumProperty = AvaloniaProperty.Register<
StarsRating,
int
>(nameof(Maximum), 5);
public int Maximum
{
get => GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
public static readonly StyledProperty<double> ValueProperty = AvaloniaProperty.Register<
StarsRating,
double
>(nameof(Value));
public double Value
{
get => GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
/// <inheritdoc />
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
itemsControl = e.NameScope.Find<ItemsControl>("PART_StarsItemsControl")!;
CreateStars();
}
/// <inheritdoc />
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (!this.IsAttachedToVisualTree())
{
return;
}
if (change.Property == ValueProperty || change.Property == MaximumProperty)
{
SyncStarState();
}
}
private void CreateStars()
{
if (itemsControl is null)
{
return;
}
// Fill stars
var stars = new List<Control>();
for (var i = 0; i < Maximum; i++)
{
var star = new SymbolIcon
{
FontSize = FontSize,
Margin = new Thickness(0, 0),
Symbol = Symbol.Star,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Tag = i
};
stars.Add(star);
OnStarAdded(star);
}
itemsControl.ItemsSource = stars;
SyncStarState();
}
private void OnStarAdded(SymbolIcon item)
{
if (IsEditable)
{
item.Tapped += (sender, args) =>
{
var star = (SymbolIcon)sender!;
Value = (int)star.Tag! + 1;
};
}
}
/// <summary>
/// Round a number to the nearest 0.5
/// </summary>
private static double RoundToHalf(double value)
{
return Math.Round(value * 2, MidpointRounding.AwayFromZero) / 2;
}
private void SyncStarState()
{
// Set star to filled when Value is greater than or equal to the star index
foreach (var star in StarItems)
{
// Add 1 to tag since its index is 0-based
var tag = (int)star.Tag! + 1;
// Fill if current is equal or lower than floor of Value
if (tag <= Math.Floor(RoundToHalf(Value)))
{
star.Symbol = Symbol.Star;
star.IsFilled = true;
star.Foreground = Foreground;
}
// If current is between floor and ceil of value, use half-star
else if (tag <= Math.Ceiling(RoundToHalf(Value)))
{
star.Symbol = Symbol.StarHalf;
star.IsFilled = true;
star.Foreground = Foreground;
}
// Otherwise no fill and gray disabled color
else
{
star.Symbol = Symbol.Star;
star.IsFilled = false;
star.Foreground = new SolidColorBrush(Colors.DarkSlateGray);
}
}
}
}