Improve keyboard/controller-based menu navigation by separating each row into its own structure.
This commit is contained in:
parent
03f2738ead
commit
60e46a83f2
136
Crawler/Menu.cpp
136
Crawler/Menu.cpp
@ -24,15 +24,15 @@ void Menu::InitializeMenus(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Menu::AddButton(const MenuButton&button){
|
void Menu::AddButton(const MenuButton&button){
|
||||||
buttons.push_back(button);
|
buttons[button.rect.pos.y].push_back(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::MenuSelect(Crawler*game){
|
void Menu::MenuSelect(Crawler*game){
|
||||||
if(selection==-1)return;
|
if(selection==vi2d{-1,-1})return;
|
||||||
buttons[selection].onClick(*this,game);
|
buttons[selection.y][selection.x].onClick(*this,game);
|
||||||
if(buttons[selection].menuDest!=MenuType::ENUM_END){
|
if(buttons[selection.y][selection.x].menuDest!=MenuType::ENUM_END){
|
||||||
if(stack.size()<32){
|
if(stack.size()<32){
|
||||||
stack.push_back(&menus[buttons[selection].menuDest]);//Navigate to the next menu.
|
stack.push_back(&menus[buttons[selection.y][selection.x].menuDest]);//Navigate to the next menu.
|
||||||
}else{
|
}else{
|
||||||
std::cout<<"WARNING! Exceeded menu stack size limit!"<<std::endl;
|
std::cout<<"WARNING! Exceeded menu stack size limit!"<<std::endl;
|
||||||
throw;
|
throw;
|
||||||
@ -43,62 +43,152 @@ void Menu::MenuSelect(Crawler*game){
|
|||||||
void Menu::Update(Crawler*game){
|
void Menu::Update(Crawler*game){
|
||||||
vf2d upperLeftPos=game->GetScreenSize()/2-size/2;
|
vf2d upperLeftPos=game->GetScreenSize()/2-size/2;
|
||||||
|
|
||||||
for(MenuButton&button:buttons){
|
for(auto&key:buttons){
|
||||||
|
for(auto&button:key.second){
|
||||||
button.hovered=false;
|
button.hovered=false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if(!MOUSE_NAVIGATION){
|
if(!MOUSE_NAVIGATION){
|
||||||
if(selection!=-1)buttons[selection].hovered=true;
|
if(selection!=vi2d{-1,-1})buttons[selection.y][selection.x].hovered=true;
|
||||||
}else{
|
}else{
|
||||||
for(MenuButton&button:buttons){
|
for(auto&key:buttons){
|
||||||
|
for(auto&button:key.second){
|
||||||
if(geom2d::overlaps(geom2d::rect<float>{button.rect.pos+upperLeftPos,button.rect.size},game->GetMousePos())){
|
if(geom2d::overlaps(geom2d::rect<float>{button.rect.pos+upperLeftPos,button.rect.size},game->GetMousePos())){
|
||||||
button.hovered=true;
|
button.hovered=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(game->GetKey(RIGHT).bPressed||game->GetKey(DOWN).bPressed){
|
|
||||||
MOUSE_NAVIGATION=false;
|
|
||||||
selection=(selection+1)%buttons.size();
|
|
||||||
}
|
|
||||||
if(game->GetKey(LEFT).bPressed||game->GetKey(UP).bPressed){
|
|
||||||
MOUSE_NAVIGATION=false;
|
|
||||||
selection--;
|
|
||||||
if(selection<0)selection+=buttons.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyboardButtonNavigation(game);
|
||||||
if(game->GetMouse(0).bPressed||game->GetKey(ENTER).bPressed||game->GetKey(SPACE).bPressed){
|
if(game->GetMouse(0).bPressed||game->GetKey(ENTER).bPressed||game->GetKey(SPACE).bPressed){
|
||||||
MOUSE_NAVIGATION=game->GetMouse(0).bPressed; //If a click occurs we use mouse controls.
|
MOUSE_NAVIGATION=game->GetMouse(0).bPressed; //If a click occurs we use mouse controls.
|
||||||
if(!MOUSE_NAVIGATION){
|
if(!MOUSE_NAVIGATION){
|
||||||
MenuSelect(game);
|
MenuSelect(game);
|
||||||
//Key presses automatically highlight the first button if it's not highlighted.
|
//Key presses automatically highlight the first button if it's not highlighted.
|
||||||
if(selection==-1&&buttons.size()>0){
|
if(selection==vi2d{-1,-1}&&buttons.size()>0){
|
||||||
selection=0;
|
//Find the first possible button entry in the map...
|
||||||
|
int firstInd=-1;
|
||||||
|
for(auto&key:buttons){
|
||||||
|
if(buttons[key.first].size()>0){
|
||||||
|
firstInd=key.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(firstInd!=-1){ //This means we found a valid menu item. If we didn't find one don't highlight any menu item...
|
||||||
|
selection={0,firstInd};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}else{//Mouse click.
|
}else{//Mouse click.
|
||||||
selection=-1;
|
selection={-1,-1};
|
||||||
|
for(auto&key:buttons){
|
||||||
int index=0;
|
int index=0;
|
||||||
for(MenuButton&button:buttons){
|
for(auto&button:key.second){
|
||||||
if(geom2d::overlaps(geom2d::rect<float>{button.rect.pos+upperLeftPos,button.rect.size},game->GetMousePos())){
|
if(geom2d::overlaps(geom2d::rect<float>{button.rect.pos+upperLeftPos,button.rect.size},game->GetMousePos())){
|
||||||
selection=index;
|
selection={index,key.first};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
MenuSelect(game);
|
MenuSelect(game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(MenuButton&button:buttons){
|
for(auto&key:buttons){
|
||||||
|
for(auto&button:key.second){
|
||||||
button.Update(game);
|
button.Update(game);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Menu::Draw(Crawler*game){
|
void Menu::Draw(Crawler*game){
|
||||||
vf2d upperLeftPos=game->GetScreenSize()/2-size/2;
|
vf2d upperLeftPos=game->GetScreenSize()/2-size/2;
|
||||||
game->FillRectDecal(upperLeftPos,size,VERY_DARK_BLUE);
|
game->FillRectDecal(upperLeftPos,size,VERY_DARK_BLUE);
|
||||||
for(MenuButton&button:buttons){
|
for(auto&key:buttons){
|
||||||
|
for(auto&button:key.second){
|
||||||
button.Draw(game,upperLeftPos);
|
button.Draw(game,upperLeftPos);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Menu::OpenMenu(MenuType menu){
|
void Menu::OpenMenu(MenuType menu){
|
||||||
stack.clear();
|
stack.clear();
|
||||||
stack.push_back(&(menus[menu]));
|
stack.push_back(&(menus[menu]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::KeyboardButtonNavigation(Crawler*game){
|
||||||
|
if(game->GetKey(RIGHT).bPressed){
|
||||||
|
if(selection==vi2d{-1,-1})return;
|
||||||
|
MOUSE_NAVIGATION=false;
|
||||||
|
selection.x=(selection.x+1)%buttons[selection.y].size();
|
||||||
|
}
|
||||||
|
if(game->GetKey(LEFT).bPressed){
|
||||||
|
if(selection==vi2d{-1,-1})return;
|
||||||
|
selection.x--;
|
||||||
|
if(selection.x<0)selection.x+=buttons[selection.y].size();
|
||||||
|
}
|
||||||
|
if(game->GetKey(DOWN).bPressed||game->GetKey(UP).bPressed){
|
||||||
|
if(game->GetKey(DOWN).bPressed){
|
||||||
|
MOUSE_NAVIGATION=false;
|
||||||
|
bool found=false;
|
||||||
|
bool selectedItem=false;
|
||||||
|
if(selection==vi2d{-1,-1}){
|
||||||
|
//Highlight first item.
|
||||||
|
for(auto&key:buttons){
|
||||||
|
selection.y=key.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
for(auto&key:buttons){
|
||||||
|
if(found){ //Once we discover the previous element, the next element becomes our next selection.
|
||||||
|
selection.y=key.first;
|
||||||
|
selectedItem=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(key.first==selection.y){
|
||||||
|
found=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!selectedItem){ //This means we need to loop around instead and pick the first one.
|
||||||
|
for(auto&key:buttons){
|
||||||
|
selection.y=key.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(game->GetKey(UP).bPressed){
|
||||||
|
MOUSE_NAVIGATION=false;
|
||||||
|
|
||||||
|
if(selection==vi2d{-1,-1}){
|
||||||
|
//Highlight last item.
|
||||||
|
for(auto&key:buttons){
|
||||||
|
selection.y=key.first;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
int prevInd=-1;
|
||||||
|
for(auto&key:buttons){
|
||||||
|
if(key.first==selection.y){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prevInd=key.first;
|
||||||
|
}
|
||||||
|
if(prevInd!=-1){
|
||||||
|
selection.y=prevInd;
|
||||||
|
}else{ //Since we didn't find it, it means we're at the top of the list or the list is empty. Go to the last element and use that one.
|
||||||
|
int lastInd=-1;
|
||||||
|
for(auto&key:buttons){
|
||||||
|
lastInd=key.first;
|
||||||
|
}
|
||||||
|
selection.y=lastInd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//In both cases, we should clamp the X index to make sure it's still valid.
|
||||||
|
if(selection.y!=-1){
|
||||||
|
selection.x=std::clamp(selection.x,0,int(buttons[selection.y].size())-1);
|
||||||
|
}else{
|
||||||
|
selection.x=-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,8 +10,9 @@ class Menu{
|
|||||||
friend class Player;
|
friend class Player;
|
||||||
static bool MOUSE_NAVIGATION;
|
static bool MOUSE_NAVIGATION;
|
||||||
static std::map<MenuType,Menu>menus;
|
static std::map<MenuType,Menu>menus;
|
||||||
std::vector<MenuButton>buttons;
|
|
||||||
int selection=-1;
|
std::map<int/*Y*/,std::vector<MenuButton>>buttons; //Buttons are stored in rows followed by their column order.
|
||||||
|
vi2d selection={-1,-1};
|
||||||
vf2d size; //Size in tiles (24x24), every menu will be tile-based
|
vf2d size; //Size in tiles (24x24), every menu will be tile-based
|
||||||
public:
|
public:
|
||||||
Menu();
|
Menu();
|
||||||
@ -26,5 +27,7 @@ private:
|
|||||||
void MenuSelect(Crawler*game);
|
void MenuSelect(Crawler*game);
|
||||||
static const Menu InitializeTestMenu();
|
static const Menu InitializeTestMenu();
|
||||||
static const Menu InitializeTestSubMenu();
|
static const Menu InitializeTestSubMenu();
|
||||||
|
|
||||||
|
void KeyboardButtonNavigation(Crawler*game);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 2
|
#define VERSION_MINOR 2
|
||||||
#define VERSION_PATCH 0
|
#define VERSION_PATCH 0
|
||||||
#define VERSION_BUILD 1615
|
#define VERSION_BUILD 1643
|
||||||
|
|
||||||
#define stringify(a) stringify_(a)
|
#define stringify(a) stringify_(a)
|
||||||
#define stringify_(a) #a
|
#define stringify_(a) #a
|
||||||
|
@ -2008,7 +2008,6 @@ namespace olc
|
|||||||
|
|
||||||
// Construct the window
|
// Construct the window
|
||||||
if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL;
|
if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL;
|
||||||
olc_UpdateWindowPos(30,30);
|
|
||||||
olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y);
|
olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y);
|
||||||
|
|
||||||
// Start the thread
|
// Start the thread
|
||||||
@ -5547,6 +5546,8 @@ namespace olc
|
|||||||
olc_hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE"), olcT(""), dwStyle,
|
olc_hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE"), olcT(""), dwStyle,
|
||||||
vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this);
|
vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this);
|
||||||
|
|
||||||
|
MoveWindow(olc_hWnd,vTopLeft.x,vTopLeft.y,width,height,false); //A hack to get the window's position updated in the correct spot (WM_MOVE reports the correct upper-left corner of the client area)
|
||||||
|
|
||||||
DragAcceptFiles(olc_hWnd, true);
|
DragAcceptFiles(olc_hWnd, true);
|
||||||
|
|
||||||
// Create Keyboard Mapping
|
// Create Keyboard Mapping
|
||||||
@ -5642,7 +5643,13 @@ namespace olc
|
|||||||
ptrPGE->olc_UpdateMouse(ix, iy);
|
ptrPGE->olc_UpdateMouse(ix, iy);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case WM_MOVE: ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0;
|
case WM_MOVE:
|
||||||
|
{
|
||||||
|
uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF;
|
||||||
|
int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y;
|
||||||
|
ptrPGE->olc_UpdateWindowPos(ix, iy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
case WM_SIZE: ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0;
|
case WM_SIZE: ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0;
|
||||||
case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0;
|
case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0;
|
||||||
case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0;
|
case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0;
|
||||||
@ -6624,7 +6631,10 @@ namespace olc
|
|||||||
{ emscripten_set_window_title(s.c_str()); return olc::OK; }
|
{ emscripten_set_window_title(s.c_str()); return olc::OK; }
|
||||||
|
|
||||||
virtual olc::rcode StartSystemEventLoop() override
|
virtual olc::rcode StartSystemEventLoop() override
|
||||||
{ return olc::OK; }
|
{
|
||||||
|
ptrPGE->olc_UpdateWindowPos(EM_ASM_INT({return Module.canvas.getBoundingClientRect().left}),EM_ASM_INT({return Module.canvas.getBoundingClientRect().top}));
|
||||||
|
return olc::OK;
|
||||||
|
}
|
||||||
|
|
||||||
virtual olc::rcode HandleSystemEvent() override
|
virtual olc::rcode HandleSystemEvent() override
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user