(GLUI) Add initial gesture support + bug fixes

This commit is contained in:
jdgleaver 2019-10-03 10:42:37 +01:00
parent 5351b56a0b
commit db030f6472
2 changed files with 467 additions and 217 deletions

View File

@ -57,6 +57,10 @@
#include "../../dynamic.h"
/* Global DPI-aware scale factor ==
* (screen DPI) * MUI_DPI_SCALE_COEFF */
#define MUI_DPI_SCALE_COEFF 2.2f
/* This struct holds the y position and the line height for each menu entry */
typedef struct
{
@ -143,8 +147,12 @@ enum
typedef struct materialui_handle
{
bool need_compute;
bool need_scroll;
bool mouse_show;
bool is_playlist;
bool is_file_list;
bool is_dropdown_list;
float dpi_scale_factor;
int cursor_size;
@ -162,6 +170,9 @@ typedef struct materialui_handle
size_t categories_selection_ptr;
size_t categories_selection_ptr_old;
size_t first_onscreen_entry;
size_t last_onscreen_entry;
/* Y position of the vertical scroll */
float scroll_y;
float content_height;
@ -184,6 +195,12 @@ typedef struct materialui_handle
video_font_raster_block_t raster_block;
video_font_raster_block_t raster_block2;
/* Pointer info */
menu_input_pointer_t pointer;
int16_t pointer_start_x;
int16_t pointer_start_y;
float pointer_start_scroll_y;
} materialui_handle_t;
static const char *materialui_texture_path(unsigned id)
@ -413,11 +430,6 @@ static void materialui_draw_tab_begin(
unsigned width, unsigned height,
float *tabs_bg_color, float *tabs_separator_color)
{
float scale_factor = menu_display_get_dpi(video_info->width,
video_info->height);
mui->tabs_height = scale_factor / 3;
/* tabs background */
menu_display_draw_quad(
video_info,
@ -602,7 +614,6 @@ static void materialui_compute_entries_box(materialui_handle_t* mui, int width,
file_list_t *list = menu_entries_get_selection_buf_ptr(0);
float sum = 0;
size_t entries_end = menu_entries_get_size();
float scale_factor = menu_display_get_dpi(width, height);
for (i = 0; i < entries_end; i++)
{
@ -638,7 +649,7 @@ static void materialui_compute_entries_box(materialui_handle_t* mui, int width,
lines = materialui_count_lines(wrapped_sublabel_str);
}
node->line_height = (scale_factor / 3) + (lines * mui->font->size);
node->line_height = (mui->dpi_scale_factor / 3) + (lines * mui->font->size);
node->y = sum;
sum += node->line_height;
}
@ -680,90 +691,124 @@ static float materialui_get_scroll(materialui_handle_t *mui)
return sum - half;
}
/* Called on each frame. We use this callback to implement the touch scroll
with acceleration */
/* Called on each frame. We use this callback to:
* - Determine current scroll postion
* - Determine index of first/last onscreen entries
* - Handle dynamic pointer input */
static void materialui_render(void *data,
unsigned width, unsigned height,
bool is_idle)
{
int bottom, header_height;
menu_input_pointer_t pointer;
size_t i = 0;
materialui_handle_t *mui = (materialui_handle_t*)data;
file_list_t *list = menu_entries_get_selection_buf_ptr(0);
materialui_handle_t *mui = (materialui_handle_t*)data;
int header_height = menu_display_get_header_height();
size_t entries_end = menu_entries_get_size();
file_list_t *list = menu_entries_get_selection_buf_ptr(0);
bool first_entry_found = false;
size_t i;
int bottom;
if (!mui)
if (!mui || !list)
return;
/* Here's a nasty issue:
* After calling populate_entries(), we need to call
* materialui_get_scroll() so the last selected item
* is correctly displayed on screen.
* But we can't do this until materialui_compute_entries_box()
* has been called, so we should delegate it until mui->need_compute
* is acted upon.
* *But* we can't do this in the same frame that mui->need_compute
* is acted upon, because of the order in which materialui_frame()
* and materialui_render() are called. Since mui->tabs_height is
* set by materialui_frame(), the first time materialui_render() is
* called after populate_entries() it has the wrong mui->tabs_height
* value...
* We therefore have to delegate the scroll until the frame after
* mui->need_compute is handled... */
if (mui->need_scroll)
{
mui->scroll_y = materialui_get_scroll(mui);
mui->need_scroll = false;
}
if (mui->need_compute)
{
if (mui->font)
materialui_compute_entries_box(mui, width, height);
/* After calling populate_entries(), we need to call
* materialui_get_scroll() so the last selected item
* is correctly displayed on screen.
* But we can't do this until materialui_compute_entries_box()
* has been called, so we delay it until here, when
* mui->need_compute is acted upon. */
mui->scroll_y = materialui_get_scroll(mui);
mui->need_compute = false;
mui->need_scroll = true;
}
/* Need to update this each frame, otherwise touchscreen
* input breaks when changing orientation */
menu_display_set_width(width);
menu_display_set_height(height);
header_height = menu_display_get_header_height();
menu_input_get_pointer_state(&pointer);
/* Read pointer state */
menu_input_get_pointer_state(&mui->pointer);
if (pointer.type != MENU_POINTER_DISABLED)
/* Need to adjust/range-check scroll postion first,
* otherwise cannot determine correct entry index for
* MENU_ENTRIES_CTL_SET_START */
if (mui->pointer.type != MENU_POINTER_DISABLED)
mui->scroll_y -= mui->pointer.y_accel;
if (mui->scroll_y < 0.0f)
mui->scroll_y = 0.0f;
bottom = mui->content_height - height + header_height + mui->tabs_height;
if (mui->scroll_y > (float)bottom)
mui->scroll_y = (float)bottom;
if (mui->content_height < (height - header_height - mui->tabs_height))
mui->scroll_y = 0.0f;
/* Loop over all entries */
mui->first_onscreen_entry = 0;
mui->last_onscreen_entry = (entries_end > 0) ? entries_end - 1 : 0;
for (i = 0; i < entries_end; i++)
{
size_t ii;
int16_t pointer_y = pointer.y;
size_t entries_end = menu_entries_get_size();
materialui_node_t *node = (materialui_node_t*)
file_list_get_userdata_at_offset(list, i);
int entry_y;
for (ii = 0; ii < entries_end; ii++)
/* Sanity check */
if (!node)
break;
/* Get current entry y postion */
entry_y = header_height - mui->scroll_y + node->y;
/* Check whether this is the first onscreen entry */
if (!first_entry_found)
{
materialui_node_t *node = (materialui_node_t*)
file_list_get_userdata_at_offset(list, ii);
if ((pointer_y > (-mui->scroll_y + header_height + node->y)) &&
(pointer_y < (-mui->scroll_y + header_height + node->y + node->line_height)))
if ((entry_y + (int)node->line_height) > header_height)
{
menu_input_set_pointer_selection(ii);
break;
mui->first_onscreen_entry = i;
first_entry_found = true;
}
}
mui->scroll_y -= pointer.y_accel;
/* Track pointer input, if required */
if (first_entry_found && (mui->pointer.type != MENU_POINTER_DISABLED))
{
int16_t pointer_y = mui->pointer.y;
if ((pointer_y > entry_y) &&
(pointer_y < (entry_y + node->line_height)))
{
menu_input_set_pointer_selection(i);
/* If pointer is pressed, stationary, and has been pressed
* for at least MENU_INPUT_PRESS_TIME_SHORT ms, select current
* entry */
if (mui->pointer.pressed &&
!mui->pointer.dragged &&
(mui->pointer.press_duration >= MENU_INPUT_PRESS_TIME_SHORT))
menu_navigation_set_selection(i);
}
}
/* Check whether this is the last onscreen entry */
if (entry_y > ((int)height - (int)mui->tabs_height))
{
/* Current entry is off screen - get index
* of previous entry */
if (i > 0)
mui->last_onscreen_entry = i - 1;
break;
}
}
if (mui->scroll_y < 0)
mui->scroll_y = 0;
bottom = mui->content_height - height + header_height + mui->tabs_height;
if (mui->scroll_y > bottom)
mui->scroll_y = bottom;
if (mui->content_height
< height - header_height - mui->tabs_height)
mui->scroll_y = 0;
menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &i);
menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &mui->first_onscreen_entry);
}
/* Display an entry value on the right of the screen. */
@ -797,8 +842,6 @@ static void materialui_render_label_value(
size_t usable_width = width - (mui->margin * 2);
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(video_info->width,
video_info->height);
settings_t *settings = config_get_ptr();
bool use_smooth_ticker = settings->bools.menu_ticker_smooth;
@ -963,20 +1006,20 @@ static void materialui_render_label_value(
menu_display_draw_text(mui->font2, wrapped_sublabel_str,
mui->margin + icon_margin,
y + (scale_factor / 4) + mui->font->size,
y + (mui->dpi_scale_factor / 4) + mui->font->size,
width, height, sublabel_color, TEXT_ALIGN_LEFT,
1.0f, false, 0, false);
}
menu_display_draw_text(mui->font, label_str,
ticker_label_x_offset + mui->margin + icon_margin,
y + (scale_factor / 5),
y + (mui->dpi_scale_factor / 5),
width, height, color, TEXT_ALIGN_LEFT, 1.0f, false, 0, false);
if (do_draw_text)
menu_display_draw_text(mui->font, value_str,
value_x_offset + width - mui->margin,
y + (scale_factor / 5),
y + (mui->dpi_scale_factor / 5),
width, height, color, TEXT_ALIGN_RIGHT, 1.0f, false, 0, false);
if (texture_switch2)
@ -984,7 +1027,7 @@ static void materialui_render_label_value(
mui->icon_size,
(uintptr_t)texture_switch2,
0,
y + (scale_factor / 6) - mui->icon_size/2,
y + (mui->dpi_scale_factor / 6) - mui->icon_size/2,
width,
height,
0,
@ -1007,7 +1050,7 @@ static void materialui_render_label_value(
mui->icon_size,
(uintptr_t)texture_switch,
width - mui->margin - mui->icon_size,
y + (scale_factor / 6) - mui->icon_size/2,
y + (mui->dpi_scale_factor / 6) - mui->icon_size/2,
width,
height,
0,
@ -1027,41 +1070,39 @@ static void materialui_render_menu_list(
uint32_t sublabel_color)
{
size_t i;
float sum = 0;
size_t entries_end = 0;
file_list_t *list = NULL;
unsigned header_height =
menu_display_get_header_height();
size_t first_entry;
size_t last_entry;
file_list_t *list = NULL;
size_t entries_end = menu_entries_get_size();
unsigned header_height = menu_display_get_header_height();
size_t selection = menu_navigation_get_selection();
mui->raster_block.carr.coords.vertices = 0;
mui->raster_block2.carr.coords.vertices = 0;
menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i);
list = menu_entries_get_selection_buf_ptr(0);
if (!list)
return;
list =
menu_entries_get_selection_buf_ptr(0);
/* Unnecessary sanity check... */
first_entry = (mui->first_onscreen_entry < entries_end) ? mui->first_onscreen_entry : entries_end;
last_entry = (mui->last_onscreen_entry < entries_end) ? mui->last_onscreen_entry : entries_end;
entries_end = menu_entries_get_size();
for (i = 0; i < entries_end; i++)
for (i = first_entry; i <= last_entry; i++)
{
menu_entry_t entry;
const char *entry_value = NULL;
const char *rich_label = NULL;
bool entry_selected = false;
materialui_node_t *node = (materialui_node_t*)
file_list_get_userdata_at_offset(list, i);
size_t selection = menu_navigation_get_selection();
int y = header_height - mui->scroll_y + sum;
const char *entry_value = NULL;
const char *rich_label = NULL;
bool entry_selected = (selection == i);
materialui_node_t *node = (materialui_node_t*)file_list_get_userdata_at_offset(list, i);
int entry_y;
sum += node->line_height;
if (y + (int)node->line_height < 0)
continue;
if (y > (int)height)
/* Sanity check */
if (!node)
break;
entry_y = header_height - mui->scroll_y + node->y;
menu_entry_init(&entry);
entry.path_enabled = false;
entry.label_enabled = false;
@ -1069,16 +1110,14 @@ static void materialui_render_menu_list(
menu_entry_get(&entry, 0, (unsigned)i, NULL, true);
menu_entry_get_value(&entry, &entry_value);
menu_entry_get_rich_label(&entry, &rich_label);
entry_selected = selection == i;
/* Render label, value, and associated icons */
materialui_render_label_value(
mui,
video_info,
node,
(int)i,
y,
entry_y,
width,
height,
menu_animation_get_ticker_idx(),
@ -1562,10 +1601,8 @@ static void materialui_frame(void *data, video_frame_info_t *video_info)
height,
header_bg_color ? &header_bg_color[0] : NULL);
mui->tabs_height = 0;
/* display tabs if depth equal one, if not hide them */
if (materialui_list_get_size(mui, MENU_LIST_PLAIN) == 1)
/* Tab bar */
if (mui->tabs_height > 0)
{
materialui_draw_tab_begin(mui,
video_info,
@ -1696,18 +1733,15 @@ static void materialui_frame(void *data, video_frame_info_t *video_info)
mui->box_message = NULL;
}
if (mui->mouse_show)
if (mui->mouse_show && (mui->pointer.type != MENU_POINTER_DISABLED))
{
menu_input_pointer_t pointer;
menu_input_get_pointer_state(&pointer);
menu_display_draw_cursor(
video_info,
&white_bg[0],
mui->cursor_size,
mui->textures.list[MUI_TEXTURE_POINTER],
pointer.x,
pointer.y,
mui->pointer.x,
mui->pointer.y,
width,
height);
}
@ -1719,30 +1753,23 @@ static void materialui_frame(void *data, video_frame_info_t *video_info)
/* Compute the positions of the widgets */
static void materialui_layout(materialui_handle_t *mui, bool video_is_threaded)
{
float scale_factor;
int new_font_size, new_font_size2;
unsigned width, height, new_header_height;
unsigned new_header_height;
video_driver_get_size(&width, &height);
new_header_height = mui->dpi_scale_factor / 3;
new_font_size = mui->dpi_scale_factor / 9;
new_font_size2 = mui->dpi_scale_factor / 12;
/* Mobiles platforms may have very small display metrics
* coupled to a high resolution, so we should be DPI aware
* to ensure the entries hitboxes are big enough.
*
* On desktops, we just care about readability, with every widget
* size proportional to the display width. */
scale_factor = menu_display_get_dpi(width, height);
mui->shadow_height = mui->dpi_scale_factor / 36;
mui->scrollbar_width = mui->dpi_scale_factor / 36;
new_header_height = scale_factor / 3;
new_font_size = scale_factor / 9;
new_font_size2 = scale_factor / 12;
mui->tabs_height = 0;
if (materialui_list_get_size(mui, MENU_LIST_PLAIN) == 1)
mui->tabs_height = mui->dpi_scale_factor / 3;
mui->shadow_height = scale_factor / 36;
mui->scrollbar_width = scale_factor / 36;
mui->tabs_height = scale_factor / 3;
mui->line_height = scale_factor / 3;
mui->margin = scale_factor / 9;
mui->icon_size = scale_factor / 3;
mui->line_height = mui->dpi_scale_factor / 3;
mui->margin = mui->dpi_scale_factor / 9;
mui->icon_size = mui->dpi_scale_factor / 3;
/* we assume the average glyph aspect ratio is close to 3:4 */
mui->glyph_width = new_font_size * 3/4;
@ -1782,7 +1809,6 @@ static void materialui_layout(materialui_handle_t *mui, bool video_is_threaded)
static void *materialui_init(void **userdata, bool video_is_threaded)
{
unsigned width, height;
float scale_factor = 0.0f;
materialui_handle_t *mui = NULL;
menu_handle_t *menu = (menu_handle_t*)
calloc(1, sizeof(*menu));
@ -1790,9 +1816,6 @@ static void *materialui_init(void **userdata, bool video_is_threaded)
if (!menu)
return NULL;
video_driver_get_size(&width, &height);
scale_factor = menu_display_get_dpi(width, height);
if (!menu_display_init_first_driver(video_is_threaded))
goto error;
@ -1801,10 +1824,23 @@ static void *materialui_init(void **userdata, bool video_is_threaded)
if (!mui)
goto error;
*userdata = mui;
mui->cursor_size = scale_factor / 3;
mui->need_compute = false;
mui->need_scroll = false;
*userdata = mui;
/* Get global DPI-aware scale factor
* Note: screen DPI is a physical characteristic.
* It doesn't change, so we only have to do this once. */
video_driver_get_size(&width, &height);
mui->dpi_scale_factor = menu_display_get_dpi(width, height) * MUI_DPI_SCALE_COEFF;
mui->cursor_size = mui->dpi_scale_factor / 3;
mui->need_compute = false;
mui->is_playlist = false;
mui->is_file_list = false;
mui->is_dropdown_list = false;
mui->first_onscreen_entry = 0;
mui->last_onscreen_entry = 0;
mui->menu_title[0] = '\0';
@ -1879,23 +1915,17 @@ static bool materialui_load_image(void *userdata, void *data, enum menu_image_ty
return true;
}
/* The navigation pointer has been updated (for example by pressing up or down
on the keyboard). We use this function to animate the scroll. */
static void materialui_navigation_set(void *data, bool scroll)
static void materialui_animate_scroll(
materialui_handle_t *mui, float scroll_pos, float duration)
{
menu_animation_ctx_entry_t entry;
materialui_handle_t *mui = (materialui_handle_t*)data;
float scroll_pos = mui ? materialui_get_scroll(mui) : 0.0f;
if (!mui || !scroll)
return;
/* mui->scroll_y will be modified by the animation
* - Set scroll acceleration to zero to minimise
* > Set scroll acceleration to zero to minimise
* potential conflicts */
menu_input_set_pointer_y_accel(0.0f);
entry.duration = 166;
entry.duration = duration;
entry.target_value = scroll_pos;
entry.subject = &mui->scroll_y;
entry.easing_enum = EASING_IN_OUT_QUAD;
@ -1907,6 +1937,22 @@ static void materialui_navigation_set(void *data, bool scroll)
menu_animation_push(&entry);
}
/* The navigation pointer has been updated (for example by pressing up or down
on the keyboard) */
static void materialui_navigation_set(void *data, bool scroll)
{
materialui_handle_t *mui = (materialui_handle_t*)data;
menu_animation_ctx_entry_t entry;
if (!mui || !scroll)
return;
materialui_animate_scroll(
mui,
materialui_get_scroll(mui),
166.0f);
}
static void materialui_list_set_selection(void *data, file_list_t *list)
{
/* This is called upon MENU_ACTION_CANCEL
@ -1925,7 +1971,7 @@ static void materialui_navigation_clear(void *data, bool pending_push)
return;
menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &i);
mui->scroll_y = 0;
mui->scroll_y = 0.0f;
menu_input_set_pointer_y_accel(0.0f);
}
@ -1939,7 +1985,7 @@ static void materialui_navigation_alphabet(void *data, size_t *unused)
materialui_navigation_set(data, true);
}
/* A new list had been pushed. We update the scroll value */
/* A new list had been pushed */
static void materialui_populate_entries(
void *data, const char *path,
const char *label, unsigned i)
@ -1949,7 +1995,53 @@ static void materialui_populate_entries(
if (!mui)
return;
/* Set menu title */
menu_entries_get_title(mui->menu_title, sizeof(mui->menu_title));
/* Check whether we are currently viewing a playlist,
* file-browser-type list or dropdown list
* (each of these is regarded as a 'plain' list,
* and potentially a 'long' list, with special
* gesture-based navigation shortcuts) */
mui->is_playlist = false;
mui->is_file_list = false;
mui->is_dropdown_list = false;
mui->is_playlist = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_PLAYLIST_LIST)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_FAVORITES_LIST));
if (!mui->is_playlist)
{
/* > All of the following count as a 'file list'
* Note: MENU_ENUM_LABEL_FAVORITES is always set
* as the 'label' when navigating directories after
* selecting load content */
mui->is_file_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CORE_UPDATER_LIST)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SCAN_DIRECTORY)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SCAN_FILE)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_IMAGES_LIST)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_MUSIC_LIST)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LOAD_CONTENT_LIST)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES));
if (!mui->is_file_list)
mui->is_dropdown_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_SPECIAL)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_RESOLUTION)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_DEFAULT_CORE)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_LABEL_DISPLAY_MODE)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_RIGHT_THUMBNAIL_MODE)) ||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_LEFT_THUMBNAIL_MODE));
}
/* Set tab bar height
* (Tab bar is displayed if menu depth equals one,
* otherwise it is hidden) */
mui->tabs_height = 0;
if (materialui_list_get_size(mui, MENU_LIST_PLAIN) == 1)
mui->tabs_height = mui->dpi_scale_factor / 3;
mui->need_compute = true;
/* Note: mui->scroll_y position needs to be set here,
@ -2296,57 +2388,132 @@ static size_t materialui_list_get_selection(void *data)
return mui->categories_selection_ptr;
}
/* The pointer or the mouse is pressed down. We use this callback to
highlight the entry that has been pressed */
/* Pointer down event
* Used purely to cache the initial pointer x/y position */
static int materialui_pointer_down(void *userdata,
unsigned x, unsigned y,
unsigned ptr, menu_file_list_cbs_t *cbs,
menu_entry_t *entry, unsigned action)
{
unsigned width, height;
unsigned header_height;
size_t entries_end = menu_entries_get_size();
materialui_handle_t *mui = (materialui_handle_t*)userdata;
materialui_handle_t *mui = (materialui_handle_t*)userdata;
if (!mui)
return 0;
return -1;
header_height = menu_display_get_header_height();
video_driver_get_size(&width, &height);
mui->pointer_start_x = x;
mui->pointer_start_y = y;
mui->pointer_start_scroll_y = mui->scroll_y;
if (y < header_height)
return 0;
}
static int materialui_pointer_up_swipe_horz_plain_list(
materialui_handle_t *mui, menu_entry_t *entry,
unsigned height, unsigned header_height, unsigned y,
size_t selection, size_t entries_end,
bool scroll_up)
{
/* A swipe in the top half of the screen ascends/
* decends the alphabet */
if (y < (height >> 1))
{
menu_entry_t *entry_ptr = entry;
size_t new_selection = selection;
menu_entry_t new_entry;
}
else if (y > height - mui->tabs_height)
{
}
else if (ptr <= (entries_end - 1))
{
size_t ii;
file_list_t *list = menu_entries_get_selection_buf_ptr(0);
for (ii = 0; ii < entries_end; ii++)
/* Check if currently active item is off screen */
if ((selection < mui->first_onscreen_entry) ||
(selection > mui->last_onscreen_entry))
{
materialui_node_t *node = (materialui_node_t*)
file_list_get_userdata_at_offset(list, ii);
/* ...if it is, must immediately select entry
* at 'edge' of screen in opposite direction to
* scroll action */
new_selection = scroll_up ?
mui->last_onscreen_entry : mui->first_onscreen_entry;
if (y > (-mui->scroll_y + header_height + node->y)
&& y < (-mui->scroll_y + header_height + node->y + node->line_height)
)
menu_navigation_set_selection(ii);
if (new_selection < entries_end)
{
menu_navigation_set_selection(new_selection);
/* Update entry pointer */
menu_entry_init(&new_entry);
new_entry.path_enabled = false;
new_entry.label_enabled = false;
new_entry.rich_label_enabled = false;
new_entry.value_enabled = false;
new_entry.sublabel_enabled = false;
menu_entry_get(&new_entry, 0, new_selection, NULL, true);
entry_ptr = &new_entry;
}
else
new_selection = selection; /* Should never happen... */
}
return menu_entry_action(
entry_ptr, (unsigned)new_selection,
scroll_up ? MENU_ACTION_SCROLL_UP : MENU_ACTION_SCROLL_DOWN);
}
/* A swipe in the bottom half of the screen scrolls
* by 10% of the list size or one 'page', whichever
* is largest */
else
{
float content_height_fraction = mui->content_height * 0.1f;
float display_height = (int)height - (int)header_height - mui->tabs_height;
float scroll_offset = (display_height > content_height_fraction) ?
display_height : content_height_fraction;
materialui_animate_scroll(
mui,
mui->scroll_y + (scroll_up ? (scroll_offset * -1.0f) : scroll_offset),
166.0f);
}
return 0;
}
/* The pointer or the left mouse button has been released.
If we clicked on the header, we perform a cancel action.
If we clicked on the tabs, we switch to a new list.
If we clicked on a menu entry, we call the entry action callback. */
static int materialui_pointer_up_swipe_horz_default(
materialui_handle_t *mui, menu_entry_t *entry,
unsigned ptr, size_t selection, size_t entries_end, enum menu_action action)
{
int ret = 0;
if ((ptr < entries_end) && (ptr == selection))
{
size_t new_selection = menu_navigation_get_selection();
ret = menu_entry_action(entry, (unsigned)selection, action);
/* If we are changing a settings value, want to scroll
* back to the 'pointer down' position. In all other cases
* we do not. An entry is of the 'settings' type if:
* - Selection pointer remains the same after MENU_ACTION event
* - Entry has a value
* Note: cannot use input (argument) entry, since this
* will always have a blank value component */
if (selection == new_selection)
{
menu_entry_t last_entry;
menu_entry_init(&last_entry);
last_entry.path_enabled = false;
last_entry.label_enabled = false;
last_entry.rich_label_enabled = false;
last_entry.sublabel_enabled = false;
menu_entry_get(&last_entry, 0, selection, NULL, true);
if (!string_is_empty(last_entry.value))
materialui_animate_scroll(
mui,
mui->pointer_start_scroll_y,
83.0f);
}
}
return ret;
}
/* Pointer up event */
static int materialui_pointer_up(void *userdata,
unsigned x, unsigned y, unsigned ptr,
enum menu_input_pointer_gesture gesture,
@ -2370,12 +2537,11 @@ static int materialui_pointer_up(void *userdata,
case MENU_INPUT_GESTURE_TAP:
case MENU_INPUT_GESTURE_SHORT_PRESS:
{
/* Normal pointer input */
/* Tap/press header: Menu back/cancel */
if (y < header_height)
{
size_t selection = menu_navigation_get_selection();
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_CANCEL);
}
/* Tap/press tab bar: Switch to corresponding top-level
* menu screen */
else if (y > height - mui->tabs_height)
{
file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
@ -2398,33 +2564,88 @@ static int materialui_pointer_up(void *userdata,
}
}
}
else if (ptr <= (entries_end - 1))
/* Tap/press menu item: Activate and/or select item */
else if (ptr < entries_end)
{
size_t ii;
file_list_t *list = menu_entries_get_selection_buf_ptr(0);
for (ii = 0; ii < entries_end; ii++)
if (gesture == MENU_INPUT_GESTURE_TAP)
{
materialui_node_t *node = (materialui_node_t*)
file_list_get_userdata_at_offset(list, ii);
/* A 'tap' always produces a menu action */
if (y > (-mui->scroll_y + header_height + node->y)
&& y < (-mui->scroll_y + header_height + node->y + node->line_height)
)
{
if (ptr == ii && cbs && cbs->action_select)
return menu_entry_action(entry, (unsigned)ii, MENU_ACTION_SELECT);
}
/* If current 'pointer' item is not active,
* activate it immediately */
if (ptr != selection)
menu_navigation_set_selection(ptr);
/* Perform a MENU_ACTION_SELECT on currently
* active item */
return menu_entry_action(entry, ptr, MENU_ACTION_SELECT);
}
else
{
/* A 'short' press is used only to activate (highlight)
* an item - it does not invoke a MENU_ACTION_SELECT
* action (this is intended for use in activating a
* settings-type entry, prior to swiping)
* Note: If everything is working correctly, the
* ptr item should already by selected at this stage
* - but menu_navigation_set_selection() just sets a
* variable, so there's no real point in performing
* a (selection != ptr) check here */
menu_navigation_set_selection(ptr);
menu_input_set_pointer_y_accel(0.0f);
}
}
}
break;
case MENU_INPUT_GESTURE_LONG_PRESS:
/* 'Reset to default' action */
if ((ptr <= (menu_entries_get_size() - 1)) &&
(ptr == selection))
if ((ptr < entries_end) && (ptr == selection))
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_START);
break;
case MENU_INPUT_GESTURE_SWIPE_LEFT:
{
/* If we are at the top level, a swipe should
* just switch between the three main menu screens
* (i.e. we don't care which item is currently selected)
* Note: For intuitive behaviour, a *left* swipe should
* trigger a *right* navigation event */
if (materialui_list_get_size(mui, MENU_LIST_PLAIN) == 1)
return menu_entry_action(entry, (unsigned)selection, MENU_ACTION_RIGHT);
/* If we are displaying a playlist/file list/dropdown list,
* swipes are used for fast navigation */
else if (mui->is_playlist || mui->is_file_list || mui->is_dropdown_list)
return materialui_pointer_up_swipe_horz_plain_list(
mui, entry, height, header_height, y,
selection, entries_end, true);
/* In all other cases, just perform a normal 'left'
* navigation event */
else
return materialui_pointer_up_swipe_horz_default(
mui, entry, ptr, selection, entries_end, MENU_ACTION_LEFT);
}
break;
case MENU_INPUT_GESTURE_SWIPE_RIGHT:
{
/* If we are at the top level, a swipe should
* just switch between the three main menu screens
* (i.e. we don't care which item is currently selected)
* Note: For intuitive behaviour, a *right* swipe should
* trigger a *left* navigation event */
if (materialui_list_get_size(mui, MENU_LIST_PLAIN) == 1)
menu_entry_action(entry, (unsigned)selection, MENU_ACTION_LEFT);
/* If we are displaying a playlist/file list/dropdown list,
* swipes are used for fast navigation */
else if (mui->is_playlist || mui->is_file_list || mui->is_dropdown_list)
return materialui_pointer_up_swipe_horz_plain_list(
mui, entry, height, header_height, y,
selection, entries_end, false);
/* In all other cases, just perform a normal 'right'
* navigation event */
else
return materialui_pointer_up_swipe_horz_default(
mui, entry, ptr, selection, entries_end, MENU_ACTION_RIGHT);
}
break;
default:
/* Ignore input */
break;
@ -2447,8 +2668,6 @@ static void materialui_list_insert(void *userdata,
size_t list_size,
unsigned type)
{
float scale_factor;
unsigned width, height;
int i = (int)list_size;
materialui_node_t *node = NULL;
settings_t *settings = config_get_ptr();
@ -2469,10 +2688,7 @@ static void materialui_list_insert(void *userdata,
return;
}
video_driver_get_size(&width, &height);
scale_factor = menu_display_get_dpi(width, height);
node->line_height = scale_factor / 3;
node->line_height = mui->dpi_scale_factor / 3;
node->y = 0;
node->texture_switch_set = false;
node->texture_switch2_set = false;

View File

@ -2029,21 +2029,55 @@ void menu_display_unset_framebuffer_dirty_flag(void)
* RGUI or XMB use this. */
float menu_display_get_dpi(unsigned width, unsigned height)
{
#ifdef RARCH_MOBILE
float diagonal = 5.0f;
#else
float diagonal = 6.5f;
#endif
/* Generic dpi calculation formula,
* the divider is the screen diagonal in inches */
float dpi = sqrt(
(width * width) + (height * height)) / diagonal;
gfx_ctx_metrics_t metrics;
float dpi = 0.0f;
settings_t *settings = config_get_ptr();
/* Use manual override, if set */
if (settings && settings->bools.menu_dpi_override_enable)
return settings->uints.menu_dpi_override_value;
/* Attempt to fetch actual screen DPI */
metrics.type = DISPLAY_METRIC_DPI;
metrics.value = &dpi;
if (!video_context_driver_get_metrics(&metrics))
{
settings_t *settings = config_get_ptr();
if (settings && settings->bools.menu_dpi_override_enable)
return settings->uints.menu_dpi_override_value;
/* Operation failed - use fallback... */
#ifdef RARCH_MOBILE
/* For mobile platforms, use janky generic dpi
* formula, assuming a screen (diagonal) size
* of 5 inches (legacy value - will almost always
* be wrong...) */
float diagonal = 5.0f;
dpi = sqrt((width * width) + (height * height)) / diagonal;
#else
/* Well, who knows what platform this is...
* All we can do is assume a 'standard' DPI of 96 */
dpi = 96.0f;
#endif
}
#if defined(ANDROID) || defined(HAVE_COCOATOUCH)
else
{
/* Okay, Andriod is a real nuisance here...
* The reported DPI is just a binned value
* corresponding to a standard pixel density
* size (low, medium, high, extra high, etc.).
* This has nothing to do with *physical* screen
* DPI. It seems the accepted way to handle this
* is to convert the DPI into a scaling factor
* based upon the Android default of 160 - so
* we divide reported DPI by 160, and multiply
* this by our target standard DPI of 96
* EDIT: It turns out that iOS devices (iPhone,
* iPad) have almost the same idiosyncrasies as
* Android when it comes to dealing with DPI,
* so we should be able to apply the same
* conversion factor */
dpi = (dpi / 160.0f) * 96.0f;
}
#endif
return dpi;
}