feat: expose drive labels to userspace, render in Files

This commit is contained in:
2026-04-22 07:45:33 +02:00
parent 0dfb3e8dbb
commit 6c2796f8bd
20 changed files with 261 additions and 38 deletions
@@ -117,11 +117,10 @@ void filemanager_on_mouse(Window* win, MouseEvent& ev) {
if (local_y >= list_y && local_x < cw - FM_SCROLLBAR_W) {
int cols = (cw - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
if (cols < 1) cols = 1;
int col = local_x / FM_GRID_CELL_W;
int row = (local_y - list_y + fm->scrollbar.scroll_offset) / FM_GRID_CELL_H;
int clicked_idx = row * cols + col;
int rel_y = local_y - list_y + fm->scrollbar.scroll_offset;
int clicked_idx = filemanager_grid_index_at(fm, cols, local_x, rel_y);
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();
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) {
int cols = (cw - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
if (cols < 1) cols = 1;
int col = local_x / FM_GRID_CELL_W;
int row = (local_y - list_y + fm->scrollbar.scroll_offset) / FM_GRID_CELL_H;
int idx = row * cols + col;
if (idx >= 0 && idx < fm->entry_count && col < cols) {
int rel_y = local_y - list_y + fm->scrollbar.scroll_offset;
int idx = filemanager_grid_index_at(fm, cols, local_x, rel_y);
if (idx >= 0 && idx < fm->entry_count) {
target_idx = 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_GRID_CELL_W = 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_PAD = 4;
@@ -151,6 +152,11 @@ void filemanager_commit_pathbar(FileManagerState* fm);
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_on_draw(Window* win, Framebuffer& fb);
void filemanager_on_mouse(Window* win, MouseEvent& ev);
@@ -85,6 +85,12 @@ void filemanager_read_drives(FileManagerState* fm) {
char label[64];
montauk::strcpy(label, "Drive ");
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);
fm->entry_types[i] = 3; // drive
fm->entry_sizes[i] = 0;
+165 -30
View File
@@ -8,6 +8,155 @@
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) {
DesktopState* ds = fm->desktop;
@@ -133,8 +282,7 @@ void filemanager_on_draw(Window* win, Framebuffer& fb) {
int list_h = c.h - list_y;
int cols = (c.w - FM_SCROLLBAR_W) / FM_GRID_CELL_W;
if (cols < 1) cols = 1;
int rows = (fm->entry_count + cols - 1) / cols;
int content_h = rows * FM_GRID_CELL_H;
int content_h = filemanager_grid_content_height(fm, cols);
// Update scrollbar
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 row = i / cols;
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
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
if (i == fm->selected) {
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);
if (sh > 0 && sw > 0)
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);
}
} 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;
const char* name = fm->entry_names[i];
int nlen = montauk::slen(name);
char label[64];
montauk::strncpy(label, name, 63);
char line1[128];
char line2[128];
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;
if (ty >= list_y && ty + system_font_height() <= c.h)
c.text(tx, ty, label, colors::TEXT_COLOR);
int fh = system_font_height();
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 {