Browse Source

Added an OR mode to Activator.

pull/4/head
Steven Kirk 12 years ago
parent
commit
eb3b6b13b6
  1. 107
      Perspex.UnitTests/Styling/ActivatorTests.cs
  2. 35
      Perspex/Styling/Activator.cs

107
Perspex.UnitTests/Styling/ActivatorTests.cs

@ -15,10 +15,10 @@
public class ActivatorTests public class ActivatorTests
{ {
[TestMethod] [TestMethod]
public void Activator_Should_Follow_Single_Input() public void Activator_And_Should_Follow_Single_Input()
{ {
var inputs = new[] { new TestSubject<bool>(false) }; var inputs = new[] { new TestSubject<bool>(false) };
var target = new Activator(inputs); var target = new Activator(inputs, ActivatorMode.And);
var result = new TestObserver<bool>(); var result = new TestObserver<bool>();
target.Subscribe(result); target.Subscribe(result);
@ -34,7 +34,7 @@
} }
[TestMethod] [TestMethod]
public void Activator_Should_AND_Multiple_Inputs() public void Activator_And_Should_AND_Multiple_Inputs()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -42,7 +42,7 @@
new TestSubject<bool>(false), new TestSubject<bool>(false),
new TestSubject<bool>(true), new TestSubject<bool>(true),
}; };
var target = new Activator(inputs); var target = new Activator(inputs, ActivatorMode.And);
var result = new TestObserver<bool>(); var result = new TestObserver<bool>();
target.Subscribe(result); target.Subscribe(result);
@ -59,7 +59,7 @@
} }
[TestMethod] [TestMethod]
public void Activator_Should_Unsubscribe_All_When_Input_Completes_On_False() public void Activator_And_Should_Unsubscribe_All_When_Input_Completes_On_False()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -67,7 +67,7 @@
new TestSubject<bool>(false), new TestSubject<bool>(false),
new TestSubject<bool>(true), new TestSubject<bool>(true),
}; };
var target = new Activator(inputs); var target = new Activator(inputs, ActivatorMode.And);
var result = new TestObserver<bool>(); var result = new TestObserver<bool>();
target.Subscribe(result); target.Subscribe(result);
@ -85,7 +85,7 @@
} }
[TestMethod] [TestMethod]
public void Activator_Should_Not_Unsubscribe_All_When_Input_Completes_On_False() public void Activator_And_Should_Not_Unsubscribe_All_When_Input_Completes_On_True()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -93,7 +93,7 @@
new TestSubject<bool>(false), new TestSubject<bool>(false),
new TestSubject<bool>(true), new TestSubject<bool>(true),
}; };
var target = new Activator(inputs); var target = new Activator(inputs, ActivatorMode.And);
var result = new TestObserver<bool>(); var result = new TestObserver<bool>();
target.Subscribe(result); target.Subscribe(result);
@ -105,5 +105,96 @@
Assert.AreEqual(1, inputs[1].SubscriberCount); Assert.AreEqual(1, inputs[1].SubscriberCount);
Assert.AreEqual(1, inputs[2].SubscriberCount); Assert.AreEqual(1, inputs[2].SubscriberCount);
} }
[TestMethod]
public void Activator_Or_Should_Follow_Single_Input()
{
var inputs = new[] { new TestSubject<bool>(false) };
var target = new Activator(inputs, ActivatorMode.Or);
var result = new TestObserver<bool>();
target.Subscribe(result);
Assert.IsFalse(result.GetValue());
inputs[0].OnNext(true);
Assert.IsTrue(result.GetValue());
inputs[0].OnNext(false);
Assert.IsFalse(result.GetValue());
inputs[0].OnNext(true);
Assert.IsTrue(result.GetValue());
Assert.AreEqual(1, inputs[0].SubscriberCount);
}
[TestMethod]
public void Activator_Or_Should_OR_Multiple_Inputs()
{
var inputs = new[]
{
new TestSubject<bool>(false),
new TestSubject<bool>(false),
new TestSubject<bool>(true),
};
var target = new Activator(inputs, ActivatorMode.Or);
var result = new TestObserver<bool>();
target.Subscribe(result);
Assert.IsTrue(result.GetValue());
inputs[2].OnNext(false);
Assert.IsFalse(result.GetValue());
inputs[0].OnNext(true);
Assert.IsTrue(result.GetValue());
Assert.AreEqual(1, inputs[0].SubscriberCount);
Assert.AreEqual(1, inputs[1].SubscriberCount);
Assert.AreEqual(1, inputs[2].SubscriberCount);
}
[TestMethod]
public void Activator_Or_Should_Unsubscribe_All_When_Input_Completes_On_True()
{
var inputs = new[]
{
new TestSubject<bool>(false),
new TestSubject<bool>(false),
new TestSubject<bool>(true),
};
var target = new Activator(inputs, ActivatorMode.Or);
var result = new TestObserver<bool>();
target.Subscribe(result);
Assert.IsTrue(result.GetValue());
inputs[2].OnNext(false);
Assert.IsFalse(result.GetValue());
inputs[0].OnNext(true);
Assert.IsTrue(result.GetValue());
inputs[0].OnCompleted();
Assert.AreEqual(0, inputs[0].SubscriberCount);
Assert.AreEqual(0, inputs[1].SubscriberCount);
Assert.AreEqual(0, inputs[2].SubscriberCount);
}
[TestMethod]
public void Activator_Or_Should_Not_Unsubscribe_All_When_Input_Completes_On_False()
{
var inputs = new[]
{
new TestSubject<bool>(false),
new TestSubject<bool>(false),
new TestSubject<bool>(true),
};
var target = new Activator(inputs, ActivatorMode.Or);
var result = new TestObserver<bool>();
target.Subscribe(result);
Assert.IsTrue(result.GetValue());
inputs[2].OnNext(false);
Assert.IsFalse(result.GetValue());
inputs[2].OnCompleted();
Assert.AreEqual(1, inputs[0].SubscriberCount);
Assert.AreEqual(1, inputs[1].SubscriberCount);
Assert.AreEqual(1, inputs[2].SubscriberCount);
}
} }
} }

35
Perspex/Styling/Activator.cs

@ -11,8 +11,16 @@ namespace Perspex.Styling
using System.Linq; using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
public enum ActivatorMode
{
And,
Or,
}
public class Activator : IObservable<bool> public class Activator : IObservable<bool>
{ {
ActivatorMode mode;
List<bool> values = new List<bool>(); List<bool> values = new List<bool>();
List<IDisposable> subscriptions = new List<IDisposable>(); List<IDisposable> subscriptions = new List<IDisposable>();
@ -21,10 +29,12 @@ namespace Perspex.Styling
bool last = false; bool last = false;
public Activator(IEnumerable<IObservable<bool>> inputs) public Activator(IEnumerable<IObservable<bool>> inputs, ActivatorMode mode = ActivatorMode.And)
{ {
int i = 0; int i = 0;
this.mode = mode;
foreach (IObservable<bool> input in inputs) foreach (IObservable<bool> input in inputs)
{ {
int iCaptured = i; int iCaptured = i;
@ -53,7 +63,19 @@ namespace Perspex.Styling
{ {
this.values[index] = value; this.values[index] = value;
bool current = this.values.All(x => x); 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 != last) if (current != last)
{ {
@ -64,10 +86,13 @@ namespace Perspex.Styling
private void Finish(int i) private void Finish(int i)
{ {
if (!this.values[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)
{ {
// If the observable has finished on 'false' then it will never go back to true
// so we can unsubscribe from all the other subscriptions now.
foreach (IDisposable subscription in this.subscriptions) foreach (IDisposable subscription in this.subscriptions)
{ {
subscription.Dispose(); subscription.Dispose();

Loading…
Cancel
Save