From cb51d403fe98d4f1d5071b70cc5a31f4d5e1758d Mon Sep 17 00:00:00 2001 From: 956MB Date: Sun, 5 Jan 2025 16:57:47 -0600 Subject: [PATCH] Based on file list widget changes in #344 --- .../archive/scenes/archive_scene_delete.c | 10 +- applications/services/gui/modules/widget.c | 14 +- applications/services/gui/modules/widget.h | 20 ++- .../modules/widget_elements/widget_element.h | 2 + .../widget_element_file_list.c | 133 ++++++++++++++++-- .../widget_elements/widget_element_i.h | 7 +- applications/services/gui/modules/widget_i.h | 10 ++ targets/f7/api_symbols.csv | 2 +- 8 files changed, 165 insertions(+), 33 deletions(-) create mode 100644 applications/services/gui/modules/widget_i.h diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c index 535776d28..dd0c37011 100644 --- a/applications/main/archive/scenes/archive_scene_delete.c +++ b/applications/main/archive/scenes/archive_scene_delete.c @@ -35,7 +35,15 @@ void archive_scene_delete_on_enter(void* context) { "\e#Delete %d files?\e#", model->selected_count); widget_add_file_list_element( - app->widget, 0, 23, 3, model->selected_files, model->selected_count); + app->widget, + 0, + 23, + 3, + model->selected_files, + model->selected_count, + 14, + FRAME_HEIGHT * 3, + false); } else { ArchiveFile_t* current = archive_get_current_file(browser); FuriString* filename = furi_string_alloc(); diff --git a/applications/services/gui/modules/widget.c b/applications/services/gui/modules/widget.c index 32eb48f9a..5996f6ba8 100644 --- a/applications/services/gui/modules/widget.c +++ b/applications/services/gui/modules/widget.c @@ -5,11 +5,6 @@ ARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST); // NOLINT -struct Widget { - View* view; - void* context; -}; - typedef struct { ElementArray_t element; } GuiWidgetModel; @@ -125,10 +120,13 @@ WidgetElement* widget_add_file_list_element( uint8_t y, uint8_t lines, FuriString** files, - size_t count) { + size_t count, + uint8_t scrollbar_y, + uint8_t scrollbar_height, + bool show_size) { furi_assert(widget); - WidgetElement* file_list_element = - widget_element_file_list_create(widget, x, y, lines, files, count); + WidgetElement* file_list_element = widget_element_file_list_create( + widget, x, y, lines, files, count, scrollbar_y, scrollbar_height, show_size); widget_add_element(widget, file_list_element); return file_list_element; } diff --git a/applications/services/gui/modules/widget.h b/applications/services/gui/modules/widget.h index 38b5706be..c912d20f7 100644 --- a/applications/services/gui/modules/widget.h +++ b/applications/services/gui/modules/widget.h @@ -43,12 +43,15 @@ View* widget_get_view(Widget* widget); /** Add File List Element * - * @param widget Widget instance - * @param x x coordinate - * @param y y coordinate - * @param lines Number of lines visible - * @param files Array of FuriString pointers - * @param count Number of files + * @param widget Widget instance + * @param x x coordinate + * @param y y coordinate + * @param lines Number of lines visible + * @param files Array of FuriString pointers + * @param count Number of files + * @param scrollbar_y Y coordinate of the scrollbar + * @param scrollbar_height Height of the scrollbar + * @param show_size Show file size */ WidgetElement* widget_add_file_list_element( Widget* widget, @@ -56,7 +59,10 @@ WidgetElement* widget_add_file_list_element( uint8_t y, uint8_t lines, FuriString** files, - size_t count); + size_t count, + uint8_t scrollbar_y, + uint8_t scrollbar_height, + bool show_size); /** Add Multi String Element * diff --git a/applications/services/gui/modules/widget_elements/widget_element.h b/applications/services/gui/modules/widget_elements/widget_element.h index 473fabd04..eede42002 100644 --- a/applications/services/gui/modules/widget_elements/widget_element.h +++ b/applications/services/gui/modules/widget_elements/widget_element.h @@ -5,6 +5,8 @@ #pragma once +#include "input/input.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/applications/services/gui/modules/widget_elements/widget_element_file_list.c b/applications/services/gui/modules/widget_elements/widget_element_file_list.c index f8eb4d852..f51547fcd 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_file_list.c +++ b/applications/services/gui/modules/widget_elements/widget_element_file_list.c @@ -6,9 +6,16 @@ #include "archive/archive_i.h" #include "assets_icons.h" +#define SCROLL_INTERVAL (333) +#define SCROLL_DELAY (2) + +const char* units_short[] = {"B", "K", "M", "G", "T"}; + typedef struct { - FuriString* name; + FuriString* path; const Icon* icon; + char size_num[8]; + char size_unit[2]; } FileListItem; typedef struct { @@ -18,8 +25,35 @@ typedef struct { FileListItem* files; size_t count; size_t offset; + uint8_t scrollbar_y; + bool show_size; + uint8_t scrollbar_height; + size_t scroll_counter; + FuriTimer* scroll_timer; } FileListModel; +static void format_file_size( + uint64_t size, + char* num_buf, + size_t num_size, + char* unit_buf, + size_t unit_size) { + double formatted_size = size; + uint8_t unit = 0; + + while(formatted_size >= 1024 && unit < COUNT_OF(units_short) - 1) { + formatted_size /= 1024; + unit++; + } + + if(unit == 0) { + snprintf(num_buf, num_size, "%d", (int)formatted_size); + } else { + snprintf(num_buf, num_size, "%.1f", (double)formatted_size); + } + snprintf(unit_buf, unit_size, "%s", units_short[unit]); +} + static void widget_element_file_list_draw(Canvas* canvas, WidgetElement* element) { furi_assert(canvas); furi_assert(element); @@ -32,11 +66,42 @@ static void widget_element_file_list_draw(Canvas* canvas, WidgetElement* element if(idx < model->count) { canvas_draw_icon( canvas, model->x + 2, model->y + (i * FRAME_HEIGHT) - 9, model->files[idx].icon); - canvas_draw_str( + + size_t inner_x = 123; + if(model->show_size && model->files[idx].size_num[0] != '\0') { + canvas_set_font(canvas, FontPrimary); + uint16_t num_width = canvas_string_width(canvas, model->files[idx].size_num); + canvas_set_font(canvas, FontSecondary); + uint16_t unit_width = canvas_string_width(canvas, model->files[idx].size_unit); + uint16_t total_width = num_width + unit_width; + inner_x = model->x + (128 - model->x) - total_width - 5; + inner_x--; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str( + canvas, inner_x, model->y + (i * FRAME_HEIGHT), model->files[idx].size_num); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str( + canvas, + inner_x + num_width + 1, + model->y + (i * FRAME_HEIGHT), + model->files[idx].size_unit); + } + + size_t scroll_counter = model->scroll_counter; + scroll_counter = + i == 0 ? (model->count > model->lines && + (scroll_counter < SCROLL_DELAY ? 0 : scroll_counter - SCROLL_DELAY)) : + 0; + + elements_scrollable_text_line( canvas, model->x + 15, model->y + (i * FRAME_HEIGHT), - furi_string_get_cstr(model->files[idx].name)); + inner_x - 19, + model->files[idx].path, + scroll_counter, + i != 0 || model->count <= model->lines); } } @@ -44,10 +109,10 @@ static void widget_element_file_list_draw(Canvas* canvas, WidgetElement* element elements_scrollbar_pos( canvas, 128, - model->y - 9, - model->lines * FRAME_HEIGHT, + model->scrollbar_y, + model->scrollbar_height, model->offset, - model->count - 2); + model->count - (model->lines - 1)); } } @@ -56,12 +121,16 @@ static bool widget_element_file_list_input(InputEvent* event, WidgetElement* ele FileListModel* model = element->model; bool consumed = false; - if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + if(model->count > model->lines && + (event->type == InputTypeShort || event->type == InputTypeRepeat)) { if(event->key == InputKeyUp) { - model->offset = (model->offset > 0) ? model->offset - 1 : model->count - 3; + model->offset = (model->offset > 0) ? model->offset - 1 : model->count - model->lines; + model->scroll_counter = 0; consumed = true; } else if(event->key == InputKeyDown) { - model->offset = ((model->offset + 3) < model->count) ? model->offset + 1 : 0; + model->offset = ((model->offset + model->lines) < model->count) ? model->offset + 1 : + 0; + model->scroll_counter = 0; consumed = true; } } @@ -69,11 +138,20 @@ static bool widget_element_file_list_input(InputEvent* event, WidgetElement* ele return consumed; } +static void widget_element_file_list_timer_callback(void* context) { + WidgetElement* element = context; + FileListModel* file_model = element->model; + file_model->scroll_counter++; + with_view_model(element->parent->view, void* _model, { UNUSED(_model); }, true); +} + static void widget_element_file_list_free(WidgetElement* element) { furi_assert(element); FileListModel* model = element->model; + furi_timer_stop(model->scroll_timer); + furi_timer_free(model->scroll_timer); for(size_t i = 0; i < model->count; i++) { - furi_string_free(model->files[i].name); + furi_string_free(model->files[i].path); } free(model->files); free(model); @@ -86,26 +164,48 @@ WidgetElement* widget_element_file_list_create( uint8_t y, uint8_t lines, FuriString** files, - size_t count) { + size_t count, + uint8_t scrollbar_y, + uint8_t scrollbar_height, + bool show_size) { // Allocate and init model FileListModel* model = malloc(sizeof(FileListModel)); model->x = x; model->y = y; model->lines = lines; model->count = count; + model->scrollbar_y = scrollbar_y; + model->scrollbar_height = scrollbar_height; + model->show_size = show_size; model->offset = 0; + model->scroll_counter = 0; model->files = malloc(sizeof(FileListItem) * count); + Storage* storage = furi_record_open(RECORD_STORAGE); + FileInfo info; for(size_t i = 0; i < count; i++) { - model->files[i].name = furi_string_alloc(); - path_extract_filename(files[i], model->files[i].name, false); + model->files[i].path = furi_string_alloc(); + path_extract_filename(files[i], model->files[i].path, false); + model->files[i].size_num[0] = '\0'; + model->files[i].size_unit[0] = '\0'; + if(storage_dir_exists(storage, furi_string_get_cstr(files[i]))) { model->files[i].icon = &I_dir_10px; } else { - const char* ext = strrchr(furi_string_get_cstr(model->files[i].name), '.'); + const char* ext = strrchr(furi_string_get_cstr(model->files[i].path), '.'); model->files[i].icon = (ext && strcasecmp(ext, ".js") == 0) ? &I_js_script_10px : &I_unknown_10px; } + + if(show_size) { + storage_common_stat(storage, furi_string_get_cstr(files[i]), &info); + format_file_size( + info.size, + model->files[i].size_num, + sizeof(model->files[i].size_num), + model->files[i].size_unit, + sizeof(model->files[i].size_unit)); + } } furi_record_close(RECORD_STORAGE); @@ -116,5 +216,10 @@ WidgetElement* widget_element_file_list_create( element->free = widget_element_file_list_free; element->parent = widget; element->model = model; + + model->scroll_timer = + furi_timer_alloc(widget_element_file_list_timer_callback, FuriTimerTypePeriodic, element); + furi_timer_start(model->scroll_timer, SCROLL_INTERVAL); + return element; } diff --git a/applications/services/gui/modules/widget_elements/widget_element_i.h b/applications/services/gui/modules/widget_elements/widget_element_i.h index ef415ce9c..f6ff6579b 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_i.h +++ b/applications/services/gui/modules/widget_elements/widget_element_i.h @@ -5,7 +5,7 @@ #pragma once -#include "../widget.h" +#include "../widget_i.h" #include "widget_element.h" #include #include @@ -41,7 +41,10 @@ WidgetElement* widget_element_file_list_create( uint8_t y, uint8_t lines, FuriString** files, - size_t count); + size_t count, + uint8_t scrollbar_y, + uint8_t scrollbar_height, + bool show_size); /** Create multi string element */ WidgetElement* widget_element_string_multiline_create( diff --git a/applications/services/gui/modules/widget_i.h b/applications/services/gui/modules/widget_i.h new file mode 100644 index 000000000..d4f0a57eb --- /dev/null +++ b/applications/services/gui/modules/widget_i.h @@ -0,0 +1,10 @@ +#pragma once + +#include "widget.h" +#include +#include + +struct Widget { + View* view; + void* context; +}; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 37d3eb238..36c8ebc2b 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3836,7 +3836,7 @@ Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list" Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t" Function,-,wctomb,int,"char*, wchar_t" Function,+,widget_add_button_element,WidgetElement*,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" -Function,+,widget_add_file_list_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, FuriString**, size_t" +Function,+,widget_add_file_list_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, FuriString**, size_t, uint8_t, uint8_t, _Bool" Function,+,widget_add_frame_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,widget_add_icon_element,WidgetElement*,"Widget*, uint8_t, uint8_t, const Icon*" Function,+,widget_add_string_element,WidgetElement*,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*"