Add optional 'looping' menu text ticker

This commit is contained in:
jdgleaver 2019-02-14 15:10:07 +00:00
parent 1daaf7385c
commit 27694b6b31
19 changed files with 272 additions and 16 deletions

View File

@ -272,6 +272,7 @@ static const float default_input_overlay_opacity = 0.7f;
#ifdef HAVE_MENU
#include "menu/menu_driver.h"
#include "menu/menu_animation.h"
static bool default_block_config_read = true;
@ -323,6 +324,8 @@ static bool menu_show_core_updater = true;
#endif
static bool menu_show_sublabels = false;
static unsigned menu_ticker_type = TICKER_TYPE_BOUNCE;
static bool content_show_settings = true;
static bool content_show_favorites = true;
#ifdef HAVE_IMAGEVIEWER

View File

@ -1666,6 +1666,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
SETTING_UINT("dpi_override_value", &settings->uints.menu_dpi_override_value, true, menu_dpi_override_value, false);
SETTING_UINT("menu_thumbnails", &settings->uints.menu_thumbnails, true, menu_thumbnails_default, false);
SETTING_UINT("menu_timedate_style", &settings->uints.menu_timedate_style, true, menu_timedate_style, false);
SETTING_UINT("menu_ticker_type", &settings->uints.menu_ticker_type, true, menu_ticker_type, false);
#ifdef HAVE_RGUI
SETTING_UINT("rgui_menu_color_theme", &settings->uints.menu_rgui_color_theme, true, rgui_color_theme, false);
SETTING_UINT("rgui_thumbnail_downscaler", &settings->uints.menu_rgui_thumbnail_downscaler, true, rgui_thumbnail_downscaler, false);

View File

@ -427,6 +427,7 @@ typedef struct settings
unsigned menu_font_color_green;
unsigned menu_font_color_blue;
unsigned menu_rgui_internal_upscale_level;
unsigned menu_ticker_type;
unsigned camera_width;
unsigned camera_height;

View File

@ -1129,6 +1129,8 @@ MSG_HASH(MENU_ENUM_LABEL_TIMEDATE_ENABLE,
"menu_timedate_enable")
MSG_HASH(MENU_ENUM_LABEL_TIMEDATE_STYLE,
"menu_timedate_style")
MSG_HASH(MENU_ENUM_LABEL_MENU_TICKER_TYPE,
"menu_ticker_type")
MSG_HASH(MENU_ENUM_LABEL_UI_COMPANION_ENABLE,
"ui_companion_enable")
MSG_HASH(MENU_ENUM_LABEL_UI_COMPANION_START_ON_BOOT,

View File

@ -3074,6 +3074,22 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_TIMEDATE_STYLE_AM_PM,
"HH:MM:SS (AM/PM)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_TICKER_TYPE,
"Ticker Text Animation"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MENU_TICKER_TYPE,
"Select horizontal scrolling method used to display long menu text strings."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_TICKER_TYPE_BOUNCE,
"Bounce Left/Right"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_MENU_TICKER_TYPE_LOOP,
"Scroll Left"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_RGUI_MENU_COLOR_THEME,
"Menu Color Theme"

View File

@ -518,6 +518,7 @@ default_sublabel_macro(action_bind_sublabel_rgui_menu_theme_preset,
default_sublabel_macro(action_bind_sublabel_menu_rgui_thumbnail_downscaler, MENU_ENUM_SUBLABEL_MENU_RGUI_THUMBNAIL_DOWNSCALER)
default_sublabel_macro(action_bind_sublabel_content_runtime_log, MENU_ENUM_SUBLABEL_CONTENT_RUNTIME_LOG)
default_sublabel_macro(action_bind_sublabel_menu_rgui_internal_upscale_level, MENU_ENUM_SUBLABEL_MENU_RGUI_INTERNAL_UPSCALE_LEVEL)
default_sublabel_macro(action_bind_sublabel_menu_ticker_type, MENU_ENUM_SUBLABEL_MENU_TICKER_TYPE)
static int action_bind_sublabel_systeminfo_controller_entry(
file_list_t *list,
@ -2256,6 +2257,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_MENU_RGUI_INTERNAL_UPSCALE_LEVEL:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_rgui_internal_upscale_level);
break;
case MENU_ENUM_LABEL_MENU_TICKER_TYPE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_ticker_type);
break;
default:
case MSG_UNKNOWN:
return -1;

View File

@ -739,6 +739,7 @@ static void materialui_render_label_value(
{
menu_entry_t entry;
menu_animation_ctx_ticker_t ticker;
static const char ticker_spacer[] = " | ";
char label_str[255];
char value_str[255];
char *sublabel_str = NULL;
@ -752,6 +753,11 @@ static void materialui_render_label_value(
int icon_margin = 0;
enum msg_file_type hash_type = msg_hash_to_file_type(msg_hash_calculate(value));
float scale_factor = menu_display_get_dpi();
settings_t *settings = config_get_ptr();
/* Initial ticker configuration */
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
label_str[0] = value_str[0] = '\0';
@ -1063,6 +1069,7 @@ static void materialui_frame(void *data, video_frame_info_t *video_info)
menu_display_ctx_clearcolor_t clearcolor;
menu_animation_ctx_ticker_t ticker;
static const char ticker_spacer[] = " | ";
menu_display_ctx_draw_t draw;
char msg[255];
char title_buf[255];
@ -1160,11 +1167,16 @@ static void materialui_frame(void *data, video_frame_info_t *video_info)
bool background_rendered = false;
bool libretro_running = video_info->libretro_running;
settings_t *settings = config_get_ptr();
materialui_handle_t *mui = (materialui_handle_t*)data;
if (!mui)
return;
/* Initial ticker configuration */
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
usable_width = width - (mui->margin * 2);
mui->frame_count++;

View File

@ -848,9 +848,14 @@ static void ozone_draw_header(ozone_handle_t *ozone, video_frame_info_t *video_i
{
char title[255];
menu_animation_ctx_ticker_t ticker;
static const char ticker_spacer[] = " | ";
settings_t *settings = config_get_ptr();
unsigned timedate_offset = 0;
/* Initial ticker configuration */
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
/* Separator */
menu_display_draw_quad(video_info, 30, ozone->dimensions.header_height, video_info->width - 60, 1, video_info->width, video_info->height, ozone->theme->header_footer_separator);

View File

@ -408,6 +408,7 @@ border_iterate:
menu_texture_item tex;
menu_entry_t entry;
menu_animation_ctx_ticker_t ticker;
static const char ticker_spacer[] = " | ";
char entry_value[255];
char rich_label[255];
char entry_value_ticker[255];
@ -418,6 +419,10 @@ border_iterate:
int text_offset = -ozone->dimensions.entry_icon_padding - ozone->dimensions.entry_icon_size;
float *icon_color = NULL;
/* Initial ticker configuration */
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
entry_value[0] = '\0';
entry_selected = selection == i;
node = (ozone_node_t*) file_list_get_userdata_at_offset(selection_buf, i);

View File

@ -109,6 +109,12 @@ void ozone_draw_sidebar(ozone_handle_t *ozone, video_frame_info_t *video_info)
unsigned i, sidebar_height;
char console_title[255];
menu_animation_ctx_ticker_t ticker;
static const char ticker_spacer[] = " | ";
settings_t *settings = config_get_ptr();
/* Initial ticker configuration */
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
unsigned selection_y = 0;
unsigned selection_old_y = 0;

View File

@ -1464,6 +1464,7 @@ static void rgui_frame(void *data, video_frame_info_t *video_info)
static void rgui_render(void *data, bool is_idle)
{
menu_animation_ctx_ticker_t ticker;
static const char ticker_spacer[] = " | ";
unsigned x, y;
size_t i, end, fb_pitch, old_start;
unsigned fb_width, fb_height;
@ -1586,10 +1587,11 @@ static void rgui_render(void *data, bool is_idle)
rgui_render_background(rgui);
}
/* We use a single ticker for all text animations.
* The same 'idx' is used in all cases, so set it
* once at the beginning. */
/* We use a single ticker for all text animations,
* with the following configuration: */
ticker.idx = frame_count / RGUI_TERM_START_X(fb_width);
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
/* If thumbnails are enabled and we are viewing a playlist,
* switch to thumbnail view mode if either current thumbnail

View File

@ -2332,6 +2332,7 @@ static int stripes_draw_item(
{
float icon_x, icon_y, label_offset;
menu_animation_ctx_ticker_t ticker;
static const char ticker_spacer[] = " | ";
char tmp[255];
char *ticker_str = NULL;
unsigned entry_type = 0;
@ -2343,6 +2344,10 @@ static int stripes_draw_item(
file_list_get_userdata_at_offset(list, i);
settings_t *settings = config_get_ptr();
/* Initial ticker configuration */
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
if (!node)
goto iterate;

View File

@ -2806,6 +2806,7 @@ static int xmb_draw_item(
float icon_x, icon_y, label_offset;
menu_animation_ctx_ticker_t ticker;
char tmp[255];
static const char ticker_spacer[] = " | ";
char *ticker_str = NULL;
unsigned entry_type = 0;
const float half_size = xmb->icon_size / 2.0f;
@ -2816,6 +2817,10 @@ static int xmb_draw_item(
file_list_get_userdata_at_offset(list, i);
settings_t *settings = config_get_ptr();
/* Initial ticker configuration */
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
if (!node)
goto iterate;

View File

@ -546,6 +546,7 @@ static void xui_render(void *data, bool is_idle)
unsigned menu_type = 0;
uint64_t frame_count = xui_frame_count;
bool msg_force = menu_display_get_msg_force();
settings_t *settings = config_get_ptr();
menu_display_get_fb_size(&fb_width, &fb_height,
&fb_pitch);
@ -565,10 +566,15 @@ static void xui_render(void *data, bool is_idle)
if (XuiHandleIsValid(m_menutitle))
{
menu_animation_ctx_ticker_t ticker;
static const char ticker_spacer[] = " | ";
menu_entries_get_title(title, sizeof(title));
mbstowcs(strw_buffer, title, sizeof(strw_buffer) / sizeof(wchar_t));
XuiTextElementSetText(m_menutitle, strw_buffer);
/* Initial ticker configuration */
ticker.type_enum = settings->uints.menu_ticker_type;
ticker.spacer = ticker_spacer;
ticker.s = title;
ticker.len = RXUI_TERM_WIDTH(fb_width) - 3;
ticker.idx = (unsigned int)frame_count / 15;

View File

@ -322,6 +322,63 @@ static void menu_animation_ticker_generic(uint64_t idx,
*width = max_width;
}
static void menu_animation_ticker_loop(uint64_t idx,
size_t max_width, size_t str_width, size_t spacer_width,
size_t *offset1, size_t *width1,
size_t *offset2, size_t *width2,
size_t *offset3, size_t *width3)
{
int ticker_period = (int)(str_width + spacer_width);
int phase = idx % ticker_period;
/* Output offsets/widths are unsigned size_t, but it's
* easier to perform the required calculations with ints,
* so create some temporary variables... */
int offset;
int width;
/* Looping text is composed of up to three strings,
* where string 1 and 2 are different regions of the
* source text and string 2 is a spacer:
*
* |-----max_width-----|
* [string 1][string 2][string 3]
*
* The following implementation could probably be optimised,
* but any performance gains would be trivial compared with
* all the string manipulation that has to happen afterwards...
*/
/* String 1 */
offset = phase < str_width ? phase : 0;
width = str_width - phase;
width = width < 0 ? 0 : width;
width = width > max_width ? max_width : width;
*offset1 = offset;
*width1 = width;
/* String 2 */
offset = phase - str_width;
offset = offset < 0 ? 0 : offset;
width = max_width - *width1;
width = width > spacer_width ? spacer_width : width;
width = width - offset;
*offset2 = offset;
*width2 = width;
/* String 3 */
width = max_width - (*width1 + *width2);
width = width < 0 ? 0 : width;
/* Note: offset is always zero here so offset3 is
* unnecessary - but include it anyway to preserve
* symmetry... */
*offset3 = 0;
*width3 = width;
}
void menu_animation_init(void)
{
da_init(anim.list);
@ -556,7 +613,6 @@ bool menu_animation_update(float anim_delta_time)
bool menu_animation_ticker(const menu_animation_ctx_ticker_t *ticker)
{
size_t str_len = utf8len(ticker->str);
size_t offset = 0;
if ((size_t)str_len <= ticker->len)
{
@ -574,18 +630,85 @@ bool menu_animation_ticker(const menu_animation_ctx_ticker_t *ticker)
return false;
}
if (str_len > ticker->len)
menu_animation_ticker_generic(
ticker->idx,
ticker->len,
&offset,
&str_len);
utf8cpy(
ticker->s,
PATH_MAX_LENGTH,
utf8skip(ticker->str, offset),
str_len);
/* Note: If we reach this point then str_len > ticker->len
* (previously had an unecessary 'if (str_len > ticker->len)'
* check here...) */
switch (ticker->type_enum)
{
case TICKER_TYPE_LOOP:
{
size_t offset1, offset2, offset3;
size_t width1, width2, width3;
/* Horribly oversized temporary buffer
* > utf8 support makes this whole thing incredibly
* ugly/inefficient. Not much we can do about it... */
char tmp[PATH_MAX_LENGTH];
tmp[0] = '\0';
ticker->s[0] = '\0';
menu_animation_ticker_loop(
ticker->idx,
ticker->len,
str_len, utf8len(ticker->spacer),
&offset1, &width1,
&offset2, &width2,
&offset3, &width3);
if (width1 > 0)
{
utf8cpy(
ticker->s,
PATH_MAX_LENGTH,
utf8skip(ticker->str, offset1),
width1);
}
if (width2 > 0)
{
utf8cpy(
tmp,
PATH_MAX_LENGTH,
utf8skip(ticker->spacer, offset2),
width2);
strlcat(ticker->s, tmp, PATH_MAX_LENGTH);
}
if (width3 > 0)
{
utf8cpy(
tmp,
PATH_MAX_LENGTH,
utf8skip(ticker->str, offset3),
width3);
strlcat(ticker->s, tmp, PATH_MAX_LENGTH);
}
break;
}
case TICKER_TYPE_BOUNCE:
default:
{
size_t offset = 0;
menu_animation_ticker_generic(
ticker->idx,
ticker->len,
&offset,
&str_len);
utf8cpy(
ticker->s,
PATH_MAX_LENGTH,
utf8skip(ticker->str, offset),
str_len);
break;
}
}
ticker_is_active = true;

View File

@ -84,6 +84,16 @@ enum menu_animation_easing_type
EASING_LAST
};
/* TODO:
* Add a reverse loop ticker for languages
* that read right to left */
enum menu_animation_ticker_type
{
TICKER_TYPE_BOUNCE = 0,
TICKER_TYPE_LOOP,
TICKER_TYPE_LAST
};
typedef struct menu_animation_ctx_delta
{
float current;
@ -114,8 +124,10 @@ typedef struct menu_animation_ctx_ticker
bool selected;
size_t len;
uint64_t idx;
enum menu_animation_ticker_type type_enum;
char *s;
const char *str;
const char *spacer;
} menu_animation_ctx_ticker_t;
typedef float menu_timer_t;

View File

@ -6132,6 +6132,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, menu_displaylist
MENU_ENUM_LABEL_MENU_RGUI_THUMBNAIL_DOWNSCALER,
PARSE_ONLY_UINT, false) == 0)
count++;
if (menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_MENU_TICKER_TYPE,
PARSE_ONLY_UINT, false) == 0)
count++;
if (count == 0)
menu_entries_append_enum(info->list,

View File

@ -891,6 +891,30 @@ static void setting_get_string_representation_uint_rgui_internal_upscale_level(
}
}
static void setting_get_string_representation_uint_menu_ticker_type(
rarch_setting_t *setting,
char *s, size_t len)
{
if (!setting)
return;
switch (*setting->value.target.unsigned_integer)
{
case TICKER_TYPE_BOUNCE:
strlcpy(s,
msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_MENU_TICKER_TYPE_BOUNCE),
len);
break;
case TICKER_TYPE_LOOP:
strlcpy(s,
msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_MENU_TICKER_TYPE_LOOP),
len);
break;
}
}
#ifdef HAVE_XMB
static void setting_get_string_representation_uint_xmb_icon_theme(
rarch_setting_t *setting,
@ -8165,6 +8189,22 @@ static bool setting_append_list(
#endif
}
CONFIG_UINT(
list, list_info,
&settings->uints.menu_ticker_type,
MENU_ENUM_LABEL_MENU_TICKER_TYPE,
MENU_ENUM_LABEL_VALUE_MENU_TICKER_TYPE,
menu_ticker_type,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint;
(*list)[list_info->index - 1].get_string_representation =
&setting_get_string_representation_uint_menu_ticker_type;
menu_settings_list_current_add_range(list, list_info, 0, TICKER_TYPE_LAST-1, 1, true, true);
END_SUB_GROUP(list, list_info, parent_group);
START_SUB_GROUP(list, list_info, "Navigation", &group_info, &subgroup_info, parent_group);

View File

@ -920,6 +920,10 @@ enum msg_hash_enums
MENU_LABEL(QUICK_MENU_STOP_RECORDING),
MENU_LABEL(QUICK_MENU_START_STREAMING),
MENU_LABEL(QUICK_MENU_STOP_STREAMING),
MENU_LABEL(MENU_TICKER_TYPE),
MENU_ENUM_LABEL_VALUE_MENU_TICKER_TYPE_BOUNCE,
MENU_ENUM_LABEL_VALUE_MENU_TICKER_TYPE_LOOP,
/* UI settings */
MENU_LABEL(VIDEO_DISABLE_COMPOSITION),