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.
//-----------------------------------------------------------------------------
//-------------------------------------
//
// Icon Button Control
// Draws the bitmap within a special button control. Only a single bitmap is used and the
// button will be drawn in a highlighted mode when the mouse hovers over it or when it
// has been clicked.
//
// Use mTextLocation to choose where within the button the text will be drawn, if at all.
// Use mTextMargin to set the text away from the button sides or from the bitmap.
// Use mButtonMargin to set everything away from the button sides.
// Use mErrorBitmapName to set the name of a bitmap to draw if the main bitmap cannot be found.
// Use mFitBitmapToButton to force the bitmap to fill the entire button extent. Usually used
// with no button text defined.
//
//
# include "platform/platform.h"
# include "gui/buttons/guiIconButtonCtrl.h"
# include "console/console.h"
# include "gfx/gfxDevice.h"
# include "gfx/gfxDrawUtil.h"
# include "console/consoleTypes.h"
# include "gui/core/guiCanvas.h"
# include "gui/core/guiDefaultControlRender.h"
# include "console/engineAPI.h"
static const ColorI colorWhite ( 255 , 255 , 255 ) ;
static const ColorI colorBlack ( 0 , 0 , 0 ) ;
IMPLEMENT_CONOBJECT ( GuiIconButtonCtrl ) ;
ConsoleDocClass ( GuiIconButtonCtrl ,
" @brief Draws the bitmap within a special button control. Only a single bitmap is used and the \n "
" button will be drawn in a highlighted mode when the mouse hovers over it or when it \n "
" has been clicked. \n \n "
" @tsexample \n "
2017-01-12 04:34:46 +00:00
" new GuiIconButtonCtrl(TestIconButton) \n "
" { \n "
" buttonMargin = \" 4 4 \" ; \n "
" iconBitmap = \" art/gui/lagIcon.png \" ; \n "
" iconLocation = \" Center \" ; \n "
" sizeIconToButton = \" 0 \" ; \n "
" makeIconSquare = \" 1 \" ; \n "
" textLocation = \" Bottom \" ; \n "
" textMargin = \" -2 \" ; \n "
2023-09-05 03:50:45 +00:00
" bitmapMargin = \" 0 \" ; \n "
" autoSize = \" 0 \" ; \n "
" text = \" Lag Icon \" ; \n "
" textID = \" \" STR_LAG \" \" ; \n "
" buttonType = \" PushButton \" ; \n "
" profile = \" GuiIconButtonProfile \" ; \n "
2017-01-12 04:34:46 +00:00
" }; \n "
2012-09-19 15:15:01 +00:00
" @endtsexample \n \n "
" @see GuiControl \n "
" @see GuiButtonCtrl \n \n "
" @ingroup GuiCore \n "
) ;
GuiIconButtonCtrl : : GuiIconButtonCtrl ( )
{
mTextLocation = TextLocLeft ;
mIconLocation = IconLocLeft ;
mTextMargin = 4 ;
mButtonMargin . set ( 4 , 4 ) ;
mFitBitmapToButton = false ;
mMakeIconSquare = false ;
mAutoSize = false ;
2023-09-05 03:50:45 +00:00
mBitmapMargin = 0 ;
2012-09-19 15:15:01 +00:00
setExtent ( 140 , 30 ) ;
}
ImplementEnumType ( GuiIconButtonTextLocation ,
" \n \n "
" @ingroup GuiImages " )
{ GuiIconButtonCtrl : : TextLocNone , " None " } ,
{ GuiIconButtonCtrl : : TextLocBottom , " Bottom " } ,
{ GuiIconButtonCtrl : : TextLocRight , " Right " } ,
{ GuiIconButtonCtrl : : TextLocTop , " Top " } ,
{ GuiIconButtonCtrl : : TextLocLeft , " Left " } ,
{ GuiIconButtonCtrl : : TextLocCenter , " Center " } ,
EndImplementEnumType ;
ImplementEnumType ( GuiIconButtonIconLocation ,
" \n \n "
" @ingroup GuiImages " )
{ GuiIconButtonCtrl : : IconLocNone , " None " } ,
{ GuiIconButtonCtrl : : IconLocLeft , " Left " } ,
{ GuiIconButtonCtrl : : IconLocRight , " Right " } ,
{ GuiIconButtonCtrl : : IconLocCenter , " Center " }
EndImplementEnumType ;
void GuiIconButtonCtrl : : initPersistFields ( )
{
2023-01-27 07:13:15 +00:00
docsURL ;
2012-09-19 15:15:01 +00:00
addField ( " buttonMargin " , TypePoint2I , Offset ( mButtonMargin , GuiIconButtonCtrl ) , " Margin area around the button. \n " ) ;
2021-07-19 06:07:08 +00:00
2025-03-30 10:22:42 +00:00
INITPERSISTFIELD_IMAGEASSET ( Bitmap , GuiIconButtonCtrl , " Bitmap file for the icon to display on the button. \n " ) ;
2021-07-19 06:07:08 +00:00
2012-09-19 15:15:01 +00:00
addField ( " iconLocation " , TYPEID < IconLocation > ( ) , Offset ( mIconLocation , GuiIconButtonCtrl ) , " Where to place the icon on the control. Options are 0 (None), 1 (Left), 2 (Right), 3 (Center). \n " ) ;
addField ( " sizeIconToButton " , TypeBool , Offset ( mFitBitmapToButton , GuiIconButtonCtrl ) , " If true, the icon will be scaled to be the same size as the button. \n " ) ;
addField ( " makeIconSquare " , TypeBool , Offset ( mMakeIconSquare , GuiIconButtonCtrl ) , " If true, will make sure the icon is square. \n " ) ;
addField ( " textLocation " , TYPEID < TextLocation > ( ) , Offset ( mTextLocation , GuiIconButtonCtrl ) , " Where to place the text on the control. \n "
2017-01-12 04:34:46 +00:00
" Options are 0 (None), 1 (Bottom), 2 (Right), 3 (Top), 4 (Left), 5 (Center). \n " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " textMargin " , TypeRangedS32 , Offset ( mTextMargin , GuiIconButtonCtrl ) , & CommonValidators : : PositiveInt , " Margin between the icon and the text. \n " ) ;
2012-09-19 15:15:01 +00:00
addField ( " autoSize " , TypeBool , Offset ( mAutoSize , GuiIconButtonCtrl ) , " If true, the text and icon will be automatically sized to the size of the control. \n " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " bitmapMargin " , TypeRangedS32 , Offset ( mBitmapMargin , GuiIconButtonCtrl ) , & CommonValidators : : PositiveInt , " Margin between the icon and the border. \n " ) ;
2023-09-05 03:50:45 +00:00
2012-09-19 15:15:01 +00:00
Parent : : initPersistFields ( ) ;
}
bool GuiIconButtonCtrl : : onWake ( )
{
if ( ! Parent : : onWake ( ) )
return false ;
setActive ( true ) ;
if ( mProfile )
mProfile - > constructBitmapArray ( ) ;
return true ;
}
void GuiIconButtonCtrl : : onSleep ( )
{
Parent : : onSleep ( ) ;
}
void GuiIconButtonCtrl : : inspectPostApply ( )
{
Parent : : inspectPostApply ( ) ;
}
void GuiIconButtonCtrl : : onStaticModified ( const char * slotName , const char * newValue )
{
if ( isProperlyAdded ( ) & & ! dStricmp ( slotName , " autoSize " ) )
resize ( getPosition ( ) , getExtent ( ) ) ;
}
bool GuiIconButtonCtrl : : resize ( const Point2I & newPosition , const Point2I & newExtent )
{
if ( ! mAutoSize | | ! mProfile - > mFont )
return Parent : : resize ( newPosition , newExtent ) ;
Point2I autoExtent ( mMinExtent ) ;
if ( mIconLocation ! = IconLocNone )
{
2024-12-25 20:23:22 +00:00
autoExtent . y = getBitmap ( ) . getHeight ( ) + mButtonMargin . y * 2 ;
autoExtent . x = getBitmap ( ) . getWidth ( ) + mButtonMargin . x * 2 ;
2012-09-19 15:15:01 +00:00
}
if ( mTextLocation ! = TextLocNone & & mButtonText & & mButtonText [ 0 ] )
{
U32 strWidth = mProfile - > mFont - > getStrWidthPrecise ( mButtonText ) ;
if ( mTextLocation = = TextLocLeft | | mTextLocation = = TextLocRight )
{
autoExtent . x + = strWidth + mTextMargin * 2 ;
}
else // Top, Bottom, Center
{
strWidth + = mTextMargin * 2 ;
if ( strWidth > autoExtent . x )
autoExtent . x = strWidth ;
}
}
return Parent : : resize ( newPosition , autoExtent ) ;
}
void GuiIconButtonCtrl : : setBitmap ( const char * name )
{
2026-01-07 23:57:53 +00:00
_setBitmap ( name ) ;
2012-09-19 15:15:01 +00:00
if ( ! isAwake ( ) )
return ;
// So that extent is recalculated if autoSize is set.
resize ( getPosition ( ) , getExtent ( ) ) ;
setUpdate ( ) ;
}
void GuiIconButtonCtrl : : onRender ( Point2I offset , const RectI & updateRect )
{
renderButton ( offset , updateRect ) ;
}
void GuiIconButtonCtrl : : renderButton ( Point2I & offset , const RectI & updateRect )
{
2022-02-18 00:04:31 +00:00
bool highlight = mHighlighted ;
2012-09-19 15:15:01 +00:00
bool depressed = mDepressed ;
2013-12-08 03:19:42 +00:00
ColorI fontColor = mActive ? ( highlight ? mProfile - > mFontColorHL : mProfile - > mFontColor ) : mProfile - > mFontColorNA ;
2023-03-16 22:21:07 +00:00
ColorI borderColor = mActive ? ( highlight ? mProfile - > mBorderColorHL : mProfile - > mBorderColor ) : mProfile - > mBorderColorNA ;
ColorI fillColor = mActive ? ( highlight ? mProfile - > mFillColorHL : mProfile - > mFillColor ) : mProfile - > mFillColorNA ;
if ( mActive & & ( depressed | | mStateOn ) )
{
fontColor = mProfile - > mFontColorSEL ;
fillColor = mProfile - > mFillColorSEL ;
borderColor = mProfile - > mBorderColorSEL ;
}
2012-09-19 15:15:01 +00:00
RectI boundsRect ( offset , getExtent ( ) ) ;
GFXDrawUtil * drawer = GFX - > getDrawUtil ( ) ;
if ( mDepressed | | mStateOn )
{
// If there is a bitmap array then render using it.
// Otherwise use a standard fill.
2023-03-06 21:22:11 +00:00
if ( mProfile - > mUseBitmapArray & & ! mProfile - > mBitmapArrayRects . empty ( ) )
2012-09-19 15:15:01 +00:00
renderBitmapArray ( boundsRect , statePressed ) ;
else
2023-03-16 22:21:07 +00:00
{
if ( mProfile - > mBorder ! = 0 )
renderFilledBorder ( boundsRect , borderColor , fillColor , mProfile - > mBorderThickness ) ;
else
2023-12-17 05:18:33 +00:00
GFX - > getDrawUtil ( ) - > drawRectFill ( boundsRect , fillColor ) ;
2023-03-16 22:21:07 +00:00
}
2012-09-19 15:15:01 +00:00
}
2022-02-18 00:04:31 +00:00
else if ( mHighlighted & & mActive )
2012-09-19 15:15:01 +00:00
{
// If there is a bitmap array then render using it.
// Otherwise use a standard fill.
2023-03-06 21:22:11 +00:00
if ( mProfile - > mUseBitmapArray & & ! mProfile - > mBitmapArrayRects . empty ( ) )
2022-04-10 06:40:15 +00:00
{
2012-09-19 15:15:01 +00:00
renderBitmapArray ( boundsRect , stateMouseOver ) ;
2022-04-10 06:40:15 +00:00
}
2012-09-19 15:15:01 +00:00
else
2022-04-10 06:40:15 +00:00
{
2023-02-14 23:32:31 +00:00
if ( mProfile - > mBorder ! = 0 )
2023-03-16 22:21:07 +00:00
renderFilledBorder ( boundsRect , borderColor , fillColor , mProfile - > mBorderThickness ) ;
2023-02-14 23:32:31 +00:00
else
2023-12-17 05:18:33 +00:00
GFX - > getDrawUtil ( ) - > drawRectFill ( boundsRect , fillColor ) ;
2022-04-10 06:40:15 +00:00
}
2012-09-19 15:15:01 +00:00
}
else
{
// If there is a bitmap array then render using it.
// Otherwise use a standard fill.
2023-03-06 21:22:11 +00:00
if ( mProfile - > mUseBitmapArray & & ! mProfile - > mBitmapArrayRects . empty ( ) )
2012-09-19 15:15:01 +00:00
{
if ( mActive )
renderBitmapArray ( boundsRect , stateNormal ) ;
else
renderBitmapArray ( boundsRect , stateDisabled ) ;
}
else
{
2023-03-16 22:21:07 +00:00
if ( mProfile - > mBorder ! = 0 )
renderFilledBorder ( boundsRect , borderColor , fillColor , mProfile - > mBorderThickness ) ;
2022-04-10 06:40:15 +00:00
else
2023-03-16 22:21:07 +00:00
GFX - > getDrawUtil ( ) - > drawRectFill ( boundsRect , mProfile - > mFillColor ) ;
2012-09-19 15:15:01 +00:00
}
}
Point2I textPos = offset ;
if ( depressed )
textPos + = Point2I ( 1 , 1 ) ;
RectI iconRect ( 0 , 0 , 0 , 0 ) ;
// Render the icon
2025-05-27 22:07:08 +00:00
if ( getBitmap ( ) & & mIconLocation ! = GuiIconButtonCtrl : : IconLocNone )
2012-09-19 15:15:01 +00:00
{
// Render the normal bitmap
drawer - > clearBitmapModulation ( ) ;
2023-09-05 03:50:45 +00:00
// Size of the bitmap
2024-12-25 20:23:22 +00:00
Point2I textureSize ( getBitmap ( ) - > getWidth ( ) , getBitmap ( ) - > getHeight ( ) ) ;
2023-09-05 03:50:45 +00:00
// Reduce the size with the margin (if set)
textureSize . x = textureSize . x - ( mBitmapMargin * 2 ) ;
textureSize . y = textureSize . y - ( mBitmapMargin * 2 ) ;
2012-09-19 15:15:01 +00:00
// Maintain the bitmap size or fill the button?
if ( ! mFitBitmapToButton )
{
iconRect . set ( offset + mButtonMargin , textureSize ) ;
if ( mIconLocation = = IconLocRight )
{
iconRect . point . x = ( offset . x + getWidth ( ) ) - ( mButtonMargin . x + textureSize . x ) ;
iconRect . point . y = offset . y + ( getHeight ( ) - textureSize . y ) / 2 ;
}
else if ( mIconLocation = = IconLocLeft )
{
iconRect . point . x = offset . x + mButtonMargin . x ;
iconRect . point . y = offset . y + ( getHeight ( ) - textureSize . y ) / 2 ;
}
else if ( mIconLocation = = IconLocCenter )
{
iconRect . point . x = offset . x + ( getWidth ( ) - textureSize . x ) / 2 ;
iconRect . point . y = offset . y + ( getHeight ( ) - textureSize . y ) / 2 ;
}
2024-12-25 20:23:22 +00:00
drawer - > drawBitmapStretch ( getBitmap ( ) , iconRect ) ;
2012-09-19 15:15:01 +00:00
}
else
{
2023-09-05 03:50:45 +00:00
// adding offset with the bitmap margin next to the button margin
Point2I bitMapOffset ( mBitmapMargin , mBitmapMargin ) ;
// set the offset
iconRect . set ( offset + mButtonMargin + bitMapOffset , getExtent ( ) - ( Point2I ( mAbs ( mButtonMargin . x - ( mBitmapMargin * 2 ) ) , mAbs ( mButtonMargin . y - ( mBitmapMargin * 2 ) ) ) * 2 ) ) ;
2012-09-19 15:15:01 +00:00
if ( mMakeIconSquare )
{
// Square the icon to the smaller axis extent.
if ( iconRect . extent . x < iconRect . extent . y )
iconRect . extent . y = iconRect . extent . x ;
else
iconRect . extent . x = iconRect . extent . y ;
}
2022-04-10 06:40:15 +00:00
if ( mIconLocation = = IconLocRight )
{
iconRect . point . x = ( offset . x + getWidth ( ) ) - iconRect . extent . x + mButtonMargin . x ;
}
else if ( mIconLocation = = IconLocLeft )
{
//default state presumes left positioning
}
else if ( mIconLocation = = IconLocCenter )
{
iconRect . point . x = offset . x + ( getWidth ( ) / 2 ) - ( iconRect . extent . x / 2 ) + mButtonMargin . x ;
iconRect . point . y = offset . y + ( getHeight ( ) / 2 ) - ( iconRect . extent . y / 2 ) + mButtonMargin . y ;
}
2024-12-25 20:23:22 +00:00
drawer - > drawBitmapStretch ( getBitmap ( ) , iconRect ) ;
2012-09-19 15:15:01 +00:00
}
}
// Render text
if ( mTextLocation ! = TextLocNone )
{
// Clip text to fit (appends ...),
// pad some space to keep it off our border
String text ( mButtonText ) ;
S32 textWidth = clipText ( text , getWidth ( ) - 4 - mTextMargin ) ;
drawer - > setBitmapModulation ( fontColor ) ;
if ( mTextLocation = = TextLocRight )
{
Point2I start ( mTextMargin , ( getHeight ( ) - mProfile - > mFont - > getHeight ( ) ) / 2 ) ;
2025-05-28 22:45:02 +00:00
if ( getBitmap ( ) & & mIconLocation ! = IconLocNone )
2012-09-19 15:15:01 +00:00
{
2025-05-25 12:40:10 +00:00
start . x = getWidth ( ) - ( iconRect . extent . x + mButtonMargin . x + textWidth ) ;
2012-09-19 15:15:01 +00:00
}
2023-12-17 05:18:33 +00:00
drawer - > setBitmapModulation ( fontColor ) ;
2012-09-19 15:15:01 +00:00
drawer - > drawText ( mProfile - > mFont , start + offset , text , mProfile - > mFontColors ) ;
}
if ( mTextLocation = = TextLocLeft )
{
Point2I start ( mTextMargin , ( getHeight ( ) - mProfile - > mFont - > getHeight ( ) ) / 2 ) ;
2023-12-17 05:18:33 +00:00
drawer - > setBitmapModulation ( fontColor ) ;
2012-09-19 15:15:01 +00:00
drawer - > drawText ( mProfile - > mFont , start + offset , text , mProfile - > mFontColors ) ;
}
if ( mTextLocation = = TextLocCenter )
{
Point2I start ;
2025-05-28 22:45:02 +00:00
if ( getBitmap ( ) & & mIconLocation = = IconLocLeft )
2012-09-19 15:15:01 +00:00
{
start . set ( ( getWidth ( ) - textWidth - iconRect . extent . x ) / 2 + iconRect . extent . x ,
( getHeight ( ) - mProfile - > mFont - > getHeight ( ) ) / 2 ) ;
}
else
start . set ( ( getWidth ( ) - textWidth ) / 2 , ( getHeight ( ) - mProfile - > mFont - > getHeight ( ) ) / 2 ) ;
2023-12-17 05:18:33 +00:00
2012-09-19 15:15:01 +00:00
drawer - > setBitmapModulation ( fontColor ) ;
drawer - > drawText ( mProfile - > mFont , start + offset , text , mProfile - > mFontColors ) ;
}
if ( mTextLocation = = TextLocBottom )
{
Point2I start ;
start . set ( ( getWidth ( ) - textWidth ) / 2 , getHeight ( ) - mProfile - > mFont - > getHeight ( ) - mTextMargin ) ;
// If the text is longer then the box size
// it will get clipped, force Left Justify
if ( textWidth > getWidth ( ) )
start . x = 0 ;
drawer - > setBitmapModulation ( fontColor ) ;
drawer - > drawText ( mProfile - > mFont , start + offset , text , mProfile - > mFontColors ) ;
}
}
renderChildControls ( offset , updateRect ) ;
}
// Draw the bitmap array's borders according to the button's state.
void GuiIconButtonCtrl : : renderBitmapArray ( RectI & bounds , S32 state )
{
switch ( state )
{
case stateNormal :
if ( mProfile - > mBorder = = - 2 )
renderSizableBitmapBordersFilled ( bounds , 1 , mProfile ) ;
else
renderFixedBitmapBordersFilled ( bounds , 1 , mProfile ) ;
break ;
case stateMouseOver :
if ( mProfile - > mBorder = = - 2 )
renderSizableBitmapBordersFilled ( bounds , 2 , mProfile ) ;
else
renderFixedBitmapBordersFilled ( bounds , 2 , mProfile ) ;
break ;
case statePressed :
if ( mProfile - > mBorder = = - 2 )
renderSizableBitmapBordersFilled ( bounds , 3 , mProfile ) ;
else
renderFixedBitmapBordersFilled ( bounds , 3 , mProfile ) ;
break ;
case stateDisabled :
if ( mProfile - > mBorder = = - 2 )
renderSizableBitmapBordersFilled ( bounds , 4 , mProfile ) ;
else
renderFixedBitmapBordersFilled ( bounds , 4 , mProfile ) ;
break ;
}
}
2025-12-25 23:47:31 +00:00
DefineEngineMethod ( GuiIconButtonCtrl , getBitmap , StringTableEntry , ( ) , , " get name " ) {
return object - > getBitmapFile ( ) ;
} DefineEngineMethod ( GuiIconButtonCtrl , getBitmapAsset , StringTableEntry , ( ) , , assetText ( Bitmap , asset reference ) ) {
return object - > _getBitmap ( ) ;
} DefineEngineMethod ( GuiIconButtonCtrl , setBitmap , void , ( const char * assetName ) , , assetText ( Bitmap , assignment . first tries asset then flat file . ) ) {
object - > setBitmap ( StringTable - > insert ( assetName ) ) ;
}