From 46cd9c8dac53a824d9dda00b06c65856bb0aa4d2 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sat, 17 Feb 2024 02:50:13 +0800 Subject: [PATCH 1/6] feat: add transition based number displayer. --- demo/Ursa.Demo/Models/MenuKeys.cs | 1 + .../Ursa.Demo/Pages/NumberDisplayerDemo.axaml | 16 +++ .../Pages/NumberDisplayerDemo.axaml.cs | 13 ++ .../Ursa.Demo/ViewModels/MainViewViewModel.cs | 1 + demo/Ursa.Demo/ViewModels/MenuViewModel.cs | 3 +- .../NumberDisplayerDemoViewModel.cs | 26 ++++ .../Controls/NumberDisplayer.axaml | 15 +++ src/Ursa.Themes.Semi/Controls/_index.axaml | 1 + .../NumberDisplayer/NumberDisplayer.cs | 119 ++++++++++++++++++ 9 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml create mode 100644 demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml.cs create mode 100644 demo/Ursa.Demo/ViewModels/NumberDisplayerDemoViewModel.cs create mode 100644 src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml create mode 100644 src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs index 56e6a49..530349b 100644 --- a/demo/Ursa.Demo/Models/MenuKeys.cs +++ b/demo/Ursa.Demo/Models/MenuKeys.cs @@ -20,6 +20,7 @@ public static class MenuKeys public const string MenuKeyMessageBox = "MessageBox"; public const string MenuKeyNavigation = "Navigation"; public const string MenuKeyNavMenu = "NavMenu"; + public const string MenuKeyNumberDisplayer = "NumberDisplayer"; public const string MenuKeyNumericUpDown = "NumericUpDown"; public const string MenuKeyPagination = "Pagination"; public const string MenuKeyRangeSlider = "RangeSlider"; diff --git a/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml new file mode 100644 index 0000000..0a7ec19 --- /dev/null +++ b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml.cs b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml.cs new file mode 100644 index 0000000..35df3ef --- /dev/null +++ b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Ursa.Demo.Pages; + +public partial class NumberDisplayerDemo : UserControl +{ + public NumberDisplayerDemo() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs index 43166bd..d8c0cb4 100644 --- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs @@ -42,6 +42,7 @@ public class MainViewViewModel : ViewModelBase MenuKeys.MenuKeyMessageBox => new MessageBoxDemoViewModel(), MenuKeys.MenuKeyNavigation => new NavigationMenuDemoViewModel(), MenuKeys.MenuKeyNavMenu => new NavMenuDemoViewModel(), + MenuKeys.MenuKeyNumberDisplayer => new NumberDisplayerDemoViewModel(), MenuKeys.MenuKeyNumericUpDown => new NumericUpDownDemoViewModel(), MenuKeys.MenuKeyPagination => new PaginationDemoViewModel(), MenuKeys.MenuKeyRangeSlider => new RangeSliderDemoViewModel(), diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs index 6f6da25..c24db24 100644 --- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs @@ -29,7 +29,8 @@ public class MenuViewModel: ViewModelBase new() { MenuHeader = "Message Box", Key = MenuKeys.MenuKeyMessageBox, Status = "New" }, new() { MenuHeader = "Navigation", Key = MenuKeys.MenuKeyNavigation, Status = "WIP" }, new() { MenuHeader = "Nav Menu", Key = MenuKeys.MenuKeyNavMenu, Status = "WIP"}, - new() { MenuHeader = "NumericUpDown", Key = MenuKeys.MenuKeyNumericUpDown, Status = "New" }, + new() { MenuHeader = "Number Displayer", Key = MenuKeys.MenuKeyNumberDisplayer, Status = "New" }, + new() { MenuHeader = "Numeric UpDown", Key = MenuKeys.MenuKeyNumericUpDown, Status = "New" }, new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination }, new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider, Status = "New"}, new() { MenuHeader = "Selection List", Key = MenuKeys.MenuKeySelectionList, Status = "New" }, diff --git a/demo/Ursa.Demo/ViewModels/NumberDisplayerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/NumberDisplayerDemoViewModel.cs new file mode 100644 index 0000000..0032e47 --- /dev/null +++ b/demo/Ursa.Demo/ViewModels/NumberDisplayerDemoViewModel.cs @@ -0,0 +1,26 @@ +using System; +using System.Windows.Input; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +namespace Ursa.Demo.ViewModels; + +public partial class NumberDisplayerDemoViewModel: ObservableObject +{ + [ObservableProperty] private int _value; + [ObservableProperty] private double _doubleValue; + public ICommand IncreaseCommand { get; } + public NumberDisplayerDemoViewModel() + { + IncreaseCommand = new RelayCommand(OnChange); + Value = 0; + DoubleValue = 0d; + } + + private void OnChange() + { + Random r = new Random(); + Value = r.Next(int.MaxValue); + DoubleValue = r.NextDouble() * 100000; + } +} \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml b/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml new file mode 100644 index 0000000..b5f5d8e --- /dev/null +++ b/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml index 0b2dae5..dee8aee 100644 --- a/src/Ursa.Themes.Semi/Controls/_index.axaml +++ b/src/Ursa.Themes.Semi/Controls/_index.axaml @@ -20,6 +20,7 @@ + diff --git a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs b/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs new file mode 100644 index 0000000..181e955 --- /dev/null +++ b/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs @@ -0,0 +1,119 @@ +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Data; + +namespace Ursa.Controls; + +public abstract class NumberDisplayerBase : TemplatedControl +{ + public static readonly DirectProperty InternalTextProperty = AvaloniaProperty.RegisterDirect( + nameof(InternalText), o => o.InternalText, (o, v) => o.InternalText = v); + private string _internalText; + public string InternalText + { + get => _internalText; + set => SetAndRaise(InternalTextProperty, ref _internalText, value); + } + + public static readonly StyledProperty DurationProperty = AvaloniaProperty.Register( + nameof(Duration)); + + public TimeSpan Duration + { + get => GetValue(DurationProperty); + set => SetValue(DurationProperty, value); + } + + public static readonly StyledProperty StringFormatProperty = AvaloniaProperty.Register( + nameof(StringFormat)); + + public string StringFormat + { + get => GetValue(StringFormatProperty); + set => SetValue(StringFormatProperty, value); + } +} + +public abstract class NumberDisplayer: NumberDisplayerBase +{ + public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register, T?>( + nameof(Value), defaultBindingMode:BindingMode.TwoWay); + + public T? Value + { + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + public static readonly StyledProperty InternalValueProperty = AvaloniaProperty.Register, T?>( + nameof(InternalValue)); + + public T? InternalValue + { + get => GetValue(InternalValueProperty); + set => SetValue(InternalValueProperty, value); + } + + static NumberDisplayer() + { + ValueProperty.Changed.AddClassHandler, T?>((item, args) => + { + item.InternalValue = args.NewValue.Value; + }); + InternalValueProperty.Changed.AddClassHandler, T?>((item, args) => + { + item.InternalText = args.NewValue.Value is null ? string.Empty : item.GetString(args.NewValue.Value); + }); + DurationProperty.Changed.AddClassHandler, TimeSpan>((item, args) =>item.OnDurationChanged(args)); + } + + private void OnDurationChanged(AvaloniaPropertyChangedEventArgs args) + { + this.Transitions ??= new Transitions(); + this.Transitions?.Clear(); + this.Transitions?.Add(GetTransition(args.NewValue.Value)); + } + + protected abstract ITransition GetTransition(TimeSpan duration); + protected abstract string GetString(T? value); +} + +public class Int32Displayer : NumberDisplayer +{ + protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); + + protected override ITransition GetTransition(TimeSpan duration) + { + return new IntegerTransition() + { + Property = InternalValueProperty, + Duration = duration + }; + } + + protected override string GetString(int value) + { + return value.ToString(StringFormat); + } +} + +public class DoubleDisplayer : NumberDisplayer +{ + protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); + + protected override ITransition GetTransition(TimeSpan duration) + { + return new DoubleTransition() + { + Property = InternalValueProperty, + Duration = duration + }; + } + + protected override string GetString(double value) + { + return value.ToString(StringFormat); + } +} From 35d0d5a7b1ebc4fb8b62aaf43d392184f7493218 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sat, 17 Feb 2024 15:07:26 +0800 Subject: [PATCH 2/6] feat: use animator instead of transition. --- .../NumberDisplayer/NumberDisplayer.cs | 76 ++++++++++++++----- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs b/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs index 181e955..946d58e 100644 --- a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs +++ b/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs @@ -3,6 +3,7 @@ using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Data; +using Avalonia.Styling; namespace Ursa.Controls; @@ -11,6 +12,7 @@ public abstract class NumberDisplayerBase : TemplatedControl public static readonly DirectProperty InternalTextProperty = AvaloniaProperty.RegisterDirect( nameof(InternalText), o => o.InternalText, (o, v) => o.InternalText = v); private string _internalText; + public string InternalText { get => _internalText; @@ -38,6 +40,9 @@ public abstract class NumberDisplayerBase : TemplatedControl public abstract class NumberDisplayer: NumberDisplayerBase { + private Animation? _animation; + private CancellationTokenSource _cts = new (); + public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register, T?>( nameof(Value), defaultBindingMode:BindingMode.TwoWay); @@ -60,7 +65,7 @@ public abstract class NumberDisplayer: NumberDisplayerBase { ValueProperty.Changed.AddClassHandler, T?>((item, args) => { - item.InternalValue = args.NewValue.Value; + item.OnValueChanged(args.OldValue.Value, args.NewValue.Value); }); InternalValueProperty.Changed.AddClassHandler, T?>((item, args) => { @@ -68,29 +73,62 @@ public abstract class NumberDisplayer: NumberDisplayerBase }); DurationProperty.Changed.AddClassHandler, TimeSpan>((item, args) =>item.OnDurationChanged(args)); } - - private void OnDurationChanged(AvaloniaPropertyChangedEventArgs args) + + protected override void OnInitialized() { - this.Transitions ??= new Transitions(); - this.Transitions?.Clear(); - this.Transitions?.Add(GetTransition(args.NewValue.Value)); + base.OnInitialized(); + _animation = new Animation(); + _animation.Duration = Duration; + _animation.FillMode = FillMode.Forward; + _animation.Children.Add(new KeyFrame() + { + Cue = new Cue(0.0), + Setters = { new Setter{Property = InternalValueProperty } } + }); + _animation.Children.Add(new KeyFrame() + { + Cue = new Cue(1.0), + Setters = { new Setter{Property = InternalValueProperty } } + }); + Animation.SetAnimator(_animation.Children[0].Setters[0], GetAnimator()); } - protected abstract ITransition GetTransition(TimeSpan duration); + private void OnDurationChanged(AvaloniaPropertyChangedEventArgs args) + { + if (_animation is null) return; + _animation.Duration = args.NewValue.Value; + } + + protected virtual void OnValueChanged(T? oldValue, T? newValue) + { + _cts.Cancel(); + _cts = new CancellationTokenSource(); + (_animation?.Children[0].Setters[0] as Setter)!.Value = oldValue; + (_animation?.Children[1].Setters[0] as Setter)!.Value = newValue; + _animation?.RunAsync(this, _cts.Token); + } + + protected abstract InterpolatingAnimator GetAnimator(); + protected abstract string GetString(T? value); } public class Int32Displayer : NumberDisplayer { protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); + - protected override ITransition GetTransition(TimeSpan duration) + protected override InterpolatingAnimator GetAnimator() { - return new IntegerTransition() + return new IntAnimator(); + } + + private class IntAnimator : InterpolatingAnimator + { + public override int Interpolate(double progress, int oldValue, int newValue) { - Property = InternalValueProperty, - Duration = duration - }; + return oldValue + (int)((newValue - oldValue) * progress); + } } protected override string GetString(int value) @@ -103,13 +141,17 @@ public class DoubleDisplayer : NumberDisplayer { protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); - protected override ITransition GetTransition(TimeSpan duration) + protected override InterpolatingAnimator GetAnimator() { - return new DoubleTransition() + return new DoubleAnimator(); + } + + private class DoubleAnimator : InterpolatingAnimator + { + public override double Interpolate(double progress, double oldValue, double newValue) { - Property = InternalValueProperty, - Duration = duration - }; + return oldValue + (newValue - oldValue) * progress; + } } protected override string GetString(double value) From e7b99842bc139160b0be3821d653f15f87609484 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sat, 17 Feb 2024 15:16:49 +0800 Subject: [PATCH 3/6] feat: add a sample for date. --- .../Ursa.Demo/Pages/NumberDisplayerDemo.axaml | 1 + .../NumberDisplayerDemoViewModel.cs | 3 ++ .../NumberDisplayer/NumberDisplayer.cs | 32 +++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml index 0a7ec19..21f67cc 100644 --- a/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml +++ b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml @@ -12,5 +12,6 @@ + diff --git a/demo/Ursa.Demo/ViewModels/NumberDisplayerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/NumberDisplayerDemoViewModel.cs index 0032e47..befdd5b 100644 --- a/demo/Ursa.Demo/ViewModels/NumberDisplayerDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/NumberDisplayerDemoViewModel.cs @@ -9,12 +9,14 @@ public partial class NumberDisplayerDemoViewModel: ObservableObject { [ObservableProperty] private int _value; [ObservableProperty] private double _doubleValue; + [ObservableProperty] private DateTime _dateValue; public ICommand IncreaseCommand { get; } public NumberDisplayerDemoViewModel() { IncreaseCommand = new RelayCommand(OnChange); Value = 0; DoubleValue = 0d; + DateValue = DateTime.Now; } private void OnChange() @@ -22,5 +24,6 @@ public partial class NumberDisplayerDemoViewModel: ObservableObject Random r = new Random(); Value = r.Next(int.MaxValue); DoubleValue = r.NextDouble() * 100000; + DateValue = DateTime.Today.AddDays(r.Next(1000)); } } \ No newline at end of file diff --git a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs b/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs index 946d58e..3beff1e 100644 --- a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs +++ b/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs @@ -91,6 +91,8 @@ public abstract class NumberDisplayer: NumberDisplayerBase Setters = { new Setter{Property = InternalValueProperty } } }); Animation.SetAnimator(_animation.Children[0].Setters[0], GetAnimator()); + Animation.SetAnimator(_animation.Children[1].Setters[0], GetAnimator()); + InternalValue = Value; } private void OnDurationChanged(AvaloniaPropertyChangedEventArgs args) @@ -101,11 +103,12 @@ public abstract class NumberDisplayer: NumberDisplayerBase protected virtual void OnValueChanged(T? oldValue, T? newValue) { + if (_animation is null) return; _cts.Cancel(); _cts = new CancellationTokenSource(); - (_animation?.Children[0].Setters[0] as Setter)!.Value = oldValue; - (_animation?.Children[1].Setters[0] as Setter)!.Value = newValue; - _animation?.RunAsync(this, _cts.Token); + (_animation.Children[0].Setters[0] as Setter)!.Value = oldValue; + (_animation.Children[1].Setters[0] as Setter)!.Value = newValue; + _animation.RunAsync(this, _cts.Token); } protected abstract InterpolatingAnimator GetAnimator(); @@ -159,3 +162,26 @@ public class DoubleDisplayer : NumberDisplayer return value.ToString(StringFormat); } } + +public class DateDisplay : NumberDisplayer +{ + protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); + + protected override InterpolatingAnimator GetAnimator() + { + return new DateAnimator(); + } + + private class DateAnimator : InterpolatingAnimator + { + public override DateTime Interpolate(double progress, DateTime oldValue, DateTime newValue) + { + return oldValue + TimeSpan.FromTicks((long)((newValue - oldValue).Ticks * progress)); + } + } + + protected override string GetString(DateTime value) + { + return value.ToString(StringFormat); + } +} From 40454bcfe02b9118085c4a1bcb1b8ba7c65be3da Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sat, 17 Feb 2024 15:53:42 +0800 Subject: [PATCH 4/6] feat: inherit font properties. --- .../Ursa.Demo/Pages/NumberDisplayerDemo.axaml | 34 ++++++++------- .../Controls/NumberDisplayer.axaml | 11 ++++- .../NumberDisplayer/NumberDisplayer.cs | 42 ++++++++++++------- 3 files changed, 56 insertions(+), 31 deletions(-) diff --git a/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml index 21f67cc..89c264c 100644 --- a/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml +++ b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml @@ -1,17 +1,23 @@ - + - - - - + + + + diff --git a/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml b/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml index b5f5d8e..5351402 100644 --- a/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml +++ b/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml @@ -8,7 +8,16 @@ - + diff --git a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs b/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs index 3beff1e..df295db 100644 --- a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs +++ b/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs @@ -1,8 +1,10 @@ using Avalonia; using Avalonia.Animation; using Avalonia.Controls; +using Avalonia.Controls.Documents; using Avalonia.Controls.Primitives; using Avalonia.Data; +using Avalonia.Media; using Avalonia.Styling; namespace Ursa.Controls; @@ -13,7 +15,7 @@ public abstract class NumberDisplayerBase : TemplatedControl nameof(InternalText), o => o.InternalText, (o, v) => o.InternalText = v); private string _internalText; - public string InternalText + internal string InternalText { get => _internalText; set => SetAndRaise(InternalTextProperty, ref _internalText, value); @@ -51,11 +53,11 @@ public abstract class NumberDisplayer: NumberDisplayerBase get => GetValue(ValueProperty); set => SetValue(ValueProperty, value); } - - public static readonly StyledProperty InternalValueProperty = AvaloniaProperty.Register, T?>( - nameof(InternalValue)); - public T? InternalValue + private static readonly StyledProperty InternalValueProperty = AvaloniaProperty.Register, T?>( + nameof(InternalValue), defaultBindingMode:BindingMode.TwoWay); + + private T? InternalValue { get => GetValue(InternalValueProperty); set => SetValue(InternalValueProperty, value); @@ -73,13 +75,15 @@ public abstract class NumberDisplayer: NumberDisplayerBase }); DurationProperty.Changed.AddClassHandler, TimeSpan>((item, args) =>item.OnDurationChanged(args)); } - - protected override void OnInitialized() + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { - base.OnInitialized(); - _animation = new Animation(); - _animation.Duration = Duration; - _animation.FillMode = FillMode.Forward; + base.OnApplyTemplate(e); + _animation = new Animation + { + Duration = Duration, + FillMode = FillMode.Forward + }; _animation.Children.Add(new KeyFrame() { Cue = new Cue(0.0), @@ -92,7 +96,9 @@ public abstract class NumberDisplayer: NumberDisplayerBase }); Animation.SetAnimator(_animation.Children[0].Setters[0], GetAnimator()); Animation.SetAnimator(_animation.Children[1].Setters[0], GetAnimator()); - InternalValue = Value; + + // Display value directly to text on initialization in case value equals to default. + SetCurrentValue(InternalTextProperty, this.GetString(Value)); } private void OnDurationChanged(AvaloniaPropertyChangedEventArgs args) @@ -101,9 +107,13 @@ public abstract class NumberDisplayer: NumberDisplayerBase _animation.Duration = args.NewValue.Value; } - protected virtual void OnValueChanged(T? oldValue, T? newValue) + private void OnValueChanged(T? oldValue, T? newValue) { - if (_animation is null) return; + if (_animation is null) + { + SetCurrentValue(InternalValueProperty, newValue); + return; + } _cts.Cancel(); _cts = new CancellationTokenSource(); (_animation.Children[0].Setters[0] as Setter)!.Value = oldValue; @@ -119,7 +129,6 @@ public abstract class NumberDisplayer: NumberDisplayerBase public class Int32Displayer : NumberDisplayer { protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); - protected override InterpolatingAnimator GetAnimator() { @@ -176,7 +185,8 @@ public class DateDisplay : NumberDisplayer { public override DateTime Interpolate(double progress, DateTime oldValue, DateTime newValue) { - return oldValue + TimeSpan.FromTicks((long)((newValue - oldValue).Ticks * progress)); + var diff = (newValue - oldValue).TotalSeconds; + return oldValue + TimeSpan.FromSeconds(diff * progress); } } From a4c214cfc2607feb0c90187b2fd42a214a382443 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sat, 17 Feb 2024 15:56:37 +0800 Subject: [PATCH 5/6] feat: re-org files. --- .../NumberDisplayer/Implementations.cs | 73 +++++++++++++++++++ ...berDisplayer.cs => NumberDisplayerBase.cs} | 73 ------------------- 2 files changed, 73 insertions(+), 73 deletions(-) create mode 100644 src/Ursa/Controls/NumberDisplayer/Implementations.cs rename src/Ursa/Controls/NumberDisplayer/{NumberDisplayer.cs => NumberDisplayerBase.cs} (68%) diff --git a/src/Ursa/Controls/NumberDisplayer/Implementations.cs b/src/Ursa/Controls/NumberDisplayer/Implementations.cs new file mode 100644 index 0000000..a43fa0a --- /dev/null +++ b/src/Ursa/Controls/NumberDisplayer/Implementations.cs @@ -0,0 +1,73 @@ +using Avalonia.Animation; + +namespace Ursa.Controls; + +public class Int32Displayer : NumberDisplayer +{ + protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); + + protected override InterpolatingAnimator GetAnimator() + { + return new IntAnimator(); + } + + private class IntAnimator : InterpolatingAnimator + { + public override int Interpolate(double progress, int oldValue, int newValue) + { + return oldValue + (int)((newValue - oldValue) * progress); + } + } + + protected override string GetString(int value) + { + return value.ToString(StringFormat); + } +} + +public class DoubleDisplayer : NumberDisplayer +{ + protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); + + protected override InterpolatingAnimator GetAnimator() + { + return new DoubleAnimator(); + } + + private class DoubleAnimator : InterpolatingAnimator + { + public override double Interpolate(double progress, double oldValue, double newValue) + { + return oldValue + (newValue - oldValue) * progress; + } + } + + protected override string GetString(double value) + { + return value.ToString(StringFormat); + } +} + +public class DateDisplay : NumberDisplayer +{ + protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); + + protected override InterpolatingAnimator GetAnimator() + { + return new DateAnimator(); + } + + private class DateAnimator : InterpolatingAnimator + { + public override DateTime Interpolate(double progress, DateTime oldValue, DateTime newValue) + { + var diff = (newValue - oldValue).TotalSeconds; + return oldValue + TimeSpan.FromSeconds(diff * progress); + } + } + + protected override string GetString(DateTime value) + { + return value.ToString(StringFormat); + } +} \ No newline at end of file diff --git a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs b/src/Ursa/Controls/NumberDisplayer/NumberDisplayerBase.cs similarity index 68% rename from src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs rename to src/Ursa/Controls/NumberDisplayer/NumberDisplayerBase.cs index df295db..848c679 100644 --- a/src/Ursa/Controls/NumberDisplayer/NumberDisplayer.cs +++ b/src/Ursa/Controls/NumberDisplayer/NumberDisplayerBase.cs @@ -1,10 +1,7 @@ using Avalonia; using Avalonia.Animation; -using Avalonia.Controls; -using Avalonia.Controls.Documents; using Avalonia.Controls.Primitives; using Avalonia.Data; -using Avalonia.Media; using Avalonia.Styling; namespace Ursa.Controls; @@ -125,73 +122,3 @@ public abstract class NumberDisplayer: NumberDisplayerBase protected abstract string GetString(T? value); } - -public class Int32Displayer : NumberDisplayer -{ - protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); - - protected override InterpolatingAnimator GetAnimator() - { - return new IntAnimator(); - } - - private class IntAnimator : InterpolatingAnimator - { - public override int Interpolate(double progress, int oldValue, int newValue) - { - return oldValue + (int)((newValue - oldValue) * progress); - } - } - - protected override string GetString(int value) - { - return value.ToString(StringFormat); - } -} - -public class DoubleDisplayer : NumberDisplayer -{ - protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); - - protected override InterpolatingAnimator GetAnimator() - { - return new DoubleAnimator(); - } - - private class DoubleAnimator : InterpolatingAnimator - { - public override double Interpolate(double progress, double oldValue, double newValue) - { - return oldValue + (newValue - oldValue) * progress; - } - } - - protected override string GetString(double value) - { - return value.ToString(StringFormat); - } -} - -public class DateDisplay : NumberDisplayer -{ - protected override Type StyleKeyOverride { get; } = typeof(NumberDisplayerBase); - - protected override InterpolatingAnimator GetAnimator() - { - return new DateAnimator(); - } - - private class DateAnimator : InterpolatingAnimator - { - public override DateTime Interpolate(double progress, DateTime oldValue, DateTime newValue) - { - var diff = (newValue - oldValue).TotalSeconds; - return oldValue + TimeSpan.FromSeconds(diff * progress); - } - } - - protected override string GetString(DateTime value) - { - return value.ToString(StringFormat); - } -} From 728185bdf7bdc6addd46425d4c32408420dc0640 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Sun, 18 Feb 2024 18:21:49 +0800 Subject: [PATCH 6/6] feat: add option to make it selectable. --- demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml | 8 ++++++++ .../Controls/NumberDisplayer.axaml | 18 ++++++++++++++++-- .../NumberDisplayer/NumberDisplayerBase.cs | 9 +++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml index 89c264c..c550a8c 100644 --- a/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml +++ b/demo/Ursa.Demo/Pages/NumberDisplayerDemo.axaml @@ -19,5 +19,13 @@ FontSize="30" StringFormat="yyyy-MM-dd" Value="{Binding DateValue}" /> + + + + diff --git a/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml b/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml index 5351402..1a62aec 100644 --- a/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml +++ b/src/Ursa.Themes.Semi/Controls/NumberDisplayer.axaml @@ -16,9 +16,23 @@ FontWeight="{TemplateBinding FontWeight}" FontStyle="{TemplateBinding FontSize}" FontStretch="{TemplateBinding FontStretch}" - Text="{TemplateBinding InternalText, - Mode=OneWay}" /> + Text="{TemplateBinding InternalText, Mode=OneWay}" /> + diff --git a/src/Ursa/Controls/NumberDisplayer/NumberDisplayerBase.cs b/src/Ursa/Controls/NumberDisplayer/NumberDisplayerBase.cs index 848c679..25e817d 100644 --- a/src/Ursa/Controls/NumberDisplayer/NumberDisplayerBase.cs +++ b/src/Ursa/Controls/NumberDisplayer/NumberDisplayerBase.cs @@ -35,6 +35,15 @@ public abstract class NumberDisplayerBase : TemplatedControl get => GetValue(StringFormatProperty); set => SetValue(StringFormatProperty, value); } + + public static readonly StyledProperty IsSelectableProperty = AvaloniaProperty.Register( + nameof(IsSelectable)); + + public bool IsSelectable + { + get => GetValue(IsSelectableProperty); + set => SetValue(IsSelectableProperty, value); + } } public abstract class NumberDisplayer: NumberDisplayerBase