diff --git a/Perspex.UnitTests/Controls/ControlTests.cs b/Perspex.UnitTests/Controls/ControlTests.cs new file mode 100644 index 0000000000..1576610485 --- /dev/null +++ b/Perspex.UnitTests/Controls/ControlTests.cs @@ -0,0 +1,105 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.UnitTests.Controls +{ + using System.Linq; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Perspex.Controls; + using Perspex.Layout; + using Perspex.Styling; + using Splat; + + [TestClass] + public class ControlTests + { + [TestMethod] + public void Classes_Should_Initially_Be_Empty() + { + var target = new Control(); + + Assert.AreEqual(0, target.Classes.Count); + } + + [TestMethod] + public void Setting_IsMouseOver_Should_Add_Mouseover_Class() + { + var target = new Control(); + + target.IsMouseOver = true; + + CollectionAssert.AreEqual(new[] { ":mouseover" }, target.Classes.ToArray()); + } + + [TestMethod] + public void Clearing_IsMouseOver_Should_Remove_Mouseover_Class() + { + var target = new Control(); + + target.IsMouseOver = true; + target.IsMouseOver = false; + + Assert.AreEqual(0, target.Classes.Count); + } + + [TestMethod] + public void Adding_Control_To_ILayoutRoot_Should_Style_Control() + { + using (Locator.CurrentMutable.WithResolver()) + { + var root = new TestRoot(); + var target = new Control(); + var styler = new Mock(); + + Locator.CurrentMutable.Register(() => styler.Object, typeof(IStyler)); + + root.Content = target; + + styler.Verify(x => x.ApplyStyles(target), Times.Once()); + } + } + + [TestMethod] + public void Adding_Tree_To_ILayoutRoot_Should_Style_Controls() + { + using (Locator.CurrentMutable.WithResolver()) + { + var root = new TestRoot(); + var parent = new Border(); + var child = new Border(); + var grandchild = new Control(); + var styler = new Mock(); + + Locator.CurrentMutable.Register(() => styler.Object, typeof(IStyler)); + + parent.Content = child; + child.Content = grandchild; + + styler.Verify(x => x.ApplyStyles(It.IsAny()), Times.Never()); + + root.Content = parent; + + styler.Verify(x => x.ApplyStyles(parent), Times.Once()); + styler.Verify(x => x.ApplyStyles(child), Times.Once()); + styler.Verify(x => x.ApplyStyles(grandchild), Times.Once()); + } + } + + private class TestRoot : Decorator, ILayoutRoot + { + public Size ClientSize + { + get { throw new System.NotImplementedException(); } + } + + public ILayoutManager LayoutManager + { + get { throw new System.NotImplementedException(); } + } + } + } +} diff --git a/Perspex.UnitTests/Perspex.UnitTests.csproj b/Perspex.UnitTests/Perspex.UnitTests.csproj index 854fcfd097..57b71a448d 100644 --- a/Perspex.UnitTests/Perspex.UnitTests.csproj +++ b/Perspex.UnitTests/Perspex.UnitTests.csproj @@ -68,6 +68,7 @@ + diff --git a/Perspex/Controls/Control.cs b/Perspex/Controls/Control.cs index a3df4f0684..e55f5e7cbd 100644 --- a/Perspex/Controls/Control.cs +++ b/Perspex/Controls/Control.cs @@ -15,6 +15,7 @@ namespace Perspex.Controls using Perspex.Layout; using Perspex.Media; using Perspex.Styling; + using Splat; public enum HorizontalAlignment { @@ -32,7 +33,7 @@ namespace Perspex.Controls Bottom, } - public abstract class Control : Interactive, ILayoutable, IStyleable + public class Control : Interactive, ILayoutable, IStyleable, IStyled { public static readonly PerspexProperty BackgroundProperty = PerspexProperty.Register("Background", inherits: true); @@ -291,26 +292,16 @@ namespace Perspex.Controls return finalSize; } - protected abstract Size MeasureContent(Size availableSize); - - protected override void AttachedToVisualTree() + protected virtual Size MeasureContent(Size availableSize) { - this.AttachStyles(this); - base.AttachedToVisualTree(); + return new Size(); } - private void AttachStyles(Control styleContainer) + protected override void AttachedToVisualTree() { - if (styleContainer != null) - { - Control parent = styleContainer.Parent; - this.AttachStyles(parent); - styleContainer.Styles.Attach(this); - } - else - { - Application.Current.Styles.Attach(this); - } + IStyler styler = Locator.Current.GetService(); + styler.ApplyStyles(this); + base.AttachedToVisualTree(); } } } diff --git a/Perspex/Perspex.csproj b/Perspex/Perspex.csproj index 864390aea9..74353bce5c 100644 --- a/Perspex/Perspex.csproj +++ b/Perspex/Perspex.csproj @@ -86,6 +86,9 @@ + + + diff --git a/Perspex/Styling/IStyled.cs b/Perspex/Styling/IStyled.cs new file mode 100644 index 0000000000..5d48b22b57 --- /dev/null +++ b/Perspex/Styling/IStyled.cs @@ -0,0 +1,15 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Styling +{ + using Perspex.Controls; + + public interface IStyled + { + Styles Styles { get; } + } +} diff --git a/Perspex/Styling/IStyler.cs b/Perspex/Styling/IStyler.cs new file mode 100644 index 0000000000..cd296a0917 --- /dev/null +++ b/Perspex/Styling/IStyler.cs @@ -0,0 +1,15 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Styling +{ + using System; + + public interface IStyler + { + void ApplyStyles(IStyleable control); + } +} diff --git a/Perspex/Styling/Styler.cs b/Perspex/Styling/Styler.cs new file mode 100644 index 0000000000..671c8fe948 --- /dev/null +++ b/Perspex/Styling/Styler.cs @@ -0,0 +1,24 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Styling +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + public class Styler : IStyler + { + public void ApplyStyles(IStyleable control) + { + IVisual visual = control as IVisual; + IStyled styleContainer = visual.GetVisualAncestorOrSelf(); + styleContainer.Styles.Attach(control); + } + } +} diff --git a/Perspex/VisualExtensions.cs b/Perspex/VisualExtensions.cs index 4bb15eb501..29e9c1ca8f 100644 --- a/Perspex/VisualExtensions.cs +++ b/Perspex/VisualExtensions.cs @@ -32,6 +32,13 @@ namespace Perspex return null; } + public static T GetVisualAncestorOrSelf(this IVisual visual) where T : class + { + Contract.Requires(visual != null); + + return (visual as T) ?? visual.GetVisualAncestor(); + } + public static IVisual GetVisualAt(this IVisual visual, Point p) { Contract.Requires(visual != null); diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs index 3f57bd0063..a70f29ef34 100644 --- a/TestApplication/Program.cs +++ b/TestApplication/Program.cs @@ -61,13 +61,13 @@ namespace TestApplication new Setter(TestBorder.BackgroundProperty, new SolidColorBrush(0xff0000ff)), } }, - //new Style(new Selector().OfType().Class(":mouseover")) - //{ - // Setters = new[] - // { - // new Setter(TestBorder.BackgroundProperty, new SolidColorBrush(0xffff0000)), - // } - //}, + new Style(new Selector().OfType().Class(":mouseover")) + { + Setters = new[] + { + new Setter(TestBorder.BackgroundProperty, new SolidColorBrush(0xffff0000)), + } + }, } },