// -----------------------------------------------------------------------
//
// Copyright 2015 MIT Licence. See licence.md for more information.
//
// -----------------------------------------------------------------------
namespace Perspex.Input
{
using System;
using System.Collections.Generic;
using System.Linq;
using Perspex.Interactivity;
///
/// Handles access keys for a window.
///
public class AccessKeyHandler : IAccessKeyHandler
{
///
/// Defines the AccessKeyPressed attached event.
///
public static readonly RoutedEvent AccessKeyPressedEvent =
RoutedEvent.Register(
"AccessKeyPressed",
RoutingStrategies.Bubble,
typeof(AccessKeyHandler));
///
/// The registered access keys.
///
private List> registered = new List>();
///
/// The window to which the handler belongs.
///
private IInputRoot owner;
///
/// Whether access keys are currently being shown;
///
private bool showingAccessKeys;
///
/// Gets or sets the window's main menu.
///
public IMainMenu MainMenu { get; set; }
///
/// Sets the owner of the access key handler.
///
/// The owner.
///
/// This method can only be called once, typically by the owner itself on creation.
///
public void SetOwner(IInputRoot owner)
{
Contract.Requires(owner != null);
if (this.owner != null)
{
throw new InvalidOperationException("AccessKeyHandler owner has already been set.");
}
this.owner = owner;
this.owner.AddHandler(InputElement.KeyDownEvent, this.OnPreviewKeyDown, RoutingStrategies.Tunnel);
this.owner.AddHandler(InputElement.KeyUpEvent, this.OnPreviewKeyUp, RoutingStrategies.Tunnel);
this.owner.AddHandler(InputElement.PointerPressedEvent, this.OnPreviewPointerPressed, RoutingStrategies.Tunnel);
}
///
/// Registers an input element to be associated with an access key.
///
/// The access key.
/// The input element.
public void Register(char accessKey, IInputElement element)
{
var existing = this.registered.FirstOrDefault(x => x.Item2 == element);
if (existing != null)
{
this.registered.Remove(existing);
}
this.registered.Add(Tuple.Create(accessKey.ToString().ToUpper(), element));
}
///
/// Unregisters the access keys associated with the input element.
///
/// The input element.
public void Unregister(IInputElement element)
{
foreach (var i in this.registered.Where(x => x.Item2 == element).ToList())
{
this.registered.Remove(i);
}
}
///
/// Handles the Alt key being pressed in the window.
///
/// The event sender.
/// The event args.
protected virtual void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftAlt)
{
this.owner.ShowAccessKeys = this.showingAccessKeys = true;
e.Handled = true;
}
else if ((KeyboardDevice.Instance.Modifiers & ModifierKeys.Alt) != 0)
{
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(AccessKeyPressedEvent));
}
}
}
///
/// Handles the Alt/F10 keys being released in the window.
///
/// The event sender.
/// The event args.
protected virtual void OnPreviewKeyUp(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.LeftAlt:
if (this.showingAccessKeys && this.MainMenu != null)
{
this.MainMenu.OpenMenu();
e.Handled = true;
}
break;
case Key.F10:
this.owner.ShowAccessKeys = this.showingAccessKeys = true;
this.MainMenu.OpenMenu();
e.Handled = true;
break;
}
}
///
/// Handles pointer presses in the window.
///
/// The event sender.
/// The event args.
protected virtual void OnPreviewPointerPressed(object sender, PointerEventArgs e)
{
if (this.showingAccessKeys)
{
this.owner.ShowAccessKeys = false;
}
}
}
}