Цель Turbo Vision обеспечить Вас рабочей оболочкой для Ваших прикладных программ, так чтобы Вы могли сконцентрироваться на наращивании "мяса" Ваших программ. Два главных инструмента Turbo Vision - это поддержка построения окон и управление событиями. Глава 4 объясняет видимые элементы, а эта глава описывает построение программ вокруг событий.Воплощение Turbo Vision в жизнь.
Мы уже описали программы Turbo Vision, управляемые событиями и кратко определили как Ваша программа должна реагировать на события.Чтение ввода пользователя.
В традиционных программах на Паскале Вы обычно пишете цикл, который читает ввод пользователя с клавиатуры, мышки и от других устройств и Вы принимаете решение на основе этого ввода внутри цикла. Вы будете вызывать процедуры или функции или переходить на этот цикл для того чтобы снова читать ввод пользователя: repeat B := ReadKey; case B of 'i': InvertArray; 'e': EditArrayParams; 'g': GraphicDisplay; 'q': Quit := true; end; until Quit; Программа управляемая событиями не очень отличается от обычной. В самом деле, трудно вообразить интерактивную программу, которая работает по другому. Однако, с точки зрения программиста программа управляемая событиями выглядит иначе. В программах на Turbo Vision Вы больше не читаете ввод пользователя поскольку Turbo Vision делает это вместо Вас. Он собирает ввод в записи Паскаля, называемые событиями и передает события соответствующим видимым элементам программы. Это означает, что Вашему коду только требуется знать как обработать соответствующий ввод. Например, если пользователь отмечает мышкой в неактивном окне, Turbo Vision читает ввод мышки, помещает его в запись события и посылает запись события в неактивное окно. Если Вы имеете опыт традиционного программирования Вы можете подумать сейчас: "Хорошо, я не должен читать ввод от пользователя. Я должен изучить как читать запись события об отметке мышкой и как сказать неактивному окну стать активным." В действительности Вы не должны писать так много кода. Видимые элементы сами могут обрабатывать большую часть ввода пользователя. Окно знает как открыться, закрыться, переместиться, стать выбранным, изменить размер и многое другое. Меню знает как открыться, взаимодействовать с пользователем и закрыться. Кнопки знают как нажиматься, как взаимодействовать между собой и как изменять цвет. Полосы скроллинга как функционировать. Неактивное окно может сделать себя активным без какой либо Вашей помощи. Что Вы должны делать как программист? Вам нужно определить новые видимые элементы с новыми функциями, которые должны знать об определенных видах событий заданных Вами. Вы также научите Ваши видимые элементы откликаться на стандартные команды и даже генерировать собственные команды (сообщения) другим видимым элементам. Этот механизм уже есть: все что Вы делаете - это генерируете команды и говорите видимым элементам что нужно сделать когда они увидят их. Как именно выглядят события в Вашей программе и как Turbo Vision обрабатывает их?Природа событий.
События лучше всего представить себе как небольшие пакеты информации, описывающие отдельные случаи (ситуации) на которые Ваша программа должна реагировать. Каждое нажатие клавиши, каждое действие мышки и любое условие, генерируемое другими компонентами программы, это отдельное событие. События не могут быть разбиты на более мелкие части; так когда пользователь набирает слово - это не одно событие, а серия отдельных событий от клавиш. В объектно-ориентированном мире Turbo Vision Вы вероятно думаете, что события это тоже объекты. Это не так. Сами события не производят действий; они только содержат информацию для других объектов и поэтому представлены записями. Ядром каждой записи типа событие является поле What типа слово. Числовое значение поля What описывает вид события, а оставшаяся часть записи типа событие содержит специальную информацию об этом событии. Скан код клавиатуры для события от клавиш, информация о позиции мышки и состоянии ее кнопок для события от мышки и т.д. Поскольку различные виды событий передаются предназначаемым им объектам различными способами, давайте вначале рассмотрим виды событий распознаваемые в Turbo Vision.Виды событий.
Давайте посмотрим на возможные значения Event.What. Существуют 4 основных базовых класса событий: события от мышки, события от клавиатуры, события сообщений и "пустые" события. Каждый класс имеет определенную маску, так что Ваш объект может быстро определить какой тип события случился, не заботясь о его конкретном виде. Например, вместо того, чтобы проверять на 4 различных вида событий от мышки, Вы можете просто проверить, есть ли флаг события в маске. Вместо if Event.What and (evMouseDown or evMouseUp or evMouseMove or evMouseAuto) <> 0 then . Вы можете использовать if Event.What and evMouse <> 0 then . Доступны следующие маски: evNothing (для "пустых" событий), evMouse, для событий от мышки; evKeyBoard, для событий от клавиатуры и evMessage для сообщений. Биты маски событий определены на рис.5.5. Рис. 5.1. Биты поля TEvent.What. +--------- Event Flags ---------+ msb lsb +-+-+-+-+-+-+-+------------------- evMessage = $FF00 | | | | | | | | +----------- evKeyboard = $0010 | | | | | | | | | +-+-+-+--- evMouse = $000F +++++++++++++++++=+=+=+++++++++++ +=+=+=+=+=+=+++++=+=+=+++++++++++ | | | | | | +--- evMouseDown = $0001 | | | | | +----- evMouseUp = $0002 | | | | +------- evMouseMove = $0004 | | | +--------- evMouseAuto = $0008 | | +----------- evKeyDown = $0010 | +------------------- evCommand = $0100 +--------------------- evBroadcast = $0200События от мышки.
Существуют 4 вида событий от мышки: нажатие или отпускание любой кнопки, изменение позиции или "авто" событие. При нажатии на кнопку мышки генерируется событие evMouseDown. Когда кнопка отпускается генерируется событие evMouseUp. Перемещение мышки генерирует событие evMouseMove. Если Вы держите кнопку нажатой, Turbo Vision периодически генерирует событие evMouseAvto, позволяяя Вашей программе такие действия как повторяющийся скроллинг. Все записи событий от мышки включают позицию мышки, так что объект, обрабатывающий событие знает где находилась мышка в этот момент.События от клавиатуры.
События от клавиатуры намного проще. Когда Вы нажимаете клавишу, Turbo Vision генерирует событие evDown, которое содержит информацию о нажатой клавише.События сообщений.
События сообщений бывают 3 видов: команды, общие сообщения и пользовательские сообщения. Они отличаются обработкой как будет объяснено позднее. Команды помечаются в поле What через evCommand, общие сообщения через evBroadcast и пользовательские сообщения константой определенной пользователем."Пустые" события.
"Пустые" события это в действительности мертвые события. Оно перестало быть событием, поскольку полностью обработано. Если поле What в записи события содержит значение evNothing, то эта запись не содержит полезной информации, которая требует обработки. Когда объект Turbo Vision заканчивает обработку события, он вызывает метод ClearEvent, который устанавливает поле What в evNothing, указывая что событие было обработано. Объекты должны просто игнорировать события evNothing, поскольку они уже обработаны другим объектом.События и команды.
Большинство событий в конечном итоге преобразуются в команды, например, отметка мышкой элемента в строке статуса генерирует событие от мышки. Когда оно поступает в объект "строка статуса", этот объект откликается на событие от мышки, генерируя событие-команду со значением поля Command, определяемым командой связанной с элементом строки статуса. Нажатие мышкой на Alt-X Exit генерирует команду cmQuit, которую программа интерпретирует как инструкцию закрытия системы и завершения.Передача событий.
Видимые элементы Turbo Vision работают по принципу "Говори только когда к тебе обратятся". Это не похоже на активный поиск ввода, они скорее пассивно ожидают, когда монитор событий скажет им, что произошло событие, на которое требуется отклик. Для того чтобы Ваша программа на Turbo Vision делала то что Вы хотите, Вы должны не только сказать видимым элементам, что делать, когда случается определенное событие, но и должны понимать как события передаются в Ваши видимые элементы. Главное в получении событий в нужном месте - это правильная маршрутиризация событий. Некоторые события передаются всем элементам программы, другие направляются точно в определенные части программы.Откуда приходят события.
Как сказано в главе 1, главный цикл обработки в TApplication, метод Run вызывает TGroup.Execute, которая основана на цикле: var E: TEvent; E.What := evNothing; { указывает что нет событий } repeat if E.What <> evNothing then EventError(E); GetEvent(E); { упаковывает запись события } HandleEvent(E); { направляет событие в нужное место } until EndState <> Continue; По существу GetEvent смотрит вокруг и проверяет, не случилось ли что либо, что должно быть событием. Если случилось, GetEvent создает соответствующую запись события. Затем HandleEvent направляет событие в соответствующие видимые элементы. Если событие не обработано (и не очищено) за время пока оно не вернется в этот цикл, то вызывается EventError, чтобы указать на ненужное событие. По умолчанию EventError ничего не делает.Куда идут события.
События всегда начинают свой путь с текущего модального видимого элемента. Для нормальных операций это обычно означает объект "Ваша программа". Когда Вы выполняете модальное диалоговое окно, это диалоговое окно - модальный видимый элемент. В любом случае, модальный видимый элемент начинает обработку события. Куда событие пойдет дальше зависит от природы события. События передаются одним из трех способов, в зависимости от вида события. Возможны три вида передач: позиционировнные, активные и общие. Важно понимать как передается каждый вид события.Позиционированные события.
Позиционированные события это всегда события от мышки (evMouse). Модальный видимый элемент получает позиционированное событие первым и начинает просматривать свои подэлементы в Z-порядке до тех пор пока не найдет подэлемент содержащий позицию, в которой возникло событие. (Z-порядок объяснен в главе 4). Затем модальный видимый элемент передает событие этому видимому элементу. Поскольку видимые элементы могут перекрываться, возможно что эта точка принадлежит более чем одному видимому элементу. Следование в Z-порядке гарантирует что это событие получит самый верхний видимый элемент, включающий эту позицию. Этот процесс продолжается до тех пор пока не может быть найден видимый элемент для передачи события, либо из-за того, что это терминальный видимый элемент (не имеет подэлементов), либо не существует подэлементов включающих позицию этого события (например отмечено пустое пространство в диалоговом окне). В этот момент событие достигло объекта, где возникло это позиционированное событие и объект обрабатывает событие.Активные события.
Активные события это нажатия клавиш (evKeyDown) или команды (evCommand) и передаются вниз по активной цепочке. (Детальное описание активных видимых элементов и активной цепочки приведено в "Выбранные и активные видимые элементы" главы 4). Текущий модальный видимый элемент получает активное событие первым и передает его в выбранный подэлемент. Если этот подэлемент содержит выбранный подэлемент, он передает событие ему. Процесс продолжается до тех пор пока не будет достигнут терминальный видимый элемент: это активный видимый элемент. Активный видимый элемент получает и обрабатывает активное событие. Если активный видимый элемент не знает как обработать какое-то из полученных событий, он передает событие вверх по активной цепочке своему владельцу. Процесс повторяется до тех пор пока событие не будет обработано или снова не достигнет модального видимого элемента. Если модальный видимый элемент не знает как обработать вернувшееся событие, он вызывает EventError. Эта ситуация - ненужное событие. (Неактивные видимые элементы могут обрабатывать активные события. См. раздел "Фаза".) События от клавиатуры иллюстрируют принцип активных событий совершенно ясно. Например, в интегрированной среде Turbo Pascal Вы можете открыть несколько файлов в окнах редактора. Когда Вы нажимаете клавишу, Вы знаете какой файл получит этот символ. Давайте посмотрим как Turbo Vision обеспечивает это. Нажатие клавиши генерирует событие evKeyDown, которое поступает в текущий модальный видимый элемент - объект TApplication. TApplication посылает событие своему выбранному элементу - панели экрана (панель экрана - всегда выбранный элемент TApplication). Панель экрана посылает событие своему выбранному видимому элементу - активному окну (с двойной рамкой). Окно редактора также имеет подэлементы - рамку, интерьер скроллинга и две полосы скроллинга. Из них может быть выбран только интерьер (и следовательно выбран по умолчанию), поэтому событие от клавиатуры приходит в него. Интерьер - редактор не имеет подэлементов и должен решать как обработать символ в событии evKeyDown.Общие события.
Общие события это обычно общие сообщения или пользовательские сообщения. Общие события не направляются как позиционированные или активные события. По определению обшие события не знают своего назначения и посылаются всем видимым подэлементам текущего модального видимого элемента. Текущий модальный видимый элемент получает событие и начинает передавать его подэлементам в Z-порядке. Если любой из этих пдэлементов - группа, он также передает событие своим подэлементам и также в Z-порядке. Процесс продолжается до тех пор пока все видимые элементы принадлежащие (прямо или косвенно) модальному видимому элементу не получат сообщения. Общие сообщения обычно используются для взаимодействия видимых элементов. Например, когда Вы отмечаете полосу скроллинга в просмотре файла, полоса скроллинга должна сказать видимому элементу просмотра, что он должен показать другую часть себя. Это значит что когда видимый элемент выдает общее сообщение "Я изменился!", другие видимые элементы, включая текст, будут получать сообщение и реагировать на него. Дополнительные детали смотри в разделе "Межэлементное взаимодействие". Примечание: Общие сообщения могут быть направлены объекту функцией Message.События определенные пользователем.
Как только Вы ознакомитесь с Turbo Vision и событиями, Вы захотите определить новую категорию событий, используя старшие биты поля What записи события. По умолчанию Turbo Vision направляет такие события как общие события. Но Вам может понадобиться сделать общие события активными или позиционированными и Turbo Vision предоставляет механизм, позволяющий сделать это. Turbo Vision определяет две маски Positional и Focused, которые содержат биты соответствующие событиям в поле What записи события, которые должны быть направлены как позиционированные или активные соответственно. По умолчанию Positional содержит все биты evMouse, а Focused содержит evKeyBoard. Если Вы определяете другой бит в новом виде события, которое Вы хотите направить как позиционированное или активное, Вы просто прибавляете бит к соответствующей маске. (Манипуляция битами в маске объясняется в главе 10).Маскирование событий.
Каждый видимый элемент имеет битовое поле EventMask, которое используется для определения, какие события будет обрабатывать видимый элемент. Биты EventMask соответствуют битам поля TEvent.What. Если бит для данного вида события установлен, видимый элемент будет принимать этот вид события для обработки. Если бит для данного вида события очищен, видимый элемент будет игнорировать этот вид событий.Фаза.
Возникают ситуации когда Вы хотите чтобы видимый элемент отличный от активного обрабатывал активные события (особенно от клавиш). Например, при просмотре текста в скроллингуемом окне Вам может понадобиться использовать клавиши для скроллинга текста, но поскольку текстовое окно это активный видимый элемент, события от клавиш приходят к нему, а не к полосам скроллинга, которые могут скроллинговать видимый элемент. Однако Turbo Vision предоставляет механизм позволяющий видимым элементам отличным от активного элемента видеть и обрабатывать активные события. Хотя передача описанная в разделе "Активные события" абсолютно корректна, существуют два исключения при точном прохожденни активной цепочки. Когда модальный видимый элемент получает для обработки активное событие, передача выполняется в три "фазы": - Событие посылается всем подэлементам (В Z-порядке), у которых установлен флаг ofPreProcess. - Если событие не очищено ни одним из них, это событие посылается в активный видимый элемент. - Если событие все еще не очищено, оно посылается в Z-порядке всем подэлементам с установленным флагом ofPostProcess. Так в предыдущем примере, если полосе скроллинга необходимо видеть клавиши, которые предназначены активному текстовому элементу, полоса скроллинга должна быть инициализирована с установленным флагом ofPreProcess. Если Вы посмотрите на программу TVDEMO09.PAS Вы заметите что полосы скроллинга для видимых элементов интерьера имеют установленными биты ofPostProcess. Если Вы модифицируете код так, чтобы эти биты не устанавливались, скроллинг от клавиатуры будет запрещен. Заметим также что в этом примере нет большой разницы, что Вы установите - ofPreProcess или ofPostProcess. Поскольку активный видимый элемент в этом случае не обрабатывает это событие (сам TScroller ничего не делает с нажатиями клавиш), полосы скроллинга могут видеть эти события как до так и после того, как событие передается в скроллер. Однако лучше использовать в таких случаях ofPostProcess, поскольку он предоставляет большую гибкость. Позже Вы можете добавить в интерьер код, который проверяет нажатие клавиш, но если эти нажатия будут использованы полосой скроллинга, до того как они будут получены активным элементом (ofPreProcess), Ваш интерьер никогда не будет реагировать на них. Примечание: Хотя Вам может требоваться перехват активных событий до того как активный элемент получает их, хорошая мысль, оставить как можно больше доступных возможностей, поскольку Вы (или кто-то еще) можете наследовать нечто новое от этого объекта в будущем.Поле Phase.
Каждая группа имеет поле Phase, которое принимает три значения: phFocused, phPreProcess и phPostProcess. Проверяя флаг Phase владельца, видимый элемент может сказать, будет ли обрабатываться событие активное событие до, во время или после передачи. Это иногда необходимо, т.к. некоторые видимые элементы ожидают различные события или реагируют на одинаковые события по разному, в зависимости от фазы. Рассмотрим случай простого диалогового окна, которое содержит строку ввода и клавишу с меткой "All right" с коротким набором "A". С управляющими элементами обычного диалогового окна Вы в действительности не имеете дело с фазой. Большинство элементов управления имеют ofPostProcess установленным по умолчанию, поэтому нажатия клавиш (активные события) будут передаваться им, позволяя перехватывать активность, если была нажата короткая клавиша. Нажатие "А" передает активность кнопке "All right". Теперь предположим что активна строка ввода, так что нажатия клавиш обрабатываются и вставляются строкой ввода. Нажатие клавиши "А" вставляет "А" в строку ввода и кнопка никогда не увидит это событие, поскольку активный видимый элемент обработал ее. Если вы зададите предварительную обработку клавиши "А" для кнопки, она сможет перехватывать короткую клавишу до того как ее обработает активный видимый элемент. К сожалению это не даст Вам набрать букву "А" в строке ввода! Решение очень просто: сделайте проверку в кнопке для различных коротких клавиш до и после того как активный элемент обработает событие. По умолчанию кнопка просматривает свою короткую клавишу в форме Alt-буква до процесса и в форме буквы после процесса. Вот почему Вы можете использовать короткий набор Alt-буква в диалоговом окне, но можете обрабатывать обычные буквы только когда активный элемент управления не "ест" клавиши. Это просто сделать. По умолчанию кнопки имеют установленные ofPreProcess и ofPostProcess, так что видят активные события и до и после активного видимого элемента, но внутри метода HandleEvent кнопка проверяет некоторые клавиши только если активный элемент управления уже видел событие: evKeyDown: { это часть оператора case } begin C := HotKey(Title^); if (Event.KeyCode = GetAltCode(C)) or (Owner^.Phase = phPostProcess) and (C <> #0) and (upcase(Event.CharCode) = C) or (State and sfFocused <> 0) and (Event.CharCode = ' ') then begin PressButton; ClearEvent(Event); end; end;Команды.
Большинство позиционированных и активных событий транслируются обрабатывающими их объектами в команды. Т.е. объект часто откликается на отметку мышкой или клавишу генерацией события команды. Например, отметив в строке статуса программы на Turbo Vision Вы генерируете позиционное (от мышки) событие. Программа определяет что отметка была позиционирована в области управляемой строкой статуса и передает событие в объект строки статуса StatusLine. StatusLine определяет какой из элементов управления статуса отмечен и читает запись элемента статуса для этого элемента. Этот элемент обычно имеет связанную с ним команду и StatusLine создает запись статуса с полем What, установленным в evCommand и с полем Command, установленным в команду, которая была связана с этим элементом статуса. Затем она очищает событие от мышки, что означает что следующее событие обнаруженное GetEvent будет только что сгенерированное событие команды.Определение команд.
Turbo Vision имеет ряд предопределенных команд и Вы можете определить еще больше своих. Когда Вы создаете новый видимый элемент, Вы также создаете команду, которая используется для вызова этого видимого элемента. Команды могут быть названы произвольно, но по соглашениям Turbo Vision идентификатор команды должен начинаться с "cm". Механизм создания команд прост - Вы только создаете константу: const cmConfuseTheCat = 100; Turbo Vision резервирует команды от 0 до 99 и от 256 до 999 для собственного использования. Ваша программа может использовать под команды номера от 100 до 255 и от 1000 до 65535. Причина того что Вы имеете два диапазона для команд только в том что команды от 0 до 255 могут быть запрещены. Turbo Vision резервирует некоторые команды, которые могут быть запрещены и некоторые команды которые не могут быть запрещены для своих стандартных команд и внутренней работы. Вы имеете полный контроль над оставшимися командами. Таблица 5.1. Диапазоны команд Turbo Vision. ------------------------------------------------ Диапазон Зарезервировано Может быть запрещено ------------------------------------------------ 099 Да Да 100255 Нет Да 256999 Да Нет 100065535 Нет Нет ------------------------------------------------Связывание команд.
Когда Вы создаете элемент строки статуса или меню, Вы связываете с ним команду. Когда пользователь выбирает этот элемент, генерируется запись события с полем What установленным в evCommand и полем Command установленным в значение связанной команды. Команда может быть стандартной командой Turbo Vision или командой определенной Вами. В тот момент, когда Вы связываете Вашу команду с элементом меню или строки статуса, Вы также можете связать ее с горячей клавишей. Т.о. пользователь может вызвать команду, нажав клавишу короткого набора или мышкой. Примечание: Важно запомнить что определение команды не указывает какое действие должно быть выполнено, когда команда появляется в записи события. Вы должны сказать соответствующим объектам, как откликаться на эту команду.Разрешение и запрещение команд.
Иногда необходимо, чтобы некоторые команды были недоступны пользователю определенное время. Например, нет открытых окон, бессмысленно разрешать пользователю генерировать стандартную команду закрытия окна cmClose. Turbo Vision предоставляет способ запретить и разрешить набор команд. Для разрешения или запрещения Вы используете глобальный тип TCommandSet, который является множеством из чисел в диапазоне от 0 до 255. (Вот почему можно запретить только команды в диапазоне 0255). Следующий код запрещает группу из 5 оконных команд: var WindowCommands: TCommandSet; begin WindowCommands := [cmNext, cmPrev, cmZoom, cmResize, cmClose]; DisableCommands(WindowCommands); end;Обработка событий.
После того, как Вы определили команду и установили элемент управления, который генерирует ее - например, элемент меню или кнопка диалового окна - Вам нужно научить Ваш видимый элемент, как реагировать, когда возникает эта команда. Каждый видимый элемент наследует метод HandleEvent, который уже знает, как реагировать на большую часть ввода пользователя. Если Вы хотите, чтобы видимый элемент делал что-то специфическое для Вашей программы, Вам необходимо перекрыть HandleEvent и научить новый HandleEvent двум вещам - как откликаться на определенные Вами команды и как откликаться на события от мышки и клавиатуры нужным Вам образом. Метод HandleEvent определяет поведение видимого элемента. Два видимых элемента с идентичными методами HandleEvent будут одинаково откликаться на события. Когда Вы порождаете новый тип видимого элемента, Вы обычно хотите, чтобы его поведение более или менее соответствовало его предку с некоторыми изменениями. Наиболее простой способ достичь этого - вызвать HandleEvent предка в методе HandleEvent нового объекта. Общий вид HandleEvent наследника: procedure NewDescendant.HandleEvent(var Event: TEvent); begin {Код, изменяющий или ограничивающий унаследованное поведение} Parent.HandleEvent(Event); {Код, выполняющий дополнительные функции} end; где Parent - тип предка. Другими словами, если Вы хотите, чтобы новый объект обрабатывал события не так, как это делал его предок, Вы должны перехватить определенные события до передачи события в метод HandleEvent предка. Если Вы хотите, чтобы Ваш новый объект вел себя также, как его предок, но с дополнительными функциями, Вы должны добавить код после вызова процедуры HandleEvent предка.Запись события.
До этого момента в этой главе обсуждались теоретические аспекты события. Мы говорили о том, как различные виды событий (мышка, клавиатура, сообщения и "пустые") определяются в поле What события. Мы так же кратко обсудили использование поля Command для событий-команд. Теперь самое время обсудить как выглядит запись события. Модуль DRIVERS.TPU в Turbo Vision определяет тип TEvent как запись с вариантами: TEvent = record What: Word; case Word of evNothing: (); evMouse: ( Buttons: Byte; Double: Boolean; Where: TPoint); evKeyDown: ( case Integer of 0: (KeyCode: Word); 1: (CharCode: Char; ScanCode: Byte)); evMessage: ( Command: Word; case Word of 0: (InfoPtr: Pointer); 1: (InfoLong: Longint); 2: (InfoWord: Word); 3: (InfoInt: Integer); 4: (InfoByte: Byte); 5: (InfoChar: Char)); end; Эта запись с вариантами просматривается по значению поля What. Так, если TEvent.What - это evMouseDown, то TEvent содержит: Buttons: Byte; Double: Boolean; Where: TPoint; Если TEvent.What - это evKeyDown, компилятор позволит Вам обратиться к данным как KeyCode: Word; или как CharCode: Char; ScanCode: Byte; Последний вариант в записи события может хранить значение типа Pointer, LongInt, Word, Integer, Byte или Char. Это поле используется в Turbo Vision различными способами. Видимые элементы могут сами генерировать события и посылать их другим видимым элементам. В этом случае они часто используют поле InfoPtr. Взаимодействие видимых элементов и поля InfoPtr описаны в разделе "Взаимодействие видимых элементов".Очистка событий.
Когда метод Handle видимого элемента обработал событие, он заканчивает этот процесс вызовом метода ClearEvent. ClearEvent устанавливает поле Event.What равным evNothing и Event.InfoPtr в @Self, что указывает на очищенное событие. Если это событие будет передано другому объекту, то он должен игнорировать "пустое" событие.Ненужные события.
Обычно каждое событие обрабатывается каким-либо из видимых элементов Вашей программы. Если ни один из видимых элементов не обработал событие, модальный видимый элемент вызывает EventError. EventError вызывает EventError владельца видимого элемента и так до тех пор, пока не будет вызван TApplication.EventError. TApplication.EventError по умолчанию ничего не делает. При необходимости Вы можете перекрыть EventError для вызова диалогового окна с ошибкой или подачи сигнала. Поскольку конечный пользователь Вашей программы не отвечает за ошибки программы, обрабатывающей событие, такое диалоговое окно вероятно должно быть удалено из коммерческой версии программы. ClearEvent так же помогает видимым элементам взаимодействовать друг с другом. Сейчас запомните, что Вы не закончите обработку события до тех пор, пока не вызовите ClearEvent.Модификация механизма событий.
Сердцем текущего модального видимого элемента является цикл типа: var E: TEvent; begin E.What := evNothing; repeat if E.What <> evNothing then EventError(E); GetEvent(E); HandleEvent(E); until EndState <> Continue; end;Централизация сбора событий.
Одно из наибольших достижений программирования управляемого событиями в том, что Ваш код не должен знать откуда поступают события. Например, объект окно должен только знать, что когда он видит в событии команду cmClose, он должен закрыться. Его не интересует то ли эта команда поступила от отметки его закрывающей кнопки, или из выбора меню, или от горячей клавиши, или пришло сообщение от другого объекта программы. Он даже не беспокоиться о том, предназначалась ли эта команда ему. Он только должен знать как обработать данное событие и обрабатывает его. Ключ к этому "черному ящику" событий - метод GetEvent программы. GetEvent - это единственная часть программы, которая интересуется источником событий. Объекты Вашей программы просто вызывают GetEvent и получают события от мышки, клавиатуры или сгенерированные другими объектами. Если Вы хотите создать новые виды событий (например, чтение символов из последовательного порта) Вы просто перекрываете TApplication.GetEvent в Вашей программе. Как Вы можете увидеть из TProgram.GetEvent в APP.PAS цикл в GetEvent сканирует мышку и клавиатуру, а затем вызывает Idle. Чтобы вставить новый источник событий, Вы можете либо перекрыть Idle для просмотра символов из последовательного порта и генерации событий, основанных на этих символах, либо перекрыть сам GetEvent, чтобы добавить GetComEvent(Event) в цикл, где GetComEvent возвращает запись события, если доступен символ от последовательного порта.Перекрытие GetEvent.
GetEvent текущего модального видимого элемента вызывает GetEvent владельца и так далее проходя весь путь по дереву видимых элементов до TApplication.GetEvent, который ищет следующее реальное событие. Поскольку Turbo Vision всегда использует TApplication.GetEvent для поиска событий, Вы можете модифицировать события всей Вашей программы, перекрывая только один метод. Например, для реализации клавиатурных макро Вы можете просматривать события, возвращаемые GetEvent, перехватывать определенные нажатия клавиш и развертывать их в макро. С точки зрения остальной программы поток событий будет приходить прямо от пользователя. procedure TMyApp.GetEvent(var Event: TEvent); begin TApplication.GetEvent(Event); end;Неиспользованное время.
Другое преимущество центрального местонахождения TApplication. GetEvent в том, что он вызывает метод TApplication.Idle, если нет готовых событий. TApplication.Idle - это пустой метод, который Вы можете перекрыть для того, чтобы выполнять параллельную обработку одновременно с текущим видимым элементом. Например предположим, Вы определили видимый элемент, названный THeapView, который использует метод UpDate для отображения доступной в данный момент памяти кучи. (Пример просмотра кучи включен в демонстрационные программы на Ваших дистрибутивных дисках.) Если Вы перекрываете TApplication.Idle, следующим кодом пользователь сможет увидеть отображение доступной памяти в куче вне зависимости от его нахождения в программе. procedure TMyApp.Idle; begin HeapViewer.Update; end;Взаимодействие видимых элементов.
Программа Turbo Vision инкапсулирована в объекты и Вы пишите код только внутри объектов. Что, если объектам требуется обмен информацией с другими объектами Вашей программы? В традиционной программе Вы вероятно будете копировать информацию из одной структуры данных в другую. В объектно-ориентированной программе это может быть непросто, поскольку объекты могут не знать, где найти другие объекты. Взаимодействие видимых элементов - это не просто передача данных между частями обычной программы на Паскале. (Хотя 2 части обычной программы на Паскале могут никогда не достичь функциональности двух видимых элементов Turbo Vision). Если Вам необходимо взаимодействие видимых элементов, первый вопрос - это правильно ли Вы разделили задачи между двумя видимыми элементами. Проблема может возникать из-за неправильного проектирования программы. Вероятно 2 видимых элемента должны быть объединены в один видимый элемент или часть одного видимого элемента должна быть перемещена в другой видимый элемент.Посредники.
Если программа спроектирована правильно и видимые элементы требуют взаимодействия между собой, один из способов - создать промежуточный видимый элемент. Например, предположим, что Вы имеете объект электронной таблицы и объект текстового процессора и хотите иметь возможность вставлять что-либо из электронной таблицы в текстовый процессор и наоборот. В программе Turbo Vision Вы можете выполнить это прямым взаимодействием видимых элементов. Но предположим, что позже Вам понадобится добавить к этой группе объектов, скажем, базу данных и вставлять в и из базы данных. В этом случае Вам потребуется дублировать связь, установленную Вами между первыми двумя объектами на все 3 объекта. Лучшее решение - это установить промежуточный видимый элемент. В этом случае, скажем, "карман". Объект должен знать только как копировать что-либо в этот карман и как вставить что-либо из кармана. Вне зависимости от того, сколько новых объектов Вы добавите в группу, взаимодействие никогда не станет более сложным, чем сейчас.Сообщения между видимыми элементами.
Если Вы тщательно проанализировали Вашу ситуацию, решили, что программа спроектирована правильно и что Вам не требуется создавать промежуточные элементы, Вы можете реализовать простое взаимодействие между двумя видимыми элементами. До того, как один видимый элемент сможет взаимодействовать с другим, Вы можете определить где находится другой видимый элемент и вероятно даже убедиться, что другой видимый элемент существует в данное время. Вначале пример. Модуль Stddlg содержит диалоговое окно TFileDialog (этот видимый элемент открывается в интегрированной среде, когда Вы хотите загрузить новый файл). TFileDialog имеет TFileList, который показывает справочник на диске, а файл InputLine отображает текущий файл для загрузки. Каждый раз, когда пользователь выбирает другой файл в FileList, FileList должен сказать FilеInputLine вывести новое имя файла. В этом случае FileList может быть уверен, что FileInputLine существует, поскольку оба инициализированы внутри одного объекта FileDialog. Как FileList сможет сказать FileInputLine, что пользователь выбрал новое имя? FileList создает и посылает сообщение. FileList.FocusItem посылает сообщение, а FileInputLine.HandleEvent получает его: procedure TFileList.FocusItem(Item: Integer); var Event: TEvent; begin TSortedListBox.FocusItem(Item); { вначале вызывает наследуемый метод } Message(TopView, evBroadcast, cmFileFocused, List^.At(Item)); { TopView указывает текущий модальный видимый элемент } end; procedure TFileInputLine.HandleEvent(var Event:TEvent); var Name: NameStr; begin TInputLine.HandleEvent(Event); if (Event.What = evBroadcast) and (Event.Command = cmFileFocused) and (State and sfSelected = 0) then begin if PSearchRec(Event.InfoPtr)^.Attr and Directory <> 0 then Data^ := PSearchRec(Event.InfoPtr)^.Name + '\' + PFileDialog(Owner)^.WildCard else Data^ := PSearchRec(Event.InfoPtr)^.Name; DrawView; end; end; Message - это функция, которая генерирует событие сообщения и возвращает указатель на объект (если есть), который обработал это событие. Заметим, что TFileList.FocusItem использует расширенный синтаксис Turbo Pascal (директива компилятора $X+), чтобы использовать функцию Message как процедуру, поскольку результат, возвращаемый Message, не нужен.Кто обрабатывает общие сообщения?
Предположим, Вам требуется определить, находится ли на панели экрана открытое окно прежде, чем выполнить некоторые действия. Как это сделать? Ваш код должен послать общее событие, на которое окна знают как ответить. "Подпись", оставленная объектом, который обработал это событие, будет говорить Вам кто (если есть) обработал его.Есть ли кто-нибудь?
Конкретный пример. В IDE Turbo Pascal, если пользователь запрашивает открыть окно просмотра, код, который открывает окно просмотра, должен проверить не открыто ли уже окно просмотра. Если нет, он открывает его; если есть, переносит наверх. Передача общего сообщения проста: AreYouThere := Message(DeskTop, evBroadcast, cmFindWindow, nil); В методе HandleEvent окна просмотра есть проверка на отклик (очистка события) на команду cmFindWindow: case Event.Command of . cmFindWindow: ClearEvent(Event); . end; Вспомним, что ClearEvent не только устанавливает поле What записи события в evNothing, но так же устанавливает поле InfoPtr в @Self. Message читает эти поля и, если событие было обработано, возвращает указатель на объект, обработавший событие-сообщение. В данном случае это окно просмотра. Так за строкой, которая посылала сообщение, мы включим: if AreYouThere = nil then CreateWatchWindow else AreYouThere^.Select; Поскольку окно просмотра - это единственный объект, который знает как отвечать на общее сообщение cmFindWindow, Ваш код может быть уверен, что когда он выполнится, будет одно и только одно окно просмотра на вершине всех видимых элементов на панели экрана.Кто сверху?
Используя технику, описанную ранее, Вы так же можете например определить, какое окно является верхним из видимых элементов его типа на панели экрана. Поскольку общее сообщение посылается каждому подэлементу модального видимого элемента в Z-порядке (порядок обратный вставке), самый последний вставленный видимый элемент - это "верхний" видимый элемент на панели экрана. Рассмотрим ситуацию, возникающую в IDE, когда пользователь имеет окно просмотра, открытое на вершине панели экрана во время пошагового выполнения кода в окне редактора. Окно просмотра может быть активным окном (двойная рамка), но курсор выполнения в окне кода требует сохранения трассы выполнимого кода. Если на панели экрана открыто несколько окон редактора, они могут не перекрываться вообще, но IDE должен знать какое из окон редактора предназначено для трассировки. Ответ: конечно самое верхнее окно редактора, которое определено как последнее вставленное. Для того, чтобы определить, какое из окон "верхнее", IDE посылает общее сообщение, отклик на которое знают только окна редактора. Первое окно редактора, которое получает общее сообщение и будет последним вставленным; оно обработает событие, очищая его, и IDE узнает какое окно использовать для трассировки кода, читая результат, возвращенный Message.Вызов HandleEvent.
Вы так же можете создать или модифицировать событие, а затем вызвать HandleEvent напрямую. Вы можете сделать 3 типа вызовов: 1. Вы можете иметь видимый элемент, вызывающий HandleEvent равного подэлемента прямо. ("Равные" видимые элементы - это подэлементы с одним владельцем). Сообщение не передается другим видимым элементам. Оно идет прямо к этому HandleEvent, затем управление возвращается к Вам. 2. Вы можете вызвать HandleEvent владельца. Событие будет затем распространяться вниз по цепочке видимых элементов. (Если Вы вызываете HandleEvent из Вашего собственного HandleEvent, Ваш HandleEvent будет вызываться рекурсивно.) Управление передается Вам после обработки события. 3. Вы можете вызвать HandleEvent видимого элемента из другой цепочки видимых элементов. Событие будет передаваться вниз по этой цепочке видимых элементов. Управление передается Вам после обработки события.Контекстная помощь.
Turbo Vision имеет встроенный инструмент, который помогает Вам реализовать контекстно-ориентированную помощь в Вашей программе. Вы можете назначить номер контекстной подсказки видимому элементу и когда видимый элемент станет активным, номер его подсказки станет текущим номером контекстной подсказки в программе. Чтобы создать глобальную контекстно-ориентированную подсказку, Вы можете реализовать HelpView, который знает номера контекстных подсказок, определенных Вами. Когда вызывается HelpView (обычно нажатием F1 или другой горячей клавиши), он должен спросить своего владельца о текущем контексте подсказки, вызвав метод GetHelpCtx. Затем HelpView может читать и отображать соответствующий текст подсказки. Пример HelpView включен в дистрибутивные диски Turbo Pascal. Контекстно-ориентированная помощь - это, вероятно, одна из последних возможностей, которую Вы будете реализовывать в Вашей программе, поэтому объекты Turbo Vision инициализируются с контекстом hcNoContext по умолчанию, что означает предопределенный контекст, который не изменяет текущего контекста. При необходимости Вы можете разработать номера подсказок, затем вставить правильный номер в соответствующий видимый элемент, вызвав SetHelpCtx сразу после создания видимого элемента. Контекстная подсказка так же используется строкой статуса для определения, какие видимые элементы отображаются. Вспомним, что когда Вы создаете строку статуса, Вы вызываете NewStatusDef, который определяет набор элементов статуса для данного диапазона значений контекстной помощи. Когда новый видимый элемент становится активным, контекст помощи этого элемента определяет, какая строка статуса будет отображаться.
Назад | Содержание | Вперед