Browse Source

Updated ActivatedSubject.

Can be expressed more cleanly given the new `ActivatedObject`.
pull/1690/head
Steven Kirk 8 years ago
committed by Steven Kirk
parent
commit
de35eb9fee
  1. 46
      src/Avalonia.Styling/Styling/ActivatedObservable.cs
  2. 59
      src/Avalonia.Styling/Styling/ActivatedSubject.cs
  3. 10
      tests/Avalonia.Styling.UnitTests/ActivatedSubjectTests.cs

46
src/Avalonia.Styling/Styling/ActivatedObservable.cs

@ -24,8 +24,6 @@ namespace Avalonia.Styling
private List<IObserver<object>> _observers;
private IDisposable _activatorSubscription;
private IDisposable _sourceSubscription;
private bool? _active;
private object _value;
private object _last = NotSent;
/// <summary>
@ -58,11 +56,21 @@ namespace Avalonia.Styling
/// </summary>
public string Description { get; }
/// <summary>
/// Gets a value indicating whether the observable is active.
/// </summary>
public bool? IsActive { get; private set; }
/// <summary>
/// Gets an observable which produces the <see cref="ActivatedValue"/>.
/// </summary>
public IObservable<object> Source { get; }
/// <summary>
/// Gets the value that will be produced when <see cref="IsActive"/> is true.
/// </summary>
public object Value { get; private set; }
public IDisposable Subscribe(IObserver<object> observer)
{
var subscribe = _observers == null;
@ -90,7 +98,7 @@ namespace Avalonia.Styling
});
}
private void NotifyCompleted()
protected virtual void NotifyCompleted()
{
foreach (var observer in _observers)
{
@ -100,7 +108,7 @@ namespace Avalonia.Styling
_observers = null;
}
private void NotifyError(Exception error)
protected virtual void NotifyError(Exception error)
{
foreach (var observer in _observers)
{
@ -110,11 +118,23 @@ namespace Avalonia.Styling
_observers = null;
}
protected virtual void NotifyValue(object value)
{
Value = value;
Update();
}
protected virtual void NotifyActive(bool active)
{
IsActive = active;
Update();
}
private void Update()
{
if (_active.HasValue)
if (IsActive.HasValue)
{
var v = _active.Value ? _value : AvaloniaProperty.UnsetValue;
var v = IsActive.Value ? Value : AvaloniaProperty.UnsetValue;
if (!Equals(v, _last))
{
@ -141,18 +161,8 @@ namespace Avalonia.Styling
void IObserver<object>.OnCompleted() => _parent.NotifyCompleted();
void IObserver<bool>.OnError(Exception error) => _parent.NotifyError(error);
void IObserver<object>.OnError(Exception error) => _parent.NotifyError(error);
void IObserver<bool>.OnNext(bool value)
{
_parent._active = value;
_parent.Update();
}
void IObserver<object>.OnNext(object value)
{
_parent._value = value;
_parent.Update();
}
void IObserver<bool>.OnNext(bool value) => _parent.NotifyActive(value);
void IObserver<object>.OnNext(object value) => _parent.NotifyValue(value);
}
}
}

59
src/Avalonia.Styling/Styling/ActivatedSubject.cs

@ -2,7 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace Avalonia.Styling
@ -19,9 +18,8 @@ namespace Avalonia.Styling
/// </remarks>
internal class ActivatedSubject : ActivatedObservable, ISubject<object>, IDescription
{
private bool? _active;
private bool _completed;
private object _value;
private object _pushValue;
/// <summary>
/// Initializes a new instance of the <see cref="ActivatedSubject"/> class.
@ -35,7 +33,6 @@ namespace Avalonia.Styling
string description)
: base(activator, source, description)
{
Activator.Subscribe(ActivatorChanged, ActivatorError, ActivatorCompleted);
}
/// <summary>
@ -46,53 +43,57 @@ namespace Avalonia.Styling
get { return (ISubject<object>)base.Source; }
}
/// <summary>
/// Notifies all subscribed observers about the end of the sequence.
/// </summary>
public void OnCompleted()
{
if (_active.Value && !_completed)
Source.OnCompleted();
}
public void OnError(Exception error)
{
Source.OnError(error);
}
public void OnNext(object value)
{
_pushValue = value;
if (IsActive == true && !_completed)
{
Source.OnCompleted();
Source.OnNext(_pushValue);
}
}
/// <summary>
/// Notifies all subscribed observers with the exception.
/// </summary>
/// <param name="error">The exception to send to all subscribed observers.</param>
/// <exception cref="ArgumentNullException"><paramref name="error"/> is null.</exception>
public void OnError(Exception error)
protected override void NotifyCompleted()
{
if (_active.Value && !_completed)
base.NotifyCompleted();
if (!_completed)
{
Source.OnError(error);
Source.OnCompleted();
_completed = true;
}
}
/// <summary>
/// Notifies all subscribed observers with the value.
/// </summary>
/// <param name="value">The value to send to all subscribed observers.</param>
public void OnNext(object value)
protected override void NotifyError(Exception error)
{
_value = value;
base.NotifyError(error);
if (_active.Value && !_completed)
if (!_completed)
{
Source.OnNext(value);
Source.OnError(error);
_completed = true;
}
}
private void ActivatorChanged(bool active)
protected override void NotifyActive(bool active)
{
bool first = !_active.HasValue;
bool first = !IsActive.HasValue;
_active = active;
base.NotifyActive(active);
if (!first)
{
Source.OnNext(active ? _value : AvaloniaProperty.UnsetValue);
Source.OnNext(active ? _pushValue : AvaloniaProperty.UnsetValue);
}
}

10
tests/Avalonia.Styling.UnitTests/ActivatedSubjectTests.cs

@ -17,6 +17,7 @@ namespace Avalonia.Styling.UnitTests
var source = new TestSubject();
var target = new ActivatedSubject(activator, source, string.Empty);
target.Subscribe();
target.OnNext("bar");
Assert.Equal(AvaloniaProperty.UnsetValue, source.Value);
activator.OnNext(true);
@ -36,6 +37,7 @@ namespace Avalonia.Styling.UnitTests
var source = new TestSubject();
var target = new ActivatedSubject(activator, source, string.Empty);
target.Subscribe();
activator.OnCompleted();
Assert.True(source.Completed);
@ -47,10 +49,14 @@ namespace Avalonia.Styling.UnitTests
var activator = new BehaviorSubject<bool>(false);
var source = new TestSubject();
var target = new ActivatedSubject(activator, source, string.Empty);
var targetError = default(Exception);
var error = new Exception();
activator.OnError(new Exception());
target.Subscribe(_ => { }, e => targetError = e);
activator.OnError(error);
Assert.NotNull(source.Error);
Assert.Same(error, source.Error);
Assert.Same(error, targetError);
}
private class TestSubject : ISubject<object>

Loading…
Cancel
Save