Browse Source

Correctly clear focus when control removed.

Focus was staying on removed element.
pull/519/head
Steven Kirk 11 years ago
parent
commit
ca301280e8
  1. 11
      src/Perspex.Input/FocusManager.cs
  2. 8
      src/Perspex.Input/InputElement.cs
  3. 49
      tests/Perspex.Input.UnitTests/InputElement_Focus.cs
  4. 5
      tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj
  5. 3
      tests/Perspex.UnitTests/TestRoot.cs
  6. 15
      tests/Perspex.UnitTests/TestServices.cs
  7. 2
      tests/Perspex.UnitTests/UnitTestApplication.cs

11
src/Perspex.Input/FocusManager.cs

@ -39,7 +39,7 @@ namespace Perspex.Input
/// <summary> /// <summary>
/// Gets the currently focused <see cref="IInputElement"/>. /// Gets the currently focused <see cref="IInputElement"/>.
/// </summary> /// </summary>
public IInputElement Current => KeyboardDevice.Instance.FocusedElement; public IInputElement Current => KeyboardDevice.Instance?.FocusedElement;
/// <summary> /// <summary>
/// Gets the current focus scope. /// Gets the current focus scope.
@ -82,9 +82,12 @@ namespace Perspex.Input
if (_focusScopes.TryGetValue(scope, out element)) if (_focusScopes.TryGetValue(scope, out element))
{ {
Focus(element, method); 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) 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) if (sender == e.Source)
{ {
var ev = (PointerPressedEventArgs)e; 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)) if (element == null || !CanFocus(element))
{ {

8
src/Perspex.Input/InputElement.cs

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

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

3
tests/Perspex.UnitTests/TestRoot.cs

@ -3,6 +3,7 @@
using System; using System;
using Perspex.Controls; using Perspex.Controls;
using Perspex.Input;
using Perspex.Layout; using Perspex.Layout;
using Perspex.Platform; using Perspex.Platform;
using Perspex.Rendering; using Perspex.Rendering;
@ -10,7 +11,7 @@ using Perspex.Styling;
namespace Perspex.UnitTests 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(); 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( public static readonly TestServices MockThreadingInterface = new TestServices(
threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true)); 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( public static readonly TestServices RealStyler = new TestServices(
styler: new Styler()); styler: new Styler());
public TestServices( public TestServices(
IAssetLoader assetLoader = null, IAssetLoader assetLoader = null,
IFocusManager focusManager = null,
IInputManager inputManager = null, IInputManager inputManager = null,
Func<IKeyboardDevice> keyboardDevice = null,
ILayoutManager layoutManager = null, ILayoutManager layoutManager = null,
IPclPlatformWrapper platformWrapper = null, IPclPlatformWrapper platformWrapper = null,
IPlatformRenderInterface renderInterface = null, IPlatformRenderInterface renderInterface = null,
@ -53,7 +60,9 @@ namespace Perspex.UnitTests
IWindowingPlatform windowingPlatform = null) IWindowingPlatform windowingPlatform = null)
{ {
AssetLoader = assetLoader; AssetLoader = assetLoader;
FocusManager = focusManager;
InputManager = inputManager; InputManager = inputManager;
KeyboardDevice = keyboardDevice;
LayoutManager = layoutManager; LayoutManager = layoutManager;
PlatformWrapper = platformWrapper; PlatformWrapper = platformWrapper;
RenderInterface = renderInterface; RenderInterface = renderInterface;
@ -67,6 +76,8 @@ namespace Perspex.UnitTests
public IAssetLoader AssetLoader { get; } public IAssetLoader AssetLoader { get; }
public IInputManager InputManager { get; } public IInputManager InputManager { get; }
public IFocusManager FocusManager { get; }
public Func<IKeyboardDevice> KeyboardDevice { get; }
public ILayoutManager LayoutManager { get; } public ILayoutManager LayoutManager { get; }
public IPclPlatformWrapper PlatformWrapper { get; } public IPclPlatformWrapper PlatformWrapper { get; }
public IPlatformRenderInterface RenderInterface { get; } public IPlatformRenderInterface RenderInterface { get; }
@ -79,7 +90,9 @@ namespace Perspex.UnitTests
public TestServices With( public TestServices With(
IAssetLoader assetLoader = null, IAssetLoader assetLoader = null,
IFocusManager focusManager = null,
IInputManager inputManager = null, IInputManager inputManager = null,
Func<IKeyboardDevice> keyboardDevice = null,
ILayoutManager layoutManager = null, ILayoutManager layoutManager = null,
IPclPlatformWrapper platformWrapper = null, IPclPlatformWrapper platformWrapper = null,
IPlatformRenderInterface renderInterface = null, IPlatformRenderInterface renderInterface = null,
@ -92,7 +105,9 @@ namespace Perspex.UnitTests
{ {
return new TestServices( return new TestServices(
assetLoader: assetLoader ?? AssetLoader, assetLoader: assetLoader ?? AssetLoader,
focusManager: focusManager ?? FocusManager,
inputManager: inputManager ?? InputManager, inputManager: inputManager ?? InputManager,
keyboardDevice: keyboardDevice ?? KeyboardDevice,
layoutManager: layoutManager ?? LayoutManager, layoutManager: layoutManager ?? LayoutManager,
platformWrapper: platformWrapper ?? PlatformWrapper, platformWrapper: platformWrapper ?? PlatformWrapper,
renderInterface: renderInterface ?? RenderInterface, renderInterface: renderInterface ?? RenderInterface,

2
tests/Perspex.UnitTests/UnitTestApplication.cs

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

Loading…
Cancel
Save