ГЛАВА 2. НАПИСАНИЕ ПРОГРАММ В TURBO VISION.
Сейчас, когда Вы увидели как выглядят программы Turbo Vision,
внутри и снаружи, Вы, вероятно, захотите написать свою программу. В
этой главе Вы выполните это, начиная с крайне простой программы и
добавляя небольшие фрагменты кода на каждом шаге так, чтобы было
видно что делает каждый фрагмент.
Вероятно в данный момент у Вас много вопросов. Как именно
работают видимые элементы? Что я должен делать с ними? Как их
настраивать в моей программе? Если бы Turbo Vision была обычной
библиотекой времени выполнения, скорее всего Вы стали бы смотреть
исходный код, чтобы получить эти ответы.
Но Turbo Vision уже работающая программа. Лучший способ
ответить на Ваши вопросы по Turbo Vision - это попробовать видимые
элементы. Как Вы увидите, Вы можете инициализировать их минимальным
кодом.
Ваша первая программа в Turbo Vision.
Программа Turbo Vision всегда начинается с создания экземпляра
объекта, порожденного от TApplication. В следующем примере Вы
создадите порожденный от TApplication тип с именем TMyApp и будете
перекрывать методы TApplication. От нового типа Вы создадите
экземпляр MyApp.
Примечание: До конца этой главы мы часто будем ссылаться к
MyApp. Когда Вы пишите собственную программу в Turbo Vision, Вы,
вероятно, назовете ее как-то иначе, в соответствии с назначением
этой программы. Мы используем MyApp, поскольку это короче, чем
сказать "экземпляр объекта, порожденного от TApplication". (В
программе существует только 1 объект TApplication.)
Начиная со следующего примера, мы создадим программу. Вместо
того, чтобы приводить листинг программы каждый раз, мы будем
включать только добавляемые или изменяемые части в тексте. Если Вы
создаете сами эту программу, Вы должны иметь хорошее представление,
что делается в каждом изменении функциональности. Мы так же
рекомендуем Вам попробовать модифицировать примеры.
Примечание: Некоторые стадии из создания примеров представлены
на Вашем дистрибутивном диске. Имена файлов указаны вместе с кодом
примеров и они соответствуют именам, объявленным в операторе
program.
Главный блок TVGUID01.PAS (и каждой программы в Turbo Vision)
выглядит подобно:
program TFirst;
uses App;
type
TMyApp = object(TApplication)
end;
var
MyApp: TMyApp;
begin
MyApp.Init;
MyApp.Run;
MyApp.Done;
end.
Заметим, что Вы еще не добавили никаких новых функций в
TMyApp. Обычно Вы никогда не объявляете полностью новый объектный
тип без полей или методов. Вы просто объявляете переменную MyApp,
как экземпляр типа TApplication. Поскольку Вы будете расширять его
позже, когда будете писать программу в Turbo Vision, Вы установите
TMyApp для гибкого расширения. По умолчанию TApplication создает
экран, как показано на рисунке 2.1.
Рис. 2.1. Экран TApplication по умолчанию.
+--------------------------------------+
| |
+--------------------------------------|
|**************************************|
|**************************************|
|**************************************|
|**************************************|
|**************************************|
|**************************************|
+--------------------------------------|
| Alt-X Exit |
+--------------------------------------+
Эта программа делает только одно: она реагирует на Alt-X и
завершает программу. Чтобы она делала что-то еще, Вам требуется
добавить дополнительные команды в строку статуса и/или в полосу
меню. В следующем разделе Вы сделаете это.
Панель экрана, полоса меню и строка статуса.
Используемые объекты: TView, TMenuView, TMenuBar, TMenuBox,
TStatusLine, TGroup, TDeskTop.
Панель экрана, полоса меню и строка статуса в TFirst создаются
методами InitDeskTop, InitMenuBar и InitStatusLine из TApplication.
Эти 3 метода вызываются в TApplication.Init и Вам никогда не нужно
вызывать их напрямую. Вместо этого метод Init Вашей программы будет
вызывать TApplication.Init в первой строке. Например
procedure TMyApp.Init;
begin
TApplication.Init;
{ код инициализации для Вашей программы }
end;
Заметим, что Вам необходимо добавить некоторые модули Turbo
Vision в оператор uses Вашей программы. Для того, чтобы
использовать меню, строку статуса и определения стандартных клавиш,
Вам необходимо кроме App использовать Objects, Menus и Drivers.
(Объекты и их модули описаны в главе 12.)
Если Ваша программа не требует какой-либо специальной
инициализации, Вы просто используете унаследованный метод Init.
Поскольку Init и методы InitDeskTop, InitMenuBar и InitStatusLine
виртуальные, вызов унаследованного Init вызывает соответствующие
методы InitStatusLine и InitMenuBar. Вы увидите это в TVGUID02.PAS.
InitDeskTop, InitMenuBar и InitStatusLine устанавливают
зхначения глобальных переменных DeskTop, MenuBar и StatusLine
соответственно. Давайте посмотрим каждую из них.
Панель экрана.
Панель экрана - это исключительно важный объект, но он требует
от Вас очень небольших действий. Вы не должны перекрывать
унаследованный метод инициализации. Пусть TApplication.InitDeskTop
обрабатывает его. DeskTop принадлежит MyApp и когда MyApp
устанавливает новый видимый элемент в ответ на ввод пользователя,
она должна подключить новый элемент пользователя к DeskTop. Панель
экрана знает как управлять видимыми элемента.
Строка статуса.
TApplication.InitStatusLine устанавливает видимый элемент
TStatusLine, вызывая StatusLine для определения и отображения
горячих клавиш. StatusLine выводится, начиная с левого края экрана
и любая часть нижней строки экрана, не требуемая для элементов
строки статуса, свободна для других видимых элементов. TStatusLine
связывает горячие клавиши с командами и сами элементы могут быть
отмечены мышкой.
Примечание: Горячие клавиши - это комбинации клавиш, которые
действуют как элементы меню или строки статуса.
TVGUID02.PAS создает строку статуса, перекрывая
TApplication.InitStatusLine:
procedure TMyApp.InitStatusLine;
var R: TRect; { хранит границы строки статуса }
begin
GetExtent(R); { устанавливает R в координаты всего}
{ экрана }
R.A.Y := R.B.Y - 1; { передвигает вершину на 1 строку }
{ выше нижней }
StatusLine := New(PStatusLine, Init(R, { создает строку }
{ статуса }
NewStatusDef(0, $FFFF, { устанавливает диапазон контекстного }
{ Help }
NewStatusKey('~Alt-X~ Exit', kbAltX, cmQuit, { определяет
элемент }
NewStatusKey('~Alt-F3~ Close', kbAltF3, cmClose, { другой }
nil)), { больше нет клавиш }
nil) { больше нет определений }
));
end;
Примечание: Не забудьте добавить
procedure InitStatusLine; virtual;
в объявление TMyApp.
Инициализация - это последовательность вложенных вызовов
стандартных фунций Turbo Vision NewStatusDef, NewStatusKey и
NewStatusBar (детально описаны в главе 14). TVGUID02 определяет
строку статуса для отображения диапазона контекстной справочной
информации от 0 до $FFFF и связывает стандартную команду cmQuit с
клавишей Alt-X, а стандартную команду cmClose с клавишей Alt-F3.
(Команды Turbo Vision - это константы. Их идентификаторы начинаются
с cm.)
Вы можете заметить, что в отличие от TMyApp.Init, метод
InitStatusLine не вызывает метод, который он перекрывает -
TApplication.InitStatusLine. Причина проста: обе программы
устанавливают строки статуса, которые охватывают одинаковый
диапазон контекстной справочной системы и назначают его одной
переменной. В TApplication.InitStatusLine нет ничего, что позволило
бы TMyApp.InitStatusLine выполнить работу более просто и, кроме
того, Вы потратите время и память на ее вызов.
Последняя строка, выводимая в строке команд этой инициализации
- "Alt-F3 Close". Часть строки, заключенная в "~", будет
подсвечиваться на экране. Пользователь может отметить мышкой любую
часть строки для активации команды.
Когда Вы выполняете TVGUID02, Вы заметите, что элемент статуса
Alt-F3 не подсвечен и отметка его мышкой не имеет эффекта. Это
происходит потому, что команда cmClose по умолчанию запрещена, и
элементы, которые генерируют запрещенные команды, так же запрещены.
После того, как Вы откроете окно, cmClose и элемент статуса будут
активированы.
Ваша строка статуса работает сразу после инициализации
StatusLine, поскольку Вы используете только предопределенные
команды (cmQuit и cmClose.) StatusLine может обрабатывать ввод
пользователя без Вашего вмешательства.
Создание новых команд.
Заметим, что команды cmQuit и cmClose, которые Вы связали с
элементами строки статуса, являются стандартными командами Turbo
Vision, поэтому Вам не требуется определять их. Для того, чтобы
использовать собственные команды, Вы просто объявляете Ваши команды
как константные значения. Например, Вы можете определить новую
команду, чтобы открыть новое окно:
const
cmNewWin = 199;
Примечание: Turbo Vision резервирует некоторые константы для
собственных команд. См. "Определение команд" в главе 5.
Затем Вы можете связать эту команду с горячей клавишей и с
элементом строки статуса.
StatusLine := New(PStatusLine, Init(R,
NewStatusDef(0, $FFFF,
NewStatusKey('~Alt-X~ Exit', kbAltX, cmQuit,
NewStatusKey('~F4~ New', kbF4, cmNewWin, { включение новой
команды }
NewStatusKey('~Alt-F3~ Close', kbAltF3, cmClose,
nil))),
nil)
));
Синтаксис инициализации строки статуса - это хорошее введение
в инициализацию меню, которая более сложна.
Полоса меню.
Переменная полосы меню MenuBar инициализируется вложенными
вызовами стандартных функций NewMenu, NewSubMenu, NewItem и
NewLine.
После того, как Вы инициализируете меню, Ваша работа
закончена. Полоса меню знает как обработать ввод пользователя без
Вашей помощи.
Инициализируем простую полосу меню с одним элементом,
содержащем один выбор:
## File #############
+----------------+**
|**Open F3*******|#*
+----------------+#*
**##################*
*********************
const
cmFileOpen = 200; { определение новой команды }
procedure TMyApp.InitMenuBar;
var R: TRect;
begin
GetExtent(R);
R.B.Y := R.A.Y + 1;
MenuBar := New(PMenuBar, Init(R, NewMenu( { создать полосу с
меню }
NewSubMenu('~F~ile', hcNoContext, NewMenu( { определить
меню }
NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext,
{ элемент }
nil)), { больше нет элементов }
nil) { больше нет подменю }
))); { конец полосы }
end;
Меню, создаваемое этим кодом, называется 'File' и элемент меню
называется 'Open'. "~" делает F символом короткого ввода в 'File',
а O - символом короткого ввода 'Open'; клавиша F3 устанавливается
как горячая клавиша для 'Open'.
Все видимые элементы Turbo Vision могут иметь номер
контекстной подсказки, связанный с ними. Номер позволяет просто
реализовать контекстно-ориентированную справочную систему в Вашей
программе. По умолчанию видимые элементы имеют контекст
hcNoContext - это специальный контекст, который не изменяет текущий
контекст. Номера контекстов подсказки появляются при инициализации
полосы меню, поскольку из-за вложенной структуры этих объектов
добавить номера позднее будет трудно. Когда Вы готовы добавить
контекст подсказки в полосу меню, Вы можете подставить свои
значения для hcNoContext в коде Init.
Чтобы добавить второй элемент в меню 'File', Вы просто
вкладываете другую функцию NewItem:
## File #############
+----------------+**
|**Open F3*******|#*
| New F4 |#*
+----------------+#*
**##################*
*********************
MenuBar := New(PMenuBar, Init(R, NewMenu(
NewSubMenu('~F~ile', hcNoContext, NewMenu(
NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext,
NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext,
nil))),
nil)
)));
Чтобы добавить второе меню, Вы вкладываете другую функцию
NewSubMenu:
## File Window###########
********+---------------+**
********|**Next F6******|#*
********| Zoom F5 |#*
********+---------------+#*
**********################*
***************************
MenuBar := New(PMenuBar, Init(hcNoContext, NewMenu(
NewSubMenu('~F~ile', hcNoContext, NewMenu(
NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext,
NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext,
nil))),
NewSubMenu('~W~indow', hcNoContext, NewMenu(
NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext,
NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext,
nil)),
nil))) { закрывающая скобка для меню }
)));
Вы связали 2 стандартных команды Turbo Vision cmNext и cmZoom
с элементами меню и горячими клавишами.
Чтобы добавить горизонтальную линию между выборами в меню,
вставьте вызов NewLine между вызовами NewItem:
## File Window######
+----------------+**
|**Open F3*******|#*
| New F4 |#*
+----------------+#*
| Exit Alt-X |#*
+----------------+#*
**##################*
*********************
{ находится в TVGUID03.PAS }
MenuBar := New(PMenuBar, Init(hcNoContext, NewMenu(
NewSubMenu('~F~ile', hcNoContext, NewMenu(
NewItem('~O~pen', 'F3', kbF3, cmFileOpen, hcNoContext,
NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext,
NewLine(
NewItem('E~x~it', 'Alt-X', kbAltX, cmNewWin, hcNoContext,
nil))))),
NewSubMenu('~W~indow', hcNoContext, NewMenu(
NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext,
NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext,
nil))),
nil))
)));
Вы можете заметить, что версия TVGUID03.PAS на Вашем диске так
же добавляет клавишу статуса в строку статуса, связывая клавишу F10
с командой cmMenu. cmMenu - это стандартная команда Turbo Vision,
которая помогает пользователям использовать полосу меню без мышки.
В этом случае клавиша F10 активирует полосу меню, позволяя выбрать
меню и элементы меню, используя клавиши курсора.
Вы можете так же заметить, что элемент статуса имеет пустую
строку в качестве текста и для него ничего не появляется на экране.
Хотя можно предупредить пользователей, что F10 будет активировать
меню, они, скорее всего, не будут указывать этот элемент мышкой.
Отметить полосу меню гораздо более удобно.
Замечания по структуре.
В данный момент доступен ряд команд, но большинство из них
запрещены, а команды cmNewWin и cmFileOpen еще не выполнили никаких
действий.
Если Ваша первоначальная реакция - одно разочарование, этого
не должно быть, Вы сделали многое! В действительности, Вы открыли
одно из больших достижений программирования управляемого событиями:
Вы отделили функции получения ввода от функций отклика на этот
ввод.
Традиционная техника программирования требует возвращения в
написанный код и добавления кода для открытия окон и т.п. Но Вы не
делали этого: Вы получили законченную систему, которая знает, как
генерировать команды. Все, что Вам требуется - это написать
несколько программ, которые реагируют на эти команды. Это Вы и
сделаете в следующем разделе.
Оболочка программ Turbo Vision делает следующий шаг по
сравнению с традиционным модульным программированием. Вы не только
разбиваете Ваш код на функциональные, вновь используемые блоки, но
и эти блоки могут быть меньше, более независимыми и более
взаимозаменяемыми.
Ваша программа сейчас имеет несколько различных способов
генерации команды (cmNewWin), чтобы открыть окно: элемент строки
статуса, элемент меню и горячая клавиша. Вы видите, как просто
сказать Вашей программе открыть окно, когда показана команда.
Наиболее важно то, что программа не заботится о том, ни как
генерируется команда, ни как создается окно. Эти фукции независимы.
Если позднее Вы захотите изменить связки команд - переместить
выбор меню, переназначить горячие клавиши - Вам не нужно об этом
беспокоиться и даже думать о том, как это будет влиять на остальной
код. Т.е. программирование, управляемое событиями, отделяет
проектирование пользовательского интерфейса от работы Вашей
программы и позволяет различным частям программы функционировать
независимо.
Открытие окон.
Используемые объекты: TRect, TView, TWindow, TGroup,
TScroller, TScrollBar.
Если Вы программист, Вы можете сразу перейти на этот раздел,
как только Вы откроете книгу. Кроме того, что может быть более
важно для написания оконных программ, чем создание окон?
Это было бы так, если бы Turbo Vision была набором
традиционных библиотечных программ. В этом случае переход на этот
раздел и попытка начать работать была бы хорошей идеей.
Но Turbo Vision - не традиционная библиотека. Если Вы читали
предыдущие разделы, Вы уже знаете об этом. Для того, чтобы
программировать в Turbo Vision необходимо выполнить некоторые
действия до того, как создавать окна. Вам необходимо понимать, что
такое окно Turbo Vision (а это объект!) и чем оно отличается от
окон, которые Вы использовали раньше. Когда Вы сделаете это, Вы
продвинитесь вперед гораздо дальше, чем Вы можете вообразить.
Поэтому, если Вы открыли книгу в этом месте, Вам необходимо
вернуться к предыдущим разделам и прочитать их.
Стандартное оформление окон.
Окно Turbo Vision - это объект со встроенной в него
возможностью реагировать на ввод пользователя без написания
специального кода. Окна Turbo Vision уже знают как открываться,
изменять размер, перемещаться и закрываться. Но Вы не пишите в окно
Turbo Vision. Окно Turbo Vision содержит то, что содержат и чем
управляют другие объекты: эти объекты отображают себя на экране.
Окно управляет видимыми элементами и функции Вашей программы - это
видимые элементы, которые окно содержит и которыми управляет.
Видимые элементы, создаваемые Вами, предоставляют большую гибкость
в том, где и как они появляются. Как же Вам комбинировать
стандартные окна с теми элементами, которые Вы хотите поместить в
них? Снова и снова запомните, что Вы получили мощную оболочку для
построения и использования. Начните со стандартного окна, затем
добавьте требуемые Вам возможности. Как только Вы просмотрите
несколько следующих примеров, Вы увидите как просто наращивается
программа вокруг представленной Turbo Vision основы.
Следующий код инициализирует окно и подсоединяет его к панели
экрана. Не забудьте добавить новые методы к объявлению Вашего типа
TMyApp. Заметим, что Вы опять определяете новый тип (TDemoWindow)
не добавляя полей и методов к типу предка. Как и раньше Вы просто
создаете основу, которую Вы сможете быстро достраивать. Вы добавите
новые методы при необходимости.
{ TVGUID04.PAS }
uses Views;
const
WinCount: Integer = 0; { инициализация счетчика окон }
type
PDemoWindow = ^TDemoWindow; { заметим, что Вы всегда объявляете
тип указателя для каждого нового
объектного типа }
TDemoWindow = object(TWindow) { определение нового типа окна }
end;
procedure TMyApp.NewWindow;
var
Window: PDemoWindow;
R: TRect;
begin
Inc(WinCount);
R.Assign(0, 0, 26, 7); { установка начального размера и
позиции }
R.Move(Random(58), Random(16)); { случайное перемещение по
экрану }
Window := New(PDemoWindow, Init(R, 'Demo Window', WinCount));
DeskTop^.Insert(Window); { вывести окно на панель экрана }
end;
procedure TMyApp.HandleEvent(var Event: TEvent);
begin
TApplication.HandleEvent(Event); { действует как предок }
if Event.What = evCommand then
begin
case Event.Command of { но откликается на дополнительные
команды }
cmNewWin: NewWindow; { определяет действие для команды
cmNewWin }
else
Exit;
end;
ClearEvent(Event); { очищает событие после обработки }
end;
end;
Чтобы использовать это окно в программе, Вам необходимо
связать программу cmNewWin с опцией меню или горячей клавишей
строки статуса, как Вы делали ранее. Когда пользователь вызывает
cmNewWin, Turbo Vision пересылает команду в TMyApp.HandleEvent,
который реагирует, вызывая TMyApp.NewWindow.
Инициализация окна.
Вам необходимо передать в окно Turbo Vision 3 параметра,
чтобы оно могло себя инициализировать: его размер и позицию на
экране, заголовок и номер окна.
Первый параметр TRect - прямоугольный объект в Turbo Vision -
определяет размер и позицию окна. Его метод Assign дает его размер
и позицию, основанную на верхнем левом и нижнем правом углах.
Существует несколько способов назначения или изменения объектов
TRect. См. главу 14 "Справочник по глобальным элементам" для
полного описания.
Примечание: Объект TRect детально описан в главе 4 "Видимые
элементы".
В TVGUID04 R создается от начала DeskTop, затем смещается на
случайное расстояние на панели экрана. "Нормальные" программы,
вероятно, не будут выполнять такое случайное движение. Это сделано
только для примера, чтобы Вы могли открыть несколько окон и чтобы
они не размещались в одном месте.
Второй параметр инициализации - это строка, которая
отображается как заголовок окна.
Последний параметр хранится в поле окна Number. Если Number от
1 до 9, он выводится в рамке окна и пользователь может выбрать
пронумерованное окно, нажав от Alt-1 до Alt-9.
Если Вам не нужно назначать номер окну, просто передайте
константу wnNoNumber.
Метод Insert.
Вставка окна в DeskTop автоматически приводит к появлению
окна. Метод Insert используется, чтобы дать визуальный контроль над
видимым элементом. Когда Вы выполняете
DeskTop^.Insert(Window);
Вы вставляете Window в панель экрана. Вы можете вставить любое
число видимых элементов в групповой объект, такой как панель
экрана. Группа, в которую Вы вставляете видимый элемент называется
видимым элементом - владельцем, а видимый элемент, который Вы
вставляете, называется видимым подэлементом. Заметим, что видимый
подэлемент сам может быть группой и может иметь свои видимые
подэлементы. Например, когда Вы вставляете окно в панель экрана,
окно является подэлементом, но окно может владеть рамкой, полосами
скроллинга и другими видимыми подэлементами.
Этот процесс установления связей между видимыми объектами
создает дерево видимых элементов. (Взаимосвязи между видимыми
элементами объяснены в главе 4.)
Закрытие окна.
Отметка закрывающей кнопки окна генерирует такую же команду
cmClose, какую Вы связали с клавишей Alt-F3 и элементом строки
статуса. По умолчанию открытие окна (через F4 или выбор в меню File
/Open) автоматически разрешает команду cmClose и видимые элементы,
которые генерируют ее (а так же другие оконные команды, такие как
cmZoom, cmNext).
Вам не требуется писать код для того, чтобы закрыть окно.
Когда пользователь отметил закрывающую кнопку окна, Turbo Vision
сделает все остальное. По умолчанию окно реагирует на команду
cmClose вызовом своего дестрактора Done:
Dispose(MyWindow, Done);
Как часть метода окна Done, он вызывает методы Done для всех
своих подэлементов. Если Вы сами распределяли дополнительную память
в констракторе окна, Вам требуется освободить ее в методе окна
Done.
Поведение окна.
Давайте попробуем поработать с Вашей программой. Она уже имеет
ряд возможностей. Она знает как открыть, закрыть, выбрать,
переместить и изменить размеры множества окон на панели экрана.
Неплохо для менее, чем ста строк кода!
После того, как TMyApp инициализирует окно, он вставляет его в
панель экрана. Как Вы помните, DeskTop - это группа, что означает,
что его назначение - владеть и управлять видимыми подэлементами,
такими как Ваше окно. Если Вы откомпилировали и выполнили код, Вы
заметите, что Вы можете изменять размер, перемещать и закрывать
новое окно. Ваш ввод от мышки преобразуется в серию событий и
направляется из панели экрана в новое окно, которое знает, как
обработать их.
Если Вы сохранили вызов cmNewWin, на панели экрана будет
появляться несколько окон с уникальными номерами. Эти окна могут
изменять размеры, выбираться и перемещаться. Рис. 2.2 показывает
панель экрана, на котором открыто несколько окон.
Рис. 2.2. TVGUID04 с несколькими открытыми окнами.
+-----------------------------------------------------------------+
| File Window |
|*****************************************************************|
|*********************+-- Demo Window 3----+**********************|
|*********************| +-- Demo Window 7--+*****|
|*********************| | |*****|
|*********************| | |*****|
|*********************| +-- Demo Window 8--+ |*****|
|*********************| | | |*****|
|*********************+--| | |*****|
|+-- Demo Window 1--+** | |---------------+*****|
|| |** | | |***************|
|| |** | | |***************|
|| +-- Demo Window 4--+------------------+ |***************|
|| | |****| +-- Demo Window 6--+Window 2--+*|
|| | |****| | | |*|
|+----| |****+--| | |*|
|*****| +=[ю]= Demo Window 9=[ш]=+ | |*|
|*****| | | | |*|
|*****+------------------| | | |*|
|************************| |--+----------+*|
|************************| |***************|
|************************| |***************|
|************************+=======================-+***************|
|*****************************************************************|
| Alt-X Exit F4 New Alt-F3 Close |
+-----------------------------------------------------------------+
TWindow - это группа, которая первоначально владеет одним
видимым элементом TFrame. Пользователь отмечает кнопки на рамке для
перемещения, изменения размера или закрытия окна. Рамка отображает
заголовок, который был получен во время инициализации окна и он
рисуется фоновым цветом окна, таким как TBackGround панели экрана.
Все это происходит без написания Вашего кода.
Просмотр в любом окне.
Если Вы работали с традиционными окнами, то следующим шагом Вы
попытаетесь записать что-либо в него. Но TWindow не пустая доска
для записи: это группа Turbo Vision, объект TGroup без экранного
представления всего, что лежит под ним. Чтобы поместить что-либо в
окно, Вам необходимо сделать дополнительный шаг, который вложит в
Ваши руки огромную мощь.
Чтобы что-либо появилось в окне, Вы создаете видимый элемент,
который знает как рисовать себя и вставляете его в окно. Этот
видимый элемент называется интерьером.
Первый интерьер будет заполнять все окно, но позже Вы узнаете
как легко уменьшить его размер и освободить место для других
видимых элементов. Окно может владеть несколькими интерьерами и
любым числом других полезных видимых элементов: строками ввода,
метками, кнопками. Вы также увидите как просто поместить полосу
скроллинга в рамку окна.
Вы можете перекрывать подэлементы в группе - видимые элементы,
с которыми Вы взаимодействуете, являются верхними. TDeskTop имеет
метод Tile, который может перекрывать видимые подэлементы после их
инициализации, но этот метод используется только с панелью экрана.
Вы создаете интерьер простым наследованием от TView. Любой
TView может иметь рамку, которая действует как рамка обычного окна.
Рамка TView, которая не может быть отмечена, находится вне области
отсечения любого вывода для этого видимого элемента. Эта рамка
просто окаймляет окно.
Если интерьер TView заполняет все окно владельца, не имеет
значения имеет ли он рамку - рамка окна накрывает рамку интерьера.
Если интерьер меньше, чем окно, рамка интерьера видима. Несколько
интерьеров внутри окна могут быть окружены рамками, как Вы увидите
в примере.
Следующий код выводит "Hello, World!" в демонстрационном окне,
как показано на рис. 2.3.
{ TVGUID05.PAS }
PInterior = ^TInterior;
TInterior = object(TView)
constructor Init(var Bounds: TRect);
procedure Draw; virtual;
end;
constructor TInterior.Init(var Bounds: TRect);
begin
TView.Init(Bounds);
GrowMode := gfGrowHiX + gfGrowHiY;
end;
procedure TInterior.Draw;
begin
TView.Draw;
WriteStr(4, 2, 'Hello, World!');
end;
constructor TDemoWindow.Init(Bounds: TRect; WinTitle: String;
WindowNo: Integer);
var
S: string[3];
Interior: PInterior;
begin
Str(WindowNo, S); { устанавливает номер окна в заголовке }
TWindow.Init(Bounds, WinTitle + ' ' + S, wnNoNumber);
GetClipRect(Bounds);
Bounds.Grow(-1,-1); { интерьер помещается внутри рамки окна }
Interior := New(PInterior, Init(Bounds));
Insert(Interior); { добавляет интерьер к окну }
end;
Рис. 2.3. TVGUID05 с открытым окном.
+-----------------------------------------------------------------+
| File Window |
|*****************************************************************|
|*****************************************************************|
|*****************************************************************|
|*****************************************************************|
|*****************************************************************|
|+=[ю]Demo Window 1 [ш]=+*****************************************|
|| |*****************************************|
|| |*****************************************|
|| Hello, World! |*****************************************|
|| |*****************************************|
|| |*****************************************|
|+=====================-+*****************************************|
|*****************************************************************|
|*****************************************************************|
|*****************************************************************|
|*****************************************************************|
|*****************************************************************|
| Alt-X Exit F4 New Alt-F3 Close |
+-----------------------------------------------------------------+
Что Вы видите?
Все объекты Turbo Vision рисуют себя сами с помощью метода
Draw. Если Вы создаете порожденный видимый объект с новым
представлением на экране, Вам потребуется перекрыть метод Draw
предка и научить новый объекта представлять себя на экране.
TInterior порожден от TView и он требует нового метода Draw.
Заметим, что новый TInterior.Draw вначале вызывает Draw своего
предка, TView, который в этом случае просто очищает прямоугольник
видимого объекта. Обычно Вам не требуется делать этого: метод Draw
интерьера должен использовать всю свою область, делая вызов
TView.Draw ненужным.
Если Вы действительно хотите поместить что-то в окно
интерьера, Вам не нужно вызывать унаследованный метод Draw вообще.
Вызов TView.Draw будет приводить к миганию, поскольку элементы
интерьера будут рисоваться более одного раза.
В качестве примера Вы можете попробовать перекомпилировать
TVGUID05.PAS с закомментированным вызовом TView.Draw. Затем
передвиньте и измените размер окна. Станет совершенно ясно, почему
видимый элемент должен покрывать всю свою область!
Примечание: Turbo Vision вызывает метод Draw видимого элемента
когда пользователь открывает, закрывает, перемещает или изменяет
размер видимого элемента. Если Вам требуется, чтобы видимый элемент
перерисовал себя сам, вызовите DrawView вместо Draw. DrawView
рисует элемент только если он этого требует. Это важно: Вы
перекрываете Draw, но никогда не вызываете его прямо; Вы вызываете
DrawView, но никогда не перекрываете его!
Лучший способ для Write.
Хотя Вы можете выполнить процедуру Write в Turbo Vision, это
неверный путь. Во-первых, если Вы просто пишете что-нибудь, нет
способа предотвратить случайное затирание окна или другого видимого
элемента. Во-вторых, Вам требуется писать в локальных координатах
текущего видимого элемента и отсекать по его границам. В-третьих,
встает вопрос об использовании цвета при выводе.
Процедура WriteStr Turbo Vision не только знает как писать в
локальных координатах и как отсекать по границам видимого элемента,
но так же как использовать палитру цветов видимого элемента.
Процедура WriteStr берет координаты X и Y, строку для записи и
индекс цвета в качестве параметров.
Аналогично WriteStr, процедура WriteChar определена:
WriteChar(X, Y, Ch, Color, Count);
Подобно WriteStr, WriteChar позиционирует свой вывод в
координаты (X, Y) внутри видимого элемента и пишет Count копий
символа Ch цветом, указываемым элементом Color из палитры видимого
элемента.
Каждый из этих методов должен вызываться только изнутри метода
Draw видимого элемента. Это единственное место, где Вам требуется
писать что-либо внутри Turbo Vision.
Простой просмотр файлов.
В этом разделе мы добавим несколько новых функций в Ваше окно
и поместим в интерьер что-нибудь реальное. Мы добавим методы для
чтения текстового файла с диска и отображения его в интерьере.
Примечание: Эта программа будет выводить некоторые "лишние"
символы. Не беспокойтесь.
{ TVGUID06.PAS }
const
MaxLines = 100; { это произвольное число строк }
var
LineCount: Integer;
Lines: array[0MaxLines - 1] of PString;
type
PInterior = ^TInterior;
TInterior = object(TView)
constructor Init(var Bounds: TRect);
procedure Draw; virtual;
end;
procedure TInterior.Draw; { это выглядит безобразно! }
var
Y: Integer;
begin
for Y := 0 to Size.Y - 1 do { простой счетчик строк }
begin
WriteStr(0, Y, Lines[Y]^, $01); { вывод каждой строки }
end;
end;
procedure ReadFile;
var
F: Text;
S: String;
begin
LineCount := 0;
Assign(F, FileToRead);
Reset(F);
while not Eof(F) and (LineCount < MaxLines) do
begin
Readln(F, S);
Lines[LineCount] := NewStr(S);
Inc(LineCount);
end;
Close(F);
end;
procedure DoneFile;
var
I: Integer;
begin
for I := 0 to LineCount - 1 do
if Lines[I] <> nil then DisposeStr(Lines[i]);
end;
Чтение текстового файла.
Ваша программа вызывает ReadFile для загрузки текстового файла
в массив Lines и DoneFile для освобождения памяти, используемой
Lines, после выполнения.
В ReadFile глобальный тип PString - это указатель на строку.
Turbo Vision так же предоставляет функцию NewStr, которая сохраняет
строку в куче и возвращает указатель на нее. Даже хотя NewStr
возвращает указатель, не используйте Dispose для ее освобождения.
Всегда используйте процедуру DisposeStr для удаления строки из
кучи.
Буферизованный вывод.
Вы заметили, что при выполнении этой программы на экране
появляется "мусор" там, где должны быть пустые строки. Это
результат неполного метода Draw. Он возникает из-за нарушения
принципа, что метод Draw видимого элемента должен покрывать всю
площадь, за которую отвечает этот видимый элемент.
Кроме того, текстовый массив Lines не является соответствующей
формой для отображения в видимом элементе. Текст обычно состоит из
строк переменной длины, многие из которых имеют нулевую длину.
Поскольку метод Draw должен перекрывать всю область интерьера,
текстовые строки должны быть расширены до ширины видимого элемента.
Буфер вывода.
Чтобы избежать этого, создайте новый Draw, который включает
каждую строку в буфер до ее вывода в окне. TDrawBuffer - это
глобальный тип:
TDrawBuffer = array[0MaxViewWidth-1] of Word;
Примечание: MaxViewWidth равен 132 символам.
TDrawBuffer содержит байты с атрибутами и с символами.
Новый TInterior.Draw имеет вид:
{ TVGUID07.PAS }
procedure TInterior.Draw;
var
Color: Byte;
Y: Integer;
B: TDrawBuffer;
begin
Color := GetColor(1);
for Y := 0 to Size.Y - 1 do
begin
MoveChar(B, ' ', Color, Size.X); { заполняет строку пробелами }
if (Y < LineCount) and (Lines[Y] <> nil) then
MoveStr(B, Copy(Lines[Y]^, 1, Size.X), Color);
WriteLine(0, Y, Size.X, 1, B);
end;
end;
Рис. 2.4 показывает TVGUID07 с несколькими открытыми окнами.
Рис. 2.4. Просмотр нескольких файлов.
+-----------------------------------------------------------------+
| File Window |
|*****************************************************************|
|***********+--- Demo Window 3 ----+******************************|
|***********|{*********************|******************************|
|***********|{ |******************************|
|***********|{ Turbo Pascal 6.0 |******************************|
|***********|{ Demo program from |******************************|
|***********|{ |******************************|
|***********+----------------------+******************************|
|+--- Demo Window 1 ---+***+=[ю]Demo Window 5 [ш]=+***************|
||{********************|***|{*********************|***************|
||{ |***|{ |***************|
||{ Turbo Pascal 6.0 |***|{ Turbo Pascal 6.0 |4 ----+********|
||{ Demo program from |***|{ Demo program from |******|2 ----+*|
||{ |***|{ | |******|*|
|+---------------------+***+=====================-+ 6.0 | |*|
|*********************************|{ Demo program from | 6.0 |*|
|*********************************|{ | from |*|
|*********************************+----------------------+ |*|
|*******************************************+-------------------+*|
|*****************************************************************|
|*****************************************************************|
|*****************************************************************|
|*****************************************************************|
| Alt-X Exit F4 New Alt-F3 Close |
+-----------------------------------------------------------------+
Draw вначале использует MoveChar для перемещения Size.X
пробелов (ширина интерьера) соответствующего цвета в TDrawBuffer.
Сейчас каждая строка дополняется пробелами до ширины интерьера.
Затем Draw использует MoveStr для копирования текстовой строки в
TDrawBuffer. Затем отображает весь буфер через вызов WriteLine.
Пересылка текста в буфер.
Turbo Vision предоставляет 4 глобальных процедуры для
пересылки текста в TDrawBuffer: MoveStr, которую Вы только что
видели, и MoveChar, MoveCStr и MoveBuf, которые пересылают символы,
управляющие строки (строки с "~" для элементов меню и статуса) и
другие буфера в буфер, соответственно. Эти процедуры объяснены
детально в главе 14.
Вывод содержимого буфера.
Turbo Vision предоставляет 2 процедуры для вывода содержимого
буфера в видимый элемент. Одна, WriteLine(X, Y, W, H, Buf), была
показана в TVGUID07.
В TInterior.Draw, WriteLine выводит TDrawBuffer на одной
строке. Если 4 параметр, H (высота), больше 1, WriteLine повторяет
буфер на последующих строках. Так, если Buf содержит "Hello,
World!", то WriteLine(0, 0, 13, 4, Buf) выводит
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Другая процедура WriteBuf(X, Y, W, H, Buf) также выводит
прямоугольную область экрана. W и H соответствуют ширине и высоте
буфера. Если Buf содержит "ABCDEFGHIJKLMNOP", то WriteBuf(0, 0, 4,
4, Buf) выводит
ABCD
EFGH
IJKL
MNOP
Вы видите, что по сравнению с небуферизованными WriteStr и
WriteChar здесь не указывается элемент в палитре цветов. Это
связано с тем, что цвет задается когда текст пересылается в буфер -
это означает, что текст в буфере может появляться с разными
атрибутами. WriteLine и WriteBuf детально объяснены в главе 14.
Назад | Содержание | Вперед