Touch UI long filenames fixes (#19262)

* Improvements to FTDI DLCache functionality.
* Better handling of long file names in Touch UI
- Long file names now truncated and shown with ellipsis.
- Increased display cache buffer to allow for longer filenames.
- Visual error message when display cache is exceeded.
This commit is contained in:
Marcio T 2020-09-06 16:37:16 -06:00 committed by GitHub
parent 86b71b83fa
commit 2b789ddab9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 187 additions and 52 deletions

View File

@ -133,6 +133,7 @@ class CLCD {
static void set_brightness (uint8_t brightness);
static uint8_t get_brightness();
static void host_cmd (unsigned char host_command, unsigned char byte2);
static uint32_t dl_size() {return CLCD::mem_read_32(REG::CMD_DL) & 0x1FFF;}
static void get_font_metrics (uint8_t font, struct FontMetrics &fm);
static uint16_t get_text_width(const uint8_t font, const char *str);

View File

@ -32,7 +32,8 @@
*
* The cache memory begins with a table at
* DL_CACHE_START: each table entry contains
* an address and size for a cached DL slot.
* an address, size and used bytes for a cached
* DL slot.
*
* Immediately following the table is the
* DL_FREE_ADDR, which points to free cache
@ -44,11 +45,14 @@
*
* DL_CACHE_START slot0_addr 4
* slot0_size 4
* slot0_used 4
* slot1_addr 4
* slot1_size 4
* slot1_used 4
* ...
* slotN_addr 4
* slotN_size 4
* slotN_used 4
* DL_FREE_ADDR dl_free_ptr 4
* cached data
* ...
@ -57,7 +61,7 @@
*/
#define DL_CACHE_START MAP::RAM_G_SIZE - 0xFFFF
#define DL_FREE_ADDR DL_CACHE_START + DL_CACHE_SLOTS * 8
#define DL_FREE_ADDR DL_CACHE_START + DL_CACHE_SLOTS * 12
using namespace FTDI;
@ -66,12 +70,12 @@ using namespace FTDI;
void DLCache::init() {
CLCD::mem_write_32(DL_FREE_ADDR, DL_FREE_ADDR + 4);
for(uint8_t slot = 0; slot < DL_CACHE_SLOTS; slot++) {
save_slot(slot, 0, 0);
save_slot(slot, 0, 0, 0);
}
}
bool DLCache::has_data() {
return dl_size != 0;
return dl_slot_size != 0;
}
bool DLCache::wait_until_idle() {
@ -93,12 +97,12 @@ bool DLCache::wait_until_idle() {
* that it can be appended later. The memory is
* dynamically allocated following DL_FREE_ADDR.
*
* If num_bytes is provided, then that many bytes
* If min_bytes is provided, then that many bytes
* will be reserved so that the cache may be re-written
* later with potentially a bigger DL.
*/
bool DLCache::store(uint32_t num_bytes /* = 0*/) {
bool DLCache::store(uint32_t min_bytes /* = 0*/) {
CLCD::CommandFifo cmd;
// Execute any commands already in the FIFO
@ -107,67 +111,67 @@ bool DLCache::store(uint32_t num_bytes /* = 0*/) {
return false;
// Figure out how long the display list is
uint32_t new_dl_size = CLCD::mem_read_32(REG::CMD_DL) & 0x1FFF;
uint32_t free_space = 0;
uint32_t dl_alloc = 0;
const uint32_t dl_size = CLCD::dl_size();
if (dl_addr == 0) {
if (dl_slot_addr == 0) {
// If we are allocating new space...
dl_addr = CLCD::mem_read_32(DL_FREE_ADDR);
free_space = MAP::RAM_G_SIZE - dl_addr;
dl_alloc = num_bytes ?: new_dl_size;
dl_size = new_dl_size;
dl_slot_addr = CLCD::mem_read_32(DL_FREE_ADDR);
dl_slot_size = max(dl_size, min_bytes);
const uint32_t free_space = MAP::RAM_G_SIZE - dl_slot_addr;
if(dl_slot_size <= free_space) {
CLCD::mem_write_32(DL_FREE_ADDR, dl_slot_addr + dl_slot_size);
} else {
// Otherwise, we can only store as much space
// as was previously allocated.
free_space = num_bytes ?: dl_size;
dl_alloc = 0;
dl_size = new_dl_size;
dl_slot_addr = 0;
dl_slot_size = 0;
dl_slot_used = 0;
}
}
if (dl_size > free_space) {
if (dl_size > dl_slot_size) {
// Not enough memory to cache the display list.
#if ENABLED(TOUCH_UI_DEBUG)
SERIAL_ECHO_START();
SERIAL_ECHOPAIR ("Not enough space in GRAM to cache display list, free space: ", free_space);
SERIAL_ECHOPAIR ("Not enough space in GRAM to cache display list, free space: ", dl_slot_size);
SERIAL_ECHOLNPAIR(" Required: ", dl_size);
#endif
dl_slot_used = 0;
save_slot();
return false;
} else {
#if ENABLED(TOUCH_UI_DEBUG)
SERIAL_ECHO_START();
SERIAL_ECHOPAIR ("Saving DL to RAMG cache, bytes: ", dl_size);
SERIAL_ECHOLNPAIR(" Free space: ", free_space);
SERIAL_ECHOPAIR ("Saving DL to RAMG cache, bytes: ", dl_slot_used);
SERIAL_ECHOLNPAIR(" Free space: ", dl_slot_size);
#endif
cmd.memcpy(dl_addr, MAP::RAM_DL, dl_size);
dl_slot_used = dl_size;
save_slot();
cmd.memcpy(dl_slot_addr, MAP::RAM_DL, dl_slot_used);
cmd.execute();
save_slot(dl_slot, dl_addr, dl_size);
if (dl_alloc > 0) {
// If we allocated space dynamically, then adjust dl_free_addr.
CLCD::mem_write_32(DL_FREE_ADDR, dl_addr + dl_alloc);
}
return true;
}
}
void DLCache::save_slot(uint8_t dl_slot, uint32_t dl_addr, uint32_t dl_size) {
CLCD::mem_write_32(DL_CACHE_START + dl_slot * 8 + 0, dl_addr);
CLCD::mem_write_32(DL_CACHE_START + dl_slot * 8 + 4, dl_size);
void DLCache::save_slot(uint8_t indx, uint32_t addr, uint16_t size, uint16_t used) {
CLCD::mem_write_32(DL_CACHE_START + indx * 12 + 0, addr);
CLCD::mem_write_32(DL_CACHE_START + indx * 12 + 4, size);
CLCD::mem_write_32(DL_CACHE_START + indx * 12 + 8, used);
}
void DLCache::load_slot() {
dl_addr = CLCD::mem_read_32(DL_CACHE_START + dl_slot * 8 + 0);
dl_size = CLCD::mem_read_32(DL_CACHE_START + dl_slot * 8 + 4);
void DLCache::load_slot(uint8_t indx, uint32_t &addr, uint16_t &size, uint16_t &used) {
addr = CLCD::mem_read_32(DL_CACHE_START + indx * 12 + 0);
size = CLCD::mem_read_32(DL_CACHE_START + indx * 12 + 4);
used = CLCD::mem_read_32(DL_CACHE_START + indx * 12 + 8);
}
void DLCache::append() {
CLCD::CommandFifo cmd;
cmd.append(dl_addr, dl_size);
cmd.append(dl_slot_addr, dl_slot_used);
#if ENABLED(TOUCH_UI_DEBUG)
cmd.execute();
wait_until_idle();
SERIAL_ECHO_START();
SERIAL_ECHOPAIR ("Appending to DL from RAMG cache, bytes: ", dl_size);
SERIAL_ECHOPAIR ("Appending to DL from RAMG cache, bytes: ", dl_slot_used);
SERIAL_ECHOLNPAIR(" REG_CMD_DL: ", CLCD::mem_read_32(REG::CMD_DL));
#endif
}

View File

@ -44,12 +44,16 @@ class DLCache {
typedef FTDI::ftdi_registers REG;
typedef FTDI::ftdi_memory_map MAP;
uint8_t dl_slot;
uint32_t dl_addr;
uint16_t dl_size;
uint8_t dl_slot_indx;
uint32_t dl_slot_addr;
uint16_t dl_slot_size;
uint16_t dl_slot_used;
void load_slot();
static void save_slot(uint8_t dl_slot, uint32_t dl_addr, uint32_t dl_size);
void load_slot() {load_slot(dl_slot_indx, dl_slot_addr, dl_slot_size, dl_slot_used);}
void save_slot() {save_slot(dl_slot_indx, dl_slot_addr, dl_slot_size, dl_slot_used);}
static void load_slot(uint8_t indx, uint32_t &addr, uint16_t &size, uint16_t &used);
static void save_slot(uint8_t indx, uint32_t addr, uint16_t size, uint16_t used);
bool wait_until_idle();
@ -57,12 +61,12 @@ class DLCache {
static void init();
DLCache(uint8_t slot) {
dl_slot = slot;
dl_slot_indx = slot;
load_slot();
}
bool has_data();
bool store(uint32_t num_bytes = 0);
bool store(uint32_t min_bytes = 0);
void append();
};

View File

@ -47,4 +47,5 @@
#include "sound_list.h"
#include "polygon.h"
#include "text_box.h"
#include "text_ellipsis.h"
#endif

View File

@ -173,10 +173,21 @@ class UncachedScreen {
template<uint8_t DL_SLOT,uint32_t DL_SIZE = 0>
class CachedScreen {
protected:
static void gfxError() {
using namespace FTDI;
CommandProcessor cmd;
cmd.cmd(CMD_DLSTART)
.cmd(CLEAR(true,true,true))
.font(30)
.text(0, 0, display_width, display_height, F("GFX MEM FULL"));
}
static bool storeBackground() {
DLCache dlcache(DL_SLOT);
if (!dlcache.store(DL_SIZE)) {
SERIAL_ECHO_MSG("CachedScreen::storeBackground() failed: not enough DL cache space");
gfxError(); // Try to cache a shorter error message instead.
dlcache.store(DL_SIZE);
return false;
}
return true;

View File

@ -0,0 +1,80 @@
/*********************
* text_ellipsis.cpp *
*********************/
/****************************************************************************
* Written By Marcio Teixeira 2019 - Aleph Objects, Inc. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <https://www.gnu.org/licenses/>. *
****************************************************************************/
#include "ftdi_extended.h"
#ifdef FTDI_EXTENDED
namespace FTDI {
/**
* Helper function for drawing text with ellipses. The str buffer may be modified and should have space for up to two extra characters.
*/
static void _draw_text_with_ellipsis(CommandProcessor& cmd, int16_t x, int16_t y, int16_t w, int16_t h, char *str, uint16_t options, uint8_t font) {
FontMetrics fm(font);
const uint16_t ellipsisWidth = fm.get_char_width('.') * 3;
// Compute the total line length, as well as
// the location in the string where it can
// split and still allow the ellipsis to fit.
uint16_t lineWidth = 0;
char *breakPoint = str;
for(char* c = str; *c; c++) {
lineWidth += fm.get_char_width(*c);
if(lineWidth + ellipsisWidth < w)
breakPoint = c;
}
if(lineWidth > w) {
*breakPoint = '\0';
strcpy_P(breakPoint,PSTR("..."));
}
cmd.apply_text_alignment(x, y, w, h, options);
#ifdef TOUCH_UI_USE_UTF8
if (has_utf8_chars(str)) {
draw_utf8_text(cmd, x, y, str, font_size_t::from_romfont(font), options);
} else
#endif
{
cmd.CLCD::CommandFifo::text(x, y, font, options);
cmd.CLCD::CommandFifo::str(str);
}
}
/**
* These functions draws text inside a bounding box, truncating the text and
* adding ellipsis if the text does not fit.
*/
void draw_text_with_ellipsis(CommandProcessor& cmd, int x, int y, int w, int h, const char *str, uint16_t options, uint8_t font) {
char tmp[strlen(str) + 3];
strcpy(tmp, str);
_draw_text_with_ellipsis(cmd, x, y, w, h, tmp, options, font);
}
void draw_text_with_ellipsis(CommandProcessor& cmd, int x, int y, int w, int h, progmem_str pstr, uint16_t options, uint8_t font) {
char tmp[strlen_P((const char*)pstr) + 3];
strcpy_P(tmp, (const char*)pstr);
_draw_text_with_ellipsis(cmd, x, y, w, h, tmp, options, font);
}
} // namespace FTDI
#endif // FTDI_EXTENDED

View File

@ -0,0 +1,31 @@
/*******************
* text_ellipsis.h *
*******************/
/****************************************************************************
* Written By Marcio Teixeira 2020 - SynDaver Labs, Inc. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <https://www.gnu.org/licenses/>. *
****************************************************************************/
#pragma once
/**
* This function draws text inside a bounding box, truncating the text and
* showing ellipsis if it does not fit.
*/
namespace FTDI {
void draw_text_with_ellipsis(class CommandProcessor& cmd, int x, int y, int w, int h, progmem_str str, uint16_t options = 0, uint8_t font = 31);
void draw_text_with_ellipsis(class CommandProcessor& cmd, int x, int y, int w, int h, const char *str, uint16_t options = 0, uint8_t font = 31);
}

View File

@ -83,15 +83,19 @@ void FilesScreen::drawFileButton(const char* filename, uint8_t tag, bool is_dir,
cmd.font(font_medium)
.rectangle( 0, BTN_Y(header_h+line), display_width, BTN_H(1));
cmd.cmd(COLOR_RGB(is_highlighted ? normal_btn.rgb : bg_text_enabled));
constexpr uint16_t dim[2] = {BTN_SIZE(6,1)};
#define POS_AND_SHORTEN(SHORTEN) BTN_POS(1,header_h+line), dim[0] - (SHORTEN), dim[1]
#define POS_AND_SIZE POS_AND_SHORTEN(0)
#if ENABLED(SCROLL_LONG_FILENAMES)
if (is_highlighted) {
cmd.cmd(SAVE_CONTEXT());
cmd.cmd(MACRO(0));
}
cmd.text(POS_AND_SIZE, filename, OPT_CENTERY | OPT_NOFIT);
} else
#endif
cmd.text (BTN_POS(1,header_h+line), BTN_SIZE(6,1), filename, OPT_CENTERY | TERN0(SCROLL_LONG_FILENAMES, OPT_NOFIT));
if (is_dir) {
cmd.text(BTN_POS(1,header_h+line), BTN_SIZE(6,1), F("> "), OPT_CENTERY | OPT_RIGHTX);
draw_text_with_ellipsis(cmd, POS_AND_SHORTEN(is_dir ? 20 : 0), filename, OPT_CENTERY, font_medium);
if (is_dir && !is_highlighted) {
cmd.text(POS_AND_SIZE, F("> "), OPT_CENTERY | OPT_RIGHTX);
}
#if ENABLED(SCROLL_LONG_FILENAMES)
if (is_highlighted) {
@ -102,7 +106,7 @@ void FilesScreen::drawFileButton(const char* filename, uint8_t tag, bool is_dir,
void FilesScreen::drawFileList() {
FileList files;
screen_data.FilesScreen.num_page = max(1,(ceil)(float(files.count()) / files_per_page));
screen_data.FilesScreen.num_page = max(1,ceil(float(files.count()) / files_per_page));
screen_data.FilesScreen.cur_page = min(screen_data.FilesScreen.cur_page, screen_data.FilesScreen.num_page-1);
screen_data.FilesScreen.flags.is_root = files.isAtRootDir();
@ -134,7 +138,6 @@ void FilesScreen::drawHeader() {
sprintf_P(str, PSTR("Page %d of %d"),
screen_data.FilesScreen.cur_page + 1, screen_data.FilesScreen.num_page);
CommandProcessor cmd;
cmd.colors(normal_btn)
.font(font_small)

View File

@ -96,7 +96,7 @@ enum {
#define STATUS_SCREEN_DL_SIZE 2048
#define ALERT_BOX_DL_SIZE 3072
#define SPINNER_DL_SIZE 3072
#define FILE_SCREEN_DL_SIZE 3072
#define FILE_SCREEN_DL_SIZE 4160
#define PRINTING_SCREEN_DL_SIZE 2048
/************************* MENU SCREEN DECLARATIONS *************************/