diff --git a/main/lcd/ui_image_browser.c b/main/lcd/ui_image_browser.c index f955e05..c3126e5 100644 --- a/main/lcd/ui_image_browser.c +++ b/main/lcd/ui_image_browser.c @@ -21,40 +21,29 @@ #define UI_IMAGE_PROBE_HEADER_LENGTH 16 #define UI_SLIDESHOW_PERIOD_MS 3000 -static lv_obj_t *image_tileview = NULL; +static lv_obj_t *image_view = NULL; +static lv_obj_t *image_widget = NULL; +static lv_obj_t *image_caption = NULL; +static lv_obj_t *image_error_label = NULL; static lv_timer_t *image_slideshow_timer = NULL; static size_t image_browser_image_count = 0; static size_t image_browser_current_index = 0; static const char **image_browser_names = NULL; -static size_t image_browser_last_logged_index = SIZE_MAX; static const char *TAG = "ui"; -static void ui_image_browser_log_current(void){ - if(image_browser_current_index == image_browser_last_logged_index) return; - image_browser_last_logged_index = image_browser_current_index; +static void ui_image_browser_load_current(void); - const char *filename = "?"; - if(image_browser_names != NULL && image_browser_current_index < image_browser_image_count){ - filename = image_browser_names[image_browser_current_index]; - } - ui_log_appendf("Show %u/%u: %s", - (unsigned int)(image_browser_current_index + 1), - (unsigned int)image_browser_image_count, - filename); -} - -static void ui_image_browser_set_index(size_t image_index, lv_anim_enable_t anim_en){ - if(image_tileview == NULL || image_browser_image_count == 0) return; +static void ui_image_browser_show_index(size_t image_index){ + if(image_widget == NULL || image_browser_image_count == 0) return; image_browser_current_index = image_index % image_browser_image_count; - lv_tileview_set_tile_by_index(image_tileview, 0, (uint32_t)image_browser_current_index, anim_en); - ui_image_browser_log_current(); + ui_image_browser_load_current(); } static void ui_image_slideshow_timer_cb(lv_timer_t *timer){ (void)timer; if(ui_current_view() != UI_VIEW_IMAGE_BROWSER) return; - ui_image_browser_set_index(image_browser_current_index + 1, LV_ANIM_ON); + ui_image_browser_show_index(image_browser_current_index + 1); } static void ui_start_image_slideshow(void){ @@ -62,15 +51,22 @@ static void ui_start_image_slideshow(void){ image_slideshow_timer = lv_timer_create(ui_image_slideshow_timer_cb, UI_SLIDESHOW_PERIOD_MS, NULL); } -static void ui_image_browser_value_changed_event_cb(lv_event_t *event){ - if(lv_event_get_code(event) != LV_EVENT_VALUE_CHANGED) return; +static void ui_image_browser_gesture_event_cb(lv_event_t *event){ + if(lv_event_get_code(event) != LV_EVENT_GESTURE) return; + if(image_browser_image_count <= 1) return; - lv_obj_t *tileview = lv_event_get_current_target(event); - lv_obj_t *active_tile = lv_tileview_get_tile_active(tileview); - if(active_tile == NULL) return; + lv_indev_t *indev = lv_indev_active(); + if(indev == NULL) return; - image_browser_current_index = (size_t)(uintptr_t)lv_obj_get_user_data(active_tile); - ui_image_browser_log_current(); + const lv_dir_t gesture_dir = lv_indev_get_gesture_dir(indev); + if(gesture_dir == LV_DIR_TOP){ + lv_indev_wait_release(indev); + ui_image_browser_show_index(image_browser_current_index + 1); + } + else if(gesture_dir == LV_DIR_BOTTOM){ + lv_indev_wait_release(indev); + ui_image_browser_show_index(image_browser_current_index + image_browser_image_count - 1); + } } static bool ui_char_equals_ignore_case(char left_char, char right_char){ @@ -203,92 +199,55 @@ static void ui_create_empty_image_browser(lv_obj_t *screen){ lv_obj_center(label); } -static lv_dir_t ui_get_tile_directions(size_t image_index, size_t image_count){ - if(image_count <= 1) return LV_DIR_NONE; - if(image_index == 0) return LV_DIR_BOTTOM; - if(image_index + 1 == image_count) return LV_DIR_TOP; - return (lv_dir_t)(LV_DIR_TOP | LV_DIR_BOTTOM); -} +static void ui_collect_image_names(size_t image_count){ + if(image_browser_names == NULL) return; -static void ui_create_image_tile(lv_obj_t *tile, const char *filename, size_t image_index, size_t image_count){ - ui_attach_toggle(tile); - lv_obj_set_style_bg_color(tile, lv_color_hex(0x070a0d), 0); - lv_obj_set_style_pad_all(tile, 0, 0); - lv_obj_remove_flag(tile, LV_OBJ_FLAG_SCROLLABLE); - - lv_obj_t *image_frame = lv_obj_create(tile); - ui_attach_toggle(image_frame); - lv_obj_set_size(image_frame, LV_PCT(100), LV_PCT(100)); - lv_obj_set_style_bg_color(image_frame, lv_color_black(), 0); - lv_obj_set_style_border_width(image_frame, 0, 0); - lv_obj_set_style_radius(image_frame, 0, 0); - lv_obj_set_style_pad_all(image_frame, 0, 0); - lv_obj_remove_flag(image_frame, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_center(image_frame); - - lv_obj_t *image = lv_image_create(image_frame); - ui_attach_toggle(image); - lv_obj_set_size(image, LV_PCT(100), LV_PCT(100)); - lv_image_set_inner_align(image, LV_IMAGE_ALIGN_CONTAIN); - - char image_source[UI_IMAGE_PATH_MAX_LENGTH]; - snprintf(image_source, sizeof(image_source), "%s%s", UI_IMAGE_PATH_PREFIX, filename); - ui_lowercase_path_extension(image_source); - lv_image_set_src(image, image_source); - lv_obj_center(image); - - lv_obj_t *info_bar = lv_obj_create(tile); - ui_attach_toggle(info_bar); - lv_obj_set_size(info_bar, LV_PCT(100), UI_IMAGE_INFO_BAR_HEIGHT); - lv_obj_align(info_bar, LV_ALIGN_BOTTOM_MID, 0, -UI_IMAGE_INFO_BAR_BOTTOM_OFFSET); - lv_obj_set_style_bg_color(info_bar, lv_color_hex(0x101418), 0); - lv_obj_set_style_bg_opa(info_bar, LV_OPA_80, 0); - lv_obj_set_style_border_width(info_bar, 0, 0); - lv_obj_set_style_radius(info_bar, 0, 0); - lv_obj_set_style_pad_hor(info_bar, 14, 0); - lv_obj_set_style_pad_ver(info_bar, 0, 0); - lv_obj_remove_flag(info_bar, LV_OBJ_FLAG_SCROLLABLE); - - lv_obj_t *caption = lv_label_create(info_bar); - ui_attach_toggle(caption); - lv_label_set_long_mode(caption, LV_LABEL_LONG_DOT); - lv_obj_set_width(caption, LV_PCT(100)); - lv_obj_set_style_text_color(caption, lv_color_hex(0xdde7ef), 0); - lv_label_set_text_fmt(caption, "%u/%u %s", (unsigned int)(image_index + 1), (unsigned int)image_count, filename); - lv_obj_center(caption); - - if(lv_image_get_src_width(image) <= 0 || lv_image_get_src_height(image) <= 0){ - const char *load_reason = ui_probe_image_load_failure(filename, image_source); - ESP_LOGW(TAG, "Image cannot be loaded by LVGL: %s reason=%s", image_source, load_reason); - ui_log_appendf("Image load failed: %s (%s)", filename, load_reason); - lv_obj_t *error_label = lv_label_create(tile); - ui_attach_toggle(error_label); - lv_label_set_text_fmt(error_label, "Image cannot be loaded\n%s\n%s", filename, load_reason); - lv_obj_set_style_text_color(error_label, lv_color_hex(0xffd166), 0); - lv_obj_set_style_text_align(error_label, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_width(error_label, LV_PCT(86)); - lv_obj_align(error_label, LV_ALIGN_CENTER, 0, -18); - } - else{ - ui_log_appendf("Loaded %u/%u: %s (%dx%d)", - (unsigned int)(image_index + 1), (unsigned int)image_count, filename, - (int)lv_image_get_src_width(image), (int)lv_image_get_src_height(image)); - } -} - -static void ui_create_image_browser_tiles(size_t image_count){ const size_t file_count = sd_get_file_count(); size_t image_index = 0; for(size_t file_index = 0; file_index < file_count && image_index < image_count; file_index++){ const char *filename = sd_get_next_file(); if(!ui_is_image_file(filename)) continue; + image_browser_names[image_index++] = filename; + } +} - lv_obj_t *tile = lv_tileview_add_tile(image_tileview, 0, (uint32_t)image_index, ui_get_tile_directions(image_index, image_count)); - lv_obj_set_user_data(tile, (void *)(uintptr_t)image_index); - if(image_browser_names != NULL) image_browser_names[image_index] = filename; - ui_create_image_tile(tile, filename, image_index, image_count); - image_index++; +static void ui_image_browser_load_current(void){ + if(image_widget == NULL || image_browser_names == NULL || image_browser_image_count == 0) return; + + const char *filename = image_browser_names[image_browser_current_index]; + + char image_source[UI_IMAGE_PATH_MAX_LENGTH]; + snprintf(image_source, sizeof(image_source), "%s%s", UI_IMAGE_PATH_PREFIX, filename); + ui_lowercase_path_extension(image_source); + lv_image_set_src(image_widget, image_source); + + if(image_caption != NULL){ + lv_label_set_text_fmt(image_caption, "%u/%u %s", + (unsigned int)(image_browser_current_index + 1), + (unsigned int)image_browser_image_count, filename); + } + + if(lv_image_get_src_width(image_widget) <= 0 || lv_image_get_src_height(image_widget) <= 0){ + const char *load_reason = ui_probe_image_load_failure(filename, image_source); + ESP_LOGW(TAG, "Image cannot be loaded by LVGL: %s reason=%s", image_source, load_reason); + ui_log_appendf("Image load failed: %s (%s)", filename, load_reason); + + lv_obj_add_flag(image_widget, LV_OBJ_FLAG_HIDDEN); + if(image_error_label != NULL){ + lv_label_set_text_fmt(image_error_label, "Image cannot be loaded\n%s\n%s", filename, load_reason); + lv_obj_remove_flag(image_error_label, LV_OBJ_FLAG_HIDDEN); + } + } + else{ + ui_log_appendf("Loaded %u/%u: %s (%dx%d)", + (unsigned int)(image_browser_current_index + 1), + (unsigned int)image_browser_image_count, filename, + (int)lv_image_get_src_width(image_widget), + (int)lv_image_get_src_height(image_widget)); + + lv_obj_remove_flag(image_widget, LV_OBJ_FLAG_HIDDEN); + if(image_error_label != NULL) lv_obj_add_flag(image_error_label, LV_OBJ_FLAG_HIDDEN); } } @@ -304,18 +263,59 @@ void ui_image_browser_build_view(lv_obj_t *screen){ return; } - image_tileview = lv_tileview_create(screen); - lv_obj_set_size(image_tileview, LV_PCT(100), LV_PCT(100)); - lv_obj_set_style_bg_color(image_tileview, lv_color_hex(0x070a0d), 0); - lv_obj_set_style_border_width(image_tileview, 0, 0); - lv_obj_set_style_pad_all(image_tileview, 0, 0); - lv_obj_set_scrollbar_mode(image_tileview, LV_SCROLLBAR_MODE_OFF); - lv_obj_add_event_cb(image_tileview, ui_image_browser_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL); image_browser_image_count = image_count; + image_browser_current_index = 0; image_browser_names = calloc(image_count, sizeof(const char *)); + ui_collect_image_names(image_count); - ui_create_image_browser_tiles(image_count); - ui_image_browser_set_index(0, LV_ANIM_OFF); + image_view = lv_obj_create(screen); + ui_attach_toggle(image_view); + lv_obj_set_size(image_view, LV_PCT(100), LV_PCT(100)); + lv_obj_center(image_view); + lv_obj_set_style_bg_color(image_view, lv_color_hex(0x070a0d), 0); + lv_obj_set_style_bg_opa(image_view, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(image_view, 0, 0); + lv_obj_set_style_radius(image_view, 0, 0); + lv_obj_set_style_pad_all(image_view, 0, 0); + lv_obj_remove_flag(image_view, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_remove_flag(image_view, LV_OBJ_FLAG_GESTURE_BUBBLE); + lv_obj_add_event_cb(image_view, ui_image_browser_gesture_event_cb, LV_EVENT_GESTURE, NULL); + + image_widget = lv_image_create(image_view); + ui_attach_toggle(image_widget); + lv_obj_set_size(image_widget, LV_PCT(100), LV_PCT(100)); + lv_image_set_inner_align(image_widget, LV_IMAGE_ALIGN_CONTAIN); + lv_obj_center(image_widget); + + lv_obj_t *info_bar = lv_obj_create(image_view); + ui_attach_toggle(info_bar); + lv_obj_set_size(info_bar, LV_PCT(100), UI_IMAGE_INFO_BAR_HEIGHT); + lv_obj_align(info_bar, LV_ALIGN_BOTTOM_MID, 0, -UI_IMAGE_INFO_BAR_BOTTOM_OFFSET); + lv_obj_set_style_bg_color(info_bar, lv_color_hex(0x101418), 0); + lv_obj_set_style_bg_opa(info_bar, LV_OPA_80, 0); + lv_obj_set_style_border_width(info_bar, 0, 0); + lv_obj_set_style_radius(info_bar, 0, 0); + lv_obj_set_style_pad_hor(info_bar, 14, 0); + lv_obj_set_style_pad_ver(info_bar, 0, 0); + lv_obj_remove_flag(info_bar, LV_OBJ_FLAG_SCROLLABLE); + + image_caption = lv_label_create(info_bar); + ui_attach_toggle(image_caption); + lv_label_set_long_mode(image_caption, LV_LABEL_LONG_DOT); + lv_obj_set_width(image_caption, LV_PCT(100)); + lv_obj_set_style_text_color(image_caption, lv_color_hex(0xdde7ef), 0); + lv_obj_center(image_caption); + + image_error_label = lv_label_create(image_view); + ui_attach_toggle(image_error_label); + lv_label_set_text(image_error_label, ""); + lv_obj_set_style_text_color(image_error_label, lv_color_hex(0xffd166), 0); + lv_obj_set_style_text_align(image_error_label, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_width(image_error_label, LV_PCT(86)); + lv_obj_align(image_error_label, LV_ALIGN_CENTER, 0, -18); + lv_obj_add_flag(image_error_label, LV_OBJ_FLAG_HIDDEN); + + ui_image_browser_show_index(0); ui_start_image_slideshow(); } @@ -327,10 +327,12 @@ void ui_image_browser_reset(void){ free((void *)image_browser_names); image_browser_names = NULL; - image_tileview = NULL; + image_view = NULL; + image_widget = NULL; + image_caption = NULL; + image_error_label = NULL; image_browser_image_count = 0; image_browser_current_index = 0; - image_browser_last_logged_index = SIZE_MAX; } size_t ui_image_browser_get_image_count(void){