Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

Глава 6. Вывод всплывающего окна

             Итак, вы создали два  типа  окон  -  основное  окно  (объект
        TStepWindow) и режимные дочерние окна,  которые создаются и унич-
        тожаются каждый раз,  когда они необходимы (например, блоки сооб-
        щений).  Однако,  в  полноценной  программе Windows дочерние окна
        часто требуется сохранять активными в течении неопределенного пе-
        риода времени (в качестве примера можно привести окно оперативной
        полосы SpeedBar в  работающей под Windows  интегрированной  среде
        IDE).

             До сих пор все дочерние окна  в  Steps  имели  фиксированный
        размер и создавались из шаблонов ресурсов. В шагах 10 - 12 вы бу-
        дете делать следующее:

             * Создавать окна с динамическим размером,  сохраняющиеся  на
               все время работы программы.

             * Добавлять в окно специализированные управляющие элементы.

             * Создавать собственное интерактивное окно.

             Наконец, мы  дадим  предложения  по  дальнейшему  расширению
        программы Steps.

                         Шаг 10: Добавление всплывающего окна
        -----------------------------------------------------------------

                            +-----------------------+
                            | Step 1: Basic App     |
                            | Step 2: Text          |
                            | Step 3: Lines         |
                            | Step 4: Menu          |
                            | Step 5: About Box     |
                            | Step 6: Pens          |
                            | Step 7: Painting      |
                            | Step 8: Streams       |
                            | Step 9: Printing      |
                            |XStepX10:XPaletteXXXXXX|
                            | Step 11: BWCC         |
                            | Step 12: Custom ctrls |
                            +-----------------------+

             Создание и уничтожение окон и  диалоговых  блоков  прекрасно
        подходит, когда они используются не часто. Но в некоторых случаях
        желательно иметь дочернее окно,  доступное большую часть времени.
        Примером такого окна является инструментальная палитра.

             В этом шаге вы будете делать следующее:

             * Добавите к основному окну поле.

             * Построите плавающую палитру пера.

             * Выведете и скроете палитру пера.

             Палитра пера, которая выводится при выборе пользователем ко-
        манды Palette|Show (Палитра|Вывод) показана на Рис. 6.1.

                    +---------------------------------------+
                    |#=#XXXXXXXXXXXXXPenPaletteXXXXXXXXXXXXX|
                    +------------------+--------------------|
                    |   @@      Add    |           Delete   |
                    | @@@@@@    Pen    | @@@@@@    Pen      |
                    |   @@             |                    |
                    +------------------+--------------------|
                    |                                       |
                    |     -----------------------------     |
                    |                                       |
                    +---------------------------------------|
                    |                                       |
                    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^     |
                    |                                       |
                    +---------------------------------------|
                    |                                       |
                    |     XXXXXXXXXXXXXXXXXXXXXXXXXXXXX     |
                    |                                       |
                    +---------------------------------------+

             Рис. 6.1 Палитра пера программы Steps с тремя перьями.

             Поскольку это первое "новое" окно, которое вы создаете и ко-
        торое будет создаваться автоматически,  неплохо рассмотреть,  как
        создаются и выводятся на экран объекты и элементы окна.

                           Добавление к окну дочернего окна
        -----------------------------------------------------------------

             Режимными дочерними окнами,  которые вы до сих пор использо-
        вали, управлять легко. Вы можете их создать, использовать и отме-
        нить. Но здесь нам нужно работать с диалоговым окном,  которое не
        является режимным.  Например, оно должно закрываться, если закры-
        вается приложение,  и становиться скрытым, если основное окно ми-
        нимизируется.

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

             Так как  основное окно должно посылать команды окну палитры,
        потребуется указатель на это окно, поэтому добавьте в TStepWindow
        его палитры пера. TStepWindow содержит теперь следующие поля:

             TStepWindow = object(TWindow)
                DragDC: DHC;
                ButtonDown: Boolean;
                FileName: array[0fsPathName] of Char;
                HasChanged, IsNewFile: Boolean;
                Drawing: PCollection;
                Printer: PPrinter;
                PenPalette: PPenPalette;      { окно палитры }
                  .
                  .
                  .
             end;

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

                               Построение окна палитры
        -----------------------------------------------------------------

             Объекты дочернего окна строятся обычно в конструкторах своих
        порождающих окон. Аналогично тому, как это происходит при инициа-
        лизации любого другого поля, вы присваиваете значения любому ука-
        зателю дочернего окна. В данном случае:

             constructor TStepWindows.Init(AParent: PWindowsObject;
                                           ATitle: PChar);
             begin
               inherited Init(AParent, ATitle);
                 .
                 .
                 .
               PenPalette := New(PPenPalette, Init(@Self, 'Pan Palette');
             end;

                             Назначение порождающего окна
        -----------------------------------------------------------------

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

             Важным исключением  является  основное окно приложения.  Так
        как оно не имеет порождающего окна,  конструктору основного  окна
        передается nil.

             Каждый оконный  объект  имеет  список  своих  дочерних окон,
        обеспечивающий создание, вывод, сокрытие и закрытие окон в нужные
        моменты. Построение и уничтожение дочерних окон автоматически об-
        новляет список дочерних окон: построение дочернего окна добавляет
        его в  список своего порождающего окна;  уничтожение окна удаляет
        его из списка.


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

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

             Когда вы в шаге 3 делали это для диалогового окна,  то вызы-
        вали ExecDialog.  Метод TApplication создает элемент экрана и вы-
        полняет режимное диалоговое окно. Соответствующим методом для не-
        режимных (или     безрежимных)     диалоговых    окон    является
        TApplication.MakeWindow.  Основным  отличием  является  то,   что
        MakeWindow  не выводит автоматически создаваемый элемент экрана и
        не переходит в режимное состояние.

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

             Тогда процесс  построения и вывода окна состоит из следующих
        трех этапов:

             * Построение оконного объекта с помощью Init.

             * Создание элемента экрана с помощью MakeWindow.

             * Вывод окна с помощью Show.

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

             В следующем разделе мы выведем дочернее окно.


                               Вывод и сокрытие палитры
        -----------------------------------------------------------------

             Дочерние окна, отличные от тех, которые были созданы и выве-
        дены по умолчанию их порождающими окнами (этот процесс управляет-
        ся с помощью EnableAutoCreate и DisableAutoCreate) в  каждом  ин-
        терфейсном объекте. Но вы можете скрыть или вывести дочернее окно
        по   команде.   Обе   функции   метода   Show   наследуются    из
        TWindowsObject.

             В зависимости от передаваемых параметров метод Show  выводит
        либо скрывает окно.  Параметр - это одна из констант sw_, опреде-
        ленная в Windows.  В ответ на команды меню  Palette|Show  (Палит-
        ра|Вывод) или Palette|Hide (Палитра|Сокрытие),  которые генериру-
        ют,  соответственно, команды cm_PalShow и cm_PalHide, TStepWindow
        вызывает метод Show палитры пера (это дополняет STEP10.PAS):

             procedure TStepWindow.CMPalShow(var Msg: TMessage);
             begin
               PenPalette^.Show(sw_ShowNA);
             end;

             procedure TStepWindow.CMPalHide(var Msg: TMessage);
             begin
               PenPalette^.Show(sw_Hide);
             end;

             Если у вас есть поле порождающего окна,  указывающее на  до-
        чернее окно,  с которым необходимо работать,  вы легко можете за-
        дать дочернее окно.

------------------------------------------------------------------------
             Шаг 11: добавление специализированных управляющих элементов
        -----------------------------------------------------------------

                            +-----------------------+
                            | Step 1: Basic App     |
                            | Step 2: Text          |
                            | Step 3: Lines         |
                            | Step 4: Menu          |
                            | Step 5: About Box     |
                            | Step 6: Pens          |
                            | Step 7: Painting      |
                            | Step 8: Streams       |
                            | Step 9: Printing      |
                            | Step 10: Palette      |
                            |XStepX11:XBWCCXXXXXXXXX|
                            | Step 12: Custom ctrls |
                            +-----------------------+

             В шаге 10 вы добавили к основному окну независимое  дочернее
        окно. Теперь вы добавите зависимые дочерние окна, которые называ-
        ются управляющими элементами. Порождающим окном этих  управляющих
        элементов является  окно палитры пера.  Нужно помнить о том,  что
        окно палитры пера является независимым дочерним окном,  для кото-
        рого порождающим окном является основное окно. Таким образом, па-
        литра пера является одновременно дочерним окном основного окна  и
        порождающим окном для управляющих элементов.

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

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

             В данном шаге вы добавите графические кнопки с помощью:

             * добавления простых управляющих кнопок;

             * реализации в программе специализированных управляющих эле-
               ментов;

             * определения графических изображений для кнопок.

             Для всех управляющих элементов,  в целом, поведение задается
        типом ObjectWindows TControl и  его  типом-потомком,  позволяющим
        работать с каждым типом управляющего элемента. Например, TListBox
        определяет объекты блока списка, а TEdit определяет каждый управ-
        ляющий  объект  редактирования.  Вы  должны  также понимать,  что
        TControl - это потомок TWindow.


                        Добавление к палитре командных кнопок
        -----------------------------------------------------------------

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

             Управляющие элементы окон задаются определением объекта. По-
        рождающее окно управляет их поведением через методы, определенные
        объектами управляющих  элементов ObjectWindows.  Например,  чтобы
        получить следующий элемент,  который пользователь выбрал в  блоке
        списка, вызовите  метод  GetSelString  объекта блока.  Аналогично
        оконному объекту или объекту диалогового блока,  объект управляю-
        щего элемента имеет соответствующий визуальный элемент.

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

             const
               id_Add = 101;
               id_Del = 102;
               MaxPens = 9;

             MaxPens задает максимальное число перьев,  которые будет со-
        держать палитра.  Значение 9 хорошо подходит для стандартного эк-
        рана VGA.

                        Объекты управляющих элементов как поля
        -----------------------------------------------------------------

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

             TPenPalette = object(TWindow)
                AddBtn, DelBtn: PButton;
                  .
                  .
                  .
             end;

             После создания экземпляра этих дочерних объектов управляющих
        элементов вы можете манипулировать ими с помощью вызовов методов.
        Например, в соответствующие моменты можно разрешать или запрещать
        командные кнопки,  вызывая их методы Enable и Disable.  Используя
        метод ChildList порождающего окна, можно получить доступ к объек-
        там управляющих элементов дочерних окон,  не записанных в  полях,
        но гораздо удобнее делать это с помощью полей.


                           Работа с управляющими элементами
        -----------------------------------------------------------------

             Любой тип окна,  который имеет объекты управляющих элементов
        (или другое дочернее окно) должен определять для построения своих
        объектов управляющих элементов конструктор Init.  Кроме того, для
        задания управляющих элементов перед выводом вы можете переопреде-
        лить SetupWindow.  Порождающее окно  (TPenPalette)  автоматически
        создает и выводит все свои дочерние окна.

             Приведем в качестве примера метод Init палитры пера. Первое,
        что он делает - это установка собственного расположения и атрибу-
        тов размера.  Так как метод Init окна отвечает за задание его ат-
        рибутов создания,  и поскольку вместе с ним создаются управляющие
        элементы окна,  вы  должны также в методе Init окна построить уп-
        равляющие элементы.  В каждом вызове конструктора первый параметр
        - это @Self (порождающее окно).  За ним следует идентификатор уп-
        равляющего элемента.

             constructor TPenPalette.Init(AParent: PWindowsObject;
                           ATitle: PChar);
             begin
               inherited Init(AParent, ATitle);
               with Attr do
               begin
                 Style := Style or ws_Tiled or ws_SysMenu or
                            ws_Visible;
                 W := 133;
                 H := GetSystemMetrics(sm_CYCaction) + 42;
                 AddBtn := New(PButton, Init(@Self, id_Add,
                               'Добавить перо', 0, 0, 65, 40, True);
                 DelBtn := New(PButton, Init(@Self, id_Del,
                               'Удалить перо', 0, 0, 65, 40, False);
             end;

             После создания окна, чтобы задать управляющие элементы окна,
        вызывается виртуальный  метод TPenPalette.SetupWindow.  Поскольку
        здесь вы имеете дело только с командными кнопками,  инициализация
        не требуется,  но TPenPalette.SetupWindow первоначально запрещает
        одну из командных кнопок. Если бы вы использовали другой управля-
        ющий элемент (например, блок списка), то для инициализации объек-
        та управляющего элемента потребовалось бы вызывать SetupWindow.

                   Примечание: Когда вы переопределяете метод SetupWindow
              окна, не   забудьте   сначала  вызывать  наследуемый  метод
              SetupWindow,  так как он создает все  дочерние  управляющие
              элементы.

             Вызов методов Init и SetupWindow вполне достаточен для  пра-
        вильного вывода в окне палитры всех управляющих элементов. Коман-
        дные кнопки можно будет активизировать ("нажимать"),  но без  ка-
        ких-либо действий.  В шаге 12 мы определим реакцию на события уп-
        равляющего элемента.


                               Сокрытие вместо закрытия
        -----------------------------------------------------------------

             Если вы дважды щелкните "мышью" в блоке системного меню  па-
        литры пера,  оно  исчезнет.  Выбор  команды Palette|Show не может
        больше выводить палитру,  так как объект и его экранные  элементы
        уничтожены. Выводить нечего.  Вы можете переопределить это, доба-
        вив метод CanClose,  который скрывает окно, а затем запрещает его
        закрытие (см. STEP11A.PAS):

             function TPenPalette.CanClose: Boolean;
             begin
               Show(sw_Hide);
               CanClose := False;
             end;

             Теперь двойной  щелчок "мышью" в блоке системного меню скры-
        вает окно, но не закрывает его, так что позднее вы можете вывести
        его снова.

             Обычно наличие дочернего окна,  которое всегда возвращает из
        CanClose False, может предотвратить закрытие всего приложения. Но
        TStepWindow перед закрытием не проверяет своих дочерних окон, так
        как в шаге 1 вы переопределили его метод CanClose.


                 Разрешение специализированных управляющих элементов
        -----------------------------------------------------------------

             Как вы  уже  знаете,  объекты  управляющих элементов создают
        стандартные управляющие элементы Windows.  Например,  только  что
        созданный вами  объект  TButton  дает в результате в палитре окна
        стандартную серую кнопку.  Однако IDE и диалоговые блоки, создан-
        ные вами  из ресурсов,  используют кнопки другого типа с располо-
        женными на них графическими изображениями.  ObjectWindows предос-
        тавляет вам  простой  способ использования в программах командных
        кнопок такого вида.

             Использовать специализированные управляющие элементы Borland
        для Windows  (BWCC) также просто,  как использование модуля.  Для
        этого нужно просто добавить BWCC в оператор uses  основной  прог-
        раммы. Это немедленно дает два эффекта. Первый состоит в том, что
        все стандартные диалоговые блоки (такое как  файловое  диалоговое
        окно, которое  вы  уже добавили в программу Steps) используют для
        таких общих элементов как кнопки OK или Cancel,  а также кнопки с
        зависимой и независимой фиксацией, вместо стандартных управляющих
        элементов специализированные.

                   Примечание: Об использовании и проектировании  специа-
              лизированных управляющих элементов Borland рассказывается в
              Главе 12.

             Фактически, после добавления в оператор uses программы Steps
        BWCC вы  можете  перекомпилировать  программу и получить доступ к
        диалоговым блокам.  Без  каких-либо  других усилий вы существенно
        улучшите внешний вид программы и ее интерфейса.

             Но как насчет кнопок в палитре пера? Они были созданы из уп-
        равляющих объектов с BWCC,  используемых в программе, но выглядят
        как обычные командные кнопки.  Ответ, конечно, состоит в том, что
        вы еще не определили для кнопок, графические изображения, так что
        по умолчанию они просто используют метки,  переданные в конструк-
        торе Init. В следующем разделе вы увидите, как добавлять к специ-
        ализированным управляющим элементам графические изображения.


                Создание для командных кнопок графических изображений
        -----------------------------------------------------------------

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

             С этими  графическими  изображениями  вы  можете  делать что
        угодно. Здесь ограничений нет.  Вы можете изменять цвет образа  в
        зависимости от состояния кнопки или перемещать образы (что обычно
        используется) при нажатии, а также добавлять вокруг текста кнопки
        линию из точек,  когда она находится в фокусе (активна).  На Рис.
        6.2 показано три графических изображения для командной кнопки Add
        Pen палитры пера.

        +------------------+  +------------------+ +------------------+
        |                  |# |          .   | |          .   |#
        |   @@      Add    |# |   @@    : Add :  | |   @@    : Add :  |#
        | @@@@@@    Pen    |# | @@@@@@  : Pen :  | | @@@@@@  : Pen :  |#
        |   @@             |# |   @@     .   | |   @@     .   |#
        |                  |# |                  | |                  |#
        +------------------+# +------------------+ +------------------+#
          ###################                        ###################

             Рис. 6.2  Графические  изображения  для   специализированной
        кнопки.

                      Нумерация ресурсов графических изображений
        -----------------------------------------------------------------

             Единственная сложная часть в определении графических изобра-
        жений для командных кнопок - это присваивание идентификаторов ре-
        сурсов. Управляющие элементы BWCC знают о том,  какое графическое
        изображение использовать,  основываясь на идентификаторе конкрет-
        ного управляющего элемента. Для командных кнопок в системах с VGA
        для ресурсов используется 1000 + идентификатор для "верхнего" об-
        раза,  3000 + идентификатор для "нижнего" образа и 5000 + иденти-
        фикатор для образа в фокусе.

                   Примечание: В системах с EGA  используются,  соответс-
              твенно,  ресурсы 2000 + идентификатор, 4000 + идентификатор
              и 6000 + идентификатор.

             Так как командная кнопка Add  Pen  имеет  идентификатор  101
        (id_Add), разрешение  использования  BWCC  принимает вид ресурсов
        1101, 3101 и 5101. В программе STEP11B.PAS, для доступа к специа-
        лизированными графическим изображениям,  для командных кнопок Add
        Pen и Del Pen, используется директива:

             {$R PENTAL.RES}


------------------------------------------------------------------------
         Шаг 12: Создание специализированного управляющего элемента окна
        -----------------------------------------------------------------

                            +-----------------------+
                            | Step 1: Basic App     |
                            | Step 2: Text          |
                            | Step 3: Lines         |
                            | Step 4: Menu          |
                            | Step 5: About Box     |
                            | Step 6: Pens          |
                            | Step 7: Painting      |
                            | Step 8: Streams       |
                            | Step 9: Printing      |
                            | Step 10: Palette      |
                            | Step 11: BWCC         |
                            |XStepX12:XCustomXctrlsX|
                            +-----------------------+

             Наиболее интересная часть создания палитры пера - это созда-
        ние вашего собственного специализированного окна  палитры.  Здесь
        вы можете,  наконец,  использовать  возможности,  предусмотренные
        стандартными инструментальными средствами окон, и что-то создать.

             На этом шаге вы сделаете следующее:

             * реализуете динамическое изменение размера окна палитры;

             * зададите реакцию на уведомляющие сообщения от  управляющих
               элементов;

             * создадите объект палитры с несколькими областями.

                       Динамическое изменение размеров палитры
        -----------------------------------------------------------------

             Так как каждое перо,  которое вы сохраняете в палитре, имеет
        один и  тот же размер (40 элементов изображения высотой и 128 ши-
        риной), вам нужно убедиться, что окно палитры может увеличиваться
        и сжиматься  на  этот размер каждый раз,  когда вы удаляете перо.
        Объект TPenPalette определяет два метода,  которые позволяют  это
        делать: Grow и Shrink.

             procedure TPenPalette.Grow
             var WindowRect: TRect;
             begin
               GetWindowRect(HWindow, WindowRect);
               with WindowRect do
                   MoveWindow(HWindow, left, top, right - left,
                      bottom - top + 40, True);
             end;

             procedure TPenPalette.Shrink;
             var WindowRect: TRect;
             begin
               GetWindowRect(HWindow, WindowRect);
               with WindowRect do
                   MoveWindow(HWindow, left, top, right - left,
                     bottom - top - 40, True);
             end;

             Оба метода находят координаты границ окна, модифицируют их и
        сообщают окну,  что нужно использовать новые  координаты  границ.
        Функция API  GetWindowRect возвращает структуру TRect, содержащую
        верхнюю, нижнюю, левую и правую координату. Grow добавляет в ниж-
        нюю область  окна 40 элементов изображения,  а Shink вычитает тот
        же объем.

             В следующем разделе вы узнаете,  как вызывать методы Grow  и
        Shrink в ответ на нажатие командных кнопок Add Pen и Del Pen.

                       Реакция на события управляющих элементов
        -----------------------------------------------------------------

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

             Пока командные кнопки выводятся в окне  палитры,  но  щелчок
        кнопкой "мыши"  не дает никакого эффекта.  Щелчок и выбор "мышью"
        являются событиями управляющего элемента. Они аналогичны событиям
        меню, на которые вы отвечали в шаге 4.

             Вы отвечали на события меню, определяя методы реакции на ко-
        манды. Что-то аналогичное нужно делать с сообщениями  управляющих
        элементов. События управляющих элементов  создают  сообщения  (на
        основе дочернего идентификатора), аналогичные командным сообщени-
        ям, но вместо идентификатора меню содержащие идентификатор управ-
        ляющего  элемента.  Для  идентификации заголовка метода на основе
        дочернего идентификатора  используйте сумму  идентификаторов  уп-
        равляющего элемента и констант id_First.

                   Примечание: Подробнее о командных сообщениях и уведом-
              ляющих  сообщениях  управляющих  элементов рассказывается в
              Главе 16 "Сообщения окон".

               Имена методов реакции на сообщения управляющих элементов
        -----------------------------------------------------------------

             Как и  в случае методов реакции на сообщения,  имена которым
        присваиваются по сообщениям, методы, основанные на дочерних иден-
        тификаторах, также  должны именоваться по идентификаторам сообще-
        ний. Так как две командные кнопки,  на которые вы хотите реагиро-
        вать, имеют идентификаторы id_Add и id_Del, TPenPalette нужны ме-
        тоды с именами IDAdd и IDDel.

             TPenPalette = object(TWindow)
               AddBtn, DelBtn: PBitButton;
               constructor Init(AParent: PWindowsObject; ATitle: PChar);
               procedure Grow;
               procedure SetupWindow; virtual;
               procedure Shrink;
               procedure IDAdd(var Msg: TMessage); virtual
                               id_First + id_Add;
               procedure IDDel(var Msg: TMessage); virtual
                               id_First + id_Del;
             end;

             Теперь для выполнения соответствующих действий  в  ответ  на
        командные кнопки осталось только определить методы IDAdd и IDDel.
        Пока что IDAdd должен просто вызывать увеличение окна,  а IDDel -
        его сжатие

             procedure TPenPalette.IDAdd(var Msg: TMessage);
             begin
               Grow;
             end;

             procedure TPenPalette.IDDel(var Msg: TMessage);
             begin
               Shrink;
             end;

                   Примечание: Это     дополняет     содержимое     файла
               STEP12A.PAS.


                             Добавление "кнопок" палитры
        -----------------------------------------------------------------

             Теперь, когда у вас есть окно палитры,  вам необходим просто
        способ вывода на экран и выбора перьев в палитре.  Для  этого  вы
        можете использовать  относительно простой потомок TWindow и набор
        объектов пера.

             В данном разделе вы сделаете следующее:

             * определите объект палитры пера;

             * выберете перья по щелчку кнопкой "мыши".


                             Определение объекта палитры
        -----------------------------------------------------------------

             Так как окно палитры пера может изменять свой размер, палит-
        ра в окне может фактически оставаться фиксированной.  Чтобы пока-
        зать только часть палитры, в которой отображаются перья, вы може-
        те использовать возможности отсечения Windows.

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

             Приведем описание объекта палитры:

             TPenPic = object(TWindow)
                PenSet: PCollection;
                CurrentPen: Integer;
                constructor Init(AParent: PWindowsObject);
                destructor Done: virtual;
                procedure Paint(PaintDC: HDC; var PaintInfo:
                                TPaintStruct); virtual;
                procedure AddPen(APen: PPen);
                procedure DeletePen;
                procedure SetupWindow; virtual;
                procedure WMLButtonDown(var Msg: TMessage);
                                virtual wm_First + wm_LButtonDown;
             private
               UpPic, DownPic: HBitMap;
             end;

             Объекту TPenPic не требуется очень много методов.  Он  имеет
        простой конструктор  для  создания набора перьев и деструктор для
        их уничтожения. Метод SetupWindow просто перемещает палитру внут-
        ри ее порождающего окна. AddPen и DeletePen включают перо в набор
        и удаляют перо из набора,  а WMLButtonDown интерпретирует  щелчки
        "мышью" для  выбора  перьев  из  палитры.  Наконец,  Paint рисует
        "кнопки", представляющие перья в наборе.

             Отметим также,  что TPenPic является потомком TWindow,  а не
        TControl. Хотя  поведение вашего нового объекта во многом напоми-
        нает поведение управляющего элемента окна,  он должен быть произ-
        водным от TWindow,  так как TControl работает только со стандарт-
        ными управляющими элементами,  такими как "нажимаемые"  командные
        кнопки и  полосы прокрутки.  При создании собственных управляющих
        элементов нужно начинать с TWindow.


                            Создание и уничтожение палитры
        -----------------------------------------------------------------

             Построение и  уничтожение объекта палитры выполняется доста-
        точно просто. Конструктор Init вызывает TWindow.Init, затем изме-
        няет стиль окна (чтобы оно стало видимым дочерним окном).  PenSet
        инициализируется как  набор  фиксированного  размера,  достаточно
        большой,  чтобы  содержать максимальное число заданных константой
        MaxPens перьев,  и не возрастающий.  Для текущего выбранного пера
        CurrentPen устанавливается в -1. Это означает, что выбранного пе-
        ра нет.

             Наконец, Init  загружает  в  UpPic и DownPic два графических
        образа. Они используются в качестве фона для каждого пера  палит-
        ры. DownPic рисуется за выбранным пером, а UpPic - в качестве фо-
        на других перьев.

                    ###################
                    #+------------------+    +------------------+
                    #|                  |    |                  |#
                    #|                  |    |                  |#
                    #|                  |    |                  |#
                     +------------------+    +------------------+#
                                               ###################

             Рис. 6.3 Фоновые образы палитры пера.

             Деструктор Done  перед вызовом наследуемого деструктора Done
        отменяет графические образы.  Вызов DeleteObject для  уничтожения
        графических  образов  имеет  важное  значение.  Подобно контексту
        дисплея, графические образы являются ресурсами Windows, поддержи-
        ваемыми в ограниченной памяти Windows. Если размещаете их в памя-
        ти Windows и не удаляете,  то ваша программа (и другие работающие
        параллельно с ней программы) потеряют доступ к этой памяти.

             Init и Done объекта палитры выглядят следующим образом:

             constructor TPenPic.Init(AParent: PWindowsObject);
             begin
               inherited Init(AParent, nil);
               AttrStyle := ws_Child or ws_Visible;
               PenSet := New(PCollection, Init(MaxPens, 0));
               CurrentPen := -1;
               UpPic := LoadBitMap(HInstance, 'PAL_UP');
               DownPic := LoadBitmap(HInstance, 'PAL_DOWN');
             end;

             destructor TPenPic.Done;
             begin
               DeleteObject(UpPic);
               DeleteObject(DownPic);
               Dispose(PenSet, Down);
               inherites Done;
             end;

                            Размещение в порождающем окне
        -----------------------------------------------------------------

             Как вы могли заметить,  TPenPic не  задает  свою  позицию  в
        конструкторе так,  как это делают большинство управляющих элемен-
        тов. Причина здесь в том,  что вы не можете полагаться на коорди-
        наты окна,  пока  оно не будет реально существовать.  TPenPalette
        обходит эту проблему,  создавая свои объекты кнопок путем  вызова
        для определения     высоты    заголовка    окна    функции    API
        GetSystemMetrics. Хотя это будет работать,  существует  и  другой
        подход.

             Вместо позиционирования палитры в конкретное место порождаю-
        щего окна вы можете поместить его в определенную позицию в порож-
        дающей  области клиента.  Таким образом,  если вы добавляете меню
        или изменяете рамку,  либо выполняете вне окна какое-либо  другое
        изменение, ваш  объект палитры все равно будет правильно позицио-
        нирован.

             Изменение позиции окна выполняется в  SetupWindow,  так  как
        окну палитры требуется использовать описатель своего порождающего
        окна, который до вызова собственного метода SetupWindow  недосту-
        пен. При достижении SetupWindow дочернее окно  может рассчитывать
        на допустимость описателя порождающего окна.

             procedure TPenPic.SetupWindow;
             var ClientRect: TRect;
             begin
               inherited SetupWindow;
               GetClientRect(Parent^.HWindow, ClientRect);
               with ClientRect do
                 MoveWindow(HWindow, 1, bottom - top + 1, 128,
                            40 * MaxPens, False;
             end;

             Для возврата координат области клиента  окна  палитры  метод
        TPicPen использует  функцию API Windows GwetClientRect.  Затем он
        перепозиционируется с помощью MoveWindow непосредственно под объ-
        екты кнопок,  задавая  высоту,  достаточную  для  размещения всех
        перьев в наборе. Заметим, что последний параметр MoveWindow - это
        значение типа Boolean,  указывающее, следует ли выполнять повтор-
        ное отображение окна после перемещения.  Так как палитра на экран
        пока не выводилась, то заново отображать ее не имеет смысла, поэ-
        тому TPenPic.SetupWindow передает значение False.


                             Добавление и удаление перьев
        -----------------------------------------------------------------

             В последнем разделе вы отвечали на сообщения от Add Pen (До-
        бавить перо) и Del Pen (Удалить перо) изменением размера окна па-
        литры. Теперь настало время изменить эту реакцию и фактически до-
        бавлять или удалять перья из палитры, что в свою очередь указыва-
        ет окну палитры на необходимость изменения размера. Вместо вызова
        собственных методов Grow и Shrink методы объекта палитры AddPen и
        DeletePen должны  вызывать  соответственно,  методы IDAdd и IDDel
        в TPenPalette.

             procedure TPenPalette.IDAdd(var Msg: TMessage);
             begin
               Pens^.AddPen(CommonPen);
             end;

             procedure TPenPalette.IDDel(var Msg: TMessage);
             begin
               Pens^.DeletePen;
             end;

             Метод AddPen воспринимает передаваемое перо,  копирует его в
        набор и отмечает перо,  как текущее выбранное. Затем он разрешает
        кнопку Del Pen в окне палитры пера, запрещает кнопку Add Pen, ес-
        ли набор полон, и сообщает порождающему окну о необходимости уве-
        личения размера, чтобы поместить новое перо.

             procedure TPenPic.AddPen(APen: PPen);
             begin
               CurrentPen := PenSet^.Count;
               with APen^ do PenSet^.Insert(New(PPen, Init(Style, With,
                                            Color)));
               with PPenPalette(Parent)^ do
               begin
                 DelBtn^.Enable;
                 if PenSet^.Count >= MaxPens tnen
                    AddBtn^.Disable;
                 Grow;
               end;
             end;

                   Примечание: Чтобы использовать  преимущества  средств,
              специфических для TPenPAlette, TPenPic может выполнять при-
              ведение типа поля Parent.  Большинство оконных объектов  не
              связаны так жестко с конкретным порождающим типом, и поэто-
              му не должны делать никаких предположений относительно типа
              порождающих их окон.

             Метод DeletePen  по существу изменяет действия AddPen на об-
        ратные. При наличии выбранного в палитре пера  оно  удаляется  из
        набора, а  набор уплотняется таким образом,  чтобы перья размеща-
        лись непрерывно.  Затем он указывает, что в данный момент выбран-
        ных перьев  нет (поскольку  выбранное  перо только что удалено) и
        запрещает командную кнопку Del Pen,  так как Del Pen  работает  с
        выбранным пером.  Далее  он  разрешает кнопку Add Pen,  поскольку
        удаление пера автоматически освобождает место для по крайней мере
        еще одного пера.  Наконец, он сообщает порождающему окну на необ-
        ходимость уменьшения его размера (так выводить теперь нужно мень-
        ше перьев).

             procedure TPenPic.DeletePen;
             begin
               if CurrentPen > -1 then
               begin
                 PenSet^.AtFree(CurrentPen);
                 PenSet^.Pack;
                 CurrentPen := -1;
                 with PPenPelette(Parent)^ do
                 begin
                   AddBtn^.Enable;
                   DelBtn^.Disable;
                   Shrink;
                 end;
               end;
             end;

             Заметим, что AddPen и DeletePen используют преимущества того
        факта, что окна палитры пера для упрощения связи с методами имеет
        указатели на  свои командные кнопки.  Если бы TPenPalette не имел
        полей AddBtn и DelBtn,  то объекту палитры пришлось бы искать  их
        по идентификаторам  и  посылать им сообщения,  либо нужно было бы
        послать сообщение порождающему окну, которое в свою очередь долж-
        но каким-то образом связываться с командными кнопками.


                           Отображение содержимого палитры
        -----------------------------------------------------------------

             За все,  что мы насоздавали в  объекте  палитры,  приходится
        расплачиваться, когда приходит  момент  отображать  ее на экране.
        Поскольку перья хранятся в наборе,  для отображения каждого  пера
        вы легко  можете  выполнять итерацию.  В самом деле,  метод Paint
        состоит только из инициализации локальной переменной-счетчика,  а
        затем выполняет итерацию по набору с помощью ForEach:

             procedure TPenPic.Paint(PaintDC: HDC;
                                     var PaintInfo: TPaintStruct);
             var PenCount: Integer;

                procedure ShowPen(P: PPen); far;
                var
                  MemDC: HDC;
                  TBitmap: HBitmap;
                begin
                  MemDC := CreateCompatibleDC(PaintDC);
                  Inc(PenCount);
                  if PenCount = CurrentPen then
                       TheBitmap = DownPic;
                  else TheBitmap := UpPic;
                  SelectObject(MemDC, TheBitmap);
                  BitBlt(PaintDC, 0, PenCount * 40, 128, 40, MemDC, 0,
                         0, SrcCopy);
                  P^.Select(PaintDC);
                  MoveTo(PaintDC, 15, PenCount * 40 + 20);
                  LineTo(PaintDC, 115, PenCount * 40 + 20);
                  P^.Delete;
                  DeleteDC(MemDC);
                end;

             begin
               PenCount := -1;
               PenSet^.ForEach(@ShowPen);
             end;

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

             Изображение графических  образов  предусматривает  три шага:
        создание контекста устройства памяти,  выбор в контексте устройс-
        тва графического образа и копирование образа в контекст экрана.

             Как вы  видели  в шаге 8,  для различных видов устройств су-
        ществует различные контексты устройства. Для работы с графически-
        ми образами (битовыми массивами) Windows позволяет создавать кон-
        текст устройства памяти.  Фактически,  вы можете только  выбирать
        битовые массивы в контексте устройства памяти, хотя они могут ко-
        пироваться в другие контексты устройства.

             Метод CreateMemoryDC создает пустой контекст устройства  па-
        мяти, совместимый с текущим PaintDC.  Затем, в зависимости от то-
        го, является ли данное конкретное перо выбранным, ShowPen выбира-
        ет в  контексте  устройства графические образы UpPic или DownPic.
        Заметим, что в  контексте  устройства  памяти  графический  образ
        интерпретируется аналогично   любому   другому   изобразительному
        средству.  Наконец,  функция  BitBlt копирует заданную часть кон-
        текста устройства памяти в PaintDC.  Заметим,  что контекст  уст-
        ройства памяти требуется уничтожать.

             После того как фон будет на месте, ShowPen использует ShowTo
        и LineTo  аналогично тому,  как это делается при рисовании непос-
        редственно в окне.

                            Выбор перьев с помощью "мыши"
        -----------------------------------------------------------------

             Наконец. TPenPic обеспечивает реакцию на щелчок в нарисован-
        ной области кнопкой "мыши".  Заметим, что хотя размер объекта па-
        литры  никогда  не  изменяется,  он получает сообщение от щелчков
        "мышью" в области, фактически показываемой на экране. Палитра от-
        секается рамкой окна палитры. Это означает, что вы можете щелкать
        кнопкой "мыши" только непосредственно в палитре.

             Поскольку каждый элемент в палитре имеет один и тот же  раз-
        мер, WMLButton  для  определения того, в каком графическом образе
        была нажата кнопка "мыши",  может просто  разделить  y-координату
        щелчка "мыши" (которая поступает в LParamHi) на 40 (размер каждо-
        го графического изображения).  Затем он делает перо,  на  котором
        была нажата  кнопка  "мыши"  текущим и задает в качестве пера для
        рисования копию выбранного пера палитры.  Поскольку  теперь  есть
        выбранное перо, он разрешает кнопку Del Pen в окне палитры, затем
        запрещает палитру для обеспечения ее повторного  отображения  для
        того, чтобы показать новый выбор.

             Код для  WMLButtonDown  имеет  следующий  вид (это дополняет
        текст STEP12B.PAS):

             procedure TPenPic.WMLButtonDwon(var Msg: TMessage);
             begin
               CurrentPen := Msg.LParamHi div 40;
               if CurrentPen <> nil then Dispose(CurrentPen, Done);
               with PPen(PenSet^.At(CurrentPen))^ do
                   CurrentPen := New(PPen, Init(Style, With, Color));
               PPenPalette(Parent)^.DelBlt^.Enable;
               InvalidateRect(HWindow, nil, False);
             end;

                                     Что дальше? 
        -----------------------------------------------------------------

             Есть много дополнений и изменений,  которые вы можете внести
        в программу  Steps,  чтобы  сделать  ее  более  полезной.  Версию
        программы Steps, которая включает в себя эти изменения, вы можете
        найти в файле GRAFFITI.PAS.

             Программа Graffiti содержит следующие изменения:

             * Многодокументальный интерфейс (MDI).

             * Сглаживание линий.

             * Отмена.

             * Улучшает поведение окна палитры.

             * Прокрутка.

                            Многодокументальный интерфейс
        -----------------------------------------------------------------

             TStepWindow может  очень легко работать в качестве дочернего
        окна MDI.  Фактически с небольшими изменениями программа Graffiti
        использует в качестве своих дочерних окон те же окна,  что  Steps
        использует  для  основного окна.  Так как владельцем окна палитры
        пера является объект TStepWindow,  каждый отдельный рисунок имеет
        свой собственный отличный набор перьев. О многодокументальном ин-
        терфейсе рассказывается в Главе 14 "Объекты MDI").

                                  Сглаживание линий
        -----------------------------------------------------------------

             Для сложных рисунков со многими длинными  линиями  повторное
        отображение рисунка может потребовать много времени. Graffiti ис-
        пользует одно из преимуществ графический функций  GDI  -  функцию
        PolyLine, которая рисует сегменты каждой линии сразу, а не по од-
        ному. Эффектом будет более быстрое и плавное отображение окна.

                                        Отмена
        -----------------------------------------------------------------

             Поскольку рисунок  состоит  из  наборов линий,  для стирания
        последней нарисованной линии легко использовать  методы  наборов.
        Graffiti связывает элемент меню Edit|Undo (Редактирование|Отмена)
        с набором линий метода AtDelete, удаляя последнюю линию в наборе,
        которая является  также  последней нарисованной линией.  Повторяя
        удаление последней линии в наборе,  вы можно эффективно  отменять
        все изображение.

             Хотя программа Graffiti этого не делает,  вы можете добавить
        также функцию Redo (Возобновление),  сохраняя удаленные  линии  в
        другом наборе,  и  перемещая их по одной в набор отображаемых ли-
        ний, либо даже в другой рисунок.


                                  Поведение палитры
        -----------------------------------------------------------------

             Вы можете  также отметить,  что при щелчке "мышью" между па-
        литрой и основным окном, окно, где вы нажимаете кнопку, становит-
        ся активным  (получает активную рамку),  а другие окна становятся
        неактивными. Если вы часто перемещаетесь между двумя окнами  (что
        может иметь  место  при работе с палитрой),  это может показаться
        весьма раздражающим.  Чтобы предотвратить это явление,  вам нужно
        перехватывать  передачу  в окна сообщений sm_NCActivate,  и когда
        параметр WParam сообщений равен 0 (попытка деактивизации  рамки),
        вы можете изменить его на 1 (активизация рамки):

             procedure TPenPalette.WVNCActivate(var Msg: TMessage);
             begin
               if Msg.WParam = 0 then Msg.WParam := 1;
               DefWndProc(Msg);
             end;

             Вызов DefWndProc обеспечивает,  что сообщение обрабатывается
        как обычно,  но теперь рамка палитры деактивизироваться не будет.
        Аналогичный перехват вы можете добавить в TStepWindow.

                                      Прокрутка
        -----------------------------------------------------------------

             Наконец, так как каждое дочернее окно MDI обычно невелико по
        размеру, Graffiti  добавляет возможность автоматической прокрутки
        изображения при рисовании.  В программе Steps, когда перо выходит
        за границы окна,  линия продолжает рисоваться,  хотя видеть ее вы
        больше не можете.  Graffiti прокручивает изображение,  так что вы
        будете видеть другие части рисунка.  
                              Назад | Содержание | Вперед

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...