Расположение окон черепицей

Задача. В правом нижнем углу рабочей области окна приложения при первом нажатии левой клавиши мыши отобразить окно с заголовком "1-е окно", при втором нажатии – окно с заголовком "2-е окно" и так до пяти окон. Начиная со второго, окна располагать под предыдущим, сдвигая вверх и влево так, чтобы был виден заголовок, а левый верхний угол 5-го окна должен совпадать с левым верхним углом рабочей области окна приложения. Если какие-либо из этих окон закрыты, то при нажатии левой клавиши мыши должно быть создано первое из закрытых окон.

Анализ задания. (Полный текст приложения приведен ниже, в листинге 1.3.)

Регистрируем класс окна приложения:

if (!RegClass(WndProc, szMainClass, COLOR_WINDOW))

return FALSE;

Фон окна приложения в задаче не указан, поэтому по табл. 1.4 выбираем стандартный цвет COLOR_WINDOW.

Регистрируем класс временных окон:

if (!RegClass(WndPopup, szPopupClass, COLOR_BTNFACE))

return FALSE;

Для разнообразия цветом фона временных окон выбираем цвет трехмерных элементов.

Создаем окно приложения:

hwnd = CreateWindow(szMainClass, szTitle,

WS_OVERLAPPEDWINDOW | WS_VISIBLE,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

0, 0, hInstance, NULL);

Для него указываем стиль WS_OVERLAPPEDWINDOW | WS_VISIBLE. Координаты окна задаем по умолчанию.

Далее активизируем цикл обработки сообщений:

while(GetMessage(&msg, 0, 0, 0)) DispatchMessage(&msg);

Сообщения из этого цикла попадают в две различные функции окон. Функция окна приложения WndProc теперь содержит описания, которые решают поставленную задачу. А функция временных окон все сообщения возвращает операционной системе. Даже сообщение о закрытии временного окна обрабатывает операционная система.

Все действия по созданию, отображению и расположению временных окон выполняет функция окна приложения при обработке кодов сообщений WM_MOVE, WM_SIZE и WM_LBUTTONDOWN.

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

left=LOWORD(lParam);

top=HIWORD(lParam);

Остается переместить существующие временные окна:

for (short j=0; j<5; j++)

if (IsWindow(hwnds[j]))

MoveWindow(hwnds[j], left+cxClient-Width-xStep*j, top+cyClient-Height-yStep*j, Width, Height, TRUE);

Координата левого края j-го временного окна отсчитывается от правого края рабочей области (left+cxClient) и равна величине: left+cxClient-Width-xStep*j.

Аналогично отсчитывается координата верхнего края j-го временного окна.

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

cxClient=LOWORD(lParam);

cyClient=HIWORD(lParam);

Тогда ширину и высоту временных окон можно вычислить по выражениям Width = cxClient/2, Height = cyClient-4*yStep.

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

xStep=(cxClient-Width)/4;

Остается только переместить существующие временные окна.

Эта часть обработки кода WM_SIZE полностью совпадает с такой же частью обработки кода WM_MOVE:

for (short j=0; j<5; j++)

if (IsWindow(hwnds[j]))

MoveWindow(hwnds[j], left+cxClient-Width-xStep*j, top+cyClient-Height-yStep*j, Width, Height, TRUE);

Здесь перебирают все элементы массива дескрипторов hwnds, и, если для j-го элемента массива (дескриптора hwnds[j]) существует окно, это окно перемещается в новые координаты.

При обработке кода WM_LBUTTONDOWN (при нажатии левой клавиши мыши) перебирают элементы массива дескрипторов hwnds до тех пор, пока не обнаруживается, что для j-го элемента массива (дескриптора hwnds[j]) не существует окна:

for (short j=0; j<5, IsWindow(hwnds[j]); j++);

Если для всех дескрипторов массива существуют окна, обработка сообщения завершается. Иначе для этого дескриптора в определенном условиями задачи месте создается окно:

hwnds[j] = CreateWindow(szPopupClass, str,

WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE,

left+cxClient-Width-xStep*j,

top+cyClient-Height-yStep*j,

Width, Height, hwnd, 0, hInstance, NULL);

Следующее действие связано с изменением расположения только что созданного окна без перемещения:

if(j>0)

{

SetWindowPos(hwnds[j], hwnds[j-1], 0, 0, Width, Height, SWP_NOMOVE);

SetForegroundWindow(hwnds[0]);

}

To есть функция SetWindowPos располагает окно hwnds[j] под предыдущим окном hwnds[j-1]. А функция SetForegroundWindow выдвигает на передний план первое окно из массива дескрипторов. Таким образом, при создании нового временного окна на переднем плане всегда окажется первое из них.

Этим завершается обработка кода WM_LBUTTONDOWN.

Листинг 1.3. Пример расположения окон черепицей в обратном порядке.

#include <windows.h>

#include <tchar.h>

//Объявление функций

BOOL RegClass(WNDPROC, LPCTSTR, UINT);

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK WndPopup(HWND, UINT, WPARAM, LPARAM);

HINSTANCE hInstance;

TCHAR szMainClass[] = TEXT("MainClass");

TCHAR szPopupClass[] = TEXT("PopupClass");

TCHAR szTitle[] = TEXT("Пример 1.3");

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)

{

MSG msg;

HWND hwnd;

hInstance = hInst;

if (!RegClass(WndProc, szMainClass, COLOR_WINDOW))

return FALSE;

if (!RegClass(WndPopup, szPopupClass, COLOR_BTNFACE))

return FALSE;

hwnd = CreateWindow(szMainClass, szTitle, WS_OVERLAPPEDWINDOW | WS_VISIBLE,

CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

0, 0, hInstance, NULL);

if (!hwnd) return FALSE;

while(GetMessage(&msg, 0, 0,0))

DispatchMessage(&msg);

return msg.wParam;

}

BOOL RegClass(WNDPROC Proc, LPCTSTR szName, UINT brBackground)

{

WNDCLASS wc;

wc.style = wc.cbClsExtra = wc.cbWndExtra = 0;

wc.lpfnWndProc = Proc;

wc.hInstance = hInstance;

wc.hIcon=LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(brBackground +1);

wc.lpszMenuName = (LPCTSTR)NULL;

wc.lpszClassName = szName;

return (RegisterClass(&wc)!= 0);

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

static short cxClient, cyClient, yStep, xStep;

static short left, top, Width, Height;

//Описываем массив дескрипторов окон

static HWND hwnds[5];

switch (msg)

{

case WM_CREATE:

{

//Шаг смещения окон no вертикали

yStep = GetSystemMetrics(SM_CYCAPTION);

return 0;

}

case WM_MOVE:

{

//Левый верхний угол рабочей области

left=LOWORD(lParam); top=HIWORD(lParam);

//Перемещаем существующие временные окна

for (short j=0; j<5; j++)

if (IsWindow(hwnds[j]))

MoveWindow(hwnds[j], left+cxClient-Width-xStep*j, top+cyClient-Height-yStep*j, Width,Height,TRUE);

return 0;

}

case WM_SIZE:

{

//Ширина и высота рабочей области

cxClient=LOWORD(lParam); cyClient=HIWORD(lParam);

//Ширина и высота временных окон

Width=cxClient/2; Height=cyClient-4*yStep;

//Шаг смещения окон по горизонтали

xStep=(cxClient-Width)/4;

//Перемещаем существующие временные окна

for (short j=0;j<5; j++)

if (IsWindow(hwnds[j]))

MoveWindow(hwnds[j], left+cxClient-Width-xStep*j, top+cyClient-Height-yStep*j, Width,Height,TRUE);

return 0;

}

case WM_LBUTTONDOWN:

{

//Ищем свободное место в массиве дескрипторов

short j;

for (j=0; j<5, IsWindow(hwnds[j]); j++);

//Если свободного места нет, возвращаемся

if (j>4) return 0;

//Формируем заголовок окна

TCHAR str1[20]; _itow(j+1, str1, 10); _tcscat(str1,TEXT("-e окно"));

//Создаем j-e временное окно

hwnds[j] = CreateWindow(szPopupClass, str1, WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE, left+cxClient-Width-xStep*j, top+cyClient-Height-yStep*j, Width, Height, hwnd, 0, hInstance, NULL);

//Если создано не первое окно, перемещаем его вниз

if(j>0)

{

SetWindowPos(hwnds[j], hwnds[j-1], 0, 0, Width, Height, SWP_NOMOVE);

//Перемещаем на передний план первое окно

SetForegroundWindow(hwnds[0]);

}

return 0;

}

case WM_DESTROY: { PostQuitMessage(0); return 0; }

}

return DefWindowProc(hwnd, msg, wParam, lParam);

}

LRESULT CALLBACK WndPopup(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

return DefWindowProc(hwnd, msg, wParam, lParam);

}

Контрольные вопросы

1. Чем отличается текст приложения, которое создает окна, от текста приложения, которое не создает окон?

2. Каков алгоритм работы главной функции приложения?

3. Какие действия должно выполнить приложение для создания окна?

4. Описания каких программных объектов нужно включить в текст приложения для регистрации класса окон?

5. В каком месте и как происходит вызов функции окна?

6. Каково назначение формальных параметров функции окна?

7. Какие сообщения получает функция окна на этапе создания окна?

8. Что происходит с теми сообщениями, которые функция окна не обрабатывает?

9. Каковы отличительные характеристики перекрывающихся, временных и дочерних окон?

10. Как единственный раз создать окно в теле функции окна-владельца?


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



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