Skip to content

Commit f15f450

Browse files
Implement advanced Pro Landscape UI with fake chat
1 parent 90a11f4 commit f15f450

1 file changed

Lines changed: 210 additions & 71 deletions

File tree

src/driver/drv_display_demo.cpp

Lines changed: 210 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -13,81 +13,229 @@
1313
#include "esp_chip_info.h"
1414

1515
static lv_obj_t *screen_main;
16+
static lv_obj_t *msg_list;
17+
static lv_obj_t *kb;
1618
static lv_obj_t *mem_label;
17-
static lv_obj_t *chip_label;
1819
static lv_obj_t *chart;
1920
static lv_chart_series_t * ser;
21+
static lv_obj_t * arc_thermo;
22+
static lv_obj_t * label_thermo;
2023
static int tick_count = 0;
2124

25+
static void add_chat_bubble(const char * txt, bool is_user) {
26+
lv_obj_t * wrapper = lv_obj_create(msg_list);
27+
lv_obj_set_width(wrapper, lv_pct(100));
28+
lv_obj_set_height(wrapper, LV_SIZE_CONTENT);
29+
lv_obj_set_style_bg_opa(wrapper, 0, 0);
30+
lv_obj_set_style_border_width(wrapper, 0, 0);
31+
lv_obj_set_style_pad_all(wrapper, 0, 0);
32+
lv_obj_clear_flag(wrapper, LV_OBJ_FLAG_SCROLLABLE);
33+
34+
lv_obj_t * msg_box = lv_obj_create(wrapper);
35+
lv_obj_set_size(msg_box, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
36+
lv_obj_set_style_radius(msg_box, 15, 0);
37+
lv_obj_set_style_border_width(msg_box, 0, 0);
38+
lv_obj_set_style_pad_all(msg_box, 15, 0);
39+
40+
lv_obj_t * label = lv_label_create(msg_box);
41+
lv_label_set_text(label, txt);
42+
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
43+
lv_obj_set_width(label, 300); // fixed max width for wrap
44+
45+
if(is_user) {
46+
lv_obj_set_style_bg_color(msg_box, lv_color_hex(0x2196F3), 0);
47+
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
48+
lv_obj_align(msg_box, LV_ALIGN_RIGHT_MID, -10, 0);
49+
} else {
50+
lv_obj_set_style_bg_color(msg_box, lv_color_hex(0xE0E0E0), 0);
51+
lv_obj_set_style_text_color(label, lv_color_hex(0x333333), 0);
52+
lv_obj_align(msg_box, LV_ALIGN_LEFT_MID, 10, 0);
53+
}
54+
lv_obj_scroll_to_view(wrapper, LV_ANIM_ON);
55+
}
56+
57+
static void ta_event_cb(lv_event_t * e) {
58+
lv_event_code_t code = lv_event_get_code(e);
59+
lv_obj_t * ta = lv_event_get_target(e);
60+
if(code == LV_EVENT_FOCUSED) {
61+
lv_keyboard_set_textarea(kb, ta);
62+
lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN);
63+
lv_obj_move_foreground(kb);
64+
} else if(code == LV_EVENT_DEFOCUSED) {
65+
lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
66+
} else if(code == LV_EVENT_READY) {
67+
const char * txt = lv_textarea_get_text(ta);
68+
if(strlen(txt) > 0) {
69+
add_chat_bubble(txt, true);
70+
// Simulate bot reply
71+
add_chat_bubble("I am a demo bot! That's interesting.", false);
72+
}
73+
lv_textarea_set_text(ta, "");
74+
lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
75+
lv_obj_clear_state(ta, LV_STATE_FOCUSED);
76+
}
77+
}
78+
79+
static void thermo_anim_cb(void * var, int32_t v) {
80+
lv_arc_set_value((lv_obj_t*)var, v);
81+
char buf[16];
82+
snprintf(buf, sizeof(buf), "%d\xc2\xb0""C", (int)v); // UTF-8 degree symbol
83+
lv_label_set_text(label_thermo, buf);
84+
}
85+
2286
static void gui_build_main_screen() {
2387
screen_main = lv_obj_create(NULL);
24-
// White background
25-
lv_obj_set_style_bg_color(screen_main, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
88+
lv_obj_set_style_bg_color(screen_main, lv_color_hex(0xF5F7FA), LV_PART_MAIN);
2689

27-
// Create a tab view
28-
lv_obj_t * tabview = lv_tabview_create(screen_main, LV_DIR_TOP, 50);
29-
lv_obj_set_style_bg_color(tabview, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
90+
// Left sidebar tab view for landscape Pro look
91+
lv_obj_t * tabview = lv_tabview_create(screen_main, LV_DIR_LEFT, 100);
92+
lv_obj_set_style_bg_color(tabview, lv_color_hex(0xF5F7FA), LV_PART_MAIN);
3093

31-
// Get the buttons part of the tabview and style it
3294
lv_obj_t * tab_btns = lv_tabview_get_tab_btns(tabview);
33-
lv_obj_set_style_bg_color(tab_btns, lv_color_hex(0xF0F0F0), LV_PART_MAIN);
34-
lv_obj_set_style_text_color(tab_btns, lv_color_hex(0x999999), LV_PART_MAIN);
95+
lv_obj_set_style_bg_color(tab_btns, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
96+
lv_obj_set_style_text_color(tab_btns, lv_color_hex(0x888888), LV_PART_MAIN);
3597
lv_obj_set_style_text_color(tab_btns, lv_color_hex(0x2196F3), (lv_style_selector_t)((int)LV_PART_ITEMS | (int)LV_STATE_CHECKED));
98+
lv_obj_set_style_border_width(tab_btns, 0, LV_PART_MAIN);
99+
lv_obj_set_style_shadow_width(tab_btns, 15, LV_PART_MAIN);
100+
lv_obj_set_style_shadow_color(tab_btns, lv_color_hex(0x000000), LV_PART_MAIN);
101+
lv_obj_set_style_shadow_opa(tab_btns, 15, LV_PART_MAIN);
102+
103+
lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "\n" LV_SYMBOL_HOME "\nHome");
104+
lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "\n" LV_SYMBOL_SETTINGS "\nCtrl");
105+
lv_obj_t * tab3 = lv_tabview_add_tab(tabview, "\n" LV_SYMBOL_BELL "\nChat");
106+
107+
// --- TAB 1: Home Dashboard ---
108+
lv_obj_set_layout(tab1, LV_LAYOUT_FLEX);
109+
lv_obj_set_flex_flow(tab1, LV_FLEX_FLOW_ROW_WRAP);
110+
lv_obj_set_flex_align(tab1, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
111+
lv_obj_set_style_pad_all(tab1, 20, 0);
112+
113+
// Card 1: Thermostat
114+
lv_obj_t * card1 = lv_obj_create(tab1);
115+
lv_obj_set_size(card1, 280, 280);
116+
lv_obj_set_style_radius(card1, 20, 0);
117+
lv_obj_set_style_border_width(card1, 0, 0);
118+
lv_obj_set_style_shadow_width(card1, 20, 0);
119+
lv_obj_set_style_shadow_color(card1, lv_color_hex(0x000000), 0);
120+
lv_obj_set_style_shadow_opa(card1, 10, 0);
121+
122+
lv_obj_t * thermo_title = lv_label_create(card1);
123+
lv_label_set_text(thermo_title, "Living Room");
124+
lv_obj_align(thermo_title, LV_ALIGN_TOP_MID, 0, 0);
125+
lv_obj_set_style_text_color(thermo_title, lv_color_hex(0x888888), 0);
126+
127+
arc_thermo = lv_arc_create(card1);
128+
lv_obj_set_size(arc_thermo, 180, 180);
129+
lv_obj_align(arc_thermo, LV_ALIGN_CENTER, 0, 15);
130+
lv_arc_set_range(arc_thermo, 10, 35);
131+
lv_arc_set_value(arc_thermo, 10);
132+
lv_obj_set_style_arc_color(arc_thermo, lv_color_hex(0xE0E0E0), LV_PART_MAIN);
133+
lv_obj_set_style_arc_color(arc_thermo, lv_color_hex(0xFF9800), LV_PART_INDICATOR);
134+
lv_obj_set_style_arc_width(arc_thermo, 15, LV_PART_MAIN);
135+
lv_obj_set_style_arc_width(arc_thermo, 15, LV_PART_INDICATOR);
136+
lv_obj_remove_style(arc_thermo, NULL, LV_PART_KNOB);
137+
138+
label_thermo = lv_label_create(card1);
139+
lv_label_set_text(label_thermo, "10\xc2\xb0""C");
140+
lv_obj_align(label_thermo, LV_ALIGN_CENTER, 0, 15);
141+
// Note: ensure font supports degree symbol, otherwise it may not show. We use default font.
36142

37-
// Add 3 tabs
38-
lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "Controls");
39-
lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "Dashboard");
40-
lv_obj_t * tab3 = lv_tabview_add_tab(tabview, "System");
41-
42-
// --- TAB 1: Controls ---
43-
// A slider
44-
lv_obj_t * slider = lv_slider_create(tab1);
45-
lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 20);
46-
lv_obj_set_width(slider, 200);
47-
lv_obj_set_style_bg_color(slider, lv_color_hex(0xDDDDDD), LV_PART_MAIN);
48-
lv_obj_set_style_bg_color(slider, lv_color_hex(0x2196F3), LV_PART_INDICATOR);
49-
50-
// A switch
51-
lv_obj_t * sw = lv_switch_create(tab1);
52-
lv_obj_align(sw, LV_ALIGN_CENTER, -60, 20);
53-
lv_obj_set_style_bg_color(sw, lv_color_hex(0x4CAF50), (lv_style_selector_t)((int)LV_PART_INDICATOR | (int)LV_STATE_CHECKED));
54-
55-
// An arc
56-
lv_obj_t * arc = lv_arc_create(tab1);
57-
lv_obj_set_size(arc, 100, 100);
58-
lv_obj_align(arc, LV_ALIGN_CENTER, 60, 20);
59-
lv_arc_set_value(arc, 40);
60-
lv_obj_set_style_arc_color(arc, lv_color_hex(0xDDDDDD), LV_PART_MAIN);
61-
lv_obj_set_style_arc_color(arc, lv_color_hex(0x2196F3), LV_PART_INDICATOR);
62-
63-
// --- TAB 2: Dashboard ---
64-
chart = lv_chart_create(tab2);
65-
lv_obj_set_size(chart, 240, 150);
66-
lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0);
143+
// Card 2: Power Chart
144+
lv_obj_t * card2 = lv_obj_create(tab1);
145+
lv_obj_set_size(card2, 340, 280);
146+
lv_obj_set_style_radius(card2, 20, 0);
147+
lv_obj_set_style_border_width(card2, 0, 0);
148+
lv_obj_set_style_shadow_width(card2, 20, 0);
149+
lv_obj_set_style_shadow_color(card2, lv_color_hex(0x000000), 0);
150+
lv_obj_set_style_shadow_opa(card2, 10, 0);
151+
152+
lv_obj_t * chart_title = lv_label_create(card2);
153+
lv_label_set_text(chart_title, "Power Usage (W)");
154+
lv_obj_align(chart_title, LV_ALIGN_TOP_MID, 0, 0);
155+
lv_obj_set_style_text_color(chart_title, lv_color_hex(0x888888), 0);
156+
157+
chart = lv_chart_create(card2);
158+
lv_obj_set_size(chart, 300, 180);
159+
lv_obj_align(chart, LV_ALIGN_CENTER, 0, 15);
67160
lv_chart_set_type(chart, LV_CHART_TYPE_LINE);
68-
lv_obj_set_style_bg_color(chart, lv_color_hex(0xFAFAFA), LV_PART_MAIN);
69-
lv_obj_set_style_line_color(chart, lv_color_hex(0xCCCCCC), LV_PART_ITEMS);
161+
lv_obj_set_style_bg_opa(chart, 0, LV_PART_MAIN);
162+
lv_obj_set_style_border_width(chart, 0, LV_PART_MAIN);
163+
lv_obj_set_style_line_width(chart, 3, LV_PART_ITEMS);
164+
lv_obj_set_style_line_color(chart, lv_color_hex(0x2196F3), LV_PART_ITEMS);
70165

71-
// Add data series
72-
ser = lv_chart_add_series(chart, lv_color_hex(0x009688), LV_CHART_AXIS_PRIMARY_Y);
166+
ser = lv_chart_add_series(chart, lv_color_hex(0x2196F3), LV_CHART_AXIS_PRIMARY_Y);
73167
for(int i = 0; i < 10; i++) {
74-
lv_chart_set_next_value(chart, ser, lv_rand(10, 90));
168+
lv_chart_set_next_value(chart, ser, lv_rand(100, 500));
169+
}
170+
171+
// --- TAB 2: Controls ---
172+
lv_obj_set_layout(tab2, LV_LAYOUT_FLEX);
173+
lv_obj_set_flex_flow(tab2, LV_FLEX_FLOW_ROW_WRAP);
174+
lv_obj_set_flex_align(tab2, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
175+
176+
for(int i=0; i<6; i++) {
177+
lv_obj_t * c_card = lv_obj_create(tab2);
178+
lv_obj_set_size(c_card, 220, 120);
179+
lv_obj_set_style_radius(c_card, 15, 0);
180+
lv_obj_set_style_border_width(c_card, 0, 0);
181+
lv_obj_set_style_shadow_width(c_card, 10, 0);
182+
lv_obj_set_style_shadow_color(c_card, lv_color_hex(0x000000), 0);
183+
lv_obj_set_style_shadow_opa(c_card, 10, 0);
184+
185+
lv_obj_t * sw = lv_switch_create(c_card);
186+
lv_obj_align(sw, LV_ALIGN_RIGHT_MID, -10, 0);
187+
lv_obj_set_style_bg_color(sw, lv_color_hex(0x4CAF50), (lv_style_selector_t)((int)LV_PART_INDICATOR | (int)LV_STATE_CHECKED));
188+
189+
lv_obj_t * l = lv_label_create(c_card);
190+
char lbuf[32];
191+
snprintf(lbuf, sizeof(lbuf), "Light %d", i+1);
192+
lv_label_set_text(l, lbuf);
193+
lv_obj_align(l, LV_ALIGN_LEFT_MID, 10, 0);
75194
}
76195

77-
// --- TAB 3: System ---
78-
mem_label = lv_label_create(tab3);
79-
lv_label_set_text(mem_label, "Mem: --");
80-
lv_obj_align(mem_label, LV_ALIGN_TOP_LEFT, 10, 10);
81-
lv_obj_set_style_text_color(mem_label, lv_color_hex(0x555555), LV_PART_MAIN);
82-
83-
chip_label = lv_label_create(tab3);
84-
esp_chip_info_t chip_info;
85-
esp_chip_info(&chip_info);
86-
char cbuf[128];
87-
snprintf(cbuf, sizeof(cbuf), "Cores: %d\nRev: %d", chip_info.cores, chip_info.revision);
88-
lv_label_set_text(chip_label, cbuf);
89-
lv_obj_align(chip_label, LV_ALIGN_TOP_LEFT, 10, 60);
90-
lv_obj_set_style_text_color(chip_label, lv_color_hex(0x555555), LV_PART_MAIN);
196+
// --- TAB 3: Fake Chat ---
197+
lv_obj_t * chat_container = lv_obj_create(tab3);
198+
lv_obj_set_size(chat_container, LV_PCT(100), LV_PCT(100));
199+
lv_obj_set_style_border_width(chat_container, 0, 0);
200+
lv_obj_set_style_bg_opa(chat_container, 0, 0);
201+
lv_obj_set_style_pad_all(chat_container, 0, 0);
202+
lv_obj_set_layout(chat_container, LV_LAYOUT_FLEX);
203+
lv_obj_set_flex_flow(chat_container, LV_FLEX_FLOW_COLUMN);
204+
205+
msg_list = lv_obj_create(chat_container);
206+
lv_obj_set_width(msg_list, LV_PCT(100));
207+
lv_obj_set_flex_grow(msg_list, 1);
208+
lv_obj_set_style_border_width(msg_list, 0, 0);
209+
lv_obj_set_style_bg_color(msg_list, lv_color_hex(0xF5F7FA), 0);
210+
lv_obj_set_layout(msg_list, LV_LAYOUT_FLEX);
211+
lv_obj_set_flex_flow(msg_list, LV_FLEX_FLOW_COLUMN);
212+
213+
lv_obj_t * ta = lv_textarea_create(chat_container);
214+
lv_textarea_set_one_line(ta, true);
215+
lv_obj_set_width(ta, LV_PCT(100));
216+
lv_textarea_set_placeholder_text(ta, "Type a message...");
217+
lv_obj_add_event_cb(ta, ta_event_cb, LV_EVENT_ALL, NULL);
218+
219+
// Global keyboard
220+
kb = lv_keyboard_create(screen_main);
221+
lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
222+
223+
// Setup initial chat
224+
add_chat_bubble("Hello! I'm your smart home assistant.", false);
225+
226+
// System info in corner
227+
mem_label = lv_label_create(screen_main);
228+
lv_obj_align(mem_label, LV_ALIGN_BOTTOM_RIGHT, -10, -10);
229+
lv_obj_set_style_text_color(mem_label, lv_color_hex(0xAAAAAA), 0);
230+
231+
// Startup Animation for thermostat
232+
lv_anim_t a;
233+
lv_anim_init(&a);
234+
lv_anim_set_var(&a, arc_thermo);
235+
lv_anim_set_exec_cb(&a, thermo_anim_cb);
236+
lv_anim_set_time(&a, 1500);
237+
lv_anim_set_values(&a, 10, 24);
238+
lv_anim_start(&a);
91239
}
92240

93241
extern "C" void DisplayDemo_Init() {
@@ -105,24 +253,15 @@ extern "C" void DisplayDemo_Init() {
105253

106254
extern "C" void DisplayDemo_OnEverySecond() {
107255
if(!g_display_ready) return;
108-
109256
tick_count++;
110257

111-
char mbuf[128];
258+
char mbuf[64];
112259
size_t free_int = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
113-
size_t max_int = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
114-
size_t free_spi = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
115-
size_t max_spi = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
116-
snprintf(mbuf, sizeof(mbuf), "INT: %u (%u max)\nSPI: %u (%u max)",
117-
(unsigned)free_int, (unsigned)max_int, (unsigned)free_spi, (unsigned)max_spi);
260+
snprintf(mbuf, sizeof(mbuf), "Free Mem: %u B", (unsigned)free_int);
118261

119262
lvgl_port_lock(-1);
120-
if(mem_label) {
121-
lv_label_set_text(mem_label, mbuf);
122-
}
123-
if (chart && ser) {
124-
lv_chart_set_next_value(chart, ser, lv_rand(10, 90));
125-
}
263+
if(mem_label) lv_label_set_text(mem_label, mbuf);
264+
if(chart && ser) lv_chart_set_next_value(chart, ser, lv_rand(100, 500));
126265
lvgl_port_unlock();
127266
}
128267

0 commit comments

Comments
 (0)