From ee11b7973812fe590d2a593be39170a47823e283 Mon Sep 17 00:00:00 2001 From: sonninnos <45124675+sonninnos@users.noreply.github.com> Date: Tue, 13 May 2025 22:14:49 +0300 Subject: [PATCH] Improve playlist thumbnail cycling (#17897) * Improve playlist thumbnail cycling * GLUI: Thumbnail cycling fixes --- input/input_driver.c | 53 +++++++++++++++++++++++--- intl/msg_hash_us.h | 8 ++-- menu/cbs/menu_cbs_scan.c | 43 ++------------------- menu/drivers/materialui.c | 45 +++++++++++++++------- menu/drivers/rgui.c | 80 ++++++++++++++++++++++----------------- menu/menu_defines.h | 2 + menu/menu_driver.c | 76 +++++++++++++++++++++++++++++++++++++ 7 files changed, 211 insertions(+), 96 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 03767c49d2..4c1c7a86e0 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -7195,15 +7195,56 @@ void input_driver_collect_system_input(input_driver_state_t *input_st, if (ret) { - if (a == RETRO_DEVICE_ID_ANALOG_Y && (float)ret / 0x7fff < -joypad_info.axis_threshold) - BIT256_SET_PTR(current_bits, RETRO_DEVICE_ID_JOYPAD_UP); - else if (a == RETRO_DEVICE_ID_ANALOG_Y && (float)ret / 0x7fff > joypad_info.axis_threshold) - BIT256_SET_PTR(current_bits, RETRO_DEVICE_ID_JOYPAD_DOWN); + bool playlist = false; + /* Replace right analog stick navigation in playlists to thumbnail cycling. */ + if (s == RETRO_DEVICE_INDEX_ANALOG_RIGHT) + { + menu_entry_t entry; + MENU_ENTRY_INITIALIZE(entry); + menu_entry_get(&entry, 0, 0, NULL, true); + + switch (entry.type) + { + case FILE_TYPE_RPL_ENTRY: + case FILE_TYPE_PLAYLIST_ENTRY: + case FILE_TYPE_PLAIN: + case FILE_TYPE_RDB: + playlist = true; + break; + default: + break; + } + } + + if (a == RETRO_DEVICE_ID_ANALOG_Y && (float)ret / 0x7fff < -joypad_info.axis_threshold) + { + if (playlist) + BIT256_SET_PTR(current_bits, RARCH_ANALOG_RIGHT_Y_MINUS); + else + BIT256_SET_PTR(current_bits, RETRO_DEVICE_ID_JOYPAD_UP); + } + else if (a == RETRO_DEVICE_ID_ANALOG_Y && (float)ret / 0x7fff > joypad_info.axis_threshold) + { + if (playlist) + BIT256_SET_PTR(current_bits, RARCH_ANALOG_RIGHT_Y_PLUS); + else + BIT256_SET_PTR(current_bits, RETRO_DEVICE_ID_JOYPAD_DOWN); + } if (a == RETRO_DEVICE_ID_ANALOG_X && (float)ret / 0x7fff < -joypad_info.axis_threshold) - BIT256_SET_PTR(current_bits, RETRO_DEVICE_ID_JOYPAD_LEFT); + { + if (playlist) + BIT256_SET_PTR(current_bits, RARCH_ANALOG_RIGHT_X_MINUS); + else + BIT256_SET_PTR(current_bits, RETRO_DEVICE_ID_JOYPAD_LEFT); + } else if (a == RETRO_DEVICE_ID_ANALOG_X && (float)ret / 0x7fff > joypad_info.axis_threshold) - BIT256_SET_PTR(current_bits, RETRO_DEVICE_ID_JOYPAD_RIGHT); + { + if (playlist) + BIT256_SET_PTR(current_bits, RARCH_ANALOG_RIGHT_X_PLUS); + else + BIT256_SET_PTR(current_bits, RETRO_DEVICE_ID_JOYPAD_RIGHT); + } } } } diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 23af5c5e6b..aa1a6d0edd 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3762,7 +3762,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_DISABLE_INFO_BUTTON, - "If enabled Info function will be disabled." + "Prevent menu info function." ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_DISABLE_SEARCH_BUTTON, @@ -3770,7 +3770,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_DISABLE_SEARCH_BUTTON, - "If enabled Search function will be disabled." + "Prevent menu search function." ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_DISABLE_LEFT_ANALOG_IN_MENU, @@ -3778,7 +3778,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_DISABLE_LEFT_ANALOG_IN_MENU, - "Prevent Left Analog stick from navigating in menu." + "Prevent menu left analog stick input." ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_DISABLE_RIGHT_ANALOG_IN_MENU, @@ -3786,7 +3786,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_DISABLE_RIGHT_ANALOG_IN_MENU, - "Prevent Right Analog stick from navigating in menu." + "Prevent menu right analog stick input. Right analog stick cycles thumbnails in playlists." ) MSG_HASH( MENU_ENUM_LABEL_VALUE_MENU_INPUT_SWAP_OK_CANCEL, diff --git a/menu/cbs/menu_cbs_scan.c b/menu/cbs/menu_cbs_scan.c index 8bad5359f0..652bf55e5c 100644 --- a/menu/cbs/menu_cbs_scan.c +++ b/menu/cbs/menu_cbs_scan.c @@ -113,6 +113,7 @@ int action_scan_directory(const char *path, } #endif +extern int action_cycle_thumbnail(unsigned mode); int action_switch_thumbnail(const char *path, const char *label, unsigned type, size_t idx) { @@ -134,46 +135,10 @@ int action_switch_thumbnail(const char *path, * types and skip if already visible. */ if (switch_enabled) { - if (settings->uints.gfx_thumbnails == 0) - { - configuration_set_uint(settings, - settings->uints.menu_left_thumbnails, - settings->uints.menu_left_thumbnails + 1); - - if (settings->uints.gfx_thumbnails == settings->uints.menu_left_thumbnails) - configuration_set_uint(settings, - settings->uints.menu_left_thumbnails, - settings->uints.menu_left_thumbnails + 1); - - if (settings->uints.menu_left_thumbnails > 3) - configuration_set_uint(settings, - settings->uints.menu_left_thumbnails, 1); - - if (settings->uints.gfx_thumbnails == settings->uints.menu_left_thumbnails) - configuration_set_uint(settings, - settings->uints.menu_left_thumbnails, - settings->uints.menu_left_thumbnails + 1); - } + if (settings->uints.gfx_thumbnails) + action_cycle_thumbnail(MENU_ACTION_CYCLE_THUMBNAIL_PRIMARY); else - { - configuration_set_uint(settings, - settings->uints.gfx_thumbnails, - settings->uints.gfx_thumbnails + 1); - - if (settings->uints.gfx_thumbnails == settings->uints.menu_left_thumbnails) - configuration_set_uint(settings, - settings->uints.gfx_thumbnails, - settings->uints.gfx_thumbnails + 1); - - if (settings->uints.gfx_thumbnails > 3) - configuration_set_uint(settings, - settings->uints.gfx_thumbnails, 1); - - if (settings->uints.gfx_thumbnails == settings->uints.menu_left_thumbnails) - configuration_set_uint(settings, - settings->uints.gfx_thumbnails, - settings->uints.gfx_thumbnails + 1); - } + action_cycle_thumbnail(MENU_ACTION_CYCLE_THUMBNAIL_PRIMARY); if (menu_st->driver_ctx) { diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 736f88653d..e2727aa1fd 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -151,6 +151,9 @@ #define MUI_BATTERY_PERCENT_MAX_LENGTH 12 #define MUI_TIMEDATE_MAX_LENGTH 255 +/* Allow force enabling secondary thumbnail */ +#define MUI_FORCE_ENABLE_SECONDARY 0 + /* Forward declarations */ extern int action_switch_thumbnail(const char *path, const char *label, unsigned type, size_t idx); @@ -3045,10 +3048,7 @@ static void materialui_compute_entries_box_playlist_list( /* Account for additional padding in portrait mode */ if (mui->flags & MUI_FLAG_IS_PORTRAIT) - { - if (mui->flags & MUI_FLAG_SECONDARY_THUMBNAIL_ENABLED) - thumbnail_margin = (int)mui->scrollbar.width; - } + thumbnail_margin = (int)mui->scrollbar.width; /* Account for additional padding in landscape mode */ else thumbnail_margin = (int)mui->margin; @@ -4735,10 +4735,7 @@ static void materialui_render_menu_entry_playlist_list( * width (to prevent the scroll bar from being * drawn on top of the secondary thumbnail) */ if (mui->flags & MUI_FLAG_IS_PORTRAIT) - { - if (mui->flags & MUI_FLAG_SECONDARY_THUMBNAIL_ENABLED) - thumbnail_margin = (int)mui->scrollbar.width; - } + thumbnail_margin = (int)mui->scrollbar.width; /* When using landscape display orientations, we * have enough screen space to improve thumbnail * appearance by adding left/right margins */ @@ -8496,6 +8493,7 @@ static void materialui_set_thumbnail_dimensions(materialui_handle_t *mui) } } +#if MUI_FORCE_ENABLE_SECONDARY /* Checks global 'Secondary Thumbnail' option - if * currently set to 'OFF', changes value to * MUI_DEFAULT_SECONDARY_THUMBNAIL_TYPE @@ -8549,6 +8547,7 @@ static bool materialui_force_enable_secondary_thumbnail( return gfx_thumbnail_is_enabled( menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT); } +#endif /* Determines whether dual thumbnails should be enabled * based on current list view mode, thumbnail dimensions @@ -8582,10 +8581,12 @@ static void materialui_set_secondary_thumbnail_enable(materialui_handle_t *mui, if (!menu_materialui_dual_thumbnail_list_view_enable) return; +#if MUI_FORCE_ENABLE_SECONDARY /* Attempt to force enable secondary thumbnails if * global 'Secondary Thumbnail' type is set to OFF */ if (!materialui_force_enable_secondary_thumbnail(mui, menu_st, settings)) return; +#endif /* Secondary thumbnails are supported/enabled * Check whether screen has sufficient @@ -8609,6 +8610,9 @@ static void materialui_set_secondary_thumbnail_enable(materialui_handle_t *mui, * primary + secondary thumbnails */ usable_width -= 2 * ((int)mui->thumbnail_width_max + thumbnail_margin); + if (!gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT)) + break; + /* > A secondary thumbnail may only be drawn * if the remaining (text) width is greater * than twice the thumbnail width */ @@ -8620,6 +8624,7 @@ static void materialui_set_secondary_thumbnail_enable(materialui_handle_t *mui, break; case MUI_LIST_VIEW_PLAYLIST_THUMB_DUAL_ICON: case MUI_LIST_VIEW_PLAYLIST_THUMB_DESKTOP: +#if MUI_FORCE_ENABLE_SECONDARY /* List view requires secondary thumbnails * > Attempt to force enable, but set * MUI_FLAG_SECONDARY_THUMBNAIL_ENABLED to 'true' @@ -8629,6 +8634,12 @@ static void materialui_set_secondary_thumbnail_enable(materialui_handle_t *mui, * a per-playlist override */ materialui_force_enable_secondary_thumbnail(mui, menu_st, settings); mui->flags |= MUI_FLAG_SECONDARY_THUMBNAIL_ENABLED; +#else + if (gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT)) + mui->flags |= MUI_FLAG_SECONDARY_THUMBNAIL_ENABLED; + else + mui->flags &= ~MUI_FLAG_SECONDARY_THUMBNAIL_ENABLED; +#endif break; case MUI_LIST_VIEW_PLAYLIST: case MUI_LIST_VIEW_DEFAULT: @@ -12119,6 +12130,9 @@ static void materialui_refresh_thumbnail_image(void *userdata, size_t i) if (refresh_enabled) { + /* Must reset also partially visible items */ + size_t j = (mui->first_onscreen_entry) ? mui->first_onscreen_entry - 1 : 0; + size_t j_max = mui->last_onscreen_entry + 1; materialui_node_t *node = NULL; file_list_t *list = MENU_LIST_GET_SELECTION(menu_list, 0); float stream_delay = gfx_thumb_get_ptr()->stream_delay; @@ -12126,12 +12140,17 @@ static void materialui_refresh_thumbnail_image(void *userdata, size_t i) if (!list) return; - if (!(node = (materialui_node_t*)list->list[(size_t)i].userdata)) - return; + materialui_update_list_view(mui, menu_st, config_get_ptr()); - /* Reset existing thumbnails */ - gfx_thumbnail_reset(&node->thumbnails.primary); - gfx_thumbnail_reset(&node->thumbnails.secondary); + for (; j < j_max; j++) + { + if (!(node = (materialui_node_t*)list->list[(size_t)j].userdata)) + continue; + + /* Reset existing thumbnails */ + gfx_thumbnail_reset(&node->thumbnails.primary); + gfx_thumbnail_reset(&node->thumbnails.secondary); + } /* No need to actually request thumbnails here * > Just set delay timer to the current maximum diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 97137d8759..6baed8e21f 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -7155,10 +7155,8 @@ static void rgui_refresh_thumbnail_image(void *userdata, size_t i) rgui_inline_thumbnails = settings->bools.menu_rgui_inline_thumbnails || (rgui->flags & RGUI_FLAG_IS_QUICK_MENU); - /* Only refresh thumbnails if thumbnails are enabled */ - if ( ((rgui->flags & RGUI_FLAG_SHOW_FULLSCREEN_THUMBNAIL) || rgui_inline_thumbnails) - && (gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) - || gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT))) + /* Only refresh thumbnails if thumbnails are visible */ + if ((rgui->flags & RGUI_FLAG_SHOW_FULLSCREEN_THUMBNAIL) || rgui_inline_thumbnails) { /* In all cases, reset current thumbnails */ rgui->fs_thumbnail.width = 0; @@ -7176,6 +7174,11 @@ static void rgui_refresh_thumbnail_image(void *userdata, size_t i) rgui->mini_left_thumbnail.is_valid = false; rgui->mini_left_thumbnail.path[0] = '\0'; + /* Skip thumbnail scan if neither are enabled */ + if ( !gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_RIGHT) + && !gfx_thumbnail_is_enabled(menu_st->thumbnail_path_data, GFX_THUMBNAIL_LEFT)) + return; + /* Only load thumbnails if currently viewing a * playlist (note that thumbnails are loaded * immediately, for an optimal user experience) */ @@ -7190,49 +7193,49 @@ static void rgui_action_switch_thumbnail(rgui_t *rgui) { settings_t *settings = config_get_ptr(); - if (settings->uints.gfx_thumbnails == 0) + if (!settings) + return; + + if (settings->uints.gfx_thumbnails) { - configuration_set_uint(settings, - settings->uints.menu_left_thumbnails, - settings->uints.menu_left_thumbnails + 1); + uint8_t cur_primary = settings->uints.gfx_thumbnails; + uint8_t cur_secondary = settings->uints.menu_left_thumbnails; + cur_primary++; + + /* Prevent dupe image */ if ( (!(rgui->flags & RGUI_FLAG_SHOW_FULLSCREEN_THUMBNAIL)) - && (settings->uints.gfx_thumbnails == settings->uints.menu_left_thumbnails)) - configuration_set_uint(settings, - settings->uints.menu_left_thumbnails, - settings->uints.menu_left_thumbnails + 1); + && (cur_primary == cur_secondary && cur_secondary)) + cur_primary++; - if (settings->uints.menu_left_thumbnails > 3) - configuration_set_uint(settings, - settings->uints.menu_left_thumbnails, 1); + /* Wrap primary to first image type, and skip logo */ + if (cur_primary > PLAYLIST_THUMBNAIL_MODE_LAST - PLAYLIST_THUMBNAIL_MODE_OFF - 2) + cur_primary = 1; + /* Final dupe check */ if ( (!(rgui->flags & RGUI_FLAG_SHOW_FULLSCREEN_THUMBNAIL)) - && (settings->uints.gfx_thumbnails == settings->uints.menu_left_thumbnails)) - configuration_set_uint(settings, - settings->uints.menu_left_thumbnails, - settings->uints.menu_left_thumbnails + 1); + && (cur_primary == cur_secondary && cur_secondary)) + cur_primary++; + + configuration_set_uint(settings, settings->uints.gfx_thumbnails, cur_primary); } else { - configuration_set_uint(settings, - settings->uints.gfx_thumbnails, - settings->uints.gfx_thumbnails + 1); + uint8_t cur_primary = settings->uints.gfx_thumbnails; + uint8_t cur_secondary = settings->uints.menu_left_thumbnails; + cur_secondary++; + + /* Prevent dupe image */ if ( (!(rgui->flags & RGUI_FLAG_SHOW_FULLSCREEN_THUMBNAIL)) - && (settings->uints.gfx_thumbnails == settings->uints.menu_left_thumbnails)) - configuration_set_uint(settings, - settings->uints.gfx_thumbnails, - settings->uints.gfx_thumbnails + 1); + && (cur_primary == cur_secondary)) + cur_secondary++; - if (settings->uints.gfx_thumbnails > 3) - configuration_set_uint(settings, - settings->uints.gfx_thumbnails, 1); + /* Wrap secondary to no image, and skip logo */ + if (cur_secondary > PLAYLIST_THUMBNAIL_MODE_LAST - PLAYLIST_THUMBNAIL_MODE_OFF - 2) + cur_secondary = 0; - if ( (!(rgui->flags & RGUI_FLAG_SHOW_FULLSCREEN_THUMBNAIL)) - && (settings->uints.gfx_thumbnails == settings->uints.menu_left_thumbnails)) - configuration_set_uint(settings, - settings->uints.gfx_thumbnails, - settings->uints.gfx_thumbnails + 1); + configuration_set_uint(settings, settings->uints.menu_left_thumbnails, cur_secondary); } rgui_refresh_thumbnail_image(rgui, 0); @@ -8206,6 +8209,15 @@ static enum menu_action rgui_parse_menu_entry_action( new_action = MENU_ACTION_NOOP; } break; + case MENU_ACTION_CYCLE_THUMBNAIL_PRIMARY: + case MENU_ACTION_CYCLE_THUMBNAIL_SECONDARY: + /* Use rgui internal cycle method always in fullscreen mode */ + if (rgui->flags & RGUI_FLAG_SHOW_FULLSCREEN_THUMBNAIL) + { + rgui_action_switch_thumbnail(rgui); + new_action = MENU_ACTION_NOOP; + } + break; case MENU_ACTION_INFO: if ( (rgui->flags & RGUI_FLAG_IS_PLAYLIST) || (rgui->flags & RGUI_FLAG_IS_EXPLORE_LIST)) diff --git a/menu/menu_defines.h b/menu/menu_defines.h index 5bff359c2e..5f0d0d3c61 100644 --- a/menu/menu_defines.h +++ b/menu/menu_defines.h @@ -459,6 +459,8 @@ enum menu_action MENU_ACTION_SCROLL_UP, MENU_ACTION_SCROLL_HOME, MENU_ACTION_SCROLL_END, + MENU_ACTION_CYCLE_THUMBNAIL_PRIMARY, + MENU_ACTION_CYCLE_THUMBNAIL_SECONDARY, MENU_ACTION_TOGGLE, MENU_ACTION_RESUME, MENU_ACTION_POINTER_MOVED, diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 59c4820ee6..2efb5771c0 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -5624,6 +5624,15 @@ unsigned menu_event( ret = MENU_ACTION_SCROLL_END; } + if (BIT256_GET_PTR(p_trigger_input, RARCH_ANALOG_RIGHT_Y_MINUS)) + ret = MENU_ACTION_CYCLE_THUMBNAIL_PRIMARY; + else if (BIT256_GET_PTR(p_trigger_input, RARCH_ANALOG_RIGHT_Y_PLUS)) + ret = MENU_ACTION_CYCLE_THUMBNAIL_SECONDARY; + else if (BIT256_GET_PTR(p_trigger_input, RARCH_ANALOG_RIGHT_X_MINUS)) + ret = MENU_ACTION_CYCLE_THUMBNAIL_PRIMARY; + else if (BIT256_GET_PTR(p_trigger_input, RARCH_ANALOG_RIGHT_X_PLUS)) + ret = MENU_ACTION_CYCLE_THUMBNAIL_SECONDARY; + if (ok_trigger) ret = MENU_ACTION_OK; else if (BIT256_GET_PTR(p_trigger_input, menu_cancel_btn)) @@ -6975,6 +6984,69 @@ clear: } #endif +/** + * action_cycle_thumbnail: + * @mode : menu action (primary/secondary) + * + * Common thumbnail cycler + * + * Returns: 0 on success, -1 on fail. +**/ +int action_cycle_thumbnail(unsigned mode) +{ + struct menu_state *menu_st = menu_state_get_ptr(); + settings_t *settings = config_get_ptr(); + + if (!settings) + return -1; + + if (mode == MENU_ACTION_CYCLE_THUMBNAIL_PRIMARY) + { + uint8_t cur_primary = settings->uints.gfx_thumbnails; + uint8_t cur_secondary = settings->uints.menu_left_thumbnails; + + cur_primary++; + + /* Prevent dupe image */ + if (cur_primary == cur_secondary && cur_secondary) + cur_primary++; + + /* Wrap primary to first image type, and skip logo */ + if (cur_primary > PLAYLIST_THUMBNAIL_MODE_LAST - PLAYLIST_THUMBNAIL_MODE_OFF - 2) + cur_primary = 1; + + /* Final dupe check */ + if (cur_primary == cur_secondary && cur_secondary) + cur_primary++; + + configuration_set_uint(settings, settings->uints.gfx_thumbnails, cur_primary); + } + else if (mode == MENU_ACTION_CYCLE_THUMBNAIL_SECONDARY) + { + uint8_t cur_primary = settings->uints.gfx_thumbnails; + uint8_t cur_secondary = settings->uints.menu_left_thumbnails; + + cur_secondary++; + + /* Prevent dupe image */ + if (cur_primary == cur_secondary) + cur_secondary++; + + /* Wrap secondary to no image, and skip logo */ + if (cur_secondary > PLAYLIST_THUMBNAIL_MODE_LAST - PLAYLIST_THUMBNAIL_MODE_OFF - 2) + cur_secondary = 0; + + configuration_set_uint(settings, settings->uints.menu_left_thumbnails, cur_secondary); + } + + if (menu_st->driver_ctx) + { + if (menu_st->driver_ctx->refresh_thumbnail_image) + menu_st->driver_ctx->refresh_thumbnail_image(menu_st->userdata, menu_st->selection_ptr); + } + + return 0; +} /** * menu_iterate: @@ -7639,6 +7711,10 @@ int generic_menu_entry_action( #endif break; } + case MENU_ACTION_CYCLE_THUMBNAIL_PRIMARY: + case MENU_ACTION_CYCLE_THUMBNAIL_SECONDARY: + action_cycle_thumbnail(action); + break; case MENU_ACTION_CANCEL: if (cbs && cbs->action_cancel) ret = cbs->action_cancel(entry->path,