Skip to content
Permalink
Browse files

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.
  • Loading branch information...
Ekdohibs committed May 31, 2016
1 parent 1d40385 commit 14ef2b445adcec770defe1abf83af9d22ccf39d8
@@ -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

--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

@@ -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;
@@ -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,16 +657,24 @@ 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);
Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, video::SColor(shadow_alpha,0,0,0), true);
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
@@ -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;
@@ -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()
@@ -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,22 +34,29 @@ 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),
name(a_name),
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 @@ class ChatBackend
// 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"));
Oops, something went wrong.

0 comments on commit 14ef2b4

Please sign in to comment.
You can’t perform that action at this time.