/* RetroArch - A frontend for libretro. * Copyright (C) 2014-2017 - Jean-André Santoni * Copyright (C) 2015-2018 - Andre Leiradella * Copyright (C) 2018-2020 - natinusala * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include "../gfx_widgets.h" #include "../gfx_animation.h" #include "../gfx_display.h" /* Widget state */ struct gfx_widget_progress_message_state { unsigned widget_width; unsigned widget_height; unsigned text_width; unsigned bar_bg_width; unsigned bar_bg_height; unsigned bar_max_width; unsigned bar_height; unsigned priority; float timer; /* float alignment */ float alpha; float widget_x; float widget_y; float text_x; float text_y; float bar_bg_x; float bar_bg_y; float bar_x; float bar_y; float bar_bg_color[16]; float bar_color[16]; float bar_disabled_color[16]; int8_t progress; char message[256]; bool active; }; typedef struct gfx_widget_progress_message_state gfx_widget_progress_message_state_t; static gfx_widget_progress_message_state_t p_w_progress_message_st = { 0, /* widget_width */ 0, /* widget_height */ 0, /* text_width */ 0, /* bar_bg_width */ 0, /* bar_bg_height */ 0, /* bar_max_width */ 0, /* bar_height */ 0, /* priority */ 0.0f, /* timer */ 0.0f, /* alpha */ 0.0f, /* widget_x */ 0.0f, /* widget_y */ 0.0f, /* text_x */ 0.0f, /* text_y */ 0.0f, /* bar_bg_x */ 0.0f, /* bar_bg_y */ 0.0f, /* bar_x */ 0.0f, /* bar_y */ COLOR_HEX_TO_FLOAT(0x3A3A3A, 1.0f), /* bar_bg_color */ COLOR_HEX_TO_FLOAT(0x198AC6, 1.0f), /* bar_color */ COLOR_HEX_TO_FLOAT(0x000000, 1.0f), /* bar_disabled_color */ -1, /* progress */ {'\0'}, /* message */ false, /* active */ }; /* Callbacks */ static void gfx_widget_progress_message_fadeout_cb(void *userdata) { gfx_widget_progress_message_state_t *state = (gfx_widget_progress_message_state_t*)userdata; /* Deactivate widget */ state->active = false; } static void gfx_widget_progress_message_fadeout(void *userdata) { gfx_animation_ctx_entry_t animation_entry; gfx_widget_progress_message_state_t *state = (gfx_widget_progress_message_state_t*)userdata; uintptr_t alpha_tag = (uintptr_t)&state->alpha; /* Trigger fade out animation */ animation_entry.easing_enum = EASING_OUT_QUAD; animation_entry.tag = alpha_tag; animation_entry.duration = MSG_QUEUE_ANIMATION_DURATION; animation_entry.target_value = 0.0f; animation_entry.subject = &state->alpha; animation_entry.cb = gfx_widget_progress_message_fadeout_cb; animation_entry.userdata = state; gfx_animation_push(&animation_entry); } /* Widget interface */ void gfx_widget_set_progress_message( const char *message, unsigned duration, unsigned priority, int8_t progress) { size_t _len; gfx_timer_ctx_entry_t timer; dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr(); gfx_widget_progress_message_state_t *state = &p_w_progress_message_st; gfx_widget_font_data_t *font_regular = &p_dispwidget->gfx_widget_fonts.regular; uintptr_t alpha_tag = (uintptr_t)&state->alpha; uintptr_t timer_tag = (uintptr_t)&state->timer; /* Ensure we have a valid message string */ if (string_is_empty(message)) return; /* If widget is currently active, ignore new * message if it has a lower priority */ if (state->active && (state->priority > priority)) return; /* Cache message parameters */ _len = strlcpy(state->message, message, sizeof(state->message)); state->priority = priority; state->progress = progress; /* Cache text width */ state->text_width = font_driver_get_message_width( font_regular->font, state->message, _len, 1.0f); /* Kill any existing timer/animation */ gfx_animation_kill_by_tag(&timer_tag); gfx_animation_kill_by_tag(&alpha_tag); /* Start new message timer */ timer.duration = duration; timer.cb = gfx_widget_progress_message_fadeout; timer.userdata = state; gfx_animation_timer_start(&state->timer, &timer); /* Set initial widget opacity */ state->alpha = 1.0f; /* Set 'active' flag */ state->active = true; } /* Widget layout() */ static void gfx_widget_progress_message_layout( void *data, bool is_threaded, const char *dir_assets, char *font_path) { float bar_padding; dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)data; gfx_widget_progress_message_state_t *state = &p_w_progress_message_st; unsigned last_video_width = p_dispwidget->last_video_width; unsigned last_video_height = p_dispwidget->last_video_height; unsigned widget_padding = p_dispwidget->simple_widget_padding; gfx_widget_font_data_t *font_regular = &p_dispwidget->gfx_widget_fonts.regular; /* Base widget layout */ state->widget_width = last_video_width; state->widget_height = (unsigned)(((float)font_regular->line_height * 3.3f) + 0.5f); state->widget_x = 0.0f; state->widget_y = (float)(last_video_height - state->widget_height); /* Text layout */ state->text_x = (float)last_video_width / 2.0f; state->text_y = (float)(last_video_height - font_regular->line_height + font_regular->line_centre_offset); /* Progress bar layout */ state->bar_bg_width = last_video_width - (2 * widget_padding); state->bar_bg_height = (unsigned)(((float)font_regular->line_height * 0.7f) + 0.5f); state->bar_bg_x = (float)widget_padding; state->bar_bg_y = (float)last_video_height - (float)state->widget_height + (((float)state->widget_height - (font_regular->line_height * 1.5f)) * 0.5f) - ((float)state->bar_bg_height * 0.5f); state->bar_height = (unsigned)(((float)font_regular->line_height * 0.5f) + 0.5f); bar_padding = (float)(state->bar_bg_height - state->bar_height) * 0.5f; state->bar_max_width = state->bar_bg_width - (unsigned)((bar_padding * 2.0f) + 0.5f); state->bar_x = state->bar_bg_x + bar_padding; state->bar_y = state->bar_bg_y + bar_padding; } /* Widget frame() */ static void gfx_widget_progress_message_frame(void *data, void *user_data) { gfx_widget_progress_message_state_t *state = &p_w_progress_message_st; if (state->active) { video_frame_info_t *video_info = (video_frame_info_t*)data; dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)user_data; unsigned video_width = video_info->width; unsigned video_height = video_info->height; void *userdata = video_info->userdata; gfx_display_t *p_disp = (gfx_display_t*)video_info->disp_userdata; float *backdrop_color = p_dispwidget->backdrop_orig; unsigned text_color = COLOR_TEXT_ALPHA(0xFFFFFFFF, (unsigned)(state->alpha * 255.0f)); gfx_widget_font_data_t *font_regular = &p_dispwidget->gfx_widget_fonts.regular; size_t msg_queue_size = p_dispwidget->current_msgs_size; unsigned bar_width = state->bar_max_width; float *bar_color = state->bar_disabled_color; /* Draw backdrop */ gfx_display_set_alpha(backdrop_color, state->alpha * DEFAULT_BACKDROP); gfx_display_draw_quad( p_disp, userdata, video_width, video_height, state->widget_x, state->widget_y, state->widget_width, state->widget_height, video_width, video_height, backdrop_color, NULL); /* Draw progress bar background */ gfx_display_set_alpha(state->bar_bg_color, state->alpha); gfx_display_draw_quad( p_disp, userdata, video_width, video_height, state->bar_bg_x, state->bar_bg_y, state->bar_bg_width, state->bar_bg_height, video_width, video_height, state->bar_bg_color, NULL); /* Draw progress bar */ if (state->progress >= 0) { bar_width = (unsigned)((((float)state->progress / 100.0f) * (float)state->bar_max_width) + 0.5f); if (bar_width > state->bar_max_width) bar_width = state->bar_max_width; bar_color = state->bar_color; } gfx_display_set_alpha(bar_color, state->alpha); gfx_display_draw_quad( p_disp, userdata, video_width, video_height, state->bar_x, state->bar_y, bar_width, state->bar_height, video_width, video_height, bar_color, NULL); /* Draw message text */ gfx_widgets_draw_text( font_regular, state->message, state->text_x, state->text_y, video_width, video_height, text_color, TEXT_ALIGN_CENTER, true); /* If the message queue is active, must flush the * text here to avoid overlaps */ if (msg_queue_size > 0) gfx_widgets_flush_text(video_width, video_height, font_regular); } } /* Widget free() */ static void gfx_widget_progress_message_free(void) { gfx_widget_progress_message_state_t *state = &p_w_progress_message_st; uintptr_t alpha_tag = (uintptr_t)&state->alpha; uintptr_t timer_tag = (uintptr_t)&state->timer; /* Kill any existing timer / animation */ gfx_animation_kill_by_tag(&timer_tag); gfx_animation_kill_by_tag(&alpha_tag); /* Deactivate widget */ state->alpha = 0.0f; state->active = false; } /* Widget definition */ const gfx_widget_t gfx_widget_progress_message = { NULL, /* init */ gfx_widget_progress_message_free, NULL, /* context_reset*/ NULL, /* context_destroy */ gfx_widget_progress_message_layout, NULL, /* iterate */ gfx_widget_progress_message_frame };