@@ -340,6 +340,19 @@ typedef struct xmb_handle
340340 xmb_node_t netplay_tab_node ;
341341 menu_input_pointer_t pointer ;
342342
343+ /* Touch drag state for direction locking */
344+ enum
345+ {
346+ XMB_DRAG_NONE ,
347+ XMB_DRAG_DETECTING ,
348+ XMB_DRAG_HORIZONTAL ,
349+ XMB_DRAG_VERTICAL
350+ } drag_mode ;
351+ float drag_start_x ;
352+ float drag_start_y ;
353+ float categories_drag_start_pos ;
354+ size_t drag_start_selection ;
355+
343356 font_data_t * font ;
344357 font_data_t * font2 ;
345358 video_font_raster_block_t raster_block ;
@@ -517,6 +530,7 @@ static int xmb_menu_entry_action(void *userdata,
517530 menu_entry_t * entry , size_t i , enum menu_action action );
518531static bool xmb_load_image (void * userdata , void * data ,
519532 enum menu_image_type type );
533+ static void xmb_navigation_set (void * data , bool scroll );
520534
521535
522536static INLINE float xmb_item_y (const xmb_handle_t * xmb ,
@@ -6782,6 +6796,123 @@ static void xmb_render(void *data,
67826796 /* Read pointer state */
67836797 menu_input_get_pointer_state (& xmb -> pointer );
67846798
6799+ /* Direction locking for horizontal category dragging */
6800+ if (xmb -> pointer .type == MENU_POINTER_TOUCHSCREEN )
6801+ {
6802+ if (xmb -> pointer .flags & MENU_INP_PTR_FLG_PRESSED )
6803+ {
6804+ if (xmb -> drag_mode == XMB_DRAG_DETECTING )
6805+ {
6806+ /* Wait for movement threshold before locking direction */
6807+ float dx = fabs ((float )xmb -> pointer .x - xmb -> drag_start_x );
6808+ float dy = fabs ((float )xmb -> pointer .y - xmb -> drag_start_y );
6809+ float threshold = 10.0f ;
6810+
6811+ if (dx > threshold || dy > threshold )
6812+ {
6813+ /* Lock to dominant direction */
6814+ if (dx > dy )
6815+ xmb -> drag_mode = XMB_DRAG_HORIZONTAL ;
6816+ else
6817+ xmb -> drag_mode = XMB_DRAG_VERTICAL ;
6818+ }
6819+ }
6820+
6821+ if (xmb -> drag_mode == XMB_DRAG_HORIZONTAL )
6822+ {
6823+ /* Only allow horizontal category switching at the top level (depth == 1)
6824+ * When in submenus (depth > 1), horizontal drag is disabled */
6825+ if (xmb -> depth == 1 )
6826+ {
6827+ /* Apply horizontal drag to categories */
6828+ size_t list_size = xmb_list_get_size (xmb , MENU_LIST_HORIZONTAL ) + xmb -> system_tab_end + 1 ;
6829+ float current_dx = (float )xmb -> pointer .x - xmb -> drag_start_x ;
6830+ float min_x = - xmb -> icon_spacing_horizontal * (float )(list_size > 0 ? list_size - 1 : 0 );
6831+ float max_x = 0.0f ;
6832+
6833+ xmb -> categories_x_pos = xmb -> categories_drag_start_pos + current_dx ;
6834+
6835+ /* Bounds checking */
6836+ if (xmb -> categories_x_pos > max_x )
6837+ xmb -> categories_x_pos = max_x ;
6838+ if (xmb -> categories_x_pos < min_x )
6839+ xmb -> categories_x_pos = min_x ;
6840+
6841+ /* Update selection and switch categories during drag */
6842+ {
6843+ float normalized = - xmb -> categories_x_pos / xmb -> icon_spacing_horizontal ;
6844+ size_t nearest = (size_t )(normalized + 0.5f );
6845+
6846+ /* Clamp to valid range */
6847+ if (nearest >= list_size )
6848+ nearest = list_size > 0 ? list_size - 1 : 0 ;
6849+
6850+ /* If category changed, do full switch */
6851+ if (nearest != xmb -> categories_selection_ptr )
6852+ {
6853+ struct menu_state * menu_st = menu_state_get_ptr ();
6854+ menu_list_t * menu_list = menu_st -> entries .list ;
6855+ file_list_t * selection_buf = MENU_LIST_GET_SELECTION (menu_list , 0 );
6856+
6857+ /* Determine direction */
6858+ enum menu_action action = (nearest > xmb -> categories_selection_ptr )
6859+ ? MENU_ACTION_RIGHT
6860+ : MENU_ACTION_LEFT ;
6861+
6862+ /* Call list_cache to update state (increments/decrements by 1) */
6863+ if (menu_st -> driver_ctx && menu_st -> driver_ctx -> list_cache )
6864+ menu_st -> driver_ctx -> list_cache (menu_st -> userdata , MENU_LIST_HORIZONTAL , action );
6865+
6866+ /* Repopulate vertical list with new category content */
6867+ menu_driver_deferred_push_content_list (selection_buf );
6868+
6869+ /* Visual update without animation during drag */
6870+ xmb_list_switch_horizontal_list (xmb , false, 0 );
6871+ }
6872+ }
6873+ }
6874+ }
6875+ else if (xmb -> drag_mode == XMB_DRAG_VERTICAL )
6876+ {
6877+ /* Apply vertical drag to list selection */
6878+ struct menu_state * menu_st = menu_state_get_ptr ();
6879+ menu_list_t * menu_list = menu_st -> entries .list ;
6880+ size_t list_size = MENU_LIST_GET_SELECTION (menu_list , 0 )-> size ;
6881+
6882+ /* Calculate how many items to move based on drag distance */
6883+ float dy = (float )xmb -> pointer .y - xmb -> drag_start_y ;
6884+ float item_height = xmb -> icon_spacing_vertical ;
6885+
6886+ /* Convert drag distance to item steps with threshold */
6887+ int steps = (int )((dy + (dy > 0 ? item_height * 0.5f : - item_height * 0.5f )) / item_height );
6888+
6889+ /* Calculate new selection (negative because dragging down = scroll up) */
6890+ int new_selection = (int )xmb -> drag_start_selection - steps ;
6891+
6892+ /* Clamp to valid range */
6893+ if (new_selection < 0 )
6894+ new_selection = 0 ;
6895+ if (new_selection >= (int )list_size )
6896+ new_selection = list_size > 0 ? (int )list_size - 1 : 0 ;
6897+
6898+ /* Update selection if changed */
6899+ if ((size_t )new_selection != menu_st -> selection_ptr )
6900+ {
6901+ file_list_t * selection_buf = MENU_LIST_GET_SELECTION (menu_list , 0 );
6902+ uintptr_t tag = (uintptr_t )selection_buf ;
6903+
6904+ menu_st -> selection_ptr = (size_t )new_selection ;
6905+
6906+ /* Kill existing animations to prevent stacking during continuous drag */
6907+ gfx_animation_kill_by_tag (& tag );
6908+
6909+ /* Enable animations for smooth scrolling */
6910+ xmb_navigation_set (xmb , true);
6911+ }
6912+ }
6913+ }
6914+ }
6915+
67856916 /* If menu screensaver is active, update
67866917 * screensaver and return */
67876918 if (xmb -> show_screensaver )
@@ -9701,6 +9832,26 @@ static bool xmb_menu_init_list(void *data)
97019832 return false;
97029833}
97039834
9835+ static int xmb_pointer_down (void * userdata ,
9836+ unsigned x , unsigned y ,
9837+ unsigned ptr , menu_file_list_cbs_t * cbs ,
9838+ menu_entry_t * entry , unsigned action )
9839+ {
9840+ xmb_handle_t * xmb = (xmb_handle_t * )userdata ;
9841+
9842+ if (!xmb )
9843+ return -1 ;
9844+
9845+ /* Initialize drag detection */
9846+ xmb -> drag_mode = XMB_DRAG_DETECTING ;
9847+ xmb -> drag_start_x = (float )x ;
9848+ xmb -> drag_start_y = (float )y ;
9849+ xmb -> categories_drag_start_pos = xmb -> categories_x_pos ;
9850+ xmb -> drag_start_selection = menu_state_get_ptr ()-> selection_ptr ;
9851+
9852+ return 0 ;
9853+ }
9854+
97049855static int xmb_pointer_up (void * userdata ,
97059856 unsigned x , unsigned y , unsigned ptr ,
97069857 enum menu_input_pointer_gesture gesture ,
@@ -9730,6 +9881,38 @@ static int xmb_pointer_up(void *userdata,
97309881 return 0 ;
97319882 }
97329883
9884+ /* Handle snap animation if we were dragging */
9885+ if (xmb -> drag_mode == XMB_DRAG_HORIZONTAL || xmb -> drag_mode == XMB_DRAG_VERTICAL )
9886+ {
9887+ /* Snap horizontal scrolling to final category position */
9888+ if (xmb -> drag_mode == XMB_DRAG_HORIZONTAL )
9889+ {
9890+ settings_t * settings = config_get_ptr ();
9891+ bool horizontal_animation = settings -> bools .menu_horizontal_animation ;
9892+ float target_x = xmb -> icon_spacing_horizontal * - (float )xmb -> categories_selection_ptr ;
9893+
9894+ /* Animate to exact category position */
9895+ if (horizontal_animation )
9896+ {
9897+ gfx_animation_ctx_entry_t anim_entry ;
9898+ anim_entry .duration = XMB_DELAY ;
9899+ anim_entry .target_value = target_x ;
9900+ anim_entry .subject = & xmb -> categories_x_pos ;
9901+ anim_entry .easing_enum = EASING_OUT_QUAD ;
9902+ anim_entry .tag = -1 ;
9903+ anim_entry .cb = NULL ;
9904+
9905+ if (anim_entry .subject )
9906+ gfx_animation_push (& anim_entry );
9907+ }
9908+ else
9909+ xmb -> categories_x_pos = target_x ;
9910+ }
9911+
9912+ xmb -> drag_mode = XMB_DRAG_NONE ;
9913+ return 0 ;
9914+ }
9915+
97339916 video_driver_get_size (& width , & height );
97349917 margin_top = (int16_t )xmb -> margins_screen_top ;
97359918 margin_left = (int16_t )xmb -> margins_screen_left ;
@@ -9921,7 +10104,7 @@ menu_ctx_driver_t menu_ctx_xmb = {
992110104 gfx_display_osk_ptr_at_pos ,
992210105 xmb_update_savestate_thumbnail_path ,
992310106 xmb_update_savestate_thumbnail_image ,
9924- NULL , /* pointer_down */
10107+ xmb_pointer_down ,
992510108 xmb_pointer_up ,
992610109 xmb_menu_entry_action
992710110};
0 commit comments