1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Refactoring of button class to prepare for configurable button

This commit is contained in:
Ivan Savenko 2024-02-27 22:19:09 +02:00
parent 850af00303
commit 07d201502e
22 changed files with 223 additions and 207 deletions

View File

@ -102,7 +102,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyChatMessage(LobbyChatMessage &
lobby->card->chat->addNewMessage(pack.playerName + ": " + pack.message);
lobby->card->setChat(true);
if(lobby->buttonChat)
lobby->buttonChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL, Colors::WHITE);
lobby->buttonChat->setTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL, Colors::WHITE);
}
}

View File

@ -35,7 +35,6 @@ class BattleInterface;
class CPicture;
class CFilledTexture;
class CButton;
class CToggleButton;
class CLabel;
class CMultiLineLabel;
class CTextBox;

View File

@ -563,9 +563,8 @@ void BattleWindow::showAlternativeActionIcon(PossiblePlayerBattleAction action)
iconName = AnimationPath::fromJson(variables["actionIconNoReturn"]);
break;
}
auto anim = GH.renderHandler().loadAnimation(iconName);
w->setImage(anim);
w->setImage(iconName);
w->redraw();
}

View File

@ -47,8 +47,8 @@ GlobalLobbyLoginWindow::GlobalLobbyLoginWindow()
auto buttonRegister = std::make_shared<CToggleButton>(Point(10, 40), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
auto buttonLogin = std::make_shared<CToggleButton>(Point(146, 40), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
buttonRegister->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.login.create"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonLogin->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.login.login"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonRegister->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.login.create"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonLogin->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.login.login"), EFonts::FONT_SMALL, Colors::YELLOW);
toggleMode = std::make_shared<CToggleGroup>(nullptr);
toggleMode->addToggle(0, buttonRegister);

View File

@ -50,8 +50,8 @@ GlobalLobbyServerSetup::GlobalLobbyServerSetup()
auto buttonPublic = std::make_shared<CToggleButton>(Point(10, 120), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
auto buttonPrivate = std::make_shared<CToggleButton>(Point(146, 120), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
buttonPublic->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.public"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonPrivate->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.private"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonPublic->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.public"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonPrivate->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.private"), EFonts::FONT_SMALL, Colors::YELLOW);
toggleRoomType = std::make_shared<CToggleGroup>(nullptr);
toggleRoomType->addToggle(0, buttonPublic);
@ -61,8 +61,8 @@ GlobalLobbyServerSetup::GlobalLobbyServerSetup()
auto buttonNewGame = std::make_shared<CToggleButton>(Point(10, 170), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
auto buttonLoadGame = std::make_shared<CToggleButton>(Point(146, 170), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
buttonNewGame->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.new"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonLoadGame->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.load"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonNewGame->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.new"), EFonts::FONT_SMALL, Colors::YELLOW);
buttonLoadGame->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.load"), EFonts::FONT_SMALL, Colors::YELLOW);
toggleGameMode = std::make_shared<CToggleGroup>(nullptr);
toggleGameMode->addToggle(0, buttonNewGame);

View File

@ -396,7 +396,7 @@ std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(co
{
for(const auto & item : config["items"].Vector())
{
button->addOverlay(buildWidget(item));
button->setOverlay(buildWidget(item));
}
}
if(!config["selected"].isNull())
@ -422,7 +422,7 @@ std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode
{
for(const auto & item : config["items"].Vector())
{
button->addOverlay(buildWidget(item));
button->setOverlay(buildWidget(item));
}
}
if(!config["imageOrder"].isNull())
@ -589,7 +589,7 @@ std::shared_ptr<ComboBox> InterfaceObjectConfigurable::buildComboBox(const JsonN
{
for(const auto & item : config["items"].Vector())
{
result->addOverlay(buildWidget(item));
result->setOverlay(buildWidget(item));
}
}
if(!config["imageOrder"].isNull())

View File

@ -126,7 +126,7 @@ CBonusSelection::CBonusSelection()
tabExtraOptions->recreate(true);
tabExtraOptions->setEnabled(false);
buttonExtraOptions = std::make_shared<CButton>(Point(643, 431), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], [this]{ tabExtraOptions->setEnabled(!tabExtraOptions->isActive()); GH.windows().totalRedraw(); }, EShortcut::NONE);
buttonExtraOptions->addTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.extraOptions.hover"), FONT_SMALL, Colors::WHITE);
buttonExtraOptions->setTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.extraOptions.hover"), FONT_SMALL, Colors::WHITE);
}
}
@ -306,7 +306,7 @@ void CBonusSelection::createBonusesIcons()
auto anim = GH.renderHandler().createAnimation();
anim->setCustom(picName, 0);
bonusButton->setImage(anim);
//TODO: bonusButton->setImage(anim);
if(CSH->campaignBonus == i)
bonusButton->setBorderColor(Colors::BRIGHT_YELLOW);
groupBonuses->addToggle(i, bonusButton);

View File

@ -61,7 +61,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
};
buttonChat = std::make_shared<CButton>(Point(619, 80), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_HIDE_CHAT);
buttonChat->addTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
buttonChat->setTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
switch(screenType)
{
@ -171,18 +171,18 @@ void CLobbyScreen::toggleMode(bool host)
return;
auto buttonColor = host ? Colors::WHITE : Colors::ORANGE;
buttonSelect->addTextOverlay(CGI->generaltexth->allTexts[500], FONT_SMALL, buttonColor);
buttonOptions->addTextOverlay(CGI->generaltexth->allTexts[501], FONT_SMALL, buttonColor);
buttonSelect->setTextOverlay(CGI->generaltexth->allTexts[500], FONT_SMALL, buttonColor);
buttonOptions->setTextOverlay(CGI->generaltexth->allTexts[501], FONT_SMALL, buttonColor);
if (buttonTurnOptions)
buttonTurnOptions->addTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.turnOptions.hover"), FONT_SMALL, buttonColor);
buttonTurnOptions->setTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.turnOptions.hover"), FONT_SMALL, buttonColor);
if (buttonExtraOptions)
buttonExtraOptions->addTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.extraOptions.hover"), FONT_SMALL, buttonColor);
buttonExtraOptions->setTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.extraOptions.hover"), FONT_SMALL, buttonColor);
if(buttonRMG)
{
buttonRMG->addTextOverlay(CGI->generaltexth->allTexts[740], FONT_SMALL, buttonColor);
buttonRMG->setTextOverlay(CGI->generaltexth->allTexts[740], FONT_SMALL, buttonColor);
buttonRMG->block(!host);
}
buttonSelect->block(!host);
@ -206,9 +206,9 @@ void CLobbyScreen::toggleChat()
{
card->toggleChat();
if(card->showChat)
buttonChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL, Colors::WHITE);
buttonChat->setTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL, Colors::WHITE);
else
buttonChat->addTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
buttonChat->setTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
}
void CLobbyScreen::updateAfterStateChange()

View File

@ -917,7 +917,7 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
CGI->generaltexth->zelp[180],
std::bind(&OptionsTab::onSetPlayerClicked, &parentTab, *s)
);
flag->hoverable = true;
flag->setHoverable(true);
flag->block(CSH->isGuest());
}
else

View File

@ -364,9 +364,9 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
if(auto w = widget<CButton>("templateButton"))
{
if(tmpl)
w->addTextOverlay(tmpl->getName(), EFonts::FONT_SMALL, Colors::WHITE);
w->setTextOverlay(tmpl->getName(), EFonts::FONT_SMALL, Colors::WHITE);
else
w->addTextOverlay(readText(variables["randomTemplate"]), EFonts::FONT_SMALL, Colors::WHITE);
w->setTextOverlay(readText(variables["randomTemplate"]), EFonts::FONT_SMALL, Colors::WHITE);
}
for(auto r : VLC->roadTypeHandler->objects)
{
@ -388,9 +388,9 @@ void RandomMapTab::setTemplate(const CRmgTemplate * tmpl)
if(auto w = widget<CButton>("templateButton"))
{
if(tmpl)
w->addTextOverlay(tmpl->getName(), EFonts::FONT_SMALL, Colors::WHITE);
w->setTextOverlay(tmpl->getName(), EFonts::FONT_SMALL, Colors::WHITE);
else
w->addTextOverlay(readText(variables["randomTemplate"]), EFonts::FONT_SMALL, Colors::WHITE);
w->setTextOverlay(readText(variables["randomTemplate"]), EFonts::FONT_SMALL, Colors::WHITE);
}
updateMapInfoByHost();
}
@ -532,11 +532,11 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab):
assert(button);
if(sel == teamId)
{
button->addOverlay(buildWidget(variables["flagsAnimation"]));
button->setOverlay(buildWidget(variables["flagsAnimation"]));
}
else
{
button->addOverlay(nullptr);
button->setOverlay(nullptr);
}
button->addCallback([this](bool)
{

View File

@ -217,7 +217,7 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
if(enableUiEnhancements)
{
buttonsSortBy.push_back(std::make_shared<CButton>(Point(371, 85), AnimationPath::builtin("lobby/selectionTabSortDate"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.sortDate")), std::bind(&SelectionTab::sortBy, this, ESortBy::_changeDate)));
buttonsSortBy.back()->setAnimateLonelyFrame(true);
//TODO: buttonsSortBy.back()->setAnimateLonelyFrame(true);
}
iconsMapFormats = GH.renderHandler().loadAnimation(AnimationPath::builtin("SCSELC.DEF"));

View File

@ -66,7 +66,7 @@ CCampaignScreen::CCampaignScreen(const JsonNode & config, std::string name)
if(!config[name]["exitbutton"].isNull())
{
buttonBack = createExitButton(config[name]["exitbutton"]);
buttonBack->hoverable = true;
buttonBack->setHoverable(true);
}
for(const JsonNode & node : config[name]["items"].Vector())

View File

@ -260,7 +260,7 @@ CMenuEntry::CMenuEntry(CMenuScreen * parent, const JsonNode & config)
for(const JsonNode & node : config["buttons"].Vector())
{
buttons.push_back(createButton(parent, node));
buttons.back()->hoverable = true;
buttons.back()->setHoverable(true);
buttons.back()->setRedrawParent(true);
}
}

View File

@ -30,30 +30,25 @@
#include "../../lib/CConfigHandler.h"
#include "../../lib/CGeneralTextHandler.h"
void CButton::update()
void ButtonBase::update()
{
if (overlay)
{
Point targetPos = Rect::createCentered( pos, overlay->pos.dimensions()).topLeft();
if (state == PRESSED)
if (state == EButtonState::PRESSED)
overlay->moveTo(targetPos + Point(1,1));
else
overlay->moveTo(targetPos);
}
int newPos = stateToIndex[int(state)];
if(animateLonelyFrame)
{
if(state == PRESSED)
image->moveBy(Point(1,1));
else
image->moveBy(Point(-1,-1));
}
if (newPos < 0)
newPos = 0;
if (state == HIGHLIGHTED && image->size() < 4)
// checkbox - has only have two frames: normal and pressed/highlighted
// hero movement speed buttons: only three frames: normal, pressed and blocked/highlighted
if (state == EButtonState::HIGHLIGHTED && image->size() < 4)
newPos = (int)image->size()-1;
image->setFrame(newPos);
@ -71,14 +66,14 @@ void CButton::addCallback(const std::function<void()> & callback)
this->callback += callback;
}
void CButton::addTextOverlay(const std::string & Text, EFonts font, ColorRGBA color)
void ButtonBase::setTextOverlay(const std::string & Text, EFonts font, ColorRGBA color)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
addOverlay(std::make_shared<CLabel>(pos.w/2, pos.h/2, font, ETextAlignment::CENTER, color, Text));
setOverlay(std::make_shared<CLabel>(pos.w/2, pos.h/2, font, ETextAlignment::CENTER, color, Text));
update();
}
void CButton::addOverlay(std::shared_ptr<CIntObject> newOverlay)
void ButtonBase::setOverlay(std::shared_ptr<CIntObject> newOverlay)
{
overlay = newOverlay;
if(overlay)
@ -90,17 +85,23 @@ void CButton::addOverlay(std::shared_ptr<CIntObject> newOverlay)
update();
}
void CButton::addImage(const AnimationPath & filename)
void ButtonBase::setImage(const AnimationPath & defName, bool playerColoredButton)
{
imageNames.push_back(filename);
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
image = std::make_shared<CAnimImage>(defName, vstd::to_underlying(getState()));
pos = image->pos;
if (playerColoredButton)
image->playerColored(LOCPLINT->playerID);
}
void CButton::addHoverText(ButtonState state, std::string text)
void CButton::addHoverText(EButtonState state, std::string text)
{
hoverTexts[state] = text;
hoverTexts[vstd::to_underlying(state)] = text;
}
void CButton::setImageOrder(int state1, int state2, int state3, int state4)
void ButtonBase::setImageOrder(int state1, int state2, int state3, int state4)
{
stateToIndex[0] = state1;
stateToIndex[1] = state2;
@ -109,44 +110,65 @@ void CButton::setImageOrder(int state1, int state2, int state3, int state4)
update();
}
void CButton::setAnimateLonelyFrame(bool agreement)
//TODO:
//void CButton::setAnimateLonelyFrame(bool agreement)
//{
// animateLonelyFrame = agreement;
//}
void ButtonBase::setStateImpl(EButtonState newState)
{
animateLonelyFrame = agreement;
}
void CButton::setState(ButtonState newState)
{
if (state == newState)
return;
if (newState == BLOCKED)
removeUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
else
addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
state = newState;
update();
}
CButton::ButtonState CButton::getState()
void CButton::setState(EButtonState newState)
{
if (getState() == newState)
return;
if (newState == EButtonState::BLOCKED)
removeUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
else
addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
setStateImpl(newState);
}
EButtonState ButtonBase::getState()
{
return state;
}
bool CButton::isBlocked()
{
return state == BLOCKED;
return getState() == EButtonState::BLOCKED;
}
bool CButton::isHighlighted()
{
return state == HIGHLIGHTED;
return getState() == EButtonState::HIGHLIGHTED;
}
void CButton::setHoverable(bool on)
{
hoverable = on;
}
void CButton::setSoundDisabled(bool on)
{
soundDisabled = on;
}
void CButton::setActOnDown(bool on)
{
actOnDown = on;
}
void CButton::block(bool on)
{
if(on || state == BLOCKED) //dont change button state if unblock requested, but it's not blocked
setState(on ? BLOCKED : NORMAL);
if(on || getState() == EButtonState::BLOCKED) //dont change button state if unblock requested, but it's not blocked
setState(on ? EButtonState::BLOCKED : EButtonState::NORMAL);
}
void CButton::onButtonClicked()
@ -169,14 +191,14 @@ void CButton::clickPressed(const Point & cursorPosition)
if(isBlocked())
return;
if (getState() != PRESSED)
if (getState() != EButtonState::PRESSED)
{
if (!soundDisabled)
{
CCS->soundh->playSound(soundBase::button);
GH.input().hapticFeedback();
}
setState(PRESSED);
setState(EButtonState::PRESSED);
if (actOnDown)
onButtonClicked();
@ -185,12 +207,12 @@ void CButton::clickPressed(const Point & cursorPosition)
void CButton::clickReleased(const Point & cursorPosition)
{
if (getState() == PRESSED)
if (getState() == EButtonState::PRESSED)
{
if(hoverable && isHovered())
setState(HIGHLIGHTED);
setState(EButtonState::HIGHLIGHTED);
else
setState(NORMAL);
setState(EButtonState::NORMAL);
if (!actOnDown)
onButtonClicked();
@ -199,12 +221,12 @@ void CButton::clickReleased(const Point & cursorPosition)
void CButton::clickCancel(const Point & cursorPosition)
{
if (getState() == PRESSED)
if (getState() == EButtonState::PRESSED)
{
if(hoverable && isHovered())
setState(HIGHLIGHTED);
setState(EButtonState::HIGHLIGHTED);
else
setState(NORMAL);
setState(EButtonState::NORMAL);
}
}
@ -219,17 +241,17 @@ void CButton::hover (bool on)
if(hoverable && !isBlocked())
{
if(on)
setState(HIGHLIGHTED);
setState(EButtonState::HIGHLIGHTED);
else
setState(NORMAL);
setState(EButtonState::NORMAL);
}
/*if(pressedL && on) // WTF is this? When this is used?
setState(PRESSED);*/
std::string name = hoverTexts[getState()].empty()
std::string name = hoverTexts[vstd::to_underlying(getState())].empty()
? hoverTexts[0]
: hoverTexts[getState()];
: hoverTexts[vstd::to_underlying(getState())];
if(!name.empty() && !isBlocked()) //if there is no name, there is nothing to display also
{
@ -240,55 +262,30 @@ void CButton::hover (bool on)
}
}
ButtonBase::ButtonBase(Point position, const AnimationPath & defName, EShortcut key, bool playerColoredButton)
: CKeyShortcut(key)
, stateToIndex({0, 1, 2, 3})
, state(EButtonState::NORMAL)
{
pos.x += position.x;
pos.y += position.y;
setImage(defName);
}
CButton::CButton(Point position, const AnimationPath &defName, const std::pair<std::string, std::string> &help, CFunctionList<void()> Callback, EShortcut key, bool playerColoredButton):
CKeyShortcut(key),
ButtonBase(position, defName, key, playerColoredButton),
callback(Callback)
{
defActions = 255-DISPOSE;
addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
stateToIndex[0] = 0;
stateToIndex[1] = 1;
stateToIndex[2] = 2;
stateToIndex[3] = 3;
state=NORMAL;
currentImage = -1;
hoverable = actOnDown = soundDisabled = false;
hoverTexts[0] = help.first;
helpBox=help.second;
pos.x += position.x;
pos.y += position.y;
if (!defName.empty())
{
imageNames.push_back(defName);
setIndex(0);
if (playerColoredButton)
image->playerColored(LOCPLINT->playerID);
}
}
void CButton::setIndex(size_t index)
{
if (index == currentImage || index>=imageNames.size())
return;
currentImage = index;
auto anim = GH.renderHandler().loadAnimation(imageNames[index]);
setImage(anim);
}
void CButton::setImage(std::shared_ptr<CAnimation> anim, int animFlags)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
image = std::make_shared<CAnimImage>(anim, getState(), 0, 0, 0, animFlags);
pos = image->pos;
}
void CButton::setPlayerColor(PlayerColor player)
void ButtonBase::setPlayerColor(PlayerColor player)
{
if (image && image->isPlayerColored())
image->playerColored(player);
@ -353,6 +350,11 @@ void CToggleBase::setSelected(bool on)
callback(on);
}
bool CToggleBase::isSelected() const
{
return selected;
}
bool CToggleBase::canActivate()
{
if (selected && !allowDeselection)
@ -365,29 +367,33 @@ void CToggleBase::addCallback(std::function<void(bool)> function)
callback += function;
}
void CToggleBase::setAllowDeselection(bool on)
{
allowDeselection = on;
}
CToggleButton::CToggleButton(Point position, const AnimationPath &defName, const std::pair<std::string, std::string> &help,
CFunctionList<void(bool)> callback, EShortcut key, bool playerColoredButton):
CButton(position, defName, help, 0, key, playerColoredButton),
CToggleBase(callback)
{
allowDeselection = true;
}
void CToggleButton::doSelect(bool on)
{
if (on)
{
setState(HIGHLIGHTED);
setState(EButtonState::HIGHLIGHTED);
}
else
{
setState(NORMAL);
setState(EButtonState::NORMAL);
}
}
void CToggleButton::setEnabled(bool enabled)
{
setState(enabled ? NORMAL : BLOCKED);
setState(enabled ? EButtonState::NORMAL : EButtonState::BLOCKED);
}
void CToggleButton::clickPressed(const Point & cursorPosition)
@ -403,7 +409,7 @@ void CToggleButton::clickPressed(const Point & cursorPosition)
{
CCS->soundh->playSound(soundBase::button);
GH.input().hapticFeedback();
setState(PRESSED);
setState(EButtonState::PRESSED);
}
}
@ -416,13 +422,13 @@ void CToggleButton::clickReleased(const Point & cursorPosition)
if(isBlocked())
return;
if (getState() == PRESSED && canActivate())
if (getState() == EButtonState::PRESSED && canActivate())
{
onButtonClicked();
setSelected(!selected);
setSelected(!isSelected());
}
else
doSelect(selected); // restore
doSelect(isSelected()); // restore
}
void CToggleButton::clickCancel(const Point & cursorPosition)
@ -434,7 +440,7 @@ void CToggleButton::clickCancel(const Point & cursorPosition)
if(isBlocked())
return;
doSelect(selected);
doSelect(isSelected());
}
void CToggleGroup::addCallback(std::function<void(int)> callback)
@ -455,7 +461,7 @@ void CToggleGroup::addToggle(int identifier, std::shared_ptr<CToggleBase> button
}
button->addCallback([=] (bool on) { if (on) selectionChanged(identifier);});
button->allowDeselection = false;
button->setAllowDeselection(false);
if(buttons.count(identifier)>0)
logAnim->error("Duplicated toggle button id %d", identifier);

View File

@ -22,64 +22,77 @@ class CAnimImage;
class CLabel;
class CAnimation;
enum class EButtonState
{
NORMAL=0,
PRESSED=1,
BLOCKED=2,
HIGHLIGHTED=3 // used for: highlighted state for selectable buttons, hovered state for hoverable buttons (e.g. main menu)
};
class ButtonBase : public CKeyShortcut
{
std::shared_ptr<CAnimImage> image; //image for this button
std::shared_ptr<CIntObject> overlay;//object-overlay, can be null
std::array<int, 4> stateToIndex; // mapping of button state to index of frame in animation
EButtonState state;//current state of button from enum
void update();//to refresh button after image or text change
protected:
ButtonBase(Point position, const AnimationPath & defName, EShortcut key, bool playerColoredButton);
void setStateImpl(EButtonState state);
EButtonState getState();
public:
/// Appearance modifiers
void setPlayerColor(PlayerColor player);
void setImage(const AnimationPath & defName, bool playerColoredButton = false);
void setImageOrder(int state1, int state2, int state3, int state4);
/// adds overlay on top of button image. Only one overlay can be active at once
void setOverlay(std::shared_ptr<CIntObject> newOverlay);
void setTextOverlay(const std::string & Text, EFonts font, ColorRGBA color);
};
/// Typical Heroes 3 button which can be inactive or active and can
/// hold further information if you right-click it
class CButton : public CKeyShortcut
class CButton : public ButtonBase
{
CFunctionList<void()> callback;
public:
enum ButtonState
{
NORMAL=0,
PRESSED=1,
BLOCKED=2,
HIGHLIGHTED=3
};
protected:
std::vector<AnimationPath> imageNames;//store list of images that can be used by this button
size_t currentImage;
ButtonState state;//current state of button from enum
std::array<int, 4> stateToIndex; // mapping of button state to index of frame in animation
std::array<std::string, 4> hoverTexts; //texts for statusbar, if empty - first entry will be used
std::optional<ColorRGBA> borderColor; // mapping of button state to border color
std::string helpBox; //for right-click help
std::shared_ptr<CAnimImage> image; //image for this button
std::shared_ptr<CIntObject> overlay;//object-overlay, can be null
bool animateLonelyFrame = false;
bool actOnDown; //runs when mouse is pressed down over it, not when up
bool hoverable; //if true, button will be highlighted when hovered (e.g. main menu)
bool soundDisabled;
protected:
void onButtonClicked(); // calls callback
void update();//to refresh button after image or text change
// internal method to change state. Public change can be done only via block()
void setState(ButtonState newState);
ButtonState getState();
void setState(EButtonState newState);
public:
bool actOnDown,//runs when mouse is pressed down over it, not when up
hoverable,//if true, button will be highlighted when hovered (e.g. main menu)
soundDisabled;
// sets the same border color for all button states.
void setBorderColor(std::optional<ColorRGBA> borderColor);
/// adds one more callback to on-click actions
void addCallback(const std::function<void()> & callback);
/// adds overlay on top of button image. Only one overlay can be active at once
void addOverlay(std::shared_ptr<CIntObject> newOverlay);
void addTextOverlay(const std::string & Text, EFonts font, ColorRGBA color);
void addHoverText(EButtonState state, std::string text);
void addImage(const AnimationPath & filename);
void addHoverText(ButtonState state, std::string text);
void setImageOrder(int state1, int state2, int state3, int state4);
void setAnimateLonelyFrame(bool agreement);
void block(bool on);
void setHoverable(bool on);
void setSoundDisabled(bool on);
void setActOnDown(bool on);
/// State modifiers
bool isBlocked();
bool isHighlighted();
@ -88,11 +101,6 @@ public:
CButton(Point position, const AnimationPath & defName, const std::pair<std::string, std::string> & help,
CFunctionList<void()> Callback = 0, EShortcut key = {}, bool playerColoredButton = false );
/// Appearance modifiers
void setIndex(size_t index);
void setImage(std::shared_ptr<CAnimation> anim, int animFlags=0);
void setPlayerColor(PlayerColor player);
/// CIntObject overrides
void showPopupWindow(const Point & cursorPosition) override;
void clickPressed(const Point & cursorPosition) override;
@ -110,10 +118,13 @@ public:
class CToggleBase
{
CFunctionList<void(bool)> callback;
protected:
bool selected;
/// if set to false - button can not be deselected normally
bool allowDeselection;
protected:
// internal method for overrides
virtual void doSelect(bool on);
@ -121,9 +132,6 @@ protected:
bool canActivate();
public:
/// if set to false - button can not be deselected normally
bool allowDeselection;
CToggleBase(CFunctionList<void(bool)> callback);
virtual ~CToggleBase();
@ -133,6 +141,10 @@ public:
/// Changes selection to "on" without calling callback
void setSelectedSilent(bool on);
bool isSelected() const;
void setAllowDeselection(bool on);
void addCallback(std::function<void(bool)> callback);
/// Set whether the toggle is currently enabled for user to use, this is only inplemented in ToggleButton, not for other toggles yet.

View File

@ -168,10 +168,11 @@ ComboBox::ComboBox(Point position, const AnimationPath & defName, const std::pai
void ComboBox::setItem(const void * item)
{
auto w = std::dynamic_pointer_cast<CLabel>(overlay);
// TODO:
//auto w = std::dynamic_pointer_cast<CLabel>(overlay);
if( w && getItemText)
addTextOverlay(getItemText(0, item), w->font, w->color);
//if( w && getItemText)
// setTextOverlay(getItemText(0, item), w->font, w->color);
if(onSetItem)
onSetItem(item);

View File

@ -206,10 +206,10 @@ CSlider::CSlider(Point position, int totalw, const std::function<void(int)> & Mo
right = std::make_shared<CButton>(Point(), AnimationPath::builtin(getOrientation() == Orientation::HORIZONTAL ? "SCNRBRT.DEF" : "SCNRBDN.DEF"), CButton::tooltip());
slider = std::make_shared<CButton>(Point(), AnimationPath::builtin("SCNRBSL.DEF"), CButton::tooltip());
}
slider->actOnDown = true;
slider->soundDisabled = true;
left->soundDisabled = true;
right->soundDisabled = true;
slider->setActOnDown(true);
slider->setSoundDisabled(true);
left->setSoundDisabled(true);
right->setSoundDisabled(true);
if (getOrientation() == Orientation::HORIZONTAL)
right->moveBy(Point(totalw - right->pos.w, 0));

View File

@ -1334,12 +1334,12 @@ void CCastleInterface::recreateIcons()
fastTownHall = std::make_shared<CButton>(Point(80, 413), AnimationPath::builtin("ITMTL.def"), CButton::tooltip(), [&](){ builds->enterTownHall(); });
fastTownHall->setImageOrder(town->hallLevel(), town->hallLevel(), town->hallLevel(), town->hallLevel());
fastTownHall->setAnimateLonelyFrame(true);
//TODO: fastTownHall->setAnimateLonelyFrame(true);
int imageIndex = town->fortLevel() == CGTownInstance::EFortLevel::NONE ? 3 : town->fortLevel() - 1;
fastArmyPurchase = std::make_shared<CButton>(Point(122, 413), AnimationPath::builtin("itmcl.def"), CButton::tooltip(), [&](){ builds->enterToTheQuickRecruitmentWindow(); });
fastArmyPurchase->setImageOrder(imageIndex, imageIndex, imageIndex, imageIndex);
fastArmyPurchase->setAnimateLonelyFrame(true);
//TODO: fastArmyPurchase->setAnimateLonelyFrame(true);
fastMarket = std::make_shared<LRClickableArea>(Rect(163, 410, 64, 42), [&]()
{

View File

@ -340,7 +340,7 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset)
};
auto upgradeBtn = std::make_shared<CButton>(Point(221 + (int)buttonIndex * 40, 5), AnimationPath::builtin("stackWindow/upgradeButton"), CGI->generaltexth->zelp[446], onClick);
upgradeBtn->addOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("CPRSMALL"), VLC->creh->objects[upgradeInfo.info.newID[buttonIndex]]->getIconIndex()));
upgradeBtn->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("CPRSMALL"), VLC->creh->objects[upgradeInfo.info.newID[buttonIndex]]->getIconIndex()));
if(buttonsToCreate == 1) // single upgrade avaialbe
upgradeBtn->assignedKey = EShortcut::RECRUITMENT_UPGRADE;
@ -366,7 +366,7 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset)
std::string tooltipText = "vcmi.creatureWindow." + btnIDs[buttonIndex];
parent->switchButtons[buttonIndex] = std::make_shared<CButton>(Point(302 + (int)buttonIndex*40, 5), AnimationPath::builtin("stackWindow/upgradeButton"), CButton::tooltipLocalized(tooltipText), onSwitch);
parent->switchButtons[buttonIndex]->addOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("stackWindow/switchModeIcons"), buttonIndex));
parent->switchButtons[buttonIndex]->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("stackWindow/switchModeIcons"), buttonIndex));
}
parent->switchButtons[parent->activeTab]->disable();
}

View File

@ -89,7 +89,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
{
questlogButton = std::make_shared<CButton>(Point(314, 429), AnimationPath::builtin("hsbtns4.def"), CButton::tooltip(heroscrn[0]), [=](){ LOCPLINT->showQuestLog(); }, EShortcut::ADVENTURE_QUEST_LOG);
backpackButton = std::make_shared<CButton>(Point(424, 429), AnimationPath::builtin("buttons/backpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"), [=](){ createBackpackWindow(); }, EShortcut::HERO_BACKPACK);
backpackButton->addOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
backpackButton->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
dismissButton = std::make_shared<CButton>(Point(534, 429), AnimationPath::builtin("hsbtns2.def"), CButton::tooltip(heroscrn[28]), [=](){ dismissCurrent(); }, EShortcut::HERO_DISMISS);
}
else
@ -197,9 +197,9 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
specName->setText(curHero->type->getSpecialtyNameTranslated());
tacticsButton = std::make_shared<CToggleButton>(Point(539, 483), AnimationPath::builtin("hsbtns8.def"), std::make_pair(heroscrn[26], heroscrn[31]), 0, EShortcut::HERO_TOGGLE_TACTICS);
tacticsButton->addHoverText(CButton::HIGHLIGHTED, CGI->generaltexth->heroscrn[25]);
tacticsButton->addHoverText(EButtonState::HIGHLIGHTED, CGI->generaltexth->heroscrn[25]);
dismissButton->addHoverText(CButton::NORMAL, boost::str(boost::format(CGI->generaltexth->heroscrn[16]) % curHero->getNameTranslated() % curHero->getClassNameTranslated()));
dismissButton->addHoverText(EButtonState::NORMAL, boost::str(boost::format(CGI->generaltexth->heroscrn[16]) % curHero->getNameTranslated() % curHero->getClassNameTranslated()));
portraitArea->hoverText = boost::str(boost::format(CGI->generaltexth->allTexts[15]) % curHero->getNameTranslated() % curHero->getClassNameTranslated());
portraitArea->text = curHero->getBiographyTranslated();
portraitImage->setFrame(curHero->getIconIndex());

View File

@ -823,11 +823,11 @@ CTownItem::CTownItem(const CGTownInstance * Town)
fastTownHall = std::make_shared<CButton>(Point(69, 31), AnimationPath::builtin("ITMTL.def"), CButton::tooltip(), [&]() { std::make_shared<CCastleBuildings>(town)->enterTownHall(); });
fastTownHall->setImageOrder(town->hallLevel(), town->hallLevel(), town->hallLevel(), town->hallLevel());
fastTownHall->setAnimateLonelyFrame(true);
//TODO: fastTownHall->setAnimateLonelyFrame(true);
int imageIndex = town->fortLevel() == CGTownInstance::EFortLevel::NONE ? 3 : town->fortLevel() - 1;
fastArmyPurchase = std::make_shared<CButton>(Point(111, 31), AnimationPath::builtin("itmcl.def"), CButton::tooltip(), [&]() { std::make_shared<CCastleBuildings>(town)->enterToTheQuickRecruitmentWindow(); });
fastArmyPurchase->setImageOrder(imageIndex, imageIndex, imageIndex, imageIndex);
fastArmyPurchase->setAnimateLonelyFrame(true);
//TODO: fastArmyPurchase->setAnimateLonelyFrame(true);
fastTavern = std::make_shared<LRClickableArea>(Rect(5, 6, 58, 64), [&]()
{
if(town->builtBuildings.count(BuildingID::TAVERN))
@ -976,7 +976,7 @@ CHeroItem::CHeroItem(const CGHeroInstance * Hero)
std::string overlay = CGI->generaltexth->overview[8+it];
auto button = std::make_shared<CToggleButton>(Point(364+(int)it*112, 46), AnimationPath::builtin("OVBUTN3"), CButton::tooltip(hover, overlay), 0);
button->addTextOverlay(CGI->generaltexth->allTexts[stringID[it]], FONT_SMALL, Colors::YELLOW);
button->setTextOverlay(CGI->generaltexth->allTexts[stringID[it]], FONT_SMALL, Colors::YELLOW);
artButtons->addToggle((int)it, button);
}
artButtons->addCallback(std::bind(&CTabbedInt::setActive, artsTabs, _1));

View File

@ -481,24 +481,24 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::func
if(LOCPLINT->cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST) //not enough gold
{
recruit->addHoverText(CButton::NORMAL, CGI->generaltexth->tavernInfo[0]); //Cannot afford a Hero
recruit->addHoverText(EButtonState::NORMAL, CGI->generaltexth->tavernInfo[0]); //Cannot afford a Hero
recruit->block(true);
}
else if(LOCPLINT->cb->howManyHeroes(true) >= CGI->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP))
{
//Cannot recruit. You already have %d Heroes.
recruit->addHoverText(CButton::NORMAL, boost::str(boost::format(CGI->generaltexth->tavernInfo[1]) % LOCPLINT->cb->howManyHeroes(true)));
recruit->addHoverText(EButtonState::NORMAL, boost::str(boost::format(CGI->generaltexth->tavernInfo[1]) % LOCPLINT->cb->howManyHeroes(true)));
recruit->block(true);
}
else if(LOCPLINT->cb->howManyHeroes(false) >= CGI->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
{
//Cannot recruit. You already have %d Heroes.
recruit->addHoverText(CButton::NORMAL, boost::str(boost::format(CGI->generaltexth->tavernInfo[1]) % LOCPLINT->cb->howManyHeroes(false)));
recruit->addHoverText(EButtonState::NORMAL, boost::str(boost::format(CGI->generaltexth->tavernInfo[1]) % LOCPLINT->cb->howManyHeroes(false)));
recruit->block(true);
}
else if(dynamic_cast<const CGTownInstance *>(TavernObj) && dynamic_cast<const CGTownInstance *>(TavernObj)->visitingHero)
{
recruit->addHoverText(CButton::NORMAL, CGI->generaltexth->tavernInfo[2]); //Cannot recruit. You already have a Hero in this town.
recruit->addHoverText(EButtonState::NORMAL, CGI->generaltexth->tavernInfo[2]); //Cannot recruit. You already have a Hero in this town.
recruit->block(true);
}
else
@ -586,7 +586,7 @@ void CTavernWindow::show(Canvas & to)
//Recruit %s the %s
if (!recruit->isBlocked())
recruit->addHoverText(CButton::NORMAL, boost::str(boost::format(CGI->generaltexth->tavernInfo[3]) % sel->h->getNameTranslated() % sel->h->getClassNameTranslated()));
recruit->addHoverText(EButtonState::NORMAL, boost::str(boost::format(CGI->generaltexth->tavernInfo[3]) % sel->h->getNameTranslated() % sel->h->getClassNameTranslated()));
}
@ -880,10 +880,10 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
std::bind(moveArtifacts, [this](bool equipped, bool baclpack) -> void {controller.moveArtifacts(false, equipped, baclpack);}));
backpackButtonLeft = std::make_shared<CButton>(Point(325, 518), AnimationPath::builtin("buttons/backpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
std::bind(openBackpack, heroInst[0]));
backpackButtonLeft->addOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
backpackButtonLeft->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
backpackButtonRight = std::make_shared<CButton>(Point(419, 518), AnimationPath::builtin("buttons/backpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
std::bind(openBackpack, heroInst[1]));
backpackButtonRight->addOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
backpackButtonRight->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
auto leftHeroBlock = heroInst[0]->tempOwner != LOCPLINT->cb->getPlayerID();
auto rightHeroBlock = heroInst[1]->tempOwner != LOCPLINT->cb->getPlayerID();
@ -1355,9 +1355,7 @@ CHillFortWindow::CHillFortWindow(const CGHeroInstance * visitor, const CGObjectI
for(int i = 0; i < slotsCount; i++)
{
upgrade[i] = std::make_shared<CButton>(Point(107 + i * 76, 171), AnimationPath(), CButton::tooltip(getTextForSlot(SlotID(i))), [=](){ makeDeal(SlotID(i)); }, vstd::next(EShortcut::SELECT_INDEX_1, i));
for(auto image : { "APHLF1R.DEF", "APHLF1Y.DEF", "APHLF1G.DEF" })
upgrade[i]->addImage(AnimationPath::builtin(image));
upgrade[i] = std::make_shared<CButton>(Point(107 + i * 76, 171), AnimationPath::builtin("APHLF1R"), CButton::tooltip(getTextForSlot(SlotID(i))), [=](){ makeDeal(SlotID(i)); }, vstd::next(EShortcut::SELECT_INDEX_1, i));
for(int j : {0,1})
{
@ -1366,9 +1364,7 @@ CHillFortWindow::CHillFortWindow(const CGHeroInstance * visitor, const CGObjectI
}
}
upgradeAll = std::make_shared<CButton>(Point(30, 231), AnimationPath(), CButton::tooltip(CGI->generaltexth->allTexts[432]), [&](){ makeDeal(SlotID(slotsCount));}, EShortcut::RECRUITMENT_UPGRADE_ALL);
for(auto image : { "APHLF4R.DEF", "APHLF4Y.DEF", "APHLF4G.DEF" })
upgradeAll->addImage(AnimationPath::builtin(image));
upgradeAll = std::make_shared<CButton>(Point(30, 231), AnimationPath::builtin("APHLF4R"), CButton::tooltip(CGI->generaltexth->allTexts[432]), [&](){ makeDeal(SlotID(slotsCount));}, EShortcut::RECRUITMENT_UPGRADE_ALL);
quit = std::make_shared<CButton>(Point(294, 275), AnimationPath::builtin("IOKAY.DEF"), CButton::tooltip(), std::bind(&CHillFortWindow::close, this), EShortcut::GLOBAL_ACCEPT);
statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
@ -1384,6 +1380,9 @@ bool CHillFortWindow::holdsGarrison(const CArmedInstance * army)
void CHillFortWindow::updateGarrisons()
{
constexpr std::array slotImages = { "APHLF1R.DEF", "APHLF1Y.DEF", "APHLF1G.DEF" };
constexpr std::array allImages = { "APHLF4R.DEF", "APHLF4Y.DEF", "APHLF4G.DEF" };
std::array<TResources, slotsCount> costs;// costs [slot ID] [resource ID] = resource count for upgrade
TResources totalSum; // totalSum[resource ID] = value
@ -1404,9 +1403,9 @@ void CHillFortWindow::updateGarrisons()
}
currState[i] = newState;
upgrade[i]->setIndex(currState[i] == -1 ? 0 : currState[i]);
upgrade[i]->setImage(AnimationPath::builtin(currState[i] == -1 ? slotImages[0] : slotImages[currState[i]]));
upgrade[i]->block(currState[i] == -1);
upgrade[i]->addHoverText(CButton::NORMAL, getTextForSlot(SlotID(i)));
upgrade[i]->addHoverText(EButtonState::NORMAL, getTextForSlot(SlotID(i)));
}
//"Upgrade all" slot
@ -1426,7 +1425,7 @@ void CHillFortWindow::updateGarrisons()
}
currState[slotsCount] = newState;
upgradeAll->setIndex(newState);
upgradeAll->setImage(AnimationPath::builtin(allImages[newState]));
garr->recreateSlots();