From ad05279ae7f1a15d493d8f76fd6c344146aeb097 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Fri, 19 Jan 2024 18:53:51 -0600 Subject: [PATCH] Analog scrolling menu implementation completed. --- Adventures in Lestoria/LoadGameWindow.cpp | 12 ++-- Adventures in Lestoria/Menu.cpp | 30 +++++++--- Adventures in Lestoria/Menu.h | 9 ++- .../ScrollableWindowComponent.h | 56 ++++++++++++++++--- Adventures in Lestoria/TODO.txt | 2 + Adventures in Lestoria/Version.h | 2 +- 6 files changed, 86 insertions(+), 25 deletions(-) diff --git a/Adventures in Lestoria/LoadGameWindow.cpp b/Adventures in Lestoria/LoadGameWindow.cpp index 44bc6215..861f2ca5 100644 --- a/Adventures in Lestoria/LoadGameWindow.cpp +++ b/Adventures in Lestoria/LoadGameWindow.cpp @@ -61,12 +61,12 @@ void Menu::InitializeLoadGameWindow(){ }}}, {game->KEY_SCROLL,{"Scroll Up/Down",[](MenuType type){ auto scrollWindow=Component(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{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 diff --git a/Adventures in Lestoria/Menu.cpp b/Adventures in Lestoria/Menu.cpp index 8c74b2a3..cb51c63b 100644 --- a/Adventures in Lestoria/Menu.cpp +++ b/Adventures in Lestoria/Menu.cpp @@ -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_ptrMenu::GetKeySelection()const{ return keyboardSelection; } -void Menu::SetSelection(std::weak_ptrbutton){ +void Menu::SetSelection(std::weak_ptrbutton,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(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>button){ +void Menu::SetSelection(std::variant>button,const bool reset){ if(std::holds_alternative(button)){ - SetSelection(std::string_view(std::get(button))); + SetSelection(std::string_view(std::get(button)),true,reset); }else if(std::holds_alternative>(button)){ - SetSelection(std::get>(button)); + SetSelection(std::get>(button),true,reset); }else{ ERR("WARNING! Specified menu opening function does not hold neither a string nor a pointer to a component to use!"); } diff --git a/Adventures in Lestoria/Menu.h b/Adventures in Lestoria/Menu.h index ff84b270..60929559 100644 --- a/Adventures in Lestoria/Menu.h +++ b/Adventures in Lestoria/Menu.h @@ -144,6 +144,7 @@ class Menu:public IAttributable{ float buttonHoldTime=0; static vi2d lastActiveMousePos; int componentCount=0; + float componentSelectionIndex=0.f; std::unique_ptrdraggingComponent; 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_ptrbutton); - void SetSelection(std::variant>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_ptrbutton,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>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_ptrGetSelection()const; const std::weak_ptrGetKeySelection()const; + void IncreaseSelectionIndex(const float val); + void DecreaseSelectionIndex(const float val); private: Menu(vf2d pos,vf2d size); static MenuType lastMenuTypeCreated; diff --git a/Adventures in Lestoria/ScrollableWindowComponent.h b/Adventures in Lestoria/ScrollableWindowComponent.h index 065ff196..946df457 100644 --- a/Adventures in Lestoria/ScrollableWindowComponent.h +++ b/Adventures in Lestoria/ScrollableWindowComponent.h @@ -44,6 +44,7 @@ All rights reserved. using A=Attribute; class ScrollableWindowComponent:public MenuComponent{ + friend class Menu; protected: ViewPort subWindow; std::vector>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_ptrcomponent){ return geom2d::overlaps(geom2d::rect{{},rect.size},geom2d::rect{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{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{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{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(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_ptrcomponent: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_ptrc1,std::weak_ptrc2){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.yscrollOffset.y){ + scrollOffset.y=targetScrollOffset.y; + } + } + + for(std::weak_ptrcomponent: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_ptrcomp){ + 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){ diff --git a/Adventures in Lestoria/TODO.txt b/Adventures in Lestoria/TODO.txt index dfe1632b..07ffbf9a 100644 --- a/Adventures in Lestoria/TODO.txt +++ b/Adventures in Lestoria/TODO.txt @@ -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. diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 3f6b59ff..1111294d 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -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