Browse Source

Merge pull request #2813 from Laserlicht/textcolor

Textcolor support
Nordsoft91 2 years ago
parent
commit
717a765123

+ 2 - 0
Global.h

@@ -122,6 +122,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #include <optional>
 #include <queue>
 #include <random>
+#include <regex>
 #include <set>
 #include <sstream>
 #include <string>
@@ -147,6 +148,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #define BOOST_ASIO_ENABLE_OLD_SERVICES
 #endif
 
+#include <boost/algorithm/hex.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/crc.hpp>
 #include <boost/current_function.hpp>

+ 27 - 0
client/render/Colors.cpp

@@ -10,6 +10,7 @@
 
 #include "StdInc.h"
 #include "Colors.h"
+#include "../../lib/JsonNode.h"
 
 const ColorRGBA Colors::YELLOW = { 229, 215, 123, ColorRGBA::ALPHA_OPAQUE };
 const ColorRGBA Colors::WHITE = { 255, 243, 222, ColorRGBA::ALPHA_OPAQUE };
@@ -23,3 +24,29 @@ const ColorRGBA Colors::RED = {255, 0, 0, ColorRGBA::ALPHA_OPAQUE};
 const ColorRGBA Colors::PURPLE = {255, 75, 125, ColorRGBA::ALPHA_OPAQUE};
 const ColorRGBA Colors::BLACK = {0, 0, 0, ColorRGBA::ALPHA_OPAQUE};
 const ColorRGBA Colors::TRANSPARENCY = {0, 0, 0, ColorRGBA::ALPHA_TRANSPARENT};
+
+std::optional<ColorRGBA> Colors::parseColor(std::string text)
+{
+	std::smatch match;
+	std::regex expr("^#([0-9a-fA-F]{6})$");
+	ui8 rgb[3] = {0, 0, 0};
+	if(std::regex_search(text, match, expr))
+	{
+		std::string tmp = boost::algorithm::unhex(match[1].str()); 
+		std::copy(tmp.begin(), tmp.end(), rgb);
+		return ColorRGBA(rgb[0], rgb[1], rgb[2]);
+	}
+
+	static const JsonNode config(JsonPath::builtin("CONFIG/textColors"));
+	auto colors = config["colors"].Struct();
+	for(auto & color : colors) {
+		if(boost::algorithm::to_lower_copy(color.first) == boost::algorithm::to_lower_copy(text))
+		{
+			std::string tmp = boost::algorithm::unhex(color.second.String()); 
+			std::copy(tmp.begin(), tmp.end(), rgb);
+			return ColorRGBA(rgb[0], rgb[1], rgb[2]);
+		}
+	}
+
+	return std::nullopt;
+}

+ 3 - 0
client/render/Colors.h

@@ -49,4 +49,7 @@ public:
 	static const ColorRGBA BLACK;
 
 	static const ColorRGBA TRANSPARENCY;
+
+	/// parse color
+	static std::optional<ColorRGBA> parseColor(std::string text);
 };

+ 30 - 2
client/widgets/TextControls.cpp

@@ -153,6 +153,17 @@ void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what)
 	//We should count delimiters length from string to correct centering later.
 	delimitersCount *= f->getStringWidth(delimeters)/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 colorText = match[1].str();
+		if(auto c = Colors::parseColor(colorText))
+			delimitersCount += f->getStringWidth(colorText + "|");
+		searchStart = match.suffix().first;
+	}
+
 	// input is rect in which given text should be placed
 	// calculate proper position for top-left corner of the text
 	if(alignment == ETextAlignment::TOPLEFT)
@@ -189,8 +200,25 @@ void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what)
 		{
 			std::string toPrint = what.substr(begin, end - begin);
 
-			if(currDelimeter % 2) // Enclosed in {} text - set to yellow
-				to.drawText(where, font, Colors::YELLOW, ETextAlignment::TOPLEFT, toPrint);
+			if(currDelimeter % 2) // Enclosed in {} text - set to yellow or defined color
+			{
+				std::smatch match;
+   				std::regex expr("^(.*?)\\|");
+				if(std::regex_search(toPrint, match, expr))
+				{
+					std::string colorText = match[1].str();
+					
+					if(auto color = Colors::parseColor(colorText))
+					{
+						toPrint = toPrint.substr(colorText.length() + 1, toPrint.length() - colorText.length());
+						to.drawText(where, font, *color, ETextAlignment::TOPLEFT, toPrint);
+					}
+					else
+						to.drawText(where, font, Colors::YELLOW, ETextAlignment::TOPLEFT, toPrint);
+				}
+				else
+					to.drawText(where, font, Colors::YELLOW, ETextAlignment::TOPLEFT, toPrint);
+			}
 			else // Non-enclosed text, use default color
 				to.drawText(where, font, color, ETextAlignment::TOPLEFT, toPrint);
 

+ 20 - 1
client/windows/CMessage.cpp

@@ -131,6 +131,7 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWi
 		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
@@ -147,9 +148,27 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWi
 
 			/* We don't count braces in string length. */
 			if (text[currPos] == '{')
+			{
 				opened=true;
+
+				std::smatch match;
+   				std::regex expr("^\\{(.*?)\\|");
+				std::string tmp = text.substr(currPos);
+				if(std::regex_search(tmp, match, expr))
+				{
+					std::string colorText = match[1].str();
+					if(auto c = Colors::parseColor(colorText))
+					{
+						color = colorText + "|";
+						currPos += colorText.length() + 1;
+					}
+				}
+			}
 			else if (text[currPos]=='}')
+			{
 				opened=false;
+				color = "";
+			}
 			else
 				lineWidth += (ui32)glyphWidth;
 			currPos += (ui32)symbolSize;
@@ -196,7 +215,7 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWi
 		{
 			/* Add an opening brace for the next line. */
 			if (text.length() != 0)
-				text.insert(0, "{");
+				text.insert(0, "{" + color);
 		}
 	}
 

+ 4 - 0
client/windows/CMessage.h

@@ -16,6 +16,10 @@ struct SDL_Surface;
 class CInfoWindow;
 class CComponent;
 
+VCMI_LIB_NAMESPACE_BEGIN
+class ColorRGBA;
+VCMI_LIB_NAMESPACE_END
+
 
 /// Class which draws formatted text messages and generates chat windows
 class CMessage

+ 171 - 0
config/textColors.json

@@ -0,0 +1,171 @@
+{
+	"colors": {
+		"r": "F80000",
+		"g": "00FC00",
+		"b": "0000F8",
+		"y": "F8FC00",
+		"o": "F88000",
+		"p": "F800F8",
+		"c": "00FCF8",
+		"k": "000000",
+		"w": "FFFFFF",
+		"Regular": "F8F0D8",
+		"Highlight": "E8D478",
+		"H3Red": "F80000",
+		"H3Cyan": "00FCF8",
+		"H3Green": "00FC00",
+		"H3Blue": "0000F8",
+		"H3Yellow": "F8FC00",
+		"H3Orange": "F88000",
+		"H3Purple": "F800F8",
+		"H3Pink": "C07888",
+		"Black": "000000",
+		"Navy": "000080",
+		"DarkBlue": "00008B",
+		"MediumBlue": "0000CD",
+		"Blue": "0000FF",
+		"DarkGreen": "006400",
+		"Green": "008000",
+		"Teal": "008080",
+		"DarkCyan": "008B8B",
+		"DeepSkyBlue": "00BFFF",
+		"DarkTurquoise": "00CED1",
+		"MediumSpringGreen": "00FA9A",
+		"Lime": "00FF00",
+		"SpringGreen": "00FF7F",
+		"Aqua": "00FFFF",
+		"Cyan": "00FFFF",
+		"MidnightBlue": "191970",
+		"DodgerBlue": "1E90FF",
+		"LightSeaGreen": "20B2AA",
+		"ForestGreen": "228B22",
+		"SeaGreen": "2E8B57",
+		"DarkSlateGray": "2F4F4F",
+		"DarkSlateGrey": "2F4F4F",
+		"LimeGreen": "32CD32",
+		"MediumSeaGreen": "3CB371",
+		"Turquoise": "40E0D0",
+		"RoyalBlue": "4169E1",
+		"SteelBlue": "4682B4",
+		"DarkSlateBlue": "483D8B",
+		"MediumTurquoise": "48D1CC",
+		"Indigo": "4B0082",
+		"DarkOliveGreen": "556B2F",
+		"CadetBlue": "5F9EA0",
+		"CornflowerBlue": "6495ED",
+		"RebeccaPurple": "663399",
+		"MediumAquaMarine": "66CDAA",
+		"DimGray": "696969",
+		"DimGrey": "696969",
+		"SlateBlue": "6A5ACD",
+		"OliveDrab": "6B8E23",
+		"SlateGray": "708090",
+		"SlateGrey": "708090",
+		"LightSlateGray": "778899",
+		"LightSlateGrey": "778899",
+		"MediumSlateBlue": "7B68EE",
+		"LawnGreen": "7CFC00",
+		"Chartreuse": "7FFF00",
+		"Aquamarine": "7FFFD4",
+		"Maroon": "800000",
+		"Purple": "800080",
+		"Olive": "808000",
+		"Gray": "808080",
+		"Grey": "808080",
+		"SkyBlue": "87CEEB",
+		"LightSkyBlue": "87CEFA",
+		"BlueViolet": "8A2BE2",
+		"DarkRed": "8B0000",
+		"DarkMagenta": "8B008B",
+		"SaddleBrown": "8B4513",
+		"DarkSeaGreen": "8FBC8F",
+		"LightGreen": "90EE90",
+		"MediumPurple": "9370DB",
+		"DarkViolet": "9400D3",
+		"PaleGreen": "98FB98",
+		"DarkOrchid": "9932CC",
+		"YellowGreen": "9ACD32",
+		"Sienna": "A0522D",
+		"Brown": "A52A2A",
+		"DarkGray": "A9A9A9",
+		"DarkGrey": "A9A9A9",
+		"LightBlue": "ADD8E6",
+		"GreenYellow": "ADFF2F",
+		"PaleTurquoise": "AFEEEE",
+		"LightSteelBlue": "B0C4DE",
+		"PowderBlue": "B0E0E6",
+		"FireBrick": "B22222",
+		"DarkGoldenRod": "B8860B",
+		"MediumOrchid": "BA55D3",
+		"RosyBrown": "BC8F8F",
+		"DarkKhaki": "BDB76B",
+		"Silver": "C0C0C0",
+		"MediumVioletRed": "C71585",
+		"IndianRed": "CD5C5C",
+		"Peru": "CD853F",
+		"Chocolate": "D2691E",
+		"Tan": "D2B48C",
+		"LightGray": "D3D3D3",
+		"LightGrey": "D3D3D3",
+		"Thistle": "D8BFD8",
+		"Orchid": "DA70D6",
+		"GoldenRod": "DAA520",
+		"PaleVioletRed": "DB7093",
+		"Crimson": "DC143C",
+		"Gainsboro": "DCDCDC",
+		"Plum": "DDA0DD",
+		"BurlyWood": "DEB887",
+		"LightCyan": "E0FFFF",
+		"Lavender": "E6E6FA",
+		"DarkSalmon": "E9967A",
+		"Violet": "EE82EE",
+		"PaleGoldenRod": "EEE8AA",
+		"LightCoral": "F08080",
+		"Khaki": "F0E68C",
+		"AliceBlue": "F0F8FF",
+		"HoneyDew": "F0FFF0",
+		"Azure": "F0FFFF",
+		"SandyBrown": "F4A460",
+		"Wheat": "F5DEB3",
+		"Beige": "F5F5DC",
+		"WhiteSmoke": "F5F5F5",
+		"MintCream": "F5FFFA",
+		"GhostWhite": "F8F8FF",
+		"Salmon": "FA8072",
+		"AntiqueWhite": "FAEBD7",
+		"Linen": "FAF0E6",
+		"LightGoldenRodYellow": "FAFAD2",
+		"OldLace": "FDF5E6",
+		"Red": "FF0000",
+		"Fuchsia": "FF00FF",
+		"Magenta": "FF00FF",
+		"DeepPink": "FF1493",
+		"OrangeRed": "FF4500",
+		"Tomato": "FF6347",
+		"HotPink": "FF69B4",
+		"Coral": "FF7F50",
+		"DarkOrange": "FF8C00",
+		"LightSalmon": "FFA07A",
+		"Orange": "FFA500",
+		"LightPink": "FFB6C1",
+		"Pink": "FFC0CB",
+		"Gold": "FFD700",
+		"PeachPuff": "FFDAB9",
+		"NavajoWhite": "FFDEAD",
+		"Moccasin": "FFE4B5",
+		"Bisque": "FFE4C4",
+		"MistyRose": "FFE4E1",
+		"BlanchedAlmond": "FFEBCD",
+		"PapayaWhip": "FFEFD5",
+		"LavenderBlush": "FFF0F5",
+		"SeaShell": "FFF5EE",
+		"Cornsilk": "FFF8DC",
+		"LemonChiffon": "FFFACD",
+		"FloralWhite": "FFFAF0",
+		"Snow": "FFFAFA",
+		"Yellow": "FFFF00",
+		"LightYellow": "FFFFE0",
+		"Ivory": "FFFFF0",
+		"White": "FFFFFF"
+	}
+}

+ 26 - 1
docs/players/Game_Mechanics.md

@@ -45,6 +45,31 @@ Some of H3 mechanics can't be straight considered as bug, but default VCMI behav
 
 Mouse click on castle icon in the town screen open quick recruitment window, where we can purhase in fast way units.
 
+## Color support
+
+Additional color are supported for text fields (e.g. map description). Uses HTML color syntax (e.g. #abcdef) / HTML predefined colors (e.g. green).
+
+##### Original Heroes III Support
+
+`This is white`
+
+<span style="color:white;background-color:black;">This is white</span>
+
+`{This is yellow}`
+
+<span style="color:yellow;background-color:black;">This is yellow</span>
+
+##### New
+
+`{#ff0000|This is red}`
+
+<span style="color:red">This is red</span>
+
+`{green|This is green}`
+
+<span style="color:green">This is green</span>
+
+
 # Manuals and guides
 
-- https://heroes.thelazy.net//index.php/Main_Page Wiki that aims to be a complete reference to Heroes of Might and Magic III. 
+- https://heroes.thelazy.net/index.php/Main_Page Wiki that aims to be a complete reference to Heroes of Might and Magic III.