灵符lingfu
L

ling-wallet

↓ 0 downloads

Ling Wallet: a post-quantum privacy-coin wallet, full node and HyperTorus miner for LNG

Windows · x86-64 ✕Linux · x86-64 ✕LingOS · x86-64LingOS · Pi 5 ARM64 ✕
Keywords wallet, cryptocurrency, lng, node, miner, ling
lingfu install ling-wallet
LicenseMIT
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (116)

ling/ui/theme.ling
# theme.ling — LingStation "Black" palette, fonts and text helpers.
# Colours are packed 0xRRGGBB numbers. Text helpers take a baseline;
# px_text itself wants the top of the line box.

bind C_BG = hex_to_number("000000")
bind C_BG_DEEP = hex_to_number("020203")
bind C_PANEL = hex_to_number("060607")
bind C_SIDE = hex_to_number("08090b")
bind C_ROW_HOVER = hex_to_number("0e1014")
bind C_ROW_ACTIVE = hex_to_number("101216")
bind C_LINE = hex_to_number("3c3c3c")
bind C_LINE_SOFT = hex_to_number("2a2c32")
bind C_LINE_FAINT = hex_to_number("16171b")
bind C_TEXT = hex_to_number("cdd0d8")
bind C_TEXT_DIM = hex_to_number("8c9098")
bind C_TEXT_FAINT = hex_to_number("5a5e66")
bind C_TEXT_STRONG = hex_to_number("f5f5f5")
bind C_SEL = hex_to_number("005c80")
bind C_QR_PLATE = hex_to_number("e9ebf0")
bind C_QR_DARK = hex_to_number("050608")

bind C_RED = hex_to_number("ed4a37")
bind C_ORANGE = hex_to_number("f7941e")
bind C_YELLOW = hex_to_number("f7d846")
bind C_GREEN = hex_to_number("45c870")
bind C_BLUE = hex_to_number("3baaeb")
bind C_INDIGO = hex_to_number("4a64d8")
bind C_VIOLET = hex_to_number("9a53d6")

bind C_METER_G = hex_to_number("5ad278")
bind C_METER_Y = hex_to_number("fac850")
bind C_METER_R = hex_to_number("ff5a40")

# Layout rhythm.
bind SIDE_W = 224
bind TOP_H = 56
bind PAD = 24

# Font slots, loaded in this order by fonts_load() so the handles equal the slots.
bind F_UI = 0
bind F_HEAD = 1
bind F_MONO = 2
bind F_LOGO = 3

# Ascender and cap height per slot, in em (from the fonts' OS/2 tables).
bind FONT_ASC = [0.93, 1.011, 0.8, 0.75]
bind FONT_CAP = [0.643, 0.72, 0.56, 0.7]

fn fonts_load() {
    # Font file names, loaded in order into slots 0..3. We try two base dirs so
    # the app finds its fonts whether the working directory is the ling/ folder
    # (dev: `lingfu run`) or the folder beside a packed exe (a desktop build run
    # from dist/, where the assets sit under ling/assets/). This mirrors how
    # install_seeds() locates the shipped seed list in real_backend.ling.
    bind names = ["Rajdhani.ttf", "Orbitron.ttf", "VT323-Regular.ttf", "Daemon.otf"]
    bind bases = ["assets/fonts/", "ling/assets/fonts/"]
    bind i = 0
    bind good = true
    while i < len(names) {
        bind h = 0 - 1
        bind b = 0
        while b < len(bases) && h != i {
            bind h = font_load(list_get(bases, b) + list_get(names, i))
            bind b = b + 1
        }
        if h != i {
            println("font missing: ", list_get(names, i))
            bind good = false
        }
        bind i = i + 1
    }
    return good
}

fn roygbiv(i) {
    bind k = floor(i) % 7
    if k == 0 { return C_RED }
    if k == 1 { return C_ORANGE }
    if k == 2 { return C_YELLOW }
    if k == 3 { return C_GREEN }
    if k == 4 { return C_BLUE }
    if k == 5 { return C_INDIGO }
    return C_VIOLET
}

fn c_r(c) { return floor(c / 65536) }
fn c_g(c) { return floor(c / 256) % 256 }
fn c_b(c) { return c % 256 }
fn c_rgb(r, g, b) { return round(r) * 65536 + round(g) * 256 + round(b) }

fn mix(a, b, t) {
    bind k = clampf(t, 0, 1)
    return c_rgb(c_r(a) + (c_r(b) - c_r(a)) * k, c_g(a) + (c_g(b) - c_g(a)) * k, c_b(a) + (c_b(b) - c_b(a)) * k)
}
# LingStation lifts accents toward white so line work reads on black.
fn lift(c, t) { return mix(c, 16777215, t) }
fn shade(c, t) { return mix(c, 0, t) }

fn sel(cond, a, b) {
    if cond { return a }
    return b
}
fn clampf(v, lo, hi) {
    if v < lo { return lo }
    if v > hi { return hi }
    return v
}

# Text drawn with its baseline at `base`; returns the advance.
fn t_text(f, size, x, base, s, col) {
    if s == "" { return 0 }
    px_text(f, x, base - list_get(FONT_ASC, f) * size, size, col, s)
    return font_width(f, size, s)
}
fn t_w(f, size, s) { return font_width(f, size, s) }
fn t_cap(f, size) { return round(list_get(FONT_CAP, f) * size) }

# Letter-spaced text (one glyph per call, so keep it to short strings).
fn t_text_sp(f, size, x, base, s, col, sp) {
    bind top = base - list_get(FONT_ASC, f) * size
    bind n = str_len(s)
    bind px = x
    bind i = 0
    while i < n {
        bind ch = substr(s, i, 1)
        if ch != " " { px_text(f, px, top, size, col, ch) }
        bind px = px + font_width(f, size, ch) + sp
        bind i = i + 1
    }
    if n > 0 { return px - x - sp }
    return 0
}
fn t_w_sp(f, size, s, sp) {
    bind n = str_len(s)
    if n == 0 { return 0 }
    return font_width(f, size, s) + sp * (n - 1)
}

# Vertically centred on `mid` by cap height.
fn t_mid(f, size, x, mid, s, col) { return t_text(f, size, x, mid + t_cap(f, size) / 2, s, col) }
fn t_right(f, size, xr, base, s, col) {
    bind w = font_width(f, size, s)
    t_text(f, size, xr - w, base, s, col)
    return w
}
fn t_center(f, size, x, y, w, h, s, col) {
    bind tw = font_width(f, size, s)
    t_mid(f, size, x + round((w - tw) / 2), y + round(h / 2), s, col)
}

# Small-caps label: uppercase Rajdhani 13 px with 1.4 px tracking.
fn label(x, base, s, col) { return t_text_sp(F_UI, 13, x, base, str_upper(s), col, 1.4) }
fn label_w(s) { return t_w_sp(F_UI, 13, str_upper(s), 1.4) }
fn label_r(xr, base, s, col) {
    bind w = label_w(s)
    label(xr - w, base, s, col)
    return w
}

# Shorten with ".." so it fits maxw. px_fit re-measures once per dropped char,
# so cut long strings (key bundles) to what could ever fit first: no glyph is
# narrower than 0.1 em.
fn t_fit(f, size, maxw, s) {
    bind cap = ceil(maxw / max(1, size * 0.1)) + 2
    if str_len(s) > cap { return px_fit(f, size, maxw, substr(s, 0, cap)) }
    return px_fit(f, size, maxw, s)
}

# Greedy word wrap; returns a list of lines.
fn t_wrap(f, size, s, maxw) {
    bind out = []
    bind paras = str_split(s, "\n")
    bind p = 0
    while p < len(paras) {
        bind words = str_split(list_get(paras, p), " ")
        bind cur = ""
        bind i = 0
        while i < len(words) {
            bind wd = list_get(words, i)
            if wd != "" {
                bind trial = sel(cur == "", wd, cur + " " + wd)
                if cur != "" && font_width(f, size, trial) > maxw {
                    bind out = list_push(out, cur)
                    bind cur = wd
                } else {
                    bind cur = trial
                }
            }
            bind i = i + 1
        }
        bind out = list_push(out, cur)
        bind p = p + 1
    }
    return out
}

# Wrapped paragraph whose first line's cap top sits at `top`; returns the height used.
fn t_para(f, size, x, top, maxw, lh, s, col) {
    bind lines = t_wrap(f, size, s, maxw)
    bind cap = t_cap(f, size)
    bind i = 0
    while i < len(lines) {
        t_text(f, size, x, top + cap + i * lh, list_get(lines, i), col)
        bind i = i + 1
    }
    return len(lines) * lh
}

# Index of a string in a list, or -1.
fn index_of(l, v) {
    bind i = 0
    bind found = 0 - 1
    while i < len(l) {
        if found < 0 && list_get(l, i) == v { bind found = i }
        bind i = i + 1
    }
    return found
}

← Back to index