feat: architectural improvements for Apps and System Configuration virtual folders in Files

This commit is contained in:
2026-04-30 16:51:08 +02:00
parent b4be0fcc0f
commit f66c9bf1f7
15 changed files with 231 additions and 206 deletions
@@ -158,7 +158,7 @@ void filemanager_open_ctx_menu(FileManagerState* fm, int local_x, int local_y, i
int tt = fm->entry_types[target_idx];
if ((filemanager_is_computer_view(fm)
&& (tt == FM_ENTRY_DRIVE || tt == FM_ENTRY_HOME || tt == FM_ENTRY_APPS_ROOT
|| tt == FM_ENTRY_SPECIAL_DIR || tt == FM_ENTRY_SYSTEM_TOOLS_ROOT))
|| tt == FM_ENTRY_SPECIAL_DIR || tt == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT))
|| (filemanager_is_virtual_view(fm) && tt == FM_ENTRY_VIRTUAL_APP)) {
// Drive, Home, Apps shortcut, or app entry: only Open
fm->ctx_items[fm->ctx_item_count++] = CTX_OPEN;
+14 -5
View File
@@ -8,11 +8,13 @@
namespace filemanager {
static constexpr const char* FM_PATH_SYSTEM_CONFIGURATION = "virtual://system-configuration";
static bool filemanager_open_virtual_path(FileManagerState* fm, const char* path) {
if (!fm || !path) return false;
if (montauk::streq(path, FILEMANAGER_PATH_SYSTEM_CONFIGURATION)) {
filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_TOOLS);
if (montauk::streq(path, FM_PATH_SYSTEM_CONFIGURATION)) {
filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION);
return true;
}
@@ -74,9 +76,12 @@ void ensure_filemanager_icons_loaded(DesktopState* ds) {
ds->icon_home_folder_lg = svg_load("0:/icons/folder-blue-home.svg", 48, 48, defColor);
ds->icon_apps = svg_load("0:/icons/folder-blue-development.svg", 16, 16, defColor);
ds->icon_apps_lg = svg_load("0:/icons/folder-blue-development.svg", 48, 48, defColor);
ds->icon_system_tools_menu = svg_load("0:/icons/preferences-system.svg", 20, 20, defColor);
ds->icon_system_tools = svg_load("0:/icons/preferences-system.svg", 16, 16, defColor);
ds->icon_system_tools_lg = svg_load("0:/icons/preferences-system.svg", 48, 48, defColor);
ds->icon_system_configuration_menu =
svg_load("0:/icons/preferences-system.svg", 20, 20, defColor);
ds->icon_system_configuration =
svg_load("0:/icons/preferences-system.svg", 16, 16, defColor);
ds->icon_system_configuration_lg =
svg_load("0:/icons/preferences-system.svg", 48, 48, defColor);
for (int sf = 0; sf < filemanager::SF_COUNT; sf++) {
char icon_path[128];
@@ -99,3 +104,7 @@ void open_filemanager(DesktopState* ds) {
void open_filemanager_path(DesktopState* ds, const char* path) {
filemanager::open_filemanager_internal(ds, path);
}
void open_system_configuration(DesktopState* ds) {
filemanager::open_filemanager_internal(ds, filemanager::FM_PATH_SYSTEM_CONFIGURATION);
}
@@ -21,20 +21,19 @@ enum FileManagerEntryType : int {
FM_ENTRY_APPS_ROOT = 5,
FM_ENTRY_VIRTUAL_APP = 6,
FM_ENTRY_SPECIAL_DIR = 7,
FM_ENTRY_SYSTEM_TOOLS_ROOT = 8,
FM_ENTRY_SYSTEM_CONFIGURATION_ROOT = 8,
};
enum FileManagerVirtualViewKind : uint8_t {
FM_VIRTUAL_VIEW_NONE = 0,
FM_VIRTUAL_VIEW_APPS,
FM_VIRTUAL_VIEW_SYSTEM_TOOLS,
FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION,
};
enum FileManagerVirtualEntryKind : uint8_t {
FM_VIRTUAL_ENTRY_NONE = 0,
FM_VIRTUAL_ENTRY_EXTERNAL_APP,
FM_VIRTUAL_ENTRY_BUILTIN,
FM_VIRTUAL_ENTRY_SHARED_APPLET,
FM_VIRTUAL_ENTRY_SYSTEM_CONFIGURATION_APPLET,
};
struct FileManagerLocation {
@@ -45,8 +44,7 @@ struct FileManagerLocation {
struct FileManagerVirtualEntry {
FileManagerVirtualEntryKind kind;
int external_app_index;
DesktopBuiltinId builtin_id;
char shared_applet_path[128];
DesktopAppletEntry applet;
SvgIcon icon_lg;
SvgIcon icon_sm;
};
@@ -147,7 +145,7 @@ inline bool filemanager_is_computer_view(const FileManagerState* fm) {
inline const char* filemanager_virtual_view_label(FileManagerVirtualViewKind view) {
switch (view) {
case FM_VIRTUAL_VIEW_APPS: return "Apps";
case FM_VIRTUAL_VIEW_SYSTEM_TOOLS: return "System Configuration";
case FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION: return "System Configuration";
default: return "Computer";
}
}
@@ -173,7 +171,7 @@ void filemanager_read_dir(FileManagerState* fm);
void filemanager_free_app_icons(FileManagerState* fm);
void filemanager_read_virtual_view(FileManagerState* fm, FileManagerVirtualViewKind view);
void filemanager_read_apps(FileManagerState* fm);
void filemanager_read_system_tools(FileManagerState* fm);
void filemanager_read_system_configuration(FileManagerState* fm);
bool filemanager_delete_recursive(const char* path);
bool filemanager_copy_file(const char* src, const char* dst);
@@ -5,18 +5,9 @@
*/
#include "filemanager_internal.hpp"
#include <montauk/toml.h>
namespace filemanager {
static int filemanager_find_external_app(DesktopState* ds, const char* bin_path) {
if (!ds || !bin_path) return -1;
for (int x = 0; x < ds->external_app_count; x++) {
if (montauk::streq(ds->external_apps[x].binary_path, bin_path)) return x;
}
return -1;
}
static void filemanager_reset_list_state(FileManagerState* fm) {
fm->selected = -1;
fm->scroll_offset = 0;
@@ -29,8 +20,9 @@ static void filemanager_reset_virtual_entry(FileManagerVirtualEntry* entry) {
if (!entry) return;
entry->kind = FM_VIRTUAL_ENTRY_NONE;
entry->external_app_index = -1;
entry->builtin_id = DESKTOP_BUILTIN_NONE;
entry->shared_applet_path[0] = '\0';
montauk::memset(&entry->applet, 0, sizeof(entry->applet));
entry->applet.kind = DESKTOP_APPLET_NONE;
entry->applet.builtin_id = DESKTOP_BUILTIN_NONE;
entry->icon_lg = {};
entry->icon_sm = {};
}
@@ -49,14 +41,11 @@ static void filemanager_add_root_entry(FileManagerState* fm,
fm->drive_indices[idx] = drive_index;
}
static void filemanager_add_virtual_entry(FileManagerState* fm,
const char* name,
const char* icon_path,
FileManagerVirtualEntryKind kind,
int external_index,
DesktopBuiltinId builtin_id,
const char* shared_applet_path) {
if (!fm || fm->entry_count >= 64) return;
static int filemanager_add_virtual_entry(FileManagerState* fm,
const char* name,
const char* icon_path,
FileManagerVirtualEntryKind kind) {
if (!fm || fm->entry_count >= 64) return -1;
int idx = fm->entry_count;
montauk::strncpy(fm->entry_names[idx], name, 63);
@@ -66,13 +55,6 @@ static void filemanager_add_virtual_entry(FileManagerState* fm,
fm->drive_indices[idx] = -1;
filemanager_reset_virtual_entry(&fm->virtual_entries[idx]);
fm->virtual_entries[idx].kind = kind;
fm->virtual_entries[idx].external_app_index = external_index;
fm->virtual_entries[idx].builtin_id = builtin_id;
if (shared_applet_path && shared_applet_path[0]) {
montauk::strncpy(fm->virtual_entries[idx].shared_applet_path,
shared_applet_path,
sizeof(fm->virtual_entries[idx].shared_applet_path));
}
if (icon_path && icon_path[0]) {
Color defColor = colors::ICON_COLOR;
@@ -82,6 +64,27 @@ static void filemanager_add_virtual_entry(FileManagerState* fm,
fm->entry_count++;
fm->virtual_entry_count = fm->entry_count;
return idx;
}
static void filemanager_add_external_app_entry(FileManagerState* fm,
const ExternalApp& app,
int external_index) {
int idx = filemanager_add_virtual_entry(fm, app.name, app.icon_path,
FM_VIRTUAL_ENTRY_EXTERNAL_APP);
if (idx >= 0) {
fm->virtual_entries[idx].external_app_index = external_index;
}
}
static void filemanager_add_system_configuration_applet_entry(
FileManagerState* fm,
const DesktopAppletEntry& applet) {
int idx = filemanager_add_virtual_entry(fm, applet.label, applet.icon_path,
FM_VIRTUAL_ENTRY_SYSTEM_CONFIGURATION_APPLET);
if (idx >= 0) {
fm->virtual_entries[idx].applet = applet;
}
}
static void filemanager_begin_virtual_view(FileManagerState* fm, FileManagerVirtualViewKind view) {
@@ -126,7 +129,8 @@ void filemanager_read_drives(FileManagerState* fm) {
}
// System configuration entry
filemanager_add_root_entry(fm, "System Configuration", FM_ENTRY_SYSTEM_TOOLS_ROOT, -1);
filemanager_add_root_entry(fm, "System Configuration",
FM_ENTRY_SYSTEM_CONFIGURATION_ROOT, -1);
// Drive entries
int drives[FM_MAX_DRIVES];
@@ -292,7 +296,9 @@ void filemanager_free_app_icons(FileManagerState* fm) {
void filemanager_read_virtual_view(FileManagerState* fm, FileManagerVirtualViewKind view) {
switch (view) {
case FM_VIRTUAL_VIEW_APPS: filemanager_read_apps(fm); break;
case FM_VIRTUAL_VIEW_SYSTEM_TOOLS: filemanager_read_system_tools(fm); break;
case FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION:
filemanager_read_system_configuration(fm);
break;
default: filemanager_read_drives(fm); break;
}
}
@@ -303,86 +309,20 @@ void filemanager_read_apps(FileManagerState* fm) {
DesktopState* ds = fm->desktop;
if (!ds) return;
// Scan manifests directly (like desktop_scan_apps but load icons at FM sizes)
montauk::fmkdir("0:/apps");
const char* entries[32];
int count = montauk::readdir("0:/apps", entries, 32);
// Extract basenames from readdir results (strip "apps/" prefix)
/*
TODO: Fix crash if 16-app ceiling is removed and remove ceiling
*/
for (int i = 0; i < count && fm->entry_count < 64; i++) {
const char* raw = entries[i];
// Strip "apps/" prefix
if (raw[0] == 'a' && raw[1] == 'p' && raw[2] == 'p' && raw[3] == 's' && raw[4] == '/')
raw += 5;
char dirname[64];
montauk::strncpy(dirname, raw, 63);
int dlen = montauk::slen(dirname);
if (dlen > 0 && dirname[dlen - 1] == '/') dirname[dlen - 1] = '\0';
if (dirname[0] == '\0') continue;
// Open manifest
char manifest_path[128];
snprintf(manifest_path, 128, "0:/apps/%s/manifest.toml", dirname);
int fh = montauk::open(manifest_path);
if (fh < 0) continue;
uint64_t sz = montauk::getsize(fh);
if (sz == 0 || sz > 4096) { montauk::close(fh); continue; }
char* text = (char*)montauk::malloc(sz + 1);
montauk::read(fh, (uint8_t*)text, 0, sz);
montauk::close(fh);
text[sz] = '\0';
auto doc = montauk::toml::parse(text);
montauk::mfree(text);
const char* name = doc.get_string("app.name", "Unknown");
const char* binary = doc.get_string("app.binary", "");
const char* icon_file = doc.get_string("app.icon", "");
char bin_path[128];
snprintf(bin_path, 128, "0:/apps/%s/%s", dirname, binary);
char icon_path[128];
icon_path[0] = '\0';
if (icon_file[0]) {
snprintf(icon_path, 128, "0:/apps/%s/%s", dirname, icon_file);
}
filemanager_add_virtual_entry(fm, name, icon_path,
FM_VIRTUAL_ENTRY_EXTERNAL_APP,
filemanager_find_external_app(ds, bin_path),
DESKTOP_BUILTIN_NONE, nullptr);
doc.destroy();
for (int i = 0; i < ds->external_app_count && fm->entry_count < 64; i++) {
filemanager_add_external_app_entry(fm, ds->external_apps[i], i);
}
filemanager_reset_list_state(fm);
}
void filemanager_read_system_tools(FileManagerState* fm) {
filemanager_begin_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_TOOLS);
void filemanager_read_system_configuration(FileManagerState* fm) {
filemanager_begin_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION);
int builtin_count = 0;
const DesktopBuiltinEntry* builtins = desktop_builtin_registry(&builtin_count);
for (int i = 0; i < builtin_count; i++) {
if (!builtins[i].system_tools_applet) continue;
filemanager_add_virtual_entry(fm, builtins[i].label, builtins[i].icon_path,
FM_VIRTUAL_ENTRY_BUILTIN,
-1, builtins[i].id, nullptr);
}
DesktopSharedAppletEntry applets[32];
DesktopAppletEntry applets[32];
int applet_count = desktop_list_system_configuration_applets(fm->desktop, applets, 32);
for (int i = 0; i < applet_count && fm->entry_count < 64; i++) {
filemanager_add_virtual_entry(fm, applets[i].label, applets[i].icon_path,
FM_VIRTUAL_ENTRY_SHARED_APPLET,
-1, DESKTOP_BUILTIN_NONE,
applets[i].library_path);
filemanager_add_system_configuration_applet_entry(fm, applets[i]);
}
filemanager_reset_list_state(fm);
@@ -188,8 +188,9 @@ void filemanager_open_entry(FileManagerState* fm, int idx) {
return;
}
if (filemanager_is_computer_view(fm) && fm->entry_types[idx] == FM_ENTRY_SYSTEM_TOOLS_ROOT) {
filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_TOOLS);
if (filemanager_is_computer_view(fm)
&& fm->entry_types[idx] == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT) {
filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION);
filemanager_push_history(fm);
return;
}
@@ -197,11 +198,6 @@ void filemanager_open_entry(FileManagerState* fm, int idx) {
if (filemanager_is_virtual_view(fm) && fm->entry_types[idx] == FM_ENTRY_VIRTUAL_APP) {
DesktopState* ds = fm->desktop;
const FileManagerVirtualEntry& entry = fm->virtual_entries[idx];
if (ds && entry.kind == FM_VIRTUAL_ENTRY_BUILTIN
&& entry.builtin_id != DESKTOP_BUILTIN_NONE) {
desktop_launch_builtin(ds, entry.builtin_id);
return;
}
if (ds && entry.kind == FM_VIRTUAL_ENTRY_EXTERNAL_APP
&& entry.external_app_index >= 0
&& entry.external_app_index < ds->external_app_count) {
@@ -211,9 +207,8 @@ void filemanager_open_entry(FileManagerState* fm, int idx) {
} else {
montauk::spawn(app.binary_path);
}
} else if (ds && entry.kind == FM_VIRTUAL_ENTRY_SHARED_APPLET
&& entry.shared_applet_path[0] != '\0') {
desktop_launch_shared_applet(ds, entry.shared_applet_path);
} else if (ds && entry.kind == FM_VIRTUAL_ENTRY_SYSTEM_CONFIGURATION_APPLET) {
desktop_launch_system_configuration_applet(ds, &entry.applet);
}
return;
}
@@ -324,9 +324,9 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
&& i < fm->virtual_entry_count
&& fm->virtual_entries[i].icon_lg.pixels) {
c.icon(icon_x, icon_y, fm->virtual_entries[i].icon_lg);
} else if (ds && fm->entry_types[i] == FM_ENTRY_SYSTEM_TOOLS_ROOT
&& ds->icon_system_tools_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_system_tools_lg);
} else if (ds && fm->entry_types[i] == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT
&& ds->icon_system_configuration_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_system_configuration_lg);
} else if (ds && fm->entry_types[i] == FM_ENTRY_APPS_ROOT && ds->icon_apps_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_apps_lg);
} else if (ds && fm->entry_types[i] == FM_ENTRY_HOME && ds->icon_home_folder_lg.pixels) {
@@ -455,9 +455,9 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
&& i < fm->virtual_entry_count
&& fm->virtual_entries[i].icon_sm.pixels) {
c.icon(ico_x, ico_y, fm->virtual_entries[i].icon_sm);
} else if (ds && fm->entry_types[i] == FM_ENTRY_SYSTEM_TOOLS_ROOT
&& ds->icon_system_tools.pixels) {
c.icon(ico_x, ico_y, ds->icon_system_tools);
} else if (ds && fm->entry_types[i] == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT
&& ds->icon_system_configuration.pixels) {
c.icon(ico_x, ico_y, ds->icon_system_configuration);
} else if (ds && fm->entry_types[i] == FM_ENTRY_APPS_ROOT && ds->icon_apps.pixels) {
c.icon(ico_x, ico_y, ds->icon_apps);
} else if (ds && fm->entry_types[i] == FM_ENTRY_HOME && ds->icon_home_folder.pixels) {
@@ -537,9 +537,13 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
// Type
if (type_col_x > 160 && ty >= list_y && ty + fm_sfh <= c.h) {
const char* type_str = "File";
if (fm->entry_types[i] == FM_ENTRY_VIRTUAL_APP) type_str = "App";
if (fm->entry_types[i] == FM_ENTRY_VIRTUAL_APP) {
type_str = fm->virtual_view == FM_VIRTUAL_VIEW_SYSTEM_CONFIGURATION
? "Applet"
: "App";
}
else if (fm->entry_types[i] == FM_ENTRY_APPS_ROOT) type_str = "Apps";
else if (fm->entry_types[i] == FM_ENTRY_SYSTEM_TOOLS_ROOT) type_str = "System";
else if (fm->entry_types[i] == FM_ENTRY_SYSTEM_CONFIGURATION_ROOT) type_str = "System";
else if (fm->entry_types[i] == FM_ENTRY_HOME) type_str = "Home";
else if (fm->entry_types[i] == FM_ENTRY_DRIVE) type_str = "Drive";
else if (fm->entry_types[i] == FM_ENTRY_DIR) type_str = "Dir";