Browse Source

More work on Menu.

pull/58/head
Steven Kirk 11 years ago
parent
commit
80064e8712
  1. 22
      Perspex.Controls/Menu.cs
  2. 58
      Perspex.Controls/MenuItem.cs
  3. 19
      Perspex.Controls/MenuItemAccessKeyHandler.cs
  4. 82
      Perspex.Input/AccessKeyHandler.cs
  5. 14
      Perspex.Input/IMainMenu.cs
  6. 5
      Perspex.SceneGraph/VisualTree/VisualExtensions.cs

22
Perspex.Controls/Menu.cs

@ -72,7 +72,7 @@ namespace Perspex.Controls
/// <summary>
/// Closes the menu.
/// </summary>
public void CloseMenu()
public void Close()
{
foreach (MenuItem i in this.GetLogicalChildren())
{
@ -86,10 +86,11 @@ namespace Perspex.Controls
/// <summary>
/// Opens the menu in response to the Alt/F10 key.
/// </summary>
public void OpenMenu()
public void Open()
{
this.SelectedIndex = 0;
((IInputElement)this.SelectedItem)?.Focus();
this.SelectedMenuItem.Focus();
this.IsOpen = true;
}
/// <summary>
@ -138,7 +139,7 @@ namespace Perspex.Controls
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
//this.CloseMenu();
//this.Close();
}
/// <summary>
@ -151,12 +152,7 @@ namespace Perspex.Controls
base.OnKeyDown(e);
if (this.IsOpen && e.Key == Key.Escape)
{
this.CloseMenu();
e.Handled = true;
}
else if (menuWasOpen)
if (menuWasOpen)
{
// If a menu item was open and we navigate to a new one with the arrow keys, open
// that menu and select the first item.
@ -199,7 +195,7 @@ namespace Perspex.Controls
/// <param name="e">The event args.</param>
private void Deactivated(object sender, EventArgs e)
{
this.CloseMenu();
this.Close();
}
/// <summary>
@ -208,7 +204,7 @@ namespace Perspex.Controls
/// <param name="e">The event args.</param>
private void OnMenuClick(RoutedEventArgs e)
{
this.CloseMenu();
this.Close();
}
/// <summary>
@ -224,7 +220,7 @@ namespace Perspex.Controls
if (!this.IsLogicalParentOf(control))
{
this.CloseMenu();
this.Close();
}
}
}

58
Perspex.Controls/MenuItem.cs

@ -277,10 +277,6 @@ namespace Perspex.Controls
}
break;
default:
e.Handled = this.HandleAccessKey(e.Text);
break;
}
if (!passStraightToParent)
@ -289,60 +285,6 @@ namespace Perspex.Controls
}
}
private bool HandleAccessKey(string text)
{
text = text.ToUpper();
if (!this.IsSubMenuOpen)
{
var match = this.GetVisualDescendents()
.OfType<AccessText>()
.FirstOrDefault(x => x.AccessKey.ToString().ToUpper() == text);
if (match != null)
{
if (this.HasSubMenu)
{
this.IsSubMenuOpen = true;
}
else
{
this.RaiseEvent(new RoutedEventArgs(ClickEvent));
}
return true;
}
}
else
{
var match = this.popup.PopupRoot.GetVisualDescendents()
.OfType<AccessText>()
.FirstOrDefault(x => x.AccessKey.ToString().ToUpper() == text)
?.GetVisualAncestors()
.OfType<MenuItem>()
.FirstOrDefault();
if (match != null)
{
var item = (MenuItem)this.ItemContainerGenerator.GetItemForContainer(match);
if (item.HasSubMenu)
{
item.SelectedIndex = 0;
item.IsSubMenuOpen = true;
}
else
{
item.RaiseEvent(new RoutedEventArgs(ClickEvent));
}
return true;
}
}
return false;
}
/// <summary>
/// Called when the pointer enters the <see cref="MenuItem"/>.
/// </summary>

19
Perspex.Controls/MenuItemAccessKeyHandler.cs

@ -92,14 +92,19 @@ namespace Perspex.Controls
/// <param name="e">The event args.</param>
protected virtual void OnKeyDown(object sender, KeyEventArgs e)
{
var text = e.Text.ToUpper();
var focus = this.registered
.Where(x => x.Item1 == text && x.Item2.IsEffectivelyVisible)
.FirstOrDefault()?.Item2;
if (focus != null)
if (!string.IsNullOrWhiteSpace(e.Text))
{
focus.RaiseEvent(new RoutedEventArgs(AccessKeyHandler.AccessKeyPressedEvent));
var text = e.Text.ToUpper();
var focus = this.registered
.Where(x => x.Item1 == text && x.Item2.IsEffectivelyVisible)
.FirstOrDefault()?.Item2;
if (focus != null)
{
focus.RaiseEvent(new RoutedEventArgs(AccessKeyHandler.AccessKeyPressedEvent));
}
e.Handled = true;
}
}
}

82
Perspex.Input/AccessKeyHandler.cs

@ -10,6 +10,7 @@ namespace Perspex.Input
using System.Collections.Generic;
using System.Linq;
using Perspex.Interactivity;
using Perspex.VisualTree;
/// <summary>
/// Handles access keys for a window.
@ -40,6 +41,11 @@ namespace Perspex.Input
/// </summary>
private bool showingAccessKeys;
/// <summary>
/// Whether to ignore the Alt KeyUp event.
/// </summary>
private bool ignoreAltUp;
/// <summary>
/// Gets or sets the window's main menu.
/// </summary>
@ -64,6 +70,7 @@ namespace Perspex.Input
this.owner = owner;
this.owner.AddHandler(InputElement.KeyDownEvent, this.OnPreviewKeyDown, RoutingStrategies.Tunnel);
this.owner.AddHandler(InputElement.KeyDownEvent, this.OnKeyDown, RoutingStrategies.Bubble);
this.owner.AddHandler(InputElement.KeyUpEvent, this.OnPreviewKeyUp, RoutingStrategies.Tunnel);
this.owner.AddHandler(InputElement.PointerPressedEvent, this.OnPreviewPointerPressed, RoutingStrategies.Tunnel);
}
@ -98,7 +105,7 @@ namespace Perspex.Input
}
/// <summary>
/// Handles the Alt key being pressed in the window.
/// Called when a key is pressed in the owner window.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
@ -106,19 +113,61 @@ namespace Perspex.Input
{
if (e.Key == Key.LeftAlt)
{
this.owner.ShowAccessKeys = this.showingAccessKeys = true;
if (this.MainMenu == null || !this.MainMenu.IsOpen)
{
// When Alt is pressed without a main menu, or with a closed main menu, show
// access key markers in the window (i.e. "_File").
this.owner.ShowAccessKeys = this.showingAccessKeys = true;
}
else
{
// If the Alt key is pressed and the main menu is open, close the main menu.
this.CloseMenu();
this.ignoreAltUp = true;
}
// We always handle the Alt key.
e.Handled = true;
}
}
/// <summary>
/// Called when a key is pressed in the owner window.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
protected virtual void OnKeyDown(object sender, KeyEventArgs e)
{
bool menuIsOpen = this.MainMenu?.IsOpen == true;
if (e.Key == Key.Escape && menuIsOpen)
{
// When the Escape key is pressed with the main menu open, close it.
this.CloseMenu();
e.Handled = true;
}
else if ((KeyboardDevice.Instance.Modifiers & ModifierKeys.Alt) != 0)
else if ((KeyboardDevice.Instance.Modifiers & ModifierKeys.Alt) != 0 || menuIsOpen)
{
// If any other key is pressed with the Alt key held down, or the main menu is open,
// find all controls who have registered that access key.
var text = e.Text.ToUpper();
var focus = this.registered
var matches = this.registered
.Where(x => x.Item1 == text && x.Item2.IsEffectivelyVisible)
.FirstOrDefault()?.Item2;
.Select(x => x.Item2);
// If the menu is open, only match controls in the menu's visual tree.
if (menuIsOpen)
{
matches = matches.Where(x => this.MainMenu.IsVisualParentOf(x));
}
var match = matches.FirstOrDefault();
if (focus != null)
// If there was a match, raise the AccessKeyPressed event on it.
if (match != null)
{
focus.RaiseEvent(new RoutedEventArgs(AccessKeyPressedEvent));
match.RaiseEvent(new RoutedEventArgs(AccessKeyPressedEvent));
e.Handled = true;
}
}
}
@ -133,9 +182,13 @@ namespace Perspex.Input
switch (e.Key)
{
case Key.LeftAlt:
if (this.showingAccessKeys && this.MainMenu != null)
if (this.ignoreAltUp)
{
this.ignoreAltUp = false;
}
else if (this.showingAccessKeys && this.MainMenu != null)
{
this.MainMenu.OpenMenu();
this.MainMenu.Open();
e.Handled = true;
}
@ -143,7 +196,7 @@ namespace Perspex.Input
case Key.F10:
this.owner.ShowAccessKeys = this.showingAccessKeys = true;
this.MainMenu.OpenMenu();
this.MainMenu.Open();
e.Handled = true;
break;
}
@ -161,5 +214,14 @@ namespace Perspex.Input
this.owner.ShowAccessKeys = false;
}
}
/// <summary>
/// Closes the <see cref="MainMenu"/> and performs other bookeeping.
/// </summary>
private void CloseMenu()
{
this.MainMenu.Close();
this.owner.ShowAccessKeys = this.showingAccessKeys = false;
}
}
}

14
Perspex.Input/IMainMenu.cs

@ -9,11 +9,21 @@ namespace Perspex.Input
/// <summary>
/// Defines the interface for a window's main menu.
/// </summary>
public interface IMainMenu
public interface IMainMenu : IVisual
{
/// <summary>
/// Gets a value indicating whether the menu is open.
/// </summary>
bool IsOpen { get; }
/// <summary>
/// Closes the menu.
/// </summary>
void Close();
/// <summary>
/// Opens the menu in response to the Alt/F10 key.
/// </summary>
void OpenMenu();
void Open();
}
}

5
Perspex.SceneGraph/VisualTree/VisualExtensions.cs

@ -117,5 +117,10 @@ namespace Perspex.VisualTree
return visual;
}
public static bool IsVisualParentOf(this IVisual visual, IVisual target)
{
return target.GetVisualAncestors().Any(x => x == visual);
}
}
}

Loading…
Cancel
Save