using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace Terminal.Gui;
///
/// Represents a helper to manipulate shortcut keys used on views.
///
public class ShortcutHelper {
// TODO: Update this to use Key, not KeyCode
private KeyCode shortcut;
///
/// This is the global setting that can be used as a global shortcut to invoke the action on the view.
///
public virtual KeyCode Shortcut {
get => shortcut;
set {
if (shortcut != value && (PostShortcutValidation (value) || value is KeyCode.Null)) {
shortcut = value;
}
}
}
///
/// The keystroke combination used in the as string.
///
public virtual string ShortcutTag => Key.ToString (shortcut, MenuBar.ShortcutDelimiter);
///
/// Return key as string.
///
/// The key to extract.
/// Correspond to the non modifier key.
static string GetKeyToString (KeyCode key, out KeyCode knm)
{
if (key == KeyCode.Null) {
knm = KeyCode.Null;
return "";
}
knm = key;
var mK = key & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask);
knm &= ~mK;
for (uint i = (uint)KeyCode.F1; i < (uint)KeyCode.F12; i++) {
if (knm == (KeyCode)i) {
mK |= (KeyCode)i;
}
}
knm &= ~mK;
uint.TryParse (knm.ToString (), out uint c);
var s = mK == KeyCode.Null ? "" : mK.ToString ();
if (s != "" && (knm != KeyCode.Null || c > 0)) {
s += ",";
}
s += c == 0 ? knm == KeyCode.Null ? "" : knm.ToString () : ((char)c).ToString ();
return s;
}
///
/// Allows to retrieve a from a
///
/// The key as string.
/// The delimiter string.
public static KeyCode GetShortcutFromTag (string tag, Rune delimiter = default)
{
var sCut = tag;
if (string.IsNullOrEmpty (sCut)) {
return default;
}
KeyCode key = KeyCode.Null;
//var hasCtrl = false;
if (delimiter == default) {
delimiter = MenuBar.ShortcutDelimiter;
}
string [] keys = sCut.Split (delimiter.ToString());
for (int i = 0; i < keys.Length; i++) {
var k = keys [i];
if (k == "Ctrl") {
//hasCtrl = true;
key |= KeyCode.CtrlMask;
} else if (k == "Shift") {
key |= KeyCode.ShiftMask;
} else if (k == "Alt") {
key |= KeyCode.AltMask;
} else if (k.StartsWith ("F") && k.Length > 1) {
int.TryParse (k.Substring (1).ToString (), out int n);
for (uint j = (uint)KeyCode.F1; j <= (uint)KeyCode.F12; j++) {
int.TryParse (((KeyCode)j).ToString ().Substring (1), out int f);
if (f == n) {
key |= (KeyCode)j;
}
}
} else {
key |= (KeyCode)Enum.Parse (typeof (KeyCode), k.ToString ());
}
}
return key;
}
///
/// Lookup for a on range of keys.
///
/// The source key.
/// First key in range.
/// Last key in range.
public static bool CheckKeysFlagRange (KeyCode key, KeyCode first, KeyCode last)
{
for (uint i = (uint)first; i < (uint)last; i++) {
if ((key | (KeyCode)i) == key) {
return true;
}
}
return false;
}
///
/// Used at key down or key press validation.
///
/// The key to validate.
/// true if is valid.falseotherwise.
public static bool PreShortcutValidation (KeyCode key)
{
if ((key & (KeyCode.CtrlMask | KeyCode.ShiftMask | KeyCode.AltMask)) == 0 && !CheckKeysFlagRange (key, KeyCode.F1, KeyCode.F12)) {
return false;
}
return true;
}
///
/// Used at key up validation.
///
/// The key to validate.
/// true if is valid.falseotherwise.
public static bool PostShortcutValidation (KeyCode key)
{
GetKeyToString (key, out KeyCode knm);
if (CheckKeysFlagRange (key, KeyCode.F1, KeyCode.F12) ||
((key & (KeyCode.CtrlMask | KeyCode.ShiftMask | KeyCode.AltMask)) != 0 && knm != KeyCode.Null)) {
return true;
}
Debug.WriteLine ($"WARNING: {Key.ToString (key)} is not a valid shortcut key.");
return false;
}
}