Browse Source

Style council.

pull/4/head
grokys 13 years ago
parent
commit
581a037cb8
  1. 2
      Perspex.UnitTests/Perspex.UnitTests.csproj
  2. 8
      Perspex.UnitTests/StyleTests.cs
  3. 68
      Perspex.UnitTests/Styling/SelectorTests.cs
  4. 59
      Perspex.UnitTests/Styling/SubscribeCheck.cs
  5. 1
      Perspex/Perspex.csproj
  6. 89
      Perspex/Styling/Activator.cs
  7. 36
      Perspex/Styling/Match.cs
  8. 50
      Perspex/Styling/Selectors.cs
  9. 6
      Perspex/Themes/Default/ButtonStyle.cs

2
Perspex.UnitTests/Perspex.UnitTests.csproj

@ -69,6 +69,8 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PerspexObjectTests.cs" /> <Compile Include="PerspexObjectTests.cs" />
<Compile Include="StyleTests.cs" /> <Compile Include="StyleTests.cs" />
<Compile Include="Styling\SelectorTests.cs" />
<Compile Include="Styling\SubscribeCheck.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Perspex\Perspex.csproj"> <ProjectReference Include="..\Perspex\Perspex.csproj">

8
Perspex.UnitTests/StyleTests.cs

@ -18,7 +18,7 @@ namespace Perspex.UnitTests
[TestMethod] [TestMethod]
public void Style_With_Only_Type_Selector_Should_Update_Value() public void Style_With_Only_Type_Selector_Should_Update_Value()
{ {
Style style = new Style(x => x.Select<TextBlock>()) Style style = new Style(x => x.Select().OfType<TextBlock>())
{ {
Setters = new[] Setters = new[]
{ {
@ -38,7 +38,7 @@ namespace Perspex.UnitTests
[TestMethod] [TestMethod]
public void Style_With_Class_Selector_Should_Update_And_Restore_Value() public void Style_With_Class_Selector_Should_Update_And_Restore_Value()
{ {
Style style = new Style(x => x.Select<TextBlock>().Class("foo")) Style style = new Style(x => x.Select().OfType<TextBlock>().Class("foo"))
{ {
Setters = new[] Setters = new[]
{ {
@ -62,7 +62,7 @@ namespace Perspex.UnitTests
[TestMethod] [TestMethod]
public void Later_Styles_Should_Override_Earlier() public void Later_Styles_Should_Override_Earlier()
{ {
Style style1 = new Style(x => x.Select<TextBlock>().Class("foo")) Style style1 = new Style(x => x.Select().OfType<TextBlock>().Class("foo"))
{ {
Setters = new[] Setters = new[]
{ {
@ -70,7 +70,7 @@ namespace Perspex.UnitTests
}, },
}; };
Style style2 = new Style(x => x.Select<TextBlock>().Class("foo")) Style style2 = new Style(x => x.Select().OfType<TextBlock>().Class("foo"))
{ {
Setters = new[] Setters = new[]
{ {

68
Perspex.UnitTests/Styling/SelectorTests.cs

@ -0,0 +1,68 @@
// -----------------------------------------------------------------------
// <copyright file="SelectorTests.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.UnitTests.Styling
{
using System;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Perspex.Styling;
using Match = Perspex.Styling.Match;
[TestClass]
public class SelectorTests
{
[TestMethod]
public void OfType_Matches_Control_Of_Correct_Type()
{
var control = new Class1();
var target = control.Select().OfType<Class1>();
CollectionAssert.AreEqual(new[] { true }, target.GetActivator().Take(1).ToEnumerable().ToArray());
}
[TestMethod]
public void OfType_Doesnt_Match_Control_Of_Wrong_Type()
{
var control = new Class2();
var target = control.Select().OfType<Class1>();
CollectionAssert.AreEqual(new[] { false }, target.GetActivator().Take(1).ToEnumerable().ToArray());
}
[TestMethod]
public void When_OfType_Matches_Control_Other_Selectors_Are_Subscribed()
{
var control = new Class1();
var target = control.Select().OfType<Class1>().SubscribeCheck();
var result = target.GetActivator().ToEnumerable().Take(1).ToArray();
Assert.AreEqual(1, control.SubscribeCheckObservable.SubscribedCount);
}
[TestMethod]
public void When_OfType_Doesnt_Match_Control_Other_Selectors_Are_Not_Subscribed()
{
var control = new Class1();
var target = control.Select().OfType<Class2>().SubscribeCheck();
var result = target.GetActivator().ToEnumerable().Take(1).ToArray();
Assert.AreEqual(0, control.SubscribeCheckObservable.SubscribedCount);
}
public class Class1 : SubscribeCheck
{
}
public class Class2 : SubscribeCheck
{
}
}
}

59
Perspex.UnitTests/Styling/SubscribeCheck.cs

@ -0,0 +1,59 @@
// -----------------------------------------------------------------------
// <copyright file="SubscribeCheck.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.UnitTests.Styling
{
using System;
using System.Reactive.Disposables;
using Perspex.Styling;
using Match = Perspex.Styling.Match;
public class TestObservable : IObservable<bool>
{
public int SubscribedCount { get; private set; }
public IDisposable Subscribe(IObserver<bool> observer)
{
++this.SubscribedCount;
observer.OnNext(true);
return Disposable.Create(() => --this.SubscribedCount);
}
}
public class SubscribeCheck : IStyleable
{
public SubscribeCheck()
{
this.Classes = new PerspexList<string>();
this.SubscribeCheckObservable = new TestObservable();
}
public PerspexList<string> Classes
{
get;
private set;
}
public TestObservable SubscribeCheckObservable
{
get;
private set;
}
public virtual void SetValue(PerspexProperty property, object value, IObservable<bool> activator)
{
}
}
public static class TestSelectors
{
public static Match SubscribeCheck(this Match match)
{
match.Observables.Add(((SubscribeCheck)match.Control).SubscribeCheckObservable);
return match;
}
}
}

1
Perspex/Perspex.csproj

@ -79,6 +79,7 @@
<Compile Include="ControlTemplate.cs" /> <Compile Include="ControlTemplate.cs" />
<Compile Include="Input\MouseEventArgs.cs" /> <Compile Include="Input\MouseEventArgs.cs" />
<Compile Include="Interactive.cs" /> <Compile Include="Interactive.cs" />
<Compile Include="Styling\Activator.cs" />
<Compile Include="Styling\IStyleable.cs" /> <Compile Include="Styling\IStyleable.cs" />
<Compile Include="Styling\IStyle.cs" /> <Compile Include="Styling\IStyle.cs" />
<Compile Include="PriorityValue.cs" /> <Compile Include="PriorityValue.cs" />

89
Perspex/Styling/Activator.cs

@ -0,0 +1,89 @@
// -----------------------------------------------------------------------
// <copyright file="Activator.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Styling
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reactive.Disposables;
public class Activator : IObservable<bool>
{
List<bool> values = new List<bool>();
List<IDisposable> subscriptions = new List<IDisposable>();
List<IObserver<bool>> observers = new List<IObserver<bool>>();
bool last = false;
public Activator(IList<IObservable<bool>> observables)
{
int i = 0;
foreach (IObservable<bool> o in observables.Reverse())
{
int iCaptured = i;
this.values.Add(false);
IDisposable subscription = o.Subscribe(
x => this.Update(iCaptured, x),
x => this.Finish(iCaptured),
() => this.Finish(iCaptured));
this.subscriptions.Add(subscription);
++i;
}
}
public IDisposable Subscribe(IObserver<bool> observer)
{
Contract.Requires<ArgumentNullException>(observer != null);
this.observers.Add(observer);
observer.OnNext(last);
return Disposable.Create(() => this.observers.Remove(observer));
}
private void Update(int index, bool value)
{
this.values[index] = value;
bool current = this.values.All(x => x);
if (current != last)
{
this.Push(current);
last = current;
}
}
private void Finish(int i)
{
if (!this.values[i])
{
// 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)
{
subscription.Dispose();
}
}
}
private void Push(bool value)
{
foreach (IObserver<bool> observer in this.observers)
{
observer.OnNext(value);
}
}
}
}

36
Perspex/Styling/Match.cs

@ -14,54 +14,38 @@ namespace Perspex.Styling
public class Match public class Match
{ {
public IStyleable Control public Match(IStyleable control)
{ {
get; this.Control = control;
set; this.Observables = new List<IObservable<bool>>();
} }
public IObservable<bool> Observable public IStyleable Control
{ {
get; get;
set; private set;
} }
public Match Previous public List<IObservable<bool>> Observables
{ {
get; get;
set; set;
} }
public string Token public string SelectorString
{ {
get; get;
set; set;
} }
public IObservable<bool> GetActivator() public Activator GetActivator()
{ {
List<IObservable<bool>> observables = new List<IObservable<bool>>(); return new Activator(this.Observables);
Match match = this;
do
{
if (match.Observable != null)
{
observables.Add(match.Observable);
}
match = match.Previous;
}
while (match != null);
return System.Reactive.Linq.Observable.CombineLatest(observables).Select(x => x.All(b => b));
} }
public override string ToString() public override string ToString()
{ {
string result = (this.Previous != null) ? this.Previous.ToString() : string.Empty; return this.SelectorString;
result += this.Token;
return result;
} }
} }
} }

50
Perspex/Styling/Selectors.cs

@ -18,47 +18,33 @@ namespace Perspex.Styling
public static class Selectors public static class Selectors
{ {
public static Match Select<T>(this IStyleable control) public static Match Select(this IStyleable control)
{ {
Contract.Requires<ArgumentNullException>(control != null); Contract.Requires<ArgumentNullException>(control != null);
if (control is T) return new Match(control);
{
return new Match
{
Control = control,
Observable = Observable.Return(true),
Token = typeof(T).Name,
};
}
else
{
return null;
}
} }
public static Match Class(this Match selector, string name) public static Match Class(this Match match, string name)
{ {
Contract.Requires<ArgumentNullException>(match != null);
Contract.Requires<ArgumentNullException>(name != null); Contract.Requires<ArgumentNullException>(name != null);
if (selector != null) match.Observables.Add(Observable
{ .Return(match.Control.Classes.Contains(name))
IObservable<bool> match = Observable .Concat(match.Control.Classes.Changed.Select(e => match.Control.Classes.Contains(name))));
.Return(selector.Control.Classes.Contains(name)) match.SelectorString += (name[0] == ':') ? name : '.' + name;
.Concat(selector.Control.Classes.Changed.Select(e => selector.Control.Classes.Contains(name)));
return new Match return match;
{ }
Control = selector.Control,
Observable = match, public static Match OfType<T>(this Match match)
Previous = selector, {
Token = (name[0] == ':') ? name : '.' + name, Contract.Requires<ArgumentNullException>(match != null);
};
} match.Observables.Add(Observable.Return(match.Control is T));
else match.SelectorString += typeof(T).Name;
{ return match;
return null;
}
} }
} }
} }

6
Perspex/Themes/Default/ButtonStyle.cs

@ -15,7 +15,7 @@ namespace Perspex.Themes.Default
{ {
this.AddRange(new[] this.AddRange(new[]
{ {
new Style(x => x.Select<Button>()) new Style(x => x.Select().OfType<Button>())
{ {
Setters = new[] Setters = new[]
{ {
@ -26,7 +26,7 @@ namespace Perspex.Themes.Default
new Setter(Button.TemplateProperty, ControlTemplate.Create<Button>(this.Template)), new Setter(Button.TemplateProperty, ControlTemplate.Create<Button>(this.Template)),
}, },
}, },
new Style(x => x.Select<Button>().Class(":mouseover")) new Style(x => x.Select().OfType<Button>().Class(":mouseover"))
{ {
Setters = new[] Setters = new[]
{ {
@ -34,7 +34,7 @@ namespace Perspex.Themes.Default
new Setter (Button.BorderBrushProperty, new SolidColorBrush(0xff3c7fb1)), new Setter (Button.BorderBrushProperty, new SolidColorBrush(0xff3c7fb1)),
}, },
}, },
new Style(x => x.Select<Button>().Class(":pressed")) new Style(x => x.Select().OfType<Button>().Class(":pressed"))
{ {
Setters = new[] Setters = new[]
{ {

Loading…
Cancel
Save