// ----------------------------------------------------------------------- // // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex { using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; internal class PriorityValue : IObservable> { private object localValue = PerspexProperty.UnsetValue; private IDisposable localBinding; private object lastValue = PerspexProperty.UnsetValue; private List styles = new List(); private List>> observers = new List>>(); private int defer; private bool dirty; public object LocalValue { get { return this.localValue; } set { if (!object.Equals(this.localValue, value)) { this.localValue = value; this.Push(); } } } public void ClearLocalBinding() { if (this.localBinding != null) { this.localBinding.Dispose(); } } public void SetLocalValue(object value) { if (this.localBinding != null) { this.localBinding.Dispose(); } this.LocalValue = value; } public void SetLocalBinding(IObservable binding) { if (this.localBinding != null) { this.localBinding.Dispose(); } this.localBinding = binding.Subscribe(value => this.LocalValue = value); } public void AddStyle(IObservable activator, object value) { Contract.Requires(activator != null); StyleEntry entry = new StyleEntry(activator, value, this.Push, e => this.styles.Remove(e)); this.styles.Add(entry); if (this.localValue == PerspexProperty.UnsetValue) { this.Push(); } } public object GetEffectiveValue() { if (this.localValue != PerspexProperty.UnsetValue) { return this.localValue; } else { foreach (StyleEntry style in Enumerable.Reverse(this.styles)) { if (style.Active) { return style.Value; } } } return PerspexProperty.UnsetValue; } public IDisposable Subscribe(IObserver> observer) { Contract.Requires(observer != null); this.observers.Add(observer); return Disposable.Create(() => this.observers.Remove(observer)); } public void BeginDeferStyleChanges() { if (this.defer++ == 0) { this.dirty = false; } } public void EndDeferStyleChanges() { if (this.defer > 0 && --this.defer == 0 && dirty) { this.Push(); } } private void Push() { if (defer == 0) { object value = this.GetEffectiveValue(); if (!object.Equals(this.lastValue, value)) { foreach (IObserver> observer in this.observers) { observer.OnNext(Tuple.Create(this.lastValue, value)); } this.lastValue = value; } } else { dirty = true; } } private class StyleEntry { private IObservable activator; public StyleEntry( IObservable activator, object value, Action activeChanged, Action completed) { Contract.Requires(activator != null); Contract.Requires(activeChanged != null); this.activator = activator; this.Value = value; this.activator.Subscribe(x => { this.Active = x; activeChanged(); }, () => completed(this)); } public bool Active { get; private set; } public object Value { get; private set; } } } }