2017-07-13 10:26:03 +02:00
/*
* CWindowObject . 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-15 10:14:49 +03:00
# include "StdInc.h"
# include "CWindowObject.h"
# include "../widgets/MiscWidgets.h"
2022-11-24 16:30:04 +02:00
# include "../widgets/Images.h"
2023-02-01 20:42:06 +02:00
# include "../widgets/TextControls.h"
2014-07-15 10:14:49 +03:00
# include "../gui/CGuiHandler.h"
2023-01-05 19:34:37 +02:00
# include "../gui/CursorHandler.h"
2022-12-09 13:38:46 +02:00
# include "../battle/BattleInterface.h"
# include "../battle/BattleInterfaceClasses.h"
2023-02-01 20:42:06 +02:00
# include "../windows/CMessage.h"
# include "../renderSDL/SDL_PixelAccess.h"
# include "../render/IImage.h"
2024-09-17 23:58:56 +02:00
# include "../render/IScreenHandler.h"
2023-09-04 17:01:44 +02:00
# include "../render/IRenderHandler.h"
2023-06-02 15:42:18 +02:00
# include "../render/Canvas.h"
2014-07-15 10:14:49 +03:00
# include "../CGameInfo.h"
# include "../CPlayerInterface.h"
# include "../../CCallback.h"
# include "../../lib/CConfigHandler.h"
2024-07-20 14:55:17 +02:00
# include "../../lib/texts/CGeneralTextHandler.h" //for Unicode related stuff
2014-07-15 10:14:49 +03:00
2023-01-30 19:55:32 +02:00
# include <SDL_surface.h>
2023-08-23 14:07:50 +02:00
CWindowObject : : CWindowObject ( int options_ , const ImagePath & imageName , Point centerAt ) :
2023-06-16 11:54:07 +02:00
WindowBase ( 0 , Point ( ) ) ,
2018-04-07 13:34:11 +02:00
options ( options_ ) ,
background ( createBg ( imageName , options & PLAYER_COLORED ) )
2014-07-15 10:14:49 +03:00
{
2023-10-31 23:05:22 +02:00
if ( ! ( options & NEEDS_ANIMATED_BACKGROUND ) ) //currently workaround for highscores (currently uses window as normal control, because otherwise videos are not played in background yet)
assert ( parent = = nullptr ) ; //Safe to remove, but windows should not have parent
2014-07-15 10:14:49 +03:00
if ( options & RCLICK_POPUP )
CCS - > curh - > hide ( ) ;
if ( background )
pos = background - > center ( centerAt ) ;
else
center ( centerAt ) ;
if ( ! ( options & SHADOW_DISABLED ) )
setShadow ( true ) ;
}
2023-08-23 14:07:50 +02:00
CWindowObject : : CWindowObject ( int options_ , const ImagePath & imageName ) :
2023-06-16 11:54:07 +02:00
WindowBase ( 0 , Point ( ) ) ,
2018-04-07 13:34:11 +02:00
options ( options_ ) ,
background ( createBg ( imageName , options_ & PLAYER_COLORED ) )
2014-07-15 10:14:49 +03:00
{
2023-10-31 23:05:22 +02:00
if ( ! ( options & NEEDS_ANIMATED_BACKGROUND ) ) //currently workaround for highscores (currently uses window as normal control, because otherwise videos are not played in background yet)
assert ( parent = = nullptr ) ; //Safe to remove, but windows should not have parent
2014-07-15 10:14:49 +03:00
2018-04-07 13:34:11 +02:00
if ( options & RCLICK_POPUP )
2014-07-15 10:14:49 +03:00
CCS - > curh - > hide ( ) ;
2018-04-07 13:34:11 +02:00
if ( background )
2014-07-15 10:14:49 +03:00
pos = background - > center ( ) ;
else
2023-02-03 18:23:53 +02:00
center ( GH . screenDimensions ( ) / 2 ) ;
2014-07-15 10:14:49 +03:00
2018-04-07 13:34:11 +02:00
if ( ! ( options & SHADOW_DISABLED ) )
2014-07-15 10:14:49 +03:00
setShadow ( true ) ;
}
2023-06-13 18:12:01 +02:00
CWindowObject : : ~ CWindowObject ( )
{
if ( options & RCLICK_POPUP )
CCS - > curh - > show ( ) ;
}
2014-07-15 10:14:49 +03:00
2023-08-23 14:07:50 +02:00
std : : shared_ptr < CPicture > CWindowObject : : createBg ( const ImagePath & imageName , bool playerColored )
2014-07-15 10:14:49 +03:00
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2014-07-15 10:14:49 +03:00
2018-04-07 13:34:11 +02:00
if ( imageName . empty ( ) )
2014-07-15 10:14:49 +03:00
return nullptr ;
2018-04-07 13:34:11 +02:00
auto image = std : : make_shared < CPicture > ( imageName ) ;
2023-03-25 23:04:40 +02:00
image - > getSurface ( ) - > setBlitMode ( EImageBlitMode : : OPAQUE ) ;
2018-04-07 13:34:11 +02:00
if ( playerColored )
2024-06-05 17:50:50 +02:00
image - > setPlayerColor ( LOCPLINT - > playerID ) ;
2014-07-15 10:14:49 +03:00
return image ;
}
2023-08-23 14:07:50 +02:00
void CWindowObject : : setBackground ( const ImagePath & filename )
2014-07-15 10:14:49 +03:00
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2014-07-15 10:14:49 +03:00
background = createBg ( filename , options & PLAYER_COLORED ) ;
2018-04-07 13:34:11 +02:00
if ( background )
2014-07-15 10:14:49 +03:00
pos = background - > center ( Point ( pos . w / 2 + pos . x , pos . h / 2 + pos . y ) ) ;
updateShadow ( ) ;
}
void CWindowObject : : updateShadow ( )
{
setShadow ( false ) ;
if ( ! ( options & SHADOW_DISABLED ) )
setShadow ( true ) ;
}
void CWindowObject : : setShadow ( bool on )
{
//size of shadow
2024-09-17 23:58:56 +02:00
int sizeOriginal = 8 ;
int size = sizeOriginal * GH . screenHandler ( ) . getScalingFactor ( ) ;
2014-07-15 10:14:49 +03:00
2018-07-25 00:36:48 +02:00
if ( on = = ! shadowParts . empty ( ) )
2014-07-15 10:14:49 +03:00
return ;
2018-07-25 00:36:48 +02:00
shadowParts . clear ( ) ;
2014-07-15 10:14:49 +03:00
//object too small to cast shadow
2018-04-07 13:34:11 +02:00
if ( pos . h < = size | | pos . w < = size )
2014-07-15 10:14:49 +03:00
return ;
2018-04-07 13:34:11 +02:00
if ( on )
2014-07-15 10:14:49 +03:00
{
//helper to set last row
auto blitAlphaRow = [ ] ( SDL_Surface * surf , size_t row )
{
2023-01-30 19:55:32 +02:00
uint8_t * ptr = ( uint8_t * ) surf - > pixels + surf - > pitch * ( row ) ;
2014-07-15 10:14:49 +03:00
for ( size_t i = 0 ; i < surf - > w ; i + + )
{
Channels : : px < 4 > : : a . set ( ptr , 128 ) ;
ptr + = 4 ;
}
} ;
// helper to set last column
auto blitAlphaCol = [ ] ( SDL_Surface * surf , size_t col )
{
2023-01-30 19:55:32 +02:00
uint8_t * ptr = ( uint8_t * ) surf - > pixels + 4 * ( col ) ;
2014-07-15 10:14:49 +03:00
for ( size_t i = 0 ; i < surf - > h ; i + + )
{
Channels : : px < 4 > : : a . set ( ptr , 128 ) ;
ptr + = surf - > pitch ;
}
} ;
static SDL_Surface * shadowCornerTempl = nullptr ;
static SDL_Surface * shadowBottomTempl = nullptr ;
static SDL_Surface * shadowRightTempl = nullptr ;
//one-time initialization
2018-04-07 13:34:11 +02:00
if ( ! shadowCornerTempl )
2014-07-15 10:14:49 +03:00
{
//create "template" surfaces
shadowCornerTempl = CSDL_Ext : : createSurfaceWithBpp < 4 > ( size , size ) ;
shadowBottomTempl = CSDL_Ext : : createSurfaceWithBpp < 4 > ( 1 , size ) ;
shadowRightTempl = CSDL_Ext : : createSurfaceWithBpp < 4 > ( size , 1 ) ;
//fill with shadow body color
2023-01-17 22:01:35 +02:00
CSDL_Ext : : fillSurface ( shadowCornerTempl , { 0 , 0 , 0 , 192 } ) ;
CSDL_Ext : : fillSurface ( shadowBottomTempl , { 0 , 0 , 0 , 192 } ) ;
CSDL_Ext : : fillSurface ( shadowRightTempl , { 0 , 0 , 0 , 192 } ) ;
2014-07-15 10:14:49 +03:00
//fill last row and column with more transparent color
blitAlphaCol ( shadowRightTempl , size - 1 ) ;
blitAlphaCol ( shadowCornerTempl , size - 1 ) ;
blitAlphaRow ( shadowBottomTempl , size - 1 ) ;
blitAlphaRow ( shadowCornerTempl , size - 1 ) ;
}
//FIXME: do something with this points
Point shadowStart ;
if ( options & BORDERED )
2024-09-17 23:58:56 +02:00
shadowStart = Point ( sizeOriginal - 14 , sizeOriginal - 14 ) ;
2014-07-15 10:14:49 +03:00
else
2024-09-17 23:58:56 +02:00
shadowStart = Point ( sizeOriginal , sizeOriginal ) ;
2014-07-15 10:14:49 +03:00
Point shadowPos ;
if ( options & BORDERED )
shadowPos = Point ( pos . w + 14 , pos . h + 14 ) ;
else
shadowPos = Point ( pos . w , pos . h ) ;
Point fullsize ;
if ( options & BORDERED )
fullsize = Point ( pos . w + 28 , pos . h + 29 ) ;
else
fullsize = Point ( pos . w , pos . h ) ;
//create base 8x8 piece of shadow
SDL_Surface * shadowCorner = CSDL_Ext : : copySurface ( shadowCornerTempl ) ;
2024-09-17 23:58:56 +02:00
SDL_Surface * shadowBottom = CSDL_Ext : : scaleSurface ( shadowBottomTempl , ( fullsize . x - sizeOriginal ) * GH . screenHandler ( ) . getScalingFactor ( ) , size ) ;
SDL_Surface * shadowRight = CSDL_Ext : : scaleSurface ( shadowRightTempl , size , ( fullsize . y - sizeOriginal ) * GH . screenHandler ( ) . getScalingFactor ( ) ) ;
2014-07-15 10:14:49 +03:00
blitAlphaCol ( shadowBottom , 0 ) ;
blitAlphaRow ( shadowRight , 0 ) ;
//generate "shadow" object with these 3 pieces in it
2018-04-07 13:34:11 +02:00
{
2024-08-09 17:30:04 +02:00
OBJECT_CONSTRUCTION ;
2018-04-07 13:34:11 +02:00
2023-09-04 17:01:44 +02:00
shadowParts . push_back ( std : : make_shared < CPicture > ( GH . renderHandler ( ) . createImage ( shadowCorner ) , Point ( shadowPos . x , shadowPos . y ) ) ) ;
shadowParts . push_back ( std : : make_shared < CPicture > ( GH . renderHandler ( ) . createImage ( shadowRight ) , Point ( shadowPos . x , shadowStart . y ) ) ) ;
shadowParts . push_back ( std : : make_shared < CPicture > ( GH . renderHandler ( ) . createImage ( shadowBottom ) , Point ( shadowStart . x , shadowPos . y ) ) ) ;
2018-04-07 13:34:11 +02:00
}
2023-01-30 13:58:13 +02:00
SDL_FreeSurface ( shadowCorner ) ;
SDL_FreeSurface ( shadowBottom ) ;
SDL_FreeSurface ( shadowRight ) ;
2014-07-15 10:14:49 +03:00
}
}
2023-06-02 15:42:18 +02:00
void CWindowObject : : showAll ( Canvas & to )
2014-07-15 10:14:49 +03:00
{
2017-06-03 07:25:10 +02:00
auto color = LOCPLINT ? LOCPLINT - > playerID : PlayerColor ( 1 ) ;
if ( settings [ " session " ] [ " spectate " ] . Bool ( ) )
color = PlayerColor ( 1 ) ; // TODO: Spectator shouldn't need special code for UI colors
2014-07-15 10:14:49 +03:00
CIntObject : : showAll ( to ) ;
2023-06-02 15:42:18 +02:00
if ( ( options & BORDERED ) & & ( pos . dimensions ( ) ! = GH . screenDimensions ( ) ) )
2024-02-26 18:32:15 +02:00
CMessage : : drawBorder ( color , to , pos . w + 28 , pos . h + 29 , pos . x - 14 , pos . y - 15 ) ;
2014-07-15 10:14:49 +03:00
}
2023-06-11 19:38:42 +02:00
bool CWindowObject : : isPopupWindow ( ) const
2014-07-15 10:14:49 +03:00
{
2023-06-11 19:38:42 +02:00
return options & RCLICK_POPUP ;
2014-07-15 10:14:49 +03:00
}