Merge remote-tracking branch '956MB:feat/asset-packs-ram-warning' into 956/prs #344

This commit is contained in:
956MB
2026-01-02 07:27:59 -06:00
10 changed files with 328 additions and 9 deletions
@@ -322,6 +322,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;
@@ -450,6 +454,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;
}
@@ -0,0 +1,64 @@
#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, 22, AlignCenter, AlignCenter, "\e#Size Warning\e#", false);
widget_add_string_multiline_element(
app->widget,
64,
33,
AlignCenter,
AlignCenter,
FontSecondary,
"Your selected pack contains\n"
"Fonts & Icons, which remain\n"
"loaded and use up memory.");
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);
}
@@ -0,0 +1,114 @@
#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_file_list_element(app->widget, 0, 12, 4, paths, num_paths, 0, 52, true);
widget_add_string_element(
app->widget, 35, 57, AlignLeft, AlignCenter, FontPrimary, furi_string_get_cstr(title));
furi_string_free(title);
furi_string_free(path);
for(size_t i = 0; i < num_paths; i++) {
furi_string_free(paths[i]);
}
free(paths);
furi_record_close(RECORD_STORAGE);
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);
}
@@ -90,9 +90,9 @@ static void widget_element_file_list_draw(Canvas* canvas, WidgetElement* element
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;
i == 0 && model->count > model->lines ?
(scroll_counter < SCROLL_DELAY ? 0 : scroll_counter - SCROLL_DELAY) :
0;
elements_scrollable_text_line(
canvas,
+21
View File
@@ -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) {
+1
View File
@@ -3502,6 +3502,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*
1 entry status name type params
3502 Function + storage_get_pubsub FuriPubSub* Storage*
3503 Function + storage_int_backup FS_Error Storage*, const char*
3504 Function + storage_int_restore FS_Error Storage*, const char*, StorageNameConverter
3505 Function + storage_list_dir _Bool Storage*, const char*, FuriString***, size_t*, _Bool, const char**, size_t
3506 Function + storage_sd_format FS_Error Storage*
3507 Function + storage_sd_info FS_Error Storage*, SDInfo*
3508 Function + storage_sd_mount FS_Error Storage*