Фаза событий
Обычно активные события (evKeyDown и evCommand) получают и обрабатывают видимые элементы, принадлежащие цепочке активности. Однако часто возникают ситуации, когда необходимо, чтобы активное событие обработал неактивный элемент. Например, если на экране активно окно скроллера с полосами скроллинга, то события от клавиатуры будут передаваться окну. Как заставить в этом случае полосы реагировать на нажатие клавиш PgUp или PgDn? Для этого в Turbo Vision предусмотрен специальный механизм, основанный на так называемой фазе события. Когда модальный элемент получает событие, его передача выполняется в следующей последовательности:
- событие посылается в Z-порядке всем видимым элементам, которые принадлежат модальному элементу и у которых поле Options имеет установленный флаг ofPreProcess;
- если событие не очищено ни одним из них, оно посылается активным элементам (по цепочке активности);
- если событие все еще не очищено, оно посылается в Z-порядке всем видимым элементам, у которых установлен флаг ofPostProcess.
Таким образом, Вы должны установить флаги ofPreProcess или ofPostProcess (или оба вместе) при инициации видимого элемента, если хотите, чтобы он мог получить активное событие до или после (или и до и после) того, как его получат активные элементы.
Для предыдущего примера необходимо инициировать полосы скроллинга с установленными флагами ofPostProcess, если требуется, чтобы полосы "увидели" и обработали нажатие на клавиши смещения курсора, PgUp, PgDn и т.д. Разумеется, в этом случае полосы получат событие evKeyDown только при условии, что скроллер сам не обработает это событие.
В некоторых ситуациях элемент, перехватывающий событие и до, и после активных элементов, должен модифицировать свое поведение в зависимости от фазы события. Рассмотрим такой типичный пример. Пусть в программе создано диалоговое окно, имеющее строку ввода и три кнопки, для которых определены командные клавиши Q, W, Е. Как добиться того, чтобы эти клавиши использовались в качестве командных клавиш, т.е. приводили к "нажатию" соответствующих кнопок при условии, что активна любая кнопка, а в сочетании с клавишей Аlt – если активна строка ввода (именно так используются командные клавиши в диалоговом окне среды Турбо Паскаля)?
Если инициировать кнопки с флагом ofPreProcess, они смогут без труда определить факт нажатия на командную клавишу, однако в строке ввода пользователь не сможет ввести буквы Q, W и E, так как они будут перехвачены кнопками до того, как событие от клавиши получит строка ввода. Если инициировать кнопки с флагом ofPostProcess, пользователь не сможет использовать сочетания Аlt+<клавиша> для нажатия кнопки, если активна строка ввода: все события evKeyDown будут в этом случае направляться в строку. Решение очевидно: нужно определить оба флага, но на препроцессорной фазе следует проверять ввод Аlt+<клавиша>, а на постпроцессорной – <клавиша>.
Для реализации этих проверок обработчик событий объекта TButton должен каким-то образом определить текущую фазу события. С этой целью в любой группе предусмотрено поле Phase. Это поле доступно только для чтения и содержит одно из значений phPreProcess, phFocused или phPostProcess в зависимости от фазы события.
Следующий фрагмент иллюстрирует ту часть обработчика событий кнопок, которая проверяет командную клавишу:
evKeyDown: {Это часть оператора саsе} begin С: = HotKey(Title*); {Получаем в С букву клавиши} {Проверяем ALT-<клавиша>:} if (Event.KeyCode = GetALTCode(С)) or {Проверяем <клавиша>:} (Owner*.Phase = phPostProcess) and (C <> #0) and (UpCase(Event.CharCode) = C) or {Проверяем активность и нажатие пробела:} (State and sfFocused <> 0) and (Event.CharCode = ' ') then Press {Да, кнопка выбрана: выдаем нужную команду} end;
В этом фрагменте не показанная здесь функция HotKey выделяет из надписи на кнопке символ командной клавиши (он обрамляется символом "~"), а стандартная для Turbo Vision функция GetALTCode преобразует этот символ в расширенный код клавиш Аlt+<клавиша>. Метод TButton.Press реализует "нажатие" на кнопку и выдает сообщение evBroadcast с командой TButton.Command.
Отметим, что рассмотренный пример приведен только в качестве иллюстрации: стандартный объект TButton реализует свой обработчик событий именно таким образом и Вам нет нужды переопределять его (по умолчанию экземпляр TButton инициируется с установленными флагами ofPreProcess и ofPostProcess).