2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "guiGameListMenuCtrl.h"
# include "console/consoleTypes.h"
# include "console/engineAPI.h"
# include "gfx/gfxDrawUtil.h"
2020-05-20 22:19:52 +00:00
# include "gui/containers/guiScrollCtrl.h"
# include "sim\actionMap.h"
# include "core\strings\stringUnit.h"
2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// GuiGameListMenuCtrl
//-----------------------------------------------------------------------------
GuiGameListMenuCtrl : : GuiGameListMenuCtrl ( )
: mSelected ( NO_ROW ) ,
2017-04-11 04:45:02 +00:00
mDebugRender ( false ) ,
2020-05-20 22:19:52 +00:00
mHighlighted ( NO_ROW ) ,
mCallbackOnInputs ( false )
2012-09-19 15:15:01 +00:00
{
VECTOR_SET_ASSOCIATION ( mRows ) ;
// initialize the control callbacks
2017-01-12 04:21:29 +00:00
mCallbackOnA = StringTable - > EmptyString ( ) ;
2012-09-19 15:15:01 +00:00
mCallbackOnB = mCallbackOnA ;
mCallbackOnX = mCallbackOnA ;
mCallbackOnY = mCallbackOnA ;
}
GuiGameListMenuCtrl : : ~ GuiGameListMenuCtrl ( )
{
for ( S32 i = 0 ; i < mRows . size ( ) ; + + i )
{
delete mRows [ i ] ;
}
}
void GuiGameListMenuCtrl : : onRender ( Point2I offset , const RectI & updateRect )
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
2015-07-14 03:51:17 +00:00
GFXDrawUtil * drawUtil = GFX - > getDrawUtil ( ) ;
2012-09-19 15:15:01 +00:00
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
bool profileHasIcons = profile - > hasArrows ( ) ;
S32 rowHeight = profile - > getRowHeight ( ) ;
Point2I currentOffset = offset ;
Point2I extent = getExtent ( ) ;
Point2I rowExtent ( extent . x , rowHeight ) ;
Point2I textOffset ( profile - > mTextOffset . x * xScale , profile - > mTextOffset . y ) ;
Point2I textExtent ( extent . x - textOffset . x , rowHeight ) ;
Point2I iconExtent , iconOffset ( 0.0f , 0.0f ) ;
if ( profileHasIcons )
{
iconExtent = profile - > getIconExtent ( ) ;
// icon is centered vertically plus any specified offset
S32 iconOffsetY = ( rowHeight - iconExtent . y ) > > 1 ;
iconOffsetY + = profile - > mIconOffset . y ;
iconOffset = Point2I ( profile - > mIconOffset . x * xScale , iconOffsetY ) ;
}
for ( Vector < Row * > : : iterator row = mRows . begin ( ) ; row < mRows . end ( ) ; + + row )
{
if ( row ! = mRows . begin ( ) )
{
// rows other than the first can have padding above them
currentOffset . y + = ( * row ) - > mHeightPad ;
currentOffset . y + = rowHeight ;
}
// select appropriate colors and textures
ColorI fontColor ;
U32 buttonTextureIndex ;
S32 iconIndex = ( * row ) - > mIconIndex ;
bool useHighlightIcon = ( * row ) - > mUseHighlightIcon ;
2020-05-20 22:19:52 +00:00
if ( ! ( * row ) - > mEnabled )
2012-09-19 15:15:01 +00:00
{
buttonTextureIndex = Profile : : TEX_DISABLED ;
fontColor = profile - > mFontColorNA ;
}
else if ( row = = & mRows [ mSelected ] )
{
if ( iconIndex ! = NO_ICON )
{
iconIndex + + ;
}
buttonTextureIndex = Profile : : TEX_SELECTED ;
fontColor = profile - > mFontColorSEL ;
}
else if ( ( mHighlighted ! = NO_ROW ) & & ( row = = & mRows [ mHighlighted ] ) )
{
if ( iconIndex ! = NO_ICON & & useHighlightIcon )
{
iconIndex + + ;
}
buttonTextureIndex = Profile : : TEX_HIGHLIGHT ;
fontColor = profile - > mFontColorHL ;
}
else
{
buttonTextureIndex = Profile : : TEX_NORMAL ;
fontColor = profile - > mFontColor ;
}
// render the row bitmap
2015-07-14 03:51:17 +00:00
drawUtil - > clearBitmapModulation ( ) ;
drawUtil - > drawBitmapStretchSR ( profile - > mTextureObject , RectI ( currentOffset , rowExtent ) , profile - > getBitmapArrayRect ( buttonTextureIndex ) ) ;
2012-09-19 15:15:01 +00:00
// render the row icon if it has one
2020-05-20 22:19:52 +00:00
if ( ( iconIndex ! = NO_ICON ) & & profileHasIcons & & ( ! profile - > getBitmapArrayRect ( ( U32 ) iconIndex ) . extent . isZero ( ) ) )
2012-09-19 15:15:01 +00:00
{
iconIndex + = Profile : : TEX_FIRST_ICON ;
2015-07-14 03:51:17 +00:00
drawUtil - > clearBitmapModulation ( ) ;
drawUtil - > drawBitmapStretchSR ( profile - > mTextureObject , RectI ( currentOffset + iconOffset , iconExtent ) , profile - > getBitmapArrayRect ( iconIndex ) ) ;
2012-09-19 15:15:01 +00:00
}
// render the row text
2015-07-14 03:51:17 +00:00
drawUtil - > setBitmapModulation ( fontColor ) ;
2012-09-19 15:15:01 +00:00
renderJustifiedText ( currentOffset + textOffset , textExtent , ( * row ) - > mLabel ) ;
2020-05-20 22:19:52 +00:00
if ( ( * row ) - > mMode = = Row : : Mode : : OptionList )
{
2020-05-25 05:51:33 +00:00
onRenderListOption ( ( * row ) , currentOffset ) ;
}
else if ( ( * row ) - > mMode = = Row : : Mode : : Slider )
{
onRenderSliderOption ( ( * row ) , currentOffset ) ;
}
else if ( ( * row ) - > mMode = = Row : : Mode : : Keybind )
{
onRenderKeybindOption ( ( * row ) , currentOffset ) ;
2020-05-20 22:19:52 +00:00
}
2012-09-19 15:15:01 +00:00
}
if ( mDebugRender )
{
onDebugRender ( offset ) ;
}
renderChildControls ( offset , updateRect ) ;
}
2020-05-25 05:51:33 +00:00
void GuiGameListMenuCtrl : : onRenderListOption ( Row * row , Point2I currentOffset )
2020-05-20 22:19:52 +00:00
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
S32 rowHeight = profile - > getRowHeight ( ) ;
bool profileHasArrows = profile - > hasArrows ( ) ;
Point2I arrowExtent ;
S32 arrowOffsetY ( 0 ) ;
if ( profileHasArrows )
{
arrowExtent = profile - > getArrowExtent ( ) ;
// icon is centered vertically
arrowOffsetY = ( rowHeight - arrowExtent . y ) > > 1 ;
}
GFXDrawUtil * drawer = GFX - > getDrawUtil ( ) ;
Point2I arrowOffset ;
S32 columnSplit = profile - > mColumnSplit * xScale ;
S32 iconIndex ;
bool hasOptions = ( row - > mOptions . size ( ) > 0 ) & & row - > mSelectedOption > - 1 ;
if ( hasOptions )
{
bool isRowSelected = ( getSelected ( ) ! = NO_ROW ) & & ( row = = mRows [ getSelected ( ) ] ) ;
bool isRowHighlighted = ( getHighlighted ( ) ! = NO_ROW ) ? ( ( row = = mRows [ getHighlighted ( ) ] ) & & ( row - > mEnabled ) ) : false ;
if ( profileHasArrows )
{
// render the left arrow
bool arrowOnL = ( isRowSelected | | isRowHighlighted ) & & ( row - > mWrapOptions | | ( row - > mSelectedOption > 0 ) ) ;
iconIndex = ( arrowOnL ) ? Profile : : TEX_L_ARROW_ON : Profile : : TEX_L_ARROW_OFF ;
arrowOffset . x = currentOffset . x + columnSplit ;
arrowOffset . y = currentOffset . y + arrowOffsetY ;
drawer - > clearBitmapModulation ( ) ;
drawer - > drawBitmapStretchSR ( profile - > mTextureObject , RectI ( arrowOffset , arrowExtent ) , profile - > getBitmapArrayRect ( ( U32 ) iconIndex ) ) ;
// render the right arrow
bool arrowOnR = ( isRowSelected | | isRowHighlighted ) & & ( row - > mWrapOptions | | ( row - > mSelectedOption < row - > mOptions . size ( ) - 1 ) ) ;
iconIndex = ( arrowOnR ) ? Profile : : TEX_R_ARROW_ON : Profile : : TEX_R_ARROW_OFF ;
arrowOffset . x = currentOffset . x + ( profile - > mHitAreaLowerRight . x - profile - > mRightPad ) * xScale - arrowExtent . x ;
arrowOffset . y = currentOffset . y + arrowOffsetY ;
drawer - > clearBitmapModulation ( ) ;
drawer - > drawBitmapStretchSR ( profile - > mTextureObject , RectI ( arrowOffset , arrowExtent ) , profile - > getBitmapArrayRect ( ( U32 ) iconIndex ) ) ;
}
// get the appropriate font color
ColorI fontColor ;
if ( ! row - > mEnabled )
{
fontColor = profile - > mFontColorNA ;
}
else if ( isRowSelected )
{
fontColor = profile - > mFontColorSEL ;
}
else if ( isRowHighlighted )
{
fontColor = profile - > mFontColorHL ;
}
else
{
fontColor = profile - > mFontColor ;
}
// calculate text to be at the center between the arrows
GFont * font = profile - > mFont ;
StringTableEntry text = row - > mOptions [ row - > mSelectedOption ] ;
S32 textWidth = font - > getStrWidth ( text ) ;
S32 columnWidth = profile - > mHitAreaLowerRight . x * xScale - profile - > mRightPad - columnSplit ;
S32 columnCenter = columnSplit + ( columnWidth > > 1 ) ;
S32 textStartX = columnCenter - ( textWidth > > 1 ) ;
Point2I textOffset ( textStartX , 0 ) ;
// render the option text itself
Point2I textExtent ( columnWidth , rowHeight ) ;
drawer - > setBitmapModulation ( fontColor ) ;
renderJustifiedText ( currentOffset + textOffset , textExtent , text ) ;
}
}
2020-05-25 05:51:33 +00:00
void GuiGameListMenuCtrl : : onRenderSliderOption ( Row * row , Point2I currentOffset )
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
S32 rowHeight = profile - > getRowHeight ( ) ;
bool profileHasArrows = profile - > hasArrows ( ) ;
Point2I arrowExtent ;
S32 arrowOffsetY ( 0 ) ;
if ( profileHasArrows )
{
arrowExtent = profile - > getArrowExtent ( ) ;
// icon is centered vertically
arrowOffsetY = ( rowHeight - arrowExtent . y ) > > 1 ;
}
GFXDrawUtil * drawer = GFX - > getDrawUtil ( ) ;
Point2I arrowOffset ;
S32 columnSplit = profile - > mColumnSplit * xScale ;
S32 iconIndex ;
bool isRowSelected = ( getSelected ( ) ! = NO_ROW ) & & ( row = = mRows [ getSelected ( ) ] ) ;
bool isRowHighlighted = ( getHighlighted ( ) ! = NO_ROW ) ? ( ( row = = mRows [ getHighlighted ( ) ] ) & & ( row - > mEnabled ) ) : false ;
if ( profileHasArrows )
{
// render the left arrow
bool arrowOnL = ( isRowSelected | | isRowHighlighted ) & & ( row - > mValue > row - > mRange . x ) ;
iconIndex = ( arrowOnL ) ? Profile : : TEX_L_ARROW_ON : Profile : : TEX_L_ARROW_OFF ;
arrowOffset . x = currentOffset . x + columnSplit ;
arrowOffset . y = currentOffset . y + arrowOffsetY ;
drawer - > clearBitmapModulation ( ) ;
drawer - > drawBitmapStretchSR ( profile - > mTextureObject , RectI ( arrowOffset , arrowExtent ) , profile - > getBitmapArrayRect ( ( U32 ) iconIndex ) ) ;
// render the right arrow
bool arrowOnR = ( isRowSelected | | isRowHighlighted ) & & ( row - > mValue < row - > mRange . y ) ;
iconIndex = ( arrowOnR ) ? Profile : : TEX_R_ARROW_ON : Profile : : TEX_R_ARROW_OFF ;
arrowOffset . x = currentOffset . x + ( profile - > mHitAreaLowerRight . x - profile - > mRightPad ) * xScale - arrowExtent . x ;
arrowOffset . y = currentOffset . y + arrowOffsetY ;
drawer - > clearBitmapModulation ( ) ;
drawer - > drawBitmapStretchSR ( profile - > mTextureObject , RectI ( arrowOffset , arrowExtent ) , profile - > getBitmapArrayRect ( ( U32 ) iconIndex ) ) ;
}
//Draw the slider bar
if ( row - > mEnabled )
{
RectI sliderRect ;
sliderRect . point . x = currentOffset . x + columnSplit + arrowExtent . x ;
sliderRect . point . y = currentOffset . y + arrowOffsetY ;
sliderRect . extent . x = ( currentOffset . x + ( profile - > mHitAreaLowerRight . x - profile - > mRightPad ) * xScale - arrowExtent . x ) - sliderRect . point . x ;
sliderRect . extent . y = arrowExtent . y ;
//Now adjust the bar to match-to our value
S32 barStart = sliderRect . point . x ;
S32 barEnd = sliderRect . point . x + sliderRect . extent . x ;
S32 xPosFill = ( ( ( row - > mValue - row - > mRange . x ) * ( barEnd - barStart ) ) / ( row - > mRange . y - row - > mRange . x ) ) + barStart ;
RectI fillRect = sliderRect ;
fillRect . extent . x = xPosFill - sliderRect . point . x ;
ColorI barColor ;
ColorI barOutlineColor ;
if ( isRowSelected )
{
barColor = profile - > mFillColorHL ;
barOutlineColor = profile - > mFillColor ;
}
else
{
barColor = profile - > mFillColor ;
barOutlineColor = profile - > mFillColorHL ;
}
drawer - > drawRectFill ( fillRect , barColor ) ;
drawer - > drawRect ( sliderRect , barOutlineColor ) ;
}
// get the appropriate font color
ColorI fontColor ;
if ( ! row - > mEnabled )
{
fontColor = profile - > mFontColorNA ;
}
else if ( isRowSelected )
{
fontColor = profile - > mFontColorSEL ;
}
else if ( isRowHighlighted )
{
fontColor = profile - > mFontColorHL ;
}
else
{
fontColor = profile - > mFontColor ;
}
// calculate text to be at the center between the arrows
GFont * font = profile - > mFont ;
ConsoleValue val ;
val . setFloatValue ( row - > mValue ) ;
const char * stringVal = val . getStringValue ( ) ;
S32 textWidth = font - > getStrWidth ( stringVal ) ;
S32 columnWidth = profile - > mHitAreaLowerRight . x * xScale - profile - > mRightPad - columnSplit ;
S32 columnCenter = columnSplit + ( columnWidth > > 1 ) ;
S32 textStartX = columnCenter - ( textWidth > > 1 ) ;
Point2I textOffset ( textStartX , 0 ) ;
// render the option text itself
Point2I textExtent ( columnWidth , rowHeight ) ;
drawer - > setBitmapModulation ( fontColor ) ;
renderJustifiedText ( currentOffset + textOffset , textExtent , stringVal ) ;
}
void GuiGameListMenuCtrl : : onRenderKeybindOption ( Row * row , Point2I currentOffset )
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
S32 columnSplit = profile - > mColumnSplit * xScale ;
S32 rowHeight = profile - > getRowHeight ( ) ;
S32 optionWidth = xScale - columnSplit ;
GFXDrawUtil * drawer = GFX - > getDrawUtil ( ) ;
//drawer->drawBitmap(row->mBitmap, )
Point2I button ;
button . x = currentOffset . x + columnSplit + ( columnSplit / 2 ) /* + (optionWidth / 2)*/ ;
button . y = currentOffset . y + ( rowHeight / 4 ) ;
Point2I buttonSize ;
buttonSize . x = rowHeight / 2 ;
buttonSize . y = rowHeight / 2 ;
if ( row - > mBitmapTex . isValid ( ) )
{
GFXTextureObject * texture = row - > mBitmapTex ;
RectI rect ( button , buttonSize ) ;
drawer - > clearBitmapModulation ( ) ;
drawer - > drawBitmapStretch ( texture , rect , GFXBitmapFlip_None , GFXTextureFilterLinear , false ) ;
}
//drawer->drawRectFill(button, ColorI::BLUE);
}
2012-09-19 15:15:01 +00:00
void GuiGameListMenuCtrl : : onDebugRender ( Point2I offset )
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
ColorI controlBorderColor ( 200 , 200 , 200 ) ; // gray
ColorI rowBorderColor ( 255 , 127 , 255 ) ; // magenta
ColorI hitBorderColor ( 255 , 0 , 0 ) ; // red
Point2I shrinker ( - 1 , - 1 ) ;
Point2I extent = getExtent ( ) ;
// render a border around the entire control
RectI borderRect ( offset , extent + shrinker ) ;
GFX - > getDrawUtil ( ) - > drawRect ( borderRect , controlBorderColor ) ;
S32 rowHeight = profile - > getRowHeight ( ) ;
Point2I currentOffset ( offset ) ;
Point2I rowExtent ( extent . x , rowHeight ) ;
rowExtent + = shrinker ;
Point2I hitAreaExtent ( profile - > getHitAreaExtent ( ) ) ;
hitAreaExtent . x * = xScale ;
hitAreaExtent + = shrinker ;
Point2I hitAreaOffset = profile - > mHitAreaUpperLeft ;
hitAreaOffset . x * = xScale ;
Point2I upperLeft ;
for ( Vector < Row * > : : iterator row = mRows . begin ( ) ; row < mRows . end ( ) ; + + row )
{
// set the top of the current row
if ( row ! = mRows . begin ( ) )
{
// rows other than the first can have padding above them
currentOffset . y + = ( * row ) - > mHeightPad ;
currentOffset . y + = rowHeight ;
}
// draw the box around the whole row's extent
upperLeft = currentOffset ;
borderRect . point = upperLeft ;
borderRect . extent = rowExtent ;
GFX - > getDrawUtil ( ) - > drawRect ( borderRect , rowBorderColor ) ;
// draw the box around the hit area of the row
upperLeft = currentOffset + hitAreaOffset ;
borderRect . point = upperLeft ;
borderRect . extent = hitAreaExtent ;
GFX - > getDrawUtil ( ) - > drawRect ( borderRect , hitBorderColor ) ;
}
}
2020-05-25 05:51:33 +00:00
void GuiGameListMenuCtrl : : addRow ( const char * label , const char * callback , S32 icon , S32 yPad , bool useHighlightIcon , bool enabled , S32 mode , const char * tooltip )
2012-09-19 15:15:01 +00:00
{
Row * row = new Row ( ) ;
2020-05-25 05:51:33 +00:00
addRow ( row , label , callback , icon , yPad , useHighlightIcon , enabled , mode , tooltip ) ;
2012-09-19 15:15:01 +00:00
}
2020-05-25 05:51:33 +00:00
void GuiGameListMenuCtrl : : addRow ( Row * row , const char * label , const char * callback , S32 icon , S32 yPad , bool useHighlightIcon , bool enabled , S32 mode , const char * tooltip )
2012-09-19 15:15:01 +00:00
{
row - > mLabel = StringTable - > insert ( label , true ) ;
row - > mScriptCallback = ( dStrlen ( callback ) > 0 ) ? StringTable - > insert ( callback , true ) : NULL ;
row - > mIconIndex = ( icon < 0 ) ? NO_ICON : icon ;
row - > mHeightPad = yPad ;
row - > mUseHighlightIcon = useHighlightIcon ;
row - > mEnabled = enabled ;
2020-05-20 22:19:52 +00:00
row - > mMode = ( Row : : Mode ) mode ;
2020-05-25 05:51:33 +00:00
row - > mTooltip = StringTable - > insert ( tooltip ) ;
2012-09-19 15:15:01 +00:00
mRows . push_back ( row ) ;
updateHeight ( ) ;
if ( mSelected = = NO_ROW )
{
selectFirstEnabledRow ( ) ;
}
}
2020-05-25 05:51:33 +00:00
void GuiGameListMenuCtrl : : addRow ( const char * label , const char * optionsList , bool wrapOptions , const char * callback , S32 icon , S32 yPad , bool enabled , const char * tooltip , const char * defaultValue )
2020-05-20 22:19:52 +00:00
{
static StringTableEntry DELIM = StringTable - > insert ( " \t " , true ) ;
Row * row = new Row ( ) ;
Vector < StringTableEntry > options ( __FILE__ , __LINE__ ) ;
2020-05-25 05:51:33 +00:00
S32 defaultOption = 0 ;
2020-05-20 22:19:52 +00:00
S32 count = StringUnit : : getUnitCount ( optionsList , DELIM ) ;
for ( S32 i = 0 ; i < count ; + + i )
{
const char * option = StringUnit : : getUnit ( optionsList , i , DELIM ) ;
options . push_back ( StringTable - > insert ( option , true ) ) ;
2020-05-25 05:51:33 +00:00
if ( dStrcmp ( option , defaultValue ) = = 0 )
defaultOption = options . size ( ) - 1 ;
2020-05-20 22:19:52 +00:00
}
row - > mOptions = options ;
bool hasOptions = row - > mOptions . size ( ) > 0 ;
2020-05-25 05:51:33 +00:00
row - > mSelectedOption = ( hasOptions ) ? defaultOption : NO_OPTION ;
2020-05-20 22:19:52 +00:00
row - > mWrapOptions = wrapOptions ;
2020-05-25 05:51:33 +00:00
addRow ( row , label , callback , icon , yPad , true , ( hasOptions ) ? enabled : false , Row : : Mode : : OptionList , tooltip ) ;
}
void GuiGameListMenuCtrl : : addRow ( const char * label , F32 defaultValue , F32 increments , Point2F range , const char * callback , S32 icon , S32 yPad , bool enabled , const char * tooltip )
{
static StringTableEntry DELIM = StringTable - > insert ( " \t " , true ) ;
Row * row = new Row ( ) ;
row - > mValue = defaultValue ;
row - > mStepSize = increments ;
row - > mRange = range ;
addRow ( row , label , callback , icon , yPad , true , enabled , Row : : Mode : : Slider , tooltip ) ;
}
void GuiGameListMenuCtrl : : addRow ( const char * label , const char * bitmapName , const char * callback , S32 icon , S32 yPad , bool enabled , const char * tooltip )
{
static StringTableEntry DELIM = StringTable - > insert ( " \t " , true ) ;
Row * row = new Row ( ) ;
row - > mBitmap = StringTable - > insert ( bitmapName ) ;
if ( row - > mBitmap ! = StringTable - > EmptyString ( ) )
row - > mBitmapTex . set ( row - > mBitmap , & GFXDefaultGUIProfile , avar ( " %s() - mTextureObject (line %d) " , __FUNCTION__ , __LINE__ ) ) ;
addRow ( row , label , callback , icon , yPad , true , enabled , Row : : Mode : : Keybind , tooltip ) ;
2020-05-20 22:19:52 +00:00
}
2012-09-19 15:15:01 +00:00
Point2I GuiGameListMenuCtrl : : getMinExtent ( ) const
{
Point2I parentMin = Parent : : getMinExtent ( ) ;
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
S32 minHeight = 0 ;
S32 rowHeight = profile - > getRowHeight ( ) ;
for ( Vector < Row * > : : const_iterator row = mRows . begin ( ) ; row < mRows . end ( ) ; + + row )
{
minHeight + = rowHeight ;
if ( row ! = mRows . begin ( ) )
{
minHeight + = ( * row ) - > mHeightPad ;
}
}
if ( minHeight > parentMin . y )
parentMin . y = minHeight ;
return parentMin ;
}
bool GuiGameListMenuCtrl : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
// If we have a non-GuiGameListMenuProfile profile, try to
// substitute it for DefaultListMenuProfile.
if ( ! hasValidProfile ( ) )
{
GuiGameListMenuProfile * profile ;
if ( ! Sim : : findObject ( " DefaultListMenuProfile " , profile ) )
{
Con : : errorf ( " GuiGameListMenuCtrl: %s can't be created with a profile of type %s. Please create it with a profile of type GuiGameListMenuProfile. " ,
getName ( ) , mProfile - > getClassName ( ) ) ;
return false ;
}
else
Con : : warnf ( " GuiGameListMenuCtrl: substituted non-GuiGameListMenuProfile in %s for DefaultListMenuProfile " , getName ( ) ) ;
setControlProfile ( profile ) ;
}
return true ;
}
bool GuiGameListMenuCtrl : : onWake ( )
{
if ( ! Parent : : onWake ( ) )
return false ;
if ( ! hasValidProfile ( ) )
return false ;
2020-05-20 22:19:52 +00:00
/*if( mRows.empty() )
2012-09-19 15:15:01 +00:00
{
Con : : errorf ( " GuiGameListMenuCtrl: %s can't be woken up without any rows. Please use \" addRow \" to add at least one row to the control before pushing it to the canvas. " ,
getName ( ) ) ;
return false ;
2020-05-20 22:19:52 +00:00
} */
2012-09-19 15:15:01 +00:00
enforceConstraints ( ) ;
selectFirstEnabledRow ( ) ;
setFirstResponder ( ) ;
mHighlighted = NO_ROW ;
return true ;
}
bool GuiGameListMenuCtrl : : hasValidProfile ( ) const
{
GuiGameListMenuProfile * profile = dynamic_cast < GuiGameListMenuProfile * > ( mProfile ) ;
return profile ;
}
void GuiGameListMenuCtrl : : enforceConstraints ( )
{
if ( hasValidProfile ( ) )
{
( ( GuiGameListMenuProfile * ) mProfile ) - > enforceConstraints ( ) ;
}
updateHeight ( ) ;
}
void GuiGameListMenuCtrl : : updateHeight ( )
{
S32 minHeight = getMinExtent ( ) . y ;
if ( getHeight ( ) < minHeight )
{
setHeight ( minHeight ) ;
}
}
void GuiGameListMenuCtrl : : onMouseDown ( const GuiEvent & event )
{
S32 hitRow = getRow ( event . mousePoint ) ;
if ( hitRow ! = NO_ROW )
{
S32 delta = ( mSelected ! = NO_ROW ) ? ( hitRow - mSelected ) : ( mSelected + 1 ) ;
changeRow ( delta ) ;
}
}
void GuiGameListMenuCtrl : : onMouseLeave ( const GuiEvent & event )
{
mHighlighted = NO_ROW ;
}
void GuiGameListMenuCtrl : : onMouseMove ( const GuiEvent & event )
{
S32 hitRow = getRow ( event . mousePoint ) ;
// allow mHighligetd to be set to NO_ROW so rows can be unhighlighted
mHighlighted = hitRow ;
}
void GuiGameListMenuCtrl : : onMouseUp ( const GuiEvent & event )
{
S32 hitRow = getRow ( event . mousePoint ) ;
if ( ( hitRow ! = NO_ROW ) & & isRowEnabled ( hitRow ) & & ( hitRow = = getSelected ( ) ) )
{
2020-05-20 22:19:52 +00:00
if ( mRows [ hitRow ] - > mMode = = Row : : Mode : : Default )
{
activateRow ( ) ;
}
else if ( mRows [ hitRow ] - > mMode = = Row : : Mode : : OptionList )
{
S32 xPos = globalToLocalCoord ( event . mousePoint ) . x ;
clickOption ( ( Row * ) mRows [ getSelected ( ) ] , xPos ) ;
}
2020-05-25 05:51:33 +00:00
else if ( mRows [ hitRow ] - > mMode = = Row : : Mode : : Slider )
{
S32 xPos = globalToLocalCoord ( event . mousePoint ) . x ;
clickSlider ( ( Row * ) mRows [ getSelected ( ) ] , xPos ) ;
}
else if ( mRows [ hitRow ] - > mMode = = Row : : Mode : : Keybind )
{
S32 xPos = globalToLocalCoord ( event . mousePoint ) . x ;
clickKeybind ( ( Row * ) mRows [ getSelected ( ) ] , xPos ) ;
}
2012-09-19 15:15:01 +00:00
}
}
void GuiGameListMenuCtrl : : activateRow ( )
{
S32 row = getSelected ( ) ;
if ( ( row ! = NO_ROW ) & & isRowEnabled ( row ) & & ( mRows [ row ] - > mScriptCallback ! = NULL ) )
{
setThisControl ( ) ;
if ( Con : : isFunction ( mRows [ row ] - > mScriptCallback ) )
{
Con : : executef ( mRows [ row ] - > mScriptCallback ) ;
}
}
}
S32 GuiGameListMenuCtrl : : getRow ( Point2I globalPoint )
{
Point2I localPoint = globalToLocalCoord ( globalPoint ) ;
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
S32 rowHeight = profile - > getRowHeight ( ) ;
Point2I currentOffset ( 0 , 0 ) ;
Point2I hitAreaUpperLeft = profile - > mHitAreaUpperLeft ;
hitAreaUpperLeft . x * = xScale ;
Point2I hitAreaLowerRight = profile - > mHitAreaLowerRight ;
hitAreaLowerRight . x * = xScale ;
Point2I upperLeft , lowerRight ;
for ( Vector < Row * > : : iterator row = mRows . begin ( ) ; row < mRows . end ( ) ; + + row )
{
if ( row ! = mRows . begin ( ) )
{
// rows other than the first can have padding above them
currentOffset . y + = ( * row ) - > mHeightPad ;
}
upperLeft = currentOffset + hitAreaUpperLeft ;
lowerRight = currentOffset + hitAreaLowerRight ;
if ( ( upperLeft . x < = localPoint . x ) & & ( localPoint . x < lowerRight . x ) & &
( upperLeft . y < = localPoint . y ) & & ( localPoint . y < lowerRight . y ) )
{
return row - mRows . begin ( ) ;
}
currentOffset . y + = rowHeight ;
}
return NO_ROW ;
}
void GuiGameListMenuCtrl : : setSelected ( S32 index )
{
if ( index = = NO_ROW )
{
// deselection
mSelected = NO_ROW ;
return ;
}
if ( ! isValidRowIndex ( index ) )
{
return ;
}
if ( ! isRowEnabled ( index ) )
{
// row is disabled, it can't be selected
return ;
}
mSelected = mClamp ( index , 0 , mRows . size ( ) - 1 ) ;
2020-05-20 22:19:52 +00:00
//If we're childed to a scroll container, make sure us changing rows has our new position visible
GuiScrollCtrl * scroll = dynamic_cast < GuiScrollCtrl * > ( getParent ( ) ) ;
if ( scroll )
{
scroll - > scrollRectVisible ( getRowBounds ( mSelected ) ) ;
}
2012-09-19 15:15:01 +00:00
}
bool GuiGameListMenuCtrl : : isRowEnabled ( S32 index ) const
{
if ( ! isValidRowIndex ( index ) )
{
return false ;
}
return mRows [ index ] - > mEnabled ;
}
void GuiGameListMenuCtrl : : setRowEnabled ( S32 index , bool enabled )
{
if ( ! isValidRowIndex ( index ) )
{
return ;
}
mRows [ index ] - > mEnabled = enabled ;
if ( getSelected ( ) = = index )
{
selectFirstEnabledRow ( ) ;
}
}
bool GuiGameListMenuCtrl : : isValidRowIndex ( S32 index ) const
{
return ( ( 0 < = index ) & & ( index < mRows . size ( ) ) ) ;
}
void GuiGameListMenuCtrl : : selectFirstEnabledRow ( )
{
setSelected ( NO_ROW ) ;
for ( Vector < Row * > : : iterator row = mRows . begin ( ) ; row < mRows . end ( ) ; + + row )
{
if ( ( * row ) - > mEnabled )
{
setSelected ( row - mRows . begin ( ) ) ;
return ;
}
}
}
2020-05-20 22:19:52 +00:00
bool GuiGameListMenuCtrl : : onInputEvent ( const InputEventInfo & event )
{
if ( mCallbackOnInputs )
{
char deviceString [ 32 ] ;
if ( ! ActionMap : : getDeviceName ( event . deviceType , event . deviceInst , deviceString ) )
return false ;
if ( event . action = = SI_MAKE | | event . action = = SI_BREAK )
{
bool isModifier = false ;
switch ( event . objInst )
{
case KEY_LCONTROL :
case KEY_RCONTROL :
case KEY_LALT :
case KEY_RALT :
case KEY_LSHIFT :
case KEY_RSHIFT :
case KEY_MAC_LOPT :
case KEY_MAC_ROPT :
isModifier = true ;
}
if ( ( event . objType = = SI_KEY ) & & isModifier )
{
char keyString [ 32 ] ;
if ( ! ActionMap : : getKeyString ( event . objInst , keyString ) )
return false ;
onInputEvent_callback ( deviceString , keyString , event . action ) ;
}
else
{
const char * actionString = ActionMap : : buildActionString ( & event ) ;
onInputEvent_callback ( deviceString , actionString , event . action ) ;
}
}
else if ( event . objType = = SI_AXIS | | event . objType = = SI_INT | | event . objType = = SI_FLOAT )
{
F32 fValue = event . fValue ;
if ( event . objType = = SI_INT )
fValue = ( F32 ) event . iValue ;
if ( ! ActionMap : : getDeviceName ( event . deviceType , event . deviceInst , deviceString ) )
return false ;
const char * actionString = ActionMap : : buildActionString ( & event ) ;
onAxisEvent_callback ( deviceString , actionString , fValue ) ;
}
}
return false ;
}
2012-09-19 15:15:01 +00:00
bool GuiGameListMenuCtrl : : onKeyDown ( const GuiEvent & event )
{
switch ( event . keyCode )
{
case KEY_UP :
changeRow ( - 1 ) ;
return true ;
case KEY_DOWN :
changeRow ( 1 ) ;
return true ;
case KEY_A :
case KEY_RETURN :
case KEY_NUMPADENTER :
case KEY_SPACE :
case XI_A :
case XI_START :
doScriptCommand ( mCallbackOnA ) ;
return true ;
case KEY_B :
case KEY_ESCAPE :
case KEY_BACKSPACE :
case KEY_DELETE :
case XI_B :
case XI_BACK :
doScriptCommand ( mCallbackOnB ) ;
return true ;
case KEY_X :
case XI_X :
doScriptCommand ( mCallbackOnX ) ;
return true ;
case KEY_Y :
case XI_Y :
doScriptCommand ( mCallbackOnY ) ;
return true ;
default :
break ;
}
return Parent : : onKeyDown ( event ) ;
}
bool GuiGameListMenuCtrl : : onGamepadAxisUp ( const GuiEvent & event )
{
changeRow ( - 1 ) ;
return true ;
}
bool GuiGameListMenuCtrl : : onGamepadAxisDown ( const GuiEvent & event )
{
changeRow ( 1 ) ;
return true ;
}
2020-05-20 22:19:52 +00:00
bool GuiGameListMenuCtrl : : onGamepadAxisLeft ( const GuiEvent & event )
{
changeOption ( - 1 ) ;
return true ;
}
bool GuiGameListMenuCtrl : : onGamepadAxisRight ( const GuiEvent & event )
{
changeOption ( 1 ) ;
return true ;
}
2012-09-19 15:15:01 +00:00
void GuiGameListMenuCtrl : : doScriptCommand ( StringTableEntry command )
{
if ( command & & command [ 0 ] )
{
setThisControl ( ) ;
Con : : evaluate ( command , false , __FILE__ ) ;
}
}
void GuiGameListMenuCtrl : : changeRow ( S32 delta )
{
S32 oldRowIndex = getSelected ( ) ;
S32 newRowIndex = oldRowIndex ;
do
{
newRowIndex + = delta ;
if ( newRowIndex > = mRows . size ( ) )
{
newRowIndex = 0 ;
}
else if ( newRowIndex < 0 )
{
newRowIndex = mRows . size ( ) - 1 ;
}
}
while ( ( ! mRows [ newRowIndex ] - > mEnabled ) & & ( newRowIndex ! = oldRowIndex ) ) ;
setSelected ( newRowIndex ) ;
// do the callback
onChange_callback ( ) ;
}
void GuiGameListMenuCtrl : : setThisControl ( )
{
smThisControl = this ;
}
StringTableEntry GuiGameListMenuCtrl : : getRowLabel ( S32 rowIndex ) const
{
AssertFatal ( isValidRowIndex ( rowIndex ) , avar ( " GuiGameListMenuCtrl: You can't get the label from row %d of %s because it is not a valid row index. Please specify a valid row index in the range [0, %d). " , rowIndex , getName ( ) , getRowCount ( ) ) ) ;
if ( ! isValidRowIndex ( rowIndex ) )
{
// not a valid row index, don't do anything
2017-01-12 04:21:29 +00:00
return StringTable - > EmptyString ( ) ;
2012-09-19 15:15:01 +00:00
}
return mRows [ rowIndex ] - > mLabel ;
}
void GuiGameListMenuCtrl : : setRowLabel ( S32 rowIndex , const char * label )
{
AssertFatal ( isValidRowIndex ( rowIndex ) , avar ( " GuiGameListMenuCtrl: You can't set the label on row %d of %s because it is not a valid row index. Please specify a valid row index in the range [0, %d). " , rowIndex , getName ( ) , getRowCount ( ) ) ) ;
if ( ! isValidRowIndex ( rowIndex ) )
{
// not a valid row index, don't do anything
return ;
}
mRows [ rowIndex ] - > mLabel = StringTable - > insert ( label , true ) ;
}
2020-05-20 22:19:52 +00:00
void GuiGameListMenuCtrl : : clearRows ( )
{
2020-05-25 05:51:33 +00:00
for ( U32 i = 0 ; i < mRows . size ( ) ; i + + )
{
if ( mRows [ i ] - > mBitmap ! = StringTable - > EmptyString ( ) )
mRows [ i ] - > mBitmapTex = nullptr ;
}
2020-05-20 22:19:52 +00:00
mRows . clear ( ) ;
2020-05-25 05:51:33 +00:00
setSelected ( - 1 ) ;
setHeight ( mMinExtent . y ) ;
}
void GuiGameListMenuCtrl : : refresh ( )
{
enforceConstraints ( ) ;
2020-05-20 22:19:52 +00:00
}
RectI GuiGameListMenuCtrl : : getRowBounds ( S32 rowIndex )
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
S32 rowHeight = profile - > getRowHeight ( ) ;
Point2I currentOffset = Point2I : : Zero ;
Point2I extent = getExtent ( ) ;
Point2I rowExtent ( extent . x , rowHeight ) ;
for ( U32 i = 1 ; i < = rowIndex ; i + + )
{
//the top row can't pad, so we'll ignore it
GuiGameListMenuCtrl : : Row * row = mRows [ i ] ;
// rows other than the first can have padding above them
currentOffset . y + = row - > mHeightPad ;
currentOffset . y + = rowHeight ;
}
return RectI ( currentOffset , rowExtent ) ;
}
2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Console stuff (GuiGameListMenuCtrl)
//-----------------------------------------------------------------------------
2020-05-20 22:19:52 +00:00
StringTableEntry GuiGameListMenuCtrl : : getCurrentOption ( S32 rowIndex ) const
{
if ( isValidRowIndex ( rowIndex ) )
{
Row * row = ( Row * ) mRows [ rowIndex ] ;
if ( row - > mSelectedOption ! = NO_OPTION )
{
return row - > mOptions [ row - > mSelectedOption ] ;
}
}
return StringTable - > insert ( " " , false ) ;
}
bool GuiGameListMenuCtrl : : selectOption ( S32 rowIndex , const char * theOption )
{
if ( ! isValidRowIndex ( rowIndex ) )
{
return false ;
}
Row * row = ( Row * ) mRows [ rowIndex ] ;
for ( Vector < StringTableEntry > : : iterator anOption = row - > mOptions . begin ( ) ; anOption < row - > mOptions . end ( ) ; + + anOption )
{
if ( dStrcmp ( * anOption , theOption ) = = 0 )
{
S32 newIndex = anOption - row - > mOptions . begin ( ) ;
row - > mSelectedOption = newIndex ;
return true ;
}
}
return false ;
}
void GuiGameListMenuCtrl : : setOptions ( S32 rowIndex , const char * optionsList )
{
static StringTableEntry DELIM = StringTable - > insert ( " \t " , true ) ;
if ( ! isValidRowIndex ( rowIndex ) )
{
return ;
}
Row * row = ( Row * ) mRows [ rowIndex ] ;
S32 count = StringUnit : : getUnitCount ( optionsList , DELIM ) ;
row - > mOptions . setSize ( count ) ;
for ( S32 i = 0 ; i < count ; + + i )
{
const char * option = StringUnit : : getUnit ( optionsList , i , DELIM ) ;
row - > mOptions [ i ] = StringTable - > insert ( option , true ) ;
}
if ( row - > mSelectedOption > = row - > mOptions . size ( ) )
{
row - > mSelectedOption = row - > mOptions . size ( ) - 1 ;
}
}
void GuiGameListMenuCtrl : : clickOption ( Row * row , S32 xPos )
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
if ( ! profile - > hasArrows ( ) )
{
return ;
}
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
S32 bitmapArrowWidth = mProfile - > getBitmapArrayRect ( Profile : : TEX_FIRST_ARROW ) . extent . x ;
S32 leftArrowX1 = profile - > mColumnSplit * xScale ;
S32 leftArrowX2 = leftArrowX1 + bitmapArrowWidth ;
S32 rightArrowX2 = ( profile - > mHitAreaLowerRight . x - profile - > mRightPad ) * xScale ;
S32 rightArrowX1 = rightArrowX2 - bitmapArrowWidth ;
if ( ( leftArrowX1 < = xPos ) & & ( xPos < = leftArrowX2 ) )
{
changeOption ( row , - 1 ) ;
}
else if ( ( rightArrowX1 < = xPos ) & & ( xPos < = rightArrowX2 ) )
{
changeOption ( row , 1 ) ;
}
}
void GuiGameListMenuCtrl : : changeOption ( S32 delta )
{
if ( getSelected ( ) ! = NO_ROW )
{
Row * row = ( Row * ) mRows [ getSelected ( ) ] ;
changeOption ( row , delta ) ;
}
}
void GuiGameListMenuCtrl : : changeOption ( Row * row , S32 delta )
{
S32 optionCount = row - > mOptions . size ( ) ;
S32 newSelection = row - > mSelectedOption + delta ;
if ( optionCount = = 0 )
{
newSelection = NO_OPTION ;
}
else if ( ! row - > mWrapOptions )
{
newSelection = mClamp ( newSelection , 0 , optionCount - 1 ) ;
}
else if ( newSelection < 0 )
{
newSelection = optionCount - 1 ;
}
else if ( newSelection > = optionCount )
{
newSelection = 0 ;
}
row - > mSelectedOption = newSelection ;
static StringTableEntry LEFT = StringTable - > insert ( " LEFT " , true ) ;
static StringTableEntry RIGHT = StringTable - > insert ( " RIGHT " , true ) ;
if ( row - > mScriptCallback ! = NULL )
{
setThisControl ( ) ;
StringTableEntry direction = NULL ;
if ( delta < 0 )
{
direction = LEFT ;
}
else if ( delta > 0 )
{
direction = RIGHT ;
}
if ( ( direction ! = NULL ) & & ( Con : : isFunction ( row - > mScriptCallback ) ) )
{
Con : : executef ( row - > mScriptCallback , direction ) ;
}
}
}
2012-09-19 15:15:01 +00:00
IMPLEMENT_CONOBJECT ( GuiGameListMenuCtrl ) ;
2020-05-25 05:51:33 +00:00
void GuiGameListMenuCtrl : : clickSlider ( Row * row , S32 xPos )
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
if ( ! profile - > hasArrows ( ) )
{
return ;
}
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
S32 bitmapArrowWidth = mProfile - > getBitmapArrayRect ( Profile : : TEX_FIRST_ARROW ) . extent . x ;
S32 leftArrowX1 = profile - > mColumnSplit * xScale ;
S32 leftArrowX2 = leftArrowX1 + bitmapArrowWidth ;
S32 rightArrowX2 = ( profile - > mHitAreaLowerRight . x - profile - > mRightPad ) * xScale ;
S32 rightArrowX1 = rightArrowX2 - bitmapArrowWidth ;
if ( ( leftArrowX1 < = xPos ) & & ( xPos < = leftArrowX2 ) )
{
row - > mValue - = row - > mStepSize ;
row - > mValue = mRound ( row - > mValue / row - > mStepSize ) * row - > mStepSize ;
if ( row - > mValue < row - > mRange . x )
row - > mValue = row - > mRange . x ;
}
else if ( ( rightArrowX1 < = xPos ) & & ( xPos < = rightArrowX2 ) )
{
//F32 snap = row->mValue % row->mStepSize;
//row->mValue.y -= snap;
row - > mValue + = row - > mStepSize ;
row - > mValue = mRound ( row - > mValue / row - > mStepSize ) * row - > mStepSize ;
if ( row - > mValue > row - > mRange . y )
row - > mValue = row - > mRange . y ;
}
else
{
//see if we clicked on the sliderbar itself
S32 barStart = leftArrowX2 ;
S32 barEnd = rightArrowX1 ;
if ( xPos > = barStart & & xPos < = barEnd )
{
//find the position
F32 newValue = ( ( ( xPos - barStart ) * ( row - > mRange . y - row - > mRange . x ) ) / ( barEnd - barStart ) ) + row - > mRange . x ;
newValue = mRound ( newValue / row - > mStepSize ) * row - > mStepSize ;
row - > mValue = newValue ;
}
}
}
void GuiGameListMenuCtrl : : clickKeybind ( Row * row , S32 xPos )
{
GuiGameListMenuProfile * profile = ( GuiGameListMenuProfile * ) mProfile ;
F32 xScale = ( float ) getWidth ( ) / profile - > getRowWidth ( ) ;
S32 columnSplit = profile - > mColumnSplit * xScale ;
S32 rowHeight = profile - > getRowHeight ( ) ;
S32 optionWidth = xScale - columnSplit ;
GFXDrawUtil * drawer = GFX - > getDrawUtil ( ) ;
//drawer->drawBitmap(row->mBitmap, )
Point2I button ;
button . x = columnSplit + ( columnSplit / 2 ) /* + (optionWidth / 2)*/ ;
button . y = rowHeight / 4 ;
Point2I buttonSize ;
buttonSize . x = rowHeight / 2 ;
buttonSize . y = rowHeight / 2 ;
GFXTextureObject * texture = row - > mBitmapTex ;
RectI rect ( button , buttonSize ) ;
if ( rect . pointInRect ( Point2I ( xPos , rowHeight / 2 ) ) )
{
if ( row - > mScriptCallback ! = StringTable - > EmptyString ( ) )
{
S32 rowId = getSelected ( ) ;
Con : : executef ( row - > mScriptCallback , rowId ) ;
}
}
}
F32 GuiGameListMenuCtrl : : getValue ( S32 rowIndex )
{
if ( ! isValidRowIndex ( rowIndex ) )
{
return 0 ;
}
Row * row = ( Row * ) mRows [ rowIndex ] ;
return row - > mValue ;
}
void GuiGameListMenuCtrl : : setValue ( S32 rowIndex , F32 value )
{
if ( ! isValidRowIndex ( rowIndex ) )
{
return ;
}
Row * row = ( Row * ) mRows [ rowIndex ] ;
row - > mValue = value ;
}
const char * GuiGameListMenuCtrl : : getTooltip ( S32 rowIndex )
{
if ( ! isValidRowIndex ( rowIndex ) )
{
return " " ;
}
Row * row = ( Row * ) mRows [ rowIndex ] ;
return row - > mTooltip ;
}
2012-09-19 15:15:01 +00:00
ConsoleDocClass ( GuiGameListMenuCtrl ,
" @brief A base class for cross platform menu controls that are gamepad friendly. \n \n "
" This class is used to build row-based menu GUIs that can be easily navigated "
" using the keyboard, mouse or gamepad. The desired row can be selected using "
" the mouse, or by navigating using the Up and Down buttons. \n \n "
" @tsexample \n \n "
" new GuiGameListMenuCtrl() \n "
" { \n "
" debugRender = \" 0 \" ; \n "
" callbackOnA = \" applyOptions(); \" ; \n "
" callbackOnB = \" Canvas.setContent(MainMenuGui); \" ; \n "
" callbackOnX = \" \" ; \n "
" callbackOnY = \" revertOptions(); \" ; \n "
" //Properties not specific to this control have been omitted from this example. \n "
" }; \n "
" @endtsexample \n \n "
" @see GuiGameListMenuProfile \n \n "
" @ingroup GuiGame "
) ;
IMPLEMENT_CALLBACK ( GuiGameListMenuCtrl , onChange , void , ( ) , ( ) ,
" Called when the selected row changes. " ) ;
2020-05-20 22:19:52 +00:00
IMPLEMENT_CALLBACK ( GuiGameListMenuCtrl , onInputEvent , void , ( const char * device , const char * action , bool state ) ,
( device , action , state ) ,
" @brief Callback that occurs when an input is triggered on this control \n \n "
" @param device The device type triggering the input, such as keyboard, mouse, etc \n "
" @param action The actual event occuring, such as a key or button \n "
" @param state True if the action is being pressed, false if it is being release \n \n " ) ;
IMPLEMENT_CALLBACK ( GuiGameListMenuCtrl , onAxisEvent , void , ( const char * device , const char * action , F32 axisValue ) ,
( device , action , axisValue ) ,
" @brief Callback that occurs when an axis event is triggered on this control \n \n "
" @param device The device type triggering the input, such as mouse, joystick, gamepad, etc \n "
" @param action The ActionMap code for the axis \n "
" @param axisValue The current value of the axis \n \n " ) ;
2012-09-19 15:15:01 +00:00
void GuiGameListMenuCtrl : : initPersistFields ( )
{
addField ( " debugRender " , TypeBool , Offset ( mDebugRender , GuiGameListMenuCtrl ) ,
" Enable debug rendering " ) ;
addField ( " callbackOnA " , TypeString , Offset ( mCallbackOnA , GuiGameListMenuCtrl ) ,
" Script callback when the 'A' button is pressed. 'A' inputs are Keyboard: A, Return, Space; Gamepad: A, Start " ) ;
addField ( " callbackOnB " , TypeString , Offset ( mCallbackOnB , GuiGameListMenuCtrl ) ,
" Script callback when the 'B' button is pressed. 'B' inputs are Keyboard: B, Esc, Backspace, Delete; Gamepad: B, Back " ) ;
addField ( " callbackOnX " , TypeString , Offset ( mCallbackOnX , GuiGameListMenuCtrl ) ,
" Script callback when the 'X' button is pressed. 'X' inputs are Keyboard: X; Gamepad: X " ) ;
addField ( " callbackOnY " , TypeString , Offset ( mCallbackOnY , GuiGameListMenuCtrl ) ,
" Script callback when the 'Y' button is pressed. 'Y' inputs are Keyboard: Y; Gamepad: Y " ) ;
2020-05-20 22:19:52 +00:00
addField ( " callbackOnInputs " , TypeBool , Offset ( mCallbackOnInputs , GuiGameListMenuCtrl ) ,
" Script callback when any inputs are detected, even if they aren't the regular 4 face buttons. Useful for secondary/speciality handling of menu navigation. " ) ;
2012-09-19 15:15:01 +00:00
Parent : : initPersistFields ( ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , addRow , void ,
2020-05-20 22:19:52 +00:00
( const char * label , const char * callback , S32 icon , S32 yPad , bool useHighlightIcon , bool enabled , int mode ) ,
( - 1 , 0 , true , true , 0 ) ,
2012-09-19 15:15:01 +00:00
" Add a row to the list control. \n \n "
" @param label The text to display on the row as a label. \n "
" @param callback Name of a script function to use as a callback when this row is activated. \n "
" @param icon [optional] Index of the icon to use as a marker. \n "
" @param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row. \n "
" @param useHighlightIcon [optional] Does this row use the highlight icon?. \n "
2020-05-20 22:19:52 +00:00
" @param enabled [optional] If this row is initially enabled. \n "
" @param mode [optional] What option mode the row is in. 0 = Default, 1 = OptionList, 2 == Keybind " )
2012-09-19 15:15:01 +00:00
{
2020-05-20 22:19:52 +00:00
object - > addRow ( label , callback , icon , yPad , useHighlightIcon , enabled , mode ) ;
2012-09-19 15:15:01 +00:00
}
DefineEngineMethod ( GuiGameListMenuCtrl , isRowEnabled , bool , ( S32 row ) , ,
" Determines if the specified row is enabled or disabled. \n \n "
" @param row The row to set the enabled status of. \n "
" @return True if the specified row is enabled. False if the row is not enabled or the given index was not valid. " )
{
return object - > isRowEnabled ( row ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , setRowEnabled , void , ( S32 row , bool enabled ) , ,
" Sets a row's enabled status according to the given parameters. \n \n "
" @param row The index to check for validity. \n "
" @param enabled Indicate true to enable the row or false to disable it. " )
{
object - > setRowEnabled ( row , enabled ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , activateRow , void , ( ) , ,
" Activates the current row. The script callback of the current row will be called (if it has one). " )
{
object - > activateRow ( ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , getRowCount , S32 , ( ) , ,
" Gets the number of rows on the control. \n \n "
" @return (int) The number of rows on the control. " )
{
return object - > getRowCount ( ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , getRowLabel , const char * , ( S32 row ) , ,
" Gets the label displayed on the specified row. \n \n "
" @param row Index of the row to get the label of. \n "
" @return The label for the row. " )
{
return object - > getRowLabel ( row ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , setRowLabel , void , ( S32 row , const char * label ) , ,
" Sets the label on the given row. \n \n "
" @param row Index of the row to set the label on. \n "
" @param label Text to set as the label of the row. \n " )
{
object - > setRowLabel ( row , label ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , setSelected , void , ( S32 row ) , ,
" Sets the selected row. Only rows that are enabled can be selected. \n \n "
" @param row Index of the row to set as selected. " )
{
object - > setSelected ( row ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , getSelectedRow , S32 , ( ) , ,
" Gets the index of the currently selected row. \n \n "
" @return Index of the selected row. " )
{
return object - > getSelected ( ) ;
}
2020-05-20 22:19:52 +00:00
DefineEngineMethod ( GuiGameListMenuCtrl , clearRows , void , ( ) , ,
" Gets the index of the currently selected row. \n \n "
" @return Index of the selected row. " )
{
return object - > clearRows ( ) ;
}
2020-05-25 05:51:33 +00:00
DefineEngineMethod ( GuiGameListMenuCtrl , refresh , void , ( ) , ,
" Gets the index of the currently selected row. \n \n "
" @return Index of the selected row. " )
{
return object - > refresh ( ) ;
}
2020-05-20 22:19:52 +00:00
DefineEngineMethod ( GuiGameListMenuCtrl , addOptionRow , void ,
2020-05-25 05:51:33 +00:00
( const char * label , const char * options , bool wrapOptions , const char * callback , S32 icon , S32 yPad , bool enabled , const char * tooltip , const char * defaultValue ) ,
( - 1 , 0 , true , " " , " " ) ,
2020-05-20 22:19:52 +00:00
" Add a row to the list control. \n \n "
" @param label The text to display on the row as a label. \n "
" @param options A tab separated list of options. \n "
" @param wrapOptions Specify true to allow options to wrap at each end or false to prevent wrapping. \n "
" @param callback Name of a script function to use as a callback when this row is activated. \n "
" @param icon [optional] Index of the icon to use as a marker. \n "
" @param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row. \n "
" @param enabled [optional] If this row is initially enabled. " )
{
2020-05-25 05:51:33 +00:00
object - > addRow ( label , options , wrapOptions , callback , icon , yPad , enabled , tooltip , defaultValue ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , addSliderRow , void ,
( const char * label , F32 defaultValue , F32 increment , Point2F range , const char * callback , S32 icon , S32 yPad , bool enabled , const char * tooltip ) ,
( - 1 , 0 , true , " " ) ,
" Add a row to the list control. \n \n "
" @param label The text to display on the row as a label. \n "
" @param options A tab separated list of options. \n "
" @param wrapOptions Specify true to allow options to wrap at each end or false to prevent wrapping. \n "
" @param callback Name of a script function to use as a callback when this row is activated. \n "
" @param icon [optional] Index of the icon to use as a marker. \n "
" @param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row. \n "
" @param enabled [optional] If this row is initially enabled. " )
{
object - > addRow ( label , defaultValue , increment , range , callback , icon , yPad , enabled , tooltip ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , addKeybindRow , void ,
( const char * label , const char * bitmapName , const char * callback , S32 icon , S32 yPad , bool enabled , const char * tooltip ) ,
( - 1 , 0 , true , " " ) ,
" Add a row to the list control. \n \n "
" @param label The text to display on the row as a label. \n "
" @param options A tab separated list of options. \n "
" @param wrapOptions Specify true to allow options to wrap at each end or false to prevent wrapping. \n "
" @param callback Name of a script function to use as a callback when this row is activated. \n "
" @param icon [optional] Index of the icon to use as a marker. \n "
" @param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row. \n "
" @param enabled [optional] If this row is initially enabled. " )
{
object - > addRow ( label , bitmapName , callback , icon , yPad , enabled , tooltip ) ;
2020-05-20 22:19:52 +00:00
}
DefineEngineMethod ( GuiGameListMenuCtrl , getCurrentOption , const char * , ( S32 row ) , ,
" Gets the text for the currently selected option of the given row. \n \n "
" @param row Index of the row to get the option from. \n "
" @return A string representing the text currently displayed as the selected option on the given row. If there is no such displayed text then the empty string is returned. " )
{
return object - > getCurrentOption ( row ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , selectOption , bool , ( S32 row , const char * option ) , ,
" Set the row's current option to the one specified \n \n "
" @param row Index of the row to set an option on. \n "
" @param option The option to be made active. \n "
" @return True if the row contained the option and was set, false otherwise. " )
{
return object - > selectOption ( row , option ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , setOptions , void , ( S32 row , const char * optionsList ) , ,
" Sets the list of options on the given row. \n \n "
" @param row Index of the row to set options on. "
" @param optionsList A tab separated list of options for the control. " )
{
object - > setOptions ( row , optionsList ) ;
}
2020-06-01 08:32:45 +00:00
DefineEngineMethod ( GuiGameListMenuCtrl , getValue , F32 , ( S32 row ) , ,
2020-05-25 05:51:33 +00:00
" Sets the list of options on the given row. \n \n "
" @param row Index of the row to set options on. "
" @param optionsList A tab separated list of options for the control. " )
{
2020-06-01 08:32:45 +00:00
return object - > getValue ( row ) ;
2020-05-25 05:51:33 +00:00
}
DefineEngineMethod ( GuiGameListMenuCtrl , setValue , void , ( S32 row , F32 value ) , ,
" Sets the list of options on the given row. \n \n "
" @param row Index of the row to set options on. "
" @param optionsList A tab separated list of options for the control. " )
{
object - > setValue ( row , value ) ;
}
DefineEngineMethod ( GuiGameListMenuCtrl , getTooltip , const char * , ( S32 row ) , ,
" Sets the list of options on the given row. \n \n "
" @param row Index of the row to set options on. "
" @param optionsList A tab separated list of options for the control. " )
{
return object - > getTooltip ( row ) ;
}
2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// GuiGameListMenuProfile
//-----------------------------------------------------------------------------
GuiGameListMenuProfile : : GuiGameListMenuProfile ( )
2020-05-20 22:19:52 +00:00
: mHitAreaUpperLeft ( 0 , 0 ) ,
mHitAreaLowerRight ( 0 , 0 ) ,
mIconOffset ( 0 , 0 ) ,
mRowSize ( 0 , 0 ) ,
mRowScale ( 1.0f , 1.0f ) ,
mColumnSplit ( 0 ) ,
mRightPad ( 0 )
2012-09-19 15:15:01 +00:00
{
}
bool GuiGameListMenuProfile : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
{
return false ;
}
// We can't call enforceConstraints() here because incRefCount initializes
// some of the things to enforce. Do a basic sanity check here instead.
2014-06-17 06:01:25 +00:00
if ( ! mBitmapName | | ! dStrlen ( mBitmapName ) )
2012-09-19 15:15:01 +00:00
{
Con : : errorf ( " GuiGameListMenuProfile: %s can't be created without a bitmap. Please add a 'Bitmap' property to the object definition. " , getName ( ) ) ;
return false ;
}
if ( mRowSize . x < 0 )
{
Con : : errorf ( " GuiGameListMenuProfile: %s can't have a negative row width. Please change the row width to be non-negative. " , getName ( ) ) ;
return false ;
}
if ( mRowSize . y < 0 )
{
Con : : errorf ( " GuiGameListMenuProfile: %s can't have a negative row height. Please change the row height to be non-negative. " , getName ( ) ) ;
return false ;
}
return true ;
}
void GuiGameListMenuProfile : : enforceConstraints ( )
{
if ( getBitmapArrayRect ( 0 ) . extent . isZero ( ) )
Con : : errorf ( " GuiGameListMenuCtrl: %s can't be created without a bitmap. Please add a bitmap to the profile's definition. " , getName ( ) ) ;
if ( mRowSize . x < 0 )
Con : : errorf ( " GuiGameListMenuProfile: %s can't have a negative row width. Please change the row width to be non-negative. " , getName ( ) ) ;
mRowSize . x = getMax ( mRowSize . x , 0 ) ;
if ( mRowSize . y < 0 )
Con : : errorf ( " GuiGameListMenuProfile: %s can't have a negative row height. Please change the row height to be non-negative. " , getName ( ) ) ;
mRowSize . y = getMax ( mRowSize . y , 0 ) ;
Point2I rowTexExtent = getBitmapArrayRect ( TEX_NORMAL ) . extent ;
mRowScale . x = ( float ) getRowWidth ( ) / rowTexExtent . x ;
mRowScale . y = ( float ) getRowHeight ( ) / rowTexExtent . y ;
2020-05-20 22:19:52 +00:00
if ( mHitAreaUpperLeft . x > mColumnSplit | | mColumnSplit > mHitAreaLowerRight . x )
Con : : errorf ( " GuiGameListOptionsProfile: You can't create %s with a ColumnSplit outside the hit area. You set the split to %d. Please change the ColumnSplit to be in the range [%d, %d]. " ,
getName ( ) , mColumnSplit , mHitAreaUpperLeft . x , mHitAreaLowerRight . x ) ;
mColumnSplit = mClamp ( mColumnSplit , mHitAreaUpperLeft . x , mHitAreaLowerRight . x ) ;
2012-09-19 15:15:01 +00:00
}
Point2I GuiGameListMenuProfile : : getIconExtent ( )
{
Point2I iconExtent = getBitmapArrayRect ( TEX_FIRST_ICON ) . extent ;
// scale both by y to keep the aspect ratio
iconExtent . x * = mRowScale . y ;
iconExtent . y * = mRowScale . y ;
return iconExtent ;
}
Point2I GuiGameListMenuProfile : : getArrowExtent ( )
{
Point2I arrowExtent = getBitmapArrayRect ( TEX_FIRST_ARROW ) . extent ;
// scale both by y to keep the aspect ratio
arrowExtent . x * = mRowScale . y ;
arrowExtent . y * = mRowScale . y ;
return arrowExtent ;
}
Point2I GuiGameListMenuProfile : : getHitAreaExtent ( )
{
if ( mHitAreaLowerRight = = mHitAreaUpperLeft )
{
return mRowSize ;
}
else
{
return mHitAreaLowerRight - mHitAreaUpperLeft ;
}
}
//-----------------------------------------------------------------------------
// Console stuff (GuiGameListMenuProfile)
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT ( GuiGameListMenuProfile ) ;
ConsoleDocClass ( GuiGameListMenuProfile ,
" @brief A GuiControlProfile with additional fields specific to GuiGameListMenuCtrl. \n \n "
" @tsexample \n "
" new GuiGameListMenuProfile() \n "
" { \n "
" hitAreaUpperLeft = \" 10 2 \" ; \n "
" hitAreaLowerRight = \" 190 18 \" ; \n "
" iconOffset = \" 10 2 \" ; \n "
" rowSize = \" 200 20 \" ; \n "
2020-05-20 22:19:52 +00:00
" columnSplit = \" 100 \" ; \n "
" rightPad = \" 4 \" ; \n "
2012-09-19 15:15:01 +00:00
" //Properties not specific to this control have been omitted from this example. \n "
" }; \n "
" @endtsexample \n \n "
" @ingroup GuiGame "
) ;
void GuiGameListMenuProfile : : initPersistFields ( )
{
addField ( " hitAreaUpperLeft " , TypePoint2I , Offset ( mHitAreaUpperLeft , GuiGameListMenuProfile ) ,
" Position of the upper left corner of the row hit area (relative to row's top left corner) " ) ;
addField ( " hitAreaLowerRight " , TypePoint2I , Offset ( mHitAreaLowerRight , GuiGameListMenuProfile ) ,
" Position of the lower right corner of the row hit area (relative to row's top left corner) " ) ;
addField ( " iconOffset " , TypePoint2I , Offset ( mIconOffset , GuiGameListMenuProfile ) ,
" Offset from the row's top left corner at which to render the row icon " ) ;
addField ( " rowSize " , TypePoint2I , Offset ( mRowSize , GuiGameListMenuProfile ) ,
" The base size ( \" width height \" ) of a row " ) ;
2020-05-20 22:19:52 +00:00
addField ( " columnSplit " , TypeS32 , Offset ( mColumnSplit , GuiGameListMenuProfile ) ,
" Padding between the leftmost edge of the control, and the row's left arrow. " ) ;
addField ( " rightPad " , TypeS32 , Offset ( mRightPad , GuiGameListMenuProfile ) ,
" Padding between the rightmost edge of the control and the row's right arrow. " ) ;
2012-09-19 15:15:01 +00:00
Parent : : initPersistFields ( ) ;
removeField ( " tab " ) ;
removeField ( " mouseOverSelected " ) ;
removeField ( " modal " ) ;
removeField ( " opaque " ) ;
removeField ( " fillColor " ) ;
removeField ( " fillColorHL " ) ;
removeField ( " fillColorNA " ) ;
removeField ( " border " ) ;
removeField ( " borderThickness " ) ;
removeField ( " borderColor " ) ;
removeField ( " borderColorHL " ) ;
removeField ( " borderColorNA " ) ;
removeField ( " bevelColorHL " ) ;
removeField ( " bevelColorLL " ) ;
removeField ( " fontColorLink " ) ;
removeField ( " fontColorLinkHL " ) ;
removeField ( " justify " ) ;
removeField ( " returnTab " ) ;
removeField ( " numbersOnly " ) ;
removeField ( " cursorColor " ) ;
removeField ( " profileForChildren " ) ;
}