feat(games): tile carousel like home menu
Replace vertical button list with a horizontal snap-scroll carousel: one 176x122 tile per slide, 64px CONTAIN icon, lisael_font_24 label, left/right arrows, dot page indicator. Mirrors home.c pattern exactly (own s_gtrack/s_gdots statics). All game callbacks and sub-screens unchanged.
This commit is contained in:
+157
-27
@@ -314,6 +314,103 @@ static void balloons_cb(lv_event_t *e)
|
||||
250, 0, false);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Games menu: horizontal tile carousel (mirrors home.c pattern).
|
||||
// ---------------------------------------------------------------------------
|
||||
#define GAMES_SLIDE_W 320
|
||||
#define GAMES_N_TILES 4
|
||||
|
||||
typedef struct {
|
||||
const lv_image_dsc_t *icon;
|
||||
const char *label;
|
||||
lv_color_t color;
|
||||
lv_event_cb_t cb;
|
||||
} game_tile_def_t;
|
||||
|
||||
static lv_obj_t *s_gtrack;
|
||||
static lv_obj_t *s_gdots[GAMES_N_TILES];
|
||||
static int s_gcur;
|
||||
|
||||
static void gdots_update(int idx)
|
||||
{
|
||||
for (int i = 0; i < GAMES_N_TILES; i++) {
|
||||
if (s_gdots[i]) {
|
||||
lv_obj_set_style_bg_opa(s_gdots[i],
|
||||
i == idx ? LV_OPA_COVER : LV_OPA_30, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void games_scroll_cb(lv_event_t *e)
|
||||
{
|
||||
lv_obj_t *t = lv_event_get_target(e);
|
||||
int idx = (lv_obj_get_scroll_x(t) + GAMES_SLIDE_W / 2) / GAMES_SLIDE_W;
|
||||
if (idx < 0) idx = 0;
|
||||
if (idx >= GAMES_N_TILES) idx = GAMES_N_TILES - 1;
|
||||
s_gcur = idx;
|
||||
gdots_update(idx);
|
||||
}
|
||||
|
||||
static void games_arrow_cb(lv_event_t *e)
|
||||
{
|
||||
int idx = s_gcur + (int)(intptr_t)lv_event_get_user_data(e);
|
||||
if (idx < 0) idx = 0;
|
||||
if (idx >= GAMES_N_TILES) idx = GAMES_N_TILES - 1;
|
||||
s_gcur = idx;
|
||||
lv_obj_scroll_to_x(s_gtrack, idx * GAMES_SLIDE_W, LV_ANIM_ON);
|
||||
gdots_update(idx);
|
||||
}
|
||||
|
||||
static void games_arrow(lv_obj_t *parent, const char *sym, lv_align_t al, int dir)
|
||||
{
|
||||
lv_obj_t *b = lv_button_create(parent);
|
||||
lv_obj_remove_style_all(b);
|
||||
lv_obj_set_size(b, 38, 60);
|
||||
lv_obj_set_style_radius(b, 19, 0);
|
||||
lv_obj_set_style_bg_opa(b, LV_OPA_40, 0);
|
||||
lv_obj_set_style_bg_color(b, lv_color_white(), 0);
|
||||
lv_obj_align(b, al, (al == LV_ALIGN_LEFT_MID) ? 2 : -2, 28);
|
||||
lv_obj_add_event_cb(b, games_arrow_cb, LV_EVENT_CLICKED, (void *)(intptr_t)dir);
|
||||
lv_obj_t *l = lv_label_create(b);
|
||||
lv_label_set_text(l, sym);
|
||||
lv_obj_set_style_text_color(l, lv_color_hex(0x495057), 0);
|
||||
lv_obj_center(l);
|
||||
}
|
||||
|
||||
static void add_game_slide(lv_obj_t *track, const game_tile_def_t *def)
|
||||
{
|
||||
lv_obj_t *slide = lv_obj_create(track);
|
||||
lv_obj_remove_style_all(slide);
|
||||
lv_obj_set_size(slide, GAMES_SLIDE_W, LV_PCT(100));
|
||||
lv_obj_clear_flag(slide, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_set_flex_flow(slide, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(slide, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
|
||||
lv_obj_t *tile = lv_button_create(slide);
|
||||
lv_obj_add_style(tile, lisael_ui_style_tile(), 0);
|
||||
lv_obj_set_style_bg_color(tile, lv_color_lighten(def->color, 90), 0);
|
||||
lv_obj_set_style_bg_grad_color(tile, def->color, 0);
|
||||
lv_obj_set_style_translate_y(tile, 4, LV_STATE_PRESSED);
|
||||
lv_obj_set_style_shadow_ofs_y(tile, 3, LV_STATE_PRESSED);
|
||||
lv_obj_set_size(tile, 176, 122);
|
||||
lv_obj_set_flex_flow(tile, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(tile, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_set_style_pad_row(tile, 6, 0);
|
||||
lv_obj_clear_flag(tile, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_event_cb(tile, def->cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
lv_obj_t *icon = lv_image_create(tile);
|
||||
lv_image_set_src(icon, def->icon);
|
||||
lv_obj_set_size(icon, 64, 64);
|
||||
lv_image_set_inner_align(icon, LV_IMAGE_ALIGN_CONTAIN);
|
||||
|
||||
lv_obj_t *txt = lv_label_create(tile);
|
||||
lv_label_set_text(txt, def->label);
|
||||
lv_obj_set_style_text_font(txt, &lisael_font_24, 0);
|
||||
}
|
||||
|
||||
lv_obj_t *lisael_screen_games(void)
|
||||
{
|
||||
if (s_menu_root) {
|
||||
@@ -321,6 +418,8 @@ lv_obj_t *lisael_screen_games(void)
|
||||
}
|
||||
s_menu_root = lv_obj_create(NULL);
|
||||
lv_obj_set_style_bg_color(s_menu_root, lv_color_hex(0xF3F0FF), 0);
|
||||
lv_obj_set_style_bg_grad_color(s_menu_root, lv_color_hex(0xFAF5FF), 0);
|
||||
lv_obj_set_style_bg_grad_dir(s_menu_root, LV_GRAD_DIR_VER, 0);
|
||||
|
||||
lisael_ui_add_back_button(s_menu_root);
|
||||
|
||||
@@ -329,34 +428,65 @@ lv_obj_t *lisael_screen_games(void)
|
||||
lv_obj_set_style_text_font(title, &lisael_font_24, 0);
|
||||
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 16);
|
||||
|
||||
lv_obj_t *list = lv_obj_create(s_menu_root);
|
||||
lv_obj_remove_style_all(list);
|
||||
lv_obj_set_size(list, LV_PCT(100), 150);
|
||||
lv_obj_align(list, LV_ALIGN_CENTER, 0, 20);
|
||||
lv_obj_set_flex_flow(list, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(list, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_set_style_pad_row(list, 10, 0);
|
||||
|
||||
struct { const char *txt; const lv_image_dsc_t *icon; lv_event_cb_t cb; } games[] = {
|
||||
{ "Memory", &lisael_ic_cat, memory_cb },
|
||||
{ "Compter", &lisael_ic_apple, count_cb },
|
||||
{ "Calme ta colère", &lisael_ic_balloon, balloons_cb },
|
||||
{ "Coloriage", &lisael_ic_palette, color_cb },
|
||||
// Tile definitions (icon, label, background color, click callback).
|
||||
static const game_tile_def_t tiles[GAMES_N_TILES] = {
|
||||
{ &lisael_ic_cat, "Memory", {0}, memory_cb },
|
||||
{ &lisael_ic_apple, "Compter", {0}, count_cb },
|
||||
{ &lisael_ic_balloon, "Calme ta col\xc3\xa8re", {0}, balloons_cb },
|
||||
{ &lisael_ic_palette, "Coloriage", {0}, color_cb },
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(games) / sizeof(games[0]); i++) {
|
||||
lv_obj_t *b = lv_button_create(list);
|
||||
lv_obj_add_style(b, lisael_ui_style_button(), 0);
|
||||
lv_obj_set_width(b, LV_PCT(82));
|
||||
lv_obj_set_flex_flow(b, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_flex_align(b, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_set_style_pad_column(b, 12, 0);
|
||||
lv_obj_add_event_cb(b, games[i].cb, LV_EVENT_CLICKED, NULL);
|
||||
lv_obj_t *ic = lv_image_create(b);
|
||||
lv_image_set_src(ic, games[i].icon);
|
||||
lv_obj_t *l = lv_label_create(b);
|
||||
lv_label_set_text(l, games[i].txt);
|
||||
static const uint32_t colors[GAMES_N_TILES] = {
|
||||
0xFFC9C9, // pink — Memory
|
||||
0xB2F2BB, // green — Compter
|
||||
0xA5D8FF, // blue — Calme ta colère
|
||||
0xFFD8A8, // orange — Coloriage
|
||||
};
|
||||
|
||||
// Scrollable track (horizontal, snap-center, no scrollbar).
|
||||
s_gtrack = lv_obj_create(s_menu_root);
|
||||
lv_obj_remove_style_all(s_gtrack);
|
||||
lv_obj_set_size(s_gtrack, GAMES_SLIDE_W, 134);
|
||||
lv_obj_align(s_gtrack, LV_ALIGN_TOP_MID, 0, 56);
|
||||
lv_obj_set_flex_flow(s_gtrack, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_style_pad_all(s_gtrack, 0, 0);
|
||||
lv_obj_set_style_pad_column(s_gtrack, 0, 0);
|
||||
lv_obj_set_scroll_dir(s_gtrack, LV_DIR_HOR);
|
||||
lv_obj_set_scroll_snap_x(s_gtrack, LV_SCROLL_SNAP_CENTER);
|
||||
lv_obj_set_scrollbar_mode(s_gtrack, LV_SCROLLBAR_MODE_OFF);
|
||||
lv_obj_add_event_cb(s_gtrack, games_scroll_cb, LV_EVENT_SCROLL_END, NULL);
|
||||
|
||||
for (int i = 0; i < GAMES_N_TILES; i++) {
|
||||
game_tile_def_t t = tiles[i];
|
||||
t.color = lv_color_hex(colors[i]);
|
||||
add_game_slide(s_gtrack, &t);
|
||||
}
|
||||
|
||||
// Dot row (page indicator).
|
||||
lv_obj_t *dotrow = lv_obj_create(s_menu_root);
|
||||
lv_obj_remove_style_all(dotrow);
|
||||
lv_obj_set_size(dotrow, LV_PCT(90), 16);
|
||||
lv_obj_align(dotrow, LV_ALIGN_BOTTOM_MID, 0, -6);
|
||||
lv_obj_set_flex_flow(dotrow, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_flex_align(dotrow, LV_FLEX_ALIGN_CENTER,
|
||||
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_clear_flag(dotrow, LV_OBJ_FLAG_SCROLLABLE);
|
||||
for (int i = 0; i < GAMES_N_TILES; i++) {
|
||||
lv_obj_t *d = lv_obj_create(dotrow);
|
||||
lv_obj_remove_style_all(d);
|
||||
lv_obj_set_size(d, 9, 9);
|
||||
lv_obj_set_style_radius(d, 5, 0);
|
||||
lv_obj_set_style_margin_left(d, 3, 0);
|
||||
lv_obj_set_style_margin_right(d, 3, 0);
|
||||
lv_obj_set_style_bg_color(d, lv_color_hex(0x6741D9), 0);
|
||||
lv_obj_set_style_bg_opa(d, i == 0 ? LV_OPA_COVER : LV_OPA_30, 0);
|
||||
s_gdots[i] = d;
|
||||
}
|
||||
|
||||
// Left/right arrow buttons.
|
||||
games_arrow(s_menu_root, LV_SYMBOL_LEFT, LV_ALIGN_LEFT_MID, -1);
|
||||
games_arrow(s_menu_root, LV_SYMBOL_RIGHT, LV_ALIGN_RIGHT_MID, +1);
|
||||
s_gcur = 0;
|
||||
gdots_update(0);
|
||||
|
||||
return s_menu_root;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user