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){ {game->KEY_SCROLL,{"Scroll Up/Down",[](MenuType type){
auto scrollWindow=Component<ScrollableWindowComponent>(type,"Game Files List"); auto scrollWindow=Component<ScrollableWindowComponent>(type,"Game Files List");
scrollWindow->SetScrollAmount(scrollWindow->GetScrollAmount()-vf2d{0,game->KEY_SCROLL.Analog()*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F}); float scrollAmt=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()})){ scrollWindow->SetScrollAmount(scrollWindow->GetTargetScrollAmount()-vf2d{0,game->KEY_SCROLL.Analog()*game->GetElapsedTime()*"Interface.AnalogScrollSpeed"_F});
Menu::menus[type]->SetSelection(component);
} //Height of these buttons is 48.
} scrollWindow->IncreaseSelectionIndex(scrollAmt/48.f);
}}}, }}},
} }
,{ //Button Navigation Rules ,{ //Button Navigation Rules

@ -288,7 +288,7 @@ void Menu::OpenMenu(MenuType menu,bool cover){
if(menus[menu]->onOpenFunc){ if(menus[menu]->onOpenFunc){
Data returnData; Data returnData;
menus[menu]->onOpenFunc(menu,returnData); menus[menu]->onOpenFunc(menu,returnData);
menus[menu]->SetSelection(returnData); menus[menu]->SetSelection(returnData,true);
} }
stack.push_back(menus[menu]); stack.push_back(menus[menu]);
} }
@ -635,38 +635,52 @@ const std::weak_ptr<MenuComponent>Menu::GetKeySelection()const{
return keyboardSelection; 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; selection=button;
if(navigationGroups.count(button.lock()->GetName())|| if(navigationGroups.count(button.lock()->GetName())||
!button.lock()->parentComponent.expired()&&navigationGroups.count(button.lock()->parentComponent.lock()->GetName())){ !button.lock()->parentComponent.expired()&&navigationGroups.count(button.lock()->parentComponent.lock()->GetName())){
keyboardSelection=button; keyboardSelection=button;
} }
if(!UsingMouseNavigation()&&!button.lock()->parentComponent.expired()){ if(scroll&&!UsingMouseNavigation()&&!button.lock()->parentComponent.expired()){
auto scrollWindow=button.lock()->parentComponent.lock(); auto scrollWindow=button.lock()->parentComponent.lock();
scrollWindow->HandleOutsideDisabledButtonSelection(Menu::menus[button.lock()->parentMenu]->components[button.lock()->GetName()]); 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)); selection=Component<MenuComponent>(type,std::string(button));
if(navigationGroups.count(std::string(button))|| if(navigationGroups.count(std::string(button))||
!selection.lock()->parentComponent.expired()&&navigationGroups.count(selection.lock()->parentComponent.lock()->GetName())){ !selection.lock()->parentComponent.expired()&&navigationGroups.count(selection.lock()->parentComponent.lock()->GetName())){
keyboardSelection=selection; keyboardSelection=selection;
} }
if(!UsingMouseNavigation()&&!selection.lock()->parentComponent.expired()){ if(scroll&&!UsingMouseNavigation()&&!selection.lock()->parentComponent.expired()){
auto scrollWindow=selection.lock()->parentComponent.lock(); auto scrollWindow=selection.lock()->parentComponent.lock();
scrollWindow->HandleOutsideDisabledButtonSelection(Menu::menus[selection.lock()->parentMenu]->components[selection.lock()->GetName()]); 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)){ 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 }else
if(std::holds_alternative<std::weak_ptr<MenuComponent>>(button)){ 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{ }else{
ERR("WARNING! Specified menu opening function does not hold neither a string nor a pointer to a component to use!"); 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; float buttonHoldTime=0;
static vi2d lastActiveMousePos; static vi2d lastActiveMousePos;
int componentCount=0; int componentCount=0;
float componentSelectionIndex=0.f;
std::unique_ptr<MenuComponent>draggingComponent; std::unique_ptr<MenuComponent>draggingComponent;
ViewPort window; ViewPort window;
@ -217,11 +218,13 @@ public:
static Renderable&GetPatchPart(int x,int y); static Renderable&GetPatchPart(int x,int y);
void RecalculateComponentCount(); void RecalculateComponentCount();
void SetSelection(std::string_view 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); 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); 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>GetSelection()const;
const std::weak_ptr<MenuComponent>GetKeySelection()const; const std::weak_ptr<MenuComponent>GetKeySelection()const;
void IncreaseSelectionIndex(const float val);
void DecreaseSelectionIndex(const float val);
private: private:
Menu(vf2d pos,vf2d size); Menu(vf2d pos,vf2d size);
static MenuType lastMenuTypeCreated; static MenuType lastMenuTypeCreated;

@ -44,6 +44,7 @@ All rights reserved.
using A=Attribute; using A=Attribute;
class ScrollableWindowComponent:public MenuComponent{ class ScrollableWindowComponent:public MenuComponent{
friend class Menu;
protected: protected:
ViewPort subWindow; ViewPort subWindow;
std::vector<std::weak_ptr<MenuComponent>>components; std::vector<std::weak_ptr<MenuComponent>>components;
@ -56,6 +57,8 @@ protected:
float scrollBarHoverTime=0; float scrollBarHoverTime=0;
vf2d scrollOffset{}; vf2d scrollOffset{};
vf2d targetScrollOffset{}; vf2d targetScrollOffset{};
float lastScrollUpdate=0.f;
float selectionIndex=0.f;
protected: protected:
inline bool OnScreen(std::weak_ptr<MenuComponent>component){ 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}}); 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){ virtual inline void SetScrollAmount(vf2d scrollOffset){
this->targetScrollOffset=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; return targetScrollOffset;
} }
virtual bool GetHoverState(AiL*game,MenuComponent*child)override{ 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.... 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()); 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: protected:
virtual inline vf2d GetScrollAmount()const{
return scrollOffset;
}
virtual inline void AfterCreate()override{ virtual inline void AfterCreate()override{
//Let's use the internal name of this component to add unique names for sub-components. //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; 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{ virtual inline void Update(AiL*game)override{
MenuComponent::Update(game); MenuComponent::Update(game);
lastScrollUpdate=std::max(0.f,lastScrollUpdate-game->GetElapsedTime());
vf2d windowAbsPos=Menu::menus[parentMenu]->pos+rect.pos; 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()); 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); 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){
if(game->GetMouseWheel()>0){ if(game->GetMouseWheel()>0){
SetScrollAmount(GetScrollAmount()+vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F}); SetScrollAmount(GetScrollAmount()+vf2d{0,"ThemeGlobal.MenuScrollWheelSpeed"_F});
@ -157,10 +168,12 @@ protected:
if(bounds.size.y-rect.size.y>0){ if(bounds.size.y-rect.size.y>0){
scrollOffset.y=std::clamp(GetScrollAmount().y,-(bounds.size.y-rect.size.y),0.f); 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{ }else{
scrollOffset.y=0; 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;}); 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; upButton.lock()->disabled=true;
downButton.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){ inline void DrawScrollbar(ViewPort&window,vf2d parentPos,bool focused){
float spaceBetweenTopAndBottomArrows=rect.size.y-24; 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); 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{ virtual inline void DrawDecal(ViewPort&window,bool focused)override{
MenuComponent::DrawDecal(window,focused); MenuComponent::DrawDecal(window,focused);
if(border){ if(border){

@ -27,6 +27,8 @@ Story proofreading/correcting/storyboarding
- Lock up unimplemented classes. - Lock up unimplemented classes.
- Don't enable all stage plates normally. - 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). 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. 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_MAJOR 0
#define VERSION_MINOR 3 #define VERSION_MINOR 3
#define VERSION_PATCH 0 #define VERSION_PATCH 0
#define VERSION_BUILD 6071 #define VERSION_BUILD 6119
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

Loading…
Cancel
Save