diff --git a/Client/gui/CGUIElement_Impl.cpp b/Client/gui/CGUIElement_Impl.cpp index 4c17052ef87..6e1bd588d4e 100644 --- a/Client/gui/CGUIElement_Impl.cpp +++ b/Client/gui/CGUIElement_Impl.cpp @@ -733,3 +733,30 @@ inline void CGUIElement_Impl::ForceRedraw() { m_pWindow->forceRedraw(); } + +namespace { + void RecursivelyForceRedrawAutoWindows(CEGUI::Window* pWindow) + { + for (uint i = 0; i < pWindow->getChildCount(); ++i) + { + CEGUI::Window* pChild = pWindow->getChildAtIdx(i); + if (pChild->getName().find("__auto_") != CEGUI::String::npos) + { + pChild->forceRedraw(); + RecursivelyForceRedrawAutoWindows(pChild); + } + } + } +} + +void CGUIElement_Impl::SetColorCodesEnabled(bool bEnabled) +{ + m_pWindow->setUserString("ColorCodesEnabled", bEnabled ? "True" : "False"); + m_pWindow->forceRedraw(); + RecursivelyForceRedrawAutoWindows(m_pWindow); +} + +bool CGUIElement_Impl::GetColorCodesEnabled() +{ + return m_pWindow->isUserStringDefined("ColorCodesEnabled") && m_pWindow->getUserString("ColorCodesEnabled") == "True"; +} diff --git a/Client/gui/CGUIElement_Impl.h b/Client/gui/CGUIElement_Impl.h index 9f85a5be0f9..be9900e206f 100644 --- a/Client/gui/CGUIElement_Impl.h +++ b/Client/gui/CGUIElement_Impl.h @@ -100,6 +100,9 @@ class CGUIElement_Impl : public CGUIElement void ForceRedraw(); + void SetColorCodesEnabled(bool bEnabled); + bool GetColorCodesEnabled(); + void SetUserData(void* pData) { m_pData = pData; } void* GetUserData() { return m_pData; } diff --git a/Client/gui/CGUIElement_Inc.h b/Client/gui/CGUIElement_Inc.h index 250fa22cfba..92614da56bf 100644 --- a/Client/gui/CGUIElement_Inc.h +++ b/Client/gui/CGUIElement_Inc.h @@ -194,6 +194,19 @@ void ForceRedraw() { CGUIElement_Impl::ForceRedraw(); }; + +// Forwarding methods to the base CGUIElement_Impl implementation to resolve +// diamond multiple-inheritance issues so subclasses aren't treated as abstract. +#ifndef SETCOLORCODESENABLED_HACK +void SetColorCodesEnabled(bool bEnabled) +{ + CGUIElement_Impl::SetColorCodesEnabled(bEnabled); +}; +#endif +bool GetColorCodesEnabled() +{ + return CGUIElement_Impl::GetColorCodesEnabled(); +}; void SetAlwaysOnTop(bool bAlwaysOnTop) { CGUIElement_Impl::SetAlwaysOnTop(bAlwaysOnTop); diff --git a/Client/gui/CGUITab_Impl.cpp b/Client/gui/CGUITab_Impl.cpp index fb5111ef399..b105beddcd3 100644 --- a/Client/gui/CGUITab_Impl.cpp +++ b/Client/gui/CGUITab_Impl.cpp @@ -87,3 +87,23 @@ bool CGUITab_Impl::IsEnabled() CEGUI::TabControl* pControl = reinterpret_cast(((CGUITabPanel_Impl*)pParent)->m_pWindow); return !pControl->getButtonForTabContents(m_pWindow)->isDisabled(); } + +void CGUITab_Impl::SetColorCodesEnabled(bool bEnabled) +{ + CGUIElement_Impl::SetColorCodesEnabled(bEnabled); + + if (m_pParent && m_pParent->GetType() == CGUI_TABPANEL) + { + CGUIElement_Impl* pParent = static_cast(m_pParent); + CEGUI::TabControl* pTabControl = reinterpret_cast(((CGUITabPanel_Impl*)pParent)->m_pWindow); + if (pTabControl) + { + CEGUI::TabButton* pButton = pTabControl->getButtonForTabContents(m_pWindow); + if (pButton) + { + pButton->setUserString("ColorCodesEnabled", bEnabled ? "True" : "False"); + pButton->forceRedraw(); + } + } + } +} diff --git a/Client/gui/CGUITab_Impl.h b/Client/gui/CGUITab_Impl.h index fd30a87c56c..7a24653d297 100644 --- a/Client/gui/CGUITab_Impl.h +++ b/Client/gui/CGUITab_Impl.h @@ -26,7 +26,9 @@ class CGUITab_Impl : public CGUITab, public CGUIElement_Impl, public CGUITabList #define SETVISIBLE_HACK #define SETENABLED_HACK +#define SETCOLORCODESENABLED_HACK #include "CGUIElement_Inc.h" +#undef SETCOLORCODESENABLED_HACK #undef SETENABLED_HACK #undef SETVISIBLE_HACK @@ -34,4 +36,6 @@ class CGUITab_Impl : public CGUITab, public CGUIElement_Impl, public CGUITabList bool IsVisible(); void SetEnabled(bool bEnabled); bool IsEnabled(); + + void SetColorCodesEnabled(bool bEnabled); }; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaGUIDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaGUIDefs.cpp index 5471c8e6089..c3ed8b9ada5 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaGUIDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaGUIDefs.cpp @@ -139,6 +139,8 @@ void CLuaGUIDefs::LoadFunctions() {"guiGetProperty", GUIGetProperty}, {"guiGetProperties", GUIGetProperties}, {"guiGetAlpha", GUIGetAlpha}, + {"guiSetColorCodesEnabled", GUISetColorCodesEnabled}, + {"guiGetColorCodesEnabled", GUIGetColorCodesEnabled}, {"guiGetText", GUIGetText}, {"guiGetFont", GUIGetFont}, {"guiGetSize", GUIGetSize}, @@ -249,6 +251,7 @@ void CLuaGUIDefs::AddGuiElementClass(lua_State* luaVM) lua_classfunction(luaVM, "getScreenSize", "guiGetScreenSize"); lua_classfunction(luaVM, "getProperties", "guiGetProperties"); lua_classfunction(luaVM, "getAlpha", "guiGetAlpha"); + lua_classfunction(luaVM, "getColorCodesEnabled", "guiGetColorCodesEnabled"); lua_classfunction(luaVM, "getFont", "guiGetFont"); lua_classfunction(luaVM, "getEnabled", "guiGetEnabled"); lua_classfunction(luaVM, "getVisible", "guiGetVisible"); @@ -259,6 +262,7 @@ void CLuaGUIDefs::AddGuiElementClass(lua_State* luaVM) lua_classfunction(luaVM, "setInputEnabled", "guiSetInputEnabled"); lua_classfunction(luaVM, "setAlpha", "guiSetAlpha"); + lua_classfunction(luaVM, "setColorCodesEnabled", "guiSetColorCodesEnabled"); lua_classfunction(luaVM, "setEnabled", "guiSetEnabled"); lua_classfunction(luaVM, "setFont", "guiSetFont"); lua_classfunction(luaVM, "setVisible", "guiSetVisible"); @@ -282,6 +286,7 @@ void CLuaGUIDefs::AddGuiElementClass(lua_State* luaVM) lua_classvariable(luaVM, "visible", "guiSetVisible", "guiGetVisible"); lua_classvariable(luaVM, "properties", NULL, "guiGetProperties"); lua_classvariable(luaVM, "alpha", "guiSetAlpha", "guiGetAlpha"); + lua_classvariable(luaVM, "colorCodesEnabled", "guiSetColorCodesEnabled", "guiGetColorCodesEnabled"); lua_classvariable(luaVM, "enabled", "guiSetEnabled", "guiGetEnabled"); lua_classvariable(luaVM, "text", "guiSetText", "guiGetText"); lua_classvariable(luaVM, "size", "guiSetSize", "guiGetSize"); @@ -1864,6 +1869,73 @@ int CLuaGUIDefs::GUIGetAlpha(lua_State* luaVM) return 1; } +static void RecursivelySetColorCodesEnabled(CClientGUIElement* pElement, bool bEnabled) +{ + pElement->GetCGUIElement()->SetColorCodesEnabled(bEnabled); + CElementListSnapshotRef pSnapshot = pElement->GetChildrenListSnapshot(); + for (CClientEntity* pChild : *pSnapshot) + { + if (pChild->GetType() == CCLIENTGUI) + { + RecursivelySetColorCodesEnabled(static_cast(pChild), bEnabled); + } + } +} + +int CLuaGUIDefs::GUISetColorCodesEnabled(lua_State* luaVM) +{ + // bool guiSetColorCodesEnabled ( element guiElement, bool enabled [, bool includeChildren = false ] ) + CClientGUIElement* guiElement; + bool bEnabled; + bool bIncludeChildren = false; + + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(guiElement); + argStream.ReadBool(bEnabled); + if (argStream.NextIsBool()) + argStream.ReadBool(bIncludeChildren); + + if (!argStream.HasErrors()) + { + if (bIncludeChildren) + { + RecursivelySetColorCodesEnabled(guiElement, bEnabled); + } + else + { + guiElement->GetCGUIElement()->SetColorCodesEnabled(bEnabled); + } + + lua_pushboolean(luaVM, true); + return 1; + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + lua_pushboolean(luaVM, false); + return 1; +} + +int CLuaGUIDefs::GUIGetColorCodesEnabled(lua_State* luaVM) +{ + // bool guiGetColorCodesEnabled ( element guiElement ) + CClientGUIElement* guiElement; + + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(guiElement); + + if (!argStream.HasErrors()) + { + lua_pushboolean(luaVM, guiElement->GetCGUIElement()->GetColorCodesEnabled()); + return 1; + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + lua_pushboolean(luaVM, false); + return 1; +} + int CLuaGUIDefs::GUISetVisible(lua_State* luaVM) { // bool guiSetVisible ( element guiElement, bool state ) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaGUIDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaGUIDefs.h index 3fa9b839be2..fabf9ddcde9 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaGUIDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaGUIDefs.h @@ -112,6 +112,8 @@ class CLuaGUIDefs : public CLuaDefs LUA_DECLARE(GUIGetPosition); LUA_DECLARE(GUIGetVisible); LUA_DECLARE(GUIGetAlpha); + LUA_DECLARE(GUISetColorCodesEnabled); + LUA_DECLARE(GUIGetColorCodesEnabled); LUA_DECLARE(GUIGetProperty); LUA_DECLARE(GUIGetProperties); LUA_DECLARE(GUICheckBoxGetSelected); diff --git a/Client/sdk/gui/CGUIElement.h b/Client/sdk/gui/CGUIElement.h index 309a2f47360..e1b024adec7 100644 --- a/Client/sdk/gui/CGUIElement.h +++ b/Client/sdk/gui/CGUIElement.h @@ -105,6 +105,9 @@ class CGUIElement virtual void ForceRedraw() = 0; + virtual void SetColorCodesEnabled(bool bEnabled) = 0; + virtual bool GetColorCodesEnabled() = 0; + virtual CRect2D AbsoluteToRelative(const CRect2D& Rect) = 0; virtual CVector2D AbsoluteToRelative(const CVector2D& Vector) = 0; diff --git a/vendor/cegui-0.4.0-custom/include/CEGUIFont.h b/vendor/cegui-0.4.0-custom/include/CEGUIFont.h index a5f8396d877..3ebebd47502 100644 --- a/vendor/cegui-0.4.0-custom/include/CEGUIFont.h +++ b/vendor/cegui-0.4.0-custom/include/CEGUIFont.h @@ -107,6 +107,11 @@ class CEGUIEXPORT Font *************************************************************************/ static const argb_t DefaultColour; //!< Colour value used whenever a colour is not specified. + // When true, drawTextLine/drawTextLineJustified parse inline #RRGGBB color codes. + // CEGUIWindow::drawSelf() sets this per-window before populateRenderCache() and + // resets it to false afterwards, giving per-element opt-in behaviour. + static bool s_colorCodesEnabled; + /************************************************************************* Text drawing methods diff --git a/vendor/cegui-0.4.0-custom/include/CEGUIRenderCache.h b/vendor/cegui-0.4.0-custom/include/CEGUIRenderCache.h index bd05ac80591..b6b97d6dc87 100644 --- a/vendor/cegui-0.4.0-custom/include/CEGUIRenderCache.h +++ b/vendor/cegui-0.4.0-custom/include/CEGUIRenderCache.h @@ -188,6 +188,7 @@ namespace CEGUI Rect customClipper; bool usingCustomClipper; bool clipToDisplay; + bool colorCodesEnabled; // Stores whether inline color codes formatting should be parsed when rendering this text. }; typedef std::vector ImageryList; diff --git a/vendor/cegui-0.4.0-custom/src/CEGUIFont.cpp b/vendor/cegui-0.4.0-custom/src/CEGUIFont.cpp index 5122e501efc..55004cdfdbb 100644 --- a/vendor/cegui-0.4.0-custom/src/CEGUIFont.cpp +++ b/vendor/cegui-0.4.0-custom/src/CEGUIFont.cpp @@ -53,10 +53,14 @@ // Start of CEGUI namespace section namespace CEGUI { +// Forward declaration of ParseColorCode to allow calls in early member functions (e.g. getTextExtent, getCharAtPixel) before its definition. +static bool ParseColorCode(const String& text, size_t c, float& r, float& g, float& b); + /************************************************************************* static data definitions *************************************************************************/ const argb_t Font::DefaultColour = 0xFFFFFFFF; +bool Font::s_colorCodesEnabled = false; const uint Font::InterGlyphPadSpace = 2; // XML related strings @@ -223,6 +227,13 @@ float Font::getTextExtent(const String& text, float x_scale) const for (size_t c = 0; c < char_count; ++c) { + float cr, cg, cb; + if (ParseColorCode(text, c, cr, cg, cb)) + { + c += 6; + continue; + } + const SCharSize* pCharSize = MapFind ( d_sizes_map, text[c] ); // Ask sub font if not if ( !pCharSize && !d_is_subfont ) { @@ -284,6 +295,13 @@ size_t Font::getCharAtPixel(const String& text, size_t start_char, float pixel, for (size_t c = start_char; c < char_count; ++c) { + float cr, cg, cb; + if (ParseColorCode(text, c, cr, cg, cb)) + { + c += 6; + continue; + } + pos = d_cp_map.find(text[c]); if (pos != end) @@ -741,6 +759,32 @@ size_t Font::getNextWord(const String& in_string, size_t start_idx, String& out_ /************************************************************************* Draw a line of text. No formatting is applied. *************************************************************************/ +// Returns true and fills r/g/b (0.0-1.0) if text[c] begins a valid #RRGGBB color code. +// A color code is exactly 7 chars: '#' followed by 6 hex digits. If text[c] is '#' +// but the following chars are not all hex digits, the '#' is treated as a literal char. +static bool ParseColorCode(const String& text, size_t c, float& r, float& g, float& b) +{ + if (!Font::s_colorCodesEnabled) + return false; + if (text[c] != '#' || c + 7 > text.length()) + return false; + + int digits[6]; + for (int k = 0; k < 6; ++k) + { + utf32 ch = text[c + 1 + k]; + if (ch >= '0' && ch <= '9') digits[k] = ch - '0'; + else if (ch >= 'a' && ch <= 'f') digits[k] = ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') digits[k] = ch - 'A' + 10; + else return false; + } + + r = static_cast((digits[0] << 4) | digits[1]) / 255.0f; + g = static_cast((digits[2] << 4) | digits[3]) / 255.0f; + b = static_cast((digits[4] << 4) | digits[5]) / 255.0f; + return true; +} + void Font::drawTextLine(const String& text, const Vector3& position, const Rect& clip_rect, const ColourRect& colours, float x_scale, float y_scale) const { Vector3 cur_pos(position); @@ -752,8 +796,19 @@ void Font::drawTextLine(const String& text, const Vector3& position, const Rect& const_cast < Font* > ( this )->refreshStringForGlyphs ( text ); // Refresh our glyph set if there are new characters + ColourRect current_colours = colours; + for (size_t c = 0; c < char_count; ++c) { + // Handle inline #RRGGBB color codes — skip the 7-char sequence and update color. + float cr, cg, cb; + if (ParseColorCode(text, c, cr, cg, cb)) + { + current_colours = ColourRect(colour(cr, cg, cb, colours.d_top_left.getAlpha())); + c += 6; + continue; + } + pos = d_cp_map.find(text[c]); const Image* img; @@ -792,7 +847,7 @@ void Font::drawTextLine(const String& text, const Vector3& position, const Rect& } cur_pos.d_y = base_y - (img->getOffsetY() - img->getOffsetY() * y_scale); Size sz(img->getWidth() * x_scale, img->getHeight() * y_scale); - img->draw(cur_pos, sz, clip_rect, colours); + img->draw(cur_pos, sz, clip_rect, current_colours); cur_pos.d_x += horz_advance * x_scale; } @@ -818,7 +873,11 @@ void Font::drawTextLineJustified(const String& text, const Rect& draw_area, cons uint space_count = 0; size_t c; for (c = 0; c < char_count; ++c) + { + float cr, cg, cb; + if (ParseColorCode(text, c, cr, cg, cb)) { c += 6; continue; } if ((text[c] == ' ') || (text[c] == '\t')) ++space_count; + } // The width that must be added to each space character in order to transform the left aligned text in justified text float shared_lost_space = 0.0; @@ -826,8 +885,18 @@ void Font::drawTextLineJustified(const String& text, const Rect& draw_area, cons const_cast < Font* > ( this )->refreshStringForGlyphs ( text ); // Refresh our glyph set if there are new characters + ColourRect current_colours = colours; + for (c = 0; c < char_count; ++c) { + float cr, cg, cb; + if (ParseColorCode(text, c, cr, cg, cb)) + { + current_colours = ColourRect(colour(cr, cg, cb, colours.d_top_left.getAlpha())); + c += 6; + continue; + } + pos = d_cp_map.find(text[c]); if (pos != end) @@ -835,7 +904,7 @@ void Font::drawTextLineJustified(const String& text, const Rect& draw_area, cons const Image* img = pos->second.d_image; cur_pos.d_y = base_y - (img->getOffsetY() - img->getOffsetY() * y_scale); Size sz(img->getWidth() * x_scale, img->getHeight() * y_scale); - img->draw(cur_pos, sz, clip_rect, colours); + img->draw(cur_pos, sz, clip_rect, current_colours); cur_pos.d_x += (float)pos->second.d_horz_advance * x_scale; // That's where we adjust the size of each space character if ((text[c] == ' ') || (text[c] == '\t')) cur_pos.d_x += shared_lost_space; diff --git a/vendor/cegui-0.4.0-custom/src/CEGUIRenderCache.cpp b/vendor/cegui-0.4.0-custom/src/CEGUIRenderCache.cpp index dfaada745cc..691147f9708 100644 --- a/vendor/cegui-0.4.0-custom/src/CEGUIRenderCache.cpp +++ b/vendor/cegui-0.4.0-custom/src/CEGUIRenderCache.cpp @@ -84,8 +84,10 @@ namespace CEGUI finalRect = (*text).target_area; finalRect.offset(basePos); + Font::s_colorCodesEnabled = (*text).colorCodesEnabled; // Temporarily restore the color codes toggle state for this text block (*text).source_font->drawText((*text).text, finalRect, baseZ + (*text).z_offset, *finalClipper, (*text).formatting, (*text).colours); } + Font::s_colorCodesEnabled = false; // Reset to default state } @@ -127,6 +129,7 @@ namespace CEGUI txtinf.z_offset = zOffset; txtinf.colours = cols; txtinf.clipToDisplay = clipToDisplay; + txtinf.colorCodesEnabled = Font::s_colorCodesEnabled; // Cache the current color code formatting enablement status if (clipper) { diff --git a/vendor/cegui-0.4.0-custom/src/CEGUIWindow.cpp b/vendor/cegui-0.4.0-custom/src/CEGUIWindow.cpp index 2cae857cd62..8b69d1c33f0 100644 --- a/vendor/cegui-0.4.0-custom/src/CEGUIWindow.cpp +++ b/vendor/cegui-0.4.0-custom/src/CEGUIWindow.cpp @@ -29,6 +29,7 @@ #include "CEGUIWindowManager.h" #include "CEGUISystem.h" #include "CEGUIFontManager.h" +#include "CEGUIFont.h" #include "CEGUIImagesetManager.h" #include "CEGUIImageset.h" #include "CEGUIMouseCursor.h" @@ -42,6 +43,25 @@ // Start of CEGUI namespace section namespace CEGUI { +namespace { + bool getColorCodesEnabledForWindow(const CEGUI::Window* window) + { + const CEGUI::Window* curr = window; + while (curr) + { + if (curr->isUserStringDefined("ColorCodesEnabled")) + return curr->getUserString("ColorCodesEnabled") == "True"; + + // Stop inheriting if the window is not an auto-window + if (curr->getName().find("__auto_") == CEGUI::String::npos) + break; + + curr = curr->getParent(); + } + return false; + } +} + const String Window::EventNamespace("Window"); /************************************************************************* @@ -1973,7 +1993,15 @@ void Window::render(void) // perform drawing for 'this' Window Renderer* renderer = System::getSingleton().getRenderer(); + + // Set per-window color codes flag before geometry is generated. + // We do this here so widgets that override drawSelf (like Falagard buttons) still respect it. + Font::s_colorCodesEnabled = getColorCodesEnabledForWindow(this); + drawSelf(renderer->getCurrentZ()); + + Font::s_colorCodesEnabled = false; + renderer->advanceZValue(); // render any child windows