committed by
GitHub
30 changed files with 603 additions and 301 deletions
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using Avalonia.Animation.Animators; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// <see cref="Transition{T}"/> using an <see cref="Animator{T}"/> to transition between values.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Type of the transitioned value.</typeparam>
|
|||
/// <typeparam name="TAnimator">Type of the animator.</typeparam>
|
|||
public abstract class AnimatorDrivenTransition<T, TAnimator> : Transition<T> where TAnimator : Animator<T>, new() |
|||
{ |
|||
private static readonly TAnimator s_animator = new TAnimator(); |
|||
|
|||
public override IObservable<T> DoTransition(IObservable<double> progress, T oldValue, T newValue) |
|||
{ |
|||
return new AnimatorTransitionObservable<T, TAnimator>(s_animator, progress, Easing, oldValue, newValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using Avalonia.Animation.Animators; |
|||
using Avalonia.Animation.Easings; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Transition observable based on an <see cref="Animator{T}"/> producing a value.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Type of the transitioned value.</typeparam>
|
|||
/// <typeparam name="TAnimator">Type of the animator.</typeparam>
|
|||
public class AnimatorTransitionObservable<T, TAnimator> : TransitionObservableBase<T> where TAnimator : Animator<T> |
|||
{ |
|||
private readonly TAnimator _animator; |
|||
private readonly Easing _easing; |
|||
private readonly T _oldValue; |
|||
private readonly T _newValue; |
|||
|
|||
public AnimatorTransitionObservable(TAnimator animator, IObservable<double> progress, Easing easing, T oldValue, T newValue) : base(progress, easing) |
|||
{ |
|||
_animator = animator; |
|||
_easing = easing; |
|||
_oldValue = oldValue; |
|||
_newValue = newValue; |
|||
} |
|||
|
|||
protected override T ProduceValue(double progress) |
|||
{ |
|||
return _animator.Interpolate(progress, _oldValue, _newValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System; |
|||
using Avalonia.Animation.Easings; |
|||
using Avalonia.Reactive; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Provides base for observables implementing transitions.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Type of the transitioned value.</typeparam>
|
|||
public abstract class TransitionObservableBase<T> : SingleSubscriberObservableBase<T>, IObserver<double> |
|||
{ |
|||
private readonly Easing _easing; |
|||
private readonly IObservable<double> _progress; |
|||
private IDisposable? _progressSubscription; |
|||
|
|||
protected TransitionObservableBase(IObservable<double> progress, Easing easing) |
|||
{ |
|||
_progress = progress; |
|||
_easing = easing; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Produces value at given progress time point.
|
|||
/// </summary>
|
|||
/// <param name="progress">Transition progress.</param>
|
|||
protected abstract T ProduceValue(double progress); |
|||
|
|||
protected override void Subscribed() |
|||
{ |
|||
_progressSubscription = _progress.Subscribe(this); |
|||
} |
|||
|
|||
protected override void Unsubscribed() |
|||
{ |
|||
_progressSubscription?.Dispose(); |
|||
} |
|||
|
|||
void IObserver<double>.OnCompleted() |
|||
{ |
|||
PublishCompleted(); |
|||
} |
|||
|
|||
void IObserver<double>.OnError(Exception error) |
|||
{ |
|||
PublishError(error); |
|||
} |
|||
|
|||
void IObserver<double>.OnNext(double value) |
|||
{ |
|||
double progress = _easing.Ease(value); |
|||
|
|||
PublishNext(ProduceValue(progress)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
<Styles xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:controls="using:Avalonia.Diagnostics.Controls" |
|||
x:CompileBindings="True" |
|||
x:DataType="controls:FilterTextBox"> |
|||
|
|||
<Design.PreviewWith> |
|||
<Border Padding="10"> |
|||
<controls:FilterTextBox Width="200" Text="Hello World" /> |
|||
</Border> |
|||
</Design.PreviewWith> |
|||
|
|||
<Style Selector="TextBox.filter-text-box"> |
|||
<Setter Property="VerticalContentAlignment" Value="Center" /> |
|||
<Setter Property="InnerRightContent"> |
|||
<Template> |
|||
<StackPanel Orientation="Horizontal" Spacing="1"> |
|||
<Button Margin="0,0,2,0" |
|||
Classes="textBoxClearButton" |
|||
ToolTip.Tip="Clear" |
|||
Cursor="Hand" |
|||
Command="{ReflectionBinding $parent[TextBox].Clear}" |
|||
Opacity="0.5" /> |
|||
<ToggleButton Classes="filter-text-box-toggle" |
|||
ToolTip.Tip="Match Case" |
|||
IsChecked="{Binding $parent[controls:FilterTextBox].UseCaseSensitiveFilter}"> |
|||
<Image> |
|||
<DrawingImage> |
|||
<GeometryDrawing Brush="{Binding $parent[ToggleButton].Foreground}"> |
|||
M7.495 9.052L8.386 11.402H9.477L6.237 3H5.217L2 11.402H3.095L3.933 9.052H7.495ZM5.811 4.453L5.855 4.588L7.173 8.162H4.255L5.562 4.588L5.606 4.453L5.644 4.297L5.676 4.145L5.697 4.019H5.72L5.744 4.145L5.773 4.297L5.811 4.453ZM13.795 10.464V11.4H14.755V7.498C14.755 6.779 14.575 6.226 14.216 5.837C13.857 5.448 13.327 5.254 12.628 5.254C12.429 5.254 12.227 5.273 12.022 5.31C11.817 5.347 11.622 5.394 11.439 5.451C11.256 5.508 11.091 5.569 10.944 5.636C10.797 5.703 10.683 5.765 10.601 5.824V6.808C10.867 6.578 11.167 6.397 11.505 6.268C11.843 6.139 12.194 6.075 12.557 6.075C12.745 6.075 12.915 6.103 13.07 6.16C13.225 6.217 13.357 6.306 13.466 6.427C13.575 6.548 13.659 6.706 13.718 6.899C13.777 7.092 13.806 7.326 13.806 7.599L11.995 7.851C11.651 7.898 11.355 7.977 11.107 8.088C10.859 8.199 10.654 8.339 10.492 8.507C10.33 8.675 10.21 8.868 10.132 9.087C10.054 9.306 10.015 9.546 10.015 9.808C10.015 10.054 10.057 10.283 10.139 10.496C10.221 10.709 10.342 10.893 10.502 11.047C10.662 11.201 10.862 11.323 11.1 11.413C11.338 11.503 11.613 11.548 11.926 11.548C12.328 11.548 12.686 11.456 13.001 11.27C13.316 11.084 13.573 10.816 13.772 10.464H13.795ZM11.667 8.721C11.843 8.657 12.068 8.607 12.341 8.572L13.806 8.367V8.976C13.806 9.222 13.765 9.451 13.683 9.664C13.601 9.877 13.486 10.063 13.34 10.221C13.194 10.379 13.019 10.503 12.816 10.593C12.613 10.683 12.39 10.728 12.148 10.728C11.961 10.728 11.795 10.703 11.653 10.652C11.511 10.601 11.392 10.53 11.296 10.441C11.2 10.352 11.127 10.247 11.076 10.125C11.025 10.003 11 9.873 11 9.732C11 9.568 11.018 9.421 11.055 9.292C11.092 9.163 11.16 9.051 11.257 8.958C11.354 8.865 11.491 8.785 11.667 8.721Z |
|||
</GeometryDrawing> |
|||
</DrawingImage> |
|||
</Image> |
|||
</ToggleButton> |
|||
<ToggleButton Classes="filter-text-box-toggle" |
|||
ToolTip.Tip="Match Whole Word" |
|||
IsChecked="{Binding $parent[controls:FilterTextBox].UseWholeWordFilter}"> |
|||
<Image> |
|||
<DrawingImage> |
|||
<GeometryDrawing Brush="{Binding $parent[ToggleButton].Foreground}"> |
|||
M1 2H15V3H1V2ZM14 4H13V12H14V4ZM11.272 8.387C11.194 8.088 11.073 7.825 10.912 7.601C10.751 7.377 10.547 7.2 10.303 7.071C10.059 6.942 9.769 6.878 9.437 6.878C9.239 6.878 9.057 6.902 8.89 6.951C8.725 7 8.574 7.068 8.437 7.156C8.301 7.244 8.18 7.35 8.072 7.474L7.893 7.732V4.578H7V12H7.893V11.425L8.019 11.6C8.106 11.702 8.208 11.79 8.323 11.869C8.44 11.947 8.572 12.009 8.721 12.055C8.87 12.101 9.035 12.123 9.219 12.123C9.572 12.123 9.885 12.052 10.156 11.911C10.428 11.768 10.655 11.573 10.838 11.325C11.021 11.075 11.159 10.782 11.252 10.446C11.345 10.108 11.392 9.743 11.392 9.349C11.391 9.007 11.352 8.686 11.272 8.387ZM9.793 7.78C9.944 7.851 10.075 7.956 10.183 8.094C10.292 8.234 10.377 8.407 10.438 8.611C10.489 8.785 10.52 8.982 10.527 9.198L10.52 9.323C10.52 9.65 10.487 9.943 10.42 10.192C10.353 10.438 10.259 10.645 10.142 10.806C10.025 10.968 9.882 11.091 9.721 11.172C9.399 11.334 8.961 11.338 8.652 11.187C8.499 11.112 8.366 11.012 8.259 10.891C8.174 10.795 8.103 10.675 8.041 10.524C8.041 10.524 7.862 10.077 7.862 9.577C7.862 9.077 8.041 8.575 8.041 8.575C8.103 8.398 8.177 8.257 8.265 8.145C8.379 8.002 8.521 7.886 8.689 7.8C8.857 7.714 9.054 7.671 9.276 7.671C9.466 7.671 9.64 7.708 9.793 7.78ZM15 13H1V14H15V13ZM2.813 10L2.085 12.031H1L1.025 11.959L3.466 4.87305H4.407L6.892 12.031H5.81L5.032 10H2.813ZM3.934 6.42205H3.912L3.007 9.17505H4.848L3.934 6.42205Z |
|||
</GeometryDrawing> |
|||
</DrawingImage> |
|||
</Image> |
|||
</ToggleButton> |
|||
<ToggleButton Classes="filter-text-box-toggle" |
|||
ToolTip.Tip="Use Regular Expression" |
|||
IsChecked="{Binding $parent[controls:FilterTextBox].UseRegexFilter}"> |
|||
<Image> |
|||
<DrawingImage> |
|||
<GeometryDrawing Brush="{Binding $parent[ToggleButton].Foreground}"> |
|||
M10.0122 2H10.9879V5.11346L13.5489 3.55609L14.034 4.44095L11.4702 6L14.034 7.55905L13.5489 8.44391L10.9879 6.88654V10H10.0122V6.88654L7.45114 8.44391L6.96606 7.55905L9.5299 6L6.96606 4.44095L7.45114 3.55609L10.0122 5.11346V2ZM2 10H6V14H2V10Z |
|||
</GeometryDrawing> |
|||
</DrawingImage> |
|||
</Image> |
|||
</ToggleButton> |
|||
</StackPanel> |
|||
</Template> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<Style Selector="TextBox.filter-text-box Button.textBoxClearButton"> |
|||
<Setter Property="IsVisible" Value="False" /> |
|||
</Style> |
|||
<Style Selector="TextBox.filter-text-box[AcceptsReturn=False][IsReadOnly=False]:focus:not(TextBox:empty) Button.textBoxClearButton"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
</Style> |
|||
|
|||
<Style Selector="ToggleButton.filter-text-box-toggle > Image"> |
|||
<Setter Property="Width" Value="16" /> |
|||
<Setter Property="Height" Value="16" /> |
|||
<Setter Property="Opacity" Value="0.5" /> |
|||
</Style> |
|||
<Style Selector="ToggleButton.filter-text-box-toggle:pointerover > Image"> |
|||
<Setter Property="Opacity" Value="0.7" /> |
|||
</Style> |
|||
<Style Selector="ToggleButton.filter-text-box-toggle:pressed > Image, ToggleButton.filter-text-box-toggle:checked > Image"> |
|||
<Setter Property="Opacity" Value="0.7" /> |
|||
</Style> |
|||
|
|||
<Style Selector="ToggleButton.filter-text-box-toggle"> |
|||
<Setter Property="Cursor" Value="Hand" /> |
|||
<Setter Property="Padding" Value="0,1" /> |
|||
<Setter Property="Width" Value="24" /> |
|||
<Setter Property="BorderThickness" Value="1" /> |
|||
<Setter Property="VerticalAlignment" Value="Top" /> |
|||
</Style> |
|||
<Style Selector="ToggleButton.filter-text-box-toggle /template/ ContentPresenter"> |
|||
<Setter Property="Background" Value="Transparent" /> |
|||
<Setter Property="BorderBrush" Value="Transparent" /> |
|||
<Setter Property="CornerRadius" Value="0" /> |
|||
</Style> |
|||
<Style Selector="ToggleButton.filter-text-box-toggle:pressed /template/ ContentPresenter, ToggleButton.filter-text-box-toggle:checked /template/ ContentPresenter"> |
|||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeAccentColor}" /> |
|||
<Setter Property="Background" Value="{DynamicResource ThemeAccentColor4}" /> |
|||
</Style> |
|||
</Styles> |
|||
@ -0,0 +1,52 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Data; |
|||
using Avalonia.Styling; |
|||
|
|||
namespace Avalonia.Diagnostics.Controls |
|||
{ |
|||
internal class FilterTextBox : TextBox, IStyleable |
|||
{ |
|||
public static readonly DirectProperty<FilterTextBox, bool> UseRegexFilterProperty = |
|||
AvaloniaProperty.RegisterDirect<FilterTextBox, bool>(nameof(UseRegexFilter), |
|||
o => o.UseRegexFilter, (o, v) => o.UseRegexFilter = v, |
|||
defaultBindingMode: BindingMode.TwoWay); |
|||
|
|||
public static readonly DirectProperty<FilterTextBox, bool> UseCaseSensitiveFilterProperty = |
|||
AvaloniaProperty.RegisterDirect<FilterTextBox, bool>(nameof(UseCaseSensitiveFilter), |
|||
o => o.UseCaseSensitiveFilter, (o, v) => o.UseCaseSensitiveFilter = v, |
|||
defaultBindingMode: BindingMode.TwoWay); |
|||
|
|||
public static readonly DirectProperty<FilterTextBox, bool> UseWholeWordFilterProperty = |
|||
AvaloniaProperty.RegisterDirect<FilterTextBox, bool>(nameof(UseWholeWordFilter), |
|||
o => o.UseWholeWordFilter, (o, v) => o.UseWholeWordFilter = v, |
|||
defaultBindingMode: BindingMode.TwoWay); |
|||
|
|||
private bool _useRegexFilter, _useCaseSensitiveFilter, _useWholeWordFilter; |
|||
|
|||
public FilterTextBox() |
|||
{ |
|||
Classes.Add("filter-text-box"); |
|||
} |
|||
|
|||
public bool UseRegexFilter |
|||
{ |
|||
get => _useRegexFilter; |
|||
set => SetAndRaise(UseRegexFilterProperty, ref _useRegexFilter, value); |
|||
} |
|||
|
|||
public bool UseCaseSensitiveFilter |
|||
{ |
|||
get => _useCaseSensitiveFilter; |
|||
set => SetAndRaise(UseCaseSensitiveFilterProperty, ref _useCaseSensitiveFilter, value); |
|||
} |
|||
|
|||
public bool UseWholeWordFilter |
|||
{ |
|||
get => _useWholeWordFilter; |
|||
set => SetAndRaise(UseWholeWordFilterProperty, ref _useWholeWordFilter, value); |
|||
} |
|||
|
|||
Type IStyleable.StyleKey => typeof(TextBox); |
|||
} |
|||
} |
|||
@ -0,0 +1,124 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
namespace Avalonia.Diagnostics.ViewModels |
|||
{ |
|||
internal class FilterViewModel : ViewModelBase, INotifyDataErrorInfo |
|||
{ |
|||
private readonly Dictionary<string, string> _errors = new Dictionary<string, string>(); |
|||
private string _filterString = string.Empty; |
|||
private bool _useRegexFilter, _useCaseSensitiveFilter, _useWholeWordFilter; |
|||
private string _processedFilter; |
|||
private Regex _filterRegex; |
|||
|
|||
public event EventHandler RefreshFilter; |
|||
|
|||
public bool Filter(string input) |
|||
{ |
|||
return _filterRegex?.IsMatch(input) ?? true; |
|||
} |
|||
|
|||
private void UpdateFilterRegex() |
|||
{ |
|||
void ClearError() |
|||
{ |
|||
if (_errors.Remove(nameof(FilterString))) |
|||
{ |
|||
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(FilterString))); |
|||
} |
|||
} |
|||
|
|||
_processedFilter = FilterString.Trim(); |
|||
|
|||
try |
|||
{ |
|||
var options = RegexOptions.Compiled; |
|||
var pattern = UseRegexFilter |
|||
? _processedFilter : Regex.Escape(_processedFilter); |
|||
if (!UseCaseSensitiveFilter) |
|||
{ |
|||
options |= RegexOptions.IgnoreCase; |
|||
} |
|||
if (UseWholeWordFilter) |
|||
{ |
|||
pattern = $"\\b(?:{pattern})\\b"; |
|||
} |
|||
|
|||
_filterRegex = new Regex(pattern, options); |
|||
ClearError(); |
|||
} |
|||
catch (Exception exception) |
|||
{ |
|||
_errors[nameof(FilterString)] = exception.Message; |
|||
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(FilterString))); |
|||
} |
|||
} |
|||
|
|||
public string FilterString |
|||
{ |
|||
get => _filterString; |
|||
set |
|||
{ |
|||
if (RaiseAndSetIfChanged(ref _filterString, value)) |
|||
{ |
|||
UpdateFilterRegex(); |
|||
RefreshFilter?.Invoke(this, EventArgs.Empty); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool UseRegexFilter |
|||
{ |
|||
get => _useRegexFilter; |
|||
set |
|||
{ |
|||
if (RaiseAndSetIfChanged(ref _useRegexFilter, value)) |
|||
{ |
|||
UpdateFilterRegex(); |
|||
RefreshFilter?.Invoke(this, EventArgs.Empty); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool UseCaseSensitiveFilter |
|||
{ |
|||
get => _useCaseSensitiveFilter; |
|||
set |
|||
{ |
|||
if (RaiseAndSetIfChanged(ref _useCaseSensitiveFilter, value)) |
|||
{ |
|||
UpdateFilterRegex(); |
|||
RefreshFilter?.Invoke(this, EventArgs.Empty); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool UseWholeWordFilter |
|||
{ |
|||
get => _useWholeWordFilter; |
|||
set |
|||
{ |
|||
if (RaiseAndSetIfChanged(ref _useWholeWordFilter, value)) |
|||
{ |
|||
UpdateFilterRegex(); |
|||
RefreshFilter?.Invoke(this, EventArgs.Empty); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public IEnumerable GetErrors(string propertyName) |
|||
{ |
|||
if (_errors.TryGetValue(propertyName, out var error)) |
|||
{ |
|||
yield return error; |
|||
} |
|||
} |
|||
|
|||
public bool HasErrors => _errors.Count > 0; |
|||
|
|||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Avalonia.Animation.Animators; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Color"/> type.
|
|||
/// </summary>
|
|||
public class ColorTransition : AnimatorDrivenTransition<Color, ColorAnimator> |
|||
{ |
|||
} |
|||
} |
|||
@ -1,28 +1,26 @@ |
|||
using System; |
|||
using System.Reactive.Linq; |
|||
using Avalonia.Animation.Animators; |
|||
using Avalonia.Media; |
|||
using Avalonia.Media.Transformation; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Animation |
|||
{ |
|||
public class TransformOperationsTransition : Transition<ITransform> |
|||
{ |
|||
private static readonly TransformOperationsAnimator _operationsAnimator = new TransformOperationsAnimator(); |
|||
private static readonly TransformOperationsAnimator s_operationsAnimator = new TransformOperationsAnimator(); |
|||
|
|||
public override IObservable<ITransform> DoTransition(IObservable<double> progress, |
|||
public override IObservable<ITransform> DoTransition( |
|||
IObservable<double> progress, |
|||
ITransform oldValue, |
|||
ITransform newValue) |
|||
{ |
|||
var oldTransform = TransformOperationsAnimator.EnsureOperations(oldValue); |
|||
var newTransform = TransformOperationsAnimator.EnsureOperations(newValue); |
|||
|
|||
return progress |
|||
.Select(p => |
|||
{ |
|||
var f = Easing.Ease(p); |
|||
|
|||
return _operationsAnimator.Interpolate(f, oldTransform, newTransform); |
|||
}); |
|||
return new AnimatorTransitionObservable<TransformOperations, TransformOperationsAnimator>( |
|||
s_operationsAnimator, progress, Easing, oldTransform, newTransform); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,78 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Reactive.Linq; |
|||
using System.Reactive.Subjects; |
|||
using System.Runtime.CompilerServices; |
|||
using Avalonia.Animation; |
|||
using Avalonia.Animation.Animators; |
|||
using Avalonia.Layout; |
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
namespace Avalonia.Benchmarks.Animations |
|||
{ |
|||
[MemoryDiagnoser] |
|||
public class TransitionBenchmark |
|||
{ |
|||
private readonly AddValueObserver _observer; |
|||
private readonly List<double> _producedValues; |
|||
private readonly Subject<double> _timeProducer; |
|||
private readonly DoubleTransition _transition; |
|||
|
|||
public TransitionBenchmark() |
|||
{ |
|||
_transition = new DoubleTransition |
|||
{ |
|||
Duration = TimeSpan.FromMilliseconds(FrameCount), Property = Layoutable.WidthProperty |
|||
}; |
|||
|
|||
_timeProducer = new Subject<double>(); |
|||
_producedValues = new List<double>(FrameCount); |
|||
|
|||
_observer = new AddValueObserver(_producedValues); |
|||
} |
|||
|
|||
[Params(10, 100)] public int FrameCount { get; set; } |
|||
|
|||
[Benchmark] |
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
public void NewTransition() |
|||
{ |
|||
var transitionObs = _transition.DoTransition(_timeProducer, 0, 1); |
|||
|
|||
_producedValues.Clear(); |
|||
|
|||
using var transitionSub = transitionObs.Subscribe(_observer); |
|||
|
|||
for (int i = 0; i < FrameCount; i++) |
|||
{ |
|||
_timeProducer.OnNext(i / 1000d); |
|||
} |
|||
|
|||
Debug.Assert(_producedValues.Count == FrameCount); |
|||
} |
|||
|
|||
private class AddValueObserver : IObserver<double> |
|||
{ |
|||
private readonly List<double> _values; |
|||
|
|||
public AddValueObserver(List<double> values) |
|||
{ |
|||
_values = values; |
|||
} |
|||
|
|||
public void OnCompleted() |
|||
{ |
|||
} |
|||
|
|||
public void OnError(Exception error) |
|||
{ |
|||
} |
|||
|
|||
public void OnNext(double value) |
|||
{ |
|||
_values.Add(value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue