Browse Source

Merge branch 'fix_carousel_presenter' of https://github.com/VitalElement/Perspex into VitalElement-fix_carousel_presenter

pull/331/merge
Steven Kirk 10 years ago
parent
commit
2dcc5e3c6b
  1. 1
      appveyor.yml
  2. 103
      samples/XamlTestApplicationPcl/ViewModels/EditorViewModel.cs
  3. 15
      samples/XamlTestApplicationPcl/ViewModels/MainWindowViewModel.cs
  4. 78
      samples/XamlTestApplicationPcl/Views/MainWindow.xaml
  5. 1
      samples/XamlTestApplicationPcl/XamlTestApplicationPcl.csproj
  6. 28
      src/Perspex.Base/PerspexObject.cs
  7. 12
      src/Perspex.Controls/Generators/ItemContainerGenerator.cs
  8. 15
      src/Perspex.Controls/Presenters/CarouselPresenter.cs
  9. 17
      src/Perspex.Controls/Primitives/SelectingItemsControl.cs
  10. 11
      src/Perspex.Input/FocusManager.cs
  11. 10
      src/Perspex.Input/Gestures.cs
  12. 8
      src/Perspex.Input/InputElement.cs
  13. 2
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs
  14. 24
      tests/Perspex.Controls.UnitTests/ListBoxTests.cs
  15. 77
      tests/Perspex.Controls.UnitTests/Presenters/CarouselPresenterTests.cs
  16. 52
      tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  17. 49
      tests/Perspex.Input.UnitTests/InputElement_Focus.cs
  18. 5
      tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj
  19. 3
      tests/Perspex.UnitTests/TestRoot.cs
  20. 15
      tests/Perspex.UnitTests/TestServices.cs
  21. 2
      tests/Perspex.UnitTests/UnitTestApplication.cs

1
appveyor.yml

@ -20,6 +20,7 @@ configuration:
- Release
after_test:
- .\packages\JetBrains.dotMemoryUnit.2.1.20150828.125449\tools\dotMemoryUnit.exe -targetExecutable="%xunit20%\xunit.console.x86.exe" -returnTargetExitCode --"tests\Perspex.LeakTests\bin\Release\Perspex.LeakTests.dll"
- ps: nuget\build-appveyor.ps1
artifacts:

103
samples/XamlTestApplicationPcl/ViewModels/EditorViewModel.cs

@ -0,0 +1,103 @@
using Perspex.Threading;
namespace XamlTestApplication.ViewModels
{
using ReactiveUI;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class ShellViewModel : ReactiveObject
{
private ShellViewModel()
{
documents = new ObservableCollection<EditorViewModel>();
AddDocumentCommand = ReactiveCommand.Create();
AddDocumentCommand.Subscribe(_ =>
{
Documents.Add(new EditorViewModel());
});
GCCommand = ReactiveCommand.Create();
GCCommand.Subscribe(_ =>
{
GC.Collect();
});
}
public static ShellViewModel Instance = new ShellViewModel();
private ObservableCollection<EditorViewModel> documents;
public ObservableCollection<EditorViewModel> Documents
{
get { return documents; }
set { this.RaiseAndSetIfChanged(ref documents, value); }
}
private EditorViewModel selectedDocument;
public EditorViewModel SelectedDocument
{
get { return selectedDocument; }
set { this.RaiseAndSetIfChanged(ref selectedDocument, value); }
}
private int instanceCount;
public int InstanceCount
{
get { return instanceCount; }
set { this.RaiseAndSetIfChanged(ref instanceCount, value); }
}
public ReactiveCommand<object> AddDocumentCommand { get; }
public ReactiveCommand<object> GCCommand { get; }
}
public class EditorViewModel : ReactiveObject
{
private static int InstanceCount = 0;
public EditorViewModel()
{
InstanceCount++;
ShellViewModel.Instance.InstanceCount = InstanceCount;
text = "This is some text.";
CloseCommand = ReactiveCommand.Create();
CloseCommand.Subscribe(_ =>
{
ShellViewModel.Instance.Documents.Remove(this);
});
}
~EditorViewModel()
{
//System.Console.WriteLine("EVM Destructed");
InstanceCount--;
Dispatcher.UIThread.InvokeAsync(() =>
{
ShellViewModel.Instance.InstanceCount = InstanceCount;
});
}
private string text;
public string Text
{
get { return text; }
set { this.RaiseAndSetIfChanged(ref text, value); }
}
public ReactiveCommand<object> CloseCommand { get; }
}
}

15
samples/XamlTestApplicationPcl/ViewModels/MainWindowViewModel.cs

@ -56,7 +56,11 @@ namespace XamlTestApplication.ViewModels
}
};
CollapseNodesCommand = ReactiveCommand.Create();
CollapseNodesCommand = ReactiveCommand.Create();
CollapseNodesCommand.Subscribe(_ => ExpandNodes(false));
ExpandNodesCommand = ReactiveCommand.Create();
ExpandNodesCommand.Subscribe(_ => ExpandNodes(true));
@ -77,6 +81,15 @@ namespace XamlTestApplication.ViewModels
ofd.ShowAsync();
});
shell = ShellViewModel.Instance;
}
private ShellViewModel shell;
public ShellViewModel Shell
{
get { return shell; }
set { shell = value; }
}
public List<TestItem> Items { get; }

78
samples/XamlTestApplicationPcl/Views/MainWindow.xaml

@ -316,6 +316,84 @@
<local:TestScrollable/>
</ScrollViewer>
</TabItem>
<TabItem Header="Mem Leak Repro">
<Grid DataContext="{Binding Shell}">
<StackPanel>
<TabStrip Name="strip" Items="{Binding Documents}" SelectedItem="{Binding SelectedDocument, Mode=TwoWay}" Focusable="false">
<TabStrip.Styles>
<Style Selector="TabStripItem">
<Setter Property="Height" Value="20" />
<Setter Property="Background" Value="#2D2D30" />
<Setter Property="BorderBrush" Value="#3E3E42" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="0 0 0 -1" />
<Setter Property="Padding" Value="4 0 4 0" />
<Setter Property="FontSize" Value="12"/>
</Style>
<Style Selector="TabStripItem:pointerover">
<Setter Property="Background" Value="#1c97ea" />
</Style>
<Style Selector="TabStripItem:selected">
<Setter Property="Background" Value="#007ACC" />
</Style>
</TabStrip.Styles>
<TabStrip.DataTemplates>
<DataTemplate>
<StackPanel Orientation="Horizontal" Gap="2">
<TextBlock Text="{Binding Title}" Foreground="WhiteSmoke" Margin="2"/>
<Button Height="14" Width="14">
<Button.Styles>
<Style Selector="Button">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Background" Value="Transparent" />
</Style>
<Style Selector="Button:pointerover">
<Setter Property="Background" Value="#1c97ea" />
</Style>
</Button.Styles>
<Path Margin="2" Stretch="Uniform" UseLayoutRounding="False" Data="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z" Fill="WhiteSmoke" />
</Button>
<Button Height="14" Width="14" Command="{Binding CloseCommand}">
<Button.Styles>
<Style Selector="Button">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Background" Value="Transparent" />
</Style>
<Style Selector="Button:pointerover">
<Setter Property="Background" Value="#1c97ea" />
</Style>
</Button.Styles>
<Path Margin="2" Stretch="Uniform" UseLayoutRounding="False" Data="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" Fill="WhiteSmoke" />
</Button>
</StackPanel>
</DataTemplate>
</TabStrip.DataTemplates>
</TabStrip>
<Grid Background="#007ACC" Height="2" />
<Button Content="AddTab" Command="{Binding AddDocumentCommand}" Width="50" Height="30" />
<Button Content="GC.Collecy" Command="{Binding GCCommand}" Width="50" Height="30" />
<TextBox Text="{Binding InstanceCount}" />
<Carousel Items="{Binding Documents}" SelectedIndex="{Binding #strip.SelectedIndex}" IsVirtualized="false">
<Carousel.DataTemplates>
<DataTemplate>
<TextBox Text="{Binding Text}" />
</DataTemplate>
</Carousel.DataTemplates>
</Carousel>
</StackPanel>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>

1
samples/XamlTestApplicationPcl/XamlTestApplicationPcl.csproj

@ -42,6 +42,7 @@
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestScrollable.cs" />
<Compile Include="ViewModels\EditorViewModel.cs" />
<Compile Include="ViewModels\MainWindowViewModel.cs" />
<Compile Include="ViewModels\TestItem.cs" />
<Compile Include="ViewModels\TestNode.cs" />

28
src/Perspex.Base/PerspexObject.cs

@ -23,17 +23,6 @@ namespace Perspex
/// </remarks>
public class PerspexObject : IPerspexObject, IPerspexObjectDebug, INotifyPropertyChanged, IPriorityValueOwner
{
/// <summary>
/// Maintains a list of direct property binding subscriptions so that the binding source
/// doesn't get collected.
/// </summary>
/// <remarks>
/// If/when we provide a ClearBindings() method, then this collection will be need to be
/// moved to an instance field and indexed by property, but until that point a static
/// collection will suffice.
/// </remarks>
private static List<IDisposable> s_directBindings = new List<IDisposable>();
/// <summary>
/// The parent object that inherited values are inherited from.
/// </summary>
@ -45,6 +34,12 @@ namespace Perspex
private readonly Dictionary<PerspexProperty, PriorityValue> _values =
new Dictionary<PerspexProperty, PriorityValue>();
/// <summary>
/// Maintains a list of direct property binding subscriptions so that the binding source
/// doesn't get collected.
/// </summary>
private List<IDisposable> _directBindings;
/// <summary>
/// Event handler for <see cref="INotifyPropertyChanged"/> implementation.
/// </summary>
@ -402,17 +397,22 @@ namespace Perspex
IDisposable subscription = null;
if (_directBindings == null)
{
_directBindings = new List<IDisposable>();
}
subscription = source
.Select(x => CastOrDefault(x, property.PropertyType))
.Do(_ => { }, () => s_directBindings.Remove(subscription))
.Do(_ => { }, () => _directBindings.Remove(subscription))
.Subscribe(x => DirectBindingSet(property, x));
s_directBindings.Add(subscription);
_directBindings.Add(subscription);
return Disposable.Create(() =>
{
subscription.Dispose();
s_directBindings.Remove(subscription);
_directBindings.Remove(subscription);
});
}
else

12
src/Perspex.Controls/Generators/ItemContainerGenerator.cs

@ -94,9 +94,15 @@ namespace Perspex.Controls.Generators
/// <inheritdoc/>
public virtual IEnumerable<ItemContainer> RemoveRange(int startingIndex, int count)
{
var result = _containers.GetRange(startingIndex, count);
_containers.RemoveRange(startingIndex, count);
Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result));
List<ItemContainer> result = new List<ItemContainer>();
if (startingIndex < _containers.Count)
{
result.AddRange(_containers.GetRange(startingIndex, count));
_containers.RemoveRange(startingIndex, count);
Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result));
}
return result;
}

15
src/Perspex.Controls/Presenters/CarouselPresenter.cs

@ -6,6 +6,7 @@ using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Perspex.Animation;
using Perspex.Controls.Generators;
using Perspex.Controls.Primitives;
using Perspex.Controls.Utils;
using Perspex.Data;
@ -103,7 +104,19 @@ namespace Perspex.Controls.Presenters
/// <inheritdoc/>
protected override void ItemsChanged(NotifyCollectionChangedEventArgs e)
{
// TODO: Handle items changing.
// TODO: Handle items changing.
switch (e.Action)
{
case NotifyCollectionChangedAction.Remove:
if (!IsVirtualized)
{
var generator = ItemContainerGenerator;
var containers = generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl));
}
break;
}
}
/// <summary>

17
src/Perspex.Controls/Primitives/SelectingItemsControl.cs

@ -377,6 +377,23 @@ namespace Perspex.Controls.Primitives
}
}
/// <inheritdoc/>
protected override void OnContainersDematerialized(ItemContainerEventArgs e)
{
base.OnContainersDematerialized(e);
var panel = (InputElement)Presenter.Panel;
foreach (var container in e.Containers)
{
if (KeyboardNavigation.GetTabOnceActiveElement(panel) == container.ContainerControl)
{
KeyboardNavigation.SetTabOnceActiveElement(panel, null);
break;
}
}
}
/// <inheritdoc/>
protected override void OnDataContextChanging()
{

11
src/Perspex.Input/FocusManager.cs

@ -39,7 +39,7 @@ namespace Perspex.Input
/// <summary>
/// Gets the currently focused <see cref="IInputElement"/>.
/// </summary>
public IInputElement Current => KeyboardDevice.Instance.FocusedElement;
public IInputElement Current => KeyboardDevice.Instance?.FocusedElement;
/// <summary>
/// Gets the current focus scope.
@ -82,9 +82,12 @@ namespace Perspex.Input
if (_focusScopes.TryGetValue(scope, out element))
{
Focus(element, method);
break;
return;
}
}
// Couldn't find a focus scope, clear focus.
SetFocusedElement(Scope, null);
}
}
@ -111,7 +114,7 @@ namespace Perspex.Input
if (Scope == scope)
{
KeyboardDevice.Instance.SetFocusedElement(element, method, modifiers);
KeyboardDevice.Instance?.SetFocusedElement(element, method, modifiers);
}
}
@ -176,7 +179,7 @@ namespace Perspex.Input
if (sender == e.Source)
{
var ev = (PointerPressedEventArgs)e;
var element = (ev.Device.Captured as IInputElement) ?? (e.Source as IInputElement);
var element = (ev.Device?.Captured as IInputElement) ?? (e.Source as IInputElement);
if (element == null || !CanFocus(element))
{

10
src/Perspex.Input/Gestures.cs

@ -18,7 +18,7 @@ namespace Perspex.Input
RoutingStrategies.Bubble,
typeof(Gestures));
private static IInteractive s_lastPress;
private static WeakReference s_lastPress;
static Gestures()
{
@ -34,9 +34,9 @@ namespace Perspex.Input
if (e.ClickCount <= 1)
{
s_lastPress = e.Source;
s_lastPress = new WeakReference(e.Source);
}
else if (e.ClickCount == 2 && s_lastPress == e.Source)
else if (s_lastPress?.IsAlive == true && e.ClickCount == 2 && s_lastPress.Target == e.Source)
{
e.Source.RaiseEvent(new RoutedEventArgs(DoubleTappedEvent));
}
@ -49,9 +49,9 @@ namespace Perspex.Input
{
var e = (PointerReleasedEventArgs)ev;
if (s_lastPress == e.Source)
if (s_lastPress?.IsAlive == true && s_lastPress.Target == e.Source)
{
s_lastPress.RaiseEvent(new RoutedEventArgs(TappedEvent));
((IInteractive)s_lastPress.Target).RaiseEvent(new RoutedEventArgs(TappedEvent));
}
}
}

8
src/Perspex.Input/InputElement.cs

@ -376,9 +376,9 @@ namespace Perspex.Input
}
/// <inheritdoc/>
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
base.OnDetachedFromVisualTreeCore(e);
if (IsFocused)
{
@ -387,9 +387,9 @@ namespace Perspex.Input
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
base.OnAttachedToVisualTreeCore(e);
UpdateIsEnabledCore();
}

2
tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs

@ -344,6 +344,7 @@ namespace Perspex.Base.UnitTests
{
var source = new Subject<string>();
var sub = target.Bind((PerspexProperty)Class1.FooProperty, source);
source.OnNext("foo");
return new WeakReference(source);
};
@ -351,6 +352,7 @@ namespace Perspex.Base.UnitTests
GC.Collect();
Assert.Equal("foo", target.Foo);
Assert.True(weakSource.IsAlive);
}

24
tests/Perspex.Controls.UnitTests/ListBoxTests.cs

@ -104,30 +104,6 @@ namespace Perspex.Controls.UnitTests
dataContexts);
}
[Fact]
public void Setting_SelectedItem_Should_Set_Panel_Keyboard_Navigation()
{
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
};
ApplyTemplate(target);
target.Presenter.Panel.Children[1].RaiseEvent(new PointerPressedEventArgs
{
RoutedEvent = InputElement.PointerPressedEvent,
MouseButton = MouseButton.Left,
});
var panel = target.Presenter.Panel;
Assert.Equal(
KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel),
panel.Children[1]);
}
private Control CreateListBoxTemplate(ITemplatedControl parent)
{
return new ScrollViewer

77
tests/Perspex.Controls.UnitTests/Presenters/CarouselPresenterTests.cs

@ -7,6 +7,7 @@ using Perspex.Controls.Generators;
using Perspex.Controls.Presenters;
using Perspex.Controls.Templates;
using Xunit;
using System.Collections.ObjectModel;
namespace Perspex.Controls.UnitTests.Presenters
{
@ -116,6 +117,82 @@ namespace Perspex.Controls.UnitTests.Presenters
Assert.Equal(2, target.Panel.Children.Count);
}
[Fact]
public void Should_Remove_Controls_When_IsVirtualized_Is_False()
{
ObservableCollection<string> items = new ObservableCollection<string>();
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 0,
IsVirtualized = false,
};
target.ApplyTemplate();
target.SelectedIndex = 0;
items.Add("foo");
target.SelectedIndex = 0;
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
items.Add("bar");
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(2, target.Panel.Children.Count);
items.Remove(items[0]);
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
items.Remove(items[0]);
Assert.Equal(0, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(0, target.Panel.Children.Count);
}
[Fact]
public void Should_have_correct_index_itemscontainer()
{
ObservableCollection<string> items = new ObservableCollection<string>();
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 0,
IsVirtualized = false,
};
target.ApplyTemplate();
target.SelectedIndex = 0;
items.Add("foo");
target.SelectedIndex = 0;
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
items.Add("bar");
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(2, target.Panel.Children.Count);
Assert.Equal(0, target.ItemContainerGenerator.Containers.First().Index);
items.Remove(items[0]);
Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(1, target.Panel.Children.Count);
Assert.Equal(1, target.ItemContainerGenerator.Containers.First().Index);
items.Remove(items[0]);
Assert.Equal(0, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(0, target.Panel.Children.Count);
}
private class TestItem : ContentControl
{
}

52
tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -8,6 +8,7 @@ using Perspex.Collections;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Input;
using Perspex.Interactivity;
using Perspex.Markup.Xaml.Data;
using Perspex.UnitTests;
@ -604,6 +605,57 @@ namespace Perspex.Controls.UnitTests.Primitives
Assert.Equal(0, root.SelectedIndex);
}
[Fact]
public void Setting_SelectedItem_With_Pointer_Should_Set_TabOnceActiveElement()
{
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz " },
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.Presenter.Panel.Children[1].RaiseEvent(new PointerPressedEventArgs
{
RoutedEvent = InputElement.PointerPressedEvent,
MouseButton = MouseButton.Left,
});
var panel = target.Presenter.Panel;
Assert.Equal(
KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel),
panel.Children[1]);
}
[Fact]
public void Removing_SelectedItem_Should_Clear_TabOnceActiveElement()
{
var items = new ObservableCollection<string>(new[] { "Foo", "Bar", "Baz " });
var target = new ListBox
{
Template = Template(),
Items = items,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.Presenter.Panel.Children[1].RaiseEvent(new PointerPressedEventArgs
{
RoutedEvent = InputElement.PointerPressedEvent,
MouseButton = MouseButton.Left,
});
items.RemoveAt(1);
var panel = target.Presenter.Panel;
Assert.Null(KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel));
}
private FuncControlTemplate Template()
{

49
tests/Perspex.Input.UnitTests/InputElement_Focus.cs

@ -0,0 +1,49 @@
// 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 Perspex.Controls;
using Perspex.UnitTests;
using Xunit;
namespace Perspex.Input.UnitTests
{
public class InputElement_Focus
{
[Fact]
public void Focus_Should_Set_FocusManager_Current()
{
Button target;
using (UnitTestApplication.Start(TestServices.RealFocus))
{
var root = new TestRoot
{
Child = target = new Button()
};
target.Focus();
Assert.Same(target, FocusManager.Instance.Current);
}
}
[Fact]
public void Focus_Should_Be_Cleared_When_Control_Is_Removed_From_VisualTree()
{
Button target;
using (UnitTestApplication.Start(TestServices.RealFocus))
{
var root = new TestRoot
{
Child = target = new Button()
};
target.Focus();
root.Child = null;
Assert.Null(FocusManager.Instance.Current);
}
}
}
}

5
tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj

@ -60,6 +60,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="InputElement_Focus.cs" />
<Compile Include="InputElement_HitTesting.cs" />
<Compile Include="KeyboardNavigationTests_Arrows.cs" />
<Compile Include="KeyboardNavigationTests_Tab.cs" />
@ -103,6 +104,10 @@
<Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project>
<Name>Perspex.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\Perspex.UnitTests\Perspex.UnitTests.csproj">
<Project>{88060192-33D5-4932-B0F9-8BD2763E857D}</Project>
<Name>Perspex.UnitTests</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />

3
tests/Perspex.UnitTests/TestRoot.cs

@ -3,6 +3,7 @@
using System;
using Perspex.Controls;
using Perspex.Input;
using Perspex.Layout;
using Perspex.Platform;
using Perspex.Rendering;
@ -10,7 +11,7 @@ using Perspex.Styling;
namespace Perspex.UnitTests
{
public class TestRoot : Decorator, ILayoutRoot, INameScope, IRenderRoot, IStyleRoot
public class TestRoot : Decorator, IFocusScope, ILayoutRoot, INameScope, IRenderRoot, IStyleRoot
{
private readonly NameScope _nameScope = new NameScope();

15
tests/Perspex.UnitTests/TestServices.cs

@ -36,12 +36,19 @@ namespace Perspex.UnitTests
public static readonly TestServices MockThreadingInterface = new TestServices(
threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true));
public static readonly TestServices RealFocus = new TestServices(
focusManager: new FocusManager(),
keyboardDevice: () => new KeyboardDevice(),
inputManager: new InputManager());
public static readonly TestServices RealStyler = new TestServices(
styler: new Styler());
public TestServices(
IAssetLoader assetLoader = null,
IFocusManager focusManager = null,
IInputManager inputManager = null,
Func<IKeyboardDevice> keyboardDevice = null,
ILayoutManager layoutManager = null,
IPclPlatformWrapper platformWrapper = null,
IPlatformRenderInterface renderInterface = null,
@ -53,7 +60,9 @@ namespace Perspex.UnitTests
IWindowingPlatform windowingPlatform = null)
{
AssetLoader = assetLoader;
FocusManager = focusManager;
InputManager = inputManager;
KeyboardDevice = keyboardDevice;
LayoutManager = layoutManager;
PlatformWrapper = platformWrapper;
RenderInterface = renderInterface;
@ -67,6 +76,8 @@ namespace Perspex.UnitTests
public IAssetLoader AssetLoader { get; }
public IInputManager InputManager { get; }
public IFocusManager FocusManager { get; }
public Func<IKeyboardDevice> KeyboardDevice { get; }
public ILayoutManager LayoutManager { get; }
public IPclPlatformWrapper PlatformWrapper { get; }
public IPlatformRenderInterface RenderInterface { get; }
@ -79,7 +90,9 @@ namespace Perspex.UnitTests
public TestServices With(
IAssetLoader assetLoader = null,
IFocusManager focusManager = null,
IInputManager inputManager = null,
Func<IKeyboardDevice> keyboardDevice = null,
ILayoutManager layoutManager = null,
IPclPlatformWrapper platformWrapper = null,
IPlatformRenderInterface renderInterface = null,
@ -92,7 +105,9 @@ namespace Perspex.UnitTests
{
return new TestServices(
assetLoader: assetLoader ?? AssetLoader,
focusManager: focusManager ?? FocusManager,
inputManager: inputManager ?? InputManager,
keyboardDevice: keyboardDevice ?? KeyboardDevice,
layoutManager: layoutManager ?? LayoutManager,
platformWrapper: platformWrapper ?? PlatformWrapper,
renderInterface: renderInterface ?? RenderInterface,

2
tests/Perspex.UnitTests/UnitTestApplication.cs

@ -40,8 +40,10 @@ namespace Perspex.UnitTests
{
PerspexLocator.CurrentMutable
.Bind<IAssetLoader>().ToConstant(Services.AssetLoader)
.Bind<IFocusManager>().ToConstant(Services.FocusManager)
.BindToSelf<IGlobalStyles>(this)
.Bind<IInputManager>().ToConstant(Services.InputManager)
.Bind<IKeyboardDevice>().ToConstant(Services.KeyboardDevice?.Invoke())
.Bind<ILayoutManager>().ToConstant(Services.LayoutManager)
.Bind<IPclPlatformWrapper>().ToConstant(Services.PlatformWrapper)
.Bind<IPlatformRenderInterface>().ToConstant(Services.RenderInterface)

Loading…
Cancel
Save