diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index b415f5d18..ce1543ba6 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -47,6 +47,8 @@ "vcmi.systemOptions.framerateButton.hover" : "Show FPS", "vcmi.systemOptions.framerateButton.help" : "{Show FPS}\n\n Toggles visibility of Frames Per Second counter in corner of game window.", + "vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel", + "vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in info bar instead of showing up as popup windows", "vcmi.adventureOptions.numericQuantities.hover" : "Numeric Creature Quantities", "vcmi.adventureOptions.numericQuantities.help" : "{Numeric Creature Quantities}\n\n Shows approximate enemy creatures quantities in numeric A-B format.", "vcmi.adventureOptions.forceMovementInfo.hover" : "Always Show Movement Cost", diff --git a/Mods/vcmi/config/vcmi/russian.json b/Mods/vcmi/config/vcmi/russian.json index 0d2c54484..857ee7a18 100644 --- a/Mods/vcmi/config/vcmi/russian.json +++ b/Mods/vcmi/config/vcmi/russian.json @@ -47,6 +47,8 @@ "vcmi.systemOptions.framerateButton.hover" : "Показывать частоту кадров", "vcmi.systemOptions.framerateButton.help" : "{Показывать частоту кадров}\n\n Включить счетчик частоты кадров в углу игрового клиента", + "vcmi.adventureOptions.infoBarPick.hover" : "Сообщения в информационной панели", + "vcmi.adventureOptions.infoBarPick.help" : "{Сообщения в информационной панели}\n\n Если сообщения помещаются, то показывать их в информационной панели (только на интерфейсе карты).", "vcmi.adventureOptions.numericQuantities.hover" : "Приблизительное число существ", "vcmi.adventureOptions.numericQuantities.help" : "{Приблизительное число существ}\n\n Показывать приблизительное число существ в формате A-B вместо словесный обозначений", "vcmi.adventureOptions.forceMovementInfo.hover" : "Всегда показывать стоимость перемещения", diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 8d004ccd3..5c2ef92b8 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1034,11 +1034,18 @@ void CPlayerInterface::yourTacticPhase(int distance) void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &text, const std::vector & components, int soundID) { EVENT_HANDLER_CALLED_BY_CLIENT; - if(type == InfoWindow::INFO) { - adventureInt->infoBar->showComponents(components, text); - if (makingTurn && GH.listInt.size() && LOCPLINT == this) - CCS->soundh->playSound(static_cast(soundID)); - return; + + bool autoTryHover = settings["gameTweaks"]["infoBarPick"].Bool() && type == EInfoWindowMode::AUTO; + auto timer = type == EInfoWindowMode::INFO ? 3000 : 4500; //Implement long info windows like in HD mod + + if(autoTryHover || type == EInfoWindowMode::INFO) + { + if(adventureInt->infoBar->tryShowComponents(components, text, timer)) + { + if (makingTurn && GH.listInt.size() && LOCPLINT == this) + CCS->soundh->playSound(static_cast(soundID)); + return; + } } if (settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown()) diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index 400eb7856..b30e1e31f 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -15,6 +15,7 @@ #include "../widgets/CComponent.h" #include "../widgets/Images.h" +#include "../windows/CMessage.h" #include "../widgets/TextControls.h" #include "../widgets/MiscWidgets.h" #include "../windows/InfoWindows.h" @@ -29,7 +30,7 @@ #include "../../lib/mapObjects/CGTownInstance.h" CInfoBar::CVisibleInfo::CVisibleInfo() - : CIntObject(0, Point(8, 12)) + : CIntObject(0, Point(offset_x, offset_y)) { } @@ -163,24 +164,31 @@ CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo() } } -CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector & compsToDisplay, std::string message) +CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector & compsToDisplay, std::string message, int textH, bool tiny) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATOT", 1, 0); - auto fullRect = Rect(4, 4, 171, 171); + auto fullRect = Rect(0, 0, data_width, data_height); auto textRect = fullRect; auto imageRect = fullRect; + auto font = FONT_SMALL; if(!compsToDisplay.empty()) { auto size = CComponent::large; if(compsToDisplay.size() > 2) size = CComponent::small; - if(message != "") + if(!message.empty()) { - textRect = Rect(4, 4, 171, 42); - imageRect = Rect(4, 42, 171, 121); + textRect = Rect(CInfoBar::offset, + CInfoBar::offset, + data_width - 2 * CInfoBar::offset, + textH); + imageRect = Rect(CInfoBar::offset, + textH + CInfoBar::offset, + data_width - 2 * CInfoBar::offset, + CInfoBar::data_height - 2* CInfoBar::offset - textH); if(compsToDisplay.size() > 4) size = CComponent::tiny; } @@ -194,8 +202,24 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector(vect, imageRect, 4, 4, 1); } + else + font = tiny ? FONT_TINY : font; - text = std::make_shared(message, textRect, 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); + if(!message.empty()) + text = std::make_shared(message, textRect, 0, font, ETextAlignment::CENTER, Colors::WHITE); +} + +int CInfoBar::getEstimatedComponentHeight(int numComps) const +{ + if (numComps > 8) //Bigger than 8 components - return invalid value + return std::numeric_limits::max(); + else if (numComps > 4) + return 48 + 20; // 24px * 2 rows + 20 to offset + else if (numComps > 2) + return 32 + 20; // 32px * 1 row + 20 to offset + else if (numComps) + return 128; // 128 px to offset + return 0; } void CInfoBar::playNewDaySound() @@ -293,13 +317,36 @@ void CInfoBar::showDate() redraw(); } -void CInfoBar::showComponents(const std::vector & comps, std::string message) +bool CInfoBar::tryShowComponents(const std::vector & components, std::string message, int timer) +{ + auto imageH = getEstimatedComponentHeight(components.size()) + (components.empty() ? 0 : 2 * CInfoBar::offset); + auto textH = CMessage::guessHeight(message,CInfoBar::data_width - 2 * CInfoBar::offset, FONT_SMALL); + auto tinyH = CMessage::guessHeight(message,CInfoBar::data_width - 2 * CInfoBar::offset, FONT_TINY); + auto header = CMessage::guessHeader(message); + auto headerH = CMessage::guessHeight(header, CInfoBar::data_width - 2 * CInfoBar::offset, FONT_SMALL); + + // Order matters - priority form should be chosen first + if(imageH + textH < CInfoBar::data_height) + showComponents(components, message, textH, false, timer); + else if(!imageH && tinyH < CInfoBar::data_height) + showComponents(components, message, tinyH, true, timer); + else if(imageH + headerH < CInfoBar::data_height) + showComponents(components, header, headerH, false, timer); + else if(imageH < CInfoBar::data_height) + showComponents(components, "", 0, false, timer); + else + return false; //We cannot fit message to infobar, fallback to window + + return true; +} + +void CInfoBar::showComponents(const std::vector & comps, std::string message, int textH, bool tiny, int timer) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); state = COMPONENT; - visibleInfo = std::make_shared(comps, message); + visibleInfo = std::make_shared(comps, message, textH, tiny); - setTimer(3000); + setTimer(timer); redraw(); } diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 6c6ea662d..f55a43ec4 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -37,12 +37,15 @@ private: /// Declare before to compute correct size of widgets static constexpr int width = 192; static constexpr int height = 192; - static constexpr int component_offset = 8; + static constexpr int offset = 4; //all visible information located in one object - for ease of replacing class CVisibleInfo : public CIntObject { public: + static constexpr int offset_x = 8; + static constexpr int offset_y = 12; + void show(SDL_Surface * to) override; protected: @@ -52,6 +55,9 @@ private: CVisibleInfo(); }; + static constexpr int data_width = width - 2 * CVisibleInfo::offset_x; + static constexpr int data_height = height - 2 * CVisibleInfo::offset_y + 4; //yes, +4 is required + class EmptyVisibleInfo : public CVisibleInfo { public: @@ -108,7 +114,7 @@ private: std::shared_ptr comps; std::shared_ptr text; public: - VisibleComponentInfo(const std::vector & compsToDisplay, std::string message); + VisibleComponentInfo(const std::vector & compsToDisplay, std::string message, int textH, bool tiny); }; enum EState @@ -119,6 +125,9 @@ private: std::shared_ptr visibleInfo; EState state; + //private helper for showing components + void showComponents(const std::vector & comps, std::string message, int textH, bool tiny = false, int timer = 3000); + //removes all information about current state, deactivates timer (if any) void reset(); @@ -137,7 +146,7 @@ public: void showDate(); /// show components for 3 seconds. Used to display picked up resources. Can display up to 8 components - void showComponents(const std::vector & comps, std::string message); + bool tryShowComponents(const std::vector & comps, std::string message, int timer = 3000); /// print enemy turn progress void startEnemyTurn(PlayerColor color); @@ -154,5 +163,8 @@ public: /// check if infobar is showed something about pickups bool showingComponents(); + + /// get estimated component height for InfoBar + int getEstimatedComponentHeight(int numComps) const; }; diff --git a/client/windows/CMessage.cpp b/client/windows/CMessage.cpp index 48903dfb6..fcd7cc559 100644 --- a/client/windows/CMessage.cpp +++ b/client/windows/CMessage.cpp @@ -202,6 +202,25 @@ std::vector CMessage::breakText( std::string text, size_t maxLineWi return ret; } +std::string CMessage::guessHeader(const std::string & msg) +{ + size_t begin = 0; + std::string delimeters = "{}"; + size_t start = msg.find_first_of(delimeters[0], begin); + size_t end = msg.find_first_of(delimeters[1], start); + if(start > msg.size() || end > msg.size()) + return ""; + return msg.substr(begin, end); +} + +int CMessage::guessHeight(const std::string & txt, int width, EFonts font) +{ + const auto f = graphics->fonts[font]; + auto lines = CMessage::breakText(txt, width, font); + int lineHeight = static_cast(f->getLineHeight()); + return lineHeight * (int)lines.size(); +} + void CMessage::drawIWindow(CInfoWindow * ret, std::string text, PlayerColor player) { bool blitOr = false; diff --git a/client/windows/CMessage.h b/client/windows/CMessage.h index f41c5d01a..4ad5e36a1 100644 --- a/client/windows/CMessage.h +++ b/client/windows/CMessage.h @@ -32,6 +32,12 @@ public: /// split text in lines static std::vector breakText(std::string text, size_t maxLineWidth, EFonts font); + /// Try to guess a header of a message + static std::string guessHeader(const std::string & msg); + + /// For convenience + static int guessHeight(const std::string & string, int width, EFonts fnt); + /// constructor static void init(); /// destructor diff --git a/client/windows/settings/AdventureOptionsTab.cpp b/client/windows/settings/AdventureOptionsTab.cpp index 29d8dd074..cd66d3f79 100644 --- a/client/windows/settings/AdventureOptionsTab.cpp +++ b/client/windows/settings/AdventureOptionsTab.cpp @@ -105,11 +105,15 @@ AdventureOptionsTab::AdventureOptionsTab() addCallback("mapSwipeChanged", [](bool value) { #if defined(VCMI_MOBILE) - setBoolSetting("general", "swipe", value); + return setBoolSetting("general", "swipe", value); #else - setBoolSetting("general", "swipeDesktop", value); + return setBoolSetting("general", "swipeDesktop", value); #endif }); + addCallback("infoBarPickChanged", [](bool value) + { + return setBoolSetting("gameTweaks", "infoBarPick", value); + }); build(config); std::shared_ptr playerHeroSpeedToggle = widget("heroMovementSpeedPicker"); @@ -142,4 +146,6 @@ AdventureOptionsTab::AdventureOptionsTab() #else mapSwipeCheckbox->setSelected(settings["general"]["swipeDesktop"].Bool()); #endif + std::shared_ptr infoBarPickCheckbox = widget("infoBarPickCheckbox"); + infoBarPickCheckbox->setSelected(settings["gameTweaks"]["infoBarPick"].Bool()); } diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 378452cbd..651e095c9 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -503,6 +503,7 @@ "numericCreaturesQuantities", "availableCreaturesAsDwellingLabel", "compactTownCreatureInfo", + "infoBarPick", "skipBattleIntroMusic" ], "properties": { @@ -526,6 +527,10 @@ "type" : "boolean", "default" : false }, + "infoBarPick" : { + "type" : "boolean", + "default" : false + }, "skipBattleIntroMusic" : { "type" : "boolean", "default" : false diff --git a/config/widgets/settings/adventureOptionsTab.json b/config/widgets/settings/adventureOptionsTab.json index cab56ffa5..1184d9069 100644 --- a/config/widgets/settings/adventureOptionsTab.json +++ b/config/widgets/settings/adventureOptionsTab.json @@ -364,6 +364,10 @@ { "position": {"x": 45, "y": 385}, "text": "vcmi.adventureOptions.mapSwipe.hover" + }, + { + "position": {"x": 45, "y": 415}, + "text": "vcmi.adventureOptions.infoBarPick.hover" } ] }, @@ -399,6 +403,13 @@ "position": {"x": 10, "y": 383}, "callback": "mapSwipeChanged" }, - + { + "name": "infoBarPickCheckbox", + "type": "toggleButton", + "image": "sysopchk.def", + "help": "vcmi.adventureOptions.infoBarPick", + "position": {"x": 10, "y": 413}, + "callback": "infoBarPickChanged" + } ] }