1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00

Merge branch 'develop' into feature/nullkiller2

This commit is contained in:
Mircea TheHonestCTO
2025-10-28 22:30:39 +01:00
55 changed files with 484 additions and 159 deletions

View File

@@ -239,7 +239,7 @@ void ClientCommandManager::handleTranslateMapsCommand()
try
{
// load and drop loaded map - we only need loader to run over all maps
loadedMaps.push_back(mapService.loadMap(mapName, nullptr));
loadedMaps.push_back(mapService.loadMap(mapName, GAME->interface()->cb.get()));
}
catch(std::exception & e)
{
@@ -260,7 +260,7 @@ void ClientCommandManager::handleTranslateMapsCommand()
{
loadedCampaigns.push_back(CampaignHandler::getCampaign(campaignName.getName()));
for (auto const & part : loadedCampaigns.back()->allScenarios())
loadedCampaigns.back()->getMap(part, nullptr);
loadedCampaigns.back()->getMap(part, GAME->interface()->cb.get());
}
catch(std::exception & e)
{

View File

@@ -104,6 +104,7 @@ public:
void visitSetAvailableArtifacts(SetAvailableArtifacts & pack) override;
void visitEntitiesChanged(EntitiesChanged & pack) override;
void visitPlayerCheated(PlayerCheated & pack) override;
void visitChangeTownName(ChangeTownName & pack) override;
};
class ApplyFirstClientNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)

View File

@@ -1074,3 +1074,16 @@ void ApplyClientNetPackVisitor::visitPlayerCheated(PlayerCheated & pack)
if(pack.colorScheme != ColorScheme::KEEP && vstd::contains(cl.playerint, pack.player))
cl.playerint[pack.player]->setColorScheme(pack.colorScheme);
}
void ApplyClientNetPackVisitor::visitChangeTownName(ChangeTownName & pack)
{
if(!adventureInt)
return;
const CGTownInstance *town = gs.getTown(pack.tid);
if(town)
{
adventureInt->onTownChanged(town);
ENGINE->windows().totalRedraw();
}
}

View File

@@ -1035,10 +1035,7 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
if(s->isControlledByAI() || GAME->server().isGuest())
labelPlayerName = std::make_shared<CLabel>(55, 10, EFonts::FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, name, 95);
else
{
labelPlayerNameEdit = std::make_shared<CTextInput>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, ETextAlignment::CENTER, false);
labelPlayerNameEdit->setText(name);
}
labelPlayerNameEdit = std::make_shared<CTextInputWithConfirm>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, ETextAlignment::CENTER, name, false, [this](){ updateName(); });
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 21, 45, 26), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->arraytxt[206 + whoCanPlay]);
@@ -1114,28 +1111,6 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
bonus = std::make_shared<SelectedBox>(Point(271, 2), *s, BONUS);
}
bool OptionsTab::PlayerOptionsEntry::captureThisKey(EShortcut key)
{
return labelPlayerNameEdit && labelPlayerNameEdit->hasFocus() && key == EShortcut::GLOBAL_ACCEPT;
}
void OptionsTab::PlayerOptionsEntry::keyPressed(EShortcut key)
{
if(labelPlayerNameEdit && key == EShortcut::GLOBAL_ACCEPT)
updateName();
}
bool OptionsTab::PlayerOptionsEntry::receiveEvent(const Point & position, int eventType) const
{
return eventType == AEventsReceiver::LCLICK; // capture all left clicks (not only within control)
}
void OptionsTab::PlayerOptionsEntry::clickReleased(const Point & cursorPosition)
{
if(labelPlayerNameEdit && !labelPlayerNameEdit->pos.isInside(cursorPosition))
updateName();
}
void OptionsTab::PlayerOptionsEntry::updateName() {
if(labelPlayerNameEdit->getText() != name)
{
@@ -1146,8 +1121,6 @@ void OptionsTab::PlayerOptionsEntry::updateName() {
set->String() = labelPlayerNameEdit->getText();
}
}
labelPlayerNameEdit->removeFocus();
name = labelPlayerNameEdit->getText();
}

View File

@@ -28,6 +28,7 @@ class CTextBox;
class CButton;
class CSlider;
class LRClickableArea;
class CTextInputWithConfirm;
class FilledTexturePlayerColored;
class TransparentFilledRectangle;
@@ -196,7 +197,7 @@ private:
std::unique_ptr<PlayerInfo> pi;
std::unique_ptr<PlayerSettings> s;
std::shared_ptr<CLabel> labelPlayerName;
std::shared_ptr<CTextInput> labelPlayerNameEdit;
std::shared_ptr<CTextInputWithConfirm> labelPlayerNameEdit;
std::shared_ptr<CMultiLineLabel> labelWhoCanPlay;
std::shared_ptr<CPicture> background;
std::shared_ptr<CButton> buttonTownLeft;
@@ -215,10 +216,6 @@ private:
PlayerOptionsEntry(const PlayerSettings & S, const OptionsTab & parentTab);
void hideUnavailableButtons();
bool captureThisKey(EShortcut key) override;
void keyPressed(EShortcut key) override;
void clickReleased(const Point & cursorPosition) override;
bool receiveEvent(const Point & position, int eventType) const override;
private:
const OptionsTab & parentTab;

View File

@@ -43,7 +43,8 @@
#include "../../lib/serializer/JsonDeserializer.h"
RandomMapTab::RandomMapTab():
InterfaceObjectConfigurable()
InterfaceObjectConfigurable(),
templateIndex(0)
{
recActions = 0;
mapGenOptions = std::make_shared<CMapGenOptions>();
@@ -140,16 +141,18 @@ RandomMapTab::RandomMapTab():
//set combo box callbacks
if(auto w = widget<ComboBox>("templateList"))
{
w->onConstructItems = [](std::vector<const void *> & curItems){
auto getTemplates = [](){
auto templates = LIBRARY->tplh->getTemplates();
boost::range::sort(templates, [](const CRmgTemplate * a, const CRmgTemplate * b){
return a->getName() < b->getName();
});
return templates;
};
w->onConstructItems = [getTemplates](std::vector<const void *> & curItems){
curItems.push_back(nullptr); //default template
for(auto & t : templates)
for(auto & t : getTemplates())
curItems.push_back(t);
};
@@ -164,6 +167,20 @@ RandomMapTab::RandomMapTab():
return readText(variables["randomTemplate"]);
return std::string("");
};
w->addCallback([this, getTemplates]() // no real dropdown... - instead open dialog
{
std::vector<std::string> texts;
texts.push_back(readText(variables["randomTemplate"]));
for(auto & t : getTemplates())
texts.push_back(t->getName());
ENGINE->windows().popWindows(1);
ENGINE->windows().createAndPushWindow<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("vcmi.lobby.templatesSelect.hover"), LIBRARY->generaltexth->translate("vcmi.lobby.templatesSelect.help"), [this](int index){
widget<ComboBox>("templateList")->setItem(index);
templateIndex = index;
}, templateIndex, std::vector<std::shared_ptr<IImage>>(), true);
});
}
loadOptions();

View File

@@ -55,6 +55,8 @@ private:
std::set<int> playerTeamsAllowed;
std::set<int> compCountAllowed;
std::set<int> compTeamsAllowed;
int templateIndex;
};
class TeamAlignmentsWidget: public InterfaceObjectConfigurable

View File

@@ -99,6 +99,7 @@ public:
virtual bool showGrid() const = 0;
virtual bool showVisitable() const = 0;
virtual bool showBlocked() const = 0;
virtual bool showInvisible() const = 0;
/// if true, spell range for teleport / scuttle boat will be visible
virtual bool showSpellRange(const int3 & position) const = 0;

View File

@@ -14,6 +14,10 @@
#include "IMapRendererContext.h"
#include "mapHandler.h"
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../GameInstance.h"
#include "../Client.h"
#include "../GameEngine.h"
#include "../render/CAnimation.h"
#include "../render/Canvas.h"
@@ -23,12 +27,15 @@
#include "../render/Graphics.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/callback/CCallback.h"
#include "../../lib/gameState/CGameState.h"
#include "../../lib/RiverHandler.h"
#include "../../lib/RoadHandler.h"
#include "../../lib/TerrainHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/MiscObjects.h"
#include "../../lib/mapObjects/ObjectTemplate.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/mapping/TerrainTile.h"
#include "../../lib/pathfinder/CGPathNode.h"
@@ -592,8 +599,10 @@ MapRendererOverlay::MapRendererOverlay()
, imageBlocked(ENGINE->renderHandler().loadImage(ImagePath::builtin("debug/blocked"), EImageBlitMode::COLORKEY))
, imageVisitable(ENGINE->renderHandler().loadImage(ImagePath::builtin("debug/visitable"), EImageBlitMode::COLORKEY))
, imageSpellRange(ENGINE->renderHandler().loadImage(ImagePath::builtin("debug/spellRange"), EImageBlitMode::COLORKEY))
, imageEvent(ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("AVZevnt0"), EImageBlitMode::COLORKEY)->getImage(0))
, imageGrail(ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("AVZgrail"), EImageBlitMode::COLORKEY)->getImage(0))
, grailPos(GAME->server().client->gameState().getMap().grailPos)
{
}
void MapRendererOverlay::renderTile(IMapRendererContext & context, Canvas & target, const int3 & coordinates)
@@ -601,7 +610,7 @@ void MapRendererOverlay::renderTile(IMapRendererContext & context, Canvas & targ
if(context.showGrid())
target.draw(imageGrid, Point(0,0));
if(context.showVisitable() || context.showBlocked())
if(GAME->interface()->cb->getStartInfo()->extraOptionsInfo.cheatsAllowed && (context.showVisitable() || context.showBlocked() || context.showInvisible()))
{
bool blocking = false;
bool visitable = false;
@@ -610,6 +619,12 @@ void MapRendererOverlay::renderTile(IMapRendererContext & context, Canvas & targ
{
const auto * object = context.getObject(objectID);
if(object->ID == Obj::EVENT && context.showInvisible())
target.draw(imageEvent, Point(0,0));
if(grailPos == coordinates && context.showInvisible())
target.draw(imageGrail, Point(0,0));
if(context.objectTransparency(objectID, coordinates) > 0 && !context.isActiveHero(object))
{
visitable |= object->visitableAt(coordinates);
@@ -643,6 +658,9 @@ uint8_t MapRendererOverlay::checksum(IMapRendererContext & context, const int3 &
if (context.showSpellRange(coordinates))
result += 8;
if (context.showInvisible())
result += 16;
return result;
}

View File

@@ -9,11 +9,11 @@
*/
#pragma once
#include "../../lib/int3.h"
#include "../../lib/filesystem/ResourcePath.h"
VCMI_LIB_NAMESPACE_BEGIN
class int3;
class ObjectInstanceID;
class CGObjectInstance;
@@ -139,6 +139,10 @@ class MapRendererOverlay
std::shared_ptr<IImage> imageVisitable;
std::shared_ptr<IImage> imageBlocked;
std::shared_ptr<IImage> imageSpellRange;
std::shared_ptr<IImage> imageEvent;
std::shared_ptr<IImage> imageGrail;
int3 grailPos;
public:
MapRendererOverlay();

View File

@@ -232,6 +232,11 @@ bool MapRendererBaseContext::showBlocked() const
return false;
}
bool MapRendererBaseContext::showInvisible() const
{
return false;
}
bool MapRendererBaseContext::showSpellRange(const int3 & position) const
{
return false;
@@ -362,6 +367,11 @@ bool MapRendererAdventureContext::showBlocked() const
return settingShowBlocked;
}
bool MapRendererAdventureContext::showInvisible() const
{
return settingShowInvisible;
}
bool MapRendererAdventureContext::showTextOverlay() const
{
return settingTextOverlay;

View File

@@ -62,6 +62,7 @@ public:
bool showGrid() const override;
bool showVisitable() const override;
bool showBlocked() const override;
bool showInvisible() const override;
bool showSpellRange(const int3 & position) const override;
};
@@ -72,6 +73,7 @@ public:
bool settingShowGrid = false;
bool settingShowVisitable = false;
bool settingShowBlocked = false;
bool settingShowInvisible = false;
bool settingTextOverlay = false;
bool settingsAdventureObjectAnimation = true;
bool settingsAdventureTerrainAnimation = true;
@@ -88,6 +90,7 @@ public:
bool showGrid() const override;
bool showVisitable() const override;
bool showBlocked() const override;
bool showInvisible() const override;
bool showTextOverlay() const override;
bool showSpellRange(const int3 & position) const override;

View File

@@ -233,6 +233,7 @@ void MapViewController::updateState()
adventureContext->settingShowGrid = settings["gameTweaks"]["showGrid"].Bool();
adventureContext->settingShowVisitable = settings["session"]["showVisitable"].Bool();
adventureContext->settingShowBlocked = settings["session"]["showBlocked"].Bool();
adventureContext->settingShowInvisible = settings["session"]["showInvisible"].Bool();
adventureContext->settingTextOverlay = (ENGINE->isKeyboardAltDown() || ENGINE->input().getNumTouchFingers() == 2) && settings["general"]["enableOverlay"].Bool();
}
}

View File

@@ -85,6 +85,7 @@ private:
//void visitSetCommanderProperty(SetCommanderProperty & pack) override;
//void visitAddQuest(AddQuest & pack) override;
//void visitChangeFormation(ChangeFormation & pack) override;
//void visitChangeTownName(ChangeTownName & pack) override;
//void visitChangeSpells(ChangeSpells & pack) override;
//void visitSetAvailableHero(SetAvailableHero & pack) override;
//void visitChangeObjectVisitors(ChangeObjectVisitors & pack) override;

View File

@@ -27,6 +27,85 @@
std::list<CFocusable *> CFocusable::focusables;
CFocusable * CFocusable::inputWithFocus;
CTextInputWithConfirm::CTextInputWithConfirm(const Rect & Pos, EFonts font, ETextAlignment alignment, std::string text, bool limitToRect, std::function<void()> confirmCallback)
: CTextInput(Pos, font, alignment, false), confirmCb(confirmCallback), limitToRect(limitToRect), initialText(text)
{
setText(text);
}
bool CTextInputWithConfirm::captureThisKey(EShortcut key)
{
return hasFocus() && (key == EShortcut::GLOBAL_ACCEPT || key == EShortcut::GLOBAL_CANCEL || key == EShortcut::GLOBAL_BACKSPACE);
}
void CTextInputWithConfirm::keyPressed(EShortcut key)
{
if(!hasFocus())
return;
if(key == EShortcut::GLOBAL_ACCEPT)
confirm();
else if(key == EShortcut::GLOBAL_CANCEL)
{
setText(initialText);
removeFocus();
}
CTextInput::keyPressed(key);
}
bool CTextInputWithConfirm::receiveEvent(const Point & position, int eventType) const
{
return eventType == AEventsReceiver::LCLICK; // capture all left clicks (not only within control)
}
void CTextInputWithConfirm::clickReleased(const Point & cursorPosition)
{
if(!pos.isInside(cursorPosition)) // clicked outside
confirm();
}
void CTextInputWithConfirm::clickPressed(const Point & cursorPosition)
{
if(pos.isInside(cursorPosition)) // clickPressed should respect control area (receiveEvent also affects this)
CTextInput::clickPressed(cursorPosition);
}
void CTextInputWithConfirm::onFocusGot()
{
initialText = getText();
CTextInput::onFocusGot();
}
void CTextInputWithConfirm::textInputted(const std::string & enteredText)
{
if(!hasFocus())
return;
std::string visibleText = getVisibleText() + enteredText;
const auto & font = ENGINE->renderHandler().loadFont(label->font);
if(!limitToRect || (font->getStringWidth(visibleText) - CLabel::getDelimitersWidth(label->font, visibleText)) < pos.w)
CTextInput::textInputted(enteredText);
}
void CTextInputWithConfirm::deactivate()
{
removeUsedEvents(LCLICK);
CTextInput::deactivate();
}
void CTextInputWithConfirm::confirm()
{
if(getText().empty())
setText(initialText);
if(confirmCb && initialText != getText())
confirmCb();
removeFocus();
}
CTextInput::CTextInput(const Rect & Pos)
:originalAlignment(ETextAlignment::CENTERLEFT)
{
@@ -196,7 +275,7 @@ void CTextInput::updateLabel()
label->alignment = originalAlignment;
const auto & font = ENGINE->renderHandler().loadFont(label->font);
while (font->getStringWidth(visibleText) > pos.w)
while ((font->getStringWidth(visibleText) - CLabel::getDelimitersWidth(label->font, visibleText)) > pos.w)
{
label->alignment = ETextAlignment::CENTERRIGHT;
visibleText = visibleText.substr(TextOperations::getUnicodeCharacterSize(visibleText[0]));

View File

@@ -45,8 +45,9 @@ public:
};
/// Text input box where players can enter text
class CTextInput final : public CFocusable
class CTextInput : public CFocusable
{
protected:
using TextEditedCallback = std::function<void(const std::string &)>;
using TextFilterCallback = std::function<void(std::string &, const std::string &)>;
@@ -71,12 +72,12 @@ class CTextInput final : public CFocusable
void createLabel(bool giveFocusToInput);
void updateLabel();
void clickPressed(const Point & cursorPosition) final;
void textInputted(const std::string & enteredText) final;
void textEdited(const std::string & enteredText) final;
void onFocusGot() final;
void onFocusLost() final;
void showPopupWindow(const Point & cursorPosition) final;
void clickPressed(const Point & cursorPosition) override;
void textInputted(const std::string & enteredText) override;
void textEdited(const std::string & enteredText) override;
void onFocusGot() override;
void onFocusLost() override;
void showPopupWindow(const Point & cursorPosition) override;
CTextInput(const Rect & Pos);
public:
@@ -105,7 +106,27 @@ public:
void setAlignment(ETextAlignment alignment);
// CIntObject interface impl
void keyPressed(EShortcut key) final;
void activate() final;
void deactivate() final;
void keyPressed(EShortcut key) override;
void activate() override;
void deactivate() override;
};
class CTextInputWithConfirm final : public CTextInput
{
std::string initialText;
std::function<void()> confirmCb;
bool limitToRect;
void confirm();
public:
CTextInputWithConfirm(const Rect & Pos, EFonts font, ETextAlignment alignment, std::string text, bool limitToRect, std::function<void()> confirmCallback);
bool captureThisKey(EShortcut key) override;
void keyPressed(EShortcut key) override;
void clickReleased(const Point & cursorPosition) override;
void clickPressed(const Point & cursorPosition) override;
bool receiveEvent(const Point & position, int eventType) const override;
void onFocusGot() override;
void textInputted(const std::string & enteredText) override;
void deactivate() override;
};

View File

@@ -182,29 +182,39 @@ std::vector<std::string> CMultiLineLabel::getLines()
return lines;
}
void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what)
const std::string delimiters = "{}";
int CTextContainer::getDelimitersWidth(EFonts font, std::string text)
{
const auto f = ENGINE->renderHandler().loadFont(font);
Point where = destRect.topLeft();
const std::string delimiters = "{}";
auto delimitersCount = std::count_if(what.cbegin(), what.cend(), [&delimiters](char c)
auto delimitersWidth = std::count_if(text.cbegin(), text.cend(), [](char c)
{
return delimiters.find(c) != std::string::npos;
});
//We should count delimiters length from string to correct centering later.
delimitersCount *= f->getStringWidth(delimiters)/2;
delimitersWidth *= f->getStringWidth(delimiters)/2;
std::smatch match;
std::regex expr("\\{(.*?)\\|");
std::string::const_iterator searchStart( what.cbegin() );
while(std::regex_search(searchStart, what.cend(), match, expr))
std::string::const_iterator searchStart( text.cbegin() );
while(std::regex_search(searchStart, text.cend(), match, expr))
{
std::string colorText = match[1].str();
if(auto c = Colors::parseColor(colorText))
delimitersCount += f->getStringWidth(colorText + "|");
delimitersWidth += f->getStringWidth(colorText + "|");
searchStart = match.suffix().first;
}
return delimitersWidth;
}
void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what)
{
const auto f = ENGINE->renderHandler().loadFont(font);
Point where = destRect.topLeft();
int delimitersWidth = getDelimitersWidth(font, what);
// input is rect in which given text should be placed
// calculate proper position for top-left corner of the text
@@ -212,10 +222,10 @@ void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what)
where.x += getBorderSize().x;
if(alignment == ETextAlignment::CENTER || alignment == ETextAlignment::TOPCENTER || alignment == ETextAlignment::BOTTOMCENTER)
where.x += (destRect.w - (static_cast<int>(f->getStringWidth(what)) - delimitersCount)) / 2;
where.x += (destRect.w - (static_cast<int>(f->getStringWidth(what)) - delimitersWidth)) / 2;
if(alignment == ETextAlignment::TOPRIGHT || alignment == ETextAlignment::BOTTOMRIGHT || alignment == ETextAlignment::CENTERRIGHT)
where.x += getBorderSize().x + destRect.w - (static_cast<int>(f->getStringWidth(what)) - delimitersCount);
where.x += getBorderSize().x + destRect.w - (static_cast<int>(f->getStringWidth(what)) - delimitersWidth);
if(alignment == ETextAlignment::TOPLEFT || alignment == ETextAlignment::TOPCENTER || alignment == ETextAlignment::TOPRIGHT)
where.y += getBorderSize().y;

View File

@@ -31,6 +31,8 @@ protected:
CTextContainer(ETextAlignment alignment, EFonts font, ColorRGBA color);
public:
static int getDelimitersWidth(EFonts font, std::string text);
ETextAlignment alignment;
EFonts font;
ColorRGBA color; // default font color. Can be overridden by placing "{}" into the string

View File

@@ -30,6 +30,7 @@
#include "../widgets/MiscWidgets.h"
#include "../widgets/CComponent.h"
#include "../widgets/CGarrisonInt.h"
#include "../widgets/CTextInput.h"
#include "../widgets/Buttons.h"
#include "../widgets/TextControls.h"
#include "../widgets/RadialMenu.h"
@@ -1435,7 +1436,15 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
garr->setRedrawParent(true);
heroes = std::make_shared<HeroSlots>(town, Point(241, 387), Point(241, 483), garr, true);
title = std::make_shared<CLabel>(85, 387, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, town->getNameTranslated());
title = std::make_shared<CTextInputWithConfirm>(Rect(83, 386, 140, 20), FONT_MEDIUM, ETextAlignment::TOPLEFT, town->getNameTranslated(), true, [this](){
std::string name = title->getText();
std::string originalName = LIBRARY->generaltexth->translate(town->getNameTextID());
if(name == originalName)
name = ""; // use textID again
GAME->interface()->cb->setTownName(town, name);
});
if(town->tempOwner != GAME->interface()->playerID) // disable changing for allied towns
title->deactivate();
income = std::make_shared<CLabel>(195, 443, FONT_SMALL, ETextAlignment::CENTER);
icon = std::make_shared<CAnimImage>(AnimationPath::builtin("ITPT"), 0, 0, 15, 387);

View File

@@ -37,6 +37,7 @@ class CGarrisonInt;
class CComponent;
class CComponentBox;
class LRClickableArea;
class CTextInputWithConfirm;
/// Building "button"
class CBuildingRect : public CShowableAnim
@@ -225,7 +226,7 @@ public:
/// Class which manages the castle window
class CCastleInterface final : public CStatusbarWindow, public IGarrisonHolder, public IArtifactsHolder
{
std::shared_ptr<CLabel> title;
std::shared_ptr<CTextInputWithConfirm> title;
std::shared_ptr<CLabel> income;
std::shared_ptr<CAnimImage> icon;

View File

@@ -86,7 +86,7 @@ std::shared_ptr<CPicture> CWindowObject::createBg(const ImagePath & imageName, b
return nullptr;
auto image = std::make_shared<CPicture>(imageName, Point(0,0), EImageBlitMode::OPAQUE);
if(playerColored)
if(playerColored && GAME->interface())
image->setPlayerColor(GAME->interface()->playerID);
return image;
}

View File

@@ -1596,7 +1596,7 @@ CObjectListWindow::CObjectListWindow(const std::vector<int> & _items, std::share
for(int id : _items)
{
std::string objectName = GAME->interface()->cb->getObjInstance(ObjectInstanceID(id))->getObjectName();
trimTextIfTooWide(objectName);
trimTextIfTooWide(objectName, false);
items.emplace_back(id, objectName);
}
itemsVisible = items;
@@ -1620,7 +1620,7 @@ CObjectListWindow::CObjectListWindow(const std::vector<std::string> & _items, st
for(size_t i = 0; i < _items.size(); i++)
{
std::string objectName = _items[i];
trimTextIfTooWide(objectName);
trimTextIfTooWide(objectName, true);
items.emplace_back(static_cast<int>(i), objectName);
}
itemsVisible = items;
@@ -1664,16 +1664,24 @@ void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::stri
searchBox->setCallback(std::bind(&CObjectListWindow::itemsSearchCallback, this, std::placeholders::_1));
}
void CObjectListWindow::trimTextIfTooWide(std::string & text) const
void CObjectListWindow::trimTextIfTooWide(std::string & text, bool preserveCountSuffix) const
{
std::string suffix = "...";
int maxWidth = pos.w - 60; // 60 px for scrollbar and borders
auto posBrace = text.find('(');
auto posClosing = text.find(')');
std::string objCount = text.substr(posBrace, posClosing - posBrace) + ')';
if(text[0] == '{')
objCount = '}' + objCount;
suffix += "}";
if (preserveCountSuffix)
{
auto posBrace = text.find_last_of("(");
auto posClosing = text.find_last_of(")");
std::string objCount = text.substr(posBrace, posClosing - posBrace) + ')';
suffix += " ";
suffix += objCount;
}
const auto & font = ENGINE->renderHandler().loadFont(FONT_SMALL);
std::string suffix = "... " + objCount;
if(font->getStringWidth(text) >= maxWidth)
{

View File

@@ -206,7 +206,7 @@ class CObjectListWindow : public CWindowObject
std::vector< std::pair<int, std::string> > itemsVisible; //visible items present in list
void init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, bool searchBoxEnabled);
void trimTextIfTooWide(std::string & text) const; // trim item's text to fit within window's width
void trimTextIfTooWide(std::string & text, bool preserveCountSuffix) const; // trim item's text to fit within window's width
void itemsSearchCallback(const std::string & text);
void exitPressed();
public: