mirror of
https://github.com/libretro/RetroArch
synced 2025-03-03 13:14:04 +00:00
Merge pull request #9128 from jdgleaver/xmb-sublabel-ticker
(XMB) Fix display of long sublabels
This commit is contained in:
commit
68872f7f55
@ -2850,6 +2850,7 @@ static int xmb_draw_item(
|
||||
uintptr_t texture_switch = 0;
|
||||
bool do_draw_text = false;
|
||||
unsigned ticker_limit = 35 * scale_mod[0];
|
||||
unsigned line_ticker_width = 45 * scale_mod[3];
|
||||
xmb_node_t * node = (xmb_node_t*)
|
||||
file_list_get_userdata_at_offset(list, i);
|
||||
settings_t *settings = config_get_ptr();
|
||||
@ -2952,15 +2953,26 @@ static int xmb_draw_item(
|
||||
)
|
||||
{
|
||||
ticker_limit = 40 * scale_mod[1];
|
||||
line_ticker_width = 50 * scale_mod[3];
|
||||
|
||||
/* Can increase text length if thumbnail is downscaled */
|
||||
if (settings->uints.menu_xmb_thumbnail_scale_factor < 100)
|
||||
{
|
||||
float ticker_scale_factor =
|
||||
1.0f - ((float)settings->uints.menu_xmb_thumbnail_scale_factor / 100.0f);
|
||||
|
||||
ticker_limit +=
|
||||
(unsigned)((1.0f - ((float)settings->uints.menu_xmb_thumbnail_scale_factor / 100.0f)) *
|
||||
15.0f * scale_mod[1]);
|
||||
(unsigned)(ticker_scale_factor * 15.0f * scale_mod[1]);
|
||||
|
||||
line_ticker_width +=
|
||||
(unsigned)(ticker_scale_factor * 10.0f * scale_mod[3]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ticker_limit = 70 * scale_mod[2];
|
||||
line_ticker_width = 60 * scale_mod[3];
|
||||
}
|
||||
}
|
||||
|
||||
menu_entry_get_rich_label(entry, &ticker_str);
|
||||
@ -2981,11 +2993,26 @@ static int xmb_draw_item(
|
||||
if (i == current && width > 320 && height > 240
|
||||
&& !string_is_empty(entry->sublabel))
|
||||
{
|
||||
menu_animation_ctx_line_ticker_t line_ticker;
|
||||
char entry_sublabel[512] = {0};
|
||||
|
||||
label_offset = - xmb->margins_label_top;
|
||||
line_ticker.type_enum = (enum menu_animation_ticker_type)settings->uints.menu_ticker_type;
|
||||
line_ticker.idx = menu_animation_get_ticker_idx();
|
||||
|
||||
word_wrap(entry_sublabel, entry->sublabel, 50 * scale_mod[3], true, 0);
|
||||
line_ticker.line_width = (size_t)(line_ticker_width);
|
||||
/* Note: max_lines should be calculated at runtime,
|
||||
* but this is a nuisance. There is room for 4 lines
|
||||
* to be displayed when using all existing XMB themes,
|
||||
* so leave this value hard coded for now. */
|
||||
line_ticker.max_lines = 4;
|
||||
|
||||
line_ticker.s = entry_sublabel;
|
||||
line_ticker.len = sizeof(entry_sublabel);
|
||||
line_ticker.str = entry->sublabel;
|
||||
|
||||
menu_animation_line_ticker(&line_ticker);
|
||||
|
||||
label_offset = - xmb->margins_label_top;
|
||||
|
||||
xmb_draw_text(video_info, xmb, entry_sublabel,
|
||||
node->x + xmb->margins_screen_left +
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <features/features_cpu.h>
|
||||
#include <lists/string_list.h>
|
||||
|
||||
#define DG_DYNARR_IMPLEMENTATION
|
||||
#include <stdio.h>
|
||||
@ -386,6 +387,60 @@ static void menu_animation_ticker_loop(uint64_t idx,
|
||||
*width3 = width;
|
||||
}
|
||||
|
||||
static size_t get_line_display_ticks(size_t line_width)
|
||||
{
|
||||
/* Mean human reading speed for all western languages,
|
||||
* characters per minute */
|
||||
float cpm = 1000.0f;
|
||||
/* Base time for which a line should be shown, in ms */
|
||||
float line_duration = (line_width * 60.0f * 1000.0f) / cpm;
|
||||
/* Ticker updates (nominally) once every TICKER_SPEED ms
|
||||
* > Return base number of ticks for which line should be shown */
|
||||
return (size_t)(line_duration / (float)TICKER_SPEED);
|
||||
}
|
||||
|
||||
static void menu_animation_line_ticker_generic(uint64_t idx,
|
||||
size_t line_width, size_t max_lines, size_t num_lines,
|
||||
size_t *line_offset)
|
||||
{
|
||||
size_t line_ticks = get_line_display_ticks(line_width);
|
||||
/* Note: This function is only called if num_lines > max_lines */
|
||||
size_t excess_lines = num_lines - max_lines;
|
||||
/* Ticker will pause for one line duration when the first
|
||||
* or last line is reached (this is mostly required for the
|
||||
* case where num_lines == (max_lines + 1), since otherwise
|
||||
* the text flicks rapidly up and down in disconcerting
|
||||
* fashion...) */
|
||||
size_t ticker_period = (excess_lines * 2) + 2;
|
||||
size_t phase = (idx / line_ticks) % ticker_period;
|
||||
|
||||
/* Pause on first line */
|
||||
if (phase > 0)
|
||||
phase--;
|
||||
/* Pause on last line */
|
||||
if (phase > excess_lines)
|
||||
phase--;
|
||||
|
||||
/* Lines scrolling upwards */
|
||||
if (phase <= excess_lines)
|
||||
*line_offset = phase;
|
||||
/* Lines scrolling downwards */
|
||||
else
|
||||
*line_offset = (excess_lines * 2) - phase;
|
||||
}
|
||||
|
||||
static void menu_animation_line_ticker_loop(uint64_t idx,
|
||||
size_t line_width, size_t num_lines,
|
||||
size_t *line_offset)
|
||||
{
|
||||
size_t line_ticks = get_line_display_ticks(line_width);
|
||||
size_t ticker_period = num_lines + 1;
|
||||
size_t phase = (idx / line_ticks) % ticker_period;
|
||||
|
||||
/* In this case, line_offset is simply equal to the phase */
|
||||
*line_offset = phase;
|
||||
}
|
||||
|
||||
static void menu_delayed_animation_cb(void *userdata)
|
||||
{
|
||||
menu_delayed_animation_t *delayed_animation = (menu_delayed_animation_t*) userdata;
|
||||
@ -771,6 +826,125 @@ bool menu_animation_ticker(menu_animation_ctx_ticker_t *ticker)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menu_animation_line_ticker(menu_animation_ctx_line_ticker_t *line_ticker)
|
||||
{
|
||||
size_t i;
|
||||
char *wrapped_str = NULL;
|
||||
struct string_list *lines = NULL;
|
||||
size_t line_offset = 0;
|
||||
bool success = false;
|
||||
bool is_active = false;
|
||||
|
||||
/* Sanity check */
|
||||
if (!line_ticker)
|
||||
return false;
|
||||
|
||||
if (string_is_empty(line_ticker->str) ||
|
||||
(line_ticker->line_width < 1) ||
|
||||
(line_ticker->max_lines < 1))
|
||||
goto end;
|
||||
|
||||
/* Line wrap input string */
|
||||
wrapped_str = (char*)malloc((strlen(line_ticker->str) + 1) * sizeof(char));
|
||||
if (!wrapped_str)
|
||||
goto end;
|
||||
|
||||
word_wrap(
|
||||
wrapped_str,
|
||||
line_ticker->str,
|
||||
(int)line_ticker->line_width,
|
||||
true, 0);
|
||||
|
||||
if (string_is_empty(wrapped_str))
|
||||
goto end;
|
||||
|
||||
/* Split into component lines */
|
||||
lines = string_split(wrapped_str, "\n");
|
||||
if (!lines)
|
||||
goto end;
|
||||
|
||||
/* Check whether total number of lines fits within
|
||||
* the set limit */
|
||||
if (lines->size <= line_ticker->max_lines)
|
||||
{
|
||||
strlcpy(line_ticker->s, wrapped_str, line_ticker->len);
|
||||
success = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Determine offset of first line in wrapped string */
|
||||
switch (line_ticker->type_enum)
|
||||
{
|
||||
case TICKER_TYPE_LOOP:
|
||||
{
|
||||
menu_animation_line_ticker_loop(
|
||||
line_ticker->idx,
|
||||
line_ticker->line_width,
|
||||
lines->size,
|
||||
&line_offset);
|
||||
|
||||
break;
|
||||
}
|
||||
case TICKER_TYPE_BOUNCE:
|
||||
default:
|
||||
{
|
||||
menu_animation_line_ticker_generic(
|
||||
line_ticker->idx,
|
||||
line_ticker->line_width,
|
||||
line_ticker->max_lines,
|
||||
lines->size,
|
||||
&line_offset);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Build output string from required lines */
|
||||
for (i = 0; i < line_ticker->max_lines; i++)
|
||||
{
|
||||
size_t offset = i + line_offset;
|
||||
size_t line_index = 0;
|
||||
bool line_valid = true;
|
||||
|
||||
if (offset < lines->size)
|
||||
line_index = offset;
|
||||
else if (offset > lines->size)
|
||||
line_index = (offset - 1) - lines->size;
|
||||
else
|
||||
line_valid = false;
|
||||
|
||||
if (line_valid)
|
||||
strlcat(line_ticker->s, lines->elems[line_index].data, line_ticker->len);
|
||||
|
||||
if (i < line_ticker->max_lines - 1)
|
||||
strlcat(line_ticker->s, "\n", line_ticker->len);
|
||||
}
|
||||
|
||||
success = true;
|
||||
is_active = true;
|
||||
ticker_is_active = true;
|
||||
|
||||
end:
|
||||
|
||||
if (wrapped_str)
|
||||
{
|
||||
free(wrapped_str);
|
||||
wrapped_str = NULL;
|
||||
}
|
||||
|
||||
if (lines)
|
||||
{
|
||||
string_list_free(lines);
|
||||
lines = NULL;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
if (line_ticker->len > 0)
|
||||
line_ticker->s[0] = '\0';
|
||||
|
||||
return is_active;
|
||||
}
|
||||
|
||||
bool menu_animation_is_active(void)
|
||||
{
|
||||
return animation_is_active || ticker_is_active;
|
||||
|
@ -126,6 +126,17 @@ typedef struct menu_animation_ctx_ticker
|
||||
const char *spacer;
|
||||
} menu_animation_ctx_ticker_t;
|
||||
|
||||
typedef struct menu_animation_ctx_line_ticker
|
||||
{
|
||||
size_t line_width;
|
||||
size_t max_lines;
|
||||
uint64_t idx;
|
||||
enum menu_animation_ticker_type type_enum;
|
||||
char *s;
|
||||
size_t len;
|
||||
const char *str;
|
||||
} menu_animation_ctx_line_ticker_t;
|
||||
|
||||
typedef float menu_timer_t;
|
||||
|
||||
typedef struct menu_timer_ctx_entry
|
||||
@ -149,6 +160,8 @@ bool menu_animation_update(void);
|
||||
|
||||
bool menu_animation_ticker(menu_animation_ctx_ticker_t *ticker);
|
||||
|
||||
bool menu_animation_line_ticker(menu_animation_ctx_line_ticker_t *line_ticker);
|
||||
|
||||
float menu_animation_get_delta_time(void);
|
||||
|
||||
bool menu_animation_is_active(void);
|
||||
|
Loading…
x
Reference in New Issue
Block a user