From cdba9b856d93d597e8192e00236763acab4952ee Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Dec 2017 18:25:57 +0100 Subject: [PATCH] Make ActivatedValue a custom Observable. And make `ActivatedObservable` inherit from it. --- .../Styling/ActivatedObservable.cs | 125 ++------------- .../Styling/ActivatedValue.cs | 147 +++++++++++++++--- 2 files changed, 139 insertions(+), 133 deletions(-) diff --git a/src/Avalonia.Styling/Styling/ActivatedObservable.cs b/src/Avalonia.Styling/Styling/ActivatedObservable.cs index 3c58bb5f9d..4933daed31 100644 --- a/src/Avalonia.Styling/Styling/ActivatedObservable.cs +++ b/src/Avalonia.Styling/Styling/ActivatedObservable.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections.Generic; -using System.Reactive.Disposables; namespace Avalonia.Styling { @@ -17,14 +15,9 @@ namespace Avalonia.Styling /// value. When the activator produces false it will produce /// . /// - internal class ActivatedObservable : IObservable, IDescription + internal class ActivatedObservable : ActivatedValue, IDescription { - private static readonly object NotSent = new object(); - private readonly Listener _listener; - private List> _observers; - private IDisposable _activatorSubscription; private IDisposable _sourceSubscription; - private object _last = NotSent; /// /// Initializes a new instance of the class. @@ -36,133 +29,49 @@ namespace Avalonia.Styling IObservable activator, IObservable source, string description) + : base(activator, AvaloniaProperty.UnsetValue, description) { - Contract.Requires(activator != null); Contract.Requires(source != null); - Activator = activator; - Description = description; Source = source; - _listener = new Listener(this); } - /// - /// Gets the activator observable. - /// - public IObservable Activator { get; } - - /// - /// Gets a description of the binding. - /// - 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; - - _observers = _observers ?? new List>(); - _observers.Add(observer); - - if (subscribe) - { - _sourceSubscription = Source.Subscribe(_listener); - _activatorSubscription = Activator.Subscribe(_listener); - } - - return Disposable.Create(() => - { - _observers.Remove(observer); - - if (_observers.Count == 0) - { - _activatorSubscription.Dispose(); - _sourceSubscription.Dispose(); - _activatorSubscription = null; - _sourceSubscription = null; - } - }); - } + protected override ActivatorListener CreateListener() => new ValueListener(this); - protected virtual void NotifyCompleted() + protected override void Deinitialize() { - foreach (var observer in _observers) - { - observer.OnCompleted(); - } - - _observers = null; + base.Deinitialize(); + _sourceSubscription.Dispose(); + _sourceSubscription = null; } - protected virtual void NotifyError(Exception error) + protected override void Initialize() { - foreach (var observer in _observers) - { - observer.OnError(error); - } - - _observers = null; + base.Initialize(); + _sourceSubscription = Source.Subscribe((ValueListener)Listener); } protected virtual void NotifyValue(object value) { Value = value; - Update(); } - protected virtual void NotifyActive(bool active) + private class ValueListener : ActivatorListener, IObserver { - IsActive = active; - Update(); - } - - private void Update() - { - if (IsActive.HasValue) - { - var v = IsActive.Value ? Value : AvaloniaProperty.UnsetValue; - - if (!Equals(v, _last)) - { - foreach (var observer in _observers) - { - observer.OnNext(v); - } - - _last = v; - } - } - } - - private class Listener : IObserver, IObserver - { - private readonly ActivatedObservable _parent; - - public Listener(ActivatedObservable parent) + public ValueListener(ActivatedObservable parent) + : base(parent) { - _parent = parent; } + protected new ActivatedObservable Parent => (ActivatedObservable)base.Parent; - void IObserver.OnCompleted() => _parent.NotifyCompleted(); - 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.NotifyActive(value); - void IObserver.OnNext(object value) => _parent.NotifyValue(value); + void IObserver.OnCompleted() => Parent.NotifyCompleted(); + void IObserver.OnError(Exception error) => Parent.NotifyError(error); + void IObserver.OnNext(object value) => Parent.NotifyValue(value); } } } diff --git a/src/Avalonia.Styling/Styling/ActivatedValue.cs b/src/Avalonia.Styling/Styling/ActivatedValue.cs index 3b9324c059..683121628c 100644 --- a/src/Avalonia.Styling/Styling/ActivatedValue.cs +++ b/src/Avalonia.Styling/Styling/ActivatedValue.cs @@ -2,8 +2,8 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Reactive; -using System.Reactive.Linq; +using System.Collections.Generic; +using System.Reactive.Disposables; namespace Avalonia.Styling { @@ -16,12 +16,13 @@ namespace Avalonia.Styling /// will produce the current value. When the activator /// produces false it will produce . /// - internal class ActivatedValue : ObservableBase, IDescription + internal class ActivatedValue : IObservable, IDescription { - /// - /// The activator. - /// - private readonly IObservable _activator; + private static readonly object NotSent = new object(); + private List> _observers = new List>(); + private IDisposable _activatorSubscription; + private object _value; + private object _last = NotSent; /// /// Initializes a new instance of the class. @@ -34,39 +35,135 @@ namespace Avalonia.Styling object value, string description) { - _activator = activator; + Contract.Requires(activator != null); + + Activator = activator; Value = value; Description = description; + Listener = CreateListener(); } /// - /// Gets the activated value. + /// Gets the activator observable. /// - public object Value - { - get; - } + public IObservable Activator { get; } /// /// Gets a description of the binding. /// - public string Description - { - get; - } + public string Description { get; } + + /// + /// Gets a value indicating whether the activator is active. + /// + public bool? IsActive { get; private set; } /// - /// Notifies the provider that an observer is to receive notifications. + /// Gets the value that will be produced when is true. /// - /// The observer. - /// IDisposable object used to unsubscribe from the observable sequence. - protected override IDisposable SubscribeCore(IObserver observer) + public object Value + { + get => _value; + protected set + { + _value = value; + PublishValue(); + } + } + + protected ActivatorListener Listener { get; } + + public virtual IDisposable Subscribe(IObserver observer) + { + _observers.Add(observer); + + if (_observers.Count == 1) + { + Initialize(); + } + + return Disposable.Create(() => + { + _observers.Remove(observer); + + if (_observers.Count == 0) + { + Deinitialize(); + } + }); + } + + protected virtual ActivatorListener CreateListener() => new ActivatorListener(this); + + protected virtual void Deinitialize() { - Contract.Requires(observer != null); + _activatorSubscription.Dispose(); + _activatorSubscription = null; + } + + protected virtual void Initialize() + { + _activatorSubscription = Activator.Subscribe(Listener); + } + + protected virtual void NotifyCompleted() + { + foreach (var observer in _observers) + { + observer.OnCompleted(); + } + + Deinitialize(); + _observers = null; + } + + protected virtual void NotifyError(Exception error) + { + foreach (var observer in _observers) + { + observer.OnError(error); + } + + Deinitialize(); + _observers = null; + } + + protected virtual void NotifyActive(bool active) + { + IsActive = active; + PublishValue(); + } + + private void PublishValue() + { + if (IsActive.HasValue) + { + var v = IsActive.Value ? Value : AvaloniaProperty.UnsetValue; + + if (!Equals(v, _last)) + { + foreach (var observer in _observers) + { + observer.OnNext(v); + } + + _last = v; + } + } + } + + protected class ActivatorListener : IObserver + { + public ActivatorListener(ActivatedValue parent) + { + Parent = parent; + } + + protected ActivatedValue Parent { get; } - return _activator - .Select(active => active ? Value : AvaloniaProperty.UnsetValue) - .Subscribe(observer); + void IObserver.OnCompleted() => Parent.NotifyCompleted(); + void IObserver.OnError(Exception error) => Parent.NotifyError(error); + void IObserver.OnNext(bool value) => Parent.NotifyActive(value); } } }