Сколько выводить?
Заметим, что TInterior.Draw выводит такую часть файла, чтобы заполнить интерьер. Иначе, Draw будет тратить большую часть времени на вывод частей файла, которые будут отсекаться границами TInterior. Если видимый элемент тратит на свою прорисовку много времени, Вы можете вызвать GetClipRect. GetClipRect возвращает прямоугольник, который доступен внутри владельца так, что Вам требуется рисовать только эту часть видимого элемента. Например, если у Вас есть сложное диалоговое окно с рядом элементов управления и Вы передвигаете его за пределы экрана, вызовите GetClipRect до рисования, чтобы не перерисовывать те части диалогового окна, которые находятся за пределами экрана.Скроллинг вверх и вниз.
Очевидно, что просмотр файла не очень-то полезен, если Вы можете просмотреть только несколько первых строк файла. Поэтому давайте изменим интерьер на видимый элемент со скроллингом и добавим в него полосы скроллинга так, чтобы TInteriоr стал окном, скользящим (скроллингуемым) по текстовому файлу. Вы так же можете изменить TDemoWindow, добавив метод MakeInterior, чтобы отделить эту функцию от механизма открытия окна. { TVGUID08.PAS } type PInterior = ^TInterior; { Заметим, что Вы изменяете предка у TInterior } TInterior = object(TScroller) constructor Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar); procedure Draw; virtual; end; PDemoWindow = ^TDemoWindow; TDemoWindow = object(TWindow) constructor Init(Bounds: TRect; WinTitle: String; WindowNo: Word); procedure MakeInterior(Bounds: TRect); end; constructor TInterior.Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar); begin TScroller.Init(Bounds, AHScrollBar, AVScrollBar); GrowMode := gfGrowHiX + gfGrowHiY; SetLimit(128, LineCount); { горизонтальная и вертикальная границы скроллинга } end; procedure TInterior.Draw; var Color: Byte; I, Y: Integer; B: TDrawBuffer; begin Color := GetColor(1); for Y := 0 to Size.Y - 1 do begin MoveChar(B, ' ', Color, Size.X); i := Delta.Y + Y; { Delta - смещение скроллинга } if (I < LineCount) and (Lines[I] <> nil) then MoveStr(B, Copy(Lines[I]^, Delta.X + 1, Size.X), Color); WriteLine(0, Y, Size.X, 1, B); end; end; procedure TDemoWindow.MakeInterior(Bounds: TRect); var HScrollBar, VScrollBar: PScrollBar; Interior: PInterior; R: TRect; begin VScrollBar := StandardScrollBar(sbVertical + sbHandleKeyboard); HScrollBar := StandardScrollBar(sbHorizontal + sbHandleKeyboard); Interior := New(PInterior, Init(Bounds, HScrollBar, VScrollBar)); Insert(Interior); end; constructor TDemoWindow.Init(Bounds: TRect; WinTitle: String; WindowNo: Word); var S: string[3]; begin Str(WindowNo, S); TWindow.Init(Bounds, WinTitle + ' ' + S, wnNoNumber); GetExtent(Bounds); Bounds.Grow(-1, -1); MakeInterior(Bounds); end; Рис. 2.5. Просмотр файла со скроллингом. +-----------------------------------------------------------------+ | File Window | |+------------- Demo Window 1 --------------+*********************| ||{*****************************************|*********************| ||{ |*********************| ||{ Turbo Pascal 6.0 |*********************| ||{ Demo program from the Turbo Vision Gui|*********************| ||{ |*********************| ||{ Copyright (c) 1990 by Borland Internat|*********************| ||{ |*********************| ||{*****************************************|*********************| || +=[ю]== Demo Window 3 ==[ш]=+*****************| ||program TVGUID08; | AVScrollBar: PScrollBar);ш*****************| || |begin #Window 2 -----+**| ||uses Objects, Driv| TScroller.Init(Bounds, AH# |**| || | GrowMode := gfGrowHiX + g#: Integer; |**| |+------------------| Options := Options or ofF#ray[0MaxLine|**| |*******************| SetLimit(128, LineCount);# |**| |*******************|end; # |**| |*******************| #object(TApplic|**| |*******************|procedure TInterior.Draw; юre HandleEvent|**| |*******************|var #re InitMenuBar|**| |*******************| Color: Byte; щre InitStatusL|**| |*******************+=<ю######################>-+--------------+**| |*****************************************************************| | Alt-X Exit F4 New Alt-F3 Close | +-----------------------------------------------------------------+ Вертикальная и горизонтальная полосы скроллинга инициализируются и вставляются в группу, а затем передаются в TScroller в его инициализации. "Скроллер" - это видимый элемент, спроектированный для отображения части большого виртуального видимого элемента. Скроллер и его полосы скроллинга объединяются для создания скользящего видимого элемента с незначительными усилиями от Вас. Все, что Вам нужно сделать - это создать метод Draw для скроллера так, чтобы он отображал соответствующую часть виртуального видимого элемента. Полосы скроллинга автоматически управляют значениями Delta.X (колонка, с которой начинается вывод) и Delta.Y (строка, с которой начинается вывод) скроллера. Вы должны перекрыть метод Draw в TScroller. Значения Delta изменяются в соответствии с полосами скроллинга. Метод Draw вызывается каждый раз, когда изменяется Delta.Несколько видимых элементов в окне.
Давайте продублируем интерьер и создадим окно с двумя скользящими видимыми элементами для текстового файла. Мышка или клавиша Tab автоматически выбирает один из двух интерьеров. Каждый видимый элемент скользит независимо и имеет собственную позицию курсора. Чтобы сделать это, расширьте метод MakeInterior так, чтобы он знал, какая часть окна является активным интерьером (поскольку различные части ведут себя несколько по-разному) и сделайте 2 вызова MakeInterior в TDemoWindow.Init. { TVGUID09.PAS } { Не забудьте изменить объявление MakeInterior } procedure TDemoWindow.MakeInterior(Bounds: TRect; Left: Boolean); var Interior: PInterior; R: TRect; begin Interior := New(PInterior, Init(Bounds, StandartScrollBar(sbHorizontal), StandartScrollBar(sbVertical))); if Left then Interior^.GrowMode := gfGrowHiY else Interior^.GrowMode := gfGrowHiX + gfGrowHiY; Insert(Interior); end; constructor TDemoWindow.Init(Bounds: TRect; WinTitle: String; WindowNo: Word); var S: string[3]; R: TRect; begin Str(WindowNo, S); TWindow.Init(Bounds, WinTitle + ' ' + S, wnNoNumber); GetExtent(Bounds); R.Assign(Bounds.A.X, Bounds.A.Y, Bounds.B.X div 2 + 1, Bounds.B.Y); MakeInterior(R, True); R.Assign(Bounds.B.X div 2, Bounds.A.Y, Bounds.B.X, Bounds.B.Y); MakeInterior(R,False); end; Рис. 2.6. Окно с несколькими панелями. +-----------------------------------------------------------+ |***********************************************************| |*+=[ю]================ Demo Window 1 ================[ш]=+*| |*| | while not Eof(F) and (LineCountш*| |*|var | begin #*| |*| LineCount: Integer;| Readln(F, S); #*| |*| Lines: array[0Max| Lines[LineCount] := NewStr(S)#*| |*| | Inc(LineCount); #*| |*|type | end; #*| |*| TMyApp = object(TAp| Close(F); #*| |*| procedure HandleE|end; #*| |*| procedure InitMen| ю*| |*| procedure InitSta|procedure DoneFile; #*| |*| procedure NewWind|var #*| |*| end; | I: Integer; #*| |*| |begin щ*| |*+=====================+=<ю############################>-+*| |***********************************************************| +-----------------------------------------------------------+ Заметим, что Вы изменили форму и содержимое MakeInterior. Вместо объявления двух статических полос скроллинга и передачи их в метод Init, Вы просто включили вызовы StandardScrollBar в качестве параметров Init. Предыдущая форма несколько яснее, но эта более эффективно. Если Вы уменьшите окна в TVGUID09.PAS, Вы заметите, что вертикальная полоса скроллинга будет перекрыта левым интерьером видимого элемента, если Вы передвините правую сторону окна слишком близко к левой. Чтобы предотвратить это, Вы можете установить нижнюю допустимую границу на уменьшение окна. Вы делаете это, перекрывая метод SizeLimits в TWindow. { TVGUID10.PAS } { Не забудьте добавить SizeLimits в TDemoWindow. Это виртуальный метод} procedure TDemoWindow.SizeLimits(var Min, Max: TPoint); var R: TRect; begin TWindow.SizeLimits(Min, Max); GetExtent(R); Min.X := R.B.X div 2; end; Заметим, что Вы не вызываете SizeLimits, Вы просто перекрываете его и он будет вызываться в соответствующее время. Здесь делается то же, что Вы делали с методом Draw: Вы говорите видимому элементу как его рисовать, но не когда. Turbo Vision уже знает когда вызвать Draw. Это же применимо и к SizeLimits: Вы устанавливаете границы, а видимый элемент знает тот момент, когда необходимо проверить их.Куда поместить функциональность.
Сейчас Вы создали окно с рядом видимых элементов: рамкой и двумя скользящими интерьерами, каждый с двумя полосами скроллинга. Вы находитесь на пути создания окна, которое выполняет определенные функции в Вашей программе. Как Вам действовать? Допустим, Вы хотите настроить Ваше окно на текстовый редактор. Поскольку окно имеет 2 видимых элемента, Вам может понадобиться поместить функции редактора в группу, а затем установить взаимодействие группы с двумя видимыми элементами. Работа группы заключается в управлении видимыми элементами. Это не очень-то естественно, не так ли? Хотя Вы можете расширять группу любым видимым элементом и Вы можете вкладывать в нее любую нужную Вам функцию, Ваша программа на Turbo Vision будет более надежна и более гибка, если Вы будете следовать двум правилам: сохраняйте объекты как можно более автономными и сохраняйте группы (такие как окна) как можно более свободными от функциональной нагрузки. Так, когда Вы создаете текстовый редактор, помещая все функции в интерьер видимого элемента: создайте видимый элемент типа текстовый редактор. Видимые элементы могут заново использоваться, если Вы их правильно спроектировали и наоборот, перенос текстового редактора в другую среду будет затруднен, если его функции были распределены между группой и видимым элементом.Создание диалоговых окон.
Используемые объекты: TView, TGroup, TDialog, TCluster, TCheckBoxes, TRadioButtons, TLabel, TInputLine. Диалоговое окно - это просто специальный вид окна. В действительности TDialog наследуется от TWindow и хотя Вы можете интерпретировать его как просто другое окно, обычно Вы будете делать некоторые вещи по-другому. В Вашей демонстрационной программе Вы добавите новый элемент меню, который генерирует команду, открывающую диалоговое окно, добавите метод в Вашу программу, который знает как это делать и добавите строку в метод HandleEvent Вашей программы, чтобы связать команду с действием. Заметим, что Вам не требуется порождать новый тип объекта от TDialog, как Вы делали с TWindow (чтобы создать TDemoWindow). Вместо создания специального типа диалогового окна Вы добавляете "разумность" в программу: вместо создания объекта типа "диалоговое окно", который знает что Вы хотите делать, Вы создаете общее диалоговое окно и говорите ему что он должен сделать. Вам редко потребуется создавать порожденный тип от TDialog, поскольку отличие между диалоговыми окнами только в их содержимом. { TVGUID11.PAS } const cmNewDialog = 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, NewItem('~N~ew', 'F4', kbF4, cmNewWin, hcNoContext, NewLine( NewItem('E~x~it', 'Alt-X', kbAltX, cmQuit, hcNoContext, nil))))), NewSubMenu('~W~indow', hcNoContext, NewMenu( NewItem('~N~ext', 'F6', kbF6, cmNext, hcNoContext, NewItem('~Z~oom', 'F5', kbF5, cmZoom, hcNoContext, NewItem('~D~ialog', 'F2', kbF2, cmNewDialog, hcNoContext, nil)))), nil)) ))); end; procedure TMyApp.NewDialog; var Dialog: PDialog; R: TRect; begin R.Assign(0, 0, 40, 13); R.Move(Random(39), Random(10)); Dialog := New(PDialog, Init(R, 'Demo Dialog')); DeskTop^.Insert(Dialog); end; procedure TMyApp.HandleEvent(var Event: TEvent); begin TApplication.HandleEvent(Event); if Event.What = evCommand then begin case Event.Command of cmNewWin: NewWindow; cmNewDialog: NewDialog; else Exit; end; ClearEvent(Event); end; end; Рис. 2.7. Простое диалоговое окно. += [ю]=== Demo Dialog Box =====+ | | | | | | | | | | | | | | +==============================+ Существует несколько различий между диалоговым окном и предыдущими окнами: - Цвет по умолчанию диалогового окна серый вместо синего. - Диалоговое окно не может изменять размер. - Диалоговое окно не имеет номера. Заметим, что Вы можете закрыть диалоговое окно, отметив его закрывающую кнопку или отметив элемент Alt-F3 в строке статуса или нажав клавишу ESC. По умолчанию клавиша ESC удаляет диалоговое окно. Это пример, так называемого, диалогового "немодального" окна (без режимов). Диалоговые окна обычно модальные, которые определяют режим действия. Обычно, когда Вы открываете диалоговое окно, активным является только это окно: это модальный видимый элемент. Отметка других окон или меню не будет вызывать никаких действий до тех пор, пока Вы находитесь в режиме диалогового окна. Иногда Вам может понадобиться немодальное диалоговое окно, но в большинстве случаев Вы будете работать с модальными диалоговыми окнами (модальные видимые элементы обсуждены в главе 4).Выполнение модального диалогового окна.
Как сделать диалоговое окно модальным? Это очень просто. Вместо вставки объекта "диалоговое окно" в панель экрана Вы выполняете его, вызывая функцию DeskTop^.ExecView: { TVGUID12.PAS } procedure TMyApp.NewDialog; var Dialog: PDialog; R: TRect; Control: Word; begin R.Assign(0, 0, 40, 13); R.Move(Random(39), Random(10)); Dialog := New(PDemoDialog, Init(R, 'Demo Dialog')); Control := DeskTop^.ExecView(Dialog); end; TDialog уже знает как реагировать на событие по клавише ESC (которое преобразуется в команду cmCancel) и событие от клавиши Enter (которое обрабатывается кнопкой диалогового окна по умолчанию TButton). В ответ на команду cmCancel диалоговое окно всегда закрывается. Вызов ExecView вставляет диалоговое окно в группу и делает диалоговое окно модальным. Выполнение происходит в ExecView до тех пор, пока диалоговое окно не закроется или не будет удалено. После этого ExecView удаляет диалоговое окно из группы и осуществляет выход. В данный момент Вы игнорируете значение, возвращаемое функцией ExecView и сохраненное в Control. Вы будете использовать это значение в TVGUID16.Управление.
Конечно, пустое диалоговое окно - это не диалоговое окно! Чтобы оно имело смысл давайте добавим элементы управления. Элементы управления - это изменяющиеся элементы внутри диалогового окна, которые позволяют Вам манипулировать информацией. Важно запомнить, что элементы управления действуют только внутри диалогового окна. Существует только одно исключение из этого правила в случае кнопки в немодальном диалоговом окне. Поскольку кнопки генерируют команды, эти команды будут распространяться от текущего модального видимого элемента. Если диалоговое окно - это немодальный видимый элемент, то эти команды будут распространяться за пределы диалогового окна, что может иметь неожиданный эффект (обработка команд объяснена в главе 5). Вообщем, когда элементы управления установлены в диалоговом окне, Вы можете отделить видимое представление от обработки данных. Это означает, что Вы можете легко спроектировать все диалоговое окно без создания кода, который устанавливает или использует данные из этого диалогового окна, так же как Вы устанавливали элементы меню и статуса без кода, реагирующего на сгенерированные команды.Кнопки.
Один из простейших объектов управления - TButton. Он работает во многом аналогично элементу строки статуса: это закрашенная область с текстовой меткой и, если Вы отметите ее, она генерирует команду. Существует так же тень от кнопки, так, что если Вы отметите кнопку, она создает эффект движения. Большинство диалоговых окон имеет, по крайней мере, одну или две кнопки. Наиболее общие кнопки "OK" (означающая "Я все сделал. Вы можете закрыть диалоговое окно и использовать результаты.") и "Cancel" (означающая "Я хочу закрыть диалоговое окно и игнорировать изменения, сделанные в нем"). Кнопка Cancel обычно генерирует ту же команду cmCancel, что и закрывающая кнопка. Модуль Dialogs определяет 5 стандартных диалоговых команд, которые могут быть связаны с TButton: cmOK, cmCancel, cmYes, cmNo, cmDefault. Первые 4 команды так же закрывают диалоговое окно, вызывая метод EndModel из TDialog, который восстанавливает предыдущий модальный видимый элемент в статус модальности. Вы можете так же использовать кнопки для генерации команд, специфичных для Вашей программы. { TVGUID13.PAS } procedure TMyApp.NewDialog; var Dialog: PDialog; R: TRect; Control: Word; begin R.Assign(20, 6, 60, 19); Dialog := New(PDialog, Init(R, 'Demo Dialog')); with Dialog^ do begin R.Assign(15, 10, 25, 12); Insert(New(PButton, Init(R, '~O~K', cmOK, bfDefault))); R.Assign(28, 10, 38, 12); Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal))); end; Control := DeskTop^.ExecView(Dialog); end; Создание кнопки требует 4 параметров в констракторе Init: 1. Область, закрываемая кнопкой (не забудьте оставить место для тени!). 2. Текст, который появляется в кнопке. 3. Команда, связанная с кнопкой. 4. Флаг типа кнопки. (Нормальная или по умолчанию) Рис. 2.8. Диалоговое окно с кнопками. +=[ю]======= Demo Dialog Box =========+ | | | | | | | | | | | | | | | OK m Cancel m | | ^^^^^^^^ ^^^^^^^^ | +=====================================+ Заметим, что Вы не подсвечиваете "С" в "Cancel", поскольку уже определена горячая клавиша (Esc) для отмены диалогового окна. Это освобождает "С" для сокращенного ввода других элементов управления.Нормальные кнопки и по умолчанию.
Когда Вы создаете кнопку, Вы устанавливаете ее флаг bfNormal или bfDefault. Большинство кнопок bfNormal. Кнопка, помеченная как bfDefault, будет кнопкой по умолчанию, т.е. она "нажимается", когда Вы нажимаете клавишу Enter. Turbo Vision не проверяет, используете ли Вы только одну кнопку по умолчанию - за это отвечаете Вы. Если Вы назначите более, чем одну кнопку по умолчанию, результат будет непредсказуемым. Обычно кнопка "OK" в диалоговом окне - это кнопка по умолчанию и пользователь просто нажимает Enter, чтобы закрыть диалоговое окно и использовать сделанные изменения.Активные элементы управления.
Заметим, что когда диалоговое окно открыто, один из элементов управления всегда подсвечен - это активный элемент управления. Активация элемента управления наиболее полезна для направления ввода с клавиатуры. Например, если кнопка активна, пользователь может "нажать" кнопку, нажав пробел. Символы могут быть введены в строку ввода только, если она активна. Пользователь может нажать клавишу Tab для того, чтобы сделать активным другой элемент управления внутри диалогового окна. Метки не могут быть активными, поэтому клавиша Tab проходит мимо них. (Метки обсуждаются позже в этой главе). Вам необходимо, чтобы пользователь мог активировать элементы в диалоговом окне в определенном порядке. Клавиша Tab активирует элементы в том порядке, в каком объекты вставлялись в диалоговое окно. Внутренне, объекты поддерживаются в диалоговом окне в циклически связанном списке с последним вставленным объектом, связываемым с первым объектом. По умолчанию активируется последний вставленный объект. Вы можете активировать другой элемент управления либо используя метод SelectNext диалогового окна, либо прямо вызывая метод Select элемента управления. SelectNext позволяет Вам передвигаться вперед или назад по списку элементов управления. SelectNext(False) передвигает Вас вперед по циклическому списку (в порядке Tab); SelectNext(True) передвигает в обратном направлении.Выбор.
Обычно выбор, который Вы хотите предложить пользователю в диалоговом окне, это не просто выбор, обрабатываемый индивидуальными кнопками. Turbo Vision обеспечивает несколько полезных стандартных функций управления для выбора ряда опций. Две наиболее полезных - это зависимые и независимые кнопки. Эти 2 функции в основном идентичны с одним исключением, что Вы можете задать несколько независимых, но только одну зависимую кнопку. Причина того, что эти кнопки появляются и ведут себя похоже в том, что они порождаются от одного объекта TCluster. Если Вы не знакомы с концепцией зависимых и независимых кнопок, Вы можете посмотреть меню Options в интегрированной среде Turbo Pascal. Многие диалоговые окна в этом меню используют такие кнопки.Создание кластера.
Не существует причин для того, чтобы создавать экземпляр от TCluster. Поскольку процесс создания кластера независимых кнопок аналогичен созданию кластера зависимых кнопок, Вам требуется детально просмотреть это процесс только однажды. Добавим следующий код в метод TMyApp.NewDialog после создания диалогового окна, но до добавления кнопок. Вставим кнопки в последнюю очередь в том порядке, в котором они должны обходиться с помощью Tab. +----------------+ | [ ] HVarti | | [ ] Tilset | | [ ] Jarlsberg | +----------------+ var B: PView; R.Assign(3, 3, 18, 6); B := New(PChecBoxes, Init(R, NewSItem('~H~varti', NewSItem('~T~ilset', NewSItem('~J~arsberg', nil))) )); Insert(B); Инициализация очень проста. Вы устанавливаете прямоугольник, в котором находятся элементы (не забудьте оставить место для самих независимых кнопок), а затем создаете связанный список указателей на строки, завершаемый nil, которые будут показаны в следующих независимых кнопках.Значения независимых кнопок.
Предыдущий код создает набор независимых кнопок с тремя выборами. Вы можете заметить, что Вы не дали указание по установке каждого элемента в списке. По умолчанию они все не установлены. Часто Вам потребуется установить все или некоторые элементы независимых кнопок. Вместо назначения значений, когда Вы создаете список, Turbo Vision предоставляет способ легко устанавливать и сохранять значения. Набор независимых кнопок может содержать до 16 элементов. Поскольку Вы имеете 16 элементов, которые могут быть включены или выключены, Вы можете представить эту информацию как 16-битное слово, каждый бит которого соответствует одному элементу. После того, как Вы закончите создание всего диалогового окна, Вы увидите как устанавливать и читать значения элементов управления. Сейчас сосредоточимся на помещении управляющих элементов в соответствующее место.Одна из многих.
Давайте добавим набор зависимых кнопок в диалоговое окно, чтобы Вы могли сравнить их с независимыми кнопками. Следующий код устанавливает набор из 3 зависимых кнопок: +--------------+ | [*] Solid | | [ ] Runny | | [ ] Melted | +--------------+ R.Assign( , , , ); B := New(PRadioButtons, Init(R, NewSItem('~S~olid', NewSItem('~R~unny', NewSItem('~M~elted', nil))) )); Insert(B); Главное отличие между независимыми и зависимыми кнопками в том, что Вы можете выбрать только одну зависимую кнопку в группе и что первый элемент в списке зависимых кнопок выбран по умолчанию. Поскольку Вам не требуется знать состояние каждой зависимой кнопки (только одна может быть выбрана, так, что Вам требуется знать только какая именно), данные о зависимых кнопках не побитовые. Это означает, что Вы можете использовать более 16 зависимых кнопок, но поскольку данные хранятся в слове, Вы ограничены 65,536 зависимыми кнопками на один кластер. Значение 0 указывает, что выбрана первая зависимая кнопка, 1 - вторая и т.д.Метки управляющих элементов.
Конечно, установки управляющих элементов может быть недостаточно. Простое предоставление набора выборов может ничего не говорить пользователю о том, что он выбирает! Turbo Vision предоставляет удобный метод для установки меток управляющих элементов в виде другого управляющего элемента TLabel. TLabel делает больше, чем кажется на первый взгляд. TLabel не только отображает текст, но и связывается с другим видимым элементом. Отметка мышкой метки приводит к активизации связанного видимого элемента. Вы так же можете определить букву сокращенного набора для метки, окружив букву "~". Чтобы пометить независимые кнопки, добавьте следующий код сразу после вставки независимых кнопок в диалоговое окно: R.Assign(2, 2, 10, 3); Insert(New(PLabel, Init(R, 'Cheeses', B))); Вы можете сейчас активировать набор независимых кнопок, отметив слово "Cheeses". Это так же предоставляет информацию об элементах в этом окне. Аналогично Вы можете добавить метку к зависимым кнопкам с помощью кода: { TVGUID14.PAS } R.Assign(21, 2, 33, 3); Insert(New(PLabel, Init(R, 'Consistency', B))); Рис. 2.9. Диалоговое окно с метками кластеров. +=[ю]======= Demo Dialog Box =============+ | | | Cheeses Consistency | | +----------------+ +--------------+ | | | [ ] HVarti | | [*] Solid | | | | [ ] Tilset | | [ ] Runny | | | | [ ] Jarlsberg | | [ ] Melted | | | +----------------+ +--------------+ | | | | | | | | | | OK m Cancel m | | ^^^^^^^^ ^^^^^^^^ | +==========================================+Строка ввода.
Существует еще один тип элемента управления, который Вы можете добавить в диалоговое окно: элемент для редактирования входной строки, называемый строкой ввода. В действительности работа строки ввода черезвычайно сложна, но с Вашей точки зрения как программиста, TInputLine - очень простой для использования объект. Добавим следующий код после кода, назначающего метку зависимым кнопкам и до выполнения диалогового окна: { TVGUID15.PAS } R.Assign(3, 8, 37, 9); B := New(PInputLine, Init(R, 128)); Insert(B); R.Assign(2, 7, 24, 8); Insert(New(PLabel, Init(R, 'Delivery instructions', B))); Установка строки ввода проста: Вы назначаете прямоугольник, который определяет длину строки ввода на экране. Необходим еще один параметр для определения максимальной длины редактируемой строки. Эта длина может превышать отображаемую длину, поскольку объект TInputLine знает как выполнять скроллинг строки. По умолчанию строка ввода может обрабатывать клавиши, команды редактирования, выбор и движение с помощью мышки. Рис. 2.10. Диалоговое окно со строкой ввода. +=[ю]======= Demo Dialog Box =============+ | | | Cheeses Consistency | | +----------------+ +--------------+ | | | [ ] HVarti | | [*] Solid | | | | [ ] Tilset | | [ ] Runny | | | | [ ] Jarlsberg | | [ ] Melted | | | +----------------+ +--------------+ | | | | Delivery instructions | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | | OK m Cancel m | | ^^^^^^^^ ^^^^^^^^ | +==========================================+ Строка ввода так же имеет метку для ясности, поскольку непомеченная строка ввода может быть еще более непонятной для пользователя, чем непомеченный кластер.Установка и получение данных.
Сейчас, когда Вы сконструировали достаточно сложное диалоговое окно, Вам нужно узнать как использовать его. Вы создали интерфейс пользователя, теперь Вам необходимо создать интерфейс с программой. Элементы управления бесполезны, если Вы не знаете как получить информацию от них! Вы должны иметь возможность сделать 2 вещи: установить начальные значения элементов управления при открытии диалогового окна и прочитать значения, когда диалоговое окно закрывается. Заметим, что Вы не будете модифицировать данные вне диалогового окна, если оно закрыто не успешно. Если пользователь решил отменить диалоговое окно, Вы будете игнорировать все изменения, сделанные в то время, когда диалоговое окно было открыто. К счатью возможности Turbo Vision позволяют сделать это. Ваша программа управляет записью информации в диалоговое окно при его открытии. Когда пользователь заканчивает работу с диалоговым окном, Вашей программе требуется проверить, отменено диалоговое окно или было закрыто нормально. Если оно было отменено, Вы просто работаете без модификации записи. Если диалоговое окно успешно закрыто, Вы можете прочитать запись из диалогового окна в той же форме, в которой она была передана в него. Методы SetData и GetData используются для копирования данных в и из видимого элемента. Каждый видимый элемент имеет методы SetData и GetData. Когда группа (такая как TDialog) инициализируется с помощью вызова SetData, она передает данные дальше, вызывая методы SetData для каждого из его подэлементов. Примечание: Когда Вы вызываете SetData для группы, Вы передаете ему запись данных, которая содержит данные для каждого видимого элемента в группе. Вам необходимо расположить данные для каждого из видимых элементов в том же порядке, в каком они были вставлены в группу. Вам так же требуется установить правильный размер данных для каждого из видимых элементов. Каждый видимый элемент имеет метод DataSize, который возвращает размер данных видимого элемента. Каждый видимый элемент копирует DataSize данных из этой записи данных, а затем передвигает указатель, чтобы показать следующему видимому элементу, с какого места следует начинать. Если данные видимого подэлемента имеют неверный размер, каждый последующий подэлемент будет копировать неверные данные. Если Вы создаете новый видимый элемент, и добавляете в него поля данных, не забудьте перекрыть DataSize, SetData и GetData, так чтобы они обрабатывали правильные значения. Порядок обработки и размеры данных всецело в Ваших руках. Компилятор не будет возвращать сообщения, если Вы сделаете ошибку. После выполнения диалогового окна, Ваша программа должна вначале убедиться, что диалоговое окно не было отменено, затем вызвать GetData для передачи информации в Вашу программу. Так, в Вашем примере Вы инициализируете по порядку кластер независимых кнопок, метку, кластер зависимых кнопок, метку, строку ввода до 128 символов, метку и две кнопки (OK и Cancel). Таблица 2.1 приводит все требуемые для этого данные. Таблица 2.1. Данные для элементов управления диалогового окна. ------------------------------------------------------------ Элементы управления Требуемые данные ------------------------------------------------------------ Независимые кнопки Word Метка Нет Зависимые кнопки Word Метка Нет Строка ввода string[128] Метка Нет Кнопка Нет Кнопка Нет ------------------------------------------------------------ Видимые элементы, которые не имеют данных (такие как метки и кнопки), используют метод GetData, который они наследуют от TView и который ничего не делает. Это означает, что когда Вы получаете и устанавливаете данные, Вы можете пропустить метки и кнопки. Таким образом, Вы должны отслеживать только 3 видимых элемента в диалоговом окне: зависимые кнопки, независимые кнопки и строку ввода. Как сказано ранее, каждый элемент хранит свои данные в поле типа Word. Данные входной строки хранятся в строке. Вы можете установить запись данных для диалогового окна в глобальном типе: DialogData = record CheckBoxData: Word; RadioButtonData: Word; InputLineData: string[128]; end; Теперь все, что Вам необходимо сделать - это инициализировать запись при запуске программы (например в MyApp.Init) установить данные, когда Вы входите в диалоговое окно и прочитать их, когда диалоговое окно успешно закрыто. Это проще сказать в Паскале, чем по-русски! После того, как Вы объявите тип, Вы объявляете глобальную переменную: var DemoDialogData: DialogData; затем добавляете одну строку до выполнения диалогового окна и одну после: Dialog^.SetData(DemoDialogData); Control := DeskTop^.ExecView(Dialog); if Control <> cmCancel then Dialog^.GetData(DemoDialogData); и добавляете 6 строк в метод TMyApp.Init, чтобы установить начальные значения для диалогового окна. { TVGUID16.PAS } with DemoDialogData do begin CheckboxData := 1; RadioButtonData := 2; InputLineData := 'Phone home.'; end; Рис. 2.11. Диалоговое окно с установленными начальными значениями. +=[ю]======= Demo Dialog Box =============+ | | | Cheeses Consistency | | +----------------+ +--------------+ | | | [ ] HVarti | | [*] Solid | | | | [ ] Tilset | | [ ] Runny | | | | [ ] Jarlsberg | | [ ] Melted | | | +----------------+ +--------------+ | | | | Delivery instructions | | Phone home. | | | | OK m Cancel m | | ^^^^^^^^ ^^^^^^^^ | +==========================================+ Сейчас все изменения, которые Вы сделали в диалоговом окне, должны сохраняться, когда Вы заново открываете его, если только Вы не отменили диалог. Одна из вещей, которую мы поняли, когда писали интегрированную среду Turbo Pascal, - хорошо, чтобы Ваша программа сохраняла информацию, которая была изменена диалоговым окном, в форме записи, которая может быть использована для установки или получения данных из диалогового окна. При этом Вы избежите создания большого числа записей данных из отдельных переменных каждый раз, когда хотите открыть диалоговое окно и распределения информации, возвращаемой из диалогового окна в различные переменные.Краткие клавиши и конфликты.
По умолчанию метки, зависимые и независимые кнопки, могут реагировать на короткие клавиши даже когда активен какой-либо другой элемент диалогового окна. Например, когда Ваше диалоговое окно открывается, активны независимые кнопки и курсор находится на первой независимой кнопке. Нажав M для "Melted" Вы немедленно делаете активной зависимую кнопку Melted и включаете ее. Желательно делать короткие клавиши как можно более мнемоническими, хотя доступны только 26 букв и 10 цифр. Это может привести к конфликтам. Например, в Вашем маленьком диалоговом окне имеет смысл установить С как короткий набор для "Cheeses", "Consistenсy" и может существовать "Cheddаr". Во-первых, хотя удобно устанавливать первую букву слова для короткого набора, это не всегда возможно. Вы можете разрешить этот конфликт между "Cheeses" и "Consistency" например сделав О как короткий набор для "Consistency", но это трудно запомнить. Другой способ - изменить метку. Вместо метки "Cheeses" Вы можете пометить это кластер "Kind of Cheese" с К для короткого набора. Это только один из способов избежания конфликтов между короткими клавишами на одном уровне. Однако существует другой подход, который Вы можете использовать для избежания конфликтов, скажем, между меткой и элементом кластера. Короткие клавиши можно сделать локальными внутри элемента диалогового окна. Например, в предыдущем примере, если Вы локализуете короткие клавиши внутри каждого кластера, нажатие М во время активизации независимых кнопок не будет активизировать кнопки "Consistency" или кнопку "Melted". М будет функционировать как короткая клавиша только если Вы с помощью мышки или клавиши Tab перейдете в кластер "Consistency". По умолчанию все короткие клавиши активны во всем диалоговом окне. Если Вы хотите локализовать короткие клавиши, измените поле Options объекта при вставке в диалоговое окно. (Поле Options и бит ofPostProcess объяснены в главе 4.) Например, если Вы хотите сделать короткие клавиши для независимых кнопок локальными, Вы можете добавить дополнительную строку до вставки в диалоговое окно: R.Assign(3, 3, 18, 6); B := New(PCheckBoxes, Init(R, NewSItem('~H~varti', NewSItem('~T~ilset', NewSItem('~J~arlsberg', nil))) )); B^.Options := B^.Options and not ofPostProcess; Insert(B); Сейчас короткие клавиши H, T и J действуют только если Вы перейдете в кластер "Cheeses". Однако Alt-H, Alt-T и Alt-J продолжают функционировать как и ранее. Запомните, что метка никогда не активируется. Однако метка должна иметь установленный бит ofPostProcess для того, чтобы работать по короткой клавише (см. главу 5). Установка ofPostProcess означает, что пользователь может быстро вводить информацию в диалоговое окно. Однако пользователь может нажать короткую клавишу, ожидая перейти в одно место, но из-за конфликта перейдет в другое. Аналогично, если пользователь ожидает, что короткие клавиши активны, а они активны только локально, это может привести к тому, что короткая клавиша ничего не делает, когда она нажата вне пределов активной области. Лучший совет, который мы можем дать Вам - активно тестируйте диалоговые окна на наличие конфликтов. Избегайте дублированных коротких клавиш где это возможно и всегда обеспечьте для пользователя ясность, какие опции доступны.Выход из дилогового окна.
Когда Вы закончили работать с диалоговым окном, Вы вызываете Dispose(D, Done). Вызов Done так же удаляет диалоговое окно с панели экрана. Обычно Вы не вызываете сам Done, поскольку это делается автоматически, когда диалоговое окно закрывается.Другие элементы управления.
Модуль Dialogs предоставляет дополнительные возможности, которые не использовались в этом примере. Однако они используются аналогичным способом: Вы создаете новый экземпляр, вставляете его в диалоговое окно и включаете соответствующие данные в запись данных. Этот раздел кратко описывает функции и их использование. Более детальная информация приведена в главе 13.Статический текст.
TStaticText - это видимый элемент, который просто отображает строку, переданную в него. Строка - это слово, располагаемое внутри прямоугольника видимого элемента с переносом. Текст будет центрироваться, если строка начинается с Ctrl-C и строка может быть разбита с помощью Ctrl-M. По умолчанию текст не может активизироваться и объект не получает данных из записи данных.Просмотр списка.
TListViewer выводит список в одну или несколько колонок и пользователь может выбрать элемент из этого списка. ListViewer может взаимодействовать с двумя полосами скроллинга. TListViewer предназначен для построения блока и не используется отдельно. Он может обрабатывать список, но сам не содержит списка. Его абстрактный метод GetText загружает элементы списка для его метода Draw. Наследник TListViewer должен перекрывать GetText для загрузки актуальных данных.Окно списка.
TListBox наследуется от TListViewer. Он владеет TCollection, который должен быть указателями на строки. TListBox поддерживает только одну полосу скроллинга. Пример окна списка - список выбора файлов в интегрированной среде Turbo Pascal или список файлов, используемый TFileDialog в STDDLG.PAS. При получении или установке данных окна списка удобно использовать тип записи TListBoxRec, который хранит указатель на список строк и слово, указывающее на текущий выбранный элемент списка.История.
THistory реализует объект, который работает со строкой ввода и связанным окном списка. Нажимая на символ стрелки, стоящий после строки ввода, пользователь вызывает список предыдущих значений для этой строки ввода и может выбрать любой из них. Это предотвращает от повторного набора. Объекты THistory используются во многих местах интегрированной среды Turbo Pascal. Например, в диалоговом окне File/Open или Search/Find.Стандартные диалоговые окна.
Модуль StdDlg содержит предопределенное диалоговое окно, называемое TFileDialog. Вы используете это диалоговое окно в интегрированной среде, когда открываете файл. TFileDialog использует ряд других объектов, которые могут быть полезны, так же из модуля StdDlg: TFileInputLine = object(TInputLine) TFileCollection = object(TSortedCollection) TSortedListBox = object(TListBox) TFileList = object(TSortedListBox) TFileInfoPane = object(TView) Поскольку исходный код для модуля Dialogs доступен, мы не описываем эти объекты детально.
Назад | Содержание | Вперед