diff --git a/platformio.ini b/platformio.ini index 3abc8ed..104336e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -32,7 +32,7 @@ build_flags = -DLV_USE_FS_STDIO=0 -DLV_USE_FS_POSIX=0 -DLV_USE_IME_PINYIN=1 - -DLVGL_DRAW_BUF_DIV=4 + -DLVGL_DRAW_BUF_DIV=2 -DLVGL_DOUBLE_BUF=1 -DLV_MEM_POOL_SIZE_KB=384 -DCORE_DEBUG_LEVEL=0 @@ -42,7 +42,7 @@ build_flags = -DARDUINO_LOOP_STACK_SIZE=16384 -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue - -Os + -O2 lib_deps = bodmer/TFT_eSPI@^2.5.43 https://github.com/PaulStoffregen/XPT2046_Touchscreen.git#v1.4 diff --git a/src/Setup_ESP32_2432S028R_ILI9341.h b/src/Setup_ESP32_2432S028R_ILI9341.h index de492cd..bd3b666 100644 --- a/src/Setup_ESP32_2432S028R_ILI9341.h +++ b/src/Setup_ESP32_2432S028R_ILI9341.h @@ -365,7 +365,7 @@ // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 // #define SPI_FREQUENCY 27000000 -#define SPI_FREQUENCY 55000000 +#define SPI_FREQUENCY 80000000 // #define SPI_FREQUENCY 55000000 // #define SPI_FREQUENCY 80000000 @@ -380,8 +380,8 @@ // then uncomment the following line: #define USE_HSPI_PORT -// Enable TFT_eSPI DMA path on classic ESP32 to improve flush throughput. -#define ESP32_DMA +// DMA disabled: causes screen corruption on this hardware. +// #define ESP32_DMA // Comment out the following #define if "SPI Transactions" do not need to be // supported. When commented out the code size will be smaller and sketches will diff --git a/src/app.h b/src/app.h index fbd2a35..ef1389e 100644 --- a/src/app.h +++ b/src/app.h @@ -283,10 +283,12 @@ class AppManager { if (bytes_read == 0 && read_chunk_file_size > 0) return false; read_chunk_bytes = bytes_read; + bool has_prev = read_chunk_index > 0; + bool has_next = ((uint64_t)offset + (uint64_t)read_chunk_bytes) < read_chunk_file_size; + editor.setTitle(current_filename); editor.setReadChunkMode(true); - editor.setReadChunkNavigationState(read_chunk_index > 0, ((uint64_t)offset + (uint64_t)read_chunk_bytes) < read_chunk_file_size); - editor.setReadChunkText(content, anchor_end); + editor.setReadChunkText(content, has_prev, has_next, anchor_end); return true; } @@ -512,7 +514,7 @@ class AppManager { if (dot < 0) return false; String ext = path.substring(dot + 1); ext.toLowerCase(); - return (ext == "jpg" || ext == "jpeg"); + return (ext == "jpg" || ext == "jpeg" || ext == "png" || ext == "bmp" || ext == "gif"); } void showPrevImage() { diff --git a/src/lv_conf.h b/src/lv_conf.h index 53ba09a..5ba0da3 100644 --- a/src/lv_conf.h +++ b/src/lv_conf.h @@ -144,7 +144,7 @@ * and can't be drawn in chunks. */ /*The target buffer size for simple layer chunks.*/ -#define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (12 * 1024) /*[bytes]*/ +#define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (24 * 1024) /*[bytes]*/ /* The stack size of the drawing thread. * NOTE: If FreeType or ThorVG is enabled, it is recommended to set it to 32KB or more. @@ -190,13 +190,13 @@ /*Allow buffering some shadow calculation. *LV_DRAW_SW_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` *Caching has LV_DRAW_SW_SHADOW_CACHE_SIZE^2 RAM cost*/ - #define LV_DRAW_SW_SHADOW_CACHE_SIZE 0 + #define LV_DRAW_SW_SHADOW_CACHE_SIZE 128 /* Set number of maximally cached circle data. * The circumference of 1/4 circle are saved for anti-aliasing * radius * 4 bytes are used per circle (the most often used radiuses are saved) * 0: to disable caching */ - #define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4 + #define LV_DRAW_SW_CIRCLE_CACHE_SIZE 32 #endif #define LV_USE_DRAW_SW_ASM LV_DRAW_SW_ASM_NONE @@ -381,11 +381,11 @@ *Used by image decoders such as `lv_lodepng` to keep the decoded image in the memory. *If size is not set to 0, the decoder will fail to decode when the cache is full. *If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/ -#define LV_CACHE_DEF_SIZE 0 +#define LV_CACHE_DEF_SIZE (64 * 1024) /*Default number of image header cache entries. The cache is used to store the headers of images *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ -#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0 +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 8 /*Number of stops allowed per gradient. Increase this to allow more stops. *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ @@ -396,7 +396,7 @@ #define LV_COLOR_MIX_ROUND_OFS 0 /* Add 2 x 32 bit variables to each lv_obj_t to speed up getting style properties */ -#define LV_OBJ_STYLE_CACHE 0 +#define LV_OBJ_STYLE_CACHE 1 /* Add `id` field to `lv_obj_t` */ #define LV_USE_OBJ_ID 0 @@ -703,8 +703,8 @@ /*0: Light mode; 1: Dark mode*/ #define LV_THEME_DEFAULT_DARK 0 - /*1: Enable grow on press*/ - #define LV_THEME_DEFAULT_GROW 1 + /*0: Disable grow on press to eliminate extra redraw area during press animations*/ + #define LV_THEME_DEFAULT_GROW 0 /*Default transition time in [ms]*/ #define LV_THEME_DEFAULT_TRANSITION_TIME 80 @@ -791,13 +791,13 @@ #endif /*LODEPNG decoder library*/ -#define LV_USE_LODEPNG 0 +#define LV_USE_LODEPNG 1 /*PNG decoder(libpng) library*/ #define LV_USE_LIBPNG 0 /*BMP decoder library*/ -#define LV_USE_BMP 0 +#define LV_USE_BMP 1 /* JPG + split JPG decoder library. * Split JPG is a custom format optimized for embedded systems. */ @@ -808,7 +808,7 @@ #define LV_USE_LIBJPEG_TURBO 0 /*GIF decoder library*/ -#define LV_USE_GIF 0 +#define LV_USE_GIF 1 #if LV_USE_GIF /*GIF decoder accelerate*/ #define LV_GIF_CACHE_DECODE_DATA 0 diff --git a/src/ui/editor.h b/src/ui/editor.h index 34b0faa..26448ca 100644 --- a/src/ui/editor.h +++ b/src/ui/editor.h @@ -20,6 +20,7 @@ class Editor { static constexpr uint8_t IME_CAND_PER_PAGE = 8; static constexpr uint16_t IME_CAND_MAX = 160; static constexpr size_t LARGE_DOC_PERF_THRESHOLD = 2 * 1024; + static constexpr int32_t TEXTAREA_SCROLL_ANIM_MS = 260; static constexpr int32_t CHUNK_NAV_BTN_SIZE = 24; static constexpr lv_opa_t CHUNK_NAV_BTN_BG_OPA = static_cast((LV_OPA_COVER * 75) / 100); lv_obj_t* screen; @@ -199,7 +200,7 @@ class Editor { lv_obj_set_style_width(textarea, 1, LV_PART_CURSOR); lv_obj_add_flag(textarea, LV_OBJ_FLAG_SCROLL_ELASTIC); lv_obj_add_flag(textarea, LV_OBJ_FLAG_SCROLL_MOMENTUM); - lv_obj_set_style_anim_duration(textarea, 260, 0); + lv_obj_set_style_anim_duration(textarea, TEXTAREA_SCROLL_ANIM_MS, 0); int32_t editor_cursor_h = EDITOR_TEXT_SIZE_PX; if (editor_cursor_h < 2) editor_cursor_h = 2; lv_obj_set_style_height(textarea, editor_cursor_h, LV_PART_CURSOR); @@ -365,12 +366,50 @@ class Editor { refreshReadChunkButtons(); } - void setReadChunkText(const String& content, bool anchor_end) { + void setReadChunkText(const String& content, bool has_prev, bool has_next, bool anchor_end) { if (!textarea) return; + + // Suppress scroll animations during content swap so that the + // internal auto-scroll triggered by lv_textarea_set_text() does + // not conflict with the explicit scroll below. This is the root + // cause of the visible "scroll-up jump" on every chunk switch. + lv_obj_set_style_anim_duration(textarea, 0, 0); + applyLargeDocPerfMode(content.length()); + + // Batch-update navigation state before the final button refresh. + read_chunk_has_prev = has_prev; + read_chunk_has_next = has_next; + + // Replace content. LVGL internally moves the cursor to the end + // of the new text and auto-scrolls, but with anim_duration == 0 + // the scroll is instantaneous and invisible. lv_textarea_set_text(textarea, content.c_str()); - if (anchor_end) moveCursorAndViewToEnd(); - else moveCursorAndViewToStart(); + + // Set cursor and record anchor for IME. + if (anchor_end) { + uint32_t len = (uint32_t)content.length(); + lv_textarea_set_cursor_pos(textarea, (int32_t)len); + ime_cursor_anchor_pos = len; + } else { + lv_textarea_set_cursor_pos(textarea, 0); + ime_cursor_anchor_pos = 0; + } + ime_cursor_anchor_valid = true; + + // Force layout so scroll metrics are accurate, then set the + // definitive scroll position in one shot. + lv_obj_update_layout(textarea); + if (anchor_end) { + int32_t bottom = lv_obj_get_scroll_bottom(textarea); + if (bottom > 0) lv_obj_scroll_by(textarea, 0, bottom, LV_ANIM_OFF); + } else { + lv_obj_scroll_to_y(textarea, 0, LV_ANIM_OFF); + } + + // Restore normal scroll animation for user interaction. + lv_obj_set_style_anim_duration(textarea, TEXTAREA_SCROLL_ANIM_MS, 0); + clearReadChunkFocus(); refreshReadChunkButtons(); }