aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSmallJoker <SmallJoker@users.noreply.github.com>2020-01-22 19:09:11 +0100
committerGitHub <noreply@github.com>2020-01-22 19:09:11 +0100
commit1892ff3c0db23ccdf7b0f6dc83cb1bdf4579b4ec (patch)
treec49c16a69e9c555141960b4bca22a88df9b17a61
parentfab3f5f7c8ce0cbfc27e509f065e3f4cfe28c514 (diff)
downloadminetest-1892ff3c0db23ccdf7b0f6dc83cb1bdf4579b4ec.tar.gz
minetest-1892ff3c0db23ccdf7b0f6dc83cb1bdf4579b4ec.tar.bz2
minetest-1892ff3c0db23ccdf7b0f6dc83cb1bdf4579b4ec.zip
StaticText/EnrichedString: Styling support (#9187)
* StaticText/EnrichedString: Styling support * Fix tooltip fg/bgcolor * Fix default color for substr(), add unittests
-rw-r--r--games/minimal/mods/test/formspec.lua16
-rw-r--r--src/client/gameui.cpp4
-rw-r--r--src/gui/guiButton.cpp1
-rw-r--r--src/gui/guiFormSpecMenu.cpp15
-rw-r--r--src/irrlicht_changes/static_text.cpp253
-rw-r--r--src/irrlicht_changes/static_text.h17
-rw-r--r--src/unittest/test_utilities.cpp20
-rw-r--r--src/util/enriched_string.cpp71
-rw-r--r--src/util/enriched_string.h28
9 files changed, 222 insertions, 203 deletions
diff --git a/games/minimal/mods/test/formspec.lua b/games/minimal/mods/test/formspec.lua
index 64b9ec0d5..bac82c965 100644
--- a/games/minimal/mods/test/formspec.lua
+++ b/games/minimal/mods/test/formspec.lua
@@ -1,3 +1,5 @@
+local color = minetest.colorize
+
local clip_fs = [[
style_type[label;noclip=%c]
style_type[button;noclip=%c]
@@ -31,8 +33,8 @@ local style_fs = [[
bgcolor_pressed=purple]
button[0,0;2.5,0.8;one_btn1;Button]
- style[one_btn2;border=false;textcolor=cyan]
- button[0,1.05;2.5,0.8;one_btn2;Text Button]
+ style[one_btn2;border=false;textcolor=cyan] ]]..
+ "button[0,1.05;2.5,0.8;one_btn2;Text " .. color("#FF0", "Yellow") .. [[]
style[one_btn3;bgimg=bubble.png;bgimg_hovered=default_apple.png;
bgimg_pressed=heart.png]
@@ -144,16 +146,18 @@ local pages = {
list[current_player;main;6,8;3,2;1]
button[9,0;2.5,1;name;]
button[9,1;2.5,1;name;]
- button[9,2;2.5,1;name;]
- label[9,0;This is a label.\nLine\nLine\nLine\nEnd]
- button[9,3;1,1;name;]
+ button[9,2;2.5,1;name;] ]]..
+ "label[9,0.5;This is a label.\nLine\nLine\nLine\nEnd]"..
+ [[button[9,3;1,1;name;]
vertlabel[9,4;VERT]
label[10,3;HORIZ]
tabheader[6.5,0;6,0.65;name;Tab 1,Tab 2,Tab 3,Secrets;1;false;false]
]],
"size[12,12]real_coordinates[true]" ..
- "label[0.375,0.375;Styled]" ..
+ ("label[0.375,0.375;Styled - %s %s]"):format(
+ color("#F00", "red text"),
+ color("#77FF00CC", "green text")) ..
"label[6.375,0.375;Unstyled]" ..
"box[0,0.75;12,0.1;#999]" ..
"box[6,0.85;0.1,11.15;#999]" ..
diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp
index 674d07fa6..3c7ed54b2 100644
--- a/src/client/gameui.cpp
+++ b/src/client/gameui.cpp
@@ -155,7 +155,7 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
m_guitext2->setVisible(m_flags.show_debug);
- setStaticText(m_guitext_info, translate_string(m_infotext).c_str());
+ setStaticText(m_guitext_info, m_infotext.c_str());
m_guitext_info->setVisible(m_flags.show_hud && g_menumgr.menuCount() == 0);
static const float statustext_time_max = 1.5f;
@@ -169,7 +169,7 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
}
}
- setStaticText(m_guitext_status, translate_string(m_statustext).c_str());
+ setStaticText(m_guitext_status, m_statustext.c_str());
m_guitext_status->setVisible(!m_statustext.empty());
if (!m_statustext.empty()) {
diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp
index ed79999cf..f7a0af2d9 100644
--- a/src/gui/guiButton.cpp
+++ b/src/gui/guiButton.cpp
@@ -53,7 +53,6 @@ GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent,
core::clamp<u32>(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255),
core::clamp<u32>(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255));
}
-
StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), false, false, this, id);
StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
// END PATCH
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index a91623f96..d03ce4516 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -3417,19 +3417,16 @@ void GUIFormSpecMenu::drawMenu()
void GUIFormSpecMenu::showTooltip(const std::wstring &text,
const irr::video::SColor &color, const irr::video::SColor &bgcolor)
{
- const std::wstring ntext = translate_string(text);
- m_tooltip_element->setOverrideColor(color);
- m_tooltip_element->setBackgroundColor(bgcolor);
- setStaticText(m_tooltip_element, ntext.c_str());
+ EnrichedString ntext(text);
+ ntext.setDefaultColor(color);
+ ntext.setBackground(bgcolor);
+
+ setStaticText(m_tooltip_element, ntext);
// Tooltip size and offset
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
-#if (IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2) || USE_FREETYPE == 1
- std::vector<std::wstring> text_rows = str_split(ntext, L'\n');
- s32 tooltip_height = m_tooltip_element->getTextHeight() * text_rows.size() + 5;
-#else
s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
-#endif
+
v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
int tooltip_offset_x = m_btn_height;
int tooltip_offset_y = m_btn_height;
diff --git a/src/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp
index 1375f033c..39b34d17c 100644
--- a/src/irrlicht_changes/static_text.cpp
+++ b/src/irrlicht_changes/static_text.cpp
@@ -32,21 +32,15 @@ StaticText::StaticText(const EnrichedString &text, bool border,
bool background)
: IGUIStaticText(environment, parent, id, rectangle),
HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT),
- Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background),
+ Border(border), WordWrap(false), Background(background),
RestrainTextInside(true), RightToLeft(false),
- OverrideColor(video::SColor(101,255,255,255)), BGColor(video::SColor(101,210,210,210)),
OverrideFont(0), LastBreakFont(0)
{
#ifdef _DEBUG
setDebugName("StaticText");
#endif
- Text = text.c_str();
- cText = text;
- if (environment && environment->getSkin())
- {
- BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE);
- }
+ setText(text);
}
@@ -73,12 +67,7 @@ void StaticText::draw()
// draw background
if (Background)
- {
- if ( !OverrideBGColorEnabled ) // skin-colors can change
- BGColor = skin->getColor(gui::EGDC_3D_FACE);
-
- driver->draw2DRectangle(BGColor, frameRect, &AbsoluteClippingRect);
- }
+ driver->draw2DRectangle(getBackgroundColor(), frameRect, &AbsoluteClippingRect);
// draw the border
@@ -89,97 +78,60 @@ void StaticText::draw()
}
// draw the text
- if (cText.size())
- {
- IGUIFont* font = getActiveFont();
-
- if (font)
+ IGUIFont *font = getActiveFont();
+ if (font && BrokenText.size()) {
+ if (font != LastBreakFont)
+ updateText();
+
+ core::rect<s32> r = frameRect;
+ s32 height_line = font->getDimension(L"A").Height + font->getKerningHeight();
+ s32 height_total = height_line * BrokenText.size();
+ if (VAlign == EGUIA_CENTER && WordWrap)
{
- if (!WordWrap)
- {
- // TODO: add colors here
- if (VAlign == EGUIA_LOWERRIGHT)
- {
- frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y -
- font->getDimension(L"A").Height - font->getKerningHeight();
- }
- if (HAlign == EGUIA_LOWERRIGHT)
- {
- frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
- font->getDimension(cText.c_str()).Width;
- }
+ r.UpperLeftCorner.Y = r.getCenter().Y - (height_total / 2);
+ }
+ else if (VAlign == EGUIA_LOWERRIGHT)
+ {
+ r.UpperLeftCorner.Y = r.LowerRightCorner.Y - height_total;
+ }
+ if (HAlign == EGUIA_LOWERRIGHT)
+ {
+ r.UpperLeftCorner.X = r.LowerRightCorner.X -
+ getTextWidth();
+ }
-#if USE_FREETYPE
- if (font->getType() == irr::gui::EGFT_CUSTOM) {
- irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
- tmp->draw(Text, frameRect,
- OverrideColorEnabled ? OverrideColor :
- skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
- HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER,
- (RestrainTextInside ? &AbsoluteClippingRect : NULL));
- } else
-#endif
- {
- font->draw(Text.c_str(), frameRect,
- skin->getColor(EGDC_BUTTON_TEXT),
- HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER,
- (RestrainTextInside ? &AbsoluteClippingRect : NULL));
- }
- }
- else
+ irr::video::SColor previous_color(255, 255, 255, 255);
+ for (const EnrichedString &str : BrokenText) {
+ if (HAlign == EGUIA_LOWERRIGHT)
{
- if (font != LastBreakFont)
- breakText();
-
- core::rect<s32> r = frameRect;
- s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
- s32 totalHeight = height * BrokenText.size();
- if (VAlign == EGUIA_CENTER)
- {
- r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
- }
- else if (VAlign == EGUIA_LOWERRIGHT)
- {
- r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
- }
-
- irr::video::SColor previous_color(255, 255, 255, 255);
- for (u32 i=0; i<BrokenText.size(); ++i)
- {
- if (HAlign == EGUIA_LOWERRIGHT)
- {
- r.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
- font->getDimension(BrokenText[i].c_str()).Width;
- }
-
- EnrichedString str = BrokenText[i];
+ r.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
+ font->getDimension(str.c_str()).Width;
+ }
- //str = colorizeText(BrokenText[i].c_str(), colors, previous_color);
- //if (!colors.empty())
- // previous_color = colors[colors.size() - 1];
+ //str = colorizeText(BrokenText[i].c_str(), colors, previous_color);
+ //if (!colors.empty())
+ // previous_color = colors[colors.size() - 1];
#if USE_FREETYPE
- if (font->getType() == irr::gui::EGFT_CUSTOM) {
- irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
- tmp->draw(str,
- r, previous_color, // FIXME
- HAlign == EGUIA_CENTER, false,
- (RestrainTextInside ? &AbsoluteClippingRect : NULL));
- } else
+ if (font->getType() == irr::gui::EGFT_CUSTOM) {
+ irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
+ tmp->draw(str,
+ r, previous_color, // FIXME
+ HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER,
+ (RestrainTextInside ? &AbsoluteClippingRect : NULL));
+ } else
#endif
- {
- // Draw non-colored text
- font->draw(str.c_str(),
- r, skin->getColor(EGDC_BUTTON_TEXT),
- HAlign == EGUIA_CENTER, false,
- (RestrainTextInside ? &AbsoluteClippingRect : NULL));
- }
+ {
+ // Draw non-colored text
+ font->draw(str.c_str(),
+ r, str.getDefaultColor(), // TODO: Implement colorization
+ HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER,
+ (RestrainTextInside ? &AbsoluteClippingRect : NULL));
+ }
- r.LowerRightCorner.Y += height;
- r.UpperLeftCorner.Y += height;
- }
- }
+ r.LowerRightCorner.Y += height_line;
+ r.UpperLeftCorner.Y += height_line;
}
}
@@ -201,7 +153,7 @@ void StaticText::setOverrideFont(IGUIFont* font)
if (OverrideFont)
OverrideFont->grab();
- breakText();
+ updateText();
}
//! Gets the override font (if any)
@@ -224,16 +176,15 @@ IGUIFont* StaticText::getActiveFont() const
//! Sets another color for the text.
void StaticText::setOverrideColor(video::SColor color)
{
- OverrideColor = color;
- OverrideColorEnabled = true;
+ ColoredText.setDefaultColor(color);
+ updateText();
}
//! Sets another color for the text.
void StaticText::setBackgroundColor(video::SColor color)
{
- BGColor = color;
- OverrideBGColorEnabled = true;
+ ColoredText.setBackground(color);
Background = true;
}
@@ -248,7 +199,10 @@ void StaticText::setDrawBackground(bool draw)
//! Gets the background color
video::SColor StaticText::getBackgroundColor() const
{
- return BGColor;
+ IGUISkin *skin = Environment->getSkin();
+
+ return (ColoredText.hasBackground() || !skin) ?
+ ColoredText.getBackground() : skin->getColor(gui::EGDC_3D_FACE);
}
@@ -298,7 +252,7 @@ const video::SColor& StaticText::getOverrideColor() const
video::SColor StaticText::getOverrideColor() const
#endif
{
- return OverrideColor;
+ return ColoredText.getDefaultColor();
}
@@ -306,13 +260,13 @@ video::SColor StaticText::getOverrideColor() const
//! color in the gui skin.
void StaticText::enableOverrideColor(bool enable)
{
- OverrideColorEnabled = enable;
+ // TODO
}
bool StaticText::isOverrideColorEnabled() const
{
- return OverrideColorEnabled;
+ return true;
}
@@ -321,7 +275,7 @@ bool StaticText::isOverrideColorEnabled() const
void StaticText::setWordWrap(bool enable)
{
WordWrap = enable;
- breakText();
+ updateText();
}
@@ -336,7 +290,7 @@ void StaticText::setRightToLeft(bool rtl)
if (RightToLeft != rtl)
{
RightToLeft = rtl;
- breakText();
+ updateText();
}
}
@@ -348,12 +302,22 @@ bool StaticText::isRightToLeft() const
//! Breaks the single text line.
-void StaticText::breakText()
+// Updates the font colors
+void StaticText::updateText()
{
- if (!WordWrap)
+ const EnrichedString &cText = ColoredText;
+ BrokenText.clear();
+
+ if (cText.hasBackground()) {
+ setBackgroundColor(cText.getBackground());
+ }
+
+ if (!WordWrap) {
+ BrokenText.push_back(cText);
return;
+ }
- BrokenText.clear();
+ // Update word wrap
IGUISkin* skin = Environment->getSkin();
IGUIFont* font = getActiveFont();
@@ -574,25 +538,20 @@ void StaticText::breakText()
//! Sets the new caption of this element.
void StaticText::setText(const wchar_t* text)
{
- setText(EnrichedString(text));
+ setText(EnrichedString(text, getOverrideColor()));
}
-//! Sets the new caption of this element.
void StaticText::setText(const EnrichedString &text)
{
- IGUIElement::setText(text.c_str());
- cText = text;
- if (text.hasBackground()) {
- setBackgroundColor(text.getBackground());
- }
- breakText();
+ ColoredText = text;
+ IGUIElement::setText(ColoredText.c_str());
+ updateText();
}
-
void StaticText::updateAbsolutePosition()
{
IGUIElement::updateAbsolutePosition();
- breakText();
+ updateText();
}
@@ -603,39 +562,31 @@ s32 StaticText::getTextHeight() const
if (!font)
return 0;
- s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
-
- if (WordWrap)
- height *= BrokenText.size();
-
- return height;
+ if (WordWrap) {
+ s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
+ return height * BrokenText.size();
+ }
+ // There may be intentional new lines without WordWrap
+ return font->getDimension(BrokenText[0].c_str()).Height;
}
s32 StaticText::getTextWidth() const
{
- IGUIFont * font = getActiveFont();
- if(!font)
+ IGUIFont *font = getActiveFont();
+ if (!font)
return 0;
- if(WordWrap)
- {
- s32 widest = 0;
+ s32 widest = 0;
- for(u32 line = 0; line < BrokenText.size(); ++line)
- {
- s32 width = font->getDimension(BrokenText[line].c_str()).Width;
-
- if(width > widest)
- widest = width;
- }
+ for (const EnrichedString &line : BrokenText) {
+ s32 width = font->getDimension(line.c_str()).Width;
- return widest;
- }
- else
- {
- return font->getDimension(cText.c_str()).Width;
+ if (width > widest)
+ widest = width;
}
+
+ return widest;
}
@@ -647,14 +598,14 @@ void StaticText::serializeAttributes(io::IAttributes* out, io::SAttributeReadWri
IGUIStaticText::serializeAttributes(out,options);
out->addBool ("Border", Border);
- out->addBool ("OverrideColorEnabled",OverrideColorEnabled);
- out->addBool ("OverrideBGColorEnabled",OverrideBGColorEnabled);
+ out->addBool ("OverrideColorEnabled",true);
+ out->addBool ("OverrideBGColorEnabled",ColoredText.hasBackground());
out->addBool ("WordWrap", WordWrap);
out->addBool ("Background", Background);
out->addBool ("RightToLeft", RightToLeft);
out->addBool ("RestrainTextInside", RestrainTextInside);
- out->addColor ("OverrideColor", OverrideColor);
- out->addColor ("BGColor", BGColor);
+ out->addColor ("OverrideColor", ColoredText.getDefaultColor());
+ out->addColor ("BGColor", ColoredText.getBackground());
out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
@@ -668,14 +619,14 @@ void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWr
IGUIStaticText::deserializeAttributes(in,options);
Border = in->getAttributeAsBool("Border");
- enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
- OverrideBGColorEnabled = in->getAttributeAsBool("OverrideBGColorEnabled");
setWordWrap(in->getAttributeAsBool("WordWrap"));
Background = in->getAttributeAsBool("Background");
RightToLeft = in->getAttributeAsBool("RightToLeft");
RestrainTextInside = in->getAttributeAsBool("RestrainTextInside");
- OverrideColor = in->getAttributeAsColor("OverrideColor");
- BGColor = in->getAttributeAsColor("BGColor");
+ if (in->getAttributeAsBool("OverrideColorEnabled"))
+ ColoredText.setDefaultColor(in->getAttributeAsColor("OverrideColor"));
+ if (in->getAttributeAsBool("OverrideBGColorEnabled"))
+ ColoredText.setBackground(in->getAttributeAsColor("BGColor"));
setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
(EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
diff --git a/src/irrlicht_changes/static_text.h b/src/irrlicht_changes/static_text.h
index 43c587284..1f111ea56 100644
--- a/src/irrlicht_changes/static_text.h
+++ b/src/irrlicht_changes/static_text.h
@@ -34,7 +34,8 @@ namespace gui
{
public:
- //! constructor
+ // StaticText is translated by EnrichedString.
+ // No need to use translate_string()
StaticText(const EnrichedString &text, bool border, IGUIEnvironment* environment,
IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
bool background = false);
@@ -201,23 +202,20 @@ namespace gui
private:
//! Breaks the single text line.
- void breakText();
+ void updateText();
EGUI_ALIGNMENT HAlign, VAlign;
bool Border;
- bool OverrideColorEnabled;
- bool OverrideBGColorEnabled;
bool WordWrap;
bool Background;
bool RestrainTextInside;
bool RightToLeft;
- video::SColor OverrideColor, BGColor;
gui::IGUIFont* OverrideFont;
gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated.
- EnrichedString cText;
- core::array< EnrichedString > BrokenText;
+ EnrichedString ColoredText;
+ std::vector<EnrichedString> BrokenText;
};
@@ -274,10 +272,7 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text)
{
- auto color = static_text->isOverrideColorEnabled()
- ? static_text->getOverrideColor()
- : irr::video::SColor(255, 255, 255, 255);
- setStaticText(static_text, EnrichedString(text, color));
+ setStaticText(static_text, EnrichedString(text, static_text->getOverrideColor()));
}
#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp
index 8e8958d18..447b591e1 100644
--- a/src/unittest/test_utilities.cpp
+++ b/src/unittest/test_utilities.cpp
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "test.h"
#include <cmath>
+#include "util/enriched_string.h"
#include "util/numeric.h"
#include "util/string.h"
@@ -49,6 +50,7 @@ public:
void testUTF8();
void testRemoveEscapes();
void testWrapRows();
+ void testEnrichedString();
void testIsNumber();
void testIsPowerOfTwo();
void testMyround();
@@ -79,6 +81,7 @@ void TestUtilities::runTests(IGameDef *gamedef)
TEST(testUTF8);
TEST(testRemoveEscapes);
TEST(testWrapRows);
+ TEST(testEnrichedString);
TEST(testIsNumber);
TEST(testIsPowerOfTwo);
TEST(testMyround);
@@ -344,6 +347,23 @@ void TestUtilities::testWrapRows()
}
}
+void TestUtilities::testEnrichedString()
+{
+ EnrichedString str(L"Test bar");
+ irr::video::SColor color(0xFF, 0, 0, 0xFF);
+
+ UASSERT(str.substr(1, 3).getString() == L"est");
+ str += L" BUZZ";
+ UASSERT(str.substr(9, std::string::npos).getString() == L"BUZZ");
+ str.setDefaultColor(color); // Blue foreground
+ UASSERT(str.getColors()[5] == color);
+ // Green background, then white and yellow text
+ str = L"\x1b(b@#0F0)Regular \x1b(c@#FF0)yellow";
+ UASSERT(str.getColors()[2] == 0xFFFFFFFF);
+ str.setDefaultColor(color); // Blue foreground
+ UASSERT(str.getColors()[13] == 0xFFFFFF00); // Still yellow text
+ UASSERT(str.getBackground() == 0xFF00FF00); // Green background
+}
void TestUtilities::testIsNumber()
{
diff --git a/src/util/enriched_string.cpp b/src/util/enriched_string.cpp
index 642188a52..d5f8aa661 100644
--- a/src/util/enriched_string.cpp
+++ b/src/util/enriched_string.cpp
@@ -45,15 +45,27 @@ EnrichedString::EnrichedString(const wchar_t *str, const SColor &color)
addAtEnd(translate_string(std::wstring(str)), color);
}
+void EnrichedString::clear()
+{
+ m_string.clear();
+ m_colors.clear();
+ m_has_background = false;
+ m_default_length = 0;
+ m_default_color = irr::video::SColor(255, 255, 255, 255);
+}
+
void EnrichedString::operator=(const wchar_t *str)
{
clear();
- addAtEnd(translate_string(std::wstring(str)), SColor(255, 255, 255, 255));
+ addAtEnd(translate_string(std::wstring(str)), m_default_color);
}
void EnrichedString::addAtEnd(const std::wstring &s, const SColor &initial_color)
{
SColor color(initial_color);
+ bool use_default = (m_default_length == m_string.size() &&
+ color == m_default_color);
+
size_t i = 0;
while (i < s.length()) {
if (s[i] != L'\x1b') {
@@ -90,6 +102,12 @@ void EnrichedString::addAtEnd(const std::wstring &s, const SColor &initial_color
continue;
}
parseColorString(wide_to_utf8(parts[1]), color, true);
+
+ // No longer use default color after first escape
+ if (use_default) {
+ m_default_length = m_string.size();
+ use_default = false;
+ }
} else if (parts[0] == L"b") {
if (parts.size() < 2) {
continue;
@@ -98,6 +116,10 @@ void EnrichedString::addAtEnd(const std::wstring &s, const SColor &initial_color
m_has_background = true;
}
}
+
+ // Update if no escape character was found
+ if (use_default)
+ m_default_length = m_string.size();
}
void EnrichedString::addChar(const EnrichedString &source, size_t i)
@@ -110,7 +132,7 @@ void EnrichedString::addCharNoColor(wchar_t c)
{
m_string += c;
if (m_colors.empty()) {
- m_colors.emplace_back(255, 255, 255, 255);
+ m_colors.emplace_back(m_default_color);
} else {
m_colors.push_back(m_colors[m_colors.size() - 1]);
}
@@ -118,35 +140,40 @@ void EnrichedString::addCharNoColor(wchar_t c)
EnrichedString EnrichedString::operator+(const EnrichedString &other) const
{
- std::vector<SColor> result;
- result.insert(result.end(), m_colors.begin(), m_colors.end());
- result.insert(result.end(), other.m_colors.begin(), other.m_colors.end());
- return EnrichedString(m_string + other.m_string, result);
+ EnrichedString result = *this;
+ result += other;
+ return result;
}
void EnrichedString::operator+=(const EnrichedString &other)
{
+ bool update_default_color = m_default_length == m_string.size();
+
m_string += other.m_string;
m_colors.insert(m_colors.end(), other.m_colors.begin(), other.m_colors.end());
+
+ if (update_default_color) {
+ m_default_length += other.m_default_length;
+ updateDefaultColor();
+ }
}
EnrichedString EnrichedString::substr(size_t pos, size_t len) const
{
- if (pos == m_string.length()) {
+ if (pos >= m_string.length())
return EnrichedString();
- }
- if (len == std::string::npos || pos + len > m_string.length()) {
- return EnrichedString(
- m_string.substr(pos, std::string::npos),
- std::vector<SColor>(m_colors.begin() + pos, m_colors.end())
- );
- }
- return EnrichedString(
+ if (len == std::string::npos || pos + len > m_string.length())
+ len = m_string.length() - pos;
+
+ EnrichedString str(
m_string.substr(pos, len),
std::vector<SColor>(m_colors.begin() + pos, m_colors.begin() + pos + len)
);
-
+ if (pos < m_default_length)
+ str.m_default_length = m_default_length - pos;
+ str.setDefaultColor(m_default_color);
+ return str;
}
const wchar_t *EnrichedString::c_str() const
@@ -163,3 +190,15 @@ const std::wstring &EnrichedString::getString() const
{
return m_string;
}
+
+void EnrichedString::setDefaultColor(const irr::video::SColor &color)
+{
+ m_default_color = color;
+ updateDefaultColor();
+}
+
+void EnrichedString::updateDefaultColor()
+{
+ for (size_t i = 0; i < m_default_length; ++i)
+ m_colors[i] = m_default_color;
+}
diff --git a/src/util/enriched_string.h b/src/util/enriched_string.h
index 202d84cb0..eaab3bd91 100644
--- a/src/util/enriched_string.h
+++ b/src/util/enriched_string.h
@@ -32,6 +32,7 @@ public:
const irr::video::SColor &color = irr::video::SColor(255, 255, 255, 255));
EnrichedString(const std::wstring &string,
const std::vector<irr::video::SColor> &colors);
+ void clear();
void operator=(const wchar_t *str);
void addAtEnd(const std::wstring &s, const irr::video::SColor &color);
@@ -50,6 +51,14 @@ public:
const wchar_t *c_str() const;
const std::vector<irr::video::SColor> &getColors() const;
const std::wstring &getString() const;
+
+ void setDefaultColor(const irr::video::SColor &color);
+ void updateDefaultColor();
+ inline const irr::video::SColor &getDefaultColor() const
+ {
+ return m_default_color;
+ }
+
inline bool operator==(const EnrichedString &other) const
{
return (m_string == other.m_string && m_colors == other.m_colors);
@@ -58,12 +67,6 @@ public:
{
return !(*this == other);
}
- inline void clear()
- {
- m_string.clear();
- m_colors.clear();
- m_has_background = false;
- }
inline bool empty() const
{
return m_string.empty();
@@ -72,6 +75,7 @@ public:
{
return m_string.size();
}
+
inline bool hasBackground() const
{
return m_has_background;
@@ -80,9 +84,19 @@ public:
{
return m_background;
}
+ inline void setBackground(const irr::video::SColor &color)
+ {
+ m_background = color;
+ m_has_background = true;
+ }
+
private:
std::wstring m_string;
std::vector<irr::video::SColor> m_colors;
- bool m_has_background = false;
+ bool m_has_background;
+ irr::video::SColor m_default_color;
irr::video::SColor m_background;
+ // This variable defines the length of the default-colored text.
+ // Change this to a std::vector if an "end coloring" tag is wanted.
+ size_t m_default_length;
};