makepad/widgets/sheets/themes/knitted/widgets.splash

2801 lines
144 KiB
Text

use mod.prelude.widgets_internal.*
// Knitted: a widget kit knitted in denim-blue wool.
// The window is a piece of stockinette: rows of V stitches, each V two plump
// legs of yarn slanting in to meet at its foot, every stitch a little
// different, a haze of loose fibre in the gaps; a ribbed cuff runs round
// the window's edge and a cord seam parts it from the fabric. Every control
// is a padded patch of finer knit sewn onto it: a bound-off rim of chain
// loops round its edge, the patch bulging a little in the middle and laying
// a soft shadow on the fabric under it. Values run in a twisted cord of
// coloured yarn in a dark cord track, thumbs and switch knobs are knitted
// balls, knobs a knitted dome in a ring of chunky loops, screens dark knit
// in a couched rope. The two sheets of the family share these faces line
// for line; only the palette differs. Every face is drawn in true points
// (one device pixel is `px`), and every shadow and halo is drawn OUTSIDE
// the layout box: the quad is grown in `vertex`, and no padding is spent
// on relief.
// ---------------------------------------------------------------- helpers
// One device pixel in points.
let mk_px = fn(dpi: float) -> float {
return 1.0 / max(dpi, 0.5)
}
// An edge rounded to a whole device pixel in window space, handed back in
// box space: a hairline on it lands on one pixel row.
let mk_snap = fn(v: float, o: float, px: float) -> float {
return Finish.snap(o + v, px) - o
}
// Distance from p to the segment a..b, in points.
let mk_seg = fn(p: vec2, a: vec2, b: vec2) -> float {
let pa = p - a
let ba = b - a
let t = clamp(dot(pa, ba) / max(dot(ba, ba), 0.0001), 0.0, 1.0)
return length(pa - ba * t)
}
// A hash that does not line up: the library's hash nearly repeats every 97
// cells, so each cell is first sent to a far cell picked by its own hash.
let mk_hash = fn(i: vec2) -> float {
let j = i + vec2(Finish.hash(i) * 419.0, Finish.hash(i + vec2(5.31, 9.17)) * 263.0)
return Finish.hash(j)
}
// Value noise on that hash, one unit per cell.
let mk_noise = fn(q: vec2) -> float {
let i = floor(q)
let f = q - i
let u = f * f * (vec2(3.0, 3.0) - f * 2.0)
let a = mk_hash(i)
let b = mk_hash(i + vec2(1.0, 0.0))
let c = mk_hash(i + vec2(0.0, 1.0))
let d = mk_hash(i + vec2(1.0, 1.0))
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y)
}
// Two slow clouds over each other, -0.5..0.5: the dye lot and the hand of
// the knitter, so no two stretches of fabric read alike.
let mk_cloud = fn(q: vec2) -> float {
return (mk_noise(q / 83.0) - 0.5) * 0.6 + (mk_noise(q / 23.0 + vec2(7.7, 3.1)) - 0.5) * 0.4
}
// How much of the light from the upper left a face turned `n` takes:
// +1 facing it, -1 turned away.
let mk_lit = fn(n: vec2) -> float {
return dot(n, vec2(-0.55, -0.835))
}
// The way the edge of a rounded box faces at `p` (the box `c`, half size
// `h`, corners `r`): the side whose edge is nearest, or the corner's own
// direction round a corner.
let mk_box_n = fn(p: vec2, c: vec2, h: vec2, r: float) -> vec2 {
let v = p - c
let s = vec2(step(0.0, v.x) * 2.0 - 1.0, step(0.0, v.y) * 2.0 - 1.0)
let rr = min(r, min(h.x, h.y))
let q = abs(v) - (h - vec2(rr, rr))
var n = vec2(0.0, s.y)
if q.x > q.y {
n = vec2(s.x, 0.0)
}
if q.x > 0.0 && q.y > 0.0 {
n = normalize(q) * s
}
return n
}
// How far round the edge of a rounded box `p` lies, in points, clockwise
// from the left end of its top side: the side whose edge is nearest, or the
// angle round a corner. A rim of loops counted on it runs on without a seam.
let mk_perim = fn(p: vec2, c: vec2, h: vec2, r: float) -> float {
let v = p - c
let rr = min(r, min(h.x, h.y))
let hx = h.x - rr
let hy = h.y - rr
let q = abs(v) - vec2(hx, hy)
let arc = rr * 1.5707963
let th = atan2(max(q.y, 0.0), max(q.x, 0.0))
var s = 0.0
if q.x > q.y {
if v.x > 0.0 {
s = 2.0 * hx + arc + (v.y + hy)
} else {
s = 4.0 * hx + 3.0 * arc + 2.0 * hy + (hy - v.y)
}
} else {
if v.y < 0.0 {
s = v.x + hx
} else {
s = 2.0 * hx + 2.0 * arc + 2.0 * hy + (hx - v.x)
}
}
if q.x > 0.0 && q.y > 0.0 {
if v.x > 0.0 && v.y < 0.0 {
s = 2.0 * hx + (1.5707963 - th) * rr
}
if v.x > 0.0 && v.y > 0.0 {
s = 2.0 * hx + arc + 2.0 * hy + th * rr
}
if v.x < 0.0 && v.y > 0.0 {
s = 4.0 * hx + 2.0 * arc + 2.0 * hy + (1.5707963 - th) * rr
}
if v.x < 0.0 && v.y < 0.0 {
s = 4.0 * hx + 3.0 * arc + 4.0 * hy + th * rr
}
}
return s
}
// The whole way round that box.
let mk_perim_len = fn(h: vec2, r: float) -> float {
let rr = min(r, min(h.x, h.y))
return 4.0 * (h.x - rr) + 4.0 * (h.y - rr) + 2.0 * PI * rr
}
// The three-quarter arc a waiting button turns in place of its label.
let mk_spinner = fn(dst: vec4, p: vec2, c: vec2, rad: float, t: float, amt: float, stroke: float, ink: vec3) -> vec4 {
let sdf = Sdf2d.viewport(p)
let a0 = t * 2.0 * PI
sdf.arc_round_caps(c.x, c.y, rad, a0, a0 + 1.5 * PI, stroke)
sdf.fill(vec4(ink, amt))
return Finish.over(dst, sdf.result)
}
// ---------------------------------------------------------------- yarn
// ONE LEG OF A STITCH: a plump loop of yarn lying along a..b, a teardrop
// `r` thick, round at `a` and drawn to a point at `b`. Handed back: .x its
// height (1 on its spine, 0 at its edge), .y the light it takes from above
// and a little in front, with the twist of its plies in it, .z how far `p`
// lies outside it (points, negative inside).
let mk_leg = fn(p: vec2, a: vec2, b: vec2, r: float, ply: float) -> vec3 {
let ba = b - a
let ll = max(dot(ba, ba), 0.0001)
let t = clamp(dot(p - a, ba) / ll, 0.0, 1.0)
let ax = ba / sqrt(ll)
let off = p - a - ba * t
let rr = r * (0.86 + 0.55 * t - 0.92 * t * t)
let d = length(off)
let u = clamp(d / max(rr, 0.01), 0.0, 1.0)
let h = sqrt(max(1.0 - u * u, 0.0))
let n2 = off / max(d, 0.0001) * u
let s = dot(p - a, ax)
let side = off.x * ax.y - off.y * ax.x
let tw = sin((s - side * 1.3) * 6.2831853 / max(ply, 0.4))
let lit = n2.x * (0.0 - 0.35) + n2.y * (0.0 - 0.55) + h * 0.68 + tw * 0.04 * h
return vec3(h, lit, d - rr)
}
// ONE STITCH of stockinette, the one in column `col` and row `row`, at `p`
// measured from the middle of the top of its cell: a V of two leaf-shaped
// legs slanting in from the top corners of the cell to meet at its foot,
// which lies just past the cell's foot, over the top of the V under it (a
// row is six tenths of a stitch's width tall, so the V is short and wide).
// Each stitch is nudged by its own hash -- its foot to one side, its legs
// fatter or thinner, its yarn a shade lighter or darker -- and each row
// pulled a little tighter or looser than the next. Handed back: .x the
// height of whichever leg stands higher at `p`, .y its light, .z the shade
// of the stitch's yarn (round 1), .w how far `p` lies outside both legs.
let mk_stitch = fn(p0: vec2, col: float, row: float, sw: float) -> vec4 {
let p = p0 - vec2((Finish.hash(vec2(row, 9.1)) - 0.5) * 0.07 * sw, 0.0)
let h1 = mk_hash(vec2(col, row))
let h2 = fract(h1 * 7.13 + 0.31)
let h3 = fract(h1 * 13.71 + 0.57)
let foot = vec2((h1 - 0.5) * 0.11 * sw, sw * (0.60 + (h2 - 0.5) * 0.07))
let spread = sw * (0.33 + (h3 - 0.5) * 0.04)
let r = sw * (0.205 + (h2 - 0.5) * 0.03)
let top = sw * ((h1 - 0.5) * 0.05)
let ply = sw * 0.30
let a = mk_leg(p, vec2(0.0 - spread, top), foot - vec2(sw * 0.05, 0.0), r, ply)
let b = mk_leg(p, vec2(spread, top + (h3 - 0.5) * 0.05 * sw), foot + vec2(sw * 0.05, 0.0), r, ply)
let shade = 1.0 + (fract(h1 * 29.3 + 0.13) - 0.5) * 0.12 + (Finish.hash(vec2(row, 4.4)) - 0.5) * 0.07
// Where the two legs meet down the middle of the V they part in a
// crease.
let crease = (1.0 - smoothstep(0.0, 0.45, abs(a.x - b.x))) * smoothstep(0.0, 0.2, min(a.x, b.x))
let sc = shade * (1.0 - 0.38 * crease)
var res = vec4(a.x, a.y, sc, min(a.z, b.z))
if b.x > a.x {
res = vec4(b.x, b.y, sc * 0.97, min(a.z, b.z))
}
return res
}
// THE STOCKINETTE at `q0` (points), stitches `sw` wide and `sh` tall, its
// columns wavering by up to `warp` points so no column is a ruled line.
// A point is reached by the legs of its own stitch and by those of the
// stitch over it (near the top of its cell) or under it (near its foot).
// The upper V lies over the top of the one under it and lays a shade on
// it along its edge, so every row reads as a row of chevrons. Handed back:
// .x the height of the yarn (0 in the gaps), .y the light it takes, .z the
// shade of its yarn (round 1, the cast shade in it), .w unused.
let mk_knit = fn(q0: vec2, sw: float, sh: float, warp: float) -> vec4 {
let wx = Finish.noise(q0 / (sh * 8.0)) - 0.5
let wy = sin(q0.x * 0.071 + q0.y * 0.013 + sin(q0.y * 0.043) * 2.0) * 0.25
let q = q0 + vec2(wx, wy) * warp
let col = floor(q.x / sw)
let row = floor(q.y / sh)
let lx = q.x - (col + 0.5) * sw
let ly = q.y - row * sh
let nb = step(sh * 0.5, ly) * 2.0 - 1.0
let cur = mk_stitch(vec2(lx, ly), col, row, sw)
let nbr = mk_stitch(vec2(lx, ly - nb * sh), col, row + nb, sw)
// Above the middle of the cell the neighbour is the stitch over this
// one; below it, the stitch under.
let up = step(0.0, nb)
let upper = mix(nbr, cur, up)
let lower = mix(cur, nbr, up)
let cast = 1.0 - 0.42 * exp(0.0 - max(upper.w, 0.0) / (0.09 * sw))
var res = vec4(lower.x, lower.y, lower.z * cast, 0.0)
let over = smoothstep(0.0, 0.28, upper.x)
res = mix(res, vec4(upper.x, upper.y, upper.z, 0.0), over)
return res
}
// The colour of yarn `col` at a point of fabric `k` (mk_knit's result):
// each leg lit by the way it faces, its plies a little lighter and darker,
// falling into the shade of `gap` toward its edge; in the gaps loose fibre
// catching a little light; over all a fibre's grain at the device pixel.
let mk_yarn = fn(col: vec3, gap: vec3, k: vec4, q: vec2, px: float, fuzz: float) -> vec3 {
let h = k.x
let y = col * k.z * (0.78 + 0.32 * clamp(k.y, -0.4, 1.0))
let cell = floor(q / px)
let hair = Finish.hash(cell + vec2(0.7, 3.9))
let g = mix(gap, col * 0.85, fuzz * hair * hair * 0.55)
var c = mix(g, y, smoothstep(0.0, 0.42, h))
c = c * (1.0 + (Finish.hash(cell * 1.37 + vec2(3.3, 1.7)) - 0.5) * 0.07)
return max(c, vec3(0.0, 0.0, 0.0))
}
// The same yarn knitted on a patch that carries words: its stitches kept
// softer the paler the yarn, so dark words on it stay clear of the knit.
let mk_soft = fn(col: vec3, y: vec3) -> vec3 {
let amt = mix(1.0, 0.400, smoothstep(0.15, 0.55, Finish.lum(col)))
return mix(col * 0.95, y, amt)
}
// A TWISTED CORD of two plies, at `s` along it and `a` across it (-1 at
// the side toward the light, 1 at the other), its plies `period` apart
// and slanting across it. .x its height, .y the light it takes.
let mk_ply = fn(s: float, a: float, period: float) -> vec2 {
let f = fract((s + a * period * 0.6) / max(period, 0.3))
let g = (f - 0.5) * 2.0
let across = sqrt(max(1.0 - a * a, 0.0))
let bump = sqrt(max(1.0 - g * g, 0.0))
let h = across * (0.45 + 0.55 * bump)
let lit = clamp((0.0 - a) * 0.75 - g * 0.35 * across + 0.15, -1.0, 1.0)
return vec2(h, lit)
}
// A RUNNING STITCH along a line: coverage of dashes `len` long, `gap`
// apart, at `s` along and `t` across (points), `w` thick, their ends
// round. .x coverage, .y the light along the dash's top.
let mk_running = fn(s: float, t: float, len: float, gap: float, w: float, px: float) -> vec2 {
let pitch = len + gap
let f = s - pitch * floor(s / pitch) - len * 0.5 - gap * 0.5
let dx = max(abs(f) - len * 0.5 + w * 0.5, 0.0)
let d = length(vec2(dx, t)) - w * 0.5
let cov = Finish.cover(d, px)
let lit = clamp(0.0 - t / max(w * 0.5, 0.2), -1.0, 1.0)
return vec2(cov, lit)
}
// ---------------------------------------------------------------- the fabric
// K1 P1 RIBBING at `q` with its ribs running along `along` (1 down the
// window, 0 across it): a column of narrow knit V's, then a sunk column of
// purl bumps, by turns. Handed back as mk_knit's result.
let mk_rib = fn(q0: vec2, sw: float, sh: float, along: float) -> vec4 {
let q = mix(q0.yx, q0, along)
var k = mk_knit(q, sw, sh, 0.4)
let colf = q.x / sw
let purl = step(0.5, fract(colf * 0.5))
// A purl column: a bead of yarn lying across each row, sunk.
let ly = q.y - sh * floor(q.y / sh) - sh * 0.5
let lx = (fract(colf) - 0.5) * sw
let pb = mk_leg(vec2(lx, ly), vec2(0.0 - sw * 0.32, 0.0), vec2(sw * 0.32, 0.0), sh * 0.40, sw * 0.3)
let pk = vec4(pb.x * 0.8, pb.y * 0.7, 0.86, 0.0)
k = mix(k, pk, purl)
// The knit columns stand proud of the purl ones: shade at their sides.
let sd = abs(fract(colf) - 0.5) * 2.0
k = vec4(k.x, k.y - 0.35 * smoothstep(0.55, 1.0, sd) * (1.0 - purl), k.z * mix(1.0, 0.92, purl), k.w)
return k
}
// THE WINDOW'S GROUND at a window point `w` (points) in a window `size`
// points large. Stockinette in the sheet's yarn, its stitches 9.5 points
// wide and six tenths of that tall, lighter and darker in two slow clouds; a ribbed cuff along
// the top, where the window's caption stands, a narrower one down the sides
// and along the foot, each sewn to the fabric with a cord seam.
let mk_ground = fn(w: vec2, px: float, size: vec2) -> vec3 {
let W = size.x
let H = size.y
let tb = min(32.0, H * 0.1)
let sb = min(7.0, W * 0.02)
// The fabric.
let k = mk_knit(w, 9.500, 9.500 * 0.6, 2.0)
var col = mk_yarn(vec3(0.145, 0.353, 0.529), vec3(0.039, 0.145, 0.251), k, w, px, 0.700)
col = mix(vec3(0.145, 0.353, 0.529) * 0.93, col, 1.000)
col = col * (1.0 + mk_cloud(w) * 0.070)
// The cuffs: how far in from the window's edge, and which way the ribs
// run there (down the window along the top and the foot).
let din = min(min(w.x, W - w.x), H - w.y)
let in_top = step(w.y, tb)
let in_side = step(din, sb) * (1.0 - in_top)
let along = clamp(in_top + step(H - sb, w.y), 0.0, 1.0)
let rk = mk_rib(w, 4.2, 2.6, along)
let rc = mk_yarn(vec3(0.106, 0.267, 0.439), vec3(0.039, 0.145, 0.251) * 0.8, rk, w, px, 0.700)
let cuff = clamp(in_top + in_side, 0.0, 1.0)
col = mix(col, rc, cuff)
// The seams: a cord where each cuff is sewn to the fabric, its shadow
// lying a little way onto the fabric under it.
let dt = w.y - tb
let dsd = sb - din
var st = dt
var sa = w.x
if in_top < 0.5 && abs(dsd) < abs(dt) {
st = dsd
sa = w.y + w.x
}
let ds = abs(st)
let cord_w = 1.6
let ac = clamp(st / cord_w, -1.0, 1.0)
let pl = mk_ply(sa, ac, 2.6)
let on_cord = 1.0 - smoothstep(cord_w - px, cord_w + px * 0.5, ds)
let cc = vec3(0.075, 0.184, 0.314) * (0.55 + 0.55 * pl.x) * (0.85 + 0.35 * pl.y)
col = mix(col, col * 0.80, (1.0 - smoothstep(0.0, 3.0, ds - cord_w)) * (1.0 - cuff) * 0.6)
col = mix(col, cc, on_cord * smoothstep(0.05, 0.4, pl.x))
return col
}
// ---------------------------------------------------------------- the patch
// A KNITTED PATCH sewn onto the fabric: a box `lo`..`hi` with corners `rad`,
// knitted in `col`. Out to in: what it throws on the fabric (`lift` 1 a soft
// shadow falling a little down, 0 only a contact line); a faint haze of
// loose fibre past its edge, which is not a ruled line but frays a little
// along its length; the bound-off rim, a band of chain loops in `rimc`
// slanting round the edge, round across like a cord, parted from the body
// by a dark line; the body, stockinette finer than the ground's, padded so
// it bulges -- lit along the upper edge, falling into shade toward the foot
// and the sides. Hovered the yarn lightens; held the patch is pressed flat
// into the fabric: its shadow goes, its bulge turns over and it darkens.
// Focused, a running stitch of the accent yarn goes round it just outside.
// `seed` sets where the knit starts, so patches of other sizes read apart.
// Premultiplied.
let mk_patch = fn(p: vec2, lo: vec2, hi: vec2, rad: float, px: float, col: vec3, rimc: vec3, hover: float, down: float, focus: float, lift: float, seed: float) -> vec4 {
let c = (lo + hi) * 0.5
let h = max((hi - lo) * 0.5, vec2(0.5, 0.5))
let r = min(rad, min(h.x, h.y))
let d = Material.sd_box(p, c, h, r)
let up = 1.0 - down
let hh = hi.y - lo.y
// What it throws: a dense shade right under its foot and a soft one
// carrying on down; to the sides both stay inside half an 8 point gap.
let s = min(hh, 26.0)
let d1 = Finish.drop(p, c, h - vec2(0.03 * s, 0.0), r, vec2(0.0, 0.07 * s), 0.06 * s)
let d2 = Finish.drop(p, c, h - vec2(0.10 * s, 0.02 * s), r, vec2(0.0, 0.16 * s), 0.12 * s)
let drop = (1.0 - (1.0 - 0.70 * d1) * (1.0 - 0.55 * d2)) * lift * up * 0.550
let contact = Finish.drop(p, c, h, r, vec2(0.0, px), px * 0.8) * 0.5
let sha = 1.0 - (1.0 - drop) * (1.0 - contact)
var res = vec4(vec3(0.008, 0.039, 0.078) * sha, sha)
// Round the edge.
let n = mk_box_n(p, c, h, r)
let s_ed = mk_perim(p, c, h, r)
let fray = (Finish.noise(vec2(s_ed / 0.9, seed * 7.0)) - 0.5) * 0.55 + (Finish.hash(floor(p / px) + vec2(seed, 1.0)) - 0.5) * 0.3
let de = d - fray
let a = Finish.cover(de, px)
// The haze of loose fibre just past the edge.
let haze = exp(0.0 - max(de, 0.0) / 0.45) * (1.0 - a) * 0.22 * 0.700 * step(0.0, de) * step(de, 2.0)
res = Finish.over(res, vec4(col * 0.9, 1.0) * haze)
// The body.
let q = p - lo + vec2(seed * 37.0, seed * 11.0)
let k = mk_knit(q, 4.6, 2.8, 0.7)
var body = mk_soft(col, mk_yarn(col, col * 0.400, k, q, px, 0.700))
// The padding: lit along the upper edge, shaded at the foot; held, the
// light turns over and the patch darkens.
let bul = clamp(h.y * 0.9, 3.0, 10.0)
let e = clamp((0.0 - d) / bul, 0.0, 1.0)
let slope = 1.0 - e
var lt = mk_lit(n) * slope
lt = mix(lt, 0.0 - lt * 0.7, down)
body = body * (1.0 + 0.30 * lt - 0.22 * slope * slope + 0.05 * e * up)
body = body * (1.0 + 0.090 * hover * up) * (1.0 - 0.13 * down)
// Pressed into the fabric, the fabric round it overhangs its top.
let dent = Finish.inset(p, c, h, r, vec2(0.0, 1.8), 1.8)
body = mix(body, body * 0.55, dent * 0.75 * down)
// The rim: chain loops round the edge, their count whole so the chain
// closes on itself.
let rw = clamp(h.y * 0.26, 1.7, 3.0)
let per = mk_perim_len(h, r)
let nl = max(floor(per / (rw * 1.3) + 0.5), 4.0)
let period = per / nl
let u = clamp((0.0 - d) / rw, 0.0, 1.0)
let ac = u * 2.0 - 1.0
let f = fract(s_ed / period + u * 0.7)
let g = (f - 0.5) * 2.0
let loop_h = sqrt(max(1.0 - g * g, 0.0)) * sqrt(max(1.0 - ac * ac * 0.8, 0.0))
let rl = mk_lit(n) * (0.0 - ac) * 0.9 - g * 0.25
var rim = rimc * (0.50 + 0.62 * loop_h) * (0.9 + 0.35 * rl * mix(1.0, -0.6, down))
rim = rim * (1.0 + (Finish.hash(floor(p / px) + vec2(9.1, seed)) - 0.5) * 0.08)
rim = rim * (1.0 + 0.090 * hover * up) * (1.0 - 0.10 * down)
let in_rim = 1.0 - smoothstep(rw - px * 0.5, rw + px * 0.5, 0.0 - d)
var face = mix(body, rim, in_rim)
// The dark line where the rim is sewn to the body.
face = mix(face, face * 0.45, exp(0.0 - ((0.0 - d - rw) * (0.0 - d - rw)) / 0.18) * 0.65)
res = Finish.over(res, vec4(face * a, a))
// Focus: a running stitch of the accent yarn two points out.
let ts = d - 2.2
let rs = mk_running(s_ed, ts, 2.2, 1.4, 1.2, px)
let fc = vec3(1.000, 0.824, 0.353) * (0.8 + 0.35 * rs.y)
res = Finish.over(res, vec4(fc, 1.0) * rs.x * focus)
return res
}
// ---------------------------------------------------------------- buttons
// Every button is a padded patch: 26 points on a 13 point line, 12 points
// either side. Its shadow falls outside the box, so the box is the patch
// and nothing is paid in padding.
mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonFlat.min_height = 26.0
// Glyphs sit at 14 points so an icon never makes a patch taller than the
// row; an icon alone gets a square patch.
mod.widgets.ButtonFlat.icon_walk.width = 14.0
mod.widgets.ButtonIcon.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonFlatIcon.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonFlatterIcon.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 4.0 bottom: 4.0}
// The short rungs keep their own heights under the row minimum.
mod.widgets.ButtonXs.min_height = 0.0
mod.widgets.ButtonSm.min_height = 0.0
mod.widgets.ButtonXs.draw_text.text_style.font_size = 7.0
mod.widgets.ButtonFlat.draw_bg.border_radius = uniform(4.0)
mod.widgets.ButtonFlat.draw_bg.border_size = uniform(0.34)
mod.widgets.ButtonFlat.clip_x = false
mod.widgets.ButtonFlat.clip_y = false
mod.widgets.ButtonFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
// The patch and its states, shared by every button template; `kind` picks
// its yarn: 0 the plain patch (flat, small, large), 1 the default action, 2
// primary, 3 secondary, 4 tertiary (sewn flatter), 5 danger, 6 outline, 7
// flatter, 8 the patch that holds an icon alone. Disabled, every patch is
// knitted in the grey of a spent skein.
let mk_button = fn(pos: vec2, size: vec2, o: vec2, dpi: float, hover: float, down: float, disabled: float, focus: float, loading: float, anim: float, kind: float) -> vec4 {
let m = 12.0
let px = mk_px(dpi)
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(size.x, o.x, px), mk_snap(size.y, o.y, px))
let live = 1.0 - disabled
var col = vec3(0.502, 0.514, 0.553)
if kind > 0.5 && kind < 1.5 {
col = vec3(0.890, 0.843, 0.741)
}
if kind > 1.5 && kind < 2.5 {
col = vec3(0.914, 0.698, 0.227)
}
if kind > 2.5 && kind < 3.5 {
col = vec3(0.173, 0.573, 0.733)
}
if kind > 3.5 && kind < 4.5 {
col = vec3(0.482, 0.251, 0.710)
}
if kind > 4.5 && kind < 5.5 {
col = vec3(0.816, 0.173, 0.239)
}
if kind > 5.5 && kind < 6.5 {
col = vec3(0.890, 0.843, 0.741)
}
if kind > 6.5 && kind < 7.5 {
col = vec3(0.502, 0.514, 0.553)
}
if kind > 7.5 {
col = vec3(0.890, 0.843, 0.741)
}
var lift = 1.0
if kind > 3.5 && kind < 4.5 {
lift = 0.6
}
col = mix(col, vec3(0.235, 0.271, 0.325), disabled)
let rimc = col * 0.840
let seed = fract(size.x * 0.0137 + size.y * 0.071)
var res = mk_patch(p, lo, hi, min((hi.y - lo.y) * 0.24, 6.0), px, col, rimc, hover * live, down * live, focus * live, lift * mix(1.0, 0.5, disabled), seed)
if kind > 5.5 && kind < 6.5 {
// The outline: a running stitch of the patch's ink sewn round
// inside its rim.
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let r = min(min((hi.y - lo.y) * 0.24, 6.0), min(h.x, h.y))
let d = Material.sd_box(p, c, h, r)
let rw = clamp(h.y * 0.26, 1.7, 3.0)
let rs = mk_running(mk_perim(p, c, h, r), d + rw + 2.0, 2.0, 1.5, 1.0, px)
res = Finish.over(res, vec4(vec3(0.118, 0.145, 0.200) * (0.85 + 0.25 * rs.y), 1.0) * rs.x * 0.8 * live)
}
if loading > 0.001 {
let c = (lo + hi) * 0.5
let rad = max(min(c.x - lo.x, c.y - lo.y) - 7.0, 2.0)
res = mk_spinner(res, p, c, rad, anim, loading, 1.5, vec3(0.965, 0.941, 0.886))
}
return res
}
mod.widgets.ButtonFlat.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 0.0), 12.0)
}
mod.widgets.Button.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 1.0), 12.0)
}
mod.widgets.ButtonIcon.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 8.0), 12.0)
}
mod.widgets.ButtonPrimary.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 2.0), 12.0)
}
mod.widgets.ButtonSecondary.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 3.0), 12.0)
}
mod.widgets.ButtonTertiary.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 4.0), 12.0)
}
mod.widgets.ButtonDanger.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 5.0), 12.0)
}
mod.widgets.ButtonOutline.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 6.0), 12.0)
}
mod.widgets.ButtonFlatter.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 7.0), 12.0)
}
// Label ink per state: each yarn its own ink, every disabled patch dim.
mod.widgets.ButtonFlat.draw_text.color = #f6f0e2
mod.widgets.ButtonFlat.draw_text.color_hover = #f6f0e2
mod.widgets.ButtonFlat.draw_text.color_down = #f6f0e2
mod.widgets.ButtonFlat.draw_text.color_focus = #f6f0e2
mod.widgets.ButtonFlat.draw_text.color_disabled = #8c95a3
mod.widgets.ButtonFlat.draw_icon.color = #f6f0e2
mod.widgets.Button.draw_text.color = #1e2533
mod.widgets.Button.draw_text.color_hover = #1e2533
mod.widgets.Button.draw_text.color_down = #1e2533
mod.widgets.Button.draw_text.color_focus = #1e2533
mod.widgets.Button.draw_text.color_disabled = #8c95a3
mod.widgets.ButtonPrimary.draw_text.color = #2a2010
mod.widgets.ButtonPrimary.draw_text.color_hover = #2a2010
mod.widgets.ButtonPrimary.draw_text.color_down = #2a2010
mod.widgets.ButtonPrimary.draw_text.color_focus = #2a2010
mod.widgets.ButtonPrimary.draw_text.color_disabled = #8c95a3
mod.widgets.ButtonPrimary.draw_icon.color = #2a2010
mod.widgets.ButtonSecondary.draw_text.color = #f6f0e2
mod.widgets.ButtonSecondary.draw_text.color_hover = #f6f0e2
mod.widgets.ButtonSecondary.draw_text.color_down = #f6f0e2
mod.widgets.ButtonSecondary.draw_text.color_focus = #f6f0e2
mod.widgets.ButtonSecondary.draw_text.color_disabled = #8c95a3
mod.widgets.ButtonSecondary.draw_icon.color = #f6f0e2
mod.widgets.ButtonTertiary.draw_text.color = #f6f0e2
mod.widgets.ButtonTertiary.draw_text.color_hover = #f6f0e2
mod.widgets.ButtonTertiary.draw_text.color_down = #f6f0e2
mod.widgets.ButtonTertiary.draw_text.color_focus = #f6f0e2
mod.widgets.ButtonTertiary.draw_text.color_disabled = #8c95a3
mod.widgets.ButtonTertiary.draw_icon.color = #f6f0e2
mod.widgets.ButtonDanger.draw_text.color = #f6f0e2
mod.widgets.ButtonDanger.draw_text.color_hover = #f6f0e2
mod.widgets.ButtonDanger.draw_text.color_down = #f6f0e2
mod.widgets.ButtonDanger.draw_text.color_focus = #f6f0e2
mod.widgets.ButtonDanger.draw_text.color_disabled = #8c95a3
mod.widgets.ButtonDanger.draw_icon.color = #f6f0e2
mod.widgets.ButtonOutline.draw_text.color = #1e2533
mod.widgets.ButtonOutline.draw_text.color_hover = #1e2533
mod.widgets.ButtonOutline.draw_text.color_down = #1e2533
mod.widgets.ButtonOutline.draw_text.color_focus = #1e2533
mod.widgets.ButtonOutline.draw_text.color_disabled = #8c95a3
mod.widgets.ButtonOutline.draw_icon.color = #1e2533
mod.widgets.ButtonFlatter.draw_text.color = #f6f0e2
mod.widgets.ButtonFlatter.draw_text.color_hover = #f6f0e2
mod.widgets.ButtonFlatter.draw_text.color_down = #f6f0e2
mod.widgets.ButtonFlatter.draw_text.color_focus = #f6f0e2
mod.widgets.ButtonFlatter.draw_text.color_disabled = #8c95a3
mod.widgets.ButtonFlat.draw_icon_end.color = #f6f0e2
// An icon takes its patch's ink, whatever ink its caller gave it: a glyph
// of the wrong ink is lost on the yarn. The icon on its own patch is the
// star of the favourites, in its own yarn.
mod.widgets.Button.draw_icon.get_color = fn() {
let base = self.eval_gradient()
return vec4(vec3(0.949, 0.722, 0.188) * base.a, base.a) * self.opacity
}
mod.widgets.ButtonIcon.draw_icon.get_color = fn() {
let base = self.eval_gradient()
return vec4(vec3(0.949, 0.722, 0.188) * base.a, base.a) * self.opacity
}
// The labels are set in the round face's medium weight.
mod.widgets.ButtonFlat.draw_text.text_style = mod.theme.font_label{}
mod.widgets.Button.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonPrimary.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonSecondary.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonTertiary.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonDanger.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonOutline.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonFlatter.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonSm.draw_text.text_style = mod.theme.font_label{font_size: 7.5}
mod.widgets.ButtonLg.draw_text.text_style = mod.theme.font_label{font_size: 10.0}
// ---------------------------------------------------------------- balls and cords
// A KNITTED BALL of radius `r` at `c` in `col`: a little stuffed ball of
// stockinette, its stitches crowding toward its edge as they turn away,
// lit from the upper left and a little in front, a fibre fray at its edge;
// it throws a soft round shadow (`lift`). Premultiplied over `dst`.
let mk_ball = fn(dst: vec4, p: vec2, c: vec2, r: float, px: float, col: vec3, hover: float, down: float, lift: float) -> vec4 {
let v = p - c
let q = v / max(r, 0.5)
let rl = length(q)
let sh = Finish.drop(p, c, vec2(r, r), r, vec2(0.0, 0.16 * r + 0.4), 0.2 * r + 0.4) * 0.65 * 0.550 * lift * (1.0 - down)
var res = Finish.over(dst, vec4(vec3(0.008, 0.039, 0.078) * sh, sh))
let z = sqrt(max(1.0 - rl * rl, 0.0))
let kq = v / (0.45 + 0.55 * z) + vec2(r * 3.0, r * 1.7)
let sw = max(r * 0.6, 2.8)
let k = mk_knit(kq, sw, sw * 0.6, 0.3)
var y = mk_yarn(col, col * 0.400, k, kq, px, 0.700)
let lit = q.x * (0.0 - 0.42) + q.y * (0.0 - 0.62) + z * 0.66
y = y * (0.52 + 0.62 * clamp(lit, -0.25, 1.0)) * (1.0 + 0.090 * hover) * (1.0 - 0.12 * down)
let fray = (Finish.hash(floor(p / px) + vec2(4.4, 0.3)) - 0.5) * 0.35
let a = Finish.cover(length(v) - r - fray, px)
return Finish.over(res, vec4(y * a, a))
}
// AN I-CORD lying from `lo` to `hi` (a pill along x when `vert` is 0,
// along y when 1): a tube knitted in `col`, one column of stockinette
// running along it so its V's point along the cord, round across and lit
// along the side toward the upper left, a little fibre fraying its edge;
// `shade` how much shadow it lays under itself. Premultiplied over `dst`.
let mk_cord = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, col: vec3, shade: float, vert: float) -> vec4 {
let c = (lo + hi) * 0.5
let h = max((hi - lo) * 0.5, vec2(0.3, 0.3))
let d = Finish.sd_pill(p, c, h)
let rad = min(h.x, h.y)
let sh = Finish.drop(p, c, h, rad, vec2(0.0, 0.3 * rad + 0.3), 0.35 * rad + 0.3) * 0.55 * 0.550 * shade
var res = Finish.over(dst, vec4(vec3(0.008, 0.039, 0.078) * sh, sh))
let v = p - c
let s = mix(v.x, 0.0 - v.y, vert)
let a = clamp(mix(v.y, v.x, vert) / rad, -1.0, 1.0)
let kq = vec2(a * rad * 0.94 + rad, s + 13.0)
let k = mk_knit(kq, rad * 2.0, rad * 1.25, 0.0)
var y = mk_yarn(col, col * 0.400, k, p, px, 0.700)
let z = sqrt(max(1.0 - a * a, 0.0))
y = y * (0.50 + 0.55 * clamp((0.0 - a) * 0.55 + z * 0.6, -0.2, 1.0))
let fray = (Finish.hash(floor(p / px) + vec2(8.4, 2.3)) - 0.5) * 0.3
let al = Finish.cover(d - fray, px)
return Finish.over(res, vec4(y * al, al))
}
// A STITCHED MARK: a line of satin stitch a..b `w` thick in `col`, its
// threads lying slant across it so it reads as sewn, with the shade it
// throws a device pixel under it. Premultiplied over `dst`.
let mk_satin = fn(dst: vec4, p: vec2, a: vec2, b: vec2, w: float, px: float, col: vec3, amt: float) -> vec4 {
let d = mk_seg(p, a, b) - w * 0.5
let ds = mk_seg(p - vec2(0.0, px * 1.2), a, b) - w * 0.5
var res = Finish.over(dst, vec4(0.0, 0.0, 0.0, 1.0) * Finish.cover(ds, px) * 0.35 * amt)
let ba = normalize(b - a + vec2(0.0001, 0.0))
let t = dot(p - a, ba)
let x = (p.x - a.x) * ba.y - (p.y - a.y) * ba.x
let thr = fract((t * 0.8 + x * 1.2) / 1.1)
let sat = col * (0.84 + 0.26 * sqrt(max(1.0 - (thr * 2.0 - 1.0) * (thr * 2.0 - 1.0), 0.0)))
return Finish.over(res, vec4(sat, 1.0) * Finish.cover(d, px) * amt)
}
// ---------------------------------------------------------------- selection
// The quad of a small control grows by this much so its shadow and focus
// stitch can fall outside the box.
mod.widgets.CheckBoxFlat.draw_bg.vertex = fn() {
let m = 6.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.CheckBoxFlat.clip_x = false
mod.widgets.CheckBoxFlat.clip_y = false
mod.widgets.CheckBoxFlat.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
mod.widgets.CheckBoxFlat.draw_bg.size = uniform(17.0)
mod.widgets.CheckBoxFlat.draw_bg.border_radius = uniform(3.0)
mod.widgets.CheckBoxFlat.label_walk.margin.left = 17.0
// The check box: a 17 point patch; set, it is knitted in the accent yarn and
// a check is sewn on it in satin stitch.
mod.widgets.CheckBoxFlat.draw_bg.pixel = fn() {
let m = 6.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let sz = self.size
let x0 = (self.rect_size.x - sz) * self.mark_at_end
let y0 = self.rect_size.y * 0.5 - sz * 0.5
let lo = vec2(mk_snap(x0, o.x, px), mk_snap(y0, o.y, px))
let hi = vec2(mk_snap(x0 + sz, o.x, px), mk_snap(y0 + sz, o.y, px))
let c = (lo + hi) * 0.5
let on = max(self.active, self.mixed)
let held = max(self.down, self.pressed)
let live = 1.0 - self.disabled
var col = mix(vec3(0.075, 0.165, 0.271), vec3(0.914, 0.698, 0.227), on)
var rimc = mix(vec3(0.890, 0.843, 0.741), vec3(0.914, 0.698, 0.227) * 0.840, on)
col = mix(col, vec3(0.235, 0.271, 0.325), self.disabled)
rimc = mix(rimc, vec3(0.235, 0.271, 0.325) * 0.840, self.disabled)
var res = mk_patch(p, lo, hi, 4.0, px, col, rimc, self.hover * live, held, self.focus * live, 0.8, 0.31)
// The mark: a check 9 points across in satin stitch; mixed, a bar.
let cm = c + vec2(0.0, px * 0.5 * held)
let kk = sz / 14.0
let ink = mix(vec3(0.165, 0.125, 0.063), vec3(0.549, 0.584, 0.639), self.disabled)
let ck = (1.0 - self.mixed) * self.active
res = mk_satin(res, p, cm + vec2(-3.4, 0.1) * kk, cm + vec2(-1.0, 2.6) * kk, 2.0 * kk, px, ink, ck)
res = mk_satin(res, p, cm + vec2(-1.0, 2.6) * kk, cm + vec2(3.6, -2.8) * kk, 2.0 * kk, px, ink, ck)
res = mk_satin(res, p, cm + vec2(-3.2, 0.0) * kk, cm + vec2(3.2, 0.0) * kk, 2.1 * kk, px, ink, self.mixed)
let d = Material.sd_box(p, c, (hi - lo) * 0.5, 4.0)
return Finish.over(res, vec4(0.94, 0.25, 0.22, 1.0) * Finish.band(d, 0.0, px, px) * self.error)
}
// THE TOGGLE: a 36 x 19 patch of the dark yarn, a square knob of the knob
// yarn sewn in it, half its length. On, the accent yarn fills the housing
// from its left end up to the knob, which stands at its right; off, the
// housing is bare and the knob at its left.
mod.widgets.ToggleFlat.draw_bg.size = uniform(19.0)
mod.widgets.ToggleFlat.draw_bg.pill_aspect = uniform(1.9)
mod.widgets.ToggleFlat.label_walk.margin.left = 36.0
mod.widgets.ToggleFlat.draw_bg.pixel = fn() {
let m = 6.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let tw = self.size * self.pill_aspect
let th = self.size
let x0 = (self.rect_size.x - tw) * self.mark_at_end
let y0 = self.rect_size.y * 0.5 - th * 0.5
let lo = vec2(mk_snap(x0, o.x, px), mk_snap(y0, o.y, px))
let hi = vec2(mk_snap(x0 + tw, o.x, px), mk_snap(y0 + th, o.y, px))
let live = 1.0 - self.disabled
let tcol = mix(vec3(0.227, 0.255, 0.314), vec3(0.235, 0.271, 0.325), self.disabled * 0.7)
var res = mk_patch(p, lo, hi, 5.0, px, tcol, tcol * 0.840, 0.0, 0.0, self.focus * live, 0.7, 0.53)
// The knob travels the housing.
let kt = mix(self.active, self.drag_pos, self.drag)
let ins = 2.4
let ks = (hi.x - lo.x - ins * 2.0) * 0.5
let kx0 = mix(lo.x + ins, hi.x - ins - ks, kt)
let klo = vec2(Finish.snap(o.x + kx0, px) - o.x, lo.y + ins)
let khi = vec2(klo.x + ks, hi.y - ins)
// On, the accent yarn fills the housing up to the knob.
let fc = mix(vec3(0.541, 0.294, 0.769), vec3(0.235, 0.271, 0.325), self.disabled)
let fill = mk_patch(p, vec2(lo.x + ins, lo.y + ins), vec2(max(klo.x + 1.0, lo.x + ins + 1.0), hi.y - ins), 3.0, px, fc, fc * 0.840, 0.0, 0.0, 0.0, 0.0, 0.77)
res = Finish.over(res, fill * smoothstep(0.0, 0.3, kt))
let kc = mix(vec3(0.914, 0.867, 0.773), vec3(0.235, 0.271, 0.325), self.disabled * 0.6)
res = Finish.over(res, mk_patch(p, klo, khi, 3.5, px, kc, kc * 0.840, self.hover * live, max(self.pressed, self.down), 0.0, 0.8, 0.19))
return res * mix(1.0, 0.7, self.disabled)
}
// THE RADIO: a round patch sewn to the fabric, in its rim of loops;
// chosen, it is knitted in the accent.
mod.widgets.RadioButtonFlat.draw_bg.vertex = fn() {
let m = 6.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RadioButtonFlat.clip_x = false
mod.widgets.RadioButtonFlat.clip_y = false
mod.widgets.RadioButtonFlat.draw_bg.size = uniform(17.0)
mod.widgets.RadioButtonFlat.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
mod.widgets.RadioButtonFlat.label_walk.margin.left = 17.0
mod.widgets.RadioButtonFlat.draw_bg.pixel = fn() {
let m = 6.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let sz = self.size
let y0 = self.rect_size.y * 0.5 - sz * 0.5
let c = vec2(Finish.snap(o.x + sz * 0.5, px * 0.5) - o.x, Finish.snap(o.y + y0 + sz * 0.5, px * 0.5) - o.y)
let on = self.active
let live = 1.0 - self.disabled
let R = sz * 0.5
var col = mix(vec3(0.847, 0.796, 0.698), vec3(0.914, 0.698, 0.227), on)
col = mix(col, vec3(0.235, 0.271, 0.325), self.disabled)
let rimc = mix(col * 0.840, vec3(0.890, 0.843, 0.741), 0.000 * live)
return mk_patch(p, c - vec2(R, R), c + vec2(R, R), R, px, col, rimc, self.hover * live, self.down, self.focus * live, 0.8, 0.71)
}
// The radio tab: a patch; chosen, it is the accent yarn, pressed into the
// fabric, its word in the accent's ink.
mod.widgets.RadioButtonTabFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RadioButtonTabFlat.clip_x = false
mod.widgets.RadioButtonTabFlat.clip_y = false
mod.widgets.RadioButtonTabFlat.padding = mod.turtle.Inset{left: 0.0 right: 10.0 top: 4.0 bottom: 4.0}
mod.widgets.RadioButtonTabFlat.min_height = 26.0
mod.widgets.RadioButtonTabFlat.draw_bg.pixel = fn() {
let m = 12.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let sel = self.active
let live = 1.0 - self.disabled
var col = mix(vec3(0.086, 0.153, 0.243), vec3(0.914, 0.698, 0.227), sel)
col = mix(col, vec3(0.235, 0.271, 0.325), self.disabled)
return mk_patch(p, lo, hi, 7.0, px, col, col * 0.840, self.hover * live, max(self.down, sel), self.focus * live, 1.0, 0.41)
}
mod.widgets.RadioButtonTabFlat.draw_text.color = #efe6d2
mod.widgets.RadioButtonTabFlat.draw_text.color_hover = #efe6d2
mod.widgets.RadioButtonTabFlat.draw_text.color_down = #efe6d2
mod.widgets.RadioButtonTabFlat.draw_text.color_focus = #efe6d2
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #2a2010
mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #8c95a3
// ---------------------------------------------------------------- sliders
// The slider's track: a cord of the dark yarn, then the value as a cord of
// the slider's yarn laid over it from the start to the thumb.
let mk_track = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, f0: float, f1: float, px: float, col: vec3, disabled: float) -> vec4 {
var res = mk_cord(dst, p, lo, hi, px, mix(vec3(0.059, 0.094, 0.157), vec3(0.235, 0.271, 0.325), disabled * 0.5), 1.0, 0.0)
if f1 - f0 > 0.01 {
let tl = vec2(f0, lo.y)
let th = vec2(max(f1, f0 + hi.y - lo.y), hi.y)
res = mk_cord(res, p, tl, th, px, mix(col, vec3(0.235, 0.271, 0.325), disabled * 0.8), 0.3, 0.0)
}
return res
}
// The thumb: a knitted ball, of the thumb yarn or of the slider's own.
let mk_thumb_col = fn(col: vec3, disabled: float) -> vec3 {
return mix(mix(vec3(0.914, 0.867, 0.773), col, 1.000), vec3(0.235, 0.271, 0.325), disabled * 0.7)
}
// The bar slider: an 8 point i-cord under its label row, the value's
// cord, and a 16 point knitted ball riding it.
mod.widgets.SliderFlat.clip_x = false
mod.widgets.SliderFlat.clip_y = false
mod.widgets.SliderFlat.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
let mk_bar_slider = fn(pos: vec2, size: vec2, o: vec2, dpi: float, slide: float, origin: float, arc_origin: float, hover: float, held: float, disabled: float, col: vec3) -> vec4 {
let m = 7.0
let px = mk_px(dpi)
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let hs = 8.0
let cy = mk_snap(20.0 + (size.y - 20.0) * 0.5, o.y, px)
let lo = vec2(mk_snap(0.0, o.x, px), cy - 4.0)
let hi = vec2(mk_snap(size.x, o.x, px), cy + 4.0)
let span = size.x - hs * 2.0
let hx = hs + slide * span
let ox = hs + origin * span
let f0 = mix(lo.x + px, min(ox, hx), arc_origin)
let f1 = mix(hx, max(ox, hx), arc_origin)
let res = mk_track(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, f0, f1, px, col, disabled)
return mk_ball(res, p, vec2(Finish.snap(o.x + hx, px) - o.x, cy), hs, px, mk_thumb_col(col, disabled), hover * (1.0 - disabled), held * 0.4, 1.0)
}
mod.widgets.SliderFlat.draw_bg.pixel = fn() {
return mk_bar_slider(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.slide_pos, self.origin_pos, self.arc_origin, self.hover, max(self.drag, self.focus), self.disabled, vec3(0.275, 0.737, 0.902))
}
// A slider's value is a text input, and the field row's 26 point minimum
// pushed it half a row below its label: here it is only as tall as its line.
mod.widgets.SliderMinimal.text_input.min_height = 0.0
mod.widgets.SliderMinimal.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.SliderFlat.text_input.min_height = 0.0
mod.widgets.SliderFlat.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.Slider.text_input.min_height = 0.0
mod.widgets.Slider.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.SliderRoundFlat.text_input.min_height = 0.0
mod.widgets.SliderRoundFlat.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.SliderRound.text_input.min_height = 0.0
mod.widgets.SliderRound.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
// A dial's value box stands in the 20 point legend row above the ring: its
// line and two points either side, so it clears the ring.
mod.widgets.RotaryFlat.text_input.min_height = 0.0
mod.widgets.RotaryFlat.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0}
mod.widgets.Rotary.text_input.min_height = 0.0
mod.widgets.Rotary.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0}
mod.widgets.RotaryGradientY.text_input.min_height = 0.0
mod.widgets.RotaryGradientY.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0}
// The minimal slider: only the cords, thinner, in the minimal's yarn; a
// small ball shows under the hand.
mod.widgets.SliderMinimal.clip_x = false
mod.widgets.SliderMinimal.clip_y = false
mod.widgets.SliderMinimal.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SliderMinimal.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let cy = mk_snap(self.offset_y + (self.rect_size.y - self.offset_y) * 0.5, o.y, px)
let lo = vec2(mk_snap(0.0, o.x, px), cy - 2.8)
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), cy + 2.8)
let lo2 = min(self.origin_pos, self.slide_pos)
let hi2 = max(self.origin_pos, self.slide_pos)
let f0 = mix(0.0, lo2, self.arc_origin) * self.rect_size.x
let f1 = mix(self.slide_pos, hi2, self.arc_origin) * self.rect_size.x
let col = vec3(0.608, 0.361, 0.839)
let res = mk_track(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, max(f0, lo.x + px), min(f1, hi.x - px), px, col, self.disabled)
let hx = self.slide_pos * self.rect_size.x
let show = max(self.hover, self.drag)
let th = mk_ball(vec4(0.0, 0.0, 0.0, 0.0), p, vec2(hx, cy), 6.5, px, mk_thumb_col(col, self.disabled), 0.0, 0.0, 1.0)
return Finish.over(res, th * show)
}
// The round slider: a label column, then the bar slider's cords in the round
// slider's yarn and its ball. The track ends short of the right edge, so
// the value the widget writes there stands on the fabric.
mod.widgets.SliderRoundFlat.clip_x = false
mod.widgets.SliderRoundFlat.clip_y = false
mod.widgets.SliderRoundFlat.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SliderRoundFlat.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let hs = 8.0
let cy = mk_snap(self.rect_size.y * 0.5, o.y, px)
let x0 = mk_snap(self.label_size, o.x, px)
let lo = vec2(x0, cy - 4.0)
let hi = vec2(mk_snap(self.rect_size.x - 37.0, o.x, px), cy + 4.0)
let span = hi.x - lo.x - hs * 2.0
let hx = lo.x + hs + self.slide_pos * span
let col = vec3(0.941, 0.541, 0.165)
let res = mk_track(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, lo.x + px, hx, px, col, self.disabled)
return mk_ball(res, p, vec2(Finish.snap(o.x + hx, px) - o.x, cy), hs, px, mk_thumb_col(col, self.disabled), self.hover * (1.0 - self.disabled), max(self.drag, self.focus) * 0.4, 1.0)
}
// THE BAND is the sheet's slider with two thumbs: the same dark cord, the
// value's cord between the thumbs in the band's yarn, two knitted balls.
// The widget's grab geometry is set to the balls' own, so what is drawn is
// what is dragged.
mod.widgets.RangeSliderFlat.clip_x = false
mod.widgets.RangeSliderFlat.clip_y = false
mod.widgets.RangeSliderFlat.track_inset = 0.0
mod.widgets.RangeSliderFlat.handle_size = 16.0
mod.widgets.RangeSliderFlat.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RangeSliderFlat.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let hs = 8.0
let cy = mk_snap(self.label_px + (self.rect_size.y - self.label_px) * 0.5, o.y, px)
let lo = vec2(mk_snap(0.0, o.x, px), cy - 4.0)
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), cy + 4.0)
let span = self.rect_size.x - hs * 2.0
let h0 = hs + self.start_pos * span
let h1 = hs + self.end_pos * span
let col = vec3(0.886, 0.314, 0.541)
var res = mk_track(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, h0, h1, px, col, self.disabled)
let tc = mk_thumb_col(col, self.disabled)
res = mk_ball(res, p, vec2(Finish.snap(o.x + h0, px) - o.x, cy), hs, px, tc, self.hot_start, self.drag * self.hot_start * 0.4, 1.0)
return mk_ball(res, p, vec2(Finish.snap(o.x + h1, px) - o.x, cy), hs, px, tc, self.hot_end, self.drag * self.hot_end * 0.4, 1.0)
}
// THE FADER: a cord of the dark yarn laid along its travel with a scale of
// running stitches either side, and a felt block riding it: a padded patch
// of the fader yarn, a satin line across its middle as its index. Hovered
// it lightens; held it is pressed flat and its shadow goes; focused, the
// running stitch round it.
mod.widgets.SliderFader.cap_size = 26.0
mod.widgets.SliderFader.draw_bg.track_size = uniform(5.0)
mod.widgets.SliderFader.draw_bg.cap_cross = uniform(0.85)
mod.widgets.SliderFader.clip_x = false
mod.widgets.SliderFader.clip_y = false
mod.widgets.SliderFader.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SliderFader.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let sz = self.rect_size
let p = self.pos * (sz + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let v = self.is_vertical
let along = mix(sz.x, sz.y, v)
let across = mix(sz.y, sz.x, v)
let travel = max(along - self.inset_px * 2.0 - self.cap_px, 1.0)
let foot = self.inset_px + self.cap_px * 0.5
// Track space: `a` from the value's zero, `b` across.
let a = mix(p.x, sz.y - p.y, v)
let b = mix(p.y, p.x, v)
let mid = across * 0.5
// The cord, snapped on screen.
let slo = mix(vec2(self.inset_px, mid - 3.0), vec2(mid - 3.0, self.inset_px), v)
let shi = mix(vec2(sz.x - self.inset_px, mid + 3.0), vec2(mid + 3.0, sz.y - self.inset_px), v)
let lo = vec2(mk_snap(slo.x, o.x, px), mk_snap(slo.y, o.y, px))
let hi = vec2(mk_snap(shi.x, o.x, px), mk_snap(shi.y, o.y, px))
var res = mk_cord(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, px, vec3(0.059, 0.094, 0.157), 1.0, v)
// The scale: a running stitch either side of the cord, a stitch every
// 6 points.
let t1 = mk_running(a - foot + 1.0, b - (mid + 7.0), 2.0, 4.0, 1.3, px)
let t2 = mk_running(a - foot + 1.0, b - (mid - 7.0), 2.0, 4.0, 1.3, px)
let inr = step(foot - 2.0, a) * step(a, foot + travel + 2.0)
res = Finish.over(res, vec4(vec3(0.890, 0.843, 0.741) * (0.85 + 0.25 * t1.y), 1.0) * t1.x * inr * 0.9)
res = Finish.over(res, vec4(vec3(0.890, 0.843, 0.741) * (0.85 + 0.25 * t2.y), 1.0) * t2.x * inr * 0.9)
// The block, its leading end on the device pixel grid.
let live = 1.0 - self.disabled
let hsz = vec2(self.cap_px * 0.5 * 0.62, min(max(across * self.cap_cross, 8.0), across) * 0.5)
let at_free = foot + self.slide_pos * travel
let lead = Finish.snap(mix(o.x + at_free - hsz.x, o.y + sz.y - at_free - hsz.x, v), px)
let at = mix(lead - o.x + hsz.x, o.y + sz.y - lead - hsz.x, v)
let cc = mix(vec2(at, mid), vec2(mid, sz.y - at), v)
let hh = mix(hsz, hsz.yx, v)
let press = self.drag * live
let col = mix(vec3(0.914, 0.698, 0.227), vec3(0.235, 0.271, 0.325), self.disabled)
res = Finish.over(res, mk_patch(p, cc - hh, cc + hh, 3.5, px, col, col * 0.840, self.hover * live, press, self.focus * live, 1.0, 0.63))
// The index: a satin line across the middle of the block.
let ia = mix(vec2(cc.x, cc.y - hh.y + 3.5), vec2(cc.x - hh.x + 3.5, cc.y), v)
let ib = mix(vec2(cc.x, cc.y + hh.y - 3.5), vec2(cc.x + hh.x - 3.5, cc.y), v)
return mk_satin(res, p + vec2(0.0, 0.0 - px * 0.5 * press), ia, ib, 1.4, px, vec3(1.000, 0.965, 0.878), 1.0)
}
// ---------------------------------------------------------------- knobs
// A KNOB: a knitted dome in a ring of chunky loops. Out to in: (in sheets
// that have them) a scale of short stitches round it; what it throws on the
// fabric; the ring, a round of fat chain loops in the dark yarn, the loops
// the value covers knitted in the arc's yarn instead -- whole loops, as a
// knitter changes yarn; the cap, a padded dome of stockinette that turns
// with the value, lit from the upper left; a satin line from near its
// middle toward the value, its index. Angles run from straight down,
// clockwise, the gap at the bottom. `rr` is the knob's whole radius,
// stitches included; `cap` 0 leaves the ring bare.
let mk_knob = fn(p: vec2, c: vec2, rr: float, px: float, slide: float, origin: float, arc_origin: float, gap: float, hover: float, held: float, focus: float, disabled: float, capc: vec3, idxc: vec3, cap: float) -> vec4 {
let q = p - c
let rl = length(q)
let nk = q / max(rl, 0.001)
let on = 1.0 - disabled
let ticks = 1.000 * cap
let tl = clamp(rr * 0.13, 2.0, 6.0) * ticks
let ring_out = rr - tl - 1.0 * ticks
let ring_w = clamp(ring_out * 0.22, 3.0, 10.0)
let ring_in = ring_out - ring_w
let ring_mid = ring_out - ring_w * 0.5
let rc = ring_in + max(px, 0.4)
let one = PI / 180.0
let sweep = 2.0 * PI - gap * one
let start = gap * one * 0.5
let ve = start + sweep * slide
let oe = start + sweep * origin
var ang = atan2(0.0 - q.x, q.y)
if ang < 0.0 {
ang = ang + 2.0 * PI
}
var res = vec4(0.0, 0.0, 0.0, 0.0)
// The scale: eleven stitches across the sweep.
let nt = 10.0
let ta = clamp(ang, start, start + sweep)
let ti = floor((ta - start) / sweep * nt + 0.5)
let tang = start + ti / nt * sweep
let tdir = vec2(0.0 - sin(tang), cos(tang))
let perp = abs(q.x * tdir.y - q.y * tdir.x)
let alng = dot(q, tdir)
let tick_d = length(vec2(perp, max(abs(alng - (ring_out + 1.0 + tl * 0.5)) - tl * 0.35, 0.0))) - 0.7
res = Finish.over(res, vec4(vec3(0.890, 0.843, 0.741), 1.0) * Finish.cover(tick_d, px) * step(0.5, ticks) * mix(1.0, 0.5, disabled))
// What the knob throws on the fabric.
let s1 = Finish.drop(p, c + vec2(0.0, 0.07 * ring_out + 0.4), vec2(ring_out, ring_out), ring_out, vec2(0.0, 0.0), 0.08 * ring_out + 0.6) * 0.7 * 0.550
res = Finish.over(res, vec4(vec3(0.008, 0.039, 0.078) * s1, s1) * (1.0 - Finish.cover(rl - ring_out, px)))
// The ring of loops, their count whole.
let nl = max(floor(2.0 * PI * ring_mid / max(ring_w * 0.78, 2.4) + 0.5), 8.0)
let ac = clamp((rl - ring_mid) / (ring_w * 0.5), -1.0, 1.0)
let lf = ang / (2.0 * PI) * nl + ac * 0.28
let li = floor(lf)
let g = (fract(lf) - 0.5) * 2.0
let loop_h = sqrt(max(1.0 - g * g, 0.0)) * sqrt(max(1.0 - ac * ac * 0.85, 0.0))
let la = (li + 0.5) / nl * 2.0 * PI
let a0 = mix(start, min(oe, ve), arc_origin)
let a1 = mix(ve, max(oe, ve), arc_origin)
let lit_loop = step(a0, la) * step(la, a1) * step(0.002, a1 - a0) * on
var ry = mix(vec3(0.094, 0.157, 0.243), vec3(0.941, 0.627, 0.188), lit_loop)
let rlit = mk_lit(nk) * ac * 0.9 - g * 0.2
ry = ry * (0.48 + 0.62 * loop_h) * (0.9 + 0.32 * rlit)
ry = ry * (1.0 + (Finish.hash(floor(p / px) + vec2(2.7, 8.8)) - 0.5) * 0.08)
let fray = (Finish.hash(floor(p / px) + vec2(5.5, 1.1)) - 0.5) * 0.3
let ra = Finish.cover(rl - ring_out - fray, px) * mix(Finish.cover(ring_in - rl - fray, px), 1.0, cap)
res = Finish.over(res, vec4(ry * ra, ra))
// The cap's shade on the ring.
let cs = Finish.drop(p, c + vec2(0.3, 0.9), vec2(rc, rc), rc, vec2(0.0, 0.0), 1.0) * 0.45 * (1.0 - Finish.cover(rl - rc, px)) * cap
res = Finish.over(res, vec4(0.0, 0.0, 0.0, cs))
// The cap: a dome of stockinette turned with the value.
let ca = cos(ve)
let sa = sin(ve)
let kq = vec2(q.x * ca + q.y * sa, q.y * ca - q.x * sa)
let z = sqrt(max(1.0 - (rl / max(rc, 0.5)) * (rl / max(rc, 0.5)), 0.0))
let kw = kq / (0.55 + 0.45 * z) + vec2(rc * 2.3, rc * 1.9)
let sw = clamp(rc * 0.24, 3.0, 6.5)
let k = mk_knit(kw, sw, sw * 0.6, 0.4)
let ccol = mix(capc, vec3(0.235, 0.271, 0.325), disabled * 0.6)
var cy = mk_yarn(ccol, ccol * 0.400, k, kw, px, 0.700)
let lit = nk.x * (1.0 - z) * (0.0 - 0.45) + nk.y * (1.0 - z) * (0.0 - 0.65) + z * 0.62
cy = cy * (0.55 + 0.55 * clamp(lit, -0.3, 1.0)) * (1.0 + 0.090 * hover) * (1.0 - 0.08 * held)
// A darker round where the cap is bound off.
cy = mix(cy, cy * 0.6, Finish.band(rl - rc, 0.0, max(rc * 0.06, 0.8), px) * 0.7)
let kfray = (Finish.hash(floor(p / px) + vec2(7.3, 4.2)) - 0.5) * 0.3
let ka = Finish.cover(rl - rc - kfray, px) * cap
res = Finish.over(res, vec4(cy * ka, ka))
// The index: a satin line from near the middle toward the value.
let vd = vec2(0.0 - sin(ve), cos(ve))
let iw = clamp(rc * 0.11, 1.4, 3.6)
res = mk_satin(res, p, c + vd * (rc * 0.22), c + vd * (rc * 0.78), iw, px, mix(idxc, vec3(0.549, 0.584, 0.639), disabled), cap)
// Focus: a running stitch of the accent round the ring.
let fr = mk_running(ang * (ring_out + 2.4), rl - ring_out - 2.4 - tl, 2.2, 1.4, 1.2, px)
return Finish.over(res, vec4(vec3(1.000, 0.824, 0.353), 1.0) * fr.x * focus * on)
}
// The quad runs 10 points past the box, room for the knob's shadow.
mod.widgets.RotaryKnob.clip_x = false
mod.widgets.RotaryKnob.clip_y = false
mod.widgets.RotaryKnob.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RotaryKnob.draw_bg.pixel = fn() {
let m = 10.0
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let dia = min(self.rect_size.x, self.rect_size.y)
let c = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x, Finish.snap(self.rect_pos.y + self.rect_size.y - dia * 0.5, px) - self.rect_pos.y)
return mk_knob(p, c, dia * 0.5 + 1.0, px, self.slide_pos, self.origin_pos, self.arc_origin, self.gap, self.hover, self.drag, self.focus, self.disabled, vec3(0.914, 0.698, 0.227), vec3(0.106, 0.141, 0.212), 1.0)
}
mod.widgets.RotaryFlat.clip_x = false
mod.widgets.RotaryFlat.clip_y = false
mod.widgets.RotaryFlat.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RotaryFlat.draw_bg.pixel = fn() {
let m = 10.0
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let dia = min(self.rect_size.x, self.rect_size.y - 22.0)
let c = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x, Finish.snap(self.rect_pos.y + 22.0 + dia * 0.5, px) - self.rect_pos.y)
return mk_knob(p, c, dia * 0.5 + 1.0, px, self.slide_pos, self.origin_pos, self.arc_origin, self.gap, self.hover, self.drag, self.focus, self.disabled, vec3(0.110, 0.180, 0.282), vec3(0.949, 0.753, 0.251), 1.0)
}
// The legends over the knobs: in the round face's bold.
mod.widgets.RotaryKnob.draw_text.text_style = mod.theme.font_bold{font_size: 8.0}
mod.widgets.RotaryFlat.draw_text.text_style = mod.theme.font_bold{font_size: 8.5}
// The spinner: the knob's ring of loops alone, three quarters of it in the
// arc's yarn. A sheet may not read the pass clock and the spinner carries
// no other, so it stands still.
mod.widgets.SpinnerFlat.size = 22.0
mod.widgets.SpinnerFlat.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let o = self.rect_pos
let c = vec2(Finish.snap(o.x + self.rect_size.x * 0.5, px), Finish.snap(o.y + self.rect_size.y * 0.5, px)) - o
let size = min(self.rect_size.x, self.rect_size.y)
let res = mk_knob(p, c, size * 0.5, px, 0.75, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, vec3(0.914, 0.698, 0.227), vec3(0.106, 0.141, 0.212), 0.0)
return res * self.alpha
}
// ---------------------------------------------------------------- fields
// A text field: a patch of the field yarn, 26 points tall. A text field
// clips what it holds, so the whole patch stands inside its own box and
// throws only its contact line. Hovered the yarn lightens; focused, a
// running stitch of the accent goes round inside its rim.
let mk_field = fn(p: vec2, size: vec2, o: vec2, px: float, hover: float, focus: float, disabled: float, col: vec3, rimc: vec3) -> vec4 {
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(size.x - px * 0.49, o.x, px), mk_snap(size.y - px * 0.49, o.y, px) - px)
let fc = mix(col, vec3(0.235, 0.271, 0.325), disabled)
let rc = mix(rimc, vec3(0.235, 0.271, 0.325) * 0.840, disabled)
var res = mk_patch(p, lo, hi, 6.0, px, fc, rc, hover * (1.0 - disabled) * 0.6, 0.0, 0.0, 0.0, fract(size.x * 0.0113))
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = Material.sd_box(p, c, h, 6.0)
let rw = clamp(h.y * 0.26, 1.7, 3.0)
let rs = mk_running(mk_perim(p, c, h, 6.0), d + rw + 1.4, 2.2, 1.4, 1.2, px)
return Finish.over(res, vec4(vec3(1.000, 0.824, 0.353) * (0.85 + 0.3 * rs.y), 1.0) * rs.x * focus * (1.0 - disabled))
}
// Text fields. An input with no fill of its own (the one a slider, a combo
// box or a well holds) draws nothing.
mod.widgets.TextInputFlat.draw_bg.pixel = fn() {
if self.color.a < 0.01 {
return vec4(0.0, 0.0, 0.0, 0.0)
}
let px = mk_px(self.draw_pass.dpi_factor)
return mk_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled, vec3(0.890, 0.843, 0.741), vec3(0.784, 0.722, 0.588))
}
// A well that holds an input and its parts (a form's field, a search
// with its icon) wears the same patch.
mod.widgets.FieldWell.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled, vec3(0.890, 0.843, 0.741), vec3(0.784, 0.722, 0.588))
}
mod.widgets.TextInputFlat.padding = mod.turtle.Inset{left: 10.0 right: 10.0 top: 4.0 bottom: 4.0}
mod.widgets.TextInputFlat.min_height = 26.0
mod.widgets.TextInputFlat.draw_cursor.color = #1e2533
mod.widgets.TextInputFlat.draw_text.color = #1e2533
mod.widgets.TextInputFlat.draw_text.color_hover = #1e2533
mod.widgets.TextInputFlat.draw_text.color_focus = #1e2533
mod.widgets.TextInputFlat.draw_text.color_down = #1e2533
mod.widgets.TextInputFlat.draw_text.color_empty = #584f41
mod.widgets.TextInputFlat.draw_text.color_disabled = #8c95a3
// The number field: a patch of the number yarn in its own rim; its stepper
// two small patches of the dark yarn, one over the other, each with an
// arrow sewn on in satin stitch.
mod.widgets.NumberField.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled, vec3(0.075, 0.137, 0.227), vec3(0.914, 0.698, 0.227))
}
mod.widgets.NumberField.spin_width = 17.0
mod.widgets.NumberSpin.width = 17.0
mod.widgets.NumberField.input.draw_text.color = #efe6d2
mod.widgets.NumberField.input.draw_text.color_hover = #efe6d2
mod.widgets.NumberField.input.draw_text.color_focus = #efe6d2
// The widget's own filled triangles are hidden; the face draws its own.
mod.widgets.NumberSpin.draw_text.color = #00000000
mod.widgets.NumberSpin.draw_text.color_disabled = #00000000
mod.widgets.NumberSpin.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * self.rect_size
let x0 = mk_snap(0.0, o.x, px) + px
let xr = mk_snap(self.rect_size.x - px * 0.49, o.x, px) - 3.5
let yt = mk_snap(0.0, o.y, px) + 3.5
let yb = mk_snap(self.rect_size.y - px * 0.49, o.y, px) - 4.5
let mid = mk_snap(self.rect_size.y * 0.5, o.y, px) - 0.5
let live = 1.0 - self.disabled
let hu = max(0.0, self.hot)
let hd = max(0.0, 0.0 - self.hot)
let du = max(0.0, self.down)
let dd = max(0.0, 0.0 - self.down)
let sc = vec3(0.133, 0.212, 0.310)
var res = mk_patch(p, vec2(x0, yt), vec2(xr, mid - 0.5), 2.5, px, sc, sc * 0.840, hu, du, 0.0, 0.0, 0.21)
res = Finish.over(res, mk_patch(p, vec2(x0, mid + 0.5), vec2(xr, yb), 2.5, px, sc, sc * 0.840, hd, dd, 0.0, 0.0, 0.67))
// The arrows.
let cx = (x0 + xr) * 0.5
let cu = (yt + mid) * 0.5 + px * 0.5 * du
let cd = (mid + yb) * 0.5 + px * 0.5 * dd
let ink = mix(vec3(0.937, 0.902, 0.824), vec3(0.549, 0.584, 0.639), self.disabled)
res = mk_satin(res, p, vec2(cx - 2.6, cu + 1.3), vec2(cx, cu - 1.3), 1.3, px, ink, 0.5 + 0.5 * live)
res = mk_satin(res, p, vec2(cx, cu - 1.3), vec2(cx + 2.6, cu + 1.3), 1.3, px, ink, 0.5 + 0.5 * live)
res = mk_satin(res, p, vec2(cx - 2.6, cd - 1.3), vec2(cx, cd + 1.3), 1.3, px, ink, 0.5 + 0.5 * live)
res = mk_satin(res, p, vec2(cx, cd + 1.3), vec2(cx + 2.6, cd - 1.3), 1.3, px, ink, 0.5 + 0.5 * live)
return res
}
// The drop-down: a patch of the choice yarn, a chevron sewn on at its right
// in satin stitch (pointing up while its list is open), a running stitch
// parting it from the words.
mod.widgets.DropDownFlat.padding = mod.turtle.Inset{left: 10.0 right: 30.0 top: 4.0 bottom: 4.0}
mod.widgets.DropDownFlat.clip_x = false
mod.widgets.DropDownFlat.clip_y = false
mod.widgets.DropDownFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
let mk_chevron = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, disabled: float, open: float, ink: vec3) -> vec4 {
let cy = (lo.y + hi.y) * 0.5
let dx = hi.x - 21.0
let rs = mk_running(p.y - lo.y, p.x - dx, 1.8, 1.6, 1.0, px)
let inr = step(lo.y + 5.0, p.y) * step(p.y, hi.y - 5.0)
var res = Finish.over(dst, vec4(ink * 0.85, 1.0) * rs.x * inr * 0.55)
let cc = vec2(dx + 10.5, cy + mix(0.8, -0.8, open))
let dy = mix(1.0, -1.0, open) * 2.0
let ik = mix(ink, vec3(0.549, 0.584, 0.639), disabled)
res = mk_satin(res, p, cc + vec2(-3.4, 0.0 - dy), cc + vec2(0.0, dy), 1.6, px, ik, 1.0)
return mk_satin(res, p, cc + vec2(0.0, dy), cc + vec2(3.4, 0.0 - dy), 1.6, px, ik, 1.0)
}
mod.widgets.DropDownFlat.draw_bg.pixel = fn() {
let m = 12.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let live = 1.0 - self.disabled
let col = mix(vec3(0.890, 0.843, 0.741), vec3(0.235, 0.271, 0.325), self.disabled)
let res = mk_patch(p, lo, hi, min((hi.y - lo.y) * 0.32, 8.0), px, col, col * 0.840, max(self.hover, self.active) * live, self.down * live, self.focus * live, 1.0, fract(self.rect_size.x * 0.0173))
return mk_chevron(res, p, lo, hi, px, self.disabled, self.active, vec3(0.118, 0.145, 0.200))
}
mod.widgets.DropDownFlat.draw_text.color = #1e2533
mod.widgets.DropDownFlat.draw_text.color_hover = #1e2533
mod.widgets.DropDownFlat.draw_text.color_focus = #1e2533
mod.widgets.DropDownFlat.draw_text.color_down = #1e2533
mod.widgets.DropDownFlat.draw_text.color_disabled = #8c95a3
// The combo box: the same patch and chevron.
mod.widgets.ComboBoxFlat.padding = mod.turtle.Inset{left: 10.0 right: 30.0 top: 4.0 bottom: 4.0}
mod.widgets.ComboBoxFlat.input.min_height = 0.0
mod.widgets.ComboBoxFlat.clip_x = false
mod.widgets.ComboBoxFlat.clip_y = false
mod.widgets.ComboBoxFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ComboBoxFlat.draw_bg.pixel = fn() {
let m = 12.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let live = 1.0 - self.disabled
let col = mix(vec3(0.890, 0.843, 0.741), vec3(0.235, 0.271, 0.325), self.disabled)
let res = mk_patch(p, lo, hi, min((hi.y - lo.y) * 0.32, 8.0), px, col, col * 0.840, self.hover * live, 0.0, self.focus * live, 1.0, fract(self.rect_size.x * 0.0173))
return mk_chevron(res, p, lo, hi, px, self.disabled, 0.0, vec3(0.118, 0.145, 0.200))
}
mod.widgets.ComboBoxFlat.input.draw_text.color = #1e2533
mod.widgets.ComboBoxFlat.input.draw_text.color_hover = #1e2533
mod.widgets.ComboBoxFlat.input.draw_text.color_focus = #1e2533
mod.widgets.ComboBoxFlat.input.draw_text.color_down = #1e2533
mod.widgets.ComboBoxFlat.input.draw_text.color_empty = #584f41
// Open lists: a patch of the list yarn; under the hand a row lightens, the
// chosen row is a patch of the accent sewn on it.
let mk_menu = fn(p: vec2, size: vec2, px: float) -> vec4 {
return mk_patch(p, vec2(0.0, 0.0), size - vec2(px, px), 6.0, px, vec3(0.882, 0.831, 0.729), vec3(0.882, 0.831, 0.729) * 0.840, 0.0, 0.0, 0.0, 0.0, fract(size.x * 0.019 + size.y * 0.007))
}
let mk_row = fn(p: vec2, size: vec2, px: float, hover: float, active: float, disabled: float) -> vec4 {
var res = vec4(1.0, 1.0, 1.0, 1.0) * 0.07 * hover * (1.0 - disabled)
let sel = mk_patch(p, vec2(2.0, 1.0), size - vec2(2.0, 1.0), 4.0, px, vec3(0.914, 0.698, 0.227), vec3(0.914, 0.698, 0.227) * 0.840, 0.0, 0.0, 0.0, 0.0, 0.27)
return Finish.over(res, sel * active)
}
mod.widgets.PopupMenuFlat.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_menu(self.pos * self.rect_size, self.rect_size, px)
}
mod.widgets.PopupMenuItem.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_row(self.pos * self.rect_size, self.rect_size, px, self.hover, self.active, self.disabled)
}
mod.widgets.PopupMenuItem.draw_text.color = #1e2533
mod.widgets.PopupMenuItem.draw_text.color_hover = #1e2533
mod.widgets.PopupMenuItem.draw_text.color_active = #2a2010
mod.widgets.ComboBoxFlat.draw_popup_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_menu(self.pos * self.rect_size, self.rect_size, px)
}
mod.widgets.ComboBoxFlat.draw_item.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_row(self.pos * self.rect_size, self.rect_size, px, self.hover, self.active, 0.0)
}
mod.widgets.ComboBoxFlat.draw_item_text.color = #1e2533
mod.widgets.ComboBoxFlat.draw_item_text.color_active = #2a2010
// ---------------------------------------------------------------- tabs
// The segmented control: the strip one patch of the segment yarn; the
// chosen answer a patch of the accent pressed flat into it, its word in the
// accent's ink.
mod.widgets.SegmentedControlFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SegmentedControlFlat.draw_bg.pixel = fn() {
let m = 12.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
return mk_patch(p, lo, hi, 8.0, px, vec3(0.086, 0.153, 0.243), vec3(0.086, 0.153, 0.243) * 0.840, 0.0, 0.0, 0.0, 1.0, 0.13)
}
// The strip lays the answers 2 points in from its edge; the quad grows so
// the chosen patch can reach the strip's rim.
mod.widgets.SegmentedControlFlat.draw_pill.vertex = fn() {
let m = 11.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
// On the column (its pill's `radius` set to -1 as the mark) the patch keeps
// to its own segment's height and reaches the strip's side rims.
mod.widgets.SegmentedControlFlat.draw_pill.pixel = fn() {
let m = 11.0
let v = 1.0 - step(0.0, self.radius)
let hz = 1.0 - v
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0 - 1.0 * v, o.x, px), mk_snap(0.0 - 1.0 * hz, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x + 1.0 * v, o.x, px), mk_snap(self.rect_size.y + 1.0 * hz, o.y, px))
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
// The shade of the dent it is pressed into.
let dent = Finish.drop(p, c, h, 6.0, vec2(0.0, 0.0), 1.2) * (1.0 - Finish.cover(Material.sd_box(p, c, h, 6.0), px)) * 0.5
var res = vec4(0.0, 0.0, 0.0, dent)
return Finish.over(res, mk_patch(p, lo, hi, 6.0, px, vec3(0.914, 0.698, 0.227), vec3(0.914, 0.698, 0.227) * 0.840, 0.0, 1.0, 0.0, 0.0, 0.57))
}
mod.widgets.SegmentedControlFlat.draw_text.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.SegmentedControlFlat.draw_text_selected.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.SegmentedControlFlat.draw_text.color = #efe6d2
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #2a2010
// The chosen word takes its ink from the control's role palette.
mod.widgets.SegmentedControlFlat.palette.on_neutral_container = #2a2010
mod.widgets.SegmentedControl.palette.on_neutral_container = #2a2010
mod.widgets.SegmentedControlVertical.draw_pill.radius = -1.0
// Tabs: each tab its own patch standing beside the next; the tab in use is
// knitted in the tab yarn and pressed into the fabric.
mod.widgets.Tabs.height = 28.0
mod.widgets.Tabs.clip_x = false
mod.widgets.Tabs.clip_y = false
let mk_tab = fn(pos: vec2, size: vec2, o: vec2, dpi: float, hover: float, active: float) -> vec4 {
let m = 12.0
let px = mk_px(dpi)
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(size.x, o.x, px) - 3.0, mk_snap(size.y, o.y, px))
let col = mix(vec3(0.086, 0.153, 0.243), vec3(0.698, 0.235, 0.651), active)
return mk_patch(p, lo, hi, 7.0, px, col, col * 0.840, hover, active * 0.7, 0.0, 1.0, fract(size.x * 0.031))
}
mod.widgets.Tabs.draw_tab.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Tabs.draw_tab_hover.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Tabs.draw_tab_active.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Tabs.draw_bg.pixel = fn() {
return vec4(0.0, 0.0, 0.0, 0.0)
}
mod.widgets.Tabs.draw_tab.pixel = fn() {
return mk_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 0.0, 0.0)
}
mod.widgets.Tabs.draw_tab_hover.pixel = fn() {
return mk_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 1.0, 0.0)
}
mod.widgets.Tabs.draw_tab_active.pixel = fn() {
return mk_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 0.0, 1.0)
}
mod.widgets.Tabs.draw_sep.pixel = fn() {
return vec4(0.0, 0.0, 0.0, 0.0)
}
// The marks: the cross that closes a tab, sewn on it in its ink, and the
// plus that adds one, sewn on the fabric in the ground's ink.
mod.widgets.Tabs.draw_mark.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let c = self.rect_size * 0.5
let r = min(self.rect_size.x, self.rect_size.y) * 0.27
let a = mix(0.7853981634, 0.0, self.plus)
let d1 = vec2(cos(a), sin(a)) * r
let d2 = vec2(0.0 - d1.y, d1.x)
let ink = mix(vec3(0.937, 0.902, 0.824), vec3(0.945, 0.914, 0.839), self.plus)
let res = mk_satin(vec4(0.0, 0.0, 0.0, 0.0), p, c - d1, c + d1, 1.6, px, ink, 1.0)
return mk_satin(res, p, c - d2, c + d2, 1.6, px, ink, 1.0)
}
mod.widgets.Tabs.draw_text.color = #efe6d2
mod.widgets.Tabs.draw_text_active.color = #f6f0e2
mod.widgets.Tabs.draw_text.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.Tabs.draw_text_active.text_style = mod.theme.font_label{font_size: 8.5}
// ---------------------------------------------------------------- lamps
// LAMPS: real LEDs, each set in a grommet of felt sewn to the fabric. The
// emitter is the light: at full current its middle burns toward white and
// its rim is its most saturated colour; at lower current the middle is no
// longer white, the colour deeper, the glow smaller; out, it is dark tinted
// glass, its colour barely there. Round it the grommet: a raised ring of
// felt lit from the upper left. Over the lens one crisp highlight of a
// dome. Its light is added, never painted: a tight hot halo and a short
// bloom in its own hue, screened over whatever is there and gone in about
// two radii; on a pale ground, where light can barely brighten, its hue is
// also laid round it as a tint that keeps the ground's luminance, so it
// never darkens or greys its ground.
let mk_led_add = fn(res: vec4, light: vec3, ground: vec3) -> vec4 {
let est = res.rgb + ground * (1.0 - res.a)
let add = (vec3(1.0, 1.0, 1.0) - exp(-light)) * max(vec3(1.0, 1.0, 1.0) - est, vec3(0.0, 0.0, 0.0))
return vec4(res.rgb + add, res.a)
}
// An LED's colour at full strength: its brightest channel at one.
let mk_led_full = fn(ink: vec3) -> vec3 {
return ink / max(max(ink.x, max(ink.y, ink.z)), 0.01)
}
// The same colour at the emitter's own chroma: the hue kept and its
// saturation raised by `vivid`.
let mk_led_hue = fn(ink: vec3, vivid: float) -> vec3 {
let f = mk_led_full(ink)
let mn = min(f.x, min(f.y, f.z))
let ch = 1.0 - mn
let pure = (f - vec3(mn, mn, mn)) / max(ch, 0.001)
return mix(f, pure, clamp(vivid, 0.0, 1.0) * 0.72 * smoothstep(0.03, 0.12, ch))
}
// The lamp: its lens a rounded box `hb` either side of `c` (round when the
// two are equal), `lit` its current 0..1, `ink` its colour and `vivid` how
// far its chroma is raised, `ground` what shows through where nothing is
// drawn, `room` how far its light may reach, `bez` and `raised` its bezel
// (0: a socket cut in what is under it; 1: a raised ring of `bez`, the
// felt grommet; 2: the same ring polished). Premultiplied over `dst`.
let mk_led_lamp = fn(dst: vec4, p: vec2, c: vec2, hb: vec2, lit: float, ink: vec3, vivid: float, px: float, ground: vec3, room: float, bez: vec3, raised: float) -> vec4 {
let rt = max(min(hb.x, hb.y), 0.4)
let d = Material.sd_box(p, c, hb, rt)
let bw = clamp(0.5 * rt, 1.0, 2.8)
let ds = d - bw
let L = clamp(lit, 0.0, 1.0)
let b = mk_led_full(ink)
let hue = mk_led_hue(ink, vivid)
let pw = L * sqrt(L)
let white = vec3(1.0, 1.0, 1.0)
let lum = vec3(0.2126, 0.7152, 0.0722)
let pale = smoothstep(0.35, 0.75, dot(ground, lum))
var res = dst
let x = max(ds, 0.0)
let reach = max(room, 1.0)
let win = 1.0 - smoothstep(0.55 * reach, reach, x)
let ex = max(hb.x - hb.y, 0.0)
let ey = max(hb.y - hb.x, 0.0)
let qx = p.x - c.x
let qy = p.y - c.y
let q = vec2(qx - clamp(qx, 0.0 - ex, ex), qy - clamp(qy, 0.0 - ey, ey)) / rt
let t = clamp(length(q), 0.0, 1.0)
let nrm = q / max(length(q), 0.001)
// What the bead and its bezel throw on the ground: a soft shadow, down.
let shd = Finish.drop(p, c, hb + vec2(bw, bw), rt + bw, vec2(0.0, 0.22 * rt + 0.3), 0.32 * rt + px) * mix(0.18, 0.34, min(raised, 1.0)) * (1.0 - 0.6 * pw)
res = Finish.over(res, vec4(ground * 0.4 * shd, shd))
if L > 0.001 && pale > 0.001 {
let h = clamp(0.42 * pw * pow(2.0, 0.0 - x / max(0.4 * reach, 0.5)) * win, 0.0, 0.45) * pale
let est = res.rgb + ground * (1.0 - res.a)
let w = clamp(dot(hue, lum) / max(dot(est, lum), 0.001), 0.0, 1.0)
res = Finish.over(res, vec4(hue * h * w, h * w))
}
let a_s = Finish.cover(ds, px)
let u = clamp(d / max(bw, 0.01), 0.0, 1.0)
let facing = dot(nrm * (u - 0.5) * 2.0, vec2(-0.45, -0.89))
let metal = clamp(raised - 1.0, 0.0, 1.0)
var rc = bez * (0.94 + mix(0.12, 0.55, metal) * facing)
rc = mix(rc, min(bez + vec3(0.30, 0.30, 0.30), white), smoothstep(mix(0.35, 0.55, metal), 0.9, facing) * mix(0.6, 0.85, metal))
rc = mix(rc, rc * 0.8, Finish.band(ds, 0.0, max(px, 0.45), px) * smoothstep(0.0, 0.8, nrm.y) * 0.8)
// The felt of the grommet: a round of small loops.
let gl = atan2(p.y - c.y, p.x - c.x) / (2.0 * PI) * max(floor(2.0 * PI * (rt + bw * 0.5) / 1.5 + 0.5), 6.0)
let gg = (fract(gl + u * 0.4) - 0.5) * 2.0
rc = rc * mix(1.0, 0.72 + 0.4 * sqrt(max(1.0 - gg * gg, 0.0)), clamp(raised, 0.0, 1.0) * (1.0 - metal))
let under = res.rgb / max(res.a, 0.001) * step(0.001, res.a) + ground * (1.0 - step(0.001, res.a))
let gy = clamp(nrm.y * t, -1.0, 1.0)
var sc = under * mix(0.55, 0.92, smoothstep(-0.9, 0.9, gy))
sc = mix(sc, min(under * 1.06 + vec3(0.28, 0.28, 0.28), white), Finish.band(ds, 0.0, max(px, 0.45), px) * smoothstep(0.2, 0.95, nrm.y) * 0.85)
sc = min(mix(sc, rc, min(raised, 1.0)), white)
res = Finish.over(res, vec4(sc * a_s, a_s))
if L > 0.001 {
let xl = max(d, 0.0)
let halo = exp(0.0 - xl / max(0.30 * rt, 0.4)) * 1.5 + exp(0.0 - xl / max(0.65 * rt, 0.7)) * 0.32
let hwin = 1.0 - smoothstep(1.1 * rt, 2.0 * rt + 1.0, xl)
res = mk_led_add(res, mix(hue, white, 0.15 * pw) * halo * pw * hwin * win, ground)
}
// THE BEAD: a glossy dome of coloured glass.
let top = smoothstep(-1.0, 0.6, q.y)
let pool = exp(0.0 - (q.y - 0.55) * (q.y - 0.55) / 0.09 - q.x * q.x / 0.30)
var on = mix(hue * mix(0.55, 0.8, L), mix(b, hue, 0.5), top)
on = mix(on, min(mix(b, hue, 0.4) + vec3(0.35, 0.35, 0.35), white), pool * 0.5)
on = mix(on, vec3(1.0, 0.995, 0.985), exp(0.0 - t * t / 0.26) * smoothstep(0.5, 1.0, L) * 0.95)
on = mix(on, hue * (0.82 + 0.18 * L), smoothstep(0.62, 1.0, t))
on = on * (0.42 + 0.58 * L)
// Out: dark tinted glass, its colour barely there, the same dome, a
// catch light along its lower rim.
let offc = mix(vec3(0.085, 0.09, 0.115), b * 0.4, 0.14)
var off = mix(offc * 0.62, offc * 1.08, top)
off = mix(off, min(offc + vec3(0.18, 0.18, 0.18), white), pool * 0.35)
off = off * mix(1.0, 0.75, smoothstep(0.75, 1.0, t))
let catchl = smoothstep(0.62, 0.9, t) * (1.0 - smoothstep(0.9, 1.0, t)) * smoothstep(0.2, 0.9, nrm.y)
off = off + vec3(0.30, 0.30, 0.32) * catchl
var col = mix(off, min(on, white), smoothstep(0.0, 0.35, L))
let e = length(vec2(q.x / 0.62, (q.y + 0.46) / 0.30))
let spec = (1.0 - smoothstep(0.55, 1.0, e)) * mix(1.0, 0.3, clamp((q.y + 0.76) / 0.6, 0.0, 1.0))
col = mix(col, white, spec * mix(0.8, 0.55, L) * smoothstep(0.9, 2.2, rt))
col = mix(col, col * 0.7, Finish.band(d, 0.0, px * 0.6, px) * 0.6 * (1.0 - smoothstep(0.0, 0.25, L)))
col = min(col, white)
let a_l = Finish.cover(d, px)
return Finish.over(res, vec4(col * a_l, a_l))
}
// The Lamp widget and the bar lamp: the lamp above in its felt grommet.
// The whole lamp -- grommet, light and shadow -- stays inside its own box,
// a point clear of its edge, so a row of lamps or a lamp beside a switch
// never lays its light on a neighbour. A dot is a third of its box tall; a
// bar is a light pipe as thick as seven tenths of a dot and twice as long
// as it is thick, laid from the start of its box, so a bar in a box drawn
// wider than the row it stands in keeps to that row. The intents: the
// sheet's accent, success green, warning yellow, error red, plain white.
mod.widgets.Lamp.halo = 0.3
mod.widgets.Lamp.halo_reach = 1.0
mod.widgets.Lamp.draw_bg.color_accent = uniform(#39b8ff)
mod.widgets.Lamp.draw_bg.color_success = uniform(#22d27a)
mod.widgets.Lamp.draw_bg.color_warning = uniform(#ffc21a)
mod.widgets.Lamp.draw_bg.color_error = uniform(#ff2a2a)
mod.widgets.Lamp.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.Lamp.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let reach = clamp(self.reach, 0.0, 1.0)
let sz = self.rect_size
let t = sz.y / (1.0 + 2.0 * reach)
let bar = step(0.5, self.bar)
// A dot in the middle of its box; a bar from the start of its box.
let th = mix(t * 0.5, t * 0.35, bar)
let hb = vec2(mix(th, th * 2.0, bar), th)
let bw = clamp(0.5 * th, 1.0, 2.8)
let c = vec2(mix(sz.x * 0.5, 2.6 + bw + hb.x, bar), sz.y * 0.5)
// The light's room: what is left of the box round the lens and its
// socket, less a point; a bar keeps two points.
let free = min(sz.x * 0.5, sz.y * 0.5) - th - bw - 1.0
let room = mix(max(free, 1.5), 2.0, bar)
let res = mk_led_lamp(vec4(0.0, 0.0, 0.0, 0.0), p, c, hb, self.lit, self.intent_color().rgb, 1.0 - step(3.5, self.intent), px, vec3(0.122, 0.306, 0.471), room, vec3(0.227, 0.290, 0.388), 1.0)
return res * self.opacity
}
// ---------------------------------------------------------------- feedback
// The colour of a cell of a ladder at `share` of its length: the sheet's
// own yarn all the way (`ladder_mono` 1), or the level code, green at the
// foot through yellow and orange to red at the top.
let mk_ladder_col = fn(share: float) -> vec3 {
var c = vec3(0.220, 0.720, 0.300)
c = mix(c, vec3(0.560, 0.800, 0.200), step(0.45, share))
c = mix(c, vec3(0.980, 0.780, 0.150), step(0.65, share))
c = mix(c, vec3(0.980, 0.480, 0.120), step(0.80, share))
c = mix(c, vec3(0.900, 0.160, 0.160), step(0.90, share))
return mix(c, vec3(0.275, 0.753, 0.361), 0.000)
}
// A cell of a ladder: one fat V stitch filling `lo`..`hi`, in `col` when
// lit and in `dark` when not. Premultiplied over `dst`.
let mk_cell = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, col: vec3, lit: float, dark: vec3, seed: float) -> vec4 {
let sw = max(hi.x - lo.x, 1.0)
let sh = max(hi.y - lo.y, 1.0)
let k = mk_stitch(p - vec2((lo.x + hi.x) * 0.5, lo.y), seed, 3.0, min(sw * 1.05, sh * 1.35))
let y = mix(dark, col, lit)
let c = y * k.z * (0.62 + 0.48 * clamp(k.y, -0.4, 1.0))
let a = smoothstep(0.0, 0.35, k.x)
return Finish.over(dst, vec4(c * a, a))
}
// The progress bar: an i-cord of the dark yarn; the value an i-cord of the
// bar's yarn laid over it.
mod.widgets.ProgressBarFlat.height = 9.0
mod.widgets.ProgressBarFlat.draw_bg.color_primary = uniform(#46c05c)
mod.widgets.ProgressBarFlat.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * self.rect_size
let th = min(max(self.thickness, 7.0), self.rect_size.y)
let y0 = mk_snap((self.rect_size.y - th) * 0.5, o.y, px)
let x0 = mk_snap(self.rect_size.x * self.start, o.x, px)
let x1 = mk_snap(self.rect_size.x * self.end, o.x, px)
let lo = vec2(x0, y0)
let hi = vec2(x1, y0 + mk_snap(th, 0.0, px) - px)
var res = vec4(0.0, 0.0, 0.0, 0.0)
if self.track > 0.5 {
res = mk_cord(res, p, lo, hi, px, vec3(0.059, 0.094, 0.157), 0.8, 0.0)
}
let fill = self.fill_color()
var v0 = 0.0
var v1 = clamp((self.value - self.start) / max(self.end - self.start, 0.001), 0.0, 1.0)
if self.value < 0.0 {
// Still: the sweep would need the pass clock, and a window at rest
// reads no clock, so the unknown is a run lit in the middle.
v0 = 0.3
v1 = 0.7
}
let span = hi.x - lo.x
let flo = vec2(lo.x + span * v0, lo.y)
let fhi = vec2(max(lo.x + span * v1, flo.x + hi.y - lo.y), hi.y)
let fs = mk_cord(vec4(0.0, 0.0, 0.0, 0.0), p, flo, fhi, px, fill.rgb, 0.0, 0.0)
res = Finish.over(res, fs * step(0.001, v1 - v0) * fill.a)
return res * self.opacity
}
// The level meter: a ladder of fat stitches on a strip of the dark yarn,
// coloured by the level code; the clip lamp an LED. 8 points thick lying,
// 14 standing.
mod.widgets.LevelMeter.height = 8.0
mod.widgets.LevelMeterColumn.width = 14.0
mod.widgets.LevelMeterColumn.draw_bg.segment = uniform(7.0)
mod.widgets.LevelMeter.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let sz = self.rect_size
let p = self.pos * sz
let v = self.vertical
let along = mix(sz.x, sz.y, v)
let across = mix(sz.y, sz.x, v)
let a = mix(p.x, sz.y - p.y, v)
let lamp_room = self.lamp * (across + 3.0)
let bar = max(4.0, along - lamp_room)
let wlo = mix(vec2(0.0, 0.0), vec2(0.0, sz.y - bar), v)
let whi = mix(vec2(bar, sz.y), vec2(sz.x, sz.y), v)
let lo = vec2(mk_snap(wlo.x, o.x, px), mk_snap(wlo.y, o.y, px))
let hi = vec2(mk_snap(whi.x, o.x, px), mk_snap(whi.y, o.y, px) - px)
var res = mk_patch(p, lo, hi, min(across * 0.3, 3.0), px, vec3(0.059, 0.094, 0.157), vec3(0.059, 0.094, 0.157) * 0.840, 0.0, 0.0, 0.0, 0.0, 0.91)
let pad = mix(1.6, 2.2, v)
let span = max(1.0, bar - pad * 2.0)
let pitch = max(self.segment, 3.0)
let gapw = mix(0.6, 1.0, v)
let k = floor((a - pad) / pitch)
let ca = pad + k * pitch
let share = clamp((ca + pitch * 0.5 - pad) / span, 0.0, 1.0)
let inr = step(0.0, k) * step(ca + pitch - gapw, bar - pad + 0.01)
let col = mk_ladder_col(share)
let on = step(share, self.level) * step(0.001, self.level)
let hold_at = pad + span * self.hold
let hc = step(abs(ca + pitch * 0.5 - hold_at), pitch * 0.5) * step(0.002, self.hold)
let clo = mix(vec2(ca, pad), vec2(pad, sz.y - ca - pitch + gapw), v)
let chi = mix(vec2(ca + pitch - gapw, across - pad - px), vec2(across - pad, sz.y - ca), v)
let cell = mk_cell(vec4(0.0, 0.0, 0.0, 0.0), p, clo, chi, px, col, max(on, hc), vec3(0.059, 0.094, 0.157) * 1.35, k)
res = Finish.over(res, cell * inr * self.opacity)
if self.lamp > 0.5 {
let lc = mix(vec2(bar + 3.0 + across * 0.5, sz.y * 0.5), vec2(sz.x * 0.5, across * 0.5), v)
let rl = clamp(across * 0.5 - 1.0, 1.2, 3.0)
res = mk_led_lamp(res, p, lc, vec2(rl, rl), step(0.5, self.over), vec3(0.950, 0.150, 0.150), 1.0, px, vec3(0.122, 0.306, 0.471), 3.0, vec3(0.227, 0.290, 0.388), 1.0)
}
return res * self.opacity
}
// ---------------------------------------------------------------- surfaces
// The scroll bar: a cord of the dark yarn, the thumb a felt patch.
mod.widgets.ScrollBar.bar_size = 9.0
mod.widgets.ScrollBar.bar_side_margin = 1.0
mod.widgets.ScrollBar.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let sz = self.rect_size
let p = self.pos * sz
let v = self.is_vertical
let c = sz * 0.5
let tw = 2.0
let tlo = mix(vec2(1.0, c.y - tw), vec2(c.x - tw, 1.0), v)
let thi = mix(vec2(sz.x - 1.0, c.y + tw), vec2(c.x + tw, sz.y - 1.0), v)
var res = mk_cord(vec4(0.0, 0.0, 0.0, 0.0), p, tlo, thi, px, vec3(0.059, 0.094, 0.157), 0.0, v)
let t0 = mix(sz.x * self.norm_scroll, sz.y * self.norm_scroll, v)
let t1 = t0 + mix(sz.x * self.norm_handle, sz.y * self.norm_handle, v)
let slo0 = mix(vec2(t0, 0.5), vec2(0.5, t0), v)
let shi0 = mix(vec2(t1, sz.y - 0.5), vec2(sz.x - 0.5, t1), v)
let slo = vec2(mk_snap(slo0.x, o.x, px), mk_snap(slo0.y, o.y, px))
let shi = vec2(mk_snap(shi0.x, o.x, px), mk_snap(shi0.y, o.y, px))
let hv = max(self.hover, self.drag)
res = Finish.over(res, mk_patch(p, slo, shi, 4.0, px, vec3(0.914, 0.698, 0.227), vec3(0.914, 0.698, 0.227) * 0.840, hv, 0.0, 0.0, 0.0, 0.47))
return res * self.opacity
}
// A SURFACE: a big knitted patch sewn on the fabric. Its rim a bound-off
// cord of loops whose yarn changes every few loops among `r1`, `r2` and
// `r3` (one yarn when they agree); a running stitch of `st` sewn round
// inside it (`st_a` how much); in its lower right corner a triangle of
// striped knit in `cn` (`cn_a`). Raised (`sunk` 0) it is padded and throws
// a shadow kept within half the gap to its neighbours; sunk, the fabric
// round it overhangs it and shades its top. Premultiplied.
let mk_surface = fn(p: vec2, lo: vec2, hi: vec2, px: float, body_c: vec3, r1: vec3, r2: vec3, r3: vec3, st: vec3, st_a: float, cn: vec3, cn_a: float, sunk: float, hover: float, seed: float) -> vec4 {
let c = (lo + hi) * 0.5
let h = max((hi - lo) * 0.5, vec2(0.5, 0.5))
let r = min(9.0, min(h.x, h.y))
let d = Material.sd_box(p, c, h, r)
let up = 1.0 - sunk
let s1 = Finish.drop(p, c, h - vec2(1.0, 0.0), r, vec2(0.0, 1.6), 1.4) * 0.65
let s2 = Finish.drop(p, c, h - vec2(4.0, 2.0), r, vec2(0.0, 3.6), 3.2) * 0.45
let sha = (1.0 - (1.0 - s1) * (1.0 - s2)) * 0.550 * up
var res = vec4(vec3(0.008, 0.039, 0.078) * sha, sha)
let n = mk_box_n(p, c, h, r)
let s_ed = mk_perim(p, c, h, r)
let fray = (Finish.noise(vec2(s_ed / 0.9, seed * 7.0)) - 0.5) * 0.55 + (Finish.hash(floor(p / px) + vec2(seed, 1.0)) - 0.5) * 0.3
let de = d - fray
let a = Finish.cover(de, px)
let haze = exp(0.0 - max(de, 0.0) / 0.45) * (1.0 - a) * 0.22 * 0.700 * step(0.0, de) * step(de, 2.0)
res = Finish.over(res, vec4(r1 * 0.9, 1.0) * haze)
// The body.
let q = p - lo + vec2(seed * 41.0, seed * 17.0)
let k = mk_knit(q, 5.0, 3.0, 0.9)
var body = mk_soft(body_c, mk_yarn(body_c, body_c * 0.400, k, q, px, 0.700))
body = body * (1.0 + mk_cloud(q * 1.7) * 0.070)
let e = clamp((0.0 - d) / 10.0, 0.0, 1.0)
let slope = 1.0 - e
let lt = mk_lit(n) * slope * mix(1.0, -1.0, sunk)
body = body * (1.0 + 0.22 * lt - 0.14 * slope * slope * up) * (1.0 + 0.090 * hover)
// Sunk: the fabric round it shades its top.
let ins = Finish.inset(p, c, h, r, vec2(0.0, 2.2), 2.4)
body = mix(body, body * 0.55, ins * 0.75 * sunk)
// The corner triangle: striped knit in the corner's yarn.
let ct = (p.x - hi.x) + (p.y - hi.y) + 26.0
let corner = smoothstep(0.0 - px, px, ct) * cn_a
let stripe = step(0.5, fract((p.x - p.y) / 5.0))
let ccol = mix(cn, cn * 0.78, stripe)
body = mix(body, mk_yarn(ccol, ccol * 0.400, k, q, px, 0.700) * (1.0 + 0.22 * lt), corner)
body = mix(body, body * 0.6, smoothstep(0.0 - px, px, ct) * (1.0 - smoothstep(px, px * 2.5, ct)) * cn_a)
// The rim.
let rw = 3.4
let per = mk_perim_len(h, r)
let nl = max(floor(per / (rw * 1.25) + 0.5), 4.0)
let period = per / nl
let u = clamp((0.0 - d) / rw, 0.0, 1.0)
let ac = u * 2.0 - 1.0
let lf = s_ed / period + u * 0.7
let f = fract(lf)
let g = (f - 0.5) * 2.0
let loop_h = sqrt(max(1.0 - g * g, 0.0)) * sqrt(max(1.0 - ac * ac * 0.8, 0.0))
let li = floor(lf)
let run = mk_hash(vec2(floor(li / 3.0), seed * 9.0))
var rimc = r1
if run > 0.66 {
rimc = r2
}
if run > 0.33 && run < 0.66 {
rimc = r3
}
let rl = mk_lit(n) * (0.0 - ac) * 0.9 - g * 0.25
var rim = rimc * (0.50 + 0.62 * loop_h) * (0.9 + 0.35 * rl * mix(1.0, -0.5, sunk))
rim = rim * (1.0 + (Finish.hash(floor(p / px) + vec2(9.1, seed)) - 0.5) * 0.08)
let in_rim = 1.0 - smoothstep(rw - px * 0.5, rw + px * 0.5, 0.0 - d)
var face = mix(body, rim, in_rim)
face = mix(face, face * 0.45, exp(0.0 - ((0.0 - d - rw) * (0.0 - d - rw)) / 0.2) * 0.6)
// The running stitch inside the rim.
let rs = mk_running(s_ed, d + rw + 2.6, 3.2, 2.0, 1.4, px)
face = Finish.over(vec4(face, 1.0), vec4(st * (0.85 + 0.3 * rs.y), 1.0) * rs.x * st_a).rgb
return Finish.over(res, vec4(face * a, a))
}
mod.widgets.PanelView.clip_x = false
mod.widgets.PanelView.clip_y = false
mod.widgets.PanelView.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 12.0 bottom: 12.0}
mod.widgets.PanelView.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.PanelView.draw_bg.pixel = fn() {
let m = 10.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let seed = fract(self.rect_size.x * 0.0071 + self.rect_size.y * 0.013)
if self.sunken > 0.5 {
return mk_surface(p, lo, hi, px, vec3(0.067, 0.118, 0.192), vec3(0.090, 0.157, 0.247), vec3(0.090, 0.157, 0.247), vec3(0.090, 0.157, 0.247), vec3(0.914, 0.698, 0.227), 1.000, vec3(0.090, 0.157, 0.247), 0.0, 1.0, 0.0, seed)
}
return mk_surface(p, lo, hi, px, vec3(0.141, 0.349, 0.549), vec3(0.541, 0.294, 0.769), vec3(0.227, 0.690, 0.816), vec3(0.914, 0.698, 0.227), vec3(0.914, 0.698, 0.227), 0.000, vec3(0.914, 0.698, 0.227), 1.000, 0.0, 0.0, seed)
}
// The rounded view keeps the colour its caller gives it: a patch of that
// yarn in a rim of a darker shade of it, flat on the fabric.
mod.widgets.RoundedView.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let lo = vec2(self.border_inset.x, self.border_inset.y)
let hi = self.rect_size - vec2(self.border_inset.z, self.border_inset.w)
var fill = self.color
if self.color_2.x > -0.5 {
fill = mix(self.color, self.color_2, mix(self.pos.y, self.pos.x, self.gradient_fill_horizontal))
}
if fill.a < 0.01 {
return vec4(0.0, 0.0, 0.0, 0.0)
}
let seed = fract(self.rect_size.x * 0.0093 + self.rect_size.y * 0.017)
let rc = mix(fill.rgb * 0.840, vec3(0.416, 0.659, 0.416), 1.000)
let res = mk_surface(p, lo, hi - vec2(px, px), px, fill.rgb, rc, rc, rc, vec3(0.416, 0.659, 0.416), 0.0, rc, 0.0, 0.0, 0.0, seed)
return res * fill.a
}
// The card: a raised patch of its own yarn; a pressable card lightens under
// the hand.
mod.widgets.Card.clip_x = false
mod.widgets.Card.clip_y = false
mod.widgets.Card.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 12.0 bottom: 12.0}
mod.widgets.Card.draw_bg.vertex = fn() {
let m = 10.0
return self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Card.draw_bg.pixel = fn() {
let m = 10.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let seed = fract(self.rect_size.x * 0.0059 + self.rect_size.y * 0.021)
return mk_surface(p, lo, hi, px, vec3(0.302, 0.188, 0.439), vec3(0.604, 0.357, 0.831), vec3(0.604, 0.357, 0.831), vec3(0.604, 0.357, 0.831), vec3(0.914, 0.698, 0.227), 0.000, vec3(0.914, 0.698, 0.227), 1.000, 0.0, self.hover, seed)
}
// LIST ROWS: a list is a strip of the list yarn, each row 22 points of
// knit with a seam under it; under the hand a row lightens. The chosen row
// is knitted in the accent, or (where the sheet keeps the list's yarn) gets
// a bar of the accent sewn down its left end.
mod.widgets.ListItem.height = 22.0
mod.widgets.ListItemOne.height = 22.0
mod.widgets.ListItem.padding = mod.turtle.Inset{left: 12.0 right: 8.0 top: 0.0 bottom: 0.0}
mod.widgets.ListItemOne.padding = mod.turtle.Inset{left: 12.0 right: 8.0 top: 0.0 bottom: 0.0}
mod.widgets.ListItem.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * self.rect_size
let live = 1.0 - self.disabled
let y0 = mk_snap(0.0, o.y, px)
let y1 = mk_snap(self.rect_size.y, o.y, px)
let x1 = mk_snap(self.rect_size.x, o.x, px)
let sel = self.selected * 1.000
let lc = mix(vec3(0.882, 0.831, 0.729), vec3(0.914, 0.698, 0.227), sel)
let q = p + vec2(3.7, 1.3)
let k = mk_knit(q, 4.6, 2.8, 0.6)
var col = mk_soft(lc, mk_yarn(lc, lc * 0.400, k, q, px, 0.700))
col = col * (1.0 + 0.08 * (self.hover + 0.5 * self.down) * live)
// The seam under the row, and the light catching the top of the next.
let t = (p.y - y0) / max(y1 - y0, 1.0)
col = col * (1.0 - 0.18 * smoothstep(0.75, 1.0, t) + 0.06 * (1.0 - smoothstep(0.0, 0.2, t)))
col = mix(col, vec3(0.714, 0.647, 0.522), step(y1 - px * 1.5, p.y))
var res = vec4(col, 1.0) * step(y0, p.y) * step(p.y, y1)
// The bar down the chosen row's left end.
let mk = Material.sd_box(p, vec2(3.5, (y0 + y1) * 0.5), vec2(1.8, (y1 - y0) * 0.5 - 2.5), 1.8)
let bar = Finish.cover(mk, px) * self.selected * 0.000
let bc = vec3(1.000, 0.824, 0.353) * (0.75 + 0.4 * (1.0 - clamp((p.x - 1.7) / 3.6, 0.0, 1.0)))
res = Finish.over(res, vec4(bc, 1.0) * bar)
return res * step(p.x, x1)
}
mod.widgets.ListItem.draw_text.color = #1e2533
mod.widgets.ListItemOne.draw_text.color = #1e2533
// The rows of a tree stand on the fabric, clear but for the faint tint of
// every other row; the open row is a patch of the accent sewn on.
mod.widgets.FileTreeNode.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let base = mix(self.color_1, self.color_2, self.is_even)
var res = vec4(base.rgb * base.a, base.a)
let sel = mk_patch(p, vec2(1.0, 1.0), self.rect_size - vec2(1.0, 1.0), 5.0, px, vec3(0.914, 0.698, 0.227), vec3(0.914, 0.698, 0.227) * 0.840, 0.0, 0.0, 0.0, 0.0, 0.29)
return Finish.over(res, sel * self.active)
}
mod.widgets.FileTreeNode.draw_text.color_active = #2a2010
mod.widgets.FileTreeNode.draw_icon.color_active = #2a2010
mod.widgets.ListItem.draw_text_2.color = #584f41
// A chip is a small patch of the segment yarn; its role colour only tints
// it.
mod.widgets.ChipFlat.draw_text.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.ChipFlat.draw_text.color = #efe6d2
// The chip's word takes its ink from its role palette.
mod.widgets.ChipFlat.palette.on_neutral_container = #efe6d2
mod.widgets.Chip.palette.on_neutral_container = #efe6d2
mod.widgets.ChipFlat.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ChipFlat.draw_bg.pixel = fn() {
let m = 8.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let l = Finish.lum(self.color.rgb)
let tint = (self.color.rgb - vec3(l, l, l)) * 0.15 * self.color.a
let col = vec3(0.086, 0.153, 0.243) + tint
var res = mk_patch(p, lo, hi, (hi.y - lo.y) * 0.5, px, col, col * 0.840, self.hover, self.down, 0.0, 0.8, 0.59)
if self.keyed > 0.0 {
let d = Material.sd_box(p, (lo + hi) * 0.5, (hi - lo) * 0.5, (hi.y - lo.y) * 0.5)
let rs = mk_running(mk_perim(p, (lo + hi) * 0.5, (hi - lo) * 0.5, (hi.y - lo.y) * 0.5), d - 2.2, 2.2, 1.4, 1.2, px)
res = Finish.over(res, vec4(vec3(1.000, 0.824, 0.353), 1.0) * rs.x * self.keyed)
}
return res
}
// A count is a small patch of the badge yarn, its figures in its ink.
mod.widgets.BadgePalette.neutral = #8a4bc4
mod.widgets.BadgePalette.on_neutral = #f6f0e2
mod.widgets.BadgeFlat.shape = mod.widgets.BadgeShape.Rounded
mod.widgets.BadgeFlat.radius = 5.0
mod.widgets.BadgeFlat.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * self.rect_size
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x - px * 0.49, o.x, px), mk_snap(self.rect_size.y - px * 0.49, o.y, px))
let fill = self.color.rgb
let res = mk_patch(p, lo, hi, 5.0, px, fill, fill * 0.840, 0.0, 0.0, 0.0, 0.0, 0.17)
return res * max(self.color.a, 0.0)
}
// ---------------------------------------------------------------- screens
// A SCREEN: a patch of the screen's dark knit sewn in a couched rope -- a
// cord of two plies `rw` thick laid round its edge -- the readouts' and the
// meter's one housing. The knit lies flat and the rope shades its top
// edge. `face` is its knit. Premultiplied.
let mk_screen = fn(p: vec2, lo: vec2, hi: vec2, px: float, face: vec3, seed: float) -> vec4 {
let c = (lo + hi) * 0.5
let h = max((hi - lo) * 0.5, vec2(0.5, 0.5))
let r = min(7.0, min(h.x, h.y))
let d = Material.sd_box(p, c, h, r)
let rw = 3.2
let contact = Finish.drop(p, c, h, r, vec2(0.0, 1.0), 1.0) * 0.55 * 0.550
var res = vec4(vec3(0.008, 0.039, 0.078) * contact, contact)
// The knit.
let q = p - lo + vec2(seed * 29.0, seed * 13.0)
let k = mk_knit(q, 4.0, 2.4, 0.5)
var body = mk_yarn(face, face * 0.45, k, q, px, 0.700 * 0.5)
let ins = Finish.inset(p, c, h - vec2(rw, rw), max(r - rw, 1.0), vec2(0.0, 1.6), 1.8)
body = mix(body, body * 0.55, ins * 0.8)
// The rope.
let n = mk_box_n(p, c, h, r)
let s_ed = mk_perim(p, c, h, r)
let ac = clamp(((0.0 - d) - rw * 0.5) / (rw * 0.5), -1.0, 1.0)
let pl = mk_ply(s_ed, ac, rw * 1.5)
let lit = mk_lit(n) * (0.0 - ac) * 0.8 + pl.y * 0.3
var rope = vec3(0.914, 0.698, 0.227) * (0.42 + 0.66 * pl.x) * (0.88 + 0.35 * lit)
rope = rope * (1.0 + (Finish.hash(floor(p / px) + vec2(6.6, seed)) - 0.5) * 0.08)
let in_rope = 1.0 - smoothstep(rw - px * 0.5, rw + px * 0.5, 0.0 - d)
var col = mix(body, rope, in_rope * smoothstep(0.02, 0.25, pl.x))
col = mix(col, col * 0.5, exp(0.0 - ((0.0 - d - rw) * (0.0 - d - rw)) / 0.3) * 0.6)
let fray = (Finish.hash(floor(p / px) + vec2(seed, 2.0)) - 0.5) * 0.3
let a = Finish.cover(d - fray, px)
return Finish.over(res, vec4(col * a, a))
}
mod.widgets.ScreenView.clip_x = false
mod.widgets.ScreenView.clip_y = false
mod.widgets.ScreenView.padding = mod.turtle.Inset{top: 8.0 right: 10.0 bottom: 8.0 left: 10.0}
mod.widgets.ScreenView.draw_bg.vertex = fn() {
let m = 4.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ScreenView.draw_bg.pixel = fn() {
let m = 4.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px) - px)
let res = mk_screen(p, lo, hi, px, self.color.rgb, fract(self.rect_size.x * 0.011))
return res * self.color.a
}
// No glass over the knit.
mod.widgets.ScreenView.sheen = 0.0
mod.widgets.ScreenView.draw_glass.pixel = fn() {
return vec4(0.0, 0.0, 0.0, 0.0)
}
// THE NEEDLE METER is a screen: the same knit in its rope, its scale sewn
// on in the screen ink -- the arc a running stitch, the ticks short satin
// stitches -- the red zone a band of red yarn along the arc, the needle a
// pale cord from a knitted bead at its root.
mod.widgets.NeedleMeter.clip_x = false
mod.widgets.NeedleMeter.clip_y = false
mod.widgets.NeedleMeter.draw_bg.color_zone = uniform(#e0383a)
mod.widgets.NeedleMeter.draw_bg.color_needle = uniform(#f6f0e2)
mod.widgets.NeedleMeter.draw_bg.vertex = fn() {
let m = 4.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.NeedleMeter.draw_bg.pixel = fn() {
let m = 4.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px) - px)
var res = mk_screen(p, lo, hi, px, self.color.rgb, 0.43)
let pv = self.pivot()
let rr = self.scale_radius()
let q = p - pv
let r = length(q)
let ang = atan2(q.x, 0.0 - q.y)
let sweep = self.sweep * 0.017453293
let t = ang / sweep + 0.5
let on_scale = step(-0.0001, t) * step(t, 1.0001)
let ink = self.color_scale.rgb
// The arc: a running stitch.
let arc = mk_running(ang * rr, r - rr, 2.0, 1.4, 1.0, px)
res = Finish.over(res, vec4(ink * (0.85 + 0.25 * arc.y), 1.0) * arc.x * on_scale * 0.8)
// The ticks: short satin stitches, the majors longer.
let n = max(floor(self.majors + 0.5), 1.0) * max(floor(self.minors + 0.5), 1.0)
let k = floor(clamp(t, 0.0, 1.0) * n + 0.5)
let ak = (k / n - 0.5) * sweep
let tdir = vec2(sin(ak), 0.0 - cos(ak))
let mn = max(floor(self.minors + 0.5), 1.0)
let major = 1.0 - step(0.5, k - mn * floor(k / mn + 0.0001))
let len = mix(self.tick_minor, self.tick_major, major)
res = mk_satin(res, p, pv + tdir * (rr - len), pv + tdir * (rr - 0.5), mix(1.0, 1.6, major), px, ink, 0.9)
if self.zone < 0.999 {
let za = (self.zone - 0.5) * sweep
let zone_t = step(za, ang) * step(ang, sweep * 0.5)
let zd = abs(r - rr - 1.6) - 1.3
let zy = vec3(0.880, 0.220, 0.220) * (0.75 + 0.35 * mk_ply(ang * rr, (r - rr - 1.6) / 1.3, 2.4).x)
res = Finish.over(res, vec4(zy, 1.0) * Finish.cover(zd, px) * zone_t)
}
// The needle: a pale cord, its shade under it.
let na = self.needle_angle(self.value)
let nd = vec2(sin(na), 0.0 - cos(na))
let tip = pv + nd * (rr + 3.0)
let root = pv - nd * max(self.pivot_size - px, 0.0)
let sd = mk_seg(p - vec2(0.4, 1.0), root, tip) - 1.0
res = Finish.over(res, vec4(0.0, 0.0, 0.0, 1.0) * Finish.cover(sd, px) * 0.35)
res = mk_satin(res, p, root, tip, 1.6, px, self.color_needle.rgb, 1.0)
// The bead at its root.
return mk_ball(res, p, pv, max(self.pivot_size, 2.5) + 0.8, px, vec3(0.914, 0.867, 0.773), 0.0, 0.0, 1.0) * self.opacity
}
// ---------------------------------------------------------------- switches
// THE SWITCHES are the sheet's own toggle: the rocker and the slide switch
// take the toggle's pill, fill and ball, its size and its label gap. The
// rocker turns on a click; the slide switch follows a drag, as the toggle
// does.
mod.widgets.ToggleRocker.draw_bg.size = uniform(19.0)
mod.widgets.ToggleSlide.draw_bg.size = uniform(19.0)
mod.widgets.ToggleRocker.draw_bg.pill_aspect = uniform(1.9)
mod.widgets.ToggleSlide.draw_bg.pill_aspect = uniform(1.9)
mod.widgets.ToggleRocker.label_walk.margin.left = 36.0
mod.widgets.ToggleSlide.label_walk.margin.left = 36.0
mod.widgets.ToggleRocker.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
mod.widgets.ToggleSlide.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
// ---------------------------------------------------------------- field row
// A ROW OF FIELDS IS ONE HEIGHT. The box metrics are stated ONCE here and
// handed to each field below, so the search box, a number and a drop-down
// beside it stand at one height.
let field_height = 26.0
let field_pad_v = 4.0
let field_radius = 6.0
let field_border = 0.34
let field_room = fn(w) {
w.min_height = field_height
w.padding.top = field_pad_v
w.padding.bottom = field_pad_v
}
let field_face = fn(d) {
d.border_radius = field_radius
d.border_size = field_border
}
// A field that states a frame and lays its own parts out inside it: a number
// with a stepper, a date, a time. The height is all these take.
let field_frame = fn(w) {
w.min_height = field_height
}
field_room(mod.widgets.TextInput)
field_face(mod.widgets.TextInput.draw_bg)
field_room(mod.widgets.ComboBox)
field_face(mod.widgets.ComboBox.draw_bg)
field_room(mod.widgets.DropDown)
field_face(mod.widgets.DropDown.draw_bg)
field_room(mod.widgets.DropDown2)
field_face(mod.widgets.DropDown2.draw_bg)
field_room(mod.widgets.FieldWell)
field_face(mod.widgets.FieldWell.draw_bg)
field_room(mod.widgets.TagField)
field_face(mod.widgets.TagField.draw_bg)
field_frame(mod.widgets.NumberField)
mod.widgets.NumberField.draw_bg.border_radius = field_radius
field_frame(mod.widgets.ValueInput)
field_frame(mod.widgets.DateField)
field_frame(mod.widgets.TimeField)
field_frame(mod.widgets.DatePicker)
field_frame(mod.widgets.DateRangePicker)
// The chrome-less input a well holds must impose no height of its own.
mod.widgets.WellInput.min_height = 0.0
// ---------------------------------------------------------------- rules
// A rule is a running stitch of the seam's yarn across the middle of its
// room.
mod.widgets.Hr.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let rs = mk_running(p.x, p.y - self.rect_size.y * 0.5, 3.0, 2.0, 1.3, px)
return vec4(vec3(0.075, 0.184, 0.314) * (0.85 + 0.3 * rs.y), 1.0) * rs.x
}
mod.widgets.Vr.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let rs = mk_running(p.y, p.x - self.rect_size.x * 0.5, 3.0, 2.0, 1.3, px)
return vec4(vec3(0.075, 0.184, 0.314) * (0.85 + 0.3 * rs.y), 1.0) * rs.x
}
// A splitter is a seam where two pieces of the fabric are sewn together:
// a running stitch of the seam's yarn down the middle of its strip, the
// fabric showing either side. Under the hand or dragged, a cord of the
// accent lies along its grip.
mod.widgets.Splitter.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let v = step(0.5, self.is_vertical)
let mid = self.rect_size * 0.5
let s = mix(p.x, p.y, v)
let t = mix(p.y - mid.y, p.x - mid.x, v)
let rs = mk_running(s, t, 3.0, 2.0, 1.3, px)
var res = vec4(vec3(0.075, 0.184, 0.314) * (0.85 + 0.3 * rs.y), 1.0) * rs.x * 0.8
let hv = clamp(max(self.hover, max(self.drag, self.focus)), 0.0, 1.0)
let hb = mix(vec2(self.bar_size * 0.5, 1.6), vec2(1.6, self.bar_size * 0.5), v)
let cord = mk_cord(vec4(0.0, 0.0, 0.0, 0.0), p, mid - hb, mid + hb, px, vec3(1.000, 0.824, 0.353), 0.0, v)
return Finish.over(res, cord * hv)
}
// ---------------------------------------------------------------- ground
// Plain views stop clipping so a shadow can fall past their edge; scroll
// views keep clipping their content.
mod.widgets.View.clip_x = false
mod.widgets.View.clip_y = false
mod.widgets.Window.pass.clear_color = #255a87
// The window's own ground: the stockinette and its ribbed cuffs
// (`mk_ground`).
mod.widgets.Window.show_bg = true
mod.widgets.Window.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
return vec4(mk_ground(p + self.rect_pos, px, self.rect_size), 1.0)
}
// ---------------------------------------------------------------- states
// Every held state is written as a plain value that cuts in at once (the
// library writes some as snap() values, which a state held as a default --
// the states page -- never applies). Live, the cut is the same as the snap.
mod.widgets.ButtonFlat.animator.hover.on.from = {all: Snap}
mod.widgets.ButtonFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
mod.widgets.ButtonFlat.animator.hover.down.from = {all: Snap}
mod.widgets.ButtonFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.CheckBoxFlat.animator.hover.down.from = {all: Snap}
mod.widgets.CheckBoxFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.RadioButtonFlat.animator.hover.down.from = {all: Snap}
mod.widgets.RadioButtonFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.TextInputFlat.animator.hover.on.from = {all: Snap}
mod.widgets.TextInputFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
mod.widgets.TextInputFlat.animator.hover.down.from = {all: Snap}
mod.widgets.TextInputFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.DropDownFlat.animator.hover.on.from = {all: Snap}
mod.widgets.DropDownFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
mod.widgets.DropDownFlat.animator.hover.down.from = {all: Snap}
mod.widgets.DropDownFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
// ---------------------------------------------------------------- ink
// Words are sewn on: a device pixel under every glyph of a label lies a
// second copy of it, the shade the raised thread throws -- faint under dark
// ink on the pale yarn, deeper under pale ink on the dark. Read from the
// glyph one device pixel above, so it costs one more sample and nothing
// else. On a patch the label also sinks with it: held, the glyphs are read
// one device pixel higher in the atlas, so they stand one pixel lower.
// Small labels come from the grayscale atlas, large ones from the
// multi-channel one; both paths carry the shade and the nudge. A template
// made from another before this sheet ran keeps its own copy of the
// label's draw, so each patch template takes the literal.
// Sewn words: on a patch a label is satin stitch, its threads lying slant
// across every stroke, a little lighter along each thread's crown. Laid in
// device pixels over the glyph's own quad, so each letter's threads run on
// their own as a sewn letter's do.
let mk_thread = fn(main: vec4, pos: vec2) -> vec4 {
let pa = max(abs(dFdx(pos.x)) + abs(dFdy(pos.x)), 0.0000001)
let pb = max(abs(dFdx(pos.y)) + abs(dFdy(pos.y)), 0.0000001)
let f = fract((pos.x / pa * 0.8 + pos.y / pb) / 2.8) * 2.0 - 1.0
let k = 0.70 + 0.34 * sqrt(max(1.0 - f * f, 0.0))
return vec4(main.rgb * k, main.a)
}
mod.widgets.ButtonFlat.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.Button.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonPrimary.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonSecondary.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonTertiary.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonDanger.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonOutline.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonFlatter.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonXs.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonSm.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonLg.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
mod.widgets.ButtonXl.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(self.sample_text_pixel(), self.pos)
}
// The same shade under every label that stands on the fabric or a patch.
mod.widgets.Label.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.CheckBoxFlat.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.RadioButtonFlat.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.SegmentedControlFlat.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return mk_thread(main, self.pos) + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(main, self.pos)
}
mod.widgets.Tabs.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return mk_thread(main, self.pos) + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(main, self.pos)
}
mod.widgets.Tabs.draw_text_active.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return mk_thread(main, self.pos) + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(main, self.pos)
}
mod.widgets.SliderMinimal.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.RadioButtonTabFlat.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return mk_thread(main, self.pos) + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(main, self.pos)
}
mod.widgets.SegmentedControlFlat.draw_text_selected.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(0.000, 0.000, 0.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.500, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return mk_thread(main, self.pos) + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return mk_thread(main, self.pos)
}
true