Подсветка пунктов меню

Архитектура проекта D3D_MouseZoneHighlights
Проект содержит четыре уникальных файла: main.cpp, main.h, MouseZoneClass.cpp и MouseZoneClass.h. Данный пример программы является усовершенствованной версией проекта D3D_MouseZones в которой сделано не так уж и много изменений.

Для программы необходимы следующие библиотеки: d3d9.lib, dxguid.lib, d3dx9dt.lib, d3dxof.lib, comctl32.lib и winmm.lib.

Заголовочный файл Main.h
Главный заголовочный файл содержит несколько новых строк кода, необходимых для реализации подсветки пунктов меню.
Главное отличие между этим рисунком и предыдущим примером программы заключается в том, что кнопка Options выделена с помощью подсветки. Кнопки меню изменяют свой цвет, когда указатель мыши проходит над ними. В этом и заключается суть подсветки зон.

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

// Переменные состояния подсветки
bool g_bMainMenu_NewGame_Highlight = 0;
bool g_bMainMenu_LoadGame_Highlight = 0;
bool g_bMainMenu_SaveGame_Highlight = 0;
bool g_bMainMenu_Options_Highlight = 0;

В рассматриваемом примере программы реализована подсветка четырех пунктов главного меню. Для этих четырех пунктов программа должна отслеживать какой именно из них в данный момент активирован. Я делаю это используя логические значения. Каждое логическое значение представляет состояние отдельного пункта меню. Если значение равно 0, соответствующая кнопка не подсвечена. Если же значение равно 1, механизм визуализации знает, что для кнопки следует включить подсветку. Весьма просто, не так ли?

Файл программы Main.cpp
Я не хочу утомлять вас, описывая код с которым вы уже знакомы, так почему бы сразу не перейти к сделанным изменениям? Итак, отправимся к функции vCheckInput(), чтобы увидеть первый набор изменений.

Как определить подсвечиваемую активную зону
Вот как выглядит код функции vCheckInput():

void vCheckInput(void)
{
bool bRet;
char szZoneHit[64];
POINT Point;
RECT rcWindowRect;
int iMouseX;
int iMouseY;

// Проверка смещения окна
GetWindowRect(g_hWnd, &rcWindowRect);

// Обновить местоположение мыши
GetCursorPos(&Point);

// Вычисление реальных координат мыши
iMouseX = Point.x - g_iXOffset - rcWindowRect.left;
iMouseY = Point.y - g_iYOffset - rcWindowRect.top;

// Проверка попадания
bRet = MZones.bCheckZones((short)iMouseX, (short)iMouseY, szZoneHit,
g_bLeftButton, g_bRightButton);
if(bRet) {
// ЛОГИКА ТИТУЛЬНОГО ЭКРАНА
if(g_iCurrentScreen == 0) {
// Переход к главному меню
if(!stricmp(szZoneHit, "TITLE_SCREEN")) {
// Делаем главное меню текущим
g_iCurrentScreen = 1;
// Устанавливаем активные зоны
vSetupMouseZones(1);
}
// Переход к экрану завершения игры
else if(!stricmp(szZoneHit, "EXIT_BUTTON")) {
// Делаем экран завершения текущим
g_iCurrentScreen = 2;
// Устанавливаем активные зоны
vSetupMouseZones(2);
}
}
// ЛОГИКА ГЛАВНОГО МЕНЮ
else if(g_iCurrentScreen == 1) {
// Выключаем подсветку всех зон
g_bMainMenu_NewGame_Highlight = 0;
g_bMainMenu_LoadGame_Highlight = 0;
g_bMainMenu_SaveGame_Highlight = 0;
g_bMainMenu_Options_Highlight = 0;
// Переход к экрану завершения игры
if(!stricmp(szZoneHit, "EXIT_BUTTON")) {
// Делаем экран завершения текущим
g_iCurrentScreen = 2;
// Устанавливаем активные зоны
vSetupMouseZones(2);
}
else if(!stricmp(szZoneHit, "MAINMENU_NEWGAME"))
{
// Добавьте сюда логику начала новой игры
}
else if(!stricmp(szZoneHit, "MAINMENU_LOADGAME"))
{
// Добавьте сюда логику для загрузки игры
}
else if(!stricmp(szZoneHit, "MAINMENU_SAVEGAME"))
{
// Добавьте сюда логику для записи игры
}
else if(!stricmp(szZoneHit, "MAINMENU_OPTIONS"))
{
// Деламем меню параметров текущим
g_iCurrentScreen = 7;
// Устанавливаем активные зоны
vSetupMouseZones(7);
}
// Проверим находится ли указатель над активной зоной
else if(!stricmp(szZoneHit, "MAINMENU_NEWGAME_H"))
{
// Активация подсветки
g_bMainMenu_NewGame_Highlight = 1;
}
else if(!stricmp(szZoneHit, "MAINMENU_LOADGAME_H"))
{
// Активация подсветки
g_bMainMenu_LoadGame_Highlight = 1;
}
else if(!stricmp(szZoneHit, "MAINMENU_SAVEGAME_H"))
{
// Активация подсветки
g_bMainMenu_SaveGame_Highlight = 1;
}
else if(!stricmp(szZoneHit, "MAINMENU_OPTIONS_H"))
{
// Активация подсветки
g_bMainMenu_Options_Highlight = 1;
}
}
// ЛОГИКА ЭКРАНА ЗАВЕРШЕНИЯ
else if(g_iCurrentScreen == 2) {
// Выходим из программы, если пользователь нажал кнопку мыши
if(!stricmp(szZoneHit, "EXIT_SCREEN")) {
// Флаг, сообщающий WinMain() о завершении программы
g_iCurrentScreen = 3;
}
}
// ЛОГИКА МЕНЮ ПАРАМЕТРОВ
else if(g_iCurrentScreen == 7) {
// Переходим к завершающему экрану
if(!stricmp(szZoneHit, "EXIT_BUTTON")) {
// Делаем завершающий экран текущим
g_iCurrentScreen = 2;
// Устанавливаем активные зоны
vSetupMouseZones(2);
}
// Возврат к главному меню
else if(!stricmp(szZoneHit, "OPTIONS_BACK")) {
// Делаем главное меню текущим
g_iCurrentScreen = 1;
// Устанавливаем активные зоны
vSetupMouseZones(1);
}
}
}
}

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

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

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

MZones.iAddZone("MAINMENU_NEWGAME_H", 192, 64, 256, 64, 3);
MZones.iAddZone("MAINMENU_LOADGAME_H", 192, 128, 256, 64, 3);
MZones.iAddZone("MAINMENU_SAVEGAME_H", 192, 192, 256, 64, 3);
MZones.iAddZone("MAINMENU_OPTIONS_H", 192, 256, 256, 64, 3);

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

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

else if(g_iCurrentScreen == 1) {
// Отображение главного меню
vDrawInterfaceObject(0, 0, 256.0f, 256.0f, 0);
vDrawInterfaceObject(256, 0, 256.0f, 256.0f, 1);
vDrawInterfaceObject(512, 0, 256.0f, 256.0f, 2);
vDrawInterfaceObject(0, 256, 256.0f, 256.0f, 3);
vDrawInterfaceObject(256, 256, 256.0f, 256.0f, 4);
vDrawInterfaceObject(512, 256, 256.0f, 256.0f, 5);
// Отображение подсвеченных зон, если они есть
// либо обычного меню без подсветки
if(g_bMainMenu_NewGame_Highlight) {
vDrawInterfaceObject(192, 64, 256.0f, 256.0f, 10);
}
else if(g_bMainMenu_LoadGame_Highlight) {
vDrawInterfaceObject(192, 64, 256.0f, 256.0f, 11);
}
else if(g_bMainMenu_SaveGame_Highlight) {
vDrawInterfaceObject(192, 64, 256.0f, 256.0f, 12);
}
else if(g_bMainMenu_Options_Highlight) {
vDrawInterfaceObject(192, 64, 256.0f, 256.0f, 13);
}
else {
// Меню без подсветки
vDrawInterfaceObject(192, 64, 256.0f, 256.0f, 7);
}
}

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