Add colored text (not only colored chat).

Add documentation, move files to a proper place and avoid memory leaks.
Make it work with most kind of texts, and allow backgrounds too.
This commit is contained in:
Ekdohibs 2016-05-31 17:30:11 +02:00
parent 1d40385d4a
commit 14ef2b445a
28 changed files with 689 additions and 318 deletions

@ -102,7 +102,7 @@ core.register_chatcommand("help", {
description = "Get help for commands or list privileges", description = "Get help for commands or list privileges",
func = function(name, param) func = function(name, param)
local function format_help_line(cmd, def) local function format_help_line(cmd, def)
local msg = core.colorize("00ffff", "/"..cmd) local msg = core.colorize("#00ffff", "/"..cmd)
if def.params and def.params ~= "" then if def.params and def.params ~= "" then
msg = msg .. " " .. def.params msg = msg .. " " .. def.params
end end

@ -198,19 +198,34 @@ function core.http_add_fetch(httpenv)
return httpenv return httpenv
end end
function core.get_color_escape_sequence(color) if minetest.setting_getbool("disable_escape_sequences") then
--if string.len(color) == 3 then
-- local r = string.sub(color, 1, 1) function core.get_color_escape_sequence(color)
-- local g = string.sub(color, 2, 2) return ""
-- local b = string.sub(color, 3, 3) end
-- color = r .. r .. g .. g .. b .. b
--end function core.get_background_escape_sequence(color)
return ""
end
function core.colorize(color, message)
return message
end
else
local ESCAPE_CHAR = string.char(0x1b)
function core.get_color_escape_sequence(color)
return ESCAPE_CHAR .. "(c@" .. color .. ")"
end
function core.get_background_escape_sequence(color)
return ESCAPE_CHAR .. "(b@" .. color .. ")"
end
function core.colorize(color, message)
return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("#ffffff")
end
--assert(#color == 6, "Color must be six characters in length.")
--return "\v" .. color
return "\v(color;" .. color .. ")"
end end
function core.colorize(color, message)
return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("ffffff")
end

@ -615,6 +615,11 @@ server_announce (Announce server) bool false
# If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net. # If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net.
serverlist_url (Serverlist URL) string servers.minetest.net serverlist_url (Serverlist URL) string servers.minetest.net
# Disable escape sequences, e.g. chat coloring.
# Use this if you want to run a server with pre-0.4.14 clients and you want to disable
# the escape sequences generated by mods.
disable_escape_sequences (Disable escape sequences) bool false
[*Network] [*Network]
# Network port to listen (UDP). # Network port to listen (UDP).

@ -1701,6 +1701,24 @@ numerical form, the raw integer value of an ARGB8 quad:
or string form, a ColorString (defined above): or string form, a ColorString (defined above):
`colorspec = "green"` `colorspec = "green"`
Escape sequences
----------------
Most text can contain escape sequences, that can for example color the text.
There are a few exceptions: tab headers, dropdowns and vertical labels can't.
The following functions provide escape sequences:
* `core.get_color_escape_sequence(color)`:
* `color` is a ColorString
* The escape sequence sets the text color to `color`
* `core.colorize(color, message)`:
* Equivalent to:
`core.get_color_escape_sequence(color) ..
message ..
core.get_color_escape_sequence("#ffffff")`
* `color.get_background_escape_sequence(color)`
* `color` is a ColorString
* The escape sequence sets the background of the whole text element to
`color`. Only defined for item descriptions and tooltips.
Spatial Vectors Spatial Vectors
--------------- ---------------
* `vector.new(a[, b, c])`: returns a vector: * `vector.new(a[, b, c])`: returns a vector:

@ -376,6 +376,7 @@ add_subdirectory(network)
add_subdirectory(script) add_subdirectory(script)
add_subdirectory(unittest) add_subdirectory(unittest)
add_subdirectory(util) add_subdirectory(util)
add_subdirectory(irrlicht_changes)
set(common_SRCS set(common_SRCS
ban.cpp ban.cpp
@ -493,6 +494,7 @@ set(client_SRCS
${common_SRCS} ${common_SRCS}
${sound_SRCS} ${sound_SRCS}
${client_network_SRCS} ${client_network_SRCS}
${client_irrlicht_changes_SRCS}
camera.cpp camera.cpp
client.cpp client.cpp
clientmap.cpp clientmap.cpp

@ -1,6 +1,7 @@
/* /*
CGUITTFont FreeType class for Irrlicht CGUITTFont FreeType class for Irrlicht
Copyright (c) 2009-2010 John Norman Copyright (c) 2009-2010 John Norman
Copyright (c) 2016 Nathanaël Courant
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -545,6 +546,13 @@ void CGUITTFont::setFontHinting(const bool enable, const bool enable_auto_hintin
void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip) void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
{ {
draw(EnrichedString(std::wstring(text.c_str()), color), position, color, hcenter, vcenter, clip);
}
void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
{
std::vector<video::SColor> colors = text.getColors();
if (!Driver) if (!Driver)
return; return;
@ -572,7 +580,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
} }
// Convert to a unicode string. // Convert to a unicode string.
core::ustring utext(text); core::ustring utext = text.getString();
// Set up our render map. // Set up our render map.
core::map<u32, CGUITTGlyphPage*> Render_Map; core::map<u32, CGUITTGlyphPage*> Render_Map;
@ -581,6 +589,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
u32 n; u32 n;
uchar32_t previousChar = 0; uchar32_t previousChar = 0;
core::ustring::const_iterator iter(utext); core::ustring::const_iterator iter(utext);
std::vector<video::SColor> applied_colors;
while (!iter.atEnd()) while (!iter.atEnd())
{ {
uchar32_t currentChar = *iter; uchar32_t currentChar = *iter;
@ -590,7 +599,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
if (currentChar == L'\r') // Mac or Windows breaks if (currentChar == L'\r') // Mac or Windows breaks
{ {
lineBreak = true; lineBreak = true;
if (*(iter + 1) == (uchar32_t)'\n') // Windows line breaks. if (*(iter + 1) == (uchar32_t)'\n') // Windows line breaks.
currentChar = *(++iter); currentChar = *(++iter);
} }
else if (currentChar == (uchar32_t)'\n') // Unix breaks else if (currentChar == (uchar32_t)'\n') // Unix breaks
@ -627,6 +636,9 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy)); page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy));
page->render_source_rects.push_back(glyph.source_rect); page->render_source_rects.push_back(glyph.source_rect);
Render_Map.set(glyph.glyph_page, page); Render_Map.set(glyph.glyph_page, page);
u32 current_color = iter.getPos();
if (current_color < colors.size())
applied_colors.push_back(colors[current_color]);
} }
offset.X += getWidthFromCharacter(currentChar); offset.X += getWidthFromCharacter(currentChar);
@ -645,8 +657,6 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
CGUITTGlyphPage* page = n->getValue(); CGUITTGlyphPage* page = n->getValue();
if (!use_transparency) color.color |= 0xff000000;
if (shadow_offset) { if (shadow_offset) {
for (size_t i = 0; i < page->render_positions.size(); ++i) for (size_t i = 0; i < page->render_positions.size(); ++i)
page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset); page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset);
@ -654,7 +664,17 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
for (size_t i = 0; i < page->render_positions.size(); ++i) for (size_t i = 0; i < page->render_positions.size(); ++i)
page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset); page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset);
} }
Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, color, true); for (size_t i = 0; i < page->render_positions.size(); ++i) {
irr::video::SColor col;
if (!applied_colors.empty()) {
col = applied_colors[i < applied_colors.size() ? i : 0];
} else {
col = irr::video::SColor(255, 255, 255, 255);
}
if (!use_transparency)
col.color |= 0xff000000;
Driver->draw2DImage(page->texture, page->render_positions[i], page->render_source_rects[i], clip, col, true);
}
} }
} }

@ -1,6 +1,7 @@
/* /*
CGUITTFont FreeType class for Irrlicht CGUITTFont FreeType class for Irrlicht
Copyright (c) 2009-2010 John Norman Copyright (c) 2009-2010 John Norman
Copyright (c) 2016 Nathanaël Courant
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any warranty. In no event will the authors be held liable for any
@ -33,6 +34,8 @@
#include <irrlicht.h> #include <irrlicht.h>
#include <ft2build.h> #include <ft2build.h>
#include <vector>
#include "util/enriched_string.h"
#include FT_FREETYPE_H #include FT_FREETYPE_H
namespace irr namespace irr
@ -258,6 +261,10 @@ namespace gui
virtual void draw(const core::stringw& text, const core::rect<s32>& position, virtual void draw(const core::stringw& text, const core::rect<s32>& position,
video::SColor color, bool hcenter=false, bool vcenter=false, video::SColor color, bool hcenter=false, bool vcenter=false,
const core::rect<s32>* clip=0); const core::rect<s32>* clip=0);
virtual void draw(const EnrichedString& text, const core::rect<s32>& position,
video::SColor color, bool hcenter=false, bool vcenter=false,
const core::rect<s32>* clip=0);
//! Returns the dimension of a character produced by this font. //! Returns the dimension of a character produced by this font.
virtual core::dimension2d<u32> getCharDimension(const wchar_t ch) const; virtual core::dimension2d<u32> getCharDimension(const wchar_t ch) const;

@ -267,28 +267,26 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
next_frags.push_back(temp_frag); next_frags.push_back(temp_frag);
} }
std::wstring name_sanitized = removeEscapes(line.name); std::wstring name_sanitized = line.name.c_str();
// Choose an indentation level // Choose an indentation level
if (line.name.empty()) { if (line.name.empty()) {
// Server messages // Server messages
hanging_indentation = 0; hanging_indentation = 0;
} } else if (name_sanitized.size() + 3 <= cols/2) {
else if (name_sanitized.size() + 3 <= cols/2) {
// Names shorter than about half the console width // Names shorter than about half the console width
hanging_indentation = line.name.size() + 3; hanging_indentation = line.name.size() + 3;
} } else {
else {
// Very long names // Very long names
hanging_indentation = 2; hanging_indentation = 2;
} }
ColoredString line_text(line.text); //EnrichedString line_text(line.text);
next_line.first = true; next_line.first = true;
bool text_processing = false; bool text_processing = false;
// Produce fragments and layout them into lines // Produce fragments and layout them into lines
while (!next_frags.empty() || in_pos < line_text.size()) while (!next_frags.empty() || in_pos < line.text.size())
{ {
// Layout fragments into lines // Layout fragments into lines
while (!next_frags.empty()) while (!next_frags.empty())
@ -326,9 +324,9 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
} }
// Produce fragment // Produce fragment
if (in_pos < line_text.size()) if (in_pos < line.text.size())
{ {
u32 remaining_in_input = line_text.size() - in_pos; u32 remaining_in_input = line.text.size() - in_pos;
u32 remaining_in_output = cols - out_column; u32 remaining_in_output = cols - out_column;
// Determine a fragment length <= the minimum of // Determine a fragment length <= the minimum of
@ -338,14 +336,14 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
while (frag_length < remaining_in_input && while (frag_length < remaining_in_input &&
frag_length < remaining_in_output) frag_length < remaining_in_output)
{ {
if (isspace(line_text[in_pos + frag_length])) if (isspace(line.text.getString()[in_pos + frag_length]))
space_pos = frag_length; space_pos = frag_length;
++frag_length; ++frag_length;
} }
if (space_pos != 0 && frag_length < remaining_in_input) if (space_pos != 0 && frag_length < remaining_in_input)
frag_length = space_pos + 1; frag_length = space_pos + 1;
temp_frag.text = line_text.substr(in_pos, frag_length); temp_frag.text = line.text.substr(in_pos, frag_length);
temp_frag.column = 0; temp_frag.column = 0;
//temp_frag.bold = 0; //temp_frag.bold = 0;
next_frags.push_back(temp_frag); next_frags.push_back(temp_frag);
@ -729,19 +727,22 @@ ChatBuffer& ChatBackend::getRecentBuffer()
return m_recent_buffer; return m_recent_buffer;
} }
std::wstring ChatBackend::getRecentChat() EnrichedString ChatBackend::getRecentChat()
{ {
std::wostringstream stream; EnrichedString result;
for (u32 i = 0; i < m_recent_buffer.getLineCount(); ++i) for (u32 i = 0; i < m_recent_buffer.getLineCount(); ++i)
{ {
const ChatLine& line = m_recent_buffer.getLine(i); const ChatLine& line = m_recent_buffer.getLine(i);
if (i != 0) if (i != 0)
stream << L"\n"; result += L"\n";
if (!line.name.empty()) if (!line.name.empty()) {
stream << L"<" << line.name << L"> "; result += L"<";
stream << line.text; result += line.name;
result += L"> ";
}
result += line.text;
} }
return stream.str(); return result;
} }
ChatPrompt& ChatBackend::getPrompt() ChatPrompt& ChatBackend::getPrompt()

@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <list> #include <list>
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include "util/coloredstring.h" #include "util/enriched_string.h"
// Chat console related classes // Chat console related classes
@ -34,9 +34,9 @@ struct ChatLine
// age in seconds // age in seconds
f32 age; f32 age;
// name of sending player, or empty if sent by server // name of sending player, or empty if sent by server
std::wstring name; EnrichedString name;
// message text // message text
ColoredString text; EnrichedString text;
ChatLine(std::wstring a_name, std::wstring a_text): ChatLine(std::wstring a_name, std::wstring a_text):
age(0.0), age(0.0),
@ -44,12 +44,19 @@ struct ChatLine
text(a_text) text(a_text)
{ {
} }
ChatLine(EnrichedString a_name, EnrichedString a_text):
age(0.0),
name(a_name),
text(a_text)
{
}
}; };
struct ChatFormattedFragment struct ChatFormattedFragment
{ {
// text string // text string
std::wstring text; EnrichedString text;
// starting column // starting column
u32 column; u32 column;
// formatting // formatting
@ -262,7 +269,7 @@ public:
// Get the recent messages buffer // Get the recent messages buffer
ChatBuffer& getRecentBuffer(); ChatBuffer& getRecentBuffer();
// Concatenate all recent messages // Concatenate all recent messages
std::wstring getRecentChat(); EnrichedString getRecentChat();
// Get the console prompt // Get the console prompt
ChatPrompt& getPrompt(); ChatPrompt& getPrompt();

@ -1,6 +1,5 @@
set(client_SRCS set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
PARENT_SCOPE PARENT_SCOPE
) )

@ -202,6 +202,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("server_name", ""); settings->setDefault("server_name", "");
settings->setDefault("server_description", ""); settings->setDefault("server_description", "");
settings->setDefault("disable_escape_sequences", "false");
#if USE_FREETYPE #if USE_FREETYPE
settings->setDefault("freetype", "true"); settings->setDefault("freetype", "true");
settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "liberationsans.ttf")); settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "liberationsans.ttf"));

@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h" #include "log.h"
#include "filesys.h" #include "filesys.h"
#include "gettext.h" #include "gettext.h"
#include "client/guiChatConsole.h" #include "guiChatConsole.h"
#include "guiFormSpecMenu.h" #include "guiFormSpecMenu.h"
#include "guiKeyChangeMenu.h" #include "guiKeyChangeMenu.h"
#include "guiPasswordChange.h" #include "guiPasswordChange.h"
@ -55,14 +55,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tool.h" #include "tool.h"
#include "util/directiontables.h" #include "util/directiontables.h"
#include "util/pointedthing.h" #include "util/pointedthing.h"
#include "irrlicht_changes/static_text.h"
#include "version.h" #include "version.h"
#include "minimap.h" #include "minimap.h"
#include "mapblock_mesh.h" #include "mapblock_mesh.h"
#if USE_FREETYPE
#include "util/statictext.h"
#endif
#include "sound.h" #include "sound.h"
#if USE_SOUND #if USE_SOUND
@ -541,7 +538,7 @@ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe,
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);
g_profiler->printPage(os, show_profiler, show_profiler_max); g_profiler->printPage(os, show_profiler, show_profiler_max);
std::wstring text = utf8_to_wide(os.str()); std::wstring text = utf8_to_wide(os.str());
guitext_profiler->setText(text.c_str()); setStaticText(guitext_profiler, text.c_str());
guitext_profiler->setVisible(true); guitext_profiler->setVisible(true);
s32 w = fe->getTextWidth(text.c_str()); s32 w = fe->getTextWidth(text.c_str());
@ -1244,7 +1241,11 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
// Get new messages from error log buffer // Get new messages from error log buffer
while (!chat_log_error_buf.empty()) { while (!chat_log_error_buf.empty()) {
chat_backend.addMessage(L"", utf8_to_wide(chat_log_error_buf.get())); std::wstring error_message = utf8_to_wide(chat_log_error_buf.get());
if (!g_settings->getBool("disable_escape_sequences")) {
error_message = L"\x1b(c@red)" + error_message + L"\x1b(c@white)";
}
chat_backend.addMessage(L"", error_message);
} }
// Get new messages from client // Get new messages from client
@ -1259,10 +1260,10 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
// Display all messages in a static text element // Display all messages in a static text element
unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount(); unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount();
std::wstring recent_chat = chat_backend.getRecentChat(); EnrichedString recent_chat = chat_backend.getRecentChat();
unsigned int line_height = g_fontengine->getLineHeight(); unsigned int line_height = g_fontengine->getLineHeight();
guitext_chat->setText(recent_chat.c_str()); setStaticText(guitext_chat, recent_chat);
// Update gui element size and position // Update gui element size and position
s32 chat_y = 5 + line_height; s32 chat_y = 5 + line_height;
@ -1271,7 +1272,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
chat_y += line_height; chat_y += line_height;
// first pass to calculate height of text to be set // first pass to calculate height of text to be set
s32 width = std::min(g_fontengine->getTextWidth(recent_chat) + 10, s32 width = std::min(g_fontengine->getTextWidth(recent_chat.c_str()) + 10,
porting::getWindowSize().X - 20); porting::getWindowSize().X - 20);
core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y); core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y);
guitext_chat->setRelativePosition(rect); guitext_chat->setRelativePosition(rect);
@ -2218,45 +2219,39 @@ bool Game::createClient(const std::string &playername,
bool Game::initGui() bool Game::initGui()
{ {
// First line of debug text // First line of debug text
guitext = guienv->addStaticText( guitext = addStaticText(guienv,
utf8_to_wide(PROJECT_NAME_C).c_str(), utf8_to_wide(PROJECT_NAME_C).c_str(),
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
false, false, guiroot); false, false, guiroot);
// Second line of debug text // Second line of debug text
guitext2 = guienv->addStaticText( guitext2 = addStaticText(guienv,
L"", L"",
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
false, false, guiroot); false, false, guiroot);
// At the middle of the screen // At the middle of the screen
// Object infos are shown in this // Object infos are shown in this
guitext_info = guienv->addStaticText( guitext_info = addStaticText(guienv,
L"", L"",
core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200), core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200),
false, true, guiroot); false, true, guiroot);
// Status text (displays info when showing and hiding GUI stuff, etc.) // Status text (displays info when showing and hiding GUI stuff, etc.)
guitext_status = guienv->addStaticText( guitext_status = addStaticText(guienv,
L"<Status>", L"<Status>",
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
false, false, guiroot); false, false, guiroot);
guitext_status->setVisible(false); guitext_status->setVisible(false);
#if USE_FREETYPE
// Colored chat support when using FreeType
guitext_chat = new gui::StaticText(L"", false, guienv, guiroot, -1, core::rect<s32>(0, 0, 0, 0), false);
guitext_chat->setWordWrap(true);
guitext_chat->drop();
#else
// Standard chat when FreeType is disabled
// Chat text // Chat text
guitext_chat = guienv->addStaticText( guitext_chat = addStaticText(
guienv,
L"", L"",
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
//false, false); // Disable word wrap as of now //false, false); // Disable word wrap as of now
false, true, guiroot); false, true, guiroot);
#endif
// Remove stale "recent" chat messages from previous connections // Remove stale "recent" chat messages from previous connections
chat_backend->clearRecentChat(); chat_backend->clearRecentChat();
@ -2270,7 +2265,7 @@ bool Game::initGui()
} }
// Profiler text (size is updated when text is updated) // Profiler text (size is updated when text is updated)
guitext_profiler = guienv->addStaticText( guitext_profiler = addStaticText(guienv,
L"<Profiler>", L"<Profiler>",
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
false, false, guiroot); false, false, guiroot);
@ -4308,12 +4303,12 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
<< ", v_range = " << draw_control->wanted_range << ", v_range = " << draw_control->wanted_range
<< std::setprecision(3) << std::setprecision(3)
<< ", RTT = " << client->getRTT(); << ", RTT = " << client->getRTT();
guitext->setText(utf8_to_wide(os.str()).c_str()); setStaticText(guitext, utf8_to_wide(os.str()).c_str());
guitext->setVisible(true); guitext->setVisible(true);
} else if (flags.show_hud || flags.show_chat) { } else if (flags.show_hud || flags.show_chat) {
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);
os << PROJECT_NAME_C " " << g_version_hash; os << PROJECT_NAME_C " " << g_version_hash;
guitext->setText(utf8_to_wide(os.str()).c_str()); setStaticText(guitext, utf8_to_wide(os.str()).c_str());
guitext->setVisible(true); guitext->setVisible(true);
} else { } else {
guitext->setVisible(false); guitext->setVisible(false);
@ -4350,7 +4345,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
} }
} }
guitext2->setText(utf8_to_wide(os.str()).c_str()); setStaticText(guitext2, utf8_to_wide(os.str()).c_str());
guitext2->setVisible(true); guitext2->setVisible(true);
core::rect<s32> rect( core::rect<s32> rect(
@ -4362,7 +4357,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
guitext2->setVisible(false); guitext2->setVisible(false);
} }
guitext_info->setText(infotext.c_str()); setStaticText(guitext_info, infotext.c_str());
guitext_info->setVisible(flags.show_hud && g_menumgr.menuCount() == 0); guitext_info->setVisible(flags.show_hud && g_menumgr.menuCount() == 0);
float statustext_time_max = 1.5; float statustext_time_max = 1.5;
@ -4376,7 +4371,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
} }
} }
guitext_status->setText(statustext.c_str()); setStaticText(guitext_status, statustext.c_str());
guitext_status->setVisible(!statustext.empty()); guitext_status->setVisible(!statustext.empty());
if (!statustext.empty()) { if (!statustext.empty()) {

@ -346,9 +346,9 @@ void GUIChatConsole::drawText()
// Draw colored text if FreeType is enabled // Draw colored text if FreeType is enabled
irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(m_font); irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(m_font);
tmp->draw( tmp->draw(
fragment.text.c_str(), fragment.text,
destrect, destrect,
fragment.text.getColors(), video::SColor(255, 255, 255, 255),
false, false,
false, false,
&AbsoluteClippingRect); &AbsoluteClippingRect);

@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h" #include "log.h"
#include "fontengine.h" #include "fontengine.h"
#include "guiscalingfilter.h" #include "guiscalingfilter.h"
#include "irrlicht_changes/static_text.h"
#ifdef __ANDROID__ #ifdef __ANDROID__
#include "client/tile.h" #include "client/tile.h"
@ -172,15 +173,16 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
m_sound_manager = &dummySoundManager; m_sound_manager = &dummySoundManager;
//create topleft header //create topleft header
std::wstring t = utf8_to_wide(std::string(PROJECT_NAME_C " ") + m_toplefttext = utf8_to_wide(std::string(PROJECT_NAME_C " ") +
g_version_hash); g_version_hash);
core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(t), g_fontengine->getTextHeight()); core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
g_fontengine->getTextHeight());
rect += v2s32(4, 0); rect += v2s32(4, 0);
m_irr_toplefttext = m_irr_toplefttext =
m_device->getGUIEnvironment()->addStaticText(t.c_str(), addStaticText(m_device->getGUIEnvironment(), m_toplefttext,
rect,false,true,0,-1); rect, false, true, 0, -1);
//create formspecsource //create formspecsource
m_formspecgui = new FormspecFormSource(""); m_formspecgui = new FormspecFormSource("");
@ -578,7 +580,7 @@ void GUIEngine::setTopleftText(std::string append)
toset += utf8_to_wide(append); toset += utf8_to_wide(append);
} }
m_irr_toplefttext->setText(toset.c_str()); m_toplefttext = toset;
updateTopLeftTextSize(); updateTopLeftTextSize();
} }
@ -586,15 +588,14 @@ void GUIEngine::setTopleftText(std::string append)
/******************************************************************************/ /******************************************************************************/
void GUIEngine::updateTopLeftTextSize() void GUIEngine::updateTopLeftTextSize()
{ {
std::wstring text = m_irr_toplefttext->getText(); core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
g_fontengine->getTextHeight());
core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(text), g_fontengine->getTextHeight()); rect += v2s32(4, 0);
rect += v2s32(4, 0);
m_irr_toplefttext->remove(); m_irr_toplefttext->remove();
m_irr_toplefttext = m_irr_toplefttext =
m_device->getGUIEnvironment()->addStaticText(text.c_str(), addStaticText(m_device->getGUIEnvironment(), m_toplefttext,
rect,false,true,0,-1); rect, false, true, 0, -1);
} }
/******************************************************************************/ /******************************************************************************/

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiFormSpecMenu.h" #include "guiFormSpecMenu.h"
#include "sound.h" #include "sound.h"
#include "client/tile.h" #include "client/tile.h"
#include "util/enriched_string.h"
/******************************************************************************/ /******************************************************************************/
/* Typedefs and macros */ /* Typedefs and macros */
@ -275,6 +276,8 @@ private:
/** pointer to gui element shown at topleft corner */ /** pointer to gui element shown at topleft corner */
irr::gui::IGUIStaticText* m_irr_toplefttext; irr::gui::IGUIStaticText* m_irr_toplefttext;
/** and text that is in it */
EnrichedString m_toplefttext;
/** initialize cloud subsystem */ /** initialize cloud subsystem */
void cloudInit(); void cloudInit();

@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/hex.h" #include "util/hex.h"
#include "util/numeric.h" #include "util/numeric.h"
#include "util/string.h" // for parseColorString() #include "util/string.h" // for parseColorString()
#include "irrlicht_changes/static_text.h"
#include "guiscalingfilter.h" #include "guiscalingfilter.h"
#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9 #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
@ -249,37 +250,6 @@ std::vector<std::string>* GUIFormSpecMenu::getDropDownValues(const std::string &
return NULL; return NULL;
} }
static std::vector<std::string> split(const std::string &s, char delim)
{
std::vector<std::string> tokens;
std::string current = "";
bool last_was_escape = false;
for (unsigned int i = 0; i < s.size(); i++) {
char si = s.c_str()[i];
if (last_was_escape) {
current += '\\';
current += si;
last_was_escape = false;
} else {
if (si == delim) {
tokens.push_back(current);
current = "";
last_was_escape = false;
} else if (si == '\\') {
last_was_escape = true;
} else {
current += si;
last_was_escape = false;
}
}
}
//push last element
tokens.push_back(current);
return tokens;
}
void GUIFormSpecMenu::parseSize(parserData* data,std::string element) void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
{ {
std::vector<std::string> parts = split(element,','); std::vector<std::string> parts = split(element,',');
@ -966,7 +936,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
} }
e->setPasswordBox(true,L'*'); e->setPasswordBox(true,L'*');
@ -1021,7 +991,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
if (name == "") if (name == "")
{ {
// spec field id to 0, this stops submit searching for a value that isn't there // spec field id to 0, this stops submit searching for a value that isn't there
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
} }
else else
{ {
@ -1056,7 +1026,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
} }
} }
@ -1117,7 +1087,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
if (name == "") if (name == "")
{ {
// spec field id to 0, this stops submit searching for a value that isn't there // spec field id to 0, this stops submit searching for a value that isn't there
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
} }
else else
{ {
@ -1161,7 +1131,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
} }
} }
m_fields.push_back(spec); m_fields.push_back(spec);
@ -1230,7 +1200,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
258+m_fields.size() 258+m_fields.size()
); );
gui::IGUIStaticText *e = gui::IGUIStaticText *e =
Environment->addStaticText(spec.flabel.c_str(), addStaticText(Environment, spec.flabel.c_str(),
rect, false, false, this, spec.fid); rect, false, false, this, spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, e->setTextAlignment(gui::EGUIA_UPPERLEFT,
gui::EGUIA_CENTER); gui::EGUIA_CENTER);
@ -1284,7 +1254,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
258+m_fields.size() 258+m_fields.size()
); );
gui::IGUIStaticText *t = gui::IGUIStaticText *t =
Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid); addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
m_fields.push_back(spec); m_fields.push_back(spec);
return; return;
@ -1910,7 +1880,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{ {
assert(m_tooltip_element == NULL); assert(m_tooltip_element == NULL);
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18)); m_tooltip_element = addStaticText(Environment, L"",core::rect<s32>(0,0,110,18));
m_tooltip_element->enableOverrideColor(true); m_tooltip_element->enableOverrideColor(true);
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor); m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setDrawBackground(true); m_tooltip_element->setDrawBackground(true);
@ -2255,7 +2225,6 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
std::wstring tooltip_text = L""; std::wstring tooltip_text = L"";
if (hovering && !m_selected_item) { if (hovering && !m_selected_item) {
tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description); tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description);
tooltip_text = unescape_enriched(tooltip_text);
} }
if (tooltip_text != L"") { if (tooltip_text != L"") {
std::vector<std::wstring> tt_rows = str_split(tooltip_text, L'\n'); std::vector<std::wstring> tt_rows = str_split(tooltip_text, L'\n');
@ -2263,7 +2232,7 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
m_tooltip_element->setOverrideColor(m_default_tooltip_color); m_tooltip_element->setOverrideColor(m_default_tooltip_color);
m_tooltip_element->setVisible(true); m_tooltip_element->setVisible(true);
this->bringToFront(m_tooltip_element); this->bringToFront(m_tooltip_element);
m_tooltip_element->setText(tooltip_text.c_str()); setStaticText(m_tooltip_element, tooltip_text.c_str());
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
#if IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2 #if IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5; s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
@ -2535,8 +2504,10 @@ void GUIFormSpecMenu::drawMenu()
iter != m_fields.end(); ++iter) { iter != m_fields.end(); ++iter) {
if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") { if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") {
if (m_old_tooltip != m_tooltips[iter->fname].tooltip) { if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
m_old_tooltip = m_tooltips[iter->fname].tooltip; m_old_tooltip = m_tooltips[iter->fname].tooltip;
m_tooltip_element->setText(m_tooltips[iter->fname].tooltip.c_str()); setStaticText(m_tooltip_element, m_tooltips[iter->fname].tooltip.c_str());
std::vector<std::wstring> tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\n'); std::vector<std::wstring> tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\n');
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5; s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
@ -2558,8 +2529,6 @@ void GUIFormSpecMenu::drawMenu()
core::position2d<s32>(tooltip_x, tooltip_y), core::position2d<s32>(tooltip_x, tooltip_y),
core::dimension2d<s32>(tooltip_width, tooltip_height))); core::dimension2d<s32>(tooltip_width, tooltip_height)));
} }
m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
m_tooltip_element->setVisible(true); m_tooltip_element->setVisible(true);
this->bringToFront(m_tooltip_element); this->bringToFront(m_tooltip_element);
break; break;
@ -2568,6 +2537,8 @@ void GUIFormSpecMenu::drawMenu()
} }
} }
m_tooltip_element->draw();
/* /*
Draw dragged item stack Draw dragged item stack
*/ */

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiTable.h" #include "guiTable.h"
#include "network/networkprotocol.h" #include "network/networkprotocol.h"
#include "util/string.h" #include "util/string.h"
#include "util/enriched_string.h"
class IGameDef; class IGameDef;
class InventoryManager; class InventoryManager;
@ -202,7 +203,8 @@ class GUIFormSpecMenu : public GUIModalMenu
fname(name), fname(name),
fid(id) fid(id)
{ {
flabel = unescape_enriched(label); //flabel = unescape_enriched(label);
flabel = label;
fdefault = unescape_enriched(default_text); fdefault = unescape_enriched(default_text);
send = false; send = false;
ftype = f_Unknown; ftype = f_Unknown;
@ -239,7 +241,8 @@ class GUIFormSpecMenu : public GUIModalMenu
bgcolor(a_bgcolor), bgcolor(a_bgcolor),
color(a_color) color(a_color)
{ {
tooltip = unescape_enriched(utf8_to_wide(a_tooltip)); //tooltip = unescape_enriched(utf8_to_wide(a_tooltip));
tooltip = utf8_to_wide(a_tooltip);
} }
std::wstring tooltip; std::wstring tooltip;
irr::video::SColor bgcolor; irr::video::SColor bgcolor;
@ -256,7 +259,8 @@ class GUIFormSpecMenu : public GUIModalMenu
rect(a_rect), rect(a_rect),
parent_button(NULL) parent_button(NULL)
{ {
text = unescape_enriched(a_text); //text = unescape_enriched(a_text);
text = a_text;
} }
StaticTextSpec(const std::wstring &a_text, StaticTextSpec(const std::wstring &a_text,
const core::rect<s32> &a_rect, const core::rect<s32> &a_rect,
@ -264,7 +268,8 @@ class GUIFormSpecMenu : public GUIModalMenu
rect(a_rect), rect(a_rect),
parent_button(a_parent_button) parent_button(a_parent_button)
{ {
text = unescape_enriched(a_text); //text = unescape_enriched(a_text);
text = a_text;
} }
std::wstring text; std::wstring text;
core::rect<s32> rect; core::rect<s32> rect;

@ -0,0 +1,7 @@
if (BUILD_CLIENT)
set(client_irrlicht_changes_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/static_text.cpp
PARENT_SCOPE
)
endif()

@ -1,12 +1,12 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt // Copyright (C) 2002-2012 Nikolaus Gebhardt
// Copyright (C) 2016 Nathanaël Courant:
// Modified the functions to use EnrichedText instead of string.
// This file is part of the "Irrlicht Engine". // This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h // For conditions of distribution and use, see copyright notice in irrlicht.h
#include "statictext.h" #include "static_text.h"
#ifdef _IRR_COMPILE_WITH_GUI_ #ifdef _IRR_COMPILE_WITH_GUI_
//Only compile this if freetype is enabled.
#include <vector> #include <vector>
#include <string> #include <string>
#include <iostream> #include <iostream>
@ -17,15 +17,21 @@
#include <rect.h> #include <rect.h>
#include <SColor.h> #include <SColor.h>
#include "cguittfont/xCGUITTFont.h" #if USE_FREETYPE
#include "cguittfont/xCGUITTFont.h"
#endif
#include "util/string.h" #include "util/string.h"
namespace irr namespace irr
{ {
#if USE_FREETYPE
namespace gui namespace gui
{ {
//! constructor //! constructor
StaticText::StaticText(const wchar_t* text, bool border, StaticText::StaticText(const EnrichedString &text, bool border,
IGUIEnvironment* environment, IGUIElement* parent, IGUIEnvironment* environment, IGUIElement* parent,
s32 id, const core::rect<s32>& rectangle, s32 id, const core::rect<s32>& rectangle,
bool background) bool background)
@ -40,7 +46,8 @@ StaticText::StaticText(const wchar_t* text, bool border,
setDebugName("StaticText"); setDebugName("StaticText");
#endif #endif
Text = text; Text = text.c_str();
cText = text;
if (environment && environment->getSkin()) if (environment && environment->getSkin())
{ {
BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE); BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE);
@ -55,7 +62,6 @@ StaticText::~StaticText()
OverrideFont->drop(); OverrideFont->drop();
} }
//! draws the element and its children //! draws the element and its children
void StaticText::draw() void StaticText::draw()
{ {
@ -88,7 +94,7 @@ void StaticText::draw()
} }
// draw the text // draw the text
if (Text.size()) if (cText.size())
{ {
IGUIFont* font = getActiveFont(); IGUIFont* font = getActiveFont();
@ -105,10 +111,11 @@ void StaticText::draw()
if (HAlign == EGUIA_LOWERRIGHT) if (HAlign == EGUIA_LOWERRIGHT)
{ {
frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X - frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
font->getDimension(Text.c_str()).Width; font->getDimension(cText.c_str()).Width;
} }
font->draw(Text.c_str(), frameRect, irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
tmp->draw(cText, frameRect,
OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
} }
@ -138,16 +145,17 @@ void StaticText::draw()
font->getDimension(BrokenText[i].c_str()).Width; font->getDimension(BrokenText[i].c_str()).Width;
} }
std::vector<irr::video::SColor> colors; //std::vector<irr::video::SColor> colors;
std::wstring str; //std::wstring str;
EnrichedString str = BrokenText[i];
str = colorizeText(BrokenText[i].c_str(), colors, previous_color); //str = colorizeText(BrokenText[i].c_str(), colors, previous_color);
if (!colors.empty()) //if (!colors.empty())
previous_color = colors[colors.size() - 1]; // previous_color = colors[colors.size() - 1];
irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font); irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
tmp->draw(str.c_str(), r, tmp->draw(str, r,
colors, previous_color, // FIXME
HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
r.LowerRightCorner.Y += height; r.LowerRightCorner.Y += height;
@ -340,17 +348,17 @@ void StaticText::breakText()
LastBreakFont = font; LastBreakFont = font;
core::stringw line; EnrichedString line;
core::stringw word; EnrichedString word;
core::stringw whitespace; EnrichedString whitespace;
s32 size = Text.size(); s32 size = cText.size();
s32 length = 0; s32 length = 0;
s32 elWidth = RelativeRect.getWidth(); s32 elWidth = RelativeRect.getWidth();
if (Border) if (Border)
elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X); elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X);
wchar_t c; wchar_t c;
std::vector<irr::video::SColor> colors; //std::vector<irr::video::SColor> colors;
// We have to deal with right-to-left and left-to-right differently // We have to deal with right-to-left and left-to-right differently
// However, most parts of the following code is the same, it's just // However, most parts of the following code is the same, it's just
@ -360,17 +368,17 @@ void StaticText::breakText()
// regular (left-to-right) // regular (left-to-right)
for (s32 i=0; i<size; ++i) for (s32 i=0; i<size; ++i)
{ {
c = Text[i]; c = cText.getString()[i];
bool lineBreak = false; bool lineBreak = false;
if (c == L'\r') // Mac or Windows breaks if (c == L'\r') // Mac or Windows breaks
{ {
lineBreak = true; lineBreak = true;
if (Text[i+1] == L'\n') // Windows breaks //if (Text[i+1] == L'\n') // Windows breaks
{ //{
Text.erase(i+1); // Text.erase(i+1);
--size; // --size;
} //}
c = '\0'; c = '\0';
} }
else if (c == L'\n') // Unix breaks else if (c == L'\n') // Unix breaks
@ -383,7 +391,8 @@ void StaticText::breakText()
if ( !isWhitespace ) if ( !isWhitespace )
{ {
// part of a word // part of a word
word += c; //word += c;
word.addChar(cText, i);
} }
if ( isWhitespace || i == (size-1)) if ( isWhitespace || i == (size-1))
@ -393,20 +402,21 @@ void StaticText::breakText()
// here comes the next whitespace, look if // here comes the next whitespace, look if
// we must break the last word to the next line. // we must break the last word to the next line.
const s32 whitelgth = font->getDimension(whitespace.c_str()).Width; const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
const std::wstring sanitized = removeEscapes(word.c_str()); //const std::wstring sanitized = removeEscapes(word.c_str());
const s32 wordlgth = font->getDimension(sanitized.c_str()).Width; const s32 wordlgth = font->getDimension(word.c_str()).Width;
if (wordlgth > elWidth) if (wordlgth > elWidth)
{ {
// This word is too long to fit in the available space, look for // This word is too long to fit in the available space, look for
// the Unicode Soft HYphen (SHY / 00AD) character for a place to // the Unicode Soft HYphen (SHY / 00AD) character for a place to
// break the word at // break the word at
int where = word.findFirst( wchar_t(0x00AD) ); int where = core::stringw(word.c_str()).findFirst( wchar_t(0x00AD) );
if (where != -1) if (where != -1)
{ {
core::stringw first = word.subString(0, where); EnrichedString first = word.substr(0, where);
core::stringw second = word.subString(where, word.size() - where); EnrichedString second = word.substr(where, word.size() - where);
BrokenText.push_back(line + first + L"-"); first.addCharNoColor(L'-');
BrokenText.push_back(line + first);
const s32 secondLength = font->getDimension(second.c_str()).Width; const s32 secondLength = font->getDimension(second.c_str()).Width;
length = secondLength; length = secondLength;
@ -437,13 +447,13 @@ void StaticText::breakText()
length += whitelgth + wordlgth; length += whitelgth + wordlgth;
} }
word = L""; word.clear();
whitespace = L""; whitespace.clear();
} }
if ( isWhitespace ) if ( isWhitespace && c != 0)
{ {
whitespace += c; whitespace.addChar(cText, i);
} }
// compute line break // compute line break
@ -452,9 +462,9 @@ void StaticText::breakText()
line += whitespace; line += whitespace;
line += word; line += word;
BrokenText.push_back(line); BrokenText.push_back(line);
line = L""; line.clear();
word = L""; word.clear();
whitespace = L""; whitespace.clear();
length = 0; length = 0;
} }
} }
@ -469,17 +479,17 @@ void StaticText::breakText()
// right-to-left // right-to-left
for (s32 i=size; i>=0; --i) for (s32 i=size; i>=0; --i)
{ {
c = Text[i]; c = cText.getString()[i];
bool lineBreak = false; bool lineBreak = false;
if (c == L'\r') // Mac or Windows breaks if (c == L'\r') // Mac or Windows breaks
{ {
lineBreak = true; lineBreak = true;
if ((i>0) && Text[i-1] == L'\n') // Windows breaks //if ((i>0) && Text[i-1] == L'\n') // Windows breaks
{ //{
Text.erase(i-1); // Text.erase(i-1);
--size; // --size;
} //}
c = '\0'; c = '\0';
} }
else if (c == L'\n') // Unix breaks else if (c == L'\n') // Unix breaks
@ -512,12 +522,13 @@ void StaticText::breakText()
length += whitelgth + wordlgth; length += whitelgth + wordlgth;
} }
word = L""; word.clear();
whitespace = L""; whitespace.clear();
} }
if (c != 0) if (c != 0)
whitespace = core::stringw(&c, 1) + whitespace; // whitespace = core::stringw(&c, 1) + whitespace;
whitespace = cText.substr(i, 1) + whitespace;
// compute line break // compute line break
if (lineBreak) if (lineBreak)
@ -525,16 +536,17 @@ void StaticText::breakText()
line = whitespace + line; line = whitespace + line;
line = word + line; line = word + line;
BrokenText.push_back(line); BrokenText.push_back(line);
line = L""; line.clear();
word = L""; word.clear();
whitespace = L""; whitespace.clear();
length = 0; length = 0;
} }
} }
else else
{ {
// yippee this is a word.. // yippee this is a word..
word = core::stringw(&c, 1) + word; //word = core::stringw(&c, 1) + word;
word = cText.substr(i, 1) + word;
} }
} }
@ -548,7 +560,17 @@ void StaticText::breakText()
//! Sets the new caption of this element. //! Sets the new caption of this element.
void StaticText::setText(const wchar_t* text) void StaticText::setText(const wchar_t* text)
{ {
IGUIElement::setText(text); setText(EnrichedString(text));
}
//! 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(); breakText();
} }
@ -598,7 +620,7 @@ s32 StaticText::getTextWidth() const
} }
else else
{ {
return font->getDimension(Text.c_str()).Width; return font->getDimension(cText.c_str()).Width;
} }
} }
@ -648,6 +670,9 @@ void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWr
} }
} // end namespace gui } // end namespace gui
#endif // USE_FREETYPE
} // end namespace irr } // end namespace irr

@ -1,4 +1,6 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt // Copyright (C) 2002-2012 Nikolaus Gebhardt
// Copyright (C) 2016 Nathanaël Courant
// Modified this class to work with EnrichedStrings too
// This file is part of the "Irrlicht Engine". // This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h // For conditions of distribution and use, see copyright notice in irrlicht.h
@ -11,18 +13,30 @@
#include "IGUIStaticText.h" #include "IGUIStaticText.h"
#include "irrArray.h" #include "irrArray.h"
#include "log.h"
#include <vector> #include <vector>
#include "util/enriched_string.h"
#include "config.h"
#include <IGUIEnvironment.h>
#if USE_FREETYPE
namespace irr namespace irr
{ {
namespace gui namespace gui
{ {
const EGUI_ELEMENT_TYPE EGUIET_ENRICHED_STATIC_TEXT = (EGUI_ELEMENT_TYPE)(0x1000);
class StaticText : public IGUIStaticText class StaticText : public IGUIStaticText
{ {
public: public:
//! constructor //! constructor
StaticText(const wchar_t* text, bool border, IGUIEnvironment* environment, StaticText(const EnrichedString &text, bool border, IGUIEnvironment* environment,
IGUIElement* parent, s32 id, const core::rect<s32>& rectangle, IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
bool background = false); bool background = false);
@ -121,6 +135,16 @@ namespace gui
//! Reads attributes of the element //! Reads attributes of the element
virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options); virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
virtual bool hasType(EGUI_ELEMENT_TYPE t) const {
return (t == EGUIET_ENRICHED_STATIC_TEXT) || (t == EGUIET_STATIC_TEXT);
};
virtual bool hasType(EGUI_ELEMENT_TYPE t) {
return (t == EGUIET_ENRICHED_STATIC_TEXT) || (t == EGUIET_STATIC_TEXT);
};
void setText(const EnrichedString &text);
private: private:
//! Breaks the single text line. //! Breaks the single text line.
@ -139,12 +163,106 @@ namespace gui
gui::IGUIFont* OverrideFont; gui::IGUIFont* OverrideFont;
gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated. gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated.
core::array< core::stringw > BrokenText; EnrichedString cText;
core::array< EnrichedString > BrokenText;
}; };
} // end namespace gui } // end namespace gui
} // end namespace irr } // end namespace irr
inline irr::gui::IGUIStaticText *addStaticText(
irr::gui::IGUIEnvironment *guienv,
const EnrichedString &text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
if (parent == NULL) {
// parent is NULL, so we must find one, or we need not to drop
// result, but then there will be a memory leak.
//
// What Irrlicht does is to use guienv as a parent, but the problem
// is that guienv is here only an IGUIEnvironment, while it is a
// CGUIEnvironment in Irrlicht, which inherits from both IGUIElement
// and IGUIEnvironment.
//
// A solution would be to dynamic_cast guienv to a
// IGUIElement*, but Irrlicht is shipped without rtti support
// in some distributions, causing the dymanic_cast to segfault.
//
// Thus, to find the parent, we create a dummy StaticText and ask
// for its parent, and then remove it.
irr::gui::IGUIStaticText *dummy_text =
guienv->addStaticText(L"", rectangle, border, wordWrap,
parent, id, fillBackground);
parent = dummy_text->getParent();
dummy_text->remove();
}
irr::gui::IGUIStaticText *result = new irr::gui::StaticText(
text, border, guienv, parent,
id, rectangle, fillBackground);
result->setWordWrap(wordWrap);
result->drop();
return result;
}
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
{
// dynamic_cast not possible due to some distributions shipped
// without rtti support in irrlicht
if (static_text->hasType(irr::gui::EGUIET_ENRICHED_STATIC_TEXT)) {
irr::gui::StaticText* stext = static_cast<irr::gui::StaticText*>(static_text);
stext->setText(text);
} else {
static_text->setText(text.c_str());
}
}
#else // USE_FREETYPE
inline irr::gui::IGUIStaticText *addStaticText(
irr::gui::IGUIEnvironment *guienv,
const EnrichedString &text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
return guienv->addStaticText(text.c_str(), rectangle, border, wordWrap, parent, id, fillBackground);
}
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
{
static_text->setText(text.c_str());
}
#endif
inline irr::gui::IGUIStaticText *addStaticText(
irr::gui::IGUIEnvironment *guienv,
const wchar_t *text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false) {
return addStaticText(guienv, EnrichedString(text), rectangle, border, wordWrap, parent, id, fillBackground);
}
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text)
{
setStaticText(static_text, EnrichedString(text));
}
#endif // _IRR_COMPILE_WITH_GUI_ #endif // _IRR_COMPILE_WITH_GUI_
#endif // C_GUI_STATIC_TEXT_H_INCLUDED #endif // C_GUI_STATIC_TEXT_H_INCLUDED

@ -345,9 +345,11 @@ void TerminalChatConsole::step(int ch)
if (p.first > m_log_level) if (p.first > m_log_level)
continue; continue;
m_chat_backend.addMessage( std::wstring error_message = utf8_to_wide(Logger::getLevelLabel(p.first));
utf8_to_wide(Logger::getLevelLabel(p.first)), if (!g_settings->getBool("disable_escape_sequences")) {
utf8_to_wide(p.second)); error_message = L"\x1b(c@red)" + error_message + L"\x1b(c@white)";
}
m_chat_backend.addMessage(error_message, utf8_to_wide(p.second));
} }
// handle input // handle input
@ -438,7 +440,7 @@ void TerminalChatConsole::draw_text()
continue; continue;
for (u32 i = 0; i < line.fragments.size(); ++i) { for (u32 i = 0; i < line.fragments.size(); ++i) {
const ChatFormattedFragment& fragment = line.fragments[i]; const ChatFormattedFragment& fragment = line.fragments[i];
addstr(wide_to_utf8(fragment.text).c_str()); addstr(wide_to_utf8(fragment.text.getString()).c_str());
} }
} }
} }

@ -1,17 +1,9 @@
if(USE_FREETYPE)
set(UTIL_FREETYPEDEP_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/statictext.cpp
)
else()
set(UTIL_FREETYPEDEP_SRCS )
endif(USE_FREETYPE)
set(UTIL_SRCS set(UTIL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/coloredstring.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
@ -20,6 +12,5 @@ set(UTIL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp ${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
${UTIL_FREETYPEDEP_SRCS}
PARENT_SCOPE) PARENT_SCOPE)

@ -1,68 +0,0 @@
/*
Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "coloredstring.h"
#include "util/string.h"
ColoredString::ColoredString()
{}
ColoredString::ColoredString(const std::wstring &string, const std::vector<SColor> &colors):
m_string(string),
m_colors(colors)
{}
ColoredString::ColoredString(const std::wstring &s) {
m_string = colorizeText(s, m_colors, SColor(255, 255, 255, 255));
}
void ColoredString::operator=(const wchar_t *str) {
m_string = colorizeText(str, m_colors, SColor(255, 255, 255, 255));
}
size_t ColoredString::size() const {
return m_string.size();
}
ColoredString ColoredString::substr(size_t pos, size_t len) const {
if (pos == m_string.length())
return ColoredString();
if (len == std::string::npos || pos + len > m_string.length()) {
return ColoredString(
m_string.substr(pos, std::string::npos),
std::vector<SColor>(m_colors.begin() + pos, m_colors.end())
);
} else {
return ColoredString(
m_string.substr(pos, len),
std::vector<SColor>(m_colors.begin() + pos, m_colors.begin() + pos + len)
);
}
}
const wchar_t *ColoredString::c_str() const {
return m_string.c_str();
}
const std::vector<SColor> &ColoredString::getColors() const {
return m_colors;
}
const std::wstring &ColoredString::getString() const {
return m_string;
}

@ -1,44 +0,0 @@
/*
Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef COLOREDSTRING_HEADER
#define COLOREDSTRING_HEADER
#include <string>
#include <vector>
#include <SColor.h>
using namespace irr::video;
class ColoredString {
public:
ColoredString();
ColoredString(const std::wstring &s);
ColoredString(const std::wstring &string, const std::vector<SColor> &colors);
void operator=(const wchar_t *str);
size_t size() const;
ColoredString substr(size_t pos = 0, size_t len = std::string::npos) const;
const wchar_t *c_str() const;
const std::vector<SColor> &getColors() const;
const std::wstring &getString() const;
private:
std::wstring m_string;
std::vector<SColor> m_colors;
};
#endif

@ -0,0 +1,166 @@
/*
Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
Copyright (C) 2016 Nore, Nathanaël Courant <nore@mesecons.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "enriched_string.h"
#include "util/string.h"
#include "log.h"
using namespace irr::video;
EnrichedString::EnrichedString()
{
clear();
}
EnrichedString::EnrichedString(const std::wstring &string,
const std::vector<SColor> &colors):
m_string(string),
m_colors(colors),
m_has_background(false)
{}
EnrichedString::EnrichedString(const std::wstring &s, const SColor &color)
{
clear();
addAtEnd(s, color);
}
EnrichedString::EnrichedString(const wchar_t *str, const SColor &color)
{
clear();
addAtEnd(std::wstring(str), color);
}
void EnrichedString::operator=(const wchar_t *str)
{
clear();
addAtEnd(std::wstring(str), SColor(255, 255, 255, 255));
}
void EnrichedString::addAtEnd(const std::wstring &s, const SColor &initial_color)
{
SColor color(initial_color);
size_t i = 0;
while (i < s.length()) {
if (s[i] != L'\x1b') {
m_string += s[i];
m_colors.push_back(color);
++i;
continue;
}
++i;
size_t start_index = i;
size_t length;
if (i == s.length()) {
break;
}
if (s[i] == L'(') {
++i;
++start_index;
while (i < s.length() && s[i] != L')') {
if (s[i] == L'\\') {
++i;
}
++i;
}
length = i - start_index;
++i;
} else {
++i;
length = 1;
}
std::wstring escape_sequence(s, start_index, length);
std::vector<std::wstring> parts = split(escape_sequence, L'@');
if (parts[0] == L"c") {
if (parts.size() < 2) {
continue;
}
parseColorString(wide_to_utf8(parts[1]), color, true);
} else if (parts[0] == L"b") {
if (parts.size() < 2) {
continue;
}
parseColorString(wide_to_utf8(parts[1]), m_background, true);
m_has_background = true;
}
continue;
}
}
void EnrichedString::addChar(const EnrichedString &source, size_t i)
{
m_string += source.m_string[i];
m_colors.push_back(source.m_colors[i]);
}
void EnrichedString::addCharNoColor(wchar_t c)
{
m_string += c;
if (m_colors.empty()) {
m_colors.push_back(SColor(255, 255, 255, 255));
} else {
m_colors.push_back(m_colors[m_colors.size() - 1]);
}
}
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);
}
void EnrichedString::operator+=(const EnrichedString &other)
{
m_string += other.m_string;
m_colors.insert(m_colors.end(), other.m_colors.begin(), other.m_colors.end());
}
EnrichedString EnrichedString::substr(size_t pos, size_t len) const
{
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())
);
} else {
return EnrichedString(
m_string.substr(pos, len),
std::vector<SColor>(m_colors.begin() + pos, m_colors.begin() + pos + len)
);
}
}
const wchar_t *EnrichedString::c_str() const
{
return m_string.c_str();
}
const std::vector<SColor> &EnrichedString::getColors() const
{
return m_colors;
}
const std::wstring &EnrichedString::getString() const
{
return m_string;
}

@ -0,0 +1,91 @@
/*
Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
Copyright (C) 2016 Nore, Nathanaël Courant <nore@mesecons.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef ENRICHEDSTRING_HEADER
#define ENRICHEDSTRING_HEADER
#include <string>
#include <vector>
#include <SColor.h>
class EnrichedString {
public:
EnrichedString();
EnrichedString(const std::wstring &s,
const irr::video::SColor &color = irr::video::SColor(255, 255, 255, 255));
EnrichedString(const wchar_t *str,
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 operator=(const wchar_t *str);
void addAtEnd(const std::wstring &s, const irr::video::SColor &color);
// Adds the character source[i] at the end.
// An EnrichedString should always be able to be copied
// to the end of an existing EnrichedString that way.
void addChar(const EnrichedString &source, size_t i);
// Adds a single character at the end, without specifying its
// color. The color used will be the one from the last character.
void addCharNoColor(wchar_t c);
EnrichedString substr(size_t pos = 0, size_t len = std::string::npos) const;
EnrichedString operator+(const EnrichedString &other) const;
void operator+=(const EnrichedString &other);
const wchar_t *c_str() const;
const std::vector<irr::video::SColor> &getColors() const;
const std::wstring &getString() const;
inline bool operator==(const EnrichedString &other) const
{
return (m_string == other.m_string && m_colors == other.m_colors);
}
inline bool operator!=(const EnrichedString &other) const
{
return !(*this == other);
}
inline void clear()
{
m_string.clear();
m_colors.clear();
m_has_background = false;
}
inline bool empty() const
{
return m_string.empty();
}
inline size_t size() const
{
return m_string.size();
}
inline bool hasBackground() const
{
return m_has_background;
}
inline irr::video::SColor getBackground() const
{
return m_background;
}
private:
std::wstring m_string;
std::vector<irr::video::SColor> m_colors;
bool m_has_background;
irr::video::SColor m_background;
};
#endif

@ -519,6 +519,38 @@ std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
return output; return output;
} }
template <typename T>
std::vector<std::basic_string<T> > split(const std::basic_string<T> &s, T delim)
{
std::vector<std::basic_string<T> > tokens;
std::basic_string<T> current;
bool last_was_escape = false;
for (size_t i = 0; i < s.length(); i++) {
T si = s[i];
if (last_was_escape) {
current += '\\';
current += si;
last_was_escape = false;
} else {
if (si == delim) {
tokens.push_back(current);
current = std::basic_string<T>();
last_was_escape = false;
} else if (si == '\\') {
last_was_escape = true;
} else {
current += si;
last_was_escape = false;
}
}
}
//push last element
tokens.push_back(current);
return tokens;
}
/** /**
* Checks that all characters in \p to_check are a decimal digits. * Checks that all characters in \p to_check are a decimal digits.
* *