Add most of the logic to word wrap list items

This commit is contained in:
Alayan 2018-10-14 04:04:37 +02:00
parent f33a9c1a18
commit bf7354a7d8
5 changed files with 165 additions and 5 deletions

View File

@ -11,6 +11,7 @@
#include "IGUIFont.h"
#include "IGUISpriteBank.h"
#include "IGUIScrollBar.h"
#include "utils/string_utils.hpp"
#include "utils/time.hpp"
@ -555,11 +556,30 @@ void CGUISTKListBox::draw()
if ( i==Selected && hl )
font_color = EGUI_LBC_TEXT_HIGHLIGHT;
Font->draw(
Items[i].m_contents[x].m_text.c_str(),
textRect,
hasItemOverrideColor(i, font_color) ? getItemOverrideColor(i, font_color) : getItemDefaultColor(font_color),
Items[i].m_contents[x].m_center, true, &clientClip);
std::wstring cell_text = Items[i].m_contents[x].m_text.c_str();
std::vector<std::wstring> cell_text_lines;
if (Items[i].m_word_wrap)
{
StringUtils::breakText(cell_text, cell_text_lines, 300, Font);
}
else
{
cell_text_lines.push_back(cell_text);
}
core::rect<s32> lineRect = textRect;
for (unsigned int i=0; i<cell_text_lines.size(); i++)
{
irr::core::stringw text_line = cell_text_lines[i].c_str();
Font->draw(
text_line,
lineRect,
hasItemOverrideColor(i, font_color) ? getItemOverrideColor(i, font_color) : getItemDefaultColor(font_color),
Items[i].m_contents[x].m_center, true, &clientClip);
lineRect.UpperLeftCorner.Y += 30;
lineRect.LowerRightCorner.Y += 30;
}
//Position back to inital pos
if (IconBank && (Items[i].m_contents[x].m_icon > -1))

View File

@ -49,6 +49,8 @@ namespace irr
std::string m_internal_name;
int m_current_id;
bool m_word_wrap = false;
// A multicolor extension
struct ListItemOverrideColor
{

View File

@ -239,6 +239,7 @@ void ListWidget::addItem( const std::string& internal_name,
ListItem newItem;
newItem.m_internal_name = internal_name;
newItem.m_contents.push_back(cell);
newItem.m_word_wrap = (m_properties[PROP_WORD_WRAP] == "true");
CGUISTKListBox* list = getIrrlichtElement<CGUISTKListBox>();
assert(list != NULL);
@ -266,6 +267,7 @@ void ListWidget::addItem(const std::string& internal_name,
{
newItem.m_contents.push_back(contents[i]);
}
newItem.m_word_wrap = (m_properties[PROP_WORD_WRAP] == "true");
CGUISTKListBox* list = getIrrlichtElement<CGUISTKListBox>();
assert(list != NULL);

View File

@ -908,6 +908,136 @@ namespace StringUtils
return out;
}
// ------------------------------------------------------------------------
/** Breaks the text so that each line is smaller than max_width with the current settings.
* The result is put into output, a vector of strings, with each line having its own string */
// TODO : try to get rid of the complications induced by wchar
void breakText(std::wstring& input, std::vector<std::wstring> &output, unsigned int max_width, irr::gui::IGUIFont* font)
{
output.clear();
// We need to use a wstring to get wchar_t later
std::wstring work_copy = input;
wchar_t c;
unsigned int index = 0;
// The index of the last character to include before the linebreak
unsigned int break_index = 0;
irr::core::dimension2du area;
// Algorithm :
// 1)If the index exceed the work_copy string size, go to step 7a)
// 2)We look at the character at the index
// 3)We check if the current character is a linebreak. If yes, go to step 7a)
// 4)We check if it is part of a multi-wchar unicode character.
// If we are in a multi-wchar unicode character, go to step 6.
// We don't check if it's the first or second one, because those characters
// are not a suitable break-point anyway. In theory, this doesn't work well
// if only such characters are used, but this is a silly theoretical case.
// 5a)We check if the length of the string including the new character is < max_width
// 5b)If it is smaller, and a suitable character to break, we update the break_index
// 5c)If it is bigger, we go to step 7
// 6)If no linebreak has been requested, increment the index, and return to step 1)
// 7a)If a linebreak has been requested, we push back the substring from 0 to break_index,
// and truncate that substring from work_copy. The index is reset to 0.
// If break_index is 0, we cut the string at the current index.
// 7b)If we have broken the whole input text into lines, we quit the loop.
while(true)
{
// We have reached the end of the string, we just need to push back the current content
// Step 1
if (index >= work_copy.size());
{
break_index = index-1;
goto push_text; // Avoid complicating things with stupid checks
}
// Step 2
c = work_copy[index];
// Step 3
if (c == L'\r' || c == L'\n')
{
break_index = index;
if (c == L'\r' && index+1 < work_copy.size() && work_copy[index+1] == L'\n') // Windows breaks
work_copy.erase(index+1);
goto push_text;
}
// Step 4
if (partOfLongUnicodeChar(c))
goto next_loop_no_push;
// Step 5
// index+1 because index starts at 0 and substr takes initial pos and length as arguments
area = font->getDimension(work_copy.substr(0,index+1).c_str());
if (area.Width < max_width)
{
if (breakable(c))
break_index = index;
}
else
{
// Not enough room to draw even 1 character, abort
if (index==0)
{
Log::error("StringUtils",
"Not enough width to fit a character. Width is %i.", max_width);
break;
}
goto push_text;
}
// Step 6
next_loop_no_push:
index++;
continue;
// Step 7
push_text:
std::wstring text_line = work_copy.substr(0,break_index);
output.push_back(text_line);
// If the line break was the last char of the input string
if (work_copy.size() == break_index+1)
{
// The text is entirely treated
break;
}
else
{
work_copy = work_copy.substr(break_index+1); // All the string except the pushed back part
index = 0;
}
} // While(true) - active until the whole string has been broken and copied
} // breakText
/* This function checks if a char is suitable to break lines.
* Currently a copy of the function found at irrlicht/include/utfwrapping.h */
bool breakable (wchar_t c)
{
if ((c > 12287 && c < 40960) || //Common CJK words
(c > 44031 && c < 55204) || //Hangul
(c > 63743 && c < 64256) || //More Chinese
c == 173 || c == L' ' || //Soft hyphen and white space
c == 47 || c == 92) //Slash and blackslash
return true;
return false;
} // breakable
/* This function checks if a char is part of a two wchars unicode symbol */
bool partOfLongUnicodeChar (wchar_t c)
{
#ifdef WIN32
if (c >= 0x10000)
return true;
return false;
#else
return false; //A wchar_t in linux or max uses 32 bits
#endif
} // partOfLongUnicodeChar
} // namespace StringUtils

View File

@ -26,6 +26,7 @@
#include <vector>
#include <sstream>
#include <irrString.h>
#include <IGUIFont.h>
#include "utils/constants.hpp"
#include "utils/types.hpp"
#include "utils/log.hpp"
@ -246,6 +247,11 @@ namespace StringUtils
std::string wideToUtf8(const irr::core::stringw& input);
std::string findAndReplace(const std::string& source, const std::string& find, const std::string& replace);
std::string removeWhitespaces(const std::string& input);
void breakText(const std::wstring& input, std::vector<std::wstring> &output,
unsigned int max_width, irr::gui::IGUIFont* font);
bool breakable (wchar_t c);
bool partOfLongUnicodeChar (wchar_t c);
inline std::string getUserAgentString()
{
std::string uagent(std::string("SuperTuxKart/") + STK_VERSION);