From f941d4e625988d4b753cb93bbd04b763ac218e5d Mon Sep 17 00:00:00 2001 From: natinusala Date: Thu, 11 Apr 2019 16:46:29 +0200 Subject: [PATCH] menu widgets: add first achievement notification widget --- .vscode/settings.json | 3 +- cheevos-new/cheevos.c | 17 ++- cheevos/cheevos.c | 26 +++- gfx/font_driver.c | 2 + intl/msg_hash_us.h | 4 + menu/widgets/menu_widgets.c | 258 ++++++++++++++++++++++++++++++++---- menu/widgets/menu_widgets.h | 5 +- msg_hash.h | 1 + 8 files changed, 278 insertions(+), 38 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f80c61030..c2e6cabd58 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -67,7 +67,8 @@ "menu_input_dialog.h": "c", "menu_filebrowser.h": "c", "ozone_sidebar.h": "c", - "menu_thumbnail_path.h": "c" + "menu_thumbnail_path.h": "c", + "badges.h": "c" }, "C_Cpp.dimInactiveRegions": false, } \ No newline at end of file diff --git a/cheevos-new/cheevos.c b/cheevos-new/cheevos.c index 5276d006d4..0524cf7338 100644 --- a/cheevos-new/cheevos.c +++ b/cheevos-new/cheevos.c @@ -35,6 +35,9 @@ #ifdef HAVE_MENU #include "../menu/menu_driver.h" #include "../menu/menu_entries.h" +#ifdef HAVE_MENU_WIDGETS +#include "../menu/widgets/menu_widgets.h" +#endif #endif #ifdef HAVE_THREADS @@ -492,9 +495,14 @@ static void cheevos_award(cheevos_cheevo_t* cheevo, int mode) cheevo->active &= ~CHEEVOS_ACTIVE_SOFTCORE; /* Show the OSD message. */ - snprintf(buffer, sizeof(buffer), "Achievement Unlocked: %s", cheevo->info->title); - runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - runloop_msg_queue_push(cheevo->info->description, 0, 3 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); +#if defined(HAVE_MENU) && defined(HAVE_MENU_WIDGETS) + if (!video_driver_has_widgets() || !menu_widgets_push_achievement(cheevo->title, cheevo->badge)) +#endif + { + snprintf(buffer, sizeof(buffer), "Achievement Unlocked: %s", cheevo->info->title); + runloop_msg_queue_push(buffer, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + runloop_msg_queue_push(cheevo->info->description, 0, 3 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } /* Start the award task. */ if ((mode & CHEEVOS_ACTIVE_HARDCORE) != 0) @@ -1676,6 +1684,9 @@ found: badges_ctx = new_badges_ctx; +#ifdef HAVE_MENU_WIDGETS + if (false) /* we always want badges if menu widgets are enabled */ +#endif { settings_t *settings = config_get_ptr(); if (!( diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index d56afeaab5..8dd2b2c995 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -36,6 +36,9 @@ #ifdef HAVE_MENU #include "../menu/menu_driver.h" #include "../menu/menu_entries.h" +#ifdef HAVE_MENU_WIDGETS +#include "../menu/widgets/menu_widgets.h" +#endif #endif #ifdef HAVE_THREADS @@ -1643,9 +1646,8 @@ static void cheevos_test_cheevo_set(const cheevoset_t *set) } else if (valid) { - char msg[256]; char url[256]; - msg[0] = url[0] = '\0'; + url[0] = '\0'; cheevo->active &= ~mode; @@ -1655,11 +1657,18 @@ static void cheevos_test_cheevo_set(const cheevoset_t *set) CHEEVOS_LOG("[CHEEVOS]: awarding cheevo %u: %s (%s).\n", cheevo->id, cheevo->title, cheevo->description); - snprintf(msg, sizeof(msg), "Achievement Unlocked: %s", - cheevo->title); - msg[sizeof(msg) - 1] = 0; - runloop_msg_queue_push(msg, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - runloop_msg_queue_push(cheevo->description, 0, 3 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); +#if defined(HAVE_MENU) && defined(HAVE_MENU_WIDGETS) + if (!video_driver_has_widgets() || !menu_widgets_push_achievement(cheevo->title, cheevo->badge)) +#endif + { + char msg[256]; + msg[0] = '\0'; + snprintf(msg, sizeof(msg), "Achievement Unlocked: %s", + cheevo->title); + msg[sizeof(msg) - 1] = 0; + runloop_msg_queue_push(msg, 0, 2 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + runloop_msg_queue_push(cheevo->description, 0, 3 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } cheevos_make_unlock_url(cheevo, url, sizeof(url)); task_push_http_transfer(url, true, NULL, @@ -3195,6 +3204,9 @@ found: badges_ctx = new_badges_ctx; +#ifdef HAVE_MENU_WIDGETS + if (false) /* we always want badges if menu widgets are enabled */ +#endif { settings_t *settings = config_get_ptr(); if (!( diff --git a/gfx/font_driver.c b/gfx/font_driver.c index 0c84999394..936872675a 100644 --- a/gfx/font_driver.c +++ b/gfx/font_driver.c @@ -1074,6 +1074,8 @@ int font_driver_get_message_width(void *font_data, const char *msg, unsigned len, float scale) { font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver); + if (len == 0 && msg) + len = (unsigned)strlen(msg); if (font && font->renderer && font->renderer->get_message_width) return font->renderer->get_message_width(font->renderer_data, msg, len, scale); return -1; diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 252f8bcc99..18203be721 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -4882,6 +4882,10 @@ MSG_HASH( MSG_SCREENSHOT_SAVED, "Screenshot saved" ) +MSG_HASH( + MSG_ACHIEVEMENT_UNLOCKED, + "Achievement Unlocked" + ) MSG_HASH( MSG_CHANGE_THUMBNAIL_TYPE, "Change thumbnail type" diff --git a/menu/widgets/menu_widgets.c b/menu/widgets/menu_widgets.c index ef76fa5f0b..999890b8e8 100644 --- a/menu/widgets/menu_widgets.c +++ b/menu/widgets/menu_widgets.c @@ -38,8 +38,9 @@ #define PI 3.14159265359f -/* TODO: Fix context reset freezing everything in place (probably kills animations when it shouldn't anymore) */ +#define max(x, y) x >= y ? x : y +/* TODO: Fix context reset freezing everything in place (probably kills animations when it shouldn't anymore) */ static float msg_queue_background[16] = COLOR_HEX_TO_FLOAT(0x3A3A3A, 1.0f); static float msg_queue_info[16] = COLOR_HEX_TO_FLOAT(0x12ACF8, 1.0f); @@ -79,6 +80,17 @@ static float menu_widgets_pure_white[16] = { 1.00, 1.00, 1.00, 1.00, }; +/* Achievement notification */ +static char *cheevo_title = NULL; +static char *cheevo_badge = NULL; +static float cheevo_unfold = 0.0f; + +static menu_timer_t cheevo_timer; + +static float cheevo_y = 0.0f; +static unsigned cheevo_width = 0; +static unsigned cheevo_height = 0; + /* Load content animation */ #define ANIMATION_LOAD_CONTENT_DURATION 333 @@ -98,7 +110,7 @@ static float load_content_animation_final_fade_alpha; static menu_timer_t load_content_animation_end_timer; -static float menu_widgets_backdrop_orig[16] = { +static float menu_widgets_backdrop_orig[16] = { 0.00, 0.00, 0.00, 0.75, 0.00, 0.00, 0.00, 0.75, 0.00, 0.00, 0.00, 0.75, @@ -188,7 +200,9 @@ enum menu_widgets_icon MENU_WIDGETS_ICON_INFO, - MENU_WIDGETS_ICON_LAST + MENU_WIDGETS_ICON_ACHIEVEMENT, + + MENU_WIDGETS_ICON_LAST, }; static char *menu_widgets_icons_names[MENU_WIDGETS_ICON_LAST] = { @@ -205,7 +219,9 @@ static char *menu_widgets_icons_names[MENU_WIDGETS_ICON_LAST] = { "menu_hourglass.png", "menu_check.png", - "menu_info.png" + "menu_info.png", + + "menu_achievements.png" }; static menu_texture_item menu_widgets_icons_textures[MENU_WIDGETS_ICON_LAST] = {0}; @@ -1211,7 +1227,7 @@ static void menu_widgets_draw_regular_msg(menu_widget_msg_t *msg, video_frame_in icon, msg_queue_spacing + msg_queue_internal_icon_offset, video_info->height - msg->offset_y - msg_queue_icon_offset_y + msg_queue_internal_icon_offset, video_info->width, video_info->height, 0, 1, menu_widgets_pure_white); - + menu_display_blend_end(video_info); } } @@ -1305,21 +1321,21 @@ void menu_widgets_frame(video_frame_info_t *video_info) menu_display_set_alpha(menu_widgets_backdrop_orig, DEFAULT_BACKDROP); menu_display_draw_quad(video_info, - 0, screenshot_y, + 0, screenshot_y, screenshot_width, screenshot_height, video_info->width, video_info->height, menu_widgets_backdrop_orig ); menu_display_set_alpha(menu_widgets_pure_white, 1.0f); - menu_widgets_draw_icon(video_info, - screenshot_thumbnail_width, screenshot_thumbnail_height, - screenshot_texture, - 0, screenshot_y, - video_info->width, video_info->height, + menu_widgets_draw_icon(video_info, + screenshot_thumbnail_width, screenshot_thumbnail_height, + screenshot_texture, + 0, screenshot_y, + video_info->width, video_info->height, 0, 1, menu_widgets_pure_white ); - + menu_display_draw_text(font_regular, msg_hash_to_str(MSG_SCREENSHOT_SAVED), screenshot_thumbnail_width + simple_widget_padding, settings->floats.video_font_size * 1.9f + screenshot_y, @@ -1347,6 +1363,86 @@ void menu_widgets_frame(video_frame_info_t *video_info) ); } + /* Achievement notification */ + if (cheevo_title) + { + unsigned unfold_offet = ((1.0f-cheevo_unfold) * cheevo_width/2); + + menu_display_set_alpha(menu_widgets_backdrop_orig, DEFAULT_BACKDROP); + + /* Default icon */ + if (!cheevo_badge) + { + /* Backdrop */ + menu_display_draw_quad(video_info, + 0, (int)cheevo_y, + cheevo_height, cheevo_height, + video_info->width, video_info->height, + menu_widgets_backdrop_orig); + + /* Icon */ + if (menu_widgets_icons_textures[MENU_WIDGETS_ICON_ACHIEVEMENT]) + { + menu_display_blend_begin(video_info); + menu_display_set_alpha(menu_widgets_pure_white, 1.0f); + menu_widgets_draw_icon(video_info, + cheevo_height, cheevo_height, + menu_widgets_icons_textures[MENU_WIDGETS_ICON_ACHIEVEMENT], 0, cheevo_y, + video_info->width, video_info->height, 0, 1, menu_widgets_pure_white); + menu_display_blend_end(video_info); + } + } + /* Badge */ + else + { + /* TODO: Display the badge */ + } + + if (cheevo_unfold != 1.0f) + { + menu_display_scissor_begin(video_info, + cheevo_height, 0, + (unsigned)((float)(cheevo_width) * cheevo_unfold), cheevo_height); + } + + /* Backdrop */ + menu_display_draw_quad(video_info, + cheevo_height, (int)cheevo_y, + cheevo_width, cheevo_height, + video_info->width, video_info->height, + menu_widgets_backdrop_orig); + + /* Title */ + menu_display_draw_text(font_regular, + msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), + cheevo_height + simple_widget_padding - unfold_offet, settings->floats.video_font_size * 1.9f + cheevo_y, + video_info->width, video_info->height, + text_color_faint, + TEXT_ALIGN_LEFT, + 1, false, 0, true + ); + + /* Title */ + + /* TODO: is a ticker necessary ? */ + + menu_display_draw_text(font_regular, + cheevo_title, + cheevo_height + simple_widget_padding - unfold_offet, settings->floats.video_font_size * 2.9f + cheevo_y, + video_info->width, video_info->height, + text_color_info, + TEXT_ALIGN_LEFT, + 1, false, 0, true + ); + + if (cheevo_unfold != 1.0f) + { + font_driver_flush(video_info->width, video_info->height, font_regular, video_info); + font_raster_regular.carr.coords.vertices = 0; + menu_display_scissor_end(video_info); + } + } + /* Volume */ if (volume_alpha > 0.0f) { @@ -1700,9 +1796,9 @@ void menu_widgets_context_reset(bool is_threaded) } /* Metrics */ - simple_widget_padding = settings->floats.video_font_size * 2/3; - simple_widget_height = settings->floats.video_font_size + simple_widget_padding; - glyph_width = font_driver_get_message_width(font_regular, "a", 1, 1); + simple_widget_padding = settings->floats.video_font_size * 2/3; + simple_widget_height = settings->floats.video_font_size + simple_widget_padding; + glyph_width = font_driver_get_message_width(font_regular, "a", 1, 1); msg_queue_height = settings->floats.video_font_size * 2.5f; @@ -1772,6 +1868,21 @@ void menu_widgets_context_destroy(void) font_bold = NULL; } +static void menu_widgets_achievement_free(void *userdata) +{ + if (cheevo_title) + { + free(cheevo_title); + cheevo_title = NULL; + } + + if (cheevo_badge) + { + free(cheevo_badge); + cheevo_badge = NULL; + } +} + void menu_widgets_free(void) { int i; @@ -1802,7 +1913,7 @@ void menu_widgets_free(void) } /* Purge everything from the list */ - if (current_msgs) + if (current_msgs) { for (i = 0; i < current_msgs->size; i++) { @@ -1813,6 +1924,10 @@ void menu_widgets_free(void) file_list_free(current_msgs); } + /* Achievement notification */ + menu_widgets_achievement_free(NULL); + + /* Screenshot texture */ video_driver_texture_unload(&screenshot_texture); /* Font */ @@ -1859,15 +1974,6 @@ bool menu_widgets_volume_update_and_show(void) volume_text_alpha = 1.0f; volume_mute = mute; - /* TODO/FIXME - natinusula - - * -menu/widgets/menu_widgets.c: In function 'menu_widgets_volume_update_and_show': -menu/widgets/menu_widgets.c:1859:19: warning: assignment to 'tween_cb' {aka 'void (*)(void *)'} from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types] - entry.cb = menu_widgets_volume_timer_end; - ^ - * - * - */ entry.cb = menu_widgets_volume_timer_end; entry.duration = VOLUME_DURATION; entry.userdata = NULL; @@ -1951,7 +2057,6 @@ void menu_widgets_screenshot_taken(const char *shotname, const char *filename) strlcpy(screenshot_shotname, shotname, sizeof(screenshot_shotname)); } - bool menu_widgets_task_msg_queue_push(retro_task_t *task, const char *msg, unsigned prio, unsigned duration, @@ -2080,6 +2185,107 @@ void menu_widgets_start_load_content_animation(const char *content_name, bool re load_content_animation_running = true; } +static void menu_widgets_achievement_dismiss(void *userdata) +{ + menu_animation_ctx_entry_t entry; + + /* Slide up animation */ + entry.cb = menu_widgets_achievement_free; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &cheevo_y; + entry.tag = generic_tag; + entry.target_value = (float)(-(int)(cheevo_height)); + entry.userdata = NULL; + + menu_animation_push(&entry); +} + +static void menu_widgets_achievement_fold(void *userdata) +{ + menu_animation_ctx_entry_t entry; + + /* Fold */ + entry.cb = menu_widgets_achievement_dismiss; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &cheevo_unfold; + entry.tag = generic_tag; + entry.target_value = 0.0f; + entry.userdata = NULL; + + menu_animation_push(&entry); +} + +static void menu_widgets_achievement_unfold(void *userdata) +{ + menu_animation_ctx_entry_t entry; + menu_timer_ctx_entry_t timer; + + /* Unfold */ + entry.cb = NULL; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &cheevo_unfold; + entry.tag = generic_tag; + entry.target_value = 1.0f; + entry.userdata = NULL; + + menu_animation_push(&entry); + + /* Wait before dismissing */ + timer.cb = menu_widgets_achievement_fold; + timer.duration = MSG_QUEUE_ANIMATION_DURATION + CHEEVO_NOTIFICATION_DURATION; + timer.userdata = NULL; + + menu_timer_start(&cheevo_timer, &timer); +} + +static void menu_widgets_start_achievement_notification() +{ + settings_t *settings = config_get_ptr(); + menu_animation_ctx_entry_t entry; + cheevo_height = settings->floats.video_font_size * 4; + cheevo_width = max( + font_driver_get_message_width(font_regular, msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), 0, 1), + font_driver_get_message_width(font_regular, cheevo_title, 0, 1) + ); + cheevo_width += simple_widget_padding * 2; + cheevo_y = (float)(-(int)cheevo_height); + cheevo_unfold = 0.0f; + + /* Slide down animation */ + entry.cb = menu_widgets_achievement_unfold; + entry.duration = MSG_QUEUE_ANIMATION_DURATION; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &cheevo_y; + entry.tag = generic_tag; + entry.target_value = 0.0f; + entry.userdata = NULL; + + menu_animation_push(&entry); +} + +bool menu_widgets_push_achievement(const char *title, const char *badge) +{ + if (!menu_widgets_inited) + return false; + + menu_widgets_achievement_free(NULL); + + /* TODO: Make a queue of notifications to display */ + + cheevo_title = strdup(title); + + /* TODO: Check if badge exists before copying it */ + /* cheevo_badge = strdup(badge); */ + cheevo_badge = NULL; + + menu_widgets_start_achievement_notification(); + + return true; +} + bool menu_widgets_ready(void) { return menu_widgets_inited; diff --git a/menu/widgets/menu_widgets.h b/menu/widgets/menu_widgets.h index a2dacaa72d..aef16bd19e 100644 --- a/menu/widgets/menu_widgets.h +++ b/menu/widgets/menu_widgets.h @@ -31,6 +31,7 @@ #define SCREENSHOT_DURATION_IN 66 #define SCREENSHOT_DURATION_OUT SCREENSHOT_DURATION_IN*10 #define SCREENSHOT_NOTIFICATION_DURATION 4000 +#define CHEEVO_NOTIFICATION_DURATION 4000 #define TASK_FINISHED_DURATION 3000 #define HOURGLASS_INTERVAL 5000 #define HOURGLASS_DURATION 1000 @@ -69,8 +70,10 @@ void menu_widgets_context_reset(bool is_threaded); void menu_widgets_context_destroy(void); +bool menu_widgets_push_achievement(const char *title, const char *badge); + /* All the functions below should be called in - * the video driver - once they are all added, set + * the video driver - once they are all added, set * enable_menu_widgets to true for that driver */ void menu_widgets_frame(video_frame_info_t *video_info); diff --git a/msg_hash.h b/msg_hash.h index 6b840ec10c..b87ace9ae6 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -347,6 +347,7 @@ enum msg_hash_enums MSG_MOVIE_PLAYBACK_ENDED, MSG_TAKING_SCREENSHOT, MSG_SCREENSHOT_SAVED, + MSG_ACHIEVEMENT_UNLOCKED, MSG_CHANGE_THUMBNAIL_TYPE, MSG_NO_THUMBNAIL_AVAILABLE, MSG_PRESS_AGAIN_TO_QUIT,