Kaynağa Gözat

Merge pull request #3641 from IvanSavenko/messagebox_refactor

Messagebox code refactor
Ivan Savenko 1 yıl önce
ebeveyn
işleme
850af00303

+ 1 - 1
client/battle/BattleWindow.cpp

@@ -813,7 +813,7 @@ void BattleWindow::showAll(Canvas & to)
 	CIntObject::showAll(to);
 
 	if (GH.screenDimensions().x != 800 || GH.screenDimensions().y !=600)
-		CMessage::drawBorder(owner.curInt->playerID, to.getInternalSurface(), pos.w+28, pos.h+29, pos.x-14, pos.y-15);
+		CMessage::drawBorder(owner.curInt->playerID, to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
 }
 
 void BattleWindow::show(Canvas & to)

+ 1 - 1
client/lobby/OptionsTab.cpp

@@ -374,7 +374,7 @@ void OptionsTab::CPlayerOptionTooltipBox::genTownWindow()
 		if(!elem.empty())
 			components.push_back(std::make_shared<CComponent>(ComponentType::CREATURE, elem.front(), std::nullopt, CComponent::tiny));
 	}
-	boxAssociatedCreatures = std::make_shared<CComponentBox>(components, Rect(10, 140, pos.w - 20, 140));
+	boxAssociatedCreatures = std::make_shared<CComponentBox>(components, Rect(10, 140, pos.w - 20, 140), 20, 10, 22, 4);
 }
 
 void OptionsTab::CPlayerOptionTooltipBox::genHeroWindow()

+ 5 - 3
client/widgets/CComponent.cpp

@@ -80,13 +80,14 @@ void CComponent::init(ComponentType Type, ComponentSubType Subtype, std::optiona
 
 	pos.h += 4; //distance between text and image
 
-	auto max = 80;
+	// WARNING: too low values will lead to bad line-breaks in CPlayerOptionTooltipBox - check right-click on starting town in pregame
+	int max = 80;
 	if (size < large)
 		max = 72;
 	if (size < medium)
-		max = 40;
+		max = 60;
 	if (size < small)
-		max = 30;
+		max = 55;
 
 	if(Type == ComponentType::RESOURCE && !ValText.empty())
 		max = 80;
@@ -426,6 +427,7 @@ void CComponentBox::placeComponents(bool selectable)
 	for(auto & comp : components)
 	{
 		addChild(comp.get());
+		comp->recActions = defActions; //FIXME: for some reason, received component might have recActions set to 0
 		comp->moveTo(Point(pos.x, pos.y));
 	}
 

+ 1 - 1
client/widgets/CComponent.h

@@ -92,7 +92,7 @@ class CComponentBox : public CIntObject
 	std::shared_ptr<CSelectableComponent> selected;
 	std::function<void(int newID)> onSelect;
 
-	static constexpr int defaultBetweenImagesMin = 20;
+	static constexpr int defaultBetweenImagesMin = 42;
 	static constexpr int defaultBetweenSubtitlesMin = 10;
 	static constexpr int defaultBetweenRows = 22;
 	static constexpr int defaultComponentsInRow = 4;

+ 11 - 0
client/widgets/TextControls.cpp

@@ -369,6 +369,17 @@ void CTextBox::sliderMoved(int to)
 	label->scrollTextTo(to);
 }
 
+void CTextBox::trimToFit()
+{
+	if (slider)
+		return;
+
+	pos.w = label->textSize.x;
+	pos.h = label->textSize.y;
+	label->pos.w = label->textSize.x;
+	label->pos.h = label->textSize.y;
+}
+
 void CTextBox::resize(Point newSize)
 {
 	pos.w = newSize.x;

+ 3 - 0
client/widgets/TextControls.h

@@ -116,6 +116,9 @@ public:
 	CTextBox(std::string Text, const Rect & rect, int SliderStyle, EFonts Font = FONT_SMALL, ETextAlignment Align = ETextAlignment::TOPLEFT, const ColorRGBA & Color = Colors::WHITE);
 
 	void resize(Point newSize);
+	/// Resizes text box to minimal size needed to fit current text
+	/// No effect if text is too large to fit and requires slider
+	void trimToFit();
 	void setText(const std::string & Txt);
 	void sliderMoved(int to);
 };

+ 2 - 2
client/windows/CHeroBackpackWindow.cpp

@@ -45,7 +45,7 @@ CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero)
 void CHeroBackpackWindow::showAll(Canvas & to)
 {
 	CIntObject::showAll(to);
-	CMessage::drawBorder(PlayerColor(LOCPLINT->playerID), to.getInternalSurface(), pos.w+28, pos.h+29, pos.x-14, pos.y-15);
+	CMessage::drawBorder(PlayerColor(LOCPLINT->playerID), to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
 }
 
 CHeroQuickBackpackWindow::CHeroQuickBackpackWindow(const CGHeroInstance * hero, ArtifactPosition targetSlot)
@@ -87,6 +87,6 @@ void CHeroQuickBackpackWindow::showAll(Canvas & to)
 		close();
 		return;
 	}
-	CMessage::drawBorder(PlayerColor(LOCPLINT->playerID), to.getInternalSurface(), pos.w + 28, pos.h + 29, pos.x - 14, pos.y - 15);
+	CMessage::drawBorder(PlayerColor(LOCPLINT->playerID), to, pos.w + 28, pos.h + 29, pos.x - 14, pos.y - 15);
 	CIntObject::showAll(to);
 }

+ 143 - 343
client/windows/CMessage.cpp

@@ -11,79 +11,40 @@
 #include "StdInc.h"
 #include "CMessage.h"
 
-#include "../CGameInfo.h"
-#include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/TextOperations.h"
 
-#include "../windows/InfoWindows.h"
-#include "../widgets/Buttons.h"
-#include "../widgets/CComponent.h"
-#include "../widgets/Slider.h"
-#include "../widgets/TextControls.h"
 #include "../gui/CGuiHandler.h"
 #include "../render/CAnimation.h"
-#include "../render/IImage.h"
-#include "../render/IRenderHandler.h"
 #include "../render/Canvas.h"
 #include "../render/Graphics.h"
 #include "../render/IFont.h"
-#include "../renderSDL/SDL_Extensions.h"
-
-#include <SDL_surface.h>
-
-const int BETWEEN_COMPS_ROWS = 10;
-const int BEFORE_COMPONENTS = 30;
-const int BETWEEN_COMPS = 30;
-const int SIDE_MARGIN = 30;
-
-template <typename T, typename U> std::pair<T,U> max(const std::pair<T,U> &x, const std::pair<T,U> &y)
-{
-	std::pair<T,U> ret;
-	ret.first = std::max(x.first,y.first);
-	ret.second = std::max(x.second,y.second);
-	return ret;
-}
-
-//One image component + subtitles below it
-class ComponentResolved : public CIntObject
-{
-public:
-	std::shared_ptr<CComponent> comp;
-
-	//blit component with image centered at this position
-	void showAll(Canvas & to) override;
-
-	//ComponentResolved();
-	ComponentResolved(std::shared_ptr<CComponent> Comp);
-	~ComponentResolved();
-};
-// Full set of components for blitting on dialog box
-struct ComponentsToBlit
-{
-	std::vector< std::vector<std::shared_ptr<ComponentResolved>>> comps;
-	int w, h;
-
-	void blitCompsOnSur(bool blitOr, int inter, int &curh, SDL_Surface *ret);
-	ComponentsToBlit(std::vector<std::shared_ptr<CComponent>> & SComps, int maxw, bool blitOr);
-	~ComponentsToBlit();
-};
+#include "../render/IImage.h"
+#include "../render/IRenderHandler.h"
+#include "../widgets/Buttons.h"
+#include "../widgets/CComponent.h"
+#include "../widgets/Images.h"
+#include "../widgets/Slider.h"
+#include "../widgets/TextControls.h"
+#include "../windows/InfoWindows.h"
 
-namespace
-{
-	std::array<std::shared_ptr<CAnimation>, PlayerColor::PLAYER_LIMIT_I> dialogBorders;
-	std::array<std::vector<std::shared_ptr<IImage>>, PlayerColor::PLAYER_LIMIT_I> piecesOfBox;
+constexpr int RIGHT_CLICK_POPUP_MIN_SIZE = 100;
+constexpr int SIDE_MARGIN = 11;
+constexpr int TOP_MARGIN = 20;
+constexpr int BOTTOM_MARGIN = 16;
+constexpr int INTERVAL_BETWEEN_BUTTONS = 18;
+constexpr int INTERVAL_BETWEEN_TEXT_AND_BUTTONS = 24;
 
-	std::shared_ptr<IImage> background;//todo: should be CFilledTexture
-}
+static std::array<std::shared_ptr<CAnimation>, PlayerColor::PLAYER_LIMIT_I> dialogBorders;
+static std::array<std::vector<std::shared_ptr<IImage>>, PlayerColor::PLAYER_LIMIT_I> piecesOfBox;
 
 void CMessage::init()
 {
-	for(int i=0; i<PlayerColor::PLAYER_LIMIT_I; i++)
+	for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
 	{
 		dialogBorders[i] = GH.renderHandler().loadAnimation(AnimationPath::builtin("DIALGBOX"));
 		dialogBorders[i]->preload();
 
-		for(int j=0; j < dialogBorders[i]->size(0); j++)
+		for(int j = 0; j < dialogBorders[i]->size(0); j++)
 		{
 			auto image = dialogBorders[i]->getImage(j, 0);
 			//assume blue color initially
@@ -92,8 +53,6 @@ void CMessage::init()
 			piecesOfBox[i].push_back(image);
 		}
 	}
-
-	background = GH.renderHandler().loadImage(ImagePath::builtin("DIBOXBCK.BMP"), EImageBlitMode::OPAQUE);
 }
 
 void CMessage::dispose()
@@ -102,61 +61,45 @@ void CMessage::dispose()
 		item.reset();
 }
 
-SDL_Surface * CMessage::drawDialogBox(int w, int h, PlayerColor playerColor)
-{
-	//prepare surface
-	SDL_Surface * ret = CSDL_Ext::newSurface(w,h);
-	for (int i=0; i<w; i+=background->width())//background
-	{
-		for (int j=0; j<h; j+=background->height())
-		{
-			background->draw(ret, i, j);
-		}
-	}
-
-	drawBorder(playerColor, ret, w, h);
-	return ret;
-}
-
-std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWidth, EFonts font )
+std::vector<std::string> CMessage::breakText(std::string text, size_t maxLineWidth, EFonts font)
 {
 	assert(maxLineWidth != 0);
-	if (maxLineWidth == 0)
-		return { text };
+	if(maxLineWidth == 0)
+		return {text};
 
 	std::vector<std::string> ret;
 
-	boost::algorithm::trim_right_if(text,boost::algorithm::is_any_of(std::string(" ")));
+	boost::algorithm::trim_right_if(text, boost::algorithm::is_any_of(std::string(" ")));
 
 	// each iteration generates one output line
-	while (text.length())
+	while(text.length())
 	{
-		ui32 lineWidth = 0;    //in characters or given char metric
-		ui32 wordBreak = -1;    //last position for line break (last space character)
-		ui32 currPos = 0;       //current position in text
-		bool opened = false;    //set to true when opening brace is found
-		std::string color = "";    //color found
+		ui32 lineWidth = 0; //in characters or given char metric
+		ui32 wordBreak = -1; //last position for line break (last space character)
+		ui32 currPos = 0; //current position in text
+		bool opened = false; //set to true when opening brace is found
+		std::string color; //color found
 
 		size_t symbolSize = 0; // width of character, in bytes
 		size_t glyphWidth = 0; // width of printable glyph, pixels
 
 		// loops till line is full or end of text reached
-		while(currPos < text.length()  &&  text[currPos] != 0x0a  &&  lineWidth < maxLineWidth)
+		while(currPos < text.length() && text[currPos] != 0x0a && lineWidth < maxLineWidth)
 		{
 			symbolSize = TextOperations::getUnicodeCharacterSize(text[currPos]);
 			glyphWidth = graphics->fonts[font]->getGlyphWidth(text.data() + currPos);
 
 			// candidate for line break
-			if (ui8(text[currPos]) <= ui8(' '))
+			if(ui8(text[currPos]) <= ui8(' '))
 				wordBreak = currPos;
 
 			/* We don't count braces in string length. */
-			if (text[currPos] == '{')
+			if(text[currPos] == '{')
 			{
-				opened=true;
+				opened = true;
 
 				std::smatch match;
-   				std::regex expr("^\\{(.*?)\\|");
+				std::regex expr("^\\{(.*?)\\|");
 				std::string tmp = text.substr(currPos);
 				if(std::regex_search(tmp, match, expr))
 				{
@@ -168,23 +111,23 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWi
 					}
 				}
 			}
-			else if (text[currPos]=='}')
+			else if(text[currPos] == '}')
 			{
-				opened=false;
+				opened = false;
 				color = "";
 			}
 			else
-				lineWidth += (ui32)glyphWidth;
-			currPos += (ui32)symbolSize;
+				lineWidth += glyphWidth;
+			currPos += symbolSize;
 		}
 
 		// long line, create line break
-		if (currPos < text.length()  &&  (text[currPos] != 0x0a))
+		if(currPos < text.length() && (text[currPos] != 0x0a))
 		{
-			if (wordBreak != ui32(-1))
+			if(wordBreak != ui32(-1))
 				currPos = wordBreak;
 			else
-				currPos -= (ui32)symbolSize;
+				currPos -= symbolSize;
 		}
 
 		//non-blank line
@@ -192,7 +135,7 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWi
 		{
 			ret.push_back(text.substr(0, currPos));
 
-			if (opened)
+			if(opened)
 				/* Close the brace for the current line. */
 				ret.back() += '}';
 
@@ -203,7 +146,7 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWi
 			ret.push_back(""); //add empty string, no extra actions needed
 		}
 
-		if (text.length() != 0 && text[0] == 0x0a)
+		if(text.length() != 0 && text[0] == 0x0a)
 		{
 			/* Remove LF */
 			text.erase(0, 1);
@@ -212,19 +155,19 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWi
 		{
 			// trim only if line does not starts with LF
 			// FIXME: necessary? All lines will be trimmed before returning anyway
-			boost::algorithm::trim_left_if(text,boost::algorithm::is_any_of(std::string(" ")));
+			boost::algorithm::trim_left_if(text, boost::algorithm::is_any_of(std::string(" ")));
 		}
 
-		if (opened)
+		if(opened)
 		{
 			/* Add an opening brace for the next line. */
-			if (text.length() != 0)
+			if(text.length() != 0)
 				text.insert(0, "{" + color);
 		}
 	}
 
 	/* Trim whitespaces of every line. */
-	for (auto & elem : ret)
+	for(auto & elem : ret)
 		boost::algorithm::trim(elem);
 
 	return ret;
@@ -244,118 +187,127 @@ std::string CMessage::guessHeader(const std::string & msg)
 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<int>(f->getLineHeight());
-	return lineHeight * (int)lines.size();
+	const auto lines = CMessage::breakText(txt, width, font);
+	size_t lineHeight = f->getLineHeight();
+	return lineHeight * lines.size();
 }
 
 int CMessage::getEstimatedComponentHeight(int numComps)
 {
-	if (numComps > 8) //Bigger than 8 components - return invalid value
+	if(numComps > 8) //Bigger than 8 components - return invalid value
 		return std::numeric_limits<int>::max();
-	else if (numComps > 2)
+	if(numComps > 2)
 		return 160; // 32px * 1 row + 20 to offset
-	else if (numComps)
+	if(numComps > 0)
 		return 118; // 118 px to offset
 	return 0;
 }
 
 void CMessage::drawIWindow(CInfoWindow * ret, std::string text, PlayerColor player)
 {
-	bool blitOr = false;
-	if(dynamic_cast<CSelWindow*>(ret)) //it's selection window, so we'll blit "or" between components
-		blitOr = true;
-
-	const int sizes[][2] = {{400, 125}, {500, 150}, {600, 200}, {480, 400}};
+	// possible sizes of text boxes section of window
+	// game should pick smallest one that can fit text without slider
+	// or, if not possible - pick last one and use slider
+	constexpr std::array textAreaSizes = {
+		// FIXME: this size should only be used for single-line texts: Point(206, 72),
+		Point(270, 72),
+		Point(270, 136),
+		Point(270, 200),
+		Point(400, 136),
+		Point(400, 200),
+		Point(590, 200)
+	};
 
 	assert(ret && ret->text);
-	for(int i = 0;
-		i < std::size(sizes)
-			&& sizes[i][0] < GH.screenDimensions().x - 150
-			&& sizes[i][1] < GH.screenDimensions().y - 150
-			&& ret->text->slider;
-		i++)
-	{
-		ret->text->resize(Point(sizes[i][0], sizes[i][1]));
-	}
 
-	if(ret->text->slider)
-	{
-		ret->text->slider->addUsedEvents(CIntObject::WHEEL | CIntObject::KEYBOARD);
-	}
-	else
+	// STEP 1: DETERMINE SIZE OF ALL ELEMENTS
+
+	for(const auto & area : textAreaSizes)
 	{
-		ret->text->resize(ret->text->label->textSize + Point(10, 10));
+		ret->text->resize(area);
+		if(!ret->text->slider)
+			break; // suitable size found, use it
 	}
 
-	std::pair<int,int> winSize(ret->text->pos.w, ret->text->pos.h); //start with text size
+	int textHeight = ret->text->pos.h;
 
-	ComponentsToBlit comps(ret->components,500, blitOr);
-	if (ret->components.size())
-		winSize.second += 10 + comps.h; //space to first component
+	if(ret->text->slider)
+		ret->text->slider->addUsedEvents(CIntObject::WHEEL | CIntObject::KEYBOARD);
 
-	int bw = 0;
-	if (ret->buttons.size())
+	int buttonsWidth = 0;
+	int buttonsHeight = 0;
+	if(!ret->buttons.empty())
 	{
-		int bh = 0;
 		// Compute total width of buttons
-		bw = 20*((int)ret->buttons.size()-1); // space between all buttons
-		for(auto & elem : ret->buttons) //and add buttons width
+		buttonsWidth = INTERVAL_BETWEEN_BUTTONS * (ret->buttons.size() - 1); // space between all buttons
+		for(const auto & elem : ret->buttons) //and add buttons width
 		{
-			bw+=elem->pos.w;
-			vstd::amax(bh, elem->pos.h);
+			buttonsWidth += elem->pos.w;
+			vstd::amax(buttonsHeight, elem->pos.h);
 		}
-		winSize.second += 20 + bh;//before button + button
 	}
 
-	// Clip window size
-	vstd::amax(winSize.second, 50);
-	vstd::amax(winSize.first, 80);
-	vstd::amax(winSize.first, comps.w);
-	vstd::amax(winSize.first, bw);
-
-	vstd::amin(winSize.first, GH.screenDimensions().x - 150);
-
-	ret->bitmap = drawDialogBox (winSize.first + 2*SIDE_MARGIN, winSize.second + 2*SIDE_MARGIN, player);
-	ret->pos.h=ret->bitmap->h;
-	ret->pos.w=ret->bitmap->w;
-	ret->center();
+	// STEP 2: COMPUTE WINDOW SIZE
 
-	int curh = SIDE_MARGIN;
-	int xOffset = (ret->pos.w - ret->text->pos.w)/2;
-
-	if(!ret->buttons.size() && !ret->components.size()) //improvement for very small text only popups -> center text vertically
+	if(ret->buttons.empty() && !ret->components)
 	{
-		if(ret->bitmap->h > ret->text->pos.h + 2*SIDE_MARGIN)
-			curh = (ret->bitmap->h - ret->text->pos.h)/2;
+		// use more compact form for right-click popup with no buttons / components
+
+		ret->pos.w = std::max(RIGHT_CLICK_POPUP_MIN_SIZE, ret->text->label->textSize.x + 2 * SIDE_MARGIN);
+		ret->pos.h = std::max(RIGHT_CLICK_POPUP_MIN_SIZE, ret->text->label->textSize.y + TOP_MARGIN + BOTTOM_MARGIN);
 	}
+	else
+	{
+		int windowContentWidth = ret->text->pos.w;
+		int windowContentHeight = ret->text->pos.h;
+		if(ret->components)
+		{
+			vstd::amax(windowContentWidth, ret->components->pos.w);
+			windowContentHeight += INTERVAL_BETWEEN_TEXT_AND_BUTTONS + ret->components->pos.h;
+		}
+		if(!ret->buttons.empty())
+		{
+			vstd::amax(windowContentWidth, buttonsWidth);
+			windowContentHeight += INTERVAL_BETWEEN_TEXT_AND_BUTTONS + buttonsHeight;
+		}
 
-	ret->text->moveBy(Point(xOffset, curh));
+		ret->pos.w = windowContentWidth + 2 * SIDE_MARGIN;
+		ret->pos.h = windowContentHeight + TOP_MARGIN + BOTTOM_MARGIN;
+	}
 
-	curh += ret->text->pos.h;
+	// STEP 3: MOVE ALL ELEMENTS IN PLACE
 
-	if (ret->components.size())
+	if(ret->buttons.empty() && !ret->components)
 	{
-		curh += BEFORE_COMPONENTS;
-		comps.blitCompsOnSur (blitOr, BETWEEN_COMPS, curh, ret->bitmap);
+		ret->text->trimToFit();
+		ret->text->center(ret->pos.center());
 	}
-	if(ret->buttons.size())
+	else
 	{
-		// Position the buttons at the bottom of the window
-		bw = (ret->bitmap->w/2) - (bw/2);
-		curh = ret->bitmap->h - SIDE_MARGIN - ret->buttons[0]->pos.h;
+		if(ret->components)
+			ret->components->moveBy(Point((ret->pos.w - ret->components->pos.w) / 2, TOP_MARGIN + ret->text->pos.h + INTERVAL_BETWEEN_TEXT_AND_BUTTONS));
 
-		for(auto & elem : ret->buttons)
+		ret->text->trimToFit();
+		ret->text->moveBy(Point((ret->pos.w - ret->text->pos.w) / 2, TOP_MARGIN + (textHeight - ret->text->pos.h) / 2 ));
+
+		if(!ret->buttons.empty())
 		{
-			elem->moveBy(Point(bw, curh));
-			bw += elem->pos.w + 20;
+			int buttonPosX = ret->pos.w / 2 - buttonsWidth / 2;
+			int buttonPosY = ret->pos.h - BOTTOM_MARGIN - ret->buttons[0]->pos.h;
+
+			for(const auto & elem : ret->buttons)
+			{
+				elem->moveBy(Point(buttonPosX, buttonPosY));
+				buttonPosX += elem->pos.w + INTERVAL_BETWEEN_BUTTONS;
+			}
 		}
 	}
-	for(size_t i=0; i<ret->components.size(); i++)
-		ret->components[i]->moveBy(Point(ret->pos.x, ret->pos.y));
+
+	ret->backgroundTexture->pos = ret->pos;
+	ret->center();
 }
 
-void CMessage::drawBorder(PlayerColor playerColor, SDL_Surface * ret, int w, int h, int x, int y)
+void CMessage::drawBorder(PlayerColor playerColor, Canvas & to, int w, int h, int x, int y)
 {
 	if(playerColor.isSpectator())
 		playerColor = PlayerColor(1);
@@ -366,188 +318,36 @@ void CMessage::drawBorder(PlayerColor playerColor, SDL_Surface * ret, int w, int
 	// Horizontal borders
 	int start_x = x + box[0]->width();
 	const int stop_x = x + w - box[1]->width();
-	const int bottom_y = y+h-box[7]->height()+1;
-	while (start_x < stop_x) {
-		int cur_w = stop_x - start_x;
-		if (cur_w > box[6]->width())
-			cur_w = box[6]->width();
+	const int bottom_y = y + h - box[7]->height() + 1;
+	while(start_x < stop_x)
+	{
 
 		// Top border
-		Rect srcR(0, 0, cur_w, box[6]->height());
-		Rect dstR(start_x, y, 0, 0);
-		box[6]->draw(ret, &dstR, &srcR);
-
+		to.draw(box[6], Point(start_x, y));
 		// Bottom border
-		dstR.y = bottom_y;
-		box[7]->draw(ret, &dstR, &srcR);
+		to.draw(box[7], Point(start_x, bottom_y));
 
-		start_x += cur_w;
+		start_x += box[6]->width();
 	}
 
 	// Vertical borders
 	int start_y = y + box[0]->height();
-	const int stop_y = y + h - box[2]->height()+1;
-	const int right_x = x+w-box[5]->width();
-	while (start_y < stop_y) {
-		int cur_h = stop_y - start_y;
-		if (cur_h > box[4]->height())
-			cur_h = box[4]->height();
+	const int stop_y = y + h - box[2]->height() + 1;
+	const int right_x = x + w - box[5]->width();
+	while(start_y < stop_y)
+	{
 
 		// Left border
-		Rect srcR(0, 0, box[4]->width(), cur_h);
-		Rect dstR(x, start_y, 0, 0);
-		box[4]->draw(ret, &dstR, &srcR);
-
+		to.draw(box[4], Point(x, start_y));
 		// Right border
-		dstR.x = right_x;
-		box[5]->draw(ret, &dstR, &srcR);
+		to.draw(box[5], Point(right_x, start_y));
 
-		start_y += cur_h;
+		start_y += box[4]->height();
 	}
 
 	//corners
-	Rect dstR(x, y, box[0]->width(), box[0]->height());
-	box[0]->draw(ret, &dstR, nullptr);
-
-	dstR=Rect(x+w-box[1]->width(), y,   box[1]->width(), box[1]->height());
-	box[1]->draw(ret, &dstR, nullptr);
-
-	dstR=Rect(x, y+h-box[2]->height()+1, box[2]->width(), box[2]->height());
-	box[2]->draw(ret, &dstR, nullptr);
-
-	dstR=Rect(x+w-box[3]->width(), y+h-box[3]->height()+1, box[3]->width(), box[3]->height());
-	box[3]->draw(ret, &dstR, nullptr);
-}
-
-ComponentResolved::ComponentResolved(std::shared_ptr<CComponent> Comp):
-	comp(Comp)
-{
-	//Temporary assign ownership on comp
-	if (parent)
-		parent->removeChild(this);
-	if (comp->parent)
-	{
-		comp->parent->addChild(this);
-		comp->parent->removeChild(comp.get());
-	}
-
-	addChild(comp.get());
-	defActions = 255 - DISPOSE;
-	pos.x = pos.y = 0;
-
-	pos.w = comp->pos.w;
-	pos.h = comp->pos.h;
-}
-
-ComponentResolved::~ComponentResolved()
-{
-	if (parent)
-	{
-		removeChild(comp.get());
-		parent->addChild(comp.get());
-	}
-}
-
-void ComponentResolved::showAll(Canvas & to)
-{
-	CIntObject::showAll(to);
-	comp->showAll(to);
-}
-
-ComponentsToBlit::~ComponentsToBlit() = default;
-
-ComponentsToBlit::ComponentsToBlit(std::vector<std::shared_ptr<CComponent>> & SComps, int maxw, bool blitOr)
-{
-	int orWidth = static_cast<int>(graphics->fonts[FONT_MEDIUM]->getStringWidth(CGI->generaltexth->allTexts[4]));
-
-	w = h = 0;
-	if(SComps.empty())
-		return;
-
-	comps.resize(1);
-	int curw = 0;
-	int curr = 0; //current row
-
-	for(auto & SComp : SComps)
-	{
-		auto cur = std::make_shared<ComponentResolved>(SComp);
-
-		int toadd = (cur->pos.w + BETWEEN_COMPS + (blitOr ? orWidth : 0));
-		if (curw + toadd > maxw)
-		{
-			curr++;
-			vstd::amax(w,curw);
-			curw = cur->pos.w;
-			comps.resize(curr+1);
-		}
-		else
-		{
-			curw += toadd;
-			vstd::amax(w,curw);
-		}
-
-		comps[curr].push_back(cur);
-	}
-
-	for(auto & elem : comps)
-	{
-		int maxHeight = 0;
-		for(size_t j=0;j<elem.size();j++)
-			vstd::amax(maxHeight, elem[j]->pos.h);
-
-		h += maxHeight + BETWEEN_COMPS_ROWS;
-	}
-}
-
-void ComponentsToBlit::blitCompsOnSur( bool blitOr, int inter, int &curh, SDL_Surface *ret )
-{
-	int orWidth = static_cast<int>(graphics->fonts[FONT_MEDIUM]->getStringWidth(CGI->generaltexth->allTexts[4]));
-
-	for (auto & elem : comps)//for each row
-	{
-		int totalw=0, maxHeight=0;
-		for(size_t j=0;j<elem.size();j++)//find max height & total width in this row
-		{
-			auto cur = elem[j];
-			totalw += cur->pos.w;
-			vstd::amax(maxHeight, cur->pos.h);
-		}
-
-		//add space between comps in this row
-		if(blitOr)
-			totalw += (inter*2+orWidth) * ((int)elem.size() - 1);
-		else
-			totalw += (inter) * ((int)elem.size() - 1);
-
-		int middleh = curh + maxHeight/2;//axis for image aligment
-		int curw = ret->w/2 - totalw/2;
-
-		for(size_t j=0;j<elem.size();j++)
-		{
-			auto cur = elem[j];
-			cur->moveTo(Point(curw, curh));
-
-			//blit component
-			Canvas canvas = Canvas::createFromSurface(ret);
-
-			cur->showAll(canvas);
-			curw += cur->pos.w;
-
-			//if there is subsequent component blit "or"
-			if(j<(elem.size()-1))
-			{
-				if(blitOr)
-				{
-					curw+=inter;
-
-					graphics->fonts[FONT_MEDIUM]->renderTextLeft(ret, CGI->generaltexth->allTexts[4], Colors::WHITE,
-							Point(curw,middleh-((int)graphics->fonts[FONT_MEDIUM]->getLineHeight()/2)));
-
-					curw+=orWidth;
-				}
-				curw+=inter;
-			}
-		}
-		curh += maxHeight + BETWEEN_COMPS_ROWS;
-	}
+	to.draw(box[0], Point(x, y));
+	to.draw(box[1], Point(x + w - box[1]->width(), y));
+	to.draw(box[2], Point(x, y + h - box[2]->height() + 1));
+	to.draw(box[3], Point(x + w - box[3]->width(), y + h - box[3]->height() + 1));
 }

+ 2 - 2
client/windows/CMessage.h

@@ -15,12 +15,12 @@
 struct SDL_Surface;
 class CInfoWindow;
 class CComponent;
+class Canvas;
 
 VCMI_LIB_NAMESPACE_BEGIN
 class ColorRGBA;
 VCMI_LIB_NAMESPACE_END
 
-
 /// Class which draws formatted text messages and generates chat windows
 class CMessage
 {
@@ -29,7 +29,7 @@ class CMessage
 
 public:
 	/// Draw border on exiting surface
-	static void drawBorder(PlayerColor playerColor, SDL_Surface * ret, int w, int h, int x=0, int y=0);
+	static void drawBorder(PlayerColor playerColor, Canvas & to, int w, int h, int x, int y);
 
 	static void drawIWindow(CInfoWindow * ret, std::string text, PlayerColor player);
 

+ 1 - 1
client/windows/CWindowObject.cpp

@@ -232,7 +232,7 @@ void CWindowObject::showAll(Canvas & to)
 
 	CIntObject::showAll(to);
 	if ((options & BORDERED) && (pos.dimensions() != GH.screenDimensions()))
-		CMessage::drawBorder(color, to.getInternalSurface(), pos.w+28, pos.h+29, pos.x-14, pos.y-15);
+		CMessage::drawBorder(color, to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
 }
 
 bool CWindowObject::isPopupWindow() const

+ 72 - 200
client/windows/InfoWindows.cpp

@@ -11,83 +11,51 @@
 #include "InfoWindows.h"
 
 #include "../CGameInfo.h"
-#include "../PlayerLocalState.h"
 #include "../CPlayerInterface.h"
-#include "../CMusicHandler.h"
+#include "../PlayerLocalState.h"
 
+#include "../adventureMap/AdventureMapInterface.h"
+#include "../gui/CGuiHandler.h"
+#include "../gui/CursorHandler.h"
+#include "../gui/Shortcut.h"
+#include "../gui/WindowHandler.h"
+#include "../widgets/Buttons.h"
 #include "../widgets/CComponent.h"
+#include "../widgets/Images.h"
 #include "../widgets/MiscWidgets.h"
-#include "../widgets/Buttons.h"
 #include "../widgets/TextControls.h"
-#include "../gui/CGuiHandler.h"
-#include "../gui/WindowHandler.h"
-#include "../battle/BattleInterface.h"
-#include "../battle/BattleInterfaceClasses.h"
-#include "../adventureMap/AdventureMapInterface.h"
 #include "../windows/CMessage.h"
-#include "../render/Canvas.h"
-#include "../renderSDL/SDL_Extensions.h"
-#include "../gui/CursorHandler.h"
-#include "../gui/Shortcut.h"
 
 #include "../../CCallback.h"
 
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CondSh.h"
-#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
+#include "../../lib/gameState/InfoAboutArmy.h"
 #include "../../lib/mapObjects/CGCreature.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/mapObjects/MiscObjects.h"
-#include "../../lib/gameState/InfoAboutArmy.h"
-
-#include <SDL_surface.h>
 
-void CSimpleWindow::show(Canvas & to)
+CSelWindow::CSelWindow( const std::string & Text, PlayerColor player, int charperline, const std::vector<std::shared_ptr<CSelectableComponent>> & comps, const std::vector<std::pair<AnimationPath, CFunctionList<void()>>> & Buttons, QueryID askID)
 {
-	if(bitmap)
-		CSDL_Ext::blitAt(bitmap, pos.x, pos.y, to.getInternalSurface());
-}
-CSimpleWindow::~CSimpleWindow()
-{
-	if (bitmap)
-	{
-		SDL_FreeSurface(bitmap);
-		bitmap=nullptr;
-	}
-}
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
 
-void CSelWindow::selectionChange(unsigned to)
-{
-	for (unsigned i=0;i<components.size();i++)
-	{
-		auto pom = std::dynamic_pointer_cast<CSelectableComponent>(components[i]);
-		if (!pom)
-			continue;
-		pom->select(i==to);
-	}
-	redraw();
-}
+	backgroundTexture = std::make_shared<CFilledTexture>(ImagePath::builtin("DiBoxBck"), pos);
 
-CSelWindow::CSelWindow(const std::string &Text, PlayerColor player, int charperline, const std::vector<std::shared_ptr<CSelectableComponent>> & comps, const std::vector<std::pair<AnimationPath, CFunctionList<void()> > > &Buttons, QueryID askID)
-{
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	ID = askID;
-	for (int i = 0; i < Buttons.size(); i++)
+	for(int i = 0; i < Buttons.size(); i++)
 	{
 		buttons.push_back(std::make_shared<CButton>(Point(0, 0), Buttons[i].first, CButton::tooltip(), Buttons[i].second));
-		if (!i  &&  askID.getNum() >= 0)
+		if(!i && askID.getNum() >= 0)
 			buttons.back()->addCallback(std::bind(&CSelWindow::madeChoice, this));
 		buttons[i]->addCallback(std::bind(&CInfoWindow::close, this)); //each button will close the window apart from call-defined actions
 	}
 
 	text = std::make_shared<CTextBox>(Text, Rect(0, 0, 250, 100), 0, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE);
 
-	if (buttons.size() > 1 && askID.getNum() >= 0) //cancel button functionality
+	if(buttons.size() > 1 && askID.getNum() >= 0) //cancel button functionality
 	{
-		buttons.back()->addCallback([askID]() {
-			LOCPLINT->cb.get()->selectionMade(0, askID);
-		});
+		buttons.back()->addCallback([askID](){LOCPLINT->cb->selectionMade(0, askID);});
 		//buttons.back()->addCallback(std::bind(&CCallback::selectionMade, LOCPLINT->cb.get(), 0, askID));
 	}
 
@@ -100,16 +68,9 @@ CSelWindow::CSelWindow(const std::string &Text, PlayerColor player, int charperl
 		buttons.back()->assignedKey = EShortcut::GLOBAL_CANCEL;
 	}
 
-	for(int i=0;i<comps.size();i++)
-	{
-		comps[i]->recActions = 255-DISPOSE;
-		addChild(comps[i].get());
-		components.push_back(comps[i]);
-		comps[i]->onSelect = std::bind(&CSelWindow::selectionChange,this,i);
-		comps[i]->onChoose = std::bind(&CSelWindow::madeChoiceAndClose,this);
-		if(i<8)
-			comps[i]->assignedKey = vstd::next(EShortcut::SELECT_INDEX_1,i);
-	}
+	if(!comps.empty())
+		components = std::make_shared<CComponentBox>(comps, Rect(0,0,0,0));
+
 	CMessage::drawIWindow(this, Text, player);
 }
 
@@ -118,14 +79,10 @@ void CSelWindow::madeChoice()
 	if(ID.getNum() < 0)
 		return;
 	int ret = -1;
-	for (int i=0;i<components.size();i++)
-	{
-		if(std::dynamic_pointer_cast<CSelectableComponent>(components[i])->selected)
-		{
-			ret = i;
-		}
-	}
-	LOCPLINT->cb->selectionMade(ret+1,ID);
+	if(components)
+		ret = components->selectedIndex();
+
+	LOCPLINT->cb->selectionMade(ret + 1, ID);
 }
 
 void CSelWindow::madeChoiceAndClose()
@@ -134,14 +91,16 @@ void CSelWindow::madeChoiceAndClose()
 	close();
 }
 
-CInfoWindow::CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo & comps, const TButtonsInfo & Buttons)
+CInfoWindow::CInfoWindow(const std::string & Text, PlayerColor player, const TCompsInfo & comps, const TButtonsInfo & Buttons)
 {
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
+
+	backgroundTexture = std::make_shared<CFilledTexture>(ImagePath::builtin("DiBoxBck"), pos);
 
 	ID = QueryID(-1);
-	for(auto & Button : Buttons)
+	for(const auto & Button : Buttons)
 	{
-		std::shared_ptr<CButton> button = std::make_shared<CButton>(Point(0,0), Button.first, CButton::tooltip(), std::bind(&CInfoWindow::close, this));
+		auto button = std::make_shared<CButton>(Point(0, 0), Button.first, CButton::tooltip(), std::bind(&CInfoWindow::close, this));
 		button->setBorderColor(Colors::METALLIC_GOLD);
 		button->addCallback(Button.second); //each button will close the window apart from call-defined actions
 		buttons.push_back(button);
@@ -164,15 +123,10 @@ CInfoWindow::CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo
 		buttons.back()->assignedKey = EShortcut::GLOBAL_CANCEL;
 	}
 
-	for(auto & comp : comps)
-	{
-		comp->recActions = 0xff & ~DISPOSE;
-		addChild(comp.get());
-		comp->recActions &= ~(SHOWALL | UPDATE);
-		components.push_back(comp);
-	}
+	if(!comps.empty())
+		components = std::make_shared<CComponentBox>(comps, Rect(0,0,0,0));
 
-	CMessage::drawIWindow(this,Text,player);
+	CMessage::drawIWindow(this, Text, player);
 }
 
 CInfoWindow::CInfoWindow()
@@ -188,128 +142,45 @@ void CInfoWindow::close()
 		LOCPLINT->showingDialog->setn(false);
 }
 
-void CInfoWindow::show(Canvas & to)
-{
-	CIntObject::show(to);
-}
-
-CInfoWindow::~CInfoWindow() = default;
-
 void CInfoWindow::showAll(Canvas & to)
 {
-	CSimpleWindow::show(to);
 	CIntObject::showAll(to);
+	CMessage::drawBorder(LOCPLINT ? LOCPLINT->playerID : PlayerColor(1), to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
 }
 
-void CInfoWindow::showInfoDialog(const std::string &text, const TCompsInfo & components, PlayerColor player)
+CInfoWindow::~CInfoWindow() = default;
+
+void CInfoWindow::showInfoDialog(const std::string & text, const TCompsInfo & components, PlayerColor player)
 {
 	GH.windows().pushWindow(CInfoWindow::create(text, player, components));
 }
 
-void CInfoWindow::showYesNoDialog(const std::string & text, const TCompsInfo & components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, PlayerColor player)
+void CInfoWindow::showYesNoDialog(const std::string & text, const TCompsInfo & components, const CFunctionList<void()> & onYes, const CFunctionList<void()> & onNo, PlayerColor player)
 {
 	assert(!LOCPLINT || LOCPLINT->showingDialog->get());
-	std::vector<std::pair<AnimationPath,CFunctionList<void()> > > pom;
-	pom.push_back( { AnimationPath::builtin("IOKAY.DEF"), 0 });
-	pom.push_back( { AnimationPath::builtin("ICANCEL.DEF"), 0 });
-	std::shared_ptr<CInfoWindow> temp =  std::make_shared<CInfoWindow>(text, player, components, pom);
+	std::vector<std::pair<AnimationPath, CFunctionList<void()>>> pom;
+	pom.emplace_back(AnimationPath::builtin("IOKAY.DEF"), nullptr);
+	pom.emplace_back(AnimationPath::builtin("ICANCEL.DEF"), nullptr);
+	auto temp = std::make_shared<CInfoWindow>(text, player, components, pom);
 
-	temp->buttons[0]->addCallback( onYes );
-	temp->buttons[1]->addCallback( onNo );
+	temp->buttons[0]->addCallback(onYes);
+	temp->buttons[1]->addCallback(onNo);
 
 	GH.windows().pushWindow(temp);
 }
 
-std::shared_ptr<CInfoWindow> CInfoWindow::create(const std::string &text, PlayerColor playerID, const TCompsInfo & components)
+std::shared_ptr<CInfoWindow> CInfoWindow::create(const std::string & text, PlayerColor playerID, const TCompsInfo & components)
 {
-	std::vector<std::pair<AnimationPath,CFunctionList<void()> > > pom;
-	pom.push_back({AnimationPath::builtin("IOKAY.DEF"), 0});
+	std::vector<std::pair<AnimationPath, CFunctionList<void()>>> pom;
+	pom.emplace_back(AnimationPath::builtin("IOKAY.DEF"), nullptr);
 	return std::make_shared<CInfoWindow>(text, playerID, components, pom);
 }
 
-std::string CInfoWindow::genText(std::string title, std::string description)
+std::string CInfoWindow::genText(const std::string & title, const std::string & description)
 {
 	return std::string("{") + title + "}" + "\n\n" + description;
 }
 
-CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free)
- :free(Free),bitmap(Bitmap)
-{
-	init(x, y);
-}
-
-
-CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, const Point &p, ETextAlignment alignment, bool Free)
- : free(Free),bitmap(Bitmap)
-{
-	switch(alignment)
-	{
-	case ETextAlignment::BOTTOMRIGHT:
-		init(p.x - Bitmap->w, p.y - Bitmap->h);
-		break;
-	case ETextAlignment::CENTER:
-		init(p.x - Bitmap->w/2, p.y - Bitmap->h/2);
-		break;
-	case ETextAlignment::TOPLEFT:
-		init(p.x, p.y);
-		break;
-	case ETextAlignment::TOPCENTER:
-		init(p.x - Bitmap->w/2, p.y);
-		break;
-	default:
-		assert(0); //not implemented
-	}
-}
-
-CInfoPopup::CInfoPopup(SDL_Surface *Bitmap, bool Free)
-{
-	CCS->curh->hide();
-
-	free=Free;
-	bitmap=Bitmap;
-
-	if(bitmap)
-	{
-		pos.x = GH.screenDimensions().x / 2 - bitmap->w / 2;
-		pos.y = GH.screenDimensions().y / 2 - bitmap->h / 2;
-		pos.h = bitmap->h;
-		pos.w = bitmap->w;
-	}
-}
-
-void CInfoPopup::close()
-{
-	if(free)
-		SDL_FreeSurface(bitmap);
-	WindowBase::close();
-}
-
-void CInfoPopup::show(Canvas & to)
-{
-	CSDL_Ext::blitAt(bitmap,pos.x,pos.y,to.getInternalSurface());
-}
-
-CInfoPopup::~CInfoPopup()
-{
-	CCS->curh->show();
-}
-
-void CInfoPopup::init(int x, int y)
-{
-	CCS->curh->hide();
-
-	pos.x = x;
-	pos.y = y;
-	pos.h = bitmap->h;
-	pos.w = bitmap->w;
-
-	// Put the window back on screen if necessary
-	vstd::amax(pos.x, 0);
-	vstd::amax(pos.y, 0);
-	vstd::amin(pos.x, GH.screenDimensions().x - bitmap->w);
-	vstd::amin(pos.y, GH.screenDimensions().y - bitmap->h);
-}
-
 bool CRClickPopup::isPopupWindow() const
 {
 	return true;
@@ -320,10 +191,10 @@ void CRClickPopup::close()
 	WindowBase::close();
 }
 
-void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps)
+void CRClickPopup::createAndPush(const std::string & txt, const CInfoWindow::TCompsInfo & comps)
 {
 	PlayerColor player = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1); //if no player, then use blue
-	if(settings["session"]["spectate"].Bool())//TODO: there must be better way to implement this
+	if(settings["session"]["spectate"].Bool()) //TODO: there must be better way to implement this
 		player = PlayerColor(1);
 
 	auto temp = std::make_shared<CInfoWindow>(txt, player, comps);
@@ -336,7 +207,7 @@ void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCom
 	GH.windows().createAndPushWindow<CRClickPopupInt>(temp);
 }
 
-void CRClickPopup::createAndPush(const std::string & txt, std::shared_ptr<CComponent> component)
+void CRClickPopup::createAndPush(const std::string & txt, const std::shared_ptr<CComponent> & component)
 {
 	CInfoWindow::TCompsInfo intComps;
 	intComps.push_back(component);
@@ -354,7 +225,7 @@ void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p,
 	else
 	{
 		std::vector<Component> components;
-		if (settings["general"]["enableUiEnhancements"].Bool())
+		if(settings["general"]["enableUiEnhancements"].Bool())
 		{
 			if(LOCPLINT->localState->getCurrentHero())
 				components = obj->getPopupComponents(LOCPLINT->localState->getCurrentHero());
@@ -363,7 +234,7 @@ void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p,
 		}
 
 		std::vector<std::shared_ptr<CComponent>> guiComponents;
-		for (auto & component : components)
+		for(auto & component : components)
 			guiComponents.push_back(std::make_shared<CComponent>(component));
 
 		if(LOCPLINT->localState->getCurrentHero())
@@ -373,7 +244,7 @@ void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p,
 	}
 }
 
-CRClickPopupInt::CRClickPopupInt(std::shared_ptr<CIntObject> our)
+CRClickPopupInt::CRClickPopupInt(const std::shared_ptr<CIntObject> & our)
 {
 	CCS->curh->hide();
 	defActions = SHOWALL | UPDATE;
@@ -403,7 +274,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGTownInstance * town)
 	InfoAboutTown iah;
 	LOCPLINT->cb->getTownInfo(town, iah, LOCPLINT->localState->getCurrentTown()); //todo: should this be nearest hero?
 
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
 	tooltip = std::make_shared<CTownTooltip>(Point(9, 10), iah);
 }
 
@@ -411,9 +282,9 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGHeroInstance * hero)
 	: CWindowObject(RCLICK_POPUP | PLAYER_COLORED, ImagePath::builtin("HEROQVBK"), toScreen(position))
 {
 	InfoAboutHero iah;
-	LOCPLINT->cb->getHeroInfo(hero, iah, LOCPLINT->localState->getCurrentHero());//todo: should this be nearest hero?
+	LOCPLINT->cb->getHeroInfo(hero, iah, LOCPLINT->localState->getCurrentHero()); //todo: should this be nearest hero?
 
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
 	tooltip = std::make_shared<CHeroTooltip>(Point(9, 10), iah);
 }
 
@@ -423,18 +294,19 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr)
 	InfoAboutTown iah;
 	LOCPLINT->cb->getTownInfo(garr, iah);
 
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
 	tooltip = std::make_shared<CArmyTooltip>(Point(9, 10), iah);
 }
 
 CInfoBoxPopup::CInfoBoxPopup(Point position, const CGCreature * creature)
-		: CWindowObject(RCLICK_POPUP | BORDERED, ImagePath::builtin("DIBOXBCK"), toScreen(position))
+	: CWindowObject(RCLICK_POPUP | BORDERED, ImagePath::builtin("DIBOXBCK"), toScreen(position))
 {
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
 	tooltip = std::make_shared<CreatureTooltip>(Point(9, 10), creature);
 }
 
-std::shared_ptr<WindowBase> CRClickPopup::createCustomInfoWindow(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
+std::shared_ptr<WindowBase>
+CRClickPopup::createCustomInfoWindow(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
 {
 	if(nullptr == specific)
 		specific = LOCPLINT->localState->getCurrentArmy();
@@ -447,16 +319,16 @@ std::shared_ptr<WindowBase> CRClickPopup::createCustomInfoWindow(Point position,
 
 	switch(specific->ID)
 	{
-	case Obj::HERO:
-		return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGHeroInstance *>(specific));
-	case Obj::TOWN:
-		return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGTownInstance *>(specific));
-	case Obj::MONSTER:
-		return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGCreature *>(specific));
-	case Obj::GARRISON:
-	case Obj::GARRISON2:
-		return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGGarrison *>(specific));
-	default:
-		return std::shared_ptr<WindowBase>();
+		case Obj::HERO:
+			return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGHeroInstance *>(specific));
+		case Obj::TOWN:
+			return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGTownInstance *>(specific));
+		case Obj::MONSTER:
+			return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGCreature *>(specific));
+		case Obj::GARRISON:
+		case Obj::GARRISON2:
+			return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGGarrison *>(specific));
+		default:
+			return std::shared_ptr<WindowBase>();
 	}
 }

+ 15 - 46
client/windows/InfoWindows.h

@@ -20,59 +20,44 @@ class CGTownInstance;
 class CGHeroInstance;
 class CGGarrison;
 class CGCreature;
-class Rect;
 
 VCMI_LIB_NAMESPACE_END
 
-struct SDL_Surface;
-class CAnimImage;
-class CLabel;
-class CAnimation;
 class CComponent;
+class CComponentBox;
 class CSelectableComponent;
 class CTextBox;
 class CButton;
-class CSlider;
-class CArmyTooltip;
-
-// Window GUI class
-class CSimpleWindow : public WindowBase
-{
-public:
-	SDL_Surface * bitmap; //background
-	void show(Canvas & to) override;
-	CSimpleWindow():bitmap(nullptr){};
-	virtual ~CSimpleWindow();
-};
+class CFilledTexture;
 
 /// text + comp. + ok button
-class CInfoWindow : public CSimpleWindow
+class CInfoWindow : public WindowBase
 {
 public:
 	using TButtonsInfo = std::vector<std::pair<AnimationPath, CFunctionList<void()>>>;
 	using TCompsInfo = std::vector<std::shared_ptr<CComponent>>;
 	QueryID ID; //for identification
+	std::shared_ptr<CFilledTexture> backgroundTexture;
 	std::shared_ptr<CTextBox> text;
+	std::shared_ptr<CComponentBox> components;
 	std::vector<std::shared_ptr<CButton>> buttons;
-	TCompsInfo components;
 
 	void close() override;
-
-	void show(Canvas & to) override;
 	void showAll(Canvas & to) override;
+
 	void sliderMoved(int to);
 
-	CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo & comps = TCompsInfo(), const TButtonsInfo & Buttons = TButtonsInfo());
+	CInfoWindow(const std::string & Text, PlayerColor player, const TCompsInfo & comps = TCompsInfo(), const TButtonsInfo & Buttons = TButtonsInfo());
 	CInfoWindow();
 	~CInfoWindow();
 
 	//use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
-	static void showInfoDialog( const std::string & text, const TCompsInfo & components, PlayerColor player = PlayerColor(1));
-	static void showYesNoDialog( const std::string & text, const TCompsInfo & components, const CFunctionList<void()> & onYes, const CFunctionList<void()> & onNo, PlayerColor player = PlayerColor(1));
+	static void showInfoDialog(const std::string & text, const TCompsInfo & components, PlayerColor player = PlayerColor(1));
+	static void showYesNoDialog(const std::string & text, const TCompsInfo & components, const CFunctionList<void()> & onYes, const CFunctionList<void()> & onNo, PlayerColor player = PlayerColor(1));
 	static std::shared_ptr<CInfoWindow> create(const std::string & text, PlayerColor playerID = PlayerColor(1), const TCompsInfo & components = TCompsInfo());
 
 	/// create text from title and description: {title}\n\n description
-	static std::string genText(std::string title, std::string description);
+	static std::string genText(const std::string & title, const std::string & description);
 };
 
 /// popup displayed on R-click
@@ -83,8 +68,8 @@ public:
 	bool isPopupWindow() const override;
 
 	static std::shared_ptr<WindowBase> createCustomInfoWindow(Point position, const CGObjectInstance * specific);
-	static void createAndPush(const std::string & txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo());
-	static void createAndPush(const std::string & txt, std::shared_ptr<CComponent> component);
+	static void createAndPush(const std::string & txt, const CInfoWindow::TCompsInfo & comps = CInfoWindow::TCompsInfo());
+	static void createAndPush(const std::string & txt, const std::shared_ptr<CComponent> & component);
 	static void createAndPush(const CGObjectInstance * obj, const Point & p, ETextAlignment alignment = ETextAlignment::BOTTOMRIGHT);
 };
 
@@ -92,24 +77,10 @@ public:
 class CRClickPopupInt : public CRClickPopup
 {
 	std::shared_ptr<CIntObject> inner;
-public:
-	CRClickPopupInt(std::shared_ptr<CIntObject> our);
-	virtual ~CRClickPopupInt();
-};
 
-class CInfoPopup : public CRClickPopup
-{
 public:
-	bool free; //TODO: comment me
-	SDL_Surface * bitmap; //popup background
-	void close() override;
-	void show(Canvas & to) override;
-	CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free=false);
-	CInfoPopup(SDL_Surface * Bitmap, const Point &p, ETextAlignment alignment, bool Free=false);
-	CInfoPopup(SDL_Surface * Bitmap = nullptr, bool Free = false);
-
-	void init(int x, int y);
-	~CInfoPopup();
+	CRClickPopupInt(const std::shared_ptr<CIntObject> & our);
+	~CRClickPopupInt();
 };
 
 /// popup on adventure map for town\hero and other objects with customized popup content
@@ -117,6 +88,7 @@ class CInfoBoxPopup : public CWindowObject
 {
 	std::shared_ptr<CIntObject> tooltip;
 	Point toScreen(Point pos);
+
 public:
 	CInfoBoxPopup(Point position, const CGTownInstance * town);
 	CInfoBoxPopup(Point position, const CGHeroInstance * hero);
@@ -128,10 +100,7 @@ public:
 class CSelWindow : public CInfoWindow
 {
 public:
-	void selectionChange(unsigned to);
 	void madeChoice(); //looks for selected component and calls callback
 	void madeChoiceAndClose();
 	CSelWindow(const std::string & text, PlayerColor player, int charperline, const std::vector<std::shared_ptr<CSelectableComponent>> & comps, const std::vector<std::pair<AnimationPath,CFunctionList<void()> > > &Buttons, QueryID askID);
-
-	//notification - this class inherits important destructor from CInfoWindow
 };

+ 1 - 1
client/windows/settings/SettingsMainWindow.cpp

@@ -185,7 +185,7 @@ void SettingsMainWindow::showAll(Canvas & to)
 		color = PlayerColor(1); // TODO: Spectator shouldn't need special code for UI colors
 
 	CIntObject::showAll(to);
-	CMessage::drawBorder(color, to.getInternalSurface(), pos.w+28, pos.h+29, pos.x-14, pos.y-15);
+	CMessage::drawBorder(color, to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
 }
 
 void SettingsMainWindow::onScreenResize()