3

In a Delphi 10.4.2 Win32 VCL Application, and based on the question + solution here which provides a way to get the string representation of a Shortcut Key (but presumably with no possibility to also pass a SHIFTSTATE for the Shortcut Key) I wrote this code:

function MyGetSpecialShortcutName(ShortCut: TShortCut): string;
// gets shortcut name for e.g. VK_NUMPAD0 where TMenuItem.Shortcut gets the wrong shortcut name
var
  ScanCode: Integer;
  KeyName: array[0..255] of Char;
begin
  Result := '';
  FillChar(KeyName, SizeOf(KeyName), 0);
  ScanCode := Winapi.Windows.MapVirtualKey(LoByte(Word(ShortCut)), 0) shl 16;
  if ScanCode <> 0 then
  begin
    if Winapi.Windows.GetKeyNameText(ScanCode, KeyName, Length(KeyName)) <> 0 then
      Result := KeyName;
  end;
end;

function GetSpecialShortcutNameWithShiftState(const AScanCode: Word; const AShiftState: System.Classes.TShiftState = []): string;
begin
  Result := MyGetSpecialShortcutName(Vcl.Menus.ShortCut(AScanCode, AShiftState));
end;

Usage:

Result := GetSpecialShortcutNameWithShiftState(VK_A, [ssCTRL]);

However, the Result is "A" where the expected Result should be "CTRL+A".

How to get the string representation of a ShortCut Key including the SHIFTSTATE?

user1580348
  • 5,721
  • 4
  • 43
  • 105

1 Answers1

5

The OP wants the key names fully localised, but for completeness I first show that the VCL already has a function to obtain a partly unlocalised string, namely, ShortCutToText in the Menus unit:

ShortCutToText(ShortCut(Ord('A'), [ssShift, ssAlt]))

This returns Shift+Alt+A on all systems.

Now, using the Win32 function GetKeyNameText already mentioned in the Q, it is easy to obtain a fully localised shortcut string:

function GetKeyName(AKey: Integer): string;
var
  name: array[0..128] of Char;
begin
  FillChar(name, SizeOf(name), 0);
  GetKeyNameText(MapVirtualKey(AKey, 0) shl 16, @name[0], Length(name));
  Result := name;
end;

function ModifierVirtualKey(AModifier: Integer): Integer;
begin
  case AModifier of
    Ord(ssShift):
      Result := VK_SHIFT;
    Ord(ssCtrl):
      Result := VK_CONTROL;
    Ord(ssAlt):
      Result := VK_MENU;
  else
    Result := 0;
  end;
end;

function ShortcutToString(AKey: Integer; AShiftState: TShiftState = []): string;
begin
  Result := '';
  for var Modifier in AShiftState do
  begin
    var ModifierKey := ModifierVirtualKey(Ord(Modifier));
    if ModifierKey <> 0 then
      Result := Result + IfThen(not Result.IsEmpty, '+') + GetKeyName(ModifierKey);
  end;
  Result := Result + IfThen(not Result.IsEmpty, '+') + GetKeyName(AKey);
end;

(Here I use a IfThen overload from StrUtils.)

Now,

ShortcutToString(Ord('A'), [ssShift, ssAlt])

returns SKIFT+ALT+A on my Swedish system. SKIFT is, as you might already have guessed, the Swedish name for the SHIFT key.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • Trivia: virtually no German person knows the locale name for `SHIFT` (it's `UMSCHALT`) - everybody also uses this english term. Same for `ENTER`. On the contrary the locale `STRG` (for `CTRL`) is mostly assumed to stand for "Strong" or "String". – AmigoJack Mar 24 '21 at 14:42
  • @AmigoJack I think you are wrong. Not all Germans are stupid. – user1580348 Mar 24 '21 at 15:18
  • @AndreasRejbrand This is the most complete `ShortcutToString` function because in addition to the Key Name it also processes the Modifiers! Very nice code! – user1580348 Mar 24 '21 at 15:24