2014-02-08 02:15:48 +00:00
# pragma once
2014-02-08 03:45:28 +00:00
# include <string>
# include <vector>
2014-02-13 23:10:28 +00:00
# include <memory>
# include "../GuiComponent.h"
# include "ImageComponent.h"
# include "../resources/Font.h"
2014-02-08 02:15:48 +00:00
2014-02-08 03:45:28 +00:00
enum CursorState
{
CURSOR_STOPPED ,
CURSOR_SCROLLING
} ;
2014-02-08 02:15:48 +00:00
2014-02-08 03:45:28 +00:00
struct ScrollTier
{
int length ; // how long we stay on this level before going to the next
int scrollDelay ; // how long between scrolls
} ;
2014-02-08 02:15:48 +00:00
2014-02-08 03:45:28 +00:00
const int SCROLL_SPEED_COUNT = 3 ;
const ScrollTier SCROLL_SPEED [ SCROLL_SPEED_COUNT ] = {
{ 500 , 500 } ,
2014-02-13 23:10:28 +00:00
{ 5000 , 114 } ,
{ 0 , 8 }
2014-02-08 03:45:28 +00:00
} ;
2014-02-08 02:15:48 +00:00
2014-02-08 03:45:28 +00:00
template < typename EntryData , typename UserData >
2014-02-13 23:10:28 +00:00
class IList : public GuiComponent
2014-02-08 03:45:28 +00:00
{
public :
struct Entry
2014-02-08 02:15:48 +00:00
{
2014-02-08 03:45:28 +00:00
std : : string name ;
UserData object ;
EntryData data ;
2014-02-08 02:15:48 +00:00
} ;
2014-02-08 03:45:28 +00:00
protected :
int mCursor ;
2014-02-08 02:15:48 +00:00
int mScrollTier ;
int mScrollVelocity ;
int mScrollTierAccumulator ;
int mScrollCursorAccumulator ;
2014-02-08 03:45:28 +00:00
2014-02-13 23:10:28 +00:00
unsigned char mTitleOverlayOpacity ;
unsigned int mTitleOverlayColor ;
ImageComponent mGradient ;
std : : shared_ptr < Font > mTitleOverlayFont ;
2014-02-08 03:45:28 +00:00
std : : vector < Entry > mEntries ;
public :
2014-02-13 23:10:28 +00:00
IList ( Window * window ) : GuiComponent ( window ) , mGradient ( window )
2014-02-08 03:45:28 +00:00
{
mCursor = 0 ;
mScrollTier = 0 ;
mScrollVelocity = 0 ;
mScrollTierAccumulator = 0 ;
mScrollCursorAccumulator = 0 ;
2014-02-13 23:10:28 +00:00
mTitleOverlayOpacity = 0x00 ;
mTitleOverlayColor = 0xFFFFFF00 ;
mGradient . setImage ( " :/scroll_gradient.png " ) ;
mGradient . setColorShift ( 0x000040FF ) ;
mGradient . setOpacity ( 0 ) ;
mTitleOverlayFont = Font : : get ( FONT_SIZE_LARGE ) ;
2014-02-08 03:45:28 +00:00
}
bool isScrolling ( ) const
{
return ( mScrollVelocity ! = 0 & & mScrollTier > 0 ) ;
}
void stopScrolling ( )
{
listInput ( 0 ) ;
onCursorChanged ( CURSOR_STOPPED ) ;
}
void clear ( )
{
mEntries . clear ( ) ;
mCursor = 0 ;
listInput ( 0 ) ;
onCursorChanged ( CURSOR_STOPPED ) ;
}
inline const std : : string & getSelectedName ( )
{
assert ( size ( ) > 0 ) ;
return mEntries . at ( mCursor ) . name ;
}
inline const UserData & getSelected ( ) const
{
assert ( size ( ) > 0 ) ;
return mEntries . at ( mCursor ) . object ;
}
void setCursor ( typename std : : vector < Entry > : : iterator & it )
{
assert ( it ! = mEntries . end ( ) ) ;
mCursor = it - mEntries . begin ( ) ;
onCursorChanged ( CURSOR_STOPPED ) ;
}
// returns true if successful (select is in our list), false if not
bool setCursor ( const UserData & obj )
{
for ( auto it = mEntries . begin ( ) ; it ! = mEntries . end ( ) ; it + + )
{
if ( ( * it ) . object = = obj )
{
mCursor = it - mEntries . begin ( ) ;
onCursorChanged ( CURSOR_STOPPED ) ;
return true ;
}
}
return false ;
}
// entry management
void add ( Entry e )
{
mEntries . push_back ( e ) ;
}
bool remove ( const UserData & obj )
{
for ( auto it = mEntries . begin ( ) ; it ! = mEntries . end ( ) ; it + + )
{
if ( ( * it ) . object = = obj )
{
remove ( it ) ;
return true ;
}
}
return false ;
}
inline int size ( ) const { return mEntries . size ( ) ; }
protected :
void remove ( typename std : : vector < Entry > : : iterator & it )
{
if ( getCursorIndex ( ) > 0 & & it - mEntries . begin ( ) < = getCursorIndex ( ) )
{
setCursorIndex ( mCursor - 1 ) ;
onCursorChanged ( CURSOR_STOPPED ) ;
}
mEntries . erase ( it ) ;
}
void listInput ( int velocity ) // a velocity of 0 = stop scrolling
{
mScrollVelocity = velocity ;
mScrollTier = 0 ;
mScrollTierAccumulator = 0 ;
mScrollCursorAccumulator = 0 ;
scroll ( mScrollVelocity ) ;
}
void listUpdate ( int deltaTime )
{
2014-02-13 23:10:28 +00:00
// update the title overlay opacity
const int dir = ( mScrollTier > = SCROLL_SPEED_COUNT - 1 ) ? 1 : - 1 ; // fade in if scroll tier is >= 1, otherwise fade out
int op = mTitleOverlayOpacity + deltaTime * dir ; // we just do a 1-to-1 time -> opacity, no scaling
if ( op > = 255 )
mTitleOverlayOpacity = 255 ;
else if ( op < = 0 )
mTitleOverlayOpacity = 0 ;
else
mTitleOverlayOpacity = ( unsigned char ) op ;
2014-02-08 03:45:28 +00:00
if ( mScrollVelocity = = 0 | | size ( ) < 2 )
return ;
mScrollCursorAccumulator + = deltaTime ;
mScrollTierAccumulator + = deltaTime ;
2014-02-13 23:10:28 +00:00
// we delay scrolling until after scroll tier has updated so isScrolling() returns accurately during onCursorChanged callbacks
// we don't just do scroll tier first because it would not catch the scrollDelay == tier length case
2014-02-08 04:17:24 +00:00
int scrollCount = 0 ;
2014-02-08 03:45:28 +00:00
while ( mScrollCursorAccumulator > = SCROLL_SPEED [ mScrollTier ] . scrollDelay )
{
mScrollCursorAccumulator - = SCROLL_SPEED [ mScrollTier ] . scrollDelay ;
2014-02-08 04:17:24 +00:00
scrollCount + + ;
2014-02-08 03:45:28 +00:00
}
// are we ready to go even FASTER?
while ( mScrollTier < SCROLL_SPEED_COUNT - 1 & & mScrollTierAccumulator > = SCROLL_SPEED [ mScrollTier ] . length )
{
mScrollTierAccumulator - = SCROLL_SPEED [ mScrollTier ] . length ;
mScrollTier + + ;
}
2014-02-08 04:17:24 +00:00
2014-02-13 23:10:28 +00:00
// actually perform the scrolling
2014-02-08 04:17:24 +00:00
for ( int i = 0 ; i < scrollCount ; i + + )
scroll ( mScrollVelocity ) ;
2014-02-08 03:45:28 +00:00
}
2014-02-13 23:10:28 +00:00
void listRenderTitleOverlay ( const Eigen : : Affine3f & trans )
{
if ( size ( ) = = 0 | | ! mTitleOverlayFont | | mTitleOverlayOpacity = = 0 )
return ;
// we don't bother caching this because it's only two letters and will change pretty much every frame if we're scrolling
const std : : string text = getSelectedName ( ) . size ( ) > = 2 ? getSelectedName ( ) . substr ( 0 , 2 ) : " ?? " ;
Eigen : : Vector2f off = mTitleOverlayFont - > sizeText ( text ) ;
off [ 0 ] = ( mSize . x ( ) - off . x ( ) ) * 0.7f ;
off [ 1 ] = ( mSize . y ( ) - off . y ( ) ) * 0.5f ;
mGradient . setOpacity ( mTitleOverlayOpacity ) ;
mGradient . render ( trans ) ;
mTitleOverlayFont - > drawText ( text , off , ( mTitleOverlayColor & 0xFFFFFF00 ) | mTitleOverlayOpacity ) ; // relies on mGradient's render to Renderer::setMatrix(trans)
}
virtual void onSizeChanged ( ) override
{
mGradient . setResize ( mSize ) ;
}
2014-02-08 03:45:28 +00:00
void scroll ( int amt )
{
if ( mScrollVelocity = = 0 | | size ( ) < 2 )
return ;
int cursor = mCursor + amt ;
int absAmt = amt < 0 ? - amt : amt ;
// stop at the end if we've been holding down the button for a long time or
// we're scrolling faster than one item at a time (e.g. page up/down)
// otherwise, loop around
if ( mScrollTier > 0 | | absAmt > 1 )
{
if ( cursor < 0 )
cursor = 0 ;
else if ( cursor > = size ( ) )
cursor = size ( ) - 1 ;
} else {
if ( cursor < 0 )
cursor + = size ( ) ;
else if ( cursor > = size ( ) )
cursor - = size ( ) ;
}
if ( cursor ! = mCursor )
onScroll ( absAmt ) ;
mCursor = cursor ;
onCursorChanged ( ( mScrollTier > 0 ) ? CURSOR_SCROLLING : CURSOR_STOPPED ) ;
}
virtual void onCursorChanged ( const CursorState & state ) { }
virtual void onScroll ( int amt ) { }
2014-02-08 02:15:48 +00:00
} ;