Декорированные окна

В библиотеке OWL предусмотрены средства для создания в окне приложения специальных приспо­соблений - панели, или линейки инструментов, инструментального планшета и линейки, или строки со­стояния (статусной строки). Окно с такого рода приспособлениями принадлежит классу TDecoratedWin-

dow и называется декорированным окном. Как видно из рис. 29.4, этот класс является производным как от класса окон с рамками TFrameWindow, так и от класса TLayoutWindow, обеспечивающего поведение окон, размеры и положение ко­торых взаимозависимы. С помощью класса TLayoutWindow можно, например, создать дочернее окно, которое будет пропорционально изменять свои размеры при изменении размеров родительского окна, или располагаться всегда на определенном расстоянии от его границ; можно задать жест­кую связь между правой границей одного дочернего окна и левой границей другого, что как бы "приклеивает" их друг к другу. В рассматриваемом далее примере эти возможности пригодятся для создания окна-клиента, совпадающего по размерам с главным окном приложения.

Класс TDecoratedFrame, включая в себя все возможности классов TFrameWindow и TLayoutWindow, добавляет к ним еще и средства размещения в окне разного рода приспособлений с заданными характе­ристиками поведения.

Сами приспособления выделены в библиотеке OWL в отдельную группу классов, являющихся про­изводными от базового класса TGadget (рис. 29.5). В рассматриваемом далее примере будут использова­ны классы TButtonGadget и TSeparatorGadget.




Наконец, такие инструменты, как инструментальная линейка, инструментальный планшет или строка состояния образуются с помощью группы классов, производных от базового для них класса TGadget-Window (который сам выводится из класса TWindow, рис. 29.6). Мы приведем примеры использования

классов TToolBox, TMessageBar и TControlBar. Рассмотренный ниже пример 29-3 прило­жения с декорированным окном является пере­работкой примера 28-4, в котором в главное окно приложения выводилось графическое отображение некоторого массива данных, при­чем форму представления графика можно было изменять с помощью немодального диалогово­го окна. В примере 29-3. для изменения формы представления данных, а также цвета графика вместо диалога с кнопками настройки исполь­зуется классический набор инструментов деко­рированного окна (рис. 29.7).



Окна и их оформление ' 277

Для создания декорированного окна с достаточно полными функциональными возможностями необ­ходимо выполнить следующие шаги:

• Описать класс главного окна (в примере 29-3 — MyWindow), который должен быть производным не
от класса TFrameWindow, как обычно, а от класса TDecoratedWindow

• Описать класс окна-клиента (в примере - MyClient), производный от базового оконного класса
TWindow. Именно окно-клиент будет выступать в дальнейшем в качестве рабочего окна приложения, и
именно для него следует описать таблицу откликов и функцию Paint()

• Привязать окно-клиент к главному окну приложения, что выполняется путем включения в конст­
руктор главного окна указателя на окно-клиент

• В программе конструктора главного (декорированного) окна предусмотреть заполнение его необ­
ходимыми приспособлениями

• Побеспокоиться заранее о разработке файлов.bmp с рисунками для кнопок инструментального
планшета и линейки инструментов и включить ссылки на них в файл ресурсов приложения. В файле ре­
сурсов должна также содержаться таблица строк (ресурс STRINGTABLE), выводимых в линейку состоя­
ния.

Рассмотрим теперь текст приложения 29-3.

//Приложение 29-3. Декорированные окна

//Файл 29-3.h

#define Dlg 100

#define CM_HELP 202

#define CM_EXIT 24310

#define CM_POINTS 101

#define CM_CURVE 102

#define CM_HISTO 103

#define CM_BLUE 104

#define CM_GREEN 105

#define FILESIZE 20

#define IDB_POINTS 301

#define IDB_CURVE 302

#define IDB_HISTO 303

#define IDB_HELP 304

#define 1DB_BLUE 305

#define IDB_GREEN 306

//Файл 29-3.rc #i nclude "29-3.h" MainMenu MENU{ POPUP "Файл"{

MENUITEM "Справка",CM_HELP

MENUITEM SEPARATOR

MENUITEM "Выход",CM_EXIT

} POPUP "Вид"{

MENUITEM "Точки",CM_POINTS

MENUITEM "Огибающая",CM_CURVE

MENUITEM "Гистограмма", CM_HISTO

} }

Dlg DIALOG 82, 70, 116, 84

STYLE WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_SYSMENU CLASS "BorDlg"


278 Глава 29

CAPTION "Справка"

FONT 8, "MS Sans Serif"{

CTEXT "В программе демонстрируется организация декорированных окон",-1,8,7,100,36

CONTROL "",IDOK,"BorBtn",BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE|WS_TABSTOP,40,49,37,25

}

IDB_POINTS BITMAP "29-3pt.bmp" IDB_CURVE BITMAP "29-3cur.bmp" IDB_HISTO BITMAP "29-3hist.bmp" IDB_HELP BITMAP "29-3hlp.bmp" IDB_BLUE BITMAP "29-3blu.bmp" IDB_GREEN BITMAP "29-3gr.bmp" STRINGTABLE{

CM_HELP "Вывод справки"

CM_EXIT "Завершение приложения"

CM_POINTS "График в виде точек"

CM_CURVE "График в виде кривой"

CM_HISTO "График в виде гистограммы"

CM_BLUE "График синего цвета"

CM_GREEN "График зеленого цвета"

}

//Файл 29-3.срр

#include <owl\framewin.h>

#include <owl\decframe.h>

#include <owl\dialog.h>

#include <owl\controlb.h>

#include <owl\toolbox.h>

#include <owl\statusba.h>

#include <owl\buttonga.h>

#include <owl\messageb.h>

#include "29-3.h"

#include <stdio.h>

enum CHOICE{POINTS=CM_POINTS,CURVE=CM_CURVE,HISTO=CM_HISTO}view;

enum COLOR{BLUE=CM_BLUE,GREEN=CM_GREEN}color;

int data[FILESIZE];//Массив для данных из файла

/*Класс приложения, производный от Tapplication*/

class MyApp:public TApplication{

public:

void InitMainWindow() ;//Замещаем функцию InitMainWindow

};

/*Класс главного окна, производный от TdecoratedFrameWlndow*/ class MyWindow:public TDecoratedFrame{

TControlBar* toolBar;

TToolBox* toolBox;

TMessageBar* statusLine; public:

MyWindow(TWindow*,char far*,TWindow*);

void GetWindowClass(WNDCLASS&); //Замещаем функцию GetWindowClass

};

/*Класс окна-клиента, производный от Twindow*/ class MyClient:public TWindow{ public:

MyClient():TWindow(0,""){};

void Paint(TDC&,bool,TRect&) ;//Замещаем функцию Paint

void CmHelp(); //Набор функций откликов на сообщения от пунктов меню

void CmExit(); //Они же используются как функции отклика

void CmPoints{);// на сообщения от кнопок инструментального планшета

void CmCurve();// и линейки инструментов

void CinHisto();

void CmBlue();

void CmGreen();

DECLARE_RESPONSE_TABLE(MyClient);// Объявляем таблицу отклика для окна-клиента

};

/*Таблица откликов класса MyClient*/ DEFINE_RESPONSE_TABLE1(MyClient,TWindow).

EV_COMMAND(CM_HELP,CmHelp),

EV_COMMAND(CM_POINTS,CmPoints),

EV_COMMAND(CM_CURVE,CmCurve),

EV_COMMAND(CM_HISTO,CmHisto),

EV_COMMAND(CM_BLUE,CmBlue),

EV_COMMAND(CM_GREEN,CmGreen), END_RESPONSE_TABLE;


Окна и их оформление 279

/*Конструктор главного окна*/

MyWindow::MyWindow(TWindow* parent,char far* title.TWindow* client):

TDecoratedFrame(parent,title,client,TRUE){ AssignMenu("MainMenu"); //Назначаем главное меню

view=POINTS; //Начальные значения переключателей-флагов вида графика соlor=BLUE//и цвета изображения

memset(data,0,sizeof(data));// Обнуляем массив данных toolBar=new TControlBar(this);

toolBar->Insert(*new TButtonGadget(IDB_POINTS,CM_POINTS)); toolBar->Insert(*new TButtonGadget(IDB_CURVE,CM_CURVE)); toolBar->Insert(*new TButtonGadget(IDB_HISTO,CM_HISTO)); toolBar->Insert(*new TSeparatorGadget(6)); toolBar->Insert(*new TButtonGadget(IDB_HELP,CM_HELP));

Insert (*toolBar, TDecoratedFrame:: Top);

toolBar->SetHintMode(TGadgetWindow::EnterHints);

toolBox=new TToolBox(this,1);

toolBox->Insert(*new TButtonGadget(IDB_BLUE,CM_BLUE)); toolBox->Insert(*new TButtonGadget(IDB_GREEN,CM_GREEN)); Insert (*toolBox,TDecoratedFrame::Left); statusLine=new TMessageBar(this); Insert(*statusLine,TDecoratedFrame::Bottom);

/*Функция отклика окна-клиента*/ void MyClient::CmHelp(){

new TDialog(this,Dig)->Execute();// Открываем модальный диалог } void MyClient::CmPoints(){

view=POINTS; //Устанавливаем значение флажка-переключателя Invalidate(); //Инициируем перерисовывание рабочего окна }

void MyClient::CmCurve(){ view=CURVE; Invalidate(); }

void MyClient::CmHisto(){ view=HISTO; Invalidate(); }

void MyClient::CmBlue(){

color=BLUE; Invalidate(); } void MyClient::CmGreen(){

color=GREEN;

Invalidate(); }

/*Функция Paint() класса MyClient*/ void MyClient::Paint(TDC&dc,bool,TRect&){ if(color==BLUE){

TPen myPen(TColor::LtBlue, 1); dc.SelectObject(myPen); TBrush myBrush(TColor::LtBlue); dc.SelectObject(myBrush); } else if(color==GREEN){

TPen myPen(TColor::LtGreen,1); dc.SelectObject(myPen); TBrush myBrush(TColor::LtGreen); dc.SelectObject(myBrush); }

TRect wndRect=GetClientRect() ;//Получим рабочую область рабочего окна int i;

switch(view){ case(POINTS): for(i=0;i<FILESIZE;i++)

dc.Ellipse(i*10+10-2,wndRect.bottom-data[i]-2,1*104-10+2,

wndRect.bottom-data[i]+2); break; case(CURVE):

dc.MoveTo(10,wndRect.bottom-data[0]); for(i=l;i<FILESIZE;i++)


280 Глава 29

dc.LineTo(i*10+10,wndRect.bottom-data[i]); break;

case(HISTO): for(i=0;i<FILESIZE;i++)

dc.Rectangle(i*10+10,wndReqt.bottom-data[i],i*10-4-10+9,wndRect.bottom); break; } }

/*Замещающая функция GetWindowClass*/ void MyWindow::GetWindowClass(WNDCLASS& wc){ TWindow::GetWindowClass(wc);

wc.style=CS_VREDRAW; //Необходимо, т.к. график рисуется снизу }

/*Замещающая функция InitMainWindow()*/ void MyApp::InitMainWindow(){ EnableBWCC();

SetMainWindow(new MyWindow(0,"Программа 29-3",new MyClient)); FILE* fp=fopen("28-4.dat","r"); //Открываем файл для чтения for(int i=0;i<FILESIZE;i++)

fscanf(fp,"%d",&data[i]); //Читаем данные в массив data fclose(fp); //Закрываем файл }

/*Главная функция приложения OwlMain*/ int OwlMain(int,char*[]){

return MyApp().Run(); }

В файле 29-3.h, как и обычно, описываются константы - идентификаторы пунктов меню (начинаю­щиеся у нас с префикса СМ_), которые будут одновременно использоваться и как идентификаторы соот­ветствующих инструментальных кнопок, а также идентификаторы ресурсов BITMAP (с префиксом IDB_).

Файл 29-3.rc состоит в данном случае из четырех частей, в которых описывается форма главного ме­ню приложения, форма модального диалогового окна с информацией о программе, список ресурсов BITMAP с указанием имен.bmp-файлов с изображениями для кнопок и, наконец, ресурс STRINGTABLE, представляющий собой перечень строк, которые будут выводиться в линейку состояния, с указанием в качестве их номеров идентификаторов соответствующих пунктов меню (и инструментальных кнопок).

В файле 29-З.срр после довольно длинного перечня включаемых файлов вводятся описания двух пе­речислимых типов, которые будут в дальнейшем использоваться в качестве переключателей-флагов ре­жимов отображения: CHOICE для определения вида графика и COLOR для задания его цвета. В качестве допустимых значений переменных этих типов использованы идентификаторы пунктов меню. Тут же объявлены и конкретные переменные перечислимых типов: view и color.

В класс главного окна MyWindow, производный от TDecoratedFrame, включены указатели на объек­ты типа TControlBar (инструментальная линейка), TToolBox (инструментальный планшет) и TMessage-Ваг (линейка состояния). В классе MyWindow замещена функция GetWindowClass(), что дает возмож­ность добавить в стиль класса бит CS_VREDRAW. Обратите внимание на формат конструктора главного окна

MyWindow(TWindow*,char far*,TWindow*);

отличающийся от много раз использованного нами конструктора окна с рамкой

MyWindow(TWindow*,char far*);

В рассматриваемом примере класс MyWindow является производным от класса TDecoratedWindow, в конструкторе которого третий параметр отводится под указатель (типа TWindow*) на окно-клиент. При конструировании объекта главного окна (в функции InitMainWindow()) мы передадим через третий па­раметр этого конструктора указатель тут же и создаваемого объекта окна-клиента:

SetMainWindow(new MyWindow(0,"Программа 29-3",new MyClient));

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

MyClient():TWindow(0,""){}

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

TWindow(HWND hWnd, TModule* module = 0);

TWindow(TWindow* parent, const char far* title = 0, TModule* module = 0);


Окна и их оформление 281

Первый конструктор используется как псевдоним для не OWL-окна и нас интересовать не будет. Второй конструктор требует трех параметров, из которых два назначаются по умолчанию и, вроде бы, могут опускаться. Однако мы не можем объявить конструктор класса MyClient в единственным парамет­ром

MyClient():TWindow(0){}

потому что в этом случае компилятор не сможет определить, каким из двух перегруженных конструкто­ров базового класса мы хотим воспользоваться. По той же причине нельзя объявить наш конструктор в форме

MyClient():TWindow(0,0){}

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

MyClient():TWindow(0,0,0){} MyClient():TWindow(0,""){}

Нами выбран второй из этих вариантов.

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

Обратимся теперь к конструктору MyWindow, в котором, собственно, и задается требуемый вид де­корированного окна. После прикрепления главного меню, задания начальных значений для флагов-переключателей view и color и обнуления массива данных создается (пока пустой) объект инструмен­тальной панели класса TControlBar. Далее этот объект начинает заполняться конкретными приспособле­ниями, для чего используется открытая виртуальная функция базового для инструментов класса TGadgetWindow Insert(), вызываемая для объекта класса TControlBar. Кнопки инструментальной панели создаются, как объекты класса TButtonGadget, в конструкторе которого указывается идентификатор ре­сурса - растрового изображения для кнопки, а также идентификатор самой кнопки (в действительности конструктор класса TButtonGadget имеет большее число параметров, но для остальных в нашем случае можно использовать значения по умолчанию). Создаваемые кнопки будут располагаться друг за другом вплотную. Для отделения одной или нескольких кнопок используется объект класса TSeparatorGadget, в качестве параметра которого указывается расстояние между кнопками (в единицах толщины рамки ок­на). По умолчанию это расстояние равно 6 единицам. Обратите внимание на вид параметра функции In-sert(): она требует в качестве параметра не указатель на объект, а сам объект, который мы получаем с по­мощью снятия ссылки с указателя.

Определив состав инструментальной линейки, ее следует включить в состав декорированного окна вызовом функции Insert() класса TDecoratedFrame. Поскольку обращение к этой функции осуществляется в другой функции-члене того же класса (конкретно - в конструкторе), ее можно (и нужно) вызывать без указания объекта. В качестве параметров этой функции указываются объект инструментальной линейки и место ее расположения. Последний параметр является перечислимым типом, описанным в классе TDe­coratedFrame, что и указано в явной форме. Параметр может принимать естественные значения Top, Bot­tom Left и Right.

Завершающим действием по созданию инструментальной линейки является включение механизма вывода в линейку состояния поясняющих надписей из файла ресурсов. Этот механизм активизируется функцией SetHintMode() с константой EnterHints, если поясняющие надписи должны появляться при прохождении курсора мыши над кнопкой инструментальной линейки, или с константой PressHints, если надписи должны появляться только при нажатии соответствующей кнопки инструментальной линейки.

Аналогично инструментальной линейке создается, заполняется кнопками и включается в состав де­корированного окна инструментальный планшет (класс TTooIBox). При создании объекта инструмен­тального планшета указывается число линеек в нем (у нас - 1). Большее число линеек используется для планшетов с большим числом кнопок. Мы расположили планшет с левой стороны окна.

Наконец, в нижней части окна создается объект линейки состояния (класс TMessageBar). Линейку состояния с большими изобразительными возможностями можно было создать на базе класса TStatusBar.

Функции откликов окна-клиента и функция Paint() того же окна вряд ли нуждаются в особых ком­ментариях. Операция открытия файла (с фиксированным именем) помещена в текст замещающей функ­ции InitMainWindow().

Последнее замечание относительно рисунков на кнопках. Для их подготовки использовалась про­грамма Resource Workshop, которая удобна тем, что позволяет задать определенный размер рисунка. В данном примере для кнопок инструментальной линейки (в верхней части окна приложения) использова­лись 16-цветные рисунки размером 20x20 пикселов; для кнопок планшета (в левой части окна) были для


282 Глава 29

разнообразия изготовлены рисунки меньшего (16x16 пикселов) размера. Это хорошо видно на рис. 29.8, где приведен вид окна приложения при выводе графика в виде точек и при активизированном модальном диалоге (пункт меню "Справка" или кнопка с вопросительным знаком).


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: