#include "Crawler.h" #include "Menu.h" #include "DEFINES.h" #include "safemap.h" bool Menu::MOUSE_NAVIGATION=true; std::vectorMenu::stack; std::mapMenu::menus; std::string Menu::themeSelection="BlueDefault"; safeunorderedmapMenu::themes; const vf2d Menu::CENTERED = {-456,-456}; INCLUDE_GFX extern vi2d WINDOW_SIZE; Menu::Menu(){} Menu::Menu(vf2d pos,vf2d size) :pos(pos==CENTERED?WINDOW_SIZE/2-size/2:vi2d{pos}),size(size){} void Menu::InitializeMenus(){ stack.reserve(32); menus[TEST]=InitializeTestMenu(); menus[TEST_2]=InitializeTestSubMenu(); for(MenuType type=TEST;typeselectable){ buttons[button->rect.pos.y].push_back(button); }else{ displayComponents.push_back(button); } if(components.count(key)){ std::cout<<"WARNING! Key "<GetMouse(Mouse::LEFT).bReleased||game->GetKey(SPACE).bReleased||game->GetKey(ENTER).bReleased){ MenuSelect(game); } } void Menu::HoverMenuSelect(Crawler*game){ if(selection==vi2d{-1,-1})return; if(buttons[selection.y][selection.x]->draggable) if(buttonHoldTime<0.3)CheckClickAndPerformMenuSelect(game); else{ draggingComponent=buttons[selection.y][selection.x]; buttons[selection.y].erase(buttons[selection.y].begin()+selection.x); } else CheckClickAndPerformMenuSelect(game); } void Menu::MenuSelect(Crawler*game){ if(selection==vi2d{-1,-1})return; buttons[selection.y][selection.x]->onClick(*this,game); if(buttons[selection.y][selection.x]->menuDest!=MenuType::ENUM_END){ if(stack.size()<32){ stack.push_back(&menus[buttons[selection.y][selection.x]->menuDest]);//Navigate to the next menu. }else{ std::cout<<"WARNING! Exceeded menu stack size limit!"<GetElapsedTime(); HoverMenuSelect(game); for(auto&key:buttons){ for(auto&button:key.second){ button->hovered=false; } } if(!MOUSE_NAVIGATION){ if(selection!=vi2d{-1,-1})buttons[selection.y][selection.x]->hovered=true; }else{ for(auto&key:buttons){ for(auto&button:key.second){ if(geom2d::overlaps(geom2d::rect{button->rect.pos+pos,button->rect.size},game->GetMousePos())){ button->hovered=true; } } } } KeyboardButtonNavigation(game,pos); for(auto&key:buttons){ for(auto&button:key.second){ button->Update(game); } } for(auto&component:displayComponents){ component->Update(game); } }; void Menu::Draw(Crawler*game){ if(GetCurrentTheme().IsScaled()){ DrawScaledWindow(game,pos); }else{ DrawTiledWindow(game,pos); } for(auto&key:buttons){ for(auto&button:key.second){ button->Draw(game,pos,this==Menu::stack.back()); } } for(auto&component:displayComponents){ component->Draw(game,pos,this==Menu::stack.back()); } }; void Menu::OpenMenu(MenuType menu){ stack.clear(); stack.push_back(&(menus[menu])); } void Menu::KeyboardButtonNavigation(Crawler*game,vf2d menuPos){ 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. int previousButtonX=buttons[selection.y][selection.x]->rect.pos.x; selection.y=key.first; int index=0; for(auto&button:key.second){ //Try to match a button in the same column as this button first. if(previousButtonX==button->rect.pos.x){ selection.x=index; break; } index++; } 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){ int previousButtonX=buttons[selection.y][selection.x]->rect.pos.x; selection.y=prevInd; int index=0; for(auto&button:buttons[prevInd]){ //Try to match a button in the same column as this button first. if(previousButtonX==button->rect.pos.x){ selection.x=index; break; } index++; } }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; } } 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. if(!MOUSE_NAVIGATION){ buttonHoldTime=0; //Key presses automatically highlight the first button if it's not highlighted. if(selection==vi2d{-1,-1}&&buttons.size()>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. selection={-1,-1}; for(auto&key:buttons){ int index=0; for(auto&button:key.second){ if(geom2d::overlaps(geom2d::rect{button->rect.pos+menuPos,button->rect.size},game->GetMousePos())){ selection={index,key.first}; break; } index++; } } buttonHoldTime=0; } } } void Menu::DrawScaledWindow(Crawler*game,vf2d menuPos){ vf2d patchSize={"Interface.9PatchSize"_f[0],"Interface.9PatchSize"_f[1]}; //Upper-Left game->DrawPartialDecal(menuPos-patchSize,patchSize,GetPatchPart(0,0).Decal(),{patchSize.x*0,patchSize.y*0},patchSize,GetRenderColor()); //Upper-Right game->DrawPartialDecal(menuPos+vf2d{size.x,-patchSize.y},patchSize,GetPatchPart(2,0).Decal(),{patchSize.x*2,patchSize.y*0},patchSize,GetRenderColor()); //Bottom-Left game->DrawPartialDecal(menuPos+vf2d{-patchSize.x,size.y},patchSize,GetPatchPart(0,2).Decal(),{patchSize.x*0,patchSize.y*2},patchSize,GetRenderColor()); //Bottom-Right game->DrawPartialDecal(menuPos+vf2d{size.x,size.y},patchSize,GetPatchPart(2,2).Decal(),{patchSize.x*2,patchSize.y*2},patchSize,GetRenderColor()); //Top game->DrawPartialDecal(menuPos+vf2d{0,-patchSize.y},vf2d{size.x,patchSize.y},GetPatchPart(1,0).Decal(),{patchSize.x*1,patchSize.y*0},patchSize,GetRenderColor()); //Left game->DrawPartialDecal(menuPos+vf2d{-patchSize.x,0},vf2d{patchSize.x,size.y},GetPatchPart(0,1).Decal(),{patchSize.x*0,patchSize.y*1},patchSize,GetRenderColor()); //Right game->DrawPartialDecal(menuPos+vf2d{size.x,0},vf2d{patchSize.x,size.y},GetPatchPart(2,1).Decal(),{patchSize.x*2,patchSize.y*1},patchSize,GetRenderColor()); //Bottom game->DrawPartialDecal(menuPos+vf2d{0,size.y},vf2d{size.x,patchSize.y},GetPatchPart(1,2).Decal(),{patchSize.x*1,patchSize.y*2},patchSize,GetRenderColor()); //Center if(GetCurrentTheme().HasBackground()){ Decal*back=GetCurrentTheme().GetBackground(); game->DrawPartialDecal(menuPos,size,back,{0,0},back->sprite->Size(),GetRenderColor()); }else{ game->DrawPartialDecal(menuPos,size,GetPatchPart(1,1).Decal(),{patchSize.x*1,patchSize.y*1},patchSize,GetRenderColor()); } } void Menu::DrawTiledWindow(Crawler*game,vf2d menuPos){ vf2d patchSize={"Interface.9PatchSize"_f[0],"Interface.9PatchSize"_f[1]}; //Upper-Left game->DrawPartialDecal(menuPos-patchSize,patchSize,GetPatchPart(0,0).Decal(),{0,0},patchSize,GetRenderColor()); //Upper-Right game->DrawPartialDecal(menuPos+vf2d{size.x,-patchSize.y},patchSize,GetPatchPart(2,0).Decal(),{0,0},patchSize,GetRenderColor()); //Bottom-Left game->DrawPartialDecal(menuPos+vf2d{-patchSize.x,size.y},patchSize,GetPatchPart(0,2).Decal(),{0,0},patchSize,GetRenderColor()); //Bottom-Right game->DrawPartialDecal(menuPos+vf2d{size.x,size.y},patchSize,GetPatchPart(2,2).Decal(),{0,0},patchSize,GetRenderColor()); //Top game->DrawPartialDecal(menuPos+vf2d{0,-patchSize.y},vf2d{size.x,patchSize.y},GetPatchPart(1,0).Decal(),{0,0},vf2d{size.x,patchSize.y},GetRenderColor()); //Left game->DrawPartialDecal(menuPos+vf2d{-patchSize.x,0},vf2d{patchSize.x,size.y},GetPatchPart(0,1).Decal(),{0,0},vf2d{patchSize.x,size.y},GetRenderColor()); //Right game->DrawPartialDecal(menuPos+vf2d{size.x,0},vf2d{patchSize.x,size.y},GetPatchPart(2,1).Decal(),{0,0},vf2d{patchSize.x,size.y},GetRenderColor()); //Bottom game->DrawPartialDecal(menuPos+vf2d{0,size.y},vf2d{size.x,patchSize.y},GetPatchPart(1,2).Decal(),{0,0},vf2d{size.x,patchSize.y},GetRenderColor()); //Center if(GetCurrentTheme().HasBackground()){ Decal*back=GetCurrentTheme().GetBackground(); game->DrawPartialDecal(menuPos,size,back,{0,0},size,GetRenderColor()); }else{ game->DrawPartialDecal(menuPos,size,GetPatchPart(1,1).Decal(),{0,0},patchSize,GetRenderColor()); } } Renderable&Menu::GetPatchPart(int x,int y){ return GFX[themeSelection+"_"+std::to_string(x)+std::to_string(y)+".png"]; } Theme&Menu::GetCurrentTheme(){ return themes[themeSelection]; } Pixel Menu::GetRenderColor(){ bool focused=Menu::stack.back()==this; Pixel col=WHITE; if(!focused){ col=WHITE*"ThemeGlobal.MenuUnfocusedColorMult"_F; } return col; }