From f15f450a88644b9efb2d315c7b0d2dcf7e7566f8 Mon Sep 17 00:00:00 2001 From: Tester23 <85486843+openshwprojects@users.noreply.github.com> Date: Mon, 11 May 2026 10:11:39 +0200 Subject: [PATCH] Implement advanced Pro Landscape UI with fake chat --- src/driver/drv_display_demo.cpp | 271 ++++++++++++++++++++++++-------- 1 file changed, 205 insertions(+), 66 deletions(-) diff --git a/src/driver/drv_display_demo.cpp b/src/driver/drv_display_demo.cpp index 8bfefa062..0484149e5 100644 --- a/src/driver/drv_display_demo.cpp +++ b/src/driver/drv_display_demo.cpp @@ -13,81 +13,229 @@ #include "esp_chip_info.h" static lv_obj_t *screen_main; +static lv_obj_t *msg_list; +static lv_obj_t *kb; static lv_obj_t *mem_label; -static lv_obj_t *chip_label; static lv_obj_t *chart; static lv_chart_series_t * ser; +static lv_obj_t * arc_thermo; +static lv_obj_t * label_thermo; static int tick_count = 0; +static void add_chat_bubble(const char * txt, bool is_user) { + lv_obj_t * wrapper = lv_obj_create(msg_list); + lv_obj_set_width(wrapper, lv_pct(100)); + lv_obj_set_height(wrapper, LV_SIZE_CONTENT); + lv_obj_set_style_bg_opa(wrapper, 0, 0); + lv_obj_set_style_border_width(wrapper, 0, 0); + lv_obj_set_style_pad_all(wrapper, 0, 0); + lv_obj_clear_flag(wrapper, LV_OBJ_FLAG_SCROLLABLE); + + lv_obj_t * msg_box = lv_obj_create(wrapper); + lv_obj_set_size(msg_box, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_obj_set_style_radius(msg_box, 15, 0); + lv_obj_set_style_border_width(msg_box, 0, 0); + lv_obj_set_style_pad_all(msg_box, 15, 0); + + lv_obj_t * label = lv_label_create(msg_box); + lv_label_set_text(label, txt); + lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); + lv_obj_set_width(label, 300); // fixed max width for wrap + + if(is_user) { + lv_obj_set_style_bg_color(msg_box, lv_color_hex(0x2196F3), 0); + lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0); + lv_obj_align(msg_box, LV_ALIGN_RIGHT_MID, -10, 0); + } else { + lv_obj_set_style_bg_color(msg_box, lv_color_hex(0xE0E0E0), 0); + lv_obj_set_style_text_color(label, lv_color_hex(0x333333), 0); + lv_obj_align(msg_box, LV_ALIGN_LEFT_MID, 10, 0); + } + lv_obj_scroll_to_view(wrapper, LV_ANIM_ON); +} + +static void ta_event_cb(lv_event_t * e) { + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * ta = lv_event_get_target(e); + if(code == LV_EVENT_FOCUSED) { + lv_keyboard_set_textarea(kb, ta); + lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN); + lv_obj_move_foreground(kb); + } else if(code == LV_EVENT_DEFOCUSED) { + lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN); + } else if(code == LV_EVENT_READY) { + const char * txt = lv_textarea_get_text(ta); + if(strlen(txt) > 0) { + add_chat_bubble(txt, true); + // Simulate bot reply + add_chat_bubble("I am a demo bot! That's interesting.", false); + } + lv_textarea_set_text(ta, ""); + lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_state(ta, LV_STATE_FOCUSED); + } +} + +static void thermo_anim_cb(void * var, int32_t v) { + lv_arc_set_value((lv_obj_t*)var, v); + char buf[16]; + snprintf(buf, sizeof(buf), "%d\xc2\xb0""C", (int)v); // UTF-8 degree symbol + lv_label_set_text(label_thermo, buf); +} + static void gui_build_main_screen() { screen_main = lv_obj_create(NULL); - // White background - lv_obj_set_style_bg_color(screen_main, lv_color_hex(0xFFFFFF), LV_PART_MAIN); + lv_obj_set_style_bg_color(screen_main, lv_color_hex(0xF5F7FA), LV_PART_MAIN); - // Create a tab view - lv_obj_t * tabview = lv_tabview_create(screen_main, LV_DIR_TOP, 50); - lv_obj_set_style_bg_color(tabview, lv_color_hex(0xFFFFFF), LV_PART_MAIN); + // Left sidebar tab view for landscape Pro look + lv_obj_t * tabview = lv_tabview_create(screen_main, LV_DIR_LEFT, 100); + lv_obj_set_style_bg_color(tabview, lv_color_hex(0xF5F7FA), LV_PART_MAIN); - // Get the buttons part of the tabview and style it lv_obj_t * tab_btns = lv_tabview_get_tab_btns(tabview); - lv_obj_set_style_bg_color(tab_btns, lv_color_hex(0xF0F0F0), LV_PART_MAIN); - lv_obj_set_style_text_color(tab_btns, lv_color_hex(0x999999), LV_PART_MAIN); + lv_obj_set_style_bg_color(tab_btns, lv_color_hex(0xFFFFFF), LV_PART_MAIN); + lv_obj_set_style_text_color(tab_btns, lv_color_hex(0x888888), LV_PART_MAIN); lv_obj_set_style_text_color(tab_btns, lv_color_hex(0x2196F3), (lv_style_selector_t)((int)LV_PART_ITEMS | (int)LV_STATE_CHECKED)); + lv_obj_set_style_border_width(tab_btns, 0, LV_PART_MAIN); + lv_obj_set_style_shadow_width(tab_btns, 15, LV_PART_MAIN); + lv_obj_set_style_shadow_color(tab_btns, lv_color_hex(0x000000), LV_PART_MAIN); + lv_obj_set_style_shadow_opa(tab_btns, 15, LV_PART_MAIN); - // Add 3 tabs - lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "Controls"); - lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "Dashboard"); - lv_obj_t * tab3 = lv_tabview_add_tab(tabview, "System"); + lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "\n" LV_SYMBOL_HOME "\nHome"); + lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "\n" LV_SYMBOL_SETTINGS "\nCtrl"); + lv_obj_t * tab3 = lv_tabview_add_tab(tabview, "\n" LV_SYMBOL_BELL "\nChat"); - // --- TAB 1: Controls --- - // A slider - lv_obj_t * slider = lv_slider_create(tab1); - lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 20); - lv_obj_set_width(slider, 200); - lv_obj_set_style_bg_color(slider, lv_color_hex(0xDDDDDD), LV_PART_MAIN); - lv_obj_set_style_bg_color(slider, lv_color_hex(0x2196F3), LV_PART_INDICATOR); + // --- TAB 1: Home Dashboard --- + lv_obj_set_layout(tab1, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(tab1, LV_FLEX_FLOW_ROW_WRAP); + lv_obj_set_flex_align(tab1, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_pad_all(tab1, 20, 0); - // A switch - lv_obj_t * sw = lv_switch_create(tab1); - lv_obj_align(sw, LV_ALIGN_CENTER, -60, 20); - lv_obj_set_style_bg_color(sw, lv_color_hex(0x4CAF50), (lv_style_selector_t)((int)LV_PART_INDICATOR | (int)LV_STATE_CHECKED)); + // Card 1: Thermostat + lv_obj_t * card1 = lv_obj_create(tab1); + lv_obj_set_size(card1, 280, 280); + lv_obj_set_style_radius(card1, 20, 0); + lv_obj_set_style_border_width(card1, 0, 0); + lv_obj_set_style_shadow_width(card1, 20, 0); + lv_obj_set_style_shadow_color(card1, lv_color_hex(0x000000), 0); + lv_obj_set_style_shadow_opa(card1, 10, 0); - // An arc - lv_obj_t * arc = lv_arc_create(tab1); - lv_obj_set_size(arc, 100, 100); - lv_obj_align(arc, LV_ALIGN_CENTER, 60, 20); - lv_arc_set_value(arc, 40); - lv_obj_set_style_arc_color(arc, lv_color_hex(0xDDDDDD), LV_PART_MAIN); - lv_obj_set_style_arc_color(arc, lv_color_hex(0x2196F3), LV_PART_INDICATOR); + lv_obj_t * thermo_title = lv_label_create(card1); + lv_label_set_text(thermo_title, "Living Room"); + lv_obj_align(thermo_title, LV_ALIGN_TOP_MID, 0, 0); + lv_obj_set_style_text_color(thermo_title, lv_color_hex(0x888888), 0); - // --- TAB 2: Dashboard --- - chart = lv_chart_create(tab2); - lv_obj_set_size(chart, 240, 150); - lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0); + arc_thermo = lv_arc_create(card1); + lv_obj_set_size(arc_thermo, 180, 180); + lv_obj_align(arc_thermo, LV_ALIGN_CENTER, 0, 15); + lv_arc_set_range(arc_thermo, 10, 35); + lv_arc_set_value(arc_thermo, 10); + lv_obj_set_style_arc_color(arc_thermo, lv_color_hex(0xE0E0E0), LV_PART_MAIN); + lv_obj_set_style_arc_color(arc_thermo, lv_color_hex(0xFF9800), LV_PART_INDICATOR); + lv_obj_set_style_arc_width(arc_thermo, 15, LV_PART_MAIN); + lv_obj_set_style_arc_width(arc_thermo, 15, LV_PART_INDICATOR); + lv_obj_remove_style(arc_thermo, NULL, LV_PART_KNOB); + + label_thermo = lv_label_create(card1); + lv_label_set_text(label_thermo, "10\xc2\xb0""C"); + lv_obj_align(label_thermo, LV_ALIGN_CENTER, 0, 15); + // Note: ensure font supports degree symbol, otherwise it may not show. We use default font. + + // Card 2: Power Chart + lv_obj_t * card2 = lv_obj_create(tab1); + lv_obj_set_size(card2, 340, 280); + lv_obj_set_style_radius(card2, 20, 0); + lv_obj_set_style_border_width(card2, 0, 0); + lv_obj_set_style_shadow_width(card2, 20, 0); + lv_obj_set_style_shadow_color(card2, lv_color_hex(0x000000), 0); + lv_obj_set_style_shadow_opa(card2, 10, 0); + + lv_obj_t * chart_title = lv_label_create(card2); + lv_label_set_text(chart_title, "Power Usage (W)"); + lv_obj_align(chart_title, LV_ALIGN_TOP_MID, 0, 0); + lv_obj_set_style_text_color(chart_title, lv_color_hex(0x888888), 0); + + chart = lv_chart_create(card2); + lv_obj_set_size(chart, 300, 180); + lv_obj_align(chart, LV_ALIGN_CENTER, 0, 15); lv_chart_set_type(chart, LV_CHART_TYPE_LINE); - lv_obj_set_style_bg_color(chart, lv_color_hex(0xFAFAFA), LV_PART_MAIN); - lv_obj_set_style_line_color(chart, lv_color_hex(0xCCCCCC), LV_PART_ITEMS); + lv_obj_set_style_bg_opa(chart, 0, LV_PART_MAIN); + lv_obj_set_style_border_width(chart, 0, LV_PART_MAIN); + lv_obj_set_style_line_width(chart, 3, LV_PART_ITEMS); + lv_obj_set_style_line_color(chart, lv_color_hex(0x2196F3), LV_PART_ITEMS); - // Add data series - ser = lv_chart_add_series(chart, lv_color_hex(0x009688), LV_CHART_AXIS_PRIMARY_Y); + ser = lv_chart_add_series(chart, lv_color_hex(0x2196F3), LV_CHART_AXIS_PRIMARY_Y); for(int i = 0; i < 10; i++) { - lv_chart_set_next_value(chart, ser, lv_rand(10, 90)); + lv_chart_set_next_value(chart, ser, lv_rand(100, 500)); } - // --- TAB 3: System --- - mem_label = lv_label_create(tab3); - lv_label_set_text(mem_label, "Mem: --"); - lv_obj_align(mem_label, LV_ALIGN_TOP_LEFT, 10, 10); - lv_obj_set_style_text_color(mem_label, lv_color_hex(0x555555), LV_PART_MAIN); + // --- TAB 2: Controls --- + lv_obj_set_layout(tab2, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(tab2, LV_FLEX_FLOW_ROW_WRAP); + lv_obj_set_flex_align(tab2, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - chip_label = lv_label_create(tab3); - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); - char cbuf[128]; - snprintf(cbuf, sizeof(cbuf), "Cores: %d\nRev: %d", chip_info.cores, chip_info.revision); - lv_label_set_text(chip_label, cbuf); - lv_obj_align(chip_label, LV_ALIGN_TOP_LEFT, 10, 60); - lv_obj_set_style_text_color(chip_label, lv_color_hex(0x555555), LV_PART_MAIN); + for(int i=0; i<6; i++) { + lv_obj_t * c_card = lv_obj_create(tab2); + lv_obj_set_size(c_card, 220, 120); + lv_obj_set_style_radius(c_card, 15, 0); + lv_obj_set_style_border_width(c_card, 0, 0); + lv_obj_set_style_shadow_width(c_card, 10, 0); + lv_obj_set_style_shadow_color(c_card, lv_color_hex(0x000000), 0); + lv_obj_set_style_shadow_opa(c_card, 10, 0); + + lv_obj_t * sw = lv_switch_create(c_card); + lv_obj_align(sw, LV_ALIGN_RIGHT_MID, -10, 0); + lv_obj_set_style_bg_color(sw, lv_color_hex(0x4CAF50), (lv_style_selector_t)((int)LV_PART_INDICATOR | (int)LV_STATE_CHECKED)); + + lv_obj_t * l = lv_label_create(c_card); + char lbuf[32]; + snprintf(lbuf, sizeof(lbuf), "Light %d", i+1); + lv_label_set_text(l, lbuf); + lv_obj_align(l, LV_ALIGN_LEFT_MID, 10, 0); + } + + // --- TAB 3: Fake Chat --- + lv_obj_t * chat_container = lv_obj_create(tab3); + lv_obj_set_size(chat_container, LV_PCT(100), LV_PCT(100)); + lv_obj_set_style_border_width(chat_container, 0, 0); + lv_obj_set_style_bg_opa(chat_container, 0, 0); + lv_obj_set_style_pad_all(chat_container, 0, 0); + lv_obj_set_layout(chat_container, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(chat_container, LV_FLEX_FLOW_COLUMN); + + msg_list = lv_obj_create(chat_container); + lv_obj_set_width(msg_list, LV_PCT(100)); + lv_obj_set_flex_grow(msg_list, 1); + lv_obj_set_style_border_width(msg_list, 0, 0); + lv_obj_set_style_bg_color(msg_list, lv_color_hex(0xF5F7FA), 0); + lv_obj_set_layout(msg_list, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(msg_list, LV_FLEX_FLOW_COLUMN); + + lv_obj_t * ta = lv_textarea_create(chat_container); + lv_textarea_set_one_line(ta, true); + lv_obj_set_width(ta, LV_PCT(100)); + lv_textarea_set_placeholder_text(ta, "Type a message..."); + lv_obj_add_event_cb(ta, ta_event_cb, LV_EVENT_ALL, NULL); + + // Global keyboard + kb = lv_keyboard_create(screen_main); + lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN); + + // Setup initial chat + add_chat_bubble("Hello! I'm your smart home assistant.", false); + + // System info in corner + mem_label = lv_label_create(screen_main); + lv_obj_align(mem_label, LV_ALIGN_BOTTOM_RIGHT, -10, -10); + lv_obj_set_style_text_color(mem_label, lv_color_hex(0xAAAAAA), 0); + + // Startup Animation for thermostat + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, arc_thermo); + lv_anim_set_exec_cb(&a, thermo_anim_cb); + lv_anim_set_time(&a, 1500); + lv_anim_set_values(&a, 10, 24); + lv_anim_start(&a); } extern "C" void DisplayDemo_Init() { @@ -105,24 +253,15 @@ extern "C" void DisplayDemo_Init() { extern "C" void DisplayDemo_OnEverySecond() { if(!g_display_ready) return; - tick_count++; - char mbuf[128]; + char mbuf[64]; size_t free_int = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); - size_t max_int = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); - size_t free_spi = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); - size_t max_spi = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM); - snprintf(mbuf, sizeof(mbuf), "INT: %u (%u max)\nSPI: %u (%u max)", - (unsigned)free_int, (unsigned)max_int, (unsigned)free_spi, (unsigned)max_spi); + snprintf(mbuf, sizeof(mbuf), "Free Mem: %u B", (unsigned)free_int); lvgl_port_lock(-1); - if(mem_label) { - lv_label_set_text(mem_label, mbuf); - } - if (chart && ser) { - lv_chart_set_next_value(chart, ser, lv_rand(10, 90)); - } + if(mem_label) lv_label_set_text(mem_label, mbuf); + if(chart && ser) lv_chart_set_next_value(chart, ser, lv_rand(100, 500)); lvgl_port_unlock(); }