Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing button shadows in dialogs #4350

Merged
merged 50 commits into from
Dec 3, 2021
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
46a601f
Add missing shadow from config button in game info dialog
a1exsh Oct 9, 2021
f93f7eb
copy over shadow from existing button
a1exsh Oct 10, 2021
c263118
Merge branch 'master' into config-btn-drop-shadow
a1exsh Oct 12, 2021
4729e8c
add shadows for okay/restart buttons in battle summary
a1exsh Oct 12, 2021
a342dff
add shadow for buttons in game config and ext. settings dialogs
a1exsh Oct 12, 2021
7fac619
remove corner-cutting code for config button: it's not needed
a1exsh Oct 12, 2021
f88a702
extract common function: drawButtonWithShadow
a1exsh Oct 12, 2021
b961f69
remove extra newline
a1exsh Oct 12, 2021
1ef1f90
remove unused include
a1exsh Oct 12, 2021
dee3cf9
move #include to the header: fix compilation
a1exsh Oct 12, 2021
469f00c
fix shadow and position of the OKAY button in battle-only screen
a1exsh Oct 12, 2021
4a088d5
Merge branch 'master' into config-btn-drop-shadow
a1exsh Oct 15, 2021
12c9cab
add buttons shadow in town portal spell dialog
a1exsh Oct 15, 2021
36ea566
better shadow in town portal dialog
a1exsh Oct 16, 2021
bb4a700
add button shadows
a1exsh Oct 30, 2021
f002314
Merge branch 'master' into config-btn-drop-shadow
a1exsh Oct 30, 2021
2d6ea30
remove debug code
a1exsh Oct 30, 2021
6f42218
Add shadows to the buttons on surrender dialog
a1exsh Oct 30, 2021
98a8a48
add captureBackground() to button
a1exsh Oct 31, 2021
33b722e
less code duplication
a1exsh Oct 31, 2021
2771d5c
Merge branch 'master' into config-btn-drop-shadow
a1exsh Oct 31, 2021
542b89a
fix button corners
a1exsh Oct 31, 2021
6b1958d
remove unneeded include
a1exsh Oct 31, 2021
28ec4ea
display is default for capture background anyway
a1exsh Nov 1, 2021
ba21e6c
Merge branch 'master' into config-btn-drop-shadow
a1exsh Nov 2, 2021
e262ef0
don't change ICNs used for okay button on game settings dialog
a1exsh Nov 2, 2021
cca6337
fix too bold shadow on pressed market and army buttons
a1exsh Nov 2, 2021
03e552d
Merge branch 'master' into config-btn-drop-shadow
a1exsh Nov 13, 2021
a4f4b08
remove debug code
a1exsh Nov 13, 2021
012cfad
AutoShadowButton
a1exsh Nov 13, 2021
58ac02c
fix ok/restart buttons
a1exsh Nov 13, 2021
2d5401d
use one memcpy
a1exsh Nov 13, 2021
64609d5
add shadows in button group
a1exsh Nov 14, 2021
ff3bbcd
add shadows in button group
a1exsh Nov 14, 2021
a6231f3
add comment
a1exsh Nov 14, 2021
c7fb156
remove capturebackground calls
a1exsh Nov 14, 2021
0ae7669
use AutoShadowButton for config button as well
a1exsh Nov 14, 2021
599850d
tidy
a1exsh Nov 14, 2021
d0370fe
add AutoBackgroundButton
a1exsh Nov 14, 2021
53c7d2d
sprite offset correction
a1exsh Nov 14, 2021
f991982
fix okay button shadow in battle summary dialog
a1exsh Nov 15, 2021
f101b27
fix TODO
a1exsh Nov 15, 2021
6b1d01d
it compiled!!
a1exsh Nov 21, 2021
419eab4
put back missing reset() call
a1exsh Nov 21, 2021
2d4ba45
put back shadows for button groups
a1exsh Nov 21, 2021
6e06938
remove move-constructor from ButtonBase
a1exsh Nov 21, 2021
cb4de6f
address review comments
a1exsh Nov 23, 2021
d074aa0
restore private access in ButtonBase
a1exsh Nov 23, 2021
4fa8b21
fix review comments
a1exsh Nov 28, 2021
759838c
Merge branch 'master' into config-btn-drop-shadow
a1exsh Dec 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/engine/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,15 @@ namespace fheroes2
}
}

void ReplaceTransform( const Image & in, Image & out )
{
if ( in.empty() || out.empty() || in.singleLayer() || out.singleLayer() || in.width() != out.width() || in.height() != out.height() ) {
assert( 0 );
return;
}
memcpy( out.transform(), in.transform(), in.width() * in.height() );
}

void Resize( const Image & in, Image & out, const bool isSubpixelAccuracy )
{
if ( in.empty() || out.empty() ) {
Expand Down Expand Up @@ -2113,4 +2122,48 @@ namespace fheroes2
}
}
}

Sprite addShadow( const Sprite & in, const Point & shadowOffset, const uint8_t transformValue )
{
if ( in.empty() || shadowOffset.x > 0 || shadowOffset.y < 0 )
return in;

Sprite out = makeShadow( in, shadowOffset, transformValue );
Blit( in, out, -shadowOffset.x, 0 );

return out;
}

Sprite makeShadow( const Sprite & in, const Point & shadowOffset, const uint8_t transformValue )
{
if ( in.empty() || shadowOffset.x > 0 || shadowOffset.y < 0 )
return Sprite();

const int32_t width = in.width();
const int32_t height = in.height();

// Shadow has (-x, +y) offset.
Sprite out( width - shadowOffset.x, height + shadowOffset.y, in.x() + shadowOffset.x, in.y() );
out.reset();

const int32_t widthOut = out.width();

const uint8_t * transformInY = in.transform();
const uint8_t * transformInYEnd = transformInY + width * height;
uint8_t * transformOutY = out.transform() + shadowOffset.y * widthOut;

for ( ; transformInY != transformInYEnd; transformInY += width, transformOutY += widthOut ) {
const uint8_t * transformInX = transformInY;
uint8_t * transformOutX = transformOutY;
const uint8_t * transformInXEnd = transformInX + width;

for ( ; transformInX != transformInXEnd; ++transformInX, ++transformOutX ) {
if ( *transformInX == 0 ) {
*transformOutX = transformValue;
}
}
}

return out;
}
}
8 changes: 8 additions & 0 deletions src/engine/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ namespace fheroes2
// Use this function only when you need to convert pixel value into transform layer
void ReplaceColorIdByTransformId( Image & image, uint8_t colorId, uint8_t transformId );

// Replace transform layer of the output image with the values found in the input image. Both images must be of the same size.
void ReplaceTransform( const Image & in, Image & out );

// Please remember that subpixel accuracy resizing is extremely slow!
void Resize( const Image & in, Image & out, const bool isSubpixelAccuracy = false );

Expand All @@ -267,4 +270,9 @@ namespace fheroes2
Image Stretch( const Image & in, int32_t inX, int32_t inY, int32_t widthIn, int32_t heightIn, int32_t widthOut, int32_t heightOut );

void Transpose( const Image & in, Image & out );

// Generates a new image with a shadow of the shape of existing image. Shadow must have only (-x, +y) offset.
Sprite addShadow( const Sprite & in, const Point & shadowOffset, const uint8_t transformValue );

Sprite makeShadow( const Sprite & in, const Point & shadowOffset, const uint8_t transformValue );
}
39 changes: 1 addition & 38 deletions src/engine/image_tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/

#include <string>

#include "image_palette.h"
#include "image_tool.h"
#include "image_palette.h"

#include <SDL_version.h>
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
Expand Down Expand Up @@ -298,39 +296,4 @@ namespace fheroes2

return sprite;
}

Sprite addShadow( const Sprite & in, const Point & shadowOffset, const uint8_t shadowType )
{
if ( in.empty() || shadowOffset.x > 0 || shadowOffset.y < 0 )
return in;

const int32_t width = in.width();
const int32_t height = in.height();

Sprite out( width - shadowOffset.x, height + shadowOffset.y );
out.reset();

Copy( in, 0, 0, out, -shadowOffset.x, 0, width, height );

const int32_t widthOut = out.width();

// Shadow has (-x, +y) offset.
const uint8_t * transformInY = out.transform() - shadowOffset.x;
const uint8_t * transformInYEnd = transformInY + widthOut * height;
uint8_t * transformOutY = out.transform() + shadowOffset.y * widthOut;

for ( ; transformInY != transformInYEnd; transformInY += widthOut, transformOutY += widthOut ) {
const uint8_t * transformInX = transformInY;
uint8_t * transformOutX = transformOutY;
const uint8_t * transformInXEnd = transformInX + width;

for ( ; transformInX != transformInXEnd; ++transformInX, ++transformOutX ) {
if ( *transformInX == 0 && *transformOutX == 1 ) {
*transformOutX = shadowType;
}
}
}

return out;
}
}
5 changes: 2 additions & 3 deletions src/engine/image_tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#pragma once

#include <string>

#include "image.h"

namespace fheroes2
Expand All @@ -32,7 +34,4 @@ namespace fheroes2
bool Load( const std::string & path, Image & image );

Sprite decodeICNSprite( const uint8_t * data, uint32_t sizeData, const int32_t width, const int32_t height, const int16_t offsetX, const int16_t offsetY );

// Generates a new image with a shadow of the shape of existing image. Shadow must have only (-x, +y) offset.
Sprite addShadow( const Sprite & in, const Point & shadowOffset, const uint8_t shadowType );
}
52 changes: 52 additions & 0 deletions src/fheroes2/agg/agg_image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,9 @@ namespace fheroes2

_icnVsSprite[id][1] = GetICN( ICN::CAMPXTRG, 5 );
_icnVsSprite[id][1].setPosition( 0, 0 );

// fix transparent corners
ReplaceTransform( _icnVsSprite[id][1], _icnVsSprite[id][0] );
return true;
case ICN::NON_UNIFORM_GOOD_CANCEL_BUTTON:
_icnVsSprite[id].resize( 2 );
Expand All @@ -876,6 +879,9 @@ namespace fheroes2

_icnVsSprite[id][1] = GetICN( ICN::CAMPXTRG, 7 );
_icnVsSprite[id][1].setPosition( 0, 0 );

// fix transparent corners
ReplaceTransform( _icnVsSprite[id][1], _icnVsSprite[id][0] );
return true;
case ICN::NON_UNIFORM_GOOD_RESTART_BUTTON:
_icnVsSprite[id].resize( 2 );
Expand All @@ -884,6 +890,9 @@ namespace fheroes2

_icnVsSprite[id][1] = GetICN( ICN::CAMPXTRG, 3 );
_icnVsSprite[id][1].setPosition( 0, 0 );

// fix transparent corners
ReplaceTransform( _icnVsSprite[id][1], _icnVsSprite[id][0] );
return true;
case ICN::NON_UNIFORM_EVIL_OKAY_BUTTON:
_icnVsSprite[id].resize( 2 );
Expand All @@ -892,6 +901,9 @@ namespace fheroes2

_icnVsSprite[id][1] = GetICN( ICN::CAMPXTRE, 5 );
_icnVsSprite[id][1].setPosition( 0, 0 );

// fix transparent corners
ReplaceTransform( _icnVsSprite[id][1], _icnVsSprite[id][0] );
return true;
case ICN::NON_UNIFORM_EVIL_CANCEL_BUTTON:
_icnVsSprite[id].resize( 2 );
Expand All @@ -900,6 +912,9 @@ namespace fheroes2

_icnVsSprite[id][1] = GetICN( ICN::CAMPXTRE, 7 );
_icnVsSprite[id][1].setPosition( 0, 0 );

// fix transparent corners
ReplaceTransform( _icnVsSprite[id][1], _icnVsSprite[id][0] );
return true;
case ICN::NON_UNIFORM_EVIL_RESTART_BUTTON:
_icnVsSprite[id].resize( 2 );
Expand All @@ -908,6 +923,9 @@ namespace fheroes2

_icnVsSprite[id][1] = GetICN( ICN::CAMPXTRE, 3 );
_icnVsSprite[id][1].setPosition( 0, 0 );

// fix transparent corners
ReplaceTransform( _icnVsSprite[id][1], _icnVsSprite[id][0] );
return true;
case ICN::UNIFORM_GOOD_MAX_BUTTON: {
_icnVsSprite[id].resize( 2 );
Expand Down Expand Up @@ -1441,6 +1459,40 @@ namespace fheroes2
}
return true;
}
case ICN::GOOD_ARMY_BUTTON:
case ICN::GOOD_MARKET_BUTTON: {
_icnVsSprite[id].resize( 2 );

const int releasedIndex = ( id == ICN::GOOD_ARMY_BUTTON ) ? 0 : 4;
_icnVsSprite[id][0] = GetICN( ICN::ADVBTNS, releasedIndex );
_icnVsSprite[id][1] = GetICN( ICN::ADVBTNS, releasedIndex + 1 );
AddTransparency( _icnVsSprite[id][0], 36 );
AddTransparency( _icnVsSprite[id][1], 36 );
AddTransparency( _icnVsSprite[id][1], 61 ); // remove the extra brown border

return true;
}
case ICN::EVIL_ARMY_BUTTON:
case ICN::EVIL_MARKET_BUTTON: {
_icnVsSprite[id].resize( 2 );

const int releasedIndex = ( id == ICN::EVIL_ARMY_BUTTON ) ? 0 : 4;
_icnVsSprite[id][0] = GetICN( ICN::ADVEBTNS, releasedIndex );
AddTransparency( _icnVsSprite[id][0], 36 );

Sprite pressed = GetICN( ICN::ADVEBTNS, releasedIndex + 1 );
AddTransparency( pressed, 36 );
AddTransparency( pressed, 61 ); // remove the extra brown border

Sprite fixed( pressed.width(), pressed.height(), pressed.x(), pressed.y() );
fixed.reset();
// put back pixels that actually should be black
Fill( fixed, 1, 4, 31, 31, 36 );
Blit( pressed, fixed );
_icnVsSprite[id][1] = fixed;

return true;
}
default:
break;
}
Expand Down
5 changes: 5 additions & 0 deletions src/fheroes2/agg/icn.h
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,11 @@ namespace ICN
MAP_TYPE_ICON,
BARBARIAN_CASTLE_CAPTAIN_QUARTERS_LEFT_SIDE,

GOOD_ARMY_BUTTON,
GOOD_MARKET_BUTTON,
EVIL_ARMY_BUTTON,
EVIL_MARKET_BUTTON,

// IMPORTANT! Put any new entry just above this one.
LASTICN
};
Expand Down
64 changes: 28 additions & 36 deletions src/fheroes2/battle/battle_dialogs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,14 +448,6 @@ bool Battle::Arena::DialogBattleSummary( const Result & res, const std::vector<A
fheroes2::Blit( sequenceBase, display, pos_rt.x + anime_ox + sequenceBase.x(), pos_rt.y + anime_oy + sequenceBase.y() );
fheroes2::Blit( sequenceStart, display, pos_rt.x + anime_ox + sequenceStart.x(), pos_rt.y + anime_oy + sequenceStart.y() );

const int buttonOffset = allowToCancel ? 39 : 121;
const int buttonOkICN
= isEvilInterface ? ( allowToCancel ? ICN::NON_UNIFORM_EVIL_OKAY_BUTTON : ICN::WINCMBBE ) : ( allowToCancel ? ICN::NON_UNIFORM_GOOD_OKAY_BUTTON : ICN::WINCMBTB );
const int buttonCancelICN = isEvilInterface ? ICN::NON_UNIFORM_EVIL_RESTART_BUTTON : ICN::NON_UNIFORM_GOOD_RESTART_BUTTON;

fheroes2::Button btn_ok( pos_rt.x + buttonOffset, pos_rt.y + 410, buttonOkICN, 0, 1 );
fheroes2::Button btnCancel( pos_rt.x + buttonOffset + 129, pos_rt.y + 410, buttonCancelICN, 0, 1 );

int32_t messageYOffset = 0;
if ( !title.empty() ) {
TextBox box( title, Font::YELLOW_BIG, bsTextWidth );
Expand Down Expand Up @@ -495,22 +487,37 @@ bool Battle::Arena::DialogBattleSummary( const Result & res, const std::vector<A
}

if ( allowToCancel ) {
fheroes2::Sprite buttonOverride = fheroes2::Crop( dialog, 20, 410, 84, 32 );
const fheroes2::Sprite & buttonOverride = fheroes2::Crop( dialog, 20, 410, 84, 32 );
fheroes2::Blit( buttonOverride, display, pos_rt.x + 116, pos_rt.y + 410 );
}

const int buttonOffset = allowToCancel ? 39 : 120;
const int buttonOkICN
= isEvilInterface ? ( allowToCancel ? ICN::NON_UNIFORM_EVIL_OKAY_BUTTON : ICN::WINCMBBE ) : ( allowToCancel ? ICN::NON_UNIFORM_GOOD_OKAY_BUTTON : ICN::WINCMBTB );
const int buttonCancelICN = isEvilInterface ? ICN::NON_UNIFORM_EVIL_RESTART_BUTTON : ICN::NON_UNIFORM_GOOD_RESTART_BUTTON;

std::unique_ptr<fheroes2::ButtonBase> btnOk;
fheroes2::AutoShadowButton btnCancel( display, pos_rt.x + buttonOffset + 129, pos_rt.y + 410, buttonCancelICN, 0, 1 );

if ( allowToCancel ) {
btnCancel.draw();
btnOk.reset( new fheroes2::AutoShadowButton( display, pos_rt.x + buttonOffset, pos_rt.y + 410, buttonOkICN, 0, 1 ) );
}
btn_ok.draw();
else {
btnOk.reset( new fheroes2::Button( pos_rt.x + buttonOffset, pos_rt.y + 410, buttonOkICN, 0, 1 ) );
}
btnOk->draw();

display.render();

while ( le.HandleEvents() ) {
le.MousePressLeft( btn_ok.area() ) ? btn_ok.drawOnPress() : btn_ok.drawOnRelease();
le.MousePressLeft( btnOk->area() ) ? btnOk->drawOnPress() : btnOk->drawOnRelease();
if ( allowToCancel ) {
le.MousePressLeft( btnCancel.area() ) ? btnCancel.drawOnPress() : btnCancel.drawOnRelease();
}

// exit
if ( HotKeyCloseWindow || le.MouseClickLeft( btn_ok.area() ) )
if ( HotKeyCloseWindow || le.MouseClickLeft( btnOk->area() ) )
break;

if ( allowToCancel && le.MouseClickLeft( btnCancel.area() ) ) {
Expand Down Expand Up @@ -539,16 +546,16 @@ bool Battle::Arena::DialogBattleSummary( const Result & res, const std::vector<A

const bool isWinnerHuman = winner && winner->isControlHuman();

btn_ok.setICNInfo( isEvilInterface ? ICN::WINCMBBE : ICN::WINCMBTB, 0, 1 );
btn_ok.setPosition( pos_rt.x + 120, pos_rt.y + 410 );
btnOk.reset( new fheroes2::Button( pos_rt.x + 120, pos_rt.y + 410, isEvilInterface ? ICN::WINCMBBE : ICN::WINCMBTB, 0, 1 ) );

for ( const Artifact & art : artifacts ) {
if ( isWinnerHuman || art.isUltimate() ) { // always show the message for ultimate artifacts
back.restore();
back.update( shadowOffset.x, shadowOffset.y, dialog.width() + BORDERWIDTH, dialog.height() + BORDERWIDTH - 1 );
fheroes2::Blit( dialogShadow, display, pos_rt.x - BORDERWIDTH, pos_rt.y + BORDERWIDTH - 1 );
fheroes2::Blit( dialog, display, pos_rt.x, pos_rt.y );
btn_ok.draw();

btnOk->draw();

std::string artMsg;
if ( art.isUltimate() ) {
Expand Down Expand Up @@ -581,14 +588,14 @@ bool Battle::Arena::DialogBattleSummary( const Result & res, const std::vector<A
const fheroes2::Rect artifactArea( artifactOffset.x, artifactOffset.y, border.width(), border.height() );

while ( le.HandleEvents() ) {
le.MousePressLeft( btn_ok.area() ) ? btn_ok.drawOnPress() : btn_ok.drawOnRelease();
le.MousePressLeft( btnOk->area() ) ? btnOk->drawOnPress() : btnOk->drawOnRelease();

// display captured artifact info on right click
if ( le.MousePressRight( artifactArea ) )
Dialog::ArtifactInfo( art.GetName(), "", art, 0 );

// exit
if ( HotKeyCloseWindow || le.MouseClickLeft( btn_ok.area() ) )
if ( HotKeyCloseWindow || le.MouseClickLeft( btnOk->area() ) )
break;

// animation
Expand Down Expand Up @@ -902,25 +909,10 @@ bool Battle::DialogBattleSurrender( const HeroBase & hero, u32 cost, Kingdom & k

const int icn = isEvilInterface ? ICN::SURRENDE : ICN::SURRENDR;

fheroes2::Button btnAccept( pos_rt.x + 91, pos_rt.y + 152, icn, 0, 1 );
fheroes2::Button btnDecline( pos_rt.x + 295, pos_rt.y + 152, icn, 2, 3 );

fheroes2::Sprite marketButtonReleased = fheroes2::AGG::GetICN( isEvilInterface ? ICN::ADVEBTNS : ICN::ADVBTNS, 4 );
fheroes2::Sprite marketButtonPressed = fheroes2::AGG::GetICN( isEvilInterface ? ICN::ADVEBTNS : ICN::ADVBTNS, 5 );
fheroes2::AddTransparency( marketButtonReleased, 36 );
fheroes2::AddTransparency( marketButtonPressed, 36 );

const fheroes2::Point buttonMarketPos( pos_rt.x + ( pos_rt.width - 16 ) / 2, pos_rt.y + 145 );

fheroes2::Sprite marketButtonReleasedBack( marketButtonReleased.width(), marketButtonReleased.height(), marketButtonReleased.x(), marketButtonReleased.y() );
fheroes2::Copy( display, buttonMarketPos.x, buttonMarketPos.y, marketButtonReleasedBack, 0, 0, marketButtonReleasedBack.width(), marketButtonReleasedBack.height() );
fheroes2::Blit( marketButtonReleased, marketButtonReleasedBack );

fheroes2::Sprite marketButtonPressedBack( marketButtonPressed.width(), marketButtonPressed.height(), marketButtonPressed.x(), marketButtonPressed.y() );
fheroes2::Copy( display, buttonMarketPos.x, buttonMarketPos.y, marketButtonPressedBack, 0, 0, marketButtonPressedBack.width(), marketButtonPressedBack.height() );
fheroes2::Blit( marketButtonPressed, marketButtonPressedBack );

fheroes2::ButtonSprite btnMarket( buttonMarketPos.x, buttonMarketPos.y, marketButtonReleasedBack, marketButtonPressedBack );
fheroes2::AutoShadowButton btnAccept( display, pos_rt.x + 91, pos_rt.y + 152, icn, 0, 1 );
fheroes2::AutoShadowButton btnDecline( display, pos_rt.x + 295, pos_rt.y + 152, icn, 2, 3 );
fheroes2::AutoShadowButton btnMarket( display, pos_rt.x + ( pos_rt.width - 16 ) / 2, pos_rt.y + 145,
isEvilInterface ? ICN::EVIL_MARKET_BUTTON : ICN::GOOD_MARKET_BUTTON, 0, 1 );

if ( !kingdom.AllowPayment( payment_t( Resource::GOLD, cost ) ) ) {
btnAccept.disable();
Expand Down
Loading