// ----------------------------------------------------------------------- // // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex.Styling { using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; public enum ActivatorMode { And, Or, } public class StyleActivator : IObservable, IDisposable { private ActivatorMode mode; private List values = new List(); private List subscriptions = new List(); private List> observers = new List>(); public StyleActivator( IEnumerable> inputs, ActivatorMode mode = ActivatorMode.And) { int i = 0; this.mode = mode; foreach (IObservable input in inputs) { int capturedIndex = i; this.values.Add(false); IDisposable subscription = input.Subscribe( x => this.Update(capturedIndex, x), x => this.Finish(capturedIndex), () => this.Finish(capturedIndex)); this.subscriptions.Add(subscription); ++i; } } public bool CurrentValue { get; private set; } public bool HasCompleted { get; private set; } public void Dispose() { foreach (IObserver observer in this.observers) { observer.OnCompleted(); } foreach (IDisposable subscription in this.subscriptions) { subscription.Dispose(); } } public IDisposable Subscribe(IObserver observer) { Contract.Requires(observer != null); this.observers.Add(observer); observer.OnNext(this.CurrentValue); return Disposable.Create(() => this.observers.Remove(observer)); } private void Update(int index, bool value) { this.values[index] = value; bool current; switch (this.mode) { case ActivatorMode.And: current = this.values.All(x => x); break; case ActivatorMode.Or: current = this.values.Any(x => x); break; default: throw new InvalidOperationException("Invalid Activator mode."); } if (current != this.CurrentValue) { this.Push(current); this.CurrentValue = current; } } private void Finish(int i) { // If the observable has finished on 'false' and we're in And mode then it will never // go back to true so we can unsubscribe from all the other subscriptions now. // Similarly in Or mode; if the completed value is true then we're done. bool unsubscribe = this.mode == ActivatorMode.And ? !this.values[i] : this.values[i]; if (unsubscribe) { foreach (IDisposable subscription in this.subscriptions) { subscription.Dispose(); } this.HasCompleted = true; } } private void Push(bool value) { foreach (IObserver observer in this.observers) { observer.OnNext(value); } } } }