Fixed text ellipsis ...
... (triple dot being added after too long text) not being UTF-8-aware. Added font::make_text_ellipsis function, that works like word_wrap_text, but instead adds ellipsis on too long text Made word_wrap_text UTF-8-aware.
This commit is contained in:
parent
c79e038759
commit
695bc5346c
4 changed files with 171 additions and 113 deletions
209
src/font.cpp
209
src/font.cpp
|
@ -336,11 +336,13 @@ SDL_Rect draw_text_line(SDL_Surface *gui_surface, const SDL_Rect& area, int size
|
|||
return res;
|
||||
}
|
||||
|
||||
const std::string etext = make_text_ellipsis(text, size, area.w);
|
||||
|
||||
if(gui_surface == NULL) {
|
||||
return text_size(font,text,colour,style);
|
||||
return text_size(font,etext,colour,style);
|
||||
}
|
||||
|
||||
scoped_sdl_surface surface(render_text(font,text,colour,style));
|
||||
scoped_sdl_surface surface(render_text(font,etext,colour,style));
|
||||
if(surface == NULL) {
|
||||
SDL_Rect res = {0,0,0,0};
|
||||
return res;
|
||||
|
@ -358,20 +360,12 @@ SDL_Rect draw_text_line(SDL_Surface *gui_surface, const SDL_Rect& area, int size
|
|||
dest.w = surface->w;
|
||||
dest.h = surface->h;
|
||||
|
||||
if(line_width(text, size) > area.w) {
|
||||
tooltips::add_tooltip(dest,text);
|
||||
}
|
||||
|
||||
if(dest.x + dest.w > area.x + area.w) {
|
||||
|
||||
if(text.size() > 3) {
|
||||
std::string txt = text;
|
||||
if(std::count(txt.end()-3,txt.end(),'.') == 3) {
|
||||
txt.erase(txt.end()-4);
|
||||
} else {
|
||||
tooltips::add_tooltip(dest,text);
|
||||
std::fill(txt.end()-3,txt.end(),'.');
|
||||
}
|
||||
|
||||
return draw_text_line(gui_surface,area,size,colour,txt,x,y,bg,false,style);
|
||||
}
|
||||
|
||||
std::cerr << "Error: text with ellipsis does not fit !\n";
|
||||
dest.w = area.x + area.w - dest.x;
|
||||
}
|
||||
|
||||
|
@ -495,96 +489,123 @@ std::string remove_first_space(const std::string& text)
|
|||
|
||||
namespace font {
|
||||
|
||||
int line_width(const std::string line, int font_size)
|
||||
{
|
||||
int line_width(const std::string line, int font_size)
|
||||
{
|
||||
|
||||
TTF_Font* const font = get_font(font_size);
|
||||
if(font == NULL) {
|
||||
std::cerr << "Could not get font\n";
|
||||
return 0;
|
||||
}
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
TTF_Font* const font = get_font(font_size);
|
||||
if(font == NULL) {
|
||||
std::cerr << "Could not get font\n";
|
||||
return 0;
|
||||
}
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
|
||||
TTF_SizeUTF8(font, line.c_str(), &w, &h);
|
||||
TTF_SizeUTF8(font, line.c_str(), &w, &h);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
std::string word_wrap_text(const std::string& unwrapped_text, int font_size, int max_width)
|
||||
{
|
||||
std::string wrapped_text; // the final result
|
||||
|
||||
size_t word_start_pos = 0;
|
||||
std::string cur_word; // including start-whitespace
|
||||
std::string cur_line; // the whole line so far
|
||||
|
||||
for(int c = 0; c < unwrapped_text.length(); c++) {
|
||||
|
||||
// Find the next word
|
||||
bool forced_line_break = false;
|
||||
if (c == unwrapped_text.length() - 1) {
|
||||
cur_word = unwrapped_text.substr(word_start_pos, c + 1 - word_start_pos);
|
||||
word_start_pos = c + 1;
|
||||
} else if (unwrapped_text[c] == '\n') {
|
||||
cur_word = unwrapped_text.substr(word_start_pos, c + 1 - word_start_pos);
|
||||
word_start_pos = c + 1;
|
||||
forced_line_break = true;
|
||||
} else if (unwrapped_text[c] == ' ') {
|
||||
cur_word = unwrapped_text.substr(word_start_pos, c - word_start_pos);
|
||||
word_start_pos = c;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test if the line should be wrapped or not
|
||||
if (line_width(cur_line + cur_word, font_size) > max_width) {
|
||||
|
||||
if (line_width(cur_word, font_size) > (max_width /*/ 2*/)) {
|
||||
// The last word is too big to fit in a nice way, split it on a char basis
|
||||
for (std::string::iterator i = cur_word.begin(); i != cur_word.end(); ++i) {
|
||||
if (line_width(cur_line + *i, font_size) > max_width) {
|
||||
wrapped_text += cur_line + '\n';
|
||||
cur_line = *i;
|
||||
} else {
|
||||
cur_line += *i;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Split the line on a word basis
|
||||
wrapped_text += cur_line + '\n';
|
||||
cur_line = remove_first_space(cur_word);
|
||||
|
||||
return w;
|
||||
}
|
||||
} else {
|
||||
cur_line += cur_word;
|
||||
}
|
||||
|
||||
if (forced_line_break) {
|
||||
wrapped_text += cur_line;
|
||||
cur_line = "";
|
||||
forced_line_break = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string word_wrap_text(const std::string& unwrapped_text, int font_size, int max_width)
|
||||
{
|
||||
std::cerr << "Wrapping word " << unwrapped_text << "\n";
|
||||
|
||||
std::string wrapped_text; // the final result
|
||||
|
||||
size_t word_start_pos = 0;
|
||||
std::string cur_word; // including start-whitespace
|
||||
std::string cur_line; // the whole line so far
|
||||
|
||||
for(int c = 0; c < unwrapped_text.length(); c++) {
|
||||
|
||||
// Find the next word
|
||||
bool forced_line_break = false;
|
||||
if (c == unwrapped_text.length() - 1) {
|
||||
cur_word = unwrapped_text.substr(word_start_pos, c + 1 - word_start_pos);
|
||||
word_start_pos = c + 1;
|
||||
} else if (unwrapped_text[c] == '\n') {
|
||||
cur_word = unwrapped_text.substr(word_start_pos, c + 1 - word_start_pos);
|
||||
word_start_pos = c + 1;
|
||||
forced_line_break = true;
|
||||
} else if (unwrapped_text[c] == ' ') {
|
||||
cur_word = unwrapped_text.substr(word_start_pos, c - word_start_pos);
|
||||
word_start_pos = c;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test if the line should be wrapped or not
|
||||
if (line_width(cur_line + cur_word, font_size) > max_width) {
|
||||
|
||||
if (line_width(cur_word, font_size) > (max_width /*/ 2*/)) {
|
||||
// The last word is too big to fit in a nice way, split it on a char basis
|
||||
std::vector<std::string> split_word = split_utf8_string(cur_word);
|
||||
|
||||
for (std::vector<std::string>::iterator i = split_word.begin(); i != split_word.end(); ++i) {
|
||||
if (line_width(cur_line + *i, font_size) > max_width) {
|
||||
wrapped_text += cur_line + '\n';
|
||||
cur_line = *i;
|
||||
} else {
|
||||
cur_line += *i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
// Split the line on a word basis
|
||||
wrapped_text += cur_line + '\n';
|
||||
cur_line = remove_first_space(cur_word);
|
||||
|
||||
}
|
||||
} else {
|
||||
cur_line += cur_word;
|
||||
}
|
||||
|
||||
if (forced_line_break) {
|
||||
wrapped_text += cur_line;
|
||||
cur_line = "";
|
||||
forced_line_break = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't forget to add the text left in cur_line
|
||||
if (cur_line != "") {
|
||||
wrapped_text += cur_line + '\n';
|
||||
}
|
||||
// Don't forget to add the text left in cur_line
|
||||
if (cur_line != "") {
|
||||
wrapped_text += cur_line + '\n';
|
||||
}
|
||||
|
||||
return wrapped_text;
|
||||
}
|
||||
return wrapped_text;
|
||||
}
|
||||
|
||||
std::string make_text_ellipsis(const std::string &text, int font_size, int max_width)
|
||||
{
|
||||
static const std::string ellipsis = "...";
|
||||
|
||||
if(line_width(text, font_size) <= max_width)
|
||||
return text;
|
||||
if(line_width(ellipsis, font_size) > max_width)
|
||||
return "";
|
||||
|
||||
std::vector<std::string> characters = split_utf8_string(text);
|
||||
std::string current_substring = "";
|
||||
|
||||
SDL_Rect draw_wrapped_text(display* gui, const SDL_Rect& area, int font_size,
|
||||
for(std::vector<std::string>::const_iterator itor = characters.begin(); itor != characters.end(); ++itor) {
|
||||
if (line_width(current_substring + *itor + ellipsis, font_size ) > max_width) {
|
||||
return current_substring + ellipsis;
|
||||
}
|
||||
|
||||
current_substring += *itor;
|
||||
}
|
||||
|
||||
return text; // Should not happen
|
||||
}
|
||||
|
||||
SDL_Rect draw_wrapped_text(display* gui, const SDL_Rect& area, int font_size,
|
||||
const SDL_Color& colour, const std::string& text,
|
||||
int x, int y, int max_width, SDL_Surface* bg)
|
||||
{
|
||||
std::string wrapped_text = word_wrap_text(text, font_size, max_width);
|
||||
return font::draw_text(gui, area, font_size, colour, wrapped_text, x, y, bg, false, NO_MARKUP);
|
||||
}
|
||||
{
|
||||
std::string wrapped_text = word_wrap_text(text, font_size, max_width);
|
||||
return font::draw_text(gui, area, font_size, colour, wrapped_text, x, y, bg, false, NO_MARKUP);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,12 @@ int line_width(const std::string line, int font_size);
|
|||
///
|
||||
std::string word_wrap_text(const std::string& unwrapped_text, int font_size, int max_width);
|
||||
|
||||
///
|
||||
/// If the text excedes the specified max width, end it with an ellipsis (...)
|
||||
///
|
||||
std::string make_text_ellipsis(const std::string& text, int font_size, int max_width);
|
||||
|
||||
|
||||
///
|
||||
/// Draw text on the screen. This method makes sure that the text
|
||||
/// fits within a given maximum width. If a line exceedes this width it
|
||||
|
|
|
@ -227,32 +227,39 @@ std::string wstring_to_utf8(const wide_string &src)
|
|||
}
|
||||
}
|
||||
|
||||
int byte_size_from_utf8_first(unsigned char ch)
|
||||
{
|
||||
int count;
|
||||
|
||||
if ((ch & 0x80) == 0)
|
||||
count = 1;
|
||||
else if ((ch & 0xE0) == 0xC0)
|
||||
count = 2;
|
||||
else if ((ch & 0xF0) == 0xE0)
|
||||
count = 3;
|
||||
else if ((ch & 0xF8) == 0xF0)
|
||||
count = 4;
|
||||
else if ((ch & 0xFC) == 0xF8)
|
||||
count = 5;
|
||||
else if ((ch & 0xFE) == 0xFC)
|
||||
count = 6;
|
||||
else
|
||||
throw invalid_utf8_exception(); /* stop on invalid characters */
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
wide_string utf8_to_wstring(const std::string &src)
|
||||
{
|
||||
wide_string ret;
|
||||
wchar_t ch;
|
||||
wchar_t ch;
|
||||
std::string::const_iterator i;
|
||||
|
||||
try {
|
||||
for(i = src.begin(); i != src.end(); ++i ) {
|
||||
ch = (unsigned char)*i;
|
||||
int count;
|
||||
|
||||
if ((ch & 0x80) == 0)
|
||||
count = 1;
|
||||
else if ((ch & 0xE0) == 0xC0)
|
||||
count = 2;
|
||||
else if ((ch & 0xF0) == 0xE0)
|
||||
count = 3;
|
||||
else if ((ch & 0xF8) == 0xF0)
|
||||
count = 4;
|
||||
else if ((ch & 0xFC) == 0xF8)
|
||||
count = 5;
|
||||
else if ((ch & 0xFE) == 0xFC)
|
||||
count = 6;
|
||||
else
|
||||
throw invalid_utf8_exception(); /* stop on invalid characters */
|
||||
|
||||
const int count = byte_size_from_utf8_first(ch);
|
||||
|
||||
if(i + count - 1 == src.end())
|
||||
throw invalid_utf8_exception();
|
||||
|
||||
|
@ -283,9 +290,32 @@ wide_string utf8_to_wstring(const std::string &src)
|
|||
|
||||
}
|
||||
|
||||
std::vector<std::string> split_utf8_string(const std::string &src)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
|
||||
try {
|
||||
for(size_t i = 0; i < src.size(); /* nop */) {
|
||||
const int size = byte_size_from_utf8_first(src[i]);
|
||||
if(i + size > src.size())
|
||||
throw invalid_utf8_exception();
|
||||
|
||||
ret.push_back(src.substr(i, size));
|
||||
i += size;
|
||||
}
|
||||
}
|
||||
|
||||
catch(invalid_utf8_exception e) {
|
||||
std::cerr << "Invalid UTF-8 string: \"" << src << "\"\n";
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string wstring_to_string(const wide_string &src)
|
||||
{
|
||||
return wstring_to_utf8(src);
|
||||
return wstring_to_utf8(src);
|
||||
}
|
||||
|
||||
wide_string string_to_wstring(const std::string &src)
|
||||
|
|
|
@ -61,6 +61,7 @@ std::string get_locale();
|
|||
|
||||
//functions for converting Unicode wide-char strings to UTF-8 encoded
|
||||
//strings, back and forth
|
||||
std::vector<std::string> split_utf8_string(const std::string &src);
|
||||
std::string wstring_to_string(const wide_string &);
|
||||
wide_string string_to_wstring(const std::string &);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue