Browse Source

Make ActivatedValue a custom Observable.

And make `ActivatedObservable` inherit from it.
pull/1690/head
Steven Kirk 9 years ago
committed by Steven Kirk
parent
commit
cdba9b856d
  1. 125
      src/Avalonia.Styling/Styling/ActivatedObservable.cs
  2. 147
      src/Avalonia.Styling/Styling/ActivatedValue.cs

125
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
/// <see cref="AvaloniaProperty.UnsetValue"/>.
/// </remarks>
internal class ActivatedObservable : IObservable<object>, IDescription
internal class ActivatedObservable : ActivatedValue, IDescription
{
private static readonly object NotSent = new object();
private readonly Listener _listener;
private List<IObserver<object>> _observers;
private IDisposable _activatorSubscription;
private IDisposable _sourceSubscription;
private object _last = NotSent;
/// <summary>
/// Initializes a new instance of the <see cref="ActivatedObservable"/> class.
@ -36,133 +29,49 @@ namespace Avalonia.Styling
IObservable<bool> activator,
IObservable<object> source,
string description)
: base(activator, AvaloniaProperty.UnsetValue, description)
{
Contract.Requires<ArgumentNullException>(activator != null);
Contract.Requires<ArgumentNullException>(source != null);
Activator = activator;
Description = description;
Source = source;
_listener = new Listener(this);
}
/// <summary>
/// Gets the activator observable.
/// </summary>
public IObservable<bool> Activator { get; }
/// <summary>
/// Gets a description of the binding.
/// </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;
_observers = _observers ?? new List<IObserver<object>>();
_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<object>
{
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<bool>, IObserver<object>
{
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<bool>.OnCompleted() => _parent.NotifyCompleted();
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.NotifyActive(value);
void IObserver<object>.OnNext(object value) => _parent.NotifyValue(value);
void IObserver<object>.OnCompleted() => Parent.NotifyCompleted();
void IObserver<object>.OnError(Exception error) => Parent.NotifyError(error);
void IObserver<object>.OnNext(object value) => Parent.NotifyValue(value);
}
}
}

147
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
/// <see cref="ActivatedValue"/> will produce the current value. When the activator
/// produces false it will produce <see cref="AvaloniaProperty.UnsetValue"/>.
/// </remarks>
internal class ActivatedValue : ObservableBase<object>, IDescription
internal class ActivatedValue : IObservable<object>, IDescription
{
/// <summary>
/// The activator.
/// </summary>
private readonly IObservable<bool> _activator;
private static readonly object NotSent = new object();
private List<IObserver<object>> _observers = new List<IObserver<object>>();
private IDisposable _activatorSubscription;
private object _value;
private object _last = NotSent;
/// <summary>
/// Initializes a new instance of the <see cref="ActivatedObservable"/> class.
@ -34,39 +35,135 @@ namespace Avalonia.Styling
object value,
string description)
{
_activator = activator;
Contract.Requires<ArgumentNullException>(activator != null);
Activator = activator;
Value = value;
Description = description;
Listener = CreateListener();
}
/// <summary>
/// Gets the activated value.
/// Gets the activator observable.
/// </summary>
public object Value
{
get;
}
public IObservable<bool> Activator { get; }
/// <summary>
/// Gets a description of the binding.
/// </summary>
public string Description
{
get;
}
public string Description { get; }
/// <summary>
/// Gets a value indicating whether the activator is active.
/// </summary>
public bool? IsActive { get; private set; }
/// <summary>
/// Notifies the provider that an observer is to receive notifications.
/// Gets the value that will be produced when <see cref="IsActive"/> is true.
/// </summary>
/// <param name="observer">The observer.</param>
/// <returns>IDisposable object used to unsubscribe from the observable sequence.</returns>
protected override IDisposable SubscribeCore(IObserver<object> observer)
public object Value
{
get => _value;
protected set
{
_value = value;
PublishValue();
}
}
protected ActivatorListener Listener { get; }
public virtual IDisposable Subscribe(IObserver<object> 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<ArgumentNullException>(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<bool>
{
public ActivatorListener(ActivatedValue parent)
{
Parent = parent;
}
protected ActivatedValue Parent { get; }
return _activator
.Select(active => active ? Value : AvaloniaProperty.UnsetValue)
.Subscribe(observer);
void IObserver<bool>.OnCompleted() => Parent.NotifyCompleted();
void IObserver<bool>.OnError(Exception error) => Parent.NotifyError(error);
void IObserver<bool>.OnNext(bool value) => Parent.NotifyActive(value);
}
}
}

Loading…
Cancel
Save