From de35eb9fee049179e41076de2fc441454e415959 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Dec 2017 15:56:22 +0100 Subject: [PATCH] Updated ActivatedSubject. Can be expressed more cleanly given the new `ActivatedObject`. --- .../Styling/ActivatedObservable.cs | 46 +++++++++------ .../Styling/ActivatedSubject.cs | 59 ++++++++++--------- .../ActivatedSubjectTests.cs | 10 +++- 3 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/Avalonia.Styling/Styling/ActivatedObservable.cs b/src/Avalonia.Styling/Styling/ActivatedObservable.cs index 1d9af6ed41..3c58bb5f9d 100644 --- a/src/Avalonia.Styling/Styling/ActivatedObservable.cs +++ b/src/Avalonia.Styling/Styling/ActivatedObservable.cs @@ -24,8 +24,6 @@ namespace Avalonia.Styling private List> _observers; private IDisposable _activatorSubscription; private IDisposable _sourceSubscription; - private bool? _active; - private object _value; private object _last = NotSent; /// @@ -58,11 +56,21 @@ namespace Avalonia.Styling /// public string Description { get; } + /// + /// Gets a value indicating whether the observable is active. + /// + public bool? IsActive { get; private set; } + /// /// Gets an observable which produces the . /// public IObservable Source { get; } + /// + /// Gets the value that will be produced when is true. + /// + public object Value { get; private set; } + public IDisposable Subscribe(IObserver 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.OnCompleted() => _parent.NotifyCompleted(); void IObserver.OnError(Exception error) => _parent.NotifyError(error); void IObserver.OnError(Exception error) => _parent.NotifyError(error); - - void IObserver.OnNext(bool value) - { - _parent._active = value; - _parent.Update(); - } - - void IObserver.OnNext(object value) - { - _parent._value = value; - _parent.Update(); - } + void IObserver.OnNext(bool value) => _parent.NotifyActive(value); + void IObserver.OnNext(object value) => _parent.NotifyValue(value); } } } diff --git a/src/Avalonia.Styling/Styling/ActivatedSubject.cs b/src/Avalonia.Styling/Styling/ActivatedSubject.cs index 4c2e8dde85..bcbf30649a 100644 --- a/src/Avalonia.Styling/Styling/ActivatedSubject.cs +++ b/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 /// internal class ActivatedSubject : ActivatedObservable, ISubject, IDescription { - private bool? _active; private bool _completed; - private object _value; + private object _pushValue; /// /// Initializes a new instance of the class. @@ -35,7 +33,6 @@ namespace Avalonia.Styling string description) : base(activator, source, description) { - Activator.Subscribe(ActivatorChanged, ActivatorError, ActivatorCompleted); } /// @@ -46,53 +43,57 @@ namespace Avalonia.Styling get { return (ISubject)base.Source; } } - /// - /// Notifies all subscribed observers about the end of the sequence. - /// 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); } } - /// - /// Notifies all subscribed observers with the exception. - /// - /// The exception to send to all subscribed observers. - /// is null. - public void OnError(Exception error) + protected override void NotifyCompleted() { - if (_active.Value && !_completed) + base.NotifyCompleted(); + + if (!_completed) { - Source.OnError(error); + Source.OnCompleted(); + _completed = true; } } - /// - /// Notifies all subscribed observers with the value. - /// - /// The value to send to all subscribed observers. - 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); } } diff --git a/tests/Avalonia.Styling.UnitTests/ActivatedSubjectTests.cs b/tests/Avalonia.Styling.UnitTests/ActivatedSubjectTests.cs index cc42e3f840..03f91d97a1 100644 --- a/tests/Avalonia.Styling.UnitTests/ActivatedSubjectTests.cs +++ b/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(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