Merge pull request #12445 from toshixm/CJK_folding_rev2

Improve message wrapping with CJK languages
This commit is contained in:
Autechre 2021-06-04 20:14:55 +02:00 committed by GitHub
commit fce9da5e76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 469 additions and 140 deletions

View File

@ -1832,6 +1832,7 @@ end:
bool gfx_animation_line_ticker(gfx_animation_ctx_line_ticker_t *line_ticker)
{
char *wrapped_str = NULL;
size_t wrapped_str_len = 0;
struct string_list lines = {0};
size_t line_offset = 0;
bool success = false;
@ -1848,15 +1849,18 @@ bool gfx_animation_line_ticker(gfx_animation_ctx_line_ticker_t *line_ticker)
goto end;
/* Line wrap input string */
wrapped_str = (char*)malloc((strlen(line_ticker->str) + 1) * sizeof(char));
wrapped_str_len = strlen(line_ticker->str) + 1 + 10; /* 10 bytes use for inserting '\n' */
wrapped_str = (char*)malloc(wrapped_str_len);
if (!wrapped_str)
goto end;
wrapped_str[0] = '\0';
word_wrap(
wrapped_str,
wrapped_str_len,
line_ticker->str,
(int)line_ticker->line_len,
true, 0);
100, 0);
if (string_is_empty(wrapped_str))
goto end;
@ -1923,6 +1927,7 @@ end:
bool gfx_animation_line_ticker_smooth(gfx_animation_ctx_line_ticker_smooth_t *line_ticker)
{
char *wrapped_str = NULL;
size_t wrapped_str_len = 0;
struct string_list lines = {0};
int glyph_width = 0;
int glyph_height = 0;
@ -1936,6 +1941,11 @@ bool gfx_animation_line_ticker_smooth(gfx_animation_ctx_line_ticker_smooth_t *li
bool success = false;
bool is_active = false;
gfx_animation_t *p_anim = anim_get_ptr();
const char *wideglyph_str = msg_hash_get_wideglyph_str();
int wideglyph_width = 100;
void (*word_wrap_func)(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines)
= wideglyph_str ? word_wrap_wideglyph : word_wrap;
/* Sanity check */
if (!line_ticker)
@ -1962,6 +1972,18 @@ bool gfx_animation_line_ticker_smooth(gfx_animation_ctx_line_ticker_smooth_t *li
if (glyph_width <= 0)
goto end;
if (wideglyph_str)
{
wideglyph_width = font_driver_get_message_width(
line_ticker->font, wideglyph_str, strlen(wideglyph_str),
line_ticker->font_scale);
if (wideglyph_width > 0)
wideglyph_width = wideglyph_width * 100 / glyph_width;
else
wideglyph_width = 100;
}
/* > Height */
glyph_height = font_driver_get_line_height(
line_ticker->font, line_ticker->font_scale);
@ -1977,15 +1999,18 @@ bool gfx_animation_line_ticker_smooth(gfx_animation_ctx_line_ticker_smooth_t *li
goto end;
/* Line wrap input string */
wrapped_str = (char*)malloc((strlen(line_ticker->src_str) + 1) * sizeof(char));
wrapped_str_len = strlen(line_ticker->src_str) + 1 + 10; /* 10 bytes use for inserting '\n' */
wrapped_str = (char*)malloc(wrapped_str_len);
if (!wrapped_str)
goto end;
wrapped_str[0] = '\0';
word_wrap(
(word_wrap_func)(
wrapped_str,
wrapped_str_len,
line_ticker->src_str,
(int)line_len,
true, 0);
wideglyph_width, 0);
if (string_is_empty(wrapped_str))
goto end;

View File

@ -361,7 +361,8 @@ void gfx_widgets_msg_queue_push(
/* Single line text > two lines text > two lines
* text with expanded width */
unsigned title_length = (unsigned)strlen(title);
char *msg = strdup(title);
char *msg = NULL;
size_t msg_len = 0;
unsigned width = menu_is_alive
? p_dispwidget->msg_queue_default_rect_width_menu_alive
: p_dispwidget->msg_queue_default_rect_width;
@ -372,6 +373,12 @@ void gfx_widgets_msg_queue_push(
1);
msg_widget->text_height = p_dispwidget->gfx_widget_fonts.msg_queue.line_height;
msg_len = title_length + 1 + 1; /* 1 byte uses for inserting '\n' */
msg = (char *)malloc(msg_len);
if (!msg)
return;
msg[0] = '\0';
/* Text is too wide, split it into two lines */
if (text_width > width)
{
@ -381,13 +388,16 @@ void gfx_widgets_msg_queue_push(
if ((text_width - (text_width >> 2)) < width)
width = text_width - (text_width >> 2);
word_wrap(msg, msg, (title_length * width) / text_width,
false, 2);
word_wrap(msg, msg_len, title, (title_length * width) / text_width,
100, 2);
msg_widget->text_height *= 2;
}
else
{
width = text_width;
strlcpy(msg, title, msg_len);
}
msg_widget->msg = msg;
msg_widget->msg_len = (unsigned)strlen(msg);

View File

@ -1811,3 +1811,8 @@ const char *msg_hash_to_str_chs(enum msg_hash_enums msg)
return "null";
}
const char *msg_hash_get_wideglyph_str_chs(void)
{
return "";
}

View File

@ -1867,3 +1867,8 @@ const char *msg_hash_to_str_cht(enum msg_hash_enums msg)
return "null";
}
const char *msg_hash_get_wideglyph_str_cht(void)
{
return "";
}

View File

@ -2319,3 +2319,8 @@ const char *msg_hash_to_str_jp(enum msg_hash_enums msg) {
return "null";
}
const char *msg_hash_get_wideglyph_str_jp(void)
{
return "";
}

View File

@ -2320,3 +2320,8 @@ const char *msg_hash_to_str_ko(enum msg_hash_enums msg) {
return "null";
}
const char *msg_hash_get_wideglyph_str_ko(void)
{
return "";
}

View File

@ -148,9 +148,61 @@ char *string_trim_whitespace_right(char *const s);
/* Remove leading and trailing whitespaces */
char *string_trim_whitespace(char *const s);
/* max_lines == 0 means no limit */
void word_wrap(char *dst, const char *src,
int line_width, bool unicode, unsigned max_lines);
/*
* Wraps string specified by 'src' to destination buffer
* specified by 'dst' and 'dst_size'.
* This function assumes that all glyphs in the string
* have an on-screen pixel width similar to that of
* regular Latin characters - i.e. it will not wrap
* correctly any text containing so-called 'wide' Unicode
* characters (e.g. CJK languages, emojis, etc.).
*
* @param dst pointer to destination buffer.
* @param dst_size size of destination buffer.
* @param src pointer to input string.
* @param line_width max number of characters per line.
* @param wideglyph_width not used, but is necessary to keep
* compatibility with word_wrap_wideglyph().
* @param max_lines max lines of destination string.
* 0 means no limit.
*/
void word_wrap(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines);
/*
* Wraps string specified by 'src' to destination buffer
* specified by 'dst' and 'dst_size'.
* This function assumes that all glyphs in the string
* are:
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
* pixel width similar to that of regular Latin characters
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
* with an on-screen pixel width defined by 'wideglyph_width'
* Note that wrapping may occur in inappropriate locations
* if 'src' string contains 'wide' Unicode characters whose
* on-screen pixel width deviates greatly from the set
* 'wideglyph_width' value.
*
* @param dst pointer to destination buffer.
* @param dst_size size of destination buffer.
* @param src pointer to input string.
* @param line_width max number of characters per line.
* @param wideglyph_width effective width of 'wide' Unicode glyphs.
* the value here is normalised relative to the
* typical on-screen pixel width of a regular
* Latin character:
* - a regular Latin character is defined to
* have an effective width of 100
* - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width)
* - e.g. if 'wide' Unicode characters in 'src'
* have an on-screen pixel width twice that of
* regular Latin characters, wideglyph_width
* would be 200
* @param max_lines max lines of destination string.
* 0 means no limit.
*/
void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines);
/* Splits string into tokens seperated by 'delim'
* > Returned token string must be free()'d

View File

@ -187,24 +187,23 @@ char *string_trim_whitespace(char *const s)
return s;
}
void word_wrap(char *dst, const char *src, int line_width, bool unicode, unsigned max_lines)
void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, unsigned max_lines)
{
char *lastspace = NULL;
unsigned counter = 0;
unsigned lines = 1;
int src_len = (int)strlen(src);
size_t src_len = strlen(src);
const char *src_end = src + src_len;
/* Prevent buffer overflow */
if (dst_size < src_len + 1)
return;
/* Early return if src string length is less
* than line width */
if (src_len < line_width)
{
/* TODO/FIXME: Would be better to use strcpy(),
* but the behaviour of this function is undefined
* if src and dst point to the same buffer */
while (src_len--)
*dst++ = *src++;
*dst = '\0';
strcpy(dst, src);
return;
}
@ -213,7 +212,7 @@ void word_wrap(char *dst, const char *src, int line_width, bool unicode, unsigne
unsigned char_len;
char_len = (unsigned)(utf8skip(src, 1) - src);
counter += unicode ? 1 : char_len;
counter++;
if (*src == ' ')
lastspace = dst; /* Remember the location of the whitespace */
@ -226,13 +225,9 @@ void word_wrap(char *dst, const char *src, int line_width, bool unicode, unsigne
/* Early return if remaining src string
* length is less than line width */
src_len = (int)(src_end - src);
if (src_len <= line_width)
if (src_end - src <= line_width)
{
while (src_len--)
*dst++ = *src++;
*dst = '\0';
strcpy(dst, src);
return;
}
}
@ -257,13 +252,135 @@ void word_wrap(char *dst, const char *src, int line_width, bool unicode, unsigne
/* Early return if remaining src string
* length is less than line width */
src_len = (int)(src_end - src);
if (src_len < line_width)
if (src_end - src < line_width)
{
while (src_len--)
*dst++ = *src++;
*dst = '\0';
strcpy(dst, src);
return;
}
}
}
}
*dst = '\0';
}
void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, unsigned max_lines)
{
char *lastspace = NULL;
char *lastwideglyph = NULL;
const char *src_end = src + strlen(src);
unsigned lines = 1;
/* 'line_width' means max numbers of characters per line,
* but this metric is only meaningful when dealing with
* 'regular' glyphs that have an on-screen pixel width
* similar to that of regular Latin characters.
* When handing so-called 'wide' Unicode glyphs, it is
* necessary to consider the actual on-screen pixel width
* of each character.
* In order to do this, we create a distinction between
* regular Latin 'non-wide' glyphs and 'wide' glyphs, and
* normalise all values relative to the on-screen pixel
* width of regular Latin characters:
* - Regular 'non-wide' glyphs have a normalised width of 100
* - 'line_width' is therefore normalised to 100 * (width_in_characters)
* - 'wide' glyphs have a normalised width of
* 100 * (wide_character_pixel_width / latin_character_pixel_width)
* - When a character is detected, the position in the current
* line is incremented by the regular normalised width of 100
* - If that character is then determined to be a 'wide'
* glyph, the position in the current line is further incremented
* by the difference between the normalised 'wide' and 'non-wide'
* width values */
unsigned counter_normalized = 0;
int line_width_normalized = line_width * 100;
int additional_counter_normalized = wideglyph_width - 100;
/* Early return if src string length is less
* than line width */
if (src_end - src < line_width)
{
strlcpy(dst, src, dst_size);
return;
}
while (*src != '\0')
{
unsigned char_len;
char_len = (unsigned)(utf8skip(src, 1) - src);
counter_normalized += 100;
/* Prevent buffer overflow */
if (char_len >= dst_size)
break;
if (*src == ' ')
lastspace = dst; /* Remember the location of the whitespace */
else if (*src == '\n')
{
/* If newlines embedded in the input,
* reset the index */
lines++;
counter_normalized = 0;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src <= line_width)
{
strlcpy(dst, src, dst_size);
return;
}
}
else if (char_len >= 3)
{
/* Remember the location of the first byte
* whose length as UTF-8 >= 3*/
lastwideglyph = dst;
counter_normalized += additional_counter_normalized;
}
dst_size -= char_len;
while (char_len--)
*dst++ = *src++;
if (counter_normalized >= (unsigned)line_width_normalized)
{
counter_normalized = 0;
if (max_lines != 0 && lines >= max_lines)
continue;
else if (lastwideglyph && (!lastspace || lastwideglyph > lastspace))
{
/* Insert newline character */
*lastwideglyph = '\n';
lines++;
src -= dst - lastwideglyph;
dst = lastwideglyph + 1;
lastwideglyph = NULL;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src <= line_width)
{
strlcpy(dst, src, dst_size);
return;
}
}
else if (lastspace)
{
/* Replace nearest (previous) whitespace
* with newline character */
*lastspace = '\n';
lines++;
src -= dst - lastspace - 1;
dst = lastspace + 1;
lastspace = NULL;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src < line_width)
{
strlcpy(dst, src, dst_size);
return;
}
}

View File

@ -196,7 +196,7 @@ START_TEST (test_word_wrap)
char output[1024];
word_wrap(output, testtxt, 40, true, 10);
word_wrap(output, sizeof(output), testtxt, 40, 100, 10);
ck_assert(!strcmp(output, expected));
}
END_TEST

View File

@ -1515,6 +1515,7 @@ typedef struct
font_data_t *font;
video_font_raster_block_t raster_block; /* ptr alignment */
unsigned glyph_width;
unsigned wideglyph_width;
int line_height;
int line_ascender;
int line_centre_offset;
@ -1606,6 +1607,9 @@ typedef struct materialui_handle
materialui_font_data_t hint; /* ptr alignment */
} font_data;
void (*word_wrap)(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines);
/* Thumbnail helpers */
gfx_thumbnail_path_data_t *thumbnail_path_data;
@ -2591,10 +2595,10 @@ static void materialui_render_messagebox(
return;
/* Split message into lines */
word_wrap(
wrapped_message, message,
(mui->word_wrap)(
wrapped_message, sizeof(wrapped_message), message,
usable_width / (int)mui->font_data.list.glyph_width,
true, 0);
mui->font_data.list.wideglyph_width, 0);
string_list_initialize(&list);
if (
@ -2753,10 +2757,10 @@ static unsigned materialui_count_sublabel_lines(
sublabel_width_max = usable_width - (int)mui->sublabel_padding -
(has_icon ? (int)mui->icon_size : 0);
word_wrap(
wrapped_sublabel_str, entry.sublabel,
(mui->word_wrap)(
wrapped_sublabel_str, sizeof(wrapped_sublabel_str), entry.sublabel,
sublabel_width_max / (int)mui->font_data.hint.glyph_width,
true, 0);
mui->font_data.hint.wideglyph_width, 0);
/* Return number of lines in wrapped string */
return materialui_count_lines(wrapped_sublabel_str);
@ -3484,6 +3488,13 @@ static bool (*materialui_render_process_entry)(
* materialui_render_process_entry() END
* ============================== */
static void materialui_init_font(
gfx_display_t *p_disp,
materialui_font_data_t *font_data,
int font_size,
bool video_is_threaded,
const char *str_latin);
static void materialui_layout(
materialui_handle_t *mui,
gfx_display_t *p_disp,
@ -4039,9 +4050,9 @@ static void materialui_render_menu_entry_default(
sublabel_y = entry_y + vertical_margin + mui->font_data.list.line_height + (int)mui->sublabel_gap + mui->font_data.hint.line_ascender;
/* Wrap sublabel string */
word_wrap(wrapped_sublabel, entry->sublabel,
(mui->word_wrap)(wrapped_sublabel, sizeof(wrapped_sublabel), entry->sublabel,
(int)((usable_width - (int)mui->sublabel_padding) / mui->font_data.hint.glyph_width),
true, 0);
mui->font_data.hint.wideglyph_width, 0);
/* Draw sublabel string
* > Note: We must allow text to be drawn off-screen
@ -4368,9 +4379,9 @@ static void materialui_render_menu_entry_playlist_list(
sublabel_y = entry_y + vertical_margin + mui->font_data.list.line_height + (int)mui->sublabel_gap + mui->font_data.hint.line_ascender;
/* Wrap sublabel string */
word_wrap(wrapped_sublabel, entry->sublabel,
(mui->word_wrap)(wrapped_sublabel, sizeof(wrapped_sublabel), entry->sublabel,
(int)((usable_width - (int)mui->sublabel_padding) / mui->font_data.hint.glyph_width),
true, 0);
mui->font_data.hint.wideglyph_width, 0);
/* Draw sublabel string
* > Note: We must allow text to be drawn off-screen
@ -7558,6 +7569,57 @@ static void materialui_update_list_view(materialui_handle_t *mui, settings_t *se
mui->need_compute = true;
}
static void materialui_init_font(
gfx_display_t *p_disp,
materialui_font_data_t *font_data,
int font_size,
bool video_is_threaded,
const char *str_latin
)
{
const char *wideglyph_str = msg_hash_get_wideglyph_str();
/* We assume the average glyph aspect ratio is close to 3:4 */
font_data->glyph_width = (int)((font_size * (3.0f / 4.0f)) + 0.5f);
if (font_data->font)
{
gfx_display_font_free(font_data->font);
font_data->font = NULL;
}
font_data->font = gfx_display_font(
p_disp,
APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI_FONT,
font_size, video_is_threaded);
if (font_data->font)
{
/* Calculate a more realistic ticker_limit */
int char_width =
font_driver_get_message_width(font_data->font, str_latin, 1, 1);
if (char_width > 0)
font_data->glyph_width = (unsigned)char_width;
font_data->wideglyph_width = 100;
if (wideglyph_str)
{
int wideglyph_width =
font_driver_get_message_width(font_data->font, wideglyph_str, strlen(wideglyph_str), 1);
if (wideglyph_width > 0 && char_width > 0)
font_data->wideglyph_width = wideglyph_width * 100 / char_width;
}
/* Get line metrics */
font_data->line_height = font_driver_get_line_height(font_data->font, 1.0f);
font_data->line_ascender = font_driver_get_line_ascender(font_data->font, 1.0f);
font_data->line_centre_offset = font_driver_get_line_centre_offset(font_data->font, 1.0f);
}
}
/* Compute the positions of the widgets */
static void materialui_layout(
materialui_handle_t *mui,
@ -7635,88 +7697,11 @@ static void materialui_layout(
mui->nav_bar_layout_height = mui->nav_bar.width;
}
/* We assume the average glyph aspect ratio is close to 3:4 */
mui->font_data.title.glyph_width = (int)((title_font_size * (3.0f / 4.0f)) + 0.5f);
mui->font_data.list.glyph_width = (int)((list_font_size * (3.0f / 4.0f)) + 0.5f);
mui->font_data.hint.glyph_width = (int)((hint_font_size * (3.0f / 4.0f)) + 0.5f);
p_disp->header_height = new_header_height;
if (mui->font_data.title.font)
{
gfx_display_font_free(mui->font_data.title.font);
mui->font_data.title.font = NULL;
}
if (mui->font_data.list.font)
{
gfx_display_font_free(mui->font_data.list.font);
mui->font_data.list.font = NULL;
}
if (mui->font_data.hint.font)
{
gfx_display_font_free(mui->font_data.hint.font);
mui->font_data.hint.font = NULL;
}
mui->font_data.title.font = gfx_display_font(
p_disp,
APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI_FONT,
title_font_size, video_is_threaded);
mui->font_data.list.font = gfx_display_font(
p_disp,
APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI_FONT,
list_font_size, video_is_threaded);
mui->font_data.hint.font = gfx_display_font(
p_disp,
APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI_FONT,
hint_font_size, video_is_threaded);
if (mui->font_data.title.font)
{
/* Calculate a more realistic ticker_limit */
int title_char_width =
font_driver_get_message_width(mui->font_data.title.font, "a", 1, 1);
if (title_char_width > 0)
mui->font_data.title.glyph_width = (unsigned)title_char_width;
/* Get line metrics */
mui->font_data.title.line_height = font_driver_get_line_height(mui->font_data.title.font, 1.0f);
mui->font_data.title.line_ascender = font_driver_get_line_ascender(mui->font_data.title.font, 1.0f);
mui->font_data.title.line_centre_offset = font_driver_get_line_centre_offset(mui->font_data.title.font, 1.0f);
}
if (mui->font_data.list.font)
{
/* Calculate a more realistic ticker_limit */
int list_char_width =
font_driver_get_message_width(mui->font_data.list.font, "a", 1, 1);
if (list_char_width > 0)
mui->font_data.list.glyph_width = (unsigned)list_char_width;
/* Get line metrics */
mui->font_data.list.line_height = font_driver_get_line_height(mui->font_data.list.font, 1.0f);
mui->font_data.list.line_ascender = font_driver_get_line_ascender(mui->font_data.list.font, 1.0f);
mui->font_data.list.line_centre_offset = font_driver_get_line_centre_offset(mui->font_data.list.font, 1.0f);
}
if (mui->font_data.hint.font)
{
/* Calculate a more realistic ticker_limit */
int hint_char_width =
font_driver_get_message_width(mui->font_data.hint.font, "t", 1, 1);
if (hint_char_width > 0)
mui->font_data.hint.glyph_width = (unsigned)hint_char_width;
/* Get line metrics */
mui->font_data.hint.line_height = font_driver_get_line_height(mui->font_data.hint.font, 1.0f);
mui->font_data.hint.line_ascender = font_driver_get_line_ascender(mui->font_data.hint.font, 1.0f);
mui->font_data.hint.line_centre_offset = font_driver_get_line_centre_offset(mui->font_data.hint.font, 1.0f);
}
materialui_init_font(p_disp, &mui->font_data.title, title_font_size, video_is_threaded, "a");
materialui_init_font(p_disp, &mui->font_data.list, list_font_size, video_is_threaded, "a");
materialui_init_font(p_disp, &mui->font_data.hint, hint_font_size, video_is_threaded, "t");
/* When updating the layout, the system bar
* cache must be invalidated (since all text
@ -7908,6 +7893,9 @@ static void *materialui_init(void **userdata, bool video_is_threaded)
p_anim->updatetime_cb = materialui_menu_animation_update_time;
/* set word_wrap function pointer */
mui->word_wrap = msg_hash_get_wideglyph_str() ? word_wrap_wideglyph : word_wrap;
return menu;
error:
if (menu)

View File

@ -869,6 +869,9 @@ static void *ozone_init(void **userdata, bool video_is_threaded)
last_use_preferred_system_color_theme = settings->bools.menu_use_preferred_system_color_theme;
p_anim->updatetime_cb = ozone_menu_animation_update_time;
/* set word_wrap function pointer */
ozone->word_wrap = msg_hash_get_wideglyph_str() ? word_wrap_wideglyph : word_wrap;
return menu;
error:
@ -985,6 +988,7 @@ static bool ozone_init_font(
{
int glyph_width = 0;
gfx_display_t *p_disp = disp_get_ptr();
const char *wideglyph_str = msg_hash_get_wideglyph_str();
/* Free existing */
if (font_data->font)
@ -1008,6 +1012,18 @@ static bool ozone_init_font(
glyph_width = font_driver_get_message_width(font_data->font, "a", 1, 1.0f);
if (glyph_width > 0)
font_data->glyph_width = glyph_width;
font_data->wideglyph_width = 100;
if (wideglyph_str)
{
int wideglyph_width =
font_driver_get_message_width(font_data->font, wideglyph_str, strlen(wideglyph_str), 1.0f);
if (wideglyph_width > 0 && glyph_width > 0)
font_data->wideglyph_width = wideglyph_width * 100 / glyph_width;
}
font_data->line_height = font_driver_get_line_height(font_data->font, 1.0f);
font_data->line_ascender = font_driver_get_line_ascender(font_data->font, 1.0f);
font_data->line_centre_offset = font_driver_get_line_centre_offset(font_data->font, 1.0f);
@ -4027,10 +4043,14 @@ void ozone_update_content_metadata(ozone_handle_t *ozone)
/* Word wrap core name string, if required */
if (!scroll_content_metadata)
{
char tmpstr[sizeof(ozone->selection_core_name)];
unsigned metadata_len =
(ozone->dimensions.thumbnail_bar_width - ((ozone->dimensions.sidebar_entry_icon_padding * 2) * 2)) /
ozone->fonts.footer.glyph_width;
word_wrap(ozone->selection_core_name, ozone->selection_core_name, metadata_len, true, 0);
strlcpy(tmpstr, ozone->selection_core_name, sizeof(tmpstr));
(ozone->word_wrap)(ozone->selection_core_name, sizeof(ozone->selection_core_name),
tmpstr, metadata_len, ozone->fonts.footer.wideglyph_width, 0);
ozone->selection_core_name_lines = ozone_count_lines(ozone->selection_core_name);
}
else
@ -4076,7 +4096,10 @@ void ozone_update_content_metadata(ozone_handle_t *ozone)
* formats. Last played strings are well defined, however
* (unlike core names), so this should never overflow the
* side bar */
word_wrap(ozone->selection_lastplayed, ozone->selection_lastplayed, 30, true, 0);
char tmpstr[sizeof(ozone->selection_lastplayed)];
strlcpy(tmpstr, ozone->selection_lastplayed, sizeof(tmpstr));
(ozone->word_wrap)(ozone->selection_lastplayed, sizeof(ozone->selection_lastplayed), tmpstr, 30, 100, 0);
ozone->selection_lastplayed_lines = ozone_count_lines(ozone->selection_lastplayed);
}
else

View File

@ -99,6 +99,7 @@ typedef struct
font_data_t *font;
video_font_raster_block_t raster_block; /* ptr alignment */
int glyph_width;
int wideglyph_width;
int line_height;
int line_ascender;
int line_centre_offset;
@ -132,6 +133,9 @@ struct ozone_handle
ozone_font_data_t sidebar;
} fonts;
void (*word_wrap)(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines);
struct
{
ozone_footer_label_t ok;

View File

@ -495,7 +495,9 @@ void ozone_draw_osk(ozone_handle_t *ozone,
text_color = ozone_theme_light.text_sublabel_rgba;
}
word_wrap(message, text, (video_width - margin*2 - padding*2) / ozone->fonts.entries_label.glyph_width, true, 0);
(ozone->word_wrap)(message, sizeof(message), text,
(video_width - margin*2 - padding*2) / ozone->fonts.entries_label.glyph_width,
ozone->fonts.entries_label.wideglyph_width, 0);
string_list_initialize(&list);
string_split_noalloc(&list, message, "\n");
@ -602,10 +604,10 @@ void ozone_draw_messagebox(
return;
/* Split message into lines */
word_wrap(
wrapped_message, message,
(ozone->word_wrap)(
wrapped_message, sizeof(wrapped_message), message,
usable_width / (int)ozone->fonts.footer.glyph_width,
true, 0);
ozone->fonts.footer.wideglyph_width, 0);
string_list_initialize(&list);
if (

View File

@ -400,9 +400,10 @@ void ozone_compute_entries_position(
sublabel_max_width -= ozone->dimensions.thumbnail_bar_width;
}
word_wrap(wrapped_sublabel_str, entry.sublabel,
(ozone->word_wrap)(wrapped_sublabel_str, sizeof(wrapped_sublabel_str), entry.sublabel,
sublabel_max_width /
ozone->fonts.entries_sublabel.glyph_width, false, 0);
ozone->fonts.entries_sublabel.glyph_width,
ozone->fonts.entries_sublabel.wideglyph_width, 0);
node->sublabel_lines = ozone_count_lines(wrapped_sublabel_str);
@ -763,7 +764,9 @@ border_iterate:
}
wrapped_sublabel_str[0] = '\0';
word_wrap(wrapped_sublabel_str, sublabel_str, sublabel_max_width / ozone->fonts.entries_sublabel.glyph_width, false, 0);
(ozone->word_wrap)(wrapped_sublabel_str, sizeof(wrapped_sublabel_str),
sublabel_str, sublabel_max_width / ozone->fonts.entries_sublabel.glyph_width,
ozone->fonts.entries_sublabel.wideglyph_width, 0);
sublabel_str = wrapped_sublabel_str;
}
}

View File

@ -3932,9 +3932,9 @@ static void rgui_render_messagebox(rgui_t *rgui, const char *message,
/* Split message into lines */
word_wrap(
wrapped_message, message,
wrapped_message, sizeof(wrapped_message), message,
rgui->term_layout.width,
true, 0);
100, 0);
string_list_initialize(&list);
if ( !string_split_noalloc(&list, wrapped_message, "\n")

View File

@ -286,6 +286,9 @@ typedef struct stripes_handle
font_data_t *font2;
video_font_raster_block_t raster_block;
video_font_raster_block_t raster_block2;
void (*word_wrap)(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines);
} stripes_handle_t;
float stripes_scale_mod[8] = {
@ -2406,7 +2409,8 @@ static int stripes_draw_item(
label_offset = - stripes->margins_label_top;
word_wrap(entry_sublabel, entry->sublabel, 50 * stripes_scale_mod[3], true, 0);
(stripes->word_wrap)(entry_sublabel, sizeof(entry_sublabel),
entry->sublabel, 50 * stripes_scale_mod[3], 100, 0);
stripes_draw_text(xmb_shadows_enable, stripes, entry_sublabel,
node->x + stripes->margins_screen_left +
@ -3374,6 +3378,9 @@ static void *stripes_init(void **userdata, bool video_is_threaded)
file_list_initialize(&stripes->horizontal_list);
stripes_init_horizontal_list(stripes);
/* set word_wrap function pointer */
stripes->word_wrap = msg_hash_get_wideglyph_str() ? word_wrap_wideglyph : word_wrap;
return menu;
error:

View File

@ -302,6 +302,9 @@ typedef struct xmb_handle
video_font_raster_block_t raster_block;
video_font_raster_block_t raster_block2;
void (*word_wrap)(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines);
menu_screensaver_t *screensaver;
gfx_thumbnail_path_data_t *thumbnail_path_data;
@ -326,6 +329,7 @@ typedef struct xmb_handle
int old_depth;
int icon_size;
int cursor_size;
int wideglyph_width;
unsigned categories_active_idx;
unsigned categories_active_idx_old;
@ -976,10 +980,10 @@ static void xmb_render_messagebox_internal(
return;
/* Split message into lines */
word_wrap(
wrapped_message, message,
(xmb->word_wrap)(
wrapped_message, sizeof(wrapped_message), message,
usable_width / (xmb->font_size * 0.6f),
true, 0);
xmb->wideglyph_width, 0);
string_list_initialize(&list);
@ -5777,6 +5781,9 @@ static void *xmb_init(void **userdata, bool video_is_threaded)
p_anim->updatetime_cb = xmb_menu_animation_update_time;
/* set word_wrap function pointer */
xmb->word_wrap = msg_hash_get_wideglyph_str() ? word_wrap_wideglyph : word_wrap;
return menu;
error:
@ -6286,6 +6293,7 @@ static void xmb_context_reset_internal(xmb_handle_t *xmb,
char iconpath[PATH_MAX_LENGTH];
char bg_file_path[PATH_MAX_LENGTH];
gfx_display_t *p_disp = disp_get_ptr();
const char *wideglyph_str = msg_hash_get_wideglyph_str();
iconpath[0] = bg_file_path[0] = '\0';
fill_pathname_application_special(bg_file_path,
@ -6321,6 +6329,19 @@ static void xmb_context_reset_internal(xmb_handle_t *xmb,
xmb->font2_size,
is_threaded);
xmb->wideglyph_width = 100;
if (wideglyph_str)
{
int char_width =
font_driver_get_message_width(xmb->font, "a", 1, 1);
int wideglyph_width =
font_driver_get_message_width(xmb->font, wideglyph_str, strlen(wideglyph_str), 1);
if (wideglyph_width > 0 && char_width > 0)
xmb->wideglyph_width = wideglyph_width * 100 / char_width;
}
if (reinit_textures)
{
xmb_context_reset_textures(xmb, iconpath);

View File

@ -536,3 +536,22 @@ void msg_hash_set_uint(enum msg_hash_action type, unsigned val)
break;
}
}
const char *msg_hash_get_wideglyph_str(void)
{
switch (uint_user_language)
{
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
return msg_hash_get_wideglyph_str_chs();
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
return msg_hash_get_wideglyph_str_cht();
case RETRO_LANGUAGE_JAPANESE:
return msg_hash_get_wideglyph_str_jp();
case RETRO_LANGUAGE_KOREAN:
return msg_hash_get_wideglyph_str_ko();
default:
break;
}
return NULL;
}

View File

@ -3272,6 +3272,37 @@ unsigned *msg_hash_get_uint(enum msg_hash_action type);
void msg_hash_set_uint(enum msg_hash_action type, unsigned val);
/* Latin languages typically consist of regular
* alpha numeric characters with a 'standard'
* on-screen pixel width.
* Non-Latin languages (e.g. CJK) typically consist
* of so-called 'wide' Unicode glyphs, which may have
* an on-screen pixel width several times that of Latin
* characters.
* In order to determine efficiently the on-screen width
* of a text string (e.g. when word wrapping), it is
* therefore necessary to:
* - Identify which languages make use of 'wide' Unicode
* glyphs
* - For each of these languages, provide a mechanism for
* measuring the typical on-screen pixel width of
* language-specific 'wide' Unicode glyphs
* As such, msg_hash_get_wideglyph_str() returns a pointer
* to a 'wide' Unicode character of typical on-screen pixel
* width for the currently set user language.
* - If msg_hash_get_wideglyph_str() returns NULL, the current
* language is assumed to be Latin-based, with no usage
* of 'wide' Unicode glyphs
* - If msg_hash_get_wideglyph_str() returns a valid pointer,
* actual 'wide' glyph width for the current language may
* be found by passing said pointer to the current font
* rendering implementation */
const char *msg_hash_get_wideglyph_str(void);
const char *msg_hash_get_wideglyph_str_chs(void);
const char *msg_hash_get_wideglyph_str_cht(void);
const char *msg_hash_get_wideglyph_str_jp(void);
const char *msg_hash_get_wideglyph_str_ko(void);
uint32_t msg_hash_calculate(const char *s);
const char *get_user_language_iso639_1(bool limit);

View File

@ -1195,8 +1195,15 @@ void CoreOptionsDialog::buildLayout()
if (!string_is_empty(option->info))
{
char *new_info = strdup(option->info);
word_wrap(new_info, new_info, 50, true, 0);
char *new_info;
size_t new_info_len = strlen(option->info) + 1;
new_info = (char *)malloc(new_info_len);
if (!new_info)
return;
new_info[0] = '\0';
word_wrap(new_info, new_info_len, option->info, 50, 100, 0);
descLabel->setToolTip(new_info);
combo_box->setToolTip(new_info);
free(new_info);