Browse Source

Merge branch 'master' of https://github.com/Perspex/Perspex into skiasharp

pull/506/head
Jason Jarvis 11 years ago
parent
commit
727dd45887
  1. 27
      Perspex.sln
  2. 3
      src/Markup/Perspex.Markup.Xaml/Data/Binding.cs
  3. 2
      src/Markup/Perspex.Markup.Xaml/Parsers/SelectorParser.cs
  4. 6
      src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs
  5. 5
      src/Markup/Perspex.Markup/Data/ExpressionNode.cs
  6. 6
      src/Markup/Perspex.Markup/Data/ExpressionObserver.cs
  7. 9
      src/Markup/Perspex.Markup/Data/ExpressionSubject.cs
  8. 3
      src/Markup/Perspex.Markup/Data/LogicalNotNode.cs
  9. 6
      src/Markup/Perspex.Markup/Data/Plugins/IPropertyAccessor.cs
  10. 3
      src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs
  11. 5
      src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs
  12. 7
      src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs
  13. 14
      src/Perspex.Base/PerspexProperty.cs
  14. 54
      src/Perspex.Base/PerspexPropertyRegistry.cs
  15. 9
      src/Perspex.Controls/Decorator.cs
  16. 4
      src/Perspex.Controls/Panel.cs
  17. 188
      src/Perspex.Controls/Presenters/ContentPresenter.cs
  18. 14
      src/Perspex.Layout/Layoutable.cs
  19. 17
      src/Perspex.SceneGraph/Visual.cs
  20. 11
      src/Perspex.SceneGraph/VisualTree/IVisual.cs
  21. 10
      src/Perspex.SceneGraph/VisualTree/VisualExtensions.cs
  22. 5
      src/Perspex.Styling/Perspex.Styling.csproj
  23. 56
      src/Perspex.Styling/Styling/ChildSelector.cs
  24. 80
      src/Perspex.Styling/Styling/DescendentSelector.cs
  25. 91
      src/Perspex.Styling/Styling/PropertyEqualsSelector.cs
  26. 135
      src/Perspex.Styling/Styling/Selector.cs
  27. 6
      src/Perspex.Styling/Styling/SelectorMatch.cs
  28. 167
      src/Perspex.Styling/Styling/Selectors.cs
  29. 2
      src/Perspex.Styling/Styling/Style.cs
  30. 54
      src/Perspex.Styling/Styling/TemplateSelector.cs
  31. 207
      src/Perspex.Styling/Styling/TypeNameAndClassSelector.cs
  32. 24
      src/Perspex.Themes.Default/Button.xaml
  33. 13
      src/Perspex.Themes.Default/ContentControl.xaml
  34. 18
      src/Perspex.Themes.Default/DropDownItem.xaml
  35. 18
      src/Perspex.Themes.Default/ListBoxItem.xaml
  36. 9
      src/Perspex.Themes.Default/PopupRoot.xaml
  37. 11
      src/Perspex.Themes.Default/TabStripItem.xaml
  38. 28
      src/Perspex.Themes.Default/ToggleButton.xaml
  39. 13
      src/Perspex.Themes.Default/ToolTip.xaml
  40. 24
      src/Perspex.Themes.Default/TreeViewItem.xaml
  41. 14
      tests/Perspex.Benchmarks/App.config
  42. 130
      tests/Perspex.Benchmarks/Perspex.Benchmarks.csproj
  43. 28
      tests/Perspex.Benchmarks/Program.cs
  44. 36
      tests/Perspex.Benchmarks/Properties/AssemblyInfo.cs
  45. 68
      tests/Perspex.Benchmarks/Styling/ApplyStyling.cs
  46. 7
      tests/Perspex.Benchmarks/packages.config
  47. 1
      tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj
  48. 11
      tests/Perspex.Markup.UnitTests/app.config
  49. 16
      tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs
  50. 16
      tests/Perspex.Styling.UnitTests/SelectorTests_Class.cs
  51. 18
      tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs
  52. 14
      tests/Perspex.Styling.UnitTests/SelectorTests_Multiple.cs
  53. 22
      tests/Perspex.Styling.UnitTests/SelectorTests_Name.cs
  54. 6
      tests/Perspex.Styling.UnitTests/SelectorTests_OfType.cs
  55. 12
      tests/Perspex.Styling.UnitTests/SelectorTests_PropertyEquals.cs
  56. 32
      tests/Perspex.Styling.UnitTests/SelectorTests_Template.cs
  57. 10
      tests/Perspex.Styling.UnitTests/TestSelectors.cs
  58. 4
      tests/Perspex.Styling.UnitTests/app.config
  59. 8
      tests/Perspex.UnitTests/Perspex.UnitTests.csproj
  60. 22
      tests/Perspex.UnitTests/TestServices.cs
  61. 4
      tests/Perspex.UnitTests/packages.config

27
Perspex.sln

@ -140,6 +140,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog", "samples\C
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.UnitTests", "tests\Perspex.UnitTests\Perspex.UnitTests.csproj", "{88060192-33D5-4932-B0F9-8BD2763E857D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Benchmarks", "tests\Perspex.Benchmarks\Perspex.Benchmarks.csproj", "{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
@ -1295,6 +1297,30 @@ Global
{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.Build.0 = Release|Any CPU
{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|Any CPU.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhone.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhone.Build.0 = Debug|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|Any CPU.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhone.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhone.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1339,5 +1365,6 @@ Global
{E1AA3DBF-9056-4530-9376-18119A7A3FFE} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{61BEC86C-F307-4295-B5B8-9428610D7D55} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{88060192-33D5-4932-B0F9-8BD2763E857D} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection
EndGlobal

3
src/Markup/Perspex.Markup.Xaml/Data/Binding.cs

@ -107,7 +107,8 @@ namespace Perspex.Markup.Xaml.Data
targetProperty?.PropertyType ?? typeof(object),
Converter ?? DefaultValueConverter.Instance,
ConverterParameter,
FallbackValue);
FallbackValue,
Priority);
return new InstancedBinding(subject, Mode, Priority);
}

2
src/Markup/Perspex.Markup.Xaml/Parsers/SelectorParser.cs

@ -37,7 +37,7 @@ namespace Perspex.Markup.Xaml.Parsers
public Selector Parse(string s)
{
var syntax = SelectorGrammar.Selector.Parse(s);
var result = new Selector();
var result = default(Selector);
foreach (var i in syntax)
{

6
src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs

@ -152,11 +152,11 @@ namespace Perspex.Markup.Xaml
InstanceLifeCycleListener = s_lifeCycleListener,
});
var control = result as IControl;
var topLevel = result as TopLevel;
if (control != null)
if (topLevel != null)
{
DelayedBinding.ApplyBindings(control);
DelayedBinding.ApplyBindings(topLevel);
}
return result;

5
src/Markup/Perspex.Markup/Data/ExpressionNode.cs

@ -3,6 +3,7 @@
using System;
using System.Reactive.Subjects;
using Perspex.Data;
namespace Perspex.Markup.Data
{
@ -76,9 +77,9 @@ namespace Perspex.Markup.Data
}
}
public virtual bool SetValue(object value)
public virtual bool SetValue(object value, BindingPriority priority)
{
return Next?.SetValue(value) ?? false;
return Next?.SetValue(value, priority) ?? false;
}
public virtual IDisposable Subscribe(IObserver<object> observer)

6
src/Markup/Perspex.Markup/Data/ExpressionObserver.cs

@ -7,6 +7,7 @@ using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Data;
using Perspex.Markup.Data.Plugins;
namespace Perspex.Markup.Data
@ -105,11 +106,12 @@ namespace Perspex.Markup.Data
/// Attempts to set the value of a property expression.
/// </summary>
/// <param name="value">The value to set.</param>
/// <param name="priority">The binding priority to use.</param>
/// <returns>
/// True if the value could be set; false if the expression does not evaluate to a
/// property.
/// </returns>
public bool SetValue(object value)
public bool SetValue(object value, BindingPriority priority = BindingPriority.LocalValue)
{
IncrementCount();
@ -120,7 +122,7 @@ namespace Perspex.Markup.Data
try
{
return _node?.SetValue(value) ?? false;
return _node?.SetValue(value, priority) ?? false;
}
finally
{

9
src/Markup/Perspex.Markup/Data/ExpressionSubject.cs

@ -5,6 +5,7 @@ using System;
using System.Globalization;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Data;
using Perspex.Utilities;
namespace Perspex.Markup.Data
@ -18,6 +19,7 @@ namespace Perspex.Markup.Data
private readonly ExpressionObserver _inner;
private readonly Type _targetType;
private readonly object _fallbackValue;
private readonly BindingPriority _priority;
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
@ -41,12 +43,14 @@ namespace Perspex.Markup.Data
/// <param name="fallbackValue">
/// The value to use when the binding is unable to produce a value.
/// </param>
/// <param name="priority">The binding priority.</param>
public ExpressionSubject(
ExpressionObserver inner,
Type targetType,
IValueConverter converter,
object converterParameter = null,
object fallbackValue = null)
object fallbackValue = null,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(inner != null);
Contract.Requires<ArgumentNullException>(targetType != null);
@ -57,6 +61,7 @@ namespace Perspex.Markup.Data
Converter = converter;
ConverterParameter = converterParameter;
_fallbackValue = fallbackValue;
_priority = priority;
}
/// <summary>
@ -100,7 +105,7 @@ namespace Perspex.Markup.Data
converted = TypeUtilities.Default(type);
}
_inner.SetValue(converted);
_inner.SetValue(converted, _priority);
}
}

3
src/Markup/Perspex.Markup/Data/LogicalNotNode.cs

@ -4,12 +4,13 @@
using System;
using System.Globalization;
using System.Reactive.Linq;
using Perspex.Data;
namespace Perspex.Markup.Data
{
internal class LogicalNotNode : ExpressionNode
{
public override bool SetValue(object value)
public override bool SetValue(object value, BindingPriority priority)
{
return false;
}

6
src/Markup/Perspex.Markup/Data/Plugins/IPropertyAccessor.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Data;
namespace Perspex.Markup.Data.Plugins
{
@ -27,9 +28,12 @@ namespace Perspex.Markup.Data.Plugins
/// <param name="value">
/// The value to set. Guaranteed to be of a valid type for the property.
/// </param>
/// <param name="priority">
/// The priority with which to set the value.
/// </param>
/// <returns>
/// True if the property was set; false if the property could not be set.
/// </returns>
bool SetValue(object value);
bool SetValue(object value, BindingPriority priority);
}
}

3
src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs

@ -6,6 +6,7 @@ using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
using Perspex.Data;
using Perspex.Utilities;
namespace Perspex.Markup.Data.Plugins
@ -106,7 +107,7 @@ namespace Perspex.Markup.Data.Plugins
}
}
public bool SetValue(object value)
public bool SetValue(object value, BindingPriority priority)
{
if (_property.CanWrite)
{

5
src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs

@ -3,6 +3,7 @@
using System;
using System.Reactive.Linq;
using Perspex.Data;
namespace Perspex.Markup.Data.Plugins
{
@ -95,11 +96,11 @@ namespace Perspex.Markup.Data.Plugins
_subscription = null;
}
public bool SetValue(object value)
public bool SetValue(object value, BindingPriority priority)
{
if (!_property.IsReadOnly)
{
Instance.SetValue(_property, value);
Instance.SetValue(_property, value, priority);
return true;
}

7
src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs

@ -8,6 +8,7 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Perspex.Data;
using Perspex.Markup.Data.Plugins;
namespace Perspex.Markup.Data
@ -26,17 +27,17 @@ namespace Perspex.Markup.Data
public Type PropertyType => _accessor?.PropertyType;
public override bool SetValue(object value)
public override bool SetValue(object value, BindingPriority priority)
{
if (Next != null)
{
return Next.SetValue(value);
return Next.SetValue(value, priority);
}
else
{
if (_accessor != null)
{
return _accessor.SetValue(value);
return _accessor.SetValue(value, priority);
}
return false;

14
src/Perspex.Base/PerspexProperty.cs

@ -22,7 +22,6 @@ namespace Perspex
public static readonly object UnsetValue = new Unset();
private static int s_nextId = 1;
private readonly int _id;
private readonly Subject<PerspexPropertyChangedEventArgs> _initialized;
private readonly Subject<PerspexPropertyChangedEventArgs> _changed;
private readonly PropertyMetadata _defaultMetadata;
@ -61,7 +60,7 @@ namespace Perspex
PropertyType = valueType;
OwnerType = ownerType;
Notifying = notifying;
_id = s_nextId++;
Id = s_nextId++;
_metadata.Add(ownerType, metadata);
_defaultMetadata = metadata;
@ -85,7 +84,7 @@ namespace Perspex
PropertyType = source.PropertyType;
OwnerType = ownerType;
Notifying = source.Notifying;
_id = source._id;
Id = source.Id;
_defaultMetadata = source._defaultMetadata;
}
@ -164,6 +163,11 @@ namespace Perspex
/// </remarks>
public Action<IPerspexObject, bool> Notifying { get; }
/// <summary>
/// Gets the integer ID that represents this property.
/// </summary>
internal int Id { get; }
/// <summary>
/// Provides access to a property's binding via the <see cref="PerspexObject"/>
/// indexer.
@ -392,13 +396,13 @@ namespace Perspex
/// <inheritdoc/>
public bool Equals(PerspexProperty other)
{
return other != null && _id == other._id;
return other != null && Id == other.Id;
}
/// <inheritdoc/>
public override int GetHashCode()
{
return _id;
return Id;
}
/// <summary>

54
src/Perspex.Base/PerspexPropertyRegistry.cs

@ -17,14 +17,14 @@ namespace Perspex
/// <summary>
/// The registered properties by type.
/// </summary>
private readonly Dictionary<Type, List<PerspexProperty>> _registered =
new Dictionary<Type, List<PerspexProperty>>();
private readonly Dictionary<Type, Dictionary<int, PerspexProperty>> _registered =
new Dictionary<Type, Dictionary<int, PerspexProperty>>();
/// <summary>
/// The registered attached properties by owner type.
/// </summary>
private readonly Dictionary<Type, List<PerspexProperty>> _attached =
new Dictionary<Type, List<PerspexProperty>>();
private readonly Dictionary<Type, Dictionary<int, PerspexProperty>> _attached =
new Dictionary<Type, Dictionary<int, PerspexProperty>>();
/// <summary>
/// Gets the <see cref="PerspexPropertyRegistry"/> instance
@ -39,11 +39,11 @@ namespace Perspex
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
public IEnumerable<PerspexProperty> GetAttached(Type ownerType)
{
List<PerspexProperty> list;
Dictionary<int, PerspexProperty> inner;
if (_attached.TryGetValue(ownerType, out list))
if (_attached.TryGetValue(ownerType, out inner))
{
return list;
return inner.Values;
}
return Enumerable.Empty<PerspexProperty>();
@ -65,13 +65,13 @@ namespace Perspex
// Ensure the type's static ctor has been run.
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
List<PerspexProperty> list;
Dictionary<int, PerspexProperty> inner;
if (_registered.TryGetValue(type, out list))
if (_registered.TryGetValue(type, out inner))
{
foreach (PerspexProperty p in list)
foreach (var p in inner)
{
yield return p;
yield return p.Value;
}
}
@ -105,15 +105,15 @@ namespace Perspex
{
while (type != null)
{
List<PerspexProperty> list;
Dictionary<int, PerspexProperty> inner;
if (_registered.TryGetValue(type, out list))
if (_registered.TryGetValue(type, out inner))
{
var index = list.IndexOf(property);
PerspexProperty result;
if (index != -1)
if (inner.TryGetValue(property.Id, out result))
{
return list[index];
return result;
}
}
@ -250,30 +250,30 @@ namespace Perspex
Contract.Requires<ArgumentNullException>(type != null);
Contract.Requires<ArgumentNullException>(property != null);
List<PerspexProperty> list;
Dictionary<int, PerspexProperty> inner;
if (!_registered.TryGetValue(type, out list))
if (!_registered.TryGetValue(type, out inner))
{
list = new List<PerspexProperty>();
_registered.Add(type, list);
inner = new Dictionary<int, PerspexProperty>();
_registered.Add(type, inner);
}
if (!list.Contains(property))
if (!inner.ContainsKey(property.Id))
{
list.Add(property);
inner.Add(property.Id, property);
}
if (property.IsAttached)
{
if (!_attached.TryGetValue(property.OwnerType, out list))
if (!_attached.TryGetValue(property.OwnerType, out inner))
{
list = new List<PerspexProperty>();
_attached.Add(property.OwnerType, list);
inner = new Dictionary<int, PerspexProperty>();
_attached.Add(property.OwnerType, inner);
}
if (!list.Contains(property))
if (!inner.ContainsKey(property.Id))
{
list.Add(property);
inner.Add(property.Id, property);
}
}
}

9
src/Perspex.Controls/Decorator.cs

@ -13,8 +13,8 @@ namespace Perspex.Controls
/// <summary>
/// Defines the <see cref="Child"/> property.
/// </summary>
public static readonly StyledProperty<Control> ChildProperty =
PerspexProperty.Register<Decorator, Control>(nameof(Child));
public static readonly StyledProperty<IControl> ChildProperty =
PerspexProperty.Register<Decorator, IControl>(nameof(Child));
/// <summary>
/// Defines the <see cref="Padding"/> property.
@ -35,7 +35,7 @@ namespace Perspex.Controls
/// Gets or sets the decorated control.
/// </summary>
[Content]
public Control Child
public IControl Child
{
get { return GetValue(ChildProperty); }
set { SetValue(ChildProperty, value); }
@ -70,8 +70,7 @@ namespace Perspex.Controls
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
Control content = Child;
content?.Arrange(new Rect(finalSize).Deflate(Padding));
Child?.Arrange(new Rect(finalSize).Deflate(Padding));
return finalSize;
}

4
src/Perspex.Controls/Panel.cs

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Perspex.LogicalTree;
using Perspex.Media;
using Perspex.Metadata;
@ -28,8 +27,6 @@ namespace Perspex.Controls
private readonly Controls _children = new Controls();
private ILogical _childLogicalParent;
/// <summary>
/// Initializes static members of the <see cref="Panel"/> class.
/// </summary>
@ -44,7 +41,6 @@ namespace Perspex.Controls
public Panel()
{
_children.CollectionChanged += ChildrenChanged;
_childLogicalParent = this;
}
/// <summary>

188
src/Perspex.Controls/Presenters/ContentPresenter.cs

@ -1,9 +1,12 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Layout;
using Perspex.LogicalTree;
using Perspex.Media;
namespace Perspex.Controls.Presenters
{
@ -12,6 +15,24 @@ namespace Perspex.Controls.Presenters
/// </summary>
public class ContentPresenter : Control, IContentPresenter
{
/// <summary>
/// Defines the <see cref="Background"/> property.
/// </summary>
public static readonly StyledProperty<Brush> BackgroundProperty =
Border.BackgroundProperty.AddOwner<ContentPresenter>();
/// <summary>
/// Defines the <see cref="BorderBrush"/> property.
/// </summary>
public static readonly PerspexProperty<Brush> BorderBrushProperty =
Border.BorderBrushProperty.AddOwner<ContentPresenter>();
/// <summary>
/// Defines the <see cref="BorderThickness"/> property.
/// </summary>
public static readonly StyledProperty<double> BorderThicknessProperty =
Border.BorderThicknessProperty.AddOwner<ContentPresenter>();
/// <summary>
/// Defines the <see cref="Child"/> property.
/// </summary>
@ -26,6 +47,30 @@ namespace Perspex.Controls.Presenters
public static readonly StyledProperty<object> ContentProperty =
ContentControl.ContentProperty.AddOwner<ContentPresenter>();
/// <summary>
/// Defines the <see cref="CornerRadius"/> property.
/// </summary>
public static readonly StyledProperty<float> CornerRadiusProperty =
Border.CornerRadiusProperty.AddOwner<ContentPresenter>();
/// <summary>
/// Defines the <see cref="HorizontalContentAlignment"/> property.
/// </summary>
public static readonly StyledProperty<HorizontalAlignment> HorizontalContentAlignmentProperty =
ContentControl.HorizontalContentAlignmentProperty.AddOwner<ContentPresenter>();
/// <summary>
/// Defines the <see cref="VerticalContentAlignment"/> property.
/// </summary>
public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner<ContentPresenter>();
/// <summary>
/// Defines the <see cref="Padding"/> property.
/// </summary>
public static readonly StyledProperty<Thickness> PaddingProperty =
Border.PaddingProperty.AddOwner<ContentPresenter>();
private IControl _child;
private bool _createdChild;
@ -38,6 +83,33 @@ namespace Perspex.Controls.Presenters
TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.TemplatedParentChanged);
}
/// <summary>
/// Gets or sets a brush with which to paint the background.
/// </summary>
public Brush Background
{
get { return GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
/// <summary>
/// Gets or sets a brush with which to paint the border.
/// </summary>
public Brush BorderBrush
{
get { return GetValue(BorderBrushProperty); }
set { SetValue(BorderBrushProperty, value); }
}
/// <summary>
/// Gets or sets the thickness of the border.
/// </summary>
public double BorderThickness
{
get { return GetValue(BorderThicknessProperty); }
set { SetValue(BorderThicknessProperty, value); }
}
/// <summary>
/// Gets the control displayed by the presenter.
/// </summary>
@ -56,6 +128,42 @@ namespace Perspex.Controls.Presenters
set { SetValue(ContentProperty, value); }
}
/// <summary>
/// Gets or sets the radius of the border rounded corners.
/// </summary>
public float CornerRadius
{
get { return GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
/// <summary>
/// Gets or sets the horizontal alignment of the content within the control.
/// </summary>
public HorizontalAlignment HorizontalContentAlignment
{
get { return GetValue(HorizontalContentAlignmentProperty); }
set { SetValue(HorizontalContentAlignmentProperty, value); }
}
/// <summary>
/// Gets or sets the vertical alignment of the content within the control.
/// </summary>
public VerticalAlignment VerticalContentAlignment
{
get { return GetValue(VerticalContentAlignmentProperty); }
set { SetValue(VerticalContentAlignmentProperty, value); }
}
/// <summary>
/// Gets or sets the padding to place around the <see cref="Child"/> control.
/// </summary>
public Thickness Padding
{
get { return GetValue(PaddingProperty); }
set { SetValue(PaddingProperty, value); }
}
/// <inheritdoc/>
public override sealed void ApplyTemplate()
{
@ -120,23 +228,93 @@ namespace Perspex.Controls.Presenters
}
/// <inheritdoc/>
protected override Size MeasureCore(Size availableSize)
public override void Render(DrawingContext context)
{
return base.MeasureCore(availableSize);
var background = Background;
var borderBrush = BorderBrush;
var borderThickness = BorderThickness;
var cornerRadius = CornerRadius;
var rect = new Rect(Bounds.Size).Deflate(BorderThickness);
if (background != null)
{
context.FillRectangle(background, rect, cornerRadius);
}
if (borderBrush != null && borderThickness > 0)
{
context.DrawRectangle(new Pen(borderBrush, borderThickness), rect, cornerRadius);
}
}
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
var child = Child;
var padding = Padding + new Thickness(BorderThickness);
if (child != null)
{
child.Measure(availableSize);
return child.DesiredSize;
child.Measure(availableSize.Deflate(padding));
return child.DesiredSize.Inflate(padding);
}
else
{
return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top);
}
}
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
var child = Child;
if (child != null)
{
var padding = Padding + new Thickness(BorderThickness);
var sizeMinusPadding = finalSize.Deflate(padding);
var size = sizeMinusPadding;
var horizontalAlignment = HorizontalContentAlignment;
var verticalAlignment = VerticalContentAlignment;
var originX = padding.Left;
var originY = padding.Top;
if (horizontalAlignment != HorizontalAlignment.Stretch)
{
size = size.WithWidth(child.DesiredSize.Width);
}
if (verticalAlignment != VerticalAlignment.Stretch)
{
size = size.WithHeight(child.DesiredSize.Height);
}
switch (horizontalAlignment)
{
case HorizontalAlignment.Stretch:
case HorizontalAlignment.Center:
originX += (sizeMinusPadding.Width - size.Width) / 2;
break;
case HorizontalAlignment.Right:
originX = size.Width - child.DesiredSize.Width;
break;
}
switch (verticalAlignment)
{
case VerticalAlignment.Stretch:
case VerticalAlignment.Center:
originY += (sizeMinusPadding.Height - size.Height) / 2;
break;
case VerticalAlignment.Bottom:
originY = size.Height - child.DesiredSize.Height;
break;
}
child.Arrange(new Rect(originX, originY, size.Width, size.Height));
}
return new Size();
return finalSize;
}
/// <summary>

14
src/Perspex.Layout/Layoutable.cs

@ -536,20 +536,22 @@ namespace Perspex.Layout
{
if (IsVisible)
{
double originX = finalRect.X + Margin.Left;
double originY = finalRect.Y + Margin.Top;
var originX = finalRect.X + Margin.Left;
var originY = finalRect.Y + Margin.Top;
var sizeMinusMargins = new Size(
Math.Max(0, finalRect.Width - Margin.Left - Margin.Right),
Math.Max(0, finalRect.Height - Margin.Top - Margin.Bottom));
var horizontalAlignment = HorizontalAlignment;
var verticalAlignment = VerticalAlignment;
var size = sizeMinusMargins;
var scale = GetLayoutScale();
if (HorizontalAlignment != HorizontalAlignment.Stretch)
if (horizontalAlignment != HorizontalAlignment.Stretch)
{
size = size.WithWidth(Math.Min(size.Width, DesiredSize.Width));
}
if (VerticalAlignment != VerticalAlignment.Stretch)
if (verticalAlignment != VerticalAlignment.Stretch)
{
size = size.WithHeight(Math.Min(size.Height, DesiredSize.Height));
}
@ -568,7 +570,7 @@ namespace Perspex.Layout
size = ArrangeOverride(size).Constrain(size);
switch (HorizontalAlignment)
switch (horizontalAlignment)
{
case HorizontalAlignment.Center:
case HorizontalAlignment.Stretch:
@ -579,7 +581,7 @@ namespace Perspex.Layout
break;
}
switch (VerticalAlignment)
switch (verticalAlignment)
{
case VerticalAlignment.Center:
case VerticalAlignment.Stretch:

17
src/Perspex.SceneGraph/Visual.cs

@ -77,19 +77,8 @@ namespace Perspex
public static readonly StyledProperty<int> ZIndexProperty =
PerspexProperty.Register<Visual, int>(nameof(ZIndex));
/// <summary>
/// The visual's bounds relative to its parent.
/// </summary>
private Rect _bounds;
/// <summary>
/// Holds the parent of the visual.
/// </summary>
private IVisual _visualParent;
/// <summary>
/// The logger for visual-level events.
/// </summary>
private readonly ILogger _visualLogger;
/// <summary>
@ -239,6 +228,11 @@ namespace Perspex
/// </summary>
IVisual IVisual.VisualParent => _visualParent;
/// <summary>
/// Gets the root of the visual tree, if the control is attached to a visual tree.
/// </summary>
IRenderRoot IVisual.VisualRoot { get; }
/// <summary>
/// Invalidates the visual and queues a repaint.
/// </summary>
@ -483,7 +477,6 @@ namespace Perspex
_visualLogger.Verbose("Attached to visual tree");
VisualRoot = e.Root;
OnAttachedToVisualTree(e);
if (VisualChildren != null)

11
src/Perspex.SceneGraph/VisualTree/IVisual.cs

@ -4,6 +4,7 @@
using System;
using Perspex.Collections;
using Perspex.Media;
using Perspex.Rendering;
namespace Perspex.VisualTree
{
@ -80,11 +81,21 @@ namespace Perspex.VisualTree
/// </summary>
IVisual VisualParent { get; }
/// <summary>
/// Gets the root of the visual tree, if the control is attached to a visual tree.
/// </summary>
IRenderRoot VisualRoot { get; }
/// <summary>
/// Gets or sets the Z index of the node.
/// </summary>
int ZIndex { get; set; }
/// <summary>
/// Invalidates the visual and queues a repaint.
/// </summary>
void InvalidateVisual();
/// <summary>
/// Renders the scene graph node to a <see cref="DrawingContext"/>.
/// </summary>

10
src/Perspex.SceneGraph/VisualTree/VisualExtensions.cs

@ -176,15 +176,7 @@ namespace Perspex.VisualTree
{
Contract.Requires<ArgumentNullException>(visual != null);
var parent = visual.VisualParent;
while (parent != null)
{
visual = parent;
parent = visual.VisualParent;
}
return visual;
return visual.VisualRoot as IVisual;
}
/// <summary>

5
src/Perspex.Styling/Perspex.Styling.csproj

@ -49,6 +49,9 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Styling\ActivatedSubject.cs" />
<Compile Include="Styling\ActivatedValue.cs" />
<Compile Include="Styling\TemplateSelector.cs" />
<Compile Include="Styling\DescendentSelector.cs" />
<Compile Include="Styling\ChildSelector.cs" />
<Compile Include="Styling\IGlobalStyles.cs" />
<Compile Include="Styling\ISetter.cs" />
<Compile Include="Styling\IStyle.cs" />
@ -67,6 +70,8 @@
<Compile Include="Styling\StyleExtensions.cs" />
<Compile Include="Styling\Styler.cs" />
<Compile Include="Styling\Styles.cs" />
<Compile Include="Styling\PropertyEqualsSelector.cs" />
<Compile Include="Styling\TypeNameAndClassSelector.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Reactive.Core">

56
src/Perspex.Styling/Styling/ChildSelector.cs

@ -0,0 +1,56 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.LogicalTree;
namespace Perspex.Styling
{
internal class ChildSelector : Selector
{
private readonly Selector _parent;
private string _selectorString;
public ChildSelector(Selector parent)
{
if (parent == null)
{
throw new InvalidOperationException("Child selector must be preceeded by a selector.");
}
_parent = parent;
}
/// <inheritdoc/>
public override bool InTemplate => _parent.InTemplate;
/// <inheritdoc/>
public override Type TargetType => null;
public override string ToString()
{
if (_selectorString == null)
{
_selectorString = _parent.ToString() + " > ";
}
return _selectorString;
}
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
var controlParent = ((ILogical)control).LogicalParent;
if (controlParent != null)
{
return _parent.Match((IStyleable)controlParent, subscribe);
}
else
{
return SelectorMatch.False;
}
}
protected override Selector MovePrevious() => null;
}
}

80
src/Perspex.Styling/Styling/DescendentSelector.cs

@ -0,0 +1,80 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Perspex.LogicalTree;
namespace Perspex.Styling
{
internal class DescendentSelector : Selector
{
private readonly Selector _parent;
private string _selectorString;
public DescendentSelector(Selector parent)
{
if (parent == null)
{
throw new InvalidOperationException("Descendent selector must be preceeded by a selector.");
}
_parent = parent;
}
/// <inheritdoc/>
public override bool InTemplate => _parent.InTemplate;
/// <inheritdoc/>
public override Type TargetType => null;
public override string ToString()
{
if (_selectorString == null)
{
_selectorString = _parent.ToString() + ' ';
}
return _selectorString;
}
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
ILogical c = (ILogical)control;
List<IObservable<bool>> descendentMatches = new List<IObservable<bool>>();
while (c != null)
{
c = c.LogicalParent;
if (c is IStyleable)
{
var match = _parent.Match((IStyleable)c, subscribe);
if (match.ImmediateResult != null)
{
if (match.ImmediateResult == true)
{
return SelectorMatch.True;
}
}
else
{
descendentMatches.Add(match.ObservableResult);
}
}
}
if (descendentMatches.Count > 0)
{
return new SelectorMatch(StyleActivator.Or(descendentMatches));
}
else
{
return SelectorMatch.False;
}
}
protected override Selector MovePrevious() => null;
}
}

91
src/Perspex.Styling/Styling/PropertyEqualsSelector.cs

@ -0,0 +1,91 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive.Linq;
using System.Text;
namespace Perspex.Styling
{
/// <summary>
/// A selector that matches the common case of a type and/or name followed by a collection of
/// style classes and pseudoclasses.
/// </summary>
internal class PropertyEqualsSelector : Selector
{
private readonly Selector _previous;
private readonly PerspexProperty _property;
private readonly object _value;
private string _selectorString;
public PropertyEqualsSelector(Selector previous, PerspexProperty property, object value)
{
Contract.Requires<ArgumentNullException>(property != null);
_previous = previous;
_property = property;
_value = value;
}
/// <inheritdoc/>
public override bool InTemplate => _previous?.InTemplate ?? false;
/// <summary>
/// Gets the name of the control to match.
/// </summary>
public string Name { get; private set; }
/// <inheritdoc/>
public override Type TargetType => _previous?.TargetType;
/// <inheritdoc/>
public override string ToString()
{
if (_selectorString == null)
{
var builder = new StringBuilder();
if (_previous != null)
{
builder.Append(_previous.ToString());
}
builder.Append('[');
if (_property.IsAttached)
{
builder.Append(_property.OwnerType.Name);
builder.Append('.');
}
builder.Append(_property.Name);
builder.Append('=');
builder.Append(_value);
builder.Append(']');
_selectorString = builder.ToString();
}
return _selectorString;
}
/// <inheritdoc/>
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
if (!PerspexPropertyRegistry.Instance.IsRegistered(control, _property))
{
return SelectorMatch.False;
}
else if (subscribe)
{
return new SelectorMatch(control.GetObservable(_property).Select(v => Equals(v, _value)));
}
else
{
return new SelectorMatch(control.GetValue(_property).Equals(_value));
}
}
protected override Selector MovePrevious() => _previous;
}
}

135
src/Perspex.Styling/Styling/Selector.cs

@ -9,123 +9,41 @@ namespace Perspex.Styling
/// <summary>
/// A selector in a <see cref="Style"/>.
/// </summary>
/// <remarks>
/// Selectors represented in markup using a CSS-like syntax, e.g. "Button &gt; .dark" which
/// means "A child of a Button with the 'dark' class applied. The preceeding example would be
/// stored in 3 <see cref="Selector"/> objects, linked by the <see cref="Previous"/> property:
/// <list type="number">
/// <item>
/// <term>.dark</term>
/// <description>
/// A selector that selects a control with the 'dark' class applied.
/// </description>
/// </item>
/// <item>
/// <term>&gt;</term>
/// <description>
/// A selector that selects a child of the previous selector.
/// </description>
/// </item>
/// <item>
/// <term>Button</term>
/// <description>
/// A selector that selects a Button type.
/// </description>
/// </item>
/// </list>
/// </remarks>
public class Selector
public abstract class Selector
{
private readonly Func<IStyleable, SelectorMatch> _evaluate;
private readonly Type _targetType;
private readonly bool _inTemplate;
private readonly bool _stopTraversal;
private string _description;
/// <summary>
/// Initializes a new instance of the <see cref="Selector"/> class.
/// </summary>
public Selector()
{
_evaluate = _ => SelectorMatch.True;
}
/// <summary>
/// Initializes a new instance of the <see cref="Selector"/> class.
/// </summary>
/// <param name="previous">The previous selector.</param>
/// <param name="evaluate">The evaluator function.</param>
/// <param name="selectorString">The string representation of the selector.</param>
/// <param name="targetType">The target type, if available.</param>
/// <param name="inTemplate">Whether to match in a control template.</param>
/// <param name="stopTraversal">Whether to stop traversal at this point.</param>
public Selector(
Selector previous,
Func<IStyleable, SelectorMatch> evaluate,
string selectorString,
Type targetType = null,
bool inTemplate = false,
bool stopTraversal = false)
: this()
{
Contract.Requires<ArgumentNullException>(previous != null);
Previous = previous;
_evaluate = evaluate;
SelectorString = selectorString;
_targetType = targetType;
_inTemplate = inTemplate || previous._inTemplate;
_stopTraversal = stopTraversal;
}
/// <summary>
/// Gets the previous selector.
/// Gets a value indicating whether either this selector or a previous selector has moved
/// into a template.
/// </summary>
public Selector Previous
{
get;
}
/// <summary>
/// Gets a string representation of the selector.
/// </summary>
public string SelectorString
{
get;
}
public abstract bool InTemplate { get; }
/// <summary>
/// Gets the target type of the selector, if available.
/// </summary>
public Type TargetType => _targetType ?? MovePrevious()?.TargetType;
/// <summary>
/// Returns the previous selector if traversal is not stopped.
/// </summary>
/// <returns>The previous selector.</returns>
public Selector MovePrevious()
{
return _stopTraversal ? null : Previous;
}
public abstract Type TargetType { get; }
/// <summary>
/// Tries to match the selector with a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="subscribe">
/// Whether the match should subscribe to changes in order to track the match over time,
/// or simply return an immediate result.
/// </param>
/// <returns>A <see cref="SelectorMatch"/>.</returns>
public SelectorMatch Match(IStyleable control)
public SelectorMatch Match(IStyleable control, bool subscribe = true)
{
List<IObservable<bool>> inputs = new List<IObservable<bool>>();
Selector selector = this;
while (selector != null)
{
if (selector._inTemplate && control.TemplatedParent == null)
if (selector.InTemplate && control.TemplatedParent == null)
{
return SelectorMatch.False;
}
var match = selector._evaluate(control);
var match = selector.Evaluate(control, subscribe);
if (match.ImmediateResult == false)
{
@ -150,24 +68,19 @@ namespace Perspex.Styling
}
/// <summary>
/// Gets a string representation of the selector.
/// Evaluates the selector for a match.
/// </summary>
/// <returns>The string representation of the selector.</returns>
public override string ToString()
{
if (_description == null)
{
string result = string.Empty;
if (Previous != null)
{
result = Previous.ToString();
}
_description = result + SelectorString;
}
/// <param name="control">The control.</param>
/// <param name="subscribe">
/// Whether the match should subscribe to changes in order to track the match over time,
/// or simply return an immediate result.
/// </param>
/// <returns>A <see cref="SelectorMatch"/>.</returns>
protected abstract SelectorMatch Evaluate(IStyleable control, bool subscribe);
return _description;
}
/// <summary>
/// Moves to the previous selector.
/// </summary>
protected abstract Selector MovePrevious();
}
}

6
src/Perspex.Styling/Styling/SelectorMatch.cs

@ -6,12 +6,12 @@ using System;
namespace Perspex.Styling
{
/// <summary>
/// Holds the result of a <see cref="Selector"/> match.
/// Holds the result of a <see cref="FuncSelector"/> match.
/// </summary>
/// <remarks>
/// There are two types of selectors - ones whose match can never change for a particular
/// control (such as <see cref="Selectors.OfType{T}(Selector)"/>) and ones whose result can
/// change over time (such as <see cref="Selectors.Class(Selector, string)"/>. For the first
/// control (such as <see cref="Selectors.OfType{T}(FuncSelector)"/>) and ones whose result can
/// change over time (such as <see cref="Selectors.Class(FuncSelector, string)"/>. For the first
/// category of selectors, the value of <see cref="ImmediateResult"/> will be set but for the
/// second, <see cref="ImmediateResult"/> will be null and <see cref="ObservableResult"/> will
/// hold an observable which tracks the match.

167
src/Perspex.Styling/Styling/Selectors.cs

@ -2,13 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reflection;
using Perspex.LogicalTree;
namespace Perspex.Styling
{
@ -24,9 +17,7 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector Child(this Selector previous)
{
Contract.Requires<ArgumentNullException>(previous != null);
return new Selector(previous, x => MatchChild(x, previous), " < ", stopTraversal: true);
return new ChildSelector(previous);
}
/// <summary>
@ -37,10 +28,17 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector Class(this Selector previous, string name)
{
Contract.Requires<ArgumentNullException>(previous != null);
Contract.Requires<ArgumentNullException>(name != null);
var tac = previous as TypeNameAndClassSelector;
return new Selector(previous, x => MatchClass(x, name), name[0] == ':' ? name : '.' + name);
if (tac != null)
{
tac.Classes.Add(name);
return tac;
}
else
{
return TypeNameAndClassSelector.ForClass(previous, name);
}
}
/// <summary>
@ -50,9 +48,7 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector Descendent(this Selector previous)
{
Contract.Requires<ArgumentNullException>(previous != null);
return new Selector(previous, x => MatchDescendent(x, previous), " ", stopTraversal: true);
return new DescendentSelector(previous);
}
/// <summary>
@ -63,9 +59,7 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector Is(this Selector previous, Type type)
{
Contract.Requires<ArgumentNullException>(previous != null);
return new Selector(previous, x => MatchIs(x, type), $":is({type.Name})", type);
return TypeNameAndClassSelector.Is(previous, type);
}
/// <summary>
@ -87,9 +81,17 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector Name(this Selector previous, string name)
{
Contract.Requires<ArgumentNullException>(previous != null);
var tac = previous as TypeNameAndClassSelector;
return new Selector(previous, x => MatchName(x, name), '#' + name);
if (tac != null)
{
tac.Name = name;
return tac;
}
else
{
return TypeNameAndClassSelector.ForName(previous, name);
}
}
/// <summary>
@ -100,9 +102,7 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector OfType(this Selector previous, Type type)
{
Contract.Requires<ArgumentNullException>(previous != null);
return new Selector(previous, x => MatchOfType(x, type), type.Name, type);
return TypeNameAndClassSelector.OfType(previous, type);
}
/// <summary>
@ -126,10 +126,7 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector PropertyEquals<T>(this Selector previous, PerspexProperty<T> property, object value)
{
Contract.Requires<ArgumentNullException>(previous != null);
Contract.Requires<ArgumentNullException>(property != null);
return new Selector(previous, x => MatchPropertyEquals(x, property, value), $"[{property.Name}={value}]");
return new PropertyEqualsSelector(previous, property, value);
}
/// <summary>
@ -141,10 +138,7 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector PropertyEquals(this Selector previous, PerspexProperty property, object value)
{
Contract.Requires<ArgumentNullException>(previous != null);
Contract.Requires<ArgumentNullException>(property != null);
return new Selector(previous, x => MatchPropertyEquals(x, property, value), $"[{property.Name}={value}]");
return new PropertyEqualsSelector(previous, property, value);
}
/// <summary>
@ -154,114 +148,7 @@ namespace Perspex.Styling
/// <returns>The selector.</returns>
public static Selector Template(this Selector previous)
{
Contract.Requires<ArgumentNullException>(previous != null);
return new Selector(
previous,
x => MatchTemplate(x, previous),
" /template/ ",
inTemplate: true,
stopTraversal: true);
}
private static SelectorMatch MatchChild(IStyleable control, Selector previous)
{
var parent = ((ILogical)control).LogicalParent;
if (parent != null)
{
return previous.Match((IStyleable)parent);
}
else
{
return SelectorMatch.False;
}
}
private static SelectorMatch MatchClass(IStyleable control, string name)
{
var observable = Observable.FromEventPattern<
NotifyCollectionChangedEventHandler,
NotifyCollectionChangedEventArgs>(
x => control.Classes.CollectionChanged += x,
x => control.Classes.CollectionChanged -= x)
.Select(_ => Unit.Default)
.StartWith(Unit.Default)
.Select(_ => control.Classes.Contains(name));
return new SelectorMatch(observable);
}
private static SelectorMatch MatchDescendent(IStyleable control, Selector previous)
{
ILogical c = (ILogical)control;
List<IObservable<bool>> descendentMatches = new List<IObservable<bool>>();
while (c != null)
{
c = c.LogicalParent;
if (c is IStyleable)
{
var match = previous.Match((IStyleable)c);
if (match.ImmediateResult != null)
{
if (match.ImmediateResult == true)
{
return SelectorMatch.True;
}
}
else
{
descendentMatches.Add(match.ObservableResult);
}
}
}
return new SelectorMatch(StyleActivator.Or(descendentMatches));
}
private static SelectorMatch MatchIs(IStyleable control, Type type)
{
var controlType = control.StyleKey ?? control.GetType();
return new SelectorMatch(type.GetTypeInfo().IsAssignableFrom(controlType.GetTypeInfo()));
}
private static SelectorMatch MatchName(IStyleable control, string name)
{
return new SelectorMatch(control.Name == name);
}
private static SelectorMatch MatchOfType(IStyleable control, Type type)
{
var controlType = control.StyleKey ?? control.GetType();
return new SelectorMatch(controlType == type);
}
private static SelectorMatch MatchPropertyEquals(IStyleable x, PerspexProperty property, object value)
{
if (!PerspexPropertyRegistry.Instance.IsRegistered(x, property))
{
return SelectorMatch.False;
}
else
{
return new SelectorMatch(x.GetObservable(property).Select(v => Equals(v, value)));
}
}
private static SelectorMatch MatchTemplate(IStyleable control, Selector previous)
{
IStyleable templatedParent = control.TemplatedParent as IStyleable;
if (templatedParent == null)
{
throw new InvalidOperationException(
"Cannot call Template selector on control with null TemplatedParent.");
}
return previous.Match(templatedParent);
return new TemplateSelector(previous);
}
}
}

2
src/Perspex.Styling/Styling/Style.cs

@ -29,7 +29,7 @@ namespace Perspex.Styling
/// <param name="selector">The style selector.</param>
public Style(Func<Selector, Selector> selector)
{
Selector = selector(new Selector());
Selector = selector(null);
}
/// <summary>

54
src/Perspex.Styling/Styling/TemplateSelector.cs

@ -0,0 +1,54 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Perspex.Styling
{
internal class TemplateSelector : Selector
{
private readonly Selector _parent;
private string _selectorString;
public TemplateSelector(Selector parent)
{
if (parent == null)
{
throw new InvalidOperationException("Template selector must be preceeded by a selector.");
}
_parent = parent;
}
/// <inheritdoc/>
public override bool InTemplate => true;
/// <inheritdoc/>
public override Type TargetType => null;
public override string ToString()
{
if (_selectorString == null)
{
_selectorString = _parent.ToString() + " /template/ ";
}
return _selectorString;
}
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
IStyleable templatedParent = control.TemplatedParent as IStyleable;
if (templatedParent == null)
{
throw new InvalidOperationException(
"Cannot call Template selector on control with null TemplatedParent.");
}
return _parent.Match(templatedParent, subscribe) ?? SelectorMatch.True;
}
protected override Selector MovePrevious() => null;
}
}

207
src/Perspex.Styling/Styling/TypeNameAndClassSelector.cs

@ -0,0 +1,207 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reactive;
using System.Reactive.Linq;
using System.Reflection;
using System.Text;
namespace Perspex.Styling
{
/// <summary>
/// A selector that matches the common case of a type and/or name followed by a collection of
/// style classes and pseudoclasses.
/// </summary>
internal class TypeNameAndClassSelector : Selector
{
private readonly Selector _previous;
private Type _targetType;
private Lazy<List<string>> _classes = new Lazy<List<string>>(() => new List<string>());
private string _selectorString;
public static TypeNameAndClassSelector OfType(Selector previous, Type targetType)
{
var result = new TypeNameAndClassSelector(previous);
result._targetType = targetType;
result.IsConcreteType = true;
return result;
}
public static TypeNameAndClassSelector Is(Selector previous, Type targetType)
{
var result = new TypeNameAndClassSelector(previous);
result._targetType = targetType;
result.IsConcreteType = false;
return result;
}
public static TypeNameAndClassSelector ForName(Selector previous, string name)
{
var result = new TypeNameAndClassSelector(previous);
result.Name = name;
return result;
}
public static TypeNameAndClassSelector ForClass(Selector previous, string className)
{
var result = new TypeNameAndClassSelector(previous);
result.Classes.Add(className);
return result;
}
protected TypeNameAndClassSelector(Selector previous)
{
_previous = previous;
}
/// <inheritdoc/>
public override bool InTemplate => _previous?.InTemplate ?? false;
/// <summary>
/// Gets the name of the control to match.
/// </summary>
public string Name { get; set; }
/// <inheritdoc/>
public override Type TargetType => _targetType ?? _previous?.TargetType;
/// <summary>
/// Whether the selector matches the concrete <see cref="TargetType"/> or any object which
/// implements <see cref="TargetType"/>.
/// </summary>
public bool IsConcreteType { get; private set; }
/// <summary>
/// The style classes which the selector matches.
/// </summary>
public IList<string> Classes => _classes.Value;
/// <inheritdoc/>
public override string ToString()
{
if (_selectorString == null)
{
_selectorString = BuildSelectorString();
}
return _selectorString;
}
/// <inheritdoc/>
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
if (TargetType != null)
{
var controlType = control.StyleKey ?? control.GetType();
if (IsConcreteType)
{
if (controlType != TargetType)
{
return SelectorMatch.False;
}
}
else
{
if (!TargetType.GetTypeInfo().IsAssignableFrom(controlType.GetTypeInfo()))
{
return SelectorMatch.False;
}
}
}
if (Name != null && control.Name != Name)
{
return SelectorMatch.False;
}
if (_classes.IsValueCreated && _classes.Value.Count > 0)
{
if (subscribe)
{
var observable = Observable.FromEventPattern<
NotifyCollectionChangedEventHandler,
NotifyCollectionChangedEventArgs>(
x => control.Classes.CollectionChanged += x,
x => control.Classes.CollectionChanged -= x)
.StartWith((EventPattern<NotifyCollectionChangedEventArgs>)null)
.Select(_ => Matches(control.Classes));
return new SelectorMatch(observable);
}
else
{
return new SelectorMatch(Matches(control.Classes));
}
}
else
{
return SelectorMatch.True;
}
}
protected override Selector MovePrevious() => _previous;
private bool Matches(IEnumerable<string> classes)
{
int remaining = Classes.Count;
foreach (var c in classes)
{
if (Classes.Contains(c))
{
--remaining;
}
}
return remaining == 0;
}
private string BuildSelectorString()
{
var builder = new StringBuilder();
if (_previous != null)
{
builder.Append(_previous.ToString());
}
if (TargetType != null)
{
if (IsConcreteType)
{
builder.Append(TargetType.Name);
}
else
{
builder.Append(":is(");
builder.Append(TargetType.Name);
builder.Append(")");
}
}
if (Name != null)
{
builder.Append('#');
builder.Append(Name);
}
if (_classes.IsValueCreated && _classes.Value.Count > 0)
{
foreach (var c in Classes)
{
if (!c.StartsWith(":"))
{
builder.Append('.');
}
builder.Append(c);
}
}
return builder.ToString();
}
}
}

24
src/Perspex.Themes.Default/Button.xaml

@ -9,24 +9,22 @@
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<ControlTemplate>
<Border Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="Button:pointerover /template/ Border#border">
<Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="Button:pressed /template/ Border#border">
<Style Selector="Button:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{StyleResource ThemeControlDarkBrush}"/>
</Style>
<Style Selector="Button:disabled">

13
src/Perspex.Themes.Default/ContentControl.xaml

@ -1,13 +1,12 @@
<Style xmlns="https://github.com/perspex" Selector="ContentControl">
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>
</Style>

18
src/Perspex.Themes.Default/DropDownItem.xaml

@ -3,21 +3,19 @@
<Setter Property="Padding" Value="2"/>
<Setter Property="Template">
<ControlTemplate>
<Border Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DropDownItem:selected /template/ Border#border">
<Style Selector="DropDownItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="#fff0f0f0"/>
</Style>
<Style Selector="DropDownItem:pointerover /template/ Border#border">
<Style Selector="DropDownItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="#ffd0d0d0"/>
</Style>
</Styles>

18
src/Perspex.Themes.Default/ListBoxItem.xaml

@ -2,21 +2,19 @@
<Style Selector="ListBoxItem">
<Setter Property="Template">
<ControlTemplate>
<Border Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ListBoxItem:selected /template/ Border#border">
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="#fff0f0f0"/>
</Style>
<Style Selector="ListBoxItem:selected:focus /template/ Border#border">
<Style Selector="ListBoxItem:selected:focus /template/ ContentPresenter">
<Setter Property="Background" Value="#ffd0d0d0"/>
</Style>
</Styles>

9
src/Perspex.Themes.Default/PopupRoot.xaml

@ -2,11 +2,10 @@
<Setter Property="Background" Value="{StyleResource ThemeBackgroundBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>
</Style>

11
src/Perspex.Themes.Default/TabStripItem.xaml

@ -4,13 +4,12 @@
<Setter Property="Foreground" Value="{StyleResource ThemeForegroundLightBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Name="PART_ContentPresenter"
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Border>
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>
</Style>

28
src/Perspex.Themes.Default/ToggleButton.xaml

@ -4,33 +4,31 @@
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderLightBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Foreground" Value="{StyleResource ThemeForegroundBrush}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<ControlTemplate>
<Border Name="border"
Padding="3"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ToggleButton:checked /template/ Border#border">
<Style Selector="ToggleButton:checked /template/ ContentPresenter">
<Setter Property="Background" Value="{StyleResource ThemeControlDarkBrush}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="ToggleButton:pointerover /template/ Border#border">
<Style Selector="ToggleButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="ToggleButton:pressed /template/ Border#border">
<Style Selector="ToggleButton:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{StyleResource ThemeControlDarkBrush}"/>
</Style>
<Style Selector="ToggleButton:disabled">

13
src/Perspex.Themes.Default/ToolTip.xaml

@ -5,13 +5,12 @@
<Setter Property="Padding" Value="4,2"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter>
</Style>

24
src/Perspex.Themes.Default/TreeViewItem.xaml

@ -7,16 +7,14 @@
<ToggleButton Name="expander"
Focusable="False"
IsChecked="{TemplateBinding Path=IsExpanded, Mode=TwoWay}"/>
<Border Name="header"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
TemplatedControl.IsTemplateFocusTarget="True"
Grid.Column="1">
<ContentPresenter Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}"
Margin="{TemplateBinding Padding}"/>
</Border>
<ContentPresenter Name="PART_HeaderPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Header}"
Padding="{TemplateBinding Padding}"
TemplatedControl.IsTemplateFocusTarget="True"
Grid.Column="1"/>
</Grid>
<ItemsPresenter Name="PART_ItemsPresenter"
IsVisible="{TemplateBinding IsExpanded}"
@ -45,15 +43,15 @@
</Setter>
</Style>
<Style Selector="TreeViewItem /template/ Border#header">
<Style Selector="TreeViewItem /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="Padding" Value="2"/>
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#header">
<Style Selector="TreeViewItem:selected /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="Background" Value="#fff0f0f0"/>
</Style>
<Style Selector="TreeViewItem:selected:focus /template/ Border#header">
<Style Selector="TreeViewItem:selected:focus /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="Background" Value="#ffd0d0d0"/>
</Style>

14
tests/Perspex.Benchmarks/App.config

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1510.2205" newVersion="4.2.1510.2205" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

130
tests/Perspex.Benchmarks/Perspex.Benchmarks.csproj

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Perspex.Benchmarks</RootNamespace>
<AssemblyName>Perspex.Benchmarks</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="BenchmarkDotNet, Version=0.9.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\BenchmarkDotNet.0.9.2\lib\net45\BenchmarkDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Build" />
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.Build.Utilities.v4.0" />
<Reference Include="Moq, Version=4.2.1510.2205, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Ploeh.AutoFixture, Version=3.40.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
<HintPath>..\..\packages\AutoFixture.3.40.0\lib\net40\Ploeh.AutoFixture.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Ploeh.AutoFixture.AutoMoq, Version=3.40.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
<HintPath>..\..\packages\AutoFixture.AutoMoq.3.40.0\lib\net40\Ploeh.AutoFixture.AutoMoq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Styling\ApplyStyling.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Perspex.Animation\Perspex.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Perspex.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.Application\Perspex.Application.csproj">
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
<Name>Perspex.Application</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.Base\Perspex.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Perspex.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.Controls\Perspex.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Perspex.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.Input\Perspex.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Perspex.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.Interactivity\Perspex.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Perspex.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.Layout\Perspex.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Perspex.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.SceneGraph\Perspex.SceneGraph.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Perspex.SceneGraph</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.Styling\Perspex.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Perspex.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Perspex.Themes.Default\Perspex.Themes.Default.csproj">
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Perspex.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\Perspex.UnitTests\Perspex.UnitTests.csproj">
<Project>{88060192-33d5-4932-b0f9-8bd2763e857d}</Project>
<Name>Perspex.UnitTests</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

28
tests/Perspex.Benchmarks/Program.cs

@ -0,0 +1,28 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Linq;
using System.Reflection;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace Perspex.Benchmarks
{
class Program
{
static void Main(string[] args)
{
// Use reflection for a more maintainable way of creating the benchmark switcher,
// Benchmarks are listed in namespace order first (e.g. BenchmarkDotNet.Samples.CPU,
// BenchmarkDotNet.Samples.IL, etc) then by name, so the output is easy to understand
var benchmarks = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.Any(m => m.GetCustomAttributes(typeof(BenchmarkAttribute), false).Any()))
.OrderBy(t => t.Namespace)
.ThenBy(t => t.Name)
.ToArray();
var benchmarkSwitcher = new BenchmarkSwitcher(benchmarks);
benchmarkSwitcher.Run(args);
}
}
}

36
tests/Perspex.Benchmarks/Properties/AssemblyInfo.cs

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Perspex.Benchmarks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Perspex.Benchmarks")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("410ac439-81a1-4eb5-b5e9-6a7fc6b77f4b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

68
tests/Perspex.Benchmarks/Styling/ApplyStyling.cs

@ -0,0 +1,68 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using BenchmarkDotNet.Attributes;
using Perspex.Controls;
using Perspex.Styling;
using Perspex.UnitTests;
using Perspex.VisualTree;
namespace Perspex.Benchmarks.Styling
{
public class ApplyStyling : IDisposable
{
private IDisposable _app;
private Window _window;
public ApplyStyling()
{
_app = UnitTestApplication.Start(TestServices.StyledWindow);
TextBox textBox;
_window = new Window
{
Content = textBox = new TextBox(),
};
_window.ApplyTemplate();
textBox.ApplyTemplate();
var border = (Border)textBox.GetVisualChildren().Single();
if (border.BorderThickness != 2)
{
throw new Exception("Styles not applied.");
}
_window.Content = null;
// Add a bunch of styles with lots of class selectors to complicate matters.
for (int i = 0; i < 100; ++i)
{
_window.Styles.Add(new Style(x => x.OfType<TextBox>().Class("foo").Class("bar").Class("baz"))
{
Setters = new[]
{
new Setter(TextBox.TextProperty, "foo"),
}
});
}
}
public void Dispose()
{
_app.Dispose();
}
[Benchmark]
public void Add_And_Style_TextBox()
{
var textBox = new TextBox();
_window.Content = textBox;
textBox.ApplyTemplate();
}
}
}

7
tests/Perspex.Benchmarks/packages.config

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoFixture" version="3.40.0" targetFramework="net45" />
<package id="AutoFixture.AutoMoq" version="3.40.0" targetFramework="net45" />
<package id="BenchmarkDotNet" version="0.9.2" targetFramework="net46" />
<package id="Moq" version="4.2.1510.2205" targetFramework="net45" />
</packages>

1
tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj

@ -102,6 +102,7 @@
<Compile Include="UnitTestSynchronizationContext.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>

11
tests/Perspex.Markup.UnitTests/app.config

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1510.2205" newVersion="4.2.1510.2205" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

16
tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs

@ -24,7 +24,7 @@ namespace Perspex.Styling.UnitTests
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Child().OfType<TestLogical2>();
var selector = default(Selector).OfType<TestLogical1>().Child().OfType<TestLogical2>();
Assert.True(selector.Match(child).ImmediateResult);
}
@ -39,7 +39,7 @@ namespace Perspex.Styling.UnitTests
parent.LogicalParent = grandparent;
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Child().OfType<TestLogical3>();
var selector = default(Selector).OfType<TestLogical1>().Child().OfType<TestLogical3>();
Assert.False(selector.Match(child).ImmediateResult);
}
@ -52,7 +52,7 @@ namespace Perspex.Styling.UnitTests
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Class("foo").Child().OfType<TestLogical2>();
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Child().OfType<TestLogical2>();
var activator = selector.Match(child).ObservableResult;
var result = new List<bool>();
@ -67,11 +67,19 @@ namespace Perspex.Styling.UnitTests
public void Child_Doesnt_Match_Control_When_It_Has_No_Parent()
{
var control = new TestLogical3();
var selector = new Selector().OfType<TestLogical1>().Child().OfType<TestLogical3>();
var selector = default(Selector).OfType<TestLogical1>().Child().OfType<TestLogical3>();
Assert.False(selector.Match(control).ImmediateResult);
}
[Fact]
public void Child_Selector_Should_Have_Correct_String_Representation()
{
var selector = default(Selector).OfType<TestLogical1>().Child().OfType<TestLogical3>();
Assert.Equal("TestLogical1 > TestLogical3", selector.ToString());
}
public abstract class TestLogical : ILogical, IStyleable
{
public TestLogical()

16
tests/Perspex.Styling.UnitTests/SelectorTests_Class.cs

@ -16,7 +16,7 @@ namespace Perspex.Styling.UnitTests
[Fact]
public void Class_Selector_Should_Have_Correct_String_Representation()
{
var target = new Selector().Class("foo");
var target = default(Selector).Class("foo");
Assert.Equal(".foo", target.ToString());
}
@ -24,7 +24,7 @@ namespace Perspex.Styling.UnitTests
[Fact]
public void PesudoClass_Selector_Should_Have_Correct_String_Representation()
{
var target = new Selector().Class(":foo");
var target = default(Selector).Class(":foo");
Assert.Equal(":foo", target.ToString());
}
@ -37,7 +37,7 @@ namespace Perspex.Styling.UnitTests
Classes = new Classes { "foo" },
};
var target = new Selector().Class("foo");
var target = default(Selector).Class("foo");
var activator = target.Match(control).ObservableResult;
Assert.True(await activator.Take(1));
@ -51,7 +51,7 @@ namespace Perspex.Styling.UnitTests
Classes = new Classes { "bar" },
};
var target = new Selector().Class("foo");
var target = default(Selector).Class("foo");
var activator = target.Match(control).ObservableResult;
Assert.False(await activator.Take(1));
@ -66,7 +66,7 @@ namespace Perspex.Styling.UnitTests
TemplatedParent = new Mock<ITemplatedControl>().Object,
};
var target = new Selector().Class("foo");
var target = default(Selector).Class("foo");
var activator = target.Match(control).ObservableResult;
Assert.True(await activator.Take(1));
@ -77,7 +77,7 @@ namespace Perspex.Styling.UnitTests
{
var control = new Control1();
var target = new Selector().Class("foo");
var target = default(Selector).Class("foo");
var activator = target.Match(control).ObservableResult;
Assert.False(await activator.Take(1));
@ -93,7 +93,7 @@ namespace Perspex.Styling.UnitTests
Classes = new Classes { "foo" },
};
var target = new Selector().Class("foo");
var target = default(Selector).Class("foo");
var activator = target.Match(control).ObservableResult;
Assert.True(await activator.Take(1));
@ -105,7 +105,7 @@ namespace Perspex.Styling.UnitTests
public async Task Multiple_Classes()
{
var control = new Control1();
var target = new Selector().Class("foo").Class("bar");
var target = default(Selector).Class("foo").Class("bar");
var activator = target.Match(control).ObservableResult;
Assert.False(await activator.Take(1));

18
tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs

@ -24,7 +24,7 @@ namespace Perspex.Styling.UnitTests
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Descendent().OfType<TestLogical2>();
var selector = default(Selector).OfType<TestLogical1>().Descendent().OfType<TestLogical2>();
Assert.True(selector.Match(child).ImmediateResult);
}
@ -39,7 +39,7 @@ namespace Perspex.Styling.UnitTests
parent.LogicalParent = grandparent;
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Descendent().OfType<TestLogical3>();
var selector = default(Selector).OfType<TestLogical1>().Descendent().OfType<TestLogical3>();
Assert.True(selector.Match(child).ImmediateResult);
}
@ -55,7 +55,7 @@ namespace Perspex.Styling.UnitTests
parent.LogicalParent = grandparent;
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>();
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>();
var activator = selector.Match(child).ObservableResult;
Assert.True(await activator.Take(1));
@ -73,7 +73,7 @@ namespace Perspex.Styling.UnitTests
parent.Classes.Add("foo");
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>();
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>();
var activator = selector.Match(child).ObservableResult;
Assert.False(await activator.Take(1));
@ -89,7 +89,7 @@ namespace Perspex.Styling.UnitTests
parent.LogicalParent = grandparent;
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>();
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>();
var activator = selector.Match(child).ObservableResult;
Assert.False(await activator.Take(1));
@ -103,6 +103,14 @@ namespace Perspex.Styling.UnitTests
Assert.False(await activator.Take(1));
}
[Fact]
public void Descendent_Selector_Should_Have_Correct_String_Representation()
{
var selector = default(Selector).OfType<TestLogical1>().Class("foo").Descendent().OfType<TestLogical3>();
Assert.Equal("TestLogical1.foo TestLogical3", selector.ToString());
}
public abstract class TestLogical : ILogical, IStyleable
{
public TestLogical()

14
tests/Perspex.Styling.UnitTests/SelectorTests_Multiple.cs

@ -31,7 +31,7 @@ namespace Perspex.Styling.UnitTests
control.ApplyTemplate();
var selector = new Selector()
var selector = default(Selector)
.OfType<Button>()
.Class("foo")
.Class("bar")
@ -54,7 +54,7 @@ namespace Perspex.Styling.UnitTests
[Fact]
public void TargetType_OfType()
{
var selector = new Selector().OfType<Button>();
var selector = default(Selector).OfType<Button>();
Assert.Equal(typeof(Button), selector.TargetType);
}
@ -62,7 +62,7 @@ namespace Perspex.Styling.UnitTests
[Fact]
public void TargetType_OfType_Class()
{
var selector = new Selector()
var selector = default(Selector)
.OfType<Button>()
.Class("foo");
@ -72,7 +72,7 @@ namespace Perspex.Styling.UnitTests
[Fact]
public void TargetType_Is_Class()
{
var selector = new Selector()
var selector = default(Selector)
.Is<Button>()
.Class("foo");
@ -82,7 +82,7 @@ namespace Perspex.Styling.UnitTests
[Fact]
public void TargetType_Child()
{
var selector = new Selector()
var selector = default(Selector)
.OfType<Button>()
.Child()
.OfType<TextBlock>();
@ -93,7 +93,7 @@ namespace Perspex.Styling.UnitTests
[Fact]
public void TargetType_Descendent()
{
var selector = new Selector()
var selector = default(Selector)
.OfType<Button>()
.Descendent()
.OfType<TextBlock>();
@ -104,7 +104,7 @@ namespace Perspex.Styling.UnitTests
[Fact]
public void TargetType_Template()
{
var selector = new Selector()
var selector = default(Selector)
.OfType<Button>()
.Template()
.OfType<TextBlock>();

22
tests/Perspex.Styling.UnitTests/SelectorTests_Name.cs

@ -12,7 +12,7 @@ namespace Perspex.Styling.UnitTests
public void Name_Matches_Control_With_Correct_Name()
{
var control = new Control1 { Name = "foo" };
var target = new Selector().Name("foo");
var target = default(Selector).Name("foo");
Assert.True(target.Match(control).ImmediateResult);
}
@ -21,7 +21,7 @@ namespace Perspex.Styling.UnitTests
public void Name_Doesnt_Match_Control_Of_Wrong_Name()
{
var control = new Control1 { Name = "foo" };
var target = new Selector().Name("bar");
var target = default(Selector).Name("bar");
Assert.False(target.Match(control).ImmediateResult);
}
@ -30,12 +30,28 @@ namespace Perspex.Styling.UnitTests
public void Name_Doesnt_Match_Control_With_TemplatedParent()
{
var control = new Control1 { TemplatedParent = new Mock<ITemplatedControl>().Object };
var target = new Selector().Name("foo");
var target = default(Selector).Name("foo");
var activator = target.Match(control);
Assert.False(target.Match(control).ImmediateResult);
}
[Fact]
public void Name_Has_Correct_String_Representation()
{
var target = default(Selector).Name("foo");
Assert.Equal("#foo", target.ToString());
}
[Fact]
public void Type_And_Name_Has_Correct_String_Representation()
{
var target = default(Selector).OfType<Control1>().Name("foo");
Assert.Equal("Control1#foo", target.ToString());
}
public class Control1 : TestControlBase
{
}

6
tests/Perspex.Styling.UnitTests/SelectorTests_OfType.cs

@ -12,7 +12,7 @@ namespace Perspex.Styling.UnitTests
public void OfType_Matches_Control_Of_Correct_Type()
{
var control = new Control1();
var target = new Selector().OfType<Control1>();
var target = default(Selector).OfType<Control1>();
Assert.True(target.Match(control).ImmediateResult);
}
@ -21,7 +21,7 @@ namespace Perspex.Styling.UnitTests
public void OfType_Doesnt_Match_Control_Of_Wrong_Type()
{
var control = new Control2();
var target = new Selector().OfType<Control1>();
var target = default(Selector).OfType<Control1>();
Assert.False(target.Match(control).ImmediateResult);
}
@ -30,7 +30,7 @@ namespace Perspex.Styling.UnitTests
public void OfType_Matches_Control_With_TemplatedParent()
{
var control = new Control1 { TemplatedParent = new Mock<ITemplatedControl>().Object };
var target = new Selector().OfType<Control1>();
var target = default(Selector).OfType<Control1>();
Assert.True(target.Match(control).ImmediateResult);
}

12
tests/Perspex.Styling.UnitTests/SelectorTests_PropertyEquals.cs

@ -15,7 +15,7 @@ namespace Perspex.Styling.UnitTests
public async Task PropertyEquals_Matches_When_Property_Has_Matching_Value()
{
var control = new TextBlock();
var target = new Selector().PropertyEquals(TextBlock.TextProperty, "foo");
var target = default(Selector).PropertyEquals(TextBlock.TextProperty, "foo");
var activator = target.Match(control).ObservableResult;
Assert.False(await activator.Take(1));
@ -24,5 +24,15 @@ namespace Perspex.Styling.UnitTests
control.Text = null;
Assert.False(await activator.Take(1));
}
[Fact]
public void PropertyEquals_Selector_Should_Have_Correct_String_Representation()
{
var target = default(Selector)
.OfType<TextBlock>()
.PropertyEquals(TextBlock.TextProperty, "foo");
Assert.Equal("TextBlock[Text=foo]", target.ToString());
}
}
}

32
tests/Perspex.Styling.UnitTests/SelectorTests_Template.cs

@ -7,17 +7,12 @@ using System.Reactive.Linq;
using Moq;
using Perspex.Controls;
using Perspex.VisualTree;
using Perspex.Diagnostics;
using Xunit;
using System.Threading.Tasks;
namespace Perspex.Styling.UnitTests
{
using System.Reactive;
using System.Reactive.Subjects;
using Collections;
using Diagnostics;
using Controls = Controls.Controls;
public class SelectorTests_Template
{
[Fact]
@ -29,7 +24,10 @@ namespace Perspex.Styling.UnitTests
BuildVisualTree(target);
var border = (Border)target.Object.GetVisualChildren().Single();
var selector = new Selector().Template().OfType<Border>();
var selector = default(Selector)
.OfType(target.Object.GetType())
.Template()
.OfType<Border>();
Assert.True(selector.Match(border).ImmediateResult);
}
@ -44,7 +42,10 @@ namespace Perspex.Styling.UnitTests
var border = (Border)target.Object.GetVisualChildren().Single();
border.SetValue(Control.TemplatedParentProperty, null);
var selector = new Selector().Template().OfType<Border>();
var selector = default(Selector)
.OfType(target.Object.GetType())
.Template()
.OfType<Border>();
Assert.False(selector.Match(border).ImmediateResult);
}
@ -58,7 +59,10 @@ namespace Perspex.Styling.UnitTests
BuildVisualTree(target);
var textBlock = (TextBlock)target.Object.VisualChildren.Single().VisualChildren.Single();
var selector = new Selector().Template().OfType<TextBlock>();
var selector = default(Selector)
.OfType(target.Object.GetType())
.Template()
.OfType<TextBlock>();
Assert.True(selector.Match(textBlock).ImmediateResult);
}
@ -74,7 +78,7 @@ namespace Perspex.Styling.UnitTests
var border = (Border)target.Object.VisualChildren.Single();
var selector = new Selector().OfType(styleKey).Template().OfType<Border>();
var selector = default(Selector).OfType(styleKey).Template().OfType<Border>();
Assert.True(selector.Match(border).ImmediateResult);
}
@ -91,7 +95,7 @@ namespace Perspex.Styling.UnitTests
styleable.Setup(x => x.StyleKey).Returns(styleKey);
styleable.Setup(x => x.Classes).Returns(new Classes("foo"));
var border = (Border)target.Object.VisualChildren.Single();
var selector = new Selector().OfType(styleKey).Class("foo").Template().OfType<Border>();
var selector = default(Selector).OfType(styleKey).Class("foo").Template().OfType<Border>();
var activator = selector.Match(border).ObservableResult;
Assert.True(await activator.Take(1));
@ -107,7 +111,7 @@ namespace Perspex.Styling.UnitTests
styleable.Setup(x => x.Classes).Returns(new Classes("bar"));
var border = (Border)target.Object.VisualChildren.Single();
var selector = new Selector().OfType(templatedControl.Object.GetType()).Class("foo").Template().OfType<Border>();
var selector = default(Selector).OfType(templatedControl.Object.GetType()).Class("foo").Template().OfType<Border>();
var activator = selector.Match(border).ObservableResult;
Assert.False(await activator.Take(1));
@ -123,7 +127,7 @@ namespace Perspex.Styling.UnitTests
styleable.Setup(x => x.Classes).Returns(new Classes("foo"));
var border = (Border)target.Object.VisualChildren.Single();
var selector = new Selector().OfType(templatedControl.Object.GetType()).Class("foo").Template().OfType<Border>();
var selector = default(Selector).OfType(templatedControl.Object.GetType()).Class("foo").Template().OfType<Border>();
var activator = selector.Match(border).ObservableResult;
var inccDebug = (INotifyCollectionChangedDebug)styleable.Object.Classes;
@ -137,7 +141,7 @@ namespace Perspex.Styling.UnitTests
private void BuildVisualTree<T>(Mock<T> templatedControl) where T : class, IVisual
{
templatedControl.Setup(x => x.VisualChildren).Returns(new Controls
templatedControl.Setup(x => x.VisualChildren).Returns(new Controls.Controls
{
new Border
{

10
tests/Perspex.Styling.UnitTests/TestSelectors.cs

@ -1,6 +1,7 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Styling;
namespace Perspex.Styling.UnitTests
@ -9,10 +10,11 @@ namespace Perspex.Styling.UnitTests
{
public static Selector SubscribeCheck(this Selector selector)
{
return new Selector(
selector,
control => new SelectorMatch(((TestControlBase)control).SubscribeCheckObservable),
"");
throw new NotImplementedException();
//return new Selector(
// selector,
// control => new SelectorMatch(((TestControlBase)control).SubscribeCheckObservable),
// "");
}
}
}

4
tests/Perspex.Styling.UnitTests/app.config

@ -14,6 +14,10 @@
<assemblyIdentity name="System.Reactive.Linq" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.2.5.0" newVersion="2.2.5.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1510.2205" newVersion="4.2.1510.2205" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

8
tests/Perspex.UnitTests/Perspex.UnitTests.csproj

@ -36,14 +36,6 @@
<HintPath>..\..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Ploeh.AutoFixture, Version=3.40.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
<HintPath>..\..\packages\AutoFixture.3.40.0\lib\net40\Ploeh.AutoFixture.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Ploeh.AutoFixture.AutoMoq, Version=3.40.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
<HintPath>..\..\packages\AutoFixture.AutoMoq.3.40.0\lib\net40\Ploeh.AutoFixture.AutoMoq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">

22
tests/Perspex.UnitTests/TestServices.cs

@ -2,29 +2,25 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reflection;
using Moq;
using Perspex.Input;
using Perspex.Layout;
using Perspex.Markup.Xaml;
using Perspex.Media;
using Perspex.Platform;
using Perspex.Shared.PlatformSupport;
using Perspex.Styling;
using Perspex.Themes.Default;
using Ploeh.AutoFixture;
using Ploeh.AutoFixture.AutoMoq;
namespace Perspex.UnitTests
{
public class TestServices
{
private static IFixture s_fixture = new Fixture().Customize(new AutoMoqCustomization());
public static readonly TestServices StyledWindow = new TestServices(
assetLoader: new AssetLoader(),
layoutManager: new LayoutManager(),
platformWrapper: new PclPlatformWrapper(),
renderInterface: s_fixture.Create<IPlatformRenderInterface>(),
renderInterface: CreateRenderInterfaceMock(),
standardCursorFactory: Mock.Of<IStandardCursorFactory>(),
styler: new Styler(),
theme: () => CreateDefaultTheme(),
@ -122,5 +118,19 @@ namespace Perspex.UnitTests
return result;
}
private static IPlatformRenderInterface CreateRenderInterfaceMock()
{
return Mock.Of<IPlatformRenderInterface>(x =>
x.CreateFormattedText(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<double>(),
It.IsAny<FontStyle>(),
It.IsAny<TextAlignment>(),
It.IsAny<FontWeight>()) == Mock.Of<IFormattedTextImpl>() &&
x.CreateStreamGeometry() == Mock.Of<IStreamGeometryImpl>(
y => y.Open() == Mock.Of<IStreamGeometryContextImpl>()));
}
}
}

4
tests/Perspex.UnitTests/packages.config

@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoFixture" version="3.40.0" targetFramework="net46" />
<package id="AutoFixture.AutoMoq" version="3.40.0" targetFramework="net46" />
<package id="Moq" version="4.2.1510.2205" targetFramework="net46" />
<package id="Moq" version="4.2.1510.2205" targetFramework="net45" />
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
<package id="Rx-Linq" version="2.2.5" targetFramework="net45" />

Loading…
Cancel
Save