feat(color): add long press menu for directories in SD browser (#5388)

This commit is contained in:
philmoz
2024-08-07 19:22:30 +10:00
committed by GitHub
parent 687b28dadd
commit ebe7414bec
28 changed files with 146 additions and 35 deletions
+25 -4
View File
@@ -222,6 +222,13 @@ void FileBrowser::onPress(uint16_t row, uint16_t col)
onPress(lv_table_get_cell_value(lvobj, row, col), is_dir);
}
bool FileBrowser::onPressLong(uint16_t row, uint16_t col)
{
bool is_dir = lv_table_has_cell_ctrl(lvobj, row, col, CELL_CTRL_DIR);
onPressLong(lv_table_get_cell_value(lvobj, row, col), is_dir);
return true;
}
void FileBrowser::onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
{
lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS);
@@ -261,13 +268,13 @@ void FileBrowser::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t*
void FileBrowser::onSelected(const char* name, bool is_dir)
{
if (is_dir) {
if (fileSelected) fileSelected(nullptr, nullptr, nullptr);
if (fileSelected) fileSelected(nullptr, nullptr, nullptr, is_dir);
return;
}
const char* path = getCurrentPath();
const char* fullpath = getFullPath(name);
if (fileSelected) fileSelected(path, name, fullpath);
if (fileSelected) fileSelected(path, name, fullpath, is_dir);
selected = name;
}
@@ -277,7 +284,7 @@ void FileBrowser::onPress(const char* name, bool is_dir)
const char* fullpath = getFullPath(name);
if (is_dir) {
f_chdir(fullpath);
if (fileSelected) fileSelected(nullptr, nullptr, nullptr);
if (fileSelected) fileSelected(nullptr, nullptr, nullptr, is_dir);
refresh();
return;
}
@@ -288,6 +295,20 @@ void FileBrowser::onPress(const char* name, bool is_dir)
}
if (fileAction){
fileAction(path, name, fullpath);
fileAction(path, name, fullpath, is_dir);
}
}
void FileBrowser::onPressLong(const char* name, bool is_dir)
{
const char* path = getCurrentPath();
const char* fullpath = getFullPath(name);
if (!selected || (selected != name)) {
onSelected(name, is_dir);
}
if (fileAction){
fileAction(path, name, fullpath, is_dir);
}
}
+3 -1
View File
@@ -27,7 +27,7 @@ class FileBrowser : public TableField
{
public:
// path, name, fullpath
typedef std::function<void(const char*, const char*, const char*)> FileAction;
typedef std::function<void(const char*, const char*, const char*, bool isDir)> FileAction;
FileBrowser(Window* parent, const rect_t& rect, const char* dir);
@@ -45,10 +45,12 @@ class FileBrowser : public TableField
// TableField methods
void onSelected(uint16_t row, uint16_t col) override;
void onPress(uint16_t row, uint16_t col) override;
bool onPressLong(uint16_t row, uint16_t col) override;
protected:
void onSelected(const char* name, bool is_dir);
void onPress(const char* name, bool is_dir);
void onPressLong(const char* name, bool is_dir);
private:
const char* selected = nullptr;
+12 -16
View File
@@ -206,26 +206,22 @@ class MenuBody : public TableField
void onPress(uint16_t row, uint16_t col) override
{
if (!isLongPressed) {
Menu* menu = getParentMenu();
if (row < lines.size()) {
if (menu->multiple) {
if (selectedIndex == (int)row)
lines[row]->onPress();
else {
setIndex(row);
lines[row]->onPress();
}
} else {
// delete menu first to avoid
// focus issues with onPress()
menu->deleteLater();
Menu* menu = getParentMenu();
if (row < lines.size()) {
if (menu->multiple) {
if (selectedIndex == (int)row)
lines[row]->onPress();
else {
setIndex(row);
lines[row]->onPress();
}
} else {
// delete menu first to avoid
// focus issues with onPress()
menu->deleteLater();
lines[row]->onPress();
}
}
isLongPressed = false;
}
void onDrawBegin(uint16_t row, uint16_t col,
+16 -7
View File
@@ -20,8 +20,6 @@
#include "etx_lv_theme.h"
static void table_event(const lv_obj_class_t* class_p, lv_event_t* e);
// Table
const lv_style_const_prop_t table_cell_props[] = {
LV_STYLE_CONST_BORDER_WIDTH(1),
@@ -52,7 +50,7 @@ static const lv_obj_class_t table_class = {
.constructor_cb = table_constructor,
.destructor_cb = nullptr,
.user_data = nullptr,
.event_cb = table_event,
.event_cb = TableField::table_event,
.width_def = 0,
.height_def = 0,
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
@@ -65,7 +63,7 @@ static lv_obj_t* table_create(lv_obj_t* parent)
return etx_create(&table_class, parent);
}
static void table_event(const lv_obj_class_t* class_p, lv_event_t* e)
void TableField::table_event(const lv_obj_class_t* class_p, lv_event_t* e)
{
lv_obj_t* obj = lv_event_get_target(e);
if (obj) {
@@ -87,7 +85,13 @@ static void table_event(const lv_obj_class_t* class_p, lv_event_t* e)
// Otherwise it's a click
if (lv_group_get_editing((lv_group_t*)lv_obj_get_group(obj)) ||
indev_type == LV_INDEV_TYPE_POINTER) {
tf->onPress(row, col);
if (tf->isLongPress) {
tf->isLongPress = false;
if (!tf->onPressLong(row, col))
tf->onPress(row, col);
} else {
tf->onPress(row, col);
}
} else {
tf->onClicked();
}
@@ -148,7 +152,11 @@ static void table_event(const lv_obj_class_t* class_p, lv_event_t* e)
lv_draw_rect(draw_ctx, &rect_dsc, &coords);
}
} break;
case LV_EVENT_RELEASED: {
case LV_EVENT_LONG_PRESSED:
tf->isLongPress = true;
break;
case LV_EVENT_RELEASED:
{
lv_table_t* table = (lv_table_t*)obj;
/*From lv_table.c: handler for LV_EVENT_RELEASED*/
lv_obj_invalidate(obj);
@@ -158,8 +166,9 @@ static void table_event(const lv_obj_class_t* class_p, lv_event_t* e)
table->row_act != LV_TABLE_CELL_NONE && scroll_obj == NULL) {
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
}
// Do not call base class event handler
return;
} break;
}
default:
break;
}
+5
View File
@@ -36,6 +36,7 @@ class TableField : public Window
void select(uint16_t row, uint16_t col, bool force = false);
virtual void onPress(uint16_t row, uint16_t col) {}
virtual bool onPressLong(uint16_t row, uint16_t col) { return false; }
virtual void onSelected(uint16_t row, uint16_t col) {}
virtual void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {}
@@ -44,6 +45,10 @@ class TableField : public Window
void adjustScroll();
void selectNext(int16_t dir);
static void table_event(const lv_obj_class_t* class_p, lv_event_t* e);
protected:
bool isLongPress = false;
void onEvent(event_t event) override;
};
@@ -148,7 +148,7 @@ void ScreenUserInterfacePage::build(Window* window)
auto line = window->newLine(grid);
new StaticText(line, rect_t{}, STR_TOP_BAR);
auto setupTopbarWidgets = new TextButton(line, rect_t{}, STR_SETUP_WIDGETS,
new TextButton(line, rect_t{}, STR_SETUP_WIDGETS,
[=]() -> uint8_t {
menu->deleteLater();
new SetupTopBarWidgetsPage();
@@ -250,17 +250,19 @@ void RadioSdManagerPage::build(Window * window)
preview = new FilePreview(box, {0, 0, PREVIEW_W, PREVIEW_H});
preview->padAll(PAD_SMALL);
browser->setFileAction([=](const char* path, const char* name, const char* fullpath) {
fileAction(path, name, fullpath);
browser->setFileAction([=](const char* path, const char* name, const char* fullpath, bool isDir) {
if (isDir)
dirAction(path, name, fullpath);
else
fileAction(path, name, fullpath);
});
browser->setFileSelected([=](const char* path, const char* name, const char* fullpath) {
browser->setFileSelected([=](const char* path, const char* name, const char* fullpath, bool isDir) {
preview->setFile(nullptr);
loading->hide();
if (fullpath) {
if (fullpath && !isDir) {
auto ext = getFileExtension(fullpath);
if (ext) {
if (strcasecmp(ext, ".bmp") == 0 || strcasecmp(ext, ".png") == 0 ||
strcasecmp(ext, ".jpg") == 0 || strcasecmp(ext, ".jpeg") == 0) {
if (isExtensionMatching(ext, BITMAPS_EXT)) {
previewFilename = fullpath;
loadPreview = 10;
loading->show();
@@ -285,6 +287,41 @@ void RadioSdManagerPage::checkEvents()
}
}
}
void RadioSdManagerPage::dirAction(const char* path, const char* name,
const char* fullpath)
{
if (strcmp(name, "..") == 0) return;
auto window = Layer::back();
auto menu = new Menu(window);
menu->addLine(STR_RENAME_FILE, [=]() {
uint8_t nameLength;
uint8_t extLength;
const char *ext = getFileExtension(name, 0, 0, &nameLength, &extLength);
const uint8_t maxNameLength = SD_SCREEN_FILE_LENGTH - extLength;
nameLength = min((uint8_t)(nameLength - extLength), maxNameLength);
std::string fname(name, nameLength);
std::string extension("");
if (ext) extension = ext;
new LabelDialog(Layer::back(), fname.c_str(), maxNameLength, STR_RENAME_FILE, [=](std::string label) {
label += extension;
f_rename((const TCHAR *)name, (const TCHAR *)label.c_str());
browser->refresh();
});
});
menu->addLine(STR_DELETE_FILE, [=]() {
if (f_unlink(fullpath) != FR_OK) {
new MessageDialog(window, STR_DELETE_FILE, STR_DEL_DIR_NOT_EMPTY);
}
browser->refresh();
});
}
void RadioSdManagerPage::fileAction(const char* path, const char* name,
const char* fullpath)
{
@@ -47,6 +47,7 @@ class RadioSdManagerPage : public PageTab
const char* previewFilename = nullptr;
void fileAction(const char* path, const char* name, const char* fullpath);
void dirAction(const char* path, const char* name, const char* fullpath);
void BootloaderUpdate(const char* fn);
#if defined(BLUETOOTH)
+2
View File
@@ -1112,3 +1112,5 @@ const char STR_WIDGET_SIZE[] = TR_WIDGET_SIZE;
const char STR_MIX_SLOW_PREC[] = TR_MIX_SLOW_PREC;
const char STR_MIX_DELAY_PREC[] = TR_MIX_DELAY_PREC;
const char STR_DEL_DIR_NOT_EMPTY[] = TR_DEL_DIR_NOT_EMPTY;
+2
View File
@@ -1277,3 +1277,5 @@ extern const char STR_WIDGET_SIZE[];
extern const char STR_MIX_SLOW_PREC[];
extern const char STR_MIX_DELAY_PREC[];
extern const char STR_DEL_DIR_NOT_EMPTY[];
+2
View File
@@ -1307,3 +1307,5 @@
#define TR_HIGH_BATT_COLOR "高电量"
#define TR_WIDGET_SIZE "小部件尺寸"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1320,3 +1320,5 @@
#define TR_HIGH_BATT_COLOR "Plně nabitá baterie"
#define TR_WIDGET_SIZE "Velikost widgetu"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1319,3 +1319,5 @@
#define TR_HIGH_BATT_COLOR "Batteri højt"
#define TR_WIDGET_SIZE "Widget størrelse"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1310,3 +1310,5 @@
#define TR_HIGH_BATT_COLOR "Farbe Akku voll"
#define TR_WIDGET_SIZE "Widget Größe"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1305,3 +1305,5 @@
#define TR_HIGH_BATT_COLOR "High battery"
#define TR_WIDGET_SIZE "Widget size"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1307,3 +1307,5 @@
#define TR_HIGH_BATT_COLOR "High battery"
#define TR_WIDGET_SIZE "Widget size"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1323,3 +1323,5 @@
#define TR_HIGH_BATT_COLOR "High battery"
#define TR_WIDGET_SIZE "Widget size"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1322,3 +1322,5 @@
#define TR_HIGH_BATT_COLOR "High battery"
#define TR_WIDGET_SIZE "Taille Widget"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1306,3 +1306,5 @@
#define TR_HIGH_BATT_COLOR "מתח סוללה גבוה"
#define TR_WIDGET_SIZE "Widget size"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1301,3 +1301,5 @@
#define TR_HIGH_BATT_COLOR "Batteria carica"
#define TR_WIDGET_SIZE "Dimensione widget"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1306,3 +1306,5 @@
#define TR_HIGH_BATT_COLOR "バッテリー高"
#define TR_WIDGET_SIZE "ウィジェットサイズ"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1313,3 +1313,5 @@
#define TR_HIGH_BATT_COLOR "High battery"
#define TR_WIDGET_SIZE "Widget size"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1306,3 +1306,5 @@
#define TR_HIGH_BATT_COLOR "Naładowana bateria"
#define TR_WIDGET_SIZE "Rozmiar widżetu"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1310,3 +1310,5 @@
#define TR_HIGH_BATT_COLOR "Bateria Alta"
#define TR_WIDGET_SIZE "Tamanho do widget"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1309,3 +1309,5 @@
#define TR_HIGH_BATT_COLOR "Заряженная АКБ"
#define TR_WIDGET_SIZE "Размер виджета"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1341,3 +1341,5 @@
#define TR_HIGH_BATT_COLOR "Högt batteri"
#define TR_WIDGET_SIZE "Widgetstorlek"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1312,3 +1312,5 @@
#define TR_HIGH_BATT_COLOR "高電量"
#define TR_WIDGET_SIZE "小部件尺寸"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"
+2
View File
@@ -1305,3 +1305,5 @@
#define TR_HIGH_BATT_COLOR "Заряджена батарея"
#define TR_WIDGET_SIZE "Розмір віджета"
#define TR_DEL_DIR_NOT_EMPTY "Directory must be empty before deletion"