summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEkdohibs <nathanael.courant@laposte.net>2016-05-31 17:30:11 +0200
committerEkdohibs <nathanael.courant@laposte.net>2016-05-31 17:34:29 +0200
commit14ef2b445adcec770defe1abf83af9d22ccf39d8 (patch)
treebe434ea35d6134f4e7b90a74283a21815ed079ee
parent1d40385d4aacf0cbea4b19ff06940e8c9bebaf47 (diff)
downloadminetest-14ef2b445adcec770defe1abf83af9d22ccf39d8.tar.gz
minetest-14ef2b445adcec770defe1abf83af9d22ccf39d8.tar.bz2
minetest-14ef2b445adcec770defe1abf83af9d22ccf39d8.zip
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.
-rw-r--r--builtin/game/chatcommands.lua2
-rw-r--r--builtin/game/misc.lua43
-rw-r--r--builtin/settingtypes.txt5
-rw-r--r--doc/lua_api.txt18
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/cguittfont/CGUITTFont.cpp30
-rw-r--r--src/cguittfont/CGUITTFont.h7
-rw-r--r--src/chat.cpp37
-rw-r--r--src/chat.h17
-rw-r--r--src/client/CMakeLists.txt1
-rw-r--r--src/defaultsettings.cpp2
-rw-r--r--src/game.cpp53
-rw-r--r--src/guiChatConsole.cpp (renamed from src/client/guiChatConsole.cpp)4
-rw-r--r--src/guiChatConsole.h (renamed from src/client/guiChatConsole.h)0
-rw-r--r--src/guiEngine.cpp23
-rw-r--r--src/guiEngine.h3
-rw-r--r--src/guiFormSpecMenu.cpp59
-rw-r--r--src/guiFormSpecMenu.h13
-rw-r--r--src/irrlicht_changes/CMakeLists.txt7
-rw-r--r--src/irrlicht_changes/static_text.cpp (renamed from src/util/statictext.cpp)139
-rw-r--r--src/irrlicht_changes/static_text.h (renamed from src/util/statictext.h)122
-rw-r--r--src/terminal_chat_console.cpp10
-rw-r--r--src/util/CMakeLists.txt11
-rw-r--r--src/util/coloredstring.cpp68
-rw-r--r--src/util/coloredstring.h44
-rw-r--r--src/util/enriched_string.cpp166
-rw-r--r--src/util/enriched_string.h91
-rw-r--r--src/util/string.h32
28 files changed, 690 insertions, 319 deletions
diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua
index 2627559a5..22755386b 100644
--- a/builtin/game/chatcommands.lua
+++ b/builtin/game/chatcommands.lua
@@ -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
diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua
index 8d5c80216..918315656 100644
--- a/builtin/game/misc.lua
+++ b/builtin/game/misc.lua
@@ -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
-
- --assert(#color == 6, "Color must be six characters in length.")
- --return "\v" .. color
- return "\v(color;" .. color .. ")"
-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
-function core.colorize(color, message)
- return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("ffffff")
end
+
diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt
index 93fb8e952..538a04f33 100644
--- a/builtin/settingtypes.txt
+++ b/builtin/settingtypes.txt
@@ -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).
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 82a0acbee..aa0d7e45d 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -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:
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ea1564eeb..f02812415 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
diff --git a/src/cguittfont/CGUITTFont.cpp b/src/cguittfont/CGUITTFont.cpp
index 2342eb748..c2d37c6c0 100644
--- a/src/cguittfont/CGUITTFont.cpp
+++ b/src/cguittfont/CGUITTFont.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;
@@ -590,7 +599,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
if (currentChar == L'\r') // Mac or Windows breaks
{
lineBreak = true;
- if (*(iter + 1) == (uchar32_t)'\n') // Windows line breaks.
+ if (*(iter + 1) == (uchar32_t)'\n') // Windows line breaks.
currentChar = *(++iter);
}
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_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);
+ }
}
}
diff --git a/src/cguittfont/CGUITTFont.h b/src/cguittfont/CGUITTFont.h
index e24d8f18b..0aa540c5c 100644
--- a/src/cguittfont/CGUITTFont.h
+++ b/src/cguittfont/CGUITTFont.h
@@ -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
@@ -258,6 +261,10 @@ namespace gui
virtual void draw(const core::stringw& text, const core::rect<s32>& position,
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;
diff --git a/src/chat.cpp b/src/chat.cpp
index 958389df5..46555b3dc 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -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"> ";
+ }
+ result += line.text;
}
- return stream.str();
+ return result;
}
ChatPrompt& ChatBackend::getPrompt()
diff --git a/src/chat.h b/src/chat.h
index 661cafc82..11061fd39 100644
--- a/src/chat.h
+++ b/src/chat.h
@@ -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();
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
index bcf114760..a1ec37fe3 100644
--- a/src/client/CMakeLists.txt
+++ b/src/client/CMakeLists.txt
@@ -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
)
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index e8b652c17..632ec0df9 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -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"));
diff --git a/src/game.cpp b/src/game.cpp
index 71a04aef5..def202fe5 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -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()) {
diff --git a/src/client/guiChatConsole.cpp b/src/guiChatConsole.cpp
index d8837556a..bb58d1305 100644
--- a/src/client/guiChatConsole.cpp
+++ b/src/guiChatConsole.cpp
@@ -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);
diff --git a/src/client/guiChatConsole.h b/src/guiChatConsole.h
index 3013a1d31..3013a1d31 100644
--- a/src/client/guiChatConsole.h
+++ b/src/guiChatConsole.h
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
index ba286a91c..96de7a4f7 100644
--- a/src/guiEngine.cpp
+++ b/src/guiEngine.cpp
@@ -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());
- rect += v2s32(4, 0);
+ 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);
}
/******************************************************************************/
diff --git a/src/guiEngine.h b/src/guiEngine.h
index d527f7222..fa98a21e4 100644
--- a/src/guiEngine.h
+++ b/src/guiEngine.h
@@ -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();
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index 99b1153c1..cf01dc38c 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -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
*/
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index ef230c81c..4122b1f56 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -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;
diff --git a/src/irrlicht_changes/CMakeLists.txt b/src/irrlicht_changes/CMakeLists.txt
new file mode 100644
index 000000000..3a265c99d
--- /dev/null
+++ b/src/irrlicht_changes/CMakeLists.txt
@@ -0,0 +1,7 @@
+if (BUILD_CLIENT)
+ set(client_irrlicht_changes_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/static_text.cpp
+ PARENT_SCOPE
+ )
+endif()
+
diff --git a/src/util/statictext.cpp b/src/irrlicht_changes/static_text.cpp
index b534b560e..703287eb3 100644
--- a/src/util/statictext.cpp
+++ b/src/irrlicht_changes/static_text.cpp
@@ -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
diff --git a/src/util/statictext.h b/src/irrlicht_changes/static_text.h
index 8d2f879e7..408a12784 100644
--- a/src/util/statictext.h
+++ b/src/irrlicht_changes/static_text.h
@@ -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
diff --git a/src/terminal_chat_console.cpp b/src/terminal_chat_console.cpp
index c86a960fa..a8c4ebaef 100644
--- a/src/terminal_chat_console.cpp
+++ b/src/terminal_chat_console.cpp
@@ -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());
}
}
}
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index e028a0435..f571ab22c 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -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)
diff --git a/src/util/coloredstring.cpp b/src/util/coloredstring.cpp
deleted file mode 100644
index 7db586550..000000000
--- a/src/util/coloredstring.cpp
+++ /dev/null
@@ -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;
-}
diff --git a/src/util/coloredstring.h b/src/util/coloredstring.h
deleted file mode 100644
index a6d98db30..000000000
--- a/src/util/coloredstring.h
+++ /dev/null
@@ -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
diff --git a/src/util/enriched_string.cpp b/src/util/enriched_string.cpp
new file mode 100644
index 000000000..a7fc3a828
--- /dev/null
+++ b/src/util/enriched_string.cpp
@@ -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;
+}
diff --git a/src/util/enriched_string.h b/src/util/enriched_string.h
new file mode 100644
index 000000000..1aca8948a
--- /dev/null
+++ b/src/util/enriched_string.h
@@ -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
diff --git a/src/util/string.h b/src/util/string.h
index 40ef3e4d3..c77c5a6f9 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -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.
*