Yesterday I discovered a situation wherein a keyboard ShortCut did not fire when I was expecting it to.
The specific situation was: I pressed the ShortCut key combination for an Action of an ActionList on an MDI child, while a side bar on the MDI form was focussed.
I always was under the impression that ShortCuts would work globally. In exactly which circumstances do or do they not fire?
That's a deceptively simple question with a surprisingly long answer. First I will deal with some basics and then follow the ShortCut through the VCL code to finally arrive at - I hope - a satisfying conclusion.
A ShortCut represents a special keyboard combination of one or more keys that cause an operation. Special means special to the programmer who gives meaning to the specific key combination.
In Delphi a ShortCut is of type TShortCut which is declared as a whole number within the Word range (0..65535). A ShortCut is often constructed out of several keys, e.g.:
CTRL+K = scCtrl + Ord('K') = 16384 + 75 = 16459.
ShortCuts can be assigned to the ShortCut or SecondaryShortCuts property of an Action or to the ShortCut property of a MenuItem, thus invoking that Action's OnExecute event or MenuItem's OnClick event when the ShortCut's keyboard combination is pressed.
For an Action's ShortCut to be processed, it is required that the Action is enabled, and added to a not suspended ActionList or attached to an enabled MenuItem. Likewise, for a MenuItem's ShortCut to be processed, it is required that the MenuItem is added to a Menu.
ShortCuts can also be interpreted from the Application's, a Form's or from an ApplicationEvents' OnShortCut event. Within those events, the Msg parameter holds the keycode in its CharCode member, and possibly special keys like Shift, Ctrl or Alt can be extracted using GetKeyState:
procedure TForm1.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
if (Msg.CharCode = Ord('K')) and (GetKeyState(VK_CONTROL) < 0) then
begin
Caption := 'CTRL+K pressed';
Handled := True;
end;
end;
If the Handled parameter is set to True, any subsequent processing for the key will be skipped.
The VCL does not keep a list of all specified ShortCuts. (How could it?). Thus all keystrokes could potentially be a ShortCut. And that is exactly how the VCL interprets ShortCuts: by evaluating every key that is pressed.
Peter Below wrote an excellent and comprehensive article dealing with A Key's Odyssey through the VCL. In short, a ShortCut is catched as follows:
TApplication.Run picks up every Windows message send to the application,TApplication.ProcessMessage calls IsKeyMsg which passes the message - if a WM_KEYDOWN message - on to the CN_KEYDOWN message handler of the focussed Control.TWinControl.CNKeyDown examines whether the key is a menu key (we'll see the definition of this menu is beyond a physical Menu):
TWinControl.IsMenuKey first examines whether the key is a ShortCut within the Control's or one of its parent's PopupMenu,
TMenu.IsShortCut 1) traverses all its (sub)menu items and calls the OnClick event handler of the enabled MenuItem with the ShortCut, if any,TCustomForm.IsShortCut 2),
OnShortCut event of the Form is called, if assigned,FActionLists. That was considered a bug and as from BDS2006, the field was eliminated and the ActionLists could be indirectly owned by the Form too.
TCustomActionList.IsShortCut traverses all its Actions and calls HandleShortCut of the enabled Action with the ShortCut set in its ShortCut or SecondaryShortCuts property, if any,Application.IsShortCut is called (via CM_APPKEYDOWN),
OnShortCut event of the Application is fired, this includes OnShortCut events of all ApplicationEvents components within the project, if any assigned,IsShortCut routine of the MainForm (see 2)), but only when the MainForm is enabled. E.g. when the active Form is a modal Form, then the MainForm will be disabled. This will fire the OnShortCut event of the MainForm or will traverse all directly or indirectly owned ActionLists of the MainForm (depending on Delphi version as mentioned above).When it is:
OnShortCut event of the currently active Form or of the MainForm, but only when the MainForm is enabled,OnShortCut event of the Applicaton or any ApplicationEvents components.When it is set for:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With