From 1892ff3c0db23ccdf7b0f6dc83cb1bdf4579b4ec Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 22 Jan 2020 19:09:11 +0100 Subject: [PATCH] StaticText/EnrichedString: Styling support (#9187) * StaticText/EnrichedString: Styling support * Fix tooltip fg/bgcolor * Fix default color for substr(), add unittests --- games/minimal/mods/test/formspec.lua | 16 +- src/client/gameui.cpp | 4 +- src/gui/guiButton.cpp | 1 - src/gui/guiFormSpecMenu.cpp | 15 +- src/irrlicht_changes/static_text.cpp | 257 +++++++++++---------------- src/irrlicht_changes/static_text.h | 17 +- src/unittest/test_utilities.cpp | 20 +++ src/util/enriched_string.cpp | 71 ++++++-- src/util/enriched_string.h | 28 ++- 9 files changed, 224 insertions(+), 205 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(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255), core::clamp(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255)); } - StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect(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 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(); + IGUIFont *font = getActiveFont(); + if (font && BrokenText.size()) { + if (font != LastBreakFont) + updateText(); - if (font) + core::rect 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) + 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(); + } + + irr::video::SColor previous_color(255, 255, 255, 255); + for (const EnrichedString &str : BrokenText) { + if (HAlign == EGUIA_LOWERRIGHT) { - // 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.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]; #if USE_FREETYPE - if (font->getType() == irr::gui::EGFT_CUSTOM) { - irr::gui::CGUITTFont *tmp = static_cast(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 + if (font->getType() == irr::gui::EGFT_CUSTOM) { + irr::gui::CGUITTFont *tmp = static_cast(font); + tmp->draw(str, + r, previous_color, // FIXME + 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 { - if (font != LastBreakFont) - breakText(); - - core::rect 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; igetDimension(BrokenText[i].c_str()).Width; - } - - EnrichedString str = BrokenText[i]; - - //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(font); - tmp->draw(str, - r, previous_color, // FIXME - HAlign == EGUIA_CENTER, false, - (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)); - } - - - r.LowerRightCorner.Y += height; - r.UpperLeftCorner.Y += height; - } + // 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_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,13 +302,23 @@ bool StaticText::isRightToLeft() const //! Breaks the single text line. -void StaticText::breakText() +// Updates the font colors +void StaticText::updateText() { - if (!WordWrap) - return; - + const EnrichedString &cText = ColoredText; BrokenText.clear(); + if (cText.hasBackground()) { + setBackgroundColor(cText.getBackground()); + } + + if (!WordWrap) { + BrokenText.push_back(cText); + return; + } + + // Update word wrap + IGUISkin* skin = Environment->getSkin(); IGUIFont* font = getActiveFont(); if (!font) @@ -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; + for (const EnrichedString &line : BrokenText) { + s32 width = font->getDimension(line.c_str()).Width; - if(width > widest) - widest = 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& 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 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 +#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 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(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(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 &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 &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 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; };