|
|
|
@@ -9,7 +9,11 @@
|
|
|
|
|
/* albums page */
|
|
|
|
|
/* */
|
|
|
|
|
/* Opened from the albums button on the top bar. A grid of album cards */
|
|
|
|
|
/* (cover, name, item count). Covers are ordinary server thumbnails: */
|
|
|
|
|
/* (cover, name, item count). The grid shows a VIEW of the list: */
|
|
|
|
|
/* g_alb_view holds g_album_* indices filtered by the name search */
|
|
|
|
|
/* (SQUARE / tap the pill) and sorted by name or last modification */
|
|
|
|
|
/* (SELECT / tap the chip). g_alb_sel is a position in that view. */
|
|
|
|
|
/* Covers are ordinary server thumbnails: */
|
|
|
|
|
/* each album's cover id becomes a g_asset entry (asset_for_id) and the */
|
|
|
|
|
/* shared thumbnail worker loads it into g_thumb[]. Only rows near the */
|
|
|
|
|
/* viewport keep their cover texture (libraries can have hundreds of */
|
|
|
|
@@ -26,19 +30,114 @@
|
|
|
|
|
#define ALB_X0 ((SCREEN_W - ALB_COLS * ALB_CW - (ALB_COLS - 1) * ALB_GAP) / 2)
|
|
|
|
|
#define ALB_GLYPH 44 /* album glyph bake size on this page */
|
|
|
|
|
|
|
|
|
|
enum { ALB_NONE, ALB_BACK, ALB_OPEN, ALB_REFRESH };
|
|
|
|
|
/* header controls: the search pill and the sort chip */
|
|
|
|
|
#define ALB_HY 9.0f /* top of pill + chip */
|
|
|
|
|
#define ALB_HH 38.0f /* their height */
|
|
|
|
|
#define ALB_SX 190.0f /* search pill */
|
|
|
|
|
#define ALB_SW 380.0f
|
|
|
|
|
#define ALB_OX (ALB_SX + ALB_SW + 14.0f) /* sort chip */
|
|
|
|
|
#define ALB_OW 230.0f
|
|
|
|
|
|
|
|
|
|
static int g_alb_sel; /* selected album */
|
|
|
|
|
enum { ALB_NONE, ALB_BACK, ALB_OPEN, ALB_REFRESH, ALB_SEARCH };
|
|
|
|
|
|
|
|
|
|
/* sort orders, cycled by SELECT */
|
|
|
|
|
enum { ALB_SORT_MOD_NEW, ALB_SORT_MOD_OLD, ALB_SORT_NAME_AZ, ALB_SORT_NAME_ZA,
|
|
|
|
|
ALB_NSORT };
|
|
|
|
|
static const char *const alb_sort_label[ALB_NSORT] = {
|
|
|
|
|
"Modified: newest", "Modified: oldest", "Name: A-Z", "Name: Z-A",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int g_alb_sel; /* selected position in the view */
|
|
|
|
|
static float g_alb_scroll, g_alb_target;
|
|
|
|
|
static int g_alb_touch, g_alb_dragged;
|
|
|
|
|
static float g_alb_tx, g_alb_ty, g_alb_t0y, g_alb_s0;
|
|
|
|
|
|
|
|
|
|
static int g_alb_sort; /* ALB_SORT_* */
|
|
|
|
|
static char g_alb_query[128]; /* name filter, "" = all albums */
|
|
|
|
|
static int *g_alb_view; /* g_album_* indices, filtered+sorted */
|
|
|
|
|
static int g_alb_vlen, g_alb_vcap;
|
|
|
|
|
|
|
|
|
|
static float alb_content_h(void)
|
|
|
|
|
{
|
|
|
|
|
int rows = (g_album_len + ALB_COLS - 1) / ALB_COLS;
|
|
|
|
|
int rows = (g_alb_vlen + ALB_COLS - 1) / ALB_COLS;
|
|
|
|
|
return ALB_TOP + rows * ALB_ROWH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ASCII case-insensitive substring test (other UTF-8 bytes match exactly) */
|
|
|
|
|
static int alb_name_match(const char *name, const char *q)
|
|
|
|
|
{
|
|
|
|
|
if (!q[0])
|
|
|
|
|
return 1;
|
|
|
|
|
for (; *name; name++) {
|
|
|
|
|
const char *a = name, *b = q;
|
|
|
|
|
while (*a && *b && tolower((unsigned char)*a) ==
|
|
|
|
|
tolower((unsigned char)*b)) {
|
|
|
|
|
a++;
|
|
|
|
|
b++;
|
|
|
|
|
}
|
|
|
|
|
if (!*b)
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int alb_cmp(const void *pa, const void *pb)
|
|
|
|
|
{
|
|
|
|
|
int a = *(const int *)pa, b = *(const int *)pb, r = 0;
|
|
|
|
|
switch (g_alb_sort) {
|
|
|
|
|
case ALB_SORT_MOD_NEW:
|
|
|
|
|
r = strcmp(g_album_updated[b], g_album_updated[a]);
|
|
|
|
|
break;
|
|
|
|
|
case ALB_SORT_MOD_OLD:
|
|
|
|
|
r = strcmp(g_album_updated[a], g_album_updated[b]);
|
|
|
|
|
break;
|
|
|
|
|
case ALB_SORT_NAME_AZ:
|
|
|
|
|
r = strcasecmp(g_album_name[a], g_album_name[b]);
|
|
|
|
|
break;
|
|
|
|
|
case ALB_SORT_NAME_ZA:
|
|
|
|
|
r = strcasecmp(g_album_name[b], g_album_name[a]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return r ? r : a - b; /* ties keep the server's order */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* rebuild g_alb_view from the list, the query and the sort order. the
|
|
|
|
|
* selected album stays selected when it is still in the view (a refresh);
|
|
|
|
|
* callers wanting the first card set g_alb_sel = -1 first. */
|
|
|
|
|
static void albums_rebuild_view(void)
|
|
|
|
|
{
|
|
|
|
|
int keep = (g_alb_sel >= 0 && g_alb_sel < g_alb_vlen) ?
|
|
|
|
|
g_alb_view[g_alb_sel] : -1;
|
|
|
|
|
g_album_list_dirty = 0;
|
|
|
|
|
g_alb_vlen = 0;
|
|
|
|
|
if (g_album_len > g_alb_vcap) {
|
|
|
|
|
int cap = GROW(g_alb_view, g_alb_vcap, g_album_len);
|
|
|
|
|
if (!cap)
|
|
|
|
|
return;
|
|
|
|
|
g_alb_vcap = cap;
|
|
|
|
|
}
|
|
|
|
|
for (int a = 0; a < g_album_len; a++)
|
|
|
|
|
if (alb_name_match(g_album_name[a], g_alb_query))
|
|
|
|
|
g_alb_view[g_alb_vlen++] = a;
|
|
|
|
|
qsort(g_alb_view, g_alb_vlen, sizeof(g_alb_view[0]), alb_cmp);
|
|
|
|
|
|
|
|
|
|
g_alb_sel = 0;
|
|
|
|
|
for (int v = 0; v < g_alb_vlen; v++)
|
|
|
|
|
if (g_alb_view[v] == keep)
|
|
|
|
|
g_alb_sel = v;
|
|
|
|
|
log_line("albums view: %d of %d (sort %d, query \"%s\")", g_alb_vlen,
|
|
|
|
|
g_album_len, g_alb_sort, g_alb_query);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* new name filter (from the keyboard); "" shows every album */
|
|
|
|
|
static void albums_set_query(const char *q)
|
|
|
|
|
{
|
|
|
|
|
snprintf(g_alb_query, sizeof(g_alb_query), "%s", q);
|
|
|
|
|
g_alb_sel = -1; /* start at the first match */
|
|
|
|
|
albums_rebuild_view();
|
|
|
|
|
g_alb_scroll = g_alb_target = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void alb_card_xy(int a, float *x, float *y)
|
|
|
|
|
{
|
|
|
|
|
*x = ALB_X0 + (a % ALB_COLS) * (ALB_CW + ALB_GAP);
|
|
|
|
@@ -89,12 +188,22 @@ static void albums_pump_covers(int first, int last)
|
|
|
|
|
|
|
|
|
|
/* rows more than one row outside the viewport drop their texture */
|
|
|
|
|
int keep0 = first - ALB_COLS, keep1 = last + ALB_COLS;
|
|
|
|
|
for (int a = 0; a < g_album_len; a++) {
|
|
|
|
|
if (a >= keep0 && a <= keep1)
|
|
|
|
|
if (keep0 < 0)
|
|
|
|
|
keep0 = 0;
|
|
|
|
|
if (keep1 >= g_alb_vlen)
|
|
|
|
|
keep1 = g_alb_vlen - 1;
|
|
|
|
|
for (int v = 0; v < g_alb_vlen; v++) {
|
|
|
|
|
if (v >= keep0 && v <= keep1)
|
|
|
|
|
continue;
|
|
|
|
|
int idx = g_album_cover[a];
|
|
|
|
|
if (idx >= 0 && g_thumb[idx] &&
|
|
|
|
|
!(g_req_state != REQ_IDLE && g_req_idx == idx)) {
|
|
|
|
|
int idx = g_album_cover[g_alb_view[v]];
|
|
|
|
|
if (idx < 0 || !g_thumb[idx] ||
|
|
|
|
|
(g_req_state != REQ_IDLE && g_req_idx == idx))
|
|
|
|
|
continue;
|
|
|
|
|
/* albums can share a cover photo: keep it if a kept card uses it */
|
|
|
|
|
int shared = 0;
|
|
|
|
|
for (int k = keep0; k <= keep1 && !shared; k++)
|
|
|
|
|
shared = g_album_cover[g_alb_view[k]] == idx;
|
|
|
|
|
if (!shared) {
|
|
|
|
|
tex_release(g_thumb[idx]);
|
|
|
|
|
g_thumb[idx] = NULL;
|
|
|
|
|
}
|
|
|
|
@@ -103,8 +212,11 @@ static void albums_pump_covers(int first, int last)
|
|
|
|
|
if (g_req_state != REQ_IDLE)
|
|
|
|
|
return;
|
|
|
|
|
for (int k = -1; k <= last - first; k++) {
|
|
|
|
|
int a = (k < 0) ? g_alb_sel : first + k; /* selection first */
|
|
|
|
|
if (a < 0 || a >= g_album_len || !g_album_thumb[a][0])
|
|
|
|
|
int v = (k < 0) ? g_alb_sel : first + k; /* selection first */
|
|
|
|
|
if (v < 0 || v >= g_alb_vlen)
|
|
|
|
|
continue;
|
|
|
|
|
int a = g_alb_view[v];
|
|
|
|
|
if (!g_album_thumb[a][0])
|
|
|
|
|
continue;
|
|
|
|
|
if (g_album_cover[a] < 0)
|
|
|
|
|
g_album_cover[a] = asset_for_id(g_album_thumb[a]);
|
|
|
|
@@ -160,14 +272,89 @@ static void draw_album_card(int a, float x, float y, int selected)
|
|
|
|
|
draw_text(x, y + ALB_COVH + 44, RGBA8(140, 140, 148, 255), 0.8f, meta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* header: search pill (query or placeholder) and the sort chip */
|
|
|
|
|
static void draw_albums_header(int n)
|
|
|
|
|
{
|
|
|
|
|
vita2d_draw_rectangle(0, 0, SCREEN_W, ALB_TOP - 8, RGBA8(16, 16, 16, 255));
|
|
|
|
|
int gs = 24;
|
|
|
|
|
vita2d_texture *og = icon_tex(ICON_CIRCLE, gs);
|
|
|
|
|
if (og)
|
|
|
|
|
vita2d_draw_texture_tint(og, 18, 16, RGBA8(235, 90, 85, 255));
|
|
|
|
|
/* same bake size as the card placeholder: icon_tex caches one
|
|
|
|
|
* size per glyph, so mixing sizes in a frame re-bakes (and frees)
|
|
|
|
|
* the texture mid-frame */
|
|
|
|
|
vita2d_texture *ag = icon_tex(ICON_ALBUM, ALB_GLYPH);
|
|
|
|
|
if (ag)
|
|
|
|
|
vita2d_draw_texture_tint_scale(ag, 56, 14, 28.0f / ALB_GLYPH,
|
|
|
|
|
28.0f / ALB_GLYPH,
|
|
|
|
|
RGBA8(235, 235, 240, 255));
|
|
|
|
|
draw_text(94, 37, RGBA8(235, 235, 240, 255), 1.1f, "Albums");
|
|
|
|
|
|
|
|
|
|
uint32_t field = RGBA8(38, 38, 42, 255);
|
|
|
|
|
uint32_t dim = RGBA8(140, 140, 148, 255);
|
|
|
|
|
uint32_t txt = RGBA8(235, 235, 240, 255);
|
|
|
|
|
float ty = ALB_HY + ALB_HH - 12;
|
|
|
|
|
|
|
|
|
|
/* search pill: [search] query ... [] (or O to clear) */
|
|
|
|
|
vita2d_texture *pm = rounded_mask_tex((int)ALB_SW, (int)ALB_HH, 10.0f);
|
|
|
|
|
if (pm)
|
|
|
|
|
vita2d_draw_texture_tint(pm, ALB_SX, ALB_HY, field);
|
|
|
|
|
else
|
|
|
|
|
vita2d_draw_rectangle(ALB_SX, ALB_HY, ALB_SW, ALB_HH, field);
|
|
|
|
|
vita2d_texture *si = icon_tex(ICON_SEARCH, 24);
|
|
|
|
|
if (si)
|
|
|
|
|
vita2d_draw_texture_tint(si, ALB_SX + 10, ALB_HY + (ALB_HH - 24) / 2,
|
|
|
|
|
RGBA8(190, 190, 196, 255));
|
|
|
|
|
char line[96];
|
|
|
|
|
if (g_alb_query[0]) {
|
|
|
|
|
alb_fit(line, sizeof(line), g_alb_query, 0.9f, (int)ALB_SW - 84);
|
|
|
|
|
draw_text(ALB_SX + 42, ty, txt, 0.9f, line);
|
|
|
|
|
if (og) /* O clears the filter */
|
|
|
|
|
vita2d_draw_texture_tint(og, ALB_SX + ALB_SW - 34,
|
|
|
|
|
ALB_HY + (ALB_HH - gs) / 2,
|
|
|
|
|
RGBA8(235, 90, 85, 255));
|
|
|
|
|
} else {
|
|
|
|
|
draw_text(ALB_SX + 42, ty, dim, 0.9f, "Search albums");
|
|
|
|
|
}
|
|
|
|
|
if (!g_alb_query[0]) {
|
|
|
|
|
vita2d_texture *sq = icon_tex(ICON_SQUARE, 20);
|
|
|
|
|
if (sq)
|
|
|
|
|
vita2d_draw_texture_tint(sq, ALB_SX + ALB_SW - 32,
|
|
|
|
|
ALB_HY + (ALB_HH - 20) / 2,
|
|
|
|
|
RGBA8(232, 120, 175, 255));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* sort chip: SELECT + the current order */
|
|
|
|
|
vita2d_texture *cm = rounded_mask_tex((int)ALB_OW, (int)ALB_HH, 10.0f);
|
|
|
|
|
if (cm)
|
|
|
|
|
vita2d_draw_texture_tint(cm, ALB_OX, ALB_HY, field);
|
|
|
|
|
else
|
|
|
|
|
vita2d_draw_rectangle(ALB_OX, ALB_HY, ALB_OW, ALB_HH, field);
|
|
|
|
|
draw_text(ALB_OX + 12, ty - 1, dim, 0.7f, "SELECT");
|
|
|
|
|
draw_text(ALB_OX + 12 + text_width(0.7f, "SELECT") + 10, ty, txt, 0.9f,
|
|
|
|
|
alb_sort_label[g_alb_sort]);
|
|
|
|
|
|
|
|
|
|
char cnt[64];
|
|
|
|
|
if (g_alb_query[0])
|
|
|
|
|
snprintf(cnt, sizeof(cnt), "%d of %d", n, g_album_len);
|
|
|
|
|
else
|
|
|
|
|
snprintf(cnt, sizeof(cnt), "%d album%s", n, n == 1 ? "" : "s");
|
|
|
|
|
draw_text(SCREEN_W - 18 - text_width(0.8f, cnt), 36, dim, 0.8f, cnt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* one frame of the album list: input, cover loading and drawing. returns
|
|
|
|
|
* ALB_OPEN (open g_alb_sel), ALB_BACK, ALB_REFRESH or ALB_NONE. */
|
|
|
|
|
* ALB_OPEN (open view position g_alb_sel), ALB_BACK, ALB_REFRESH,
|
|
|
|
|
* ALB_SEARCH (bring up the keyboard) or ALB_NONE. */
|
|
|
|
|
static int albums_page(unsigned int pressed, unsigned int nav)
|
|
|
|
|
{
|
|
|
|
|
int action = ALB_NONE;
|
|
|
|
|
int n = g_album_len;
|
|
|
|
|
if (g_album_list_dirty)
|
|
|
|
|
albums_rebuild_view();
|
|
|
|
|
int n = g_alb_vlen;
|
|
|
|
|
if (g_alb_sel >= n)
|
|
|
|
|
g_alb_sel = n > 0 ? n - 1 : 0;
|
|
|
|
|
if (g_alb_sel < 0)
|
|
|
|
|
g_alb_sel = 0;
|
|
|
|
|
|
|
|
|
|
if (n > 0) {
|
|
|
|
|
if (nav & SCE_CTRL_RIGHT && g_alb_sel + 1 < n)
|
|
|
|
@@ -183,10 +370,17 @@ static int albums_page(unsigned int pressed, unsigned int nav)
|
|
|
|
|
if (pressed & SCE_CTRL_CROSS)
|
|
|
|
|
action = ALB_OPEN;
|
|
|
|
|
}
|
|
|
|
|
if (pressed & SCE_CTRL_CIRCLE)
|
|
|
|
|
int do_sort = (pressed & SCE_CTRL_SELECT) != 0;
|
|
|
|
|
if (pressed & SCE_CTRL_CIRCLE) {
|
|
|
|
|
if (g_alb_query[0])
|
|
|
|
|
albums_set_query(""); /* O first clears the filter */
|
|
|
|
|
else
|
|
|
|
|
action = ALB_BACK;
|
|
|
|
|
}
|
|
|
|
|
if (pressed & SCE_CTRL_TRIANGLE)
|
|
|
|
|
action = ALB_REFRESH;
|
|
|
|
|
if (pressed & SCE_CTRL_SQUARE)
|
|
|
|
|
action = ALB_SEARCH;
|
|
|
|
|
|
|
|
|
|
float maxs = alb_content_h() - SCREEN_H + 16;
|
|
|
|
|
if (maxs < 0)
|
|
|
|
@@ -216,8 +410,18 @@ static int albums_page(unsigned int pressed, unsigned int nav)
|
|
|
|
|
g_alb_ty = ty;
|
|
|
|
|
} else if (g_alb_touch) {
|
|
|
|
|
g_alb_touch = 0;
|
|
|
|
|
if (!g_alb_dragged && g_alb_ty < ALB_TOP - 8 && g_alb_tx < 140) {
|
|
|
|
|
action = ALB_BACK; /* tap on the header's back label */
|
|
|
|
|
if (!g_alb_dragged && g_alb_ty < ALB_TOP - 8) {
|
|
|
|
|
/* header taps: back label, search pill (its O end
|
|
|
|
|
* clears an active filter), sort chip */
|
|
|
|
|
if (g_alb_tx < 140)
|
|
|
|
|
action = ALB_BACK;
|
|
|
|
|
else if (g_alb_tx >= ALB_SX && g_alb_tx < ALB_SX + ALB_SW) {
|
|
|
|
|
if (g_alb_query[0] && g_alb_tx > ALB_SX + ALB_SW - 48)
|
|
|
|
|
albums_set_query("");
|
|
|
|
|
else
|
|
|
|
|
action = ALB_SEARCH;
|
|
|
|
|
} else if (g_alb_tx >= ALB_OX && g_alb_tx < ALB_OX + ALB_OW)
|
|
|
|
|
do_sort = 1;
|
|
|
|
|
} else if (!g_alb_dragged) {
|
|
|
|
|
float cy = g_alb_ty + g_alb_scroll - ALB_TOP;
|
|
|
|
|
int row = (int)(cy / ALB_ROWH);
|
|
|
|
@@ -234,6 +438,13 @@ static int albums_page(unsigned int pressed, unsigned int nav)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (do_sort) {
|
|
|
|
|
/* a new order starts from its top */
|
|
|
|
|
g_alb_sort = (g_alb_sort + 1) % ALB_NSORT;
|
|
|
|
|
g_alb_sel = -1;
|
|
|
|
|
albums_rebuild_view();
|
|
|
|
|
g_alb_scroll = g_alb_target = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* keep the selected card on screen (d-pad), with an eased scroll */
|
|
|
|
|
if (!g_alb_touch) {
|
|
|
|
@@ -263,37 +474,23 @@ static int albums_page(unsigned int pressed, unsigned int nav)
|
|
|
|
|
vita2d_start_drawing();
|
|
|
|
|
vita2d_clear_screen();
|
|
|
|
|
vita2d_draw_rectangle(0, 0, SCREEN_W, SCREEN_H, RGBA8(16, 16, 16, 255));
|
|
|
|
|
for (int a = first; a <= last && a < n; a++) {
|
|
|
|
|
for (int v = first; v <= last && v < n; v++) {
|
|
|
|
|
float x, y;
|
|
|
|
|
alb_card_xy(a, &x, &y);
|
|
|
|
|
draw_album_card(a, x, y - g_alb_scroll, a == g_alb_sel);
|
|
|
|
|
alb_card_xy(v, &x, &y);
|
|
|
|
|
draw_album_card(g_alb_view[v], x, y - g_alb_scroll,
|
|
|
|
|
v == g_alb_sel);
|
|
|
|
|
}
|
|
|
|
|
if (n == 0)
|
|
|
|
|
if (n == 0 && g_alb_query[0]) {
|
|
|
|
|
char msg[160];
|
|
|
|
|
snprintf(msg, sizeof(msg), "No albums match \"%.64s\"", g_alb_query);
|
|
|
|
|
draw_centered(SCREEN_H / 2, RGBA8(160, 160, 165, 255), msg);
|
|
|
|
|
} else if (n == 0) {
|
|
|
|
|
draw_centered(SCREEN_H / 2, RGBA8(160, 160, 165, 255),
|
|
|
|
|
"No albums on this account");
|
|
|
|
|
|
|
|
|
|
/* header: back label, title, count (drawn last: cards scroll under it) */
|
|
|
|
|
vita2d_draw_rectangle(0, 0, SCREEN_W, ALB_TOP - 8, RGBA8(16, 16, 16, 255));
|
|
|
|
|
{
|
|
|
|
|
int gs = 24;
|
|
|
|
|
vita2d_texture *og = icon_tex(ICON_CIRCLE, gs);
|
|
|
|
|
if (og)
|
|
|
|
|
vita2d_draw_texture_tint(og, 18, 16, RGBA8(235, 90, 85, 255));
|
|
|
|
|
/* same bake size as the card placeholder: icon_tex caches one
|
|
|
|
|
* size per glyph, so mixing sizes in a frame re-bakes (and frees)
|
|
|
|
|
* the texture mid-frame */
|
|
|
|
|
vita2d_texture *ag = icon_tex(ICON_ALBUM, ALB_GLYPH);
|
|
|
|
|
if (ag)
|
|
|
|
|
vita2d_draw_texture_tint_scale(ag, 56, 14, 28.0f / ALB_GLYPH,
|
|
|
|
|
28.0f / ALB_GLYPH,
|
|
|
|
|
RGBA8(235, 235, 240, 255));
|
|
|
|
|
draw_text(94, 37, RGBA8(235, 235, 240, 255), 1.1f, "Albums");
|
|
|
|
|
char cnt[64];
|
|
|
|
|
snprintf(cnt, sizeof(cnt), "%d album%s X open /\\ refresh",
|
|
|
|
|
n, n == 1 ? "" : "s");
|
|
|
|
|
draw_text(SCREEN_W - 18 - text_width(0.8f, cnt), 36,
|
|
|
|
|
RGBA8(140, 140, 148, 255), 0.8f, cnt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* drawn last: cards scroll under it */
|
|
|
|
|
draw_albums_header(n);
|
|
|
|
|
vita2d_end_drawing();
|
|
|
|
|
vita2d_swap_buffers();
|
|
|
|
|
return action;
|
|
|
|
|