aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTriBlade9 <triblade9@mail.com>2015-01-16 14:54:26 +0800
committerEkdohibs <nathanael.courant@laposte.net>2016-05-31 17:34:29 +0200
commit1d40385d4aacf0cbea4b19ff06940e8c9bebaf47 (patch)
tree39732d23598a1c14d514ff35d241f9499f0a3c13 /src
parent0e44af9f7056a78a8e561f708751acceacd149c1 (diff)
downloadminetest-1d40385d4aacf0cbea4b19ff06940e8c9bebaf47.tar.gz
minetest-1d40385d4aacf0cbea4b19ff06940e8c9bebaf47.tar.bz2
minetest-1d40385d4aacf0cbea4b19ff06940e8c9bebaf47.zip
Colored chat working as expected for both freetype and non-freetype builds. @nerzhul improvements * Add unit tests * Fix coding style * move guiChatConsole.hpp to client/
Diffstat (limited to 'src')
-rw-r--r--src/chat.cpp29
-rw-r--r--src/chat.h6
-rw-r--r--src/client/CMakeLists.txt1
-rw-r--r--src/client/guiChatConsole.cpp (renamed from src/guiChatConsole.cpp)31
-rw-r--r--src/client/guiChatConsole.h (renamed from src/guiChatConsole.h)0
-rw-r--r--src/game.cpp14
-rw-r--r--src/util/CMakeLists.txt10
-rw-r--r--src/util/coloredstring.cpp68
-rw-r--r--src/util/coloredstring.h44
-rw-r--r--src/util/statictext.cpp654
-rw-r--r--src/util/statictext.h150
11 files changed, 980 insertions, 27 deletions
diff --git a/src/chat.cpp b/src/chat.cpp
index cebe31225..958389df5 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "chat.h"
#include "debug.h"
+#include "config.h"
#include "util/strfnd.h"
#include <cctype>
#include <sstream>
@@ -251,8 +252,7 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
u32 hanging_indentation = 0;
// Format the sender name and produce fragments
- if (!line.name.empty())
- {
+ if (!line.name.empty()) {
temp_frag.text = L"<";
temp_frag.column = 0;
//temp_frag.bold = 0;
@@ -267,28 +267,28 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
next_frags.push_back(temp_frag);
}
+ std::wstring name_sanitized = removeEscapes(line.name);
+
// Choose an indentation level
- if (line.name.empty())
- {
+ if (line.name.empty()) {
// Server messages
hanging_indentation = 0;
}
- else if (line.name.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);
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 +326,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 +338,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[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);
@@ -686,9 +686,6 @@ ChatBackend::~ChatBackend()
void ChatBackend::addMessage(std::wstring name, std::wstring text)
{
- name = unescape_enriched(name);
- text = unescape_enriched(text);
-
// Note: A message may consist of multiple lines, for example the MOTD.
WStrfnd fnd(text);
while (!fnd.at_end())
diff --git a/src/chat.h b/src/chat.h
index db4146d35..661cafc82 100644
--- a/src/chat.h
+++ b/src/chat.h
@@ -20,11 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef CHAT_HEADER
#define CHAT_HEADER
-#include "irrlichttypes.h"
#include <string>
#include <vector>
#include <list>
+#include "irrlichttypes.h"
+#include "util/coloredstring.h"
+
// Chat console related classes
struct ChatLine
@@ -34,7 +36,7 @@ struct ChatLine
// name of sending player, or empty if sent by server
std::wstring name;
// message text
- std::wstring text;
+ ColoredString text;
ChatLine(std::wstring a_name, std::wstring a_text):
age(0.0),
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
index a1ec37fe3..bcf114760 100644
--- a/src/client/CMakeLists.txt
+++ b/src/client/CMakeLists.txt
@@ -1,5 +1,6 @@
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/guiChatConsole.cpp b/src/client/guiChatConsole.cpp
index 17a1689c7..d8837556a 100644
--- a/src/guiChatConsole.cpp
+++ b/src/client/guiChatConsole.cpp
@@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#if USE_FREETYPE
-#include "xCGUITTFont.h"
+ #include "xCGUITTFont.h"
#endif
inline u32 clamp_u8(s32 value)
@@ -340,13 +340,28 @@ void GUIChatConsole::drawText()
s32 x = (fragment.column + 1) * m_fontsize.X;
core::rect<s32> destrect(
x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y);
- m_font->draw(
- fragment.text.c_str(),
- destrect,
- video::SColor(255, 255, 255, 255),
- false,
- false,
- &AbsoluteClippingRect);
+
+
+ #if USE_FREETYPE
+ // Draw colored text if FreeType is enabled
+ irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(m_font);
+ tmp->draw(
+ fragment.text.c_str(),
+ destrect,
+ fragment.text.getColors(),
+ false,
+ false,
+ &AbsoluteClippingRect);
+ #else
+ // Otherwise use standard text
+ m_font->draw(
+ fragment.text.c_str(),
+ destrect,
+ video::SColor(255, 255, 255, 255),
+ false,
+ false,
+ &AbsoluteClippingRect);
+ #endif
}
}
}
diff --git a/src/guiChatConsole.h b/src/client/guiChatConsole.h
index 3013a1d31..3013a1d31 100644
--- a/src/guiChatConsole.h
+++ b/src/client/guiChatConsole.h
diff --git a/src/game.cpp b/src/game.cpp
index c5211a042..71a04aef5 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 "guiChatConsole.h"
+#include "client/guiChatConsole.h"
#include "guiFormSpecMenu.h"
#include "guiKeyChangeMenu.h"
#include "guiPasswordChange.h"
@@ -59,6 +59,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "minimap.h"
#include "mapblock_mesh.h"
+#if USE_FREETYPE
+ #include "util/statictext.h"
+#endif
+
#include "sound.h"
#if USE_SOUND
@@ -2239,12 +2243,20 @@ bool Game::initGui()
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(
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();
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index 0e7cbad07..e028a0435 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -1,7 +1,16 @@
+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}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
@@ -11,5 +20,6 @@ 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
new file mode 100644
index 000000000..7db586550
--- /dev/null
+++ b/src/util/coloredstring.cpp
@@ -0,0 +1,68 @@
+/*
+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
new file mode 100644
index 000000000..a6d98db30
--- /dev/null
+++ b/src/util/coloredstring.h
@@ -0,0 +1,44 @@
+/*
+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/statictext.cpp b/src/util/statictext.cpp
new file mode 100644
index 000000000..b534b560e
--- /dev/null
+++ b/src/util/statictext.cpp
@@ -0,0 +1,654 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#include "statictext.h"
+#ifdef _IRR_COMPILE_WITH_GUI_
+
+//Only compile this if freetype is enabled.
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <IGUISkin.h>
+#include <IGUIEnvironment.h>
+#include <IGUIFont.h>
+#include <IVideoDriver.h>
+#include <rect.h>
+#include <SColor.h>
+
+#include "cguittfont/xCGUITTFont.h"
+#include "util/string.h"
+
+namespace irr
+{
+namespace gui
+{
+//! constructor
+StaticText::StaticText(const wchar_t* text, bool border,
+ IGUIEnvironment* environment, IGUIElement* parent,
+ s32 id, const core::rect<s32>& rectangle,
+ bool background)
+: IGUIStaticText(environment, parent, id, rectangle),
+ HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT),
+ Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background),
+ RestrainTextInside(true), RightToLeft(false),
+ OverrideColor(video::SColor(101,255,255,255)), BGColor(video::SColor(101,210,210,210)),
+ OverrideFont(0), LastBreakFont(0)
+{
+ #ifdef _DEBUG
+ setDebugName("StaticText");
+ #endif
+
+ Text = text;
+ if (environment && environment->getSkin())
+ {
+ BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE);
+ }
+}
+
+
+//! destructor
+StaticText::~StaticText()
+{
+ if (OverrideFont)
+ OverrideFont->drop();
+}
+
+
+//! draws the element and its children
+void StaticText::draw()
+{
+ if (!IsVisible)
+ return;
+
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+ video::IVideoDriver* driver = Environment->getVideoDriver();
+
+ core::rect<s32> frameRect(AbsoluteRect);
+
+ // draw background
+
+ if (Background)
+ {
+ if ( !OverrideBGColorEnabled ) // skin-colors can change
+ BGColor = skin->getColor(gui::EGDC_3D_FACE);
+
+ driver->draw2DRectangle(BGColor, frameRect, &AbsoluteClippingRect);
+ }
+
+ // draw the border
+
+ if (Border)
+ {
+ skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect);
+ frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X);
+ }
+
+ // draw the text
+ if (Text.size())
+ {
+ IGUIFont* font = getActiveFont();
+
+ if (font)
+ {
+ if (!WordWrap)
+ {
+ // TODO: add colors here
+ if (VAlign == EGUIA_LOWERRIGHT)
+ {
+ frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y -
+ font->getDimension(L"A").Height - font->getKerningHeight();
+ }
+ if (HAlign == EGUIA_LOWERRIGHT)
+ {
+ frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
+ font->getDimension(Text.c_str()).Width;
+ }
+
+ font->draw(Text.c_str(), frameRect,
+ OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
+ HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
+ }
+ else
+ {
+ if (font != LastBreakFont)
+ breakText();
+
+ core::rect<s32> r = frameRect;
+ s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
+ s32 totalHeight = height * BrokenText.size();
+ if (VAlign == EGUIA_CENTER)
+ {
+ r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
+ }
+ else if (VAlign == EGUIA_LOWERRIGHT)
+ {
+ r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
+ }
+
+ irr::video::SColor previous_color(255, 255, 255, 255);
+ for (u32 i=0; i<BrokenText.size(); ++i)
+ {
+ if (HAlign == EGUIA_LOWERRIGHT)
+ {
+ r.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
+ font->getDimension(BrokenText[i].c_str()).Width;
+ }
+
+ std::vector<irr::video::SColor> colors;
+ std::wstring str;
+
+ 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,
+ HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
+
+ r.LowerRightCorner.Y += height;
+ r.UpperLeftCorner.Y += height;
+ }
+ }
+ }
+ }
+
+ IGUIElement::draw();
+}
+
+
+//! Sets another skin independent font.
+void StaticText::setOverrideFont(IGUIFont* font)
+{
+ if (OverrideFont == font)
+ return;
+
+ if (OverrideFont)
+ OverrideFont->drop();
+
+ OverrideFont = font;
+
+ if (OverrideFont)
+ OverrideFont->grab();
+
+ breakText();
+}
+
+//! Gets the override font (if any)
+IGUIFont * StaticText::getOverrideFont() const
+{
+ return OverrideFont;
+}
+
+//! Get the font which is used right now for drawing
+IGUIFont* StaticText::getActiveFont() const
+{
+ if ( OverrideFont )
+ return OverrideFont;
+ IGUISkin* skin = Environment->getSkin();
+ if (skin)
+ return skin->getFont();
+ return 0;
+}
+
+//! Sets another color for the text.
+void StaticText::setOverrideColor(video::SColor color)
+{
+ OverrideColor = color;
+ OverrideColorEnabled = true;
+}
+
+
+//! Sets another color for the text.
+void StaticText::setBackgroundColor(video::SColor color)
+{
+ BGColor = color;
+ OverrideBGColorEnabled = true;
+ Background = true;
+}
+
+
+//! Sets whether to draw the background
+void StaticText::setDrawBackground(bool draw)
+{
+ Background = draw;
+}
+
+
+//! Gets the background color
+video::SColor StaticText::getBackgroundColor() const
+{
+ return BGColor;
+}
+
+
+//! Checks if background drawing is enabled
+bool StaticText::isDrawBackgroundEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return Background;
+}
+
+
+//! Sets whether to draw the border
+void StaticText::setDrawBorder(bool draw)
+{
+ Border = draw;
+}
+
+
+//! Checks if border drawing is enabled
+bool StaticText::isDrawBorderEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return Border;
+}
+
+
+void StaticText::setTextRestrainedInside(bool restrainTextInside)
+{
+ RestrainTextInside = restrainTextInside;
+}
+
+
+bool StaticText::isTextRestrainedInside() const
+{
+ return RestrainTextInside;
+}
+
+
+void StaticText::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
+{
+ HAlign = horizontal;
+ VAlign = vertical;
+}
+
+
+#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7
+const video::SColor& StaticText::getOverrideColor() const
+#else
+video::SColor StaticText::getOverrideColor() const
+#endif
+{
+ return OverrideColor;
+}
+
+
+//! Sets if the static text should use the overide color or the
+//! color in the gui skin.
+void StaticText::enableOverrideColor(bool enable)
+{
+ OverrideColorEnabled = enable;
+}
+
+
+bool StaticText::isOverrideColorEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return OverrideColorEnabled;
+}
+
+
+//! Enables or disables word wrap for using the static text as
+//! multiline text control.
+void StaticText::setWordWrap(bool enable)
+{
+ WordWrap = enable;
+ breakText();
+}
+
+
+bool StaticText::isWordWrapEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return WordWrap;
+}
+
+
+void StaticText::setRightToLeft(bool rtl)
+{
+ if (RightToLeft != rtl)
+ {
+ RightToLeft = rtl;
+ breakText();
+ }
+}
+
+
+bool StaticText::isRightToLeft() const
+{
+ return RightToLeft;
+}
+
+
+//! Breaks the single text line.
+void StaticText::breakText()
+{
+ if (!WordWrap)
+ return;
+
+ BrokenText.clear();
+
+ IGUISkin* skin = Environment->getSkin();
+ IGUIFont* font = getActiveFont();
+ if (!font)
+ return;
+
+ LastBreakFont = font;
+
+ core::stringw line;
+ core::stringw word;
+ core::stringw whitespace;
+ s32 size = Text.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;
+
+ // 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
+ // some order and boundaries which change.
+ if (!RightToLeft)
+ {
+ // regular (left-to-right)
+ for (s32 i=0; i<size; ++i)
+ {
+ c = Text[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;
+ }
+ c = '\0';
+ }
+ else if (c == L'\n') // Unix breaks
+ {
+ lineBreak = true;
+ c = '\0';
+ }
+
+ bool isWhitespace = (c == L' ' || c == 0);
+ if ( !isWhitespace )
+ {
+ // part of a word
+ word += c;
+ }
+
+ if ( isWhitespace || i == (size-1))
+ {
+ if (word.size())
+ {
+ // 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;
+
+ 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) );
+ 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"-");
+ const s32 secondLength = font->getDimension(second.c_str()).Width;
+
+ length = secondLength;
+ line = second;
+ }
+ else
+ {
+ // No soft hyphen found, so there's nothing more we can do
+ // break to next line
+ if (length)
+ BrokenText.push_back(line);
+ length = wordlgth;
+ line = word;
+ }
+ }
+ else if (length && (length + wordlgth + whitelgth > elWidth))
+ {
+ // break to next line
+ BrokenText.push_back(line);
+ length = wordlgth;
+ line = word;
+ }
+ else
+ {
+ // add word to line
+ line += whitespace;
+ line += word;
+ length += whitelgth + wordlgth;
+ }
+
+ word = L"";
+ whitespace = L"";
+ }
+
+ if ( isWhitespace )
+ {
+ whitespace += c;
+ }
+
+ // compute line break
+ if (lineBreak)
+ {
+ line += whitespace;
+ line += word;
+ BrokenText.push_back(line);
+ line = L"";
+ word = L"";
+ whitespace = L"";
+ length = 0;
+ }
+ }
+ }
+
+ line += whitespace;
+ line += word;
+ BrokenText.push_back(line);
+ }
+ else
+ {
+ // right-to-left
+ for (s32 i=size; i>=0; --i)
+ {
+ c = Text[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;
+ }
+ c = '\0';
+ }
+ else if (c == L'\n') // Unix breaks
+ {
+ lineBreak = true;
+ c = '\0';
+ }
+
+ if (c==L' ' || c==0 || i==0)
+ {
+ if (word.size())
+ {
+ // 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 s32 wordlgth = font->getDimension(word.c_str()).Width;
+
+ if (length && (length + wordlgth + whitelgth > elWidth))
+ {
+ // break to next line
+ BrokenText.push_back(line);
+ length = wordlgth;
+ line = word;
+ }
+ else
+ {
+ // add word to line
+ line = whitespace + line;
+ line = word + line;
+ length += whitelgth + wordlgth;
+ }
+
+ word = L"";
+ whitespace = L"";
+ }
+
+ if (c != 0)
+ whitespace = core::stringw(&c, 1) + whitespace;
+
+ // compute line break
+ if (lineBreak)
+ {
+ line = whitespace + line;
+ line = word + line;
+ BrokenText.push_back(line);
+ line = L"";
+ word = L"";
+ whitespace = L"";
+ length = 0;
+ }
+ }
+ else
+ {
+ // yippee this is a word..
+ word = core::stringw(&c, 1) + word;
+ }
+ }
+
+ line = whitespace + line;
+ line = word + line;
+ BrokenText.push_back(line);
+ }
+}
+
+
+//! Sets the new caption of this element.
+void StaticText::setText(const wchar_t* text)
+{
+ IGUIElement::setText(text);
+ breakText();
+}
+
+
+void StaticText::updateAbsolutePosition()
+{
+ IGUIElement::updateAbsolutePosition();
+ breakText();
+}
+
+
+//! Returns the height of the text in pixels when it is drawn.
+s32 StaticText::getTextHeight() const
+{
+ IGUIFont* font = getActiveFont();
+ if (!font)
+ return 0;
+
+ s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
+
+ if (WordWrap)
+ height *= BrokenText.size();
+
+ return height;
+}
+
+
+s32 StaticText::getTextWidth() const
+{
+ IGUIFont * font = getActiveFont();
+ if(!font)
+ return 0;
+
+ if(WordWrap)
+ {
+ s32 widest = 0;
+
+ for(u32 line = 0; line < BrokenText.size(); ++line)
+ {
+ s32 width = font->getDimension(BrokenText[line].c_str()).Width;
+
+ if(width > widest)
+ widest = width;
+ }
+
+ return widest;
+ }
+ else
+ {
+ return font->getDimension(Text.c_str()).Width;
+ }
+}
+
+
+//! Writes attributes of the element.
+//! Implement this to expose the attributes of your element for
+//! scripting languages, editors, debuggers or xml serialization purposes.
+void StaticText::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
+{
+ IGUIStaticText::serializeAttributes(out,options);
+
+ out->addBool ("Border", Border);
+ out->addBool ("OverrideColorEnabled",OverrideColorEnabled);
+ out->addBool ("OverrideBGColorEnabled",OverrideBGColorEnabled);
+ out->addBool ("WordWrap", WordWrap);
+ out->addBool ("Background", Background);
+ out->addBool ("RightToLeft", RightToLeft);
+ out->addBool ("RestrainTextInside", RestrainTextInside);
+ out->addColor ("OverrideColor", OverrideColor);
+ out->addColor ("BGColor", BGColor);
+ out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
+ out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
+
+ // out->addFont ("OverrideFont", OverrideFont);
+}
+
+
+//! Reads attributes of the element
+void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
+{
+ IGUIStaticText::deserializeAttributes(in,options);
+
+ Border = in->getAttributeAsBool("Border");
+ enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+ OverrideBGColorEnabled = in->getAttributeAsBool("OverrideBGColorEnabled");
+ setWordWrap(in->getAttributeAsBool("WordWrap"));
+ Background = in->getAttributeAsBool("Background");
+ RightToLeft = in->getAttributeAsBool("RightToLeft");
+ RestrainTextInside = in->getAttributeAsBool("RestrainTextInside");
+ OverrideColor = in->getAttributeAsColor("OverrideColor");
+ BGColor = in->getAttributeAsColor("BGColor");
+
+ setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
+ (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
+
+ // OverrideFont = in->getAttributeAsFont("OverrideFont");
+}
+
+} // end namespace gui
+} // end namespace irr
+
+
+#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/util/statictext.h b/src/util/statictext.h
new file mode 100644
index 000000000..8d2f879e7
--- /dev/null
+++ b/src/util/statictext.h
@@ -0,0 +1,150 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef __C_GUI_STATIC_TEXT_H_INCLUDED__
+#define __C_GUI_STATIC_TEXT_H_INCLUDED__
+
+#include "IrrCompileConfig.h"
+#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include "IGUIStaticText.h"
+#include "irrArray.h"
+
+#include <vector>
+
+namespace irr
+{
+namespace gui
+{
+ class StaticText : public IGUIStaticText
+ {
+ public:
+
+ //! constructor
+ StaticText(const wchar_t* text, bool border, IGUIEnvironment* environment,
+ IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
+ bool background = false);
+
+ //! destructor
+ virtual ~StaticText();
+
+ //! draws the element and its children
+ virtual void draw();
+
+ //! Sets another skin independent font.
+ virtual void setOverrideFont(IGUIFont* font=0);
+
+ //! Gets the override font (if any)
+ virtual IGUIFont* getOverrideFont() const;
+
+ //! Get the font which is used right now for drawing
+ virtual IGUIFont* getActiveFont() const;
+
+ //! Sets another color for the text.
+ virtual void setOverrideColor(video::SColor color);
+
+ //! Sets another color for the background.
+ virtual void setBackgroundColor(video::SColor color);
+
+ //! Sets whether to draw the background
+ virtual void setDrawBackground(bool draw);
+
+ //! Gets the background color
+ virtual video::SColor getBackgroundColor() const;
+
+ //! Checks if background drawing is enabled
+ virtual bool isDrawBackgroundEnabled() const;
+
+ //! Sets whether to draw the border
+ virtual void setDrawBorder(bool draw);
+
+ //! Checks if border drawing is enabled
+ virtual bool isDrawBorderEnabled() const;
+
+ //! Sets alignment mode for text
+ virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
+
+ //! Gets the override color
+ #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7
+ virtual const video::SColor& getOverrideColor() const;
+ #else
+ virtual video::SColor getOverrideColor() const;
+ #endif
+
+ //! Sets if the static text should use the overide color or the
+ //! color in the gui skin.
+ virtual void enableOverrideColor(bool enable);
+
+ //! Checks if an override color is enabled
+ virtual bool isOverrideColorEnabled() const;
+
+ //! Set whether the text in this label should be clipped if it goes outside bounds
+ virtual void setTextRestrainedInside(bool restrainedInside);
+
+ //! Checks if the text in this label should be clipped if it goes outside bounds
+ virtual bool isTextRestrainedInside() const;
+
+ //! Enables or disables word wrap for using the static text as
+ //! multiline text control.
+ virtual void setWordWrap(bool enable);
+
+ //! Checks if word wrap is enabled
+ virtual bool isWordWrapEnabled() const;
+
+ //! Sets the new caption of this element.
+ virtual void setText(const wchar_t* text);
+
+ //! Returns the height of the text in pixels when it is drawn.
+ virtual s32 getTextHeight() const;
+
+ //! Returns the width of the current text, in the current font
+ virtual s32 getTextWidth() const;
+
+ //! Updates the absolute position, splits text if word wrap is enabled
+ virtual void updateAbsolutePosition();
+
+ //! Set whether the string should be interpreted as right-to-left (RTL) text
+ /** \note This component does not implement the Unicode bidi standard, the
+ text of the component should be already RTL if you call this. The
+ main difference when RTL is enabled is that the linebreaks for multiline
+ elements are performed starting from the end.
+ */
+ virtual void setRightToLeft(bool rtl);
+
+ //! Checks if the text should be interpreted as right-to-left text
+ virtual bool isRightToLeft() const;
+
+ //! Writes attributes of the element.
+ virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
+
+ //! Reads attributes of the element
+ virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
+
+ private:
+
+ //! Breaks the single text line.
+ void breakText();
+
+ EGUI_ALIGNMENT HAlign, VAlign;
+ bool Border;
+ bool OverrideColorEnabled;
+ bool OverrideBGColorEnabled;
+ bool WordWrap;
+ bool Background;
+ bool RestrainTextInside;
+ bool RightToLeft;
+
+ video::SColor OverrideColor, BGColor;
+ gui::IGUIFont* OverrideFont;
+ gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated.
+
+ core::array< core::stringw > BrokenText;
+ };
+
+} // end namespace gui
+} // end namespace irr
+
+#endif // _IRR_COMPILE_WITH_GUI_
+
+#endif // C_GUI_STATIC_TEXT_H_INCLUDED