Analog scrolling menu implementation completed.

pull/35/head
sigonasr2 11 months ago
parent df10dc43a8
commit ad05279ae7
  1. 12
      Adventures in Lestoria/LoadGameWindow.cpp
  2. 30
      Adventures in Lestoria/Menu.cpp
  3. 9
      Adventures in Lestoria/Menu.h
  4. 56
      Adventures in Lestoria/ScrollableWindowComponent.h
  5. 2
      Adventures in Lestoria/TODO.txt
  6. 2
      Adventures in Lestoria/Version.h

@ -61,12 +61,12 @@ void Menu::InitializeLoadGameWindow(){
}}},
{game->KEY_SCROLL,{"Scroll Up/Down",[](MenuType type){
auto scrollWindow=Component<ScrollableWindowComponent>(type,"Game Files List");
scrollWindow->SetScrollAmount(scrollWindow->GetScrollAmount()-vf2d{0,game->KEY_SCROLL.Analog()*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F});
for(auto component:scrollWindow->GetComponents()){
if(geom2d::overlaps(scrollWindow->GetPos()+scrollWindow->GetSize()/2,geom2d::rect<float>{component.lock()->GetPos(),component.lock()->GetSize()})){
Menu::menus[type]->SetSelection(component);
}
}
float scrollAmt=game->KEY_SCROLL.Analog()*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F;
scrollWindow->SetScrollAmount(scrollWindow->GetTargetScrollAmount()-vf2d{0,game->KEY_SCROLL.Analog()*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F});
//Height of these buttons is 48.
scrollWindow->IncreaseSelectionIndex(scrollAmt/48.f);
}}},
}
,{ //Button Navigation Rules

@ -288,7 +288,7 @@ void Menu::OpenMenu(MenuType menu,bool cover){
if(menus[menu]->onOpenFunc){
Data returnData;
menus[menu]->onOpenFunc(menu,returnData);
menus[menu]->SetSelection(returnData);
menus[menu]->SetSelection(returnData,true);
}
stack.push_back(menus[menu]);
}
@ -635,38 +635,52 @@ const std::weak_ptr<MenuComponent>Menu::GetKeySelection()const{
return keyboardSelection;
}
void Menu::SetSelection(std::weak_ptr<MenuComponent>button){
void Menu::SetSelection(std::weak_ptr<MenuComponent>button,const bool scroll,const bool reset){
selection=button;
if(navigationGroups.count(button.lock()->GetName())||
!button.lock()->parentComponent.expired()&&navigationGroups.count(button.lock()->parentComponent.lock()->GetName())){
keyboardSelection=button;
}
if(!UsingMouseNavigation()&&!button.lock()->parentComponent.expired()){
if(scroll&&!UsingMouseNavigation()&&!button.lock()->parentComponent.expired()){
auto scrollWindow=button.lock()->parentComponent.lock();
scrollWindow->HandleOutsideDisabledButtonSelection(Menu::menus[button.lock()->parentMenu]->components[button.lock()->GetName()]);
scrollWindow->selectionIndex=scrollWindow->GetComponentIndex(selection);
}
if(!selection.lock()->parentComponent.expired()&&reset){
auto scrollWindow=selection.lock()->parentComponent.lock();
scrollWindow->targetScrollOffset.y=0.f;
scrollWindow->selectionIndex=0.f;
}
}
void Menu::SetSelection(std::string_view button){
void Menu::SetSelection(std::string_view button,const bool scroll,const bool reset){
selection=Component<MenuComponent>(type,std::string(button));
if(navigationGroups.count(std::string(button))||
!selection.lock()->parentComponent.expired()&&navigationGroups.count(selection.lock()->parentComponent.lock()->GetName())){
keyboardSelection=selection;
}
if(!UsingMouseNavigation()&&!selection.lock()->parentComponent.expired()){
if(scroll&&!UsingMouseNavigation()&&!selection.lock()->parentComponent.expired()){
auto scrollWindow=selection.lock()->parentComponent.lock();
scrollWindow->HandleOutsideDisabledButtonSelection(Menu::menus[selection.lock()->parentMenu]->components[selection.lock()->GetName()]);
scrollWindow->selectionIndex=scrollWindow->GetComponentIndex(selection);
}
if(!selection.lock()->parentComponent.expired()&&reset){
auto scrollWindow=selection.lock()->parentComponent.lock();
scrollWindow->targetScrollOffset.y=0.f;
scrollWindow->selectionIndex=0.f;
}
}
void Menu::SetSelection(std::variant<std::string,std::weak_ptr<MenuComponent>>button){
void Menu::SetSelection(std::variant<std::string,std::weak_ptr<MenuComponent>>button,const bool reset){
if(std::holds_alternative<std::string>(button)){
SetSelection(std::string_view(std::get<std::string>(button)));
SetSelection(std::string_view(std::get<std::string>(button)),true,reset);
}else
if(std::holds_alternative<std::weak_ptr<MenuComponent>>(button)){
SetSelection(std::get<std::weak_ptr<MenuComponent>>(button));
SetSelection(std::get<std::weak_ptr<MenuComponent>>(button),true,reset);
}else{
ERR("WARNING! Specified menu opening function does not hold neither a string nor a pointer to a component to use!");
}

@ -144,6 +144,7 @@ class Menu:public IAttributable{
float buttonHoldTime=0;
static vi2d lastActiveMousePos;
int componentCount=0;
float componentSelectionIndex=0.f;
std::unique_ptr<MenuComponent>draggingComponent;
ViewPort window;
@ -217,11 +218,13 @@ public:
static Renderable&GetPatchPart(int x,int y);
void RecalculateComponentCount();
void SetSelection(std::string_view button);
void SetSelection(std::weak_ptr<MenuComponent>button);
void SetSelection(std::variant<std::string,std::weak_ptr<MenuComponent>>button);
void SetSelection(std::string_view button,const bool scroll=true,const bool reset=false); // Use the reset parameter when a window is opening up, as this will cause the window now to scroll to its previous target.
void SetSelection(std::weak_ptr<MenuComponent>button,const bool scroll=true,const bool reset=false); // Use the reset parameter when a window is opening up, as this will cause the window now to scroll to its previous target.
void SetSelection(std::variant<std::string,std::weak_ptr<MenuComponent>>button,const bool reset=false); // Use the reset parameter when a window is opening up, as this will cause the window now to scroll to its previous target.
const std::weak_ptr<MenuComponent>GetSelection()const;
const std::weak_ptr<MenuComponent>GetKeySelection()const;
void IncreaseSelectionIndex(const float val);
void DecreaseSelectionIndex(const float val);
private:
Menu(vf2d pos,vf2d size);
static MenuType lastMenuTypeCreated;

@ -44,6 +44,7 @@ All rights reserved.
using A=Attribute;
class ScrollableWindowComponent:public MenuComponent{
friend class Menu;
protected:
ViewPort subWindow;
std::vector<std::weak_ptr<MenuComponent>>components;
@ -56,6 +57,8 @@ protected:
float scrollBarHoverTime=0;
vf2d scrollOffset{};
vf2d targetScrollOffset{};
float lastScrollUpdate=0.f;
float selectionIndex=0.f;
protected:
inline bool OnScreen(std::weak_ptr<MenuComponent>component){
return geom2d::overlaps(geom2d::rect<float>{{},rect.size},geom2d::rect<float>{component.lock()->rect.pos+vf2d{2,2},component.lock()->rect.size-vf2d{2,2}});
@ -89,14 +92,24 @@ public:
virtual inline void SetScrollAmount(vf2d scrollOffset){
this->targetScrollOffset=scrollOffset;
}
virtual inline vf2d GetScrollAmount(){
//Use this when you need to add more scrolling offset to a previous amount as GetScrollAmount() is used to get the internal scroll offset specifically.
virtual inline vf2d GetTargetScrollAmount()const{
return targetScrollOffset;
}
virtual bool GetHoverState(AiL*game,MenuComponent*child)override{
return geom2d::overlaps(geom2d::rect<float>{Menu::menus[parentMenu]->pos+rect.pos,rect.size},game->GetMousePos())&& //Make sure the mouse is inside the parent window component first....
geom2d::overlaps(geom2d::rect<float>{Menu::menus[parentMenu]->pos+rect.pos+child->rect.pos,child->rect.size},game->GetMousePos());
}
inline void IncreaseSelectionIndex(const float val){
float prevIndex=selectionIndex;
selectionIndex=std::clamp(selectionIndex+val,0.f,float(components.size()-1));
if(size_t(prevIndex)!=size_t(selectionIndex)){Menu::menus[parentMenu]->SetSelection(components[size_t(selectionIndex)],false);}
}
protected:
virtual inline vf2d GetScrollAmount()const{
return scrollOffset;
}
virtual inline void AfterCreate()override{
//Let's use the internal name of this component to add unique names for sub-components.
upButton=Menu::menus[parentMenu]->ADD(name+vf2d(rect.pos+vf2d{rect.size.x-12,0}).str()+"_"+vf2d(12,12).str(),MenuComponent)(geom2d::rect<float>{rect.pos+vf2d{rect.size.x-12,0},{12,12}},"^",[&](MenuFuncData dat){SetScrollAmount(GetScrollAmount()+vf2d{0,"ThemeGlobal.MenuButtonScrollSpeed"_F});return true;},ButtonAttr::UNSELECTABLE_VIA_KEYBOARD)DEPTH depth-1 END;
@ -116,6 +129,8 @@ protected:
virtual inline void Update(AiL*game)override{
MenuComponent::Update(game);
lastScrollUpdate=std::max(0.f,lastScrollUpdate-game->GetElapsedTime());
vf2d windowAbsPos=Menu::menus[parentMenu]->pos+rect.pos;
bool mouseOverScrollbar=geom2d::overlaps(geom2d::rect<float>(windowAbsPos+vf2d{rect.size.x-12,scrollBarTop+12},{12,scrollBarHeight}),game->GetMousePos());
@ -143,10 +158,6 @@ protected:
scrollBarHoverTime=std::max(scrollBarHoverTime-game->GetElapsedTime(),0.f);
}
for(std::weak_ptr<MenuComponent>component:components){
component.lock()->rect.pos=component.lock()->originalPos+targetScrollOffset;
}
if(game->GetMouseWheel()!=0){
if(game->GetMouseWheel()>0){
SetScrollAmount(GetScrollAmount()+vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F});
@ -157,10 +168,12 @@ protected:
if(bounds.size.y-rect.size.y>0){
scrollOffset.y=std::clamp(GetScrollAmount().y,-(bounds.size.y-rect.size.y),0.f);
SetScrollAmount({GetScrollAmount().x,std::clamp(GetScrollAmount().y,-(bounds.size.y-rect.size.y),0.f)});
SetScrollAmount({targetScrollOffset.x,std::clamp(targetScrollOffset.y,-(bounds.size.y-rect.size.y),0.f)});
selectionIndex=std::clamp(selectionIndex,0.f,float(components.size()-1));
}else{
scrollOffset.y=0;
SetScrollAmount({GetScrollAmount().x,0});
SetScrollAmount({targetScrollOffset.x,0});
selectionIndex=std::clamp(selectionIndex,0.f,float(components.size()-1));
}
std::sort(components.begin(),components.end(),[](std::weak_ptr<MenuComponent>c1,std::weak_ptr<MenuComponent>c2){return c1.lock()->depth>c2.lock()->depth;});
@ -175,6 +188,31 @@ protected:
upButton.lock()->disabled=true;
downButton.lock()->disabled=true;
}
#pragma region Move scroll offset towards target offset
if(scrollOffset.y!=targetScrollOffset.y){
if(lastScrollUpdate==0.f){
float diff=fabs(targetScrollOffset.y-scrollOffset.y);
if(targetScrollOffset.y>scrollOffset.y){
scrollOffset.y+=diff/4.f;
if(targetScrollOffset.y<scrollOffset.y){
scrollOffset.y=targetScrollOffset.y;
}
}else{
scrollOffset.y-=diff/4.f;
if(targetScrollOffset.y>scrollOffset.y){
scrollOffset.y=targetScrollOffset.y;
}
}
for(std::weak_ptr<MenuComponent>component:components){
component.lock()->rect.pos=component.lock()->originalPos+scrollOffset;
}
lastScrollUpdate=1/60.f;
}
}
#pragma endregion
}
inline void DrawScrollbar(ViewPort&window,vf2d parentPos,bool focused){
float spaceBetweenTopAndBottomArrows=rect.size.y-24;
@ -191,6 +229,10 @@ protected:
window.DrawRectDecal(rect.pos+parentPos+vf2d{rect.size.x-11.75f,scrollBarTop+12},{12,scrollBarHeight},WHITE*focusedWindowColorMult);
}
inline float GetComponentIndex(std::weak_ptr<MenuComponent>comp){
return float(std::distance(GetComponents().begin(),std::find_if(GetComponents().begin(),GetComponents().end(),[&](auto&component){return &*comp.lock()==&*component.lock();})));
}
virtual inline void DrawDecal(ViewPort&window,bool focused)override{
MenuComponent::DrawDecal(window,focused);
if(border){

@ -27,6 +27,8 @@ Story proofreading/correcting/storyboarding
- Lock up unimplemented classes.
- Don't enable all stage plates normally.
- Auto targeting for controller / keyboard.
A "Debug" version of the game that simply outputs all std::cout to a file as well (debug.log).
ERR messages become just output messages in release build and won't crash the game.

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 0
#define VERSION_MINOR 3
#define VERSION_PATCH 0
#define VERSION_BUILD 6071
#define VERSION_BUILD 6119
#define stringify(a) stringify_(a)
#define stringify_(a) #a

Loading…
Cancel
Save