2017-07-13 10:26:03 +02:00
/*
* CComponent . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2014-07-05 12:58:42 +03:00
# include "StdInc.h"
# include "CComponent.h"
2022-09-18 14:47:49 +02:00
# include "Images.h"
2021-02-15 14:03:32 +02:00
# include <vcmi/spells/Service.h>
# include <vcmi/spells/Spell.h>
2014-07-13 20:53:37 +03:00
# include "../gui/CGuiHandler.h"
2023-01-05 19:34:37 +02:00
# include "../gui/CursorHandler.h"
2023-02-01 20:42:06 +02:00
# include "../gui/TextAlignment.h"
2023-04-27 19:21:06 +02:00
# include "../gui/Shortcut.h"
2023-06-02 15:42:18 +02:00
# include "../render/Canvas.h"
2023-07-31 18:50:55 +02:00
# include "../render/IFont.h"
# include "../render/Graphics.h"
2023-02-01 20:42:06 +02:00
# include "../windows/CMessage.h"
2023-02-10 15:50:46 +02:00
# include "../windows/InfoWindows.h"
2023-02-01 20:42:06 +02:00
# include "../widgets/TextControls.h"
2014-07-05 12:58:42 +03:00
# include "../CGameInfo.h"
2023-05-17 15:52:16 +02:00
# include "../../lib/ArtifactUtils.h"
2014-07-05 12:58:42 +03:00
# include "../../lib/CTownHandler.h"
2023-09-28 18:43:04 +02:00
# include "../../lib/CHeroHandler.h"
2023-10-23 12:59:15 +02:00
# include "../../lib/networkPacks/Component.h"
2023-01-02 00:06:42 +02:00
# include "../../lib/spells/CSpellHandler.h"
2014-07-05 12:58:42 +03:00
# include "../../lib/CCreatureHandler.h"
2017-08-30 12:35:23 +02:00
# include "../../lib/CSkillHandler.h"
2014-07-05 12:58:42 +03:00
# include "../../lib/CGeneralTextHandler.h"
2023-02-01 20:42:06 +02:00
# include "../../lib/CArtHandler.h"
2023-06-22 16:08:16 +02:00
# include "../../lib/CArtifactInstance.h"
2014-07-05 12:58:42 +03:00
2023-10-31 11:09:56 +02:00
CComponent : : CComponent ( ComponentType Type , ComponentSubType Subtype , std : : optional < int32_t > Val , ESize imageSize , EFonts font )
2014-07-05 12:58:42 +03:00
{
2023-10-09 21:43:43 +02:00
init ( Type , Subtype , Val , imageSize , font , " " ) ;
}
2023-11-02 13:18:40 +02:00
CComponent : : CComponent ( ComponentType Type , ComponentSubType Subtype , const std : : string & Val , ESize imageSize , EFonts font )
2023-10-09 21:43:43 +02:00
{
2023-10-31 11:09:56 +02:00
init ( Type , Subtype , std : : nullopt , imageSize , font , Val ) ;
2014-07-05 12:58:42 +03:00
}
2023-03-10 00:26:44 +02:00
CComponent : : CComponent ( const Component & c , ESize imageSize , EFonts font )
2014-07-05 12:58:42 +03:00
{
2023-11-02 13:18:40 +02:00
init ( c . type , c . subType , c . value , imageSize , font , " " ) ;
2014-07-05 12:58:42 +03:00
}
2023-11-02 13:18:40 +02:00
void CComponent : : init ( ComponentType Type , ComponentSubType Subtype , std : : optional < int32_t > Val , ESize imageSize , EFonts fnt , const std : : string & ValText )
2014-07-05 12:58:42 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2023-06-13 18:33:35 +02:00
addUsedEvents ( SHOW_POPUP ) ;
2014-07-05 12:58:42 +03:00
2023-10-31 11:09:56 +02:00
data . type = Type ;
data . subType = Subtype ;
data . value = Val ;
customSubtitle = ValText ;
2014-07-05 12:58:42 +03:00
size = imageSize ;
2023-03-10 00:26:44 +02:00
font = fnt ;
2014-07-05 12:58:42 +03:00
assert ( size < sizeInvalid ) ;
2020-10-01 10:38:06 +02:00
setSurface ( getFileName ( ) [ size ] , ( int ) getIndex ( ) ) ;
2014-07-05 12:58:42 +03:00
pos . w = image - > pos . w ;
pos . h = image - > pos . h ;
if ( imageSize < small )
2023-03-10 00:26:44 +02:00
font = FONT_TINY ; //for tiny images - tiny font
2014-07-05 12:58:42 +03:00
pos . h + = 4 ; //distance between text and image
2024-02-26 22:04:02 +02:00
// WARNING: too low values will lead to bad line-breaks in CPlayerOptionTooltipBox - check right-click on starting town in pregame
int max = 80 ;
2023-03-10 00:26:44 +02:00
if ( size < large )
2023-03-10 17:54:37 +02:00
max = 72 ;
2023-03-07 02:18:40 +02:00
if ( size < medium )
2024-02-26 22:04:02 +02:00
max = 60 ;
2023-03-07 02:18:40 +02:00
if ( size < small )
2024-02-26 22:04:02 +02:00
max = 55 ;
2023-03-07 02:18:40 +02:00
2023-10-31 11:09:56 +02:00
if ( Type = = ComponentType : : RESOURCE & & ! ValText . empty ( ) )
2023-10-09 21:43:43 +02:00
max = 80 ;
2023-03-07 02:18:40 +02:00
std : : vector < std : : string > textLines = CMessage : : breakText ( getSubtitle ( ) , std : : max < int > ( max , pos . w ) , font ) ;
2014-07-05 12:58:42 +03:00
for ( auto & line : textLines )
{
2020-10-01 10:38:06 +02:00
int height = static_cast < int > ( graphics - > fonts [ font ] - > getLineHeight ( ) ) ;
2022-11-26 23:12:20 +02:00
auto label = std : : make_shared < CLabel > ( pos . w / 2 , pos . h + height / 2 , font , ETextAlignment : : CENTER , Colors : : WHITE , line ) ;
2014-07-05 12:58:42 +03:00
pos . h + = height ;
2018-04-07 13:34:11 +02:00
if ( label - > pos . w > pos . w )
2014-07-05 12:58:42 +03:00
{
pos . x - = ( label - > pos . w - pos . w ) / 2 ;
pos . w = label - > pos . w ;
}
2018-04-07 13:34:11 +02:00
lines . push_back ( label ) ;
2014-07-05 12:58:42 +03:00
}
}
2023-11-02 13:18:40 +02:00
std : : vector < AnimationPath > CComponent : : getFileName ( ) const
2014-07-05 12:58:42 +03:00
{
2023-10-31 11:09:56 +02:00
static const std : : array < std : : string , 4 > primSkillsArr = { " PSKIL32 " , " PSKIL32 " , " PSKIL42 " , " PSKILL " } ;
static const std : : array < std : : string , 4 > secSkillsArr = { " SECSK32 " , " SECSK32 " , " SECSKILL " , " SECSK82 " } ;
static const std : : array < std : : string , 4 > resourceArr = { " SMALRES " , " RESOURCE " , " RESOURCE " , " RESOUR82 " } ;
static const std : : array < std : : string , 4 > creatureArr = { " CPRSMALL " , " CPRSMALL " , " CPRSMALL " , " TWCRPORT " } ;
static const std : : array < std : : string , 4 > artifactArr = { " Artifact " , " Artifact " , " Artifact " , " Artifact " } ;
static const std : : array < std : : string , 4 > spellsArr = { " SpellInt " , " SpellInt " , " SpellInt " , " SPELLSCR " } ;
static const std : : array < std : : string , 4 > moraleArr = { " IMRL22 " , " IMRL30 " , " IMRL42 " , " imrl82 " } ;
static const std : : array < std : : string , 4 > luckArr = { " ILCK22 " , " ILCK30 " , " ILCK42 " , " ilck82 " } ;
static const std : : array < std : : string , 4 > heroArr = { " PortraitsSmall " , " PortraitsSmall " , " PortraitsSmall " , " PortraitsLarge " } ;
static const std : : array < std : : string , 4 > flagArr = { " CREST58 " , " CREST58 " , " CREST58 " , " CREST58 " } ;
auto gen = [ ] ( const std : : array < std : : string , 4 > & arr ) - > std : : vector < AnimationPath >
2014-07-05 12:58:42 +03:00
{
2023-08-23 14:07:50 +02:00
return { AnimationPath : : builtin ( arr [ 0 ] ) , AnimationPath : : builtin ( arr [ 1 ] ) , AnimationPath : : builtin ( arr [ 2 ] ) , AnimationPath : : builtin ( arr [ 3 ] ) } ;
2014-07-05 12:58:42 +03:00
} ;
2023-10-31 11:09:56 +02:00
switch ( data . type )
2014-07-05 12:58:42 +03:00
{
2023-10-31 11:09:56 +02:00
case ComponentType : : PRIM_SKILL :
case ComponentType : : EXPERIENCE :
case ComponentType : : MANA :
case ComponentType : : LEVEL :
return gen ( primSkillsArr ) ;
2024-04-25 13:04:45 +02:00
case ComponentType : : NONE :
2023-10-31 11:09:56 +02:00
case ComponentType : : SEC_SKILL :
return gen ( secSkillsArr ) ;
case ComponentType : : RESOURCE :
case ComponentType : : RESOURCE_PER_DAY :
return gen ( resourceArr ) ;
case ComponentType : : CREATURE :
return gen ( creatureArr ) ;
case ComponentType : : ARTIFACT :
return gen ( artifactArr ) ;
case ComponentType : : SPELL_SCROLL :
case ComponentType : : SPELL :
return gen ( spellsArr ) ;
case ComponentType : : MORALE :
return gen ( moraleArr ) ;
case ComponentType : : LUCK :
return gen ( luckArr ) ;
case ComponentType : : BUILDING :
return std : : vector < AnimationPath > ( 4 , ( * CGI - > townh ) [ data . subType . as < BuildingTypeUniqueID > ( ) . getFaction ( ) ] - > town - > clientInfo . buildingsIcons ) ;
case ComponentType : : HERO_PORTRAIT :
return gen ( heroArr ) ;
case ComponentType : : FLAG :
return gen ( flagArr ) ;
default :
assert ( 0 ) ;
return { } ;
2014-07-05 12:58:42 +03:00
}
}
2023-11-02 13:18:40 +02:00
size_t CComponent : : getIndex ( ) const
2014-07-05 12:58:42 +03:00
{
2023-10-31 11:09:56 +02:00
switch ( data . type )
2014-07-05 12:58:42 +03:00
{
2024-04-25 13:04:45 +02:00
case ComponentType : : NONE :
return 0 ;
2023-10-31 11:09:56 +02:00
case ComponentType : : PRIM_SKILL :
return data . subType . getNum ( ) ;
case ComponentType : : EXPERIENCE :
case ComponentType : : LEVEL :
return 4 ; // for whatever reason, in H3 experience icon is located in primary skills icons
case ComponentType : : MANA :
return 5 ; // for whatever reason, in H3 mana points icon is located in primary skills icons
case ComponentType : : SEC_SKILL :
return data . subType . getNum ( ) * 3 + 3 + data . value . value_or ( 0 ) - 1 ;
case ComponentType : : RESOURCE :
case ComponentType : : RESOURCE_PER_DAY :
return data . subType . getNum ( ) ;
case ComponentType : : CREATURE :
return CGI - > creatures ( ) - > getById ( data . subType . as < CreatureID > ( ) ) - > getIconIndex ( ) ;
case ComponentType : : ARTIFACT :
return CGI - > artifacts ( ) - > getById ( data . subType . as < ArtifactID > ( ) ) - > getIconIndex ( ) ;
case ComponentType : : SPELL_SCROLL :
case ComponentType : : SPELL :
return ( size < large ) ? data . subType . getNum ( ) + 1 : data . subType . getNum ( ) ;
case ComponentType : : MORALE :
return data . value . value_or ( 0 ) + 3 ;
case ComponentType : : LUCK :
return data . value . value_or ( 0 ) + 3 ;
case ComponentType : : BUILDING :
return data . subType . as < BuildingTypeUniqueID > ( ) . getBuilding ( ) ;
case ComponentType : : HERO_PORTRAIT :
return CGI - > heroTypes ( ) - > getById ( data . subType . as < HeroTypeID > ( ) ) - > getIconIndex ( ) ;
case ComponentType : : FLAG :
return data . subType . getNum ( ) ;
default :
assert ( 0 ) ;
return 0 ;
2014-07-05 12:58:42 +03:00
}
}
2023-11-02 13:18:40 +02:00
std : : string CComponent : : getDescription ( ) const
2014-07-05 12:58:42 +03:00
{
2023-10-31 11:09:56 +02:00
switch ( data . type )
2016-01-21 19:23:45 +02:00
{
2023-10-31 11:09:56 +02:00
case ComponentType : : PRIM_SKILL :
return CGI - > generaltexth - > arraytxt [ 2 + data . subType . getNum ( ) ] ;
case ComponentType : : EXPERIENCE :
case ComponentType : : LEVEL :
return CGI - > generaltexth - > allTexts [ 241 ] ;
case ComponentType : : MANA :
return CGI - > generaltexth - > allTexts [ 149 ] ;
case ComponentType : : SEC_SKILL :
return CGI - > skillh - > getByIndex ( data . subType . getNum ( ) ) - > getDescriptionTranslated ( data . value . value_or ( 0 ) ) ;
case ComponentType : : RESOURCE :
case ComponentType : : RESOURCE_PER_DAY :
return CGI - > generaltexth - > allTexts [ 242 ] ;
2024-04-25 13:04:45 +02:00
case ComponentType : : NONE :
2023-10-31 11:09:56 +02:00
case ComponentType : : CREATURE :
return " " ;
case ComponentType : : ARTIFACT :
return VLC - > artifacts ( ) - > getById ( data . subType . as < ArtifactID > ( ) ) - > getDescriptionTranslated ( ) ;
case ComponentType : : SPELL_SCROLL :
2016-01-21 19:23:45 +02:00
{
2023-10-31 11:09:56 +02:00
auto description = VLC - > arth - > objects [ ArtifactID : : SPELL_SCROLL ] - > getDescriptionTranslated ( ) ;
ArtifactUtils : : insertScrrollSpellName ( description , data . subType . as < SpellID > ( ) ) ;
return description ;
2016-01-21 19:23:45 +02:00
}
2023-10-31 11:09:56 +02:00
case ComponentType : : SPELL :
return VLC - > spells ( ) - > getById ( data . subType . as < SpellID > ( ) ) - > getDescriptionTranslated ( data . value . value_or ( 0 ) ) ;
case ComponentType : : MORALE :
return CGI - > generaltexth - > heroscrn [ 4 - ( data . value . value_or ( 0 ) > 0 ) + ( data . value . value_or ( 0 ) < 0 ) ] ;
case ComponentType : : LUCK :
return CGI - > generaltexth - > heroscrn [ 7 - ( data . value . value_or ( 0 ) > 0 ) + ( data . value . value_or ( 0 ) < 0 ) ] ;
case ComponentType : : BUILDING :
{
auto index = data . subType . as < BuildingTypeUniqueID > ( ) ;
return ( * CGI - > townh ) [ index . getFaction ( ) ] - > town - > buildings [ index . getBuilding ( ) ] - > getDescriptionTranslated ( ) ;
}
case ComponentType : : HERO_PORTRAIT :
return " " ;
case ComponentType : : FLAG :
return " " ;
default :
assert ( 0 ) ;
2023-11-02 13:18:40 +02:00
return " " ;
2014-07-05 12:58:42 +03:00
}
}
2023-11-02 13:18:40 +02:00
std : : string CComponent : : getSubtitle ( ) const
2014-07-05 12:58:42 +03:00
{
2023-10-31 11:09:56 +02:00
if ( ! customSubtitle . empty ( ) )
return customSubtitle ;
2014-07-05 12:58:42 +03:00
2023-10-31 11:09:56 +02:00
switch ( data . type )
2014-07-05 12:58:42 +03:00
{
2023-10-31 11:09:56 +02:00
case ComponentType : : PRIM_SKILL :
if ( data . value )
return boost : : str ( boost : : format ( " %+d %s " ) % data . value . value_or ( 0 ) % CGI - > generaltexth - > primarySkillNames [ data . subType . getNum ( ) ] ) ;
2023-01-02 18:00:51 +02:00
else
2023-10-31 11:09:56 +02:00
return CGI - > generaltexth - > primarySkillNames [ data . subType . getNum ( ) ] ;
case ComponentType : : EXPERIENCE :
return std : : to_string ( data . value . value_or ( 0 ) ) ;
case ComponentType : : LEVEL :
{
std : : string level = CGI - > generaltexth - > allTexts [ 442 ] ;
boost : : replace_first ( level , " 1 " , std : : to_string ( data . value . value_or ( 0 ) ) ) ;
return level ;
2023-01-02 18:00:51 +02:00
}
2023-10-31 11:09:56 +02:00
case ComponentType : : MANA :
return boost : : str ( boost : : format ( " %+d %s " ) % data . value . value_or ( 0 ) % CGI - > generaltexth - > allTexts [ 387 ] ) ;
case ComponentType : : SEC_SKILL :
return CGI - > generaltexth - > levels [ data . value . value_or ( 0 ) - 1 ] + " \n " + CGI - > skillh - > getById ( data . subType . as < SecondarySkill > ( ) ) - > getNameTranslated ( ) ;
case ComponentType : : RESOURCE :
return std : : to_string ( data . value . value_or ( 0 ) ) ;
case ComponentType : : RESOURCE_PER_DAY :
2023-11-02 13:18:40 +02:00
return boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 3 ] ) % data . value . value_or ( 0 ) ) ;
2023-10-31 11:09:56 +02:00
case ComponentType : : CREATURE :
2014-07-05 12:58:42 +03:00
{
2023-10-31 11:09:56 +02:00
auto creature = CGI - > creh - > getById ( data . subType . as < CreatureID > ( ) ) ;
2023-11-02 13:18:40 +02:00
if ( data . value )
return std : : to_string ( * data . value ) + " " + ( * data . value > 1 ? creature - > getNamePluralTranslated ( ) : creature - > getNameSingularTranslated ( ) ) ;
2014-07-05 12:58:42 +03:00
else
2023-11-02 13:18:40 +02:00
return creature - > getNamePluralTranslated ( ) ;
2014-07-05 12:58:42 +03:00
}
2023-10-31 11:09:56 +02:00
case ComponentType : : ARTIFACT :
return CGI - > artifacts ( ) - > getById ( data . subType . as < ArtifactID > ( ) ) - > getNameTranslated ( ) ;
case ComponentType : : SPELL_SCROLL :
case ComponentType : : SPELL :
return CGI - > spells ( ) - > getById ( data . subType . as < SpellID > ( ) ) - > getNameTranslated ( ) ;
2024-04-25 13:04:45 +02:00
case ComponentType : : NONE :
2023-10-31 11:09:56 +02:00
case ComponentType : : MORALE :
case ComponentType : : LUCK :
2024-04-25 13:04:45 +02:00
case ComponentType : : HERO_PORTRAIT :
2023-10-31 11:09:56 +02:00
return " " ;
case ComponentType : : BUILDING :
2016-01-19 09:30:55 +02:00
{
2023-10-31 11:09:56 +02:00
auto index = data . subType . as < BuildingTypeUniqueID > ( ) ;
auto building = ( * CGI - > townh ) [ index . getFaction ( ) ] - > town - > buildings [ index . getBuilding ( ) ] ;
if ( ! building )
{
logGlobal - > error ( " Town of faction %s has no building #%d " , ( * CGI - > townh ) [ index . getFaction ( ) ] - > town - > faction - > getNameTranslated ( ) , index . getBuilding ( ) . getNum ( ) ) ;
return ( boost : : format ( " Missing building #%d " ) % index . getBuilding ( ) . getNum ( ) ) . str ( ) ;
}
return building - > getNameTranslated ( ) ;
2016-01-19 09:30:55 +02:00
}
2023-10-31 11:09:56 +02:00
case ComponentType : : FLAG :
return CGI - > generaltexth - > capColors [ data . subType . as < PlayerColor > ( ) . getNum ( ) ] ;
default :
assert ( 0 ) ;
return " " ;
2014-07-05 12:58:42 +03:00
}
}
2023-08-23 14:07:50 +02:00
void CComponent : : setSurface ( const AnimationPath & defName , int imgPos )
2014-07-05 12:58:42 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING ( 255 - DISPOSE ) ;
image = std : : make_shared < CAnimImage > ( defName , imgPos ) ;
2014-07-05 12:58:42 +03:00
}
2023-07-08 13:33:04 +02:00
void CComponent : : showPopupWindow ( const Point & cursorPosition )
2014-07-05 12:58:42 +03:00
{
2023-06-11 17:20:10 +02:00
if ( ! getDescription ( ) . empty ( ) )
2023-02-10 15:50:46 +02:00
CRClickPopup : : createAndPush ( getDescription ( ) ) ;
2014-07-05 12:58:42 +03:00
}
2023-07-08 13:33:04 +02:00
void CSelectableComponent : : clickPressed ( const Point & cursorPosition )
2014-07-05 12:58:42 +03:00
{
2023-07-08 13:33:04 +02:00
if ( onSelect )
onSelect ( ) ;
2014-07-05 12:58:42 +03:00
}
2023-09-13 23:17:44 +02:00
void CSelectableComponent : : clickDouble ( const Point & cursorPosition )
{
2024-05-10 18:05:59 +02:00
if ( ! selected )
{
clickPressed ( cursorPosition ) ;
}
else
{
if ( onChoose )
onChoose ( ) ;
}
2023-09-13 23:17:44 +02:00
}
2014-07-05 12:58:42 +03:00
void CSelectableComponent : : init ( )
{
selected = false ;
}
CSelectableComponent : : CSelectableComponent ( const Component & c , std : : function < void ( ) > OnSelect ) :
CComponent ( c ) , onSelect ( OnSelect )
{
2023-07-03 18:24:12 +02:00
setRedrawParent ( true ) ;
2023-09-13 23:17:44 +02:00
addUsedEvents ( LCLICK | DOUBLECLICK | KEYBOARD ) ;
2014-07-05 12:58:42 +03:00
init ( ) ;
}
2023-10-31 11:09:56 +02:00
CSelectableComponent : : CSelectableComponent ( ComponentType Type , ComponentSubType Sub , int Val , ESize imageSize , std : : function < void ( ) > OnSelect ) :
2014-07-05 12:58:42 +03:00
CComponent ( Type , Sub , Val , imageSize ) , onSelect ( OnSelect )
{
2023-07-03 18:24:12 +02:00
setRedrawParent ( true ) ;
2023-09-13 23:17:44 +02:00
addUsedEvents ( LCLICK | DOUBLECLICK | KEYBOARD ) ;
2014-07-05 12:58:42 +03:00
init ( ) ;
}
void CSelectableComponent : : select ( bool on )
{
if ( on ! = selected )
{
selected = on ;
redraw ( ) ;
}
}
2023-06-02 15:42:18 +02:00
void CSelectableComponent : : showAll ( Canvas & to )
2014-07-05 12:58:42 +03:00
{
CComponent : : showAll ( to ) ;
if ( selected )
{
2023-06-02 15:42:18 +02:00
to . drawBorder ( Rect : : createAround ( image - > pos , 1 ) , Colors : : BRIGHT_YELLOW ) ;
2014-07-05 12:58:42 +03:00
}
}
2018-04-07 13:34:11 +02:00
void CComponentBox : : selectionChanged ( std : : shared_ptr < CSelectableComponent > newSelection )
2014-07-05 12:58:42 +03:00
{
if ( newSelection = = selected )
return ;
if ( selected )
selected - > select ( false ) ;
selected = newSelection ;
if ( onSelect )
onSelect ( selectedIndex ( ) ) ;
if ( selected )
selected - > select ( true ) ;
}
int CComponentBox : : selectedIndex ( )
{
if ( selected )
2020-10-01 10:38:06 +02:00
return static_cast < int > ( std : : find ( components . begin ( ) , components . end ( ) , selected ) - components . begin ( ) ) ;
2014-07-05 12:58:42 +03:00
return - 1 ;
}
Point CComponentBox : : getOrTextPos ( CComponent * left , CComponent * right )
{
int leftSubtitle = ( left - > pos . w - left - > image - > pos . w ) / 2 ;
int rightSubtitle = ( right - > pos . w - right - > image - > pos . w ) / 2 ;
int fullDistance = getDistance ( left , right ) + leftSubtitle + rightSubtitle ;
return Point ( fullDistance / 2 - leftSubtitle , ( left - > image - > pos . h + right - > image - > pos . h ) / 4 ) ;
}
int CComponentBox : : getDistance ( CComponent * left , CComponent * right )
{
int leftSubtitle = ( left - > pos . w - left - > image - > pos . w ) / 2 ;
int rightSubtitle = ( right - > pos . w - right - > image - > pos . w ) / 2 ;
int subtitlesOffset = leftSubtitle + rightSubtitle ;
return std : : max ( betweenSubtitlesMin , betweenImagesMin - subtitlesOffset ) ;
}
void CComponentBox : : placeComponents ( bool selectable )
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2014-07-05 12:58:42 +03:00
if ( components . empty ( ) )
return ;
//prepare components
for ( auto & comp : components )
{
2018-04-07 13:34:11 +02:00
addChild ( comp . get ( ) ) ;
2024-02-26 23:26:06 +02:00
comp - > recActions = defActions ; //FIXME: for some reason, received component might have recActions set to 0
2014-07-05 12:58:42 +03:00
comp - > moveTo ( Point ( pos . x , pos . y ) ) ;
}
struct RowData
{
size_t comps ;
int width ;
int height ;
RowData ( size_t Comps , int Width , int Height ) :
comps ( Comps ) , width ( Width ) , height ( Height ) { } ;
} ;
std : : vector < RowData > rows ;
rows . push_back ( RowData ( 0 , 0 , 0 ) ) ;
//split components in rows
2018-04-07 13:34:11 +02:00
std : : shared_ptr < CComponent > prevComp ;
2014-07-05 12:58:42 +03:00
2018-04-07 13:34:11 +02:00
for ( std : : shared_ptr < CComponent > comp : components )
2014-07-05 12:58:42 +03:00
{
//make sure that components are smaller than our width
//assert(pos.w == 0 || pos.w < comp->pos.w);
2018-04-07 13:34:11 +02:00
const int distance = prevComp ? getDistance ( prevComp . get ( ) , comp . get ( ) ) : 0 ;
2014-07-05 12:58:42 +03:00
//start next row
if ( ( pos . w ! = 0 & & rows . back ( ) . width + comp - > pos . w + distance > pos . w ) // row is full
2023-03-10 17:54:37 +02:00
| | rows . back ( ) . comps > = componentsInRow )
2014-07-05 12:58:42 +03:00
{
prevComp = nullptr ;
rows . push_back ( RowData ( 0 , 0 , 0 ) ) ;
}
if ( prevComp )
rows . back ( ) . width + = distance ;
rows . back ( ) . comps + + ;
rows . back ( ) . width + = comp - > pos . w ;
vstd : : amax ( rows . back ( ) . height , comp - > pos . h ) ;
prevComp = comp ;
}
if ( pos . w = = 0 )
{
for ( auto & row : rows )
vstd : : amax ( pos . w , row . width ) ;
}
2020-10-01 10:38:06 +02:00
int height = ( ( int ) rows . size ( ) - 1 ) * betweenRows ;
2014-07-05 12:58:42 +03:00
for ( auto & row : rows )
height + = row . height ;
//assert(pos.h == 0 || pos.h < height);
if ( pos . h = = 0 )
pos . h = height ;
auto iter = components . begin ( ) ;
int currentY = ( pos . h - height ) / 2 ;
//move components to their positions
for ( auto & rows_row : rows )
{
// amount of free space we may add on each side of every component
2020-10-01 10:38:06 +02:00
int freeSpace = ( pos . w - rows_row . width ) / ( ( int ) rows_row . comps * 2 ) ;
2014-07-05 12:58:42 +03:00
prevComp = nullptr ;
int currentX = 0 ;
for ( size_t col = 0 ; col < rows_row . comps ; col + + )
{
currentX + = freeSpace ;
if ( prevComp )
{
if ( selectable )
{
2018-04-07 13:34:11 +02:00
Point orPos = Point ( currentX - freeSpace , currentY ) + getOrTextPos ( prevComp . get ( ) , iter - > get ( ) ) ;
2014-07-05 12:58:42 +03:00
2022-11-26 23:12:20 +02:00
orLabels . push_back ( std : : make_shared < CLabel > ( orPos . x , orPos . y , FONT_MEDIUM , ETextAlignment : : CENTER , Colors : : WHITE , CGI - > generaltexth - > allTexts [ 4 ] ) ) ;
2014-07-05 12:58:42 +03:00
}
2018-04-07 13:34:11 +02:00
currentX + = getDistance ( prevComp . get ( ) , iter - > get ( ) ) ;
2014-07-05 12:58:42 +03:00
}
( * iter ) - > moveBy ( Point ( currentX , currentY ) ) ;
currentX + = ( * iter ) - > pos . w ;
currentX + = freeSpace ;
prevComp = * ( iter + + ) ;
}
currentY + = rows_row . height + betweenRows ;
}
}
2018-04-07 13:34:11 +02:00
CComponentBox : : CComponentBox ( std : : vector < std : : shared_ptr < CComponent > > _components , Rect position ) :
2023-03-10 17:54:37 +02:00
CComponentBox ( _components , position , defaultBetweenImagesMin , defaultBetweenSubtitlesMin , defaultBetweenRows , defaultComponentsInRow )
2023-03-06 01:30:21 +02:00
{
}
2023-03-10 17:54:37 +02:00
CComponentBox : : CComponentBox ( std : : vector < std : : shared_ptr < CComponent > > _components , Rect position , int betweenImagesMin , int betweenSubtitlesMin , int betweenRows , int componentsInRow ) :
2023-03-06 01:30:21 +02:00
components ( _components ) ,
betweenImagesMin ( betweenImagesMin ) ,
betweenSubtitlesMin ( betweenSubtitlesMin ) ,
2023-03-10 17:54:37 +02:00
betweenRows ( betweenRows ) ,
componentsInRow ( componentsInRow )
2014-07-05 12:58:42 +03:00
{
2023-07-03 18:24:12 +02:00
setRedrawParent ( true ) ;
2022-12-15 23:24:03 +02:00
pos = position + pos . topLeft ( ) ;
2014-07-05 12:58:42 +03:00
placeComponents ( false ) ;
}
2018-04-07 13:34:11 +02:00
CComponentBox : : CComponentBox ( std : : vector < std : : shared_ptr < CSelectableComponent > > _components , Rect position , std : : function < void ( int newID ) > _onSelect ) :
2023-03-10 17:54:37 +02:00
CComponentBox ( _components , position , _onSelect , defaultBetweenImagesMin , defaultBetweenSubtitlesMin , defaultBetweenRows , defaultComponentsInRow )
2023-03-06 01:30:21 +02:00
{
}
2023-03-10 17:54:37 +02:00
CComponentBox : : CComponentBox ( std : : vector < std : : shared_ptr < CSelectableComponent > > _components , Rect position , std : : function < void ( int newID ) > _onSelect , int betweenImagesMin , int betweenSubtitlesMin , int betweenRows , int componentsInRow ) :
2014-07-05 12:58:42 +03:00
components ( _components . begin ( ) , _components . end ( ) ) ,
2023-03-06 01:30:21 +02:00
onSelect ( _onSelect ) ,
betweenImagesMin ( betweenImagesMin ) ,
betweenSubtitlesMin ( betweenSubtitlesMin ) ,
2023-03-10 17:54:37 +02:00
betweenRows ( betweenRows ) ,
componentsInRow ( componentsInRow )
2014-07-05 12:58:42 +03:00
{
2023-07-03 18:24:12 +02:00
setRedrawParent ( true ) ;
2022-12-15 23:24:03 +02:00
pos = position + pos . topLeft ( ) ;
2014-07-05 12:58:42 +03:00
placeComponents ( true ) ;
assert ( ! components . empty ( ) ) ;
2023-04-27 19:21:06 +02:00
auto key = EShortcut : : SELECT_INDEX_1 ;
2014-07-05 12:58:42 +03:00
for ( auto & comp : _components )
{
2014-08-09 15:14:31 +03:00
comp - > onSelect = std : : bind ( & CComponentBox : : selectionChanged , this , comp ) ;
2023-04-27 19:21:06 +02:00
comp - > assignedKey = key ;
2023-04-27 23:29:16 +02:00
key = vstd : : next ( key , 1 ) ;
2014-07-05 12:58:42 +03:00
}
selectionChanged ( _components . front ( ) ) ;
}