feat: system settings stored as applets in virtual folder

This commit is contained in:
2026-04-23 15:36:03 +02:00
parent 348ff6981b
commit d912a636e9
21 changed files with 747 additions and 359 deletions
@@ -10,7 +10,8 @@ namespace filemanager {
void filemanager_do_copy(FileManagerState* fm) {
if (fm->selected < 0 || fm->selected >= fm->entry_count) return;
if (fm->at_drives_root || fm->entry_types[fm->selected] == 3) return;
if (filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm) ||
fm->entry_types[fm->selected] == FM_ENTRY_DRIVE) return;
filemanager_build_fullpath(fm->clipboard_path, 256,
fm->current_path, fm->entry_names[fm->selected]);
@@ -20,7 +21,8 @@ void filemanager_do_copy(FileManagerState* fm) {
void filemanager_do_cut(FileManagerState* fm) {
if (fm->selected < 0 || fm->selected >= fm->entry_count) return;
if (fm->at_drives_root || fm->entry_types[fm->selected] == 3) return;
if (filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm) ||
fm->entry_types[fm->selected] == FM_ENTRY_DRIVE) return;
filemanager_build_fullpath(fm->clipboard_path, 256,
fm->current_path, fm->entry_names[fm->selected]);
@@ -29,7 +31,7 @@ void filemanager_do_cut(FileManagerState* fm) {
}
void filemanager_do_paste(FileManagerState* fm) {
if (!fm->clipboard_has_data || fm->at_drives_root) return;
if (!fm->clipboard_has_data || filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm)) return;
const char* basename = path_basename(fm->clipboard_path);
char dst[512];
@@ -59,7 +61,8 @@ void filemanager_do_paste(FileManagerState* fm) {
void filemanager_start_rename(FileManagerState* fm) {
if (fm->selected < 0 || fm->selected >= fm->entry_count) return;
if (fm->at_drives_root || fm->entry_types[fm->selected] == 3) return;
if (filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm) ||
fm->entry_types[fm->selected] == FM_ENTRY_DRIVE) return;
fm->rename_active = true;
fm->rename_idx = fm->selected;
@@ -100,7 +103,7 @@ void filemanager_cancel_rename(FileManagerState* fm) {
}
void filemanager_new_folder(FileManagerState* fm) {
if (fm->at_drives_root) return;
if (filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm)) return;
// Find a unique name
char name[64] = "New Folder";
@@ -130,7 +133,8 @@ void filemanager_new_folder(FileManagerState* fm) {
void filemanager_delete_selected(FileManagerState* fm) {
if (fm->selected < 0 || fm->selected >= fm->entry_count) return;
if (fm->at_drives_root || fm->entry_types[fm->selected] == 3) return;
if (filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm) ||
fm->entry_types[fm->selected] == FM_ENTRY_DRIVE) return;
char fullpath[512];
filemanager_build_fullpath(fullpath, 512,
@@ -152,8 +156,10 @@ void filemanager_open_ctx_menu(FileManagerState* fm, int local_x, int local_y, i
if (target_idx >= 0 && target_idx < fm->entry_count) {
int tt = fm->entry_types[target_idx];
if ((fm->at_drives_root && (tt == 3 || tt == 4 || tt == 5 || tt == 7)) ||
(fm->at_apps_view && tt == 6)) {
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))
|| (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;
} else {
@@ -165,6 +171,10 @@ void filemanager_open_ctx_menu(FileManagerState* fm, int local_x, int local_y, i
}
} else {
// Background click
if (filemanager_is_virtual_view(fm)) {
fm->ctx_open = false;
return;
}
if (fm->clipboard_has_data)
fm->ctx_items[fm->ctx_item_count++] = CTX_PASTE;
fm->ctx_items[fm->ctx_item_count++] = CTX_NEW_FOLDER;
+24 -6
View File
@@ -8,6 +8,17 @@
namespace filemanager {
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);
return true;
}
return false;
}
static void open_filemanager_internal(DesktopState* ds, const char* initial_path) {
int idx = desktop_create_window(ds, "Files", 150, 120, 560, 420);
if (idx < 0) return;
@@ -27,13 +38,17 @@ static void open_filemanager_internal(DesktopState* ds, const char* initial_path
ensure_filemanager_icons_loaded(ds);
if (initial_path && initial_path[0] != '\0') {
int probe_fd = montauk::open(initial_path);
if (probe_fd >= 0) {
montauk::close(probe_fd);
montauk::strncpy(fm->current_path, initial_path, sizeof(fm->current_path));
filemanager_read_dir(fm);
if (filemanager_open_virtual_path(fm, initial_path)) {
// Virtual view already loaded.
} else {
filemanager_read_drives(fm);
int probe_fd = montauk::open(initial_path);
if (probe_fd >= 0) {
montauk::close(probe_fd);
montauk::strncpy(fm->current_path, initial_path, sizeof(fm->current_path));
filemanager_read_dir(fm);
} else {
filemanager_read_drives(fm);
}
}
} else {
filemanager_read_drives(fm);
@@ -59,6 +74,9 @@ 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);
for (int sf = 0; sf < filemanager::SF_COUNT; sf++) {
char icon_path[128];
@@ -12,13 +12,43 @@ namespace filemanager {
inline constexpr int FM_MAX_DRIVES = 16;
enum FileManagerEntryType : int {
FM_ENTRY_FILE = 0,
FM_ENTRY_DIR = 1,
FM_ENTRY_EXEC = 2,
FM_ENTRY_DRIVE = 3,
FM_ENTRY_HOME = 4,
FM_ENTRY_APPS_ROOT = 5,
FM_ENTRY_VIRTUAL_APP = 6,
FM_ENTRY_SPECIAL_DIR = 7,
FM_ENTRY_SYSTEM_TOOLS_ROOT = 8,
};
enum FileManagerVirtualViewKind : uint8_t {
FM_VIRTUAL_VIEW_NONE = 0,
FM_VIRTUAL_VIEW_APPS,
FM_VIRTUAL_VIEW_SYSTEM_TOOLS,
};
struct FileManagerLocation {
char path[256];
FileManagerVirtualViewKind virtual_view;
};
struct FileManagerVirtualEntry {
int external_app_index;
DesktopBuiltinId builtin_id;
SvgIcon icon_lg;
SvgIcon icon_sm;
};
struct FileManagerState {
char current_path[256];
char history[16][256];
FileManagerLocation history[16];
int history_pos;
int history_count;
char entry_names[64][128];
int entry_types[64]; // 0=file, 1=dir, 2=exec, 3=drive, 4=home, 5=apps, 6=app, 7=special_dir
int entry_types[64];
int entry_sizes[64];
int entry_count;
int selected;
@@ -29,8 +59,7 @@ struct FileManagerState {
Scrollbar scrollbar;
DesktopState* desktop;
bool grid_view;
bool at_drives_root;
int drive_indices[FM_MAX_DRIVES]; // which drive number each entry maps to
int drive_indices[64]; // drive number or special-folder index for Computer view entries
// Clipboard
char clipboard_path[256];
@@ -58,12 +87,10 @@ struct FileManagerState {
int pathbar_cursor;
int pathbar_len;
// Apps view
bool at_apps_view;
int app_map[64]; // entry index -> external_apps[] index
SvgIcon app_icons_lg[64]; // 48x48 icons for grid view
SvgIcon app_icons_sm[64]; // 16x16 icons for list view
int app_icon_count;
// Virtual views
FileManagerVirtualViewKind virtual_view;
FileManagerVirtualEntry virtual_entries[64];
int virtual_entry_count;
};
inline constexpr int FM_TOOLBAR_H = 32;
@@ -100,6 +127,22 @@ inline constexpr int CTX_NEW_FOLDER = 6;
inline constexpr int CTX_MENU_W = 140;
inline constexpr int CTX_ITEM_H = 24;
inline bool filemanager_is_virtual_view(const FileManagerState* fm) {
return fm && fm->virtual_view != FM_VIRTUAL_VIEW_NONE;
}
inline bool filemanager_is_computer_view(const FileManagerState* fm) {
return fm && !filemanager_is_virtual_view(fm) && fm->current_path[0] == '\0';
}
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";
default: return "Computer";
}
}
int special_folder_index(const char* name);
const char* ctx_label(int action);
@@ -119,7 +162,9 @@ const char* path_basename(const char* path);
void filemanager_read_drives(FileManagerState* fm);
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);
bool filemanager_delete_recursive(const char* path);
bool filemanager_copy_file(const char* src, const char* dst);
@@ -9,33 +9,92 @@
namespace filemanager {
void filemanager_read_drives(FileManagerState* fm) {
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;
fm->scrollbar.scroll_offset = 0;
fm->last_click_item = -1;
fm->last_click_time = 0;
}
static void filemanager_reset_virtual_entry(FileManagerVirtualEntry* entry) {
if (!entry) return;
entry->external_app_index = -1;
entry->builtin_id = DESKTOP_BUILTIN_NONE;
entry->icon_lg = {};
entry->icon_sm = {};
}
static void filemanager_add_root_entry(FileManagerState* fm,
const char* name,
FileManagerEntryType type,
int drive_index) {
if (!fm || fm->entry_count >= 64) return;
int idx = fm->entry_count++;
montauk::strncpy(fm->entry_names[idx], name, 63);
fm->entry_types[idx] = type;
fm->entry_sizes[idx] = 0;
fm->is_dir[idx] = true;
fm->drive_indices[idx] = drive_index;
}
static void filemanager_add_virtual_entry(FileManagerState* fm,
const char* name,
const char* icon_path,
int external_index,
DesktopBuiltinId builtin_id) {
if (!fm || fm->entry_count >= 64) return;
int idx = fm->entry_count;
montauk::strncpy(fm->entry_names[idx], name, 63);
fm->entry_types[idx] = FM_ENTRY_VIRTUAL_APP;
fm->entry_sizes[idx] = 0;
fm->is_dir[idx] = false;
fm->drive_indices[idx] = -1;
filemanager_reset_virtual_entry(&fm->virtual_entries[idx]);
fm->virtual_entries[idx].external_app_index = external_index;
fm->virtual_entries[idx].builtin_id = builtin_id;
if (icon_path && icon_path[0]) {
Color defColor = colors::ICON_COLOR;
fm->virtual_entries[idx].icon_lg = svg_load(icon_path, 48, 48, defColor);
fm->virtual_entries[idx].icon_sm = svg_load(icon_path, 16, 16, defColor);
}
fm->entry_count++;
fm->virtual_entry_count = fm->entry_count;
}
static void filemanager_begin_virtual_view(FileManagerState* fm, FileManagerVirtualViewKind view) {
filemanager_free_app_icons(fm);
fm->current_path[0] = '\0';
fm->virtual_view = view;
fm->entry_count = 0;
fm->at_drives_root = true;
}
void filemanager_read_drives(FileManagerState* fm) {
filemanager_free_app_icons(fm);
fm->entry_count = 0;
fm->virtual_view = FM_VIRTUAL_VIEW_NONE;
fm->current_path[0] = '\0';
// Home folder entry (if we have a valid home directory)
DesktopState* ds = fm->desktop;
if (ds && ds->home_dir[0] != '\0') {
int i = fm->entry_count;
montauk::strncpy(fm->entry_names[i], "Home", 63);
fm->entry_types[i] = 4; // home
fm->entry_sizes[i] = 0;
fm->is_dir[i] = true;
fm->drive_indices[i] = -1;
fm->entry_count++;
filemanager_add_root_entry(fm, "Home", FM_ENTRY_HOME, -1);
}
// Apps entry
{
int i = fm->entry_count;
montauk::strncpy(fm->entry_names[i], "Apps", 63);
fm->entry_types[i] = 5; // apps
fm->entry_sizes[i] = 0;
fm->is_dir[i] = true;
fm->drive_indices[i] = -1;
fm->entry_count++;
}
filemanager_add_root_entry(fm, "Apps", FM_ENTRY_APPS_ROOT, -1);
// Special user folders (Documents, Desktop, Music, etc.) - only if they exist
if (ds && ds->home_dir[0] != '\0') {
@@ -51,24 +110,20 @@ void filemanager_read_drives(FileManagerState* fm) {
int probe_fd = montauk::open(probe_path);
if (probe_fd >= 0) {
montauk::close(probe_fd);
int i = fm->entry_count;
montauk::strncpy(fm->entry_names[i], sf_names[sf], 63);
fm->entry_types[i] = 7; // special_dir
fm->entry_sizes[i] = 0;
fm->is_dir[i] = true;
fm->drive_indices[i] = sf; // special folder index
fm->entry_count++;
filemanager_add_root_entry(fm, sf_names[sf], FM_ENTRY_SPECIAL_DIR, sf);
}
}
}
// System configuration entry
filemanager_add_root_entry(fm, "System Configuration", FM_ENTRY_SYSTEM_TOOLS_ROOT, -1);
// Drive entries
int drives[FM_MAX_DRIVES];
int driveCount = montauk::drivelist(drives, FM_MAX_DRIVES);
for (int di = 0; di < driveCount; di++) {
int d = drives[di];
int i = fm->entry_count;
char probe[8];
if (d < 10) {
probe[0] = '0' + d;
@@ -91,28 +146,22 @@ void filemanager_read_drives(FileManagerState* fm) {
str_append(label, volume_label, 64);
str_append(label, "]", 64);
}
montauk::strncpy(fm->entry_names[i], label, 63);
fm->entry_types[i] = 3; // drive
fm->entry_sizes[i] = 0;
fm->is_dir[i] = true;
fm->drive_indices[i] = d;
fm->entry_count++;
filemanager_add_root_entry(fm, label, FM_ENTRY_DRIVE, d);
if (fm->entry_count >= 64) break;
}
fm->selected = -1;
fm->scroll_offset = 0;
fm->scrollbar.scroll_offset = 0;
fm->last_click_item = -1;
fm->last_click_time = 0;
filemanager_reset_list_state(fm);
}
void filemanager_read_dir(FileManagerState* fm) {
fm->at_drives_root = false;
if (fm->at_apps_view) {
fm->at_apps_view = false;
filemanager_free_app_icons(fm);
if (fm->current_path[0] == '\0') {
filemanager_read_drives(fm);
return;
}
filemanager_free_app_icons(fm);
fm->virtual_view = FM_VIRTUAL_VIEW_NONE;
const char* names[64];
fm->entry_count = montauk::readdir(fm->current_path, names, 64);
if (fm->entry_count < 0) fm->entry_count = 0;
@@ -210,33 +259,36 @@ void filemanager_read_dir(FileManagerState* fm) {
fm->is_dir[j + 1] = tmp_isdir;
}
fm->selected = -1;
fm->scroll_offset = 0;
fm->scrollbar.scroll_offset = 0;
fm->last_click_item = -1;
fm->last_click_time = 0;
filemanager_reset_list_state(fm);
}
void filemanager_free_app_icons(FileManagerState* fm) {
for (int i = 0; i < fm->app_icon_count; i++) {
if (fm->app_icons_lg[i].pixels) {
montauk::mfree(fm->app_icons_lg[i].pixels);
fm->app_icons_lg[i].pixels = nullptr;
if (!fm) return;
for (int i = 0; i < fm->virtual_entry_count; i++) {
if (fm->virtual_entries[i].icon_lg.pixels) {
montauk::mfree(fm->virtual_entries[i].icon_lg.pixels);
fm->virtual_entries[i].icon_lg.pixels = nullptr;
}
if (fm->app_icons_sm[i].pixels) {
montauk::mfree(fm->app_icons_sm[i].pixels);
fm->app_icons_sm[i].pixels = nullptr;
if (fm->virtual_entries[i].icon_sm.pixels) {
montauk::mfree(fm->virtual_entries[i].icon_sm.pixels);
fm->virtual_entries[i].icon_sm.pixels = nullptr;
}
filemanager_reset_virtual_entry(&fm->virtual_entries[i]);
}
fm->virtual_entry_count = 0;
}
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;
default: filemanager_read_drives(fm); break;
}
fm->app_icon_count = 0;
}
void filemanager_read_apps(FileManagerState* fm) {
filemanager_free_app_icons(fm);
fm->at_drives_root = false;
fm->at_apps_view = true;
fm->entry_count = 0;
filemanager_begin_virtual_view(fm, FM_VIRTUAL_VIEW_APPS);
DesktopState* ds = fm->desktop;
if (!ds) return;
@@ -283,45 +335,36 @@ void filemanager_read_apps(FileManagerState* fm) {
const char* binary = doc.get_string("app.binary", "");
const char* icon_file = doc.get_string("app.icon", "");
int idx = fm->entry_count;
montauk::strncpy(fm->entry_names[idx], name, 63);
fm->entry_types[idx] = 6; // app entry
fm->entry_sizes[idx] = 0;
fm->is_dir[idx] = false;
// Store binary path in drive_indices as an app_map index
fm->app_map[idx] = -1;
// Find matching external_app by binary path
char bin_path[128];
snprintf(bin_path, 128, "0:/apps/%s/%s", dirname, binary);
for (int x = 0; x < ds->external_app_count; x++) {
if (montauk::streq(ds->external_apps[x].binary_path, bin_path)) {
fm->app_map[idx] = x;
break;
}
char icon_path[128];
icon_path[0] = '\0';
if (icon_file[0]) {
snprintf(icon_path, 128, "0:/apps/%s/%s", dirname, icon_file);
}
// Load icons at file manager sizes
fm->app_icons_lg[idx] = {};
fm->app_icons_sm[idx] = {};
if (icon_file[0]) {
char icon_path[128];
snprintf(icon_path, 128, "0:/apps/%s/%s", dirname, icon_file);
Color defColor = colors::ICON_COLOR;
fm->app_icons_lg[idx] = svg_load(icon_path, 48, 48, defColor);
fm->app_icons_sm[idx] = svg_load(icon_path, 16, 16, defColor);
}
filemanager_add_virtual_entry(fm, name, icon_path,
filemanager_find_external_app(ds, bin_path),
DESKTOP_BUILTIN_NONE);
doc.destroy();
fm->entry_count++;
fm->app_icon_count = fm->entry_count;
}
fm->selected = -1;
fm->scroll_offset = 0;
fm->scrollbar.scroll_offset = 0;
fm->last_click_item = -1;
fm->last_click_time = 0;
filemanager_reset_list_state(fm);
}
void filemanager_read_system_tools(FileManagerState* fm) {
filemanager_begin_virtual_view(fm, FM_VIRTUAL_VIEW_SYSTEM_TOOLS);
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,
-1, builtins[i].id);
}
filemanager_reset_list_state(fm);
}
bool filemanager_delete_recursive(const char* path) {
@@ -8,14 +8,54 @@
namespace filemanager {
void filemanager_push_history(FileManagerState* fm) {
// Don't push if same as current position
if (fm->history_count > 0 && fm->history_pos >= 0) {
if (montauk::streq(fm->history[fm->history_pos], fm->current_path)) return;
static void filemanager_store_location(FileManagerLocation* location,
const char* path,
FileManagerVirtualViewKind virtual_view) {
if (!location) return;
montauk::strncpy(location->path, path ? path : "", sizeof(location->path));
location->virtual_view = virtual_view;
}
static bool filemanager_location_matches_current(const FileManagerState* fm,
const FileManagerLocation& location) {
return fm
&& location.virtual_view == fm->virtual_view
&& montauk::streq(location.path, fm->current_path);
}
static void filemanager_restore_location(FileManagerState* fm, const FileManagerLocation& location) {
montauk::strncpy(fm->current_path, location.path, sizeof(fm->current_path));
if (location.virtual_view != FM_VIRTUAL_VIEW_NONE) {
filemanager_read_virtual_view(fm, location.virtual_view);
} else if (location.path[0] == '\0') {
filemanager_read_drives(fm);
} else {
filemanager_read_dir(fm);
}
}
void filemanager_push_history(FileManagerState* fm) {
if (!fm) return;
if (fm->history_count > 0 && fm->history_pos >= 0) {
if (filemanager_location_matches_current(fm, fm->history[fm->history_pos])) return;
}
if (fm->history_pos < fm->history_count - 1) {
fm->history_count = fm->history_pos + 1;
}
if (fm->history_count >= 16) {
for (int i = 1; i < 16; i++) {
fm->history[i - 1] = fm->history[i];
}
fm->history_count = 15;
fm->history_pos = 14;
}
fm->history_pos++;
if (fm->history_pos >= 16) fm->history_pos = 15;
montauk::strcpy(fm->history[fm->history_pos], fm->current_path);
filemanager_store_location(&fm->history[fm->history_pos], fm->current_path, fm->virtual_view);
fm->history_count = fm->history_pos + 1;
}
@@ -25,17 +65,14 @@ void filemanager_navigate(FileManagerState* fm, const char* name) {
str_append(fm->current_path, "/", 256);
}
str_append(fm->current_path, name, 256);
filemanager_push_history(fm);
filemanager_read_dir(fm);
filemanager_push_history(fm);
}
void filemanager_go_up(FileManagerState* fm) {
if (fm->at_drives_root) return;
if (filemanager_is_computer_view(fm)) return;
// From apps view, go back to Computer
if (fm->at_apps_view) {
fm->at_apps_view = false;
filemanager_free_app_icons(fm);
if (filemanager_is_virtual_view(fm)) {
filemanager_read_drives(fm);
filemanager_push_history(fm);
return;
@@ -46,7 +83,6 @@ void filemanager_go_up(FileManagerState* fm) {
// At drive root (e.g. "0:/" or "10:/") -- go to drives view
bool is_drive_root = false;
if (len >= 3 && fm->current_path[len - 1] == '/') {
// Check if path is just "N:/" or "NN:/"
int colon = -1;
for (int i = 0; i < len; i++) {
if (fm->current_path[i] == ':') { colon = i; break; }
@@ -69,24 +105,19 @@ void filemanager_go_up(FileManagerState* fm) {
if (fm->current_path[i] == '/') { last_slash = i; break; }
}
if (last_slash >= 0) {
// Keep the slash only if it's the root slash (right after "N:")
if (last_slash > 0 && fm->current_path[last_slash - 1] == ':') {
fm->current_path[last_slash + 1] = '\0';
} else {
fm->current_path[last_slash] = '\0';
}
}
filemanager_push_history(fm);
filemanager_read_dir(fm);
filemanager_push_history(fm);
}
void filemanager_navigate_to_history(FileManagerState* fm) {
montauk::strcpy(fm->current_path, fm->history[fm->history_pos]);
if (fm->current_path[0] == '\0') {
filemanager_read_drives(fm);
} else {
filemanager_read_dir(fm);
}
if (!fm || fm->history_pos < 0 || fm->history_pos >= fm->history_count) return;
filemanager_restore_location(fm, fm->history[fm->history_pos]);
}
void filemanager_go_back(FileManagerState* fm) {
@@ -102,17 +133,13 @@ void filemanager_go_forward(FileManagerState* fm) {
}
void filemanager_go_home(FileManagerState* fm) {
if (fm->at_apps_view) {
fm->at_apps_view = false;
filemanager_free_app_icons(fm);
}
filemanager_read_drives(fm);
filemanager_push_history(fm);
}
void filemanager_start_pathbar(FileManagerState* fm) {
fm->pathbar_editing = true;
if (fm->at_drives_root) {
if (filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm)) {
fm->pathbar_buf[0] = '\0';
fm->pathbar_len = 0;
} else {
@@ -134,15 +161,14 @@ void filemanager_commit_pathbar(FileManagerState* fm) {
return;
}
montauk::strcpy(fm->current_path, fm->pathbar_buf);
filemanager_push_history(fm);
filemanager_read_dir(fm);
filemanager_push_history(fm);
}
void filemanager_open_entry(FileManagerState* fm, int idx) {
if (idx < 0 || idx >= fm->entry_count) return;
// Special user folder in Computer view
if (fm->at_drives_root && fm->entry_types[idx] == 7) {
if (filemanager_is_computer_view(fm) && fm->entry_types[idx] == FM_ENTRY_SPECIAL_DIR) {
DesktopState* ds = fm->desktop;
if (ds && ds->home_dir[0] != '\0') {
montauk::strcpy(fm->current_path, ds->home_dir);
@@ -150,23 +176,33 @@ void filemanager_open_entry(FileManagerState* fm, int idx) {
if (plen > 0 && fm->current_path[plen - 1] != '/')
str_append(fm->current_path, "/", 256);
str_append(fm->current_path, fm->entry_names[idx], 256);
filemanager_push_history(fm);
filemanager_read_dir(fm);
filemanager_push_history(fm);
}
return;
}
// Apps shortcut in Computer view
if (fm->at_drives_root && fm->entry_types[idx] == 5) {
filemanager_read_apps(fm);
if (filemanager_is_computer_view(fm) && fm->entry_types[idx] == FM_ENTRY_APPS_ROOT) {
filemanager_read_virtual_view(fm, FM_VIRTUAL_VIEW_APPS);
filemanager_push_history(fm);
return;
}
// App entry in apps view - launch it
if (fm->at_apps_view && fm->entry_types[idx] == 6) {
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);
filemanager_push_history(fm);
return;
}
if (filemanager_is_virtual_view(fm) && fm->entry_types[idx] == FM_ENTRY_VIRTUAL_APP) {
DesktopState* ds = fm->desktop;
if (ds && fm->app_map[idx] >= 0 && fm->app_map[idx] < ds->external_app_count) {
const ExternalApp& app = ds->external_apps[fm->app_map[idx]];
const FileManagerVirtualEntry& entry = fm->virtual_entries[idx];
if (ds && entry.builtin_id != DESKTOP_BUILTIN_NONE) {
desktop_launch_builtin(ds, entry.builtin_id);
return;
}
if (ds && entry.external_app_index >= 0 && entry.external_app_index < ds->external_app_count) {
const ExternalApp& app = ds->external_apps[entry.external_app_index];
if (app.launch_with_home) {
montauk::spawn(app.binary_path, ds->home_dir);
} else {
@@ -176,18 +212,17 @@ void filemanager_open_entry(FileManagerState* fm, int idx) {
return;
}
if (fm->at_drives_root && fm->entry_types[idx] == 4) {
// Home folder
if (filemanager_is_computer_view(fm) && fm->entry_types[idx] == FM_ENTRY_HOME) {
DesktopState* ds = fm->desktop;
if (ds && ds->home_dir[0] != '\0') {
montauk::strcpy(fm->current_path, ds->home_dir);
filemanager_push_history(fm);
filemanager_read_dir(fm);
filemanager_push_history(fm);
}
return;
}
if (fm->at_drives_root && fm->entry_types[idx] == 3) {
if (filemanager_is_computer_view(fm) && fm->entry_types[idx] == FM_ENTRY_DRIVE) {
int d = fm->drive_indices[idx];
char dpath[8];
if (d < 10) {
@@ -196,8 +231,8 @@ void filemanager_open_entry(FileManagerState* fm, int idx) {
dpath[0] = '1'; dpath[1] = '0' + (d - 10); dpath[2] = ':'; dpath[3] = '/'; dpath[4] = '\0';
}
montauk::strcpy(fm->current_path, dpath);
filemanager_push_history(fm);
filemanager_read_dir(fm);
filemanager_push_history(fm);
return;
}
@@ -202,9 +202,10 @@ void filemanager_draw_header(Canvas& c, FileManagerState* fm, Color toolbar_colo
// Action buttons: Copy, Cut, Paste, Rename, New Folder, Delete
bool has_sel = fm->selected >= 0 && fm->selected < fm->entry_count
&& !fm->at_drives_root && !fm->at_apps_view
&& fm->entry_types[fm->selected] != 3;
bool has_clip = fm->clipboard_has_data && !fm->at_drives_root && !fm->at_apps_view;
&& !filemanager_is_computer_view(fm) && !filemanager_is_virtual_view(fm)
&& fm->entry_types[fm->selected] != FM_ENTRY_DRIVE;
bool has_clip = fm->clipboard_has_data && !filemanager_is_computer_view(fm)
&& !filemanager_is_virtual_view(fm);
Color dim_bg = Color::from_rgb(0xF0, 0xF0, 0xF0);
// Icon toolbar buttons: Copy, Cut, Paste, Rename, New Folder, Delete
@@ -214,7 +215,8 @@ void filemanager_draw_header(Canvas& c, FileManagerState* fm, Color toolbar_colo
{ 188, ds ? &ds->icon_cut : nullptr, has_sel },
{ 216, ds ? &ds->icon_paste : nullptr, has_clip },
{ 244, ds ? &ds->icon_rename : nullptr, has_sel },
{ 272, ds ? &ds->icon_folder_new : nullptr, !fm->at_drives_root && !fm->at_apps_view },
{ 272, ds ? &ds->icon_folder_new : nullptr,
!filemanager_is_computer_view(fm) && !filemanager_is_virtual_view(fm) },
{ 300, ds ? &ds->icon_delete : nullptr, has_sel },
};
@@ -252,8 +254,8 @@ void filemanager_draw_header(Canvas& c, FileManagerState* fm, Color toolbar_colo
} else {
c.fill_rect(0, pathbar_y, c.w, FM_PATHBAR_H, Color::from_rgb(0xF0, 0xF0, 0xF0));
const char* pathbar_text = fm->current_path;
if (fm->at_drives_root) pathbar_text = "Computer";
else if (fm->at_apps_view) pathbar_text = "Apps";
if (filemanager_is_computer_view(fm)) pathbar_text = "Computer";
else if (filemanager_is_virtual_view(fm)) pathbar_text = filemanager_virtual_view_label(fm->virtual_view);
c.text(8, pathbar_y + 4, pathbar_text, colors::TEXT_COLOR);
}
@@ -313,22 +315,27 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
int icon_y = cell_y + FM_GRID_PAD;
// Check for special folder icon (type 7 in Computer, or type 1 dir with matching name)
int sfi = -1;
if (fm->entry_types[i] == 7) sfi = fm->drive_indices[i];
else if (ds && fm->entry_types[i] == 1) sfi = special_folder_index(fm->entry_names[i]);
if (fm->entry_types[i] == FM_ENTRY_SPECIAL_DIR) sfi = fm->drive_indices[i];
else if (ds && fm->entry_types[i] == FM_ENTRY_DIR) sfi = special_folder_index(fm->entry_names[i]);
if (sfi >= 0 && ds && ds->icon_special_folder_lg[sfi].pixels) {
c.icon(icon_x, icon_y, ds->icon_special_folder_lg[sfi]);
} else if (fm->entry_types[i] == 6 && i < fm->app_icon_count && fm->app_icons_lg[i].pixels) {
c.icon(icon_x, icon_y, fm->app_icons_lg[i]);
} else if (ds && fm->entry_types[i] == 5 && ds->icon_apps_lg.pixels) {
} else if (fm->entry_types[i] == FM_ENTRY_VIRTUAL_APP
&& 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_APPS_ROOT && ds->icon_apps_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_apps_lg);
} else if (ds && fm->entry_types[i] == 4 && ds->icon_home_folder_lg.pixels) {
} else if (ds && fm->entry_types[i] == FM_ENTRY_HOME && ds->icon_home_folder_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_home_folder_lg);
} else if (ds && fm->entry_types[i] == 3 && ds->icon_drive_lg.pixels) {
} else if (ds && fm->entry_types[i] == FM_ENTRY_DRIVE && ds->icon_drive_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_drive_lg);
} else if (ds && fm->entry_types[i] == 1 && ds->icon_folder_lg.pixels) {
} else if (ds && fm->entry_types[i] == FM_ENTRY_DIR && ds->icon_folder_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_folder_lg);
} else if (ds && fm->entry_types[i] == 2 && ds->icon_exec_lg.pixels) {
} else if (ds && fm->entry_types[i] == FM_ENTRY_EXEC && ds->icon_exec_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_exec_lg);
} else if (ds && ds->icon_file_lg.pixels) {
c.icon(icon_x, icon_y, ds->icon_file_lg);
@@ -438,23 +445,28 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
int ico_y = iy + (FM_ITEM_H - 16) / 2;
// Check for special folder icon
int sfi_sm = -1;
if (fm->entry_types[i] == 7) sfi_sm = fm->drive_indices[i];
else if (ds && fm->entry_types[i] == 1) sfi_sm = special_folder_index(fm->entry_names[i]);
if (fm->entry_types[i] == FM_ENTRY_SPECIAL_DIR) sfi_sm = fm->drive_indices[i];
else if (ds && fm->entry_types[i] == FM_ENTRY_DIR) sfi_sm = special_folder_index(fm->entry_names[i]);
if (ico_y < list_y) { /* clipped by header repaint */ }
else if (sfi_sm >= 0 && ds && ds->icon_special_folder[sfi_sm].pixels) {
c.icon(ico_x, ico_y, ds->icon_special_folder[sfi_sm]);
} else if (fm->entry_types[i] == 6 && i < fm->app_icon_count && fm->app_icons_sm[i].pixels) {
c.icon(ico_x, ico_y, fm->app_icons_sm[i]);
} else if (ds && fm->entry_types[i] == 5 && ds->icon_apps.pixels) {
} else if (fm->entry_types[i] == FM_ENTRY_VIRTUAL_APP
&& 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_APPS_ROOT && ds->icon_apps.pixels) {
c.icon(ico_x, ico_y, ds->icon_apps);
} else if (ds && fm->entry_types[i] == 4 && ds->icon_home_folder.pixels) {
} else if (ds && fm->entry_types[i] == FM_ENTRY_HOME && ds->icon_home_folder.pixels) {
c.icon(ico_x, ico_y, ds->icon_home_folder);
} else if (ds && fm->entry_types[i] == 3 && ds->icon_drive.pixels) {
} else if (ds && fm->entry_types[i] == FM_ENTRY_DRIVE && ds->icon_drive.pixels) {
c.icon(ico_x, ico_y, ds->icon_drive);
} else if (ds && fm->entry_types[i] == 1 && ds->icon_folder.pixels) {
} else if (ds && fm->entry_types[i] == FM_ENTRY_DIR && ds->icon_folder.pixels) {
c.icon(ico_x, ico_y, ds->icon_folder);
} else if (ds && fm->entry_types[i] == 2 && ds->icon_exec.pixels) {
} else if (ds && fm->entry_types[i] == FM_ENTRY_EXEC && ds->icon_exec.pixels) {
c.icon(ico_x, ico_y, ds->icon_exec);
} else if (ds && ds->icon_file.pixels) {
c.icon(ico_x, ico_y, ds->icon_file);
@@ -525,12 +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] == 6) type_str = "App";
else if (fm->entry_types[i] == 5) type_str = "Apps";
else if (fm->entry_types[i] == 4) type_str = "Home";
else if (fm->entry_types[i] == 3) type_str = "Drive";
else if (fm->entry_types[i] == 1) type_str = "Dir";
else if (fm->entry_types[i] == 2) type_str = "Exec";
if (fm->entry_types[i] == FM_ENTRY_VIRTUAL_APP) type_str = "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_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";
else if (fm->entry_types[i] == FM_ENTRY_EXEC) type_str = "Exec";
c.text(type_col_x, ty, type_str, dim);
}
}