In a Delphi 10.4.2 Win32 VCL Application, on Windows10 X64 (German language) I set the shortcuts for some menu items programmatically:
mRasterizedDoubleSize.Shortcut := VK_ADD;
mRasterizedHalveSize.Shortcut := VK_SUBTRACT;
mRasterizedResetToOriginalSVGSize.Shortcut := VK_NUMPAD0;
This results in the following menu at run-time:
("ZEHNERTASTATUR" is German for NUMERIC KEYPAD)
Why "Zehnertastatur" (numeric keypad) is not shown for the third menu item?
How can I show "ZEHNERTASTATUR" (NUMERIC KEYPAD) for the menu item shortcut assigned with VK_NUMPAD0
?
I have filed a Quality Report for this bug in Vcl.Menus: https://quality.embarcadero.com/browse/RSP-33296 Please vote for it!
EDIT: I have tried Andreas' advice, but it does work only programmatically at run-time, not at design-time in the Object Inspector:
mRasterizedResetToOriginalSVGSize.Caption := mRasterizedResetToOriginalSVGSize.Caption + #9 + '0 (NUMPAD) ';
Isn't there a function that translates the word "NUMPAD" into the current system language at-run-time?
EDIT2: I have tried this to get the word for the VK_NUMPAD0
shortcut, but it only gives back the same "0" without the "NUMPAD" suffix:
var s: TShortCut;
s := ShortCut(VK_NUMPAD0, []);
CodeSite.Send('TformMain.FormCreate: ShortCutToText(s)', ShortCutToText(s));
EDIT3: I now have debugged Vcl.Menus
: The bug seems to be in Vcl.Menus.ShortCutToText
: While VK_ADD
($6B) is correctly translated by GetSpecialName(ShortCut)
, VK_NUMPAD0
($60) is NOT being translated by GetSpecialName(ShortCut)
!
EDIT4: I have found the solution:
function MyGetSpecialName(ShortCut: TShortCut): string;
var
ScanCode: Integer;
KeyName: array[0..255] of Char;
begin
Result := '';
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;
var s: System.Classes.TShortCut;
s := ShortCut(VK_NUMPAD0, []);
CodeSite.Send('ShortCutToText', MyGetSpecialName(s));
One approach is like this:
Use a TActionList
. This is good practice in general. Define your actions within this list, and then simply map them to menu items, buttons, check boxes, etc. The action list facility is one of the very best parts of the VCL IMHO.
Now, create an action named aResetZoom
with Caption = 'Reset zoom'#9'Numpad 0'
and NO ShortCut
. Put this on the menu bar.
Then, create a new action named aResetZoomShortcut
with the same OnExecute
(and possibly the same OnUpdate
) and shortcut Num 0
(set at design time or programmatically at run time). Don't put this on the main menu.
The result:
and the action is triggered when I press numpad 0 (but not the alpha 0).
There are many variants to this approach. Maybe you can make it work with a single action with no ShortCut
but with Num 0 in its SecondaryShortCuts
list. Or you can use the form's KeyPreview
and OnKeyPress
properties instead of the "dummy" action.
Many options. Choose the one that is best suited for your particular scenario.
Please note it is perfectly possibly to set captions with tabs at design time using the Object Inspector. See example video.
You can probably do localisation using the Win32 GetKeyNameText
function. The following code is adapted from the VCL:
var
name: array[0..128] of Char;
begin
FillChar(name, SizeOf(name), 0);
GetKeyNameText(MapVirtualKey(VK_NUMPAD0, 0) shl 16, @name[0], Length(name));
// string(name) now is 'NUM 0' on my system
That being said, personally I don't mind if shortcut descriptions are non-localized or manually localised -- like the rest of the application.
A clarification on how to use the localisation code:
procedure TForm5.FormCreate(Sender: TObject);
var
name: array[0..128] of Char;
NameAsANormalString: string;
begin
FillChar(name, SizeOf(name), 0);
GetKeyNameText(MapVirtualKey(VK_NUMPAD0, 0) shl 16, @name[0], Length(name));
NameAsANormalString := name;
ShowMessage(name);
end;
produces
on my system.