1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2011-2017 - Daniel De Matteis
3 * Copyright (C) 2014-2017 - Jean-André Santoni
4 * Copyright (C) 2016-2019 - Brad Parker
5 * Copyright (C) 2018 - Alfredo Monclús
6 * Copyright (C) 2018 - natinusala
7 * Copyright (C) 2019 - Patrick Scheurenbrand
8 *
9 * RetroArch is free software: you can redistribute it and/or modify it under the terms
10 * of the GNU General Public License as published by the Free Software Found-
11 * ation, either version 3 of the License, or (at your option) any later version.
12 *
13 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
14 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with RetroArch.
18 * If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "ozone.h"
22 #include "ozone_texture.h"
23 #include "ozone_display.h"
24
25 #include <string/stdstring.h>
26 #include <encodings/utf.h>
27
28 #include "../../menu_driver.h"
29 #include "../../../gfx/gfx_animation.h"
30
31 #include "../../../configuration.h"
32
ozone_get_entries_padding(ozone_handle_t * ozone,bool old_list)33 static int ozone_get_entries_padding(ozone_handle_t* ozone, bool old_list)
34 {
35 if (ozone->depth == 1)
36 {
37 if (!old_list)
38 return ozone->dimensions.entry_padding_horizontal_half;
39 }
40 else if (ozone->depth == 2)
41 {
42 if (old_list && !ozone->fade_direction) /* false = left to right */
43 return ozone->dimensions.entry_padding_horizontal_half;
44 }
45 return ozone->dimensions.entry_padding_horizontal_full;
46 }
47
ozone_draw_entry_value(ozone_handle_t * ozone,gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,char * value,unsigned x,unsigned y,uint32_t alpha_uint32,menu_entry_t * entry)48 static void ozone_draw_entry_value(
49 ozone_handle_t *ozone,
50 gfx_display_t *p_disp,
51 void *userdata,
52 unsigned video_width,
53 unsigned video_height,
54 char *value,
55 unsigned x, unsigned y,
56 uint32_t alpha_uint32,
57 menu_entry_t *entry)
58 {
59 bool switch_is_on = true;
60 bool do_draw_text = false;
61 float scale_factor = ozone->last_scale_factor;
62 gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
63
64 /* check icon */
65 if (entry->checked)
66 {
67 float *col = ozone->theme_dynamic.entries_checkmark;
68 if (dispctx && dispctx->blend_begin)
69 dispctx->blend_begin(userdata);
70 ozone_draw_icon(
71 p_disp,
72 userdata,
73 video_width,
74 video_height,
75 30 * scale_factor,
76 30 * scale_factor,
77 ozone->theme->textures[OZONE_THEME_TEXTURE_CHECK],
78 x - 20 * scale_factor,
79 y - 22 * scale_factor,
80 video_width,
81 video_height,
82 0,
83 1,
84 col);
85 if (dispctx && dispctx->blend_end)
86 dispctx->blend_end(userdata);
87 return;
88 }
89 else if (string_is_empty(value))
90 return;
91
92
93 /* text value */
94 if (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) ||
95 (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))))
96 switch_is_on = false;
97 else if (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) ||
98 (string_is_equal(value, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)))) { }
99 else
100 {
101 if (!string_is_empty(entry->value))
102 {
103 if (string_is_equal(entry->value, "..."))
104 return;
105 if (string_starts_with_size(entry->value, "(", STRLEN_CONST("(")) &&
106 string_ends_with (entry->value, ")")
107 )
108 {
109 if (
110 string_is_equal(entry->value, "(PRESET)") ||
111 string_is_equal(entry->value, "(SHADER)") ||
112 string_is_equal(entry->value, "(COMP)") ||
113 string_is_equal(entry->value, "(CORE)") ||
114 string_is_equal(entry->value, "(MOVIE)") ||
115 string_is_equal(entry->value, "(MUSIC)") ||
116 string_is_equal(entry->value, "(DIR)") ||
117 string_is_equal(entry->value, "(RDB)") ||
118 string_is_equal(entry->value, "(CURSOR)")||
119 string_is_equal(entry->value, "(CFILE)") ||
120 string_is_equal(entry->value, "(FILE)") ||
121 string_is_equal(entry->value, "(IMAGE)")
122 )
123 return;
124 }
125 }
126
127 do_draw_text = true;
128 }
129
130 if (do_draw_text)
131 {
132 gfx_display_draw_text(
133 ozone->fonts.entries_label.font,
134 value,
135 x,
136 y,
137 video_width,
138 video_height,
139 COLOR_TEXT_ALPHA(ozone->theme->text_selected_rgba, alpha_uint32),
140 TEXT_ALIGN_RIGHT,
141 1.0f,
142 false,
143 1.0f,
144 false);
145 }
146 else
147 gfx_display_draw_text(
148 ozone->fonts.entries_label.font,
149 (switch_is_on ? msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON) : msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)),
150 x,
151 y,
152 video_width,
153 video_height,
154 COLOR_TEXT_ALPHA((switch_is_on ? ozone->theme->text_selected_rgba : ozone->theme->text_sublabel_rgba), alpha_uint32),
155 TEXT_ALIGN_RIGHT,
156 1.0f,
157 false,
158 1.0f,
159 false);
160 }
161
ozone_thumbnail_bar_hide_end(void * userdata)162 static void ozone_thumbnail_bar_hide_end(void *userdata)
163 {
164 ozone_handle_t *ozone = (ozone_handle_t*) userdata;
165 ozone->show_thumbnail_bar = false;
166 ozone->pending_hide_thumbnail_bar = false;
167 }
168
ozone_draw_no_thumbnail_available(ozone_handle_t * ozone,gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,unsigned x_position,unsigned sidebar_width,unsigned y_offset)169 static void ozone_draw_no_thumbnail_available(
170 ozone_handle_t *ozone,
171 gfx_display_t *p_disp,
172 void *userdata,
173 unsigned video_width,
174 unsigned video_height,
175 unsigned x_position,
176 unsigned sidebar_width,
177 unsigned y_offset)
178 {
179 unsigned icon = OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO;
180 unsigned icon_size = (unsigned)((float)
181 ozone->dimensions.sidebar_entry_icon_size * 1.5f);
182 gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
183 float *col = ozone->theme->entries_icon;
184
185 if (dispctx)
186 {
187 if (dispctx->blend_begin)
188 dispctx->blend_begin(userdata);
189 if (dispctx->draw)
190 ozone_draw_icon(
191 p_disp,
192 userdata,
193 video_width,
194 video_height,
195 icon_size,
196 icon_size,
197 ozone->icons_textures[icon],
198 x_position + sidebar_width/2 - icon_size/2,
199 video_height/2 - icon_size/2 - y_offset,
200 video_width,
201 video_height,
202 0, 1, col);
203 if (dispctx->blend_end)
204 dispctx->blend_end(userdata);
205 }
206
207 gfx_display_draw_text(
208 ozone->fonts.footer.font,
209 msg_hash_to_str(MSG_NO_THUMBNAIL_AVAILABLE),
210 x_position + sidebar_width / 2,
211 video_height / 2 + icon_size / 2
212 + ozone->fonts.footer.line_ascender - y_offset,
213 video_width,
214 video_height,
215 ozone->theme->text_rgba,
216 TEXT_ALIGN_CENTER,
217 1.0f,
218 false,
219 1.0f,
220 true);
221 }
222
ozone_content_metadata_line(unsigned video_width,unsigned video_height,ozone_handle_t * ozone,unsigned * y,unsigned column_x,const char * text,uint32_t color,unsigned lines_count)223 static void ozone_content_metadata_line(
224 unsigned video_width,
225 unsigned video_height,
226 ozone_handle_t *ozone,
227 unsigned *y,
228 unsigned column_x,
229 const char *text,
230 uint32_t color,
231 unsigned lines_count)
232 {
233 gfx_display_draw_text(
234 ozone->fonts.footer.font,
235 text,
236 column_x,
237 *y + ozone->fonts.footer.line_ascender,
238 video_width,
239 video_height,
240 color,
241 TEXT_ALIGN_LEFT,
242 1.0f,
243 false,
244 1.0f,
245 true);
246
247 if (lines_count > 0)
248 *y += (unsigned)(ozone->fonts.footer.line_height * (lines_count - 1)) + (unsigned)((float)ozone->fonts.footer.line_height * 1.5f);
249 }
250
251
252 /* Compute new scroll position
253 * If the center of the currently selected entry is not in the middle
254 * And if we can scroll so that it's in the middle
255 * Then scroll
256 */
ozone_update_scroll(ozone_handle_t * ozone,bool allow_animation,ozone_node_t * node)257 void ozone_update_scroll(ozone_handle_t *ozone, bool allow_animation, ozone_node_t *node)
258 {
259 unsigned video_info_height;
260 gfx_animation_ctx_entry_t entry;
261 float new_scroll = 0, entries_middle;
262 float bottom_boundary, current_selection_middle_onscreen;
263 file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
264 uintptr_t tag = (uintptr_t) selection_buf;
265
266 video_driver_get_size(NULL, &video_info_height);
267
268 current_selection_middle_onscreen =
269 ozone->dimensions.header_height +
270 ozone->dimensions.entry_padding_vertical +
271 ozone->animations.scroll_y +
272 node->position_y +
273 node->height / 2;
274
275 bottom_boundary = video_info_height - ozone->dimensions.header_height - ozone->dimensions.spacer_1px - ozone->dimensions.footer_height;
276 entries_middle = video_info_height/2;
277
278 new_scroll = ozone->animations.scroll_y - (current_selection_middle_onscreen - entries_middle);
279
280 if (new_scroll + ozone->entries_height < bottom_boundary)
281 new_scroll = bottom_boundary - ozone->entries_height - ozone->dimensions.entry_padding_vertical * 2;
282
283 if (new_scroll > 0)
284 new_scroll = 0;
285
286 /* Kill any existing scroll animation */
287 gfx_animation_kill_by_tag(&tag);
288
289 /* ozone->animations.scroll_y will be modified
290 * > Set scroll acceleration to zero to minimise
291 * potential conflicts */
292 menu_input_set_pointer_y_accel(0.0f);
293
294 if (allow_animation)
295 {
296 /* Cursor animation */
297 ozone->animations.cursor_alpha = 0.0f;
298
299 entry.cb = NULL;
300 entry.duration = ANIMATION_CURSOR_DURATION;
301 entry.easing_enum = EASING_OUT_QUAD;
302 entry.subject = &ozone->animations.cursor_alpha;
303 entry.tag = tag;
304 entry.target_value = 1.0f;
305 entry.userdata = NULL;
306
307 gfx_animation_push(&entry);
308
309 /* Scroll animation */
310 entry.cb = NULL;
311 entry.duration = ANIMATION_CURSOR_DURATION;
312 entry.easing_enum = EASING_OUT_QUAD;
313 entry.subject = &ozone->animations.scroll_y;
314 entry.tag = tag;
315 entry.target_value = new_scroll;
316 entry.userdata = NULL;
317
318 gfx_animation_push(&entry);
319 }
320 else
321 {
322 ozone->selection_old = ozone->selection;
323 ozone->animations.scroll_y = new_scroll;
324 }
325 }
326
ozone_compute_entries_position(ozone_handle_t * ozone,settings_t * settings,size_t entries_end)327 void ozone_compute_entries_position(
328 ozone_handle_t *ozone,
329 settings_t *settings,
330 size_t entries_end)
331 {
332 /* Compute entries height and adjust scrolling if needed */
333 unsigned video_info_height;
334 unsigned video_info_width;
335 size_t i;
336 file_list_t *selection_buf = NULL;
337 int entry_padding = ozone_get_entries_padding(ozone, false);
338 float scale_factor = ozone->last_scale_factor;
339 bool menu_show_sublabels = settings->bools.menu_show_sublabels;
340
341 menu_entries_ctl(MENU_ENTRIES_CTL_START_GET, &i);
342
343 selection_buf = menu_entries_get_selection_buf_ptr(0);
344
345 video_driver_get_size(&video_info_width, &video_info_height);
346
347 ozone->entries_height = 0;
348
349 for (i = 0; i < entries_end; i++)
350 {
351 /* Entry */
352 menu_entry_t entry;
353 ozone_node_t *node = NULL;
354
355 MENU_ENTRY_INIT(entry);
356 entry.path_enabled = false;
357 entry.label_enabled = false;
358 entry.rich_label_enabled = false;
359 entry.value_enabled = false;
360 menu_entry_get(&entry, 0, (unsigned)i, NULL, true);
361
362 /* Empty playlist detection:
363 only one item which icon is
364 OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO */
365 if (ozone->is_playlist && entries_end == 1)
366 {
367 uintptr_t tex = ozone_entries_icon_get_texture(ozone, entry.enum_idx, entry.type, false);
368 ozone->empty_playlist = tex == ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO];
369 }
370 else
371 ozone->empty_playlist = false;
372
373 /* Cache node */
374 node = (ozone_node_t*)selection_buf->list[i].userdata;
375
376 if (!node)
377 continue;
378
379 node->height = ozone->dimensions.entry_height;
380 node->wrap = false;
381 node->sublabel_lines = 0;
382
383 if (menu_show_sublabels)
384 {
385 if (!string_is_empty(entry.sublabel))
386 {
387 int sublabel_max_width;
388 char wrapped_sublabel_str[MENU_SUBLABEL_MAX_LENGTH];
389 wrapped_sublabel_str[0] = '\0';
390
391 node->height += ozone->dimensions.entry_spacing + 40 * scale_factor;
392
393 sublabel_max_width = video_info_width -
394 entry_padding * 2 - ozone->dimensions.entry_icon_padding * 2;
395
396 if (ozone->depth == 1)
397 {
398 sublabel_max_width -= (unsigned) ozone->dimensions_sidebar_width;
399
400 if (ozone->show_thumbnail_bar)
401 sublabel_max_width -= ozone->dimensions.thumbnail_bar_width;
402 }
403
404 (ozone->word_wrap)(wrapped_sublabel_str, sizeof(wrapped_sublabel_str), entry.sublabel,
405 sublabel_max_width /
406 ozone->fonts.entries_sublabel.glyph_width,
407 ozone->fonts.entries_sublabel.wideglyph_width, 0);
408
409 node->sublabel_lines = ozone_count_lines(wrapped_sublabel_str);
410
411 if (node->sublabel_lines > 1)
412 {
413 node->height += (node->sublabel_lines - 1) * ozone->fonts.entries_sublabel.line_height;
414 node->wrap = true;
415 }
416 }
417 }
418
419 node->position_y = ozone->entries_height;
420
421 ozone->entries_height += node->height;
422 }
423
424 /* Update scrolling */
425 ozone->selection = menu_navigation_get_selection();
426 ozone_update_scroll(ozone, false, (ozone_node_t*)selection_buf->list[ozone->selection].userdata);
427 }
428
ozone_entries_update_thumbnail_bar(ozone_handle_t * ozone,bool is_playlist,bool allow_animation)429 void ozone_entries_update_thumbnail_bar(ozone_handle_t *ozone, bool is_playlist, bool allow_animation)
430 {
431 struct gfx_animation_ctx_entry entry;
432 uintptr_t tag = (uintptr_t)&ozone->show_thumbnail_bar;
433
434 entry.duration = ANIMATION_CURSOR_DURATION;
435 entry.easing_enum = EASING_OUT_QUAD;
436 entry.tag = tag;
437 entry.subject = &ozone->animations.thumbnail_bar_position;
438
439 gfx_animation_kill_by_tag(&tag);
440
441 /* Show it
442 * > We only want to trigger a 'show' animation
443 * if 'show_thumbnail_bar' is currently false.
444 * However: 'show_thumbnail_bar' is only set
445 * to false by the 'ozone_thumbnail_bar_hide_end'
446 * callback. If the above 'gfx_animation_kill_by_tag()'
447 * kills an existing 'hide' animation, then the
448 * callback will not fire - so the sidebar will be
449 * off screen, but a subsequent attempt to show it
450 * here will fail, since 'show_thumbnail_bar' will
451 * be a false positive. We therefore require an
452 * additional 'pending_hide_thumbnail_bar' parameter
453 * to track mid-animation state changes... */
454 if (is_playlist &&
455 !ozone->cursor_in_sidebar &&
456 (!ozone->show_thumbnail_bar || ozone->pending_hide_thumbnail_bar) &&
457 (ozone->depth == 1))
458 {
459 if (allow_animation)
460 {
461 ozone->show_thumbnail_bar = true;
462
463 entry.cb = NULL;
464 entry.userdata = NULL;
465 entry.target_value = ozone->dimensions.thumbnail_bar_width;
466
467 gfx_animation_push(&entry);
468 }
469 else
470 {
471 ozone->animations.thumbnail_bar_position = ozone->dimensions.thumbnail_bar_width;
472 ozone->show_thumbnail_bar = true;
473 }
474
475 ozone->pending_hide_thumbnail_bar = false;
476 }
477 /* Hide it */
478 else
479 {
480 if (allow_animation)
481 {
482 entry.cb = ozone_thumbnail_bar_hide_end;
483 entry.userdata = ozone;
484 entry.target_value = 0.0f;
485
486 ozone->pending_hide_thumbnail_bar = true;
487 gfx_animation_push(&entry);
488 }
489 else
490 {
491 ozone->animations.thumbnail_bar_position = 0.0f;
492 ozone_thumbnail_bar_hide_end(ozone);
493 }
494 }
495 }
496
ozone_draw_entries(ozone_handle_t * ozone,gfx_display_t * p_disp,gfx_animation_t * p_anim,settings_t * settings,void * userdata,unsigned video_width,unsigned video_height,unsigned selection,unsigned selection_old,file_list_t * selection_buf,float alpha,float scroll_y,bool is_playlist)497 void ozone_draw_entries(
498 ozone_handle_t *ozone,
499 gfx_display_t *p_disp,
500 gfx_animation_t *p_anim,
501 settings_t *settings,
502 void *userdata,
503 unsigned video_width,
504 unsigned video_height,
505 unsigned selection,
506 unsigned selection_old,
507 file_list_t *selection_buf,
508 float alpha,
509 float scroll_y,
510 bool is_playlist)
511 {
512 uint32_t alpha_uint32;
513 size_t i;
514 float bottom_boundary;
515 unsigned video_info_height, video_info_width;
516 bool menu_show_sublabels = settings->bools.menu_show_sublabels;
517 bool use_smooth_ticker = settings->bools.menu_ticker_smooth;
518 enum gfx_animation_ticker_type
519 menu_ticker_type = (enum gfx_animation_ticker_type)
520 settings->uints.menu_ticker_type;
521 bool old_list = selection_buf == &ozone->selection_buf_old;
522 int x_offset = 0;
523 size_t selection_y = 0; /* 0 means no selection (we assume that no entry has y = 0) */
524 size_t old_selection_y = 0;
525 int entry_padding = ozone_get_entries_padding(ozone, old_list);
526 float scale_factor = ozone->last_scale_factor;
527 gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
528 size_t entries_end = selection_buf ? selection_buf->size : 0;
529 size_t y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.entry_padding_vertical;
530 float sidebar_offset = ozone->sidebar_offset;
531 unsigned entry_width = video_width - (unsigned) ozone->dimensions_sidebar_width - ozone->sidebar_offset - entry_padding * 2 - ozone->animations.thumbnail_bar_position;
532 unsigned button_height = ozone->dimensions.entry_height; /* height of the button (entry minus sublabel) */
533 float invert = (ozone->fade_direction) ? -1 : 1;
534 float alpha_anim = old_list ? alpha : 1.0f - alpha;
535
536 video_driver_get_size(&video_info_width, &video_info_height);
537
538 bottom_boundary = video_info_height - ozone->dimensions.header_height - ozone->dimensions.footer_height;
539
540 if (old_list)
541 {
542 alpha = 1.0f - alpha;
543 if (alpha != 1.0f)
544 x_offset += invert * -(alpha_anim * 120 * scale_factor); /* left */
545 }
546 else
547 {
548 if (alpha != 1.0f)
549 x_offset += invert * (alpha_anim * 120 * scale_factor); /* right */
550 }
551
552 x_offset += (int)sidebar_offset;
553 alpha_uint32 = (uint32_t)(alpha * 255.0f);
554
555 /* Borders layer */
556 for (i = 0; i < entries_end; i++)
557 {
558 bool entry_selected = selection == i;
559 bool entry_old_selected = selection_old == i;
560
561 int border_start_x, border_start_y;
562
563 ozone_node_t *node = NULL;
564
565 if (entry_selected && selection_y == 0)
566 selection_y = y;
567
568 if (entry_old_selected && old_selection_y == 0)
569 old_selection_y = y;
570
571 node = (ozone_node_t*)selection_buf->list[i].userdata;
572
573 if (!node || ozone->empty_playlist)
574 goto border_iterate;
575
576 if (y + scroll_y + node->height + 20 * scale_factor < ozone->dimensions.header_height + ozone->dimensions.entry_padding_vertical)
577 goto border_iterate;
578 else if (y + scroll_y - node->height - 20 * scale_factor > bottom_boundary)
579 goto border_iterate;
580
581 border_start_x = (unsigned) ozone->dimensions_sidebar_width
582 + x_offset + entry_padding;
583 border_start_y = y + scroll_y;
584
585 gfx_display_set_alpha(ozone->theme_dynamic.entries_border, alpha);
586 gfx_display_set_alpha(ozone->theme_dynamic.entries_checkmark, alpha);
587
588 /* Borders */
589 gfx_display_draw_quad(
590 p_disp,
591 userdata,
592 video_width,
593 video_height,
594 border_start_x,
595 border_start_y,
596 entry_width,
597 ozone->dimensions.spacer_1px,
598 video_width,
599 video_height,
600 ozone->theme_dynamic.entries_border);
601 gfx_display_draw_quad(
602 p_disp,
603 userdata,
604 video_width,
605 video_height,
606 border_start_x,
607 border_start_y + button_height,
608 entry_width,
609 ozone->dimensions.spacer_1px,
610 video_width,
611 video_height,
612 ozone->theme_dynamic.entries_border);
613
614 border_iterate:
615 if (node)
616 y += node->height;
617 }
618
619 /* Cursor(s) layer - current */
620 if (!ozone->cursor_in_sidebar)
621 ozone_draw_cursor(
622 ozone,
623 p_disp,
624 userdata,
625 video_width,
626 video_height,
627 (unsigned) ozone->dimensions_sidebar_width
628 + x_offset + entry_padding + ozone->dimensions.spacer_3px,
629 entry_width - ozone->dimensions.spacer_5px,
630 button_height + ozone->dimensions.spacer_1px,
631 selection_y + scroll_y,
632 ozone->animations.cursor_alpha * alpha);
633
634 /* Old*/
635 if (!ozone->cursor_in_sidebar_old)
636 ozone_draw_cursor(
637 ozone,
638 p_disp,
639 userdata,
640 video_width,
641 video_height,
642 (unsigned)ozone->dimensions_sidebar_width
643 + x_offset + entry_padding + ozone->dimensions.spacer_3px,
644 /* TODO/FIXME - undefined behavior reported by ASAN -
645 *-35.2358 is outside the range of representable values
646 of type 'unsigned int'
647 * */
648 entry_width - ozone->dimensions.spacer_5px,
649 button_height + ozone->dimensions.spacer_1px,
650 old_selection_y + scroll_y,
651 (1-ozone->animations.cursor_alpha) * alpha);
652
653 /* Icons + text */
654 y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.entry_padding_vertical;
655
656 if (old_list)
657 y += ozone->old_list_offset_y;
658
659 for (i = 0; i < entries_end; i++)
660 {
661 char rich_label[255];
662 char entry_value_ticker[255];
663 char wrapped_sublabel_str[MENU_SUBLABEL_MAX_LENGTH];
664 uintptr_t tex;
665 menu_entry_t entry;
666 gfx_animation_ctx_ticker_t ticker;
667 gfx_animation_ctx_ticker_smooth_t ticker_smooth;
668 unsigned ticker_x_offset = 0;
669 unsigned ticker_str_width = 0;
670 int value_x_offset = 0;
671 static const char* const
672 ticker_spacer = OZONE_TICKER_SPACER;
673 const char *sublabel_str = NULL;
674 ozone_node_t *node = NULL;
675 const char *entry_rich_label = NULL;
676 const char *entry_value = NULL;
677 bool entry_selected = false;
678 int text_offset = -ozone->dimensions.entry_icon_padding - ozone->dimensions.entry_icon_size;
679 float *icon_color = NULL;
680
681 /* Initial ticker configuration */
682 if (use_smooth_ticker)
683 {
684 ticker_smooth.idx = p_anim->ticker_pixel_idx;
685 ticker_smooth.font = ozone->fonts.entries_label.font;
686 ticker_smooth.font_scale = 1.0f;
687 ticker_smooth.type_enum = menu_ticker_type;
688 ticker_smooth.spacer = ticker_spacer;
689 ticker_smooth.x_offset = &ticker_x_offset;
690 ticker_smooth.dst_str_width = &ticker_str_width;
691 }
692 else
693 {
694 ticker.idx = p_anim->ticker_idx;
695 ticker.type_enum = menu_ticker_type;
696 ticker.spacer = ticker_spacer;
697 }
698
699 node = (ozone_node_t*)selection_buf->list[i].userdata;
700
701 if (!node)
702 continue;
703
704 if (y + scroll_y + node->height + 20 * scale_factor
705 < ozone->dimensions.header_height
706 + ozone->dimensions.entry_padding_vertical)
707 {
708 y += node->height;
709 continue;
710 }
711 else if (y + scroll_y - node->height - 20 * scale_factor
712 > bottom_boundary)
713 {
714 y += node->height;
715 continue;
716 }
717
718 entry_selected = selection == i;
719
720 MENU_ENTRY_INIT(entry);
721 entry.path_enabled = false;
722 entry.label_enabled = false;
723 menu_entry_get(&entry, 0, (unsigned)i, selection_buf, true);
724
725 if (entry.enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD)
726 entry_value = entry.password_value;
727 else
728 entry_value = entry.value;
729
730 /* Prepare text */
731 if (!string_is_empty(entry.rich_label))
732 entry_rich_label = entry.rich_label;
733 else
734 entry_rich_label = entry.path;
735
736 if (use_smooth_ticker)
737 {
738 ticker_smooth.selected = entry_selected && !ozone->cursor_in_sidebar;
739 ticker_smooth.field_width = entry_width - entry_padding - (10 * scale_factor) - ozone->dimensions.entry_icon_padding;
740 ticker_smooth.src_str = entry_rich_label;
741 ticker_smooth.dst_str = rich_label;
742 ticker_smooth.dst_str_len = sizeof(rich_label);
743
744 gfx_animation_ticker_smooth(&ticker_smooth);
745 }
746 else
747 {
748 ticker.s = rich_label;
749 ticker.str = entry_rich_label;
750 ticker.selected = entry_selected && !ozone->cursor_in_sidebar;
751 ticker.len = (entry_width - entry_padding - (10 * scale_factor) - ozone->dimensions.entry_icon_padding) / ozone->fonts.entries_label.glyph_width;
752
753 gfx_animation_ticker(&ticker);
754 }
755
756 if (ozone->empty_playlist)
757 {
758 /* Note: This entry can never be selected, so ticker_x_offset
759 * is irrelevant here (i.e. this text will never scroll) */
760 unsigned text_width = font_driver_get_message_width(ozone->fonts.entries_label.font, rich_label, (unsigned)strlen(rich_label), 1);
761 x_offset = (video_info_width - (unsigned)
762 ozone->dimensions_sidebar_width - entry_padding * 2)
763 / 2 - text_width / 2 - 60 * scale_factor;
764 y = video_info_height / 2 - 60 * scale_factor;
765 }
766
767 sublabel_str = entry.sublabel;
768
769 if (menu_show_sublabels)
770 {
771 if (node->wrap && !string_is_empty(sublabel_str))
772 {
773 int sublabel_max_width = video_info_width -
774 entry_padding * 2 - ozone->dimensions.entry_icon_padding * 2;
775
776 if (ozone->depth == 1)
777 {
778 sublabel_max_width -= (unsigned)
779 ozone->dimensions_sidebar_width;
780
781 if (ozone->show_thumbnail_bar)
782 sublabel_max_width -= ozone->dimensions.thumbnail_bar_width;
783 }
784
785 wrapped_sublabel_str[0] = '\0';
786 (ozone->word_wrap)(wrapped_sublabel_str, sizeof(wrapped_sublabel_str),
787 sublabel_str, sublabel_max_width / ozone->fonts.entries_sublabel.glyph_width,
788 ozone->fonts.entries_sublabel.wideglyph_width, 0);
789 sublabel_str = wrapped_sublabel_str;
790 }
791 }
792
793 /* Icon */
794 tex = ozone_entries_icon_get_texture(ozone, entry.enum_idx, entry.type, entry_selected);
795 if (tex != ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING])
796 {
797 uintptr_t texture = tex;
798
799 /* Console specific icons */
800 if ( entry.type == FILE_TYPE_RPL_ENTRY
801 && ozone->categories_selection_ptr > ozone->system_tab_end)
802 {
803 ozone_node_t *sidebar_node = (ozone_node_t*) file_list_get_userdata_at_offset(&ozone->horizontal_list, ozone->categories_selection_ptr - ozone->system_tab_end-1);
804
805 if (!sidebar_node || !sidebar_node->content_icon)
806 texture = tex;
807 else
808 texture = sidebar_node->content_icon;
809 }
810
811 /* Cheevos badges should not be recolored */
812 if (!(
813 (entry.type >= MENU_SETTINGS_CHEEVOS_START) &&
814 (entry.type < MENU_SETTINGS_NETPLAY_ROOMS_START)
815 ))
816 icon_color = ozone->theme_dynamic.entries_icon;
817 else
818 icon_color = ozone->pure_white;
819
820 gfx_display_set_alpha(icon_color, alpha);
821
822 if (dispctx)
823 {
824 if (dispctx->blend_begin)
825 dispctx->blend_begin(userdata);
826 if (dispctx->draw)
827 ozone_draw_icon(
828 p_disp,
829 userdata,
830 video_width,
831 video_height,
832 ozone->dimensions.entry_icon_size,
833 ozone->dimensions.entry_icon_size,
834 texture,
835 (unsigned)ozone->dimensions_sidebar_width
836 + x_offset + entry_padding
837 + ozone->dimensions.entry_icon_padding,
838 y + scroll_y + ozone->dimensions.entry_height
839 / 2 - ozone->dimensions.entry_icon_size / 2,
840 video_width,
841 video_height,
842 0,
843 1,
844 icon_color);
845 if (dispctx->blend_end)
846 dispctx->blend_end(userdata);
847 }
848
849 if (icon_color == ozone->pure_white)
850 gfx_display_set_alpha(icon_color, 1.0f);
851
852 text_offset = 0;
853 }
854
855 /* Draw text */
856 gfx_display_draw_text(
857 ozone->fonts.entries_label.font,
858 rich_label,
859 ticker_x_offset + text_offset + (unsigned)
860 ozone->dimensions_sidebar_width + x_offset +
861 entry_padding + ozone->dimensions.entry_icon_size +
862 ozone->dimensions.entry_icon_padding * 2,
863 y + ozone->dimensions.entry_height / 2.0f +
864 ozone->fonts.entries_label.line_centre_offset +
865 scroll_y,
866 video_width,
867 video_height,
868 COLOR_TEXT_ALPHA(ozone->theme->text_rgba, alpha_uint32),
869 TEXT_ALIGN_LEFT,
870 1.0f,
871 false,
872 1.0f,
873 false);
874
875 if (menu_show_sublabels)
876 {
877 if (!string_is_empty(sublabel_str))
878 gfx_display_draw_text(
879 ozone->fonts.entries_sublabel.font,
880 sublabel_str,
881 (unsigned) ozone->dimensions_sidebar_width +
882 x_offset + entry_padding +
883 ozone->dimensions.entry_icon_padding,
884 y + ozone->dimensions.entry_height - ozone->dimensions.spacer_1px + (node->height - ozone->dimensions.entry_height - (node->sublabel_lines * ozone->fonts.entries_sublabel.line_height))/2.0f + ozone->fonts.entries_sublabel.line_ascender + scroll_y,
885 video_width,
886 video_height,
887 COLOR_TEXT_ALPHA(ozone->theme->text_sublabel_rgba,alpha_uint32),
888 TEXT_ALIGN_LEFT,
889 1.0f,
890 false,
891 1.0f,
892 false);
893 }
894
895 /* Value */
896 if (use_smooth_ticker)
897 {
898 ticker_smooth.selected = entry_selected && !ozone->cursor_in_sidebar;
899 ticker_smooth.field_width = (entry_width - ozone->dimensions.entry_icon_size - ozone->dimensions.entry_icon_padding * 2 -
900 ((unsigned)utf8len(entry_rich_label) * ozone->fonts.entries_label.glyph_width));
901 ticker_smooth.src_str = entry_value;
902 ticker_smooth.dst_str = entry_value_ticker;
903 ticker_smooth.dst_str_len = sizeof(entry_value_ticker);
904
905 /* Value text is right aligned, so have to offset x
906 * by the 'padding' width at the end of the ticker string... */
907 if (gfx_animation_ticker_smooth(&ticker_smooth))
908 value_x_offset = (ticker_x_offset + ticker_str_width) - ticker_smooth.field_width;
909 }
910 else
911 {
912 ticker.s = entry_value_ticker;
913 ticker.str = entry_value;
914 ticker.selected = entry_selected && !ozone->cursor_in_sidebar;
915 ticker.len = (entry_width - ozone->dimensions.entry_icon_size - ozone->dimensions.entry_icon_padding * 2 -
916 ((unsigned)utf8len(entry_rich_label) * ozone->fonts.entries_label.glyph_width)) / ozone->fonts.entries_label.glyph_width;
917
918 gfx_animation_ticker(&ticker);
919 }
920
921 ozone_draw_entry_value(ozone,
922 p_disp,
923 userdata,
924 video_width,
925 video_height,
926 entry_value_ticker,
927 value_x_offset + (unsigned) ozone->dimensions_sidebar_width
928 + entry_padding + x_offset
929 + entry_width - ozone->dimensions.entry_icon_padding,
930 y + ozone->dimensions.entry_height / 2 + ozone->fonts.entries_label.line_centre_offset + scroll_y,
931 alpha_uint32,
932 &entry);
933
934 y += node->height;
935 }
936
937 /* Text layer */
938 ozone_font_flush(video_width, video_height, &ozone->fonts.entries_label);
939
940 if (menu_show_sublabels)
941 ozone_font_flush(video_width, video_height, &ozone->fonts.entries_sublabel);
942 }
943
ozone_draw_thumbnail_bar(ozone_handle_t * ozone,gfx_display_t * p_disp,gfx_animation_t * p_anim,settings_t * settings,void * userdata,unsigned video_width,unsigned video_height,bool libretro_running,float menu_framebuffer_opacity)944 void ozone_draw_thumbnail_bar(
945 ozone_handle_t *ozone,
946 gfx_display_t *p_disp,
947 gfx_animation_t *p_anim,
948 settings_t *settings,
949 void *userdata,
950 unsigned video_width,
951 unsigned video_height,
952 bool libretro_running,
953 float menu_framebuffer_opacity)
954 {
955 enum gfx_thumbnail_alignment right_thumbnail_alignment;
956 enum gfx_thumbnail_alignment left_thumbnail_alignment;
957 unsigned sidebar_width = ozone->dimensions.thumbnail_bar_width;
958 unsigned thumbnail_width = sidebar_width - (ozone->dimensions.sidebar_entry_icon_padding * 2);
959 int right_thumbnail_y_position = 0;
960 int left_thumbnail_y_position = 0;
961 int bottom_row_y_position = 0;
962 bool show_right_thumbnail = false;
963 bool show_left_thumbnail = false;
964 unsigned sidebar_height = video_height - ozone->dimensions.header_height - ozone->dimensions.sidebar_gradient_height * 2 - ozone->dimensions.footer_height;
965 unsigned x_position = video_width - (unsigned) ozone->animations.thumbnail_bar_position;
966 int thumbnail_x_position = x_position + ozone->dimensions.sidebar_entry_icon_padding;
967 unsigned thumbnail_height = (video_height - ozone->dimensions.header_height - ozone->dimensions.spacer_2px - ozone->dimensions.footer_height - (ozone->dimensions.sidebar_entry_icon_padding * 3)) / 2;
968 float scale_factor = ozone->last_scale_factor;
969 gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
970
971 /* Background */
972 if (!libretro_running || (menu_framebuffer_opacity >= 1.0f))
973 {
974 gfx_display_draw_quad(
975 p_disp,
976 userdata,
977 video_width,
978 video_height,
979 x_position,
980 ozone->dimensions.header_height + ozone->dimensions.spacer_1px,
981 (unsigned)ozone->animations.thumbnail_bar_position,
982 ozone->dimensions.sidebar_gradient_height,
983 video_width,
984 video_height,
985 ozone->theme->sidebar_top_gradient);
986 gfx_display_draw_quad(
987 p_disp,
988 userdata,
989 video_width,
990 video_height,
991 x_position,
992 ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_gradient_height,
993 (unsigned)ozone->animations.thumbnail_bar_position,
994 sidebar_height,
995 video_width,
996 video_height,
997 ozone->theme->sidebar_background);
998 gfx_display_draw_quad(
999 p_disp,
1000 userdata,
1001 video_width,
1002 video_height,
1003 x_position,
1004 video_height - ozone->dimensions.footer_height - ozone->dimensions.sidebar_gradient_height - ozone->dimensions.spacer_1px,
1005 (unsigned) ozone->animations.thumbnail_bar_position,
1006 ozone->dimensions.sidebar_gradient_height + ozone->dimensions.spacer_1px,
1007 video_width,
1008 video_height,
1009 ozone->theme->sidebar_bottom_gradient);
1010 }
1011
1012 /* Thumbnails */
1013 show_right_thumbnail =
1014 (ozone->thumbnails.right.status != GFX_THUMBNAIL_STATUS_MISSING) &&
1015 gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_RIGHT);
1016 show_left_thumbnail =
1017 (ozone->thumbnails.left.status != GFX_THUMBNAIL_STATUS_MISSING) &&
1018 gfx_thumbnail_is_enabled(ozone->thumbnail_path_data, GFX_THUMBNAIL_LEFT) &&
1019 !ozone->selection_core_is_viewer;
1020
1021 /* If this entry is associated with the image viewer
1022 * and no right thumbnail is available, show a centred
1023 * message and return immediately */
1024 if (ozone->selection_core_is_viewer && !show_right_thumbnail)
1025 {
1026 ozone_draw_no_thumbnail_available(
1027 ozone,
1028 p_disp,
1029 userdata,
1030 video_width,
1031 video_height,
1032 x_position, sidebar_width, 0);
1033 return;
1034 }
1035
1036 /* Top row
1037 * > Displays one item, with the following order
1038 * of preference:
1039 * 1) Right thumbnail, if available
1040 * 2) Left thumbnail, if available
1041 * 3) 'No thumbnail available' message */
1042
1043 /* > If this entry is associated with the image viewer
1044 * core, there can be only one thumbnail and no
1045 * content metadata -> centre image vertically */
1046 if (ozone->selection_core_is_viewer)
1047 {
1048 right_thumbnail_y_position =
1049 ozone->dimensions.header_height +
1050 ((thumbnail_height / 2) +
1051 (int)(1.5f * (float)ozone->dimensions.sidebar_entry_icon_padding));
1052
1053 right_thumbnail_alignment = GFX_THUMBNAIL_ALIGN_CENTRE;
1054 }
1055 else
1056 {
1057 right_thumbnail_y_position =
1058 ozone->dimensions.header_height + ozone->dimensions.spacer_1px +
1059 ozone->dimensions.sidebar_entry_icon_padding;
1060
1061 right_thumbnail_alignment = GFX_THUMBNAIL_ALIGN_BOTTOM;
1062 }
1063
1064 /* > If we have a right thumbnail, show it */
1065 if (show_right_thumbnail)
1066 gfx_thumbnail_draw(
1067 userdata,
1068 video_width,
1069 video_height,
1070 &ozone->thumbnails.right,
1071 (float)thumbnail_x_position,
1072 (float)right_thumbnail_y_position,
1073 thumbnail_width,
1074 thumbnail_height,
1075 right_thumbnail_alignment,
1076 1.0f, 1.0f, NULL);
1077 /* > If we have neither a right thumbnail nor
1078 * a left thumbnail to show in its place,
1079 * display 'no thumbnail available' message */
1080 else if (!show_left_thumbnail)
1081 ozone_draw_no_thumbnail_available(
1082 ozone,
1083 p_disp,
1084 userdata,
1085 video_width,
1086 video_height,
1087 x_position,
1088 sidebar_width,
1089 thumbnail_height / 2);
1090
1091 /* Bottom row
1092 * > Displays one item, with the following order
1093 * of preference:
1094 * 1) Left thumbnail, if available
1095 * *and*
1096 * right thumbnail has been placed in the top row
1097 * *and*
1098 * content metadata override is not enabled
1099 * 2) Content metadata */
1100
1101 /* > Get baseline 'start' position of bottom row */
1102 bottom_row_y_position = ozone->dimensions.header_height + ozone->dimensions.spacer_1px +
1103 thumbnail_height +
1104 (ozone->dimensions.sidebar_entry_icon_padding * 2);
1105
1106 /* > If we have a left thumbnail, show it */
1107 if (show_left_thumbnail)
1108 {
1109 float left_thumbnail_alpha;
1110
1111 /* Normally a right thumbnail will be shown
1112 * in the top row - if so, left thumbnail
1113 * goes at the bottom */
1114 if (show_right_thumbnail)
1115 {
1116 left_thumbnail_y_position = bottom_row_y_position;
1117 left_thumbnail_alignment = GFX_THUMBNAIL_ALIGN_TOP;
1118 /* In this case, thumbnail opacity is dependent
1119 * upon the content metadata override
1120 * > i.e. Need to handle fade in/out animations
1121 * and set opacity to zero when override
1122 * is fully active */
1123 left_thumbnail_alpha = ozone->animations.left_thumbnail_alpha;
1124 }
1125 /* If right thumbnail is missing, shift left
1126 * thumbnail up to the top row */
1127 else
1128 {
1129 left_thumbnail_y_position = right_thumbnail_y_position;
1130 left_thumbnail_alignment = right_thumbnail_alignment;
1131 /* In this case, there is no dependence on content
1132 * metadata - thumbnail is always shown at full
1133 * opacity */
1134 left_thumbnail_alpha = 1.0f;
1135 }
1136
1137 /* Note: This is a NOOP when alpha is zero
1138 * (i.e. no performance impact when content
1139 * metadata override is fully active) */
1140 gfx_thumbnail_draw(
1141 userdata,
1142 video_width,
1143 video_height,
1144 &ozone->thumbnails.left,
1145 (float)thumbnail_x_position,
1146 (float)left_thumbnail_y_position,
1147 thumbnail_width,
1148 thumbnail_height,
1149 left_thumbnail_alignment,
1150 left_thumbnail_alpha,
1151 1.0f, NULL);
1152 }
1153
1154 /* > Display content metadata in the bottom
1155 * row if:
1156 * - This is *not* image viewer content
1157 * *and*
1158 * - There is no left thumbnail
1159 * *or*
1160 * left thumbnail has been shifted to
1161 * the top row
1162 * *or*
1163 * content metadata override is enabled
1164 * (i.e. fade in, fade out, or fully
1165 * active) */
1166 if (!ozone->selection_core_is_viewer &&
1167 (!show_left_thumbnail || !show_right_thumbnail ||
1168 (ozone->animations.left_thumbnail_alpha < 1.0f)))
1169 {
1170 char ticker_buf[255];
1171 gfx_animation_ctx_ticker_t ticker;
1172 gfx_animation_ctx_ticker_smooth_t ticker_smooth;
1173 static const char* const ticker_spacer = OZONE_TICKER_SPACER;
1174 unsigned ticker_x_offset = 0;
1175 bool scroll_content_metadata = settings->bools.ozone_scroll_content_metadata;
1176 bool use_smooth_ticker = settings->bools.menu_ticker_smooth;
1177 enum gfx_animation_ticker_type
1178 menu_ticker_type = (enum gfx_animation_ticker_type)
1179 settings->uints.menu_ticker_type;
1180 bool show_entry_idx = settings->bools.playlist_show_entry_idx;
1181 unsigned y = (unsigned)bottom_row_y_position;
1182 unsigned separator_padding = ozone->dimensions.sidebar_entry_icon_padding*2;
1183 unsigned column_x = x_position + separator_padding;
1184 bool metadata_override_enabled = show_left_thumbnail &&
1185 show_right_thumbnail &&
1186 (ozone->animations.left_thumbnail_alpha < 1.0f);
1187 float metadata_alpha = metadata_override_enabled ?
1188 (1.0f - ozone->animations.left_thumbnail_alpha) : 1.0f;
1189 uint32_t text_color = COLOR_TEXT_ALPHA(
1190 ozone->theme->text_rgba, (uint32_t)(metadata_alpha * 255.0f));
1191
1192 if (scroll_content_metadata)
1193 {
1194 /* Initial ticker configuration */
1195 if (use_smooth_ticker)
1196 {
1197 ticker_smooth.idx = p_anim->ticker_pixel_idx;
1198 ticker_smooth.font_scale = 1.0f;
1199 ticker_smooth.type_enum = menu_ticker_type;
1200 ticker_smooth.spacer = ticker_spacer;
1201 ticker_smooth.x_offset = &ticker_x_offset;
1202 ticker_smooth.dst_str_width = NULL;
1203
1204 ticker_smooth.font = ozone->fonts.footer.font;
1205 ticker_smooth.selected = true;
1206 ticker_smooth.field_width = sidebar_width - (separator_padding * 2);
1207 ticker_smooth.dst_str = ticker_buf;
1208 ticker_smooth.dst_str_len = sizeof(ticker_buf);
1209 }
1210 else
1211 {
1212 ticker.idx = p_anim->ticker_idx;
1213 ticker.type_enum = menu_ticker_type;
1214 ticker.spacer = ticker_spacer;
1215
1216 ticker.selected = true;
1217 ticker.len = (sidebar_width - (separator_padding * 2)) / ozone->fonts.footer.glyph_width;
1218 ticker.s = ticker_buf;
1219 }
1220 }
1221
1222 /* Content metadata */
1223
1224 /* Separator */
1225 gfx_display_set_alpha(ozone->theme_dynamic.entries_border, metadata_alpha);
1226
1227 gfx_display_draw_quad(
1228 p_disp,
1229 userdata,
1230 video_width,
1231 video_height,
1232 x_position + separator_padding,
1233 y,
1234 sidebar_width - separator_padding*2,
1235 ozone->dimensions.spacer_1px,
1236 video_width,
1237 video_height,
1238 ozone->theme_dynamic.entries_border);
1239
1240 y += 18 * scale_factor;
1241
1242 if (scroll_content_metadata)
1243 {
1244 /* Entry enumeration */
1245 if (show_entry_idx)
1246 {
1247 ticker_buf[0] = '\0';
1248
1249 if (use_smooth_ticker)
1250 {
1251 ticker_smooth.src_str = ozone->selection_entry_enumeration;
1252 gfx_animation_ticker_smooth(&ticker_smooth);
1253 }
1254 else
1255 {
1256 ticker.str = ozone->selection_entry_enumeration;
1257 gfx_animation_ticker(&ticker);
1258 }
1259
1260 ozone_content_metadata_line(
1261 video_width,
1262 video_height,
1263 ozone,
1264 &y,
1265 ticker_x_offset + column_x,
1266 ticker_buf,
1267 text_color,
1268 1);
1269 }
1270
1271 /* Core association */
1272 ticker_buf[0] = '\0';
1273
1274 if (use_smooth_ticker)
1275 {
1276 ticker_smooth.src_str = ozone->selection_core_name;
1277 gfx_animation_ticker_smooth(&ticker_smooth);
1278 }
1279 else
1280 {
1281 ticker.str = ozone->selection_core_name;
1282 gfx_animation_ticker(&ticker);
1283 }
1284
1285 ozone_content_metadata_line(
1286 video_width,
1287 video_height,
1288 ozone,
1289 &y,
1290 ticker_x_offset + column_x,
1291 ticker_buf,
1292 text_color,
1293 1);
1294
1295 /* Playtime
1296 * Note: It is essentially impossible for this string
1297 * to exceed the width of the sidebar, but since we
1298 * are ticker-texting everything else, we include this
1299 * by default */
1300 ticker_buf[0] = '\0';
1301
1302 if (use_smooth_ticker)
1303 {
1304 ticker_smooth.src_str = ozone->selection_playtime;
1305 gfx_animation_ticker_smooth(&ticker_smooth);
1306 }
1307 else
1308 {
1309 ticker.str = ozone->selection_playtime;
1310 gfx_animation_ticker(&ticker);
1311 }
1312
1313 ozone_content_metadata_line(
1314 video_width,
1315 video_height,
1316 ozone,
1317 &y,
1318 ticker_x_offset + column_x,
1319 ticker_buf,
1320 text_color,
1321 1);
1322
1323 /* Last played */
1324 ticker_buf[0] = '\0';
1325
1326 if (use_smooth_ticker)
1327 {
1328 ticker_smooth.src_str = ozone->selection_lastplayed;
1329 gfx_animation_ticker_smooth(&ticker_smooth);
1330 }
1331 else
1332 {
1333 ticker.str = ozone->selection_lastplayed;
1334 gfx_animation_ticker(&ticker);
1335 }
1336
1337 ozone_content_metadata_line(
1338 video_width,
1339 video_height,
1340 ozone,
1341 &y,
1342 ticker_x_offset + column_x,
1343 ticker_buf,
1344 text_color,
1345 1);
1346 }
1347 else
1348 {
1349 /* Entry enumeration */
1350 if (show_entry_idx)
1351 ozone_content_metadata_line(
1352 video_width,
1353 video_height,
1354 ozone,
1355 &y,
1356 column_x,
1357 ozone->selection_entry_enumeration,
1358 text_color,
1359 1);
1360
1361 /* Core association */
1362 ozone_content_metadata_line(
1363 video_width,
1364 video_height,
1365 ozone,
1366 &y,
1367 column_x,
1368 ozone->selection_core_name,
1369 text_color,
1370 ozone->selection_core_name_lines);
1371
1372 /* Playtime */
1373 ozone_content_metadata_line(
1374 video_width,
1375 video_height,
1376 ozone,
1377 &y,
1378 column_x,
1379 ozone->selection_playtime,
1380 text_color,
1381 1);
1382
1383 /* Last played */
1384 ozone_content_metadata_line(
1385 video_width,
1386 video_height,
1387 ozone,
1388 &y,
1389 column_x,
1390 ozone->selection_lastplayed,
1391 text_color,
1392 ozone->selection_lastplayed_lines);
1393 }
1394
1395 /* If metadata override is active, display an
1396 * icon to notify that a left thumbnail image
1397 * is available */
1398 if (metadata_override_enabled)
1399 {
1400 float *col = ozone->theme_dynamic.entries_icon;
1401 /* Icon should be small and unobtrusive
1402 * > Make it 80% of the normal entry icon size */
1403 unsigned icon_size = (unsigned)((float)ozone->dimensions.sidebar_entry_icon_size * 0.8f);
1404
1405 /* > Set its opacity to a maximum of 80% */
1406 gfx_display_set_alpha(ozone->theme_dynamic.entries_icon, metadata_alpha * 0.8f);
1407
1408 if (dispctx)
1409 {
1410 /* Draw icon in the bottom right corner of
1411 * the thumbnail bar */
1412 if (dispctx->blend_begin)
1413 dispctx->blend_begin(userdata);
1414 if (dispctx->draw)
1415 ozone_draw_icon(
1416 p_disp,
1417 userdata,
1418 video_width,
1419 video_height,
1420 icon_size,
1421 icon_size,
1422 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_IMAGE],
1423 x_position + sidebar_width - separator_padding - icon_size,
1424 video_height - ozone->dimensions.footer_height - ozone->dimensions.sidebar_entry_icon_padding - icon_size,
1425 video_width,
1426 video_height,
1427 0, 1, col);
1428 if (dispctx->blend_end)
1429 dispctx->blend_end(userdata);
1430 }
1431 }
1432 }
1433 }
1434