// files.zap - a file browser app for ZeaxOS. // Click a folder to open it, a file to select 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 - 26 let ROWS = (BOT - LISTY - 8) / ROWH let cwd = "/" let names = [] let dirs = [] let top = 0 let sel = -1 let info = "ready" let running = true fn inr(x, y, rx, ry, rw, rh) { return x >= rx && x < rx + rw && y >= ry && y < ry + rh } fn reload() { let raw = sort(list_dir(cwd)) let tn = [] let td = [] for n in raw { push(tn, n) push(td, is_dir(join(cwd, n))) } names = [] dirs = [] let pass = 0 while pass < 2 { let i = 0 while i < len(tn) { if td[i] == (pass == 0) { push(names, tn[i]) push(dirs, td[i]) } i = i + 1 } pass = pass + 1 } top = 0 sel = -1 info = str(len(names)) + " items" } fn hit(x, y) { if inr(x, y, M, TB, 64, TH) { return 0 } if inr(x, y, M + 76, TB, 84, TH) { return 1 } if inr(x, y, W - M - 176, TB, 76, TH) { return 2 } if inr(x, y, W - M - 90, TB, 76, TH) { return 3 } if x >= LISTX && x < LISTX + LISTW && y >= LISTY && y < LISTY + ROWS * ROWH { let r = (y - LISTY) / ROWH if r < ROWS && top + 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 } if id == 2 { top = top - ROWS if top < 0 { top = 0 } return } if id == 3 { top = top + ROWS return } let idx = top + id - 100 if idx < 0 || idx >= len(names) { return } sel = idx if dirs[idx] { cwd = join(cwd, names[idx]) reload() } else { info = names[idx] + " " + size_text(file_size(join(cwd, names[idx]))) } } 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, 84, "Quit", hid, 1) tb(W - M - 176, 76, "Prev", hid, 2) tb(W - M - 90, 76, "Next", hid, 3) text(M, PATHY, cwd, ACCENT) let i = 0 while i < ROWS { let idx = top + i if idx < len(names) { let ry = LISTY + i * ROWH let cur = idx == sel let hot = hid == 100 + i let col = cur ? GREEN : (hot ? ACCENT : FG) rect(LISTX, ry, LISTW, ROWH - 4, cur ? GREEN : (hot ? ACCENT : LINE)) text(LISTX + 10, ry + 3, names[idx], col) if dirs[idx] { text(LISTX + 10 + text_w(names[idx]), ry + 3, "/", col) } } i = i + 1 } fill_rect(0, BOT, W, 2, LINE) text(8, BOT + 4, info, MUTED) } 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 let click = mb % 2 != 0 && pmb % 2 == 0 pmx = mx pmy = my pmb = mb let id = hit(mx, my) 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()