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",
func = function(name, param)
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
msg = msg .. " " .. def.params
end

@ -198,19 +198,34 @@ function core.http_add_fetch(httpenv)
return httpenv
end
function core.get_color_escape_sequence(color)
--if string.len(color) == 3 then
-- local r = string.sub(color, 1, 1)
-- local g = string.sub(color, 2, 2)
-- local b = string.sub(color, 3, 3)
-- color = r .. r .. g .. g .. b .. b
--end
if minetest.setting_getbool("disable_escape_sequences") then
function core.get_color_escape_sequence(color)
return ""
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
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.
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 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):
`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
---------------
* `vector.new(a[, b, c])`: returns a vector:

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

@ -1,6 +1,7 @@
/*
CGUITTFont FreeType class for Irrlicht
Copyright (c) 2009-2010 John Norman
Copyright (c) 2016 Nathanaël Courant
This software is provided 'as-is', without any express or implied
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)
{
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)
return;
@ -572,7 +580,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
}
// Convert to a unicode string.
core::ustring utext(text);
core::ustring utext = text.getString();
// Set up our 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;
uchar32_t previousChar = 0;
core::ustring::const_iterator iter(utext);
std::vector<video::SColor> applied_colors;
while (!iter.atEnd())
{
uchar32_t currentChar = *iter;
@ -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_source_rects.push_back(glyph.source_rect);
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);
@ -645,8 +657,6 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
CGUITTGlyphPage* page = n->getValue();
if (!use_transparency) color.color |= 0xff000000;
if (shadow_offset) {
for (size_t i = 0; i < page->render_positions.size(); ++i)
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)
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
Copyright (c) 2009-2010 John Norman
Copyright (c) 2016 Nathanaël Courant
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
@ -33,6 +34,8 @@
#include <irrlicht.h>
#include <ft2build.h>
#include <vector>
#include "util/enriched_string.h"
#include FT_FREETYPE_H
namespace irr
@ -259,6 +262,10 @@ namespace gui
video::SColor color, bool hcenter=false, bool vcenter=false,
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.
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);
}
std::wstring name_sanitized = removeEscapes(line.name);
std::wstring name_sanitized = line.name.c_str();
// Choose an indentation level
if (line.name.empty()) {
// Server messages
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
hanging_indentation = line.name.size() + 3;
}
else {
} else {
// Very long names
hanging_indentation = 2;
}
ColoredString line_text(line.text);
//EnrichedString line_text(line.text);
next_line.first = true;
bool text_processing = false;
// 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
while (!next_frags.empty())
@ -326,9 +324,9 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
}
// 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;
// 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 &&
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;
++frag_length;
}
if (space_pos != 0 && frag_length < remaining_in_input)
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.bold = 0;
next_frags.push_back(temp_frag);
@ -729,19 +727,22 @@ ChatBuffer& ChatBackend::getRecentBuffer()
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)
{
const ChatLine& line = m_recent_buffer.getLine(i);
if (i != 0)
stream << L"\n";
if (!line.name.empty())
stream << L"<" << line.name << L"> ";
stream << line.text;
result += L"\n";
if (!line.name.empty()) {
result += L"<";
result += line.name;
result += L"> ";
}
return stream.str();
result += line.text;
}
return result;
}
ChatPrompt& ChatBackend::getPrompt()

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

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

@ -202,6 +202,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("server_name", "");
settings->setDefault("server_description", "");
settings->setDefault("disable_escape_sequences", "false");
#if USE_FREETYPE
settings->setDefault("freetype", "true");
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 "filesys.h"
#include "gettext.h"
#include "client/guiChatConsole.h"
#include "guiChatConsole.h"
#include "guiFormSpecMenu.h"
#include "guiKeyChangeMenu.h"
#include "guiPasswordChange.h"
@ -55,14 +55,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tool.h"
#include "util/directiontables.h"
#include "util/pointedthing.h"
#include "irrlicht_changes/static_text.h"
#include "version.h"
#include "minimap.h"
#include "mapblock_mesh.h"
#if USE_FREETYPE
#include "util/statictext.h"
#endif
#include "sound.h"
#if USE_SOUND
@ -541,7 +538,7 @@ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe,
std::ostringstream os(std::ios_base::binary);
g_profiler->printPage(os, show_profiler, show_profiler_max);
std::wstring text = utf8_to_wide(os.str());
guitext_profiler->setText(text.c_str());
setStaticText(guitext_profiler, text.c_str());
guitext_profiler->setVisible(true);
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
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
@ -1259,10 +1260,10 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
// Display all messages in a static text element
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();
guitext_chat->setText(recent_chat.c_str());
setStaticText(guitext_chat, recent_chat);
// Update gui element size and position
s32 chat_y = 5 + line_height;
@ -1271,7 +1272,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
chat_y += line_height;
// 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);
core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y);
guitext_chat->setRelativePosition(rect);
@ -2218,45 +2219,39 @@ bool Game::createClient(const std::string &playername,
bool Game::initGui()
{
// First line of debug text
guitext = guienv->addStaticText(
guitext = addStaticText(guienv,
utf8_to_wide(PROJECT_NAME_C).c_str(),
core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
// Second line of debug text
guitext2 = guienv->addStaticText(
guitext2 = addStaticText(guienv,
L"",
core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
// At the middle of the screen
// Object infos are shown in this
guitext_info = guienv->addStaticText(
guitext_info = addStaticText(guienv,
L"",
core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200),
false, true, guiroot);
// Status text (displays info when showing and hiding GUI stuff, etc.)
guitext_status = guienv->addStaticText(
guitext_status = addStaticText(guienv,
L"<Status>",
core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
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
guitext_chat = guienv->addStaticText(
guitext_chat = addStaticText(
guienv,
L"",
core::rect<s32>(0, 0, 0, 0),
//false, false); // Disable word wrap as of now
false, true, guiroot);
#endif
// Remove stale "recent" chat messages from previous connections
chat_backend->clearRecentChat();
@ -2270,7 +2265,7 @@ bool Game::initGui()
}
// Profiler text (size is updated when text is updated)
guitext_profiler = guienv->addStaticText(
guitext_profiler = addStaticText(guienv,
L"<Profiler>",
core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
@ -4308,12 +4303,12 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
<< ", v_range = " << draw_control->wanted_range
<< std::setprecision(3)
<< ", RTT = " << client->getRTT();
guitext->setText(utf8_to_wide(os.str()).c_str());
setStaticText(guitext, utf8_to_wide(os.str()).c_str());
guitext->setVisible(true);
} else if (flags.show_hud || flags.show_chat) {
std::ostringstream os(std::ios_base::binary);
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);
} else {
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);
core::rect<s32> rect(
@ -4362,7 +4357,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
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);
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());
if (!statustext.empty()) {

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

@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "fontengine.h"
#include "guiscalingfilter.h"
#include "irrlicht_changes/static_text.h"
#ifdef __ANDROID__
#include "client/tile.h"
@ -172,15 +173,16 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
m_sound_manager = &dummySoundManager;
//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);
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);
m_irr_toplefttext =
m_device->getGUIEnvironment()->addStaticText(t.c_str(),
rect,false,true,0,-1);
addStaticText(m_device->getGUIEnvironment(), m_toplefttext,
rect, false, true, 0, -1);
//create formspecsource
m_formspecgui = new FormspecFormSource("");
@ -578,7 +580,7 @@ void GUIEngine::setTopleftText(std::string append)
toset += utf8_to_wide(append);
}
m_irr_toplefttext->setText(toset.c_str());
m_toplefttext = toset;
updateTopLeftTextSize();
}
@ -586,15 +588,14 @@ void GUIEngine::setTopleftText(std::string append)
/******************************************************************************/
void GUIEngine::updateTopLeftTextSize()
{
std::wstring text = m_irr_toplefttext->getText();
core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(text), g_fontengine->getTextHeight());
core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
g_fontengine->getTextHeight());
rect += v2s32(4, 0);
m_irr_toplefttext->remove();
m_irr_toplefttext =
m_device->getGUIEnvironment()->addStaticText(text.c_str(),
rect,false,true,0,-1);
addStaticText(m_device->getGUIEnvironment(), m_toplefttext,
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 "sound.h"
#include "client/tile.h"
#include "util/enriched_string.h"
/******************************************************************************/
/* Typedefs and macros */
@ -275,6 +276,8 @@ private:
/** pointer to gui element shown at topleft corner */
irr::gui::IGUIStaticText* m_irr_toplefttext;
/** and text that is in it */
EnrichedString m_toplefttext;
/** initialize cloud subsystem */
void cloudInit();

@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/hex.h"
#include "util/numeric.h"
#include "util/string.h" // for parseColorString()
#include "irrlicht_changes/static_text.h"
#include "guiscalingfilter.h"
#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;
}
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)
{
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();
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'*');
@ -1021,7 +991,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
if (name == "")
{
// 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
{
@ -1056,7 +1026,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
int font_height = g_fontengine->getTextHeight();
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 == "")
{
// 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
{
@ -1161,7 +1131,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
int font_height = g_fontengine->getTextHeight();
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);
@ -1230,7 +1200,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
258+m_fields.size()
);
gui::IGUIStaticText *e =
Environment->addStaticText(spec.flabel.c_str(),
addStaticText(Environment, spec.flabel.c_str(),
rect, false, false, this, spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT,
gui::EGUIA_CENTER);
@ -1284,7 +1254,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
258+m_fields.size()
);
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);
m_fields.push_back(spec);
return;
@ -1910,7 +1880,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{
assert(m_tooltip_element == NULL);
// 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->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setDrawBackground(true);
@ -2255,7 +2225,6 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
std::wstring tooltip_text = L"";
if (hovering && !m_selected_item) {
tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description);
tooltip_text = unescape_enriched(tooltip_text);
}
if (tooltip_text != L"") {
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->setVisible(true);
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;
#if IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
@ -2535,8 +2504,10 @@ void GUIFormSpecMenu::drawMenu()
iter != m_fields.end(); ++iter) {
if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") {
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_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');
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
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::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);
this->bringToFront(m_tooltip_element);
break;
@ -2568,6 +2537,8 @@ void GUIFormSpecMenu::drawMenu()
}
}
m_tooltip_element->draw();
/*
Draw dragged item stack
*/

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiTable.h"
#include "network/networkprotocol.h"
#include "util/string.h"
#include "util/enriched_string.h"
class IGameDef;
class InventoryManager;
@ -202,7 +203,8 @@ class GUIFormSpecMenu : public GUIModalMenu
fname(name),
fid(id)
{
flabel = unescape_enriched(label);
//flabel = unescape_enriched(label);
flabel = label;
fdefault = unescape_enriched(default_text);
send = false;
ftype = f_Unknown;
@ -239,7 +241,8 @@ class GUIFormSpecMenu : public GUIModalMenu
bgcolor(a_bgcolor),
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;
irr::video::SColor bgcolor;
@ -256,7 +259,8 @@ class GUIFormSpecMenu : public GUIModalMenu
rect(a_rect),
parent_button(NULL)
{
text = unescape_enriched(a_text);
//text = unescape_enriched(a_text);
text = a_text;
}
StaticTextSpec(const std::wstring &a_text,
const core::rect<s32> &a_rect,
@ -264,7 +268,8 @@ class GUIFormSpecMenu : public GUIModalMenu
rect(a_rect),
parent_button(a_parent_button)
{
text = unescape_enriched(a_text);
//text = unescape_enriched(a_text);
text = a_text;
}
std::wstring text;
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) 2016 Nathanaël Courant:
// Modified the functions to use EnrichedText instead of string.
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "statictext.h"
#include "static_text.h"
#ifdef _IRR_COMPILE_WITH_GUI_
//Only compile this if freetype is enabled.
#include <vector>
#include <string>
#include <iostream>
@ -17,15 +17,21 @@
#include <rect.h>
#include <SColor.h>
#include "cguittfont/xCGUITTFont.h"
#if USE_FREETYPE
#include "cguittfont/xCGUITTFont.h"
#endif
#include "util/string.h"
namespace irr
{
#if USE_FREETYPE
namespace gui
{
//! constructor
StaticText::StaticText(const wchar_t* text, bool border,
StaticText::StaticText(const EnrichedString &text, bool border,
IGUIEnvironment* environment, IGUIElement* parent,
s32 id, const core::rect<s32>& rectangle,
bool background)
@ -40,7 +46,8 @@ StaticText::StaticText(const wchar_t* text, bool border,
setDebugName("StaticText");
#endif
Text = text;
Text = text.c_str();
cText = text;
if (environment && environment->getSkin())
{
BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE);
@ -55,7 +62,6 @@ StaticText::~StaticText()
OverrideFont->drop();
}
//! draws the element and its children
void StaticText::draw()
{
@ -88,7 +94,7 @@ void StaticText::draw()
}
// draw the text
if (Text.size())
if (cText.size())
{
IGUIFont* font = getActiveFont();
@ -105,10 +111,11 @@ void StaticText::draw()
if (HAlign == EGUIA_LOWERRIGHT)
{
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),
HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
}
@ -138,16 +145,17 @@ void StaticText::draw()
font->getDimension(BrokenText[i].c_str()).Width;
}
std::vector<irr::video::SColor> colors;
std::wstring str;
//std::vector<irr::video::SColor> colors;
//std::wstring str;
EnrichedString str = BrokenText[i];
str = colorizeText(BrokenText[i].c_str(), colors, previous_color);
if (!colors.empty())
previous_color = colors[colors.size() - 1];
//str = colorizeText(BrokenText[i].c_str(), colors, previous_color);
//if (!colors.empty())
// previous_color = colors[colors.size() - 1];
irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
tmp->draw(str.c_str(), r,
colors,
tmp->draw(str, r,
previous_color, // FIXME
HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
r.LowerRightCorner.Y += height;
@ -340,17 +348,17 @@ void StaticText::breakText()
LastBreakFont = font;
core::stringw line;
core::stringw word;
core::stringw whitespace;
s32 size = Text.size();
EnrichedString line;
EnrichedString word;
EnrichedString whitespace;
s32 size = cText.size();
s32 length = 0;
s32 elWidth = RelativeRect.getWidth();
if (Border)
elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X);
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
// However, most parts of the following code is the same, it's just
@ -360,17 +368,17 @@ void StaticText::breakText()
// regular (left-to-right)
for (s32 i=0; i<size; ++i)
{
c = Text[i];
c = cText.getString()[i];
bool lineBreak = false;
if (c == L'\r') // Mac or Windows breaks
{
lineBreak = true;
if (Text[i+1] == L'\n') // Windows breaks
{
Text.erase(i+1);
--size;
}
//if (Text[i+1] == L'\n') // Windows breaks
//{
// Text.erase(i+1);
// --size;
//}
c = '\0';
}
else if (c == L'\n') // Unix breaks
@ -383,7 +391,8 @@ void StaticText::breakText()
if ( !isWhitespace )
{
// part of a word
word += c;
//word += c;
word.addChar(cText, i);
}
if ( isWhitespace || i == (size-1))
@ -393,20 +402,21 @@ void StaticText::breakText()
// here comes the next whitespace, look if
// we must break the last word to the next line.
const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
const std::wstring sanitized = removeEscapes(word.c_str());
const s32 wordlgth = font->getDimension(sanitized.c_str()).Width;
//const std::wstring sanitized = removeEscapes(word.c_str());
const s32 wordlgth = font->getDimension(word.c_str()).Width;
if (wordlgth > elWidth)
{
// This word is too long to fit in the available space, look for
// the Unicode Soft HYphen (SHY / 00AD) character for a place to
// 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)
{
core::stringw first = word.subString(0, where);
core::stringw second = word.subString(where, word.size() - where);
BrokenText.push_back(line + first + L"-");
EnrichedString first = word.substr(0, where);
EnrichedString second = word.substr(where, word.size() - where);
first.addCharNoColor(L'-');
BrokenText.push_back(line + first);
const s32 secondLength = font->getDimension(second.c_str()).Width;
length = secondLength;
@ -437,13 +447,13 @@ void StaticText::breakText()
length += whitelgth + wordlgth;
}
word = L"";
whitespace = L"";
word.clear();
whitespace.clear();
}
if ( isWhitespace )
if ( isWhitespace && c != 0)
{
whitespace += c;
whitespace.addChar(cText, i);
}
// compute line break
@ -452,9 +462,9 @@ void StaticText::breakText()
line += whitespace;
line += word;
BrokenText.push_back(line);
line = L"";
word = L"";
whitespace = L"";
line.clear();
word.clear();
whitespace.clear();
length = 0;
}
}
@ -469,17 +479,17 @@ void StaticText::breakText()
// right-to-left
for (s32 i=size; i>=0; --i)
{
c = Text[i];
c = cText.getString()[i];
bool lineBreak = false;
if (c == L'\r') // Mac or Windows breaks
{
lineBreak = true;
if ((i>0) && Text[i-1] == L'\n') // Windows breaks
{
Text.erase(i-1);
--size;
}
//if ((i>0) && Text[i-1] == L'\n') // Windows breaks
//{
// Text.erase(i-1);
// --size;
//}
c = '\0';
}
else if (c == L'\n') // Unix breaks
@ -512,12 +522,13 @@ void StaticText::breakText()
length += whitelgth + wordlgth;
}
word = L"";
whitespace = L"";
word.clear();
whitespace.clear();
}
if (c != 0)
whitespace = core::stringw(&c, 1) + whitespace;
// whitespace = core::stringw(&c, 1) + whitespace;
whitespace = cText.substr(i, 1) + whitespace;
// compute line break
if (lineBreak)
@ -525,16 +536,17 @@ void StaticText::breakText()
line = whitespace + line;
line = word + line;
BrokenText.push_back(line);
line = L"";
word = L"";
whitespace = L"";
line.clear();
word.clear();
whitespace.clear();
length = 0;
}
}
else
{
// 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.
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();
}
@ -598,7 +620,7 @@ s32 StaticText::getTextWidth() const
}
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
#endif // USE_FREETYPE
} // end namespace irr

@ -1,4 +1,6 @@
// 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".
// For conditions of distribution and use, see copyright notice in irrlicht.h
@ -11,18 +13,30 @@
#include "IGUIStaticText.h"
#include "irrArray.h"
#include "log.h"
#include <vector>
#include "util/enriched_string.h"
#include "config.h"
#include <IGUIEnvironment.h>
#if USE_FREETYPE
namespace irr
{
namespace gui
{
const EGUI_ELEMENT_TYPE EGUIET_ENRICHED_STATIC_TEXT = (EGUI_ELEMENT_TYPE)(0x1000);
class StaticText : public IGUIStaticText
{
public:
//! 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,
bool background = false);
@ -121,6 +135,16 @@ namespace gui
//! Reads attributes of the element
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:
//! Breaks the single text line.
@ -139,12 +163,106 @@ namespace gui
gui::IGUIFont* OverrideFont;
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 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 // C_GUI_STATIC_TEXT_H_INCLUDED

@ -345,9 +345,11 @@ void TerminalChatConsole::step(int ch)
if (p.first > m_log_level)
continue;
m_chat_backend.addMessage(
utf8_to_wide(Logger::getLevelLabel(p.first)),
utf8_to_wide(p.second));
std::wstring error_message = utf8_to_wide(Logger::getLevelLabel(p.first));
if (!g_settings->getBool("disable_escape_sequences")) {
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
@ -438,7 +440,7 @@ void TerminalChatConsole::draw_text()
continue;
for (u32 i = 0; i < line.fragments.size(); ++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
${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/coloredstring.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
@ -20,6 +12,5 @@ set(UTIL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
${UTIL_FREETYPEDEP_SRCS}
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;
}
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.
*