diff options
author | TriBlade9 <triblade9@mail.com> | 2015-01-16 14:54:26 +0800 |
---|---|---|
committer | Ekdohibs <nathanael.courant@laposte.net> | 2016-05-31 17:34:29 +0200 |
commit | 1d40385d4aacf0cbea4b19ff06940e8c9bebaf47 (patch) | |
tree | 39732d23598a1c14d514ff35d241f9499f0a3c13 | |
parent | 0e44af9f7056a78a8e561f708751acceacd149c1 (diff) | |
download | minetest-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/
-rw-r--r-- | builtin/game/chatcommands.lua | 2 | ||||
-rw-r--r-- | builtin/game/misc.lua | 17 | ||||
-rw-r--r-- | src/chat.cpp | 29 | ||||
-rw-r--r-- | src/chat.h | 6 | ||||
-rw-r--r-- | src/client/CMakeLists.txt | 1 | ||||
-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.cpp | 14 | ||||
-rw-r--r-- | src/util/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/util/coloredstring.cpp | 68 | ||||
-rw-r--r-- | src/util/coloredstring.h | 44 | ||||
-rw-r--r-- | src/util/statictext.cpp | 654 | ||||
-rw-r--r-- | src/util/statictext.h | 150 |
13 files changed, 998 insertions, 28 deletions
diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 3350140ee..2627559a5 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 = "/"..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 de41cfc91..8d5c80216 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -197,3 +197,20 @@ 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 + +function core.colorize(color, message) + return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("ffffff") +end 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 |