mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-30 21:58:55 -07:00
Added file list widget to delete screen
- New file list widget was created to show the to-be-deleted files on the delete screen, rather than just "Delete n files?" It's also scrollable. - 'Select' action menu option was removed from the favorites tab since favorites already have a mechanism for that stuff.
This commit is contained in:
@@ -733,3 +733,18 @@ void archive_clear_selection(ArchiveBrowserViewModel* model) {
|
||||
file->selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
void archive_deselect_children(ArchiveBrowserViewModel* model, const char* parent) {
|
||||
size_t write_idx = 0;
|
||||
for(size_t i = 0; i < model->selected_count; i++) {
|
||||
if(!furi_string_start_with(model->selected_files[i], parent)) {
|
||||
if(write_idx != i) {
|
||||
model->selected_files[write_idx] = model->selected_files[i];
|
||||
}
|
||||
write_idx++;
|
||||
} else {
|
||||
furi_string_free(model->selected_files[i]);
|
||||
}
|
||||
}
|
||||
model->selected_count = write_idx;
|
||||
}
|
||||
|
||||
@@ -112,3 +112,4 @@ void archive_leave_dir(ArchiveBrowserView* browser);
|
||||
void archive_refresh_dir(ArchiveBrowserView* browser);
|
||||
|
||||
void archive_clear_selection(ArchiveBrowserViewModel* model);
|
||||
void archive_deselect_children(ArchiveBrowserViewModel* model, const char* parent);
|
||||
|
||||
@@ -307,20 +307,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
||||
if(!current->selected) {
|
||||
// If current file type is a folder, deselect all files that start with the same path to not have a conflict.
|
||||
if(current->type == ArchiveFileTypeFolder) {
|
||||
size_t write_idx = 0;
|
||||
for(size_t i = 0; i < model->selected_count; i++) {
|
||||
if(!furi_string_start_with(
|
||||
model->selected_files[i], current->path)) {
|
||||
if(write_idx != i) {
|
||||
model->selected_files[write_idx] =
|
||||
model->selected_files[i];
|
||||
}
|
||||
write_idx++;
|
||||
} else {
|
||||
furi_string_free(model->selected_files[i]);
|
||||
}
|
||||
}
|
||||
model->selected_count = write_idx;
|
||||
archive_deselect_children(model, furi_string_get_cstr(current->path));
|
||||
}
|
||||
model->selected_files[model->selected_count] =
|
||||
furi_string_alloc_set(current->path);
|
||||
|
||||
@@ -28,12 +28,14 @@ void archive_scene_delete_on_enter(void* context) {
|
||||
browser->view,
|
||||
ArchiveBrowserViewModel * model,
|
||||
{
|
||||
if(model->select_mode && model->selected_count > 0) {
|
||||
if(model->select_mode && model->selected_count > 1) {
|
||||
snprintf(
|
||||
delete_str,
|
||||
sizeof(delete_str),
|
||||
"\e#Delete %d files?\e#",
|
||||
model->selected_count);
|
||||
widget_add_file_list_element(
|
||||
app->widget, 0, 23, 3, model->selected_files, model->selected_count);
|
||||
} else {
|
||||
ArchiveFile_t* current = archive_get_current_file(browser);
|
||||
FuriString* filename = furi_string_alloc();
|
||||
|
||||
@@ -125,10 +125,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
|
||||
menu_array_push_raw(model->context_menu),
|
||||
"Info",
|
||||
ArchiveBrowserEventFileMenuInfo);
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
model->select_mode ? "Deselect" : "Select",
|
||||
ArchiveBrowserEventFileMenuSelectMode);
|
||||
if(!favorites) {
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
model->select_mode ? "Deselect" : "Select",
|
||||
ArchiveBrowserEventFileMenuSelectMode);
|
||||
}
|
||||
if(selected->type != ArchiveFileTypeFolder) {
|
||||
archive_menu_add_item(
|
||||
menu_array_push_raw(model->context_menu),
|
||||
|
||||
@@ -119,6 +119,20 @@ static void widget_add_element(Widget* widget, WidgetElement* element) {
|
||||
true);
|
||||
}
|
||||
|
||||
WidgetElement* widget_add_file_list_element(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t lines,
|
||||
FuriString** files,
|
||||
size_t count) {
|
||||
furi_assert(widget);
|
||||
WidgetElement* file_list_element =
|
||||
widget_element_file_list_create(widget, x, y, lines, files, count);
|
||||
widget_add_element(widget, file_list_element);
|
||||
return file_list_element;
|
||||
}
|
||||
|
||||
WidgetElement* widget_add_string_multiline_element(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
|
||||
@@ -41,6 +41,23 @@ void widget_reset(Widget* widget);
|
||||
*/
|
||||
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
|
||||
*/
|
||||
WidgetElement* widget_add_file_list_element(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t lines,
|
||||
FuriString** files,
|
||||
size_t count);
|
||||
|
||||
/** Add Multi String Element
|
||||
*
|
||||
* @param widget Widget instance
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#include "assets_icons.h"
|
||||
#include "path.h"
|
||||
#include "widget_element_i.h"
|
||||
#include <gui/elements.h>
|
||||
#include <core/common_defines.h>
|
||||
#include "archive/archive_i.h"
|
||||
|
||||
typedef struct {
|
||||
FuriString* name;
|
||||
bool is_dir;
|
||||
} FileListItem;
|
||||
|
||||
typedef struct {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t lines;
|
||||
FileListItem* files;
|
||||
size_t count;
|
||||
size_t offset;
|
||||
} FileListModel;
|
||||
|
||||
static void widget_element_file_list_draw(Canvas* canvas, WidgetElement* element) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(element);
|
||||
FileListModel* model = element->model;
|
||||
size_t items_visible = MIN(model->lines, model->count);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
for(size_t i = 0; i < items_visible; i++) {
|
||||
size_t idx = model->offset + i;
|
||||
if(idx < model->count) {
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
model->x + 2,
|
||||
model->y + (i * FRAME_HEIGHT) - 9,
|
||||
model->files[idx].is_dir ? &I_dir_10px : &I_unknown_10px);
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
model->x + 15,
|
||||
model->y + (i * FRAME_HEIGHT),
|
||||
furi_string_get_cstr(model->files[idx].name));
|
||||
}
|
||||
}
|
||||
|
||||
if(model->count > model->lines) {
|
||||
elements_scrollbar_pos(
|
||||
canvas,
|
||||
128,
|
||||
model->y - 9,
|
||||
model->lines * FRAME_HEIGHT,
|
||||
model->offset,
|
||||
model->count - 2);
|
||||
}
|
||||
}
|
||||
|
||||
static bool widget_element_file_list_input(InputEvent* event, WidgetElement* element) {
|
||||
furi_assert(element);
|
||||
FileListModel* model = element->model;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->offset = (model->offset > 0) ? model->offset - 1 : model->count - 3;
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->offset = ((model->offset + 3) < model->count) ? model->offset + 1 : 0;
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void widget_element_file_list_free(WidgetElement* element) {
|
||||
furi_assert(element);
|
||||
FileListModel* model = element->model;
|
||||
for(size_t i = 0; i < model->count; i++) {
|
||||
furi_string_free(model->files[i].name);
|
||||
}
|
||||
free(model->files);
|
||||
free(model);
|
||||
free(element);
|
||||
}
|
||||
|
||||
WidgetElement* widget_element_file_list_create(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t lines,
|
||||
FuriString** files,
|
||||
size_t count) {
|
||||
// Allocate and init model
|
||||
FileListModel* model = malloc(sizeof(FileListModel));
|
||||
model->x = x;
|
||||
model->y = y;
|
||||
model->lines = lines;
|
||||
model->count = count;
|
||||
model->offset = 0;
|
||||
model->files = malloc(sizeof(FileListItem) * count);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
for(size_t i = 0; i < count; i++) {
|
||||
FileInfo fileinfo;
|
||||
model->files[i].name = furi_string_alloc();
|
||||
path_extract_filename(files[i], model->files[i].name, false);
|
||||
model->files[i].is_dir = false;
|
||||
|
||||
if(storage_common_stat(storage, furi_string_get_cstr(files[i]), &fileinfo) == FSE_OK) {
|
||||
model->files[i].is_dir = file_info_is_dir(&fileinfo);
|
||||
}
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
// Allocate and init Element
|
||||
WidgetElement* element = malloc(sizeof(WidgetElement));
|
||||
element->draw = widget_element_file_list_draw;
|
||||
element->input = widget_element_file_list_input;
|
||||
element->free = widget_element_file_list_free;
|
||||
element->parent = widget;
|
||||
element->model = model;
|
||||
return element;
|
||||
}
|
||||
@@ -34,6 +34,15 @@ struct WidgetElement {
|
||||
Widget* parent;
|
||||
};
|
||||
|
||||
/** Create file list element */
|
||||
WidgetElement* widget_element_file_list_create(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t lines,
|
||||
FuriString** files,
|
||||
size_t count);
|
||||
|
||||
/** Create multi string element */
|
||||
WidgetElement* widget_element_string_multiline_create(
|
||||
uint8_t x,
|
||||
|
||||
@@ -3836,6 +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_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*"
|
||||
|
||||
|
Reference in New Issue
Block a user