Fix menu navigation issues with Bluetooth and initial focus

- Initialize menu context when showing menu to properly track focused item
- Fix double-activation bug that caused immediate exit from Bluetooth menu
- Add separate refresh_bt_device_list() to avoid infinite discovery loop
- Delete old BT page before creating new one to prevent memory leaks
- Add "Clear Paired" option to Bluetooth menu
- Improve Bluetooth menu refresh behavior to show "Scanning..." message

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Brent Perteet
2025-11-13 19:55:32 -06:00
parent 19e8ca30c3
commit 115105c032

View File

@@ -116,6 +116,39 @@ static lv_style_t _styleFocusedBtn;
static menu_context_t _menuContext; static menu_context_t _menuContext;
// Hide *all* headers/back buttons that LVGL may show for this menu
static void menu_hide_headers(lv_obj_t *menu) {
if (!menu) return;
// Main header (normal menus)
lv_obj_t *mh = lv_menu_get_main_header(menu);
if (mh) {
lv_obj_add_flag(mh, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_height(mh, 0);
}
lv_obj_t *mh_back = lv_menu_get_main_header_back_button(menu);
if (mh_back) {
lv_obj_add_flag(mh_back, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_width(mh_back, 0);
lv_obj_set_height(mh_back, 0);
}
// Sidebar header (if you ever use sidebar mode)
lv_obj_t *sh = lv_menu_get_sidebar_header(menu);
if (sh) {
lv_obj_add_flag(sh, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_height(sh, 0);
}
lv_obj_t *sh_back = lv_menu_get_sidebar_header_back_button(menu);
if (sh_back) {
lv_obj_add_flag(sh_back, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_width(sh_back, 0);
lv_obj_set_height(sh_back, 0);
}
}
static bool notify_lvgl_flush_ready(void *user_ctx) { static bool notify_lvgl_flush_ready(void *user_ctx) {
if (disp) { if (disp) {
lv_display_flush_ready(disp); lv_display_flush_ready(disp);
@@ -127,10 +160,14 @@ static lv_obj_t* create_main_page(void) {
lv_obj_t* main_page; lv_obj_t* main_page;
ensure_menu_styles(); ensure_menu_styles();
lv_obj_t* menu = create_menu_container(); lv_obj_t* menu = create_menu_container();
main_page = lv_menu_page_create(menu, NULL); main_page = lv_menu_page_create(menu, NULL);
lv_obj_set_style_radius(main_page, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_radius(main_page, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_border_width(main_page, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_border_width(main_page, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_pad_all(main_page, 0, LV_PART_MAIN);
lv_obj_set_style_pad_row(main_page, 0, LV_PART_MAIN);
lv_obj_set_style_pad_column(main_page, 0, LV_PART_MAIN);
lv_obj_set_scrollbar_mode(main_page, LV_SCROLLBAR_MODE_AUTO); lv_obj_set_scrollbar_mode(main_page, LV_SCROLLBAR_MODE_AUTO);
lv_obj_set_size(main_page, lv_pct(100), lv_pct(100)); lv_obj_set_size(main_page, lv_pct(100), lv_pct(100));
@@ -138,25 +175,30 @@ static lv_obj_t* create_main_page(void) {
lv_obj_t* tmpObj; lv_obj_t* tmpObj;
tmpObj = addMenuItem(main_page, "Bluetooth"); tmpObj = addMenuItem(main_page, "Bluetooth");
lv_obj_add_state(tmpObj, LV_STATE_FOCUSED); // First item focused lv_obj_add_state(tmpObj, LV_STATE_FOCUSED); // First item focused
addMenuItem(main_page, "Calibration"); addMenuItem(main_page, "Calibration");
addMenuItem(main_page, "Volume"); addMenuItem(main_page, "Volume");
addMenuItem(main_page, "About"); //addMenuItem(main_page, "About");
addMenuItem(main_page, "Exit"); addMenuItem(main_page, "Exit");
return main_page; return main_page;
} }
static void show_menu(void) { static void show_menu(void) {
lv_obj_t* menu = create_menu_container(); lv_obj_t* menu = create_menu_container();
lv_obj_t* main_page = create_main_page(); lv_obj_t* main_page = create_main_page();
// Clear the menu stack when starting fresh menu navigation // Clear the menu stack when starting fresh menu navigation
menu_stack_clear(); menu_stack_clear();
lv_menu_set_page(menu, main_page); lv_menu_set_page(menu, main_page);
_currentPage = main_page; _currentPage = main_page;
// Initialize menu context to track the focused item
currentFocusIndex(&_menuContext);
menu_hide_headers(menu);
lv_obj_remove_flag(menu, LV_OBJ_FLAG_HIDDEN); lv_obj_remove_flag(menu, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN);
} }
@@ -359,6 +401,8 @@ static const char * items[] = { // your menu entries
static lv_obj_t * btn_array[ITEM_COUNT]; static lv_obj_t * btn_array[ITEM_COUNT];
static int selected_idx = 0; static int selected_idx = 0;
// Called whenever a button is "activated" (Enter/Click) // Called whenever a button is "activated" (Enter/Click)
static void btn_click_cb(lv_event_t * e) { static void btn_click_cb(lv_event_t * e) {
lv_obj_t * btn = lv_event_get_target(e); lv_obj_t * btn = lv_event_get_target(e);
@@ -487,22 +531,41 @@ static void activate_selected(void)
UNLOCK(); UNLOCK();
} }
// Event handler to control label scrolling based on focus state
static void label_scroll_focus_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * lbl = lv_obj_get_child(btn, 0); // Get the label child
if (!lbl) return;
if (code == LV_EVENT_FOCUSED) {
// Start scrolling when focused
lv_label_set_long_mode(lbl, LV_LABEL_LONG_SCROLL_CIRCULAR);
}
else if (code == LV_EVENT_DEFOCUSED) {
// Stop scrolling and reset position when defocused
lv_label_set_long_mode(lbl, LV_LABEL_LONG_CLIP);
}
}
static lv_obj_t * addMenuItem(lv_obj_t *page, const char *text) static lv_obj_t * addMenuItem(lv_obj_t *page, const char *text)
{ {
if (!page || !text) { if (!page || !text) {
ESP_LOGE(TAG, "Null parameters in addMenuItem"); ESP_LOGE(TAG, "Null parameters in addMenuItem");
return NULL; return NULL;
} }
// Ensure styles are initialized // Ensure styles are initialized
ensure_menu_styles(); ensure_menu_styles();
lv_obj_t * btn = lv_btn_create(page); lv_obj_t * btn = lv_btn_create(page);
if (!btn) { if (!btn) {
ESP_LOGE(TAG, "Failed to create button in addMenuItem"); ESP_LOGE(TAG, "Failed to create button in addMenuItem");
return NULL; return NULL;
} }
lv_obj_set_size(btn, LV_PCT(100), ROW_H); lv_obj_set_size(btn, LV_PCT(100), ROW_H);
lv_obj_add_style(btn, &_styleUnfocusedBtn, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_add_style(btn, &_styleUnfocusedBtn, LV_PART_MAIN | LV_STATE_DEFAULT);
@@ -513,7 +576,7 @@ static lv_obj_t * addMenuItem(lv_obj_t *page, const char *text)
// style it just like your old list // style it just like your old list
// lv_obj_set_style_bg_color(btn, lv_color_white(), 0); // lv_obj_set_style_bg_color(btn, lv_color_white(), 0);
lv_obj_set_style_radius(btn, 0, LV_PART_MAIN); lv_obj_set_style_radius(btn, 0, LV_PART_MAIN);
lv_obj_set_style_border_width(btn, 0, LV_PART_MAIN); lv_obj_set_style_border_width(btn, 0, LV_PART_MAIN);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE);
@@ -524,12 +587,19 @@ static lv_obj_t * addMenuItem(lv_obj_t *page, const char *text)
lv_obj_del(btn); lv_obj_del(btn);
return NULL; return NULL;
} }
lv_label_set_text(lbl, text); lv_label_set_text(lbl, text);
lv_obj_set_style_radius(lbl, 0, LV_PART_MAIN | LV_STATE_ANY); lv_label_set_long_mode(lbl, LV_LABEL_LONG_CLIP); // Start with clipping, scrolling only when focused
lv_obj_set_width(lbl, LV_PCT(95)); // Set width to allow scrolling
lv_obj_set_style_radius(lbl, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_border_width(lbl, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_border_width(lbl, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_anim_time(lbl, 4000, LV_PART_MAIN); // Set scrolling duration (4 seconds per cycle - slower)
lv_obj_align(lbl, LV_ALIGN_LEFT_MID, 0, 0); // Center vertically, align left horizontally lv_obj_align(lbl, LV_ALIGN_LEFT_MID, 0, 0); // Center vertically, align left horizontally
// Add focus event handler to control scrolling
lv_obj_add_event_cb(btn, label_scroll_focus_cb, LV_EVENT_FOCUSED, NULL);
lv_obj_add_event_cb(btn, label_scroll_focus_cb, LV_EVENT_DEFOCUSED, NULL);
// click callback // click callback
lv_obj_add_event_cb(btn, btn_click_cb, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(btn, btn_click_cb, LV_EVENT_CLICKED, NULL);
@@ -587,13 +657,41 @@ static void ensure_menu_styles(void) {
static lv_obj_t* create_menu_container(void) { static lv_obj_t* create_menu_container(void) {
if (_menu == NULL) { if (_menu == NULL) {
_menu = lv_menu_create(lv_scr_act()); _menu = lv_menu_create(lv_scr_act());
menu_hide_headers(_menu);
lv_obj_set_style_radius(_menu, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_radius(_menu, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_border_width(_menu, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_border_width(_menu, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_size(_menu, lv_pct(100), lv_pct(100)); lv_obj_set_size(_menu, lv_pct(100), lv_pct(100));
lv_obj_center(_menu); lv_obj_center(_menu);
lv_obj_set_scrollbar_mode(_menu, LV_SCROLLBAR_MODE_AUTO); lv_obj_set_scrollbar_mode(_menu, LV_SCROLLBAR_MODE_AUTO);
lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN); // Hidden by default lv_obj_add_flag(_menu, LV_OBJ_FLAG_HIDDEN); // Hidden by default
// Hide the sidebar/header
lv_menu_set_sidebar_page(_menu, NULL);
lv_obj_set_style_pad_all(_menu, 0, LV_PART_MAIN);
// Disable the back button in header
lv_menu_set_mode_root_back_button(_menu, LV_MENU_ROOT_BACK_BUTTON_DISABLED);
// Find and hide the header container
// The menu has 2 children: child 0 is header, child 1 is content area
uint32_t menu_child_count = lv_obj_get_child_count(_menu);
ESP_LOGI(TAG, "Menu container has %d children", (int)menu_child_count);
if (menu_child_count >= 1) {
lv_obj_t* header = lv_obj_get_child(_menu, 0);
int32_t h = lv_obj_get_height(header);
int32_t w = lv_obj_get_width(header);
ESP_LOGI(TAG, " Header (child 0): %p, size=%dx%d", header, (int)w, (int)h);
// Hide the header completely
lv_obj_add_flag(header, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_height(header, 0);
ESP_LOGI(TAG, " Hidden header");
}
} }
return _menu; return _menu;
} }
@@ -643,8 +741,23 @@ static void bt_device_click_cb(lv_event_t * e) {
return; return;
} else if (strcmp(txt, "Refresh") == 0) { } else if (strcmp(txt, "Refresh") == 0) {
LOCK(); LOCK();
// Use system event instead of direct BT call // Recreate the BT page with discovery enabled to show "Scanning..."
system_requestBtRefresh(); show_bt_device_list();
UNLOCK();
return;
} else if (strcmp(txt, "Clear Paired") == 0) {
LOCK();
// Clear all paired devices from NVS and device list
esp_err_t ret = system_clearAllPairedDevices();
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Successfully cleared all paired devices from NVS");
// Also clear the in-memory device list used by GUI
bt_clear_all_devices();
// Refresh the device list to show updated state
system_requestBtRefresh();
} else {
ESP_LOGE(TAG, "Failed to clear paired devices: %s", esp_err_to_name(ret));
}
UNLOCK(); UNLOCK();
return; return;
} }
@@ -676,36 +789,47 @@ static void bt_device_click_cb(lv_event_t * e) {
} }
} }
static lv_obj_t* create_bt_device_page(void) { static lv_obj_t* create_bt_device_page(bool start_discovery) {
ESP_LOGI(TAG, "Creating Bluetooth device page"); ESP_LOGI(TAG, "Creating Bluetooth device page (start_discovery=%d)", start_discovery);
lv_obj_t* menu = create_menu_container(); lv_obj_t* menu = create_menu_container();
if (!menu) { if (!menu) {
ESP_LOGE(TAG, "Failed to create menu container"); ESP_LOGE(TAG, "Failed to create menu container");
return NULL; return NULL;
} }
// Delete old BT page if it exists to prevent memory leaks
if (_bt_page) {
ESP_LOGI(TAG, "Deleting old Bluetooth page");
lv_obj_del(_bt_page);
_bt_page = NULL;
}
_bt_page = lv_menu_page_create(menu, NULL); _bt_page = lv_menu_page_create(menu, NULL);
if (!_bt_page) { if (!_bt_page) {
ESP_LOGE(TAG, "Failed to create Bluetooth page"); ESP_LOGE(TAG, "Failed to create Bluetooth page");
return NULL; return NULL;
} }
lv_obj_set_style_radius(_bt_page, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_radius(_bt_page, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_border_width(_bt_page, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_border_width(_bt_page, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_pad_all(_bt_page, 0, LV_PART_MAIN);
lv_obj_set_style_pad_row(_bt_page, 0, LV_PART_MAIN);
lv_obj_set_style_pad_column(_bt_page, 0, LV_PART_MAIN);
lv_obj_set_scrollbar_mode(_bt_page, LV_SCROLLBAR_MODE_AUTO); lv_obj_set_scrollbar_mode(_bt_page, LV_SCROLLBAR_MODE_AUTO);
lv_obj_set_size(_bt_page, lv_pct(100), lv_pct(100)); lv_obj_set_size(_bt_page, lv_pct(100), lv_pct(100));
ESP_LOGI(TAG, "Starting Bluetooth discovery"); bool discovery_started = false;
bool discovery_started = true; if (start_discovery) {
#if 0 ESP_LOGI(TAG, "Starting Bluetooth discovery");
// Try to start discovery (may fail if BT stack is busy) // Try to start discovery (may fail if BT stack is busy)
bool discovery_started = bt_start_discovery(); discovery_started = bt_start_discovery();
if (!discovery_started) { if (!discovery_started) {
ESP_LOGW(TAG, "Discovery not started - will show paired devices only"); ESP_LOGW(TAG, "Discovery not started - will show paired devices only");
}
} }
#endif
// Get device list // Get device list
ESP_LOGI(TAG, "Getting device list"); ESP_LOGI(TAG, "Getting device list");
bt_device_list_t* device_list = bt_get_device_list(); bt_device_list_t* device_list = bt_get_device_list();
@@ -716,9 +840,10 @@ static lv_obj_t* create_bt_device_page(void) {
if (device_list->count == 0) { if (device_list->count == 0) {
// Show appropriate message based on discovery status // Show appropriate message based on discovery status
const char* msg = discovery_started ? "Scanning for devices..." : "No devices found"; const char* msg = discovery_started ? "Scanning..." : "No devices found";
lv_obj_t* tmpObj = addMenuItem(_bt_page, msg); lv_obj_t* tmpObj = addMenuItem(_bt_page, msg);
lv_obj_add_state(tmpObj, LV_STATE_DISABLED); lv_obj_add_state(tmpObj, LV_STATE_DISABLED);
lv_obj_clear_state(tmpObj, LV_STATE_FOCUSED); // Remove focus from disabled item
} else { } else {
// Add devices to the page // Add devices to the page
bool first = true; bool first = true;
@@ -738,41 +863,92 @@ static lv_obj_t* create_bt_device_page(void) {
} }
} }
// Add back/refresh options // Add back/refresh/clear options
lv_obj_t* refresh_btn = addMenuItem(_bt_page, "Refresh"); lv_obj_t* refresh_btn = addMenuItem(_bt_page, "Refresh");
lv_obj_add_event_cb(refresh_btn, bt_device_click_cb, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(refresh_btn, bt_device_click_cb, LV_EVENT_CLICKED, NULL);
// If no devices were listed, focus on Refresh button
if (device_list->count == 0) {
lv_obj_add_state(refresh_btn, LV_STATE_FOCUSED);
}
lv_obj_t* clear_btn = addMenuItem(_bt_page, "Clear Paired");
lv_obj_add_event_cb(clear_btn, bt_device_click_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t* back_btn = addMenuItem(_bt_page, "Back"); lv_obj_t* back_btn = addMenuItem(_bt_page, "Back");
lv_obj_add_event_cb(back_btn, bt_device_click_cb, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(back_btn, bt_device_click_cb, LV_EVENT_CLICKED, NULL);
return _bt_page; return _bt_page;
} }
static void show_bt_device_list(void) { static void show_bt_device_list(void) {
ESP_LOGI(TAG, "Showing Bluetooth device list"); ESP_LOGI(TAG, "Showing Bluetooth device list with discovery");
// Start discovery when explicitly showing the BT device list
bool should_start_discovery = true;
lv_obj_t* menu = create_menu_container(); lv_obj_t* menu = create_menu_container();
if (!menu) { if (!menu) {
ESP_LOGE(TAG, "Failed to create menu container for Bluetooth list"); ESP_LOGE(TAG, "Failed to create menu container for Bluetooth list");
return; return;
} }
lv_obj_t* bt_page = create_bt_device_page(); lv_obj_t* bt_page = create_bt_device_page(should_start_discovery);
if (!bt_page) { if (!bt_page) {
ESP_LOGE(TAG, "Failed to create Bluetooth device page"); ESP_LOGE(TAG, "Failed to create Bluetooth device page");
return; return;
} }
lv_menu_set_page(menu, bt_page); lv_menu_set_page(menu, bt_page);
_currentPage = bt_page; _currentPage = bt_page;
_mode = GUI_MENU; // Keep in menu mode _mode = GUI_MENU; // Keep in menu mode
menu_hide_headers(menu);
lv_obj_remove_flag(menu, LV_OBJ_FLAG_HIDDEN); lv_obj_remove_flag(menu, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN);
ESP_LOGI(TAG, "Bluetooth device list displayed"); ESP_LOGI(TAG, "Bluetooth device list displayed");
} }
static void refresh_bt_device_list(void) {
ESP_LOGI(TAG, "Refreshing Bluetooth device list without new discovery");
// Don't start new discovery, just refresh the display with current devices
bool should_start_discovery = false;
lv_obj_t* menu = create_menu_container();
if (!menu) {
ESP_LOGE(TAG, "Failed to create menu container for Bluetooth list");
return;
}
lv_obj_t* bt_page = create_bt_device_page(should_start_discovery);
if (!bt_page) {
ESP_LOGE(TAG, "Failed to create Bluetooth device page");
return;
}
lv_menu_set_page(menu, bt_page);
_currentPage = bt_page;
_mode = GUI_MENU; // Keep in menu mode
menu_hide_headers(menu);
lv_obj_remove_flag(menu, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN);
ESP_LOGI(TAG, "Bluetooth device list refreshed");
}
// ───── VOLUME CONTROL PAGE ───── // ───── VOLUME CONTROL PAGE ─────
static lv_obj_t* create_volume_page(void) { static lv_obj_t* create_volume_page(void) {
ESP_LOGI(TAG, "Creating volume control page"); ESP_LOGI(TAG, "Creating volume control page");
@@ -788,12 +964,20 @@ static lv_obj_t* create_volume_page(void) {
ESP_LOGE(TAG, "Failed to create volume page"); ESP_LOGE(TAG, "Failed to create volume page");
return NULL; return NULL;
} }
lv_obj_set_style_radius(_volume_page, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_radius(_volume_page, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_border_width(_volume_page, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_border_width(_volume_page, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_scrollbar_mode(_volume_page, LV_SCROLLBAR_MODE_OFF); lv_obj_set_scrollbar_mode(_volume_page, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_size(_volume_page, lv_pct(100), lv_pct(100)); lv_obj_set_size(_volume_page, lv_pct(100), lv_pct(100));
// Hide header
uint32_t child_count = lv_obj_get_child_count(_volume_page);
if (child_count >= 1) {
lv_obj_t* header = lv_obj_get_child(_volume_page, 0);
lv_obj_add_flag(header, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_height(header, 0);
}
// Create title label // Create title label
lv_obj_t* title = lv_label_create(_volume_page); lv_obj_t* title = lv_label_create(_volume_page);
lv_label_set_text(title, "Volume Control"); lv_label_set_text(title, "Volume Control");
@@ -856,6 +1040,8 @@ static void show_volume_control(void) {
lv_menu_set_page(menu, volume_page); lv_menu_set_page(menu, volume_page);
_currentPage = volume_page; _currentPage = volume_page;
_mode = GUI_MENU; // Keep in menu mode _mode = GUI_MENU; // Keep in menu mode
menu_hide_headers(menu);
lv_obj_remove_flag(menu, LV_OBJ_FLAG_HIDDEN); lv_obj_remove_flag(menu, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(_bubble, LV_OBJ_FLAG_HIDDEN);
@@ -963,11 +1149,20 @@ static void build_scrollable_menu(void) {
lv_obj_t * main = lv_menu_page_create(menu, NULL); lv_obj_t * main = lv_menu_page_create(menu, NULL);
lv_obj_set_style_radius(main, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_radius(main, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_border_width(main, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_border_width(main, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_scrollbar_mode(main, LV_SCROLLBAR_MODE_AUTO); lv_obj_set_scrollbar_mode(main, LV_SCROLLBAR_MODE_AUTO);
lv_obj_set_size(main, lv_pct(100), lv_pct(100)); lv_obj_set_size(main, lv_pct(100), lv_pct(100));
// Hide header
uint32_t child_count = lv_obj_get_child_count(main);
if (child_count >= 1) {
lv_obj_t* header = lv_obj_get_child(main, 0);
lv_obj_add_flag(header, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_height(header, 0);
}
lv_menu_set_page(menu, main); lv_menu_set_page(menu, main);
lv_obj_t * tmpObj; lv_obj_t * tmpObj;
@@ -975,10 +1170,19 @@ static void build_scrollable_menu(void) {
lv_obj_t * calMenu = lv_menu_page_create(menu, NULL); lv_obj_t * calMenu = lv_menu_page_create(menu, NULL);
lv_obj_set_style_radius(calMenu, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_radius(calMenu, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_style_border_width(calMenu, 0, LV_PART_MAIN | LV_STATE_ANY); lv_obj_set_style_border_width(calMenu, 0, LV_PART_MAIN | LV_STATE_ANY);
lv_obj_set_scrollbar_mode(calMenu, LV_SCROLLBAR_MODE_AUTO); lv_obj_set_scrollbar_mode(calMenu, LV_SCROLLBAR_MODE_AUTO);
// Hide header
child_count = lv_obj_get_child_count(calMenu);
if (child_count >= 1) {
lv_obj_t* header = lv_obj_get_child(calMenu, 0);
lv_obj_add_flag(header, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_height(header, 0);
}
tmpObj = addMenuItem(main, "Bluetooth"); tmpObj = addMenuItem(main, "Bluetooth");
lv_obj_add_state(tmpObj, LV_STATE_FOCUSED); lv_obj_add_state(tmpObj, LV_STATE_FOCUSED);
//lv_obj_add_flag(tmpObj, LV_OBJ_FLAG_USER_1); //lv_obj_add_flag(tmpObj, LV_OBJ_FLAG_USER_1);
@@ -1056,7 +1260,7 @@ static void gui_task(void *pvParameters)
system_requestVolumeUp(); system_requestVolumeUp();
} }
} else { } else {
menuNext(); menuPrevious();
} }
} }
ESP_LOGI(TAG, "MAIN: Button UP SHORT"); ESP_LOGI(TAG, "MAIN: Button UP SHORT");
@@ -1076,7 +1280,7 @@ static void gui_task(void *pvParameters)
system_requestVolumeDown(); system_requestVolumeDown();
} }
} else { } else {
menuPrevious(); menuNext();
} }
} }
ESP_LOGI(TAG, "MAIN: Button DOWN SHORT"); ESP_LOGI(TAG, "MAIN: Button DOWN SHORT");
@@ -1095,19 +1299,15 @@ static void gui_task(void *pvParameters)
else if (_mode == GUI_MENU) else if (_mode == GUI_MENU)
{ {
activate_selected(); activate_selected();
// After activation, check if we should stay in menu or exit
// Check if we're in special pages - don't auto-exit // (activation may have changed _currentPage or _mode)
if (_currentPage == _bt_page) { if (_mode == GUI_BUBBLE) {
// Don't automatically exit to bubble mode from Bluetooth page // Already handled by the menu item (e.g., "Exit" was selected)
} else if (_currentPage == _volume_page) { } else if (_currentPage == _bt_page || _currentPage == _volume_page) {
// Don't automatically exit to bubble mode from Volume page // Stay in menu mode on these pages
} else {
ESP_LOGI(TAG, "return to main");
// In main menu - activate selection and exit to bubble
activate_selected();
_mode = GUI_BUBBLE;
show_bubble(); // Cleanup menu and show bubble
} }
// Note: For main menu items that navigate to subpages,
// the btn_click_cb handles the navigation and keeps _mode = GUI_MENU
} }
UNLOCK(); UNLOCK();
ESP_LOGI(TAG, "MAIN: Button 0 LONG - Exit"); ESP_LOGI(TAG, "MAIN: Button 0 LONG - Exit");
@@ -1136,17 +1336,28 @@ static void gui_task(void *pvParameters)
} }
// Check for system events
uint32_t notifiedBits = 0;
xTaskNotifyWait(
0, // don't clear on entry
0xFFFFFFFF, // clear bits on exit
&notifiedBits,
pdMS_TO_TICKS(10));
if (notifiedBits & EM_EVENT_BT_DISCOVERY_COMPLETE) {
// Discovery completed - refresh the BT page if we're on it
if (_mode == GUI_MENU && _currentPage == _bt_page) {
ESP_LOGI(TAG, "Discovery complete, refreshing Bluetooth page");
LOCK();
// Recreate the BT page with updated device list (without starting new discovery)
refresh_bt_device_list();
UNLOCK();
}
}
if (_mode == GUI_BUBBLE) if (_mode == GUI_BUBBLE)
{ {
uint32_t notifiedBits = 0; if (notifiedBits & EM_EVENT_NEW_DATA)
// clear on entry (first param), wait for any bit, block forever
xTaskNotifyWait(
0xFFFFFFFF, // clear any old bits on entry
0xFFFFFFFF, // clear bits on exit
&notifiedBits,
pdMS_TO_TICKS(100));
if (notifiedBits & EM_EVENT_NEW_DATA)
{ {
ImuData_t d = system_getImuData(); ImuData_t d = system_getImuData();
bubble_setValue(_bubble, -d.angle); bubble_setValue(_bubble, -d.angle);