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-2020 - 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_theme.h"
23 #include "ozone_display.h"
24 #include "ozone_sidebar.h"
25
26 #include <string/stdstring.h>
27 #include <file/file_path.h>
28 #include <formats/image.h>
29
30 #include "../../../gfx/gfx_animation.h"
31
32 #include "../../../configuration.h"
33
34 static const enum msg_hash_enums ozone_system_tabs_value[OZONE_SYSTEM_TAB_LAST] = {
35 MENU_ENUM_LABEL_VALUE_MAIN_MENU,
36 MENU_ENUM_LABEL_VALUE_SETTINGS_TAB,
37 MENU_ENUM_LABEL_VALUE_HISTORY_TAB,
38 MENU_ENUM_LABEL_VALUE_FAVORITES_TAB,
39 MENU_ENUM_LABEL_VALUE_MUSIC_TAB,
40 #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
41 MENU_ENUM_LABEL_VALUE_VIDEO_TAB,
42 #endif
43 #ifdef HAVE_IMAGEVIEWER
44 MENU_ENUM_LABEL_VALUE_IMAGES_TAB,
45 #endif
46 #ifdef HAVE_NETWORKING
47 MENU_ENUM_LABEL_VALUE_NETPLAY_TAB,
48 #endif
49 #ifdef HAVE_LIBRETRODB
50 MENU_ENUM_LABEL_VALUE_ADD_TAB,
51 MENU_ENUM_LABEL_VALUE_EXPLORE_TAB
52 #else
53 MENU_ENUM_LABEL_VALUE_ADD_TAB
54 #endif
55 };
56
57 static const enum menu_settings_type ozone_system_tabs_type[OZONE_SYSTEM_TAB_LAST] = {
58 MENU_SETTINGS,
59 MENU_SETTINGS_TAB,
60 MENU_HISTORY_TAB,
61 MENU_FAVORITES_TAB,
62 MENU_MUSIC_TAB,
63 #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
64 MENU_VIDEO_TAB,
65 #endif
66 #ifdef HAVE_IMAGEVIEWER
67 MENU_IMAGES_TAB,
68 #endif
69 #ifdef HAVE_NETWORKING
70 MENU_NETPLAY_TAB,
71 #endif
72 #ifdef HAVE_LIBRETRODB
73 MENU_ADD_TAB,
74 MENU_EXPLORE_TAB
75 #else
76 MENU_ADD_TAB
77 #endif
78 };
79
80 static const enum msg_hash_enums ozone_system_tabs_idx[OZONE_SYSTEM_TAB_LAST] = {
81 MENU_ENUM_LABEL_MAIN_MENU,
82 MENU_ENUM_LABEL_SETTINGS_TAB,
83 MENU_ENUM_LABEL_HISTORY_TAB,
84 MENU_ENUM_LABEL_FAVORITES_TAB,
85 MENU_ENUM_LABEL_MUSIC_TAB,
86 #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
87 MENU_ENUM_LABEL_VIDEO_TAB,
88 #endif
89 #ifdef HAVE_IMAGEVIEWER
90 MENU_ENUM_LABEL_IMAGES_TAB,
91 #endif
92 #ifdef HAVE_NETWORKING
93 MENU_ENUM_LABEL_NETPLAY_TAB,
94 #endif
95 #ifdef HAVE_LIBRETRODB
96 MENU_ENUM_LABEL_ADD_TAB,
97 MENU_ENUM_LABEL_EXPLORE_TAB
98 #else
99 MENU_ENUM_LABEL_ADD_TAB
100 #endif
101 };
102
103 static const unsigned ozone_system_tabs_icons[OZONE_SYSTEM_TAB_LAST] = {
104 OZONE_TAB_TEXTURE_MAIN_MENU,
105 OZONE_TAB_TEXTURE_SETTINGS,
106 OZONE_TAB_TEXTURE_HISTORY,
107 OZONE_TAB_TEXTURE_FAVORITES,
108 OZONE_TAB_TEXTURE_MUSIC,
109 #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
110 OZONE_TAB_TEXTURE_VIDEO,
111 #endif
112 #ifdef HAVE_IMAGEVIEWER
113 OZONE_TAB_TEXTURE_IMAGE,
114 #endif
115 #ifdef HAVE_NETWORKING
116 OZONE_TAB_TEXTURE_NETWORK,
117 #endif
118 OZONE_TAB_TEXTURE_SCAN_CONTENT
119 };
120
ozone_sidebar_collapse_end(void * userdata)121 static void ozone_sidebar_collapse_end(void *userdata)
122 {
123 ozone_handle_t *ozone = (ozone_handle_t*)userdata;
124
125 ozone->sidebar_collapsed = true;
126 }
127
ozone_sidebar_get_scroll_y(ozone_handle_t * ozone,unsigned video_height)128 static float ozone_sidebar_get_scroll_y(
129 ozone_handle_t *ozone, unsigned video_height)
130 {
131 float scroll_y =
132 ozone->animations.scroll_y_sidebar;
133 float selected_position_y =
134 ozone_get_selected_sidebar_y_position(ozone);
135 float current_selection_middle_onscreen =
136 ozone->dimensions.header_height
137 + ozone->dimensions.spacer_1px
138 + ozone->animations.scroll_y_sidebar
139 + selected_position_y
140 + ozone->dimensions.sidebar_entry_height / 2.0f;
141 float bottom_boundary =
142 (float)video_height
143 - (ozone->dimensions.header_height + ozone->dimensions.spacer_1px)
144 - ozone->dimensions.footer_height;
145 float entries_middle = (float)video_height / 2.0f;
146 float entries_height = ozone_get_sidebar_height(ozone);
147
148 if (current_selection_middle_onscreen != entries_middle)
149 scroll_y = ozone->animations.scroll_y_sidebar - (current_selection_middle_onscreen - entries_middle);
150
151 if (scroll_y + entries_height < bottom_boundary)
152 scroll_y = bottom_boundary - entries_height - ozone->dimensions.sidebar_padding_vertical;
153
154 if (scroll_y > 0.0f)
155 return 0.0f;
156 return scroll_y;
157 }
158
ozone_draw_sidebar(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)159 void ozone_draw_sidebar(
160 ozone_handle_t *ozone,
161 gfx_display_t *p_disp,
162 gfx_animation_t *p_anim,
163 settings_t *settings,
164 void *userdata,
165 unsigned video_width,
166 unsigned video_height,
167 bool libretro_running,
168 float menu_framebuffer_opacity
169 )
170 {
171 size_t y;
172 int entry_width;
173 char console_title[255];
174 unsigned i, sidebar_height;
175 gfx_animation_ctx_ticker_t ticker;
176 gfx_animation_ctx_ticker_smooth_t ticker_smooth;
177 static const char* const
178 ticker_spacer = OZONE_TICKER_SPACER;
179 unsigned ticker_x_offset = 0;
180 uint32_t text_alpha = ozone->animations.sidebar_text_alpha
181 * 255.0f;
182 bool use_smooth_ticker = settings->bools.menu_ticker_smooth;
183 float scale_factor = ozone->last_scale_factor;
184 enum gfx_animation_ticker_type
185 menu_ticker_type = (enum gfx_animation_ticker_type)
186 settings->uints.menu_ticker_type;
187 unsigned selection_y = 0;
188 unsigned selection_old_y = 0;
189 unsigned horizontal_list_size = 0;
190 gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
191
192 /* Initial ticker configuration */
193 if (use_smooth_ticker)
194 {
195 ticker_smooth.idx = p_anim->ticker_pixel_idx;
196 ticker_smooth.font = ozone->fonts.sidebar.font;
197 ticker_smooth.font_scale = 1.0f;
198 ticker_smooth.type_enum = menu_ticker_type;
199 ticker_smooth.spacer = ticker_spacer;
200 ticker_smooth.x_offset = &ticker_x_offset;
201 ticker_smooth.dst_str_width = NULL;
202 }
203 else
204 {
205 ticker.idx = p_anim->ticker_idx;
206 ticker.type_enum = menu_ticker_type;
207 ticker.spacer = ticker_spacer;
208 }
209
210 horizontal_list_size = (unsigned)ozone->horizontal_list.size;
211
212 gfx_display_scissor_begin(
213 p_disp,
214 userdata,
215 video_width, video_height,
216 0,
217 ozone->dimensions.header_height + ozone->dimensions.spacer_1px,
218 (unsigned) ozone->dimensions_sidebar_width,
219 video_height - ozone->dimensions.header_height - ozone->dimensions.footer_height - ozone->dimensions.spacer_1px);
220
221 /* Background */
222 sidebar_height = video_height - ozone->dimensions.header_height - ozone->dimensions.sidebar_gradient_height * 2 - ozone->dimensions.footer_height;
223
224 if (!libretro_running || (menu_framebuffer_opacity >= 1.0f))
225 {
226 gfx_display_draw_quad(
227 p_disp,
228 userdata,
229 video_width,
230 video_height,
231 ozone->sidebar_offset,
232 ozone->dimensions.header_height + ozone->dimensions.spacer_1px,
233 (unsigned)ozone->dimensions_sidebar_width,
234 ozone->dimensions.sidebar_gradient_height,
235 video_width,
236 video_height,
237 ozone->theme->sidebar_top_gradient);
238 gfx_display_draw_quad(
239 p_disp,
240 userdata,
241 video_width,
242 video_height,
243 ozone->sidebar_offset,
244 ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_gradient_height,
245 (unsigned)ozone->dimensions_sidebar_width,
246 sidebar_height,
247 video_width,
248 video_height,
249 ozone->theme->sidebar_background);
250 gfx_display_draw_quad(
251 p_disp,
252 userdata,
253 video_width,
254 video_height,
255 ozone->sidebar_offset,
256 video_height
257 - ozone->dimensions.footer_height
258 - ozone->dimensions.sidebar_gradient_height
259 - ozone->dimensions.spacer_1px,
260 (unsigned)ozone->dimensions_sidebar_width,
261 ozone->dimensions.sidebar_gradient_height
262 + ozone->dimensions.spacer_1px,
263 video_width,
264 video_height,
265 ozone->theme->sidebar_bottom_gradient);
266 }
267
268 /* Tabs */
269 /* y offset computation */
270 y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_padding_vertical;
271 for (i = 0; i < ozone->system_tab_end + horizontal_list_size + 1; i++)
272 {
273 if (i == ozone->categories_selection_ptr)
274 {
275 selection_y = (unsigned)y;
276 if (ozone->categories_selection_ptr > ozone->system_tab_end)
277 selection_y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px;
278 }
279
280 if (i == ozone->categories_active_idx_old)
281 {
282 selection_old_y = (unsigned)y;
283 if (ozone->categories_active_idx_old > ozone->system_tab_end)
284 selection_old_y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px;
285 }
286
287 y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical;
288 }
289
290 entry_width = (unsigned) ozone->dimensions_sidebar_width - ozone->dimensions.sidebar_padding_horizontal * 2;
291
292 /* Cursor */
293 if (ozone->cursor_in_sidebar)
294 ozone_draw_cursor(
295 ozone,
296 p_disp,
297 userdata,
298 video_width,
299 video_height,
300 ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.spacer_3px,
301 entry_width - ozone->dimensions.spacer_5px,
302 ozone->dimensions.sidebar_entry_height + ozone->dimensions.spacer_1px,
303 selection_y + ozone->animations.scroll_y_sidebar,
304 ozone->animations.cursor_alpha);
305
306 if (ozone->cursor_in_sidebar_old)
307 ozone_draw_cursor(
308 ozone,
309 p_disp,
310 userdata,
311 video_width,
312 video_height,
313 ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.spacer_3px,
314 entry_width - ozone->dimensions.spacer_5px,
315 ozone->dimensions.sidebar_entry_height + ozone->dimensions.spacer_1px,
316 selection_old_y + ozone->animations.scroll_y_sidebar,
317 1-ozone->animations.cursor_alpha);
318
319 /* Menu tabs */
320 y = ozone->dimensions.header_height + ozone->dimensions.spacer_1px + ozone->dimensions.sidebar_padding_vertical;
321 if (dispctx && dispctx->blend_begin)
322 dispctx->blend_begin(userdata);
323
324 for (i = 0; i < (unsigned)(ozone->system_tab_end+1); i++)
325 {
326 enum msg_hash_enums value_idx;
327 const char *title = NULL;
328 bool selected = (ozone->categories_selection_ptr == i);
329 unsigned icon = ozone_system_tabs_icons[ozone->tabs[i]];
330
331 uint32_t text_color = COLOR_TEXT_ALPHA((selected ? ozone->theme->text_selected_rgba : ozone->theme->text_rgba), text_alpha);
332 float *col = (selected ? ozone->theme->text_selected : ozone->theme->entries_icon);
333
334 if (!col)
335 col = ozone->pure_white;
336
337 /* Icon */
338 ozone_draw_icon(
339 p_disp,
340 userdata,
341 video_width,
342 video_height,
343 ozone->dimensions.sidebar_entry_icon_size,
344 ozone->dimensions.sidebar_entry_icon_size,
345 ozone->tab_textures[icon],
346 ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.sidebar_entry_icon_padding,
347 y + ozone->dimensions.sidebar_entry_height / 2 - ozone->dimensions.sidebar_entry_icon_size / 2 + ozone->animations.scroll_y_sidebar,
348 video_width,
349 video_height,
350 0,
351 1,
352 col);
353
354 value_idx = ozone_system_tabs_value[ozone->tabs[i]];
355 title = msg_hash_to_str(value_idx);
356
357 /* Text */
358 if (!ozone->sidebar_collapsed)
359 gfx_display_draw_text(
360 ozone->fonts.sidebar.font,
361 title,
362 ozone->sidebar_offset
363 + ozone->dimensions.sidebar_padding_horizontal
364 + ozone->dimensions.sidebar_entry_icon_padding * 2
365 + ozone->dimensions.sidebar_entry_icon_size,
366 y + ozone->dimensions.sidebar_entry_height / 2.0f
367 + ozone->fonts.sidebar.line_centre_offset
368 + ozone->animations.scroll_y_sidebar,
369 video_width,
370 video_height,
371 text_color,
372 TEXT_ALIGN_LEFT,
373 1.0f,
374 false,
375 1.0f,
376 true);
377
378 y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical;
379 }
380
381 if (dispctx && dispctx->blend_end)
382 dispctx->blend_end(userdata);
383
384 /* Console tabs */
385 if (horizontal_list_size > 0)
386 {
387 gfx_display_draw_quad(
388 p_disp,
389 userdata,
390 video_width,
391 video_height,
392 ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal,
393 y + ozone->animations.scroll_y_sidebar,
394 entry_width,
395 ozone->dimensions.spacer_1px,
396 video_width,
397 video_height,
398 ozone->theme->entries_border);
399
400 y += ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px;
401
402 if (dispctx && dispctx->blend_begin)
403 dispctx->blend_begin(userdata);
404
405 for (i = 0; i < horizontal_list_size; i++)
406 {
407 bool selected = (ozone->categories_selection_ptr == ozone->system_tab_end + 1 + i);
408
409 uint32_t text_color = COLOR_TEXT_ALPHA((selected ? ozone->theme->text_selected_rgba : ozone->theme->text_rgba), text_alpha);
410
411 ozone_node_t *node = (ozone_node_t*)ozone->horizontal_list.list[i].userdata;
412 float *col = (selected ? ozone->theme->text_selected : ozone->theme->entries_icon);
413
414 if (!node)
415 goto console_iterate;
416
417 if (!col)
418 col = ozone->pure_white;
419
420 /* Icon */
421 ozone_draw_icon(
422 p_disp,
423 userdata,
424 video_width,
425 video_height,
426 ozone->dimensions.sidebar_entry_icon_size,
427 ozone->dimensions.sidebar_entry_icon_size,
428 node->icon,
429 ozone->sidebar_offset + ozone->dimensions.sidebar_padding_horizontal + ozone->dimensions.sidebar_entry_icon_padding,
430 y + ozone->dimensions.sidebar_entry_height / 2 - ozone->dimensions.sidebar_entry_icon_size / 2 + ozone->animations.scroll_y_sidebar,
431 video_width,
432 video_height,
433 0,
434 1,
435 col);
436
437 /* Text */
438 if (ozone->sidebar_collapsed)
439 goto console_iterate;
440
441 if (use_smooth_ticker)
442 {
443 ticker_smooth.selected = selected;
444 /* TODO/FIXME - undefined behavior reported by ASAN -
445 *-12.549 is outside the range of representable values
446 of type 'unsigned int'
447 * */
448 ticker_smooth.field_width = (entry_width - ozone->dimensions.sidebar_entry_icon_size - 40 * scale_factor);
449 ticker_smooth.src_str = node->console_name;
450 ticker_smooth.dst_str = console_title;
451 ticker_smooth.dst_str_len = sizeof(console_title);
452
453 gfx_animation_ticker_smooth(&ticker_smooth);
454 }
455 else
456 {
457 ticker.len = (entry_width - ozone->dimensions.sidebar_entry_icon_size - 40 * scale_factor) / ozone->fonts.sidebar.glyph_width;
458 ticker.s = console_title;
459 ticker.selected = selected;
460 ticker.str = node->console_name;
461
462 gfx_animation_ticker(&ticker);
463 }
464
465 gfx_display_draw_text(
466 ozone->fonts.sidebar.font,
467 console_title,
468 ticker_x_offset + ozone->sidebar_offset
469 + ozone->dimensions.sidebar_padding_horizontal
470 + ozone->dimensions.sidebar_entry_icon_padding * 2
471 + ozone->dimensions.sidebar_entry_icon_size,
472 y + ozone->dimensions.sidebar_entry_height / 2
473 + ozone->fonts.sidebar.line_centre_offset
474 + ozone->animations.scroll_y_sidebar,
475 video_width,
476 video_height,
477 text_color,
478 TEXT_ALIGN_LEFT,
479 1.0f,
480 false,
481 1.0f,
482 true);
483
484 console_iterate:
485 y += ozone->dimensions.sidebar_entry_height + ozone->dimensions.sidebar_entry_padding_vertical;
486 }
487
488 if (dispctx && dispctx->blend_end)
489 dispctx->blend_end(userdata);
490 }
491
492 ozone_font_flush(video_width, video_height, &ozone->fonts.sidebar);
493
494 if (dispctx && dispctx->scissor_end)
495 dispctx->scissor_end(userdata,
496 video_width, video_height);
497 }
498
ozone_go_to_sidebar(ozone_handle_t * ozone,settings_t * settings,uintptr_t tag)499 void ozone_go_to_sidebar(ozone_handle_t *ozone, settings_t *settings,
500 uintptr_t tag)
501 {
502 struct gfx_animation_ctx_entry entry;
503
504 ozone->selection_old = ozone->selection;
505 ozone->cursor_in_sidebar_old = ozone->cursor_in_sidebar;
506 ozone->cursor_in_sidebar = true;
507
508 /* Cursor animation */
509 ozone->animations.cursor_alpha = 0.0f;
510
511 entry.cb = NULL;
512 entry.duration = ANIMATION_CURSOR_DURATION;
513 entry.easing_enum = EASING_OUT_QUAD;
514 entry.subject = &ozone->animations.cursor_alpha;
515 entry.tag = tag;
516 entry.target_value = 1.0f;
517 entry.userdata = NULL;
518
519 gfx_animation_push(&entry);
520
521 ozone_sidebar_update_collapse(ozone, settings, true);
522 }
523
ozone_leave_sidebar(ozone_handle_t * ozone,settings_t * settings,uintptr_t tag)524 void ozone_leave_sidebar(ozone_handle_t *ozone,
525 settings_t *settings,
526 uintptr_t tag)
527 {
528 struct gfx_animation_ctx_entry entry;
529
530 if (ozone->empty_playlist)
531 return;
532
533 ozone_update_content_metadata(ozone);
534
535 ozone->categories_active_idx_old = ozone->categories_selection_ptr;
536 ozone->cursor_in_sidebar_old = ozone->cursor_in_sidebar;
537 ozone->cursor_in_sidebar = false;
538
539 /* Cursor animation */
540 ozone->animations.cursor_alpha = 0.0f;
541
542 entry.cb = NULL;
543 entry.duration = ANIMATION_CURSOR_DURATION;
544 entry.easing_enum = EASING_OUT_QUAD;
545 entry.subject = &ozone->animations.cursor_alpha;
546 entry.tag = tag;
547 entry.target_value = 1.0f;
548 entry.userdata = NULL;
549
550 gfx_animation_push(&entry);
551
552 ozone_sidebar_update_collapse(ozone, settings, true);
553 }
554
ozone_get_selected_sidebar_y_position(ozone_handle_t * ozone)555 unsigned ozone_get_selected_sidebar_y_position(ozone_handle_t *ozone)
556 {
557 return ozone->categories_selection_ptr * ozone->dimensions.sidebar_entry_height +
558 (ozone->categories_selection_ptr - 1) * ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.sidebar_padding_vertical +
559 (ozone->categories_selection_ptr > ozone->system_tab_end ? ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px : 0);
560 }
561
ozone_get_sidebar_height(ozone_handle_t * ozone)562 unsigned ozone_get_sidebar_height(ozone_handle_t *ozone)
563 {
564 int entries = (int)(ozone->system_tab_end + 1 + (ozone->horizontal_list.size ));
565 return entries * ozone->dimensions.sidebar_entry_height + (entries - 1) * ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.sidebar_padding_vertical +
566 (ozone->horizontal_list.size > 0 ? ozone->dimensions.sidebar_entry_padding_vertical + ozone->dimensions.spacer_1px : 0);
567 }
568
ozone_sidebar_update_collapse(ozone_handle_t * ozone,settings_t * settings,bool allow_animation)569 void ozone_sidebar_update_collapse(
570 ozone_handle_t *ozone,
571 settings_t *settings,
572 bool allow_animation)
573 {
574 /* Collapse sidebar if needed */
575 struct gfx_animation_ctx_entry entry;
576 bool is_playlist = ozone_is_playlist(ozone, false);
577 uintptr_t tag = (uintptr_t)&ozone->sidebar_collapsed;
578 bool collapse_sidebar = settings->bools.ozone_collapse_sidebar;
579
580 entry.easing_enum = EASING_OUT_QUAD;
581 entry.tag = tag;
582 entry.userdata = ozone;
583 entry.duration = ANIMATION_CURSOR_DURATION;
584
585 gfx_animation_kill_by_tag(&tag);
586
587 /* Collapse it */
588 if (collapse_sidebar || (is_playlist && !ozone->cursor_in_sidebar))
589 {
590 if (allow_animation)
591 {
592 entry.cb = ozone_sidebar_collapse_end;
593
594 /* Text alpha */
595 entry.subject = &ozone->animations.sidebar_text_alpha;
596 entry.target_value = 0.0f;
597
598 gfx_animation_push(&entry);
599
600 /* Collapse */
601 entry.subject = &ozone->dimensions_sidebar_width;
602 entry.target_value = ozone->dimensions.sidebar_width_collapsed;
603
604 gfx_animation_push(&entry);
605 }
606 else
607 {
608 ozone->animations.sidebar_text_alpha = 0.0f;
609 ozone->dimensions_sidebar_width =
610 ozone->dimensions.sidebar_width_collapsed;
611 ozone->sidebar_collapsed = true;
612 }
613 }
614 /* Show it */
615 else if (ozone->cursor_in_sidebar || (!is_playlist && !collapse_sidebar))
616 {
617 if (allow_animation)
618 {
619 ozone->sidebar_collapsed = false;
620
621 entry.cb = NULL;
622
623 /* Text alpha */
624 entry.subject = &ozone->animations.sidebar_text_alpha;
625 entry.target_value = 1.0f;
626
627 gfx_animation_push(&entry);
628
629 /* Collapse */
630 entry.subject = &ozone->dimensions_sidebar_width;
631 entry.target_value = ozone->dimensions.sidebar_width_normal;
632
633 gfx_animation_push(&entry);
634 }
635 else
636 {
637 ozone->animations.sidebar_text_alpha = 1.0f;
638 ozone->dimensions_sidebar_width = ozone->dimensions.sidebar_width_normal;
639 ozone->sidebar_collapsed = false;
640 }
641 }
642
643 ozone_entries_update_thumbnail_bar(ozone, is_playlist, allow_animation);
644 }
645
ozone_sidebar_goto(ozone_handle_t * ozone,unsigned new_selection)646 void ozone_sidebar_goto(ozone_handle_t *ozone, unsigned new_selection)
647 {
648 unsigned video_info_height;
649 struct gfx_animation_ctx_entry entry;
650 uintptr_t tag = (uintptr_t)ozone;
651
652 video_driver_get_size(NULL, &video_info_height);
653
654 if (ozone->categories_selection_ptr != new_selection)
655 {
656 ozone->categories_active_idx_old = ozone->categories_selection_ptr;
657 ozone->categories_selection_ptr = new_selection;
658
659 ozone->cursor_in_sidebar_old = ozone->cursor_in_sidebar;
660
661 gfx_animation_kill_by_tag(&tag);
662 }
663
664 /* ozone->animations.scroll_y_sidebar will be modified
665 * > Set scroll acceleration to zero to minimise
666 * potential conflicts */
667 menu_input_set_pointer_y_accel(0.0f);
668
669 /* Cursor animation */
670 ozone->animations.cursor_alpha = 0.0f;
671
672 entry.cb = NULL;
673 entry.duration = ANIMATION_CURSOR_DURATION;
674 entry.easing_enum = EASING_OUT_QUAD;
675 entry.subject = &ozone->animations.cursor_alpha;
676 entry.tag = tag;
677 entry.target_value = 1.0f;
678 entry.userdata = NULL;
679
680 gfx_animation_push(&entry);
681
682 /* Scroll animation */
683 entry.cb = NULL;
684 entry.duration = ANIMATION_CURSOR_DURATION;
685 entry.easing_enum = EASING_OUT_QUAD;
686 entry.subject = &ozone->animations.scroll_y_sidebar;
687 entry.tag = tag;
688 entry.target_value = ozone_sidebar_get_scroll_y(ozone, video_info_height);
689 entry.userdata = NULL;
690
691 gfx_animation_push(&entry);
692
693 if (new_selection > ozone->system_tab_end)
694 ozone_change_tab(ozone, MENU_ENUM_LABEL_HORIZONTAL_MENU, MENU_SETTING_HORIZONTAL_MENU);
695 else
696 ozone_change_tab(ozone, ozone_system_tabs_idx[ozone->tabs[new_selection]], ozone_system_tabs_type[ozone->tabs[new_selection]]);
697 }
698
ozone_refresh_sidebars(ozone_handle_t * ozone,settings_t * settings,unsigned video_height)699 void ozone_refresh_sidebars(
700 ozone_handle_t *ozone,
701 settings_t *settings,
702 unsigned video_height)
703 {
704 uintptr_t collapsed_tag = (uintptr_t)&ozone->sidebar_collapsed;
705 uintptr_t offset_tag = (uintptr_t)&ozone->sidebar_offset;
706 uintptr_t thumbnail_tag = (uintptr_t)&ozone->show_thumbnail_bar;
707 uintptr_t scroll_tag = (uintptr_t)ozone;
708 bool is_playlist = ozone_is_playlist(ozone, false);
709 bool collapse_sidebar = settings->bools.ozone_collapse_sidebar;
710
711 /* Kill any existing animations */
712 gfx_animation_kill_by_tag(&collapsed_tag);
713 gfx_animation_kill_by_tag(&offset_tag);
714 gfx_animation_kill_by_tag(&thumbnail_tag);
715 if (ozone->depth == 1)
716 gfx_animation_kill_by_tag(&scroll_tag);
717
718 /* Set sidebar width */
719 if (collapse_sidebar || (is_playlist && !ozone->cursor_in_sidebar))
720 {
721 ozone->animations.sidebar_text_alpha = 0.0f;
722 ozone->dimensions_sidebar_width = ozone->dimensions.sidebar_width_collapsed;
723 ozone->sidebar_collapsed = true;
724 }
725 else if (ozone->cursor_in_sidebar || (!is_playlist && !collapse_sidebar))
726 {
727 ozone->animations.sidebar_text_alpha = 1.0f;
728 ozone->dimensions_sidebar_width = ozone->dimensions.sidebar_width_normal;
729 ozone->sidebar_collapsed = false;
730 }
731
732 /* Set sidebar offset */
733 if (ozone->depth == 1)
734 {
735 ozone->sidebar_offset = 0.0f;
736 ozone->draw_sidebar = true;
737 }
738 else if (ozone->depth > 1)
739 {
740 ozone->sidebar_offset = -ozone->dimensions_sidebar_width;
741 ozone->draw_sidebar = false;
742 }
743
744 /* Set thumbnail bar position */
745 if (is_playlist && !ozone->cursor_in_sidebar && ozone->depth == 1)
746 {
747 ozone->animations.thumbnail_bar_position = ozone->dimensions.thumbnail_bar_width;
748 ozone->show_thumbnail_bar = true;
749 }
750 else
751 {
752 ozone->animations.thumbnail_bar_position = 0.0f;
753 ozone->show_thumbnail_bar = false;
754 }
755 ozone->pending_hide_thumbnail_bar = false;
756
757 /* If sidebar is on-screen, update scroll position */
758 if (ozone->depth == 1)
759 {
760 ozone->animations.cursor_alpha = 1.0f;
761 ozone->animations.scroll_y_sidebar = ozone_sidebar_get_scroll_y(ozone, video_height);
762 }
763 }
764
ozone_change_tab(ozone_handle_t * ozone,enum msg_hash_enums tab,enum menu_settings_type type)765 void ozone_change_tab(ozone_handle_t *ozone,
766 enum msg_hash_enums tab,
767 enum menu_settings_type type)
768 {
769 file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0);
770 file_list_t *selection_buf = menu_entries_get_selection_buf_ptr(0);
771 size_t stack_size = menu_stack->size;
772
773 if (menu_stack->list[stack_size - 1].label)
774 free(menu_stack->list[stack_size - 1].label);
775 menu_stack->list[stack_size - 1].label = NULL;
776
777 menu_stack->list[stack_size - 1].label =
778 strdup(msg_hash_to_str(tab));
779 menu_stack->list[stack_size - 1].type =
780 type;
781
782 ozone_list_cache(ozone, MENU_LIST_HORIZONTAL,
783 MENU_ACTION_LEFT);
784
785 menu_driver_deferred_push_content_list(selection_buf);
786 }
787
ozone_init_horizontal_list(ozone_handle_t * ozone,settings_t * settings)788 void ozone_init_horizontal_list(ozone_handle_t *ozone,
789 settings_t *settings)
790 {
791 menu_displaylist_info_t info;
792 size_t list_size;
793 size_t i;
794 const char *dir_playlist = settings->paths.directory_playlist;
795 bool menu_content_show_playlists = settings->bools.menu_content_show_playlists;
796 bool ozone_truncate_playlist_name = settings->bools.ozone_truncate_playlist_name;
797 bool ozone_sort_after_truncate = settings->bools.ozone_sort_after_truncate_playlist_name;
798
799 menu_displaylist_info_init(&info);
800
801 info.list = &ozone->horizontal_list;
802 info.path = strdup(dir_playlist);
803 info.label = strdup(
804 msg_hash_to_str(MENU_ENUM_LABEL_PLAYLISTS_TAB));
805 info.exts = strdup("lpl");
806 info.type_default = FILE_TYPE_PLAIN;
807 info.enum_idx = MENU_ENUM_LABEL_PLAYLISTS_TAB;
808
809 if (menu_content_show_playlists && !string_is_empty(info.path))
810 {
811 if (menu_displaylist_ctl(DISPLAYLIST_DATABASE_PLAYLISTS_HORIZONTAL,
812 &info, settings))
813 menu_displaylist_process(&info);
814 }
815
816 menu_displaylist_info_free(&info);
817
818 /* Loop through list and set console names */
819 list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL);
820
821 for (i = 0; i < list_size; i++)
822 {
823 char playlist_file_noext[255];
824 char *console_name = NULL;
825 const char *playlist_file = ozone->horizontal_list.list[i].path;
826
827 playlist_file_noext[0] = '\0';
828
829 if (!playlist_file)
830 {
831 file_list_set_alt_at_offset(&ozone->horizontal_list, i, NULL);
832 continue;
833 }
834
835 /* Remove extension */
836 fill_pathname_base_noext(playlist_file_noext,
837 playlist_file, sizeof(playlist_file_noext));
838
839 console_name = playlist_file_noext;
840
841 /* Truncate playlist names, if required
842 * > Format: "Vendor - Console"
843 Remove everything before the hyphen
844 and the subsequent space */
845 if (ozone_truncate_playlist_name)
846 {
847 bool hyphen_found = false;
848
849 for (;;)
850 {
851 /* Check for "- " */
852 if (*console_name == '\0')
853 break;
854 else if (*console_name == '-' && *(console_name + 1) == ' ')
855 {
856 hyphen_found = true;
857 break;
858 }
859
860 console_name++;
861 }
862
863 if (hyphen_found)
864 console_name += 2;
865 else
866 console_name = playlist_file_noext;
867 }
868
869 /* Assign console name to list */
870 file_list_set_alt_at_offset(&ozone->horizontal_list, i, console_name);
871 }
872
873 /* If playlist names were truncated and option is
874 * enabled, re-sort list by console name */
875 if (ozone_truncate_playlist_name &&
876 ozone_sort_after_truncate &&
877 (list_size > 0))
878 file_list_sort_on_alt(&ozone->horizontal_list);
879 }
880
ozone_refresh_horizontal_list(ozone_handle_t * ozone,settings_t * settings)881 void ozone_refresh_horizontal_list(ozone_handle_t *ozone,
882 settings_t *settings)
883 {
884 ozone_context_destroy_horizontal_list(ozone);
885 ozone_free_list_nodes(&ozone->horizontal_list, false);
886 file_list_deinitialize(&ozone->horizontal_list);
887
888 menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
889
890 file_list_initialize(&ozone->horizontal_list);
891 ozone_init_horizontal_list(ozone, settings);
892
893 ozone_context_reset_horizontal_list(ozone);
894 }
895
ozone_context_reset_horizontal_list(ozone_handle_t * ozone)896 void ozone_context_reset_horizontal_list(ozone_handle_t *ozone)
897 {
898 unsigned i;
899 size_t list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL);
900
901 for (i = 0; i < list_size; i++)
902 {
903 const char *path = NULL;
904 const char *console_name = NULL;
905 ozone_node_t *node = (ozone_node_t*)ozone->horizontal_list.list[i].userdata;
906
907 if (!node)
908 {
909 node = ozone_alloc_node();
910 if (!node)
911 continue;
912 }
913
914 if (!(path = ozone->horizontal_list.list[i].path))
915 continue;
916 if (string_ends_with_size(path, ".lpl",
917 strlen(path), STRLEN_CONST(".lpl")))
918 {
919 struct texture_image ti;
920 char sysname[PATH_MAX_LENGTH];
921 char texturepath[PATH_MAX_LENGTH];
922 char content_texturepath[PATH_MAX_LENGTH];
923 char icons_path[PATH_MAX_LENGTH];
924
925 strlcpy(icons_path, ozone->icons_path, sizeof(icons_path));
926
927 sysname[0] = texturepath[0] = content_texturepath[0] = '\0';
928
929 fill_pathname_base_noext(sysname, path, sizeof(sysname));
930
931 fill_pathname_join_concat(texturepath, icons_path, sysname,
932 ".png", sizeof(texturepath));
933
934 /* If the playlist icon doesn't exist return default */
935
936 if (!path_is_valid(texturepath))
937 fill_pathname_join_concat(texturepath, icons_path, "default",
938 ".png", sizeof(texturepath));
939
940 ti.width = 0;
941 ti.height = 0;
942 ti.pixels = NULL;
943 ti.supports_rgba = video_driver_supports_rgba();
944
945 if (image_texture_load(&ti, texturepath))
946 {
947 if (ti.pixels)
948 {
949 video_driver_texture_unload(&node->icon);
950 video_driver_texture_load(&ti,
951 TEXTURE_FILTER_MIPMAP_LINEAR, &node->icon);
952 }
953
954 image_texture_free(&ti);
955 }
956
957 fill_pathname_join_delim(sysname, sysname,
958 "content.png", '-', sizeof(sysname));
959 strlcat(content_texturepath, icons_path, sizeof(content_texturepath));
960 strlcat(content_texturepath, PATH_DEFAULT_SLASH(), sizeof(content_texturepath));
961 strlcat(content_texturepath, sysname, sizeof(content_texturepath));
962
963 /* If the content icon doesn't exist return default-content */
964 if (!path_is_valid(content_texturepath))
965 {
966 strlcat(icons_path,
967 PATH_DEFAULT_SLASH() "default", sizeof(icons_path));
968 fill_pathname_join_delim(content_texturepath, icons_path,
969 "content.png", '-', sizeof(content_texturepath));
970 }
971
972 if (image_texture_load(&ti, content_texturepath))
973 {
974 if (ti.pixels)
975 {
976 video_driver_texture_unload(&node->content_icon);
977 video_driver_texture_load(&ti,
978 TEXTURE_FILTER_MIPMAP_LINEAR, &node->content_icon);
979 }
980
981 image_texture_free(&ti);
982 }
983
984 /* Console name */
985 console_name = ozone->horizontal_list.list[i].alt
986 ? ozone->horizontal_list.list[i].alt
987 : ozone->horizontal_list.list[i].path;
988
989 if (node->console_name)
990 free(node->console_name);
991
992 /* Note: console_name will *always* be valid here,
993 * but provide a fallback to prevent NULL pointer
994 * dereferencing in case of unknown errors... */
995 if (console_name)
996 node->console_name = strdup(console_name);
997 else
998 node->console_name = strdup(path);
999 }
1000 }
1001 }
1002
ozone_context_destroy_horizontal_list(ozone_handle_t * ozone)1003 void ozone_context_destroy_horizontal_list(ozone_handle_t *ozone)
1004 {
1005 unsigned i;
1006 size_t list_size = ozone_list_get_size(ozone, MENU_LIST_HORIZONTAL);
1007
1008 for (i = 0; i < list_size; i++)
1009 {
1010 const char *path = NULL;
1011 ozone_node_t *node = (ozone_node_t*)ozone->horizontal_list.list[i].userdata;
1012
1013 if (!node)
1014 continue;
1015
1016 if (!(path = ozone->horizontal_list.list[i].path))
1017 continue;
1018 if (string_ends_with_size(path, ".lpl",
1019 strlen(path), STRLEN_CONST(".lpl")))
1020 {
1021 video_driver_texture_unload(&node->icon);
1022 video_driver_texture_unload(&node->content_icon);
1023 }
1024 }
1025 }
1026
ozone_is_playlist(ozone_handle_t * ozone,bool depth)1027 bool ozone_is_playlist(ozone_handle_t *ozone, bool depth)
1028 {
1029 bool is_playlist;
1030
1031 if (ozone->categories_selection_ptr > ozone->system_tab_end)
1032 is_playlist = true;
1033 else
1034 {
1035 switch (ozone->tabs[ozone->categories_selection_ptr])
1036 {
1037 case OZONE_SYSTEM_TAB_MAIN:
1038 case OZONE_SYSTEM_TAB_SETTINGS:
1039 case OZONE_SYSTEM_TAB_ADD:
1040 #ifdef HAVE_NETWORKING
1041 case OZONE_SYSTEM_TAB_NETPLAY:
1042 #endif
1043 #ifdef HAVE_LIBRETRODB
1044 case OZONE_SYSTEM_TAB_EXPLORE:
1045 #endif
1046 is_playlist = false;
1047 break;
1048 case OZONE_SYSTEM_TAB_HISTORY:
1049 case OZONE_SYSTEM_TAB_FAVORITES:
1050 case OZONE_SYSTEM_TAB_MUSIC:
1051 #if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
1052 case OZONE_SYSTEM_TAB_VIDEO:
1053 #endif
1054 #ifdef HAVE_IMAGEVIEWER
1055 case OZONE_SYSTEM_TAB_IMAGES:
1056 #endif
1057 default:
1058 is_playlist = true;
1059 break;
1060 }
1061 }
1062
1063 if (depth)
1064 return is_playlist && ozone->depth == 1;
1065
1066 return is_playlist;
1067 }
1068