use mod.prelude.widgets_internal.* // Kawaii. Every part is a sweet on a candy-pink ground: a key is a glossy // pastel jelly, outlined in a deeper shade of its own colour, lit from the // upper left -- a soft gloss over its upper half and a crisp white glint at // its upper left corner -- while the light that comes back through the jelly // pools as a brighter glow low inside it. Thumbs, radios and handles are // pearls; checks, pointers and the fader cap are jelly hearts. Shadows are // soft and rose-tinted, never grey. Every face is drawn in true points (one // device pixel is `px`); shadows, glows and rings that fall outside a layout // box are drawn in a quad grown in `vertex`, and no padding is spent on // relief. // ---------------------------------------------------------------- helpers // One device pixel in points. let kw_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. let kw_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 kw_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) } // The sweet box: one pastel per kind of part. 0 strawberry milk, 1 lavender, // 2 mint, 3 peach, 4 lemon, 5 baby blue, 6 raspberry, 7 cream, 8 hot pink, // 9 pearl white, 10 lilac grey. let kw_col = fn(k: float) -> vec3 { if k < 0.5 { return vec3(0.980, 0.680, 0.850) } if k < 1.5 { return vec3(0.820, 0.702, 0.988) } if k < 2.5 { return vec3(0.702, 0.929, 0.761) } if k < 3.5 { return vec3(0.988, 0.769, 0.639) } if k < 4.5 { return vec3(0.988, 0.890, 0.561) } if k < 5.5 { return vec3(0.702, 0.780, 0.988) } if k < 6.5 { return vec3(0.800, 0.122, 0.420) } if k < 7.5 { return vec3(1.0, 0.957, 0.925) } if k < 8.5 { return vec3(0.957, 0.447, 0.690) } if k < 9.5 { return vec3(1.0, 0.969, 0.984) } return vec3(0.800, 0.753, 0.871) } // The colour deep in the jelly where it is thickest: richer and a little // darker, as light that has crossed more of a coloured sweet is. let kw_deep = fn(c: vec3) -> vec3 { return pow(c, vec3(1.7, 1.7, 1.7)) * 0.97 } // The outline a sweet is drawn with: its own hue, deep and saturated. let kw_rim = fn(c: vec3) -> vec3 { return pow(c, vec3(2.7, 2.7, 2.7)) * 0.86 } // A part that cannot be used: its colour washed to a pale lilac grey. let kw_dull = fn(c: vec3, disabled: float) -> vec3 { let l = Finish.lum(c) let g = mix(c, vec3(l, l, l), 0.7 * disabled) return mix(g, vec3(0.60, 0.55, 0.70), 0.62 * disabled) } // The shadow every sweet throws on the ground (unpremultiplied ink): a deep // rose, so a shadow on the pink ground reads as a darker pink, never grey. let kw_shade = fn() -> vec3 { return vec3(0.64, 0.16, 0.40) } // Signed grain, -1..1, one value per device pixel of window space `w`. // Each pixel is first sent to a far cell picked by the hash of its own, so // the hash's near-repeat every 97 pixels never lines up into a lattice. let kw_grain = fn(w: vec2, px: float) -> float { let i = floor(w / px) let j = i + vec2(Finish.hash(i) * 419.0, Finish.hash(i + vec2(5.31, 9.17)) * 263.0) return Finish.hash(j) - Finish.hash(j + vec2(17.13, 3.71)) } // Which way the outline of a rounded box faces at `p`: a straight side's // normal on the sides, an arc's on the corners. let kw_box_n = fn(p: vec2, c: vec2, h: vec2, r: float) -> vec2 { let s = vec2(step(0.0, p.x - c.x) * 2.0 - 1.0, step(0.0, p.y - c.y) * 2.0 - 1.0) let rr = clamp(r, 0.0, min(h.x, h.y)) let q = abs(p - c) - (h - vec2(rr, rr)) let qc = max(q, vec2(0.0, 0.0)) 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 = qc / max(length(qc), 0.0001) * s } return n } // The angle of `q` from straight down, clockwise, 0..2 pi. let kw_ang = fn(q: vec2) -> float { var ang = atan2(0.0 - q.x, q.y) if ang < 0.0 { ang = ang + 2.0 * PI } return ang } // ---------------------------------------------------------------- shapes // A heart standing upright, centred on `c`, `s` points from its middle to // its top and to its tip (it is a tenth wider than it is tall): the signed // distance in points, negative inside. let kw_heart_d = fn(p: vec2, c: vec2, s: float) -> float { let k = 0.552 let u = (p - c) / max(s, 0.01) * k let q = vec2(abs(u.x), k - u.y) var d = 0.0 if q.y + q.x > 1.0 { d = length(q - vec2(0.25, 0.75)) - 0.35355 } else { let a = q - vec2(0.0, 1.0) let m = max(q.x + q.y, 0.0) * 0.5 let b = q - vec2(m, m) d = sqrt(min(dot(a, a), dot(b, b))) * sign(q.x - q.y) } return d * max(s, 0.01) / k } // The same heart turned so its tip points along `tip` (a unit vector). let kw_heart_turned = fn(p: vec2, c: vec2, s: float, tip: vec2) -> float { let v = p - c let l = vec2(dot(v, vec2(tip.y, 0.0 - tip.x)), dot(v, tip)) return kw_heart_d(l, vec2(0.0, 0.0), s) } // A five-pointed star standing on two points, centred on `c`, its points // `r` out and its inner corners `r * rf` in, rounded by `rr`. let kw_star_d = fn(p: vec2, c: vec2, r: float, rf: float, rr: float) -> float { let k1 = vec2(0.809016994, -0.587785252) let k2 = vec2(0.0 - k1.x, k1.y) var q = vec2(abs(p.x - c.x), c.y - p.y) q = q - k1 * (2.0 * max(dot(k1, q), 0.0)) q = q - k2 * (2.0 * max(dot(k2, q), 0.0)) q = vec2(abs(q.x), q.y - r) let ba = vec2(0.0 - k1.y, k1.x) * rf - vec2(0.0, 1.0) let h = clamp(dot(q, ba) / dot(ba, ba), 0.0, r) return length(q - ba * h) * sign(q.y * ba.x - q.x * ba.y) - rr } // A four-pointed sparkle, its points `r` out: coverage 0..1, crisp at `px`. let kw_sparkle = fn(p: vec2, c: vec2, r: float, px: float) -> float { let q = abs(p - c) let d = min(q.x * 3.2 + q.y, q.x + q.y * 3.2) - r return Finish.cover(d / 2.2, px) } // ---------------------------------------------------------------- materials // The gloss of a wet sweet in its own 0..1 frame `uv`: a lens of light over // the upper part, brightest at the top, its lower edge crisp and rising // toward the sides. let kw_gloss = fn(uv: vec2) -> float { let x = uv.x * 2.0 - 1.0 let yb = 0.47 - 0.2 * x * x let body = (1.0 - smoothstep(yb - 0.07, yb + 0.02, uv.y)) * smoothstep(0.0, 0.12, uv.y) return body * mix(0.62, 1.0, clamp(1.0 - uv.y / max(yb, 0.05), 0.0, 1.0)) } // The light, from the upper left and toward the viewer. let kw_light = fn() -> vec3 { return normalize(vec3(-0.42, -0.70, 0.58)) } // THE JELLY of a shape whose signed distance at `p` is `d` and whose outline // faces `n` there, premultiplied, without its shadow. `bw` is the width of // its rounded shoulder, `t` 0..1 down its height, `uv` its own 0..1 frame // for the gloss. The jelly is `base`; toward the edge it thickens to its // deep colour, the shoulder facing the light is lit and the far one in // shade; light coming back through it pools in its lower half and lines its // lower edge just inside the outline; a soft gloss lies over its upper half // and the outline is its rim colour. let kw_jelly_face = fn(d: float, n: vec2, bw: float, t: float, uv: vec2, px: float, base: vec3, gloss: float, glow: float) -> vec4 { let white = vec3(1.0, 1.0, 1.0) let deep = kw_deep(base) let rim = kw_rim(base) let s = clamp(1.0 + d / max(bw, 0.5), 0.0, 1.0) let facing = dot(n, vec2(-0.38, -0.92)) var col = base * (0.97 + 0.05 * t) col = mix(col, deep, 0.62 * s * s) col = col * (1.0 + 0.10 * facing * s) // The glow: light through the jelly, gathered low in it. let pool = smoothstep(0.42, 0.98, t) * (1.0 - smoothstep(0.55, 1.0, s)) col = mix(col, min(base * 1.06 + vec3(0.10, 0.10, 0.10), white), pool * 0.6 * glow) // The gloss: a bright lens over the upper half. col = mix(col, white, kw_gloss(uv) * (1.0 - s * 0.8) * 0.5 * gloss) // The outline, and just inside it the clear edge of the jelly catching // the light: brightest along the top, again along the lower edge where // light comes back through. let ow = clamp(bw * 0.38, max(px, 0.6), 1.45) let lower = smoothstep(0.1, 0.9, n.y) let upper = smoothstep(0.2, 0.9, 0.0 - n.y) col = mix(col, min(base + vec3(0.3, 0.3, 0.3), white), Finish.band(d, ow, ow + px * 1.5, px) * (0.3 + 0.35 * upper + 0.3 * lower * glow)) col = mix(col, rim, Finish.band(d, 0.0, ow, px) * 0.95) let a = Finish.cover(d, px) return vec4(min(col, white) * a, a) } // The crisp glint of a jelly: a short white stroke following its upper left // shoulder and a dot after it, as a window seen in a wet sweet. let kw_glint = fn(dst: vec4, p: vec2, a: vec2, b: vec2, w: float, px: float, amt: float) -> vec4 { let d1 = kw_seg(p, a, b) - w let dot_c = b + (b - a) * 0.55 + vec2(0.0, w * 0.3) let d2 = length(p - dot_c) - w * 0.8 let cov = max(Finish.cover(d1, px), Finish.cover(d2, px)) return Finish.over(dst, vec4(1.0, 1.0, 1.0, 1.0) * cov * amt) } // A jelly key standing on the ground, premultiplied: `lo`..`hi` its box, `r` // its corner. Pressed, it sinks: its shadow tightens to a contact shade, the // ground's shade falls in along its top edge and its gloss dims. `lift` 1 // throws the full soft shadow, 0 seats it with the contact shade only. let kw_candy = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float, base: vec3, hover: float, down: float, disabled: float, focus: float, lift: float) -> vec4 { let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let hh = hi.y - lo.y let sz = min(min(hh, hi.x - lo.x), 40.0) let live = 1.0 - disabled let dn = clamp(down, 0.0, 1.0) * live let d = Material.sd_box(p, c, h, r) // The shadow: soft and rose, falling straight down, and a tight contact // shade along the foot. let reach = clamp(0.1 * sz, 1.0, 3.2) let s1 = Finish.drop(p, c, h - vec2(0.04 * sz, 0.0), r, vec2(0.0, reach), reach * 0.9) * 0.34 * lift * (1.0 - 0.8 * dn) let s2 = Finish.drop(p, c, h, r, vec2(0.0, px + 0.03 * sz), px + 0.03 * sz) * 0.32 let sh = (1.0 - (1.0 - s1) * (1.0 - s2)) * (1.0 - 0.5 * disabled) * (1.0 - Finish.cover(d, px)) var res = vec4(kw_shade() * sh, sh) var b = kw_dull(base, disabled) b = b + (vec3(1.0, 1.0, 1.0) - b) * 0.16 * hover * live b = b * (1.0 - 0.07 * dn) // Under the hand the sweet glows: a soft halo of its own colour. let halo = Finish.glow(d, 1.6) * 0.45 * hover * live * (1.0 - dn) * (1.0 - Finish.cover(d, px)) res = Finish.over(res, vec4(kw_rim(b) * halo, halo) * 0.6) let n = kw_box_n(p, c, h, r) let bw = clamp(0.3 * min(h.x, h.y), 2.0, 7.0) let t = clamp((p.y - lo.y) / max(hh, 1.0), 0.0, 1.0) let gi = vec2(min(r * 0.6, h.x), 1.0) let uv = clamp((p - lo - gi) / max(hi - lo - gi * 2.0, vec2(1.0, 1.0)), vec2(0.0, 0.0), vec2(1.0, 1.0)) let gloss = (1.0 - 0.75 * disabled) * (1.0 - 0.55 * dn) var face = kw_jelly_face(d, n, bw, t, uv, px, b, gloss, 1.0 - 0.5 * disabled) // Pressed: the ground's shade falls in along the top edge. let ins = Finish.inset(p, c, h, r, vec2(0.0, 1.6), 1.4) * dn face = vec4(mix(face.rgb, kw_rim(b) * face.a, ins * 0.45), face.a) res = Finish.over(res, face) // The glint, over the upper left shoulder; it slides down when pressed. let gy = lo.y + clamp(0.24 * hh, 2.2, 6.0) + 0.8 * dn let gx = lo.x + max(r * 0.55, 2.5) let gl = clamp(0.14 * (hi.x - lo.x), 2.5, 9.0) let gw = clamp(0.05 * hh, 0.7, 1.5) res = kw_glint(res, p, vec2(gx, gy + gw), vec2(gx + gl, gy), gw, px, 0.9 * gloss * step(14.0, hh)) // Focus: a hot-pink ring standing just off the key, with a breath of // pink light. let ring = Finish.ring_out(d, px, px * 2.6, px) + Finish.glow(d - px * 2.6, 1.2) * 0.35 * (1.0 - Finish.cover(d, px)) return Finish.over(res, vec4(1.0, 0.37, 0.64, 1.0) * clamp(ring, 0.0, 1.0) * focus * live) } // A JELLY HEART, premultiplied over `dst`: its shadow, its jelly, a glint. // `tip` the way its point faces (straight down for an upright heart). let kw_jelly_heart = fn(dst: vec4, p: vec2, c: vec2, s: float, tip: vec2, px: float, base: vec3, hover: float, down: float, disabled: float, lift: float) -> vec4 { let live = 1.0 - disabled var b = kw_dull(base, disabled) b = b + (vec3(1.0, 1.0, 1.0) - b) * 0.12 * hover * live b = b * (1.0 - 0.06 * down * live) let d = kw_heart_turned(p, c, s, tip) let e = max(px * 0.5, 0.05) let gx = kw_heart_turned(p + vec2(e, 0.0), c, s, tip) - d let gy = kw_heart_turned(p + vec2(0.0, e), c, s, tip) - d let n = vec2(gx, gy) / max(length(vec2(gx, gy)), 0.0001) let ds = kw_heart_turned(p - vec2(0.0, 0.16 * s + 0.4) * lift, c, s * 0.97, tip) let shs = max(0.22 * s, px) let sh = (1.0 - smoothstep(0.0 - shs, shs * 1.4, ds)) * 0.36 * lift * (1.0 - 0.5 * disabled) * (1.0 - Finish.cover(d, px)) var res = Finish.over(dst, vec4(kw_shade() * sh, sh)) let t = clamp((p.y - (c.y - s)) / max(2.0 * s, 1.0), 0.0, 1.0) let uv = clamp((p - (c - vec2(s * 0.8, s * 0.9))) / max(vec2(s * 1.6, s * 1.6), vec2(1.0, 1.0)), vec2(0.0, 0.0), vec2(1.0, 1.0)) let face = kw_jelly_face(d, n, clamp(0.42 * s, 1.2, 6.0), t, uv, px, b, 1.0 - 0.75 * disabled, 1.0 - 0.5 * disabled) res = Finish.over(res, face) // The glint on the left lobe. let g = c + vec2(0.0 - 0.52 * s, 0.0 - 0.42 * s) let gq = p - g let gd = length(vec2(gq.x * 0.8 + gq.y * 0.6, (gq.y * 0.8 - gq.x * 0.6) * 1.9)) - max(0.15 * s, 0.6) return Finish.over(res, vec4(1.0, 1.0, 1.0, 1.0) * Finish.cover(gd, px) * 0.9 * (1.0 - 0.7 * disabled) * Finish.cover(d, px)) } // A PEARL laid over `dst`, premultiplied: its soft rose shadow, a lit // sphere of nacre -- white with a blush, its turned-away rim taking the // faint lilac, mint and peach play of a pearl -- a broad soft lustre and a // crisp white glint up and to the left. `tint` blushes it. let kw_pearl = fn(dst: vec4, p: vec2, c: vec2, rad: float, px: float, tint: vec3, hover: float, held: float, disabled: float, lift: float) -> vec4 { let live = 1.0 - disabled var b = kw_dull(tint, disabled) b = b + (vec3(1.0, 1.0, 1.0) - b) * 0.15 * hover * live b = b * (1.0 - 0.05 * held * live) let so = c + vec2(0.04 * rad, 0.30 * rad) * lift let sd = length(p - so) - rad * 0.9 let shs = max(0.32 * rad, px) let sh = (1.0 - smoothstep(0.0 - shs, shs * 1.6, sd)) * 0.38 * lift let cd = length(p - (c + vec2(0.0, 0.12 * rad))) - rad let ct = (1.0 - smoothstep(-0.1 * rad, 0.18 * rad + px, cd)) * 0.3 let shadow = (1.0 - (1.0 - sh) * (1.0 - ct)) * (1.0 - 0.5 * disabled) var res = Finish.over(dst, vec4(kw_shade() * shadow, shadow)) let q = (p - c) / max(rad, 0.5) let r2 = dot(q, q) let z = sqrt(max(1.0 - r2, 0.0)) let n = vec3(q.x, q.y, z) let l = kw_light() let dif = max(dot(n, l), 0.0) let shade = mix(b * vec3(0.80, 0.70, 0.84), b, smoothstep(0.0, 0.9, dif)) var col = shade // The play of colour on the turned-away rim of the nacre. let fres = pow(1.0 - z, 1.6) let ang = atan2(q.y, q.x) let irid = mix(vec3(0.86, 0.80, 1.0), vec3(0.80, 0.98, 0.90), 0.5 + 0.5 * sin(ang * 2.0 + 0.6)) col = mix(col, mix(irid, vec3(1.0, 0.86, 0.80), 0.5 + 0.5 * sin(ang * 3.0 - 1.1)) * b, fres * 0.45) // Light coming back through the nacre, low on the far side. col = col + (vec3(1.0, 1.0, 1.0) - col) * smoothstep(0.35, 0.95, q.y) * smoothstep(0.4, 0.95, sqrt(r2)) * 0.35 let hv = normalize(l + vec3(0.0, 0.0, 1.0)) let nh = max(dot(n, hv), 0.0) let gloss = 1.0 - 0.8 * disabled col = col + vec3(1.0, 1.0, 1.0) * (pow(nh, 6.0) * 0.20 + pow(nh, 80.0) * 1.1) * gloss col = min(col, vec3(1.0, 1.0, 1.0)) let d = length(p - c) - rad col = mix(col, kw_rim(mix(b, vec3(0.98, 0.72, 0.86), 0.7)), Finish.band(d, 0.0, max(px * 1.2, 0.7), px) * 0.55) let a = Finish.cover(d, px) return Finish.over(res, vec4(col * a, a)) } // A JELLY BEAD laid over `dst`: a sphere of coloured jelly, its colour // deepening to the rim, light pooled low inside it and a crisp glint. let kw_bead = fn(dst: vec4, p: vec2, c: vec2, rad: float, px: float, base: vec3, lift: float) -> vec4 { let so = c + vec2(0.0, 0.32 * rad) * lift let sd = length(p - so) - rad * 0.9 let shs = max(0.3 * rad, px) let sh = (1.0 - smoothstep(0.0 - shs, shs * 1.5, sd)) * 0.3 * lift var res = Finish.over(dst, vec4(kw_shade() * sh, sh)) let q = (p - c) / max(rad, 0.3) let r2 = dot(q, q) let z = sqrt(max(1.0 - r2, 0.0)) let n = vec3(q.x, q.y, z) let l = kw_light() let dif = max(dot(n, l), 0.0) let deep = kw_deep(base) var col = mix(deep, base * 1.04, smoothstep(0.0, 0.85, dif)) col = mix(col, kw_rim(base), smoothstep(0.55, 1.0, sqrt(r2)) * 0.45) col = col + (vec3(1.0, 1.0, 1.0) - col) * smoothstep(0.2, 0.9, q.y) * (1.0 - smoothstep(0.75, 1.0, sqrt(r2))) * 0.4 let hv = normalize(l + vec3(0.0, 0.0, 1.0)) let nh = max(dot(n, hv), 0.0) col = col + vec3(1.0, 1.0, 1.0) * (pow(nh, 7.0) * 0.18 + pow(nh, 70.0) * 1.0) let d = length(p - c) - rad col = mix(col, kw_rim(base), Finish.band(d, 0.0, max(px, 0.6), px) * 0.6) let a = Finish.cover(d, px) return Finish.over(res, vec4(min(col, vec3(1.0, 1.0, 1.0)) * a, a)) } // A channel pressed into the candy, premultiplied: a pale strawberry-milk // floor `fl`, the ground's rose shade falling in along its upper wall, a // soft catch light on its lower lip and a rose outline. let kw_groove = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float, fl: vec3) -> vec4 { let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let d = Material.sd_box(p, c, h, r) let hh = hi.y - lo.y let t = clamp((p.y - lo.y) / max(hh, 1.0), 0.0, 1.0) var col = fl * (0.95 + 0.06 * t) let ins = Finish.inset(p, c, h, r, vec2(0.0, clamp(0.3 * hh, 0.6, 2.2)), clamp(0.28 * hh, px, 1.8)) col = mix(col, kw_deep(fl) * vec3(0.92, 0.80, 0.88), ins * 0.55) col = mix(col, kw_rim(mix(fl, vec3(0.97, 0.62, 0.80), 0.6)), Finish.band(d, 0.0, max(px * 1.3, 0.8), px) * 0.85) let a = Finish.cover(d, px) let lip = Finish.ring_out(d, 0.0, px * 1.5, px) * smoothstep(c.y - h.y * 0.2, c.y + h.y * 0.7, p.y) * 0.6 return Finish.over(vec4(vec3(1.0, 0.97, 0.99) * lip, lip), vec4(col * a, a)) } // A rod of jelly lying in a channel, laid over `dst`: round across, lit from // above, its gloss a line along its upper third and its lower half glowing. let kw_rod = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, base: vec3, disabled: float) -> vec4 { let c = (lo + hi) * 0.5 let h = max((hi - lo) * 0.5, vec2(0.25, 0.25)) let d = Material.sd_box(p, c, h, min(h.x, h.y)) let b = kw_dull(base, disabled) let deep = kw_deep(b) let v = clamp((p.y - c.y) / max(h.y, 0.25), -1.0, 1.0) let z = sqrt(max(1.0 - v * v, 0.0)) var col = mix(deep, b * 1.03, clamp(0.3 + 0.6 * z - 0.25 * v, 0.0, 1.0)) col = col + (vec3(1.0, 1.0, 1.0) - col) * smoothstep(0.2, 0.9, v) * 0.25 col = col + vec3(1.0, 1.0, 1.0) * exp(-((v + 0.48) * (v + 0.48)) / 0.05) * 0.5 * (1.0 - 0.8 * disabled) // Glitter suspended in the jelly: here and there a fleck catching the // light, white or pale gold, one device pixel each. let cell = floor((p + vec2(13.7, 5.3)) / px) let fl = step(0.93, Finish.hash(cell)) * Finish.cover(d + px, px) col = mix(col, mix(vec3(1.0, 1.0, 1.0), vec3(1.0, 0.92, 0.70), Finish.hash(cell + vec2(3.1, 1.7))), fl * 0.6 * (1.0 - disabled)) col = mix(col, kw_rim(b), Finish.band(d, 0.0, max(px, 0.6), px) * 0.6) let a = Finish.cover(d, px) return Finish.over(dst, vec4(min(col, vec3(1.0, 1.0, 1.0)) * a, a)) } // A PUFFY FRAME round a window, premultiplied: a cushion `bw` wide of // `rim`, round across like a pillow, lit along its crown; its outer edge // and the seam where the window starts drawn in the rim's deep shade; the // window's floor `fill` (at `fill_a`) with the cushion's shade falling in // under its top edge. let kw_frame = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float, bw: float, rim: vec3, fill: vec3, fill_a: float, hover: float, focus: float, disabled: float) -> vec4 { let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let d = Material.sd_box(p, c, h, r) let ih = max(h - vec2(bw, bw), vec2(0.5, 0.5)) let ir = max(r - bw, 0.5) let di = Material.sd_box(p, c, ih, ir) let live = 1.0 - disabled var rc = kw_dull(rim, disabled) rc = rc + (vec3(1.0, 1.0, 1.0) - rc) * 0.12 * hover * live rc = mix(rc, vec3(1.0, 0.45, 0.70), 0.55 * focus * live) let deep = kw_deep(rc) let insh = Finish.inset(p, c, ih, ir, vec2(0.0, 1.3), 1.1) let win = mix(fill, kw_deep(fill) * vec3(0.95, 0.85, 0.92), 0.45 * insh) let n = kw_box_n(p, c, h, r) let across = clamp(di / max(bw, 0.5), 0.0, 1.0) * 2.0 - 1.0 let facing = dot(n * across, vec2(-0.38, -0.92)) var fr = mix(deep, rc * 1.04, clamp(0.6 + 0.45 * facing, 0.0, 1.0)) let crown = exp(-((across + 0.1) * (across + 0.1)) / 0.12) * (0.55 + 0.45 * smoothstep(-0.2, 0.9, -n.y)) fr = mix(fr, vec3(1.0, 1.0, 1.0), crown * 0.5 * (1.0 - 0.6 * disabled)) let inframe = Finish.cover(0.0 - di, px) var col = mix(win, fr, inframe) col = mix(col, kw_rim(rc), Finish.band(di, 0.0, max(px, 0.6), px) * 0.6) col = mix(col, kw_rim(rc), Finish.band(d, 0.0, max(px * 1.4, 0.9), px) * 0.9) let a = Finish.cover(d, px) * mix(fill_a, 1.0, inframe) let fo = Finish.ring_out(d, px, px * 2.4, px) * focus * live return Finish.over(vec4(col * a, a), vec4(1.0, 0.37, 0.64, 1.0) * fo) } // A well pressed into the candy, where the chosen answer sits: a deep // raspberry jelly floor, the ground's shade falling in from its top edge, // light caught along its lower lip. let kw_sunk = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float, hover: float) -> vec4 { let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let d = Material.sd_box(p, c, h, r) let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0) let base = kw_col(6.0) + vec3(0.03, 0.02, 0.02) * hover var col = mix(base * 0.78, base * 1.04, t) let ins = Finish.inset(p, c, h, r, vec2(0.0, 1.8), 1.5) col = mix(col, vec3(0.36, 0.03, 0.17), ins * 0.55) col = mix(col, vec3(1.0, 0.62, 0.80), Finish.band(d, px, px * 2.4, px) * smoothstep(c.y + h.y * 0.3, hi.y, p.y) * 0.55) col = mix(col, vec3(0.42, 0.04, 0.20), Finish.band(d, 0.0, max(px * 1.4, 0.9), px) * 0.9) let a = Finish.cover(d, px) let lip = Finish.ring_out(d, 0.0, px * 1.5, px) * smoothstep(c.y, hi.y, p.y) * 0.7 return Finish.over(vec4(vec3(1.0, 0.97, 0.99) * lip, lip), vec4(col * a, a)) } // The focus ring: a hot-pink line standing a device pixel off the edge of // a shape at distance `d`, with a breath of pink light outside it. let kw_focus = fn(d: float, px: float) -> float { let ring = Finish.ring_out(d, px, px * 2.5, px) return clamp(ring + Finish.glow(d - px * 2.5, 1.0) * 0.35 * (1.0 - Finish.cover(d - px * 2.5, px)), 0.0, 1.0) } let kw_focus_col = fn() -> vec4 { return vec4(1.0, 0.37, 0.64, 1.0) } // The three-quarter arc a waiting button turns in place of its label. let kw_spinner = fn(dst: vec4, p: vec2, c: vec2, rad: float, t: float, amt: float, stroke: float) -> 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(0.69, 0.06, 0.37, amt)) return Finish.over(dst, sdf.result) } // ---------------------------------------------------------------- ornaments // THE ORNAMENTS of the frame and the panels' corners: a satin bow, a jelly // heart, a lemon star, a strawberry and a sparkle. Each is drawn whole over // `dst`, `s` its half size in points. // A satin ribbon bow centred on `c`, `s` its half width: two loops either // side of a knot, two tails falling below, the loops in shade where they // gather into the knot and lit along their upper curves. let kw_bow = fn(dst: vec4, p: vec2, c: vec2, s: float, base: vec3, px: float) -> vec4 { let q = (p - c) / max(s, 0.5) let m = vec2(abs(q.x), q.y) let rim = kw_rim(base) let deep = kw_deep(base) // The tails, behind the loops: ribbons falling outward, their ends cut // in a notch. let ta = vec2(0.08, 0.12) let tb = vec2(0.46, 0.86) let tdir = normalize(tb - ta) let tq = m - ta let along = dot(tq, tdir) let tw = 0.15 + 0.03 * clamp(along, 0.0, 1.0) var dt = kw_seg(m, ta, tb) - tw let notch = abs(dot(m - tb, vec2(tdir.y, 0.0 - tdir.x))) * 0.9 - dot(m - tb, tdir) * 1.2 - 0.12 dt = max(dt, 0.0 - notch + 0.12) // The loops: each a soft rounded wedge from the knot out to a broad end. let lc = vec2(0.52, -0.10) let lq = m - lc let rot = vec2(lq.x * 0.96 + lq.y * 0.28, lq.y * 0.96 - lq.x * 0.28) let pinch = 0.16 + 0.24 * smoothstep(-0.55, 0.35, rot.x) let dl = length(vec2(rot.x / 0.5, rot.y / max(pinch * 1.7, 0.05))) - 1.0 let dloop = dl * min(0.5, pinch * 1.7) * 0.85 // The knot. let dk = Material.sd_box(q, vec2(0.0, 0.0), vec2(0.16, 0.17), 0.09) let pxs = px / max(s, 0.5) var res = dst // Its soft shadow. let dall = min(min(dt, dloop), dk) let qs = (p - c - vec2(0.0, 0.12 * s)) / max(s, 0.5) let ms = vec2(abs(qs.x), qs.y) let lqs = ms - lc let rots = vec2(lqs.x * 0.96 + lqs.y * 0.28, lqs.y * 0.96 - lqs.x * 0.28) let pinchs = 0.16 + 0.24 * smoothstep(-0.55, 0.35, rots.x) let dls = (length(vec2(rots.x / 0.5, rots.y / max(pinchs * 1.7, 0.05))) - 1.0) * min(0.5, pinchs * 1.7) * 0.85 let shd = (1.0 - smoothstep(-0.08, 0.16, min(dls, kw_seg(ms, ta, tb) - tw))) * 0.3 * (1.0 - Finish.cover(dall, pxs)) res = Finish.over(res, vec4(kw_shade() * shd, shd)) // Tails. var tcol = mix(base * 0.92, deep, clamp(along * 0.8, 0.0, 1.0) * 0.6) tcol = mix(tcol, rim, Finish.band(dt, 0.0, pxs * 1.4, pxs) * 0.9) let at = Finish.cover(dt, pxs) res = Finish.over(res, vec4(tcol * at, at)) // Loops. let gather = 1.0 - smoothstep(-0.35, 0.3, rot.x) var lcol = mix(base * 1.03, deep * 0.92, gather * 0.75) let crest = exp(-((rot.y + pinch * 0.9) * (rot.y + pinch * 0.9)) / 0.012) * smoothstep(-0.2, 0.3, rot.x) lcol = mix(lcol, vec3(1.0, 1.0, 1.0), crest * 0.55) // The fold line running into each loop from the knot. let fold = Finish.cover(abs(rot.y + 0.02 - rot.x * 0.12) - pxs * 0.9, pxs) * smoothstep(0.3, -0.3, rot.x) * 0.5 lcol = mix(lcol, rim, fold) lcol = mix(lcol, rim, Finish.band(dloop, 0.0, pxs * 1.5, pxs) * 0.9) let al = Finish.cover(dloop, pxs) res = Finish.over(res, vec4(min(lcol, vec3(1.0, 1.0, 1.0)) * al, al)) // Knot. var kcol = mix(base * 1.02, deep, smoothstep(-0.15, 0.2, q.y) * 0.5) kcol = mix(kcol, vec3(1.0, 1.0, 1.0), exp(-((q.y + 0.08) * (q.y + 0.08)) / 0.004) * 0.45) kcol = mix(kcol, rim, Finish.band(dk, 0.0, pxs * 1.5, pxs) * 0.9) let ak = Finish.cover(dk, pxs) return Finish.over(res, vec4(min(kcol, vec3(1.0, 1.0, 1.0)) * ak, ak)) } // A lemon star with a soft rose shadow, a round shoulder and a glint. let kw_star = fn(dst: vec4, p: vec2, c: vec2, s: float, base: vec3, px: float) -> vec4 { let d = kw_star_d(p, c, s * 0.86, 0.46, s * 0.12) let ds = kw_star_d(p - vec2(0.0, 0.14 * s + 0.3), c, s * 0.86, 0.46, s * 0.12) let sh = (1.0 - smoothstep(0.0 - 0.2 * s, 0.25 * s, ds)) * 0.3 * (1.0 - Finish.cover(d, px)) var res = Finish.over(dst, vec4(kw_shade() * sh, sh)) let e = max(px * 0.5, 0.05) let gx = kw_star_d(p + vec2(e, 0.0), c, s * 0.86, 0.46, s * 0.12) - d let gy = kw_star_d(p + vec2(0.0, e), c, s * 0.86, 0.46, s * 0.12) - d let n = vec2(gx, gy) / max(length(vec2(gx, gy)), 0.0001) let t = clamp((p.y - (c.y - s)) / max(2.0 * s, 1.0), 0.0, 1.0) let uv = clamp((p - (c - vec2(s * 0.7, s * 0.8))) / max(vec2(s * 1.4, s * 1.4), vec2(1.0, 1.0)), vec2(0.0, 0.0), vec2(1.0, 1.0)) res = Finish.over(res, kw_jelly_face(d, n, clamp(0.3 * s, 0.8, 4.0), t, uv, px, base, 0.8, 1.0)) let gq = p - (c + vec2(0.0 - 0.18 * s, 0.0 - 0.3 * s)) let gd = length(vec2(gq.x, gq.y * 1.8)) - max(0.1 * s, 0.5) return Finish.over(res, vec4(1.0, 1.0, 1.0, 1.0) * Finish.cover(gd, px) * 0.85 * Finish.cover(d, px)) } // A strawberry: a rounded berry of red-pink jelly speckled with pale seeds, // a crown of mint leaves and a short stalk. let kw_strawberry = fn(dst: vec4, p: vec2, c: vec2, s: float, px: float) -> vec4 { let berry = vec3(0.98, 0.42, 0.52) let leaf = vec3(0.50, 0.85, 0.55) // The berry: a heart turned over, softened, its broad shoulders up. let bc = c + vec2(0.0, 0.12 * s) let db = kw_heart_turned(p, bc, s * 0.78, vec2(0.0, 1.0)) - 0.0 let q = (p - bc) / max(s, 0.5) let dbody = max(db, length(q * vec2(0.92, 1.0) - vec2(0.0, 0.02)) * s - s * 0.86) - 0.06 * s let e = max(px * 0.5, 0.05) let gx = max(kw_heart_turned(p + vec2(e, 0.0), bc, s * 0.78, vec2(0.0, 1.0)), length((p + vec2(e, 0.0) - bc) / max(s, 0.5) * vec2(0.92, 1.0) - vec2(0.0, 0.02)) * s - s * 0.86) - 0.06 * s - dbody let gy = max(kw_heart_turned(p + vec2(0.0, e), bc, s * 0.78, vec2(0.0, 1.0)), length((p + vec2(0.0, e) - bc) / max(s, 0.5) * vec2(0.92, 1.0) - vec2(0.0, 0.02)) * s - s * 0.86) - 0.06 * s - dbody let n = vec2(gx, gy) / max(length(vec2(gx, gy)), 0.0001) let dsh = length((p - bc - vec2(0.0, 0.16 * s)) / max(s, 0.5) * vec2(0.92, 1.0)) * s - s * 0.8 let sh = (1.0 - smoothstep(0.0 - 0.2 * s, 0.25 * s, dsh)) * 0.3 * (1.0 - Finish.cover(dbody, px)) var res = Finish.over(dst, vec4(kw_shade() * sh, sh)) let t = clamp((p.y - (c.y - s)) / max(2.0 * s, 1.0), 0.0, 1.0) let uv = clamp((p - (c - vec2(s * 0.7, s * 0.6))) / max(vec2(s * 1.4, s * 1.4), vec2(1.0, 1.0)), vec2(0.0, 0.0), vec2(1.0, 1.0)) var body = kw_jelly_face(dbody, n, clamp(0.35 * s, 0.8, 5.0), t, uv, px, berry, 0.7, 1.0) // The seeds: small pale drops set in rows, each in its own dimple. let sp = max(0.28 * s, 1.2) let row = floor((p.y - bc.y) / sp + 0.5) let sx = (p.x - bc.x) / sp + 0.5 * (row - 2.0 * floor(row * 0.5)) let cell = vec2((fract(sx + 0.5) - 0.5) * sp, ((p.y - bc.y) / sp - row) * sp) let sd = length(vec2(cell.x * 1.4, cell.y)) - max(0.06 * s, 0.45) let inner = Finish.cover(dbody + 0.18 * s, px) body = vec4(mix(body.rgb, vec3(1.0, 0.95, 0.70) * body.a, Finish.cover(sd, px) * inner * 0.95), body.a) res = Finish.over(res, body) // The crown of leaves: five pointed leaves fanned over the top. var dl = 100.0 var i = 0.0 loop { if i > 4.5 { break } let a = -1.15 + i * 0.575 let dir = vec2(sin(a), 0.0 - cos(a) * 0.55) let lc = c + vec2(0.0, 0.0 - 0.5 * s) let tipp = lc + dir * s * 0.62 let dd = kw_seg(p, lc, tipp) - s * 0.13 * (1.0 - 0.75 * clamp(dot(p - lc, dir) / max(s * 0.62, 0.1), 0.0, 1.0)) dl = min(dl, dd) i = i + 1.0 } let dstalk = kw_seg(p, c + vec2(0.0, 0.0 - 0.5 * s), c + vec2(0.1 * s, 0.0 - 0.88 * s)) - 0.07 * s dl = min(dl, dstalk) var lcol = mix(leaf * 1.05, kw_deep(leaf), smoothstep(c.y - 0.9 * s, c.y - 0.3 * s, p.y) * 0.5) lcol = mix(lcol, kw_rim(leaf), Finish.band(dl, 0.0, max(px * 1.2, 0.6), px) * 0.9) let al = Finish.cover(dl, px) return Finish.over(res, vec4(lcol * al, al)) } // A LACE TRIM along a straight edge: a row of scallops `rad` round hanging // from the line y = `y0` (in the direction `dir`, 1 down, -1 up) across x, // each pierced by an eyelet and a pinhole either side, white with a rose // shade under it. Returned premultiplied. let kw_lace = fn(p: vec2, x0: float, x1: float, y0: float, dir: float, rad: float, px: float) -> vec4 { let span = max(x1 - x0, 1.0) let n = max(floor(span / (rad * 2.0)), 1.0) let pitch = span / n let k = clamp(floor((p.x - x0) / pitch), 0.0, n - 1.0) let cx = x0 + (k + 0.5) * pitch let v = (p.y - y0) * dir let ds = length(vec2(p.x - cx, max(v, 0.0) * pitch / (rad * 2.0) * 1.05)) - pitch * 0.5 var d = max(ds, 0.0 - v - rad * 0.55) d = max(d, p.x - x1) d = max(d, x0 - p.x) // The eyelet in each scallop and pinholes along the band. let eye = length(vec2(p.x - cx, v - rad * 0.35)) - rad * 0.3 let pin = length(vec2(p.x - (cx + pitch * 0.5), v + rad * 0.15)) - rad * 0.14 let holes = min(eye, pin) let dl = max(d, 0.0 - holes) var col = vec3(1.0, 0.985, 0.99) col = mix(col, vec3(0.97, 0.80, 0.89), smoothstep(0.0, rad, v) * 0.35) col = mix(col, vec3(0.93, 0.62, 0.78), Finish.band(dl, 0.0, max(px, 0.6), px) * 0.75) let a = Finish.cover(dl, px) // A soft rose shade round the lace where it lies on the candy. let s = (1.0 - smoothstep(0.0, 1.3, d)) * 0.22 * (1.0 - a) return vec4(col * a, a) + vec4(kw_shade() * s, s) } // ---------------------------------------------------------------- buttons // Every button is a jelly candy, round-ended so its label sits in a soft // pill, each kind in its own pastel with its label in a deep shade of the // same colour: the plain button strawberry milk, the primary lavender, the // secondary mint, the tertiary peach, the flat one lilac, the large one // baby blue, the icon key lemon; the danger button is a deep raspberry // jelly with white words, and the outline is a cream sweet ringed in rose. mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 13.0 right: 13.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonFlat.min_height = 24.0 mod.widgets.ButtonFlat.icon_walk.width = 14.0 mod.widgets.ButtonIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonFlatIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonFlatterIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonPrimary.padding = mod.turtle.Inset{left: 13.0 right: 13.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonSecondary.padding = mod.turtle.Inset{left: 13.0 right: 13.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonTertiary.padding = mod.turtle.Inset{left: 13.0 right: 13.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonDanger.padding = mod.turtle.Inset{left: 13.0 right: 13.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonOutline.padding = mod.turtle.Inset{left: 13.0 right: 13.0 top: 3.0 bottom: 3.0} mod.widgets.ButtonXs.min_height = 0.0 mod.widgets.ButtonSm.min_height = 0.0 mod.widgets.ButtonSm.padding = mod.turtle.Inset{left: 9.0 right: 9.0 top: 1.0 bottom: 1.0} mod.widgets.ButtonLg.padding = mod.turtle.Inset{left: 15.0 right: 15.0 top: 2.0 bottom: 2.0} mod.widgets.ButtonXs.draw_text.text_style.font_size = 6.5 mod.widgets.ButtonFlat.draw_bg.border_radius = uniform(4.0) mod.widgets.ButtonFlat.draw_bg.border_size = uniform(0.67) mod.widgets.ButtonFlat.clip_x = false mod.widgets.ButtonFlat.clip_y = false mod.widgets.ButtonFlat.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)) } // The candy's colour for a button `kind`: 0 flat (lilac), 1 plain // (strawberry milk), 2 primary (lavender), 3 secondary (mint), 4 tertiary // (peach), 5 danger (raspberry), 8 icon (lemon), 9 large (baby blue). let kw_key_col = fn(kind: float) -> vec3 { if kind < 0.5 { return vec3(0.910, 0.868, 0.996) } if kind < 1.5 { return kw_col(0.0) } if kind < 2.5 { return kw_col(1.0) } if kind < 3.5 { return kw_col(2.0) } if kind < 4.5 { return kw_col(3.0) } if kind < 5.5 { return kw_col(6.0) } if kind < 8.5 { return kw_col(4.0) } return kw_col(5.0) } // The candy and its states, shared by every button template; 6 is the // outline and 7 the flatter button, a pale pink candy only under the hand. let kw_button = fn(pos: vec2, size: vec2, o: vec2, dpi: float, hover: float, down: float, disabled: float, focus: float, loading: float, anim: float, kind: float, dash: float) -> vec4 { let m = 8.0 let px = kw_px(dpi) let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m) let lo = vec2(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(size.x, o.x, px), kw_snap(size.y, o.y, px)) let hh = hi.y - lo.y let r = min(clamp(0.42 * hh, 4.0, 14.0), min(hi.x - lo.x, hh) * 0.5) var res = vec4(0.0, 0.0, 0.0, 0.0) if kind > 5.5 && kind < 7.5 { if kind > 6.5 { if hover + down + focus > 0.001 { res = kw_candy(p, lo, hi, r, px, kw_col(0.0), hover, down, disabled, focus, 0.6) * clamp(max(max(hover, down), focus), 0.0, 1.0) } } else { // The outline: a cream sweet ringed in a rose band; a dashed // outline breaks its ring. var key = kw_candy(p, lo, hi, r, px, kw_col(7.0), hover, down, disabled, focus, 1.0) let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let d = Material.sd_box(p, c, h, r) var on = 1.0 if dash > 0.0 { on = step(0.5, fract((p.x + p.y) / (dash * 2.0))) } let ring = Finish.band(d, 0.0, 1.8, px) * on * (1.0 - 0.5 * disabled) key = vec4(mix(key.rgb, kw_dull(vec3(0.93, 0.47, 0.70), disabled) * key.a, ring * 0.9), key.a) res = key } } else { res = kw_candy(p, lo, hi, r, px, kw_key_col(kind), hover, down, disabled, focus, 1.0) } if loading > 0.001 { let rad = max(min(hi.x - lo.x, hh) * 0.5 - 6.0, 2.0) res = kw_spinner(res, p, (lo + hi) * 0.5, rad, anim, loading, 1.5) } return res } mod.widgets.ButtonFlat.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.Button.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.ButtonSm.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.ButtonLg.draw_bg.pixel = fn() { return self.host_over(kw_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, 9.0, 0.0), 8.0) } mod.widgets.ButtonXs.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.ButtonXl.draw_bg.pixel = fn() { return self.host_over(kw_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, 9.0, 0.0), 8.0) } mod.widgets.ButtonIcon.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.ButtonPrimary.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.ButtonSecondary.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.ButtonTertiary.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.ButtonDanger.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } mod.widgets.ButtonOutline.draw_bg.pixel = fn() { return self.host_over(kw_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, self.border_dash), 8.0) } mod.widgets.ButtonFlatter.draw_bg.pixel = fn() { return self.host_over(kw_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, 0.0), 8.0) } // Label ink per candy: a deep shade of the candy's own colour, at least 5:1 // on it; white on the raspberry; disabled, a pale lilac on the grey sweet. mod.widgets.ButtonFlat.draw_text.color = #5a1fa0 mod.widgets.ButtonFlat.draw_text.color_hover = #4a1490 mod.widgets.ButtonFlat.draw_text.color_down = #4a1490 mod.widgets.ButtonFlat.draw_text.color_focus = #5a1fa0 mod.widgets.ButtonFlat.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonFlat.draw_icon.color = #5a1fa0 mod.widgets.Button.draw_text.color = #7e0a46 mod.widgets.Button.draw_text.color_hover = #6e0640 mod.widgets.Button.draw_text.color_down = #6e0640 mod.widgets.Button.draw_text.color_focus = #7e0a46 mod.widgets.Button.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonSm.draw_text.color = #7e0a46 mod.widgets.ButtonSm.draw_text.color_hover = #6e0640 mod.widgets.ButtonSm.draw_text.color_down = #6e0640 mod.widgets.ButtonSm.draw_text.color_focus = #7e0a46 mod.widgets.ButtonSm.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonLg.draw_text.color = #2e2a96 mod.widgets.ButtonLg.draw_text.color_hover = #26228a mod.widgets.ButtonLg.draw_text.color_down = #26228a mod.widgets.ButtonLg.draw_text.color_focus = #2e2a96 mod.widgets.ButtonLg.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonPrimary.draw_text.color = #5a1fa0 mod.widgets.ButtonPrimary.draw_text.color_hover = #4a1490 mod.widgets.ButtonPrimary.draw_text.color_down = #4a1490 mod.widgets.ButtonPrimary.draw_text.color_focus = #5a1fa0 mod.widgets.ButtonPrimary.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonPrimary.draw_icon.color = #5a1fa0 mod.widgets.ButtonSecondary.draw_text.color = #0b5e33 mod.widgets.ButtonSecondary.draw_text.color_hover = #08502b mod.widgets.ButtonSecondary.draw_text.color_down = #08502b mod.widgets.ButtonSecondary.draw_text.color_focus = #0b5e33 mod.widgets.ButtonSecondary.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonSecondary.draw_icon.color = #0b5e33 mod.widgets.ButtonTertiary.draw_text.color = #8a2a0c mod.widgets.ButtonTertiary.draw_text.color_hover = #782208 mod.widgets.ButtonTertiary.draw_text.color_down = #782208 mod.widgets.ButtonTertiary.draw_text.color_focus = #8a2a0c mod.widgets.ButtonTertiary.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonTertiary.draw_icon.color = #8a2a0c mod.widgets.ButtonDanger.draw_text.color = #ffffff mod.widgets.ButtonDanger.draw_text.color_hover = #ffffff mod.widgets.ButtonDanger.draw_text.color_down = #ffeef6 mod.widgets.ButtonDanger.draw_text.color_focus = #ffffff mod.widgets.ButtonDanger.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonDanger.draw_icon.color = #ffffff mod.widgets.ButtonOutline.draw_text.color = #7e1f52 mod.widgets.ButtonOutline.draw_text.color_hover = #6e1446 mod.widgets.ButtonOutline.draw_text.color_down = #6e1446 mod.widgets.ButtonOutline.draw_text.color_focus = #7e1f52 mod.widgets.ButtonOutline.draw_text.color_disabled = #f6f1fb mod.widgets.ButtonOutline.draw_icon.color = #7e1f52 mod.widgets.ButtonFlat.draw_icon_end.color = #8e1050 // The labels on the candies are set in the semibold. mod.widgets.ButtonFlat.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.Button.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.ButtonPrimary.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.ButtonSecondary.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.ButtonTertiary.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.ButtonDanger.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.ButtonOutline.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.ButtonFlatter.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.ButtonSm.draw_text.text_style = mod.theme.font_label{font_size: 7.75} mod.widgets.ButtonLg.draw_text.text_style = mod.theme.font_label{font_size: 9.75} // ---------------------------------------------------------------- selection // The quad of a small control grows by this much so its shadow and focus // ring 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(15.0) mod.widgets.CheckBoxFlat.draw_bg.border_radius = uniform(4.0) mod.widgets.CheckBoxFlat.label_walk.margin.left = 15.0 // A small soft well pressed into the candy: a milky floor, the ground's // rose shade falling in from the top, a lilac outline that turns pink under // the hand. let kw_cushion = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float, fill: vec3, rim: vec3, hover: float, disabled: float) -> vec4 { let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let d = Material.sd_box(p, c, h, r) let live = 1.0 - disabled let f = mix(fill, vec3(0.92, 0.89, 0.94), disabled) let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0) var col = f * (0.96 + 0.05 * t) let ins = Finish.inset(p, c, h, r, vec2(0.0, 1.4), 1.3) col = mix(col, vec3(0.93, 0.72, 0.84), ins * 0.6) let rc = mix(mix(rim, vec3(0.93, 0.43, 0.68), hover * live), vec3(0.78, 0.73, 0.84), disabled) col = mix(col, rc, Finish.band(d, 0.0, max(px * 1.8, 1.0), px) * 0.95) let a = Finish.cover(d, px) let lip = Finish.ring_out(d, 0.0, px * 1.5, px) * smoothstep(c.y, hi.y, p.y) * 0.7 return Finish.over(vec4(vec3(1.0, 0.97, 0.99) * lip, lip), vec4(col * a, a)) } // The check box: a soft milky well. Checked, it blushes pink and a glossy // raspberry jelly heart sits in it; mixed, a pink jelly bar lies across it. mod.widgets.CheckBoxFlat.draw_bg.pixel = fn() { let m = 6.0 let px = kw_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(kw_snap(x0, o.x, px), kw_snap(y0, o.y, px)) let hi = vec2(kw_snap(x0 + sz, o.x, px), kw_snap(y0 + sz, o.y, px)) let c = (lo + hi) * 0.5 let r = 4.0 let live = 1.0 - self.disabled let on = self.active * (1.0 - self.mixed) let fill = mix(vec3(1.0, 0.965, 0.98), vec3(0.99, 0.84, 0.92), max(on, self.mixed)) let rim = mix(vec3(0.76, 0.66, 0.88), vec3(0.88, 0.36, 0.62), max(on, self.mixed)) var res = kw_cushion(p, lo, hi, r, px, fill * (1.0 - 0.04 * self.down * live), rim, self.hover, self.disabled) let d = Material.sd_box(p, c, (hi - lo) * 0.5, r) // Mixed: a short pink jelly bar. if self.mixed > 0.001 { let bar = kw_candy(p, vec2(c.x - 4.0, c.y - 1.6), vec2(c.x + 4.0, c.y + 1.6), 1.6, px, kw_col(8.0), 0.0, 0.0, self.disabled, 0.0, 0.3) res = Finish.over(res, bar * self.mixed) } // Checked: the heart, pressed a little when held. if on > 0.001 { let hs = sz * 0.36 * (1.0 - 0.06 * self.down * live) let heart = kw_jelly_heart(vec4(0.0, 0.0, 0.0, 0.0), p, c + vec2(0.0, 0.3), hs, vec2(0.0, 1.0), px, vec3(0.93, 0.25, 0.53), self.hover, self.down, self.disabled, 0.6) res = Finish.over(res, heart * on) } // The error: the outline turns raspberry. res = Finish.over(res, vec4(0.85, 0.10, 0.36, 1.0) * Finish.ring_out(d, 0.0, px * 2.0, px) * self.error) let fr = kw_focus(d - px * 0.5, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * self.focus * live) } // The toggle: a pill of jelly with a pearl riding in it. On, the jelly is // candy pink and the pearl stands at its right end; off, it is a pale lilac // jelly with the pearl at the left. mod.widgets.ToggleFlat.draw_bg.size = uniform(18.0) mod.widgets.ToggleFlat.draw_bg.pill_aspect = uniform(1.89) mod.widgets.ToggleFlat.label_walk.margin.left = 34.0 let kw_toggle = fn(p: vec2, lo: vec2, hi: vec2, o: vec2, px: float, kt: float, on: float, hover: float, down: float, disabled: float, focus: float, tint: vec3) -> vec4 { let th = hi.y - lo.y let tw = hi.x - lo.x let r = th * 0.5 let col = mix(vec3(0.84, 0.79, 0.92), tint, on) var res = kw_candy(p, lo, hi, r, px, col, hover * 0.5, 0.0, disabled, 0.0, 0.7) // The pearl: a bead the pill's height less three points, riding from // end to end. let rad = th * 0.5 - 1.6 let kx = lo.x + 1.6 + rad + (tw - 3.2 - 2.0 * rad) * kt res = kw_pearl(res, p, vec2(kx, (lo.y + hi.y) * 0.5 - 0.2), rad - 0.3 * down, px, vec3(1.0, 0.97, 0.985), hover, down, disabled, 0.7) let d = Material.sd_box(p, (lo + hi) * 0.5, (hi - lo) * 0.5, r) let fr = kw_focus(d, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * focus * (1.0 - disabled)) } mod.widgets.ToggleFlat.draw_bg.pixel = fn() { let m = 5.0 let px = kw_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(kw_snap(x0, o.x, px), kw_snap(y0, o.y, px)) let hi = vec2(kw_snap(x0 + tw, o.x, px), kw_snap(y0 + th, o.y, px)) let kt = mix(self.active, self.drag_pos, self.drag) return kw_toggle(p, lo, hi, o, px, kt, self.active, self.hover, max(self.pressed, self.down), self.disabled, self.focus, kw_col(8.0)) } // The radio: a round milky well. Chosen, its ring turns pink and a pink // pearl sits in it, pressed a little deeper when held. 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(15.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 = 15.0 mod.widgets.RadioButtonFlat.draw_bg.pixel = fn() { let m = 6.0 let px = kw_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 lo = vec2(kw_snap(0.0, o.x, px), kw_snap(y0, o.y, px)) let hi = vec2(kw_snap(sz, o.x, px), kw_snap(y0 + sz, o.y, px)) let c = (lo + hi) * 0.5 let on = self.active let live = 1.0 - self.disabled let rim = mix(vec3(0.76, 0.66, 0.88), vec3(0.88, 0.36, 0.62), on) var res = kw_cushion(p, lo, hi, sz * 0.5, px, mix(vec3(1.0, 0.965, 0.98), vec3(0.99, 0.88, 0.94), on), rim, self.hover, self.disabled) if on > 0.001 { let rad = sz * 0.5 - 3.4 - 0.4 * self.down * live let pearl = kw_pearl(vec4(0.0, 0.0, 0.0, 0.0), p, c, rad, px, vec3(0.98, 0.52, 0.74), self.hover, self.down, self.disabled, 0.35) res = Finish.over(res, pearl * on) } let d = length(p - c) - sz * 0.5 let fr = kw_focus(d - px * 0.5, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * self.focus * live) } // The radio tab: a strawberry-milk candy; chosen, it is pressed into the // candy, a raspberry well with its word lit. mod.widgets.RadioButtonTabFlat.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.RadioButtonTabFlat.clip_x = false mod.widgets.RadioButtonTabFlat.clip_y = false mod.widgets.RadioButtonTabFlat.padding = mod.turtle.Inset{left: 0.0 right: 8.0 top: 3.0 bottom: 3.0} mod.widgets.RadioButtonTabFlat.min_height = 24.0 mod.widgets.RadioButtonTabFlat.draw_bg.pixel = fn() { let m = 8.0 let px = kw_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(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), kw_snap(self.rect_size.y, o.y, px)) let r = min(clamp(0.42 * (hi.y - lo.y), 4.0, 12.0), (hi.y - lo.y) * 0.5) let key = kw_candy(p, lo, hi, r, px, kw_col(0.0), self.hover, self.down, self.disabled, self.focus, 1.0) let well = kw_sunk(p, lo, hi, r, px, self.hover) return mix(key, well, self.active) } mod.widgets.RadioButtonTabFlat.draw_text.color = #7e0a46 mod.widgets.RadioButtonTabFlat.draw_text.color_hover = #6e0640 mod.widgets.RadioButtonTabFlat.draw_text.color_active = #ffffff mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #f6f1fb // ---------------------------------------------------------------- sliders // A slider is a channel pressed into the candy with a rod of pink jelly // lying in it up to the value, and a pearl bead riding it with a small // raspberry heart set in its face. Each kind has its jelly: the slider and // the round one candy pink, the minimal one lavender, the band lilac. Its // value sits in a little lavender pill. let kw_heart_pearl = fn(dst: vec4, p: vec2, c: vec2, rad: float, px: float, hover: float, held: float, disabled: float, heart: vec3) -> vec4 { var res = kw_pearl(dst, p, c, rad, px, vec3(1.0, 0.975, 0.985), hover, held, disabled, 1.0) let hs = rad * 0.40 let hc = c + vec2(0.0, 0.06 * rad) let dh = kw_heart_d(p, hc, hs) let live = 1.0 - disabled let hb = kw_dull(heart, disabled) let t = clamp((p.y - (hc.y - hs)) / max(2.0 * hs, 0.5), 0.0, 1.0) var col = mix(hb * 1.08, kw_deep(hb), t * 0.55) col = col + vec3(1.0, 1.0, 1.0) * exp(-((t - 0.18) * (t - 0.18)) / 0.012) * 0.3 * live col = mix(col, kw_rim(hb), Finish.band(dh, 0.0, max(px, 0.55), px) * 0.6) let a = Finish.cover(dh, px) return Finish.over(res, vec4(min(col, vec3(1.0, 1.0, 1.0)) * a, a)) } let kw_slider = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, f0: float, f1: float, hx: vec2, px: float, rad: float, base: vec3, hover: float, held: float, disabled: float) -> vec4 { let th = hi.y - lo.y let fl = mix(vec3(1.0, 0.92, 0.96), vec3(0.93, 0.91, 0.95), disabled) var res = Finish.over(dst, kw_groove(p, lo, hi, th * 0.5, px, fl)) let ins = 1.0 if f1 - f0 > 0.01 { res = kw_rod(res, p, vec2(max(f0, lo.x + ins), lo.y + ins), vec2(min(f1, hi.x - ins), hi.y - ins), px, base, disabled) } if rad > 0.0 { res = kw_heart_pearl(res, p, hx, rad, px, hover, held, disabled, vec3(0.93, 0.25, 0.53)) } return res } // The value pill of a slider kind: a pale lavender window in a puffy // lavender rim; focused, the rim warms to pink. let kw_valbox = fn(pos: vec2, size: vec2, o: vec2, dpi: float, rim: vec3, hover: float, focus: float, disabled: float) -> vec4 { let px = kw_px(dpi) let p = pos * size if size.x < 1.0 || size.y < 1.0 { return vec4(0.0, 0.0, 0.0, 0.0) } let lo = vec2(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(size.x - px * 0.49, o.x, px), kw_snap(size.y - px * 0.49, o.y, px)) let r = min((hi.y - lo.y) * 0.5, 9.0) return kw_frame(p, lo, hi, r, px, 2.0, rim, vec3(0.955, 0.93, 1.0), 1.0, hover, focus, disabled) } mod.widgets.SliderFlat.clip_x = false mod.widgets.SliderFlat.clip_y = false mod.widgets.SliderFlat.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.SliderFlat.draw_bg.pixel = fn() { let m = 12.0 let px = kw_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 rad = 9.0 let cy = kw_snap(20.0 + (self.rect_size.y - 20.0) * 0.5, o.y, px) let lo = vec2(kw_snap(0.0, o.x, px), cy - 3.5) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), cy + 3.5) let span = self.rect_size.x - rad * 2.0 let hx = rad + self.slide_pos * span let ox = rad + self.origin_pos * span let f0 = mix(lo.x, min(ox, hx), self.arc_origin) let f1 = mix(hx, max(ox, hx), self.arc_origin) var res = kw_slider(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, f0, f1, vec2(hx, cy - 0.3), px, rad, vec3(0.97, 0.56, 0.78), self.hover, self.drag, self.disabled) let d = length(p - vec2(hx, cy - 0.3)) - rad let fr = kw_focus(d, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * self.focus * (1.0 - self.disabled)) } // The value field rides in the label row: padded into a small pill. mod.widgets.SliderMinimal.text_input.min_height = 0.0 mod.widgets.SliderMinimal.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0} mod.widgets.SliderFlat.text_input.min_height = 0.0 mod.widgets.SliderFlat.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0} mod.widgets.Slider.text_input.min_height = 0.0 mod.widgets.Slider.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0} mod.widgets.SliderRoundFlat.text_input.min_height = 0.0 mod.widgets.SliderRoundFlat.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0} mod.widgets.SliderRound.text_input.min_height = 0.0 mod.widgets.SliderRound.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0} 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} mod.widgets.SliderFlat.text_input.draw_bg.pixel = fn() { return kw_valbox(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, kw_col(1.0), self.hover, self.focus, self.disabled) } mod.widgets.Slider.text_input.draw_bg.pixel = fn() { return kw_valbox(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, kw_col(1.0), self.hover, self.focus, self.disabled) } mod.widgets.SliderMinimal.text_input.draw_bg.pixel = fn() { return kw_valbox(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, kw_col(1.0), self.hover, self.focus, self.disabled) } mod.widgets.SliderRoundFlat.text_input.draw_bg.pixel = fn() { return kw_valbox(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, kw_col(1.0), self.hover, self.focus, self.disabled) } mod.widgets.SliderRound.text_input.draw_bg.pixel = fn() { return kw_valbox(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, kw_col(1.0), self.hover, self.focus, self.disabled) } mod.widgets.RotaryFlat.text_input.draw_bg.pixel = fn() { return kw_valbox(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, kw_col(1.0), self.hover, self.focus, self.disabled) } mod.widgets.Rotary.text_input.draw_bg.pixel = fn() { return kw_valbox(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, kw_col(1.0), self.hover, self.focus, self.disabled) } mod.widgets.RotaryGradientY.text_input.draw_bg.pixel = fn() { return kw_valbox(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, kw_col(1.0), self.hover, self.focus, self.disabled) } // The minimal slider: a slimmer channel, the lavender jelly and a smaller // pearl. mod.widgets.SliderMinimal.clip_x = false mod.widgets.SliderMinimal.clip_y = false mod.widgets.SliderMinimal.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.SliderMinimal.draw_bg.pixel = fn() { let m = 12.0 let px = kw_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 rad = 7.5 let cy = kw_snap(self.offset_y + (self.rect_size.y - self.offset_y) * 0.5, o.y, px) let lo = vec2(kw_snap(0.0, o.x, px), cy - 2.75) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), cy + 2.75) let span = self.rect_size.x - rad * 2.0 let hx = rad + self.slide_pos * span let ox = rad + self.origin_pos * span let f0 = mix(lo.x, min(ox, hx), self.arc_origin) let f1 = mix(hx, max(ox, hx), self.arc_origin) var res = kw_slider(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, f0, f1, vec2(hx, cy - 0.3), px, rad, vec3(0.78, 0.64, 0.98), self.hover, self.drag, self.disabled) let d = length(p - vec2(hx, cy - 0.3)) - rad let fr = kw_focus(d, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * self.focus * (1.0 - self.disabled)) } // The round slider: a label column, then the channel with the pink jelly // and a jelly heart for its thumb; the channel ends short of the right // edge, where its value pill stands. mod.widgets.SliderRoundFlat.clip_x = false mod.widgets.SliderRoundFlat.clip_y = false mod.widgets.SliderRoundFlat.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.SliderRoundFlat.draw_bg.pixel = fn() { let m = 12.0 let px = kw_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 rad = 9.0 let cy = kw_snap(self.rect_size.y * 0.5, o.y, px) let x0 = kw_snap(self.label_size, o.x, px) let lo = vec2(x0, cy - 3.5) let hi = vec2(kw_snap(self.rect_size.x - 46.0, o.x, px), cy + 3.5) let span = hi.x - lo.x - rad * 2.0 let hx = lo.x + rad + self.slide_pos * span let base = vec3(0.97, 0.56, 0.78) var res = kw_slider(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, lo.x, hx, vec2(hx, cy), px, 0.0, base, self.hover, self.drag, self.disabled) res = kw_jelly_heart(res, p, vec2(hx, cy - 0.4), rad * 0.95, vec2(0.0, 1.0), px, vec3(0.97, 0.45, 0.70), self.hover, self.drag, self.disabled, 1.0) let d = kw_heart_d(p, vec2(hx, cy - 0.4), rad * 0.95) let fr = kw_focus(d, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * self.focus * (1.0 - self.disabled)) } // THE BAND is the slider with two round pearls, in lilac jelly between // them. The widget's grab geometry is the pearls' own. mod.widgets.RangeSliderFlat.clip_x = false mod.widgets.RangeSliderFlat.clip_y = false mod.widgets.RangeSliderFlat.track_inset = 0.0 mod.widgets.RangeSliderFlat.handle_size = 18.0 mod.widgets.RangeSliderFlat.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.RangeSliderFlat.draw_bg.pixel = fn() { let m = 12.0 let px = kw_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 rad = 8.5 let cy = kw_snap(self.label_px + (self.rect_size.y - self.label_px) * 0.5, o.y, px) let lo = vec2(kw_snap(0.0, o.x, px), cy - 3.5) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), cy + 3.5) let span = self.rect_size.x - rad * 2.0 let h0 = rad + self.start_pos * span let h1 = rad + self.end_pos * span let col = vec3(0.86, 0.62, 0.96) var res = kw_slider(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, h0, h1, vec2(h0, cy), px, 0.0, col, 0.0, 0.0, self.disabled) res = kw_pearl(res, p, vec2(h0, cy - 0.3), rad, px, vec3(1.0, 0.94, 0.97), self.hot_start, self.drag * self.hot_start, self.disabled, 1.0) res = kw_pearl(res, p, vec2(h1, cy - 0.3), rad, px, vec3(1.0, 0.94, 0.97), self.hot_end, self.drag * self.hot_end, self.disabled, 1.0) let d = min(length(p - vec2(h0, cy - 0.3)), length(p - vec2(h1, cy - 0.3))) - rad let fr = kw_focus(d, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * self.focus * (1.0 - self.disabled)) } // THE FADER: a slot pressed into the candy with a column of small pink dots // beside it for its scale, a lavender jelly rod lying in it up to the value, // and a glossy pink jelly heart for its cap, throwing its shadow down. mod.widgets.SliderFader.cap_size = 22.0 // A standing fader is 34 points wide, so a bank of four keeps to its column. mod.widgets.SliderFaderY.width = 34.0 mod.widgets.SliderFader.track_inset = 8.0 mod.widgets.SliderFader.draw_bg.track_size = uniform(5.0) mod.widgets.SliderFader.draw_bg.cap_cross = uniform(0.68) mod.widgets.SliderFader.clip_x = false mod.widgets.SliderFader.clip_y = false mod.widgets.SliderFader.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.SliderFader.draw_bg.pixel = fn() { let m = 12.0 let px = kw_px(self.draw_pass.dpi_factor) let sz = self.rect_size let o = self.rect_pos 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 inset = self.inset_px let travel = max(along - inset * 2.0 - self.cap_px, 1.0) let foot = inset + self.cap_px * 0.5 let dis = self.disabled // Track space: `a` along the track from the value's zero (up the screen // when the fader stands), `b` across it. let a = mix(p.x, sz.y - p.y, v) let b = mix(p.y, p.x, v) let mid = kw_snap(across * 0.5, mix(o.y, o.x, v), px) let tw = min(self.track_size, across) let slo = mix(vec2(inset, mid - tw * 0.5), vec2(mid - tw * 0.5, inset), v) let shi = mix(vec2(along - inset, mid + tw * 0.5), vec2(mid + tw * 0.5, along - inset), v) var res = kw_groove(p, slo, shi, tw * 0.5, px, vec3(1.0, 0.92, 0.96)) // The scale: eleven small pink dots in a column beside the slot. let pitch = travel / 10.0 let k = clamp(floor((a - foot) / max(pitch, 1.0) + 0.5), 0.0, 10.0) let dq = vec2(a - (foot + k * pitch), b - (mid + tw * 0.5 + 5.5)) let dotd = length(dq) - 1.25 res = Finish.over(res, vec4(0.93, 0.45, 0.68, 1.0) * Finish.cover(dotd, px) * mix(0.85, 0.4, dis)) // The value: a lavender jelly rod in the slot. var lo = foot + self.fill_lo * travel var hi = foot + self.fill_hi * travel if self.fill_lo < 0.0005 { lo = inset + 1.0 } if self.fill_hi > 0.9995 { hi = along - inset - 1.0 } if hi - lo > 0.1 { let rlo = mix(vec2(lo, mid - tw * 0.5 + 1.0), vec2(mid - tw * 0.5 + 1.0, sz.y - hi), v) let rhi = mix(vec2(hi, mid + tw * 0.5 - 1.0), vec2(mid + tw * 0.5 - 1.0, sz.y - lo), v) res = kw_rod(res, p, rlo, rhi, px, vec3(0.80, 0.66, 0.98), dis) } // The cap: a jelly heart sitting on the value. let at = foot + self.slide_pos * travel let cc = mix(vec2(at, mid), vec2(mid, sz.y - at), v) let ccs = vec2(Finish.snap(o.x + cc.x, px), Finish.snap(o.y + cc.y, px)) - o let hs = min(min(self.cap_px * 0.5, across * self.cap_cross * 0.5), 12.0) let held = self.drag * (1.0 - dis) res = kw_jelly_heart(res, p, ccs + vec2(0.0, 0.5), hs, vec2(0.0, 1.0), px, vec3(0.98, 0.62, 0.80), self.hover, held, dis, 1.0) let d = kw_heart_d(p, ccs + vec2(0.0, 0.5), hs) let fr = kw_focus(d, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * self.focus * (1.0 - dis)) } // ---------------------------------------------------------------- knobs // A jelly dome seen from above, laid over `dst`: its soft rose shadow, a // low sphere of jelly deepening to its rim, light pooled low inside it, a // broad soft gloss and a crisp glint at the upper left. `facets` cuts its // shoulder into that many flat faces turned by `turn` (0: a smooth dome), // so a dome that turns shows it. let kw_dome = fn(dst: vec4, p: vec2, c: vec2, rk: float, px: float, base: vec3, hover: float, held: float, disabled: float, facets: float, turn: float) -> vec4 { let live = 1.0 - disabled var b = kw_dull(base, disabled) b = b + (vec3(1.0, 1.0, 1.0) - b) * 0.10 * hover * live b = b * (1.0 - 0.05 * held * live) let q = p - c let rl = length(q) // The shadow, down and soft. let sd = length(q - vec2(0.0, 0.16 * rk)) - rk * 0.94 let sh = (1.0 - smoothstep(-0.2 * rk, 0.32 * rk, sd)) * 0.42 * (1.0 - 0.5 * disabled) * (1.0 - Finish.cover(rl - rk, px)) var res = Finish.over(dst, vec4(kw_shade() * sh, sh)) let qn = q / max(rk, 0.5) let r1 = clamp(length(qn), 0.0, 1.0) var n = normalize(vec3(qn.x * 0.85, qn.y * 0.85, sqrt(max(1.0 - r1 * r1 * 0.72, 0.05)))) var edge = 0.0 var fb = 1.0 var table_at = 0.0 if facets > 0.5 { // The cut: a flat table in the middle, a ring of faces round it. let ang = kw_ang(q) - turn let k = floor(ang / (2.0 * PI) * facets + 0.5) let fa = k / facets * 2.0 * PI + turn let fd = vec2(0.0 - sin(fa), cos(fa)) let table = 0.52 let tilt = smoothstep(table - 0.02, table + 0.02, r1) n = normalize(mix(vec3(0.0, 0.0, 1.0), vec3(fd * 0.62, 0.78), tilt)) // Each face catches the light a little differently, as cut faces do. fb = mix(1.0, 0.9 + 0.22 * Finish.hash(vec2(k + 3.0, 7.1)), tilt) table_at = 1.0 - tilt // The edges where the faces meet, and the table's rim. let wedge = abs(fract(ang / (2.0 * PI) * facets + 0.5) - 0.5) * 2.0 * PI / facets * rl edge = max(Finish.cover(wedge - px * 0.6, px) * tilt, Finish.cover(abs(rl - rk * table) - px * 0.6, px)) } let l = kw_light() let dif = max(dot(n, l), 0.0) let deep = kw_deep(b) var col = mix(deep, b * 1.06, smoothstep(0.05, 0.95, dif)) col = mix(col, kw_rim(b), smoothstep(0.7, 1.0, r1) * 0.35) // Light come back through the jelly, low inside. col = mix(col, min(b * 1.08 + vec3(0.12, 0.12, 0.12), vec3(1.0, 1.0, 1.0)), smoothstep(0.15, 0.85, qn.y) * (1.0 - smoothstep(0.78, 1.0, r1)) * 0.5) col = col * fb // The table: a soft fall of light across it from the upper left. col = col * (1.0 + 0.14 * table_at * clamp(0.0 - qn.x - qn.y, -1.0, 1.0)) col = mix(col, min(b + vec3(0.2, 0.2, 0.2), vec3(1.0, 1.0, 1.0)), edge * 0.5) let hv = normalize(l + vec3(0.0, 0.0, 1.0)) let nh = max(dot(n, hv), 0.0) let gloss = 1.0 - 0.8 * disabled col = col + vec3(1.0, 1.0, 1.0) * (pow(nh, 5.0) * 0.16 + pow(nh, 60.0) * 0.75) * gloss // The window in the gloss: a soft lens over the upper part. let gq = vec2(qn.x * 1.0 + 0.05, qn.y + 0.42) let lens = 1.0 - smoothstep(0.25, 0.62, length(vec2(gq.x * 0.85, gq.y * 1.7))) col = mix(col, vec3(1.0, 1.0, 1.0), lens * 0.28 * gloss) let dk = rl - rk col = mix(col, kw_rim(b), Finish.band(dk, 0.0, max(px * 1.6, 0.9), px) * 0.9) let a = Finish.cover(dk, px) res = Finish.over(res, vec4(min(col, vec3(1.0, 1.0, 1.0)) * a, a)) // The crisp glint: a curved white stroke on the upper left shoulder. let ga = c + vec2(0.0 - 0.62 * rk, 0.0 - 0.18 * rk) let gb = c + vec2(0.0 - 0.30 * rk, 0.0 - 0.58 * rk) res = kw_glint(res, p, ga, gb, max(0.055 * rk, 0.7), px, 0.85 * gloss * a) // A cut stone flashes: a small four-pointed sparkle on its table. let sp = kw_sparkle(p, c + vec2(0.0 - 0.24 * rk, 0.0 - 0.26 * rk), 0.16 * rk, px) * step(0.5, facets) return Finish.over(res, vec4(1.0, 1.0, 1.0, 1.0) * sp * 0.9 * gloss * a) } // A small knob: a glossy lavender jelly dome set in a ring of seed pearls, // as a trinket is; the pearls up to the value are pink jelly beads, the // rest white pearls; a pink jelly heart on the dome is its pointer, its tip // toward the middle. Angles run from straight down, clockwise, the gap at // the bottom. let kw_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) -> vec4 { let q = p - c let rl = length(q) let ang = kw_ang(q) 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 let a0 = mix(start, min(oe, ve), arc_origin) let a1 = mix(ve, max(oe, ve), arc_origin) let live = 1.0 - disabled var res = vec4(0.0, 0.0, 0.0, 0.0) // The dome, and its ring of beads touching one another over the sweep. let br = clamp(0.1 * rr, 1.3, 2.6) let rk = rr - br * 2.0 - 0.8 res = kw_dome(res, p, c, rk, px, kw_col(1.0), hover, held, disabled, 0.0, 0.0) let rp = rk + br + 0.2 let nd = max(floor(sweep * rp / (br * 2.05)), 2.0) + 1.0 let kf = clamp(floor((ang - start) / sweep * (nd - 1.0) + 0.5), 0.0, nd - 1.0) let ta = start + kf / (nd - 1.0) * sweep let bc = c + vec2(0.0 - sin(ta), cos(ta)) * rp let lit = step(a0 - 0.001, ta) * step(ta, a1 + 0.001) * step(0.002, a1 - a0) if length(p - bc) < br * 2.0 { if lit > 0.5 { res = kw_bead(res, p, bc, br, px, kw_dull(vec3(0.97, 0.42, 0.68), disabled), 0.5) } else { res = kw_pearl(res, p, bc, br * 0.92, px, vec3(1.0, 0.96, 0.98), 0.0, 0.0, disabled, 0.4) } } // The heart pointer. let dir = vec2(0.0 - sin(ve), cos(ve)) let hs = clamp(0.24 * rk, 2.4, 7.0) res = kw_jelly_heart(res, p, c + dir * (rk * 0.56), hs, vec2(0.0 - dir.x, 0.0 - dir.y), px, vec3(0.96, 0.36, 0.62), 0.0, held, disabled, 0.35) let fr = kw_focus(rl - rr, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * focus * live) } mod.widgets.RotaryKnob.clip_x = false mod.widgets.RotaryKnob.clip_y = false mod.widgets.RotaryKnob.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.RotaryKnob.draw_bg.pixel = fn() { let m = 6.0 let px = kw_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 kw_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) } // The pastel rainbow, `t` 0..1: baby blue, mint, lemon, peach, strawberry, // raspberry. let kw_rainbow = fn(t: float) -> vec3 { let k = clamp(t, 0.0, 1.0) * 5.0 var col = mix(vec3(0.62, 0.76, 1.0), vec3(0.56, 0.92, 0.74), clamp(k, 0.0, 1.0)) col = mix(col, vec3(1.0, 0.89, 0.46), clamp(k - 1.0, 0.0, 1.0)) col = mix(col, vec3(1.0, 0.70, 0.52), clamp(k - 2.0, 0.0, 1.0)) col = mix(col, vec3(0.99, 0.56, 0.76), clamp(k - 3.0, 0.0, 1.0)) return mix(col, vec3(0.92, 0.30, 0.56), clamp(k - 4.0, 0.0, 1.0)) } // THE DIAL is a candy jewel on a lace doily. Round the doily a ring of // pastel rainbow beads is lit up to the value, pale pearls past it. The // doily is a round of white lace, scalloped at its edge and pierced with a // ring of eyelets. On it stands a big pink candy, cut like a jewel -- a flat // table in the middle and a ring of faces round it -- that turns with the // value, a raspberry jelly heart on its shoulder pointing in at the middle. let kw_dial = 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) -> vec4 { let q = p - c let rl = length(q) let ang = kw_ang(q) 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 let a0 = mix(start, min(oe, ve), arc_origin) let a1 = mix(ve, max(oe, ve), arc_origin) let live = 1.0 - disabled var res = vec4(0.0, 0.0, 0.0, 0.0) // The beads: 19 over the sweep. let n = 19.0 let br = clamp(0.055 * rr, 1.6, 4.4) let tr = rr - br - 0.5 let kf = clamp(floor((ang - start) / sweep * (n - 1.0) + 0.5), 0.0, n - 1.0) let ta = start + kf / (n - 1.0) * sweep let tdir = vec2(0.0 - sin(ta), cos(ta)) let lit = step(a0 - 0.001, ta) * step(ta, a1 + 0.001) * step(0.002, a1 - a0) let bc = c + tdir * tr if length(p - bc) < br * 2.2 { if lit > 0.5 { res = kw_bead(res, p, bc, br, px, kw_dull(kw_rainbow(kf / (n - 1.0)), disabled), 0.8) } else { res = kw_pearl(res, p, bc, br * 0.82, px, vec3(0.95, 0.88, 0.94), 0.0, 0.0, disabled, 0.5) } } // The doily: a scalloped round of lace. let rb = tr - br - clamp(0.05 * rr, 1.5, 4.0) let ns = 18.0 let sa = kw_ang(q) let sk = fract(sa / (2.0 * PI) * ns) - 0.5 let scal = rb - 0.11 * rb * (1.0 - sqrt(max(1.0 - 4.0 * sk * sk, 0.0))) let dl = rl - scal let dls = length(q - vec2(0.0, 0.07 * rb)) - rb * 0.97 let lsh = (1.0 - smoothstep(-0.05 * rb, 0.12 * rb, dls)) * 0.32 * (1.0 - Finish.cover(dl, px)) res = Finish.over(res, vec4(kw_shade() * lsh, lsh)) // Its eyelets: a ring of small holes, and a ring of pinholes inside it. let ek = floor(sa / (2.0 * PI) * ns) + 0.5 let ea = ek / ns * 2.0 * PI let ep = c + vec2(0.0 - sin(ea), cos(ea)) * (rb * 0.86) let eye = length(p - ep) - max(0.035 * rb, 0.8) let pa = (floor(sa / (2.0 * PI) * ns * 2.0) + 0.5) / (ns * 2.0) * 2.0 * PI let pp = c + vec2(0.0 - sin(pa), cos(pa)) * (rb * 0.79) let pin = length(p - pp) - max(0.016 * rb, 0.45) let holes = min(eye, pin) var lace = vec3(1.0, 0.975, 0.985) lace = mix(lace, vec3(0.98, 0.86, 0.92), smoothstep(0.0, rb, q.y + rb * 0.3) * 0.35) lace = mix(lace, vec3(0.92, 0.62, 0.78), Finish.band(dl, 0.0, max(px * 1.2, 0.7), px) * 0.85) lace = mix(lace, vec3(0.93, 0.70, 0.82), Finish.ring_out(holes, 0.0, max(px, 0.5), px) * 0.6) let la = Finish.cover(dl, px) * Finish.cover(0.0 - holes, px) res = Finish.over(res, vec4(kw_dull(lace, disabled * 0.5) * la, la)) // The jewel candy, turning with the value. let rk = rb * 0.70 res = kw_dome(res, p, c, rk, px, vec3(0.99, 0.66, 0.82), hover, held, disabled, 10.0, ve) // The heart pointer. let dir = vec2(0.0 - sin(ve), cos(ve)) let hs = clamp(0.17 * rk, 2.4, 9.0) res = kw_jelly_heart(res, p, c + dir * (rk * 0.74), hs, vec2(0.0 - dir.x, 0.0 - dir.y), px, vec3(0.86, 0.16, 0.44), 0.0, held, disabled, 0.4) let fr = kw_focus(rl - rr, px) return Finish.over(res, kw_focus_col() * clamp(fr, 0.0, 1.0) * focus * live) } mod.widgets.RotaryFlat.clip_x = false mod.widgets.RotaryFlat.clip_y = false mod.widgets.RotaryFlat.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.RotaryFlat.draw_bg.pixel = fn() { let m = 12.0 let px = kw_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 + 20.0 + dia * 0.5, px) - self.rect_pos.y) return kw_dial(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) } // ---------------------------------------------------------------- fields // A text field: a milky window in a puffy strawberry-milk cushion, round // ended, all inside its own box (a field clips what it holds), the last row // left for its soft shadow. Under the hand the cushion lightens; focused it // warms to hot pink and a pink line lights along the window's inner edge. // Read only, it is a lilac-grey sweet. let kw_field = fn(p: vec2, size: vec2, o: vec2, px: float, rim: vec3, fill: vec3, hover: float, focus: float, disabled: float) -> vec4 { let lo = vec2(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(size.x - px * 0.49, o.x, px), kw_snap(size.y - px * 0.49, o.y, px) - 1.0) let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let r = min(9.0, h.y) let bw = 2.6 let sh = Finish.drop(p, c, h, r, vec2(0.0, 0.8), 0.6) * 0.32 var res = vec4(kw_shade() * sh, sh) let f = mix(fill + (vec3(1.0, 1.0, 1.0) - fill) * 0.3 * hover, vec3(0.87, 0.83, 0.92), disabled) let rc = mix(rim, vec3(0.80, 0.74, 0.90), disabled) res = Finish.over(res, kw_frame(p, lo, hi, r, px, bw, rc, f, 1.0, hover, focus * 0.6, 0.0)) let di = Material.sd_box(p, c, h - vec2(bw, bw), r - bw) let fo = focus * (1.0 - disabled) return Finish.over(res, vec4(1.0, 0.45, 0.70, 1.0) * Finish.band(di, px, px * 2.2, px) * 0.6 * fo) } // 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 = kw_px(self.draw_pass.dpi_factor) return kw_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, vec3(0.99, 0.79, 0.89), vec3(1.0, 0.965, 0.98), self.hover, self.focus, self.disabled) } // A field well (a form's field, a search box with icons) wears the same // cushion and carries its input's state into it. mod.widgets.FieldWell.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) return kw_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, vec3(0.99, 0.79, 0.89), vec3(1.0, 0.965, 0.98), self.hover, self.focus, self.disabled) } mod.widgets.FormField.input.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) return kw_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, vec3(0.99, 0.79, 0.89), vec3(1.0, 0.965, 0.98), self.hover, self.focus, self.disabled) } mod.widgets.TextInputFlat.padding = mod.turtle.Inset{left: 11.0 right: 11.0 top: 3.0 bottom: 4.0} mod.widgets.TextInputFlat.min_height = 24.0 mod.widgets.TextInputFlat.draw_cursor.color = #d81b72 mod.widgets.TextInputFlat.draw_text.color = #5a1d55 mod.widgets.TextInputFlat.draw_text.color_empty = #8e4f83 mod.widgets.TextInputFlat.draw_text.color_disabled = #6e5a80 // The number field: a lavender cushion, its stepper a pair of small // lavender candies standing inside the window's right end. mod.widgets.NumberField.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) return kw_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, vec3(0.86, 0.78, 0.99), vec3(0.985, 0.97, 1.0), self.hover, self.focus, self.disabled) } mod.widgets.NumberField.spin_width = 15.0 mod.widgets.NumberSpin.width = 15.0 mod.widgets.NumberSpin.draw_text.color = #00000000 mod.widgets.NumberSpin.draw_text.color_disabled = #00000000 mod.widgets.NumberSpin.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) let o = self.rect_pos let p = self.pos * self.rect_size let x0 = kw_snap(0.0, o.x, px) let xr = kw_snap(self.rect_size.x - px * 0.49, o.x, px) - 3.5 let yt = kw_snap(0.0, o.y, px) + 3.5 let yb = kw_snap(self.rect_size.y - px * 0.49, o.y, px) - 4.5 let mid = (yt + yb) * 0.5 let live = 1.0 - self.disabled let col = kw_col(1.0) var res = kw_candy(p, vec2(x0, yt), vec2(xr, mid - 0.5), 3.0, px, col, max(0.0, self.hot), max(0.0, self.down), self.disabled, 0.0, 0.3) res = Finish.over(res, kw_candy(p, vec2(x0, mid + 0.5), vec2(xr, yb), 3.0, px, col, max(0.0, 0.0 - self.hot), max(0.0, 0.0 - self.down), self.disabled, 0.0, 0.3)) let ink = mix(vec3(0.35, 0.12, 0.63), vec3(0.96, 0.94, 0.98), self.disabled) let cx = (x0 + xr) * 0.5 let cu = (yt + mid) * 0.5 let cd = (mid + yb) * 0.5 let du = min(kw_seg(p, vec2(cx - 2.2, cu + 1.0), vec2(cx, cu - 1.0)), kw_seg(p, vec2(cx, cu - 1.0), vec2(cx + 2.2, cu + 1.0))) let dd = min(kw_seg(p, vec2(cx - 2.2, cd - 1.0), vec2(cx, cd + 1.0)), kw_seg(p, vec2(cx, cd + 1.0), vec2(cx + 2.2, cd - 1.0))) let k = min(du, dd) - 0.6 return Finish.over(res, vec4(ink, 1.0) * Finish.cover(k, px) * (0.6 + 0.4 * live)) } // The drop-down: a strawberry-milk candy with a small raspberry jelly heart // at its right for its opener. mod.widgets.DropDownFlat.padding = mod.turtle.Inset{left: 11.0 right: 26.0 top: 3.0 bottom: 3.0} mod.widgets.DropDownFlat.clip_x = false mod.widgets.DropDownFlat.clip_y = false mod.widgets.DropDownFlat.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)) } // The opener: a little heart at the right end, `ink` its jelly. let kw_opener = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, ink: vec3, disabled: float) -> vec4 { let cc = vec2(hi.x - 13.0, (lo.y + hi.y) * 0.5 + 0.3) return kw_jelly_heart(dst, p, cc, 4.6, vec2(0.0, 1.0), px, ink, 0.0, 0.0, disabled, 0.25) } mod.widgets.DropDownFlat.draw_bg.pixel = fn() { let m = 8.0 let px = kw_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(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), kw_snap(self.rect_size.y, o.y, px)) let r = min(clamp(0.42 * (hi.y - lo.y), 4.0, 12.0), (hi.y - lo.y) * 0.5) var res = kw_candy(p, lo, hi, r, px, kw_col(0.0), self.hover, max(self.down, self.active), self.disabled, self.focus, 1.0) return kw_opener(res, p, lo, hi, px, vec3(0.93, 0.25, 0.53), self.disabled) } mod.widgets.DropDownFlat.draw_text.color = #7e0a46 mod.widgets.DropDownFlat.draw_text.color_hover = #6e0640 mod.widgets.DropDownFlat.draw_text.color_focus = #7e0a46 mod.widgets.DropDownFlat.draw_text.color_disabled = #f6f1fb mod.widgets.DropDownFlat.draw_text.text_style = mod.theme.font_label{font_size: 8.75} // The combo box: a lavender cushion round a milky window, its words plum, // its list opened from the heart at its right. mod.widgets.ComboBoxFlat.padding = mod.turtle.Inset{left: 11.0 right: 26.0 top: 3.0 bottom: 4.0} mod.widgets.ComboBoxFlat.input.min_height = 0.0 mod.widgets.ComboBoxFlat.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) let p = self.pos * self.rect_size let o = self.rect_pos let lo = vec2(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x - px * 0.49, o.x, px), kw_snap(self.rect_size.y - px * 0.49, o.y, px) - 1.0) var res = kw_field(p, self.rect_size, o, px, vec3(0.86, 0.78, 0.99), vec3(0.975, 0.955, 1.0), self.hover, self.focus, self.disabled) return kw_opener(res, p, lo, hi, px, vec3(0.93, 0.25, 0.53), self.disabled) } mod.widgets.ComboBoxFlat.input.draw_text.color = #4a1d80 mod.widgets.ComboBoxFlat.input.draw_text.color_hover = #3e1470 mod.widgets.ComboBoxFlat.input.draw_text.color_focus = #3e1470 mod.widgets.ComboBoxFlat.input.draw_text.color_empty = #8a68b0 mod.widgets.ComboBoxFlat.input.draw_text.color_disabled = #6e5a80 mod.widgets.ComboBoxFlat.input.draw_cursor.color = #d81b72 // Selected words in the combo box: a wash of pink the plum reads through. mod.widgets.ComboBoxFlat.input.draw_selection.color = uniform(#f9a8d44d) mod.widgets.ComboBoxFlat.input.draw_selection.color_hover = uniform(#f9a8d44d) mod.widgets.ComboBoxFlat.input.draw_selection.color_focus = uniform(#f78fc266) mod.widgets.ComboBoxFlat.input.draw_selection.color_down = uniform(#f78fc266) // Open lists: a tray of strawberry milk in a puffy pink cushion, rows that // blush under the hand, and the chosen row a pink candy. let kw_menu = fn(p: vec2, size: vec2, px: float) -> vec4 { let lo = vec2(0.0, 0.0) let hi = size return kw_frame(p, lo, hi, 8.0, px, 2.6, vec3(0.99, 0.74, 0.87), vec3(1.0, 0.955, 0.975), 1.0, 0.0, 0.0, 0.0) } let kw_row = fn(p: vec2, size: vec2, px: float, hover: float, active: float, disabled: float) -> vec4 { let lo = vec2(3.0, 1.0) let hi = size - vec2(3.0, 1.0) let r = min(6.0, (hi.y - lo.y) * 0.5) var res = vec4(0.98, 0.70, 0.84, 1.0) * 0.35 * hover * (1.0 - disabled) * Finish.cover(Material.sd_box(p, (lo + hi) * 0.5, (hi - lo) * 0.5, r), px) let key = kw_candy(p, lo, hi, r, px, kw_col(0.0), hover, 0.0, 0.0, 0.0, 0.0) return mix(res, key, active) } mod.widgets.PopupMenuFlat.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) return kw_menu(self.pos * self.rect_size, self.rect_size, px) } mod.widgets.PopupMenuItem.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) return kw_row(self.pos * self.rect_size, self.rect_size, px, self.hover, self.active, self.disabled) } mod.widgets.PopupMenuItem.draw_text.color_active = #6a0a3e mod.widgets.ComboBoxFlat.draw_popup_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) return kw_menu(self.pos * self.rect_size, self.rect_size, px) } mod.widgets.ComboBoxFlat.draw_item.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) return kw_row(self.pos * self.rect_size, self.rect_size, px, self.hover, self.active, 0.0) } mod.widgets.ComboBoxFlat.draw_item_text.color_active = #6a0a3e // ---------------------------------------------------------------- tabs // The segmented control: the whole strip one long baby-blue candy; the // chosen answer is pressed down into it, a raspberry jelly well with its // word lit white and a little satin bow tied on its top edge. mod.widgets.SegmentedControlFlat.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.SegmentedControlFlat.draw_bg.pixel = fn() { let m = 8.0 let px = kw_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(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), kw_snap(self.rect_size.y, o.y, px)) let r = min(clamp(0.42 * (hi.y - lo.y), 4.0, 12.0), (hi.y - lo.y) * 0.5) return kw_candy(p, lo, hi, r, px, kw_col(5.0), 0.0, 0.0, 0.0, 0.0, 1.0) } // The answers stand 2 points in from the strip's edge; the well takes the // strip's height less a point and a half at either side. mod.widgets.SegmentedControlFlat.draw_pill.vertex = fn() { let m = 9.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_pill.pixel = fn() { let m = 9.0 let v = 1.0 - step(0.0, self.radius) let hz = 1.0 - v let px = kw_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(kw_snap(0.0 * hz - 0.5 * v, o.x, px), kw_snap(-0.5 * hz, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x + 0.5 * v, o.x, px), kw_snap(self.rect_size.y + 0.5 * hz, o.y, px)) let r = min(clamp(0.42 * (hi.y - lo.y), 4.0, 11.0), (hi.y - lo.y) * 0.5) var res = kw_sunk(p, lo, hi, r, px, 0.0) // A soft pool of the word's light on the floor under it. let c = (lo + hi) * 0.5 let qq = vec2((p.x - c.x) / max((hi.x - lo.x) * 0.36, 1.0), (p.y - c.y) / max((hi.y - lo.y) * 0.45, 1.0)) let pool = exp(0.0 - dot(qq, qq)) * 0.14 * Finish.cover(Material.sd_box(p, c, (hi - lo) * 0.5, r), px) res = Finish.over(res, vec4(1.0, 0.55, 0.78, 1.0) * pool) // The bow on its top edge (a horizontal strip only). if hz > 0.5 { res = kw_bow(res, p, vec2(c.x, lo.y + 0.2), 5.4, vec3(0.99, 0.62, 0.80), px) } return res } mod.widgets.SegmentedControlFlat.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.SegmentedControlFlat.draw_text_selected.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.SegmentedControlFlat.draw_text.color = #2e2f96 mod.widgets.SegmentedControlFlat.draw_text_selected.color = #ffffff mod.widgets.SegmentedControlVertical.draw_pill.radius = -1.0 // Tabs: each tab a mint candy of its own with a small gap to the next; the // tab in use is pressed into the candy like the chosen segment, a raspberry // well with its word lit white. mod.widgets.Tabs.height = 26.0 mod.widgets.Tabs.clip_x = false mod.widgets.Tabs.clip_y = false let kw_tab = fn(pos: vec2, size: vec2, o: vec2, dpi: float, hover: float, active: float) -> vec4 { let m = 8.0 let px = kw_px(dpi) let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m) let lo = vec2(kw_snap(1.5, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(size.x - 1.5, o.x, px), kw_snap(size.y, o.y, px)) let r = min(clamp(0.42 * (hi.y - lo.y), 4.0, 11.0), (hi.y - lo.y) * 0.5) if active > 0.5 { return kw_sunk(p, lo, hi, r, px, hover) } return kw_candy(p, lo, hi, r, px, kw_col(2.0), hover, 0.0, 0.0, 0.0, 1.0) } mod.widgets.Tabs.draw_tab.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.Tabs.draw_tab_hover.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.Tabs.draw_tab_active.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.Tabs.draw_bg.pixel = fn() { return vec4(0.0, 0.0, 0.0, 0.0) } mod.widgets.Tabs.draw_tab.pixel = fn() { return kw_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 kw_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 kw_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 shut cross on the tab in use is a white stroke on its // raspberry well; the add mark is a round cream candy with a raspberry plus. mod.widgets.Tabs.draw_mark.color = #ffe6f2 mod.widgets.Tabs.draw_mark.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.Tabs.draw_mark.pixel = fn() { let m = 4.0 let px = kw_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 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 rad = min(self.rect_size.x, self.rect_size.y) * 0.5 - 2.0 let a = mix(0.7853981634, 0.0, self.plus) let arm = mix(rad * 0.62, rad * 0.5, self.plus) let ax = cos(a) * arm let ay = sin(a) * arm let w = mix(0.75, 1.15, self.plus) let dx = min(kw_seg(p, c - vec2(ax, ay), c + vec2(ax, ay)), kw_seg(p, c + vec2(ay, 0.0 - ax), c - vec2(ay, 0.0 - ax))) - w var res = vec4(0.0, 0.0, 0.0, 0.0) if self.plus > 0.5 { res = kw_candy(p, c - vec2(rad, rad), c + vec2(rad, rad), rad, px, kw_col(7.0), 0.0, 0.0, 0.0, 0.0, 0.8) return Finish.over(res, vec4(0.80, 0.12, 0.42, 1.0) * Finish.cover(dx, px)) } return vec4(self.color.rgb, 1.0) * Finish.cover(dx, px) * self.color.a } mod.widgets.Tabs.draw_text.color = #0b5e33 mod.widgets.Tabs.draw_text_active.color = #ffffff mod.widgets.Tabs.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.Tabs.draw_text_active.text_style = mod.theme.font_label{font_size: 8.75} // ---------------------------------------------------------------- feedback // The progress bar: a channel pressed into the candy with a string of jelly // beads lying in it up to the value, each bead touching the next; still and // unknown, a short string in the middle third. mod.widgets.ProgressBarFlat.height = 8.0 mod.widgets.ProgressBarFlat.draw_bg.pixel = fn() { let px = kw_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 = kw_snap((self.rect_size.y - th) * 0.5, o.y, px) let x0 = kw_snap(self.rect_size.x * self.start, o.x, px) let x1 = kw_snap(self.rect_size.x * self.end, o.x, px) let lo = vec2(x0, y0) let hi = vec2(x1, y0 + kw_snap(th, 0.0, px) - px) var res = vec4(0.0, 0.0, 0.0, 0.0) if self.track > 0.5 { res = kw_groove(p, lo, hi, (hi.y - lo.y) * 0.5, px, vec3(1.0, 0.93, 0.965)) } let ins = 0.9 let span = hi.x - lo.x - ins * 2.0 var f0 = lo.x + ins var f1 = f0 + span * clamp((self.value - self.start) / max(self.end - self.start, 0.001), 0.0, 1.0) if self.value < 0.0 { f0 = lo.x + ins + span * 0.3 f1 = lo.x + ins + span * 0.7 } let fill = self.fill_color() let rad = max((hi.y - lo.y) * 0.5 - ins, 1.0) let pitch = rad * 2.0 let count = floor((f1 - f0) / pitch + 0.001) if count > 0.5 { let k = clamp(floor((p.x - f0) / pitch), 0.0, count - 1.0) let bc = vec2(f0 + (k + 0.5) * pitch, (lo.y + hi.y) * 0.5) let bead = kw_bead(vec4(0.0, 0.0, 0.0, 0.0), p, bc, rad, px, fill.rgb, 0.0) res = Finish.over(res, bead * fill.a) } return res * self.opacity } mod.widgets.ProgressBarFlat.draw_bg.color_primary = #f78cc0 // The level meter: a case of deep plum glass holding a row of jelly gems in // the pastel rainbow, baby blue at the foot through mint, lemon and peach // to raspberry at the top; an unlit gem is dark glass of its own colour. // The clip lamp is a small LED. mod.widgets.LevelMeter.height = 8.0 mod.widgets.LevelMeterColumn.width = 11.0 let kw_case = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float) -> vec4 { let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let d = Material.sd_box(p, c, h, r) let glass = vec3(0.23, 0.09, 0.21) let ins = Finish.inset(p, c, h, r, vec2(0.0, 1.0), 1.0) var col = mix(glass * 1.15, glass * 0.6, ins) col = mix(col, vec3(0.92, 0.46, 0.70), Finish.band(d, 0.0, max(px * 1.4, 0.9), px) * 0.9) let a = Finish.cover(d, px) let lip = Finish.ring_out(d, 0.0, px * 1.5, px) * smoothstep(c.y, hi.y, p.y) * 0.6 return Finish.over(vec4(vec3(1.0, 0.97, 0.99) * lip, lip), vec4(col * a, a)) } mod.widgets.LevelMeter.draw_bg.pixel = fn() { let px = kw_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 b = mix(p.y, p.x, 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(kw_snap(wlo.x, o.x, px), kw_snap(wlo.y, o.y, px)) let hi = vec2(kw_snap(whi.x, o.x, px), kw_snap(whi.y, o.y, px) - px) var res = kw_case(p, lo, hi, min(across * 0.5, 3.5), px) let pad = 1.5 let span = max(1.0, bar - pad * 2.0) // The gems: about a gem and a half of the meter's thickness long. let pitch = span / max(floor(span / max(across * 1.5, 4.0) + 0.5), 1.0) let k = floor((a - pad) / pitch) let ca = pad + (k + 0.5) * pitch let share = clamp((ca - pad) / span, 0.0, 1.0) let lc = kw_rainbow(share) let on = step(share - 0.5 / max(span / pitch, 1.0), self.level) * step(0.001, self.level) let held = step(abs(ca - (pad + span * self.hold)), pitch * 0.5) * step(0.002, self.hold) let lit = max(on, held) let cw = max(pitch - 1.4, 1.0) let cb = across - pad * 2.0 let cq = vec2(a - ca, b - across * 0.5) let cd = Material.sd_box(cq, vec2(0.0, 0.0), vec2(cw * 0.5, cb * 0.5), min(1.6, cb * 0.5)) let inb = step(pad, a) * step(a, bar - pad) let t = clamp(cq.y / max(cb * 0.5, 0.5), -1.0, 1.0) var cc = mix(min(lc * 1.08 + vec3(0.08, 0.08, 0.08), vec3(1.0, 1.0, 1.0)), kw_deep(lc), clamp(0.3 + 0.45 * t, 0.0, 1.0)) cc = cc + vec3(1.0, 1.0, 1.0) * exp(-((t + 0.5) * (t + 0.5)) / 0.06) * 0.45 let unl = mix(vec3(0.20, 0.08, 0.19), kw_deep(lc) * 0.45, 0.45) cc = mix(unl, min(cc, vec3(1.0, 1.0, 1.0)), lit) res = Finish.over(res, vec4(cc, 1.0) * Finish.cover(cd, px) * inb * self.opacity) if self.lamp > 0.5 { let lcen = 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 - 0.5, 1.5, 3.5) res = kw_led_lamp(res, p, lcen, vec2(rl, rl), step(0.5, self.over), vec3(1.0, 0.16, 0.36), 1.0, px, vec3(0.992, 0.827, 0.898), rl * 1.2, vec3(1.0, 0.93, 0.96), 1.0) } return res * self.opacity } // The spinner: a ring of eight jelly beads, a candy pink one at the head // and each after it smaller and paler. A sheet may not read the pass clock, // so it stands still. mod.widgets.SpinnerFlat.size = 20.0 mod.widgets.SpinnerFlat.draw_bg.pixel = fn() { let px = kw_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 q = p - c let ang = kw_ang(q) let n = 8.0 let k = floor(ang / (2.0 * PI) * n + 0.5) let ba = k / n * 2.0 * PI let f = fract((k + n - 2.0) / n) let rad = size * 0.5 - 2.6 let br = mix(1.1, 2.4, f) let bc = c + vec2(0.0 - sin(ba), cos(ba)) * rad let col = mix(vec3(0.97, 0.86, 0.95), vec3(0.96, 0.42, 0.68), f) let res = kw_bead(vec4(0.0, 0.0, 0.0, 0.0), p, bc, br, px, col, 0.4) return res * self.alpha } // A count is a little baby-blue candy, its figures indigo. mod.widgets.BadgePalette.neutral = #c3cffd mod.widgets.BadgePalette.on_neutral = #2e2f96 mod.widgets.BadgeFlat.shape = mod.widgets.BadgeShape.Rounded mod.widgets.BadgeFlat.radius = 8.0 mod.widgets.BadgeFlat.draw_bg.vertex = fn() { let m = 5.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.BadgeFlat.draw_bg.pixel = fn() { let m = 5.0 let px = kw_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(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), kw_snap(self.rect_size.y, o.y, px)) let r = min(hi.x - lo.x, hi.y - lo.y) * 0.5 return kw_candy(p, lo, hi, r, px, self.color.rgb, 0.0, 0.0, 0.0, 0.0, 0.6) * max(self.color.a, 0.0) } // A chip is a small strawberry-milk candy, its label raspberry; a keyed // chip wears the focus ring. mod.widgets.ChipFlat.draw_text.text_style = mod.theme.font_label{font_size: 8.75} mod.widgets.ChipFlat.draw_text.color = #7e0a46 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 = kw_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(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), kw_snap(self.rect_size.y, o.y, px)) let r = min(clamp(0.42 * (hi.y - lo.y), 4.0, 12.0), (hi.y - lo.y) * 0.5) return kw_candy(p, lo, hi, r, px, kw_col(0.0), self.hover, self.down, 0.0, self.keyed, 1.0) } // ---------------------------------------------------------------- surfaces // The scroll bar: a channel pressed into the candy, the thumb a lavender // jelly rod in it. mod.widgets.ScrollBar.bar_size = 9.0 mod.widgets.ScrollBar.bar_side_margin = 1.0 mod.widgets.ScrollBar.draw_bg.pixel = fn() { let px = kw_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 lo = vec2(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(sz.x, o.x, px), kw_snap(sz.y, o.y, px)) var res = kw_groove(p, lo, hi, min(hi.x - lo.x, hi.y - lo.y) * 0.5, px, vec3(1.0, 0.93, 0.965)) 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 tlo = mix(vec2(t0 + 1.0, 1.5), vec2(1.5, t0 + 1.0), v) let thi = mix(vec2(t1 - 1.0, sz.y - 1.5), vec2(sz.x - 1.5, t1 - 1.0), v) let slo = vec2(kw_snap(tlo.x, o.x, px), kw_snap(tlo.y, o.y, px)) let shi = vec2(kw_snap(thi.x, o.x, px), kw_snap(thi.y, o.y, px)) let base = vec3(0.80, 0.68, 0.98) let col = base + (vec3(1.0, 1.0, 1.0) - base) * 0.12 * max(self.hover, self.drag) // The rod lies along the bar either way; lit from the upper left, so a // standing rod is shaded across x. let c = (slo + shi) * 0.5 let h = max((shi - slo) * 0.5, vec2(0.5, 0.5)) let d = Material.sd_box(p, c, h, min(h.x, h.y)) let u = clamp(mix((p.y - c.y) / h.y, (p.x - c.x) / h.x, v), -1.0, 1.0) let z = sqrt(max(1.0 - u * u, 0.0)) var rc = mix(kw_deep(col), col * 1.04, clamp(0.3 + 0.6 * z - 0.3 * u, 0.0, 1.0)) rc = rc + vec3(1.0, 1.0, 1.0) * exp(-((u + 0.45) * (u + 0.45)) / 0.05) * 0.45 rc = mix(rc, kw_rim(col), Finish.band(d, 0.0, max(px, 0.6), px) * 0.7) res = Finish.over(res, vec4(min(rc, vec3(1.0, 1.0, 1.0)), 1.0) * Finish.cover(d, px)) return res * self.opacity } // A PANEL is a pastel cushion: its face the panel's colour, softly lit // from the upper left, sewn round with a line of small stitches just inside // a puffy rim of a paler tint that is round across like a pillow, lit along // its crown and outlined in the panel's deep shade. It stands on the candy // with a soft rose shadow held within half the gap to its neighbours. let kw_slab = fn(p: vec2, size: vec2, o: vec2, px: float, r: float, base: vec3, hover: float) -> vec4 { let lo = vec2(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(size.x, o.x, px), kw_snap(size.y, o.y, px)) let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let d = Material.sd_box(p, c, h, r) let s1 = Finish.drop(p, c, h - vec2(2.0, 0.0), r, vec2(0.0, 3.4), 3.0) * 0.30 let s2 = Finish.drop(p, c, h, r, vec2(0.0, px + 0.7), px + 0.9) * 0.30 let sh = (1.0 - (1.0 - s1) * (1.0 - s2)) * (1.0 - Finish.cover(d, px)) var res = vec4(kw_shade() * sh, sh) let b = base + (vec3(1.0, 1.0, 1.0) - base) * 0.06 * hover let deep = kw_deep(b) let rim = kw_rim(b) let puff = mix(b, vec3(1.0, 0.985, 0.995), 0.62) // The puffy rim, 5.5 points. let bw = 5.5 let ih = max(h - vec2(bw, bw), vec2(0.5, 0.5)) let ir = max(r - bw, 1.0) let di = Material.sd_box(p, c, ih, ir) let n = kw_box_n(p, c, h, r) let across = clamp(di / bw, 0.0, 1.0) * 2.0 - 1.0 let facing = dot(n * across, vec2(-0.38, -0.92)) var fr = mix(mix(puff, deep, 0.35), puff * 1.02, clamp(0.6 + 0.5 * facing, 0.0, 1.0)) let crown = exp(-((across + 0.1) * (across + 0.1)) / 0.14) fr = mix(fr, vec3(1.0, 1.0, 1.0), crown * 0.55) // The face. let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0) var face = b * (1.03 - 0.05 * t) let uv = clamp((p - lo) / max(hi - lo, vec2(1.0, 1.0)), vec2(0.0, 0.0), vec2(1.0, 1.0)) let refl = exp(-((uv.x - 0.22) * (uv.x - 0.22) / 0.08 + (uv.y - 0.1) * (uv.y - 0.1) / 0.03)) face = mix(face, vec3(1.0, 1.0, 1.0), refl * 0.22) let ins = Finish.inset(p, c, ih, ir, vec2(0.0, 1.2), 1.6) face = mix(face, deep, ins * 0.35) face = face + vec3(1.0, 1.0, 1.0) * kw_grain(p + o, px) * 0.006 // The stitches: short dashes 2 points inside the seam, round the face. let ds = Material.sd_box(p, c, ih - vec2(2.2, 2.2), max(ir - 2.2, 1.0)) let nn = kw_box_n(p, c, ih - vec2(2.2, 2.2), max(ir - 2.2, 1.0)) let run = mix(p.x, p.y, abs(nn.x)) let dash = step(0.45, fract(run / 4.0)) face = mix(face, rim * 1.08, Finish.cover(abs(ds) - px * 0.75, px) * dash * 0.55) var col = mix(face, fr, Finish.cover(0.0 - di, px)) col = mix(col, rim, Finish.band(di, 0.0, max(px, 0.6), px) * 0.5) col = mix(col, rim, Finish.band(d, 0.0, max(px * 1.6, 1.0), px) * 0.9) let a = Finish.cover(d, px) return Finish.over(res, vec4(col * a, a)) } // The well of a sunken panel: a deeper floor of the same pastel with the // cushion's shade falling in from the top and the sides and a catch light // along its lower lip. let kw_pocket = fn(dst: vec4, p: vec2, size: vec2, o: vec2, px: float, r: float, base: vec3, frame: float) -> vec4 { let lo = vec2(kw_snap(frame, o.x, px), kw_snap(frame, o.y, px)) let hi = vec2(kw_snap(size.x - frame, o.x, px), kw_snap(size.y - frame, o.y, px)) let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let rr = max(r - frame, 2.0) let d = Material.sd_box(p, c, h, rr) let deep = kw_deep(base) let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0) var col = mix(base * 0.93, base * 1.0, t) let ins = Finish.inset(p, c, h, rr, vec2(0.0, 2.6), 2.4) col = mix(col, deep * 0.85, ins * 0.7) col = mix(col, mix(base, vec3(1.0, 1.0, 1.0), 0.5), Finish.band(d, 0.0, px * 1.5, px) * smoothstep(c.y + h.y * 0.4, hi.y, p.y) * 0.7) col = mix(col, kw_rim(base), Finish.band(d, 0.0, max(px, 0.6), px) * smoothstep(c.y, lo.y, p.y) * 0.6) col = col + vec3(1.0, 1.0, 1.0) * kw_grain(p + o, px) * 0.006 let a = Finish.cover(d, px) return Finish.over(dst, vec4(col * a, a)) } mod.widgets.PanelView.clip_x = false mod.widgets.PanelView.clip_y = false mod.widgets.PanelView.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 12.0 bottom: 12.0} mod.widgets.PanelView.draw_bg.vertex = fn() { let m = 14.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)) } // A raised panel is a lavender cushion with a lace doily peeping out under // its foot and a pink jelly heart at its top right corner; a sunken one is // a mint cushion whose middle is a well, a strawberry at its corner. mod.widgets.PanelView.draw_bg.pixel = fn() { let m = 14.0 let px = kw_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 lo = vec2(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(sz.x, o.x, px), kw_snap(sz.y, o.y, px)) if self.sunken > 0.5 { let g = vec3(0.78, 0.94, 0.85) let slab = kw_slab(p, sz, o, px, 14.0, g, 0.0) var res = kw_pocket(slab, p, sz, o, px, 14.0, g, 6.0) if sz.x > 60.0 && sz.y > 40.0 { res = kw_strawberry(res, p, vec2(hi.x - 7.5, lo.y + 7.5), 6.2, px) } return res } var res = kw_slab(p, sz, o, px, 14.0, vec3(0.87, 0.81, 0.99), 0.0) if sz.x > 60.0 { let face = Finish.cover(Material.sd_box(p, (lo + hi) * 0.5, (hi - lo) * 0.5, 14.0), px) res = Finish.over(res, kw_lace(p, lo.x + 13.0, hi.x - 13.0, hi.y + 0.3, 1.0, 3.6, px) * (1.0 - face)) } if sz.x > 60.0 && sz.y > 40.0 { res = kw_jelly_heart(res, p, vec2(hi.x - 7.0, lo.y + 6.5), 5.2, vec2(0.0, 1.0), px, vec3(0.97, 0.45, 0.70), 0.0, 0.0, 0.0, 0.6) } return res } // The rounded view keeps the colour its caller gives it: opaque, it is a // cushion of jelly in that colour, its outline its own deep shade and a // soft gloss over its upper left; translucent, it stays a plain tint. mod.widgets.RoundedView.draw_bg.pixel = fn() { let px = kw_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) let c = (lo + hi) * 0.5 let h = max((hi - lo) * 0.5, vec2(0.5, 0.5)) let r = min(2.0 * max(1.0, self.border_radius), min(h.x, h.y)) let d = Material.sd_box(p, c, h, r) 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)) } let solid = step(0.98, fill.a) let b = fill.rgb let n = kw_box_n(p, c, h, r) let bw = clamp(0.3 * min(h.x, h.y), 1.0, 10.0) let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0) let uv = clamp((p - lo) / max(hi - lo, vec2(1.0, 1.0)), vec2(0.0, 0.0), vec2(1.0, 1.0)) let jelly = kw_jelly_face(d, n, bw, t, uv, px, b, 0.35, 0.8) var col = mix(b, jelly.rgb / max(jelly.a, 0.001), solid) var ink = self.border_color if self.border_size > 0.01 { col = mix(col, ink.rgb, Finish.band(d, 0.0, max(self.border_size, px), px) * ink.a) } let a = Finish.cover(d, px) return vec4(col * a * fill.a, a * fill.a) + vec4(ink.rgb * ink.a, ink.a) * Finish.band(d, 0.0, max(self.border_size, px), px) * a * (1.0 - fill.a) * step(0.01, self.border_size) } // The card: a strawberry-milk cushion with a satin bow tied at its top // right corner, its face pearlescent: a faint play of lilac, mint and peach // running across it on the diagonal; a pressable card lifts under the hand. mod.widgets.Card.clip_x = false mod.widgets.Card.clip_y = false mod.widgets.Card.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 12.0 bottom: 12.0} mod.widgets.Card.draw_bg.vertex = fn() { let m = 14.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 = 14.0 let px = kw_px(self.draw_pass.dpi_factor) let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m) let o = self.rect_pos let sz = self.rect_size var res = kw_slab(p, sz, o, px, 12.0, vec3(0.99, 0.80, 0.90), self.hover) let inner = Finish.cover(Material.sd_box(p, sz * 0.5, sz * 0.5 - vec2(5.5, 5.5), 7.0), px) let u = (p.x + p.y * 0.7) / max(sz.x + sz.y * 0.7, 1.0) let irid = mix(mix(vec3(0.88, 0.80, 1.0), vec3(0.80, 0.97, 0.90), smoothstep(0.15, 0.5, u)), vec3(1.0, 0.86, 0.80), smoothstep(0.5, 0.9, u)) res = vec4(mix(res.rgb, irid * res.a, inner * 0.22), res.a) if sz.x > 60.0 && sz.y > 40.0 { let hi = vec2(kw_snap(sz.x, o.x, px), kw_snap(sz.y, o.y, px)) res = kw_bow(res, p, vec2(hi.x - 13.0, kw_snap(0.0, o.y, px) + 3.0), 10.0, vec3(0.99, 0.60, 0.79), px) } return res } // List rows: 22 points, the words 20 points in. Each row carries a small // heart at its left drawn in a pink line; the chosen row is a pink candy // with its heart a filled raspberry jelly. mod.widgets.ListItem.height = 22.0 mod.widgets.ListItemOne.height = 22.0 mod.widgets.ListItem.padding = mod.turtle.Inset{left: 21.0 right: 8.0 top: 0.0 bottom: 1.0} mod.widgets.ListItemOne.padding = mod.turtle.Inset{left: 21.0 right: 8.0 top: 0.0 bottom: 1.0} mod.widgets.ListItem.draw_bg.pixel = fn() { let px = kw_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 lo = vec2(kw_snap(1.0, o.x, px), kw_snap(1.5, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x - 2.0, o.x, px), kw_snap(self.rect_size.y - 1.5, o.y, px)) let r = min(8.0, (hi.y - lo.y) * 0.5) let d = Material.sd_box(p, (lo + hi) * 0.5, (hi - lo) * 0.5, r) var res = vec4(0.98, 0.70, 0.84, 1.0) * (0.3 * self.hover + 0.2 * self.down) * live * Finish.cover(d, px) let key = kw_candy(p, lo, hi, r, px, kw_col(0.0), self.hover, self.down, self.disabled, 0.0, 0.0) res = mix(res, key, self.selected) let hc = vec2(kw_snap(11.0, o.x, px), (lo.y + hi.y) * 0.5 + 0.2) let dh = kw_heart_d(p, hc, 3.6) let line = Finish.cover(abs(dh) - max(px * 0.8, 0.5), px) * (1.0 - self.selected) res = Finish.over(res, vec4(0.90, 0.36, 0.62, 1.0) * line * mix(1.0, 0.5, self.disabled)) if self.selected > 0.001 { res = kw_jelly_heart(res, p, hc, 3.9, vec2(0.0, 1.0), px, vec3(0.86, 0.16, 0.44), 0.0, 0.0, self.disabled, 0.3) } return res } mod.widgets.ListItem.draw_text.color = #5a1d55 mod.widgets.ListItem.draw_text_2.color = #7c3f72 // The tree's rows stand on the candy, clear; the open row is a pink candy. mod.widgets.FileTreeNode.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) let p = self.pos * self.rect_size let lo = vec2(1.0, 1.0) let hi = self.rect_size - vec2(2.0, 1.0) let r = min(8.0, (hi.y - lo.y) * 0.5) let key = kw_candy(p, lo, hi, r, px, kw_col(0.0), 0.0, 0.0, 0.0, 0.0, 0.0) return key * self.active } mod.widgets.FileTreeNode.draw_text.color_active = #6a0a3e mod.widgets.FileTreeNode.draw_icon.color_active = #8e1050 // ---------------------------------------------------------------- ink // Words on the ground are pressed into the sugar: a soft white catch light // one device pixel under each glyph. Words on a sweet are sticker lettering: // a soft white halo all round each glyph (under the white words of the // raspberry key, a deep raspberry shade instead); a key's label sinks with // it when it is pressed. The word of a chosen // segment or tab is lit: white, with a pink glow round it. mod.widgets.ButtonFlat.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.Button.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonPrimary.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonSecondary.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonTertiary.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonDanger.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let s = clamp(self.alpha_at(sample - dy, px_x, px_y), 0.0, 1.0) * 0.55 * c.a return main + vec4(0.38, 0.02, 0.16, 1.0) * s * (1.0 - main.a) } 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)) * 0.55 * c.a return main + vec4(0.38, 0.02, 0.16, 1.0) * s * (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)) * 0.55 * c.a return main + vec4(0.38, 0.02, 0.16, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonOutline.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonFlatter.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonXs.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonSm.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonLg.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.ButtonXl.draw_text.pixel = fn() { let nudge = dFdy(self.t.xy) * 2.0 * step(0.5, self.down) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - dFdy(self.pos) * 2.0 * step(0.5, self.down) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let ex = dFdx(self.pos) * 1.25 let ey = dFdy(self.pos) * 1.25 let g = self.alpha_at(sample + ex, px_x, px_y) + self.alpha_at(sample - ex, px_x, px_y) + self.alpha_at(sample + ey, px_x, px_y) + self.alpha_at(sample - ey, px_x, px_y) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.sdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.sdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (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 ex = dFdx(self.t.xy) * 1.25 let ey = dFdy(self.t.xy) * 1.25 let k = vec4(0.0, 0.0, 0.0, 1.0) let tc = self.t.xy - nudge let g = self.msdf(scale, clamp(tc + ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ex, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc + ey, self.t_min + half_texel, self.t_max - half_texel), k) + self.msdf(scale, clamp(tc - ey, self.t_min + half_texel, self.t_max - half_texel), k) let s = clamp(g * 0.3, 0.0, 1.0) * 0.7 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.Label.draw_text.pixel = fn() { let nudge = vec2(0.0, 0.0) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - vec2(0.0, 0.0) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let s = clamp(self.alpha_at(sample - dy, px_x, px_y), 0.0, 1.0) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } 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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.CheckBoxFlat.draw_text.pixel = fn() { let nudge = vec2(0.0, 0.0) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - vec2(0.0, 0.0) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let s = clamp(self.alpha_at(sample - dy, px_x, px_y), 0.0, 1.0) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } 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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.RadioButtonFlat.draw_text.pixel = fn() { let nudge = vec2(0.0, 0.0) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - vec2(0.0, 0.0) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let s = clamp(self.alpha_at(sample - dy, px_x, px_y), 0.0, 1.0) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } 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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.DropDownFlat.draw_text.pixel = fn() { let nudge = vec2(0.0, 0.0) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - vec2(0.0, 0.0) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let s = clamp(self.alpha_at(sample - dy, px_x, px_y), 0.0, 1.0) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } 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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.SegmentedControlFlat.draw_text.pixel = fn() { let nudge = vec2(0.0, 0.0) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - vec2(0.0, 0.0) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let s = clamp(self.alpha_at(sample - dy, px_x, px_y), 0.0, 1.0) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } 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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.SegmentedControlFlat.draw_text_selected.pixel = fn() { let main = self.sample_text_pixel() if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let ex = dFdx(self.pos) * 1.5 let ey = dFdy(self.pos) * 1.5 var g = self.alpha_at(self.pos + ex, px_x, px_y) g = g + self.alpha_at(self.pos - ex, px_x, px_y) g = g + self.alpha_at(self.pos + ey, px_x, px_y) g = g + self.alpha_at(self.pos - ey, px_x, px_y) let lit = vec4(1.0, 0.99, 1.0, self.color.a) let cov = clamp(self.alpha_at(self.pos, px_x, px_y), 0.0, 1.0) let lm = vec4(lit.rgb * lit.a * cov, lit.a * cov) let a = clamp(g * 0.25, 0.0, 1.0) * 0.6 * lit.a return lm + vec4(1.0 * a, 0.42 * a, 0.70 * a, a) * (1.0 - lm.a) } if self.texture_index < 0.5 { let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) 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 ex = dFdx(self.t.xy) * 1.5 let ey = dFdy(self.t.xy) * 1.5 let k = vec4(0.0, 0.0, 0.0, 1.0) var g = self.sdf(scale, clamp(self.t.xy + ex, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.sdf(scale, clamp(self.t.xy - ex, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.sdf(scale, clamp(self.t.xy + ey, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.sdf(scale, clamp(self.t.xy - ey, self.t_min + half_texel, self.t_max - half_texel), k) let c = self.get_color() let a = clamp(g * 0.25, 0.0, 1.0) * 0.5 * c.a return main + vec4(1.0 * a, 0.42 * a, 0.70 * a, a) * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.5 let ey = dFdy(self.t.xy) * 1.5 let k = vec4(0.0, 0.0, 0.0, 1.0) var g = self.msdf(scale, clamp(self.t.xy + ex, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.msdf(scale, clamp(self.t.xy - ex, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.msdf(scale, clamp(self.t.xy + ey, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.msdf(scale, clamp(self.t.xy - ey, self.t_min + half_texel, self.t_max - half_texel), k) let c = self.get_color() let a = clamp(g * 0.25, 0.0, 1.0) * 0.5 * c.a return main + vec4(1.0 * a, 0.42 * a, 0.70 * a, a) * (1.0 - main.a) } return main } mod.widgets.Tabs.draw_text.pixel = fn() { let nudge = vec2(0.0, 0.0) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - vec2(0.0, 0.0) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let s = clamp(self.alpha_at(sample - dy, px_x, px_y), 0.0, 1.0) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } 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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } return self.sample_text_pixel() } mod.widgets.Tabs.draw_text_active.pixel = fn() { let main = self.sample_text_pixel() if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let ex = dFdx(self.pos) * 1.5 let ey = dFdy(self.pos) * 1.5 var g = self.alpha_at(self.pos + ex, px_x, px_y) g = g + self.alpha_at(self.pos - ex, px_x, px_y) g = g + self.alpha_at(self.pos + ey, px_x, px_y) g = g + self.alpha_at(self.pos - ey, px_x, px_y) let lit = vec4(1.0, 0.99, 1.0, self.color.a) let cov = clamp(self.alpha_at(self.pos, px_x, px_y), 0.0, 1.0) let lm = vec4(lit.rgb * lit.a * cov, lit.a * cov) let a = clamp(g * 0.25, 0.0, 1.0) * 0.6 * lit.a return lm + vec4(1.0 * a, 0.42 * a, 0.70 * a, a) * (1.0 - lm.a) } if self.texture_index < 0.5 { let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) 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 ex = dFdx(self.t.xy) * 1.5 let ey = dFdy(self.t.xy) * 1.5 let k = vec4(0.0, 0.0, 0.0, 1.0) var g = self.sdf(scale, clamp(self.t.xy + ex, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.sdf(scale, clamp(self.t.xy - ex, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.sdf(scale, clamp(self.t.xy + ey, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.sdf(scale, clamp(self.t.xy - ey, self.t_min + half_texel, self.t_max - half_texel), k) let c = self.get_color() let a = clamp(g * 0.25, 0.0, 1.0) * 0.5 * c.a return main + vec4(1.0 * a, 0.42 * a, 0.70 * a, a) * (1.0 - main.a) } 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 ex = dFdx(self.t.xy) * 1.5 let ey = dFdy(self.t.xy) * 1.5 let k = vec4(0.0, 0.0, 0.0, 1.0) var g = self.msdf(scale, clamp(self.t.xy + ex, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.msdf(scale, clamp(self.t.xy - ex, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.msdf(scale, clamp(self.t.xy + ey, self.t_min + half_texel, self.t_max - half_texel), k) g = g + self.msdf(scale, clamp(self.t.xy - ey, self.t_min + half_texel, self.t_max - half_texel), k) let c = self.get_color() let a = clamp(g * 0.25, 0.0, 1.0) * 0.5 * c.a return main + vec4(1.0 * a, 0.42 * a, 0.70 * a, a) * (1.0 - main.a) } return main } mod.widgets.SliderMinimal.draw_text.pixel = fn() { let nudge = vec2(0.0, 0.0) let c = self.get_color() let dxt = length(dFdx(self.t)) let dyt = length(dFdy(self.t)) if self.texture_index > 2.5 { if self.slug_curve_count() < 0.5 { return vec4(0.0, 0.0, 0.0, 0.0) } let dy = dFdy(self.pos) let sample = self.pos - vec2(0.0, 0.0) let px_x = max(abs(dFdx(self.pos.x)) + abs(dFdy(self.pos.x)), 0.00001) let px_y = max(abs(dFdx(self.pos.y)) + abs(dFdy(self.pos.y)), 0.00001) let grid = if self.aa_4x4 > 0.5 {4.0} else if self.aa_2x2 > 0.5 {2.0} else {1.0} let inv_grid = if self.aa_4x4 > 0.5 {0.25} else if self.aa_2x2 > 0.5 {0.5} else {1.0} var alpha_sum = 0.0 var sample_index = 0.0 loop { if sample_index >= grid * grid { break } let row = floor(sample_index * inv_grid) let col = sample_index - row * grid let offset = (vec2(col, row) + 0.5 - grid * 0.5) * inv_grid * vec2(px_x, px_y) alpha_sum = alpha_sum + self.alpha_at(sample + offset, px_x, px_y) sample_index = sample_index + 1.0 } let alpha_base = clamp(alpha_sum * inv_grid * inv_grid, 0.0, 1.0) let darken = clamp(max(px_x, px_y) * self.stem_darken, 0.0, self.stem_darken_max) let edge_weight = clamp(1.0 - abs(alpha_base * 2.0 - 1.0), 0.0, 1.0) let alpha = clamp(alpha_base + darken * edge_weight, 0.0, 1.0) let c = self.get_color() let main = vec4(c.rgb * c.a * alpha, c.a * alpha) let s = clamp(self.alpha_at(sample - dy, px_x, px_y), 0.0, 1.0) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } 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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (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)) * 0.55 * c.a return main + vec4(1.0, 0.98, 0.99, 1.0) * 0.6 * s * (1.0 - main.a) } return self.sample_text_pixel() } // ---------------------------------------------------------------- field row // A ROW OF FIELDS IS ONE HEIGHT. Left to themselves the library's fields each // come out at a size of their own, so the box metrics are stated ONCE here // and handed to each field below; a field added to the library later joins // the row by being named here. let field_height = 24.0 let field_pad_v = 3.0 let field_radius = 4.0 let field_border = 0.67 // The room a Fit field takes: a minimum height, and the padding with it. let field_room = fn(w) { w.min_height = field_height w.padding.top = field_pad_v w.padding.bottom = field_pad_v + 1.0 } // The box a field draws, given the draw object. 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: 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) mod.widgets.WellInput.min_height = 0.0 // The drop-down stands in the field row: the text box's padding, so the // two are one height (sheet_checks::field_row). mod.widgets.DropDown.padding.top = 3.0 mod.widgets.DropDown.padding.bottom = 4.0 // ---------------------------------------------------------------- rules // A rule is a seam pressed into the candy: a rose line over a white one. mod.widgets.Hr.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) let p = self.pos * self.rect_size let y0 = kw_snap(self.rect_size.y * 0.5 - px, self.rect_pos.y, px) let dark = step(y0, p.y) * step(p.y, y0 + px) let light = step(y0 + px, p.y) * step(p.y, y0 + px * 2.0) return vec4(0.88, 0.48, 0.68, 1.0) * dark * 0.8 + vec4(1.0, 0.98, 0.99, 1.0) * light * 0.8 } mod.widgets.Vr.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) let p = self.pos * self.rect_size let x0 = kw_snap(self.rect_size.x * 0.5 - px, self.rect_pos.x, px) let dark = step(x0, p.x) * step(p.x, x0 + px) let light = step(x0 + px, p.x) * step(p.x, x0 + px * 2.0) return vec4(0.88, 0.48, 0.68, 1.0) * dark * 0.8 + vec4(1.0, 0.98, 0.99, 1.0) * light * 0.8 } // ---------------------------------------------------------------- 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 // A splitter's strip is the ground itself: nothing is drawn there at rest. mod.widgets.Splitter.draw_bg.color_bg = uniform(#00000000) mod.widgets.View.clip_y = false mod.widgets.Window.pass.clear_color = #fab5d3 // THE FLOOR is a soft pink tartan woven on the diagonal, laid in window // space so it never moves with a scroll. One sett, `kw_sett`, gives the // colour of the thread at distance `t` across it: every 108 points a broad // band of rose pink a third of the repeat wide, a pale pink band half as // wide between the broad bands, and beside each broad band a group of four // pinstripes, pale and rose in turn, each about five device pixels wide, all // on a candy-pink ground. The tones lie close together: a soft check. let kw_sett = fn(t: float, px: float) -> vec3 { let q = t - 108.0 * floor(t / 108.0 + 0.5) let a = abs(q) let w = vec3(0.988, 0.773, 0.871) let band = vec3(0.980, 0.671, 0.804) var c = vec3(0.980, 0.710, 0.827) c = mix(c, band, Finish.cover(a - 18.0, px)) c = mix(c, w, Finish.cover(abs(a - 54.0) - 9.0, px)) let e = a - 18.0 c = mix(c, w, Finish.cover(abs(e - 3.6) - 1.6, px)) c = mix(c, band, Finish.cover(abs(e - 8.8) - 1.6, px)) c = mix(c, w, Finish.cover(abs(e - 14.0) - 1.6, px)) c = mix(c, band, Finish.cover(abs(e - 19.2) - 1.6, px)) return c } // The pale stitch down the middle of a broad band, as thread sewn into the // cloth: dashes about 8 points long with gaps of about 5, nearly two points // wide, `across` the distance across the band's repeat and `along` the // distance along it. Each dash is soft: its edge feathers out over about a // device pixel either side, its ends round off and sink a little into the // cloth, and a faint sheen runs down its middle where the thread is round. let kw_stitch = fn(across: float, along: float, px: float) -> float { let q = across - 108.0 * floor(across / 108.0 + 0.5) let k = along - 13.4 * floor(along / 13.4 + 0.5) let d = length(vec2(max(abs(k) - 3.2, 0.0), q)) - 0.9 let soft = clamp(0.5 - d / (2.0 * px), 0.0, 1.0) let x = clamp(abs(q) / 0.9, 0.0, 1.0) let sheen = 0.72 + 0.28 * (1.0 - x * x) let ends = 1.0 - 0.35 * smoothstep(1.6, 4.1, abs(k)) return soft * sheen * ends } // The floor at window point `w`: the sett runs both ways at 45 degrees and // the two are woven. In a two-over-two twill the threads of one way show // over those of the other in runs of two device pixels, so where the two // differ a fine hatch shows: the crossing of two broad bands is the deepest // rose, one broad band over the ground a mid pink, two pale bands the // palest pink square. The pale stitches lie over the weave. let kw_floor = fn(w: vec2, size: vec2, px: float) -> vec3 { let u = (w.x + w.y) * 0.70710678 let v = (w.x - w.y) * 0.70710678 let cu = kw_sett(u, px) let cv = kw_sett(v, px) let ix = floor(w.x / px) let h = step(2.0, ix - 4.0 * floor(ix / 4.0)) var col = mix(cu, cv, 0.5 + 0.12 * (h * 2.0 - 1.0)) let st = max(kw_stitch(u, v, px), kw_stitch(v, u, px)) col = mix(col, vec3(0.988, 0.773, 0.871), st * 0.58) return col + vec3(1.0, 1.0, 1.0) * kw_grain(w, px) * 0.004 } // The window's own ground: the floor, and round its edge a frame: a narrow // band of deep candy pink outside, then a puffy cushion of pale strawberry // milk, round across like a pillow, lit along its crown with glints of // sugar, outlined in rose and sewn to the floor at its inner seam, where it // throws a soft rose shade in. A lace trim runs along the foot of the frame. // The ornaments keep to the corners: a satin bow at the top left, a jelly // heart and a lemon star at the top right, a strawberry and a star at the // bottom left, and at the bottom right two hearts with a star and a // sparkle. Past the rounded corners, the rose behind the window. mod.widgets.Window.show_bg = true mod.widgets.Window.draw_bg.pixel = fn() { let px = kw_px(self.draw_pass.dpi_factor) let sz = self.rect_size let p = self.pos * sz let w = p + self.rect_pos var col = kw_floor(w, sz, px) let c = sz * 0.5 let h = sz * 0.5 - vec2(0.5, 0.5) let r = 20.0 let d = Material.sd_box(p, c, h, r) let n = kw_box_n(p, c, h, r) let band = 2.4 let fw = 8.6 // The cushion's shade on the floor just inside its seam. let seam = d + fw col = mix(col, vec3(0.88, 0.50, 0.70), exp(0.0 - max(0.0 - seam, 0.0) / 2.4) * 0.34 * step(seam, 0.0)) // The cushion, between the band and the seam. let x = clamp((0.0 - d - band) / (fw - band), 0.0, 1.0) let across = 1.0 - x * 2.0 let facing = dot(n * across, vec2(-0.38, -0.92)) let milk = vec3(0.995, 0.88, 0.93) var cu = mix(vec3(0.95, 0.66, 0.80), milk, clamp(0.62 + 0.5 * facing, 0.0, 1.0)) let crown = exp(-((across - 0.12) * (across - 0.12)) / 0.13) cu = mix(cu, vec3(1.0, 0.99, 0.995), crown * 0.6) // Glints of sugar on the cushion's crown. let gcell = floor(w / 11.0) let gh = Finish.hash(gcell + vec2(1.7, 8.3)) let gp = (gcell + vec2(Finish.hash(gcell + vec2(4.1, 0.7)), Finish.hash(gcell + vec2(9.3, 5.5)))) * 11.0 let glint = kw_sparkle(w, gp, 1.9, px) * step(0.84, gh) * crown cu = mix(cu, vec3(1.0, 1.0, 1.0), glint) let rim = vec3(0.86, 0.36, 0.60) cu = mix(cu, rim, Finish.band(0.0 - seam, 0.0, max(px * 1.4, 0.9), px) * 0.8) // The outer band: deep candy pink, lit a little along its own crown. let bx = clamp((0.0 - d) / band, 0.0, 1.0) var bandc = mix(vec3(0.93, 0.47, 0.68), vec3(0.97, 0.60, 0.77), exp(-((bx - 0.45) * (bx - 0.45)) / 0.08)) let inband = Finish.cover(0.0 - d - band, px) cu = mix(cu, bandc, inband) cu = mix(cu, rim, Finish.band(0.0 - d - band, 0.0, max(px * 1.2, 0.8), px) * 0.7) let inc = Finish.cover(0.0 - seam, px) col = mix(col, cu, inc) col = mix(col, rim * 0.9, Finish.band(d, 0.0, max(px * 1.2, 0.8), px) * 0.9) // The lace along the foot of the frame. let lace = kw_lace(p, 30.0, sz.x - 30.0, sz.y - 0.5 - fw + 0.5, -1.0, 3.4, px) col = col * (1.0 - lace.a) + lace.rgb // The ornaments, each drawn only near its corner. var orn = vec4(0.0, 0.0, 0.0, 0.0) if p.x < 60.0 && p.y < 50.0 { orn = kw_bow(orn, p, vec2(24.0, 17.0), 19.0, vec3(0.99, 0.60, 0.79), px) } if p.x > sz.x - 34.0 && p.y < 60.0 { orn = kw_jelly_heart(orn, p, vec2(sz.x - 9.0, 9.5), 6.8, vec2(0.0, 1.0), px, vec3(0.97, 0.38, 0.64), 0.0, 0.0, 0.0, 0.7) orn = kw_star(orn, p, vec2(sz.x - 9.5, 34.0), 6.0, vec3(1.0, 0.88, 0.50), px) orn = Finish.over(orn, vec4(1.0, 1.0, 1.0, 1.0) * kw_sparkle(p, vec2(sz.x - 20.0, 46.0), 3.0, px) * 0.95) } if p.x < 54.0 && p.y > sz.y - 40.0 { orn = kw_strawberry(orn, p, vec2(16.5, sz.y - 17.5), 11.0, px) orn = kw_star(orn, p, vec2(37.0, sz.y - 8.0), 5.6, vec3(1.0, 0.88, 0.50), px) } if p.x > sz.x - 70.0 && p.y > sz.y - 70.0 { orn = kw_jelly_heart(orn, p, vec2(sz.x - 42.0, sz.y - 10.5), 6.4, vec2(0.0, 1.0), px, vec3(0.99, 0.62, 0.80), 0.0, 0.0, 0.0, 0.6) orn = kw_jelly_heart(orn, p, vec2(sz.x - 20.0, sz.y - 20.5), 12.5, vec2(0.0, 1.0), px, vec3(0.97, 0.38, 0.64), 0.0, 0.0, 0.0, 0.8) orn = kw_star(orn, p, vec2(sz.x - 10.5, sz.y - 47.0), 6.0, vec3(1.0, 0.88, 0.50), px) orn = Finish.over(orn, vec4(1.0, 1.0, 1.0, 1.0) * kw_sparkle(p, vec2(sz.x - 52.0, sz.y - 27.0), 3.8, px) * 0.95) } // Past the rounded corners: the rose behind the window; the ornaments // stand over the corner. col = mix(vec3(0.80, 0.42, 0.60), col, Finish.cover(d, px)) col = col * (1.0 - orn.a) + orn.rgb return vec4(col, 1.0) } // ---------------------------------------------------------------- screens // A SCREEN: deep plum glass in a puffy candy-pink cushion, the readouts' // and the meter's one housing; the glass shaded under its top edge, a quiet // diagonal reflection over it. Its figures are the screen ink, a candy pink // glowing in the glass. mod.widgets.ScreenView.clip_x = false mod.widgets.ScreenView.clip_y = false mod.widgets.ScreenView.padding = mod.turtle.Inset{top: 9.0 right: 11.0 bottom: 9.0 left: 11.0} mod.widgets.ScreenView.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)) } let kw_screen_rim = fn() -> vec3 { return vec3(0.985, 0.70, 0.84) } mod.widgets.ScreenView.draw_bg.pixel = fn() { let m = 6.0 let px = kw_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(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), kw_snap(self.rect_size.y, o.y, px)) let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let sh = Finish.drop(p, c, h - vec2(1.0, 0.0), 9.0, vec2(0.0, 2.2), 1.8) * 0.32 var res = vec4(kw_shade() * sh, sh) var fill = self.color.rgb if self.scan > 0.0 { fill = fill * mix(1.0, Finish.scan(p.y, px, 3.0 * px, self.scan), 0.6) } res = Finish.over(res, kw_frame(p, lo, hi, 9.0, px, max(self.bezel, 3.4), kw_screen_rim(), fill, 1.0, 0.0, 0.0, 0.0)) return res * self.color.a } mod.widgets.ScreenView.sheen = 0.06 mod.widgets.ScreenView.draw_glass.pixel = fn() { let p = self.pos * self.rect_size let px = kw_px(self.draw_pass.dpi_factor) let lo = vec2(Finish.snap(self.rect_pos.x, px), Finish.snap(self.rect_pos.y, px)) - self.rect_pos let hi = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x, px), Finish.snap(self.rect_pos.y + self.rect_size.y, px)) - self.rect_pos let c = (lo + hi) * 0.5 let ho = (hi - lo) * 0.5 let b = max(self.bezel, 3.4) + px let hf = max(ho - vec2(b, b), vec2(0.5, 0.5)) let fd = Material.sd_box(p, c, hf, 5.5) let u = (p - (c - hf)) / max(hf * 2.0, vec2(1.0, 1.0)) let diag = u.x + u.y * 0.6 let band = smoothstep(0.0, 0.12, diag) * (1.0 - smoothstep(0.28, 0.5, diag)) let a = clamp(self.sheen * band, 0.0, 1.0) * Finish.cover(fd + px, px) return vec4(vec3(1.0, 0.88, 0.95) * a, a) } // THE NEEDLE METER is a screen: the same pink cushion and plum glass, its // scale printed on the glass in the screen ink, the hot zone a pink band // along the arc, the needle hot pink, its root under a pearl. mod.widgets.NeedleMeter.clip_x = false mod.widgets.NeedleMeter.clip_y = false mod.widgets.NeedleMeter.draw_bg.color_zone = uniform(#ff5fa2) mod.widgets.NeedleMeter.draw_bg.color_needle = uniform(#ff5fa2) mod.widgets.NeedleMeter.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.NeedleMeter.draw_bg.pixel = fn() { let m = 6.0 let px = kw_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(kw_snap(0.0, o.x, px), kw_snap(0.0, o.y, px)) let hi = vec2(kw_snap(self.rect_size.x, o.x, px), kw_snap(self.rect_size.y, o.y, px)) let c = (lo + hi) * 0.5 let h = (hi - lo) * 0.5 let sh = Finish.drop(p, c, h - vec2(1.0, 0.0), 9.0, vec2(0.0, 2.2), 1.8) * 0.32 var res = vec4(kw_shade() * sh, sh) var face = vec3(0.227, 0.086, 0.212) 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 = vec3(1.0, 0.824, 0.918) let arc = Finish.cover(abs(r - rr) - px * 0.75, px) * on_scale let nn = 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) * nn + 0.5) let ak = (k / nn - 0.5) * sweep let perp = abs(q.x * cos(ak) + q.y * sin(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) let radial = max(rr - len - r, r - rr) let tick = Finish.cover(perp - px * mix(0.6, 1.1, major), px) * Finish.cover(radial, px) * step(0.0, 0.0 - q.y * cos(ak) + q.x * sin(ak)) face = mix(face, ink, max(arc, tick) * 0.9) if self.zone < 0.999 { let zone_t = Finish.cover((self.zone - t) * sweep * r, px) * Finish.cover((t - 1.0) * sweep * r, px) let zone_r = Finish.cover(max(rr + px - r, r - rr - 3.0), px) face = mix(face, self.color_zone.rgb, zone_t * zone_r) } let na = self.needle_angle(self.value) let nd = vec2(sin(na), 0.0 - cos(na)) let along = dot(q, nd) let across = abs(q.x * nd.y - q.y * nd.x) let needle = Finish.cover(across - 0.75, px) * Finish.cover(along - (rr + 3.0), px) * Finish.cover(0.0 - along, px) face = mix(face, self.color_needle.rgb, needle) res = Finish.over(res, kw_frame(p, lo, hi, 9.0, px, 3.4, kw_screen_rim(), face, 1.0, 0.0, 0.0, 0.0)) // The pivot: a pearl. res = kw_pearl(res, p, pv, max(self.pivot_size, 3.2), px, vec3(1.0, 0.94, 0.97), 0.0, 0.0, 0.0, 0.4) // The glass's quiet reflection. let hf = h - vec2(3.4, 3.4) let u = (p - (c - hf)) / max(hf * 2.0, vec2(1.0, 1.0)) let diag = u.x + u.y * 0.6 let band = smoothstep(0.0, 0.12, diag) * (1.0 - smoothstep(0.28, 0.5, diag)) let ga = 0.06 * band * Finish.cover(Material.sd_box(p, c, hf, 5.5), px) res = Finish.over(res, vec4(vec3(1.0, 0.88, 0.95) * ga, ga)) return res * self.color.a * self.opacity } // ---------------------------------------------------------------- switches // THE SWITCHES are the toggle's jelly and pearl in their own colours: the // rocker lights lavender, the slide switch mint. mod.widgets.ToggleRocker.draw_bg.size = uniform(18.0) mod.widgets.ToggleSlide.draw_bg.size = uniform(18.0) mod.widgets.ToggleRocker.draw_bg.pill_aspect = uniform(1.89) mod.widgets.ToggleSlide.draw_bg.pill_aspect = uniform(1.89) mod.widgets.ToggleRocker.label_walk.margin.left = 34.0 mod.widgets.ToggleSlide.label_walk.margin.left = 34.0 mod.widgets.ToggleRocker.draw_bg.pixel = fn() { let m = 5.0 let px = kw_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(kw_snap(x0, o.x, px), kw_snap(y0, o.y, px)) let hi = vec2(kw_snap(x0 + tw, o.x, px), kw_snap(y0 + th, o.y, px)) let kt = mix(self.active, self.drag_pos, self.drag) return kw_toggle(p, lo, hi, o, px, kt, self.active, self.hover, max(self.pressed, self.down), self.disabled, self.focus, vec3(0.74, 0.58, 0.98)) } mod.widgets.ToggleSlide.draw_bg.pixel = fn() { let m = 5.0 let px = kw_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(kw_snap(x0, o.x, px), kw_snap(y0, o.y, px)) let hi = vec2(kw_snap(x0 + tw, o.x, px), kw_snap(y0 + th, o.y, px)) let kt = mix(self.active, self.drag_pos, self.drag) return kw_toggle(p, lo, hi, o, px, kt, self.active, self.hover, max(self.pressed, self.down), self.disabled, self.focus, vec3(0.50, 0.87, 0.66)) } // ---------------------------------------------------------------- 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.RadioButtonTabFlat.animator.hover.down.from = {all: Snap} mod.widgets.RadioButtonTabFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}} mod.widgets.ToggleFlat.animator.hover.down.from = {all: Snap} mod.widgets.ToggleFlat.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}} // ---------------------------------------------------------------- lamps // LAMPS: an LED as a jewel box has it, a glossy jelly gem of coloured // glass. 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 a bezel: a raised ring of pearl lit from // the upper left, or a socket cut into the candy, its upper wall in shadow // and a catch light on its lower rim. Over the gem one crisp highlight of a // dome. Its light is added, never painted: a tight hot halo and a short // bloom in its own hue, gone in about two radii. let kw_led_lamp_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 kw_led_lamp_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` (0 leaves a pale colour pale; a colour with // next to no hue, a white or a plain lamp, stays neutral either way). let kw_led_lamp_hue = fn(ink: vec3, vivid: float) -> vec3 { let f = kw_led_lamp_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 // (raised 0: a recess cut in what is under it; 1: a raised ring of `bez`; // 2: the same ring polished metal). Premultiplied over `dst`. let kw_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.3 * rt, 0.7, 2.4) let ds = d - bw let L = clamp(lit, 0.0, 1.0) let b = kw_led_lamp_full(ink) let hue = kw_led_lamp_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) // The lens's own frame: `q` in lens radii from its centre line, `t` its // distance out, `nrm` the way its outline faces. 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(kw_shade() * shd, shd)) if L > 0.001 && pale > 0.001 { // The tint, on a pale ground only: its hue laid over the candy, tight // and saturated, so even a pink lamp on the pink ground glows. let h = clamp(0.62 * pw * pow(2.0, -x / max(0.32 * reach, 0.5)) * win, 0.0, 0.6) * pale let est = res.rgb + ground * (1.0 - res.a) let w = clamp(dot(hue, lum) / max(dot(est, lum), 0.001), 0.6, 1.0) res = Finish.over(res, vec4(hue * h * w, h * w)) } // The bezel: raised, a ring of `bez` rounded over, lit from the upper // left; or a socket cut into what is under it, its upper wall in shadow // and a catch light on its lower rim. 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) 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)) // The light: a tight hot halo and a short bloom in its hue, gone in // about two radii, falling on the bezel too. if L > 0.001 { let xl = max(d, 0.0) let halo = exp(-xl / max(0.30 * rt, 0.4)) * 1.5 + exp(-xl / max(0.65 * rt, 0.7)) * 0.32 let hwin = 1.0 - smoothstep(1.1 * rt, 2.0 * rt + 1.0, xl) res = kw_led_lamp_add(res, mix(hue, white, 0.15 * pw) * halo * pw * hwin * win, ground) } // THE BEAD: a glossy dome of coloured glass. Its colour deepens toward // the top; light pools low inside, where it comes through the dome; lit, // its middle burns toward white at full current and its rim is its most // saturated colour. let top = smoothstep(-1.0, 0.6, q.y) let pool = exp(-(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.99, 0.975), exp(-t * t / 0.20) * smoothstep(0.5, 1.0, L) * 0.85) 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, a deep plum with its own colour barely there, // the same dome, a catch light along its lower rim. let offc = mix(vec3(0.15, 0.065, 0.13), b * 0.42, 0.16) 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 * (1.0 - 0.5 * pale) var col = mix(off, min(on, white), smoothstep(0.0, 0.35, L)) // One crisp highlight: a white oval across its upper third. 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)) // Out, a faint seam where the dome meets its bezel; lit, none: the rim // is the colour. 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: a glossy jelly gem set in a raised ring // of pearl on the candy, with its own soft rose shadow. The halo's // room is held in the lamp's own box (the library reserves a lens // thickness either side once a halo is asked for), so the light is never // cut at its edge. mod.widgets.Lamp.halo = 0.3 mod.widgets.Lamp.halo_reach = 1.0 mod.widgets.Lamp.draw_bg.color_accent = uniform(#ff3d9a) mod.widgets.Lamp.draw_bg.color_success = uniform(#2ee07a) mod.widgets.Lamp.draw_bg.color_warning = uniform(#ffb21a) mod.widgets.Lamp.draw_bg.color_error = uniform(#ff2448) mod.widgets.Lamp.draw_bg.vertex = fn() { let m = 2.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.Lamp.draw_bg.pixel = fn() { let m = 2.0 let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m) let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5) let reach = clamp(self.reach, 0.0, 1.0) // The bead takes nearly half its box, the rest is room for its bezel // and light. let t = min(self.rect_size.y / (1.0 + 2.0 * reach) * 1.35, self.rect_size.y * 0.5) let room = (self.rect_size.y - t) * 0.5 let c = self.rect_size * 0.5 // A round lamp: the bead the lamp's size in the middle of its box. A // bar: a short light pipe three fifths of a bead thick and at most 11 // points long, set from the start of its box with its light gone // within a point and a half, so in a row it never reaches toward what // stands after it. let tb = t * 0.6 let blen = min(max(self.rect_size.x - 2.0 * room, tb), min(2.0 * tb, 11.0)) let cb = vec2(1.5 + 0.9 + blen * 0.5, self.rect_size.y * 0.5) let cc = mix(c, cb, self.bar) let hb = mix(vec2(t * 0.5, t * 0.5), vec2(blen * 0.5, tb * 0.5), self.bar) let reach_px = mix(max(room - 0.5, 2.0), 1.5, self.bar) let res = kw_led_lamp(vec4(0.0, 0.0, 0.0, 0.0), p, cc, hb, self.lit, self.intent_color().rgb, 1.0 - step(3.5, self.intent), px, vec3(0.992, 0.827, 0.898), reach_px, vec3(1.0, 0.93, 0.96), 1.0) return res * self.opacity } mod.widgets.LampBar.draw_bg.color_accent = uniform(#ff3d9a) mod.widgets.LampBar.draw_bg.color_success = uniform(#2ee07a) mod.widgets.LampBar.draw_bg.color_warning = uniform(#ffb21a) mod.widgets.LampBar.draw_bg.color_error = uniform(#ff2448) true