// calculator.zap - a calculator app for ZeaxOS. // Click the keys with the mouse; Ctrl+Esc quits. // #engine let S = 100 let acc = 0 let entry = "" let op = "" let fresh = true let error = nil let disp = "0.00" fn parse(s) { let neg = substr(s, 0, 1) == "-" let i = neg ? 1 : 0 let n = len(s) let w = 0 let f = 0 let fd = 0 let dot = false while i < n { let c = substr(s, i, 1) if c == "." { dot = true } else { let d = char_code(c) - 48 if d >= 0 && d <= 9 { if dot { if fd < 2 { f = f * 10 + d fd = fd + 1 } } else { w = w * 10 + d } } } i = i + 1 } if fd == 1 { f = f * 10 } return neg ? 0 - (w * S + f) : w * S + f } fn fmt(v) { let neg = v < 0 let a = abs(v) let w = a / S let f = a % S let fs = str(f) if f < 10 { fs = "0" + fs } let s = str(w) + "." + fs return neg ? "-" + s : s } fn calc(a, o, b) { if o == "+" { return a + b } if o == "-" { return a - b } if o == "*" { return a * b / S } if b == 0 && (o == "/" || o == "%") { throw "divide by zero" } if o == "/" { return a * S / b } if o == "%" { return a % b } return b } fn refresh() { if error != nil { disp = error return } disp = (entry != "" && !fresh) ? entry : fmt(acc) } fn reset() { acc = 0 entry = "" op = "" fresh = true error = nil refresh() } fn press(t) { error = nil if t == "C" { reset() return } if t == "DEL" { if entry != "" && !fresh { entry = substr(entry, 0, len(entry) - 1) } } else if t == "+/-" { if entry != "" { entry = substr(entry, 0, 1) == "-" ? substr(entry, 1, len(entry) - 1) : "-" + entry } else { acc = 0 - acc } } else if t == "." { if fresh { entry = "0." fresh = false } else if index_of(entry, ".") < 0 { entry = entry + "." } } else if t == "+" || t == "-" || t == "*" || t == "/" || t == "%" { let c = entry != "" ? parse(entry) : acc if op != "" { try { acc = calc(acc, op, c) } catch (e) { error = e } } else { acc = c } op = t entry = "" fresh = true } else if t == "=" { if op != "" { let c = entry != "" ? parse(entry) : acc try { acc = calc(acc, op, c) } catch (e) { error = e } op = "" } else { acc = entry != "" ? parse(entry) : acc } entry = "" fresh = true } else { if fresh { entry = t fresh = false } else { entry = entry + t } } refresh() } // #main let W = gfx_width() let H = gfx_height() let TOP = 16 let ACCENT = rgb(0, 229, 255) let MUTED = rgb(122, 138, 154) let BW = 96 let BH = 52 let GAP = 10 let GX = 16 let DP = "Ctrl+Esc quits" let GY = TOP + 104 let labels = ["C", "DEL", "%", "/", "7", "8", "9", "*", "4", "5", "6", "-", "1", "2", "3", "+", "+/-", "0", ".", "="] fn bhit(x, y) { if x < GX || y < GY { return -1 } let c = (x - GX) / (BW + GAP) let r = (y - GY) / (BH + GAP) if c > 3 || r > 4 { return -1 } if x >= GX + c * (BW + GAP) + BW || y >= GY + r * (BH + GAP) + BH { return -1 } return r * 4 + c } reset() let pmx = -1 let pmy = -1 let pmb = 0 let hid = -2 let redraw = true cursor_show() while true { let k = key() if k == 269 { break } let mx = mouse_x() let my = mouse_y() let mb = mouse_buttons() let click = mb % 2 != 0 && pmb % 2 == 0 let moved = mx != pmx || my != pmy || mb != pmb pmx = mx pmy = my pmb = mb let bid = bhit(mx, my) gui_begin() if bid != hid || click || redraw { cursor_hide() let act = false window(0, TOP, W, H - TOP, "Calculator") label(GX, TOP + 40, DP) text(GX, TOP + 68, disp, ACCENT) if op != "" { text(GX + text_w(disp) + 14, TOP + 68, op, MUTED) } let r = 0 while r < 5 { let c = 0 while c < 4 { if button(GX + c * (BW + GAP), GY + r * (BH + GAP), BW, BH, labels[r * 4 + c]) { press(labels[r * 4 + c]) act = true } c = c + 1 } r = r + 1 } cursor_move(mx, my) cursor_show() hid = bid redraw = act } else if moved { cursor_move(mx, my) } gui_end() sleep(16) } cursor_hide()