42 changed files with 1413 additions and 602 deletions
@ -0,0 +1,29 @@ |
|||
<?xml version="1.0"?> |
|||
<package> |
|||
<metadata> |
|||
<id>Perspex.Desktop</id> |
|||
<version>#VERSION#</version> |
|||
<authors>Perspex Team</authors> |
|||
<owners>stevenk</owners> |
|||
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl> |
|||
<projectUrl>https://github.com/Perspex/Perspex/</projectUrl> |
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance> |
|||
<description>The Perspex UI framework</description> |
|||
<releaseNotes></releaseNotes> |
|||
<copyright>Copyright 2015</copyright> |
|||
<tags>Perspex</tags> |
|||
<dependencies> |
|||
<dependency id="Serilog" version="1.5.9" /> |
|||
<dependency id="Splat" version="1.6.2" /> |
|||
<dependency id="Sprache" version="2.0.0.47" /> |
|||
<dependency id="Rx-Core" version="2.2.5" /> |
|||
<dependency id="Rx-Interfaces" version="2.2.5" /> |
|||
<dependency id="Rx-Linq" version="2.2.5" /> |
|||
<dependency id="Rx-Main" version="2.2.5" /> |
|||
<dependency id="Rx-PlatformServices" version="2.2.5" /> |
|||
<dependency id="SharpDX" version="2.6.3"/> |
|||
<dependency id="SharpDX.Direct2D1" version="2.6.3"/> |
|||
<dependency id="SharpDX.DXGI" version="2.6.3"/> |
|||
</dependencies> |
|||
</metadata> |
|||
</package> |
|||
@ -1,11 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
|
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup Condition="'$(MSBuildThisFileDirectory)' != '' And HasTrailingSlash('$(MSBuildThisFileDirectory)')"> |
|||
<PlatformLibs Include="$(MSBuildThisFileDirectory)**\*.dll" /> |
|||
<Content Include="@(PlatformLibs)"> |
|||
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link> |
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|||
</Content> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,255 @@ |
|||
// 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.Linq; |
|||
using System.Reflection; |
|||
|
|||
namespace Perspex |
|||
{ |
|||
/// <summary>
|
|||
/// Tracks registered <see cref="PerspexProperty"/> instances.
|
|||
/// </summary>
|
|||
public class PerspexPropertyRegistry |
|||
{ |
|||
/// <summary>
|
|||
/// The registered properties by type.
|
|||
/// </summary>
|
|||
private readonly Dictionary<Type, List<PerspexProperty>> _registered = |
|||
new Dictionary<Type, List<PerspexProperty>>(); |
|||
|
|||
/// <summary>
|
|||
/// The registered attached properties by owner type.
|
|||
/// </summary>
|
|||
private readonly Dictionary<Type, List<PerspexProperty>> _attached = |
|||
new Dictionary<Type, List<PerspexProperty>>(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="PerspexPropertyRegistry"/> instance
|
|||
/// </summary>
|
|||
public static PerspexPropertyRegistry Instance { get; } |
|||
= new PerspexPropertyRegistry(); |
|||
|
|||
/// <summary>
|
|||
/// Gets all attached <see cref="PerspexProperty"/>s registered by an owner.
|
|||
/// </summary>
|
|||
/// <param name="ownerType">The owner type.</param>
|
|||
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
|
|||
public IEnumerable<PerspexProperty> GetAttached(Type ownerType) |
|||
{ |
|||
List<PerspexProperty> list; |
|||
|
|||
if (_attached.TryGetValue(ownerType, out list)) |
|||
{ |
|||
return list; |
|||
} |
|||
|
|||
return Enumerable.Empty<PerspexProperty>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets all <see cref="PerspexProperty"/>s registered on a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
|
|||
public IEnumerable<PerspexProperty> GetRegistered(Type type) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
|
|||
var i = type.GetTypeInfo(); |
|||
|
|||
while (type != null) |
|||
{ |
|||
List<PerspexProperty> list; |
|||
|
|||
if (_registered.TryGetValue(type, out list)) |
|||
{ |
|||
foreach (PerspexProperty p in list) |
|||
{ |
|||
yield return p; |
|||
} |
|||
} |
|||
|
|||
type = type.GetTypeInfo().BaseType; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets all <see cref="PerspexProperty"/>s registered on a object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object.</param>
|
|||
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
|
|||
public IEnumerable<PerspexProperty> GetRegistered(PerspexObject o) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(o != null); |
|||
|
|||
return GetRegistered(o.GetType()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds <see cref="PerspexProperty"/> registered on a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <returns>The registered property or null if not found.</returns>
|
|||
/// <remarks>
|
|||
/// Calling AddOwner on a PerspexProperty creates a new PerspexProperty that is a
|
|||
/// different object but is equal according to <see cref="object.Equals(object)"/>.
|
|||
/// </remarks>
|
|||
public PerspexProperty FindRegistered(Type type, PerspexProperty property) |
|||
{ |
|||
while (type != null) |
|||
{ |
|||
List<PerspexProperty> list; |
|||
|
|||
if (_registered.TryGetValue(type, out list)) |
|||
{ |
|||
var index = list.IndexOf(property); |
|||
|
|||
if (index != -1) |
|||
{ |
|||
return list[index]; |
|||
} |
|||
} |
|||
|
|||
type = type.GetTypeInfo().BaseType; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds <see cref="PerspexProperty"/> registered on an object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <returns>The registered property or null if not found.</returns>
|
|||
/// <remarks>
|
|||
/// Calling AddOwner on a PerspexProperty creates a new PerspexProperty that is a
|
|||
/// different object but is equal according to <see cref="object.Equals(object)"/>.
|
|||
/// </remarks>
|
|||
public PerspexProperty FindRegistered(object o, PerspexProperty property) |
|||
{ |
|||
return FindRegistered(o.GetType(), property); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds a registered property on a type by name.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="name">
|
|||
/// The property name. If an attached property it should be in the form
|
|||
/// "OwnerType.PropertyName".
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The registered property or null if no matching property found.
|
|||
/// </returns>
|
|||
public PerspexProperty FindRegistered(Type type, string name) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
Contract.Requires<ArgumentNullException>(name != null); |
|||
|
|||
var parts = name.Split('.'); |
|||
|
|||
if (parts.Length < 1 || parts.Length > 2) |
|||
{ |
|||
throw new ArgumentException("Invalid property name."); |
|||
} |
|||
|
|||
string propertyName; |
|||
var results = GetRegistered(type); |
|||
|
|||
if (parts.Length == 1) |
|||
{ |
|||
propertyName = parts[0]; |
|||
} |
|||
else |
|||
{ |
|||
var types = GetImplementedTypes(type); |
|||
|
|||
if (!types.Contains(parts[0])) |
|||
{ |
|||
results = results.Where(x => x.OwnerType.Name == parts[0]); |
|||
} |
|||
|
|||
propertyName = parts[1]; |
|||
} |
|||
|
|||
return results.FirstOrDefault(x => x.Name == propertyName); |
|||
} |
|||
|
|||
private IEnumerable<string> GetImplementedTypes(Type type) |
|||
{ |
|||
while (type != null) |
|||
{ |
|||
yield return type.Name; |
|||
type = type.GetTypeInfo().BaseType; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks whether a <see cref="PerspexProperty"/> is registered on a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <returns>True if the property is registered, otherwise false.</returns>
|
|||
public bool IsRegistered(Type type, PerspexProperty property) |
|||
{ |
|||
return FindRegistered(type, property) != null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks whether a <see cref="PerspexProperty"/> is registered on a object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <returns>True if the property is registered, otherwise false.</returns>
|
|||
public bool IsRegistered(object o, PerspexProperty property) |
|||
{ |
|||
return IsRegistered(o.GetType(), property); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Registers a <see cref="PerspexProperty"/> on a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="property">The property.</param>
|
|||
/// <remarks>
|
|||
/// You won't usually want to call this method directly, instead use the
|
|||
/// <see cref="PerspexProperty.Register"/> method.
|
|||
/// </remarks>
|
|||
public void Register(Type type, PerspexProperty property) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
Contract.Requires<ArgumentNullException>(property != null); |
|||
|
|||
List<PerspexProperty> list; |
|||
|
|||
if (!_registered.TryGetValue(type, out list)) |
|||
{ |
|||
list = new List<PerspexProperty>(); |
|||
_registered.Add(type, list); |
|||
} |
|||
|
|||
if (!list.Contains(property)) |
|||
{ |
|||
list.Add(property); |
|||
} |
|||
|
|||
if (property.IsAttached) |
|||
{ |
|||
if (!_attached.TryGetValue(property.OwnerType, out list)) |
|||
{ |
|||
list = new List<PerspexProperty>(); |
|||
_attached.Add(property.OwnerType, list); |
|||
} |
|||
|
|||
if (!list.Contains(property)) |
|||
{ |
|||
list.Add(property); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,177 @@ |
|||
// 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.Reactive.Linq; |
|||
using Xunit; |
|||
|
|||
namespace Perspex.Base.UnitTests |
|||
{ |
|||
public class PerspexPropertyRegistryTests |
|||
{ |
|||
public PerspexPropertyRegistryTests() |
|||
{ |
|||
// Ensure properties are registered.
|
|||
PerspexProperty p; |
|||
p = Class1.FooProperty; |
|||
p = Class2.BarProperty; |
|||
p = AttachedOwner.AttachedProperty; |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetRegistered_Returns_Registered_Properties() |
|||
{ |
|||
string[] names = PerspexPropertyRegistry.Instance.GetRegistered(typeof(Class1)) |
|||
.Select(x => x.Name) |
|||
.ToArray(); |
|||
|
|||
Assert.Equal(new[] { "Foo", "Baz", "Qux", "Attached" }, names); |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetRegistered_Returns_Registered_Properties_For_Base_Types() |
|||
{ |
|||
string[] names = PerspexPropertyRegistry.Instance.GetRegistered(typeof(Class2)) |
|||
.Select(x => x.Name) |
|||
.ToArray(); |
|||
|
|||
Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux", "Attached" }, names); |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetAttached_Returns_Registered_Properties_For_Base_Types() |
|||
{ |
|||
string[] names = PerspexPropertyRegistry.Instance.GetAttached(typeof(AttachedOwner)).Select(x => x.Name).ToArray(); |
|||
|
|||
Assert.Equal(new[] { "Attached" }, names); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_Untyped_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Foo"); |
|||
|
|||
Assert.Equal(Class1.FooProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_Typed_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Class1.Foo"); |
|||
|
|||
Assert.Equal(Class1.FooProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_Typed_Inherited_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Class1.Foo"); |
|||
|
|||
Assert.Equal(Class2.FooProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_Inherited_Property_With_Derived_Type_Name() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Class2.Foo"); |
|||
|
|||
Assert.Equal(Class2.FooProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_Attached_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class2), "AttachedOwner.Attached"); |
|||
|
|||
Assert.Equal(AttachedOwner.AttachedProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_AddOwnered_Untyped_Attached_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Attached"); |
|||
|
|||
Assert.Equal(AttachedOwner.AttachedProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_AddOwnered_Typed_Attached_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Class3.Attached"); |
|||
|
|||
Assert.Equal(AttachedOwner.AttachedProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_AddOwnered_AttachedTyped_Attached_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class3), "AttachedOwner.Attached"); |
|||
|
|||
Assert.Equal(AttachedOwner.AttachedProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Finds_AddOwnered_BaseTyped_Attached_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Class1.Attached"); |
|||
|
|||
Assert.Equal(AttachedOwner.AttachedProperty, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Doesnt_Find_Nonregistered_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Bar"); |
|||
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindRegistered_Doesnt_Find_Nonregistered_Attached_Property() |
|||
{ |
|||
var result = PerspexPropertyRegistry.Instance.FindRegistered(typeof(Class4), "AttachedOwner.Attached"); |
|||
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
private class Class1 : PerspexObject |
|||
{ |
|||
public static readonly PerspexProperty<string> FooProperty = |
|||
PerspexProperty.Register<Class1, string>("Foo"); |
|||
|
|||
public static readonly PerspexProperty<string> BazProperty = |
|||
PerspexProperty.Register<Class1, string>("Baz"); |
|||
|
|||
public static readonly PerspexProperty<int> QuxProperty = |
|||
PerspexProperty.Register<Class1, int>("Qux"); |
|||
} |
|||
|
|||
private class Class2 : Class1 |
|||
{ |
|||
public static readonly PerspexProperty<string> BarProperty = |
|||
PerspexProperty.Register<Class2, string>("Bar"); |
|||
|
|||
public static readonly PerspexProperty<double> FlobProperty = |
|||
PerspexProperty.Register<Class2, double>("Flob"); |
|||
|
|||
public static readonly PerspexProperty<double?> FredProperty = |
|||
PerspexProperty.Register<Class2, double?>("Fred"); |
|||
} |
|||
|
|||
private class Class3 : Class1 |
|||
{ |
|||
public static readonly PerspexProperty<string> AttachedProperty = |
|||
AttachedOwner.AttachedProperty.AddOwner<Class3>(); |
|||
} |
|||
|
|||
public class Class4 : PerspexObject |
|||
{ |
|||
} |
|||
|
|||
private class AttachedOwner |
|||
{ |
|||
public static readonly PerspexProperty<string> AttachedProperty = |
|||
PerspexProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
// 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 Perspex.Controls.Generators; |
|||
using Xunit; |
|||
|
|||
namespace Perspex.Controls.UnitTests.Generators |
|||
{ |
|||
public class ItemContainerGeneratorTests |
|||
{ |
|||
[Fact] |
|||
public void Materialize_Should_Create_Containers() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = target.Materialize(0, items, null); |
|||
var result = containers.OfType<TextBlock>().Select(x => x.Text).ToList(); |
|||
|
|||
Assert.Equal(items, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ContainerFromIndex_Should_Return_Materialized_Containers() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = target.Materialize(0, items, null).ToList(); |
|||
|
|||
Assert.Equal(containers[0], target.ContainerFromIndex(0)); |
|||
Assert.Equal(containers[1], target.ContainerFromIndex(1)); |
|||
Assert.Equal(containers[2], target.ContainerFromIndex(2)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void IndexFromContainer_Should_Return_Index() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = target.Materialize(0, items, null).ToList(); |
|||
|
|||
Assert.Equal(0, target.IndexFromContainer(containers[0])); |
|||
Assert.Equal(1, target.IndexFromContainer(containers[1])); |
|||
Assert.Equal(2, target.IndexFromContainer(containers[2])); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dematerialize_Should_Remove_Container() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = target.Materialize(0, items, null).ToList(); |
|||
|
|||
target.Dematerialize(1, 1); |
|||
|
|||
Assert.Equal(containers[0], target.ContainerFromIndex(0)); |
|||
Assert.Equal(null, target.ContainerFromIndex(1)); |
|||
Assert.Equal(containers[2], target.ContainerFromIndex(2)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dematerialize_Should_Return_Removed_Containers() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = target.Materialize(0, items, null); |
|||
var expected = target.Containers.Take(2).ToList(); |
|||
var result = target.Dematerialize(0, 2); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveRange_Should_Alter_Successive_Container_Indexes() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator(owner); |
|||
var containers = target.Materialize(0, items, null).ToList(); |
|||
|
|||
var removed = target.RemoveRange(1, 1).Single(); |
|||
|
|||
Assert.Equal(containers[0], target.ContainerFromIndex(0)); |
|||
Assert.Equal(containers[2], target.ContainerFromIndex(1)); |
|||
Assert.Equal(containers[1], removed); |
|||
} |
|||
} |
|||
} |
|||
@ -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 Perspex.Controls.Generators; |
|||
using Xunit; |
|||
|
|||
namespace Perspex.Controls.UnitTests.Generators |
|||
{ |
|||
public class ItemContainerGeneratorTypedTests |
|||
{ |
|||
[Fact] |
|||
public void Materialize_Should_Create_Containers() |
|||
{ |
|||
var items = new[] { "foo", "bar", "baz" }; |
|||
var owner = new Decorator(); |
|||
var target = new ItemContainerGenerator<ListBoxItem>(owner, ListBoxItem.ContentProperty); |
|||
var containers = target.Materialize(0, items, null); |
|||
var result = containers |
|||
.OfType<ListBoxItem>() |
|||
.Select(x => x.Content) |
|||
.OfType<TextBlock>() |
|||
.Select(x => x.Text).ToList(); |
|||
|
|||
Assert.Equal(items, result); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue