makepad/widgets/sheets/themes/kawaii/widgets.splash

4122 lines
221 KiB
Text

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