fix(ui): make card view scrollable via rotary encoder with pinned header and footer
All checks were successful
ESP32 Build & Release / build (push) Successful in 1m11s

This commit is contained in:
2026-09-12 17:46:01 +02:00
parent 05b50d0a89
commit 4f7b1870bb

View File

@@ -119,6 +119,9 @@ static lv_obj_t *s_ota_bar = NULL;
static lv_obj_t *s_ota_pct_lbl = NULL; static lv_obj_t *s_ota_pct_lbl = NULL;
static lv_obj_t *s_ota_bytes_lbl = NULL; static lv_obj_t *s_ota_bytes_lbl = NULL;
/* Card view scrollable container */
static lv_obj_t *s_card_scroll_cont = NULL;
/* Forward declarations */ /* Forward declarations */
static void view_menu(void); static void view_menu(void);
static void view_scanner(void); static void view_scanner(void);
@@ -248,6 +251,7 @@ static const char *get_json_str(cJSON *root, const char *key, const char *fallba
static void ui_clean_screen(void) static void ui_clean_screen(void)
{ {
free_art_buffer(); free_art_buffer();
s_card_scroll_cont = NULL;
s_ota_bar = NULL; s_ota_bar = NULL;
s_ota_pct_lbl = NULL; s_ota_pct_lbl = NULL;
s_ota_bytes_lbl = NULL; s_ota_bytes_lbl = NULL;
@@ -1425,9 +1429,10 @@ static void view_card(void)
lv_obj_set_style_border_width(card_box, 2, 0); lv_obj_set_style_border_width(card_box, 2, 0);
lv_obj_set_style_radius(card_box, 6, 0); lv_obj_set_style_radius(card_box, 6, 0);
lv_obj_set_style_pad_all(card_box, 6, 0); lv_obj_set_style_pad_all(card_box, 6, 0);
lv_obj_clear_flag(card_box, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_scrollbar_mode(card_box, LV_SCROLLBAR_MODE_OFF); lv_obj_set_scrollbar_mode(card_box, LV_SCROLLBAR_MODE_OFF);
/* Name */ /* Pinned Header: Name */
lv_obj_t *name_lbl = lv_label_create(card_box); lv_obj_t *name_lbl = lv_label_create(card_box);
lv_label_set_text(name_lbl, name); lv_label_set_text(name_lbl, name);
lv_obj_set_style_text_font(name_lbl, &lv_font_montserrat_14, 0); lv_obj_set_style_text_font(name_lbl, &lv_font_montserrat_14, 0);
@@ -1437,7 +1442,7 @@ static void view_card(void)
lv_label_set_long_mode(name_lbl, LV_LABEL_LONG_DOT); lv_label_set_long_mode(name_lbl, LV_LABEL_LONG_DOT);
lv_obj_align(name_lbl, LV_ALIGN_TOP_LEFT, 0, 0); lv_obj_align(name_lbl, LV_ALIGN_TOP_LEFT, 0, 0);
/* Mana */ /* Pinned Header: Mana */
if (mana[0]) { if (mana[0]) {
lv_obj_t *mana_lbl = lv_label_create(card_box); lv_obj_t *mana_lbl = lv_label_create(card_box);
lv_label_set_text(mana_lbl, mana); lv_label_set_text(mana_lbl, mana);
@@ -1446,41 +1451,7 @@ static void view_card(void)
lv_obj_align(mana_lbl, LV_ALIGN_TOP_RIGHT, 0, 0); lv_obj_align(mana_lbl, LV_ALIGN_TOP_RIGHT, 0, 0);
} }
int cur_y = 20; /* Pinned Footer: P/T or Loyalty */
/* Art Crop Image */
if (has_art) {
lv_obj_t *img = lv_img_create(card_box);
lv_img_set_src(img, &s_art_img_dsc);
lv_obj_align(img, LV_ALIGN_TOP_MID, 0, cur_y);
lv_obj_set_style_border_color(img, lv_color_hex(0x8B6508), 0);
lv_obj_set_style_border_width(img, 1, 0);
lv_obj_set_style_radius(img, 3, 0);
cur_y += s_art_img_dsc.header.h + 4;
}
/* Type */
lv_obj_t *type_lbl = lv_label_create(card_box);
lv_label_set_text(type_lbl, type);
lv_obj_set_style_text_font(type_lbl, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(type_lbl, lv_color_white(), 0);
lv_obj_set_width(type_lbl, DISP_W - 28);
lv_label_set_long_mode(type_lbl, LV_LABEL_LONG_DOT);
lv_obj_align(type_lbl, LV_ALIGN_TOP_LEFT, 0, cur_y);
cur_y += 18;
/* Oracle text */
if (oracle[0]) {
lv_obj_t *oracle_lbl = lv_label_create(card_box);
lv_label_set_text(oracle_lbl, oracle);
lv_label_set_long_mode(oracle_lbl, LV_LABEL_LONG_WRAP);
lv_obj_set_width(oracle_lbl, DISP_W - 32);
lv_obj_set_style_text_font(oracle_lbl, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(oracle_lbl, lv_color_hex(COLOR_AMBER), 0);
lv_obj_align(oracle_lbl, LV_ALIGN_TOP_LEFT, 0, cur_y);
}
/* Bottom: P/T, Loyalty, and Price */
if (power[0] || tough[0]) { if (power[0] || tough[0]) {
lv_obj_t *pt_lbl = lv_label_create(card_box); lv_obj_t *pt_lbl = lv_label_create(card_box);
lv_label_set_text_fmt(pt_lbl, "%s / %s", power, tough); lv_label_set_text_fmt(pt_lbl, "%s / %s", power, tough);
@@ -1495,6 +1466,7 @@ static void view_card(void)
lv_obj_align(loy_lbl, LV_ALIGN_BOTTOM_RIGHT, 0, 0); lv_obj_align(loy_lbl, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
} }
/* Pinned Footer: Price */
if (price[0]) { if (price[0]) {
lv_obj_t *pr_lbl = lv_label_create(card_box); lv_obj_t *pr_lbl = lv_label_create(card_box);
lv_label_set_text_fmt(pr_lbl, "$%s", price); lv_label_set_text_fmt(pr_lbl, "$%s", price);
@@ -1503,6 +1475,56 @@ static void view_card(void)
lv_obj_align(pr_lbl, LV_ALIGN_BOTTOM_LEFT, 0, 0); lv_obj_align(pr_lbl, LV_ALIGN_BOTTOM_LEFT, 0, 0);
} }
/* Middle Scrollable Container (Art + Type + Oracle Text) */
s_card_scroll_cont = lv_obj_create(card_box);
lv_obj_set_size(s_card_scroll_cont, lv_pct(100), 250);
lv_obj_set_pos(s_card_scroll_cont, 0, 22);
lv_obj_set_style_bg_opa(s_card_scroll_cont, LV_OPA_TRANSP, 0);
lv_obj_set_style_border_width(s_card_scroll_cont, 0, 0);
lv_obj_set_style_pad_all(s_card_scroll_cont, 0, 0);
lv_obj_set_style_pad_right(s_card_scroll_cont, 6, 0);
lv_obj_set_style_pad_bottom(s_card_scroll_cont, 24, 0);
lv_obj_set_scrollbar_mode(s_card_scroll_cont, LV_SCROLLBAR_MODE_AUTO);
lv_obj_set_scroll_dir(s_card_scroll_cont, LV_DIR_VER);
lv_obj_set_style_bg_color(s_card_scroll_cont, lv_color_hex(COLOR_GOLD), LV_PART_SCROLLBAR);
lv_obj_set_style_bg_opa(s_card_scroll_cont, LV_OPA_70, LV_PART_SCROLLBAR);
lv_obj_set_style_width(s_card_scroll_cont, 4, LV_PART_SCROLLBAR);
int cur_y = 0;
/* Art Crop Image */
if (has_art) {
lv_obj_t *img = lv_img_create(s_card_scroll_cont);
lv_img_set_src(img, &s_art_img_dsc);
lv_obj_align(img, LV_ALIGN_TOP_MID, 0, cur_y);
lv_obj_set_style_border_color(img, lv_color_hex(0x8B6508), 0);
lv_obj_set_style_border_width(img, 1, 0);
lv_obj_set_style_radius(img, 3, 0);
cur_y += s_art_img_dsc.header.h + 4;
}
/* Type */
lv_obj_t *type_lbl = lv_label_create(s_card_scroll_cont);
lv_label_set_text(type_lbl, type);
lv_obj_set_style_text_font(type_lbl, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(type_lbl, lv_color_white(), 0);
lv_obj_set_width(type_lbl, DISP_W - 32);
lv_label_set_long_mode(type_lbl, LV_LABEL_LONG_WRAP);
lv_obj_align(type_lbl, LV_ALIGN_TOP_LEFT, 0, cur_y);
lv_obj_update_layout(type_lbl);
cur_y += lv_obj_get_height(type_lbl) + 4;
/* Oracle text */
if (oracle[0]) {
lv_obj_t *oracle_lbl = lv_label_create(s_card_scroll_cont);
lv_label_set_text(oracle_lbl, oracle);
lv_label_set_long_mode(oracle_lbl, LV_LABEL_LONG_WRAP);
lv_obj_set_width(oracle_lbl, DISP_W - 36);
lv_obj_set_style_text_font(oracle_lbl, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(oracle_lbl, lv_color_hex(COLOR_AMBER), 0);
lv_obj_align(oracle_lbl, LV_ALIGN_TOP_LEFT, 0, cur_y);
}
lvgl_port_unlock(); lvgl_port_unlock();
cJSON_Delete(root); cJSON_Delete(root);
} }
@@ -1855,6 +1877,16 @@ static void handle_encoder_rotation(int32_t delta)
if (delta == 0) return; if (delta == 0) return;
app_state_t cur = app_get_view(); app_state_t cur = app_get_view();
if (cur == APP_STATE_CARD_VIEW) {
if (s_card_scroll_cont != NULL) {
lvgl_port_lock(0);
lv_obj_scroll_by_bounded(s_card_scroll_cont, 0, -delta * 35, LV_ANIM_ON);
lvgl_port_unlock();
}
buzzer_chirp(600, 10);
return;
}
int32_t dir = (delta > 0) ? 1 : -1; int32_t dir = (delta > 0) ? 1 : -1;
int32_t count = (delta > 0) ? delta : -delta; int32_t count = (delta > 0) ? delta : -delta;