diff --git a/Perspex.UnitTests/Perspex.UnitTests.csproj b/Perspex.UnitTests/Perspex.UnitTests.csproj index 02f45f0230..b07df6a6e8 100644 --- a/Perspex.UnitTests/Perspex.UnitTests.csproj +++ b/Perspex.UnitTests/Perspex.UnitTests.csproj @@ -35,6 +35,9 @@ 4 + + ..\packages\Splat.1.1.1\lib\Net45\Splat.dll + False diff --git a/Perspex.UnitTests/StyleTests.cs b/Perspex.UnitTests/StyleTests.cs index 3bfde8e957..bf2d80d8b3 100644 --- a/Perspex.UnitTests/StyleTests.cs +++ b/Perspex.UnitTests/StyleTests.cs @@ -15,18 +15,33 @@ namespace Perspex.UnitTests public class StyleTests { [TestMethod] - public void Style_Should_Update_And_Restore_Value() + public void Style_With_Only_Type_Selector_Should_Update_Value() { - Style style = new Style + Style style = new Style(x => x.Select()) { - Selector = x => x.Select().Class("foo"), Setters = new[] { - new Setter - { - Property = TextBlock.TextProperty, - Value = "Foo", - } + new Setter(TextBlock.TextProperty, "Foo"), + }, + }; + + TextBlock textBlock = new TextBlock + { + Text = "Original", + }; + + style.Attach(textBlock); + Assert.AreEqual("Foo", textBlock.Text); + } + + [TestMethod] + public void Style_With_Class_Selector_Should_Update_And_Restore_Value() + { + Style style = new Style(x => x.Select().Class("foo")) + { + Setters = new[] + { + new Setter(TextBlock.TextProperty, "Foo"), }, }; @@ -46,29 +61,19 @@ namespace Perspex.UnitTests [TestMethod] public void Later_Styles_Should_Override_Earlier() { - Style style1 = new Style + Style style1 = new Style(x => x.Select().Class("foo")) { - Selector = x => x.Select().Class("foo"), Setters = new[] { - new Setter - { - Property = TextBlock.TextProperty, - Value = "Foo", - } + new Setter(TextBlock.TextProperty, "Foo"), }, }; - Style style2 = new Style + Style style2 = new Style(x => x.Select().Class("foo")) { - Selector = x => x.Select().Class("foo"), Setters = new[] { - new Setter - { - Property = TextBlock.TextProperty, - Value = "Bar", - } + new Setter(TextBlock.TextProperty, "Bar"), }, }; diff --git a/Perspex.UnitTests/packages.config b/Perspex.UnitTests/packages.config index 09f179893f..45110d2bb6 100644 --- a/Perspex.UnitTests/packages.config +++ b/Perspex.UnitTests/packages.config @@ -3,4 +3,5 @@ + \ No newline at end of file diff --git a/Perspex.Windows/Perspex.Windows.csproj b/Perspex.Windows/Perspex.Windows.csproj index 104eec82fd..e8950efe2c 100644 --- a/Perspex.Windows/Perspex.Windows.csproj +++ b/Perspex.Windows/Perspex.Windows.csproj @@ -39,6 +39,9 @@ ..\packages\SharpDX.DXGI.2.5.0\lib\net40\SharpDX.DXGI.dll + + ..\packages\Splat.1.1.1\lib\Net45\Splat.dll + diff --git a/Perspex.Windows/packages.config b/Perspex.Windows/packages.config index 9bf3829fc5..b2cc097609 100644 --- a/Perspex.Windows/packages.config +++ b/Perspex.Windows/packages.config @@ -8,5 +8,6 @@ + \ No newline at end of file diff --git a/Perspex/IBindingDescription.cs b/Perspex/IBindingDescription.cs new file mode 100644 index 0000000000..3ade895491 --- /dev/null +++ b/Perspex/IBindingDescription.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex +{ + public interface IBindingDescription + { + string Description { get; } + } +} diff --git a/Perspex/Match.cs b/Perspex/Match.cs index 73661f40c5..ca858ad1aa 100644 --- a/Perspex/Match.cs +++ b/Perspex/Match.cs @@ -28,5 +28,18 @@ namespace Perspex get; set; } + + public string Token + { + get; + set; + } + + public override string ToString() + { + string result = (this.Previous != null) ? this.Previous.ToString() : string.Empty; + result += this.Token; + return result; + } } } diff --git a/Perspex/Perspex.csproj b/Perspex/Perspex.csproj index 35c78dac72..e2c4e172ca 100644 --- a/Perspex/Perspex.csproj +++ b/Perspex/Perspex.csproj @@ -76,6 +76,7 @@ + @@ -108,6 +109,9 @@ + + ..\packages\Splat.1.1.1\lib\Portable-Net45+WinRT45+WP8\Splat.dll + ..\packages\Rx-Core.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Core.dll diff --git a/Perspex/PerspexObject.cs b/Perspex/PerspexObject.cs index dc250c08b5..c9d5cc1ec3 100644 --- a/Perspex/PerspexObject.cs +++ b/Perspex/PerspexObject.cs @@ -14,6 +14,7 @@ namespace Perspex using System.Reactive; using System.Reactive.Linq; using System.Reflection; + using Splat; /// /// An object with support. @@ -21,7 +22,7 @@ namespace Perspex /// /// This class is analogous to DependencyObject in WPF. /// - public class PerspexObject + public class PerspexObject : IEnableLogger { /// /// The registered properties by type. @@ -168,6 +169,12 @@ namespace Perspex { binding.Dispose.Dispose(); this.bindings.Remove(property); + + this.Log().Debug(string.Format( + "Cleared binding on {0}.{1} (#{2:x8})", + this.GetType().Name, + property.Name, + this.GetHashCode())); } } @@ -177,6 +184,7 @@ namespace Perspex /// The property. public void ClearValue(PerspexProperty property) { + // TODO: Implement this by using SetValue(UnsetValue). Contract.Requires(property != null); this.ClearBinding(property); this.values.Remove(property); @@ -194,6 +202,13 @@ namespace Perspex if (this.bindings.TryGetValue(property, out binding)) { this.bindings.Remove(property); + + this.Log().Debug(string.Format( + "Extracted binding on {0}.{1} (#{2:x8})", + this.GetType().Name, + property.Name, + this.GetHashCode())); + return (IObservable)binding.Observable; } else @@ -214,6 +229,13 @@ namespace Perspex if (this.bindings.TryGetValue(property, out binding)) { this.bindings.Remove(property); + + this.Log().Debug(string.Format( + "Extracted binding on {0}.{1} (#{2:x8})", + this.GetType().Name, + property.Name, + this.GetHashCode())); + return (IObservable)binding.Observable; } else @@ -390,6 +412,8 @@ namespace Perspex else { IObservable observable = value as IObservable; + IBindingDescription bindingDescription = value as IBindingDescription; + string description = (bindingDescription != null) ? bindingDescription.Description : value.GetType().Name; if (observable == null) { @@ -409,6 +433,13 @@ namespace Perspex this.SetValueImpl(property, x); }), }); + + this.Log().Debug(string.Format( + "Bound {0}.{1} (#{2:x8}) to {3}", + this.GetType().Name, + property.Name, + this.GetHashCode(), + description)); } } @@ -500,6 +531,13 @@ namespace Perspex { this.values[property] = value; this.RaisePropertyChanged(property, oldValue, value); + + this.Log().Debug(string.Format( + "Set value of {0}.{1} (#{2:x8}) to '{3}'", + this.GetType().Name, + property.Name, + this.GetHashCode(), + value)); } } diff --git a/Perspex/Selectors.cs b/Perspex/Selectors.cs index a583acdf08..061146ba8d 100644 --- a/Perspex/Selectors.cs +++ b/Perspex/Selectors.cs @@ -27,6 +27,7 @@ namespace Perspex return new Match { Control = control, + Token = typeof(T).Name, }; } else @@ -50,27 +51,7 @@ namespace Perspex Control = selector.Control, Observable = match, Previous = selector, - }; - } - else - { - return null; - } - } - - public static Match PropertyEquals(this Match selector, PerspexProperty property, T value) - { - Contract.Requires(property != null); - - if (selector != null) - { - IObservable match = selector.Control.GetObservable(property).Select(x => object.Equals(x, value)); - - return new Match - { - Control = selector.Control, - Observable = match, - Previous = selector, + Token = (name[0] == ':') ? name : '.' + name, }; } else diff --git a/Perspex/Setter.cs b/Perspex/Setter.cs index 7d8f591284..872788c6d6 100644 --- a/Perspex/Setter.cs +++ b/Perspex/Setter.cs @@ -38,13 +38,13 @@ namespace Perspex set; } - internal Subject CreateSubject(Control control) + internal Subject CreateSubject(Control control, string description) { object oldValue = control.GetValue(this.Property); - return new Subject(control, this.Value, oldValue); + return new Subject(control, this.Value, oldValue, description); } - internal class Subject : IObservable + internal class Subject : IObservable, IBindingDescription { private Control control; @@ -54,12 +54,19 @@ namespace Perspex private List> observers; - public Subject(Control control, object onValue, object offValue) + public Subject(Control control, object onValue, object offValue, string description) { this.control = control; this.onValue = onValue; this.offValue = offValue; this.observers = new List>(); + this.Description = description; + } + + public string Description + { + get; + private set; } public IDisposable Subscribe(IObserver observer) diff --git a/Perspex/Style.cs b/Perspex/Style.cs index abdd9317ea..699941db0c 100644 --- a/Perspex/Style.cs +++ b/Perspex/Style.cs @@ -44,6 +44,7 @@ namespace Perspex if (match != null) { + string description = "Style " + match.ToString(); List> o = new List>(); while (match != null) @@ -60,20 +61,30 @@ namespace Perspex foreach (Setter setter in this.Setters) { - Setter.Subject subject = setter.CreateSubject(control); + Setter.Subject subject = setter.CreateSubject(control, description); subjects.Add(subject); control.SetValue(setter.Property, subject); } - Observable.CombineLatest(o).Subscribe(x => + if (o.Count == 0) { - bool on = x.All(y => y); - foreach (Setter.Subject subject in subjects) { - subject.Push(on); + subject.Push(true); } - }); + } + else + { + Observable.CombineLatest(o).Subscribe(x => + { + bool on = x.All(y => y); + + foreach (Setter.Subject subject in subjects) + { + subject.Push(on); + } + }); + } } } } diff --git a/Perspex/packages.config b/Perspex/packages.config index 69ab7f7d86..d96c5b1754 100644 --- a/Perspex/packages.config +++ b/Perspex/packages.config @@ -5,5 +5,6 @@ + \ No newline at end of file diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs index 5d72f27b00..26e7eed7cb 100644 --- a/TestApplication/Program.cs +++ b/TestApplication/Program.cs @@ -11,14 +11,32 @@ using Perspex.Media; using Perspex.Windows; using Perspex.Windows.Media; using Perspex.Windows.Threading; +using Splat; namespace TestApplication { + class TestLogger : ILogger + { + + public LogLevel Level + { + get; + set; + } + + public void Write(string message, LogLevel logLevel) + { + if ((int)logLevel < (int)Level) return; + System.Diagnostics.Debug.WriteLine(message); + } + } + class Program { static void Main(string[] args) { ServiceLocator.Register(() => new TextService(new SharpDX.DirectWrite.Factory())); + Locator.CurrentMutable.Register(() => new TestLogger(), typeof(ILogger)); Application application = new Application { @@ -30,7 +48,7 @@ namespace TestApplication { new Setter(Button.BackgroundProperty, new SolidColorBrush(0xffdddddd)), new Setter(Button.BorderBrushProperty, new SolidColorBrush(0xff707070)), - new Setter(Button.BorderThicknessProperty, 1), + new Setter(Button.BorderThicknessProperty, 2.0), new Setter(Button.ForegroundProperty, new SolidColorBrush(0xff000000)), }, }, @@ -52,8 +70,6 @@ namespace TestApplication Content = "Hello World", HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, - BorderThickness = 2, - BorderBrush = new SolidColorBrush(0xff000000), }, }; diff --git a/TestApplication/TestApplication.csproj b/TestApplication/TestApplication.csproj index ca5b64162b..84342f5b53 100644 --- a/TestApplication/TestApplication.csproj +++ b/TestApplication/TestApplication.csproj @@ -44,6 +44,9 @@ False ..\packages\SharpDX.DXGI.2.5.0\lib\net40\SharpDX.DXGI.dll + + ..\packages\Splat.1.1.1\lib\Net45\Splat.dll + diff --git a/TestApplication/packages.config b/TestApplication/packages.config index 5f137a719c..53c0a163fa 100644 --- a/TestApplication/packages.config +++ b/TestApplication/packages.config @@ -8,4 +8,5 @@ + \ No newline at end of file diff --git a/packages/Splat.1.1.1/Splat.1.1.1.nupkg b/packages/Splat.1.1.1/Splat.1.1.1.nupkg new file mode 100644 index 0000000000..2a37dbd2a1 Binary files /dev/null and b/packages/Splat.1.1.1/Splat.1.1.1.nupkg differ diff --git a/packages/Splat.1.1.1/Splat.1.1.1.nuspec b/packages/Splat.1.1.1/Splat.1.1.1.nuspec new file mode 100644 index 0000000000..1c7772c8a4 --- /dev/null +++ b/packages/Splat.1.1.1/Splat.1.1.1.nuspec @@ -0,0 +1,20 @@ + + + + Splat + 1.1.1 + Splat + Paul Betts + Paul Betts + https://github.com/xpaulbettsx/splat/blob/master/COPYING + https://github.com/xpaulbettsx/splat + http://f.cl.ly/items/1307401C3x2g3F2p2Z36/Logo.png + false + A library to make things cross-platform that should be + A library to make things cross-platform that should be + + + + portable + + \ No newline at end of file diff --git a/packages/Splat.1.1.1/lib/MonoMac/Splat.dll b/packages/Splat.1.1.1/lib/MonoMac/Splat.dll new file mode 100644 index 0000000000..64ca16e949 Binary files /dev/null and b/packages/Splat.1.1.1/lib/MonoMac/Splat.dll differ diff --git a/packages/Splat.1.1.1/lib/MonoMac/Splat.dll.mdb b/packages/Splat.1.1.1/lib/MonoMac/Splat.dll.mdb new file mode 100644 index 0000000000..435fec5443 Binary files /dev/null and b/packages/Splat.1.1.1/lib/MonoMac/Splat.dll.mdb differ diff --git a/packages/Splat.1.1.1/lib/Net45/Splat.dll b/packages/Splat.1.1.1/lib/Net45/Splat.dll new file mode 100644 index 0000000000..a0c28d1c1c Binary files /dev/null and b/packages/Splat.1.1.1/lib/Net45/Splat.dll differ diff --git a/packages/Splat.1.1.1/lib/NetCore45/Splat.dll b/packages/Splat.1.1.1/lib/NetCore45/Splat.dll new file mode 100644 index 0000000000..3850b13000 Binary files /dev/null and b/packages/Splat.1.1.1/lib/NetCore45/Splat.dll differ diff --git a/packages/Splat.1.1.1/lib/NetCore45/Splat.pri b/packages/Splat.1.1.1/lib/NetCore45/Splat.pri new file mode 100644 index 0000000000..f340032854 Binary files /dev/null and b/packages/Splat.1.1.1/lib/NetCore45/Splat.pri differ diff --git a/packages/Splat.1.1.1/lib/Portable-Net45+WinRT45+WP8/Splat.dll b/packages/Splat.1.1.1/lib/Portable-Net45+WinRT45+WP8/Splat.dll new file mode 100644 index 0000000000..a9b2c53804 Binary files /dev/null and b/packages/Splat.1.1.1/lib/Portable-Net45+WinRT45+WP8/Splat.dll differ diff --git a/packages/Splat.1.1.1/lib/Portable-Net45+WinRT45+WP8/Splat.dll.mdb b/packages/Splat.1.1.1/lib/Portable-Net45+WinRT45+WP8/Splat.dll.mdb new file mode 100644 index 0000000000..435fec5443 Binary files /dev/null and b/packages/Splat.1.1.1/lib/Portable-Net45+WinRT45+WP8/Splat.dll.mdb differ diff --git a/packages/Splat.1.1.1/lib/monoandroid/Splat.dll b/packages/Splat.1.1.1/lib/monoandroid/Splat.dll new file mode 100644 index 0000000000..64ca16e949 Binary files /dev/null and b/packages/Splat.1.1.1/lib/monoandroid/Splat.dll differ diff --git a/packages/Splat.1.1.1/lib/monoandroid/Splat.dll.mdb b/packages/Splat.1.1.1/lib/monoandroid/Splat.dll.mdb new file mode 100644 index 0000000000..435fec5443 Binary files /dev/null and b/packages/Splat.1.1.1/lib/monoandroid/Splat.dll.mdb differ diff --git a/packages/Splat.1.1.1/lib/monotouch/Splat.dll b/packages/Splat.1.1.1/lib/monotouch/Splat.dll new file mode 100644 index 0000000000..63b6702145 Binary files /dev/null and b/packages/Splat.1.1.1/lib/monotouch/Splat.dll differ diff --git a/packages/Splat.1.1.1/lib/monotouch/Splat.dll.mdb b/packages/Splat.1.1.1/lib/monotouch/Splat.dll.mdb new file mode 100644 index 0000000000..391eae9033 Binary files /dev/null and b/packages/Splat.1.1.1/lib/monotouch/Splat.dll.mdb differ diff --git a/packages/Splat.1.1.1/lib/wp8/Splat.dll b/packages/Splat.1.1.1/lib/wp8/Splat.dll new file mode 100644 index 0000000000..055f947b2d Binary files /dev/null and b/packages/Splat.1.1.1/lib/wp8/Splat.dll differ