ГЛАВА 1. ИЗУЧЕНИЕ TURBO VISION.

Оболочка оконной программы.

Turbo Vision - это оболочка оконной программы, управляемой событиями. Здесь нет ни грамма мяса, только сильный гибкий скелет. Вы наращиваете мышцы на скелет, используя расширенные возможности объектно-ориентированного программирования Turbo Pascal. Turbo Vision предоставляет Вам прикладной объект TApplication и Вы создаете порожденный объект от TApplication - назовем его MyApplication, который действует как Ваше приложение. Затем Вы добавляете в MyApplication все, что Вам требуется для необходимой работы. На очень высоком уровне это все, что нужно сделать. Весь блок begin.end Вашей программы выглядит: begin MyApplication.Init; { начальная установка } MyApplication.Run; { выполнение } MyApplication.Done; { закрытие } end;

Новый взгляд на разработку программ.

Вы, вероятно, использовали библиотеки процедур и функций и ранее, а на первый взгляд Turbo Vision выглядит во многом как обычные библиотеки. Кроме того, библиотеки могут быть разработаны для поддержки меню, окон, мышки и т.д. Но под внешним сходством кроется существенное различие. Во-первых, не забывайте, что Вы сейчас работаете с объектами. В традиционном структурном программировании, когда инструмент, такой как монитор меню не полностью Вас удовлетворяет, Вы модифицируете исходный код инструмента, если он есть. Изменение исходного кода - это шаг, который труден для возврата, если Вы не фиксируете точно, как выглядел оригинальный код. Более того, измененный исходный код (особенно исходный код, написанный кем-либо другим) - это прекрасный путь внести новые ошибки в систему. С Turbo Vision Вы никогда не модифицируете исходный код. Вы "изменяете" Turbo Vision РАСШИРЯЯ его. TApplication остается неизменной внутри APP.TPU. Вы добавляете к нему, порождая новые типы объектов, и изменяете так, как Вам требуется, перекрывая унаследованные методы методами, которые Вы пишите для новых объектов. Кроме того, Turbo Vision - это иерархия, а не разрозненный набор инструментов. Если Вы используете любой из них, Вы должны использовать ВСЕ из них. Здесь дается представление каждого компонента Turbo Vision и как они работают вместе. Эти рекомендации лежат в основании принципов разработки на Turbo Vision: полностью используйте объектно-ориентированную технику и используйте все элементы Turbo Vision в Вашей программе. Это означает играть по "правилам" Turbo Vision и использовать его типы объектов. Мы создали Turbo Vision, чтобы уберечь Вас от большого количества ненужной повторяющейся работы и предоставить Вам оболочку прикладных программ.

Элементы Turbo Vision.

До того, как мы ознакомимся с работой Turbo Vision, давайте посмотрим, какие инструменты дает Turbo Vision для построения Вашей программы.

Новые понятия.

Turbo Vision - это объединение видимых элементов, событий и невидимых объектов.

Видимые элементы.

Видимый элемент - это любой элемент программы, который виден на экране и все эти элементы являются объектами. В контексте Turbo Vision, если Вы можете увидеть элемент, то он видимый. Поля, рамки окон, полосы скроллинга, полосы меню и диалоговые окна - это все видимые элементы. Видимые элементы могут объединяться для формирования более сложных элементов таких, как окна и диалоговые окна. Эти наборы видимых элементов называются группами и они работают вместе так, как если бы это был один элемент. Концептуально группы могут рассматриваться как видимые элементы. Видимый элемент всегда прямоугольный. Они включают в себя прямоугольники, которые содержат 1 символ или линии высотой в 1 символ или линии шириной в 1 символ.

События.

Событие - это что-то, на что Ваша программа должна отреагировать. События могут приходить от клавиатуры, от мышки или от других частей Turbo Vision. Например, нажатие клавиши - это событие такое же, как и нажатие кнопки мышки. События поступают в очередь внутри Turbo Vision по мере их появления и затем обрабатываются обработчиком событий. Объект TApplication, который является ядром Вашей программы, содержит обработчик событий. Через механизм, который будет описан позднее, события, которые не обрабатываются TApplication, передаются в другие видимые элементы до тех пор, пока не найдется видимый элемент, который обработает событие, или пока не возникнет ошибка "отказ от события". Например, клавиша F1 вызывает справочную систему. Если какой-то видимый элемент не имеет собственной части в справочной системе (как может случиться в контекстно-ориентированной справочной системе), клавиша F1 обрабатывается обработчиком событий главной программы. С другой стороны, алфавитно-цифровые клавиши или клавиши редактирования должны быть обработаны видимым элементом, который в данный момент активен; т.е. видимым элементом, который в данный момент взаимодействует с пользователем.

Невидимые объекты.

Невидимые объекты - это любые другие объекты программы, отличные от видимых элементов. Они "невидимы", поскольку сами ничего не выводят на экран. Они производят вычисления, связь с периферией и выполняют другую работу прикладной программы. Когда невидимому объекту необходимо вывести что-либо на экран, он должен выполнить это, связавшись с видимым элементом. Эта концепция очень важна для поддержания порядка в программе Turbo Vision: только видимые элементы могут взаимодействовать с дисплеем. Примечание: Ничто не запретит Вам вывести из невидимого объекта на экран информацию с помощью операторов Write и Writeln. Однако, если Вы сами выводите на экран, Ваш текст будет разрушать текст, выводимый Turbo Vision, а текст, который выводит Turbo Vision (например, передвигая или изменяя размер окон) будет затирать Ваш текст.

Другие элементы.

Поскольку Turbo Vision был спроектирован, чтобы реализовать стандартизованный рациональный подход к конструированию экрана, Ваши программы приобретают зрение и чувства. Это зрение и чувства основаны на годах опыта и тестирования. Рисунок 1.1 показывает набор общих объектов, которые могут появляться как часть программы Turbo Vision. Панель экрана (Desktop) заштрихована фоновым цветом в отличие от остальных элементов на экране. Как и все в Turbo Vision, панель экрана - это объект. Так же видны полоса меню наверху экрана и строка статуса внизу. Слова в полосе меню представляют меню, которые выпадают при выборе этих слов мышкой или при нажатии горячих клавиш. Рис. 1.1. Объекты Turbo Vision на экране. +--------------------------------------+ | Полоса меню | +--------------------------------------| |**************************************| |**************************************| |**************************************| |** Панель экрана ********************| |**************************************| |**************************************| +--------------------------------------| | Строка статуса | +--------------------------------------+ Текст, который появляется в строке статуса, обычно выводит сообщения о текущем состоянии программы, показывая доступные горячие клавиши или подсказки для команд, которые доступны пользователю в данный момент. Когда выпадает меню, полоса подсветки пробегает по списку элементов меню при движении мышки или нажатии на клавиши курсора. Когда Вы нажимаете Enter или левую кнопку мышки, выбирается подсвеченный элемент. Выбранный элемент меню посылает команду в определенную часть программы. Ваша программа обычно взаимодействует с пользователем через одно или более окон или диалоговых окон, которые появляются и исчезают на панели экрана в соответствии с командами от мышки или клавиатуры. Turbo Vision предоставляет большой набор окон для ввода и отображения информации. Окна можно сделать со скроллингом, что позволяет выводить в окнах большие объемы данных. Скроллинг окна по информации выполняется передвижением по полосе скроллинга внизу окна, с правой стороны окна или обоим. Полоса скроллинга указывает относительную позицию окна в отображаемых данных. Диалоговые окна часто содержат кнопки - подсвеченные слова - которые могут выбираться мышкой (или переходом через клавишу Tab и нажатием пробела). Отображаемые слова, выбранные мышкой, могут быть установлены для передачи команд в программу.

Чтение ввода пользователя.

При написании обычных программ на Паскале, для получения ввода пользователя и его обработки часто используются блоки вида: repeat B := ReadKey; { чтение ввода пользователя } case B of { обработка ввода } 'i' : InvertArray; 'e' : EditArray; 'g' : GraphicDisplay; 'q' : Quit := True; end; until Quit; . Программа, управляемая событиями, не очень отличается от обычной по принципу. Однако, с точки зрения программиста, различия есть. В программах на urboVision ввод пользователя читается самой TurboVision, упаковывается в записи Паскаля, называемые событиями (Event), и передается видимым элементам программы для обработки. К примеру, если пользователь отметил мышкой неактивное окно, программа обнаруживает это событие, упаковывает его и направляет вниз по дереву видимых элементов. Когда событие достигнет отмеченного окна, окно реагирует на него, становясь активным. Видимые элементы TurboVision сами могут обрабатывать большую часть ввода: окно знает, как открыться, закрыться, переместиться, изменить размер; меню знает, как открыться, взаимодействовать с пользователем и закрыться; кнопки - как нажиматься и т.д. Механизм передачи событий уже существует. Программист, в соответствии со своими задачами, определяет новые видимые элементы с новыми функциями, которые должны знать об определенных видах событий, заданных программистом, и реагировать на них.

Виды событий.

События в TurboVision можно разделить на четыре класса: 1). События от мышки: - нажатие или отпускание любой кнопки мыши; - изменение позиции курсора мыши; - "авто"-событие (длительное удерживание кнопки мыши нажатой). 2). События от клавиатуры (нажатия клавиш). 3). События-сообщения - специальный вид событий для обеспечения взаимодействия видимых элементов программы между собой. События-сообщения бывают трех типов: события-команды, общие сообщения и пользовательские сообщения. 4). "Пустые" события, - этот класс событий включает в себя всего один элемент - пустое событие. Пустое событие не несет полезной информации и генерируется объектом, обработавшим "непустое" событие, для указания, что событие уже обработано. Объекты просто игнорируют пустые события.

События-сообщения. Команды. Разрешение и запрещение команд.

События, циркулирующие внутри дерева видимых элементов, можно условно разделить на две группы - события внешние и внутренние. К первым относятся события, воздействующие на объекты программы извне - это события от мышки и клавиатуры; внутренние события генерируются самими объектами программы, к ним и относятся события-сообщения. События - сообщения служат для передачи информации между взаимодействующими объектами. Необходимость использования сообщений следует из того, что объекты не могут связаться напрямую, т.к. не знают указателей друг на друга. Событие - сообщение содержит команду, и объект, обнаруживший известную ему команду, реагирует на нее определенным образом. Каждой команде соответствует уникальный код - константа типа Word, имя которой, по соглашению TurboVision, имеет вид cmXXXX (command). Например, объекты полосы скроллинга TScrollBar и скроллера текста TScroller, принадлежа объекту окна TWindow, оба находятся в списке подэлементов окна. Отметка мышью в полосе скроллинга передается TScrollBar и полоса скроллинга должна сообщить скроллеру, чтобы тот показал другую часть текста. Полоса скроллинга посылает общее сообщение, которое содержит команду cmScrollBarChanged и указатель на себя (т.е. на объект TScrollBar), и, когда TScroller обнаружит это сообщение, он может связаться с TScrollBar для получения новых параметров вывода текста. Для уже определенных в TurboVision команд зарезервированы два диапазона: от 0 до 99 и от 256 до 999, остальные значения ( 100-255 и 1000-65535) программист может использовать для определения своих команд, без конфликтов с предопределенными командами. Наличие двух диапазонов для команд связано с тем, что команды с кодами от 0 до 255 могут быть запрещены, что означает временную отмену их действия (на запрещенные команды объекты не реагируют). Для этого определена глобальная переменная типа TCommandSet = set of Byte;, в которой хранится набор разрешенных команд. Методы TView EnableCommands, DisableCommands, GetCommands, и SetCommands предназначены для получения и изменения текущего набора разрешенных команд. Большинство внешних событий транслируется обрабатывающими их объектами в команды. Например, в меню каждому элементу соответствует некоторая команда, и выбор элемента меню приводит к генерации события-сообщения с соответствующей командой. Команды связываются с элементами меню в вызове конструктора объекта меню. Подобным образом объект кнопки (TButton) имеет ассоциированную с кнопкой команду, определяемую параметром ACommand в вызове конструктора TButton.Init: TButton.Init (var Bounds: TRect; ATitle: TTitleStr; ACommand: Word;AFlags: Byte);. В TurboVision события - сообщения разделяются на два типа: события - команды и события - общие сообщения. От типа события-сообщения зависит, каким образом оно будет передаваться по дереву видимых элементов. Программист может ввести свой тип событий-сообщений - пользовательские сообщения, и задать способ их передачи.

Запись события. Тип TEvent.

Для упаковки и передачи событий в TurboVision определен специальный тип - запись с вариантами 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; . Поле TEvent.What содержит информацию о виде события. Это битовое поле: каждому классу событий соответствует один бит (события от клавиатуры) или группа битов (события от мышки и события-сообщения) (см. рис.2). Для работы с битами TEvent.What определен набор констант evXXXX(Таблица). Константы evNothing (пустое событие), evMouse(мышь), evKeyboard(клавиатура) и evMessage(сообщение) могут использоваться для маскирования событий; они позволяют быстро определить, к какому классу принадлежит событие. Например, для определения, является ли событие событием от мышки, удобно использовать код if Event.What and evMouse <> 0 then DoMouseEvent;, не проверяя отдельно четыре первых бита TEvent.What. Оставшиеся неопределенными биты TEvent.What программист может использовать для новых видов событий. Рис.2. Структура поля TEvent.What.

Константа

Значение

Назначение

evMouseDown

$0001

Кнопка мышки нажата.

evMouseUp

$0002

Кнопка мышки отпущена.

evMouseMove

$0004

Мышка движется.

evMouseAuto

$0008

Кнопка удерживается нажатой (авто-событие).

evKeyDown

$0010

Клавиша нажата.

evCommand

$0100

Событие-команда.

evBroadcast

$0200

Событие-общее сообщение.

Маски для классов событий

evNothing

$0000

Пустое событие.

evMouse

$000F

Событие от мышки.

evKeyboard

$0010

Событие от клавиатуры.

evMessage

$FF00

Событие-сообщение (команда, общее сообщение
или пользовательское сообщение).

     Таблица. Константы evXXXX.

	 
В  зависимости  от  содержания поля TEvent.What, заполняется та или
иная  группа  полей   из   оставшихся  в записи TEvent. Если TEvent
содержит  запись события  от  мышки,  то  поле  Buttons  указывает,
какая  кнопка была  нажата  или отпущена, Double  устанавливается в
True, если было произведено двойное нажатие, а поле  Where содержит
координаты курсора мыши в координатной системе экрана TApplication.
В  случае события  от  клавиатуры  поле  KeyCode  содержит скэн-код
нажатой клавиши.
Если  же TEvent  -  запись  события - сообщения,  то   поле Command
содержится код  команды, а  в  одном  из  полей InfoPtr,  InfoLong,
InfoWord,   InfoInt,   InfoByte   или   InfoChar  может  помещаться
дополняющая команду информация.
Пустому событию соответствует значение поля What, равное evNothing.


Цикл событий. Функция Execute.

Обработку событий всегда начинает модальный видимый элемент. Модальный видимый элемент, в том числе и программа, получает и обрабатывает события, используя функцию Execute (TProgram.Run вызывает наследуемый метод TGroup.Execute). Функция Execute - это основной цикл событий модального элемента: в нем элемент периодически получает события с помощью процедуры GetEvent и обрабатывает их, вызывая свой метод HandleEvent: function TGroup.Execute: Word; var E: TEvent; begin ... EndState := 0; repeat GetEvent(E); { Получение события } HandleEvent(E); { Обработка события } if E.What <> evNothing then EventError(E); until EndState <> 0; { EndState - private поле в TGroup, которое изменяется в процедуре EndModal для завершения модального состояния } ... end; . Процедура GetEvent возвращает следующее доступное событие, а процедура HandleEvent должна обработать его сама, либо передать для обработки другим видимым элементам программы. Если событие не было обработано методом HandleEvent (т.е. оно непустое), то вызывается метод EventError для сигнализации о ненужном событии.

Методы GetEvent и PutEvent. Централизация сбора событий.

Каждый видимый элемент может получать события, используя наследуемый от TView виртуальный метод GetEvent(var Event: TEvent). Этот метод возвращает в параметре Event следующее доступное событие. TView.GetEvent просто вызывает GetEvent своего владельца; так же функционируют методы GetEvent всех потомков TView, кроме TProgram. Таким образом, вызов GetEvent любого элемента приводит в конечном итоге к вызову TProgram.GetEvent (TApplication наследует GetEvent от TProgram), который перекрывает TView.GetEvent для действительного получения событий. Метод PutEvent(var Event: TEvent) используется видимыми элементами для генерации событий. Аналогично GetEvent, TView.PutEvent вызывает PutEvent владельца. TProgram.PutEvent сохраняет копию записи события Event в буфере, и при следующем обращении к GetEvent будет возвращено это событие (буфер может содержать только одно событие). Объекты меню и кнопки используют PutEvent для генерации команд, ассоциированных с ними. TProgram.GetEvent сначала проверяет, не было ли сгенерировано событие с помощью PutEvent, и, если так, то возвращает это событие. Если такого события нет, то выполняется процедура GetMouseEvent для получения события от мышки. Если GetMouseEvent возвращает пустое событие, вызывается GetKeyEvent для получения события от клавиатуры. В случае, когда обе процедуры возвращают evNothing, указывая, что нет ввода от пользователя, GetEvent вызывает процедуру TProgram.Idle для выполнения фоновых задач (напр., обновление строки статуса). До возврата GetEvent передает все события evKeyDown и evMouseDown в строку статуса, благодаря чему строка статуса всегда остается активной, независимо от модального элемента. В программе на TurboVision получение событий происходит только в одном месте, в TProgram.GetEvent. Централизация сбора событий позволяет легко ввести в программу новые виды событий (напр., чтение символов из последовательного порта) - для этого достаточно перекрыть метод TApplication.GetEvent. С точки зрения остальной программы, поток событий будет приходить прямо от пользователя. Другое преимущество управляемого событиями программирования состоит в том, что код не должен знать, откуда поступают события. Например, объект TWindow должен знать только, как реагировать на команду закрытия окна cmClose, но ему совершенно безразлично, откуда поступила команда.

Обработка событий. Метод HandleEvent.

События передаются элементу посредством вызова наследуемого от TView виртуального метода HandleEvent, определяющего реакцию видимого элемента на события. HandleEvent - это центральный метод, через который осуществляется вся обработка событий в TurboVision. TView.HandleEvent обрабатывает только одно событие - evMouseDown, следующим образом: если видимый элемент не выбран и не запрещен (т.е. не установлены флаги sfSelected и sfDisabled), и если видимый элемент выбираемый (установлен атрибут ofSelectable), то элемент выбирает себя, вызывая процедуру Select. Почти всегда потомки TView перекрывают HandleEvent для реализации своих типов поведения. Общий вид HandleEvent: procedure HandleEvent(var Event: TEvent); { Параметр Event содержит запись передаваемого объекту события. } begin { Код, изменяющий или ограничивающий поведение предка. } TParent.HandleEvent(Event); { Код предка. } { Код, выполняющий дополнительные функции. } end; Видимый элемент может либо обработать событие, заданное в Event, либо проигнорировать его, тогда событие будет возвращено элементу, вызвавшему HandleEvent данного видимого элемента. Для индикации того, что событие обработано, видимый элемент должен вызвать процедуру ClearEvent(var Event: TEvent);. ClearEvent очищает событие, устанавливая поле Event.What в evNothing; поле Event.InfoPtr устанавливается в @Self для указания, каким объектом было обработано событие. TGroup перекрывает HandleEvent для передачи событий своим подэлементам, осуществляя маршрутизацию событий.
Маршрутизация событий. Переменные FocusedEvents и PositionalEvents. Поле EventMask.
Видимые элементы TurboVision работают по принципу "говори только тогда, когда к тебе обратятся", они ожидают, когда монитор событий скажет им, что пришло событие, на которое требуется отклик. В связи с этим, главное в получении событий в нужном месте - это правильная маршрутизация событий. Некоторые события передаются всем элементам программы, другие направляются точно в определенную часть программы. Объект - группа передает события своим подэлементам, вызывая их методы HandleEvent. Передача события осуществляется одним из трех способов, в зависимости от вида события: 1). Позиционированные события. Это события от мышки (evMouse). Позиционированное событие передается первому подэлементу в Z-порядке, содержащему позицию, на которую указывает курсор мыши. Поскольку видимые элементы могут перекрываться, возможно, что отмеченная позиция принадлежит более чем одному элементу, и передача события в Z-порядке гарантирует, что событие получит самый верхний подэлемент, содержащий эту позицию. 2). Активные (сфокусированные) события. Этот тип событий включает события от клавиатуры (evKeyboard) и события-команды (evCommand). Передача активных событий происходит в три фазы (поле TGroup.Phase отображает фазу передачи события): ·Pre-process: поле группы Phase устанавливается в phPreProcess и событие передается тем подэлементам, которых установлен атрибут ofPreProcess; ·Process: Phase устанавливается в phFocused и событие передается выбранному подэлементу группы; ·Post-process: группа устанавливает Phase в phPostProcess и передает событие подэлементам, имеющим установленный атрибут ofPostProcess.Если какой-либо подэлемент группы обработал событие, то передача события прекращается. Для чего активные события передаются в три фазы? Рассмотрим следующий пример: допустим, в программе есть диалоговое окно, которое содержит строку ввода (TInputLine) и кнопку (TButton) с горячей клавишей "A". Кнопка должна нажиматься по короткому набору "A", когда строка ввода не сфокусирована (т.е. не производится ввод в нее), и по набору "Alt-A", когда строка ввода сфокусирована (пользователь вводит символы в строке ввода). Следовательно, кнопка должна получать активные события (нажатия клавиш) и в то время, когда сама она не является выбранным и сфокусированным подэлементом: procedure TButton.HandleEvent(var Event: TEvent); var C:Char; begin ... case Event.What of ... evKeyDown: begin C:=HotKey(Title^); { Возвращает символ горячей клавиши } if (Event.KeyCode=GetAltCode(C)) or {GetAltCode(C) возвращает код, соответствующий нажатию "Alt + C" } (State and sfFocused <> 0) and (Event.CharCode = ' ') or (Owner^.Phase = phPostProcess) and (C<>#0) and (UpCase(Event.CharCode)=C) {UpCase(Ch: Char) возвращает символ верхнего ряда, соответствующий заданному параметром Ch } then begin PressButton; { Нажатие кнопки } ClearEvent(Event); { Очистка обработанного события } end; end; ... end; ... end; Как видно из приведенного кода, кнопка нажимается при получении события от клавиатуры в трех случаях: - когда код нажатой клавиши соответствует коду нажатия "Alt + горячая клавиша"; - когда кнопка сфокусирована и нажат пробел; - когда нажата горячая клавиша и событие передано в фазе PostProcess; фаза должна быть именно PostProcess, позволяя сфокусированному элементу обработать нажатие клавиши до того, как это событие "увидит" кнопка. 3). Общие сообщения (не активные и не позиционированные события). Эти события, по определению, не знают своего назначения и передаются всем подэлементам группы в Z-порядке, пока какой-нибудь из них не обработает событие или оно не будет передано всем подэлементам. Общие сообщения используются для взаимодействия различных объектов, а также для посылки события - сообщения сразу нескольким объектам. Глобальные переменные PositionalEvents иFocusedEvents (тип Word) определяют, какие события относятся к позиционированным и сфокусированным событиям. В TurboVision, по умолчанию, для них устанавливаются следующие значения: PositionalEvents = evMouse, а FocusedEvents= evKeyboard+ evCommand. Если событие не содержится в PositionalEvents и FocusedEvents, то оно передается как общее сообщение. Поле видимого элемента EventMask типа Word, наследуемое от TView, определяет виды событий, не обрабатываемые видимым элементом. Биты EventMask соответствуют битам TEvent.What. Если событие не содержится в поле EventMask подэлемента, то группа никогда не передаст его этому элементу. Установка EventMask в $FFFF позволяет реагировать элементу на все классы событий, установка в $0 запрещает передачу элементу любых событий. TView.EventMask разрешает передачу событий evMouseDown, evKeyDown и evCommand.

"Hello, World!" в Turbo Vision.

Традиционный способ демонстрации любого нового языка или инструментария заключается в выводе "Hello, World!". Эта программа обычно состоит из кода, выводящего строку "Hello, World!" на экран и возврата в DOS. Turbo Vision предоставляет другой способ сказать "Hello, World!". Классическая программа "Hello, World!" не интерактивна, а Turbo Vision - это инструмент для создания интерактивных программ. Простейшая программа на Turbo Vision делает гораздо больше, чем Writeln между begin и end. По-сравнению с классической программой "Hello, World!", программа HELLO.PAS на Вашем дистрибутивном диске выполняет: - Заполняет панель экрана полутеневым шаблоном. - Выводит полосу меню и строку статуса наверху и внизу экрана. - Устанавливает обработчик для событий от клавиш и мышки. - Строит объект меню и соединяет его с полосой меню. - Строит диалоговое окно. - Связывает диалоговое окно с меню. - Ожидает Ваших действий через мышку или клавиатуру. В этом списке нет ничего о выводе текста на экран. Текст готовится и ожидает вызова команды. Когда Вы изучаете Turbo Vision, Вам нужно помнить: сущность программирования на Turbo Vision - это проектирование того, что видит пользователь и обучение его что делать, когда получена команда. Оболочка Turbo Vision заботится о создании соответствующего изображения для полученных команд. Вам необходимо думать только о том, что нужно сделать, когда команда от клавиши, мышки или меню поступила в Ваш код. Мышцы Вашей программы - это код, который выполняет требуемую работу в соответствии с командами, введенными пользователем - и этот код содержится в видимых объектах, созданных Вами.

Выполнение HELLO.PAS.

До того, как мы разберем HELLO.PAS детально, давайте загрузим программу, откомпилируем ее и выполним. При выполнении Hello очищает экран и создает панель экрана, как показано на рисунке 1.2. Окна не открываются, а в полосе меню появляется только один элемент: команда Hello. Заметим, что "H" в Hello выделена; и что строка статуса содержит сообщение: Alt-X Exit. Рис. 1.2. Начальный экран HELLO.PAS. +--------------------------------------+ | Hello | +--------------------------------------| |**************************************| |**************************************| |**************************************| |**************************************| |**************************************| |**************************************| +--------------------------------------| | Alt-X Exit | +--------------------------------------+ Теперь самое время дать 2 общих правила для программирования любого пользовательского интерфейса: никогда не помещайте пользователя в точку, где он не знает что делать дальше и всегда давайте пользователю способ пройти вперед и вернуться назад. Перед тем, как что-либо сделать, пользователь HELLO имеет 2 ясных выбора: либо выбрать элемент меню Hello, либо нажать Alt-X, чтобы выйти из программы.

Выпадающие меню.

Выберите Hello из полосы меню. Для этого есть 3 способа: - Передвиньте указатель мышки на Hello и нажмите левую кнопку. - Нажмите F10 для перехода курсора в полосу меню, при этом Hello подсвечивается. Нажмите Enter для выбора Hello. - Нажмите Alt-H, где Н - подсвеченный символ в слове Hello. Во всех 3 случаях выпадающее меню появляется под элементом Hello. Это знакомо Вам, как программисту на Turbo Pascal. IDE Turbo Pascal действует таким же образом. Вы видите, что Turbo Vision использует все соглашения интегрированной среды Turbo Pascal, более того, IDE - это программа на Turbo Vision! Появившееся меню показано на рисунке 1.3. В этом меню только 2 элемента, разделенных линией на 2 панели. Hello - это простая программа, в которой в каждой панели только один элемент меню, но в действительности в панели может быть любое число элементов, зависимое только от ограничений экрана. Рис. 1.3. Меню HELLO.PAS. +------------------+ |**Greeting.*****| +------------------| | Alt-X Exit | +------------------+ Примечание: "." после элемента меню указывают на то, что элемент вызывает диалоговое окно. Вы можете выбрать элемент меню с помощью клавиатуры или мышки. Клавиши курсора передвигают полосу подсветки вверх и вниз. Чтобы выбрать подсвеченный элемент с клавиатуры, нажмите Enter, когда будет подсвечен нужный элемент. Более интересен выбор мышкой: Вы нажимаете на полосе подсветки на левую кнопку мышки и передвигаете мышку. До тех пор, пока Вы держите кнопку нажатой, Вы можете передвигать полосу вверх и вниз по списку элементов внутри меню. Вы выбираете один из элементов меню, отпуская кнопку мышки, когда полоса подсветки находится на требуемом элементе меню.

Диалоговое окно.

Когда Вы выбираете элемент Greeting, открывается диалоговое окно, показанное на рисунке (XXX) Диалоговое окно появляется в центре экрана, но Вы можете передвигать его по экрану, установив указатель мышки на верхнюю строку диалогового окна, нажав левую кнопку мышки и передвигая мышку до тех пор, пока Вы держите кнопку мышки. Как только Вы отпустите кнопку, диалоговое окно остановится и будет оставаться в этом месте. Рис. 1.4. ДиалоговоеокноHello,World! `+=[ю]======Hello, World!===============+ | | | Terrific | | --------------+| | OK | | How are you? --------------+| | Lousy | | --------------+| | Cancel | | --------------+| +======================================+ Диалоговое окно имеет заголовок "Hello, World!" и закрывающую кнопку в левом верхнем углу. Если выбрать закрывающую кнопку мышкой, диалоговое окно закрывается и исчезает. Внутри диалогового окна выводится строка "How are you?" Это пример статического текста, который можно прочитать, но который не содержит интерактивных возможностей. Другими словами, статический текст используется для того, чтобы выдать какую-то информацию, но если Вы выберите его, ничего не случится.

Кнопки.

4 прямоугольника с правой стороны окна - наиболее интересная часть диалогового окна Hello, World! Они называются кнопками и введены для примера элементов управления. Они называются кнопками, поскольку они сходны с кнопками электронных приборов. Каждая кнопка имеет метку, которая указывает, что случится, когда эта кнопка нажата. Вы нажимаете на кнопку, выбрав ее мышкой или сделав кнопку кнопкой по умолчанию (объяснено позже в этом разделе) и нажав Enter. Попытайтесь нажать одну из кнопок мышкой и посмотрите что случится. Тело кнопки передвигается на 1 позицию вправо и ее тень исчезает. Это создает иллюзию, что кнопка была нажата на экране. Когда Вы отпускаете кнопку мышки, выполняется действие, связанное с этой кнопкой. Заметьте, что надпись внутри кнопки Cancel выведена другим цветом, чем остальные кнопки. Отличие в цвете указывает на то, что кнопка Cancel является кнопкой по умолчанию внутри диалогового окна. Если Вы нажмете Enter, когда Cancel по умолчанию, действие будет таким же, как при нажатии кнопки Cancel. (Монохромные системы указывают кнопку по умолчанию символами ">> <<".) Кнопка по умолчанию внутри диалогового окна изменяется нажатием клавиши Tab. Попробуйте использование Tab внутри диалогового окна Hello, World! Цвет кнопки по умолчанию передвигается от одной кнопки к другой при каждом нажатии клавиши Tab. Это позволяет пользователю нажимать кнопку без использования мышки, передвигая умолчание к требуемой кнопке с помощью клавиши Tab и нажимая Enter или пробел, чтобы "нажать" кнопку.

Выход.

Нажатие любой кнопки в Hello приведет к исчезанию диалогового окна. Вы можете открыть меню Hello снова и использовать диалоговое окно любое количество раз. Чтобы выйти из программы, Вы можете выбрать элемент Exit в меню Hello или использовать короткую форму Exit: Alt-X. Заметим, что короткая форма представлена как внутри меню Hello, так и в строке статуса внизу экрана. Примечание: Хорошая практика: всегда предоставляйте пользователю простой выход из программы. Пользователи, которые не могут выйти из программы, начинают перезагружать компьютер, не давая Вашей программе закрыть файлы или выполнить другие действия перед закрытием системы.

Внутри HELLO.PAS.

Что делает HELLO, когда Вы выполняете ее? Как она делает все, что Вы видите? Большая часть кода HELLO наследована от предопределенных объектов из Turbo Vision. Наследуется так много, что когда программа выполняется, это может показаться несколько мистическим. Трассировка выполнения с помощью встроенного отладчика не покажет всей картины, поскольку Turbo Vision поставляется в виде откомпилированных модулей. Однако, если у Вас есть время разобраться в том, что здесь делается, точное понимание как делается, необязательно. Для понимания приложений Turbo Vision начните с мысли о том, что приложение Turbo Vision - это совокупность объектов, работающих вместе. Найдите главные объекты и разберитесь, как они работают вместе. Затем посмотрите, как сопутствующие объекты поддерживают главные объекты. Прочитайте и поймите определения объектов до чтения о реализации методов. Важно, чтобы Вы понимали, что содержат объекты и как связаны объекты между собой.

Прикладной объект.

Краеугольным объектом любой программы является объект TApplication. В действительности, Вы никогда не создаете экземпляра объекта типа TApplication. TApplication - это абстрактный тип объекта. Он ничего не делает. Вы используете TApplication, создавая порожденные типы от TApplication, которые содержат Ваш программный код. В HELLO определяется порожденный тип объекта THelloApp: PHelloApp =^THelloApp; THelloApp = object(TApplication) procedure GreetingBox; procedure HandleEvent(var Event: TEvent); virtual; procedure InitMenuBar; virtual; procedure InitStatusLine; virtual; end; Как показано здесь, полезно определить тип указателя на каждый тип, определяемого Вами объекта, поскольку большая часть работы над объектами производится через указатели. Полиморфизм главным образом работает через указатели. THelloApp содержит гораздо больше, чем эти 4 метода; порожденный объект наследует все от его предка. В определении THelloApp Вы определяете, чем новый объект отличается от его предка TApplication. Все, что Вы не переопределяете, наследуется в неизменном виде от TApplication. 4 метода, определенные в THelloApp, завершают "большую картину" Вашего приложения: - Как прикладным функциям указывается, что событие произошло и как они отвечают на них. Вы должны определить метод HadleEvent для выполнения этого требования. Метод HandleEvent, определенный в TApplication, работает с общими событиями, которые возникают внутри любой программы, но Вы должны обеспечить обработку событий, специфичных для Вашей программы. - Метод InitMenuBar устанавливает меню для полосы меню Вашей программы. TApplication содержит полосу меню, но не сами меню; если Вам необходимы меню, Вы просто определяете метод для определения меню. Вас может удивить, почему код InitMenuBar не является частью констрактора THelloApp. Может быть и так, но большие возможности предоставляет выбор из нескольких меню для начального меню. Лучше вывести это за пределы констрактора, оставляя внутри констрактора только те вещи, которые необходимо выполнять всегда при каждом выполнении программы. - Метод InitStatusLine устанавливает текст строки статуса внизу экрана. Этот текст обычно отображает сообщение о текущем состоянии программы, показывая доступные горячие клавиши или напоминая пользователю о некоторых действиях. - Метод GreetingBox вызывает диалоговое окно в ответ на элемент меню Greeting. GreetingBox вызывается из метода HandleEvent в ответ на событие, переключаемое выбором элемента меню Greeting. В более сложных программах Вы можете использовать различные методы в ответ на каждый элемент меню, определенный в начальном меню. Короче, методы из THelloApp обеспечивают все, что должны выполнять объекты главной программы: установку программы, выполнение действий в ответ на события и методы реализующие отклики на отдельные события. Именно эти 3 вещи Вы должны добавить к TApplication при создании порожденного типа объекта.

Объект диалоговое окно.

Другим важным объектом, используемым в Hello, является диалоговое окно. Поскольку диалоговое окно не выполняет ничего специального, Hello использует экземпляр объекта TDialog. Не требуется порождать специального объекта из TDialog. Сам TDialog не содержит интерактивных элементов. Это ничего более, чем оболочка; Вы предоставляете поля или элементы управления, взаимодействующие с пользователем. THelloApp.GreetingBox строится на основе TDialog, вставляя 4 кнопки, которые являются видимыми элементами Turbo Vision. (Вспомним, что все элементы программы, которые выводят что-либо на экран, должны быть видимыми элементами Turbo Vision!) Это обычная ситуация при использовании диалоговых окон. Обычно Вы просто вставляете элементы управления, которые необходимы в диалоговом окне. Все остальное, что должно быть в диалоговом окне (включая обработчик событий) встроено в TDialog.

Выполнение и отладка.

Поскольку программы Turbo Vision управляются событиями, их код структурирован несколько по другому, чем у обычных программ. Программы, управляемые событиями, отделяют управляющие структуры, которые читают и оценивают ввод пользователя (и другие события) от процедур и функций, которые выполняют действия на основе этого ввода. Конвенциональные программы обычно содержат множество блоков кода, каждый из которых включает получение ввода, решение, какой код получает этот ввод, вызов соответствующих программ для обработки ввода и выполнение этих действий снова. Кроме того код, который заканчивает обработку ввода, должен знать, где получить управление для следующей порции ввода. С другой стороны, программы, управляемые событиями, имеют центральный механизм диспетчеризации событий так, что Ваша программа не должна заботиться о получении ввода и решении что делать с ним. Ваши программы просто ждут, когда центральный диспетчер обработает их ввод. Это важно при отладке Ваших программ: Вы вероятно, измените стратегии отладки, установите точки прерывания в программах обработки событий для проверки сообщений и установите точки прерывания в коде отклика на события для проверки, что он функционирует правильно.

Главная программа.

На верхнем уровне абстракции главная программа на Turbo Vision выглядит аналогично Hello: var HelloWorld: THelloApp; begin HelloWorld.Init; HelloWorld.Run; HelloWorld.Done; end; Каждый из этих 3 методов требует небольшого пояснения.

Метод Init.

Первый из трех операторов HelloWorld.Init - это необходимый вызов констрактора. Все объекты, содержащие виртуальные методы, должны быть созданы (через вызов их констрактора) до вызовов любых других методов этого объекта. Все констракторы Turbo Vision имеют имя Init. Это удобное соглашение, которым мы рекомендуем пользоваться. HelloWorld.Init устанавливает объекты главной программы для использования. Он очищает экран, устанавливает некоторые значения для важных переменных, заполняет панель экрана и выводит строку статуса и полосу меню. Он вызывает констракторы многих других объектов, некоторые из которых никогда не видны, поскольку они вызываются внутри. Интересно использование встроенного отладчика для трассировки HelloWorld.Init через F8 с последующим просмотром дисплея через Alt -F5. Панель экрана, полоса меню и строка статуса будут появляться, готовя главную программу для использования.

Метод Run.

Почти вся работа программы в Turbo Vision выполняется внутри метода главной программы Run. Мистика начинается, когда Вы смотрите на определение THelloApp для нахождения определения метода Run. Его здесь нет - поскольку Run наследуется от родительского объекта TApplication. Run - это та части, в которой Ваша программа будет, вероятно, тратить большую часть своего времени. Она состоит главным образом из цикла repeat.until, показанном здесь в псевдокоде: repeat Получить событие; Обработать событие; until Quit; Это не точный код, а общая схема реализации Run. В сущности программа Turbo Vision циклически выполняет 2 задачи: получение события (где событие - это, в сущности, "сделать что-нибудь") и обработка этого события. Одно из событий вычисляется в некоторый вид команды выхода и цикл завершается.

Метод Done.

Дестрактор Done в действительности очень прост: он уничтожает все объекты программы - полосу меню, строку статуса и панель экрана - и удаляет обработчик ошибок и драйверы Turbo Vision. Метод Done должен отменять все действия, выполненные констрактором Init, а затем вызывать TApplication.Done, который обрабатывает все стандартные элементы. Если Вы перекрыли TApplication.Init, Вы, вероятно, будете перекрывать TApplication.Done.

Итоги.

Эта глава дает Вам возможность попробовать Turbo Vision. Вы увидели объекты, взаимодействующие в оболочке, управляемой событиями, и получили некоторое представление об инструментах Turbo Vision. С этой точки Вы можете чувствовать себя достаточно уверенными для того, чтобы модифицировать программу HELLO.PAS. Одна из лучших возможностей Turbo Vision - это предоставление Вам свободы изменять Ваши программы с минимальными усилиями. Следующая глава позволяет Вам строить программы Turbo Vision из нашей основы.