Глава 10. Объекты окон


    Объекты окон (или оконные объекты) - это интерфейсные объекты, предназначенные для облегчения работы с окнами. В данной главе поясняется, как создавать и заполнять окна приложения. Это предусматривает следующие задачи:

Что такое объекты окон?


    Термин "объект окна" относится к любому интерфейсному объекту, представляющему окно, а в Windows это почти все, что выводится на экран. В качестве шаблона определения большей части фундаментального поведения основного окна и любого всплывающего окна приложения ObjectWindows использует тип TWindow.

Окна, которые не являются окнами


    TWindow имеет три типа-потомка: TMDIWindow, TControl и TEditWindow, так что все они также являются оконными объектами, хотя на самом деле это не окна в полном смысле слова. Типы MDI используются в приложениях ObjectWindows, которые соответствуют стандарту многодокументального интерфейса Windows. Об MDI и этих типах рассказывается в Главе 14. TControl определяет управляющие элементы, такие как командные кнопки и блоки списков (см. Главу 12). Чаще всего новые оконные типы являются производными от TWindow.

    Эта глава охватывает типы TWindow и TEditWindow и содержит примеры регистрации новых классов окон.

Где найти объекты окон


    Каждое приложение Windows имеет основное окно. Это окно может выводиться в виде пиктограммы или не выводиться снова (скрытое окно), но существует всегда. Приложения ObjectWindows не являются исключением: они должны иметь основное окно, представленное оконным объектом.

    Примером минимальной программы ObjectWindows ("скелета" программы) является TestApp в Главе 8. Основное окно программы ObjectWindows является обычно экземпляром TWindow или определяемого в программе наследующего типа. Многие приложения имеют другие окна, которые обычно являются дочерними окнами основного окна. Эти дополнительные окна также являются экземплярами TWindow или одного из его потомков.

    Например, графическая программа может определять для своего основного окна тип TPaintWindow, а для окна, показывающего графический рисунок - тип TZoomWindow. В этом случае TPaintWindow и TZoomWindow являются наследниками TWindow.

Инициализация объектов окон


    Оконные объекты представляют элементы окна, связанные через описатели, сохраненные в наследуемом из TWindowsObject поле HWindow. Так как объект окна имеет две части, его создание требует двух шагов: инициализации объекта и создания визуального элемента.

    Инициализация окна - это процесс создания оконного объекта ObjectWindows путем вызова конструктора Init:

     Window1 := New(PWindow,Init(nil, 'Заголовок окна 1'));
     Window2 := New(PNewWindowType,Init(nil,'Заголовок окна 2'));

    Init создает новый оконный объект и устанавливает поле Title в Attr в передаваемый аргумент PChar. Первый аргумент вызова Init - это оконный объект порождающего окна. Если окно является основным окном (не имеющим порождающего окна), то это nil.

Установка атрибутов создания


    Типичное приложение Windows имеет много различных типов окон: перекрывающиеся или всплывающие, окна с рамкой, прокручиваемые окна, окна с заголовком и др. Эти атрибуты стилей, а также заголовок и меню окна задаются при инициализации оконного объекта и используются при создании элементов окна.

    Атрибуты создания оконного объекта, такие как стиль, заголовок и меню, записываются в поле Attr объекта - записи типа TWindowAttr. TWindowAttr содержит следующие поля:

Атрибуты создания окна. Таблица 10.1
ПолеТипИспользование
Title PChar Строка заголовка.
Style Longint Комбинированная константа стиля.
MenuHMenu Описатель ресурса меню.
XInteger Горизонтальная координата экрана верхнего левого угла окна.
YInteger Вертикальная координата экрана верхнего левого угла окна.
WInteger Начальная ширина окна в координатах экрана.
HInteger Начальная высота окна в координатах экрана.


Рис. 10.1 Атрибуты окна.

Используемые по умолчанию атрибуты окна


    По умолчанию TWindow.Init устанавливает Attr.Style в ws_Visible. Если окно является основным окном приложения, то Style равно ws_OverlappedWindow or ws_Visible.

    Menu по умолчанию устанавливается в 0. Это означает, что меню не определено.

    X, Y, W и H устанавливаются в cw_UseDefault, что дает в результате перекрывающееся окно удовлетворительного размера. Когда создается окно, не являющееся основным, значения X, Y, W и H вы обычно устанавливаете сами.

Переопределение используемых по умолчанию атрибутов


    При создании новых оконных типов, производных от TWindow, вы обычно определяете новый конструктор Init (особенно если хотите получить атрибут создания, отличных от используемого по умолчанию). Если вы хотите переопределить Init, то можете заново задать атрибуты объекта, непосредственно изменяя поле Attr после вызова Init.

    Если вы переопределили Init, убедитесь, что первое, что он делает - это вызов наследуемого метода TWindow.Init, устанавливающего используемые по умолчанию атрибуты. Затем вы можете изменить по своему выбору любой из атрибутов. Например, типичное окно может определять конструктор Init, который устанавливает атрибут Menu:

     constructor TWindowType.Init(AParent: PWindowsObject;
                                  ATitle: PChar);
     begin
        inherited Init(AParent, ATitle);
        Attr.Menu := LoadMenu(HInstance, 'TheMenu');
        AChildWindow := New(PChildWindowType, Init(@Self,
                            'Заголовок дочернего окна'));
        List1 := New(PListBox, Init(@Self, id_ListBox,
                     201, 20, 20, 180, 80));
            .
            .
            .
     end;

Атрибуты порожденного окна


    Конструктор TWindowType отвечает за построение его дочерних объектов, таких как всплывающие окна и блоки списка. Тип порожденного окна, в свою очередь, может устанавливать атрибуты в своем собственном конструкторе Init:

     constructor TChilwWindowType.Init(AParent: PWindowsObject;
                                  ATitle: PChar);
     begin
        inherited Init(AParent, ATitle);
        with Attr do
        begin
           Style := Style or ws_PopupWindow or ws_Caption;
           X := 100; Y := 100; W := 300; H := 300;
        end;
     end;

    В качестве альтернативы вы можете не определять потомка типа окна, а сначала построить объект окна, а затем переустановить его атрибуты (все это в конструкторе Init порождающего окна):

     constructor TWindowType.Init(AParent: PWindowsObject;
                                  ATitle: PChar);
     begin
        inherited Init(AParent, ATitle);
        Attr.Menu := LoadMenu(HInstance, 'TheMenu');
        AChildWindow := New(PChildWindowType, Init(@Self,
                            'Заголовок дочернего окна'));
        with Attr do
        begin
           Style := Style or ws_PopupWindow or ws_Caption;
           X := 100; Y := 100; W := 300; H := 300;
        end;
            .
            .
            .
     end;

Создание элементов окна


    После построения оконного объекта вам нужно сообщить Windows, что требуется создать связанные с объектом элементы экрана. Это делается с помощью вызова MakeWindow объекта приложения и передачи ему в качестве параметра указателя на объект окна.

     if Application^.MakeWindow(AWindow) <> nil then
          { успешное создание }
       else
          { неуспешное создание }

    MakeWindow вызывает два важных метода: ValidWindow и Create. ValidWindow проверяет успешность построение объекта окна, проверяя поле Status. Если по каким-либо причинам конструктор завершился неуспешно, то MakeWindow возвращает nil. При успешном выполнении конструктора MakeWindow переходит на метод Create оконного объекта.

    Create - это метод, который фактически сообщает Windows о создании элемента экрана. Если Create завершается неудачно, MakeWindow возвращает nil. В противном случае возвращается указатель на оконный объект. Для работы с элементом экрана Create также устанавливает поле HWindow.

    Хотя этот метод фактически создает элемент экрана, вы обычно не можете вызывать Create явно. Основное окно приложения автоматически создается при запуске программы методом TApplication.InitInstance.

    Все прочие окна приложения являются дочерними окнами, прямо или косвенно порождаемыми основным окном, а дочерние окна создаются обычно в методе SetupWindow или в его порождающих оконных объектах, либо с помощью MakeWindow динамически на этапе выполнения.

    Примечание: Дочерние окна и SetupWindow описываются в Главе 9 "Интерфейсный объекты".

    В общем случае порождающие окна обычно вызывают для своих дочерних окон методы Init и MakeWindow. Атрибуты оконного объекта обычно устанавливаются их методами объекта порождающего окна. Поскольку основное окно приложения не имеет порождающего окна, объект приложения строит и создает его при запуске приложения.

Задание атрибутов регистрации


    В ходе инициализации оконного объекта путем заполнения поля объекта Attr вы можете установить несколько атрибутов окна, такие как его стиль, расположение и меню. Эти атрибуты используются для создания соответствующего оконного элемента, поэтому они называются атрибутами создания.

    Другие атрибуты, включая фоновый цвет, пиктограмму представления и курсора "мыши", более тесно связаны с данным типом оконного объекта и не могут изменяться в ходе работы программы. Эти присущие окну атрибуты называются атрибутами регистрации, так как они устанавливаются при регистрации класса окна в Windows.

Классы окон


    С каждым типом оконного объекта связан список атрибутов регистрации, которые называются классом окна. Список атрибутов регистрации во многом напоминает список атрибутов создания, записанных в поле записи Attr объекта окна. Однако, атрибуты регистрации сохраняются в записи с именем TWndClass, который определяется и поддерживается Windows.

    Процесс связи класса окна с типом оконного объекта называется регистрацией класса окна. ObjectWindows автоматизирует процесс регистрации. Таким образом, если вы хотите изменить какую-либо из используемых по умолчанию характеристик объекта, то можете не беспокоиться о классе регистрации окна.

    Поля записи TWndClass и их типы перечислены в следующей таблице:

Атрибуты регистрации окна. Таблица 10.2
ХарактеристикаПолеТип
стиль классаstyleWord
пиктограммаhIconHIcon
курсорhCursorHCursor
фоновый цветhbrBackgroundHBrush
меню по умолчаниюlpszMenuNamePChar

Поля стиля класса

    Это поле стиля отличается от атрибута стиля окна (ws_), задаваемого при инициализации окна, поскольку задает поведение, присущее операциям окна (в отличие от их визуального представления). Это поле может заполняться комбинацией констант стиля (cs_).

    Например, cs_HRedraw приводит к повторному отображению окна при изменении его размера по горизонтали; cs_DoubleClk позволяет окну получать сообщения о двойном нажатии кнопки "мыши"; cs_NoClose предотвращает выбор параметра Close меню Control, а cs_ParentDC дает окну контекст дисплея порождающего окна.

Поле пиктограммы

    Это поле содержит описатель пиктограммы, которое используется для представления окна в его минимизированном состоянии. Обычно для представления основного окна программы выбирается ресурс пиктограммы.

Поле курсора

    Поле hCursor содержит описатель курсора, который используется для представления указателя "мыши" при позиционировании его в окне.

Поле фонового цвета

    Это поле задает фоновый цвет окна. Для большинства приложений используется стандартный назначаемый по умолчанию цвет окна, который может устанавливаться пользователем в управляющей панели. Однако вы можете путем установки этого поля в описатель физической кисти подставить конкретный цвет. Либо вы можете установить любое из значений цветов Windows, такие как color_ActiveCaption. К любому значению цвета всегда добавляйте 1.

Поле используемого по умолчанию меню

    Это поле указывает на имя ресурса меню, которое служит используемым по умолчанию меню для данного класса. Например, если вы определите тип EditWindow, который всегда имеет стандартное меню редактирования, то можете задать здесь это меню. Это устранит необходимость задания меню в методе Init. Если данный ресурс меню имеет идентификатор 'MyMenu', вы можете установить это поле следующим образом:

     
     AWndClass.IpszMenuName := 'MyMenu';

Используемые по умолчанию атрибуты регистрации


    Тип TWindow определяет класс окна 'TurboWindow' с пустой пиктограммой, курсором-стрелкой и стандартным цветом окна. Используемый по умолчанию класс ObjectWindows (TurboWindow) имеет следующие атрибуты:

Регистрация нового класса


    Чтобы изменить атрибут регистрации, такой как курсор или пиктограмму, вам нужно написать два метода - GetClassName и GetWindowClass - и определить новый класс окна. Каждый раз, когда вы изменяете атрибуты регистрации, вам нужно изменить имя класса. Если класс регистрации с данным именем уже зарегистрирован в Windows, другие классы с тем же именем класса регистрироваться не будут - они получат атрибуты уже зарегистрированного класса.

Изменение имени класса


    GetClassName - это функция, которая возвращает имя (PChar) класса окна. TWindow.GetClassName возвращает 'TurboWindow', имя используемого по умолчанию класса окна. TWindow.GetClassName возвращает 'TurboWindow' - имя используемого по умолчанию класса окна:

     function TWindow.GetClassName: PChar;
     begin
       GetClassName := 'TurboWindow';
     end;

    Чтобы определить тип объекта окна с именем IBeamWindow, который использует вместо стандартной стрелки I-образный курсор, переопределите наследуемый метод следующим образом:

     function TBeamWindow.GetClassName: PChar;
     begin
       GetClassName := 'IBeamWindow';
     end;

    Примечание: Имя класса не обязательно должно соответствовать имени объектного типа.

    Имя класса должно быть уникальным.

Определение новых атрибутов регистрации


    Чтобы отклониться от стандартных характеристик, вы должны заполнить поля записи TWndClass с различными данными в методе GetWindowClass.

    GetWindowClass воспринимает в качестве аргумента-переменной запись TWndClass и заполняет ее поля новыми атрибутами регистрации. Когда вы определяете новый метод GetWindowClass, вам следует всегда сначала для установки значений по умолчанию вызывать наследуемый метод TWindow.GetWindowClass, а затем устанавливать поля, которые вы хотите изменить.

    Например, в поле hCursor хранится описатель ресурса курсора. Для IBeamWindow определяется метод GetWindowClass:

     procedure IBeamWindow.GetWindowClass(var AWndClass:
                                          TWndClass);
     begin
       inherited GetWindowClass(AWndClass);
       AWndClass.hCursor := LoadCursor(0, idc_IBeam);
     end;

    Примечание: idc_Beam - это константа, представляющая один из курсоров Windows.

    Кроме окон, диалоговым окнам (не диалоговым блокам) необходимо регистрировать классы окна (см. Главу 11). Диалоговым блокам и управляющим элементам классы окон не требуются.

Использование специализированных окон


    ObjectWindows предусматривает два потомка TWindow, являющихся специализированными окнами для редактирования текста. Объектный тип TEditWindow обеспечивает простой текстовый редактор, не обладающий возможностями чтения из файла или записи в него. Тип TFileWindow, наследующий из TEditWindow, обеспечивает текстовый редактор с возможностями чтения/записи файлов.

    Эти объекты можно использовать непосредственно как стандартные компоненты ваших приложений. Вы можете также построить производные от них типы и создать свои собственные специализированные редакторы. Программы или модули, использующие окна редактирования или файловые окна, должны включать в свой оператор uses модуль OStdWnds.

Использование окон редактирования


    Окно редактирования - это окно с управляющим элементом редактирования, заполняющим его область клиента. TEditWindow.Init инициализирует поле Editor окна редактирования, чтобы оно указывало на управляющий элемент объекта редактирования. TEditWindow.SetupWindow устанавливает размеры управляющего элемента редактирования в соответствии с областью клиента окна и создает экранный управляющий элемент редактирования.

    Метод WMSize обеспечивает изменение размера управляющего элемента редактирования при изменении размера его окна. Метод WMSetFocus обеспечивает, что управляющий элемент редактирования получает фокус ввода при получении окном сообщения wm_SetFocus.

    Показанная ниже программа EditWindowTester использует окно редактирования, чтобы пользователь мог редактировать текст для простой (нефункциональной) электронной почты.


Рис. 10.2 Окно редактирования.

     
     program EditWindowTester;
     {$R EWNDTEST.RES}
     uses ODialogs, WinTypes, WinProcs, Strings, OStdWnds;
     const cm_sendText = 399;
     type
       TestApplication = object(TApplication)
          procedure InitMainWindow; virtual;
       end;
       PMyEditWindow = ^MyEditWindow;
       MyEditWindow = object(TEditWindow)
          constructor Init(AParent: PWindowsObject;
                           ATitle: PChar);
          procedure CMSendText(var Msg: TMessage); virtual
                           cm_First + cm_SendText;
       end;
     constructor MyEditWindow.Init(AParent: PWindowsObject;
                           Atitle: PChar);
     begin
       inherited Init(AParent, ATitle);
       Attr.Menu := LoadMenu(HInstance, MakeIntResource(102));
     end
     procedure MyEditWindows.CMSendText(var Msg: TMessage);
     var
       Lines: Integer;
       TestString: string[3];
       Text: array[0..20] of Char;
     begin
       Lines := Editor^.GetNumLines;
       Str(Lines, TextString);
       StrCat(Text, ' строк послано');
       MessageBox(HWindow, @Text, 'Передано сообщение', mb_Ok);
     end;
     procedure TestApplication.InitMainWindow;
     begin
       MainWindow := New(PMyEditWindow, Init(nil,
          'Окно редактирования - попробуйте набор и редактирование'));
     end;
     var TestApp: TestApplication;
     begin
       TestApp.Init('EditWindowTester');
       TestApp.Run;
       TestApp.Done;
     end.

Использование файловых окон


    Файловое окно - это окно редактирования с дополнительными возможностями, позволяющими считывать и записывать данные в файл. TFileWindow.Init воспринимает в качестве аргумента заголовок окна и устанавливает поле FileDialog таким образом, чтобы оно указывало на файловый диалоговый объект.

    Для работы с файлами TFileWindow имеет четыре метода. Методы Open, Save и SaveAs для вывода пользователю подсказки с именем файла используют поле TFileWindow.FileDialog (см. Главу 11). Метод New дает пользователю возможность отмены, если редактирование нового файла приведет к потере изменений текущего текста. Чтобы дать пользователю возможность доступа к этим методам, создайте свое меню со следующими идентификаторами меню:

Методы и идентификаторы меню файлового окна. Таблица 10.3
МетодИдентификатор меню для вызова
Newcm_FileNew
Opencm_FileOpen
Savecm_FileSave
SaveAscm_FileSaveAs

    Вы можете использовать файловые окна без модификации как простые автономные текстовые редакторы. Однако, иногда желательно создать производные от TFileWindow типы и обеспечить дополнительные функциональные возможности. Например, можно предусмотреть средство поиска. Помните, что вы все равно будете иметь доступ к управляющему элементу редактирования TFileWindow.Editor.

Прокрутка содержимого окон


    В большинстве случаев для прокрутки текущей области просмотра пользователи используют полосы прокрутки вдоль края окна. В отличие от стандартных управляющих элементов типа полос прокрутки полосы прокрутки окна являются частью самого окна.

    ObjectWindows управляет прокруткой окна, предоставляя каждому оконному объекту поле Scroller, которое может указывать на объект TScroller. Объект прокрутки TScroller обеспечивает автоматизированный способ прокрутки в окнах текста и графики. Кроме того, TScroller может прокручивать окна, когда пользователь перемещает "мышь" за область клиента окна (это называется автоматической прокруткой и действует даже для окон, которые не имеют полос прокрутки).

Что такое объект прокрутки?


    TScroller содержит значения, определяющие, насколько должно прокручивается окно. Эти значения записываются в полях XUnit, YUnit, XLine, YLine, XRange, YRange, XPage и YPage объекта TScroller. Поля, начинающиеся с буквы X, представляют горизонтальные значения, а начинающиеся с буквы Y - вертикальные.

Единицы прокрутки

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

    Например, если вы выводите текст с шириной символа 8 элементов изображения и высотой 15, то в качестве значений XUnit и YUnit полезно задать, соответственно, 8 и 15.

Строки, страницы и диапазон

    Другие атрибуты прокрутки - строка, страница и диапазон выражаются в единицах прокрутки. Значения Line (строка) и Page (страница) - это число единиц, на которые выполняется прокрутка в ответ на запрос пользователя. Запрос может иметь форму щелчка кнопкой "мыши" на концах полосы прокрутки (построчная прокрутка). Щелчок "мышью" в самой полосе прокрутки (но не на маркере полосы прокрутки) позволяет выполнять постраничную прокрутку. Атрибуты диапазона (XRange, YRange) представляют общее число единиц, на которое можно выполнять прокрутку. Обычно этот диапазон определяется на основе размера редактируемого документа.

Типичный объект прокрутки

    В качестве примера рассмотрим текстовое окно редактирования. Если вы хотите вывести на экран текстовый файл, имеющий 400 строк текста с границей 80 символов и 50 строками на странице, то можно выбрать следующие значения:

Типичные значения для окна редактирования. Таблица 10.4
ПолеЗначениеСмысл
XUnit8ширина символа
YUnit15высота символа
XLine, YLine11 единица на строку
XPage4040 символов по горизонтали на страницу
YPage5050 символов по вертикали на страницу
XRange80максимальный горизонтальный диапазон
YRange400максимальный вертикальный диапазон

    Объект TScroller с данными значениями позволяет выполнять построчную или постраничную прокрутку. С помощью полос прокрутки или автоматической прокрутки выполняется просмотр всего файла.

Значения по умолчанию

    По умолчанию XLine и YLine имеют значение 1, так что без явной необходимости устанавливать их в другие значения не нужно. Для установки значений прокрутки на страницу также существует используемая по умолчанию схема, согласно которой страница прокрутки будет соответствовать текущей высоте или ширине области клиента окна (в зависимости от направлений прокрутки). Если вы не хотите переопределить данный механизм, переустанавливать эти значения не требуется.

Задание для окна объекта прокрутки


    Чтобы задать для окна объект прокрутки, постройте в конструкторе своего оконного объекта объект TScroller и присвойте его полю Scroller. Вам нужно установить начальный размер единицы и диапазона, но позднее вы можете их изменить.

    При использовании объекта прокрутки для автоматической прокрутки полосы прокрутки не требуются, но многие прокручиваемые окна их имеют. Чтобы добавить в окно полосы прокрутки, добавьте в поле Attr.Style ws_VScroll, ws_HScroll (или то и другое).

    Приведем пример конструктора для текстового окна редактирования:

     constructor TTextWindow.Init(AParent: PWindowsObject;
                                  ATitle: PChar);
     begin
       inherited Init(AParent, ATitle);
       Attr.Style := Attr.Style or ws_VScroll or ws_YScroll;
       Scroller := New(PScroller, Init(@Self, 8, 15, 80, 400));
     end;

    В качестве аргументов TScroller воспринимает прокручиваемое окно и начальные значения для полей XUnit, YUnit, XRange и YRange соответственно. Атрибуты строки и страницы получают значения по умолчанию.

    После вывода окна на экран содержимое его области клиента можно прокручивать вертикально и горизонтально, используя для этого полосу прокрутки или автоматическую прокрутку. Метод Pant окна просто рисует на экране графическую информацию, необходимую для уведомления о прокрутке. Как описывается в конце этого раздела, метод Paint можно оптимизировать для вывода только части рисунка.

Пример прокрутки


    Scroll - это полное приложение с графическим выводом, допускающим прокрутку. Показанная ниже программа рисует последовательность прямоугольников, затем увеличивает их размер, так что вся картинка не умещается в область клиента окна, отображенного на обычном экране VGA. С помощью полос прокрутки вы можете просматривать различные части рисунка или автоматически прокручивать картинку, удерживая нажатой левую кнопку "мыши" и перемещая ее из области клиента.

     program Scroll;
     uses Strings, WinTypes, WinProcs, OWindows;
     type
       TScrollApp = object(TApplication)
           procedure InitMainWindow; virtual;
       end;
       PScrollWindow = ^TScrollWindow;
       TScrollWindow = object(TWindow)
         constructor Init(ATitle: PChar);
         procedure Paint(PaintDC: HDC;
                         var PaintInfo: TPaintStruct); virtual;
       end;
     procedure TScrollApp.InitMainWindow;
     begin
       MainWindow := New(PScrollWindow, Init('Boxes'));
     end;
     constructor TScrollWindow.Init(ATitle: PChar);
     begin
       inherited Init(nil, ATitle);
       Attr.Style := Attr.Style or ws_VScroll or ws_HScroll;
       Scroller := New(PScroller, Init(@Self, 8, 15, 80, 60));
     end;
     procedure TScrollWindow.PAint(PaintDC: HDC;
                                   var PaintInfo: TPaintStruct);
     var X1, Y1, I: Integer;
     begin
       for I := 0 to 49 do
       begin
         X1 := 10 + I*8;
         Y1 := 30 + I*5;
         Rectangle(PaintDC, X1, Y1, X1 + X1, X1 + Y1 * 2);
       end;
     end;
     var ScrollApp: TScrollApp;
     begin
       ScrollApp.Init('ScrollApp');
       ScrollApp.Run;
       ScrollApp.Done:
     end.

Запрещение автоматической прокрутки


    Объект TScroller может по умолчанию выполнять автоматическую прокрутку, но установка поля AutoMode TScroller в значение False отключает это средство. Окно-владелец может сделать это в конструкторе после построения объекта TScroller:

     Scroller := New(PScroller, Init(@Self, 8, 15, 80, 60));
     Scroller^.AutoMode :=False;

    Если AutoMode равно False, то прокрутка может выполняться только с помощью полос прокрутки. Полезная особенность автоматической прокрутки состоит в том, что чем дальше вы сдвинете "мышь" от области клиента окна, тем быстрее будет происходить прокрутка окна. В зависимости от удаления мыши приращение прокрутки будет обратно пропорционально значению параметра строк и прямо пропорционально значению параметра страницы.

Отслеживание полос прокрутки


    В дополнение к автоматической прокрутке, приведенный выше пример программы будет отслеживать запросы на прокрутку, сдвигая при нажатой кнопке "мыши" маркер полосы прокрутки. Другими словами картинка сдвигается уже при нажатой кнопке. Эта особенность дает действительную обратную связь, и пользователь может сдвигать нужную часть изображения не отпуская кнопку "мыши".

    Однако, в некоторых случаях этот эффект нежелателен. Например, если вы просматриваете большой текстовый файл, такое отслеживание может замедлить работу, поскольку возникает необходимость постоянно считывать информацию с диска и отображать порцию текста для каждого движения "мыши". В такой ситуации лучше отменить этот эффект:

     Scroller^.TrackMode:=False;

    Теперь никакой прокрутки не происходит до момента отпускания кнопки на мыши, и в области клиента будет лишь однократно показана нужная часть картинки.

Модификация единиц прокрутки и диапазона


    В приведенных выше примерах мы предполагали, что к моменту построения TScroller известны значения единиц и диапазонов. Во многих случаях эта информация неизвестна или может меняться при изменении размеров отображаемой информации. В этом случае может потребоваться установить или изменить значения диапазона (а может быть и единиц) позднее. Если значения заранее неизвестны, то их можно задать как 0 в конструкторе TScroller.

Изменение диапазона

    Метод SetRange воспринимает два целочисленных аргумента число горизонтальных и вертикальных единиц, которые определяют общий диапазон прокрутки. Метод SetRange должен использоваться при изменении размеров картинки. Например, при подготовке изображения картинки шириной 100 единиц и высотой 300, данная команда установит диапазон прокрутки надлежащим образом:

     Scroller^.setRange(100, 300);

Изменение единиц прокрутки

    Если при инициализации объекта TScroller единицы неизвестны, то их значения могут быть установлены непосредственно перед прокруткой. Например, они могут быть установлены методом окна SetupWindow:

     procedure ScrollWindow.SetupWindow;
     begin
        TWindow.SetupWindow;
        Scroller^.XUnit:=10;
        Scroller^.YUnit:=20;
     end;

Изменение позиции прокрутки


    Windows с помощью методов ScrollTo и ScrollBy может выполнять принудительную прокрутку. Каждый из них воспринимает два целочисленных аргумента в терминах горизонтальных и вертикальных единиц прокрутки. Например, если нужно переместиться к левому верхнему углу картинки, то используется ScrollTo:

     Scroller^.ScrollTo(0, 0);

    Приведем другой пример. Если картинка имеет длину 400 единиц в вертикальном направлении, то позицию прокрутки можно переместить к середине картинки следующим образом:

     Scroller^.ScrollTo(0, 200);

    Метод ScrollBy может перемещать позицию просмотра на заданное число единиц вверх, вниз, влево или вправо. Отрицательные значения осуществляют сдвиг к левому верхнему углу, а положительные - к правому нижнему. Если нужно сместиться на 10 единиц вправо и на 20 единиц вниз, то это можно сделать командой:

     Scroller^.ScrollBy(10, 20);

Установка размеров страницы


    По умолчанию размер страницы (XPage и YPage) устанавливается в соответствии с размером области клиента окна. При изменении размеров окна механизм прокрутки учитывает эту информацию. Метод окна WMSize вызывает метод прокрутки SetPageSize, который устанавливает поля объекта XPage и YPage на основании текущих размеров области клиента окна и значений XUnit и YUnit. Для отмены этого механизма и непосредственной установки размеров страницы вы должны переписать унаследованный метод объекта окна WMSize и не вызывать SetPageSize:

     procedure TTestWindow.WMSize(var Msg: TMessage);
     begin
       DefWndProc(Msg);
     end;

    Затем вы можете непосредственно установить XPage и YPage в конструкторе окна (или в производном конструкторе TScroller):

     constructor ScrollWindow.Init(AParent:PWindowsObject;
                                   ATitle: PChar);
     begin
       inherited Init(AParent, ATitle);
       Attr.Style:=Attr.Style or ws_VScroll or ws_HScroll;
       Scroller:=New(PScroller, Init(@Self, 8, 15, 80, 400));
       Scroller^.XPage:=40;
       Scroller^.YPage:=100;
     end;

Оптимизация методов Paint для прокрутки


    В приведенном выше примере рисуется 50 прямоугольников, но не делается даже попытки определить, все ли прямоугольники видны в области клиента окна. Это может привести к излишним усилиям на дорисовку невидимых изображений. Для оптимизации рисования в окне методом Paint можно использовать метод TScroller.IsVisibleRect.

    Приведенный ниже метод ScrollWindow.Paint использует IsVisibleRect для определения, нужно ли вызывать функцию Windows Rectange. Rectange воспринимает аргументы в единицах устройства, а VisibleRect в единицах прокрутки. С этой целью вершина прямоугольника X1 Y1 и ширина прямоугольника (X2-X1) и его высота (Y2-Y1) должны быть разделены на соответствующее число единиц до вызова IsVisibleRect:

     procedure TScrollWindow.Paint(PaintDC: HDC; var PaintInfo:
                                   TPaintStruct);
     var
       X1, Y1, X2, Y2, I: Integer;
     begin
        for I:=0 to 49 do
        begin
           X1 := 10 + I * 8;
           Y1 := 30 + I * 5;
           X2 := X1 + X1;
           Y2 := X1 + Y1 * 2;
           if Scroller^.IsVisibleRect(X1 div 8,
                                      Y1 div 15, (X2-X1) div 8,
                                      (Y2-Y1) div 15) then
           Rectangle(PaintDC, X1, Y1, X2, Y2);
         end;
     end;