mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 14:53:52 +00:00
490 lines
14 KiB
C++
490 lines
14 KiB
C++
#include "MainMenuUI.h"
|
|
|
|
#include "FontFamily.h"
|
|
#include "GpApplicationName.h"
|
|
#include "HostDisplayDriver.h"
|
|
#include "GliderProtos.h"
|
|
#include "Externs.h"
|
|
#include "IGpDisplayDriver.h"
|
|
#include "PLCore.h"
|
|
#include "PLQDraw.h"
|
|
#include "PLStandardColors.h"
|
|
#include "PLSysCalls.h"
|
|
#include "RenderedFont.h"
|
|
#include "GpRenderedFontMetrics.h"
|
|
#include "ResolveCachingColor.h"
|
|
#include "PLTimeTaggedVOSEvent.h"
|
|
#include "WindowDef.h"
|
|
#include "WindowManager.h"
|
|
#include "Vec2i.h"
|
|
|
|
struct MainMenuControlState
|
|
{
|
|
Window *m_window;
|
|
|
|
int m_targetHorizontalCoordinate;
|
|
Point m_dimensions;
|
|
int m_page;
|
|
};
|
|
|
|
struct MainMenuUIState
|
|
{
|
|
enum ControlID
|
|
{
|
|
Control_NewGame,
|
|
Control_LoadSavedGame,
|
|
Control_HighScores,
|
|
Control_Page0To1,
|
|
|
|
Control_LoadHouse,
|
|
Control_Demo,
|
|
Control_Page1To2,
|
|
|
|
Control_AboutApplication,
|
|
Control_AboutFramework,
|
|
Control_Page2To0,
|
|
|
|
Control_Count,
|
|
};
|
|
|
|
enum ControlStyle
|
|
{
|
|
ControlStyle_Icon,
|
|
ControlStyle_Text,
|
|
};
|
|
|
|
MainMenuControlState m_controls[Control_Count];
|
|
int m_scrollInOffset;
|
|
int m_scrollInStep;
|
|
int m_scrollOutDistance;
|
|
int m_currentPage;
|
|
|
|
ControlID m_activeControl;
|
|
|
|
static const unsigned int kControlHeight = 40;
|
|
static const unsigned int kControlFontSize = 24;
|
|
static const unsigned int kControlLeftSpacing = 20;
|
|
static const unsigned int kControlBottomSpacing = 20;
|
|
static const unsigned int kControlIntermediateSpacing = 16;
|
|
static const unsigned int kControlInteriorSpacing = 6;
|
|
static const unsigned int kControlScrollInDecay = 2;
|
|
static const unsigned int kControlScrollInDecayFalloffBits = 0;
|
|
};
|
|
|
|
static MainMenuUIState mainMenu;
|
|
|
|
static MainMenuUIState::ControlStyle GetControlStyleForControl(MainMenuUIState::ControlID controlID)
|
|
{
|
|
switch (controlID)
|
|
{
|
|
case MainMenuUIState::Control_Page0To1:
|
|
case MainMenuUIState::Control_Page1To2:
|
|
case MainMenuUIState::Control_Page2To0:
|
|
return MainMenuUIState::ControlStyle_Icon;
|
|
|
|
default:
|
|
return MainMenuUIState::ControlStyle_Text;
|
|
};
|
|
}
|
|
|
|
static PLPasStr GetTextForControl(MainMenuUIState::ControlID controlID)
|
|
{
|
|
switch (controlID)
|
|
{
|
|
case MainMenuUIState::Control_NewGame:
|
|
return PSTR("New Game");
|
|
case MainMenuUIState::Control_LoadSavedGame:
|
|
return PSTR("Resume Game");
|
|
case MainMenuUIState::Control_LoadHouse:
|
|
return PSTR("Load House");
|
|
case MainMenuUIState::Control_Demo:
|
|
return PSTR("Demo");
|
|
case MainMenuUIState::Control_HighScores:
|
|
return PSTR("High Scores");
|
|
case MainMenuUIState::Control_AboutApplication:
|
|
return PSTR("About Glider PRO");
|
|
case MainMenuUIState::Control_AboutFramework:
|
|
return PSTR("About " GP_APPLICATION_NAME);
|
|
|
|
default:
|
|
return PSTR("");
|
|
};
|
|
}
|
|
|
|
static void DrawMainMenuControl(DrawSurface *surface, MainMenuUIState::ControlID controlID, bool clicked)
|
|
{
|
|
Rect surfaceRect = surface->m_port.GetRect();
|
|
|
|
PortabilityLayer::ResolveCachingColor blackColor(StdColors::Black());
|
|
PortabilityLayer::ResolveCachingColor whiteColor(StdColors::White());
|
|
PortabilityLayer::ResolveCachingColor borderColor1(PortabilityLayer::RGBAColor::Create(255, 51, 51, 255));
|
|
PortabilityLayer::ResolveCachingColor borderColor2(PortabilityLayer::RGBAColor::Create(255, 153, 51, 255));
|
|
|
|
surface->FrameRect(surfaceRect, blackColor);
|
|
surface->FrameRect(surfaceRect.Inset(1, 1), borderColor1);
|
|
surface->FrameRect(surfaceRect.Inset(2, 2), borderColor2);
|
|
|
|
if (clicked)
|
|
surface->FillRect(surfaceRect.Inset(3, 3), borderColor2);
|
|
else
|
|
surface->FillRect(surfaceRect.Inset(3, 3), whiteColor);
|
|
|
|
switch (GetControlStyleForControl(controlID))
|
|
{
|
|
case MainMenuUIState::ControlStyle_Text:
|
|
{
|
|
PortabilityLayer::RenderedFont *rfont = GetHandwritingFont(MainMenuUIState::kControlFontSize, PortabilityLayer::FontFamilyFlag_None, true);
|
|
if (!rfont)
|
|
return;
|
|
|
|
int32_t verticalOffset = (static_cast<int32_t>(surface->m_port.GetRect().Height()) + rfont->GetMetrics().m_ascent) / 2;
|
|
|
|
surface->DrawString(Point::Create(MainMenuUIState::kControlInteriorSpacing, verticalOffset), GetTextForControl(controlID), blackColor, rfont);
|
|
}
|
|
break;
|
|
case MainMenuUIState::ControlStyle_Icon:
|
|
{
|
|
const int dotSize = 4;
|
|
const int dotSpacing = 2;
|
|
|
|
const int dotCount = 3;
|
|
const int dotTotalSize = dotSpacing * (dotCount - 1) + dotSize * dotCount;
|
|
|
|
int32_t dotHorizontalOffset = (static_cast<int32_t>(surface->m_port.GetRect().Width()) - dotTotalSize) / 2;
|
|
int32_t dotVerticalOffset = (static_cast<int32_t>(surface->m_port.GetRect().Height()) - dotSize) / 2;
|
|
|
|
for (int i = 0; i < dotCount; i++)
|
|
{
|
|
int32_t hCoord = dotHorizontalOffset + i * (dotSize + dotSpacing);
|
|
surface->FillEllipse(Rect::Create(dotVerticalOffset, hCoord, dotVerticalOffset + dotSize, hCoord + dotSize), blackColor);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void StartScrollForPage()
|
|
{
|
|
unsigned int displayHeight = 0;
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->GetDisplayResolution(nullptr, &displayHeight);
|
|
|
|
DismissMainMenuUI();
|
|
|
|
int page = mainMenu.m_currentPage;
|
|
|
|
PortabilityLayer::WindowManager *wm = PortabilityLayer::WindowManager::GetInstance();
|
|
|
|
int totalWidth = 0;
|
|
|
|
for (int controlID = 0; controlID < MainMenuUIState::Control_Count; controlID++)
|
|
{
|
|
if (mainMenu.m_controls[controlID].m_page == page)
|
|
totalWidth = mainMenu.m_controls[controlID].m_targetHorizontalCoordinate + mainMenu.m_controls[controlID].m_dimensions.h;
|
|
}
|
|
|
|
mainMenu.m_scrollInStep = 1;
|
|
mainMenu.m_scrollInOffset = 0;
|
|
while (mainMenu.m_scrollInOffset < totalWidth)
|
|
{
|
|
mainMenu.m_scrollInOffset += (mainMenu.m_scrollInStep >> MainMenuUIState::kControlScrollInDecayFalloffBits);
|
|
mainMenu.m_scrollInStep += MainMenuUIState::kControlScrollInDecay;
|
|
}
|
|
|
|
mainMenu.m_scrollOutDistance = totalWidth;
|
|
|
|
for (int i = 0; i < MainMenuUIState::Control_Count; i++)
|
|
{
|
|
MainMenuControlState &control = mainMenu.m_controls[i];
|
|
|
|
if (control.m_page == page)
|
|
{
|
|
|
|
const uint16_t styleFlags = PortabilityLayer::WindowStyleFlags::kBorderless;
|
|
|
|
PortabilityLayer::WindowDef wdef = PortabilityLayer::WindowDef::Create(Rect::Create(0, 0, control.m_dimensions.v, control.m_dimensions.h), styleFlags, true, 0, 0, PSTR(""));
|
|
|
|
control.m_window = wm->CreateWindow(wdef);
|
|
control.m_window->SetPosition(PortabilityLayer::Vec2i(control.m_targetHorizontalCoordinate - mainMenu.m_scrollInOffset, displayHeight - MainMenuUIState::kControlBottomSpacing - control.m_dimensions.v));
|
|
wm->PutWindowBehind(control.m_window, wm->GetPutInFrontSentinel());
|
|
|
|
DrawMainMenuControl(control.m_window->GetDrawSurface(), static_cast<MainMenuUIState::ControlID>(i), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void StartMainMenuUI()
|
|
{
|
|
DismissMainMenuUI();
|
|
|
|
PortabilityLayer::RenderedFont *rfont = GetHandwritingFont(MainMenuUIState::kControlFontSize, PortabilityLayer::FontFamilyFlag_None, true);
|
|
if (!rfont)
|
|
return;
|
|
|
|
for (int controlID = 0; controlID < MainMenuUIState::Control_Count; controlID++)
|
|
{
|
|
MainMenuControlState &control = mainMenu.m_controls[controlID];
|
|
|
|
MainMenuUIState::ControlStyle controlStyle = GetControlStyleForControl(static_cast<MainMenuUIState::ControlID>(controlID));
|
|
|
|
if (controlStyle == MainMenuUIState::ControlStyle_Text)
|
|
{
|
|
size_t textLength = rfont->MeasurePStr(GetTextForControl(static_cast<MainMenuUIState::ControlID>(controlID)));
|
|
|
|
control.m_dimensions.h = textLength + MainMenuUIState::kControlInteriorSpacing * 2;
|
|
control.m_dimensions.v = MainMenuUIState::kControlHeight;
|
|
}
|
|
else
|
|
control.m_dimensions.h = control.m_dimensions.v = MainMenuUIState::kControlHeight;
|
|
}
|
|
|
|
mainMenu.m_controls[MainMenuUIState::Control_LoadHouse].m_page = 1;
|
|
mainMenu.m_controls[MainMenuUIState::Control_Demo].m_page = 1;
|
|
mainMenu.m_controls[MainMenuUIState::Control_Page1To2].m_page = 1;
|
|
|
|
mainMenu.m_controls[MainMenuUIState::Control_AboutApplication].m_page = 2;
|
|
mainMenu.m_controls[MainMenuUIState::Control_AboutFramework].m_page = 2;
|
|
mainMenu.m_controls[MainMenuUIState::Control_Page2To0].m_page = 2;
|
|
|
|
for (int i = 0; i < MainMenuUIState::Control_Count; i++)
|
|
{
|
|
if (i == 0 || mainMenu.m_controls[i].m_page != mainMenu.m_controls[i - 1].m_page)
|
|
mainMenu.m_controls[i].m_targetHorizontalCoordinate = MainMenuUIState::kControlLeftSpacing;
|
|
else
|
|
mainMenu.m_controls[i].m_targetHorizontalCoordinate = mainMenu.m_controls[i - 1].m_targetHorizontalCoordinate + mainMenu.m_controls[i - 1].m_dimensions.h + MainMenuUIState::kControlIntermediateSpacing;
|
|
}
|
|
|
|
mainMenu.m_currentPage = 0;
|
|
StartScrollForPage();
|
|
}
|
|
|
|
static void DismissMainMenuUIPage()
|
|
{
|
|
unsigned int displayHeight = 0;
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->GetDisplayResolution(nullptr, &displayHeight);
|
|
|
|
PortabilityLayer::WindowManager *wm = PortabilityLayer::WindowManager::GetInstance();
|
|
|
|
while (mainMenu.m_scrollInOffset < mainMenu.m_scrollOutDistance)
|
|
{
|
|
mainMenu.m_scrollInOffset += (mainMenu.m_scrollInStep >> MainMenuUIState::kControlScrollInDecayFalloffBits);
|
|
mainMenu.m_scrollInStep += MainMenuUIState::kControlScrollInDecay;
|
|
|
|
for (int i = 0; i < MainMenuUIState::Control_Count; i++)
|
|
{
|
|
MainMenuControlState &control = mainMenu.m_controls[i];
|
|
if (control.m_page == mainMenu.m_currentPage)
|
|
control.m_window->SetPosition(PortabilityLayer::Vec2i(control.m_targetHorizontalCoordinate - mainMenu.m_scrollInOffset, displayHeight - MainMenuUIState::kControlBottomSpacing - control.m_dimensions.v));
|
|
}
|
|
|
|
Delay(1, nullptr);
|
|
}
|
|
}
|
|
|
|
|
|
void TickMainMenuUI()
|
|
{
|
|
if (mainMenu.m_scrollInOffset > 0)
|
|
{
|
|
PortabilityLayer::WindowManager *wm = PortabilityLayer::WindowManager::GetInstance();
|
|
|
|
unsigned int displayHeight = 0;
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->GetDisplayResolution(nullptr, &displayHeight);
|
|
|
|
mainMenu.m_scrollInStep -= MainMenuUIState::kControlScrollInDecay;
|
|
mainMenu.m_scrollInOffset -= (mainMenu.m_scrollInStep >> MainMenuUIState::kControlScrollInDecayFalloffBits);
|
|
|
|
for (int i = 0; i < MainMenuUIState::Control_Count; i++)
|
|
{
|
|
MainMenuControlState &control = mainMenu.m_controls[i];
|
|
if (control.m_page == mainMenu.m_currentPage)
|
|
control.m_window->SetPosition(PortabilityLayer::Vec2i(control.m_targetHorizontalCoordinate - mainMenu.m_scrollInOffset, displayHeight - MainMenuUIState::kControlBottomSpacing - control.m_dimensions.v));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DismissMainMenuUI()
|
|
{
|
|
PortabilityLayer::WindowManager *wm = PortabilityLayer::WindowManager::GetInstance();
|
|
|
|
for (int i = 0; i < MainMenuUIState::Control_Count; i++)
|
|
{
|
|
MainMenuControlState &control = mainMenu.m_controls[i];
|
|
|
|
if (control.m_window)
|
|
{
|
|
wm->DestroyWindow(control.m_window);
|
|
control.m_window = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HandleMainMenuUIResolutionChange()
|
|
{
|
|
PortabilityLayer::WindowManager *wm = PortabilityLayer::WindowManager::GetInstance();
|
|
|
|
unsigned int displayHeight = 0;
|
|
PortabilityLayer::HostDisplayDriver::GetInstance()->GetDisplayResolution(nullptr, &displayHeight);
|
|
|
|
for (int i = 0; i < MainMenuUIState::Control_Count; i++)
|
|
{
|
|
MainMenuControlState &control = mainMenu.m_controls[i];
|
|
|
|
if (control.m_window)
|
|
{
|
|
PortabilityLayer::Vec2i pos = control.m_window->GetPosition();
|
|
pos.m_y = displayHeight - MainMenuUIState::kControlBottomSpacing - MainMenuUIState::kControlHeight;
|
|
control.m_window->SetPosition(pos);
|
|
|
|
wm->PutWindowBehind(control.m_window, wm->GetPutInFrontSentinel());
|
|
}
|
|
}
|
|
}
|
|
|
|
static void MainMenuUIMouseMove(Window *window, MainMenuUIState::ControlID controlID, const Point &localPoint)
|
|
{
|
|
if (window->GetSurfaceRect().Contains(localPoint))
|
|
{
|
|
if (mainMenu.m_activeControl != controlID)
|
|
{
|
|
DrawMainMenuControl(window->GetDrawSurface(), controlID, true);
|
|
mainMenu.m_activeControl = controlID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mainMenu.m_activeControl == controlID)
|
|
{
|
|
DrawMainMenuControl(window->GetDrawSurface(), controlID, false);
|
|
mainMenu.m_activeControl = MainMenuUIState::Control_Count;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void HandleMainMenuUISelection(MainMenuUIState::ControlID controlID)
|
|
{
|
|
switch (controlID)
|
|
{
|
|
case MainMenuUIState::Control_NewGame:
|
|
DismissMainMenuUIPage();
|
|
DoGameMenu(iNewGame);
|
|
break;
|
|
|
|
case MainMenuUIState::Control_LoadSavedGame:
|
|
DismissMainMenuUIPage();
|
|
DoGameMenu(iOpenSavedGame);
|
|
StartMainMenuUI();
|
|
break;
|
|
|
|
case MainMenuUIState::Control_HighScores:
|
|
DismissMainMenuUIPage();
|
|
DoOptionsMenu(iHighScores);
|
|
StartMainMenuUI();
|
|
break;
|
|
|
|
case MainMenuUIState::Control_LoadHouse:
|
|
DismissMainMenuUIPage();
|
|
DoGameMenu(iLoadHouse);
|
|
StartMainMenuUI();
|
|
break;
|
|
|
|
case MainMenuUIState::Control_Demo:
|
|
DismissMainMenuUIPage();
|
|
DoOptionsMenu(iHelp);
|
|
StartMainMenuUI();
|
|
break;
|
|
|
|
case MainMenuUIState::Control_AboutApplication:
|
|
DismissMainMenuUIPage();
|
|
DoAppleMenu(iAbout);
|
|
StartScrollForPage();
|
|
break;
|
|
|
|
case MainMenuUIState::Control_AboutFramework:
|
|
DismissMainMenuUIPage();
|
|
DoAppleMenu(iAboutAerofoil);
|
|
StartScrollForPage();
|
|
break;
|
|
|
|
case MainMenuUIState::Control_Page0To1:
|
|
DismissMainMenuUIPage();
|
|
mainMenu.m_currentPage = 1;
|
|
StartScrollForPage();
|
|
break;
|
|
|
|
case MainMenuUIState::Control_Page1To2:
|
|
DismissMainMenuUIPage();
|
|
mainMenu.m_currentPage = 2;
|
|
StartScrollForPage();
|
|
break;
|
|
|
|
case MainMenuUIState::Control_Page2To0:
|
|
DismissMainMenuUIPage();
|
|
mainMenu.m_currentPage = 0;
|
|
StartScrollForPage();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool HandleMainMenuUIClick(Window *window, const Point &pt)
|
|
{
|
|
MainMenuUIState::ControlID controlID = MainMenuUIState::Control_Count;
|
|
|
|
if (mainMenu.m_scrollInOffset != 0)
|
|
return true;
|
|
|
|
for (int i = 0; i < MainMenuUIState::Control_Count; i++)
|
|
{
|
|
const MainMenuControlState &control = mainMenu.m_controls[i];
|
|
|
|
if (control.m_window == window)
|
|
{
|
|
controlID = static_cast<MainMenuUIState::ControlID>(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (controlID == MainMenuUIState::Control_Count)
|
|
return false;
|
|
|
|
DrawMainMenuControl(mainMenu.m_controls[controlID].m_window->GetDrawSurface(), controlID, true);
|
|
|
|
mainMenu.m_activeControl = controlID;
|
|
|
|
for (;;)
|
|
{
|
|
TimeTaggedVOSEvent evt;
|
|
if (WaitForEvent(&evt, 1))
|
|
{
|
|
if (evt.m_vosEvent.m_eventType == GpVOSEventTypes::kMouseInput)
|
|
{
|
|
const GpMouseInputEvent &mouseEvt = evt.m_vosEvent.m_event.m_mouseInputEvent;
|
|
|
|
if (mouseEvt.m_eventType == GpMouseEventTypes::kLeave)
|
|
return true;
|
|
|
|
if (mouseEvt.m_eventType == GpMouseEventTypes::kMove || mouseEvt.m_eventType == GpMouseEventTypes::kDown || mouseEvt.m_eventType == GpMouseEventTypes::kUp)
|
|
{
|
|
MainMenuUIMouseMove(window, controlID, window->MouseToLocal(mouseEvt));
|
|
}
|
|
|
|
if (mouseEvt.m_eventType == GpMouseEventTypes::kUp)
|
|
{
|
|
if (mainMenu.m_activeControl != MainMenuUIState::Control_Count)
|
|
{
|
|
DrawMainMenuControl(mainMenu.m_controls[controlID].m_window->GetDrawSurface(), controlID, false);
|
|
HandleMainMenuUISelection(controlID);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|