From 3734ccbcc092682aaa6b1a0ce12c5290c1b90748 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Wed, 23 Oct 2019 15:34:17 +0100 Subject: [PATCH] (GLUI) Menu improvements (Round 3) --- config.def.h | 20 + configuration.c | 4 +- configuration.h | 2 + intl/msg_hash_lbl.h | 4 + intl/msg_hash_us.h | 44 ++ menu/cbs/menu_cbs_sublabel.c | 8 + menu/drivers/materialui.c | 1188 +++++++++++++++++++++++++--------- menu/menu_defines.h | 12 + menu/menu_displaylist.c | 2 + menu/menu_setting.c | 83 ++- msg_hash.h | 10 + 11 files changed, 1068 insertions(+), 309 deletions(-) diff --git a/config.def.h b/config.def.h index effd49a690..b2198c8dfe 100644 --- a/config.def.h +++ b/config.def.h @@ -114,9 +114,29 @@ #endif #ifdef HAVE_MATERIALUI +/* Show icons to the left of each menu entry */ #define DEFAULT_MATERIALUI_ICONS_ENABLE true #endif +/* Material UI colour theme */ +#define DEFAULT_MATERIALUI_THEME MATERIALUI_THEME_OZONE_DARK + +/* Type of animation to use when performing menu transitions + * > 'Auto' follows Material UI standards: + * - Slide when switching between parent menus (tabs) + * - Fade when changing levels in a menu + * Note: Not wrapping this with a HAVE_MATERIALUI ifdef + * because there's too much baggage involved... */ +#define DEFAULT_MATERIALUI_TRANSITION_ANIM MATERIALUI_TRANSITION_ANIM_AUTO + +/* Adjust menu padding etc. to better fit the + * screen when using landscape layouts */ +#if defined(RARCH_MOBILE) +#define DEFAULT_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT false +#else +#define DEFAULT_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT true +#endif + #define DEFAULT_CRT_SWITCH_RESOLUTION CRT_SWITCH_NONE #define DEFAULT_CRT_SWITCH_RESOLUTION_SUPER 2560 diff --git a/configuration.c b/configuration.c index 5db9f819d9..0913fc6aa5 100644 --- a/configuration.c +++ b/configuration.c @@ -1556,6 +1556,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("menu_show_advanced_settings", &settings->bools.menu_show_advanced_settings, true, DEFAULT_SHOW_ADVANCED_SETTINGS, false); #ifdef HAVE_MATERIALUI SETTING_BOOL("materialui_icons_enable", &settings->bools.menu_materialui_icons_enable, true, DEFAULT_MATERIALUI_ICONS_ENABLE, false); + SETTING_BOOL("materialui_optimize_landscape_layout", &settings->bools.menu_materialui_optimize_landscape_layout, true, DEFAULT_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT, false); #endif #ifdef HAVE_RGUI SETTING_BOOL("rgui_background_filler_thickness_enable", &settings->bools.menu_rgui_background_filler_thickness_enable, true, true, false); @@ -1808,7 +1809,8 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("menu_font_color_blue", &settings->uints.menu_font_color_blue, true, menu_font_color_blue, false); SETTING_UINT("menu_xmb_thumbnail_scale_factor", &settings->uints.menu_xmb_thumbnail_scale_factor, true, xmb_thumbnail_scale_factor, false); #endif - SETTING_UINT("materialui_menu_color_theme", &settings->uints.menu_materialui_color_theme, true, MATERIALUI_THEME_BLUE, false); + SETTING_UINT("materialui_menu_color_theme", &settings->uints.menu_materialui_color_theme, true, DEFAULT_MATERIALUI_THEME, false); + SETTING_UINT("materialui_menu_transition_animation", &settings->uints.menu_materialui_transition_animation, true, DEFAULT_MATERIALUI_TRANSITION_ANIM, false); SETTING_UINT("menu_shader_pipeline", &settings->uints.menu_xmb_shader_pipeline, true, DEFAULT_MENU_SHADER_PIPELINE, false); #ifdef HAVE_OZONE SETTING_UINT("ozone_menu_color_theme", &settings->uints.menu_ozone_color_theme, true, 1, false); diff --git a/configuration.h b/configuration.h index b163d1edd7..88a5579d33 100644 --- a/configuration.h +++ b/configuration.h @@ -186,6 +186,7 @@ typedef struct settings bool menu_show_video_layout; #endif bool menu_materialui_icons_enable; + bool menu_materialui_optimize_landscape_layout; bool menu_rgui_background_filler_thickness_enable; bool menu_rgui_border_filler_thickness_enable; bool menu_rgui_border_filler_enable; @@ -532,6 +533,7 @@ typedef struct settings unsigned menu_xmb_color_theme; unsigned menu_xmb_thumbnail_scale_factor; unsigned menu_materialui_color_theme; + unsigned menu_materialui_transition_animation; unsigned menu_ozone_color_theme; unsigned menu_font_color_red; unsigned menu_font_color_green; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index aa24b37f5b..8a500da076 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -727,6 +727,8 @@ MSG_HASH(MENU_ENUM_LABEL_OZONE_SCROLL_CONTENT_METADATA, "ozone_scroll_content_metadata") MSG_HASH(MENU_ENUM_LABEL_MATERIALUI_MENU_COLOR_THEME, "materialui_menu_color_theme") +MSG_HASH(MENU_ENUM_LABEL_MATERIALUI_MENU_TRANSITION_ANIMATION, + "materialui_menu_transition_animation") MSG_HASH(MENU_ENUM_LABEL_MATERIALUI_MENU_FOOTER_OPACITY, "materialui_menu_footer_opacity") MSG_HASH(MENU_ENUM_LABEL_MATERIALUI_MENU_HEADER_OPACITY, @@ -1663,6 +1665,8 @@ MSG_HASH(MENU_ENUM_LABEL_GOTO_VIDEO, "goto_video") MSG_HASH(MENU_ENUM_LABEL_MATERIALUI_ICONS_ENABLE, "materialui_icons_enable") +MSG_HASH(MENU_ENUM_LABEL_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT, + "materialui_optimize_landscape_layout") MSG_HASH(MENU_ENUM_LABEL_RENAME_ENTRY, "rename_entry") MSG_HASH(MENU_ENUM_LABEL_MENU_SHOW_LOAD_CORE, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 41f9caef4b..de18f23534 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1746,6 +1746,42 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_OZONE_DARK, "Ozone Dark" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_NORD, + "Nord" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_GRUVBOX_DARK, + "Gruvbox Dark" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_SOLARIZED_DARK, + "Solarized Dark" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIMATION, + "Menu Transition Animation" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MATERIALUI_MENU_TRANSITION_ANIMATION, + "Enable smooth animation effects when navigating between different levels of the menu." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_AUTO, + "Auto" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_FADE, + "Fade" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_SLIDE, + "Slide" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_NONE, + "OFF" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_FOOTER_OPACITY, "Footer Opacity" @@ -6895,6 +6931,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_MATERIALUI_ICONS_ENABLE, "Show icons at the left of the menu entries." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT, + "Optimize Landscape Layout" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT, + "Automatically adjust menu layout to better fit the screen when using landscape display orientations." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_XMB_MAIN_MENU_ENABLE_SETTINGS, "Settings Tab" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 0bd2558ccc..0e80830aed 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -198,6 +198,7 @@ default_sublabel_macro(action_bind_sublabel_netplay_settings, MENU_ default_sublabel_macro(action_bind_sublabel_user_bind_settings, MENU_ENUM_SUBLABEL_INPUT_USER_BINDS) default_sublabel_macro(action_bind_sublabel_input_hotkey_settings, MENU_ENUM_SUBLABEL_INPUT_HOTKEY_BINDS) default_sublabel_macro(action_bind_sublabel_materialui_icons_enable, MENU_ENUM_SUBLABEL_MATERIALUI_ICONS_ENABLE) +default_sublabel_macro(action_bind_sublabel_materialui_optimize_landscape_layout, MENU_ENUM_SUBLABEL_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT) default_sublabel_macro(action_bind_sublabel_add_content_list, MENU_ENUM_SUBLABEL_ADD_CONTENT_LIST) default_sublabel_macro(action_bind_sublabel_video_frame_delay, MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY) default_sublabel_macro(action_bind_sublabel_video_shader_delay, MENU_ENUM_SUBLABEL_VIDEO_SHADER_DELAY) @@ -486,6 +487,7 @@ default_sublabel_macro(action_bind_sublabel_xmb_shadows_enable, default_sublabel_macro(action_bind_sublabel_xmb_vertical_thumbnails, MENU_ENUM_SUBLABEL_XMB_VERTICAL_THUMBNAILS) default_sublabel_macro(action_bind_sublabel_menu_xmb_thumbnail_scale_factor, MENU_ENUM_SUBLABEL_MENU_XMB_THUMBNAIL_SCALE_FACTOR) default_sublabel_macro(action_bind_sublabel_menu_color_theme, MENU_ENUM_SUBLABEL_MATERIALUI_MENU_COLOR_THEME) +default_sublabel_macro(action_bind_sublabel_materialui_menu_transition_animation, MENU_ENUM_SUBLABEL_MATERIALUI_MENU_TRANSITION_ANIMATION) default_sublabel_macro(action_bind_sublabel_ozone_menu_color_theme, MENU_ENUM_SUBLABEL_OZONE_MENU_COLOR_THEME) default_sublabel_macro(action_bind_sublabel_ozone_collapse_sidebar, MENU_ENUM_SUBLABEL_OZONE_COLLAPSE_SIDEBAR) default_sublabel_macro(action_bind_sublabel_ozone_truncate_playlist_name, MENU_ENUM_SUBLABEL_OZONE_TRUNCATE_PLAYLIST_NAME) @@ -1249,6 +1251,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_MATERIALUI_ICONS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_materialui_icons_enable); break; + case MENU_ENUM_LABEL_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_materialui_optimize_landscape_layout); + break; case MENU_ENUM_LABEL_VIDEO_VIEWPORT_CUSTOM_HEIGHT: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_viewport_custom_height); break; @@ -1709,6 +1714,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_XMB_MENU_COLOR_THEME: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_color_theme); break; + case MENU_ENUM_LABEL_MATERIALUI_MENU_TRANSITION_ANIMATION: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_materialui_menu_transition_animation); + break; case MENU_ENUM_LABEL_XMB_SHADOWS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_xmb_shadows_enable); break; diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 82532be020..8b14e5931f 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -63,6 +63,16 @@ * 22 inch, 96 DPI display */ #define MUI_DIP_BASE_UNIT_SIZE 212.0f +/* Spacer for left scrolling ticker text */ +#if defined(__APPLE__) +/* UTF-8 support is currently broken on Apple devices... */ +#define MUI_TICKER_SPACER " | " +#else +/* + * UCN equivalent: "\u2003\u2022\u2003" */ +#define MUI_TICKER_SPACER "\xE2\x80\x83\xE2\x80\xA2\xE2\x80\x83" +#endif + /* ============================== * Colour Themes START * ============================== */ @@ -95,11 +105,13 @@ typedef struct uint32_t nav_bar_icon_passive; uint32_t nav_bar_icon_disabled; /* Misc. colours */ - uint32_t shadow; + uint32_t header_shadow; + uint32_t landscape_border_shadow; uint32_t scrollbar; uint32_t divider; uint32_t screen_fade; - float shadow_opacity; + float header_shadow_opacity; + float landscape_border_shadow_opacity; float screen_fade_opacity; } materialui_theme_t; @@ -129,11 +141,13 @@ static const materialui_theme_t materialui_theme_blue = { 0x9ea7aa, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ 0x0069c0, /* scrollbar */ 0x9ea7aa, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.35f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -163,11 +177,13 @@ static const materialui_theme_t materialui_theme_blue_grey = { 0xaeaeae, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ 0x34515e, /* scrollbar */ 0xc2c2c2, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.35f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -197,11 +213,13 @@ static const materialui_theme_t materialui_theme_dark_blue = { 0x8eacbb, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x3B3B3B, /* landscape_border_shadow */ 0x90caf9, /* scrollbar */ 0x607d8b, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.45f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -231,11 +249,13 @@ static const materialui_theme_t materialui_theme_green = { 0xaeaeae, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ 0x087f23, /* scrollbar */ 0xaabb97, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.35f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -265,11 +285,13 @@ static const materialui_theme_t materialui_theme_red = { 0xaeaeae, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ 0xba000d, /* scrollbar */ 0xbf5f82, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.35f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -299,11 +321,13 @@ static const materialui_theme_t materialui_theme_yellow = { 0xaeaeae, /* nav_bar_icon_passive */ 0xFFFFFF, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ 0xc6a700, /* scrollbar */ 0xcbba83, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.35f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -333,11 +357,13 @@ static const materialui_theme_t materialui_theme_nvidia_shield = { 0x558b2f, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x3B3B3B, /* landscape_border_shadow */ 0x7ab547, /* scrollbar */ 0x498515, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.45f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -353,7 +379,7 @@ static const materialui_theme_t materialui_theme_materialui = { 0x3700B3, /* sys_bar_background */ 0x6200ee, /* title_bar_background */ 0xF5F5F6, /* list_background */ - 0xF2E7FE, /* list_highlighted_background */ + 0xe7b9ff, /* list_highlighted_background */ 0xE1E2E1, /* nav_bar_background */ 0xFFFFFF, /* surface_background */ /* List icon colours */ @@ -367,11 +393,13 @@ static const materialui_theme_t materialui_theme_materialui = { 0xaeaeae, /* nav_bar_icon_passive */ 0xffffff, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ 0x018786, /* scrollbar */ 0x018786, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.35f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -401,11 +429,13 @@ static const materialui_theme_t materialui_theme_materialui_dark = { 0x00a895, /* nav_bar_icon_passive */ 0x000000, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x3B3B3B, /* landscape_border_shadow */ 0xC89EFC, /* scrollbar */ 0x03DAC6, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.45f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -435,11 +465,121 @@ static const materialui_theme_t materialui_theme_ozone_dark = { 0xDADADA, /* nav_bar_icon_passive */ 0x242424, /* nav_bar_icon_disabled */ /* Misc. colours */ - 0x000000, /* shadow */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ 0xFFFFFF, /* scrollbar */ 0xFFFFFF, /* divider */ 0x000000, /* screen_fade */ - 0.3f, /* shadow_opacity */ + 0.3f, /* header_shadow_opacity */ + 0.45f, /* landscape_border_shadow_opacity */ + 0.75f /* screen_fade_opacity */ +}; + +static const materialui_theme_t materialui_theme_nord = { + /* Text (& small inline icon) colours */ + 0xD8DEE9, /* on_sys_bar */ + 0xECEFF4, /* on_header */ + 0xD8DEE9, /* list_text */ + 0xECEFF4, /* list_text_highlighted */ + 0x93E5CC, /* list_hint_text */ + 0x93E5CC, /* list_hint_text_highlighted */ + /* Background colours */ + 0x000000, /* sys_bar_background */ + 0x4C566A, /* title_bar_background */ + 0x2E3440, /* list_background */ + 0x3f444f, /* list_highlighted_background */ + 0x3B4252, /* nav_bar_background */ + 0x3B4252, /* surface_background */ + /* List icon colours */ + 0xD8DEE9, /* list_icon */ + 0xA3BE8C, /* list_switch_on */ + 0x7E946D, /* list_switch_on_background */ + 0xB48EAD, /* list_switch_off */ + 0x8A6D84, /* list_switch_off_background */ + /* Navigation bar icon colours */ + 0xD8DEE9, /* nav_bar_icon_active */ + 0x81A1C1, /* nav_bar_icon_passive */ + 0x242A33, /* nav_bar_icon_disabled */ + /* Misc. colours */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ + 0xA0A5AD, /* scrollbar */ + 0x81A1C1, /* divider */ + 0x000000, /* screen_fade */ + 0.4f, /* header_shadow_opacity */ + 0.45f, /* landscape_border_shadow_opacity */ + 0.75f /* screen_fade_opacity */ +}; + +static const materialui_theme_t materialui_theme_gruvbox_dark = { + /* Text (& small inline icon) colours */ + 0xA89984, /* on_sys_bar */ + 0xFBF1C7, /* on_header */ + 0xEBDBB2, /* list_text */ + 0xFBF1C7, /* list_text_highlighted */ + 0xD79921, /* list_hint_text */ + 0xFABD2F, /* list_hint_text_highlighted */ + /* Background colours */ + 0x000000, /* sys_bar_background */ + 0x504945, /* title_bar_background */ + 0x282828, /* list_background */ + 0x3C3836, /* list_highlighted_background */ + 0x1D2021, /* nav_bar_background */ + 0x32302F, /* surface_background */ + /* List icon colours */ + 0xA89984, /* list_icon */ + 0xB8BB26, /* list_switch_on */ + 0x98971A, /* list_switch_on_background */ + 0xFB4934, /* list_switch_off */ + 0xCC241D, /* list_switch_off_background */ + /* Navigation bar icon colours */ + 0xBF9137, /* nav_bar_icon_active */ + 0xA89984, /* nav_bar_icon_passive */ + 0x504945, /* nav_bar_icon_disabled */ + /* Misc. colours */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ + 0x7C6F64, /* scrollbar */ + 0xD5C4A1, /* divider */ + 0x000000, /* screen_fade */ + 0.4f, /* header_shadow_opacity */ + 0.45f, /* landscape_border_shadow_opacity */ + 0.75f /* screen_fade_opacity */ +}; + +static const materialui_theme_t materialui_theme_solarized_dark = { + /* Text (& small inline icon) colours */ + 0x657B83, /* on_sys_bar */ + 0x93A1A1, /* on_header */ + 0x839496, /* list_text */ + 0x93A1A1, /* list_text_highlighted */ + 0x2AA198, /* list_hint_text */ + 0x2AA198, /* list_hint_text_highlighted */ + /* Background colours */ + 0x000000, /* sys_bar_background */ + 0x053542, /* title_bar_background */ + 0x002B36, /* list_background */ + 0x073642, /* list_highlighted_background */ + 0x003541, /* nav_bar_background */ + 0x073642, /* surface_background */ + /* List icon colours */ + 0x657B83, /* list_icon */ + 0x859900, /* list_switch_on */ + 0x667500, /* list_switch_on_background */ + 0x6C71C4, /* list_switch_off */ + 0x565A9C, /* list_switch_off_background */ + /* Navigation bar icon colours */ + 0x839496, /* nav_bar_icon_active */ + 0x2AA198, /* nav_bar_icon_passive */ + 0x00222B, /* nav_bar_icon_disabled */ + /* Misc. colours */ + 0x000000, /* header_shadow */ + 0x000000, /* landscape_border_shadow */ + 0x586E75, /* scrollbar */ + 0x2AA198, /* divider */ + 0x000000, /* screen_fade */ + 0.4f, /* header_shadow_opacity */ + 0.45f, /* landscape_border_shadow_opacity */ 0.75f /* screen_fade_opacity */ }; @@ -473,10 +613,13 @@ typedef struct float nav_bar_icon_passive[16]; float nav_bar_icon_disabled[16]; /* Misc. colours */ - float shadow[16]; + float header_shadow[16]; + float landscape_border_shadow_left[16]; + float landscape_border_shadow_right[16]; float scrollbar[16]; float divider[16]; float screen_fade[16]; + float landscape_border_shadow_opacity; } materialui_colors_t; /* ============================== @@ -611,13 +754,29 @@ typedef struct int timedate_width; } materialui_sys_bar_cache_t; +/* Animation defines */ +#define MUI_ANIM_DURATION_SCROLL 166.66667f +#define MUI_ANIM_DURATION_SCROLL_RESET 83.333333f +/* According to Material UI specifications, animations + * that affect a large portion of the screen should + * have a duration of between 250ms and 300ms. This + * should therefore be the value used for menu + * transitions - but even 250ms feels too slow... + * We compromise by setting a time of 200ms, which + * is the same as the 'short press' duration. + * This is reasonably fast, without making slide + * animations too 'jarring'... */ +#define MUI_ANIM_DURATION_MENU_TRANSITION 200.0f + typedef struct materialui_handle { + bool is_portrait; bool need_compute; bool mouse_show; bool is_playlist; bool is_file_list; bool is_dropdown_list; + bool last_optimize_landscape_layout; unsigned last_width; unsigned last_height; @@ -629,19 +788,22 @@ typedef struct materialui_handle unsigned sys_bar_height; unsigned title_bar_height; unsigned tabs_height; - unsigned shadow_height; + unsigned header_shadow_height; unsigned scrollbar_width; unsigned divider_width; unsigned icon_size; unsigned sys_bar_icon_size; unsigned margin; unsigned sys_bar_margin; + unsigned landscape_entry_margin; unsigned categories_active_idx; unsigned categories_active_idx_old; size_t categories_selection_ptr; size_t categories_selection_ptr_old; + bool categories_navigation_wrapped; + size_t first_onscreen_entry; size_t last_onscreen_entry; @@ -651,7 +813,7 @@ typedef struct materialui_handle float textures_arrow_alpha; float categories_x_pos; - char *box_message; + char msgbox[1024]; char menu_title[255]; @@ -696,6 +858,11 @@ typedef struct materialui_handle unsigned touch_feedback_selection; float touch_feedback_alpha; + /* Menu transition animation parameters */ + float transition_alpha; + float transition_x_offset; + size_t last_stack_size; + } materialui_handle_t; static const materialui_theme_t *materialui_get_theme(enum materialui_color_theme color_theme) @@ -722,6 +889,12 @@ static const materialui_theme_t *materialui_get_theme(enum materialui_color_them return &materialui_theme_materialui_dark; case MATERIALUI_THEME_OZONE_DARK: return &materialui_theme_ozone_dark; + case MATERIALUI_THEME_NORD: + return &materialui_theme_nord; + case MATERIALUI_THEME_GRUVBOX_DARK: + return &materialui_theme_gruvbox_dark; + case MATERIALUI_THEME_SOLARIZED_DARK: + return &materialui_theme_solarized_dark; default: break; } @@ -802,8 +975,14 @@ static void materialui_prepare_colors( /* > Misc. colours */ hex32_to_rgba_normalized( - current_theme->shadow, - mui->colors.shadow, 0.0f); + current_theme->header_shadow, + mui->colors.header_shadow, 0.0f); + hex32_to_rgba_normalized( + current_theme->landscape_border_shadow, + mui->colors.landscape_border_shadow_left, 0.0f); + hex32_to_rgba_normalized( + current_theme->landscape_border_shadow, + mui->colors.landscape_border_shadow_right, 0.0f); hex32_to_rgba_normalized( current_theme->scrollbar, mui->colors.scrollbar, 1.0f); @@ -814,10 +993,15 @@ static void materialui_prepare_colors( current_theme->screen_fade, mui->colors.screen_fade, current_theme->screen_fade_opacity); - /* Shadow colour requires special handling - * (since it is a gradient) */ - mui->colors.shadow[11] = current_theme->shadow_opacity; - mui->colors.shadow[15] = current_theme->shadow_opacity; + /* Shadow colours require special handling + * (since they are gradients) */ + mui->colors.header_shadow[11] = current_theme->header_shadow_opacity; + mui->colors.header_shadow[15] = current_theme->header_shadow_opacity; + mui->colors.landscape_border_shadow_left[7] = current_theme->landscape_border_shadow_opacity; + mui->colors.landscape_border_shadow_left[15] = current_theme->landscape_border_shadow_opacity; + mui->colors.landscape_border_shadow_right[3] = current_theme->landscape_border_shadow_opacity; + mui->colors.landscape_border_shadow_right[11] = current_theme->landscape_border_shadow_opacity; + mui->colors.landscape_border_shadow_opacity = current_theme->landscape_border_shadow_opacity; } static const char *materialui_texture_path(unsigned id) @@ -1102,38 +1286,6 @@ static void materialui_draw_tab_end(materialui_handle_t *mui, mui->colors.nav_bar_icon_active); } -/* Draw the scrollbar */ -static void materialui_draw_scrollbar(materialui_handle_t *mui, - video_frame_info_t *video_info, - unsigned width, unsigned height) -{ - unsigned header_height = menu_display_get_header_height(); - float total_height = height - header_height - mui->tabs_height; - float scrollbar_margin = mui->scrollbar_width; - float scrollbar_height = total_height / (mui->content_height / total_height); - float y = total_height * mui->scroll_y / mui->content_height; - - /* apply a margin on the top and bottom of the scrollbar for aestetic */ - scrollbar_height -= scrollbar_margin * 2; - y += scrollbar_margin; - - if (mui->content_height < total_height) - return; - - /* if the scrollbar is extremely short, display it as a square */ - if (scrollbar_height <= mui->scrollbar_width) - scrollbar_height = mui->scrollbar_width; - - menu_display_draw_quad( - video_info, - width - mui->scrollbar_width - scrollbar_margin, - header_height + y, - mui->scrollbar_width, - scrollbar_height, - width, height, - mui->colors.scrollbar); -} - static void materialui_get_message(void *data, const char *message) { materialui_handle_t *mui = (materialui_handle_t*)data; @@ -1141,50 +1293,60 @@ static void materialui_get_message(void *data, const char *message) if (!mui || !message || !*message) return; - if (!string_is_empty(mui->box_message)) - free(mui->box_message); - mui->box_message = strdup(message); + mui->msgbox[0] = '\0'; + + if (!string_is_empty(message)) + strlcpy(mui->msgbox, message, sizeof(mui->msgbox)); } -/* Draw the modal */ static void materialui_render_messagebox(materialui_handle_t *mui, - video_frame_info_t *video_info, const char *message) + video_frame_info_t *video_info, int y_centre, const char *message) { - unsigned i, y_position; - int x, y, longest = 0, longest_width = 0; + unsigned i; + int x = 0; + int y = 0; + int longest_width = 0; + size_t longest_len = 0; unsigned width = video_info->width; unsigned height = video_info->height; struct string_list *list = NULL; + /* Sanity check */ if (!mui || !mui->font_data.list.font) goto end; - list = (struct string_list*) - string_split(message, "\n"); + /* Split message into lines */ + list = string_split(message, "\n"); if (!list || list->elems == 0) goto end; - y_position = height / 2; - if (menu_input_dialog_get_display_kb()) - y_position = height / 4; + /* Get coordinates of message box centre */ + x = width / 2; + y = (int)(y_centre - (list->size - 1) * (mui->font_data.list.font_height / 2)); - x = width / 2; - y = (int)(y_position - (list->size-1) * mui->font_data.list.font_height / 2); + /* TODO/FIXME: Reduce text scale if width or height + * are too large to fit on screen */ - /* find the longest line width */ + /* Find the longest line width */ for (i = 0; i < list->size; i++) { - const char *msg = list->elems[i].data; - int len = (int)utf8len(msg); - if (len > longest) + const char *line = list->elems[i].data; + + if (!string_is_empty(line)) { - longest = len; - longest_width = font_driver_get_message_width( - mui->font_data.list.font, msg, (unsigned)strlen(msg), 1); + size_t len = utf8len(line); + + if (len > longest_len) + { + longest_len = len; + longest_width = font_driver_get_message_width( + mui->font_data.list.font, line, (unsigned)strlen(line), 1); + } } } + /* Draw message box background */ menu_display_draw_quad( video_info, x - longest_width / 2.0 - mui->margin * 2.0, @@ -1195,27 +1357,20 @@ static void materialui_render_messagebox(materialui_handle_t *mui, height, mui->colors.surface_background); - /* print each line */ + /* Print each line of the message */ for (i = 0; i < list->size; i++) { - const char *msg = list->elems[i].data; - if (msg) + const char *line = list->elems[i].data; + + if (!string_is_empty(line)) menu_display_draw_text( - mui->font_data.list.font, msg, + mui->font_data.list.font, line, x - longest_width/2.0, y + i * mui->font_data.list.font_height + mui->font_data.list.font_height / 3, width, height, mui->colors.list_text, - TEXT_ALIGN_LEFT, 1.0f, false, 0, false); + TEXT_ALIGN_LEFT, 1.0f, false, 0, true); } - if (menu_input_dialog_get_display_kb()) - menu_display_draw_keyboard( - mui->textures.list[MUI_TEXTURE_KEY_HOVER], - mui->font_data.list.font, - video_info, - menu_event_get_osk_grid(), menu_event_get_osk_ptr(), - 0xffffffff); - end: if (list) string_list_free(list); @@ -1237,7 +1392,7 @@ static void materialui_compute_entries_box(materialui_handle_t* mui, int width, int height) { unsigned i; - size_t usable_width = width - (mui->margin * 2); + size_t usable_width = width - (mui->margin * 2) - (mui->landscape_entry_margin * 2); file_list_t *list = menu_entries_get_selection_buf_ptr(0); float sum = 0; size_t entries_end = menu_entries_get_size(); @@ -1298,10 +1453,6 @@ static float materialui_get_scroll(materialui_handle_t *mui) if (!mui) return 0; - /* Whenever we perform a 'manual' scroll, scroll - * acceleration must be reset */ - menu_input_set_pointer_y_accel(0.0f); - video_driver_get_size(&width, &height); half = height / 2; @@ -1332,6 +1483,7 @@ static void materialui_render(void *data, unsigned width, unsigned height, bool is_idle) { + settings_t *settings = config_get_ptr(); materialui_handle_t *mui = (materialui_handle_t*)data; int header_height = menu_display_get_header_height(); size_t entries_end = menu_entries_get_size(); @@ -1341,30 +1493,30 @@ static void materialui_render(void *data, int bottom; float scale_factor; - if (!mui || !list) + if (!settings || !mui || !list) return; - /* Check whether menu scale factor has changed */ + /* Check whether screen dimensions, menu scale + * factor or layout optimisation settings have changed */ scale_factor = menu_display_get_dpi_scale(width, height); - if (scale_factor != mui->last_scale_factor) - { - mui->dip_base_unit_size = scale_factor * MUI_DIP_BASE_UNIT_SIZE; - mui->last_scale_factor = scale_factor; - materialui_context_reset_internal(mui, video_driver_is_threaded()); - } - /* Check whether screen dimensions have changed - * (this can happen without changing the scaling factor, - * and it affects list spacing) */ - if ((width != mui->last_width) || (height != mui->last_height)) + if ((scale_factor != mui->last_scale_factor) || + (width != mui->last_width) || + (height != mui->last_height) || + (settings->bools.menu_materialui_optimize_landscape_layout != mui->last_optimize_landscape_layout)) { - mui->need_compute = true; - mui->last_width = width; - mui->last_height = height; + mui->dip_base_unit_size = scale_factor * MUI_DIP_BASE_UNIT_SIZE; + mui->last_scale_factor = scale_factor; + mui->last_width = width; + mui->last_height = height; + mui->last_optimize_landscape_layout = settings->bools.menu_materialui_optimize_landscape_layout; + materialui_context_reset_internal(mui, video_driver_is_threaded()); } if (mui->need_compute) { + menu_animation_ctx_tag tag = (uintptr_t)&mui->scroll_y; + if (mui->font_data.list.font && mui->font_data.hint.font) materialui_compute_entries_box(mui, width, height); @@ -1374,8 +1526,15 @@ static void materialui_render(void *data, * 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); + /* Kill any existing scroll animation */ + menu_animation_kill_by_tag(&tag); + + /* Reset scroll accleration */ + menu_input_set_pointer_y_accel(0.0f); + + /* Get new scroll position */ + mui->scroll_y = materialui_get_scroll(mui); mui->need_compute = false; } @@ -1433,30 +1592,37 @@ static void materialui_render(void *data, /* Track pointer input, if required */ if (first_entry_found && (mui->pointer.type != MENU_POINTER_DISABLED)) { + int16_t pointer_x = mui->pointer.x; int16_t pointer_y = mui->pointer.y; - if ((pointer_y > entry_y) && - (pointer_y < (entry_y + node->line_height))) + if ((pointer_x > mui->landscape_entry_margin) && + (pointer_x < width - mui->landscape_entry_margin) && + (pointer_y >= header_height) && + (pointer_y <= height - mui->tabs_height)) { - menu_input_set_pointer_selection(i); - - /* The first time this runs following a pointer - * down event, we have to cache the current - * pointer selection value in order to correctly - * handle touch feedback animations */ - if (mui->touch_feedback_cache_selection) + if ((pointer_y > entry_y) && + (pointer_y < (entry_y + node->line_height))) { - mui->touch_feedback_selection = i; - mui->touch_feedback_cache_selection = false; - } + 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); + /* The first time this runs following a pointer + * down event, we have to cache the current + * pointer selection value in order to correctly + * handle touch feedback animations */ + if (mui->touch_feedback_cache_selection) + { + mui->touch_feedback_selection = i; + mui->touch_feedback_cache_selection = false; + } + + /* 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); + } } } @@ -1541,7 +1707,7 @@ static void materialui_render_switch_icon( materialui_handle_t *mui, video_frame_info_t *video_info, float y, - unsigned width, unsigned height, + unsigned width, unsigned height, int x_offset, bool on) { unsigned switch_texture_index = on ? @@ -1550,13 +1716,15 @@ static void materialui_render_switch_icon( mui->colors.list_switch_on_background : mui->colors.list_switch_off_background; float *switch_color = on ? mui->colors.list_switch_on : mui->colors.list_switch_off; + int x = + x_offset + width - (int)mui->margin - (int)mui->landscape_entry_margin - (int)mui->icon_size; /* Draw background */ if (mui->textures.list[MUI_TEXTURE_SWITCH_BG]) materialui_draw_icon(video_info, mui->icon_size, mui->textures.list[MUI_TEXTURE_SWITCH_BG], - width - mui->margin - mui->icon_size, + x, y, width, height, @@ -1569,7 +1737,7 @@ static void materialui_render_switch_icon( materialui_draw_icon(video_info, mui->icon_size, mui->textures.list[switch_texture_index], - width - mui->margin - mui->icon_size, + x, y, width, height, @@ -1587,7 +1755,8 @@ static void materialui_render_menu_entry( bool entry_selected, bool touch_feedback_active, unsigned header_height, - unsigned width, unsigned height) + unsigned width, unsigned height, + int x_offset) { const char *entry_value = NULL; const char *entry_label = NULL; @@ -1597,11 +1766,12 @@ static void materialui_render_menu_entry( size_t entry_value_width = 0; enum msg_file_type entry_file_type = FILE_TYPE_NONE; int entry_y = header_height - mui->scroll_y + node->y; - int entry_margin = mui->margin; - int usable_width = width - (mui->margin * 2); + int entry_margin = (int)mui->margin + (int)mui->landscape_entry_margin; + int usable_width = (int)width - (int)(mui->margin * 2) - (int)(mui->landscape_entry_margin * 2); int label_y = 0; int value_icon_y = 0; uintptr_t icon_texture = 0; + bool draw_text_outside = (x_offset != 0); /* Initial ticker configuration * > Note: ticker is only used for labels/values, @@ -1622,7 +1792,7 @@ static void materialui_render_menu_entry( entry_file_type = msg_hash_to_file_type(msg_hash_calculate(entry_value)); entry_value_type = materialui_get_entry_value_type( - mui, entry_value, entry->checked, entry_type, entry_file_type); + mui, entry_value, entry->checked, entry_type, entry_file_type); /* Draw entry icon * > Has to be done first, since it affects the left @@ -1656,7 +1826,7 @@ static void materialui_render_menu_entry( materialui_draw_icon(video_info, mui->icon_size, (uintptr_t)icon_texture, - 0, + x_offset + (int)mui->landscape_entry_margin, entry_y + (node->line_height / 2.0f) - (mui->icon_size / 2.0f), width, height, @@ -1684,12 +1854,12 @@ static void materialui_render_menu_entry( /* Draw sublabel string */ menu_display_draw_text(mui->font_data.hint.font, wrapped_sublabel, - entry_margin, + x_offset + entry_margin, entry_y + (mui->dip_base_unit_size / 5) + mui->font_data.list.font_height, width, height, (entry_selected || touch_feedback_active) ? mui->colors.list_hint_text_highlighted : mui->colors.list_hint_text, - TEXT_ALIGN_LEFT, 1.0f, false, 0, false); + TEXT_ALIGN_LEFT, 1.0f, false, 0, draw_text_outside); /* If we have a sublabel, entry label y position has a * fixed vertical offset */ @@ -1754,25 +1924,25 @@ static void materialui_render_menu_entry( /* Draw value string */ menu_display_draw_text(mui->font_data.list.font, value_buf, - value_x_offset + width - mui->margin, + (int)(x_offset + value_x_offset + width - mui->margin - mui->landscape_entry_margin), label_y, width, height, (entry_selected || touch_feedback_active) ? mui->colors.list_text_highlighted : mui->colors.list_text, - TEXT_ALIGN_RIGHT, 1.0f, false, 0, false); + TEXT_ALIGN_RIGHT, 1.0f, false, 0, draw_text_outside); } break; case MUI_ENTRY_VALUE_SWITCH_ON: { materialui_render_switch_icon( - mui, video_info, value_icon_y, width, height, true); + mui, video_info, value_icon_y, width, height, x_offset, true); entry_value_width = mui->icon_size; } break; case MUI_ENTRY_VALUE_SWITCH_OFF: { materialui_render_switch_icon( - mui, video_info, value_icon_y, width, height, false); + mui, video_info, value_icon_y, width, height, x_offset, false); entry_value_width = mui->icon_size; } break; @@ -1783,7 +1953,7 @@ static void materialui_render_menu_entry( materialui_draw_icon(video_info, mui->icon_size, mui->textures.list[MUI_TEXTURE_CHECKMARK], - width - mui->margin - mui->icon_size, + (int)(x_offset + width - mui->margin - mui->landscape_entry_margin - mui->icon_size), value_icon_y, width, height, @@ -1838,21 +2008,63 @@ static void materialui_render_menu_entry( /* Draw label string */ menu_display_draw_text(mui->font_data.list.font, label_buf, - mui->ticker_x_offset + entry_margin, + x_offset + (int)mui->ticker_x_offset + entry_margin, label_y, width, height, (entry_selected || touch_feedback_active) ? mui->colors.list_text_highlighted : mui->colors.list_text, - TEXT_ALIGN_LEFT, 1.0f, false, 0, false); + TEXT_ALIGN_LEFT, 1.0f, false, 0, draw_text_outside); } } } +static void materialui_render_scrollbar( + materialui_handle_t *mui, + video_frame_info_t *video_info, + unsigned header_height, + unsigned width, unsigned height, + int x_offset) +{ + float total_height = height - header_height - mui->tabs_height; + float scrollbar_margin = mui->scrollbar_width; + float scrollbar_height = total_height / (mui->content_height / total_height); + float y = total_height * mui->scroll_y / mui->content_height; + int x; + + if (mui->content_height < total_height) + return; + + /* Apply a margin on the top and bottom of the scrollbar + * for aesthetic reasons */ + scrollbar_height -= scrollbar_margin * 2; + y += scrollbar_margin; + + /* If the scrollbar is extremely short, display + * it as a square */ + if (scrollbar_height < mui->scrollbar_width) + scrollbar_height = mui->scrollbar_width; + + /* Get x position */ + x = x_offset + (int)width - (int)mui->scrollbar_width - scrollbar_margin; + if (mui->landscape_entry_margin > mui->margin) + x -= (int)mui->landscape_entry_margin - (int)mui->margin; + + menu_display_draw_quad( + video_info, + x, + header_height + y, + mui->scrollbar_width, + scrollbar_height, + width, height, + mui->colors.scrollbar); +} + /* Draws current menu list */ static void materialui_render_menu_list( materialui_handle_t *mui, video_frame_info_t *video_info, - unsigned width, unsigned height) + unsigned width, unsigned height, + int x_offset) { size_t i; size_t first_entry; @@ -1899,8 +2111,13 @@ static void materialui_render_menu_list( touch_feedback_active, header_height, width, - height); + height, + x_offset); } + + /* Draw scrollbar */ + materialui_render_scrollbar( + mui, video_info, header_height, width, height, x_offset); } static size_t materialui_list_get_size(void *data, enum menu_list_type type) @@ -1923,7 +2140,7 @@ static void materialui_render_background(materialui_handle_t *mui, video_frame_i menu_display_ctx_draw_t draw; bool add_opacity = false; float opacity_override = 1.0f; - float color_white[16] = { + float draw_color[16] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, @@ -1944,11 +2161,11 @@ static void materialui_render_background(materialui_handle_t *mui, video_frame_i draw.pipeline.id = 0; draw.pipeline.active = false; draw.pipeline.backend_data = NULL; + draw.color = draw_color; if (mui->textures.bg && !video_info->libretro_running) { draw.texture = mui->textures.bg; - draw.color = color_white; /* We are showing a wallpaper - set opacity * override to menu_wallpaper_opacity */ @@ -1958,7 +2175,9 @@ static void materialui_render_background(materialui_handle_t *mui, video_frame_i else { draw.texture = menu_display_white_texture; - draw.color = mui->colors.list_background; + + /* Copy 'list_background' colour to draw colour */ + memcpy(draw_color, mui->colors.list_background, sizeof(draw_color)); /* We are not showing a wallpaper - if content * is running, set opacity override to @@ -1970,21 +2189,6 @@ static void materialui_render_background(materialui_handle_t *mui, video_frame_i } } - /* If content is not running, have to call menu_display_clear_color() - * (have no idea if or why this is necessary, but it was - * included in the original code...) */ - if (!video_info->libretro_running) - { - menu_display_ctx_clearcolor_t clearcolor; - - clearcolor.r = mui->colors.list_background[0]; - clearcolor.g = mui->colors.list_background[1]; - clearcolor.b = mui->colors.list_background[2]; - clearcolor.a = mui->colors.list_background[3]; - - menu_display_clear_color(&clearcolor, video_info); - } - /* Draw background */ menu_display_blend_begin(video_info); menu_display_draw_bg(&draw, video_info, add_opacity, opacity_override); @@ -1992,9 +2196,94 @@ static void materialui_render_background(materialui_handle_t *mui, video_frame_i menu_display_blend_end(video_info); } +static void materialui_render_landscape_border( + materialui_handle_t *mui, video_frame_info_t *video_info, + unsigned width, unsigned height, unsigned header_height, int x_offset) +{ + if (mui->landscape_entry_margin != 0) + { + int border_width = (int)mui->landscape_entry_margin - (int)mui->margin; + int border_height = height - header_height - mui->tabs_height; + int left_x = x_offset; + int right_x = x_offset + (int)width - (int)mui->landscape_entry_margin + (int)mui->margin; + int y = (int)header_height; + + /* Sanity check */ + if (border_width <= 0) + return; + + /* Draw left border */ + menu_display_draw_quad( + video_info, + left_x, + y, + (unsigned)border_width, + (unsigned)border_height, + width, + height, + mui->colors.landscape_border_shadow_left); + + /* Draw right border */ + menu_display_draw_quad( + video_info, + right_x, + y, + (unsigned)border_width, + (unsigned)border_height, + width, + height, + mui->colors.landscape_border_shadow_right); + } +} + +static void materialui_render_selection_highlight( + materialui_handle_t *mui, video_frame_info_t *video_info, + unsigned width, unsigned height, unsigned header_height, int x_offset, + size_t selection, float *color) +{ + /* Only draw highlight if selection is onscreen */ + if ((selection >= mui->first_onscreen_entry) && + (selection <= mui->last_onscreen_entry)) + { + file_list_t *list = NULL; + materialui_node_t *node = NULL; + int highlight_x_offset = x_offset; + int highlight_width = (int)width; + + /* If landscape optimisations are enabled/active, + * adjust highlight layout */ + if (mui->landscape_entry_margin > 0) + { + highlight_x_offset += (int)mui->landscape_entry_margin - (int)mui->margin; + highlight_width -= (int)(2 * mui->landscape_entry_margin) - (int)(2 * mui->margin); + highlight_width = (highlight_width < 0) ? 0 : highlight_width; + } + + list = menu_entries_get_selection_buf_ptr(0); + + if (!list) + return; + + node = (materialui_node_t*)file_list_get_userdata_at_offset(list, selection); + + if (!node) + return; + + menu_display_draw_quad( + video_info, + highlight_x_offset, + header_height - mui->scroll_y + node->y, + (unsigned)highlight_width, + node->line_height, + width, + height, + color); + } +} + static void materialui_render_entry_touch_feedback( materialui_handle_t *mui, video_frame_info_t *video_info, - unsigned width, unsigned height, unsigned header_height, + unsigned width, unsigned height, unsigned header_height, int x_offset, size_t current_selection) { /* Check whether pointer is currently @@ -2004,9 +2293,14 @@ static void materialui_render_entry_touch_feedback( /* If pointer is held and stationary, need to check * that current pointer selection is valid * i.e. item touched at pointer down event may - * have changed due to scroll acceleration */ + * have changed due to scroll acceleration, or + * user may be touching the header/navigation bar */ if (pointer_active) - pointer_active = (mui->touch_feedback_selection == menu_input_get_pointer_selection()); + pointer_active = (mui->touch_feedback_selection == menu_input_get_pointer_selection()) && + (mui->pointer.x > mui->landscape_entry_margin) && + (mui->pointer.x < width - mui->landscape_entry_margin) && + (mui->pointer.y >= header_height) && + (mui->pointer.y <= height - mui->tabs_height); /* Touch feedback highlight fades in when pointer * is held stationary on a menu entry */ @@ -2039,37 +2333,17 @@ static void materialui_render_entry_touch_feedback( * touch feedback highlight */ if (mui->touch_feedback_alpha > 0.0f) { - /* Sanity check */ - if ((mui->touch_feedback_selection >= mui->first_onscreen_entry) && - (mui->touch_feedback_selection <= mui->last_onscreen_entry)) - { - file_list_t *list = NULL; - materialui_node_t *node = NULL; - float higlight_color[16]; + float higlight_color[16]; - list = menu_entries_get_selection_buf_ptr(0); - node = (materialui_node_t*)file_list_get_userdata_at_offset( - list, mui->touch_feedback_selection); + /* Set highlight colour */ + memcpy(higlight_color, mui->colors.list_highlighted_background, sizeof(higlight_color)); + menu_display_set_alpha(higlight_color, mui->transition_alpha * mui->touch_feedback_alpha); - /* Sanity check */ - if (!node) - return; - - /* Set highlight colour */ - memcpy(higlight_color, mui->colors.list_highlighted_background, sizeof(higlight_color)); - menu_display_set_alpha(higlight_color, mui->touch_feedback_alpha); - - /* Draw highlight */ - menu_display_draw_quad( - video_info, - 0, - header_height - mui->scroll_y + node->y, - width, - node->line_height, - width, - height, - higlight_color); - } + /* Draw highlight */ + materialui_render_selection_highlight( + mui, video_info, width, height, header_height, x_offset, + mui->touch_feedback_selection, + higlight_color); } } @@ -2084,6 +2358,11 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ size_t sys_bar_battery_width = 0; size_t sys_bar_clock_width = 0; int sys_bar_text_y = (mui->sys_bar_height / 2.0f) + (mui->font_data.hint.font_height / 4.0f); + int title_x_offset = 0; + int title_x = 0; + bool show_back_icon = menu_entries_ctl(MENU_ENTRIES_CTL_SHOW_BACK, NULL); + bool show_search_icon = mui->is_playlist || mui->is_file_list; + bool use_landscape_layout = !mui->is_portrait && settings->bools.menu_materialui_optimize_landscape_layout; char menu_title_buf[255]; menu_title_buf[0] = '\0'; @@ -2101,10 +2380,10 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ 0, mui->sys_bar_height + mui->title_bar_height, width, - mui->shadow_height, + mui->header_shadow_height, width, height, - mui->colors.shadow); + mui->colors.header_shadow); /* > Title bar background */ menu_display_draw_quad( @@ -2158,7 +2437,7 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ mui->sys_bar_cache.battery_percent_width = font_driver_get_message_width( mui->font_data.hint.font, mui->sys_bar_cache.battery_percent_str, - strlen(mui->sys_bar_cache.battery_percent_str), + (unsigned)strlen(mui->sys_bar_cache.battery_percent_str), 1.0f); } @@ -2242,7 +2521,7 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ mui->sys_bar_cache.timedate_width = font_driver_get_message_width( mui->font_data.hint.font, mui->sys_bar_cache.timedate_str, - utf8len(mui->sys_bar_cache.timedate_str), + (unsigned)strlen(mui->sys_bar_cache.timedate_str), 1.0f); } @@ -2301,7 +2580,7 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ } menu_display_draw_text(mui->font_data.hint.font, core_title_buf, - mui->ticker_x_offset + mui->sys_bar_margin, + (int)(mui->ticker_x_offset + mui->sys_bar_margin), sys_bar_text_y, width, height, mui->colors.sys_bar_text, TEXT_ALIGN_LEFT, 1.0f, false, 0, false); } @@ -2311,7 +2590,7 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ /* > Draw 'back' icon, if required */ menu_title_margin = mui->margin; - if (menu_entries_ctl(MENU_ENTRIES_CTL_SHOW_BACK, NULL)) + if (show_back_icon) { menu_title_margin = mui->icon_size; @@ -2330,7 +2609,7 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ usable_title_bar_width -= menu_title_margin; /* > Draw 'search' icon, if required */ - if (mui->is_playlist || mui->is_file_list) + if (show_search_icon) { materialui_draw_icon(video_info, mui->icon_size, @@ -2348,6 +2627,14 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ else usable_title_bar_width -= mui->margin; + /* If landscape optimisation is enabled and we are + * drawing a back icon but no search icon, title + * maximum width must be reduced (otherwise cannot + * centre properly...) */ + if (use_landscape_layout) + if (show_back_icon && !show_search_icon) + usable_title_bar_width -= (mui->icon_size - mui->margin); + usable_title_bar_width = (usable_title_bar_width > 0) ? usable_title_bar_width : 0; /* > Draw title string */ @@ -2360,24 +2647,116 @@ static void materialui_render_header(materialui_handle_t *mui, video_frame_info_ mui->ticker_smooth.dst_str = menu_title_buf; mui->ticker_smooth.dst_str_len = sizeof(menu_title_buf); - menu_animation_ticker_smooth(&mui->ticker_smooth); + /* If ticker is not active and landscape + * optimisation is enabled, centre the title text */ + if (!menu_animation_ticker_smooth(&mui->ticker_smooth)) + if (use_landscape_layout) + title_x = (usable_title_bar_width - mui->ticker_str_width) >> 1; } else { mui->ticker.s = menu_title_buf; - mui->ticker.len = (unsigned)(usable_title_bar_width / mui->font_data.title.glyph_width); + mui->ticker.len = (unsigned)(usable_title_bar_width / mui->font_data.title.glyph_width) - 1; mui->ticker.str = mui->menu_title; mui->ticker.selected = true; - menu_animation_ticker(&mui->ticker); + /* If ticker is not active and landscape + * optimisation is enabled, centre the title text */ + if (!menu_animation_ticker(&mui->ticker)) + if (use_landscape_layout) + title_x = + (usable_title_bar_width - + ((int)utf8len(menu_title_buf) * + mui->font_data.title.glyph_width)) >> 1; } + title_x += (int)(mui->ticker_x_offset + menu_title_margin); + menu_display_draw_text(mui->font_data.title.font, menu_title_buf, - mui->ticker_x_offset + menu_title_margin, + title_x, mui->sys_bar_height + (mui->title_bar_height / 2.0f) + (mui->font_data.title.font_height / 4.0f), width, height, mui->colors.header_text, TEXT_ALIGN_LEFT, 1.0f, false, 0, false); } +/* Sets transparency of all menu list colours if + * a transition animation is in process */ +static void materialui_colors_set_transition_alpha(materialui_handle_t *mui) +{ + if (mui->transition_alpha < 1.0f) + { + float alpha = mui->transition_alpha; + unsigned alpha_255 = (unsigned)((255.0f * alpha) + 0.5f); + + /* Text colours */ + mui->colors.list_text = (mui->colors.list_text & 0xFFFFFF00) | alpha_255; + mui->colors.list_text_highlighted = (mui->colors.list_text_highlighted & 0xFFFFFF00) | alpha_255; + mui->colors.list_hint_text = (mui->colors.list_hint_text & 0xFFFFFF00) | alpha_255; + mui->colors.list_hint_text_highlighted = (mui->colors.list_hint_text_highlighted & 0xFFFFFF00) | alpha_255; + + /* Background/object colours */ + menu_display_set_alpha(mui->colors.list_highlighted_background, alpha); + menu_display_set_alpha(mui->colors.list_icon, alpha); + menu_display_set_alpha(mui->colors.list_switch_on, alpha); + menu_display_set_alpha(mui->colors.list_switch_on_background, alpha); + menu_display_set_alpha(mui->colors.list_switch_off, alpha); + menu_display_set_alpha(mui->colors.list_switch_off_background, alpha); + menu_display_set_alpha(mui->colors.scrollbar, alpha); + + /* Landscape border shadow only fades if: + * - Landscape border is shown + * - We are currently performaing a slide animation */ + if ((mui->landscape_entry_margin != 0) && + (mui->transition_x_offset != 0.0f)) + { + float border_shadow_alpha = + mui->colors.landscape_border_shadow_opacity * alpha; + + mui->colors.landscape_border_shadow_left[7] = border_shadow_alpha; + mui->colors.landscape_border_shadow_left[15] = border_shadow_alpha; + mui->colors.landscape_border_shadow_right[3] = border_shadow_alpha; + mui->colors.landscape_border_shadow_right[11] = border_shadow_alpha; + } + } +} + +/* Resets transparency of all menu list colours if + * previously altered by a menu transition animation */ +static void materialui_colors_reset_transition_alpha(materialui_handle_t *mui) +{ + if (mui->transition_alpha < 1.0f) + { + /* Text colours */ + mui->colors.list_text = (mui->colors.list_text | 0xFF); + mui->colors.list_text_highlighted = (mui->colors.list_text_highlighted | 0xFF); + mui->colors.list_hint_text = (mui->colors.list_hint_text | 0xFF); + mui->colors.list_hint_text_highlighted = (mui->colors.list_hint_text_highlighted | 0xFF); + + /* Background/object colours */ + menu_display_set_alpha(mui->colors.list_highlighted_background, 1.0f); + menu_display_set_alpha(mui->colors.list_icon, 1.0f); + menu_display_set_alpha(mui->colors.list_switch_on, 1.0f); + menu_display_set_alpha(mui->colors.list_switch_on_background, 1.0f); + menu_display_set_alpha(mui->colors.list_switch_off, 1.0f); + menu_display_set_alpha(mui->colors.list_switch_off_background, 1.0f); + menu_display_set_alpha(mui->colors.scrollbar, 1.0f); + + /* Landscape border shadow only fades if: + * - Landscape border is shown + * - We are currently performaing a slide animation */ + if ((mui->landscape_entry_margin != 0) && + (mui->transition_x_offset != 0.0f)) + { + float border_shadow_alpha = + mui->colors.landscape_border_shadow_opacity; + + mui->colors.landscape_border_shadow_left[7] = border_shadow_alpha; + mui->colors.landscape_border_shadow_left[15] = border_shadow_alpha; + mui->colors.landscape_border_shadow_right[3] = border_shadow_alpha; + mui->colors.landscape_border_shadow_right[11] = border_shadow_alpha; + } + } +} + /* Main function of the menu driver * Draws all menu elements */ static void materialui_frame(void *data, video_frame_info_t *video_info) @@ -2388,16 +2767,13 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) unsigned height = video_info->height; unsigned header_height = menu_display_get_header_height(); size_t selection = menu_navigation_get_selection(); + int list_x_offset; if (!mui || !settings) return; menu_display_set_viewport(width, height); - /* It seems that we have to call this every frame - * (don't know why...) */ - menu_animation_ctl(MENU_ANIMATION_CTL_SET_ACTIVE, NULL); - /* Clear text */ font_driver_bind_block(mui->font_data.title.font, &mui->font_data.title.raster_block); font_driver_bind_block(mui->font_data.list.font, &mui->font_data.list.raster_block); @@ -2428,40 +2804,34 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) mui->ticker.type_enum = (enum menu_animation_ticker_type)settings->uints.menu_ticker_type; } + /* Handle any transparency adjustments required + * by menu transition animations */ + materialui_colors_set_transition_alpha(mui); + + /* Get x offset for list items, required by + * menu transition 'slide' animations */ + list_x_offset = (int)(mui->transition_x_offset * (float)width); + /* Draw background */ materialui_render_background(mui, video_info); + /* Draw landscape border + * (does nothing in portrait mode, or if landscape + * optimisations are disabled) */ + materialui_render_landscape_border( + mui, video_info, width, height, header_height, list_x_offset); + /* Draw 'highlighted entry' selection box */ - if ((selection >= mui->first_onscreen_entry) && - (selection <= mui->last_onscreen_entry)) - { - file_list_t *list = NULL; - materialui_node_t *node = NULL; - - list = menu_entries_get_selection_buf_ptr(0); - node = (materialui_node_t*)file_list_get_userdata_at_offset(list, selection); - - if (node) - menu_display_draw_quad( - video_info, - 0, - header_height - mui->scroll_y + node->y, - width, - node->line_height, - width, - height, - mui->colors.list_highlighted_background); - } + materialui_render_selection_highlight( + mui, video_info, width, height, header_height, list_x_offset, selection, + mui->colors.list_highlighted_background); /* Draw 'short press' touch feedback highlight */ materialui_render_entry_touch_feedback( - mui, video_info, width, height, header_height, selection); + mui, video_info, width, height, header_height, list_x_offset, selection); /* Draw menu list */ - if (menu_display_get_update_pending()) - materialui_render_menu_list(mui, video_info, width, height); - - materialui_draw_scrollbar(mui, video_info, width, height); + materialui_render_menu_list(mui, video_info, width, height, list_x_offset); /* Flush first layer of text * > Menu list only uses list and hint fonts */ @@ -2492,9 +2862,8 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) font_driver_flush(width, height, mui->font_data.title.font, video_info); font_driver_flush(width, height, mui->font_data.hint.font, video_info); - font_driver_bind_block(mui->font_data.title.font, NULL); - font_driver_bind_block(mui->font_data.list.font, NULL); - font_driver_bind_block(mui->font_data.hint.font, NULL); + mui->font_data.title.raster_block.carr.coords.vertices = 0; + mui->font_data.hint.raster_block.carr.coords.vertices = 0; /* Handle onscreen keyboard */ if (menu_input_dialog_get_display_kb()) @@ -2505,23 +2874,43 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) msg[0] = '\0'; + /* Darken screen */ menu_display_draw_quad(video_info, 0, 0, width, height, width, height, mui->colors.screen_fade); - snprintf(msg, sizeof(msg), "%s\n%s", label, str); - materialui_render_messagebox(mui, video_info, msg); + /* Draw message box */ + snprintf(msg, sizeof(msg), "%s\n%s", label, str); + materialui_render_messagebox(mui, video_info, height / 4, msg); + + /* Draw onscreen keyboard */ + menu_display_draw_keyboard( + mui->textures.list[MUI_TEXTURE_KEY_HOVER], + mui->font_data.list.font, + video_info, + menu_event_get_osk_grid(), menu_event_get_osk_ptr(), + 0xFFFFFFFF); + + /* Flush message box & osk text + * > Message box & osk only use list font */ + font_driver_flush(width, height, mui->font_data.list.font, video_info); + mui->font_data.list.raster_block.carr.coords.vertices = 0; } /* Draw message box */ - if (!string_is_empty(mui->box_message)) + if (!string_is_empty(mui->msgbox)) { + /* Darken screen */ menu_display_draw_quad(video_info, 0, 0, width, height, width, height, mui->colors.screen_fade); - materialui_render_messagebox(mui, video_info, mui->box_message); + /* Draw message box */ + materialui_render_messagebox(mui, video_info, height / 2, mui->msgbox); + mui->msgbox[0] = '\0'; - free(mui->box_message); - mui->box_message = NULL; + /* Flush message box text + * > Message box only uses list font */ + font_driver_flush(width, height, mui->font_data.list.font, video_info); + mui->font_data.list.raster_block.carr.coords.vertices = 0; } /* Draw mouse cursor */ @@ -2545,7 +2934,15 @@ static void materialui_frame(void *data, video_frame_info_t *video_info) height); } - menu_display_restore_clear_color(); + /* Undo any transparency adjustments caused + * by menu transition animations */ + materialui_colors_reset_transition_alpha(mui); + + /* Unbind fonts */ + font_driver_bind_block(mui->font_data.title.font, NULL); + font_driver_bind_block(mui->font_data.list.font, NULL); + font_driver_bind_block(mui->font_data.hint.font, NULL); + menu_display_unset_viewport(width, height); } @@ -2557,36 +2954,59 @@ static void materialui_layout(materialui_handle_t *mui, bool video_is_threaded) int hint_font_size; unsigned new_header_height; - mui->cursor_size = mui->dip_base_unit_size / 3; + mui->is_portrait = mui->last_height >= mui->last_width; - mui->sys_bar_height = mui->dip_base_unit_size / 7; - mui->title_bar_height = mui->dip_base_unit_size / 3; - new_header_height = mui->sys_bar_height + mui->title_bar_height; + mui->cursor_size = mui->dip_base_unit_size / 3; - title_font_size = mui->dip_base_unit_size / 7; - list_font_size = mui->dip_base_unit_size / 9; - hint_font_size = mui->dip_base_unit_size / 11; + mui->sys_bar_height = mui->dip_base_unit_size / 7; + mui->title_bar_height = mui->dip_base_unit_size / 3; + new_header_height = mui->sys_bar_height + mui->title_bar_height; - mui->shadow_height = mui->dip_base_unit_size / 36; - mui->scrollbar_width = mui->dip_base_unit_size / 36; + title_font_size = mui->dip_base_unit_size / 7; + list_font_size = mui->dip_base_unit_size / 9; + hint_font_size = mui->dip_base_unit_size / 11; - mui->divider_width = (mui->last_scale_factor > 1.0f) ? + mui->header_shadow_height = mui->dip_base_unit_size / 36; + mui->scrollbar_width = mui->dip_base_unit_size / 36; + + mui->divider_width = (mui->last_scale_factor > 1.0f) ? (unsigned)(mui->last_scale_factor + 0.5f) : 1; - mui->tabs_height = 0; + mui->tabs_height = 0; if (materialui_list_get_size(mui, MENU_LIST_PLAIN) == 1) - mui->tabs_height = mui->dip_base_unit_size / 3; + mui->tabs_height = mui->dip_base_unit_size / 3; - mui->margin = mui->dip_base_unit_size / 9; - mui->icon_size = mui->dip_base_unit_size / 3; + mui->margin = mui->dip_base_unit_size / 9; + mui->icon_size = mui->dip_base_unit_size / 3; - mui->sys_bar_margin = mui->dip_base_unit_size / 12; - mui->sys_bar_icon_size = mui->dip_base_unit_size / 7; + mui->sys_bar_margin = mui->dip_base_unit_size / 12; + mui->sys_bar_icon_size = mui->dip_base_unit_size / 7; + + /* In landscape orientations, menu lists are too wide + * (to the extent that they are rather uncomfortable + * to look at...) + * > When using a landscape layout, we therefore use + * additional padding at the left/right sides of + * the screen */ + mui->landscape_entry_margin = 0; + if (!mui->is_portrait && mui->last_optimize_landscape_layout) + { + /* After testing various approaches, it seems that + * simply enforcing a 4:3 aspect ratio produces the + * best results */ + const float base_aspect = 4.0f / 3.0f; + float landscape_margin = + ((float)mui->last_width - (base_aspect * (float)mui->last_height)) / 2.0f; + + /* Note: Want to round down here */ + if (landscape_margin > 1.0f) + mui->landscape_entry_margin = (unsigned)landscape_margin; + } /* We assume the average glyph aspect ratio is close to 3:4 */ - mui->font_data.title.glyph_width = title_font_size * 3/4; - mui->font_data.list.glyph_width = list_font_size * 3/4; - mui->font_data.hint.glyph_width = hint_font_size * 3/4; + mui->font_data.title.glyph_width = (int)((title_font_size * (3.0f / 4.0f)) + 0.5f); + mui->font_data.list.glyph_width = (int)((list_font_size * (3.0f / 4.0f)) + 0.5f); + mui->font_data.hint.glyph_width = (int)((hint_font_size * (3.0f / 4.0f)) + 0.5f); menu_display_set_header_height(new_header_height); @@ -2671,9 +3091,10 @@ 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; - settings_t *settings = config_get_ptr(); - materialui_handle_t *mui = NULL; - menu_handle_t *menu = (menu_handle_t*)calloc(1, sizeof(*menu)); + settings_t *settings = config_get_ptr(); + materialui_handle_t *mui = NULL; + menu_handle_t *menu = (menu_handle_t*)calloc(1, sizeof(*menu)); + static const char* const ticker_spacer = MUI_TICKER_SPACER; if (!menu || !settings) return NULL; @@ -2714,15 +3135,22 @@ static void *materialui_init(void **userdata, bool video_is_threaded) /* Initial ticker configuration */ mui->use_smooth_ticker = settings->bools.menu_ticker_smooth; mui->ticker_smooth.font_scale = 1.0f; - mui->ticker_smooth.spacer = NULL; + mui->ticker_smooth.spacer = ticker_spacer; mui->ticker_smooth.x_offset = &mui->ticker_x_offset; mui->ticker_smooth.dst_str_width = &mui->ticker_str_width; - mui->ticker.spacer = NULL; + mui->ticker.spacer = ticker_spacer; - /* Ensure touch feedback parameters are zeroed out */ + /* Ensure menu animation parameters are properly + * reset */ mui->touch_feedback_cache_selection = false; mui->touch_feedback_selection = 0; mui->touch_feedback_alpha = 0.0f; + mui->transition_alpha = 1.0f; + mui->transition_x_offset = 0.0f; + mui->last_stack_size = 1; + + /* Ensure message box string is empty */ + mui->msgbox[0] = '\0'; return menu; error: @@ -2808,23 +3236,28 @@ static bool materialui_load_image(void *userdata, void *data, enum menu_image_ty static void materialui_animate_scroll( materialui_handle_t *mui, float scroll_pos, float duration) { - menu_animation_ctx_entry_t entry; + menu_animation_ctx_tag animation_tag = (uintptr_t)&mui->scroll_y; + menu_animation_ctx_entry_t animation_entry; + + /* Kill any existing scroll animation */ + menu_animation_kill_by_tag(&animation_tag); /* mui->scroll_y will be modified by the animation * > Set scroll acceleration to zero to minimise * potential conflicts */ menu_input_set_pointer_y_accel(0.0f); - entry.duration = duration; - entry.target_value = scroll_pos; - entry.subject = &mui->scroll_y; - entry.easing_enum = EASING_IN_OUT_QUAD; - /* TODO/FIXME - integer conversion resulted in change of sign */ - entry.tag = -1; - entry.cb = NULL; + /* Configure animation */ + animation_entry.easing_enum = EASING_IN_OUT_QUAD; + animation_entry.tag = animation_tag; + animation_entry.duration = duration; + animation_entry.target_value = scroll_pos; + animation_entry.subject = &mui->scroll_y; + animation_entry.cb = NULL; + animation_entry.userdata = NULL; - if (entry.subject) - menu_animation_push(&entry); + /* Push animation */ + menu_animation_push(&animation_entry); } /* The navigation pointer has been updated (for example by pressing up or down @@ -2839,7 +3272,7 @@ static void materialui_navigation_set(void *data, bool scroll) materialui_animate_scroll( mui, materialui_get_scroll(mui), - 166.0f); + MUI_ANIM_DURATION_SCROLL); } static void materialui_list_set_selection(void *data, file_list_t *list) @@ -2860,8 +3293,11 @@ static void materialui_navigation_clear(void *data, bool pending_push) return; menu_entries_ctl(MENU_ENTRIES_CTL_SET_START, &i); - mui->scroll_y = 0.0f; - menu_input_set_pointer_y_accel(0.0f); + + materialui_animate_scroll( + mui, + 0.0f, + MUI_ANIM_DURATION_SCROLL_RESET); } static void materialui_navigation_set_last(void *data) @@ -2874,6 +3310,113 @@ static void materialui_navigation_alphabet(void *data, size_t *unused) materialui_navigation_set(data, true); } +static void materialui_init_transition_animation(materialui_handle_t *mui) +{ + settings_t *settings = config_get_ptr(); + size_t stack_size = materialui_list_get_size(mui, MENU_LIST_PLAIN); + menu_animation_ctx_tag alpha_tag = (uintptr_t)&mui->transition_alpha; + menu_animation_ctx_tag x_offset_tag = (uintptr_t)&mui->transition_x_offset; + menu_animation_ctx_entry_t alpha_entry; + menu_animation_ctx_entry_t x_offset_entry; + + if (!settings) + return; + + /* If animations are disabled, reset alpha/x offset + * values and return immediately */ + if (settings->uints.menu_materialui_transition_animation == + MATERIALUI_TRANSITION_ANIM_NONE) + { + mui->transition_alpha = 1.0f; + mui->transition_x_offset = 0.0f; + mui->last_stack_size = stack_size; + return; + } + + /* Fade in animation (alpha) + * This is *always* used, regardless of the set animation + * type */ + + /* > Kill any existing animations and set + * initial alpha value */ + menu_animation_kill_by_tag(&alpha_tag); + mui->transition_alpha = 0.0f; + + /* > Configure animation */ + alpha_entry.easing_enum = EASING_OUT_QUAD; + alpha_entry.tag = alpha_tag; + alpha_entry.duration = MUI_ANIM_DURATION_MENU_TRANSITION; + alpha_entry.target_value = 1.0f; + alpha_entry.subject = &mui->transition_alpha; + alpha_entry.cb = NULL; + alpha_entry.userdata = NULL; + + /* > Push animation */ + menu_animation_push(&alpha_entry); + + /* Slide animation (x offset) */ + + /* > Kill any existing animations and set + * initial x offset value */ + menu_animation_kill_by_tag(&x_offset_tag); + mui->transition_x_offset = 0.0f; + + /* >> Menu 'forward' action */ + if (mui->last_stack_size < stack_size) + { + if (settings->uints.menu_materialui_transition_animation == + MATERIALUI_TRANSITION_ANIM_SLIDE) + mui->transition_x_offset = 1.0f; + } + /* >> Menu 'back' action */ + else if (mui->last_stack_size > stack_size) + { + if (settings->uints.menu_materialui_transition_animation == + MATERIALUI_TRANSITION_ANIM_SLIDE) + mui->transition_x_offset = -1.0f; + } + /* >> Menu tab switch action */ + else if ((stack_size == 1) && + (settings->uints.menu_materialui_transition_animation != + MATERIALUI_TRANSITION_ANIM_FADE)) + { + /* We're not changing menu levels here, so set + * slide to match horizontal list 'movement' + * direction */ + if (mui->categories_selection_ptr < mui->categories_selection_ptr_old) + { + if (mui->categories_navigation_wrapped) + mui->transition_x_offset = 1.0f; + else + mui->transition_x_offset = -1.0f; + } + else if (mui->categories_selection_ptr > mui->categories_selection_ptr_old) + { + if (mui->categories_navigation_wrapped) + mui->transition_x_offset = -1.0f; + else + mui->transition_x_offset = 1.0f; + } + } + + mui->last_stack_size = stack_size; + + if (mui->transition_x_offset != 0.0f) + { + /* > Configure animation */ + x_offset_entry.easing_enum = EASING_OUT_QUAD; + x_offset_entry.tag = x_offset_tag; + x_offset_entry.duration = MUI_ANIM_DURATION_MENU_TRANSITION; + x_offset_entry.target_value = 0.0f; + x_offset_entry.subject = &mui->transition_x_offset; + x_offset_entry.cb = NULL; + x_offset_entry.userdata = NULL; + + /* > Push animation */ + menu_animation_push(&x_offset_entry); + } +} + /* A new list had been pushed */ static void materialui_populate_entries( void *data, const char *path, @@ -2944,6 +3487,9 @@ static void materialui_populate_entries( mui->touch_feedback_cache_selection = false; mui->touch_feedback_selection = 0; mui->touch_feedback_alpha = 0.0f; + + /* Initialise menu transition animation */ + materialui_init_transition_animation(mui); } static void materialui_context_reset_internal( @@ -3051,8 +3597,9 @@ static void materialui_list_cache(void *data, if (!mui) return; - mui->need_compute = true; - list_size = MUI_SYSTEM_TAB_END; + mui->need_compute = true; + list_size = MUI_SYSTEM_TAB_END; + mui->categories_navigation_wrapped = false; switch (type) { @@ -3066,8 +3613,9 @@ static void materialui_list_cache(void *data, case MENU_ACTION_LEFT: if (mui->categories_selection_ptr == 0) { - mui->categories_selection_ptr = list_size; - mui->categories_active_idx = (unsigned)(list_size - 1); + mui->categories_selection_ptr = list_size; + mui->categories_active_idx = (unsigned)(list_size - 1); + mui->categories_navigation_wrapped = true; } else mui->categories_selection_ptr--; @@ -3075,8 +3623,9 @@ static void materialui_list_cache(void *data, default: if (mui->categories_selection_ptr == list_size) { - mui->categories_selection_ptr = 0; - mui->categories_active_idx = 1; + mui->categories_selection_ptr = 0; + mui->categories_active_idx = 1; + mui->categories_navigation_wrapped = true; } else mui->categories_selection_ptr++; @@ -3379,7 +3928,7 @@ static int materialui_pointer_up_swipe_horz_plain_list( materialui_animate_scroll( mui, mui->scroll_y + (scroll_up ? (scroll_offset * -1.0f) : scroll_offset), - 166.0f); + MUI_ANIM_DURATION_SCROLL); } return 0; @@ -3400,13 +3949,21 @@ static int materialui_pointer_up_swipe_horz_default( * 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 + * - Entry value type is: + * > MUI_ENTRY_VALUE_TEXT + * > MUI_ENTRY_VALUE_SWITCH_ON + * > MUI_ENTRY_VALUE_SWITCH_OFF * Note: cannot use input (argument) entry, since this * will always have a blank value component */ if (selection == new_selection) { + const char *entry_value = NULL; + unsigned entry_type = 0; + enum msg_file_type entry_file_type = FILE_TYPE_NONE; + enum materialui_entry_value_type entry_value_type = MUI_ENTRY_VALUE_NONE; menu_entry_t last_entry; + /* Get entry */ menu_entry_init(&last_entry); last_entry.path_enabled = false; last_entry.label_enabled = false; @@ -3415,11 +3972,21 @@ static int materialui_pointer_up_swipe_horz_default( menu_entry_get(&last_entry, 0, selection, NULL, true); - if (!string_is_empty(last_entry.value)) + /* Parse entry */ + menu_entry_get_value(&last_entry, &entry_value); + entry_type = menu_entry_get_type_new(&last_entry); + entry_file_type = msg_hash_to_file_type(msg_hash_calculate(entry_value)); + entry_value_type = materialui_get_entry_value_type( + mui, entry_value, last_entry.checked, entry_type, entry_file_type); + + /* If entry has a 'settings' type, reset scroll position */ + if ((entry_value_type == MUI_ENTRY_VALUE_TEXT) || + (entry_value_type == MUI_ENTRY_VALUE_SWITCH_ON) || + (entry_value_type == MUI_ENTRY_VALUE_SWITCH_OFF)) materialui_animate_scroll( mui, mui->pointer_start_scroll_y, - 83.0f); + MUI_ANIM_DURATION_SCROLL_RESET); } } @@ -3485,7 +4052,12 @@ static int materialui_pointer_up(void *userdata, if ((x >= start) && (x < (start + tab_width))) { - mui->categories_selection_ptr = i; + /* Do nothing if currently selected tab is pressed */ + if (mui->categories_selection_ptr == i) + break; + + mui->categories_selection_ptr_old = mui->categories_selection_ptr; + mui->categories_selection_ptr = i; materialui_preswitch_tabs(mui, action); @@ -3496,7 +4068,9 @@ static int materialui_pointer_up(void *userdata, } } /* Tap/press menu item: Activate and/or select item */ - else if (ptr < entries_end) + else if ((ptr < entries_end) && + (x > mui->landscape_entry_margin) && + (x < width - mui->landscape_entry_margin)) { if (gesture == MENU_INPUT_GESTURE_TAP) { diff --git a/menu/menu_defines.h b/menu/menu_defines.h index 0c30a63d80..456e5791a7 100644 --- a/menu/menu_defines.h +++ b/menu/menu_defines.h @@ -154,9 +154,21 @@ enum materialui_color_theme MATERIALUI_THEME_MATERIALUI, MATERIALUI_THEME_MATERIALUI_DARK, MATERIALUI_THEME_OZONE_DARK, + MATERIALUI_THEME_NORD, + MATERIALUI_THEME_GRUVBOX_DARK, + MATERIALUI_THEME_SOLARIZED_DARK, MATERIALUI_THEME_LAST }; +enum materialui_transition_animation +{ + MATERIALUI_TRANSITION_ANIM_AUTO = 0, + MATERIALUI_TRANSITION_ANIM_FADE, + MATERIALUI_TRANSITION_ANIM_SLIDE, + MATERIALUI_TRANSITION_ANIM_NONE, + MATERIALUI_TRANSITION_ANIM_LAST +}; + enum xmb_color_theme { XMB_THEME_LEGACY_RED = 0, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 4c0389c4e5..4ee7cf7f9a 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5446,7 +5446,9 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct {MENU_ENUM_LABEL_OZONE_COLLAPSE_SIDEBAR, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_OZONE_TRUNCATE_PLAYLIST_NAME, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_MATERIALUI_ICONS_ENABLE, PARSE_ONLY_BOOL}, + {MENU_ENUM_LABEL_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_MATERIALUI_MENU_COLOR_THEME, PARSE_ONLY_UINT}, + {MENU_ENUM_LABEL_MATERIALUI_MENU_TRANSITION_ANIMATION, PARSE_ONLY_UINT}, {MENU_ENUM_LABEL_MATERIALUI_MENU_HEADER_OPACITY, PARSE_ONLY_FLOAT}, {MENU_ENUM_LABEL_MATERIALUI_MENU_FOOTER_OPACITY, PARSE_ONLY_FLOAT}, {MENU_ENUM_LABEL_MENU_USE_PREFERRED_SYSTEM_COLOR_THEME, PARSE_ONLY_BOOL }, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 6bcaf11323..76211c3f2f 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -3868,6 +3868,55 @@ static void setting_get_string_representation_uint_materialui_menu_color_theme( msg_hash_to_str( MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_OZONE_DARK), len); break; + case MATERIALUI_THEME_NORD: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_NORD), len); + break; + case MATERIALUI_THEME_GRUVBOX_DARK: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_GRUVBOX_DARK), len); + break; + case MATERIALUI_THEME_SOLARIZED_DARK: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_SOLARIZED_DARK), len); + break; + default: + break; + } +} + +static void setting_get_string_representation_uint_materialui_menu_transition_animation( + rarch_setting_t *setting, + char *s, size_t len) +{ + if (!setting) + return; + + switch (*setting->value.target.unsigned_integer) + { + case MATERIALUI_TRANSITION_ANIM_AUTO: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_AUTO), len); + break; + case MATERIALUI_TRANSITION_ANIM_FADE: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_FADE), len); + break; + case MATERIALUI_TRANSITION_ANIM_SLIDE: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_SLIDE), len); + break; + case MATERIALUI_TRANSITION_ANIM_NONE: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_NONE), len); + break; default: break; } @@ -12864,7 +12913,7 @@ static bool setting_append_list( &settings->uints.menu_materialui_color_theme, MENU_ENUM_LABEL_MATERIALUI_MENU_COLOR_THEME, MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME, - MATERIALUI_THEME_BLUE, + DEFAULT_MATERIALUI_THEME, &group_info, &subgroup_info, parent_group, @@ -12876,6 +12925,38 @@ static bool setting_append_list( menu_settings_list_current_add_range(list, list_info, 0, MATERIALUI_THEME_LAST-1, 1, true, true); (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX; + CONFIG_UINT( + list, list_info, + &settings->uints.menu_materialui_transition_animation, + MENU_ENUM_LABEL_MATERIALUI_MENU_TRANSITION_ANIMATION, + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIMATION, + DEFAULT_MATERIALUI_TRANSITION_ANIM, + &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_materialui_menu_transition_animation; + menu_settings_list_current_add_range(list, list_info, 0, MATERIALUI_TRANSITION_ANIM_LAST-1, 1, true, true); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX; + + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_materialui_optimize_landscape_layout, + MENU_ENUM_LABEL_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT, + MENU_ENUM_LABEL_VALUE_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT, + DEFAULT_MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); + /* TODO: These should be removed entirely, but just * comment out for now in case users complain... CONFIG_FLOAT( diff --git a/msg_hash.h b/msg_hash.h index 31f6f64223..f99e3fcaed 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -517,6 +517,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_AUTO, MENU_LABEL(MATERIALUI_ICONS_ENABLE), + MENU_LABEL(MATERIALUI_OPTIMIZE_LANDSCAPE_LAYOUT), MENU_ENUM_LABEL_VALUE_RGUI_MENU_COLOR_THEME_CUSTOM, MENU_ENUM_LABEL_VALUE_RGUI_MENU_COLOR_THEME_CLASSIC_RED, @@ -602,6 +603,15 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_MATERIALUI, MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_MATERIALUI_DARK, MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_OZONE_DARK, + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_NORD, + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_GRUVBOX_DARK, + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_COLOR_THEME_SOLARIZED_DARK, + + MENU_LABEL(MATERIALUI_MENU_TRANSITION_ANIMATION), + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_AUTO, + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_FADE, + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_SLIDE, + MENU_ENUM_LABEL_VALUE_MATERIALUI_MENU_TRANSITION_ANIM_NONE, MENU_ENUM_LABEL_VALUE_INPUT_POLL_TYPE_BEHAVIOR_LATE, MENU_ENUM_LABEL_VALUE_INPUT_POLL_TYPE_BEHAVIOR_NORMAL,