mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-30 21:58:55 -07:00
Asset Packs: Warning for RAM usage
- Checks asset packs if they contain Fonts/Icons and adds "Size Warning" list entry that opens new scenes. - First scene is just a warning with text explaining that fonts and icons remain loaded and use up memory. - Second scene uses the file list widget to display all the selected asset packs fonts and icons, as well as their sizes. This information is just about being extra informative. No actions can be done on any of the files shown.
This commit is contained in:
@@ -313,6 +313,10 @@ MomentumApp* momentum_app_alloc() {
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, MomentumAppViewDialogEx, dialog_ex_get_view(app->dialog_ex));
|
||||
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, MomentumAppViewWidget, widget_get_view(app->widget));
|
||||
|
||||
// Settings init
|
||||
|
||||
app->asset_pack_index = 0;
|
||||
@@ -441,6 +445,8 @@ void momentum_app_free(MomentumApp* app) {
|
||||
popup_free(app->popup);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MomentumAppViewDialogEx);
|
||||
dialog_ex_free(app->dialog_ex);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MomentumAppViewWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
// View Dispatcher and Scene Manager
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <gui/modules/number_input.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/widget.h>
|
||||
|
||||
#include <momentum/asset_packs.h>
|
||||
#include <loader/loader_menu.h>
|
||||
@@ -63,7 +64,7 @@ typedef struct {
|
||||
NumberInput* number_input;
|
||||
Popup* popup;
|
||||
DialogEx* dialog_ex;
|
||||
|
||||
Widget* widget;
|
||||
CharList_t asset_pack_names;
|
||||
uint8_t asset_pack_index;
|
||||
CharList_t mainmenu_app_labels;
|
||||
@@ -107,6 +108,7 @@ typedef enum {
|
||||
MomentumAppViewNumberInput,
|
||||
MomentumAppViewPopup,
|
||||
MomentumAppViewDialogEx,
|
||||
MomentumAppViewWidget,
|
||||
} MomentumAppView;
|
||||
|
||||
bool momentum_app_apply(MomentumApp* app);
|
||||
|
||||
@@ -2,6 +2,8 @@ ADD_SCENE(momentum_app, start, Start)
|
||||
ADD_SCENE(momentum_app, interface, Interface)
|
||||
ADD_SCENE(momentum_app, interface_graphics, InterfaceGraphics)
|
||||
ADD_SCENE(momentum_app, interface_graphics_pack, InterfaceGraphicsPack)
|
||||
ADD_SCENE(momentum_app, interface_graphics_pack_warning, InterfaceGraphicsPackWarning)
|
||||
ADD_SCENE(momentum_app, interface_graphics_pack_warning_info, InterfaceGraphicsPackWarningInfo)
|
||||
ADD_SCENE(momentum_app, interface_mainmenu, InterfaceMainmenu)
|
||||
ADD_SCENE(momentum_app, interface_mainmenu_add, InterfaceMainmenuAdd)
|
||||
ADD_SCENE(momentum_app, interface_mainmenu_add_main, InterfaceMainmenuAddMain)
|
||||
|
||||
@@ -2,11 +2,29 @@
|
||||
|
||||
enum VarItemListIndex {
|
||||
VarItemListIndexAssetPack,
|
||||
VarItemListIndexPackWarning,
|
||||
VarItemListIndexAnimSpeed,
|
||||
VarItemListIndexCycleAnims,
|
||||
VarItemListIndexUnlockAnims,
|
||||
};
|
||||
|
||||
static bool check_asset_pack_folders(const char* pack_name) {
|
||||
if(pack_name == NULL) return false;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FuriString* path = furi_string_alloc();
|
||||
bool result = false;
|
||||
|
||||
furi_string_printf(path, "%s/%s/Icons", ASSET_PACKS_PATH, pack_name);
|
||||
if(storage_dir_exists(storage, furi_string_get_cstr(path))) result = true;
|
||||
if(!result) {
|
||||
furi_string_printf(path, "%s/%s/Fonts", ASSET_PACKS_PATH, pack_name);
|
||||
if(storage_dir_exists(storage, furi_string_get_cstr(path))) result = true;
|
||||
}
|
||||
|
||||
furi_string_free(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
void momentum_app_scene_interface_graphics_var_item_list_callback(void* context, uint32_t index) {
|
||||
MomentumApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
@@ -24,6 +42,12 @@ static void momentum_app_scene_interface_graphics_asset_pack_changed(VariableIte
|
||||
app->asset_pack_index = index;
|
||||
app->save_settings = true;
|
||||
app->apply_pack = true;
|
||||
|
||||
// Workaround to force scene reload to rebuild the list with/without pack warning and
|
||||
// return to 0 index ("Asset Pack").
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneInterfaceGraphics);
|
||||
scene_manager_set_scene_state(app->scene_manager, MomentumAppSceneInterfaceGraphics, 0);
|
||||
}
|
||||
|
||||
const char* const anim_speed_names[] = {
|
||||
@@ -118,6 +142,10 @@ void momentum_app_scene_interface_graphics_on_enter(void* context) {
|
||||
VariableItem* item;
|
||||
uint8_t value_index;
|
||||
|
||||
const char* selected_asset_pack =
|
||||
app->asset_pack_index == 0 ?
|
||||
"Default" :
|
||||
*CharList_get(app->asset_pack_names, app->asset_pack_index - 1);
|
||||
item = variable_item_list_add(
|
||||
var_item_list,
|
||||
"Asset Pack",
|
||||
@@ -125,11 +153,14 @@ void momentum_app_scene_interface_graphics_on_enter(void* context) {
|
||||
momentum_app_scene_interface_graphics_asset_pack_changed,
|
||||
app);
|
||||
variable_item_set_current_value_index(item, app->asset_pack_index);
|
||||
variable_item_set_current_value_text(
|
||||
item,
|
||||
app->asset_pack_index == 0 ?
|
||||
"Default" :
|
||||
*CharList_get(app->asset_pack_names, app->asset_pack_index - 1));
|
||||
variable_item_set_current_value_text(item, selected_asset_pack);
|
||||
|
||||
if(app->asset_pack_index > 0) {
|
||||
if(check_asset_pack_folders(selected_asset_pack)) {
|
||||
item = variable_item_list_add(var_item_list, "Size Warning", 0, NULL, app);
|
||||
variable_item_set_current_value_text(item, ">");
|
||||
}
|
||||
}
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list,
|
||||
@@ -183,6 +214,11 @@ bool momentum_app_scene_interface_graphics_on_event(void* context, SceneManagerE
|
||||
switch(event.event) {
|
||||
case VarItemListIndexAssetPack:
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneInterfaceGraphicsPack);
|
||||
break;
|
||||
case VarItemListIndexPackWarning:
|
||||
scene_manager_next_scene(
|
||||
app->scene_manager, MomentumAppSceneInterfaceGraphicsPackWarning);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
#include "../momentum_app.h"
|
||||
|
||||
void momentum_app_scene_interface_graphics_pack_warning_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
MomentumApp* app = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void momentum_app_scene_interface_graphics_pack_warning_on_enter(void* context) {
|
||||
MomentumApp* app = context;
|
||||
|
||||
widget_add_button_element(
|
||||
app->widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Back",
|
||||
momentum_app_scene_interface_graphics_pack_warning_widget_callback,
|
||||
app);
|
||||
widget_add_button_element(
|
||||
app->widget,
|
||||
GuiButtonTypeRight,
|
||||
"Info",
|
||||
momentum_app_scene_interface_graphics_pack_warning_widget_callback,
|
||||
app);
|
||||
|
||||
widget_add_text_box_element(
|
||||
app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, "\e#Size Warning\e#", false);
|
||||
|
||||
const char* body =
|
||||
"Your selected pack contains Fonts & Icons, which remain loaded and use up memory";
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 0, 18, AlignLeft, AlignTop, FontSecondary, body);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MomentumAppViewWidget);
|
||||
}
|
||||
|
||||
bool momentum_app_scene_interface_graphics_pack_warning_on_event(
|
||||
void* context,
|
||||
SceneManagerEvent event) {
|
||||
MomentumApp* app = context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
return scene_manager_previous_scene(app->scene_manager);
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(
|
||||
app->scene_manager, MomentumAppSceneInterfaceGraphicsPackWarningInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void momentum_app_scene_interface_graphics_pack_warning_on_exit(void* context) {
|
||||
MomentumApp* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
#include "../momentum_app.h"
|
||||
|
||||
void momentum_app_scene_interface_graphics_pack_warning_info_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
MomentumApp* app = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void momentum_app_scene_interface_graphics_pack_warning_info_on_enter(void* context) {
|
||||
MomentumApp* app = context;
|
||||
|
||||
widget_add_button_element(
|
||||
app->widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Exit",
|
||||
momentum_app_scene_interface_graphics_pack_warning_info_widget_callback,
|
||||
app);
|
||||
|
||||
const char* dirs[] = {"Fonts", "Icons"};
|
||||
const char* font_exts[] = {".u8f"};
|
||||
const char* icon_exts[] = {".bm", ".bmx"};
|
||||
const char** exts[] = {font_exts, icon_exts};
|
||||
const size_t ext_counts[] = {COUNT_OF(font_exts), COUNT_OF(icon_exts)};
|
||||
const char* selected_pack =
|
||||
app->asset_pack_index == 0 ?
|
||||
"Default" :
|
||||
*CharList_get(app->asset_pack_names, app->asset_pack_index - 1);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FuriString* path = furi_string_alloc();
|
||||
FuriString** paths = NULL;
|
||||
size_t num_paths = 0;
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(dirs); i++) {
|
||||
FuriString** files = NULL;
|
||||
size_t num_files = 0;
|
||||
|
||||
furi_string_printf(path, "%s/%s/%s", ASSET_PACKS_PATH, selected_pack, dirs[i]);
|
||||
if(storage_dir_exists(storage, furi_string_get_cstr(path))) {
|
||||
if(storage_list_dir(
|
||||
storage,
|
||||
furi_string_get_cstr(path),
|
||||
&files,
|
||||
&num_files,
|
||||
true,
|
||||
exts[i],
|
||||
ext_counts[i])) {
|
||||
paths = realloc(paths, (num_paths + num_files) * sizeof(FuriString*));
|
||||
memcpy(&paths[num_paths], files, num_files * sizeof(FuriString*));
|
||||
num_paths += num_files;
|
||||
free(files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t f_count = 0;
|
||||
size_t i_count = 0;
|
||||
for(size_t i = 0; i < num_paths; i++) {
|
||||
if(furi_string_start_with_str(paths[i], ASSET_PACKS_PATH)) {
|
||||
if(furi_string_search_str(paths[i], "/Fonts/") != FURI_STRING_FAILURE) {
|
||||
f_count++;
|
||||
} else if(furi_string_search_str(paths[i], "/Icons/") != FURI_STRING_FAILURE) {
|
||||
i_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FuriString* title = furi_string_alloc();
|
||||
if(f_count > 0 && i_count > 0) {
|
||||
furi_string_printf(title, "%zu Fonts, %zu Icons", f_count, i_count);
|
||||
} else if(f_count > 0) {
|
||||
furi_string_printf(title, "%zu Fonts", f_count);
|
||||
} else if(i_count > 0) {
|
||||
furi_string_printf(title, "%zu Icons", i_count);
|
||||
}
|
||||
|
||||
widget_add_string_element(
|
||||
app->widget, 35, 57, AlignLeft, AlignCenter, FontPrimary, furi_string_get_cstr(title));
|
||||
furi_string_free(title);
|
||||
|
||||
widget_add_file_list_element(app->widget, 0, 12, 4, paths, num_paths, 0, 52, true);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MomentumAppViewWidget);
|
||||
}
|
||||
|
||||
bool momentum_app_scene_interface_graphics_pack_warning_info_on_event(
|
||||
void* context,
|
||||
SceneManagerEvent event) {
|
||||
MomentumApp* app = context;
|
||||
|
||||
if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) ||
|
||||
event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, MomentumAppSceneInterfaceGraphics);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void momentum_app_scene_interface_graphics_pack_warning_info_on_exit(void* context) {
|
||||
MomentumApp* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -5,11 +5,6 @@
|
||||
|
||||
ARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST); // NOLINT
|
||||
|
||||
struct Widget {
|
||||
View* view;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ElementArray_t element;
|
||||
} GuiWidgetModel;
|
||||
@@ -119,6 +114,23 @@ 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,
|
||||
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, scrollbar_y, scrollbar_height, show_size);
|
||||
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,29 @@ 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
|
||||
* @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,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t lines,
|
||||
FuriString** files,
|
||||
size_t count,
|
||||
uint8_t scrollbar_y,
|
||||
uint8_t scrollbar_height,
|
||||
bool show_size);
|
||||
|
||||
/** Add Multi String Element
|
||||
*
|
||||
* @param widget Widget instance
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "input/input.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
#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"
|
||||
#include "assets_icons.h"
|
||||
|
||||
#define SCROLL_INTERVAL (333)
|
||||
#define SCROLL_DELAY (2)
|
||||
|
||||
const char* units_short[] = {"B", "K", "M", "G", "T"};
|
||||
|
||||
typedef struct {
|
||||
FuriString* path;
|
||||
const Icon* icon;
|
||||
char size_num[8];
|
||||
char size_unit[2];
|
||||
} FileListItem;
|
||||
|
||||
typedef struct {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t lines;
|
||||
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);
|
||||
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].icon);
|
||||
|
||||
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),
|
||||
inner_x - 19,
|
||||
model->files[idx].path,
|
||||
scroll_counter,
|
||||
i != 0 || model->count <= model->lines);
|
||||
}
|
||||
}
|
||||
|
||||
if(model->count > model->lines) {
|
||||
elements_scrollbar_pos(
|
||||
canvas,
|
||||
128,
|
||||
model->scrollbar_y,
|
||||
model->scrollbar_height,
|
||||
model->offset,
|
||||
model->count - (model->lines - 1));
|
||||
}
|
||||
}
|
||||
|
||||
static bool widget_element_file_list_input(InputEvent* event, WidgetElement* element) {
|
||||
furi_assert(element);
|
||||
FileListModel* model = element->model;
|
||||
bool consumed = false;
|
||||
|
||||
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 - model->lines;
|
||||
model->scroll_counter = 0;
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->offset = ((model->offset + model->lines) < model->count) ? model->offset + 1 :
|
||||
0;
|
||||
model->scroll_counter = 0;
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
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].path);
|
||||
}
|
||||
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,
|
||||
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].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].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);
|
||||
|
||||
// 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;
|
||||
|
||||
model->scroll_timer =
|
||||
furi_timer_alloc(widget_element_file_list_timer_callback, FuriTimerTypePeriodic, element);
|
||||
furi_timer_start(model->scroll_timer, SCROLL_INTERVAL);
|
||||
|
||||
return element;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../widget.h"
|
||||
#include "../widget_i.h"
|
||||
#include "widget_element.h"
|
||||
#include <furi.h>
|
||||
#include <gui/view.h>
|
||||
@@ -34,6 +34,18 @@ 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,
|
||||
uint8_t scrollbar_y,
|
||||
uint8_t scrollbar_height,
|
||||
bool show_size);
|
||||
|
||||
/** Create multi string element */
|
||||
WidgetElement* widget_element_string_multiline_create(
|
||||
uint8_t x,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "widget.h"
|
||||
#include <furi.h>
|
||||
#include <m-array.h>
|
||||
|
||||
struct Widget {
|
||||
View* view;
|
||||
void* context;
|
||||
};
|
||||
@@ -274,6 +274,27 @@ bool storage_dir_rewind(File* file);
|
||||
*/
|
||||
bool storage_dir_exists(Storage* storage, const char* path);
|
||||
|
||||
/**
|
||||
* @brief List the contents of a directory.
|
||||
*
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @param path pointer to a zero-terminated string containing the path of the directory in question.
|
||||
* @param files pointer to an array of FuriString pointers to contain the file names.
|
||||
* @param num_files pointer to the number of files in the directory.
|
||||
* @param ignore_dirs if true, only files will be returned, not directories.
|
||||
* @param include_ext pointer to an array of zero-terminated strings of extensions to include.
|
||||
* @param ext_count the number of extensions in the include_ext array.
|
||||
* @return true if the directory was successfully listed, false otherwise.
|
||||
*/
|
||||
bool storage_list_dir(
|
||||
Storage* storage,
|
||||
const char* path,
|
||||
FuriString*** files,
|
||||
size_t* num_files,
|
||||
bool ignore_dirs,
|
||||
const char** include_ext,
|
||||
size_t ext_count);
|
||||
|
||||
/******************* Common Functions *******************/
|
||||
|
||||
/**
|
||||
|
||||
@@ -440,6 +440,79 @@ bool storage_dir_exists(Storage* storage, const char* path) {
|
||||
|
||||
return exist;
|
||||
}
|
||||
|
||||
bool storage_list_dir(
|
||||
Storage* storage,
|
||||
const char* path,
|
||||
FuriString*** files,
|
||||
size_t* num_files,
|
||||
bool ignore_dirs,
|
||||
const char** include_ext,
|
||||
size_t ext_count) {
|
||||
furi_check(storage);
|
||||
furi_check(path);
|
||||
furi_check(files);
|
||||
furi_check(num_files);
|
||||
|
||||
FileInfo fileinfo;
|
||||
bool result = false;
|
||||
char* name = malloc(MAX_NAME_LENGTH);
|
||||
File* dir = storage_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!storage_dir_open(dir, path)) break;
|
||||
|
||||
while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
|
||||
if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;
|
||||
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
if(!ignore_dirs) {
|
||||
*files = realloc(*files, (*num_files + 1) * sizeof(FuriString*));
|
||||
(*files)[*num_files] = furi_string_alloc_printf("%s/%s", path, name);
|
||||
(*num_files)++;
|
||||
}
|
||||
FuriString* subpath = furi_string_alloc_printf("%s/%s", path, name);
|
||||
storage_list_dir(
|
||||
storage,
|
||||
furi_string_get_cstr(subpath),
|
||||
files,
|
||||
num_files,
|
||||
ignore_dirs,
|
||||
include_ext,
|
||||
ext_count);
|
||||
furi_string_free(subpath);
|
||||
} else {
|
||||
bool add_file = (include_ext == NULL || ext_count == 0);
|
||||
|
||||
if(!add_file) {
|
||||
const char* ext = strrchr(name, '.');
|
||||
if(ext != NULL) {
|
||||
for(size_t i = 0; i < ext_count; i++) {
|
||||
if(strcasecmp(ext, include_ext[i]) == 0) {
|
||||
add_file = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(add_file) {
|
||||
*files = realloc(*files, (*num_files + 1) * sizeof(FuriString*));
|
||||
(*files)[*num_files] = furi_string_alloc_printf("%s/%s", path, name);
|
||||
(*num_files)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
storage_dir_close(dir);
|
||||
storage_file_free(dir);
|
||||
free(name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************** COMMON ******************/
|
||||
|
||||
FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp) {
|
||||
|
||||
@@ -3333,6 +3333,7 @@ Function,+,storage_get_next_filename,void,"Storage*, const char*, const char*, c
|
||||
Function,+,storage_get_pubsub,FuriPubSub*,Storage*
|
||||
Function,+,storage_int_backup,FS_Error,"Storage*, const char*"
|
||||
Function,+,storage_int_restore,FS_Error,"Storage*, const char*, StorageNameConverter"
|
||||
Function,+,storage_list_dir,_Bool,"Storage*, const char*, FuriString***, size_t*, _Bool, const char**, size_t"
|
||||
Function,+,storage_sd_format,FS_Error,Storage*
|
||||
Function,+,storage_sd_info,FS_Error,"Storage*, SDInfo*"
|
||||
Function,+,storage_sd_mount,FS_Error,Storage*
|
||||
@@ -3836,6 +3837,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, 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*"
|
||||
|
||||
|
Reference in New Issue
Block a user