Pārlūkot izejas kodu

Merge pull request #3646 from IvanSavenko/configurable_button

Configurable buttons
Ivan Savenko 1 gadu atpakaļ
vecāks
revīzija
4b2cba96cc
62 mainītis faili ar 1069 papildinājumiem un 304 dzēšanām
  1. 0 0
      Mods/vcmi/Data/heroWindow/artifactSlotEmpty.png
  2. 0 0
      Mods/vcmi/Data/heroWindow/backpackButtonIcon.png
  3. BIN
      Mods/vcmi/Data/heroWindow/commanderButtonIcon.png
  4. 0 0
      Mods/vcmi/Data/lobby/selectionTabSortDate.png
  5. 0 8
      Mods/vcmi/Sprites/buttons/backpack.json
  6. BIN
      Mods/vcmi/Sprites/buttons/backpackNormal.png
  7. BIN
      Mods/vcmi/Sprites/buttons/backpackPressed.png
  8. 0 8
      Mods/vcmi/Sprites/buttons/commander.json
  9. BIN
      Mods/vcmi/Sprites/buttons/commanderNormal.png
  10. BIN
      Mods/vcmi/Sprites/buttons/commanderPressed.png
  11. 0 7
      Mods/vcmi/Sprites/lobby/selectionTabSortDate.json
  12. 0 10
      Mods/vcmi/Sprites/settingsWindow/button190.json
  13. BIN
      Mods/vcmi/Sprites/settingsWindow/button190Normal.png
  14. BIN
      Mods/vcmi/Sprites/settingsWindow/button190NormalSelected.png
  15. BIN
      Mods/vcmi/Sprites/settingsWindow/button190Pressed.png
  16. BIN
      Mods/vcmi/Sprites/settingsWindow/button190PressedSelected.png
  17. 0 10
      Mods/vcmi/Sprites/settingsWindow/button32.json
  18. BIN
      Mods/vcmi/Sprites/settingsWindow/button32Normal.png
  19. BIN
      Mods/vcmi/Sprites/settingsWindow/button32NormalSelected.png
  20. BIN
      Mods/vcmi/Sprites/settingsWindow/button32Pressed.png
  21. BIN
      Mods/vcmi/Sprites/settingsWindow/button32PressedSelected.png
  22. 0 10
      Mods/vcmi/Sprites/settingsWindow/button46.json
  23. BIN
      Mods/vcmi/Sprites/settingsWindow/button46Normal.png
  24. BIN
      Mods/vcmi/Sprites/settingsWindow/button46NormalSelected.png
  25. BIN
      Mods/vcmi/Sprites/settingsWindow/button46Pressed.png
  26. BIN
      Mods/vcmi/Sprites/settingsWindow/button46PressedSelected.png
  27. 0 10
      Mods/vcmi/Sprites/settingsWindow/button80.json
  28. BIN
      Mods/vcmi/Sprites/settingsWindow/button80Normal.png
  29. BIN
      Mods/vcmi/Sprites/settingsWindow/button80NormalSelected.png
  30. BIN
      Mods/vcmi/Sprites/settingsWindow/button80Pressed.png
  31. BIN
      Mods/vcmi/Sprites/settingsWindow/button80PressedSelected.png
  32. 1 1
      client/NetPacksLobbyClient.cpp
  33. 0 1
      client/battle/BattleInterfaceClasses.h
  34. 2 3
      client/battle/BattleWindow.cpp
  35. 2 2
      client/globalLobby/GlobalLobbyLoginWindow.cpp
  36. 4 4
      client/globalLobby/GlobalLobbyServerSetup.cpp
  37. 3 3
      client/gui/InterfaceObjectConfigurable.cpp
  38. 5 6
      client/lobby/CBonusSelection.cpp
  39. 8 8
      client/lobby/CLobbyScreen.cpp
  40. 1 1
      client/lobby/OptionsTab.cpp
  41. 6 6
      client/lobby/RandomMapTab.cpp
  42. 3 2
      client/lobby/SelectionTab.cpp
  43. 1 1
      client/mainmenu/CCampaignScreen.cpp
  44. 1 1
      client/mainmenu/CMainMenu.cpp
  45. 172 107
      client/widgets/Buttons.cpp
  46. 67 49
      client/widgets/Buttons.h
  47. 2 3
      client/widgets/ComboBox.cpp
  48. 4 4
      client/widgets/Slider.cpp
  49. 4 6
      client/windows/CCastleInterface.cpp
  50. 2 2
      client/windows/CCreatureWindow.cpp
  51. 6 5
      client/windows/CHeroWindow.cpp
  52. 7 7
      client/windows/CKingdomInterface.cpp
  53. 18 19
      client/windows/GUIClasses.cpp
  54. 22 0
      config/widgets/buttons/campaignBonusSelection.json
  55. 22 0
      config/widgets/buttons/castleInterfaceQuickAccess.json
  56. 114 0
      config/widgets/buttons/heroBackpack.json
  57. 114 0
      config/widgets/buttons/heroCommander.json
  58. 22 0
      config/widgets/buttons/selectionTabSortDate.json
  59. 114 0
      config/widgets/buttons/settingsWindow/button190.json
  60. 114 0
      config/widgets/buttons/settingsWindow/button32.json
  61. 114 0
      config/widgets/buttons/settingsWindow/button46.json
  62. 114 0
      config/widgets/buttons/settingsWindow/button80.json

+ 0 - 0
Mods/vcmi/Sprites/heroWindow/artifactSlotEmpty.png → Mods/vcmi/Data/heroWindow/artifactSlotEmpty.png


+ 0 - 0
Mods/vcmi/Sprites/buttons/backpackButtonIcon.png → Mods/vcmi/Data/heroWindow/backpackButtonIcon.png


BIN
Mods/vcmi/Data/heroWindow/commanderButtonIcon.png


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


+ 0 - 8
Mods/vcmi/Sprites/buttons/backpack.json

@@ -1,8 +0,0 @@
-{
-	"basepath" : "buttons/",
-	"images" :
-	[
-		{ "frame" : 0, "file" : "backpackNormal.png"},
-		{ "frame" : 1, "file" : "backpackPressed.png"}
-	]
-}

BIN
Mods/vcmi/Sprites/buttons/backpackNormal.png


BIN
Mods/vcmi/Sprites/buttons/backpackPressed.png


+ 0 - 8
Mods/vcmi/Sprites/buttons/commander.json

@@ -1,8 +0,0 @@
-{
-	"basepath" : "buttons/",
-	"images" :
-	[
-		{ "frame" : 0, "file" : "commanderNormal.png"},
-		{ "frame" : 1, "file" : "commanderPressed.png"}
-	]
-}

BIN
Mods/vcmi/Sprites/buttons/commanderNormal.png


BIN
Mods/vcmi/Sprites/buttons/commanderPressed.png


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

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

+ 0 - 10
Mods/vcmi/Sprites/settingsWindow/button190.json

@@ -1,10 +0,0 @@
-{
-	"basepath" : "settingsWindow/",
-	"images" :
-	[
-		{ "frame" : 0, "file" : "button190Normal.png"},
-		{ "frame" : 1, "file" : "button190PressedSelected.png"},
-		{ "frame" : 2, "file" : "button190Pressed.png"},
-		{ "frame" : 3, "file" : "button190NormalSelected.png"}
-	]
-}

BIN
Mods/vcmi/Sprites/settingsWindow/button190Normal.png


BIN
Mods/vcmi/Sprites/settingsWindow/button190NormalSelected.png


BIN
Mods/vcmi/Sprites/settingsWindow/button190Pressed.png


BIN
Mods/vcmi/Sprites/settingsWindow/button190PressedSelected.png


+ 0 - 10
Mods/vcmi/Sprites/settingsWindow/button32.json

@@ -1,10 +0,0 @@
-{
-	"basepath" : "settingsWindow/",
-	"images" :
-	[
-		{ "frame" : 0, "file" : "button32Normal.png"},
-		{ "frame" : 1, "file" : "button32PressedSelected.png"},
-		{ "frame" : 2, "file" : "button32Pressed.png"},
-		{ "frame" : 3, "file" : "button32NormalSelected.png"}
-	]
-}

BIN
Mods/vcmi/Sprites/settingsWindow/button32Normal.png


BIN
Mods/vcmi/Sprites/settingsWindow/button32NormalSelected.png


BIN
Mods/vcmi/Sprites/settingsWindow/button32Pressed.png


BIN
Mods/vcmi/Sprites/settingsWindow/button32PressedSelected.png


+ 0 - 10
Mods/vcmi/Sprites/settingsWindow/button46.json

@@ -1,10 +0,0 @@
-{
-	"basepath" : "settingsWindow/",
-	"images" :
-	[
-		{ "frame" : 0, "file" : "button46Normal.png"},
-		{ "frame" : 1, "file" : "button46PressedSelected.png"},
-		{ "frame" : 2, "file" : "button46Pressed.png"},
-		{ "frame" : 3, "file" : "button46NormalSelected.png"}
-	]
-}

BIN
Mods/vcmi/Sprites/settingsWindow/button46Normal.png


BIN
Mods/vcmi/Sprites/settingsWindow/button46NormalSelected.png


BIN
Mods/vcmi/Sprites/settingsWindow/button46Pressed.png


BIN
Mods/vcmi/Sprites/settingsWindow/button46PressedSelected.png


+ 0 - 10
Mods/vcmi/Sprites/settingsWindow/button80.json

@@ -1,10 +0,0 @@
-{
-	"basepath" : "settingsWindow/",
-	"images" :
-	[
-		{ "frame" : 0, "file" : "button80Normal.png"},
-		{ "frame" : 1, "file" : "button80PressedSelected.png"},
-		{ "frame" : 2, "file" : "button80Pressed.png"},
-		{ "frame" : 3, "file" : "button80NormalSelected.png"}
-	]
-}

BIN
Mods/vcmi/Sprites/settingsWindow/button80Normal.png


BIN
Mods/vcmi/Sprites/settingsWindow/button80NormalSelected.png


BIN
Mods/vcmi/Sprites/settingsWindow/button80Pressed.png


BIN
Mods/vcmi/Sprites/settingsWindow/button80PressedSelected.png


+ 1 - 1
client/NetPacksLobbyClient.cpp

@@ -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);
 	}
 }
 

+ 0 - 1
client/battle/BattleInterfaceClasses.h

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

+ 2 - 3
client/battle/BattleWindow.cpp

@@ -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();
 }
 

+ 2 - 2
client/globalLobby/GlobalLobbyLoginWindow.cpp

@@ -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);

+ 4 - 4
client/globalLobby/GlobalLobbyServerSetup.cpp

@@ -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);

+ 3 - 3
client/gui/InterfaceObjectConfigurable.cpp

@@ -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())

+ 5 - 6
client/lobby/CBonusSelection.cpp

@@ -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);
 	}
 }
 
@@ -299,14 +299,13 @@ void CBonusSelection::createBonusesIcons()
 			break;
 		}
 
-		std::shared_ptr<CToggleButton> bonusButton = std::make_shared<CToggleButton>(Point(475 + i * 68, 455), AnimationPath(), CButton::tooltip(desc.toString(), desc.toString()));
+		std::shared_ptr<CToggleButton> bonusButton = std::make_shared<CToggleButton>(Point(475 + i * 68, 455), AnimationPath::builtin("campaignBonusSelection"), CButton::tooltip(desc.toString(), desc.toString()));
 
 		if(picNumber != -1)
-			picName += ":" + std::to_string(picNumber);
+			bonusButton->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin(picName), picNumber));
+		else
+			bonusButton->setOverlay(std::make_shared<CPicture>(ImagePath::builtin(picName)));
 
-		auto anim = GH.renderHandler().createAnimation();
-		anim->setCustom(picName, 0);
-		bonusButton->setImage(anim);
 		if(CSH->campaignBonus == i)
 			bonusButton->setBorderColor(Colors::BRIGHT_YELLOW);
 		groupBonuses->addToggle(i, bonusButton);

+ 8 - 8
client/lobby/CLobbyScreen.cpp

@@ -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()

+ 1 - 1
client/lobby/OptionsTab.cpp

@@ -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

+ 6 - 6
client/lobby/RandomMapTab.cpp

@@ -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)
 				{

+ 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)));
-		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"));

+ 1 - 1
client/mainmenu/CCampaignScreen.cpp

@@ -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())

+ 1 - 1
client/mainmenu/CMainMenu.cpp

@@ -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);
 	}
 }

+ 172 - 107
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,33 +30,28 @@
 
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/filesystem/Filesystem.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 (image)
 	{
-		if(state == PRESSED)
-			image->moveBy(Point(1,1));
-		else
-			image->moveBy(Point(-1,-1));
+		// 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 (newPos < 0)
-		newPos = 0;
-
-	if (state == HIGHLIGHTED && image->size() < 4)
-		newPos = (int)image->size()-1;
-	image->setFrame(newPos);
 
 	if (isActive())
 		redraw();
@@ -71,14 +67,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(const std::shared_ptr<CIntObject>& newOverlay)
 {
 	overlay = newOverlay;
 	if(overlay)
@@ -90,17 +86,59 @@ void CButton::addOverlay(std::shared_ptr<CIntObject> newOverlay)
 	update();
 }
 
-void CButton::addImage(const AnimationPath & filename)
+void ButtonBase::setImage(const AnimationPath & defName, bool playerColoredButton)
+{
+	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
+
+	configurable.reset();
+	image = std::make_shared<CAnimImage>(defName, vstd::to_underlying(getState()));
+	pos = image->pos;
+
+	if (playerColoredButton)
+		image->playerColored(LOCPLINT->playerID);
+}
+
+const JsonNode & ButtonBase::getCurrentConfig() const
 {
-	imageNames.push_back(filename);
+	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(ButtonState state, std::string text)
+void CButton::addHoverText(EButtonState state, const 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 +147,79 @@ void CButton::setImageOrder(int state1, int state2, int state3, int state4)
 	update();
 }
 
-void CButton::setAnimateLonelyFrame(bool agreement)
+std::shared_ptr<CIntObject> ButtonBase::getOverlay()
 {
-	animateLonelyFrame = agreement;
+	return overlay;
 }
-void CButton::setState(ButtonState newState)
+
+void ButtonBase::setStateImpl(EButtonState newState)
 {
-	if (state == 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();
+}
+
+void CButton::setState(EButtonState newState)
+{
+	if (getState() == newState)
 		return;
 
-	if (newState == BLOCKED)
+	if (newState == EButtonState::BLOCKED)
 		removeUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
 	else
 		addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
 
-
-	state = newState;
-	update();
+	setStateImpl(newState);
 }
 
-CButton::ButtonState CButton::getState()
+EButtonState ButtonBase::getState() const
 {
 	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 +242,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 +258,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,18 +272,18 @@ 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);
 	}
 }
 
 void CButton::showPopupWindow(const Point & cursorPosition)
 {
-	if(helpBox.size()) //there is no point to show window with nothing inside...
+	if(!helpBox.empty()) //there is no point to show window with nothing inside...
 		CRClickPopup::createAndPush(helpBox);
 }
 
@@ -219,17 +292,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 +313,43 @@ void CButton::hover (bool on)
 	}
 }
 
-CButton::CButton(Point position, const AnimationPath &defName, const std::pair<std::string, std::string> &help, CFunctionList<void()> Callback, EShortcut key, bool playerColoredButton):
-    CKeyShortcut(key),
-    callback(Callback)
+ButtonBase::ButtonBase(Point position, const AnimationPath & defName, EShortcut key, bool playerColoredButton)
+	: CKeyShortcut(key)
+	, stateToIndex({0, 1, 2, 3})
+	, state(EButtonState::NORMAL)
 {
-	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())
+	JsonPath jsonConfig = defName.toType<EResType::JSON>().addPrefix("CONFIG/WIDGETS/BUTTONS/");
+	if (CResourceHandler::get()->existsResource(jsonConfig))
 	{
-		imageNames.push_back(defName);
-		setIndex(0);
-		if (playerColoredButton)
-			image->playerColored(LOCPLINT->playerID);
+		setConfigurable(jsonConfig, playerColoredButton);
+		return;
 	}
-}
-
-void CButton::setIndex(size_t index)
-{
-	if (index == currentImage || index>=imageNames.size())
+	else
+	{
+		setImage(defName, playerColoredButton);
 		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);
+ButtonBase::~ButtonBase() = default;
 
-	image = std::make_shared<CAnimImage>(anim, getState(), 0, 0, 0, animFlags);
-	pos = image->pos;
+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),
+	helpBox(help.second),
+	actOnDown(false),
+	hoverable(false),
+	soundDisabled(false)
+{
+	defActions = 255-DISPOSE;
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
+	hoverTexts[0] = help.first;
 }
 
-void CButton::setPlayerColor(PlayerColor player)
+void ButtonBase::setPlayerColor(PlayerColor player)
 {
 	if (image && image->isPlayerColored())
 		image->playerColored(player);
@@ -353,41 +414,48 @@ void CToggleBase::setSelected(bool on)
 		callback(on);
 }
 
-bool CToggleBase::canActivate()
+bool CToggleBase::isSelected() const
 {
-	if (selected && !allowDeselection)
-		return false;
-	return true;
+	return selected;
 }
 
-void CToggleBase::addCallback(std::function<void(bool)> function)
+bool CToggleBase::canActivate() const
+{
+	return !selected || allowDeselection;
+}
+
+void CToggleBase::addCallback(const 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 +471,7 @@ void CToggleButton::clickPressed(const Point & cursorPosition)
 	{
 		CCS->soundh->playSound(soundBase::button);
 		GH.input().hapticFeedback();
-		setState(PRESSED);
+		setState(EButtonState::PRESSED);
 	}
 }
 
@@ -416,13 +484,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,10 +502,10 @@ void CToggleButton::clickCancel(const Point & cursorPosition)
 	if(isBlocked())
 		return;
 
-	doSelect(selected);
+	doSelect(isSelected());
 }
 
-void CToggleGroup::addCallback(std::function<void(int)> callback)
+void CToggleGroup::addCallback(const std::function<void(int)> & callback)
 {
 	onChange += callback;
 }
@@ -447,7 +515,7 @@ void CToggleGroup::resetCallback()
 	onChange.clear();
 }
 
-void CToggleGroup::addToggle(int identifier, std::shared_ptr<CToggleBase> button)
+void CToggleGroup::addToggle(int identifier, const std::shared_ptr<CToggleBase> & button)
 {
 	if(auto intObj = std::dynamic_pointer_cast<CIntObject>(button)) // hack-ish workagound to avoid diamond problem with inheritance
 	{
@@ -455,7 +523,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);
@@ -474,11 +542,8 @@ void CToggleGroup::setSelected(int id)
 
 void CToggleGroup::setSelectedOnly(int id)
 {
-	for(auto it = buttons.begin(); it != buttons.end(); it++)
-	{
-		int buttonId = it->first;
-		buttons[buttonId]->setEnabled(buttonId == id);
-	}
+	for(const auto & button : buttons)
+		button.second->setEnabled(button.first == id);
 
 	selectionChanged(id);
 }

+ 67 - 49
client/widgets/Buttons.h

@@ -19,67 +19,86 @@ class Rect;
 VCMI_LIB_NAMESPACE_END
 
 class CAnimImage;
-class CLabel;
-class CAnimation;
+class InterfaceObjectConfigurable;
 
-/// Typical Heroes 3 button which can be inactive or active and can
-/// hold further information if you right-click it
-class CButton : public CKeyShortcut
+enum class EButtonState
 {
-	CFunctionList<void()> callback;
+	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<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
+
+	EButtonState state;//current state of button from enum
+
+	void update();//to refresh button after image or text change
+
+	const JsonNode & getCurrentConfig() const;
 
-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;
+	ButtonBase(Point position, const AnimationPath & defName, EShortcut key, bool playerColoredButton);
+	~ButtonBase();
 
-	ButtonState state;//current state of button from enum
+	std::shared_ptr<CIntObject> getOverlay();
+	void setStateImpl(EButtonState state);
+	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
+	void setOverlay(const 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 ButtonBase
+{
+	CFunctionList<void()> callback;
 
-	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 addImage(const AnimationPath & filename);
-	void addHoverText(ButtonState state, std::string text);
+	void addHoverText(EButtonState state, const 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 +107,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,20 +124,20 @@ 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);
 
 	// returns true if toggle can change its state
-	bool canActivate();
+	bool canActivate() const;
 
 public:
-	/// if set to false - button can not be deselected normally
-	bool allowDeselection;
-
 	CToggleBase(CFunctionList<void(bool)> callback);
 	virtual ~CToggleBase();
 
@@ -133,7 +147,11 @@ public:
 	/// Changes selection to "on" without calling callback
 	void setSelectedSilent(bool on);
 
-	void addCallback(std::function<void(bool)> callback);
+	bool isSelected() const;
+
+	void setAllowDeselection(bool on);
+
+	void addCallback(const 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.
 	virtual void setEnabled(bool enabled);
@@ -169,11 +187,11 @@ public:
 
 	CToggleGroup(const CFunctionList<void(int)> & OnChange);
 
-	void addCallback(std::function<void(int)> callback);
+	void addCallback(const std::function<void(int)> & callback);
 	void resetCallback();
 
 	/// add one toggle/button into group
-	void addToggle(int index, std::shared_ptr<CToggleBase> button);
+	void addToggle(int index, const std::shared_ptr<CToggleBase> & button);
 	/// Changes selection to specific value. Will select toggle with this ID, if present
 	void setSelected(int id);
 	/// in some cases, e.g. LoadGame difficulty selection, after refreshing the UI, the ToggleGroup should 

+ 2 - 3
client/widgets/ComboBox.cpp

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

+ 4 - 4
client/widgets/Slider.cpp

@@ -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));

+ 4 - 6
client/windows/CCastleInterface.cpp

@@ -1324,14 +1324,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());
-	fastTownHall->setAnimateLonelyFrame(true);
+	fastTownHall = std::make_shared<CButton>(Point(80, 413), AnimationPath::builtin("castleInterfaceQuickAccess"), CButton::tooltip(), [this](){ 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);
-	fastArmyPurchase->setAnimateLonelyFrame(true);
+	fastArmyPurchase = std::make_shared<CButton>(Point(122, 413), AnimationPath::builtin("castleInterfaceQuickAccess"), CButton::tooltip(), [this](){ builds->enterToTheQuickRecruitmentWindow(); });
+	fastArmyPurchase->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("itmcl"), imageIndex));
 
 	fastMarket = std::make_shared<LRClickableArea>(Rect(163, 410, 64, 42), [&]()
 	{

+ 2 - 2
client/windows/CCreatureWindow.cpp

@@ -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();
 	}

+ 6 - 5
client/windows/CHeroWindow.cpp

@@ -88,8 +88,8 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
 	if(settings["general"]["enableUiEnhancements"].Bool())
 	{
 		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 = std::make_shared<CButton>(Point(424, 429), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"), [=](){ createBackpackWindow(); }, EShortcut::HERO_BACKPACK);
+		backpackButton->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
 		dismissButton = std::make_shared<CButton>(Point(534, 429), AnimationPath::builtin("hsbtns2.def"), CButton::tooltip(heroscrn[28]), [=](){ dismissCurrent(); }, EShortcut::HERO_DISMISS);
 	}
 	else
@@ -106,7 +106,8 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
 
 	if(hero->commander)
 	{
-		commanderButton = std::make_shared<CButton>(Point(317, 18), AnimationPath::builtin("buttons/commander"), CButton::tooltipLocalized("vcmi.heroWindow.openCommander"), [&](){ commanderWindow(); }, EShortcut::HERO_COMMANDER);
+		commanderButton = std::make_shared<CButton>(Point(317, 18), AnimationPath::builtin("heroCommander"), CButton::tooltipLocalized("vcmi.heroWindow.openCommander"), [&](){ commanderWindow(); }, EShortcut::HERO_COMMANDER);
+		commanderButton->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/commanderButtonIcon")));
 	}
 
 	//right list of heroes
@@ -197,9 +198,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());

+ 7 - 7
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());
-	fastTownHall->setAnimateLonelyFrame(true);
+	fastTownHall = std::make_shared<CButton>(Point(69, 31), AnimationPath::builtin("castleInterfaceQuickAccessz"), CButton::tooltip(), [this]() { 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);
-	fastArmyPurchase->setAnimateLonelyFrame(true);
+	fastArmyPurchase = std::make_shared<CButton>(Point(111, 31), AnimationPath::builtin("castleInterfaceQuickAccessz"), CButton::tooltip(), [this]() { 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))
@@ -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));

+ 18 - 19
client/windows/GUIClasses.cpp

@@ -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()));
 
 		}
 
@@ -878,12 +878,12 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
 			std::bind(moveArtifacts, [this](bool equipped, bool baclpack) -> void {controller.swapArtifacts(equipped, baclpack);}));
 		moveArtifactsButtonRight = std::make_shared<CButton>(Point(425, 154), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/artLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]),
 			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"),
+		backpackButtonLeft       = std::make_shared<CButton>(Point(325, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
 			std::bind(openBackpack, heroInst[0]));
-		backpackButtonLeft->addOverlay(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"),
+		backpackButtonLeft->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
+		backpackButtonRight      = std::make_shared<CButton>(Point(419, 518), AnimationPath::builtin("heroBackpack"), 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("heroWindow/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))), [this, i](){ makeDeal(SlotID(i)); }, vstd::next(EShortcut::SELECT_INDEX_1, i));
 
 		for(int j : {0,1})
 		{
@@ -1366,11 +1364,9 @@ 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]), [this](){ 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);
+	quit = std::make_shared<CButton>(Point(294, 275), AnimationPath::builtin("IOKAY.DEF"), CButton::tooltip(), [this](){close();}, 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));
 
 	garr = std::make_shared<CGarrisonInt>(Point(108, 60), 18, Point(), hero, nullptr);
@@ -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();
 

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

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

+ 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" : []
+	},
+}

+ 114 - 0
config/widgets/buttons/heroBackpack.json

@@ -0,0 +1,114 @@
+{
+	"normal" : {
+		"width": 52,
+		"height": 36,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 52, "h": 36}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 52, "h": 36},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"pressed" : {
+		"width": 52,
+		"height": 36,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 1, "y": 1, "w": 51, "h": 35}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 52, "h": 36},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 3, "y" : 3}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 96 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 48 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 96 ] },
+
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : 2, "y" : -3}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : 2}, "color" : [ 255, 255, 255, 128 ] },
+				]
+			}
+		]
+	},
+	"blocked" : {
+		"width": 52,
+		"height": 36,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 52, "h": 36}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 52, "h": 36},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"highlighted" : {
+		"width": 52,
+		"height": 36,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 52, "h": 36}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 52, "h": 36},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 160 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+				]
+			}
+		]
+	},
+}

+ 114 - 0
config/widgets/buttons/heroCommander.json

@@ -0,0 +1,114 @@
+{
+	"normal" : {
+		"width": 92,
+		"height": 38,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 92, "h": 38}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 92, "h": 38},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"pressed" : {
+		"width": 92,
+		"height": 38,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 1, "y": 1, "w": 91, "h": 37}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 92, "h": 38},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 3, "y" : 3}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 96 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 48 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 96 ] },
+
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : 2, "y" : -3}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : 2}, "color" : [ 255, 255, 255, 128 ] },
+				]
+			}
+		]
+	},
+	"blocked" : {
+		"width": 92,
+		"height": 38,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 92, "h": 38}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 92, "h": 38},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"highlighted" : {
+		"width": 92,
+		"height": 38,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 92, "h": 38}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 92, "h": 38},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 160 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+				]
+			}
+		]
+	},
+}

+ 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" : []
+	},
+}

+ 114 - 0
config/widgets/buttons/settingsWindow/button190.json

@@ -0,0 +1,114 @@
+{
+	"normal" : {
+		"width": 190,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 190, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 190, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"pressed" : {
+		"width": 190,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 1, "y": 1, "w": 189, "h": 31}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 190, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 3, "y" : 3}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 96 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 48 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 96 ] },
+
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : 2, "y" : -3}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : 2}, "color" : [ 255, 255, 255, 128 ] },
+				]
+			}
+		]
+	},
+	"blocked" : {
+		"width": 190,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 190, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 190, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"highlighted" : {
+		"width": 190,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 190, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 190, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 255, 255, 255, 255 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 160 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+				]
+			}
+		]
+	},
+}

+ 114 - 0
config/widgets/buttons/settingsWindow/button32.json

@@ -0,0 +1,114 @@
+{
+	"normal" : {
+		"width": 32,
+		"height": 24,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 32, "h": 24}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 32, "h": 24},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"pressed" : {
+		"width": 32,
+		"height": 24,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 1, "y": 1, "w": 31, "h": 23}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 32, "h": 24},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 3, "y" : 3}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 96 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 48 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 96 ] },
+
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : 2, "y" : -3}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : 2}, "color" : [ 255, 255, 255, 128 ] },
+				]
+			}
+		]
+	},
+	"blocked" : {
+		"width": 32,
+		"height": 24,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 32, "h": 24}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 32, "h": 24},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"highlighted" : {
+		"width": 32,
+		"height": 24,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 32, "h": 24}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 32, "h": 24},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 255, 255, 255, 255 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 160 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+				]
+			}
+		]
+	},
+}

+ 114 - 0
config/widgets/buttons/settingsWindow/button46.json

@@ -0,0 +1,114 @@
+{
+	"normal" : {
+		"width": 46,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 46, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 46, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"pressed" : {
+		"width": 46,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 1, "y": 1, "w": 45, "h": 31}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 46, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 3, "y" : 3}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 96 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 48 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 96 ] },
+
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : 2, "y" : -3}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : 2}, "color" : [ 255, 255, 255, 128 ] },
+				]
+			}
+		]
+	},
+	"blocked" : {
+		"width": 46,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 46, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 46, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"highlighted" : {
+		"width": 46,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 46, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 46, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 255, 255, 255, 255 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 160 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+				]
+			}
+		]
+	},
+}

+ 114 - 0
config/widgets/buttons/settingsWindow/button80.json

@@ -0,0 +1,114 @@
+{
+	"normal" : {
+		"width": 80,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 80, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 80, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"pressed" : {
+		"width": 80,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 1, "y": 1, "w": 79, "h": 31}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 80, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 3, "y" : 3}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 96 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 48 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 96 ] },
+
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : 2, "y" : -3}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : 2}, "color" : [ 255, 255, 255, 128 ] },
+				]
+			}
+		]
+	},
+	"blocked" : {
+		"width": 80,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 80, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 80, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : 0, "y" : -1}, "color" : [ 255, 255, 255, 64 ] },
+					{ "type" : "line", "a" : { "x" : 0, "y" : 0}, "b" : { "x" : -1, "y" : 0}, "color" : [ 255, 255, 255, 128 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 80 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 0, 0, 0, 192 ] },
+					
+					{ "type" : "line", "a" : { "x" : 0, "y" : -1}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+					{ "type" : "line", "a" : { "x" : -1, "y" : 0}, "b" : { "x" : -1, "y" : -1}, "color" : [ 0, 0, 0, 255 ] },
+				]
+			}
+		]
+	},
+	"highlighted" : {
+		"width": 80,
+		"height": 32,
+		"items" : [
+			{
+				"type": "texture",
+				"image": "DiBoxBck",
+				"rect": {"x": 0, "y": 0, "w": 80, "h": 32}
+			},
+			{
+				"type": "graphicalPrimitive",
+				"rect": {"x": 0, "y": 0, "w": 80, "h": 32},
+				"primitives" : [
+					{ "type" : "filledBox", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 0, 0, 0, 80 ] },
+				
+					{ "type" : "rectangle", "a" : { "x" : 2, "y" : 2}, "b" : { "x" : -3, "y" : -3}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : -2}, "b" : { "x" : -2, "y" : -2}, "color" : [ 255, 255, 255, 255 ] },
+					{ "type" : "line", "a" : { "x" : -2, "y" : 1}, "b" : { "x" : -2, "y" : -2}, "color" : [ 255, 255, 255, 255 ] },
+					
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : 1, "y" : -2}, "color" : [ 255, 255, 255, 160 ] },
+					{ "type" : "line", "a" : { "x" : 1, "y" : 1}, "b" : { "x" : -2, "y" : 1}, "color" : [ 255, 255, 255, 160 ] },
+				]
+			}
+		]
+	},
+}