#include "ui_main.h" #include #include "esp_log.h" #include "lvgl.h" #include "card_assets.h" #include "settings.h" #include "tarot_logic.h" #include "ui_settings.h" static const char *TAG = "ui_main"; static lv_style_t s_style_screen; static lv_style_t s_style_title; static lv_style_t s_style_button; static lv_style_t s_style_card; static lv_style_t s_style_muted; static bool s_styles_ready; static tarot_reading_t s_current_reading; static void show_reading_cb(lv_event_t *event); static void show_home_cb(lv_event_t *event); static void show_settings_cb(lv_event_t *event); static lv_obj_t *create_button(lv_obj_t *parent, const char *text, lv_event_cb_t cb) { lv_obj_t *button = lv_btn_create(parent); lv_obj_add_style(button, &s_style_button, 0); lv_obj_set_size(button, 210, 54); lv_obj_add_event_cb(button, cb, LV_EVENT_CLICKED, NULL); lv_obj_t *label = lv_label_create(button); lv_label_set_text(label, text); lv_obj_center(label); return button; } void ui_apply_current_theme(void) { tarot_settings_t settings = settings_get(); lv_color_t bg = lv_color_hex(0x111018); lv_color_t fg = lv_color_hex(0xf5efe6); lv_color_t card = lv_color_hex(0x201827); lv_color_t accent = lv_color_hex(0x8d5cf6); if (settings.theme == TAROT_THEME_LIGHT) { bg = lv_color_hex(0xf7f0e6); fg = lv_color_hex(0x221b18); card = lv_color_hex(0xffffff); accent = lv_color_hex(0x805b2f); } else if (settings.theme == TAROT_THEME_MYSTIC) { bg = lv_color_hex(0x080b19); fg = lv_color_hex(0xf1e8ff); card = lv_color_hex(0x151b36); accent = lv_color_hex(0xc39bff); } if (!s_styles_ready) { lv_style_init(&s_style_screen); lv_style_init(&s_style_title); lv_style_init(&s_style_button); lv_style_init(&s_style_card); lv_style_init(&s_style_muted); s_styles_ready = true; } lv_style_reset(&s_style_screen); lv_style_set_bg_color(&s_style_screen, bg); lv_style_set_text_color(&s_style_screen, fg); lv_style_set_pad_all(&s_style_screen, 18); lv_style_reset(&s_style_title); lv_style_set_text_color(&s_style_title, fg); lv_style_set_text_font(&s_style_title, LV_FONT_DEFAULT); lv_style_reset(&s_style_button); lv_style_set_bg_color(&s_style_button, accent); lv_style_set_text_color(&s_style_button, lv_color_white()); lv_style_set_radius(&s_style_button, 8); lv_style_reset(&s_style_card); lv_style_set_bg_color(&s_style_card, card); lv_style_set_text_color(&s_style_card, fg); lv_style_set_radius(&s_style_card, 8); lv_style_set_pad_all(&s_style_card, 10); lv_style_set_border_width(&s_style_card, 1); lv_style_set_border_color(&s_style_card, accent); lv_style_reset(&s_style_muted); lv_style_set_text_color(&s_style_muted, lv_color_hex(0xaaa2b8)); } void ui_main_init(void) { ui_apply_current_theme(); } void ui_show_home(void) { ui_apply_current_theme(); lv_obj_t *screen = lv_obj_create(NULL); lv_obj_add_style(screen, &s_style_screen, 0); lv_obj_set_flex_flow(screen, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(screen, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_t *title = lv_label_create(screen); lv_obj_add_style(title, &s_style_title, 0); lv_label_set_text(title, "ESP32-P4 Tarot"); lv_obj_set_style_text_font(title, LV_FONT_DEFAULT, 0); lv_obj_t *subtitle = lv_label_create(screen); lv_obj_add_style(subtitle, &s_style_muted, 0); lv_label_set_text(subtitle, "On-device card art ready"); create_button(screen, "Draw Cards", show_reading_cb); create_button(screen, "Settings", show_settings_cb); lv_scr_load(screen); } static void add_card_tile(lv_obj_t *grid, const tarot_drawn_card_t *drawn, size_t index) { const tarot_card_t *card = drawn->card; lv_obj_t *tile = lv_obj_create(grid); lv_obj_add_style(tile, &s_style_card, 0); lv_obj_set_size(tile, 230, 340); lv_obj_set_flex_flow(tile, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(tile, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_t *position = lv_label_create(tile); lv_label_set_text_fmt(position, "%u. %s", (unsigned)(index + 1), drawn->position_meaning); lv_label_set_long_mode(position, LV_LABEL_LONG_WRAP); lv_obj_set_width(position, 205); card_assets_create_card_art(tile, card, drawn->reversed); lv_obj_t *name = lv_label_create(tile); lv_label_set_text(name, card->name); lv_label_set_long_mode(name, LV_LABEL_LONG_WRAP); lv_obj_set_width(name, 205); lv_obj_t *orientation = lv_label_create(tile); lv_obj_add_style(orientation, &s_style_muted, 0); lv_label_set_text(orientation, drawn->reversed ? "Reversed" : "Upright"); lv_obj_t *meaning = lv_label_create(tile); lv_label_set_text(meaning, drawn->reversed ? card->reversed_meaning : card->upright_meaning); lv_label_set_long_mode(meaning, LV_LABEL_LONG_WRAP); lv_obj_set_width(meaning, 205); } void ui_show_reading(void) { tarot_settings_t settings = settings_get(); tarot_logic_draw_reading(&s_current_reading, settings.selected_spread, settings.reversed_cards_enabled); ESP_LOGI(TAG, "Drew %u unique tarot cards", (unsigned)s_current_reading.count); lv_obj_t *screen = lv_obj_create(NULL); lv_obj_add_style(screen, &s_style_screen, 0); lv_obj_set_flex_flow(screen, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(screen, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_t *header = lv_obj_create(screen); lv_obj_set_width(header, LV_PCT(100)); lv_obj_set_height(header, LV_SIZE_CONTENT); lv_obj_set_style_bg_opa(header, LV_OPA_TRANSP, 0); lv_obj_set_style_border_width(header, 0, 0); lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(header, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_t *title = lv_label_create(header); lv_label_set_text_fmt(title, "%s Reading", tarot_spread_name(s_current_reading.spread)); lv_obj_t *actions = lv_obj_create(header); lv_obj_set_style_bg_opa(actions, LV_OPA_TRANSP, 0); lv_obj_set_style_border_width(actions, 0, 0); lv_obj_set_size(actions, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_flex_flow(actions, LV_FLEX_FLOW_ROW); create_button(actions, "New Reading", show_reading_cb); create_button(actions, "Back Home", show_home_cb); lv_obj_t *grid = lv_obj_create(screen); lv_obj_set_width(grid, LV_PCT(100)); lv_obj_set_flex_grow(grid, 1); lv_obj_set_style_bg_opa(grid, LV_OPA_TRANSP, 0); lv_obj_set_style_border_width(grid, 0, 0); lv_obj_set_flex_flow(grid, LV_FLEX_FLOW_ROW_WRAP); lv_obj_set_flex_align(grid, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); for (size_t i = 0; i < s_current_reading.count; i++) { add_card_tile(grid, &s_current_reading.cards[i], i); } lv_scr_load(screen); } static void show_reading_cb(lv_event_t *event) { (void)event; ui_show_reading(); } static void show_home_cb(lv_event_t *event) { (void)event; ui_show_home(); } static void show_settings_cb(lv_event_t *event) { (void)event; ui_show_settings(); }