feat: expose drive labels to userspace, render in Files
This commit is contained in:
@@ -122,4 +122,8 @@ namespace Montauk {
|
|||||||
static int Sys_DriveList(int* outDrives, int maxEntries) {
|
static int Sys_DriveList(int* outDrives, int maxEntries) {
|
||||||
return Fs::Vfs::VfsDriveList(outDrives, maxEntries);
|
return Fs::Vfs::VfsDriveList(outDrives, maxEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int Sys_DriveLabel(int driveNumber, char* outLabel, int maxLen) {
|
||||||
|
return Fs::Vfs::VfsDriveLabel(driveNumber, outLabel, maxLen);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -209,6 +209,10 @@ namespace Montauk {
|
|||||||
if ((int64_t)frame->arg2 < 0) return -1;
|
if ((int64_t)frame->arg2 < 0) return -1;
|
||||||
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(int), true)) return -1;
|
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(int), true)) return -1;
|
||||||
return (int64_t)Sys_DriveList((int*)frame->arg1, (int)frame->arg2);
|
return (int64_t)Sys_DriveList((int*)frame->arg1, (int)frame->arg2);
|
||||||
|
case SYS_DRIVELABEL:
|
||||||
|
if ((int64_t)frame->arg3 <= 0) return -1;
|
||||||
|
if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return -1;
|
||||||
|
return (int64_t)Sys_DriveLabel((int)frame->arg1, (char*)frame->arg2, (int)frame->arg3);
|
||||||
case SYS_TERMSCALE:
|
case SYS_TERMSCALE:
|
||||||
return Sys_TermScale(frame->arg1, frame->arg2);
|
return Sys_TermScale(frame->arg1, frame->arg2);
|
||||||
case SYS_RESOLVE:
|
case SYS_RESOLVE:
|
||||||
|
|||||||
@@ -222,6 +222,9 @@ namespace Montauk {
|
|||||||
/* Input.hpp */
|
/* Input.hpp */
|
||||||
static constexpr uint64_t SYS_INPUT_WAIT = 123;
|
static constexpr uint64_t SYS_INPUT_WAIT = 123;
|
||||||
|
|
||||||
|
/* Filesystem.hpp */
|
||||||
|
static constexpr uint64_t SYS_DRIVELABEL = 124;
|
||||||
|
|
||||||
static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024;
|
static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024;
|
||||||
|
|
||||||
static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0;
|
static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ namespace Fs {
|
|||||||
Ramdisk::Delete,
|
Ramdisk::Delete,
|
||||||
Ramdisk::Mkdir,
|
Ramdisk::Mkdir,
|
||||||
Ramdisk::Rename,
|
Ramdisk::Rename,
|
||||||
|
Ramdisk::GetLabel,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1592,6 +1592,11 @@ namespace Fs::Ext2 {
|
|||||||
// Template thunks — generate unique function pointers per instance
|
// Template thunks — generate unique function pointers per instance
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
|
static const char* GetLabelImpl(int inst) {
|
||||||
|
if (inst < 0 || inst >= g_instanceCount || !g_instances[inst].active) return nullptr;
|
||||||
|
return g_instances[inst].volumeLabel[0] ? g_instances[inst].volumeLabel : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
template<int N> struct Thunks {
|
template<int N> struct Thunks {
|
||||||
static int Open(const char* p) { return OpenImpl(N, p); }
|
static int Open(const char* p) { return OpenImpl(N, p); }
|
||||||
static int Read(int h, uint8_t* b, uint64_t o, uint64_t s) { return ReadImpl(N, h, b, o, s); }
|
static int Read(int h, uint8_t* b, uint64_t o, uint64_t s) { return ReadImpl(N, h, b, o, s); }
|
||||||
@@ -1603,6 +1608,7 @@ namespace Fs::Ext2 {
|
|||||||
static int Delete(const char* p) { return DeleteImpl(N, p); }
|
static int Delete(const char* p) { return DeleteImpl(N, p); }
|
||||||
static int Mkdir(const char* p) { return MkdirImpl(N, p); }
|
static int Mkdir(const char* p) { return MkdirImpl(N, p); }
|
||||||
static int Rename(const char* o, const char* n) { return RenameImpl(N, o, n); }
|
static int Rename(const char* o, const char* n) { return RenameImpl(N, o, n); }
|
||||||
|
static const char* GetLabel() { return GetLabelImpl(N); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<int N>
|
template<int N>
|
||||||
@@ -1618,6 +1624,7 @@ namespace Fs::Ext2 {
|
|||||||
Thunks<N>::Delete,
|
Thunks<N>::Delete,
|
||||||
Thunks<N>::Mkdir,
|
Thunks<N>::Mkdir,
|
||||||
Thunks<N>::Rename,
|
Thunks<N>::Rename,
|
||||||
|
Thunks<N>::GetLabel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1748,6 +1748,14 @@ namespace Fs::Fat32 {
|
|||||||
// Template thunks — generate unique function pointers per instance
|
// Template thunks — generate unique function pointers per instance
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
|
static const char* GetLabelImpl(int inst) {
|
||||||
|
if (inst < 0 || inst >= g_instanceCount || !g_instances[inst].active) return nullptr;
|
||||||
|
|
||||||
|
const char* label = g_instances[inst].volumeLabel;
|
||||||
|
if (label[0] == '\0' || StrEqualNoCase(label, "NO NAME")) return nullptr;
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
template<int N> struct Thunks {
|
template<int N> struct Thunks {
|
||||||
static int Open(const char* p) { return OpenImpl(N, p); }
|
static int Open(const char* p) { return OpenImpl(N, p); }
|
||||||
static int Read(int h, uint8_t* b, uint64_t o, uint64_t s) { return ReadImpl(N, h, b, o, s); }
|
static int Read(int h, uint8_t* b, uint64_t o, uint64_t s) { return ReadImpl(N, h, b, o, s); }
|
||||||
@@ -1759,6 +1767,7 @@ namespace Fs::Fat32 {
|
|||||||
static int Delete(const char* p) { return DeleteImpl(N, p); }
|
static int Delete(const char* p) { return DeleteImpl(N, p); }
|
||||||
static int Mkdir(const char* p) { return MkdirImpl(N, p); }
|
static int Mkdir(const char* p) { return MkdirImpl(N, p); }
|
||||||
static int Rename(const char* o, const char* n) { return RenameImpl(N, o, n); }
|
static int Rename(const char* o, const char* n) { return RenameImpl(N, o, n); }
|
||||||
|
static const char* GetLabel() { return GetLabelImpl(N); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<int N>
|
template<int N>
|
||||||
@@ -1774,6 +1783,7 @@ namespace Fs::Fat32 {
|
|||||||
Thunks<N>::Delete,
|
Thunks<N>::Delete,
|
||||||
Thunks<N>::Mkdir,
|
Thunks<N>::Mkdir,
|
||||||
Thunks<N>::Rename,
|
Thunks<N>::Rename,
|
||||||
|
Thunks<N>::GetLabel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ namespace Fs::Ramdisk {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* GetLabel() {
|
||||||
|
return "Ramdisk";
|
||||||
|
}
|
||||||
|
|
||||||
static bool StartsWith(const char* str, const char* prefix) {
|
static bool StartsWith(const char* str, const char* prefix) {
|
||||||
while (*prefix) {
|
while (*prefix) {
|
||||||
if (*str != *prefix) return false;
|
if (*str != *prefix) return false;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace Fs::Ramdisk {
|
|||||||
int Delete(const char* path);
|
int Delete(const char* path);
|
||||||
int Mkdir(const char* path);
|
int Mkdir(const char* path);
|
||||||
int Rename(const char* oldPath, const char* newPath);
|
int Rename(const char* oldPath, const char* newPath);
|
||||||
|
const char* GetLabel();
|
||||||
int GetFileCount();
|
int GetFileCount();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,6 +208,38 @@ namespace Fs::Vfs {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VfsDriveLabel(int driveNumber, char* outLabel, int maxLen) {
|
||||||
|
if (outLabel == nullptr || maxLen <= 0) return -1;
|
||||||
|
outLabel[0] = '\0';
|
||||||
|
|
||||||
|
vfsLock.Acquire();
|
||||||
|
if (driveNumber < 0 || driveNumber >= MaxDrives || driveTable[driveNumber] == nullptr) {
|
||||||
|
vfsLock.Release();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driveTable[driveNumber]->GetLabel == nullptr) {
|
||||||
|
vfsLock.Release();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* label = driveTable[driveNumber]->GetLabel();
|
||||||
|
if (label == nullptr || label[0] == '\0') {
|
||||||
|
vfsLock.Release();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int copied = 0;
|
||||||
|
while (label[copied] != '\0' && copied < maxLen - 1) {
|
||||||
|
outLabel[copied] = label[copied];
|
||||||
|
copied++;
|
||||||
|
}
|
||||||
|
outLabel[copied] = '\0';
|
||||||
|
|
||||||
|
vfsLock.Release();
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
|
||||||
int VfsRename(const char* oldPath, const char* newPath) {
|
int VfsRename(const char* oldPath, const char* newPath) {
|
||||||
int oldDrive, newDrive;
|
int oldDrive, newDrive;
|
||||||
const char* oldLocal;
|
const char* oldLocal;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ namespace Fs::Vfs {
|
|||||||
int (*Delete)(const char* path);
|
int (*Delete)(const char* path);
|
||||||
int (*Mkdir)(const char* path);
|
int (*Mkdir)(const char* path);
|
||||||
int (*Rename)(const char* oldPath, const char* newPath);
|
int (*Rename)(const char* oldPath, const char* newPath);
|
||||||
|
const char* (*GetLabel)();
|
||||||
};
|
};
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
@@ -48,5 +49,6 @@ namespace Fs::Vfs {
|
|||||||
|
|
||||||
// Returns number of registered drives, fills outDrives[] with their indices
|
// Returns number of registered drives, fills outDrives[] with their indices
|
||||||
int VfsDriveList(int* outDrives, int maxEntries);
|
int VfsDriveList(int* outDrives, int maxEntries);
|
||||||
|
int VfsDriveLabel(int driveNumber, char* outLabel, int maxLen);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ namespace Montauk {
|
|||||||
static constexpr uint64_t SYS_CLIPBOARD_GET_TEXT = 121;
|
static constexpr uint64_t SYS_CLIPBOARD_GET_TEXT = 121;
|
||||||
static constexpr uint64_t SYS_CLIPBOARD_CLEAR = 122;
|
static constexpr uint64_t SYS_CLIPBOARD_CLEAR = 122;
|
||||||
static constexpr uint64_t SYS_INPUT_WAIT = 123;
|
static constexpr uint64_t SYS_INPUT_WAIT = 123;
|
||||||
|
static constexpr uint64_t SYS_DRIVELABEL = 124;
|
||||||
|
|
||||||
static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024;
|
static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024;
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ extern "C" {
|
|||||||
#define MTK_SYS_FMKDIR 78
|
#define MTK_SYS_FMKDIR 78
|
||||||
#define MTK_SYS_FRENAME 94
|
#define MTK_SYS_FRENAME 94
|
||||||
#define MTK_SYS_DRIVELIST 79
|
#define MTK_SYS_DRIVELIST 79
|
||||||
|
#define MTK_SYS_DRIVELABEL 124
|
||||||
#define MTK_SYS_AUDIOOPEN 80
|
#define MTK_SYS_AUDIOOPEN 80
|
||||||
#define MTK_SYS_AUDIOCLOSE 81
|
#define MTK_SYS_AUDIOCLOSE 81
|
||||||
#define MTK_SYS_AUDIOWRITE 82
|
#define MTK_SYS_AUDIOWRITE 82
|
||||||
|
|||||||
@@ -161,6 +161,9 @@ namespace montauk {
|
|||||||
inline int drivelist(int* outDrives, int max) {
|
inline int drivelist(int* outDrives, int max) {
|
||||||
return (int)syscall2(Montauk::SYS_DRIVELIST, (uint64_t)outDrives, (uint64_t)max);
|
return (int)syscall2(Montauk::SYS_DRIVELIST, (uint64_t)outDrives, (uint64_t)max);
|
||||||
}
|
}
|
||||||
|
inline int drivelabel(int drive, char* outLabel, int maxLen) {
|
||||||
|
return (int)syscall3(Montauk::SYS_DRIVELABEL, (uint64_t)drive, (uint64_t)outLabel, (uint64_t)maxLen);
|
||||||
|
}
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
inline void* alloc(uint64_t size) { return (void*)syscall1(Montauk::SYS_ALLOC, size); }
|
inline void* alloc(uint64_t size) { return (void*)syscall1(Montauk::SYS_ALLOC, size); }
|
||||||
|
|||||||
@@ -117,11 +117,10 @@ void filemanager_on_mouse(Window* win, MouseEvent& ev) {
|
|||||||
if (local_y >= list_y && local_x < cw - FM_SCROLLBAR_W) {
|
if (local_y >= list_y && local_x < cw - FM_SCROLLBAR_W) {
|
||||||
int cols = (cw - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
|
int cols = (cw - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
|
||||||
if (cols < 1) cols = 1;
|
if (cols < 1) cols = 1;
|
||||||
int col = local_x / FM_GRID_CELL_W;
|
int rel_y = local_y - list_y + fm->scrollbar.scroll_offset;
|
||||||
int row = (local_y - list_y + fm->scrollbar.scroll_offset) / FM_GRID_CELL_H;
|
int clicked_idx = filemanager_grid_index_at(fm, cols, local_x, rel_y);
|
||||||
int clicked_idx = row * cols + col;
|
|
||||||
|
|
||||||
if (clicked_idx >= 0 && clicked_idx < fm->entry_count && col < cols) {
|
if (clicked_idx >= 0 && clicked_idx < fm->entry_count) {
|
||||||
uint64_t now = montauk::get_milliseconds();
|
uint64_t now = montauk::get_milliseconds();
|
||||||
|
|
||||||
if (fm->last_click_item == clicked_idx &&
|
if (fm->last_click_item == clicked_idx &&
|
||||||
@@ -179,10 +178,9 @@ void filemanager_on_mouse(Window* win, MouseEvent& ev) {
|
|||||||
if (local_x < cw - FM_SCROLLBAR_W) {
|
if (local_x < cw - FM_SCROLLBAR_W) {
|
||||||
int cols = (cw - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
|
int cols = (cw - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
|
||||||
if (cols < 1) cols = 1;
|
if (cols < 1) cols = 1;
|
||||||
int col = local_x / FM_GRID_CELL_W;
|
int rel_y = local_y - list_y + fm->scrollbar.scroll_offset;
|
||||||
int row = (local_y - list_y + fm->scrollbar.scroll_offset) / FM_GRID_CELL_H;
|
int idx = filemanager_grid_index_at(fm, cols, local_x, rel_y);
|
||||||
int idx = row * cols + col;
|
if (idx >= 0 && idx < fm->entry_count) {
|
||||||
if (idx >= 0 && idx < fm->entry_count && col < cols) {
|
|
||||||
target_idx = idx;
|
target_idx = idx;
|
||||||
fm->selected = idx;
|
fm->selected = idx;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ inline constexpr int FM_ITEM_H = 24;
|
|||||||
inline constexpr int FM_SCROLLBAR_W = 12;
|
inline constexpr int FM_SCROLLBAR_W = 12;
|
||||||
inline constexpr int FM_GRID_CELL_W = 80;
|
inline constexpr int FM_GRID_CELL_W = 80;
|
||||||
inline constexpr int FM_GRID_CELL_H = 80;
|
inline constexpr int FM_GRID_CELL_H = 80;
|
||||||
|
inline constexpr int FM_GRID_CELL_H_WRAPPED = 92;
|
||||||
inline constexpr int FM_GRID_ICON = 48;
|
inline constexpr int FM_GRID_ICON = 48;
|
||||||
inline constexpr int FM_GRID_PAD = 4;
|
inline constexpr int FM_GRID_PAD = 4;
|
||||||
|
|
||||||
@@ -151,6 +152,11 @@ void filemanager_commit_pathbar(FileManagerState* fm);
|
|||||||
|
|
||||||
void filemanager_open_entry(FileManagerState* fm, int idx);
|
void filemanager_open_entry(FileManagerState* fm, int idx);
|
||||||
|
|
||||||
|
int filemanager_grid_row_height(FileManagerState* fm, int row, int cols);
|
||||||
|
int filemanager_grid_row_y(FileManagerState* fm, int row, int cols);
|
||||||
|
int filemanager_grid_content_height(FileManagerState* fm, int cols);
|
||||||
|
int filemanager_grid_index_at(FileManagerState* fm, int cols, int x, int rel_y);
|
||||||
|
|
||||||
void filemanager_draw_header(Canvas& c, FileManagerState* fm, Color toolbar_color, Color btn_bg);
|
void filemanager_draw_header(Canvas& c, FileManagerState* fm, Color toolbar_color, Color btn_bg);
|
||||||
void filemanager_on_draw(Window* win, Framebuffer& fb);
|
void filemanager_on_draw(Window* win, Framebuffer& fb);
|
||||||
void filemanager_on_mouse(Window* win, MouseEvent& ev);
|
void filemanager_on_mouse(Window* win, MouseEvent& ev);
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ void filemanager_read_drives(FileManagerState* fm) {
|
|||||||
char label[64];
|
char label[64];
|
||||||
montauk::strcpy(label, "Drive ");
|
montauk::strcpy(label, "Drive ");
|
||||||
str_append(label, probe, 64);
|
str_append(label, probe, 64);
|
||||||
|
char volume_label[64];
|
||||||
|
if (montauk::drivelabel(d, volume_label, 64) > 0 && volume_label[0] != '\0') {
|
||||||
|
str_append(label, " [", 64);
|
||||||
|
str_append(label, volume_label, 64);
|
||||||
|
str_append(label, "]", 64);
|
||||||
|
}
|
||||||
montauk::strncpy(fm->entry_names[i], label, 63);
|
montauk::strncpy(fm->entry_names[i], label, 63);
|
||||||
fm->entry_types[i] = 3; // drive
|
fm->entry_types[i] = 3; // drive
|
||||||
fm->entry_sizes[i] = 0;
|
fm->entry_sizes[i] = 0;
|
||||||
|
|||||||
@@ -8,6 +8,155 @@
|
|||||||
|
|
||||||
namespace filemanager {
|
namespace filemanager {
|
||||||
|
|
||||||
|
static bool label_break_char(char ch) {
|
||||||
|
return ch == ' ' || ch == '-' || ch == '_' || ch == '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_label_range(char* dst, int dst_max, const char* src, int start, int end) {
|
||||||
|
if (!dst || dst_max <= 0) return;
|
||||||
|
while (start < end && src[start] == ' ') start++;
|
||||||
|
while (end > start && src[end - 1] == ' ') end--;
|
||||||
|
|
||||||
|
int out = 0;
|
||||||
|
for (int i = start; i < end && out < dst_max - 1; i++) {
|
||||||
|
dst[out++] = src[i];
|
||||||
|
}
|
||||||
|
dst[out] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fitted_prefix_len(const char* src, int max_w, int max_len) {
|
||||||
|
char tmp[128];
|
||||||
|
int best = 0;
|
||||||
|
int limit = max_len < 127 ? max_len : 127;
|
||||||
|
for (int len = 1; len <= limit; len++) {
|
||||||
|
copy_label_range(tmp, sizeof(tmp), src, 0, len);
|
||||||
|
if (text_width(tmp) > max_w) break;
|
||||||
|
best = len;
|
||||||
|
}
|
||||||
|
return best > 0 ? best : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fit_label_with_ellipsis(char* dst, int dst_max, const char* src, int max_w) {
|
||||||
|
montauk::strncpy(dst, src, dst_max);
|
||||||
|
if (text_width(dst) <= max_w) return;
|
||||||
|
|
||||||
|
int ellipsis_w = text_width("...");
|
||||||
|
int len = montauk::slen(dst);
|
||||||
|
while (len > 0) {
|
||||||
|
dst[len] = '\0';
|
||||||
|
if (text_width(dst) + ellipsis_w <= max_w) break;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > dst_max - 4) len = dst_max - 4;
|
||||||
|
if (len < 0) len = 0;
|
||||||
|
dst[len] = '.';
|
||||||
|
dst[len + 1] = '.';
|
||||||
|
dst[len + 2] = '.';
|
||||||
|
dst[len + 3] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_grid_label_lines(const char* name, int max_w,
|
||||||
|
char* line1, int line1_max,
|
||||||
|
char* line2, int line2_max) {
|
||||||
|
line1[0] = '\0';
|
||||||
|
line2[0] = '\0';
|
||||||
|
|
||||||
|
if (text_width(name) <= max_w) {
|
||||||
|
montauk::strncpy(line1, name, line1_max);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nlen = montauk::slen(name);
|
||||||
|
int break_end = -1;
|
||||||
|
int break_next = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < nlen - 1; i++) {
|
||||||
|
if (!label_break_char(name[i])) continue;
|
||||||
|
|
||||||
|
int end = (name[i] == ' ') ? i : i + 1;
|
||||||
|
int next = i + 1;
|
||||||
|
|
||||||
|
char candidate[128];
|
||||||
|
copy_label_range(candidate, sizeof(candidate), name, 0, end);
|
||||||
|
if (candidate[0] != '\0' && text_width(candidate) <= max_w) {
|
||||||
|
break_end = end;
|
||||||
|
break_next = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (break_end < 0) {
|
||||||
|
break_end = fitted_prefix_len(name, max_w, nlen);
|
||||||
|
break_next = break_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_label_range(line1, line1_max, name, 0, break_end);
|
||||||
|
fit_label_with_ellipsis(line2, line2_max, name + break_next, max_w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_centered_grid_label(Canvas& c, int cell_x, int y, const char* label, Color color) {
|
||||||
|
int tw = text_width(label);
|
||||||
|
int tx = cell_x + (FM_GRID_CELL_W - tw) / 2;
|
||||||
|
if (tx < cell_x + 2) tx = cell_x + 2;
|
||||||
|
c.text(tx, y, label, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
int filemanager_grid_row_height(FileManagerState* fm, int row, int cols) {
|
||||||
|
if (!fm || cols <= 0 || row < 0) return FM_GRID_CELL_H;
|
||||||
|
|
||||||
|
int start = row * cols;
|
||||||
|
int end = start + cols;
|
||||||
|
if (end > fm->entry_count) end = fm->entry_count;
|
||||||
|
|
||||||
|
int max_w = FM_GRID_CELL_W - 4;
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
if (text_width(fm->entry_names[i]) > max_w) {
|
||||||
|
return FM_GRID_CELL_H_WRAPPED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FM_GRID_CELL_H;
|
||||||
|
}
|
||||||
|
|
||||||
|
int filemanager_grid_row_y(FileManagerState* fm, int row, int cols) {
|
||||||
|
int y = 0;
|
||||||
|
for (int r = 0; r < row; r++) {
|
||||||
|
y += filemanager_grid_row_height(fm, r, cols);
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int filemanager_grid_content_height(FileManagerState* fm, int cols) {
|
||||||
|
if (!fm || cols <= 0) return 0;
|
||||||
|
|
||||||
|
int rows = (fm->entry_count + cols - 1) / cols;
|
||||||
|
int h = 0;
|
||||||
|
for (int r = 0; r < rows; r++) {
|
||||||
|
h += filemanager_grid_row_height(fm, r, cols);
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
int filemanager_grid_index_at(FileManagerState* fm, int cols, int x, int rel_y) {
|
||||||
|
if (!fm || cols <= 0 || x < 0 || rel_y < 0) return -1;
|
||||||
|
|
||||||
|
int col = x / FM_GRID_CELL_W;
|
||||||
|
if (col < 0 || col >= cols) return -1;
|
||||||
|
|
||||||
|
int rows = (fm->entry_count + cols - 1) / cols;
|
||||||
|
int row_y = 0;
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
int row_h = filemanager_grid_row_height(fm, row, cols);
|
||||||
|
if (rel_y < row_y + row_h) {
|
||||||
|
int idx = row * cols + col;
|
||||||
|
return idx < fm->entry_count ? idx : -1;
|
||||||
|
}
|
||||||
|
row_y += row_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void filemanager_draw_header(Canvas& c, FileManagerState* fm, Color toolbar_color, Color btn_bg) {
|
void filemanager_draw_header(Canvas& c, FileManagerState* fm, Color toolbar_color, Color btn_bg) {
|
||||||
DesktopState* ds = fm->desktop;
|
DesktopState* ds = fm->desktop;
|
||||||
|
|
||||||
@@ -133,8 +282,7 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
|
|||||||
int list_h = c.h - list_y;
|
int list_h = c.h - list_y;
|
||||||
int cols = (c.w - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
|
int cols = (c.w - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
|
||||||
if (cols < 1) cols = 1;
|
if (cols < 1) cols = 1;
|
||||||
int rows = (fm->entry_count + cols - 1) / cols;
|
int content_h = filemanager_grid_content_height(fm, cols);
|
||||||
int content_h = rows * FM_GRID_CELL_H;
|
|
||||||
|
|
||||||
// Update scrollbar
|
// Update scrollbar
|
||||||
fm->scrollbar.bounds = {c.w - FM_SCROLLBAR_W, list_y, FM_SCROLLBAR_W, list_h};
|
fm->scrollbar.bounds = {c.w - FM_SCROLLBAR_W, list_y, FM_SCROLLBAR_W, list_h};
|
||||||
@@ -145,15 +293,16 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
|
|||||||
int col = i % cols;
|
int col = i % cols;
|
||||||
int row = i / cols;
|
int row = i / cols;
|
||||||
int cell_x = col * FM_GRID_CELL_W;
|
int cell_x = col * FM_GRID_CELL_W;
|
||||||
int cell_y = list_y + row * FM_GRID_CELL_H - fm->scrollbar.scroll_offset;
|
int row_h = filemanager_grid_row_height(fm, row, cols);
|
||||||
|
int cell_y = list_y + filemanager_grid_row_y(fm, row, cols) - fm->scrollbar.scroll_offset;
|
||||||
|
|
||||||
// Skip if entirely off-screen
|
// Skip if entirely off-screen
|
||||||
if (cell_y + FM_GRID_CELL_H <= list_y || cell_y >= c.h) continue;
|
if (cell_y + row_h <= list_y || cell_y >= c.h) continue;
|
||||||
|
|
||||||
// Selection highlight
|
// Selection highlight
|
||||||
if (i == fm->selected) {
|
if (i == fm->selected) {
|
||||||
int sy = gui_max(cell_y, list_y);
|
int sy = gui_max(cell_y, list_y);
|
||||||
int sh = gui_min(cell_y + FM_GRID_CELL_H, c.h) - sy;
|
int sh = gui_min(cell_y + row_h, c.h) - sy;
|
||||||
int sw = gui_min(FM_GRID_CELL_W, c.w - FM_SCROLLBAR_W - cell_x);
|
int sw = gui_min(FM_GRID_CELL_W, c.w - FM_SCROLLBAR_W - cell_x);
|
||||||
if (sh > 0 && sw > 0)
|
if (sh > 0 && sw > 0)
|
||||||
c.fill_rect(cell_x, sy, sw, sh, colors::MENU_HOVER);
|
c.fill_rect(cell_x, sy, sw, sh, colors::MENU_HOVER);
|
||||||
@@ -217,35 +366,21 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
|
|||||||
c.vline(cx, ty, system_font_height(), colors::ACCENT);
|
c.vline(cx, ty, system_font_height(), colors::ACCENT);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Truncate by pixel width so names never overflow cells
|
// Prefer two grid label rows before truncating.
|
||||||
int max_w = FM_GRID_CELL_W - 4;
|
int max_w = FM_GRID_CELL_W - 4;
|
||||||
const char* name = fm->entry_names[i];
|
const char* name = fm->entry_names[i];
|
||||||
int nlen = montauk::slen(name);
|
char line1[128];
|
||||||
char label[64];
|
char line2[128];
|
||||||
montauk::strncpy(label, name, 63);
|
build_grid_label_lines(name, max_w, line1, sizeof(line1), line2, sizeof(line2));
|
||||||
|
|
||||||
if (text_width(label) > max_w && nlen > 3) {
|
|
||||||
int ellipsis_w = text_width("...");
|
|
||||||
int final_len = nlen;
|
|
||||||
while (final_len > 1) {
|
|
||||||
label[final_len] = '\0';
|
|
||||||
int w = text_width(label);
|
|
||||||
if (w + ellipsis_w <= max_w) break;
|
|
||||||
final_len--;
|
|
||||||
}
|
|
||||||
if (final_len > 60) final_len = 60;
|
|
||||||
label[final_len] = '.';
|
|
||||||
label[final_len + 1] = '.';
|
|
||||||
label[final_len + 2] = '.';
|
|
||||||
label[final_len + 3] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
int tw = text_width(label);
|
|
||||||
int tx = cell_x + (FM_GRID_CELL_W - tw) / 2;
|
|
||||||
if (tx < cell_x) tx = cell_x;
|
|
||||||
int ty = icon_y + FM_GRID_ICON + 2;
|
int ty = icon_y + FM_GRID_ICON + 2;
|
||||||
if (ty >= list_y && ty + system_font_height() <= c.h)
|
int fh = system_font_height();
|
||||||
c.text(tx, ty, label, colors::TEXT_COLOR);
|
if (ty >= list_y && ty + fh <= c.h) {
|
||||||
|
draw_centered_grid_label(c, cell_x, ty, line1, colors::TEXT_COLOR);
|
||||||
|
}
|
||||||
|
if (line2[0] != '\0' && ty + fh >= list_y && ty + fh * 2 <= c.h) {
|
||||||
|
draw_centered_grid_label(c, cell_x, ty + fh, line2, colors::TEXT_COLOR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ namespace Montauk {
|
|||||||
static constexpr uint64_t SYS_FDELETE = 77;
|
static constexpr uint64_t SYS_FDELETE = 77;
|
||||||
static constexpr uint64_t SYS_FMKDIR = 78;
|
static constexpr uint64_t SYS_FMKDIR = 78;
|
||||||
static constexpr uint64_t SYS_DRIVELIST = 79;
|
static constexpr uint64_t SYS_DRIVELIST = 79;
|
||||||
|
static constexpr uint64_t SYS_DRIVELABEL = 124;
|
||||||
static constexpr uint64_t SYS_TERMSCALE = 43;
|
static constexpr uint64_t SYS_TERMSCALE = 43;
|
||||||
static constexpr uint64_t SYS_RESOLVE = 44;
|
static constexpr uint64_t SYS_RESOLVE = 44;
|
||||||
static constexpr uint64_t SYS_GETRANDOM = 45;
|
static constexpr uint64_t SYS_GETRANDOM = 45;
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ extern "C" {
|
|||||||
#define MTK_SYS_FMKDIR 78
|
#define MTK_SYS_FMKDIR 78
|
||||||
#define MTK_SYS_FRENAME 94
|
#define MTK_SYS_FRENAME 94
|
||||||
#define MTK_SYS_DRIVELIST 79
|
#define MTK_SYS_DRIVELIST 79
|
||||||
|
#define MTK_SYS_DRIVELABEL 124
|
||||||
#define MTK_SYS_AUDIOOPEN 80
|
#define MTK_SYS_AUDIOOPEN 80
|
||||||
#define MTK_SYS_AUDIOCLOSE 81
|
#define MTK_SYS_AUDIOCLOSE 81
|
||||||
#define MTK_SYS_AUDIOWRITE 82
|
#define MTK_SYS_AUDIOWRITE 82
|
||||||
|
|||||||
@@ -152,6 +152,9 @@ namespace montauk {
|
|||||||
inline int drivelist(int* outDrives, int max) {
|
inline int drivelist(int* outDrives, int max) {
|
||||||
return (int)syscall2(Montauk::SYS_DRIVELIST, (uint64_t)outDrives, (uint64_t)max);
|
return (int)syscall2(Montauk::SYS_DRIVELIST, (uint64_t)outDrives, (uint64_t)max);
|
||||||
}
|
}
|
||||||
|
inline int drivelabel(int drive, char* outLabel, int maxLen) {
|
||||||
|
return (int)syscall3(Montauk::SYS_DRIVELABEL, (uint64_t)drive, (uint64_t)outLabel, (uint64_t)maxLen);
|
||||||
|
}
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
inline void* alloc(uint64_t size) { return (void*)syscall1(Montauk::SYS_ALLOC, size); }
|
inline void* alloc(uint64_t size) { return (void*)syscall1(Montauk::SYS_ALLOC, size); }
|
||||||
|
|||||||
Reference in New Issue
Block a user