mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 07:59:42 +00:00
improve message wrapping with CJK languages
This commit is contained in:
parent
2c21e3df8b
commit
4fab84ca9e
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 "菜";
|
||||
}
|
||||
|
@ -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 "主";
|
||||
}
|
||||
|
@ -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 "漢";
|
||||
}
|
||||
|
@ -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 "메";
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 (
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
@ -5771,6 +5775,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:
|
||||
@ -6280,6 +6287,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,
|
||||
@ -6315,6 +6323,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);
|
||||
|
19
msg_hash.c
19
msg_hash.c
@ -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;
|
||||
}
|
||||
|
31
msg_hash.h
31
msg_hash.h
@ -3269,6 +3269,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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user