# pragma region License
/*
License ( OLC - 3 )
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Copyright 2024 Joshua Sigona < sigonasr2 @ gmail . com >
Redistribution and use in source and binary forms , with or without modification ,
are permitted provided that the following conditions are met :
1. Redistributions or derivations of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice . This list of conditions and the following disclaimer must be
reproduced in the documentation and / or other materials provided with the distribution .
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND ANY
EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED
TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR
BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE .
Portions of this software are copyright © 2024 The FreeType
Project ( www . freetype . org ) . Please see LICENSE_FT . txt for more information .
All rights reserved .
*/
# pragma endregion
# pragma once
# include "Menu.h"
# include "MenuComponent.h"
# include "AdventuresInLestoria.h"
using A = Attribute ;
# define ADDSUB(key,componentType) _AddSubcomponent<componentType>(name,key,std::make_shared<componentType>
INCLUDE_game
class ScrollableWindowComponent : public MenuComponent {
friend class Menu ;
protected :
ViewPort subWindow ;
std : : vector < std : : weak_ptr < MenuComponent > > components ;
std : : weak_ptr < MenuComponent > upButton ;
std : : weak_ptr < MenuComponent > downButton ;
geom2d : : rect < float > bounds ; //It's for the scrollbar.
std : : vector < std : : weak_ptr < MenuComponent > > subcomponents ; //Subcomponents are anything that is created internally by another component.
float scrollBarHeight = 0 ;
float scrollBarTop = 0 ;
bool scrollBarSelected = false ;
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 } } ) ;
}
public :
inline ScrollableWindowComponent ( geom2d : : rect < float > rect , ComponentAttr attributes = ComponentAttr : : BACKGROUND | ComponentAttr : : OUTLINE )
: MenuComponent ( rect , " " , [ ] ( MenuFuncData data ) { return true ; } , ButtonAttr : : UNSELECTABLE | ButtonAttr : : UNSELECTABLE_VIA_KEYBOARD ) {
background = attributes & ComponentAttr : : BACKGROUND ;
border = attributes & ComponentAttr : : OUTLINE ;
}
virtual inline void RemoveAllComponents ( ) {
while ( components . size ( ) > 0 ) {
RemoveButton ( components . back ( ) ) ;
}
while ( subcomponents . size ( ) > 0 ) {
std : : weak_ptr < MenuComponent > button = subcomponents . back ( ) ;
std : : string componentName = button . lock ( ) - > GetName ( ) ;
subcomponents . erase ( subcomponents . end ( ) - 1 ) ;
size_t removedCount = 0 ;
MenuType parentMenu = button . lock ( ) - > parentMenu ;
removedCount + = Menu : : menus [ parentMenu ] - > components . erase ( componentName ) ;
if ( removedCount ! = 1 ) {
LOG ( " WARNING! Attempted to remove subbuttons from button listing, but not found! " ) ;
}
}
targetScrollOffset . y = std : : min ( targetScrollOffset . y , bounds . bottom ( ) . end . y ) ;
scrollOffset . y = std : : min ( scrollOffset . y , bounds . bottom ( ) . end . y ) ;
selectionIndex = std : : min ( size_t ( selectionIndex ) , components . size ( ) - 1 ) ;
}
virtual inline void RemoveButton ( std : : weak_ptr < MenuComponent > button ) {
auto componentSearchResults = std : : find_if ( components . begin ( ) , components . end ( ) , [ & ] ( std : : weak_ptr < MenuComponent > ptr ) { return & * ptr . lock ( ) = = & * button . lock ( ) ; } ) ;
auto subcomponentSearchResults = std : : find_if ( subcomponents . begin ( ) , subcomponents . end ( ) , [ & ] ( std : : weak_ptr < MenuComponent > ptr ) { return & * ptr . lock ( ) = = & * button . lock ( ) ; } ) ;
if ( componentSearchResults = = components . end ( ) & & subcomponentSearchResults = = subcomponents . end ( ) ) ERR ( " Could not find Component " < < std : : quoted ( button . lock ( ) - > GetName ( ) ) < < " inside the component or subcomponent lists! " ) ;
if ( componentSearchResults ! = components . end ( ) ) components . erase ( componentSearchResults ) ;
if ( subcomponentSearchResults ! = subcomponents . end ( ) ) subcomponents . erase ( subcomponentSearchResults ) ;
size_t removedCount = 0 ;
MenuType parentMenu = button . lock ( ) - > parentMenu ;
removedCount + = Menu : : menus [ parentMenu ] - > components . erase ( button . lock ( ) - > GetName ( ) ) ;
if ( removedCount ! = 1 ) {
LOG ( " WARNING! Attempted to remove buttons from button listing, but not found! " ) ;
}
CalculateBounds ( ) ;
}
virtual inline size_t GetMainComponentCount ( ) const {
return components . size ( ) ;
}
virtual inline size_t GetSubcomponentCount ( ) const {
return subcomponents . size ( ) ;
}
virtual inline void SetScrollAmount ( vf2d scrollOffset ) {
this - > targetScrollOffset = scrollOffset ;
}
//Provide an amount to scroll by in units/sec.
virtual inline void Scroll ( float amt ) {
SetScrollAmount ( GetTargetScrollAmount ( ) - vf2d { 0 , amt * game - > GetElapsedTime ( ) * " Interface.AnalogScrollSpeed " _F } ) ;
}
//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 SetSelectionIndex ( 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 ) ; }
}
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 ;
downButton = Menu : : menus [ parentMenu ] - > ADD ( name + vf2d ( rect . pos + rect . size - vf2d { 12 , 12 } ) . str ( ) + " _ " + vf2d ( 12 , 12 ) . str ( ) , MenuComponent ) ( geom2d : : rect < float > { rect . pos + rect . size - vf2d { 12 , 12 } , { 12 , 12 } } , " v " , [ & ] ( MenuFuncData dat ) { SetScrollAmount ( GetScrollAmount ( ) - vf2d { 0 , " ThemeGlobal.MenuButtonScrollSpeed " _F } ) ; return true ; } , ButtonAttr : : UNSELECTABLE_VIA_KEYBOARD ) DEPTH depth - 1 END ;
subWindow = ViewPort : : rectViewPort ( { } , rect . size , Menu : : menus [ parentMenu ] - > pos + rect . pos ) ;
if ( IsEnabled ( ) ) {
upButton . lock ( ) - > Enable ( ) ;
} else {
upButton . lock ( ) - > Disable ( ) ;
}
if ( IsEnabled ( ) ) {
downButton . lock ( ) - > Enable ( ) ;
} else {
downButton . lock ( ) - > Disable ( ) ;
}
}
virtual inline void BeforeUpdate ( AiL * game ) override {
MenuComponent : : BeforeUpdate ( game ) ;
for ( std : : weak_ptr < MenuComponent > component : components ) {
std : : shared_ptr < MenuComponent > componentPtr = component . lock ( ) ;
if ( componentPtr - > renderInMain ) ERR ( std : : format ( " WARNING! Component {} is inside a ScrollableWindowComponent but renders in main instead! Parent Component: {} " , componentPtr - > GetName ( ) , GetName ( ) ) ) ;
componentPtr - > _BeforeUpdate ( game ) ;
}
for ( const auto & component : subcomponents ) {
std : : shared_ptr < MenuComponent > componentPtr = component . lock ( ) ;
if ( componentPtr - > renderInMain ) ERR ( std : : format ( " WARNING! Subcomponent {} is inside a ScrollableWindowComponent but renders in main instead! Parent Component: {} " , componentPtr - > GetName ( ) , GetName ( ) ) ) ;
componentPtr - > _BeforeUpdate ( game ) ;
}
}
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 ;
auto ScrollToClickedPosition = [ & ] ( ) {
float spaceBetweenTopAndBottomArrows = rect . size . y - 24 ;
float viewHeight = rect . size . y ;
float totalContentHeight = bounds . size . y ;
if ( totalContentHeight = = 0 ) totalContentHeight = 1 ;
float scrollBarScale = ( spaceBetweenTopAndBottomArrows / totalContentHeight ) ;
//The scroll amount moves centered on the position the mouse is at.
float newScrollbarTop = ( game - > GetMousePos ( ) . y - windowAbsPos . y - 12 ) - scrollBarHeight / 2 ;
SetScrollAmount ( { GetScrollAmount ( ) . x , ( - newScrollbarTop + 1 ) / scrollBarScale } ) ;
} ;
bool mouseOverScrollbar = geom2d : : overlaps ( geom2d : : rect < float > ( windowAbsPos + vf2d { rect . size . x - 12 , scrollBarTop + 12 } , { 12 , scrollBarHeight } ) , game - > GetMousePos ( ) ) ;
if ( mouseOverScrollbar | | scrollBarSelected ) scrollBarHoverTime = std : : min ( scrollBarHoverTime + game - > GetElapsedTime ( ) , " ThemeGlobal.HighlightTime " _F ) ;
else scrollBarHoverTime = std : : max ( scrollBarHoverTime - game - > GetElapsedTime ( ) , 0.f ) ;
if ( scrollBarSelected ) {
if ( game - > GetMouse ( 0 ) . bPressed & & ! geom2d : : contains ( rect , bounds ) ) {
scrollBarSelected = true ;
Menu : : menus [ parentMenu ] - > alreadyClicked = true ;
}
if ( game - > GetMouse ( 0 ) . bReleased ) {
scrollBarSelected = false ;
Menu : : scrolling = false ;
}
if ( scrollBarSelected ) {
Menu : : scrolling = true ;
ScrollToClickedPosition ( ) ;
}
} else {
//It's possible to still scroll by clicking outside the scrollbar but in the scroll region.
if ( game - > GetMouse ( 0 ) . bHeld & & ! geom2d : : contains ( rect , bounds ) & &
geom2d : : overlaps ( geom2d : : rect < float > ( windowAbsPos + vf2d { rect . size . x - 12.f , 12.f } , { 12.f , rect . size . y - 24.f } ) , game - > GetMousePos ( ) ) ) {
ScrollToClickedPosition ( ) ;
scrollBarSelected = true ;
Menu : : menus [ parentMenu ] - > alreadyClicked = true ;
}
}
if ( game - > GetMouseWheel ( ) ! = 0 & & geom2d : : overlaps ( game - > GetMousePos ( ) , geom2d : : rect < float > { windowAbsPos , rect . size } ) ) {
if ( game - > GetMouseWheel ( ) > 0 ) {
SetScrollAmount ( GetTargetScrollAmount ( ) + vf2d { 0 , " ThemeGlobal.MenuScrollWheelSpeed " _F } ) ;
} else {
SetScrollAmount ( GetTargetScrollAmount ( ) - vf2d { 0 , " ThemeGlobal.MenuScrollWheelSpeed " _F } ) ;
}
}
if ( bounds . size . y - rect . size . y > 0 ) {
scrollOffset . y = 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 ( { 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 ; } ) ;
for ( std : : weak_ptr < MenuComponent > component : components ) {
if ( OnScreen ( component . lock ( ) ) ) {
component . lock ( ) - > EnableOutsideWindow ( ) ;
} else {
component . lock ( ) - > DisableOutsideWindow ( ) ;
}
component . lock ( ) - > _Update ( game ) ;
}
std : : sort ( subcomponents . begin ( ) , subcomponents . end ( ) , [ ] ( std : : weak_ptr < MenuComponent > c1 , std : : weak_ptr < MenuComponent > c2 ) { return c1 . lock ( ) - > depth > c2 . lock ( ) - > depth ; } ) ;
for ( std : : weak_ptr < MenuComponent > component : subcomponents ) {
if ( OnScreen ( component . lock ( ) ) ) {
component . lock ( ) - > EnableOutsideWindow ( ) ;
} else {
component . lock ( ) - > DisableOutsideWindow ( ) ;
}
component . lock ( ) - > _Update ( game ) ;
}
upButton . lock ( ) - > disable = false ;
downButton . lock ( ) - > disable = false ;
if ( geom2d : : contains ( rect , bounds ) ) { //This means we have no reason to show a scrollbar.
upButton . lock ( ) - > disable = true ;
downButton . lock ( ) - > disable = 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 ;
}
for ( std : : weak_ptr < MenuComponent > component : subcomponents ) {
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 ;
float viewHeight = rect . size . y ;
float totalContentHeight = bounds . size . y ;
if ( totalContentHeight = = 0 ) totalContentHeight = 1 ;
float scrollBarScale = ( spaceBetweenTopAndBottomArrows / totalContentHeight ) ;
scrollBarHeight = std : : min ( spaceBetweenTopAndBottomArrows , viewHeight * scrollBarScale - 1 ) ;
scrollBarTop = - GetScrollAmount ( ) . y * scrollBarScale + 1 ;
float focusedWindowColorMult = ( focused ? 1 : " ThemeGlobal.MenuUnfocusedColorMult " _F ) ;
window . FillRectDecal ( rect . pos + parentPos + vf2d { rect . size . x - 12.f , scrollBarTop + 12 } , { 12 , scrollBarHeight } , PixelLerp ( Menu : : GetCurrentTheme ( ) . GetButtonCol ( ) , Menu : : GetCurrentTheme ( ) . GetHighlightCol ( ) , scrollBarHoverTime / " ThemeGlobal.HighlightTime " _F ) * focusedWindowColorMult ) ;
window . DrawRectDecal ( rect . pos + parentPos + vf2d { rect . size . x - 12.f , 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 ) {
window . DrawRectDecal ( rect . pos , rect . size ) ;
}
for ( std : : weak_ptr < MenuComponent > component : components ) {
component . lock ( ) - > _DrawDecal ( subWindow , focused ) ;
}
for ( std : : weak_ptr < MenuComponent > component : subcomponents ) {
component . lock ( ) - > _DrawDecal ( subWindow , focused ) ;
}
if ( ! geom2d : : contains ( rect , bounds ) ) {
DrawScrollbar ( window , { } , focused ) ;
}
}
public :
//Calculates the bounds of all components.
inline void CalculateBounds ( ) {
bounds = { } ;
for ( std : : weak_ptr < MenuComponent > component : components ) {
if ( component . lock ( ) - > rect . pos . x < bounds . pos . x ) {
float sizeIncrease = bounds . pos . x - component . lock ( ) - > rect . pos . x ;
bounds . size . x + = sizeIncrease ;
bounds . pos . x = component . lock ( ) - > rect . pos . x ;
}
if ( component . lock ( ) - > rect . right ( ) . start . x > bounds . right ( ) . start . x ) {
float sizeIncrease = component . lock ( ) - > rect . right ( ) . start . x - bounds . right ( ) . start . x ;
bounds . size . x + = sizeIncrease ;
}
if ( component . lock ( ) - > rect . pos . y < bounds . pos . y ) {
float sizeIncrease = bounds . pos . y - component . lock ( ) - > rect . pos . y ;
bounds . size . y + = sizeIncrease ;
bounds . pos . y = component . lock ( ) - > rect . pos . y ;
}
if ( component . lock ( ) - > rect . bottom ( ) . start . y > bounds . bottom ( ) . start . y ) {
float sizeIncrease = component . lock ( ) - > rect . bottom ( ) . start . y - bounds . bottom ( ) . start . y ;
bounds . size . y + = sizeIncrease ;
}
}
for ( std : : weak_ptr < MenuComponent > component : subcomponents ) {
if ( component . lock ( ) - > rect . pos . x < bounds . pos . x ) {
float sizeIncrease = bounds . pos . x - component . lock ( ) - > rect . pos . x ;
bounds . size . x + = sizeIncrease ;
bounds . pos . x = component . lock ( ) - > rect . pos . x ;
}
if ( component . lock ( ) - > rect . right ( ) . start . x > bounds . right ( ) . start . x ) {
float sizeIncrease = component . lock ( ) - > rect . right ( ) . start . x - bounds . right ( ) . start . x ;
bounds . size . x + = sizeIncrease ;
}
if ( component . lock ( ) - > rect . pos . y < bounds . pos . y ) {
float sizeIncrease = bounds . pos . y - component . lock ( ) - > rect . pos . y ;
bounds . size . y + = sizeIncrease ;
bounds . pos . y = component . lock ( ) - > rect . pos . y ;
}
if ( component . lock ( ) - > rect . bottom ( ) . start . y > bounds . bottom ( ) . start . y ) {
float sizeIncrease = component . lock ( ) - > rect . bottom ( ) . start . y - bounds . bottom ( ) . start . y ;
bounds . size . y + = sizeIncrease ;
}
}
}
public :
template < class T >
std : : shared_ptr < T > _AddSubcomponent ( std : : string parentKey , std : : string key , std : : shared_ptr < T > button ) {
if ( Menu : : menus [ parentMenu ] - > components . count ( key ) ) {
ERR ( " WARNING! Key " < < key < < " for menu " < < parentMenu < < " already exists! Key names must be unique! " )
}
subcomponents . push_back ( button ) ;
button - > renderInMain = false ; //Now we are in control!
button - > parentComponent = DYNAMIC_POINTER_CAST < ScrollableWindowComponent > ( Menu : : menus [ parentMenu ] - > components [ this - > GetName ( ) ] ) ;
button - > disable = disable ;
CalculateBounds ( ) ;
Menu : : menus [ parentMenu ] - > _AddComponent ( key , button ) ;
button - > subcomponentParent = Menu : : menus [ parentMenu ] - > components . at ( parentKey ) ;
return button ;
}
template < class T >
std : : shared_ptr < T > _AddComponent ( std : : string key , std : : shared_ptr < T > button ) {
if ( Menu : : menus [ parentMenu ] - > components . count ( key ) ) {
ERR ( " WARNING! Key " < < key < < " for menu " < < parentMenu < < " already exists! Key names must be unique! " )
}
components . push_back ( button ) ;
button - > renderInMain = false ; //Now we are in control!
button - > parentComponent = DYNAMIC_POINTER_CAST < ScrollableWindowComponent > ( Menu : : menus [ parentMenu ] - > components [ this - > GetName ( ) ] ) ;
button - > disable = disable ;
CalculateBounds ( ) ;
Menu : : menus [ parentMenu ] - > _AddComponent ( key , button ) ;
return button ;
}
virtual inline bool PointWithinParent ( MenuComponent * child , vi2d drawPos ) override {
return geom2d : : overlaps ( geom2d : : rect < float > { Menu : : menus [ parentMenu ] - > pos + rect . pos , rect . size } , drawPos ) ;
}
virtual inline bool PointWithinParent ( MenuComponent * child , geom2d : : rect < float > drawRect ) override {
return geom2d : : overlaps ( geom2d : : rect < float > { Menu : : menus [ parentMenu ] - > pos + rect . pos , rect . size } , drawRect ) ;
}
virtual inline bool HandleOutsideDisabledButtonSelection ( std : : weak_ptr < MenuComponent > disabledButton ) override {
//Set the offset so the center is highlighted by this button.
SetScrollAmount ( vf2d { GetScrollAmount ( ) . x , GetScrollAmount ( ) . y - disabledButton . lock ( ) - > rect . pos . y + disabledButton . lock ( ) - > rect . size . y } ) ;
return true ;
} ;
virtual void Cleanup ( ) override { }
inline std : : vector < std : : weak_ptr < MenuComponent > > & GetComponents ( ) {
return components ;
}
inline std : : vector < std : : weak_ptr < MenuComponent > > & GetSubcomponents ( ) {
return subcomponents ;
}
virtual inline void Enable ( ) override final {
MenuComponent : : Enable ( ) ;
for ( std : : weak_ptr < MenuComponent > component : components ) {
component . lock ( ) - > Enable ( ) ;
}
for ( std : : weak_ptr < MenuComponent > component : subcomponents ) {
component . lock ( ) - > Enable ( ) ;
}
if ( upButton . lock ( ) ) { upButton . lock ( ) - > Enable ( ) ; }
if ( downButton . lock ( ) ) { downButton . lock ( ) - > Enable ( ) ; }
} ;
virtual inline void Disable ( ) override final {
MenuComponent : : Disable ( ) ;
for ( std : : weak_ptr < MenuComponent > component : components ) {
component . lock ( ) - > Disable ( ) ;
}
for ( std : : weak_ptr < MenuComponent > component : subcomponents ) {
component . lock ( ) - > Disable ( ) ;
}
if ( upButton . lock ( ) ) { upButton . lock ( ) - > Disable ( ) ; }
if ( downButton . lock ( ) ) { downButton . lock ( ) - > Disable ( ) ; }
} ;
} ;