Когда огонь ShortCut?

Вчера я обнаружил ситуацию, когда клавиатура ShortCut не срабатывала, когда я ожидал ее.

Конкретная ситуация: я нажал комбинацию клавиш ShortCut для Action ActionList для дочернего MDI, в то время как боковая панель в форме MDI была сфокусирована.

У меня всегда создавалось впечатление, что ShortCuts будет работать во всем мире. В каких именно обстоятельствах они не стреляют?

Ответ 1

Это обманчиво простой вопрос с удивительно длинным ответом. Сначала я рассмотрю некоторые основы, а затем последую за ShortCut через код VCL, чтобы наконец прийти - надеюсь, - удовлетворительный вывод.

Что такое ShortCut?

ShortCut представляет собой специальную комбинацию клавиш из одной или нескольких клавиш, которые вызывают операцию. Специальные средства, специально предназначенные для программистов, которые придают смысл конкретной комбинации клавиш.

В Delphi ShortCut имеет тип TShortCut, который объявляется как целое число в диапазоне Word (0..65535). ShortCut часто строится из нескольких клавиш, например:

CTRL + K= scCtrl + Ord('K')= 16384 + 75= 16459.

Как указать ShortCut?

ShortCuts можно присвоить свойству ShortCut или SecondaryShortCuts свойства Action или для свойства ShortCut для MenuItem, тем самым вызывая событие Action OnExecute event или MenuItem OnClick, когда комбинация клавиш ShortCut.

Чтобы обработать Action ShortCut, требуется, чтобы действие было активировано и добавлено к не приостановленному списку действий или подключен к включенному элементу MenuItem. Аналогично, для обработки MenuItem ShortCut требуется, чтобы MenuItem был добавлен в Меню.

ShortCuts также можно интерпретировать из Application, Form или из событие ApplicationEvents 'OnShortCut. Внутри этих событий параметр Msg содержит код ключа в своем элементе CharCode, и, возможно, специальные клавиши, такие как Shift, CTRL или Alt, могут быть извлечены с помощью 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;

Если для параметра Handled установлено значение True, любая последующая обработка ключа будет пропущена.

Как выхватили ShortCut?

VCL не сохраняет список всех указанных ShortCuts. (Как это могло быть?). Таким образом, все нажатия клавиш могут быть ShortCut. И именно так VCL интерпретирует ShortCuts: оценивая каждый нажатый ключ.

Питер Ниже написал превосходную и всеобъемлющую статью, посвященную ключевой одиссее через VCL. Короче говоря, ShortCut отображается следующим образом:

  • TApplication.Run выбирает каждое сообщение Windows, отправляемое в приложение,
  • TApplication.ProcessMessage вызывает IsKeyMsg, который передает сообщение - если сообщение WM_KEYDOWN - на обработчик сообщения CN_KEYDOWN элемента с фокусом.

Как обрабатывается ShortCut?

TWinControl.CNKeyDown проверяет, является ли ключ клавишей меню (мы увидим, что определение этого меню выходит за пределы физического меню):

  • TWinControl.IsMenuKey сначала проверяет, является ли ключ ShortCut в элементе управления или одним из его родительских PopupMenu
    • TMenu.IsShortCut 1) перемещает все его пункты (под) и вызывает обработчик события OnClick включенного MenuItem с помощью ShortCut, если есть,
  • Если он не обрабатывается, он проверяет, является ли ключ ShortCut формы, на которой находится элемент управления, вызывая TCustomForm.IsShortCut 2),
    • Вызывается OnShortCut событие, если оно назначено,
    • Если он не обрабатывается, он проверяет, является ли ключ ShortCut в форме MainMenu (см. 1)), если таковой имеется,
    • Если он не обрабатывается, он отправляет ключ всем спискам действий, принадлежащим Form (все еще говорит об активной форме). До Delphi версии 10 (BDS2006) эти списки действий должны быть непосредственно принадлежат Форме и хранились в защищенном (что сделало возможным вмешательство в случае необходимости) поле FActionLists. Это было признано ошибкой, и с BDS2006 поле было удалено, и ActionLists может быть косвенно принадлежит Форме.
      • TCustomActionList.IsShortCut обходит все свои действия и вызывает HandleShortCut активированного действия с набором ShortCut в свойстве ShortCut или SecondaryShortCuts, если он
  • Если не обрабатывается, Application.IsShortCut вызывается (через CM_APPKEYDOWN),
    • Запускается OnShortCut событие, в том числе OnShortCut событий всех компонентов ApplicationEvents в проекте, если они назначены,
    • Если он не обрабатывается, он вызывает процедуру IsShortCut MainForm (см. 2)), но только при включенном MainForm. Например. когда активная форма является модальной формой, тогда MainForm будет отключен. Это вызовет событие OnShortCut MainFormили будет проходить все прямые или косвенно принадлежащие ActionLists MainForm (в зависимости от версии Delphi, как указано выше).

Итак, когда обрабатывается ShortCut?

Если это:

  • Установить для активированного MenuItem в PopupMenu, который прикреплен к текущему сосредоточенному элементу управления или любому из его родителей,
  • Установить для включенного MenuItem в MainMenu, который отображается в активной активной форме или в MainForm, но только при включенном MainForm,
  • Установить для активированного действия в не приостановленном ActionList, который принадлежит текущей активной форме или MainForm, но только при включенном MainForm,
  • Подключено к событию OnShortCut активной активной формы или MainForm, но только при включенном MainForm,
  • Отслеживается в событии OnShortCut приложения или любых компонентов ApplicationEvents.

А когда ShortCut не обрабатывается?

Когда он установлен для:

  • Отключено MenuItem,
  • Меню Menu без меню,
  • Элемент MenuItem в MainMenu, который не привязан к форме,
  • Элемент MenuItem в PopupMenu, прикрепленный к родному брату,
  • Отключенное действие,
  • Действие без ActionList,
  • Действие в списке действий, которое не принадлежит активной форме или MainForm. Например: другая форма, DataModule, приложение, модуль утилит и т.д.
  • Действие в списке действий, которое непосредственно не принадлежит активной форме или MainForm в версиях Delphi ниже BDS2006.