浏览代码

Implemented configurable buttons. Replaced 'animateLonelyFrame' logic.

Ivan Savenko 1 年之前
父节点
当前提交
0051ffa7a9

+ 0 - 0
Mods/vcmi/Sprites/lobby/selectionTabSortDate.png → Mods/vcmi/Data/lobby/selectionTabSortDate.png


+ 0 - 7
Mods/vcmi/Sprites/lobby/selectionTabSortDate.json

@@ -1,7 +0,0 @@
-{
-	"basepath" : "lobby/",
-	"images" :
-	[
-		{ "frame" : 0, "file" : "selectionTabSortDate.png"}
-	]
-}

+ 3 - 2
client/lobby/SelectionTab.cpp

@@ -216,8 +216,9 @@ 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)));
-//TODO:		buttonsSortBy.back()->setAnimateLonelyFrame(true);
+		auto sortByDate = std::make_shared<CButton>(Point(371, 85), AnimationPath::builtin("selectionTabSortDate"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.sortDate")), std::bind(&SelectionTab::sortBy, this, ESortBy::_changeDate));
+		sortByDate->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("lobby/selectionTabSortDate")));
+		buttonsSortBy.push_back(sortByDate);
 	}
 
 	iconsMapFormats = GH.renderHandler().loadAnimation(AnimationPath::builtin("SCSELC.DEF"));

+ 75 - 17
client/widgets/Buttons.cpp

@@ -22,6 +22,7 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/MouseButton.h"
 #include "../gui/Shortcut.h"
+#include "../gui/InterfaceObjectConfigurable.h"
 #include "../windows/InfoWindows.h"
 #include "../render/CAnimation.h"
 #include "../render/Canvas.h"
@@ -29,6 +30,7 @@
 
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/filesystem/Filesystem.h"
 
 void ButtonBase::update()
 {
@@ -42,15 +44,14 @@ void ButtonBase::update()
 			overlay->moveTo(targetPos);
 	}
 
-	int newPos = stateToIndex[int(state)];
-	if (newPos < 0)
-		newPos = 0;
-
-	// 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);
+	if (image)
+	{
+		// 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)
+			image->setFrame(image->size()-1);
+		image->setFrame(stateToIndex[vstd::to_underlying(state)]);
+	}
 
 	if (isActive())
 		redraw();
@@ -89,6 +90,7 @@ void ButtonBase::setImage(const AnimationPath & defName, bool playerColoredButto
 {
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
 
+	configurable.reset();
 	image = std::make_shared<CAnimImage>(defName, vstd::to_underlying(getState()));
 	pos = image->pos;
 
@@ -96,6 +98,41 @@ void ButtonBase::setImage(const AnimationPath & defName, bool playerColoredButto
 		image->playerColored(LOCPLINT->playerID);
 }
 
+const JsonNode & ButtonBase::getCurrentConfig() const
+{
+	if (!config)
+		throw std::runtime_error("No config found in button!");
+
+	static constexpr std::array stateToConfig = {
+		"normal",
+		"pressed",
+		"blocked",
+		"highlighted"
+	};
+
+	std::string key = stateToConfig[vstd::to_underlying(getState())];
+	const JsonNode & value = (*config)[key];
+
+	if (value.isNull())
+		throw std::runtime_error("No config found in button for state " + key + "!");
+
+	return value;
+}
+
+void ButtonBase::setConfigurable(const JsonPath & jsonName, bool playerColoredButton)
+{
+	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
+
+	config = std::make_unique<JsonNode>(jsonName);
+
+	image.reset();
+	configurable = std::make_shared<InterfaceObjectConfigurable>(getCurrentConfig());
+	pos = configurable->pos;
+
+	if (playerColoredButton)
+		image->playerColored(LOCPLINT->playerID);
+}
+
 void CButton::addHoverText(EButtonState state, std::string text)
 {
 	hoverTexts[vstd::to_underlying(state)] = text;
@@ -110,15 +147,24 @@ void ButtonBase::setImageOrder(int state1, int state2, int state3, int state4)
 	update();
 }
 
-//TODO:
-//void CButton::setAnimateLonelyFrame(bool agreement)
-//{
-//	animateLonelyFrame = agreement;
-//}
-
 void ButtonBase::setStateImpl(EButtonState newState)
 {
 	state = newState;
+
+	if (configurable)
+	{
+		OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
+		configurable = std::make_shared<InterfaceObjectConfigurable>(getCurrentConfig());
+		pos = configurable->pos;
+
+		if (overlay)
+		{
+			// Force overlay on top
+			removeChild(overlay.get());
+			addChild(overlay.get());
+		}
+	}
+
 	update();
 }
 
@@ -135,7 +181,7 @@ void CButton::setState(EButtonState newState)
 	setStateImpl(newState);
 }
 
-EButtonState ButtonBase::getState()
+EButtonState ButtonBase::getState() const
 {
 	return state;
 }
@@ -270,9 +316,21 @@ ButtonBase::ButtonBase(Point position, const AnimationPath & defName, EShortcut
 	pos.x += position.x;
 	pos.y += position.y;
 
-	setImage(defName);
+	JsonPath jsonConfig = defName.toType<EResType::JSON>().addPrefix("CONFIG/WIDGETS/BUTTONS/");
+	if (CResourceHandler::get()->existsResource(jsonConfig))
+	{
+		setConfigurable(jsonConfig, playerColoredButton);
+		return;
+	}
+	else
+	{
+		setImage(defName, playerColoredButton);
+		return;
+	}
 }
 
+ButtonBase::~ButtonBase() = default;
+
 CButton::CButton(Point position, const AnimationPath &defName, const std::pair<std::string, std::string> &help, CFunctionList<void()> Callback, EShortcut key, bool playerColoredButton):
 	ButtonBase(position, defName, key, playerColoredButton),
     callback(Callback)

+ 8 - 3
client/widgets/Buttons.h

@@ -19,8 +19,7 @@ class Rect;
 VCMI_LIB_NAMESPACE_END
 
 class CAnimImage;
-class CLabel;
-class CAnimation;
+class InterfaceObjectConfigurable;
 
 enum class EButtonState
 {
@@ -33,7 +32,9 @@ enum class EButtonState
 class ButtonBase : public CKeyShortcut
 {
 	std::shared_ptr<CAnimImage> image; //image for this button
+	std::shared_ptr<InterfaceObjectConfigurable> configurable; //image for this button
 	std::shared_ptr<CIntObject> overlay;//object-overlay, can be null
+	std::unique_ptr<JsonNode> config;
 
 	std::array<int, 4> stateToIndex; // mapping of button state to index of frame in animation
 
@@ -41,16 +42,20 @@ class ButtonBase : public CKeyShortcut
 
 	void update();//to refresh button after image or text change
 
+	const JsonNode & getCurrentConfig() const;
+
 protected:
 	ButtonBase(Point position, const AnimationPath & defName, EShortcut key, bool playerColoredButton);
+	~ButtonBase();
 
 	void setStateImpl(EButtonState state);
-	EButtonState getState();
+	EButtonState getState() const;
 
 public:
 	/// Appearance modifiers
 	void setPlayerColor(PlayerColor player);
 	void setImage(const AnimationPath & defName, bool playerColoredButton = false);
+	void setConfigurable(const JsonPath & jsonName, 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

+ 4 - 6
client/windows/CCastleInterface.cpp

@@ -1332,14 +1332,12 @@ void CCastleInterface::recreateIcons()
 	hall = std::make_shared<CTownInfo>(80, 413, town, true);
 	fort = std::make_shared<CTownInfo>(122, 413, town, false);
 
-	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());
-//TODO:	fastTownHall->setAnimateLonelyFrame(true);
+	fastTownHall = std::make_shared<CButton>(Point(80, 413), AnimationPath::builtin("castleInterfaceQuickAccess"), CButton::tooltip(), [&](){ builds->enterTownHall(); });
+	fastTownHall->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("ITMTL"), town->hallLevel()));
 
 	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);
-//TODO:	fastArmyPurchase->setAnimateLonelyFrame(true);
+	fastArmyPurchase = std::make_shared<CButton>(Point(122, 413), AnimationPath::builtin("castleInterfaceQuickAccess"), CButton::tooltip(), [&](){ builds->enterToTheQuickRecruitmentWindow(); });
+	fastArmyPurchase->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("itmcl"), imageIndex));
 
 	fastMarket = std::make_shared<LRClickableArea>(Rect(163, 410, 64, 42), [&]()
 	{

+ 6 - 6
client/windows/CKingdomInterface.cpp

@@ -821,13 +821,13 @@ CTownItem::CTownItem(const CGTownInstance * Town)
 		available.push_back(std::make_shared<CCreaInfo>(Point(48+37*(int)i, 78), town, (int)i, true, false));
 	}
 
-	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());
-//TODO:	fastTownHall->setAnimateLonelyFrame(true);
+	fastTownHall = std::make_shared<CButton>(Point(69, 31), AnimationPath::builtin("castleInterfaceQuickAccessz"), CButton::tooltip(), [&]() { std::make_shared<CCastleBuildings>(town)->enterTownHall(); });
+	fastTownHall->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("ITMTL"), town->hallLevel()));
+
 	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);
-//TODO:	fastArmyPurchase->setAnimateLonelyFrame(true);
+	fastArmyPurchase = std::make_shared<CButton>(Point(111, 31), AnimationPath::builtin("castleInterfaceQuickAccessz"), CButton::tooltip(), [&]() { std::make_shared<CCastleBuildings>(town)->enterToTheQuickRecruitmentWindow(); });
+	fastArmyPurchase->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("itmcl"), imageIndex));
+
 	fastTavern = std::make_shared<LRClickableArea>(Rect(5, 6, 58, 64), [&]()
 	{
 		if(town->builtBuildings.count(BuildingID::TAVERN))

+ 22 - 0
config/widgets/buttons/castleInterfaceQuickAccess.json

@@ -0,0 +1,22 @@
+{
+	"normal" : {
+		"width": 38,
+		"height": 38,
+		"items" : []
+	},
+	"pressed" : {
+		"width": 38,
+		"height": 38,
+		"items" : []
+	},
+	"blocked" : {
+		"width": 38,
+		"height": 38,
+		"items" : []
+	},
+	"highlighted" : {
+		"width": 38,
+		"height": 38,
+		"items" : []
+	},
+}

+ 22 - 0
config/widgets/buttons/selectionTabSortDate.json

@@ -0,0 +1,22 @@
+{
+	"normal" : {
+		"width": 18,
+		"height": 31,
+		"items" : []
+	},
+	"pressed" : {
+		"width": 18,
+		"height": 31,
+		"items" : []
+	},
+	"blocked" : {
+		"width": 18,
+		"height": 31,
+		"items" : []
+	},
+	"highlighted" : {
+		"width": 18,
+		"height": 31,
+		"items" : []
+	},
+}