diff --git a/Perspex.Controls/Menu.cs b/Perspex.Controls/Menu.cs index 924e8b4cec..982b550d89 100644 --- a/Perspex.Controls/Menu.cs +++ b/Perspex.Controls/Menu.cs @@ -132,16 +132,6 @@ namespace Perspex.Controls this.subscription.Dispose(); } - /// - /// Called when the loses focus. - /// - /// The event args. - protected override void OnLostFocus(RoutedEventArgs e) - { - base.OnLostFocus(e); - //this.Close(); - } - /// /// Called when a key is pressed within the menu. /// @@ -205,6 +195,8 @@ namespace Perspex.Controls private void OnMenuClick(RoutedEventArgs e) { this.Close(); + FocusManager.Instance.Focus(null); + e.Handled = true; } /// diff --git a/Perspex.Controls/PopupRoot.cs b/Perspex.Controls/PopupRoot.cs index aaf7cbe948..01f1d318fb 100644 --- a/Perspex.Controls/PopupRoot.cs +++ b/Perspex.Controls/PopupRoot.cs @@ -11,11 +11,12 @@ namespace Perspex.Controls using Perspex.Media; using Perspex.Platform; using Splat; + using Perspex.VisualTree; /// /// The root window of a . /// - public class PopupRoot : TopLevel, IInteractive + public class PopupRoot : TopLevel, IInteractive, IHostedVisualTreeRoot { /// /// Initializes static members of the class. @@ -64,6 +65,14 @@ namespace Perspex.Controls get { return this.Parent; } } + /// + /// Gets the control that is hosting the popup root. + /// + IVisual IHostedVisualTreeRoot.Host + { + get { return this.Parent; } + } + /// /// Sets the position of the popup in screen coordinates. /// diff --git a/Perspex.Input/FocusManager.cs b/Perspex.Input/FocusManager.cs index 9f2a6db517..8d47ec6bf9 100644 --- a/Perspex.Input/FocusManager.cs +++ b/Perspex.Input/FocusManager.cs @@ -59,19 +59,29 @@ namespace Perspex.Input { if (control != null) { - var scope = control.GetSelfAndVisualAncestors() - .OfType() - .FirstOrDefault() ?? this.Scope; + var scope = GetFocusScopeAncestors(control) + .FirstOrDefault(); if (scope != null) { this.Scope = scope; this.SetFocusedElement(scope, control, keyboardNavigated); + System.Diagnostics.Debug.WriteLine("Focused " + control.GetType().Name); } } - else + else if (this.Current != null) { - this.SetFocusedElement(this.Scope, null); + // If control is null, set focus to the topmost focus scope. + foreach (var scope in GetFocusScopeAncestors(this.Current).Reverse().ToList()) + { + IInputElement element; + + if (this.focusScopes.TryGetValue(scope, out element)) + { + this.Focus(element, keyboardNavigated); + break; + } + } } } @@ -124,5 +134,26 @@ namespace Perspex.Input this.Scope = scope; this.Focus(e); } + + /// + /// Gets the focus scope ancestors of the specified control, traversing popups. + /// + /// The control. + /// The focus scopes. + private static IEnumerable GetFocusScopeAncestors(IInputElement control) + { + while (control != null) + { + var scope = control as IFocusScope; + + if (scope != null) + { + yield return scope; + } + + control = control.GetVisualParent() ?? + ((control as IHostedVisualTreeRoot)?.Host as IInputElement); + } + } } } diff --git a/Perspex.Input/MouseDevice.cs b/Perspex.Input/MouseDevice.cs index e1102598a3..63f2dffae3 100644 --- a/Perspex.Input/MouseDevice.cs +++ b/Perspex.Input/MouseDevice.cs @@ -143,7 +143,7 @@ namespace Perspex.Input IInputElement focusable = this.GetFocusable(hit); - if (focusable != null && focusable.Focusable) + if (focusable != null && focusable.Focusable && focusable.IsEffectivelyVisible) { focusable.Focus(); } diff --git a/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/Perspex.SceneGraph/Perspex.SceneGraph.csproj index 064cbc5a20..2026a0eabd 100644 --- a/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -101,6 +101,7 @@ + diff --git a/Perspex.SceneGraph/VisualTree/IHostedVisualTreeRoot.cs b/Perspex.SceneGraph/VisualTree/IHostedVisualTreeRoot.cs new file mode 100644 index 0000000000..e76ae5ebde --- /dev/null +++ b/Perspex.SceneGraph/VisualTree/IHostedVisualTreeRoot.cs @@ -0,0 +1,22 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.VisualTree +{ + /// + /// Interface for controls that are at the root of a hosted visual tree, such as popups. + /// + public interface IHostedVisualTreeRoot + { + /// + /// Gets the visual tree host. + /// + /// + /// The visual tree host. + /// + IVisual Host { get; } + } +}