feat(box): activity carousel for home menu
The 7 mode tiles overflowed the grid and scrolled. Replace the wrapping tile grid with the same Lunii-style horizontal carousel as Histoires: one big activity tile per full-width slide, snap-scroll swipe, tap arrows, a dot per activity. Header (date/time/weather) unchanged. Drop the now-unused make_tile.
This commit is contained in:
+118
-30
@@ -50,35 +50,93 @@ static void tile_cb(lv_event_t *e)
|
||||
lisael_ui_goto(mode);
|
||||
}
|
||||
|
||||
static lv_obj_t *make_tile(lv_obj_t *parent, const tile_def_t *def)
|
||||
// --- activity carousel (Lunii/Merlin style launcher) -------------------------
|
||||
// One big activity tile per full-width slide; swipe (or arrows) to move one at a
|
||||
// time, tap the tile to launch. Dots show the position.
|
||||
#define HOME_SLIDE_W 320
|
||||
|
||||
static lv_obj_t *s_track;
|
||||
static lv_obj_t *s_adots[8];
|
||||
static int s_acur;
|
||||
static int s_n_acts;
|
||||
|
||||
static void adots_update(int idx)
|
||||
{
|
||||
lv_obj_t *tile = lv_button_create(parent);
|
||||
for (int i = 0; i < s_n_acts; i++) {
|
||||
if (s_adots[i]) {
|
||||
lv_obj_set_style_bg_opa(s_adots[i], i == idx ? LV_OPA_COVER : LV_OPA_30, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void home_scroll_cb(lv_event_t *e)
|
||||
{
|
||||
lv_obj_t *t = lv_event_get_target(e);
|
||||
int idx = (lv_obj_get_scroll_x(t) + HOME_SLIDE_W / 2) / HOME_SLIDE_W;
|
||||
if (idx < 0) idx = 0;
|
||||
if (idx >= s_n_acts) idx = s_n_acts - 1;
|
||||
s_acur = idx;
|
||||
adots_update(idx);
|
||||
}
|
||||
|
||||
static void home_arrow_cb(lv_event_t *e)
|
||||
{
|
||||
int idx = s_acur + (int)(intptr_t)lv_event_get_user_data(e);
|
||||
if (idx < 0) idx = 0;
|
||||
if (idx >= s_n_acts) idx = s_n_acts - 1;
|
||||
s_acur = idx;
|
||||
lv_obj_scroll_to_x(s_track, idx * HOME_SLIDE_W, LV_ANIM_ON);
|
||||
adots_update(idx);
|
||||
}
|
||||
|
||||
static void home_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, home_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_act_slide(lv_obj_t *track, const tile_def_t *def)
|
||||
{
|
||||
lv_obj_t *slide = lv_obj_create(track);
|
||||
lv_obj_remove_style_all(slide);
|
||||
lv_obj_set_size(slide, HOME_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);
|
||||
// Glossy 3D look: lighter at the top fading to the tile colour (the shared
|
||||
// style sets LV_GRAD_DIR_VER + the soft drop shadow).
|
||||
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);
|
||||
// Press feedback: the tile sinks a little.
|
||||
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, 94, 72);
|
||||
lv_obj_add_event_cb(tile, tile_cb, LV_EVENT_CLICKED,
|
||||
(void *)(intptr_t)def->mode);
|
||||
|
||||
lv_obj_t *col = lv_obj_create(tile);
|
||||
lv_obj_remove_style_all(col);
|
||||
lv_obj_set_size(col, LV_PCT(100), LV_PCT(100));
|
||||
lv_obj_set_flex_flow(col, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(col, LV_FLEX_ALIGN_CENTER,
|
||||
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_clear_flag(col, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_set_style_pad_row(tile, 6, 0);
|
||||
lv_obj_clear_flag(tile, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_event_cb(tile, tile_cb, LV_EVENT_CLICKED, (void *)(intptr_t)def->mode);
|
||||
|
||||
lv_obj_t *icon = lv_image_create(col);
|
||||
lv_obj_t *icon = lv_image_create(tile);
|
||||
lv_image_set_src(icon, def->icon);
|
||||
lv_obj_set_size(icon, 64, 64); // scale-to-fit by size (reliable, unlike set_scale)
|
||||
lv_image_set_inner_align(icon, LV_IMAGE_ALIGN_CONTAIN);
|
||||
|
||||
lv_obj_t *txt = lv_label_create(col);
|
||||
lv_obj_t *txt = lv_label_create(tile);
|
||||
lv_label_set_text(txt, def->label);
|
||||
return tile;
|
||||
lv_obj_set_style_text_font(txt, &lisael_font_24, 0);
|
||||
}
|
||||
|
||||
// --- internal temperature sensor (graceful fallback) ------------------------
|
||||
@@ -135,16 +193,7 @@ lv_obj_t *lisael_screen_home(void)
|
||||
lv_label_set_text(s_sensor_lbl, "");
|
||||
lv_obj_align(s_sensor_lbl, LV_ALIGN_TOP_RIGHT, -12, 58);
|
||||
|
||||
// --- mode tiles ----------------------------------------------------------
|
||||
lv_obj_t *grid = lv_obj_create(s_root);
|
||||
lv_obj_remove_style_all(grid);
|
||||
lv_obj_set_size(grid, LV_PCT(100), 160);
|
||||
lv_obj_align(grid, LV_ALIGN_BOTTOM_MID, 0, -2);
|
||||
lv_obj_set_flex_flow(grid, LV_FLEX_FLOW_ROW_WRAP);
|
||||
lv_obj_set_flex_align(grid, LV_FLEX_ALIGN_SPACE_EVENLY,
|
||||
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_EVENLY);
|
||||
lv_obj_set_style_pad_row(grid, 8, 0);
|
||||
|
||||
// --- activities: horizontal carousel (one big tile per slide) -----------
|
||||
static const tile_def_t tiles[] = {
|
||||
{ &lisael_ic_histoires, "Histoires", LISAEL_MODE_HISTOIRES, {0} },
|
||||
{ &lisael_ic_book, "Lire", LISAEL_MODE_LIRE, {0} },
|
||||
@@ -157,12 +206,51 @@ lv_obj_t *lisael_screen_home(void)
|
||||
static const uint32_t colors[] = {
|
||||
0xFFE08A, 0xFFD8A8, 0xB2F2BB, 0xA5D8FF, 0xD0BFFF, 0xFFC9C9, 0xCED4DA,
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(tiles) / sizeof(tiles[0]); i++) {
|
||||
s_n_acts = (int)(sizeof(tiles) / sizeof(tiles[0]));
|
||||
|
||||
s_track = lv_obj_create(s_root);
|
||||
lv_obj_remove_style_all(s_track);
|
||||
lv_obj_set_size(s_track, HOME_SLIDE_W, 134);
|
||||
lv_obj_align(s_track, LV_ALIGN_TOP_MID, 0, 80);
|
||||
lv_obj_set_flex_flow(s_track, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_style_pad_all(s_track, 0, 0);
|
||||
lv_obj_set_style_pad_column(s_track, 0, 0);
|
||||
lv_obj_set_scroll_dir(s_track, LV_DIR_HOR);
|
||||
lv_obj_set_scroll_snap_x(s_track, LV_SCROLL_SNAP_CENTER);
|
||||
lv_obj_set_scrollbar_mode(s_track, LV_SCROLLBAR_MODE_OFF);
|
||||
lv_obj_add_event_cb(s_track, home_scroll_cb, LV_EVENT_SCROLL_END, NULL);
|
||||
|
||||
for (int i = 0; i < s_n_acts; i++) {
|
||||
tile_def_t t = tiles[i];
|
||||
t.color = lv_color_hex(colors[i]);
|
||||
make_tile(grid, &t);
|
||||
add_act_slide(s_track, &t);
|
||||
}
|
||||
|
||||
lv_obj_t *dotrow = lv_obj_create(s_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 < s_n_acts; 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(0x1971C2), 0);
|
||||
lv_obj_set_style_bg_opa(d, i == 0 ? LV_OPA_COVER : LV_OPA_30, 0);
|
||||
s_adots[i] = d;
|
||||
}
|
||||
|
||||
home_arrow(s_root, LV_SYMBOL_LEFT, LV_ALIGN_LEFT_MID, -1);
|
||||
home_arrow(s_root, LV_SYMBOL_RIGHT, LV_ALIGN_RIGHT_MID, +1);
|
||||
s_acur = 0;
|
||||
adots_update(0);
|
||||
|
||||
// NB: the live values are filled in shortly by the tick/net tasks via
|
||||
// lisael_home_update_*(). We do NOT call them here: this builder runs while
|
||||
// the caller already holds the display lock, and those helpers take the
|
||||
|
||||
Reference in New Issue
Block a user