From ceede38804df256b4b1103d7349a8a36d694d81c Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:04:04 +0800 Subject: [PATCH 01/15] feat: initialize Rating. --- demo/Ursa.Demo/Pages/RatingDemo.axaml | 114 ++++++++ demo/Ursa.Demo/Pages/RatingDemo.axaml.cs | 13 + .../Ursa.Demo/ViewModels/MainViewViewModel.cs | 1 + demo/Ursa.Demo/ViewModels/MenuViewModel.cs | 2 + .../ViewModels/RatingDemoViewModel.cs | 20 ++ src/Ursa.Themes.Semi/Controls/Rating.axaml | 65 +++++ src/Ursa.Themes.Semi/Controls/_index.axaml | 1 + src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml | 4 + src/Ursa.Themes.Semi/Themes/Dark/_index.axaml | 1 + .../Themes/Light/Rating.axaml | 4 + .../Themes/Light/_index.axaml | 1 + .../Themes/Shared/Rating.axaml | 5 + .../Themes/Shared/_index.axaml | 1 + src/Ursa/Controls/Rating/Rating.cs | 250 ++++++++++++++++++ src/Ursa/Controls/Rating/RatingCharacter.cs | 40 +++ 15 files changed, 522 insertions(+) create mode 100644 demo/Ursa.Demo/Pages/RatingDemo.axaml create mode 100644 demo/Ursa.Demo/Pages/RatingDemo.axaml.cs create mode 100644 demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs create mode 100644 src/Ursa.Themes.Semi/Controls/Rating.axaml create mode 100644 src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml create mode 100644 src/Ursa.Themes.Semi/Themes/Light/Rating.axaml create mode 100644 src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml create mode 100644 src/Ursa/Controls/Rating/Rating.cs create mode 100644 src/Ursa/Controls/Rating/RatingCharacter.cs diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml b/demo/Ursa.Demo/Pages/RatingDemo.axaml new file mode 100644 index 0000000..856f02e --- /dev/null +++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml.cs b/demo/Ursa.Demo/Pages/RatingDemo.axaml.cs new file mode 100644 index 0000000..8168903 --- /dev/null +++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Ursa.Demo.ViewModels; + +namespace Ursa.Demo.Pages; + +public partial class RatingDemo : UserControl +{ + public RatingDemo() + { + InitializeComponent(); + this.DataContext = new RatingDemoViewModel(); + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs index f981fa6..89a8532 100644 --- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs @@ -51,6 +51,7 @@ public class MainViewViewModel : ViewModelBase MenuKeys.MenuKeyNumPad => new NumPadDemoViewModel(), MenuKeys.MenuKeyPagination => new PaginationDemoViewModel(), MenuKeys.MenuKeyRangeSlider => new RangeSliderDemoViewModel(), + MenuKeys.MenuKeyRating => new RatingDemoViewModel(), MenuKeys.MenuKeyScrollToButton => new ScrollToButtonDemoViewModel(), MenuKeys.MenuKeySelectionList => new SelectionListDemoViewModel(), MenuKeys.MenuKeySkeleton => new SkeletonDemoViewModel(), diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs index ead5ab8..ffd9908 100644 --- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs @@ -38,6 +38,7 @@ public class MenuViewModel: ViewModelBase new() { MenuHeader = "NumPad", Key = MenuKeys.MenuKeyNumPad }, new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination }, new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider }, + new() { MenuHeader = "Rating", Key = MenuKeys.MenuKeyRating }, new() { MenuHeader = "Scroll To", Key = MenuKeys.MenuKeyScrollToButton }, new() { MenuHeader = "Selection List", Key = MenuKeys.MenuKeySelectionList }, new() { MenuHeader = "Skeleton", Key = MenuKeys.MenuKeySkeleton }, @@ -83,6 +84,7 @@ public static class MenuKeys public const string MenuKeyNumPad = "NumPad"; public const string MenuKeyPagination = "Pagination"; public const string MenuKeyRangeSlider = "RangeSlider"; + public const string MenuKeyRating = "Rating"; public const string MenuKeyScrollToButton = "ScrollToButton"; public const string MenuKeySelectionList = "SelectionList"; public const string MenuKeyTagInput = "TagInput"; diff --git a/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs new file mode 100644 index 0000000..20aa9e7 --- /dev/null +++ b/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs @@ -0,0 +1,20 @@ +using System.Collections.ObjectModel; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Ursa.Demo.ViewModels; + +public partial class RatingDemoViewModel : ViewModelBase +{ + [ObservableProperty] private bool _allowClear = true; + [ObservableProperty] private bool _allowHalf = true; + [ObservableProperty] private bool _allowFocus; + [ObservableProperty] private bool _isEnabled = true; + + [ObservableProperty] private double _value; + + // [ObservableProperty] private object _character; + [ObservableProperty] private int _count = 10; + [ObservableProperty] private double _defaultValue = 5; + + public ObservableCollection Tooltips { get; set; } = ["1", "2", "3", "4", "5"]; +} \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/Rating.axaml b/src/Ursa.Themes.Semi/Controls/Rating.axaml new file mode 100644 index 0000000..d643eca --- /dev/null +++ b/src/Ursa.Themes.Semi/Controls/Rating.axaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml index bf6acad..23ba36e 100644 --- a/src/Ursa.Themes.Semi/Controls/_index.axaml +++ b/src/Ursa.Themes.Semi/Controls/_index.axaml @@ -28,6 +28,7 @@ + diff --git a/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml new file mode 100644 index 0000000..196c29f --- /dev/null +++ b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml @@ -0,0 +1,4 @@ + + + + diff --git a/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml b/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml index 7f6bc9c..86727f2 100644 --- a/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml +++ b/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml @@ -12,6 +12,7 @@ + diff --git a/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml new file mode 100644 index 0000000..0aa4d07 --- /dev/null +++ b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml @@ -0,0 +1,4 @@ + + + + diff --git a/src/Ursa.Themes.Semi/Themes/Light/_index.axaml b/src/Ursa.Themes.Semi/Themes/Light/_index.axaml index 7f6bc9c..86727f2 100644 --- a/src/Ursa.Themes.Semi/Themes/Light/_index.axaml +++ b/src/Ursa.Themes.Semi/Themes/Light/_index.axaml @@ -12,6 +12,7 @@ + diff --git a/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml new file mode 100644 index 0000000..da8d010 --- /dev/null +++ b/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml @@ -0,0 +1,5 @@ + + M12 5.02863L9.56867 9.10197C9.39633 9.39072 9.10801 9.59094 8.77762 9.65133L4.47932 10.4369L7.47279 13.9041C7.69195 14.158 7.79145 14.494 7.74589 14.8265L7.13481 19.2864L11.5403 17.3919C11.8338 17.2657 12.1662 17.2657 12.4597 17.3919L16.8652 19.2864L16.2541 14.8265C16.2086 14.494 16.308 14.158 16.5272 13.9041L19.5207 10.4369L15.2224 9.65133C14.892 9.59095 14.6037 9.39072 14.4313 9.10198L12 5.02863ZM10.9998 2.56831C11.4521 1.81056 12.5479 1.81057 13.0002 2.56831L16.028 7.64098L21.5427 8.64887C22.4298 8.81101 22.8049 9.87773 22.215 10.561L18.4119 14.9659L19.1938 20.672C19.3171 21.5718 18.4126 22.2617 17.5795 21.9035L12 19.5041L6.4205 21.9035C5.58741 22.2617 4.68294 21.5718 4.80622 20.672L5.58806 14.9659L1.78503 10.561C1.19513 9.87772 1.57018 8.81101 2.45731 8.64887L7.97197 7.64098L10.9998 2.56831Z + M10.7525 1.90411C11.1451 0.698628 12.8549 0.698631 13.2475 1.90411L15.2395 8.01946H21.6858C22.9565 8.01946 23.4848 9.64143 22.4568 10.3865L17.2417 14.1659L19.2337 20.2813C19.6263 21.4868 18.2431 22.4892 17.2151 21.7442L12 17.9647L6.78489 21.7442C5.75687 22.4892 4.37368 21.4868 4.76635 20.2813L6.75834 14.1659L1.54323 10.3865C0.515206 9.64142 1.04354 8.01946 2.31425 8.01946H8.76048L10.7525 1.90411Z + diff --git a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml index 658f6ae..5350d09 100644 --- a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml +++ b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml @@ -13,6 +13,7 @@ + diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs new file mode 100644 index 0000000..37b5a84 --- /dev/null +++ b/src/Ursa/Controls/Rating/Rating.cs @@ -0,0 +1,250 @@ +using System.Collections; +using System.Collections.ObjectModel; +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; + +namespace Ursa.Controls; + +[PseudoClasses(PC_Selected)] +[TemplatePart(PART_ItemsControl, typeof(ItemsControl))] +public class Rating : TemplatedControl +{ + public const string PART_ItemsControl = "PART_ItemsControl"; + protected const string PC_Selected = ":selected"; + + private ItemsControl? _itemsControl; + + public static readonly StyledProperty ValueProperty = + AvaloniaProperty.Register(nameof(Value), defaultBindingMode: BindingMode.TwoWay); + + public static readonly StyledProperty AllowClearProperty = + AvaloniaProperty.Register(nameof(AllowClear), true); + + public static readonly StyledProperty AllowHalfProperty = + AvaloniaProperty.Register(nameof(AllowHalf)); + + public static readonly StyledProperty AllowFocusProperty = + AvaloniaProperty.Register(nameof(AllowFocus)); + + public static readonly StyledProperty CharacterProperty = + AvaloniaProperty.Register(nameof(Character)); + + public static readonly StyledProperty CountProperty = + AvaloniaProperty.Register(nameof(Count), 5); + + 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)); + + public double Value + { + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + public bool AllowClear + { + get => GetValue(AllowClearProperty); + set => SetValue(AllowClearProperty, value); + } + + public bool AllowHalf + { + get => GetValue(AllowHalfProperty); + set => SetValue(AllowHalfProperty, value); + } + + public bool AllowFocus + { + get => GetValue(AllowFocusProperty); + set => SetValue(AllowFocusProperty, value); + } + + public object Character + { + get => GetValue(CharacterProperty); + set => SetValue(CharacterProperty, value); + } + + public int Count + { + get => GetValue(CountProperty); + set => SetValue(CountProperty, value); + } + + public double DefaultValue + { + get => GetValue(DefaultValueProperty); + 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( + nameof(Items), o => o.Items); + + private IList _items; + + public IList Items + { + get => _items; + private set => SetAndRaise(ItemsProperty, ref _items, value); + } + + public Rating() + { + Items = new AvaloniaList(); + Tooltips = new ObservableCollection(); + } + + static Rating() + { + ValueProperty.Changed.AddClassHandler((s, e) => s.OnValueChanged(e)); + CountProperty.Changed.AddClassHandler((s, e) => s.OnCountChanged(e)); + } + + private void OnValueChanged(AvaloniaPropertyChangedEventArgs e) + { + UpdateItems((int)Value - 1); + } + + private void OnCountChanged(AvaloniaPropertyChangedEventArgs e) + { + if (!IsLoaded) return; + + var currentCount = Items.Count; + var newCount = e.GetNewValue(); + + if (currentCount < newCount) + { + var itemsToAdd = newCount - currentCount; + for (var i = 0; i < itemsToAdd; i++) + { + Items.Add(new RatingCharacter()); + } + } + else if (currentCount > newCount) + { + var itemsToRemove = currentCount - newCount; + for (var i = 0; i < itemsToRemove && currentCount > i; i++) + { + Items.RemoveAt(currentCount - i - 1); + } + } + + if (Value > newCount) + { + SetCurrentValue(ValueProperty, Math.Max(newCount, 0)); + } + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + _itemsControl = e.NameScope.Find(PART_ItemsControl); + for (var i = 0; i < Count; i++) + { + Items.Add(new RatingCharacter()); + } + + UpdateItems((int)DefaultValue - 1); + if (DefaultValue > Count) + { + SetCurrentValue(ValueProperty, Math.Max(Count, 0)); + } + else + { + SetCurrentValue(ValueProperty, DefaultValue); + } + } + + 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); + } + } + + UpdateItems(index); + } + + protected override void OnPointerExited(PointerEventArgs e) + { + UpdateItems((int)Value - 1); + } + + public void Select(RatingCharacter o) + { + var index = Items.IndexOf(o); + if (AllowClear && index == (int)Value - 1) + { + UpdateItems(-1); + SetCurrentValue(ValueProperty, 0); + } + else + { + UpdateItems(index); + SetCurrentValue(ValueProperty, index + 1); + } + } + + private void UpdateItems(int index) + { + for (var i = 0; i <= index && i < Items.Count; i++) + { + if (Items[i] is RatingCharacter item) + { + item.Select(true); + } + } + + for (var i = index + 1; i >= 0 && i < Items.Count; i++) + { + if (Items[i] is RatingCharacter item) + { + item.Select(false); + } + } + } +} \ No newline at end of file diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs new file mode 100644 index 0000000..f3e2c2b --- /dev/null +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -0,0 +1,40 @@ +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.LogicalTree; + +namespace Ursa.Controls; + +[PseudoClasses(PC_Selected)] +[TemplatePart(PART_IconGlyph, typeof(PathIcon))] +public class RatingCharacter : ContentControl +{ + public const string PART_IconGlyph = "PART_IconGlyph"; + protected const string PC_Selected = ":selected"; + + private PathIcon? _icon; + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + _icon = e.NameScope.Find(PART_IconGlyph); + } + + protected override void OnPointerEntered(PointerEventArgs e) + { + var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); + parent?.Preview(this); + } + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); + parent?.Select(this); + } + + public void Select(bool value) + { + PseudoClasses.Set(PC_Selected, value); + } +} \ No newline at end of file From f000c68597b10f7dd85f30b3319353b1519dca52 Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:39:50 +0800 Subject: [PATCH 02/15] feat: optimize. --- src/Ursa.Themes.Semi/Controls/Rating.axaml | 33 ++++++++----------- src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml | 6 ++-- .../Themes/Light/Rating.axaml | 6 ++-- .../Themes/Shared/Rating.axaml | 8 ++--- src/Ursa/Controls/Rating/RatingCharacter.cs | 10 +----- 5 files changed, 24 insertions(+), 39 deletions(-) diff --git a/src/Ursa.Themes.Semi/Controls/Rating.axaml b/src/Ursa.Themes.Semi/Controls/Rating.axaml index d643eca..39390c4 100644 --- a/src/Ursa.Themes.Semi/Controls/Rating.axaml +++ b/src/Ursa.Themes.Semi/Controls/Rating.axaml @@ -2,29 +2,24 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:u="https://irihi.tech/ursa"> + + - - - - - - - - - + - + @@ -32,7 +27,7 @@ - + diff --git a/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml index 196c29f..d522d56 100644 --- a/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml +++ b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml index 0aa4d07..0b233f6 100644 --- a/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml +++ b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml index da8d010..37ceecd 100644 --- a/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml +++ b/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml @@ -1,5 +1,3 @@ - - M12 5.02863L9.56867 9.10197C9.39633 9.39072 9.10801 9.59094 8.77762 9.65133L4.47932 10.4369L7.47279 13.9041C7.69195 14.158 7.79145 14.494 7.74589 14.8265L7.13481 19.2864L11.5403 17.3919C11.8338 17.2657 12.1662 17.2657 12.4597 17.3919L16.8652 19.2864L16.2541 14.8265C16.2086 14.494 16.308 14.158 16.5272 13.9041L19.5207 10.4369L15.2224 9.65133C14.892 9.59095 14.6037 9.39072 14.4313 9.10198L12 5.02863ZM10.9998 2.56831C11.4521 1.81056 12.5479 1.81057 13.0002 2.56831L16.028 7.64098L21.5427 8.64887C22.4298 8.81101 22.8049 9.87773 22.215 10.561L18.4119 14.9659L19.1938 20.672C19.3171 21.5718 18.4126 22.2617 17.5795 21.9035L12 19.5041L6.4205 21.9035C5.58741 22.2617 4.68294 21.5718 4.80622 20.672L5.58806 14.9659L1.78503 10.561C1.19513 9.87772 1.57018 8.81101 2.45731 8.64887L7.97197 7.64098L10.9998 2.56831Z - M10.7525 1.90411C11.1451 0.698628 12.8549 0.698631 13.2475 1.90411L15.2395 8.01946H21.6858C22.9565 8.01946 23.4848 9.64143 22.4568 10.3865L17.2417 14.1659L19.2337 20.2813C19.6263 21.4868 18.2431 22.4892 17.2151 21.7442L12 17.9647L6.78489 21.7442C5.75687 22.4892 4.37368 21.4868 4.76635 20.2813L6.75834 14.1659L1.54323 10.3865C0.515206 9.64142 1.04354 8.01946 2.31425 8.01946H8.76048L10.7525 1.90411Z - + + M10.7525 1.90411C11.1451 0.698628 12.8549 0.698631 13.2475 1.90411L15.2395 8.01946H21.6858C22.9565 8.01946 23.4848 9.64143 22.4568 10.3865L17.2417 14.1659L19.2337 20.2813C19.6263 21.4868 18.2431 22.4892 17.2151 21.7442L12 17.9647L6.78489 21.7442C5.75687 22.4892 4.37368 21.4868 4.76635 20.2813L6.75834 14.1659L1.54323 10.3865C0.515206 9.64142 1.04354 8.01946 2.31425 8.01946H8.76048L10.7525 1.90411Z + \ No newline at end of file diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs index f3e2c2b..44cc6c9 100644 --- a/src/Ursa/Controls/Rating/RatingCharacter.cs +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -8,19 +8,11 @@ namespace Ursa.Controls; [PseudoClasses(PC_Selected)] [TemplatePart(PART_IconGlyph, typeof(PathIcon))] -public class RatingCharacter : ContentControl +public class RatingCharacter : TemplatedControl { public const string PART_IconGlyph = "PART_IconGlyph"; protected const string PC_Selected = ":selected"; - private PathIcon? _icon; - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - _icon = e.NameScope.Find(PART_IconGlyph); - } - protected override void OnPointerEntered(PointerEventArgs e) { var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); From 0a30d01b7dc18f4c21bc4d2c4dc5d281be5a80ea Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Tue, 4 Jun 2024 09:11:22 +0800 Subject: [PATCH 03/15] feat: WIP. --- .../ViewModels/RatingDemoViewModel.cs | 2 +- src/Ursa.Themes.Semi/Controls/Rating.axaml | 33 ++++++++++--- src/Ursa/Controls/Rating/Rating.cs | 11 ++++- src/Ursa/Controls/Rating/RatingCharacter.cs | 48 ++++++++++++++++++- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs index 20aa9e7..5911125 100644 --- a/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs @@ -14,7 +14,7 @@ public partial class RatingDemoViewModel : ViewModelBase // [ObservableProperty] private object _character; [ObservableProperty] private int _count = 10; - [ObservableProperty] private double _defaultValue = 5; + [ObservableProperty] private double _defaultValue = 5.5; public ObservableCollection Tooltips { get; set; } = ["1", "2", "3", "4", "5"]; } \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/Rating.axaml b/src/Ursa.Themes.Semi/Controls/Rating.axaml index 39390c4..6a8fb81 100644 --- a/src/Ursa.Themes.Semi/Controls/Rating.axaml +++ b/src/Ursa.Themes.Semi/Controls/Rating.axaml @@ -3,16 +3,29 @@ xmlns:u="https://irihi.tech/ursa"> + - + + + + + + + + + + diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs index 37b5a84..98648ff 100644 --- a/src/Ursa/Controls/Rating/Rating.cs +++ b/src/Ursa/Controls/Rating/Rating.cs @@ -19,6 +19,7 @@ public class Rating : TemplatedControl protected const string PC_Selected = ":selected"; private ItemsControl? _itemsControl; + private const double Tolerance = 0.0001; public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register(nameof(Value), defaultBindingMode: BindingMode.TwoWay); @@ -217,7 +218,13 @@ public class Rating : TemplatedControl public void Select(RatingCharacter o) { var index = Items.IndexOf(o); - if (AllowClear && index == (int)Value - 1) + double newValue = index + 1; + if (o.IsHalf) + { + newValue = index + 0.5; + } + + if (AllowClear && Math.Abs(Value - newValue) < Tolerance) { UpdateItems(-1); SetCurrentValue(ValueProperty, 0); @@ -225,7 +232,7 @@ public class Rating : TemplatedControl else { UpdateItems(index); - SetCurrentValue(ValueProperty, index + 1); + SetCurrentValue(ValueProperty, newValue); } } diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs index 44cc6c9..50344a5 100644 --- a/src/Ursa/Controls/Rating/RatingCharacter.cs +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -6,12 +6,35 @@ using Avalonia.LogicalTree; namespace Ursa.Controls; -[PseudoClasses(PC_Selected)] -[TemplatePart(PART_IconGlyph, typeof(PathIcon))] +[PseudoClasses(PC_Selected, PC_Half)] +[TemplatePart(PART_IconGlyph, typeof(Control))] public class RatingCharacter : TemplatedControl { public const string PART_IconGlyph = "PART_IconGlyph"; protected const string PC_Selected = ":selected"; + protected const string PC_Half = ":half"; + + private Control? _icon; + + private bool _isHalf; + + internal bool IsHalf + { + get => _isHalf; + set + { + if (_isHalf == value) return; + _isHalf = value; + if (_icon is null) return; + _icon.Width = value ? Bounds.Width / 2 : Bounds.Width; + } + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + _icon = e.NameScope.Find(PART_IconGlyph); + } protected override void OnPointerEntered(PointerEventArgs e) { @@ -19,6 +42,27 @@ public class RatingCharacter : TemplatedControl parent?.Preview(this); } + protected override void OnPointerMoved(PointerEventArgs e) + { + var p = e.GetPosition(this); + var flag = p.X < Bounds.Width / 2; + PseudoClasses.Set(PC_Half, flag); + IsHalf = flag; + // if (flag) + // { + // _icon.Width = Bounds.Width / 2; + // } + // else + // { + // _icon.Width = Bounds.Width; + // } + } + + // protected override void OnPointerExited(PointerEventArgs e) + // { + // // _icon.Width = Bounds.Width; + // } + protected override void OnPointerReleased(PointerReleasedEventArgs e) { var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); From fd7010967e38a34cef25a73843ed18ecd96dff29 Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Wed, 5 Jun 2024 10:31:56 +0800 Subject: [PATCH 04/15] feat: WIP. --- demo/Ursa.Demo/Pages/RatingDemo.axaml | 1 - .../ViewModels/RatingDemoViewModel.cs | 4 +- src/Ursa.Themes.Semi/Controls/Rating.axaml | 13 +---- src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml | 5 +- .../Themes/Light/Rating.axaml | 5 +- src/Ursa/Controls/Rating/Rating.cs | 55 +++++++++++++++---- src/Ursa/Controls/Rating/RatingCharacter.cs | 35 +++++------- 7 files changed, 67 insertions(+), 51 deletions(-) diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml b/demo/Ursa.Demo/Pages/RatingDemo.axaml index 856f02e..d7c9f7c 100644 --- a/demo/Ursa.Demo/Pages/RatingDemo.axaml +++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml @@ -14,7 +14,6 @@ - Tooltips { get; set; } = ["1", "2", "3", "4", "5"]; } \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/Rating.axaml b/src/Ursa.Themes.Semi/Controls/Rating.axaml index 6a8fb81..0e84c51 100644 --- a/src/Ursa.Themes.Semi/Controls/Rating.axaml +++ b/src/Ursa.Themes.Semi/Controls/Rating.axaml @@ -3,7 +3,7 @@ xmlns:u="https://irihi.tech/ursa"> - + @@ -15,7 +15,6 @@ Data="{DynamicResource RatingStarIconGlyph}" Fill="{TemplateBinding Background}" /> - diff --git a/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml index d522d56..f9c4707 100644 --- a/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml +++ b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml @@ -1,4 +1,5 @@ - - + + + \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml index 0b233f6..affa457 100644 --- a/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml +++ b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml @@ -1,4 +1,5 @@ - - + + + \ No newline at end of file diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs index 98648ff..1176daf 100644 --- a/src/Ursa/Controls/Rating/Rating.cs +++ b/src/Ursa/Controls/Rating/Rating.cs @@ -137,7 +137,11 @@ public class Rating : TemplatedControl private void OnValueChanged(AvaloniaPropertyChangedEventArgs e) { - UpdateItems((int)Value - 1); + if (e.NewValue is double newValue) + { + UpdateItemsByValue(newValue); + AdjustWidth(newValue); + } } private void OnCountChanged(AvaloniaPropertyChangedEventArgs e) @@ -164,10 +168,8 @@ public class Rating : TemplatedControl } } - if (Value > newCount) - { - SetCurrentValue(ValueProperty, Math.Max(newCount, 0)); - } + UpdateItemsByValue(Value); + AdjustWidth(Value); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -180,7 +182,6 @@ public class Rating : TemplatedControl Items.Add(new RatingCharacter()); } - UpdateItems((int)DefaultValue - 1); if (DefaultValue > Count) { SetCurrentValue(ValueProperty, Math.Max(Count, 0)); @@ -207,15 +208,15 @@ public class Rating : TemplatedControl } } - UpdateItems(index); + UpdateItemsByIndex(index); } protected override void OnPointerExited(PointerEventArgs e) { - UpdateItems((int)Value - 1); + UpdateItemsByValue(Value); } - public void Select(RatingCharacter o) + public void PointerReleasedHandler(RatingCharacter o) { var index = Items.IndexOf(o); double newValue = index + 1; @@ -226,23 +227,25 @@ public class Rating : TemplatedControl if (AllowClear && Math.Abs(Value - newValue) < Tolerance) { - UpdateItems(-1); + UpdateItemsByValue(-1); SetCurrentValue(ValueProperty, 0); } else { - UpdateItems(index); + UpdateItemsByValue(newValue); SetCurrentValue(ValueProperty, newValue); } } - private void UpdateItems(int index) + 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; } } @@ -251,6 +254,34 @@ public class Rating : TemplatedControl if (Items[i] is RatingCharacter item) { item.Select(false); + item.IsHalf = false; + } + } + } + + private void UpdateItemsByValue(double newValue) + { + var index = (int)Math.Ceiling(newValue - 1); + UpdateItemsByIndex(index); + } + + private void AdjustWidth(double newValue) + { + var ratio = Math.Abs(newValue - Math.Floor(newValue)); + foreach (var character in Items) + { + if (character is RatingCharacter item) + { + if (item.IsHalf) + { + item.Ratio = ratio; + } + else + { + item.Ratio = 1; + } + + item.AdjustWidth(); } } } diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs index 50344a5..4dea017 100644 --- a/src/Ursa/Controls/Rating/RatingCharacter.cs +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -6,13 +6,12 @@ using Avalonia.LogicalTree; namespace Ursa.Controls; -[PseudoClasses(PC_Selected, PC_Half)] +[PseudoClasses(PC_Selected)] [TemplatePart(PART_IconGlyph, typeof(Control))] public class RatingCharacter : TemplatedControl { public const string PART_IconGlyph = "PART_IconGlyph"; protected const string PC_Selected = ":selected"; - protected const string PC_Half = ":half"; private Control? _icon; @@ -23,13 +22,14 @@ public class RatingCharacter : TemplatedControl get => _isHalf; set { - if (_isHalf == value) return; _isHalf = value; if (_icon is null) return; - _icon.Width = value ? Bounds.Width / 2 : Bounds.Width; + _icon.Width = value ? Bounds.Width * 0.5 : Bounds.Width; } } + internal double Ratio { get; set; } + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); @@ -45,32 +45,25 @@ public class RatingCharacter : TemplatedControl protected override void OnPointerMoved(PointerEventArgs e) { var p = e.GetPosition(this); - var flag = p.X < Bounds.Width / 2; - PseudoClasses.Set(PC_Half, flag); - IsHalf = flag; - // if (flag) - // { - // _icon.Width = Bounds.Width / 2; - // } - // else - // { - // _icon.Width = Bounds.Width; - // } + IsHalf = p.X < Bounds.Width * 0.5; } - // protected override void OnPointerExited(PointerEventArgs e) - // { - // // _icon.Width = Bounds.Width; - // } - protected override void OnPointerReleased(PointerReleasedEventArgs e) { var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); - parent?.Select(this); + parent?.PointerReleasedHandler(this); } public void Select(bool value) { PseudoClasses.Set(PC_Selected, value); } + + public void AdjustWidth() + { + if (_icon is not null) + { + _icon.Width = Bounds.Width * Ratio; + } + } } \ No newline at end of file From 0a033e032898103adf09d7cc4b8f975e789c301b Mon Sep 17 00:00:00 2001 From: rabbitism Date: Wed, 5 Jun 2024 23:55:28 +0800 Subject: [PATCH 05/15] feat: make sure value is initialized after elements loaded. --- src/Ursa/Controls/Rating/Rating.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs index 1176daf..b1b819d 100644 --- a/src/Ursa/Controls/Rating/Rating.cs +++ b/src/Ursa/Controls/Rating/Rating.cs @@ -8,6 +8,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Input; +using Avalonia.Interactivity; namespace Ursa.Controls; @@ -192,6 +193,12 @@ public class Rating : TemplatedControl } } + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + AdjustWidth(Value); + } + public void Preview(RatingCharacter o) { var index = Items.IndexOf(o); 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 06/15] 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) { From ee2e4c4395dd5554479bee49f740ef66e4fbb93f Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:23:29 +0800 Subject: [PATCH 07/15] fix: fix AllowHalf initialization. --- src/Ursa/Controls/Rating/Rating.cs | 11 ++++------- src/Ursa/Controls/Rating/RatingCharacter.cs | 4 +--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs index 66a4edb..f750002 100644 --- a/src/Ursa/Controls/Rating/Rating.cs +++ b/src/Ursa/Controls/Rating/Rating.cs @@ -179,16 +179,13 @@ public class Rating : TemplatedControl Items.Add(new RatingCharacter()); } + foreach (var item in Items) + { + item.AllowHalf = AllowHalf; + } 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); diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs index aae77a2..6b060a7 100644 --- a/src/Ursa/Controls/Rating/RatingCharacter.cs +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -17,9 +17,7 @@ public class RatingCharacter : TemplatedControl private Control? _icon; - public static readonly StyledProperty AllowHalfProperty = AvaloniaProperty.Register( - nameof(AllowHalf)); - + public static readonly StyledProperty AllowHalfProperty = Rating.AllowHalfProperty.AddOwner(); public bool AllowHalf { From e5628f7f23dd5c517d1ae36ec0249b843094e3ee Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:52:22 +0800 Subject: [PATCH 08/15] feat: optimize. --- demo/Ursa.Demo/Pages/RatingDemo.axaml | 1 + src/Ursa/Controls/Rating/Rating.cs | 63 +++++++++++---------- src/Ursa/Controls/Rating/RatingCharacter.cs | 14 ++--- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml b/demo/Ursa.Demo/Pages/RatingDemo.axaml index 856958f..dadcd77 100644 --- a/demo/Ursa.Demo/Pages/RatingDemo.axaml +++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml @@ -15,6 +15,7 @@ ValueProperty = AvaloniaProperty.Register(nameof(Value), defaultBindingMode: BindingMode.TwoWay); @@ -120,7 +118,6 @@ public class Rating : TemplatedControl if (e.NewValue is double newValue) { UpdateItemsByValue(newValue); - AdjustWidth(newValue); } } @@ -148,13 +145,12 @@ public class Rating : TemplatedControl } } - UpdateItemsByValue(Value); foreach (var item in Items) { item.AllowHalf = AllowHalf; } - AdjustWidth(Value); + UpdateItemsByValue(Value); } private void OnAllowHalfChanged(AvaloniaPropertyChangedEventArgs e) @@ -166,7 +162,6 @@ public class Rating : TemplatedControl } UpdateItemsByValue(Value); - AdjustWidth(Value); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -183,12 +178,19 @@ public class Rating : TemplatedControl { item.AllowHalf = AllowHalf; } + SetCurrentValue(ValueProperty, DefaultValue); } - public void Preview(RatingCharacter o) + internal void PointerEnteredHandler(RatingCharacter o) { var index = Items.IndexOf(o); + var item = Items.FirstOrDefault(item => item.IsLast); + if (item is not null) + { + item.IsHalf = false; + } + UpdateItemsByIndex(index); } @@ -201,7 +203,7 @@ public class Rating : TemplatedControl newValue = index + 0.5; } - if (AllowClear && Math.Abs(Value - newValue) < Tolerance) + if (AllowClear && Math.Abs(Value - newValue) < double.Epsilon) { SetCurrentValue(ValueProperty, 0); } @@ -213,40 +215,43 @@ public class Rating : TemplatedControl internal void UpdateItemsByValue(double newValue) { + RestorePreviousLastItem(); var index = (int)Math.Ceiling(newValue - 1); UpdateItemsByIndex(index); + UpdateChosenItem(newValue); + } + + private void RestorePreviousLastItem() + { + if (!AllowHalf) return; + var item = Items.FirstOrDefault(item => item.IsLast); + if (item is null) return; + item.Ratio = 1; + item.ApplyRatio(); } 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; + Items[i].SetSelectedState(i <= index); + Items[i].IsLast = i == index; } - - 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) + private void UpdateChosenItem(double newValue) { - var ratio = Math.Abs(newValue - Math.Floor(newValue)); - var isInt = ratio < Tolerance; - ratio = AllowHalf && !isInt ? ratio : 1; - foreach (var item in Items) + var ratio = newValue - Math.Floor(newValue); + var isFraction = ratio >= double.Epsilon; + ratio = AllowHalf && isFraction ? ratio : 1; + var item = Items.FirstOrDefault(item => item.IsLast); + if (item is null) return; + if (!AllowHalf && isFraction) { - item.Ratio = item.IsLast ? ratio : 1; - item.AdjustWidth(); + item.SetSelectedState(false); } + + item.Ratio = ratio; + item.ApplyRatio(); } } \ No newline at end of file diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs index 6b060a7..1ae89d2 100644 --- a/src/Ursa/Controls/Rating/RatingCharacter.cs +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -17,7 +17,8 @@ public class RatingCharacter : TemplatedControl private Control? _icon; - public static readonly StyledProperty AllowHalfProperty = Rating.AllowHalfProperty.AddOwner(); + public static readonly StyledProperty AllowHalfProperty = + Rating.AllowHalfProperty.AddOwner(); public bool AllowHalf { @@ -41,12 +42,12 @@ public class RatingCharacter : TemplatedControl } } - internal double Ratio { get; set; } + internal double Ratio { get; set; } = 1; protected override void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); - AdjustWidth(); + ApplyRatio(); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -58,7 +59,7 @@ public class RatingCharacter : TemplatedControl protected override void OnPointerEntered(PointerEventArgs e) { var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); - parent?.Preview(this); + parent?.PointerEnteredHandler(this); } protected override void OnPointerMoved(PointerEventArgs e) @@ -72,7 +73,6 @@ public class RatingCharacter : TemplatedControl { var parent = this.GetLogicalAncestors().OfType().FirstOrDefault(); parent?.UpdateItemsByValue(parent.Value); - parent?.AdjustWidth(parent.Value); } protected override void OnPointerReleased(PointerReleasedEventArgs e) @@ -81,12 +81,12 @@ public class RatingCharacter : TemplatedControl parent?.PointerReleasedHandler(this); } - internal void Select(bool value) + internal void SetSelectedState(bool value) { PseudoClasses.Set(PC_Selected, value); } - internal void AdjustWidth() + internal void ApplyRatio() { if (_icon is not null) { From 34568a4af853a964c0cc60f6a6dfb8aa44c15907 Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Fri, 7 Jun 2024 10:52:35 +0800 Subject: [PATCH 09/15] feat: add property Character. --- demo/Ursa.Demo/Pages/RatingDemo.axaml | 25 ++++++++++++----- src/Ursa.Themes.Semi/Controls/Rating.axaml | 10 +++---- .../Themes/Shared/Rating.axaml | 2 +- src/Ursa/Controls/Rating/Rating.cs | 27 ++++++++++--------- src/Ursa/Controls/Rating/RatingCharacter.cs | 9 +++++++ 5 files changed, 48 insertions(+), 25 deletions(-) diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml b/demo/Ursa.Demo/Pages/RatingDemo.axaml index dadcd77..f2b4af0 100644 --- a/demo/Ursa.Demo/Pages/RatingDemo.axaml +++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml @@ -10,19 +10,32 @@ x:CompileBindings="True" x:DataType="vm:RatingDemoViewModel" mc:Ignorable="d"> + + + M12 5.99999C10.5 2.12432 5.75193 2.0557 3.40383 4.4038C0.865423 6.94221 0.999999 10.5 3.50001 14C5.52139 16.8299 9.83088 20.3136 11.4069 21.5438C11.7573 21.8172 12.2427 21.8172 12.5931 21.5438C14.1691 20.3136 18.4786 16.8299 20.5 14C23 10.5 23.1346 6.94221 20.5962 4.4038C18.2481 2.0557 14.5 2.12432 12 5.99999Z + + - + + DefaultValue="{Binding DefaultValue}" /> + diff --git a/src/Ursa.Themes.Semi/Controls/Rating.axaml b/src/Ursa.Themes.Semi/Controls/Rating.axaml index 0e84c51..25e8e4d 100644 --- a/src/Ursa.Themes.Semi/Controls/Rating.axaml +++ b/src/Ursa.Themes.Semi/Controls/Rating.axaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:u="https://irihi.tech/ursa"> - + @@ -12,14 +12,14 @@ @@ -36,6 +36,8 @@ + + @@ -45,8 +47,6 @@ - M10.7525 1.90411C11.1451 0.698628 12.8549 0.698631 13.2475 1.90411L15.2395 8.01946H21.6858C22.9565 8.01946 23.4848 9.64143 22.4568 10.3865L17.2417 14.1659L19.2337 20.2813C19.6263 21.4868 18.2431 22.4892 17.2151 21.7442L12 17.9647L6.78489 21.7442C5.75687 22.4892 4.37368 21.4868 4.76635 20.2813L6.75834 14.1659L1.54323 10.3865C0.515206 9.64142 1.04354 8.01946 2.31425 8.01946H8.76048L10.7525 1.90411Z + M10.7525 1.90411C11.1451 0.698628 12.8549 0.698631 13.2475 1.90411L15.2395 8.01946H21.6858C22.9565 8.01946 23.4848 9.64143 22.4568 10.3865L17.2417 14.1659L19.2337 20.2813C19.6263 21.4868 18.2431 22.4892 17.2151 21.7442L12 17.9647L6.78489 21.7442C5.75687 22.4892 4.37368 21.4868 4.76635 20.2813L6.75834 14.1659L1.54323 10.3865C0.515206 9.64142 1.04354 8.01946 2.31425 8.01946H8.76048L10.7525 1.90411Z \ No newline at end of file diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs index af8bee4..08ece69 100644 --- a/src/Ursa/Controls/Rating/Rating.cs +++ b/src/Ursa/Controls/Rating/Rating.cs @@ -115,6 +115,7 @@ public class Rating : TemplatedControl private void OnValueChanged(AvaloniaPropertyChangedEventArgs e) { + if (!IsLoaded) return; if (e.NewValue is double newValue) { UpdateItemsByValue(newValue); @@ -133,7 +134,11 @@ public class Rating : TemplatedControl var itemsToAdd = newCount - currentCount; for (var i = 0; i < itemsToAdd; i++) { - Items.Add(new RatingCharacter()); + Items.Add(new RatingCharacter + { + Character = Character, + AllowHalf = AllowHalf + }); } } else if (currentCount > newCount) @@ -145,11 +150,6 @@ public class Rating : TemplatedControl } } - foreach (var item in Items) - { - item.AllowHalf = AllowHalf; - } - UpdateItemsByValue(Value); } @@ -171,26 +171,27 @@ public class Rating : TemplatedControl _itemsControl = e.NameScope.Find(PART_ItemsControl); for (var i = 0; i < Count; i++) { - Items.Add(new RatingCharacter()); - } - - foreach (var item in Items) - { - item.AllowHalf = AllowHalf; + Items.Add(new RatingCharacter + { + Character = Character, + AllowHalf = AllowHalf + } + ); } SetCurrentValue(ValueProperty, DefaultValue); + UpdateItemsByValue(DefaultValue); } internal void PointerEnteredHandler(RatingCharacter o) { - var index = Items.IndexOf(o); var item = Items.FirstOrDefault(item => item.IsLast); if (item is not null) { item.IsHalf = false; } + var index = Items.IndexOf(o); UpdateItemsByIndex(index); } diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs index 1ae89d2..0bfaddc 100644 --- a/src/Ursa/Controls/Rating/RatingCharacter.cs +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -20,12 +20,21 @@ public class RatingCharacter : TemplatedControl public static readonly StyledProperty AllowHalfProperty = Rating.AllowHalfProperty.AddOwner(); + public static readonly StyledProperty CharacterProperty = + Rating.CharacterProperty.AddOwner(); + public bool AllowHalf { get => GetValue(AllowHalfProperty); set => SetValue(AllowHalfProperty, value); } + public object Character + { + get => GetValue(CharacterProperty); + set => SetValue(CharacterProperty, value); + } + internal bool IsLast { get; set; } private bool _isHalf; From 0b72578de1975d2fa18a3d12b80fcace042704ae Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Fri, 7 Jun 2024 11:04:06 +0800 Subject: [PATCH 10/15] feat: Foreground. --- demo/Ursa.Demo/Pages/RatingDemo.axaml | 1 + src/Ursa.Themes.Semi/Controls/Rating.axaml | 8 ++++---- src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml | 1 - src/Ursa.Themes.Semi/Themes/Light/Rating.axaml | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml b/demo/Ursa.Demo/Pages/RatingDemo.axaml index f2b4af0..0344261 100644 --- a/demo/Ursa.Demo/Pages/RatingDemo.axaml +++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml @@ -35,6 +35,7 @@ Value="{Binding Value}" Count="{Binding Count}" DefaultValue="{Binding DefaultValue}" + Foreground="{StaticResource SemiRed5}" Character="{StaticResource LoveHeartGeometry}" /> diff --git a/src/Ursa.Themes.Semi/Controls/Rating.axaml b/src/Ursa.Themes.Semi/Controls/Rating.axaml index 25e8e4d..05f8352 100644 --- a/src/Ursa.Themes.Semi/Controls/Rating.axaml +++ b/src/Ursa.Themes.Semi/Controls/Rating.axaml @@ -15,6 +15,7 @@ Data="{TemplateBinding Character}" Fill="{TemplateBinding Background}" /> - - + - diff --git a/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml index f9c4707..f0cc810 100644 --- a/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml +++ b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml @@ -1,5 +1,4 @@ - \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml index affa457..ddb703a 100644 --- a/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml +++ b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml @@ -1,5 +1,4 @@ - \ No newline at end of file From 8c53aea8902e299a39ff55a8e10ded025c8f2c35 Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Fri, 7 Jun 2024 17:13:05 +0800 Subject: [PATCH 11/15] feat: RatingCharacter scaling. --- src/Ursa.Themes.Semi/Controls/Rating.axaml | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Ursa.Themes.Semi/Controls/Rating.axaml b/src/Ursa.Themes.Semi/Controls/Rating.axaml index 05f8352..594c5f4 100644 --- a/src/Ursa.Themes.Semi/Controls/Rating.axaml +++ b/src/Ursa.Themes.Semi/Controls/Rating.axaml @@ -5,27 +5,27 @@ + - - - + + + - - - - - - + Fill="{TemplateBinding Foreground}" /> + + \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml index aab761e..5065bb6 100644 --- a/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml +++ b/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml @@ -1,3 +1,6 @@  + 24 + 16 + 3 4 M10.7525 1.90411C11.1451 0.698628 12.8549 0.698631 13.2475 1.90411L15.2395 8.01946H21.6858C22.9565 8.01946 23.4848 9.64143 22.4568 10.3865L17.2417 14.1659L19.2337 20.2813C19.6263 21.4868 18.2431 22.4892 17.2151 21.7442L12 17.9647L6.78489 21.7442C5.75687 22.4892 4.37368 21.4868 4.76635 20.2813L6.75834 14.1659L1.54323 10.3865C0.515206 9.64142 1.04354 8.01946 2.31425 8.01946H8.76048L10.7525 1.90411Z \ No newline at end of file diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs index 08ece69..494ab10 100644 --- a/src/Ursa/Controls/Rating/Rating.cs +++ b/src/Ursa/Controls/Rating/Rating.cs @@ -38,6 +38,9 @@ public class Rating : TemplatedControl public static readonly StyledProperty DefaultValueProperty = AvaloniaProperty.Register(nameof(DefaultValue)); + public static readonly StyledProperty SizeProperty = + AvaloniaProperty.Register(nameof(Size)); + public static readonly StyledProperty ItemTemplateProperty = AvaloniaProperty.Register(nameof(ItemTemplate)); @@ -83,6 +86,12 @@ public class Rating : TemplatedControl set => SetValue(DefaultValueProperty, value); } + public double Size + { + get => GetValue(SizeProperty); + set => SetValue(SizeProperty, value); + } + public IDataTemplate? ItemTemplate { get => GetValue(ItemTemplateProperty); @@ -137,7 +146,8 @@ public class Rating : TemplatedControl Items.Add(new RatingCharacter { Character = Character, - AllowHalf = AllowHalf + AllowHalf = AllowHalf, + Size = Size, }); } } @@ -174,7 +184,8 @@ public class Rating : TemplatedControl Items.Add(new RatingCharacter { Character = Character, - AllowHalf = AllowHalf + AllowHalf = AllowHalf, + Size = Size, } ); } diff --git a/src/Ursa/Controls/Rating/RatingCharacter.cs b/src/Ursa/Controls/Rating/RatingCharacter.cs index 0bfaddc..08f5034 100644 --- a/src/Ursa/Controls/Rating/RatingCharacter.cs +++ b/src/Ursa/Controls/Rating/RatingCharacter.cs @@ -23,6 +23,9 @@ public class RatingCharacter : TemplatedControl public static readonly StyledProperty CharacterProperty = Rating.CharacterProperty.AddOwner(); + public static readonly StyledProperty SizeProperty = + Rating.SizeProperty.AddOwner(); + public bool AllowHalf { get => GetValue(AllowHalfProperty); @@ -35,6 +38,12 @@ public class RatingCharacter : TemplatedControl set => SetValue(CharacterProperty, value); } + public double Size + { + get => GetValue(SizeProperty); + set => SetValue(SizeProperty, value); + } + internal bool IsLast { get; set; } private bool _isHalf; From 6a21aec1dc5e77600524cdaa5d1d9b3a8001719a Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:20:33 +0800 Subject: [PATCH 13/15] misc: rename & remove. --- demo/Ursa.Demo/Pages/RatingDemo.axaml | 13 ------------- demo/Ursa.Demo/ViewModels/MenuViewModel.cs | 2 +- demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs | 9 +-------- src/Ursa.Themes.Semi/Controls/Rating.axaml | 4 ++-- src/Ursa/Controls/Rating/Rating.cs | 13 ------------- src/Ursa/Controls/Rating/RatingCharacter.cs | 8 ++++---- 6 files changed, 8 insertions(+), 41 deletions(-) diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml b/demo/Ursa.Demo/Pages/RatingDemo.axaml index 770b251..89ab51f 100644 --- a/demo/Ursa.Demo/Pages/RatingDemo.axaml +++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml @@ -22,7 +22,6 @@ -