From 012a010ba363bee5fc7d6e5ad5531db9bb36bb53 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Mar 2014 03:46:07 +0100 Subject: [PATCH] Made all tests pass again. --- Perspex.UnitTests/PerspexObjectTests.cs | 92 +++++-------------------- Perspex.UnitTests/StyleTests.cs | 2 +- Perspex/PerspexObject.cs | 35 +++++----- Perspex/PriorityValue.cs | 51 ++++++++++++-- Perspex/Styling/StyleBinding.cs | 82 ++++++++++------------ 5 files changed, 117 insertions(+), 145 deletions(-) diff --git a/Perspex.UnitTests/PerspexObjectTests.cs b/Perspex.UnitTests/PerspexObjectTests.cs index 54088d66cb..0f39aff767 100644 --- a/Perspex.UnitTests/PerspexObjectTests.cs +++ b/Perspex.UnitTests/PerspexObjectTests.cs @@ -261,7 +261,7 @@ namespace Perspex.UnitTests Class1 source = new Class1(); source.SetValue(Class1.FooProperty, "initial"); - target.SetValue((PerspexProperty)Class1.FooProperty, source.GetObservable(Class1.FooProperty)); + target.Bind((PerspexProperty)Class1.FooProperty, source.GetObservable(Class1.FooProperty)); Assert.AreEqual("initial", target.GetValue(Class1.FooProperty)); } @@ -321,93 +321,33 @@ namespace Perspex.UnitTests [TestMethod] public void StyleBinding_Overrides_Default_Value() { - throw new NotImplementedException(); - //Class1 target = new Class1(); + Class1 target = new Class1(); - //target.Bind(Class1.FooProperty, "stylevalue", Observable.Return(true)); + target.Bind(Class1.FooProperty, this.Single("stylevalue"), BindingPriority.Style); - //Assert.AreEqual("stylevalue", target.GetValue(Class1.FooProperty)); + Assert.AreEqual("stylevalue", target.GetValue(Class1.FooProperty)); } [TestMethod] public void StyleBinding_Doesnt_Override_Local_Value() { - throw new NotImplementedException(); - //Class1 target = new Class1(); - - //target.SetValue(Class1.FooProperty, "newvalue"); - //target.SetValue(Class1.FooProperty, "stylevalue", Observable.Return(true)); - - //Assert.AreEqual("newvalue", target.GetValue(Class1.FooProperty)); - } - - [TestMethod] - public void StyleBinding_Deactivated_Doesnt_Override_Default_Value() - { - throw new NotImplementedException(); - //Class1 target = new Class1(); - - //target.SetValue(Class1.FooProperty, "stylevalue", Observable.Return(false)); - - //Assert.AreEqual("foodefault", target.GetValue(Class1.FooProperty)); - } - - [TestMethod] - public void StyleBinding_Toggles_On_Activation() - { - throw new NotImplementedException(); - //Class1 target = new Class1(); - - //Subject source = new Subject(); - //target.SetValue(Class1.FooProperty, "stylevalue", source); - - //Assert.AreEqual("foodefault", target.GetValue(Class1.FooProperty)); - //source.OnNext(true); - //Assert.AreEqual("stylevalue", target.GetValue(Class1.FooProperty)); - //source.OnNext(false); - //Assert.AreEqual("foodefault", target.GetValue(Class1.FooProperty)); - } - - [TestMethod] - public void StyleBinding_Detaches_OnCompleted() - { - throw new NotImplementedException(); - //Class1 target = new Class1(); + Class1 target = new Class1(); - //Subject source = new Subject(); - //target.SetValue(Class1.FooProperty, "stylevalue", source); + target.SetValue(Class1.FooProperty, "newvalue"); + target.Bind(Class1.FooProperty, this.Single("stylevalue"), BindingPriority.Style); - //Assert.AreEqual("foodefault", target.GetValue(Class1.FooProperty)); - //source.OnNext(true); - //Assert.AreEqual("stylevalue", target.GetValue(Class1.FooProperty)); - //source.OnCompleted(); - //Assert.AreEqual("foodefault", target.GetValue(Class1.FooProperty)); + Assert.AreEqual("newvalue", target.GetValue(Class1.FooProperty)); } - [TestMethod] - public void Later_StyleBindings_Have_Precedence() + /// + /// Returns an observable that returns a single value but does not complete. + /// + /// The type of the observable. + /// The value. + /// The observable. + private IObservable Single(T value) { - throw new NotImplementedException(); - //Class1 target = new Class1(); - - //Subject source1 = new Subject(); - //Subject source2 = new Subject(); - //target.SetValue(Class1.FooProperty, "style1", source1); - //target.SetValue(Class1.FooProperty, "style2", source2); - - //Assert.AreEqual("foodefault", target.GetValue(Class1.FooProperty)); - //source1.OnNext(true); - //Assert.AreEqual("style1", target.GetValue(Class1.FooProperty)); - //source2.OnNext(true); - //Assert.AreEqual("style2", target.GetValue(Class1.FooProperty)); - //source1.OnNext(false); - //Assert.AreEqual("style2", target.GetValue(Class1.FooProperty)); - //source2.OnNext(false); - //Assert.AreEqual("foodefault", target.GetValue(Class1.FooProperty)); - //source2.OnNext(true); - //Assert.AreEqual("style2", target.GetValue(Class1.FooProperty)); - //source1.OnNext(true); - //Assert.AreEqual("style2", target.GetValue(Class1.FooProperty)); + return Observable.Never().StartWith(value); } private class Class1 : PerspexObject diff --git a/Perspex.UnitTests/StyleTests.cs b/Perspex.UnitTests/StyleTests.cs index 24ec380d2a..3a88ac2849 100644 --- a/Perspex.UnitTests/StyleTests.cs +++ b/Perspex.UnitTests/StyleTests.cs @@ -105,7 +105,7 @@ namespace Perspex.UnitTests target.Classes.Add("foo"); target.Classes.Remove("foo"); - CollectionAssert.AreEqual(new[] { "foodefault", "Bar", "foodefault" }, values); + CollectionAssert.AreEqual(new[] { "foodefault", "Foo", "Bar", "foodefault" }, values); } private class Class1 : Control diff --git a/Perspex/PerspexObject.cs b/Perspex/PerspexObject.cs index b323b3260f..9b5342f836 100644 --- a/Perspex/PerspexObject.cs +++ b/Perspex/PerspexObject.cs @@ -120,7 +120,6 @@ namespace Perspex /// public void BeginDeferChanges() { - throw new NotImplementedException(); //foreach (PriorityValue v in this.values.Values) //{ // v.BeginDeferChanges(); @@ -138,7 +137,6 @@ namespace Perspex /// public void EndDeferChanges() { - throw new NotImplementedException(); //foreach (PriorityValue v in this.values.Values) //{ // v.EndDeferChanges(); @@ -397,6 +395,7 @@ namespace Perspex { Contract.Requires(property != null); + const int priority = (int)BindingPriority.LocalValue; PriorityValue v; if (!this.values.TryGetValue(property, out v)) @@ -410,8 +409,8 @@ namespace Perspex this.values.Add(property, v); } - throw new NotImplementedException(); - //v.SetLocalValue(value); + v.Clear(priority); + v.Add(Observable.Never().StartWith(value), priority); } /// @@ -439,7 +438,22 @@ namespace Perspex IObservable source, BindingPriority priority = BindingPriority.LocalValue) { - throw new NotImplementedException(); + Contract.Requires(property != null); + + PriorityValue v; + + if (!this.values.TryGetValue(property, out v)) + { + v = this.CreatePriorityValue(property); + this.values.Add(property, v); + } + + if (priority == BindingPriority.LocalValue) + { + v.Clear((int)priority); + } + + v.Add(source, (int)priority); this.Log().Debug(string.Format( "Bound value of {0}.{1} (#{2:x8})", @@ -465,17 +479,6 @@ namespace Perspex this.Bind((PerspexProperty)property, (IObservable)source, priority); } - private static IObservable BoxObservable(IObservable observable) - { - return Observable.Create(observer => - { - return observable.Subscribe(value => - { - observer.OnNext(value); - }); - }); - } - private PriorityValue CreatePriorityValue(PerspexProperty property) { PriorityValue result = new PriorityValue(); diff --git a/Perspex/PriorityValue.cs b/Perspex/PriorityValue.cs index acbf3bb1db..d6ffe15cb5 100644 --- a/Perspex/PriorityValue.cs +++ b/Perspex/PriorityValue.cs @@ -8,7 +8,6 @@ namespace Perspex { using System; using System.Collections.Generic; - using System.Linq; using System.Reactive.Disposables; using System.Reactive.Subjects; @@ -101,12 +100,39 @@ namespace Perspex return Disposable.Create(() => { - entry.Dispose(); - this.bindings.Remove(entry); - this.UpdateValue(); + this.Remove(entry); }); } + /// + /// Removes all bindings with the specified priority. + /// + /// The priority. + public void Clear(int priority) + { + LinkedListNode item = this.bindings.First; + bool removed = false; + + while (item != null && item.Value.Priority <= priority) + { + LinkedListNode next = item.Next; + + if (item.Value.Priority == priority) + { + item.Value.Dispose(); + this.bindings.Remove(item); + removed = true; + } + + item = next; + } + + if (removed && priority <= this.valuePriority) + { + this.UpdateValue(); + } + } + /// /// Gets the currently active bindings on this object. /// @@ -134,9 +160,7 @@ namespace Perspex /// The completed entry. private void EntryCompleted(BindingEntry entry) { - entry.Dispose(); - this.bindings.Remove(entry); - this.UpdateValue(); + this.Remove(entry); } /// @@ -157,6 +181,17 @@ namespace Perspex } } + /// + /// Removes the specified binding entry and updates the current value. + /// + /// The binding entry to remove. + private void Remove(BindingEntry entry) + { + entry.Dispose(); + this.bindings.Remove(entry); + this.UpdateValue(); + } + /// /// Updates the current value. /// @@ -170,6 +205,8 @@ namespace Perspex return; } } + + this.SetValue(PerspexProperty.UnsetValue, int.MaxValue); } /// diff --git a/Perspex/Styling/StyleBinding.cs b/Perspex/Styling/StyleBinding.cs index 3aebf48195..547af13467 100644 --- a/Perspex/Styling/StyleBinding.cs +++ b/Perspex/Styling/StyleBinding.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright 2013 Tricycle. All rights reserved. // // ----------------------------------------------------------------------- @@ -7,79 +7,71 @@ namespace Perspex.Styling { using System; - using System.Collections.Generic; - using System.Linq; - using System.Reactive.Disposables; using System.Reactive.Subjects; - using System.Text; - using System.Threading.Tasks; + /// + /// Provides an observable for a style. + /// + /// + /// This class takes an activator and a value. The activator is an observable which produces + /// a bool. When the activator produces true, this observable will produce . + /// When the activator produces false (and before the activator returns a value) it will + /// produce . + /// internal class StyleBinding : IObservable, IObservableDescription { - private List> observers = new List>(); + /// + /// The subject that provides the observable implementation. + /// + private BehaviorSubject subject = new BehaviorSubject(PerspexProperty.UnsetValue); + /// + /// Initializes a new instance of the class. + /// + /// The activator. + /// The activated value. + /// The binding description. public StyleBinding( - StyleActivator activator, + IObservable activator, object activatedValue, string description) { - this.Activator = activator; this.ActivatedValue = activatedValue; this.Description = description; - this.Activator.Subscribe( - active => this.OnNext(active ? this.ActivatedValue : PerspexProperty.UnsetValue), - error => this.OnError(error), - () => this.OnCompleted()); - } - - public StyleActivator Activator - { - get; - private set; + activator.Subscribe( + active => this.subject.OnNext(active ? this.ActivatedValue : PerspexProperty.UnsetValue), + error => this.subject.OnError(error), + () => this.subject.OnCompleted()); } + /// + /// Gets a description of the binding. + /// public string Description { get; private set; } + /// + /// Gets the activated value. + /// public object ActivatedValue { get; private set; } + /// + /// Notifies the provider that an observer is to receive notifications. + /// + /// The observer. + /// IDisposable object used to unsubscribe from the observable sequence. public IDisposable Subscribe(IObserver observer) { Contract.Requires(observer != null); - this.observers.Add(observer); - return Disposable.Create(() => this.observers.Remove(observer)); - } - - private void OnCompleted() - { - foreach (var observer in this.observers) - { - observer.OnCompleted(); - } - } - - private void OnError(Exception error) - { - foreach (var observer in this.observers) - { - observer.OnError(error); - } - } - - private void OnNext(object value) - { - foreach (var observer in this.observers) - { - observer.OnNext(value); - } + return this.subject.Subscribe(observer); } } }