// files.zap - a file browser app for ZeaxOS. // Click a folder to open it; Ctrl+Esc quits. // #engine fn join(a, b) { if a == "/" { return "/" + b } if ends_with(a, "/") { return a + b } return a + "/" + b } fn parent(p) { if p == "/" { return "/" } let q = p if ends_with(q, "/") { q = substr(q, 0, len(q) - 1) } let i = len(q) - 1 while i > 0 && substr(q, i, 1) != "/" { i = i - 1 } if i <= 0 { return "/" } return substr(q, 0, i) } fn size_text(n) { if n < 1024 { return str(n) + " B" } return str(n / 1024) + "." + str(n % 1024 * 10 / 1024) + " KB" } // #main let W = gfx_width() let H = gfx_height() let TOP = 16 let BG = rgb(10, 14, 18) let FG = rgb(230, 237, 243) let MUTED = rgb(122, 138, 154) let ACCENT = rgb(0, 229, 255) let GREEN = rgb(0, 255, 102) let LINE = rgb(42, 58, 74) let QH = "Ctrl+Esc quits" let M = 14 let TB = TOP + 44 let TH = 30 let PATHY = TB + 44 let LISTX = M let LISTY = PATHY + 28 let LISTW = W - 2 * M let ROWH = 26 let BOT = H - 10 let ROWS = (BOT - LISTY - 8) / ROWH let cwd = "/" let names = [] let isdir = [] let labels = [] let sel = -1 let running = true fn inr(x, y, rx, ry, rw, rh) { return x >= rx && x < rx + rw && y >= ry && y < ry + rh } fn reload() { names = [] isdir = [] labels = [] let raw = sort(list_dir(cwd)) let pass = 0 while pass < 2 { for n in raw { let f = join(cwd, n) let d = is_dir(f) if d == (pass == 0) { push(names, n) push(isdir, d) if d { push(labels, n + "/") } else { push(labels, n + " " + size_text(file_size(f))) } } } pass = pass + 1 } sel = -1 } fn hit(x, y) { if inr(x, y, M, TB, 64, TH) { return 0 } if inr(x, y, M + 76, TB, 90, TH) { return 1 } if x >= LISTX && x < LISTX + LISTW && y >= LISTY && y < LISTY + ROWS * ROWH { let r = (y - LISTY) / ROWH if r < ROWS && r < len(names) { return 100 + r } } return -1 } fn on_click(id) { if id == 0 { cwd = parent(cwd) reload() return } if id == 1 { running = false return } let idx = id - 100 if idx >= 0 && idx < len(names) { sel = idx if isdir[idx] { cwd = join(cwd, names[idx]) reload() } } } fn tb(x, w, textstr, hid, id) { let hot = hid == id rect(x, TB, w, TH, hot ? ACCENT : LINE) text(x + (w - text_w(textstr)) / 2, TB + (TH - 16) / 2, textstr, hot ? ACCENT : FG) } fn render(hid) { fill_rect(0, TOP, W, H - TOP, BG) text(8, TOP + 4, "Files", GREEN) text(W - text_w(QH) - 8, TOP + 4, QH, MUTED) fill_rect(0, TOP + 26, W, 2, LINE) tb(M, 64, "Up", hid, 0) tb(M + 76, 90, "Quit", hid, 1) text(M, PATHY, cwd, ACCENT) if sel >= 0 && sel < len(names) { text(M, BOT - 24, names[sel], MUTED) } let i = 0 while i < ROWS { if i < len(names) { let ry = LISTY + i * ROWH let cur = i == sel let hot = hid == 100 + i rect(LISTX, ry, LISTW, ROWH - 4, cur ? GREEN : (hot ? ACCENT : LINE)) text(LISTX + 10, ry + 3, labels[i], cur ? GREEN : (hot ? ACCENT : FG)) } i = i + 1 } } reload() let hid = -2 let pmx = -1 let pmy = -1 let pmb = 0 cursor_show() while running { let k = key() if k == 269 { break } let mx = mouse_x() let my = mouse_y() let mb = mouse_buttons() let moved = mx != pmx || my != pmy || mb != pmb pmx = mx pmy = my pmb = mb let id = hit(mx, my) let click = mb % 2 != 0 && pmb % 2 == 0 if click && id >= 0 { on_click(id) } if id != hid || click { cursor_hide() render(id) cursor_move(mx, my) cursor_show() hid = id } else if moved { cursor_move(mx, my) } sleep(5) } cursor_hide()