makepad/widgets/sheets/themes/mecha-light/widgets.splash

2985 lines
159 KiB
Text

use mod.prelude.widgets_internal.*
// Mecha light: a mobile-suit cockpit in white armour.
// Every part is an armour plate with its corners cut at 45 degrees,
// standing on the plating of the hull:
// a bevel all round that catches the light from the upper left, a dark
// outline, a fine step line where the bevel meets the face. Values are lit
// in the suit's colour code -- blue first, red and yellow beside it --
// fields and screens are navy glass in a steel frame, knobs are machined
// steel with a red index in a dark ring. The light and the dark sheet share
// these faces line for line; only the palette differs. Every face is drawn
// in true points (one device pixel is `px`), and every shadow and glow is
// drawn OUTSIDE the layout box: the quad is grown in `vertex`, the
// templates that throw a shadow stop clipping, and no padding is spent on
// relief.
// ---------------------------------------------------------------- helpers
// One device pixel in points.
let mk_px = fn(dpi: float) -> float {
return 1.0 / max(dpi, 0.5)
}
// An edge rounded to a whole device pixel in window space, handed back in
// box space: a hairline on it lands on one pixel row.
let mk_snap = fn(v: float, o: float, px: float) -> float {
return Finish.snap(o + v, px) - o
}
// Distance from p to the segment a..b, in points.
let mk_seg = fn(p: vec2, a: vec2, b: vec2) -> float {
let pa = p - a
let ba = b - a
let t = clamp(dot(pa, ba) / max(dot(ba, ba), 0.0001), 0.0, 1.0)
return length(pa - ba * t)
}
// A hash that does not line up: the library's hash nearly repeats every 97
// cells, so each cell is first sent to a far cell picked by its own hash.
let mk_hash = fn(i: vec2) -> float {
let j = i + vec2(Finish.hash(i) * 419.0, Finish.hash(i + vec2(5.31, 9.17)) * 263.0)
return Finish.hash(j)
}
// Value noise on that hash, one unit per cell.
let mk_noise = fn(q: vec2) -> float {
let i = floor(q)
let f = q - i
let u = f * f * (vec2(3.0, 3.0) - f * 2.0)
let a = mk_hash(i)
let b = mk_hash(i + vec2(1.0, 0.0))
let c = mk_hash(i + vec2(0.0, 1.0))
let d = mk_hash(i + vec2(1.0, 1.0))
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y)
}
// Grain, one value per device pixel.
let mk_grain = fn(q: vec2, px: float, amt: float) -> float {
let i = floor(q / px)
return (mk_hash(i) - mk_hash(i + vec2(17.13, 3.71))) * amt
}
// Painted metal: a fine grain over two slow clouds, so no two plates of the
// same paint read alike and nothing repeats.
let mk_paint = fn(q: vec2, px: float, amt: float) -> float {
let cloud = (mk_noise(q / 70.0) - 0.5) * 0.6 + (mk_noise(q / 13.0 + vec2(7.7, 3.1)) - 0.5) * 0.4
return cloud * amt * 1.6 + mk_grain(q, px, amt)
}
// THE EIGHT-SIDED SHAPE every part of the suit is built from: a box `c`,
// half size `h`, its corners cut `k` along each edge at 45 degrees. The
// distance is the farthest of its eight edge lines, so inside it is the
// true distance to the outline and outside its contours stay eight-sided:
// a rim, a ring, a glow or a shadow laid on it keeps the crisp cut corner
// instead of rounding off round it.
let mk_oct = fn(p: vec2, c: vec2, h: vec2, k: float) -> float {
let q = abs(p - c)
let kk = clamp(k, 0.0, min(h.x, h.y))
return max(max(q.x - h.x, q.y - h.y), (q.x + q.y - h.x - h.y + kk) * 0.70710678)
}
// The same shape grown (`g` > 0) or shrunk by `g` points on every side,
// its cut moved with it so the new edges stay parallel to the old.
let mk_oct_k = fn(k: float, g: float) -> float {
return max(k + 0.58578644 * g, 0.0)
}
// What an eight-sided part throws: its own outline moved by `off`, its
// edge softened over `sigma` points, half dark on the outline itself.
let mk_oct_drop = fn(p: vec2, c: vec2, h: vec2, k: float, off: vec2, sigma: float) -> float {
let d = mk_oct(p - off, c, h, k)
return 1.0 - smoothstep(-1.5 * sigma, 1.5 * sigma, d)
}
// The shade the surround throws into an eight-sided well, moved by `off`
// toward the side away from the light; the caller clips it to the well.
let mk_oct_inset = fn(p: vec2, c: vec2, h: vec2, k: float, off: vec2, sigma: float) -> float {
return smoothstep(-1.5 * sigma, 1.5 * sigma, mk_oct(p - off, c, h, k))
}
// The way the outline of a chamfered box faces at `p` (the box `c`, half
// size `h`, corners cut `k` along each edge): the side, or the cut, whose
// edge is nearest.
let mk_cham_n = fn(p: vec2, c: vec2, h: vec2, k: 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 q = abs(p - c) - h
let kk = clamp(k, 0.0, min(h.x, h.y))
let dd = (q.x + q.y + kk) * 0.70710678
var n = vec2(0.0, 1.0)
if q.x > q.y {
n = vec2(1.0, 0.0)
}
if dd > max(q.x, q.y) && kk > 0.01 {
n = vec2(0.70710678, 0.70710678)
}
return n * s
}
// How much of the light from the upper left a face turned `n` takes:
// +1 facing it, -1 turned away.
let mk_lit = fn(n: vec2) -> float {
return dot(n, vec2(-0.55, -0.835))
}
// The three-quarter arc a waiting button turns in place of its label.
let mk_spinner = fn(dst: vec4, p: vec2, c: vec2, rad: float, t: float, amt: float, stroke: float, ink: vec3) -> vec4 {
let sdf = Sdf2d.viewport(p)
let a0 = t * 2.0 * PI
sdf.arc_round_caps(c.x, c.y, rad, a0, a0 + 1.5 * PI, stroke)
sdf.fill(vec4(ink, amt))
return Finish.over(dst, sdf.result)
}
// ---------------------------------------------------------------- the hull
// Which panel of the cockpit's floor a point lies on: rows 560 points
// tall, each cut into panels 680 to 900 points wide with the cuts of every
// row offset. Handed back as the panel's x0, y0, x1, y1 in points.
let mk_floor_cell = fn(w: vec2) -> vec4 {
let rh = 560.0
let row = floor(w.y / rh)
let cw = 680.0 + 220.0 * mk_hash(vec2(row, 3.7))
let off = mk_hash(vec2(row, 11.3)) * cw
let col = floor((w.x + off) / cw)
let x0 = col * cw - off
return vec4(x0, row * rh, x0 + cw, row * rh + rh)
}
// Coverage of a row of `n` slots slanted forward, cut in a plate: each
// slot 2 points wide and `h` tall, 4 points apart, starting at `o`. `.x`
// the dark inside, `.y` the light on the lower lip.
let mk_vent = fn(w: vec2, o: vec2, n: float, h: float, px: float) -> vec2 {
let u = w - o
let sl = u.x + u.y * 0.7
let f = sl / 4.0
let inr = step(0.0, f) * step(f, n) * smoothstep(0.0 - px, px, u.y) * smoothstep(0.0 - px, px, h - u.y)
let fra = fract(f)
let slot = smoothstep(0.0, px / 4.0, fra) * smoothstep(0.0, px / 4.0, 0.5 - fra) * inr
let lip = smoothstep(0.5, 0.5 + px / 4.0, fra) * smoothstep(0.0, px / 4.0, 0.5 + px * 1.5 / 4.0 - fra) * inr
return vec2(slot, lip)
}
// Coverage of a slanted stripe (a parallelogram) `wd` wide and `h` tall
// whose foot starts at `o`.
let mk_slant = fn(w: vec2, o: vec2, wd: float, h: float, px: float) -> float {
let u = w - o
let sl = u.x - (h - u.y) * 0.6
return smoothstep(0.0 - px, px, sl) * smoothstep(0.0 - px, px, wd - sl) * smoothstep(0.0 - px, px, u.y) * smoothstep(0.0 - px, px, h - u.y)
}
// A light strip set in the frame: a pill `hs` either side of `c`, its
// middle burning toward white, its colour at the edge, a short glow.
// Premultiplied over `dst`.
let mk_frame_light = fn(dst: vec4, w: vec2, c: vec2, hs: vec2, px: float, col: vec3) -> vec4 {
let d = Finish.sd_pill(w, c, hs)
let x = max(d, 0.0)
let g = (0.5 * exp(0.0 - (x * x) / (2.0 * 0.9 * 0.9)) + 0.2 * exp(0.0 - (x * x) / (2.0 * 2.2 * 2.2))) * (1.0 - Finish.cover(d, px)) * 0.750
var o = Finish.over(dst, vec4(col, 1.0) * g)
let q = (w - c) / max(hs, vec2(0.5, 0.5))
let qq = q * vec2(0.6, 1.6)
let core = exp(0.0 - dot(qq, qq))
let ink = mix(col, mix(col, vec3(1.0, 1.0, 1.0), 0.85), core)
o = Finish.over(o, vec4(vec3(0.490, 0.522, 0.573), 1.0) * Finish.ring_out(d, 0.0, px, px) * 0.8)
return Finish.over(o, vec4(ink, 1.0) * Finish.cover(d, px))
}
// A screw head sunk in a plate at `c`, radius `r`: a shadowed socket ring,
// a domed head lit from the upper left, and a hex socket in its middle.
// Premultiplied over `dst`.
let mk_screw = fn(dst: vec4, p: vec2, c: vec2, r: float, px: float) -> vec4 {
let q = p - c
let rl = length(q)
let n = q / max(rl, 0.001)
let sock = Finish.cover(rl - r - px * 1.5, px) * (1.0 - Finish.cover(rl - r, px))
var o = Finish.over(dst, vec4(vec3(0.490, 0.522, 0.573), 1.0) * sock * mix(0.85, 0.35, smoothstep(-0.8, 0.8, n.y)))
o = Finish.over(o, vec4(vec3(0.984, 0.988, 0.992), 1.0) * sock * smoothstep(0.3, 0.9, n.y) * 0.8)
let t = clamp(mk_lit(n) * smoothstep(0.0, r, rl), -1.0, 1.0)
var head = mix(vec3(0.541, 0.569, 0.616), vec3(0.984, 0.988, 0.992), smoothstep(-0.9, 0.9, t))
let hx = abs(q.x) * 0.866 + abs(q.y) * 0.5
let hex = max(hx, abs(q.y)) - r * 0.42
head = mix(head, vec3(0.243, 0.275, 0.322), Finish.cover(hex, px) * 0.85)
head = mix(head, vec3(0.243, 0.275, 0.322), Finish.band(rl - r, 0.0, px, px) * 0.7)
return Finish.over(o, vec4(head, 1.0) * Finish.cover(rl - r, px))
}
// THE HULL: the window's ground at a window point `w` (points) in a window
// `size` points large. As the cockpit in the pictures, the armour is a
// FRAME round the window, and the floor it holds is calm.
// THE FRAME: a raised band of armour 9 points wide down the sides and along
// the foot, deep along the top (94 points: the window's caption and the
// bar under it stand on it), its inner corners cut at 45 degrees. Its edge
// toward the floor is a bevel lit by the way it faces (shaded along the
// top band's foot and the left band, lit along the foot band and the right
// band), with the frame's shade lying a little way onto the floor. The heavy
// detail lives on it: a raised visor plate in the middle of the top band,
// its lower corners cut; vents of slanted slots either side of it; the
// suit's colour code in slanted stripes at both ends; screws in its
// corners; red light strips set in the side bands, blue ones in the foot.
// THE FLOOR: painted armour, lighter toward the top, its grain and a slow
// cloud in the paint, parted into a few big panels by a faint panel line.
let mk_hull = fn(w: vec2, px: float, size: vec2) -> vec3 {
let W = size.x
let H = size.y
let side = min(9.0, W * 0.02)
let tb = min(94.0, H * 0.22)
let ilo = vec2(side, tb)
let ihi = vec2(W - side, H - side)
let ic = (ilo + ihi) * 0.5
let ih = max((ihi - ilo) * 0.5, vec2(1.0, 1.0))
let ik = 16.0
let di = mk_oct(w, ic, ih, ik)
let tx = mk_paint(w, px, 0.012)
// The floor.
let t = clamp((w.y - ilo.y) / max(ihi.y - ilo.y, 1.0), 0.0, 1.0)
var col = mix(vec3(0.804, 0.820, 0.843), vec3(0.745, 0.761, 0.788), 0.1 + 0.8 * t) + vec3(tx, tx, tx * 1.04)
let r = mk_floor_cell(w - ilo)
let rc = (r.xy + r.zw) * 0.5 + ilo
let rh = (r.zw - r.xy) * 0.5
let id = mk_hash(r.xy * 0.013 + vec2(1.3, 7.1))
col = col * (1.0 + (id - 0.5) * 0.025)
let ds = mk_oct(w, rc, rh, 12.0)
let nf = mk_cham_n(w, rc, rh, 12.0)
let lf = mk_lit(nf)
// The faint panel line: a device pixel of shade on the panel's edge, a
// device pixel of light inside it where it faces the light. It shows
// where it leaves the frame and fades to a trace a few points in,
// under the content, so no line runs through words.
let near = mix(0.1, 1.0, 1.0 - smoothstep(4.0, 14.0, 0.0 - di))
col = mix(col, vec3(0.490, 0.522, 0.573), Finish.band(ds, 0.0, px, px) * 0.24 * near)
col = mix(col, vec3(0.984, 0.988, 0.992), Finish.band(ds, px, px * 2.0, px) * smoothstep(0.0, 0.8, lf) * 0.35 * near)
// The frame's shade on the floor along the top band and the left band.
let nin = mk_cham_n(w, ic, ih, ik)
let lin = mk_lit(nin)
col = mix(col, col * 0.84, (1.0 - smoothstep(0.0, 6.0, 0.0 - di)) * smoothstep(0.0, 0.8, lin))
// THE FRAME.
let tf = clamp(w.y / max(H, 1.0), 0.0, 1.0)
var fr = mix(vec3(0.910, 0.918, 0.929), vec3(0.847, 0.859, 0.878), tf) + vec3(tx, tx, tx * 1.04)
fr = fr + (vec3(0.910, 0.918, 0.929) - vec3(0.847, 0.859, 0.878)) * (1.0 - smoothstep(0.0, tb, w.y)) * 0.6
// The visor: a raised plate in the middle of the top band, its lower
// corners cut.
let vc = vec2(W * 0.5, tb * 0.17)
let vh = vec2(min(W * 0.17, 240.0), tb * 0.17 + 2.0)
let dv = Finish.sd_notch(w, vc, vh, vec4(0.0, 0.0, 14.0, 14.0))
let nv = mk_cham_n(w, vc, vh, 14.0)
let lv = mk_lit(nv)
fr = mix(fr, fr + (vec3(0.910, 0.918, 0.929) - vec3(0.847, 0.859, 0.878)) * 0.8, Finish.cover(dv, px))
fr = mix(fr, mix(vec3(0.490, 0.522, 0.573), vec3(0.984, 0.988, 0.992), smoothstep(-0.7, 0.7, lv)), Finish.band(dv, 0.0, px * 2.0, px) * 0.8 * step(px * 2.0, w.y))
// The vents either side of the visor.
let va = mk_vent(w, vec2(vc.x - vh.x - 60.0, 9.0), 9.0, 12.0, px)
let vb = mk_vent(w, vec2(vc.x + vh.x + 22.0, 9.0), 9.0, 12.0, px)
fr = mix(fr, vec3(0.490, 0.522, 0.573) * 0.75, (va.x + vb.x) * 0.9)
fr = mix(fr, vec3(0.984, 0.988, 0.992), (va.y + vb.y) * 0.7)
// The colour code at both ends of the top band.
let s1 = mk_slant(w, vec2(16.0, 9.0), 4.0, 12.0, px)
let s2 = mk_slant(w, vec2(24.0, 9.0), 4.0, 12.0, px)
let s3 = mk_slant(w, vec2(32.0, 9.0), 4.0, 12.0, px)
let s4 = mk_slant(w, vec2(vc.x + vh.x + 76.0, 9.0), 4.0, 12.0, px)
let s5 = mk_slant(w, vec2(vc.x + vh.x + 84.0, 9.0), 4.0, 12.0, px)
fr = mix(fr, vec3(0.850, 0.150, 0.170), (s1 + s4) * 0.95)
fr = mix(fr, vec3(0.110, 0.420, 0.950), (s2 + s5) * 0.95)
fr = mix(fr, vec3(0.990, 0.780, 0.130), s3 * 0.95)
// The frame's edge toward the floor: a bevel two points wide, lit by
// the way it faces, and the dark line where it meets the floor.
let lb = mk_lit(0.0 - nin)
fr = mix(fr, mix(vec3(0.490, 0.522, 0.573), vec3(0.984, 0.988, 0.992), smoothstep(-0.7, 0.7, lb)), (1.0 - smoothstep(0.0, 2.2, di)) * 0.85)
fr = mix(fr, vec3(0.490, 0.522, 0.573), Finish.band(0.0 - di, 0.0, px, px) * 0.9)
// The window's own edge: lit along its top and left, shaded along its
// foot and right.
let de = min(min(w.x, w.y), min(W - w.x, H - w.y))
let edge = 1.0 - smoothstep(px, px * 2.5, de)
let lit_edge = clamp(step(w.y, px * 2.5) + step(w.x, px * 2.5), 0.0, 1.0)
fr = mix(fr, mix(vec3(0.490, 0.522, 0.573), vec3(0.984, 0.988, 0.992), lit_edge), edge * 0.7)
var fo = vec4(fr, 1.0)
// Screws in the corners of the frame.
let sxl = side * 0.5 + 4.0
fo = mk_screw(fo, w, vec2(sxl, tb - 11.0), 2.4, px)
fo = mk_screw(fo, w, vec2(W - sxl, tb - 11.0), 2.4, px)
fo = mk_screw(fo, w, vec2(sxl, 6.0), 2.2, px)
fo = mk_screw(fo, w, vec2(W - sxl, 6.0), 2.2, px)
fo = mk_screw(fo, w, vec2(side * 0.5, H - side * 0.5), 2.2, px)
fo = mk_screw(fo, w, vec2(W - side * 0.5, H - side * 0.5), 2.2, px)
// The light strips: red in the side bands, blue in the foot band.
fo = mk_frame_light(fo, w, vec2(side * 0.5, ic.y), vec2(1.2, 34.0), px, vec3(1.0, 0.150, 0.150))
fo = mk_frame_light(fo, w, vec2(W - side * 0.5, ic.y), vec2(1.2, 34.0), px, vec3(1.0, 0.150, 0.150))
fo = mk_frame_light(fo, w, vec2(W * 0.28, H - side * 0.5), vec2(40.0, 1.2), px, vec3(0.180, 0.520, 1.0))
fo = mk_frame_light(fo, w, vec2(W * 0.72, H - side * 0.5), vec2(40.0, 1.2), px, vec3(0.180, 0.520, 1.0))
let frame = Finish.cover(0.0 - di, px)
return mix(col, fo.rgb, frame)
}
// ---------------------------------------------------------------- the cap
// AN ARMOUR CAP: a plate with its corners cut `k` points at 45 degrees,
// standing on the hull. Under it what it throws (`lift` 1 a drop shadow
// falling a little down and right, 0 only the contact line); its dark
// outline; a bevel `bw` points wide all round lit by the way it faces --
// near white where it faces the light at the upper left, shaded toward the
// foot and the right -- falling into the face over its width; a fine step
// line where the bevel meets the face; the face falling from `top` to
// `bot`, a sheen over its upper half and the grain of its paint. `rivets` 1
// sets a small screw mark in each corner of the face. Held, the bevel turns
// (lit at the foot, shaded at the top) and the face drops a shade: the cap
// is pushed into the hull. Premultiplied.
let mk_cap = fn(p: vec2, lo: vec2, hi: vec2, k: float, px: float, hover: float, down: float, focus: float, lift: float, top: vec3, bot: vec3, hl: vec3, sh: vec3, ol: vec3, line: vec3, bw: float, rivets: float) -> vec4 {
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let hh = hi.y - lo.y
let d = mk_oct(p, c, h, k)
let up = 1.0 - down
// What it throws: a dense shade right under the foot and a soft one
// carrying on down and a little right; to the sides both stay inside
// half an 8 point gap.
let s = min(hh, 26.0)
let d1 = mk_oct_drop(p, c, h - vec2(0.04 * s, 0.0), mk_oct_k(k, -0.02 * s), vec2(0.02 * s, 0.07 * s), 0.05 * s)
let d2 = mk_oct_drop(p, c, h - vec2(0.12 * s, 0.03 * s), mk_oct_k(k, -0.075 * s), vec2(0.05 * s, 0.15 * s), 0.13 * s)
let drop = (1.0 - (1.0 - 0.75 * d1) * (1.0 - 0.6 * d2)) * lift * up * 0.420
let contact = mk_oct_drop(p, c, h, k, vec2(0.0, px), px * 0.7) * 0.55
let sha = 1.0 - (1.0 - drop) * (1.0 - contact)
var res = vec4(vec3(0.102, 0.133, 0.196) * sha, sha)
// The face.
let t = clamp((p.y - lo.y) / max(hh, 1.0), 0.0, 1.0)
var face = mix(top, bot, smoothstep(0.0, 1.0, t))
let uv = clamp((p - lo) / max(hi - lo, vec2(1.0, 1.0)), vec2(0.0, 0.0), vec2(1.0, 1.0))
face = face + (hl - face) * Finish.sheen(uv, 0.0, 0.5, 0.25) * 0.18 * up
let tx = mk_paint(p + lo * 3.1, px, 0.010)
face = face + vec3(tx, tx, tx * 1.04)
face = face + vec3(1.0, 1.0, 1.0) * 0.035 * hover * up
face = mix(face, face * 0.88, down)
// A rivet in each corner of the face, on a cap big enough to hold them.
let inner = h - vec2(bw + px * 2.0 + 2.6, bw + px * 2.0 + 2.6)
let rc = c + vec2(step(c.x, p.x) * 2.0 - 1.0, step(c.y, p.y) * 2.0 - 1.0) * inner
let rl = length(p - rc)
let rv = rivets * step(20.0, hh) * step(48.0, hi.x - lo.x)
face = mix(face, line, Finish.cover(rl - 0.8, px) * rv * 0.8)
face = mix(face, hl, Finish.cover(length(p - rc - vec2(0.35, 0.35)) - 0.45, px) * rv * 0.7)
// The bevel: lit by the way it faces; held, turned over.
let n = mk_cham_n(p, c, h, k)
var l = mk_lit(n)
l = mix(l, 0.0 - l * 0.85, down)
let inb = Finish.band(d, px, px + bw, px)
let u = clamp((0.0 - d - px) / max(bw, px), 0.0, 1.0)
var bev = mix(sh, hl, smoothstep(-0.8, 0.75, l))
bev = mix(bev, mix(bev, face, 0.45), u)
face = mix(face, bev, inb)
// The outer row of the bevel catches the most light.
face = mix(face, hl, Finish.band(d, px, px * 2.0, px) * smoothstep(0.3, 0.95, l) * 0.8)
// The step into the face, and the outline.
face = mix(face, line, Finish.band(d, px + bw, px * 2.0 + bw, px) * 0.75)
face = mix(face, ol, Finish.band(d, 0.0, px, px))
let a = Finish.cover(d, px)
res = Finish.over(res, vec4(face * a, a))
// Focus: a lit blue ring just outside the outline and a breath of it.
let ring = Finish.ring_out(d, 0.0, px * 1.5, px) * 0.9 + Finish.glow(d - px * 1.5, 1.0) * 0.35 * (1.0 - a)
res = Finish.over(res, vec4(0.200, 0.520, 1.0, 1.0) * clamp(ring, 0.0, 1.0) * focus)
return res
}
// A light strip lying along the foot of a cap's face: a pill `hw` half wide
// and two points tall whose middle burns toward white, its colour at the
// edge, a glow round it on the face. `clip` keeps the glow on the face.
// Premultiplied over `dst`.
let mk_strip = fn(dst: vec4, p: vec2, cx: float, cy: float, hw: float, px: float, col: vec3, lit: float, clip: float) -> vec4 {
let d = Finish.sd_pill(p, vec2(cx, cy), vec2(hw, 1.0))
let x = max(d, 0.0)
let g = (0.55 * exp(-(x * x) / (2.0 * 0.9 * 0.9)) + 0.25 * exp(-(x * x) / (2.0 * 2.6 * 2.6))) * (1.0 - Finish.cover(d, px))
var o = Finish.over(dst, vec4(col, 1.0) * g * lit * clip)
let core = 1.0 - smoothstep(0.0, 0.9, abs(p.y - cy + 0.2))
let ink = mix(col, mix(col, vec3(1.0, 1.0, 1.0), 0.85), core)
o = Finish.over(o, vec4(mix(col * 0.25, ink, lit), 1.0) * Finish.cover(d, px))
return o
}
// ---------------------------------------------------------------- buttons
// Every button is the same armour cap: 26 points on a 13 point line, 12
// points either side. Its shadow falls outside the box, so the box is the
// outline and nothing is paid in padding.
mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonFlat.min_height = 26.0
// Glyphs sit at 14 points so an icon never makes a cap taller than the row;
// an icon alone gets a square cap.
mod.widgets.ButtonFlat.icon_walk.width = 14.0
mod.widgets.ButtonIcon.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonFlatIcon.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonFlatterIcon.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 4.0 bottom: 4.0}
// The caps that carry a light strip lift their label one device pixel, so
// the strip clears the descenders.
mod.widgets.ButtonPrimary.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 3.3333 bottom: 4.6667}
mod.widgets.ButtonDanger.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 3.3333 bottom: 4.6667}
// The short rungs keep their own heights under the row minimum.
mod.widgets.ButtonXs.min_height = 0.0
mod.widgets.ButtonSm.min_height = 0.0
mod.widgets.ButtonXs.draw_text.text_style.font_size = 7.0
mod.widgets.ButtonFlat.draw_bg.border_radius = uniform(1.0)
mod.widgets.ButtonFlat.draw_bg.border_size = uniform(0.34)
mod.widgets.ButtonFlat.clip_x = false
mod.widgets.ButtonFlat.clip_y = false
mod.widgets.ButtonFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
// The cap and its states, shared by every button template; `kind` picks the
// variant: 0 the plain cap, 1 the red cap (the default action), 2 primary (a
// blue light strip), 3 secondary, 4 tertiary (seated lower), 5 danger (a
// red light strip), 6 outline, 7 flatter, 8 the navy cap that holds an icon.
// Disabled, every cap turns dark slate with pale words.
let mk_button = fn(pos: vec2, size: vec2, o: vec2, dpi: float, hover: float, down: float, disabled: float, focus: float, loading: float, anim: float, kind: float, dash: float) -> vec4 {
let m = 12.0
let px = mk_px(dpi)
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(size.x, o.x, px), mk_snap(size.y, o.y, px))
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let k = clamp((hi.y - lo.y) * 0.22, 3.0, 6.5)
let bw = clamp((hi.y - lo.y) * 0.1, 1.5, 3.0)
let live = 1.0 - disabled
var res = vec4(0.0, 0.0, 0.0, 0.0)
if kind > 5.5 && kind < 7.5 {
// Outline: no face, the cap's frame alone -- outline, bevel and
// step line round the bare hull; under the hand a faint cap shows,
// held it sinks.
let d = mk_oct(p, c, h, k)
let n = mk_cham_n(p, c, h, k)
let l = mk_lit(n)
if kind < 6.5 {
var ring = Finish.band(d, 0.0, px, px)
var frame = Finish.band(d, px, px + bw, px)
if dash > 0.0 {
let dm = step(0.5, fract((p.x + p.y) / (dash * 2.0)))
ring = ring * dm
frame = frame * dm
}
let bev = mix(vec3(0.498, 0.529, 0.580), vec3(1.000, 1.000, 1.000), smoothstep(-0.8, 0.75, l))
res = vec4(bev, 1.0) * frame * mix(1.0, 0.6, disabled)
res = Finish.over(res, vec4(vec3(0.604, 0.631, 0.678), 1.0) * Finish.band(d, px + bw, px * 2.0 + bw, px) * 0.8)
res = Finish.over(res, vec4(vec3(0.290, 0.322, 0.373), 1.0) * ring * mix(1.0, 0.5, disabled))
}
if hover + down > 0.001 {
let cap = mk_cap(p, lo, hi, k, px, hover, down, focus, 0.4, vec3(0.906, 0.914, 0.925), vec3(0.812, 0.827, 0.855), vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), vec3(0.290, 0.322, 0.373), vec3(0.604, 0.631, 0.678), bw, 0.0)
res = Finish.over(res, cap * clamp(max(hover, down), 0.0, 1.0) * live)
}
let fr = Finish.ring_out(d, 0.0, px * 1.5, px) * 0.9 + Finish.glow(d - px * 1.5, 1.0) * 0.35 * (1.0 - Finish.cover(d, px))
res = Finish.over(res, vec4(0.200, 0.520, 1.0, 1.0) * clamp(fr, 0.0, 1.0) * focus * live)
} else {
var top = vec3(0.906, 0.914, 0.925)
var bot = vec3(0.812, 0.827, 0.855)
var hl = vec3(1.000, 1.000, 1.000)
var sh = vec3(0.498, 0.529, 0.580)
var ol = vec3(0.290, 0.322, 0.373)
var line = vec3(0.604, 0.631, 0.678)
var lift = 1.0
if kind > 0.5 && kind < 1.5 {
// The red cap: lacquer red lit from the top, a hot bevel.
top = vec3(0.835, 0.235, 0.235)
bot = vec3(0.620, 0.090, 0.098)
hl = vec3(1.0, 0.640, 0.620)
sh = vec3(0.330, 0.035, 0.045)
ol = vec3(0.220, 0.020, 0.030)
line = vec3(0.470, 0.060, 0.070)
}
if kind > 7.5 {
top = vec3(0.271, 0.322, 0.416)
bot = vec3(0.169, 0.208, 0.275)
hl = vec3(0.553, 0.616, 0.714)
sh = vec3(0.071, 0.098, 0.145)
ol = vec3(0.071, 0.098, 0.145)
line = vec3(0.071, 0.098, 0.145)
}
if kind > 3.5 && kind < 4.5 {
lift = 0.55
}
top = mix(top, vec3(0.545, 0.576, 0.631), disabled)
bot = mix(bot, vec3(0.431, 0.463, 0.522), disabled)
hl = mix(hl, vec3(0.725, 0.753, 0.792), disabled)
sh = mix(sh, vec3(0.290, 0.318, 0.365), disabled)
line = mix(line, vec3(0.290, 0.318, 0.365), disabled)
ol = mix(ol, vec3(0.290, 0.318, 0.365) * 0.8, disabled)
let fo = focus * live
if kind > 0.5 && kind < 1.5 {
// The red cap glows a little onto the hull round it.
let d = mk_oct(p, c, h, k)
res = vec4(0.95, 0.20, 0.20, 1.0) * Finish.glow(d, 2.2) * 0.30 * 0.750 * (1.0 - Finish.cover(d, px)) * live * (1.0 - down * 0.5)
}
res = Finish.over(res, mk_cap(p, lo, hi, k, px, hover * live, down * live, fo, lift, top, bot, hl, sh, ol, line, bw, 1.0))
if kind > 1.5 && kind < 2.5 || kind > 4.5 && kind < 5.5 {
// The light strip along the foot of the face: blue on the
// primary, red on the danger cap; held, its light halves; out
// (disabled) it is dark glass.
var col = vec3(0.180, 0.520, 1.0)
if kind > 4.5 {
col = vec3(1.0, 0.210, 0.200)
}
let d = mk_oct(p, c, h, k)
let clip = Finish.cover(d + bw + px * 2.0, px)
let cy = mk_snap(hi.y - bw - px * 2.0 - 1.0, o.y, px) - px * 0.5
res = mk_strip(res, p, c.x, cy, min(14.0, h.x * 0.42), px, col, live * (1.0 - down * 0.5), clip)
}
}
if loading > 0.001 {
let rad = max(min(h.x, h.y) - 7.0, 2.0)
res = mk_spinner(res, p, c, rad, anim, loading, 1.5, vec3(0.078, 0.106, 0.169))
}
return res
}
mod.widgets.ButtonFlat.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 0.0, 0.0), 12.0)
}
mod.widgets.Button.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 1.0, 0.0), 12.0)
}
mod.widgets.ButtonIcon.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 8.0, 0.0), 12.0)
}
mod.widgets.ButtonPrimary.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 2.0, 0.0), 12.0)
}
mod.widgets.ButtonSecondary.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 3.0, 0.0), 12.0)
}
mod.widgets.ButtonTertiary.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 4.0, 0.0), 12.0)
}
mod.widgets.ButtonDanger.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 5.0, 0.0), 12.0)
}
mod.widgets.ButtonOutline.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 6.0, self.border_dash), 12.0)
}
mod.widgets.ButtonFlatter.draw_bg.pixel = fn() {
return self.host_over(mk_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 7.0, 0.0), 12.0)
}
// Label ink per state: dark ink on the pale caps, white on the red cap and
// the navy one, pale words on every disabled cap.
mod.widgets.ButtonFlat.draw_text.color = #141b2b
mod.widgets.ButtonFlat.draw_text.color_hover = #141b2b
mod.widgets.ButtonFlat.draw_text.color_down = #141b2b
mod.widgets.ButtonFlat.draw_text.color_focus = #141b2b
mod.widgets.ButtonFlat.draw_text.color_disabled = #eef1f5
mod.widgets.Button.draw_text.color = #ffffff
mod.widgets.Button.draw_text.color_hover = #ffffff
mod.widgets.Button.draw_text.color_down = #f4dede
mod.widgets.Button.draw_text.color_focus = #ffffff
mod.widgets.Button.draw_text.color_disabled = #eef1f5
mod.widgets.ButtonIcon.draw_text.color = #f2f5f9
mod.widgets.ButtonPrimary.draw_text.color = #141b2b
mod.widgets.ButtonPrimary.draw_text.color_hover = #141b2b
mod.widgets.ButtonPrimary.draw_text.color_down = #141b2b
mod.widgets.ButtonPrimary.draw_text.color_focus = #141b2b
mod.widgets.ButtonPrimary.draw_text.color_disabled = #eef1f5
mod.widgets.ButtonPrimary.draw_icon.color = #141b2b
mod.widgets.ButtonSecondary.draw_text.color = #141b2b
mod.widgets.ButtonSecondary.draw_text.color_hover = #141b2b
mod.widgets.ButtonSecondary.draw_text.color_down = #141b2b
mod.widgets.ButtonSecondary.draw_text.color_focus = #141b2b
mod.widgets.ButtonSecondary.draw_text.color_disabled = #eef1f5
mod.widgets.ButtonSecondary.draw_icon.color = #141b2b
mod.widgets.ButtonTertiary.draw_text.color = #141b2b
mod.widgets.ButtonTertiary.draw_text.color_hover = #141b2b
mod.widgets.ButtonTertiary.draw_text.color_down = #141b2b
mod.widgets.ButtonTertiary.draw_text.color_focus = #141b2b
mod.widgets.ButtonTertiary.draw_text.color_disabled = #eef1f5
mod.widgets.ButtonTertiary.draw_icon.color = #141b2b
mod.widgets.ButtonDanger.draw_text.color = #141b2b
mod.widgets.ButtonDanger.draw_text.color_hover = #141b2b
mod.widgets.ButtonDanger.draw_text.color_down = #141b2b
mod.widgets.ButtonDanger.draw_text.color_focus = #141b2b
mod.widgets.ButtonDanger.draw_text.color_disabled = #eef1f5
mod.widgets.ButtonDanger.draw_icon.color = #141b2b
mod.widgets.ButtonOutline.draw_text.color = #141b2b
mod.widgets.ButtonOutline.draw_text.color_hover = #141b2b
mod.widgets.ButtonOutline.draw_text.color_down = #141b2b
mod.widgets.ButtonOutline.draw_text.color_focus = #141b2b
mod.widgets.ButtonOutline.draw_text.color_disabled = #79818f
mod.widgets.ButtonOutline.draw_icon.color = #141b2b
mod.widgets.ButtonFlatter.draw_text.color = #141b2b
mod.widgets.ButtonFlatter.draw_text.color_hover = #141b2b
mod.widgets.ButtonFlatter.draw_text.color_down = #141b2b
mod.widgets.ButtonFlatter.draw_text.color_focus = #141b2b
mod.widgets.ButtonFlatter.draw_text.color_disabled = #79818f
mod.widgets.ButtonFlat.draw_icon_end.color = #141b2b
// An icon on the red cap or the navy cap is white, whatever ink its caller
// gave it: a dark glyph is lost on either.
mod.widgets.Button.draw_icon.get_color = fn() {
let base = self.eval_gradient()
return vec4(vec3(1.0, 1.0, 1.0) * base.a, base.a) * self.opacity
}
mod.widgets.ButtonIcon.draw_icon.get_color = fn() {
let base = self.eval_gradient()
return vec4(vec3(0.949, 0.961, 0.976) * base.a, base.a) * self.opacity
}
// The labels are set in the face's medium weight.
mod.widgets.ButtonFlat.draw_text.text_style = mod.theme.font_label{}
mod.widgets.Button.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonPrimary.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonSecondary.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonTertiary.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonDanger.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonOutline.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonFlatter.draw_text.text_style = mod.theme.font_label{}
mod.widgets.ButtonSm.draw_text.text_style = mod.theme.font_label{font_size: 7.5}
mod.widgets.ButtonLg.draw_text.text_style = mod.theme.font_label{font_size: 10.0}
// ---------------------------------------------------------------- wells
// A SLOT cut into the hull: a box `lo`..`hi` with corners `r`, dark under
// its top edge where the hull shades it and lighter toward its foot, a
// dark outline, a shade along the hull over its top edge and a light lip
// on the hull under its foot. Premultiplied.
let mk_slot = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float, top: vec3, bot: vec3) -> vec4 {
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = Material.sd_box(p, c, h, min(r, min(h.x, h.y)))
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
var col = mix(top, bot, smoothstep(0.0, 1.0, t))
col = mix(col, col * 0.55, Finish.inset(p, c, h, min(r, min(h.x, h.y)), vec2(0.0, 1.0), 0.9) * 0.8)
col = mix(col, vec3(0.290, 0.322, 0.373), Finish.band(d, 0.0, px, px) * mix(1.0, 0.5, step(c.y, p.y)))
let lip = Finish.ring_out(d, 0.0, px * 1.5, px) * smoothstep(-0.2, 0.6, (p.y - c.y) / max(h.y, 0.5))
let shade = Finish.ring_out(d, 0.0, px, px) * (1.0 - step(c.y, p.y))
var res = vec4(vec3(0.541, 0.569, 0.612), 1.0) * Finish.ring_out(d, 0.0, px, px) * 0.000
res = Finish.over(res, vec4(vec3(0.957, 0.961, 0.969), 1.0) * lip * 0.9)
res = Finish.over(res, vec4(vec3(0.102, 0.133, 0.196), 1.0) * shade * 0.35)
let a = Finish.cover(d, px)
return Finish.over(res, vec4(col * a, a))
}
// The same slot cut as an eight-sided plate is: its corners cut `k` at 45
// degrees, its shade, outline, lip and shadow all following the cut. The
// bars, the ladders, the fader's slot and the scroll bar are cut so.
let mk_slot8 = fn(p: vec2, lo: vec2, hi: vec2, k: float, px: float, top: vec3, bot: vec3) -> vec4 {
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = mk_oct(p, c, h, k)
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
var col = mix(top, bot, smoothstep(0.0, 1.0, t))
col = mix(col, col * 0.55, mk_oct_inset(p, c, h, k, vec2(0.0, 1.0), 0.6) * 0.8)
col = mix(col, vec3(0.290, 0.322, 0.373), Finish.band(d, 0.0, px, px) * mix(1.0, 0.5, step(c.y, p.y)))
let lip = Finish.ring_out(d, 0.0, px * 1.5, px) * smoothstep(-0.2, 0.6, (p.y - c.y) / max(h.y, 0.5))
let shade = Finish.ring_out(d, 0.0, px, px) * (1.0 - step(c.y, p.y))
var res = vec4(vec3(0.541, 0.569, 0.612), 1.0) * Finish.ring_out(d, 0.0, px, px) * 0.000
res = Finish.over(res, vec4(vec3(0.957, 0.961, 0.969), 1.0) * lip * 0.9)
res = Finish.over(res, vec4(vec3(0.102, 0.133, 0.196), 1.0) * shade * 0.35)
let a = Finish.cover(d, px)
return Finish.over(res, vec4(col * a, a))
}
// A WELL IN A FRAME: navy glass sunk in a steel frame `bw` wide, the frame
// itself a chamfered plate standing on the hull -- its outline, its bevel
// lit from the upper left, a dark line where the glass begins -- and the
// glass shaded under its top edge, a faint light along its foot. `fill_top`
// and `fill_bot` are the glass; focus lights the frame's outer edge blue.
// Premultiplied.
let mk_framed = fn(p: vec2, lo: vec2, hi: vec2, k: float, px: float, bw: float, fill_top: vec3, fill_bot: vec3, focus: float, hover: float) -> vec4 {
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = mk_oct(p, c, h, k)
// The frame stands a little off the hull.
let contact = mk_oct_drop(p, c, h, k, vec2(0.3, 1.0), 0.9) * 0.5 * 0.420
var res = vec4(vec3(0.102, 0.133, 0.196) * contact, contact)
let n = mk_cham_n(p, c, h, k)
let l = mk_lit(n)
var fr = mix(vec3(0.478, 0.510, 0.561), vec3(1.000, 1.000, 1.000), smoothstep(-0.8, 0.75, l))
let u = clamp((0.0 - d - px) / max(bw, px), 0.0, 1.0)
fr = mix(fr, mix(fr, vec3(0.776, 0.796, 0.827), 0.5), smoothstep(0.3, 1.0, u))
fr = mix(fr, vec3(1.000, 1.000, 1.000), Finish.band(d, px, px * 2.0, px) * smoothstep(0.3, 0.95, l) * 0.7)
// The glass.
let gk = mk_oct_k(k, 0.0 - (bw + px))
let gh = h - vec2(bw + px, bw + px)
let gd = mk_oct(p, c, gh, gk)
let t = clamp((p.y - (c.y - gh.y)) / max(gh.y * 2.0, 1.0), 0.0, 1.0)
var gl = mix(fill_top, fill_bot, t) + vec3(0.02, 0.022, 0.026) * hover
gl = mix(gl, gl * 0.45, (1.0 - smoothstep(0.0, 2.6, p.y - (c.y - gh.y))) * 0.8)
let gn = mk_cham_n(p, c, gh, gk)
gl = mix(gl, mix(gl, vec3(1.000, 1.000, 1.000), 0.25), Finish.band(gd, px, px * 2.0, px) * smoothstep(0.4, 0.95, gn.y))
gl = mix(gl, vec3(0.271, 0.302, 0.353), Finish.band(gd, 0.0, px, px))
var col = mix(fr, gl, Finish.cover(gd, px))
col = mix(col, vec3(0.271, 0.302, 0.353), Finish.band(d, 0.0, px, px))
let a = Finish.cover(d, px)
res = Finish.over(res, vec4(col * a, a))
let ring = Finish.ring_out(d, 0.0, px * 1.5, px) * 0.9 + Finish.glow(d - px * 1.5, 1.2) * 0.4 * (1.0 - a)
return Finish.over(res, vec4(0.200, 0.520, 1.0, 1.0) * clamp(ring, 0.0, 1.0) * focus)
}
// BLUE GLASS LIT FROM WITHIN: what is chosen -- the chosen segment, the tab
// in use, a check box set. Its glow on the hull round it, a pale bevel
// turned over (the part is pushed in), the face saturated blue, brightest
// under its top edge and deeper toward its foot, a lighter line inside its
// edge. Premultiplied.
let mk_blue = fn(p: vec2, lo: vec2, hi: vec2, k: float, px: float, bw: float, hover: float, sunk: float, glow: float) -> vec4 {
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = mk_oct(p, c, h, k)
let a = Finish.cover(d, px)
var res = vec4(0.150, 0.470, 1.0, 1.0) * Finish.glow(d, 2.4) * 0.55 * glow * 0.750 * (1.0 - a)
let cap = mk_cap(p, lo, hi, k, px, hover, sunk, 0.0, 1.0 - sunk, vec3(0.200, 0.520, 1.0), vec3(0.035, 0.290, 0.820), vec3(0.720, 0.850, 1.0), vec3(0.020, 0.150, 0.470), vec3(0.010, 0.080, 0.300), vec3(0.420, 0.680, 1.0), bw, 0.0)
res = Finish.over(res, cap)
// A bloom under the top edge of the glass.
let top = max(p.y - lo.y - bw - px * 2.0, 0.0)
let ux = (p.x - c.x) / max(h.x, 1.0)
let bloom = exp(-top / max(0.30 * (hi.y - lo.y), 1.0)) * (1.0 - 0.5 * ux * ux) * Finish.cover(d + bw + px * 2.0, px)
return Finish.over(res, vec4(0.55, 0.75, 1.0, 1.0) * bloom * 0.35)
}
// ---------------------------------------------------------------- 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(17.0)
mod.widgets.CheckBoxFlat.draw_bg.border_radius = uniform(1.0)
mod.widgets.CheckBoxFlat.label_walk.margin.left = 17.0
// The check box: a 17 point armour cap, its face pale. Set, the face is
// blue glass lit from within and a white check stands on it.
mod.widgets.CheckBoxFlat.draw_bg.pixel = fn() {
let m = 6.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let sz = self.size
let x0 = (self.rect_size.x - sz) * self.mark_at_end
let y0 = self.rect_size.y * 0.5 - sz * 0.5
let lo = vec2(mk_snap(x0, o.x, px), mk_snap(y0, o.y, px))
let hi = vec2(mk_snap(x0 + sz, o.x, px), mk_snap(y0 + sz, o.y, px))
let c = (lo + hi) * 0.5
let on = max(self.active, self.mixed) * (1.0 - self.disabled)
let held = max(self.down, self.pressed)
let k = 3.5
let bw = 2.0
var top = mix(vec3(0.906, 0.914, 0.925), vec3(0.180, 0.500, 1.0), on)
var bot = mix(vec3(0.812, 0.827, 0.855), vec3(0.030, 0.270, 0.800), on)
top = mix(top, mix(vec3(0.906, 0.914, 0.925), vec3(0.812, 0.827, 0.855), 0.5), self.disabled * 0.6)
var res = mk_cap(p, lo, hi, k, px, self.hover * (1.0 - self.disabled), held, self.focus, 1.0 - 0.5 * self.disabled, top, bot, vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), vec3(0.290, 0.322, 0.373), mix(vec3(0.604, 0.631, 0.678), vec3(0.010, 0.120, 0.400), on), bw, 0.0)
let d = mk_oct(p, c, (hi - lo) * 0.5, k)
// Lit, the glass glows a little onto the hull round it.
res = Finish.over(res, vec4(0.150, 0.470, 1.0, 1.0) * Finish.glow(d, 1.6) * 0.40 * 0.750 * (1.0 - Finish.cover(d, px)) * on)
res = Finish.over(res, vec4(0.94, 0.25, 0.22, 1.0) * Finish.band(d, 0.0, px, px) * self.error)
// The mark: a check 9 points across in a 1.9 point stroke, white; mixed
// is a white dash across the middle.
let cm = c + vec2(0.0, px * 0.5 * held)
let kk = sz / 14.0
let dm = min(mk_seg(p, cm + vec2(-3.5, 0.2) * kk, cm + vec2(-1.1, 2.6) * kk), mk_seg(p, cm + vec2(-1.1, 2.6) * kk, cm + vec2(3.6, -2.7) * kk))
let dd = mk_seg(p, vec2(c.x - 3.4 * kk, c.y), vec2(c.x + 3.4 * kk, c.y))
let dk = mix(dm - 0.95, dd - 1.0, self.mixed)
let ink = mix(vec3(1.0, 1.0, 1.0), vec3(0.933, 0.945, 0.961), self.disabled)
res = Finish.over(res, vec4(0.0, 0.05, 0.25, 1.0) * Finish.cover(dk - px, px) * 0.35 * on)
return Finish.over(res, vec4(ink, 1.0) * Finish.cover(dk, px) * max(self.active, self.mixed))
}
// THE TOGGLE: a 36 x 19 armour housing, a slot cut along it, and an
// octagonal steel knob riding the slot. On, the slot is a blue tube lit from
// within and the knob stands at its right; off, the slot is dark and the
// knob at its left.
mod.widgets.ToggleFlat.draw_bg.size = uniform(19.0)
mod.widgets.ToggleFlat.draw_bg.pill_aspect = uniform(1.9)
mod.widgets.ToggleFlat.label_walk.margin.left = 36.0
// A steel knob: an octagon `hs` half wide at `c`, its eight facets lit by
// the way they face, a flat top a shade lighter, a dark outline; it throws a
// short shadow. Premultiplied over `dst`.
let mk_knob8 = fn(dst: vec4, p: vec2, c: vec2, hs: float, px: float, hover: float, down: float) -> vec4 {
let k = hs * 0.586
let d = mk_oct(p, c, vec2(hs, hs), k)
let sh = mk_oct_drop(p, c, vec2(hs, hs), k, vec2(0.4, 1.0), 0.9) * 0.55 * 0.420 * (1.0 - down)
var res = Finish.over(dst, vec4(vec3(0.102, 0.133, 0.196) * sh, sh))
let n = mk_cham_n(p, c, vec2(hs, hs), k)
let l = mk_lit(n)
let fw = max(hs * 0.32, 1.5)
let facet = Finish.band(d, px, fw, px)
var col = mix(vec3(0.804, 0.820, 0.847), vec3(0.984, 0.988, 0.992), 0.55 + 0.25 * hover)
let t = clamp((p.y - (c.y - hs)) / max(hs * 2.0, 1.0), 0.0, 1.0)
col = mix(col, vec3(0.804, 0.820, 0.847), t * 0.5)
col = mix(col, mix(vec3(0.541, 0.569, 0.616), vec3(0.984, 0.988, 0.992), smoothstep(-0.8, 0.8, l)), facet)
col = mix(col, vec3(0.243, 0.275, 0.322), Finish.band(d, 0.0, px, px))
let a = Finish.cover(d, px)
return Finish.over(res, vec4(col * a, a))
}
// A tube of coloured glass lit from within, laid in a slot: a pale line
// along its top, the colour, a deeper foot; it glows a little past its end
// and onto the slot round it. Premultiplied over `dst`.
let mk_tube = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, r: float, px: float, col: vec3, lit: float) -> vec4 {
let c = (lo + hi) * 0.5
let h = max((hi - lo) * 0.5, vec2(0.01, 0.01))
let d = Material.sd_box(p, c, h, min(r, min(h.x, h.y)))
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
var ink = mix(col * 1.05, col * 0.62, smoothstep(0.35, 1.0, t))
let hot = exp(-((t - 0.28) * (t - 0.28)) / 0.012)
ink = mix(ink, mix(col, vec3(1.0, 1.0, 1.0), 0.75), hot * 0.85)
ink = min(ink, vec3(1.0, 1.0, 1.0))
var res = Finish.over(dst, vec4(col, 1.0) * Finish.glow(d, 1.4) * 0.45 * 0.750 * (1.0 - Finish.cover(d, px)) * lit * step(0.01, hi.x - lo.x))
return Finish.over(res, vec4(mix(col * 0.3, ink, lit), 1.0) * Finish.cover(d, px))
}
mod.widgets.ToggleFlat.draw_bg.pixel = fn() {
let m = 6.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let tw = self.size * self.pill_aspect
let th = self.size
let x0 = (self.rect_size.x - tw) * self.mark_at_end
let y0 = self.rect_size.y * 0.5 - th * 0.5
let lo = vec2(mk_snap(x0, o.x, px), mk_snap(y0, o.y, px))
let hi = vec2(mk_snap(x0 + tw, o.x, px), mk_snap(y0 + th, o.y, px))
let on = self.active * (1.0 - self.disabled)
// The housing: a low cap.
var res = mk_cap(p, lo, hi, 4.0, px, 0.0, 0.0, self.focus, 0.5, vec3(0.906, 0.914, 0.925), vec3(0.812, 0.827, 0.855), vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), vec3(0.290, 0.322, 0.373), vec3(0.604, 0.631, 0.678), 1.5, 0.0)
// The slot along it, three points in.
let ins = 3.5
let slo = vec2(lo.x + ins, lo.y + ins)
let shi = vec2(hi.x - ins, hi.y - ins)
let sr = (shi.y - slo.y) * 0.5
res = Finish.over(res, mk_slot(p, slo, shi, sr, px, vec3(0.361, 0.392, 0.435), vec3(0.490, 0.522, 0.565)))
// The knob travels the slot.
let kt = mix(self.active, self.drag_pos, self.drag)
let hs = sr + 1.3
let kx = mix(slo.x + sr, shi.x - sr, kt)
// On, the tube fills the slot from its left end to under the knob.
res = mk_tube(res, p, slo + vec2(px, px), vec2(max(kx, slo.x + px), shi.y - px), sr - px, px, vec3(0.150, 0.470, 1.0), on)
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, (slo.y + shi.y) * 0.5)
res = mk_knob8(res, p, kc, hs, px, self.hover * (1.0 - self.disabled), max(self.pressed, self.down))
// Disabled: the whole switch dims toward the hull.
return res * mix(1.0, 0.55, self.disabled)
}
// THE RADIO: a ring of turned steel set in the hull round a dark well.
// Chosen, the well holds a blue lamp lit from within, its middle burning
// toward white, its light on the steel round it.
mod.widgets.RadioButtonFlat.draw_bg.vertex = fn() {
let m = 6.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RadioButtonFlat.clip_x = false
mod.widgets.RadioButtonFlat.clip_y = false
mod.widgets.RadioButtonFlat.draw_bg.size = uniform(17.0)
mod.widgets.RadioButtonFlat.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
mod.widgets.RadioButtonFlat.label_walk.margin.left = 17.0
mod.widgets.RadioButtonFlat.draw_bg.pixel = fn() {
let m = 6.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let sz = self.size
let y0 = self.rect_size.y * 0.5 - sz * 0.5
let c = vec2(Finish.snap(o.x + sz * 0.5, px * 0.5) - o.x, Finish.snap(o.y + y0 + sz * 0.5, px * 0.5) - o.y)
let on = self.active * (1.0 - self.disabled)
let R = sz * 0.5
let q = p - c
let rl = length(q)
let nk = q / max(rl, 0.001)
let held = self.down
// What it throws.
let sh = Finish.drop(p, c + vec2(0.4, 1.2), vec2(R, R), R, vec2(0.0, 0.0), 1.2) * 0.6 * 0.420 * (1.0 - held)
var res = vec4(vec3(0.102, 0.133, 0.196) * sh, sh)
// The steel ring: lit where it faces the light, a dark outline both
// sides; held, its light turns over.
let ri = R - 3.4
var l = mk_lit(nk)
l = mix(l, 0.0 - l, held)
var ring = mix(vec3(0.541, 0.569, 0.616), vec3(0.984, 0.988, 0.992), smoothstep(-0.8, 0.8, l))
let u = clamp((rl - ri) / max(R - ri, 0.5), 0.0, 1.0)
ring = mix(ring, mix(vec3(0.804, 0.820, 0.847), vec3(0.984, 0.988, 0.992), 0.3 + 0.2 * self.hover), 1.0 - abs(u * 2.0 - 1.0) * 0.6)
ring = mix(ring, vec3(0.243, 0.275, 0.322), Finish.band(rl - R, 0.0, px, px))
// The well.
let t = clamp((q.y + ri) / max(ri * 2.0, 1.0), 0.0, 1.0)
var well = mix(vec3(0.361, 0.392, 0.435), vec3(0.490, 0.522, 0.565), t)
// The lamp: blue glass from the middle out, a white-hot core.
let lr = ri - 0.8
let lt = clamp(rl / max(lr, 0.5), 0.0, 1.0)
var lamp = mix(vec3(0.85, 0.93, 1.0), vec3(0.200, 0.540, 1.0), smoothstep(0.0, 0.7, lt))
lamp = mix(lamp, vec3(0.040, 0.300, 0.850), smoothstep(0.7, 1.0, lt))
let sp = q - vec2(-0.3 * lr, -0.45 * lr)
lamp = mix(lamp, vec3(1.0, 1.0, 1.0), exp(-dot(sp, sp) / max(0.08 * lr * lr, 0.01)) * 0.6)
well = mix(well, lamp, on * Finish.cover(rl - lr, px))
well = mix(well, vec3(0.243, 0.275, 0.322), Finish.band(rl - ri, 0.0, px, px))
var col = mix(ring, well, Finish.cover(rl - ri, px))
col = mix(col, mix(col, vec3(0.545, 0.576, 0.631), 0.5), self.disabled)
let a = Finish.cover(rl - R, px)
res = Finish.over(res, vec4(col * a, a))
// Lit, a breath of blue on the hull round it.
res = Finish.over(res, vec4(0.150, 0.470, 1.0, 1.0) * Finish.glow(rl - R, 1.4) * 0.35 * 0.750 * (1.0 - a) * on)
let fr = Finish.ring_out(rl - R, 0.0, px * 1.5, px) * 0.9 + Finish.glow(rl - R - px * 1.5, 1.0) * 0.35 * (1.0 - a)
return Finish.over(res, vec4(0.200, 0.520, 1.0, 1.0) * clamp(fr, 0.0, 1.0) * self.focus * (1.0 - self.disabled))
}
// The radio tab: an armour cap; chosen, it is pushed in and lit blue from
// within, its word white.
mod.widgets.RadioButtonTabFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RadioButtonTabFlat.clip_x = false
mod.widgets.RadioButtonTabFlat.clip_y = false
mod.widgets.RadioButtonTabFlat.padding = mod.turtle.Inset{left: 0.0 right: 10.0 top: 4.0 bottom: 4.0}
mod.widgets.RadioButtonTabFlat.min_height = 26.0
mod.widgets.RadioButtonTabFlat.draw_bg.pixel = fn() {
let m = 12.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let sel = self.active
let live = 1.0 - self.disabled
let k = clamp((hi.y - lo.y) * 0.2, 3.0, 5.0)
let plain = mk_cap(p, lo, hi, k, px, self.hover * live, self.down, self.focus, 1.0, mix(vec3(0.906, 0.914, 0.925), vec3(0.545, 0.576, 0.631), self.disabled * 0.5), mix(vec3(0.812, 0.827, 0.855), vec3(0.431, 0.463, 0.522), self.disabled * 0.5), vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), vec3(0.290, 0.322, 0.373), vec3(0.604, 0.631, 0.678), 2.0, 0.0)
let blue = mk_blue(p, lo, hi, k, px, 2.0, self.hover, 1.0, live)
return mix(plain, blue, sel)
}
mod.widgets.RadioButtonTabFlat.draw_text.color = #141b2b
mod.widgets.RadioButtonTabFlat.draw_text.color_hover = #141b2b
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #ffffff
mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #79818f
// ---------------------------------------------------------------- sliders
// A slider's thumb: the octagonal steel knob, a small arrow of the value's
// colour on its face pointing along the travel, a notch cut in its top and
// foot facets. Held or focused, the arrow is lit. Premultiplied over `dst`.
let mk_thumb = fn(dst: vec4, p: vec2, c: vec2, hs: float, px: float, hover: float, held: float, col: vec3, disabled: float) -> vec4 {
var res = mk_knob8(dst, p, c, hs, px, hover * (1.0 - disabled), 0.0)
// The arrow: a triangle 4 points tall, its point at the middle.
let q = p - c - vec2(-hs * 0.18, 0.0)
let tri = max(abs(q.y) * 1.25 + q.x - 0.4, 0.0 - q.x - hs * 0.42)
let ink = mix(col, mix(col, vec3(1.0, 1.0, 1.0), 0.5), held) * (1.0 - 0.6 * disabled)
res = Finish.over(res, vec4(ink, 1.0) * Finish.cover(tri, px) * 0.95)
// The notches.
let nt = Finish.cover(abs(p.x - c.x) - px * 0.5, px) * step(hs * 0.62, abs(p.y - c.y)) * step(abs(p.y - c.y), hs - px)
return Finish.over(res, vec4(vec3(0.243, 0.275, 0.322), 1.0) * nt * 0.6)
}
// A round thumb: a turned steel disc, its rim lit where it faces the light
// and shaded at its foot, the dome of its face lighter at the top with the
// fine rings of the lathe, a dot of the value's colour in its middle (lit
// while held). It throws a short shadow. Premultiplied over `dst`.
let mk_thumb_round = fn(dst: vec4, p: vec2, c: vec2, r: float, px: float, hover: float, held: float, col: vec3, disabled: float) -> vec4 {
let q = p - c
let rl = length(q)
let nk = q / max(rl, 0.001)
let sh = Finish.drop(p, c + vec2(0.4, 1.0), vec2(r, r), r, vec2(0.0, 0.0), 0.9) * 0.55 * 0.420
var res = Finish.over(dst, vec4(vec3(0.102, 0.133, 0.196) * sh, sh))
let rim = clamp((rl - r * 0.62) / max(r * 0.38, 0.5), 0.0, 1.0)
let l = mk_lit(nk)
var m = mix(vec3(0.984, 0.988, 0.992), vec3(0.804, 0.820, 0.847), smoothstep(-0.6, 1.0, q.y / max(r, 0.5)) * 0.6)
m = m * (1.0 + 0.05 * (mk_noise(vec2(rl * 1.8, 0.4)) - 0.5)) + vec3(0.03, 0.03, 0.03) * hover * (1.0 - disabled)
m = mix(m, mix(vec3(0.541, 0.569, 0.616), vec3(0.984, 0.988, 0.992), smoothstep(-0.9, 0.9, l)), smoothstep(0.0, 0.15, rim))
m = mix(m, vec3(0.243, 0.275, 0.322), Finish.band(rl - r, 0.0, px, px))
let a = Finish.cover(rl - r, px)
res = Finish.over(res, vec4(m * a, a))
let ink = mix(col, mix(col, vec3(1.0, 1.0, 1.0), 0.5), held) * (1.0 - 0.6 * disabled)
return Finish.over(res, vec4(ink, 1.0) * Finish.cover(rl - r * 0.26, px))
}
// The slider's track: the slot, then the value as a lit tube of the
// slider's colour from the start to the thumb.
let mk_track = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, f0: float, f1: float, px: float, col: vec3, disabled: float) -> vec4 {
let r = (hi.y - lo.y) * 0.5
var res = Finish.over(dst, mk_slot(p, lo, hi, r, px, vec3(0.361, 0.392, 0.435), vec3(0.490, 0.522, 0.565)))
let tl = vec2(f0, lo.y + px)
let th = vec2(f1, hi.y - px)
if f1 - f0 > 0.01 {
res = mk_tube(res, p, tl, th, r - px, px, mix(col, vec3(0.5, 0.52, 0.56), disabled * 0.7), 1.0 - disabled * 0.6)
}
return res
}
// The bar slider: a 7 point slot under its label row, the lit tube, and
// the 19 point steel thumb riding it. Its colour is blue.
mod.widgets.SliderFlat.clip_x = false
mod.widgets.SliderFlat.clip_y = false
mod.widgets.SliderFlat.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
let mk_bar_slider = fn(pos: vec2, size: vec2, o: vec2, dpi: float, slide: float, origin: float, arc_origin: float, hover: float, held: float, disabled: float, col: vec3) -> vec4 {
let m = 7.0
let px = mk_px(dpi)
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let hs = 9.0
let cy = mk_snap(20.0 + (size.y - 20.0) * 0.5, o.y, px)
let lo = vec2(mk_snap(0.0, o.x, px), cy - 3.3333)
let hi = vec2(mk_snap(size.x, o.x, px), cy + 3.3333)
let span = size.x - hs * 2.0
let hx = hs + slide * span
let ox = hs + origin * span
let f0 = mix(lo.x + px, min(ox, hx), arc_origin)
let f1 = mix(hx, max(ox, hx), arc_origin)
var res = mk_track(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, f0, f1, px, col, disabled)
return mk_thumb(res, p, vec2(Finish.snap(o.x + hx, px) - o.x, cy), hs, px, hover, held, col, disabled)
}
mod.widgets.SliderFlat.draw_bg.pixel = fn() {
return mk_bar_slider(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.slide_pos, self.origin_pos, self.arc_origin, self.hover, max(self.drag, self.focus), self.disabled, vec3(0.140, 0.470, 1.0))
}
// A slider's value is a text input, and the field row's 26 point minimum
// pushed it half a row below its label: here it is only as tall as its line.
mod.widgets.SliderMinimal.text_input.min_height = 0.0
mod.widgets.SliderMinimal.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.SliderFlat.text_input.min_height = 0.0
mod.widgets.SliderFlat.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.Slider.text_input.min_height = 0.0
mod.widgets.Slider.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.SliderRoundFlat.text_input.min_height = 0.0
mod.widgets.SliderRoundFlat.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.SliderRound.text_input.min_height = 0.0
mod.widgets.SliderRound.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
// A dial's value box stands in the 20 point legend row above the ring: its
// line and two points either side, so it clears the ring.
mod.widgets.RotaryFlat.text_input.min_height = 0.0
mod.widgets.RotaryFlat.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0}
mod.widgets.Rotary.text_input.min_height = 0.0
mod.widgets.Rotary.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0}
mod.widgets.RotaryGradientY.text_input.min_height = 0.0
mod.widgets.RotaryGradientY.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0}
// The minimal slider: only the slot and the lit tube, green; a small steel
// thumb shows under the hand.
mod.widgets.SliderMinimal.clip_x = false
mod.widgets.SliderMinimal.clip_y = false
mod.widgets.SliderMinimal.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SliderMinimal.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let cy = mk_snap(self.offset_y + (self.rect_size.y - self.offset_y) * 0.5, o.y, px)
let lo = vec2(mk_snap(0.0, o.x, px), cy - 2.6667)
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), cy + 2.6667)
let lo2 = min(self.origin_pos, self.slide_pos)
let hi2 = max(self.origin_pos, self.slide_pos)
let f0 = mix(0.0, lo2, self.arc_origin) * self.rect_size.x
let f1 = mix(self.slide_pos, hi2, self.arc_origin) * self.rect_size.x
let col = vec3(0.110, 0.760, 0.470)
var res = mk_track(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, max(f0, lo.x + px), min(f1, hi.x - px), px, col, self.disabled)
let hx = self.slide_pos * self.rect_size.x
let show = max(self.hover, self.drag)
let th = mk_thumb(vec4(0.0, 0.0, 0.0, 0.0), p, vec2(hx, cy), 6.5, px, 0.0, self.drag, col, self.disabled)
return Finish.over(res, th * show)
}
// The round slider: a label column, then the bar slider's slot with the lit
// tube, purple, and the steel thumb. The slot ends short of the right edge,
// so the value the widget writes there stands on the hull.
mod.widgets.SliderRoundFlat.clip_x = false
mod.widgets.SliderRoundFlat.clip_y = false
mod.widgets.SliderRoundFlat.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SliderRoundFlat.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let hs = 9.0
let cy = mk_snap(self.rect_size.y * 0.5, o.y, px)
let x0 = mk_snap(self.label_size, o.x, px)
let lo = vec2(x0, cy - 3.3333)
let hi = vec2(mk_snap(self.rect_size.x - 37.0, o.x, px), cy + 3.3333)
let span = hi.x - lo.x - hs * 2.0
let hx = lo.x + hs + self.slide_pos * span
let col = vec3(0.640, 0.300, 0.950)
var res = mk_track(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, lo.x + px, hx, px, col, self.disabled)
return mk_thumb(res, p, vec2(Finish.snap(o.x + hx, px) - o.x, cy), hs, px, self.hover, max(self.drag, self.focus), col, self.disabled)
}
// THE BAND is the sheet's slider with two thumbs: the same slot, the value
// lit between the thumbs in the suit's yellow, two round steel thumbs. The
// widget's grab geometry is set to the thumbs' own, so what is drawn is
// what is dragged.
mod.widgets.RangeSliderFlat.clip_x = false
mod.widgets.RangeSliderFlat.clip_y = false
mod.widgets.RangeSliderFlat.track_inset = 0.0
mod.widgets.RangeSliderFlat.handle_size = 18.0
mod.widgets.RangeSliderFlat.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RangeSliderFlat.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let hs = 9.0
let cy = mk_snap(self.label_px + (self.rect_size.y - self.label_px) * 0.5, o.y, px)
let lo = vec2(mk_snap(0.0, o.x, px), cy - 3.3333)
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), cy + 3.3333)
let span = self.rect_size.x - hs * 2.0
let h0 = hs + self.start_pos * span
let h1 = hs + self.end_pos * span
let col = vec3(1.0, 0.780, 0.120)
var res = mk_track(vec4(0.0, 0.0, 0.0, 0.0), p, lo, hi, h0, h1, px, col, self.disabled)
res = mk_thumb_round(res, p, vec2(Finish.snap(o.x + h0, px) - o.x, cy), hs, px, self.hot_start, max(self.drag * self.hot_start, self.focus), col, self.disabled)
return mk_thumb_round(res, p, vec2(Finish.snap(o.x + h1, px) - o.x, cy), hs, px, self.hot_end, max(self.drag * self.hot_end, self.focus), col, self.disabled)
}
// THE FADER: a dark slot cut into the hull with a scale of ticks beside it,
// and a steel block riding it, its top cut as a low pyramid: a flat middle
// and four facets falling to its edges, each lit by the way it faces, its
// corners cut at 45 degrees like every plate of the suit. Hovered it
// brightens; held it sinks and its shadow goes; focused, the blue ring.
mod.widgets.SliderFader.cap_size = 30.0
mod.widgets.SliderFader.draw_bg.track_size = uniform(4.0)
mod.widgets.SliderFader.draw_bg.cap_cross = uniform(0.8)
mod.widgets.SliderFader.clip_x = false
mod.widgets.SliderFader.clip_y = false
mod.widgets.SliderFader.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SliderFader.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let sz = self.rect_size
let p = self.pos * (sz + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let v = self.is_vertical
let along = mix(sz.x, sz.y, v)
let across = mix(sz.y, sz.x, v)
let travel = max(along - self.inset_px * 2.0 - self.cap_px, 1.0)
let foot = self.inset_px + self.cap_px * 0.5
// Track space: `a` from the value's zero, `b` across.
let a = mix(p.x, sz.y - p.y, v)
let b = mix(p.y, p.x, v)
let mid = across * 0.5
// The slot, snapped on screen.
let slo = mix(vec2(self.inset_px, mid - 2.0), vec2(mid - 2.0, self.inset_px), v)
let shi = mix(vec2(sz.x - self.inset_px, mid + 2.0), vec2(mid + 2.0, sz.y - self.inset_px), v)
let lo = vec2(mk_snap(slo.x, o.x, px), mk_snap(slo.y, o.y, px))
let hi = vec2(mk_snap(shi.x, o.x, px), mk_snap(shi.y, o.y, px))
var res = mk_slot8(p, lo, hi, 1.5, px, vec3(0.361, 0.392, 0.435), vec3(0.490, 0.522, 0.565))
// The scale: a tick every 6 points on the far side, the ends and the
// middle longer.
let pitch = 6.0
let k = floor((a - foot) / pitch + 0.5)
let ka = foot + k * pitch
let inrange = step(0.0, k) * step(ka, foot + travel + 0.1)
let tk = mid + 6.5
let major = step(abs(k - floor(travel / pitch * 0.5 + 0.5)), 0.1) + step(k, 0.1) + step(abs(ka - (foot + travel)), 0.1)
let tlen = mix(3.0, 5.5, clamp(major, 0.0, 1.0))
let tick = (1.0 - smoothstep(px * 0.4, px * 0.6, abs(a - ka))) * step(tk, b) * step(b, tk + tlen) * inrange
res = Finish.over(res, vec4(vec3(0.165, 0.192, 0.231), 1.0) * tick * 0.85)
// The block, its leading end on the device pixel grid.
let live = 1.0 - self.disabled
let hsz = vec2(self.cap_px * 0.5, min(max(across * self.cap_cross, 8.0), across) * 0.5)
let at_free = foot + self.slide_pos * travel
let lead = Finish.snap(mix(o.x + at_free - hsz.x, o.y + sz.y - at_free - hsz.x, v), px)
let at = mix(lead - o.x + hsz.x, o.y + sz.y - lead - hsz.x, v)
let cc = mix(vec2(at, mid), vec2(mid, sz.y - at), v)
let hh = mix(hsz, hsz.yx, v)
let press = self.drag * live
let kk = min(hh.x, hh.y) * 0.32
let d = mk_oct(p, cc, hh, kk)
let s1 = mk_oct_drop(p, cc, hh, kk, vec2(0.6, 1.8), 1.6) * 0.7 * 0.420 * (1.0 - press) * live
res = Finish.over(res, vec4(vec3(0.102, 0.133, 0.196) * s1, s1))
// The pyramid: a flat middle 0.45 of the block, four facets round it.
let q = p - cc
let inner = hh * vec2(0.45, 0.45)
let fx = (abs(q.x) - inner.x) / max(hh.x - inner.x, 0.5)
let fy = (abs(q.y) - inner.y) / max(hh.y - inner.y, 0.5)
var n = vec2(0.0, 0.0)
if fx > fy && fx > 0.0 {
n = vec2(step(0.0, q.x) * 2.0 - 1.0, 0.0)
}
if fy >= fx && fy > 0.0 {
n = vec2(0.0, step(0.0, q.y) * 2.0 - 1.0)
}
let l = mk_lit(n)
var col = mix(vec3(0.804, 0.820, 0.847), vec3(0.984, 0.988, 0.992), 0.55)
col = mix(col, mix(vec3(0.541, 0.569, 0.616), vec3(0.984, 0.988, 0.992), smoothstep(-0.9, 0.9, l)), step(0.001, abs(n.x) + abs(n.y)))
// The ridge lines where the facets meet the middle and each other.
let ridge = Finish.cover(abs(abs(q.x) - inner.x) - px * 0.5, px) * step(abs(q.y), inner.y) + Finish.cover(abs(abs(q.y) - inner.y) - px * 0.5, px) * step(abs(q.x), inner.x)
let diag = Finish.cover(abs((abs(q.x) - inner.x) / max(hh.x - inner.x, 0.5) - (abs(q.y) - inner.y) / max(hh.y - inner.y, 0.5)) * min(hh.x - inner.x, hh.y - inner.y) - px * 0.5, px) * step(inner.x, abs(q.x)) * step(inner.y, abs(q.y))
col = mix(col, vec3(0.541, 0.569, 0.616), clamp(ridge + diag, 0.0, 1.0) * 0.45)
// A line across the middle, the fader's index.
let idx_d = mix(abs(q.x), abs(q.y), v)
let idx_e = mix(abs(q.y), abs(q.x), v)
let idx = Finish.cover(idx_d - px * 0.5, px) * step(idx_e, mix(inner.y, inner.x, v) * 0.9)
col = mix(col, vec3(0.850, 0.120, 0.140), idx * 0.9)
col = col + vec3(0.04, 0.04, 0.045) * self.hover * (1.0 - press) * live
col = mix(col, col * 0.86, press)
col = mix(col, vec3(0.243, 0.275, 0.322), Finish.band(d, 0.0, px, px))
let fl = Finish.lum(col)
col = mix(col, mix(col, vec3(fl, fl, fl), 0.5) * 0.85, self.disabled)
let ca = Finish.cover(d, px)
res = Finish.over(res, vec4(col * ca, ca))
let ring = Finish.ring_out(d, 0.0, px * 1.5, px) * 0.9 + Finish.glow(d - px * 1.5, 1.0) * 0.35 * (1.0 - ca)
return Finish.over(res, vec4(0.200, 0.520, 1.0, 1.0) * clamp(ring, 0.0, 1.0) * self.focus * live)
}
// ---------------------------------------------------------------- knobs
// A KNOB: machined steel in a dark ring. Out to in: a scale of short ticks
// printed on the hull round it; the ring, charcoal, its top edge catching
// the light, the value lit in it as a blue arc; the cap, a sixteen-sided
// block of steel whose rim is cut in facets, each lit by the way it faces
// (the facets turn with the knob), its flat top lighter, with the fine
// rings of the lathe; the red index line from the middle toward the value,
// a white pin at its root. Angles run from straight down, clockwise, the gap
// at the bottom. `rr` is the knob's whole radius, ticks included.
let mk_knob = fn(p: vec2, c: vec2, rr: float, px: float, slide: float, origin: float, arc_origin: float, gap: float, hover: float, held: float, focus: float, disabled: float) -> vec4 {
let q = p - c
let rl = length(q)
let nk = q / max(rl, 0.001)
let on = 1.0 - disabled
let tl = clamp(rr * 0.14, 2.5, 6.0)
let ring_out = rr - tl - 1.0
let ring_w = clamp(ring_out * 0.2, 2.5, 9.0)
let ring_in = ring_out - ring_w
let rc = ring_in - max(px, ring_out * 0.015)
let one = PI / 180.0
let sweep = 2.0 * PI - gap * one
let start = gap * one * 0.5
let ve = start + sweep * slide
let oe = start + sweep * origin
var ang = atan2(0.0 - q.x, q.y)
if ang < 0.0 {
ang = ang + 2.0 * PI
}
// The ticks: eleven across the sweep, the ends and the middle longer.
let nt = 10.0
let ta = clamp(ang, start, start + sweep)
let ti = floor((ta - start) / sweep * nt + 0.5)
let tang = start + ti / nt * sweep
let tdir = vec2(0.0 - sin(tang), cos(tang))
let perp = abs(q.x * tdir.y - q.y * tdir.x)
let alng = dot(q, tdir)
let major = step(abs(ti - nt * 0.5), 0.1) + step(ti, 0.1) + step(nt - 0.1, ti)
let t0 = ring_out + 1.2 + mix(1.0, 0.0, clamp(major, 0.0, 1.0)) * tl * 0.35
let tick = Finish.cover(perp - mix(px * 0.6, px, clamp(major, 0.0, 1.0)), px) * step(t0, alng) * step(alng, rr) * step(0.0, alng)
var res = vec4(vec3(0.165, 0.192, 0.231), 1.0) * tick * 0.8 * mix(1.0, 0.5, disabled)
// What the knob throws on the hull.
let s1 = Finish.drop(p, c + vec2(0.04 * ring_out, 0.08 * ring_out), vec2(ring_out, ring_out), ring_out, vec2(0.0, 0.0), 0.07 * ring_out + 0.6) * 0.75 * 0.420
res = Finish.over(res, vec4(vec3(0.102, 0.133, 0.196) * s1, s1) * (1.0 - Finish.cover(rl - ring_out, px)))
// The ring.
let dr = rl - ring_out
var ring = mix(vec3(0.165, 0.188, 0.224), vec3(0.416, 0.447, 0.494), smoothstep(0.2, 0.95, 0.0 - nk.y) * 0.6)
let segs = 24.0
let sa = fract((atan2(q.y, q.x) / (2.0 * PI)) * segs + 0.5)
ring = mix(ring, ring * 0.7, Finish.cover(abs(sa - 0.5) * (2.0 * PI * ring_in / segs) - px * 0.5, px) * 0.8)
ring = mix(ring, vec3(0.416, 0.447, 0.494), Finish.band(dr, px, px * 2.0, px) * smoothstep(0.1, 0.9, 0.0 - nk.y))
// The value: a blue arc lit in the ring.
let a0 = mix(start, min(oe, ve), arc_origin)
let a1 = mix(ve, max(oe, ve), arc_origin)
let ac = clamp(ang, a0, a1)
let ap = c + vec2(0.0 - sin(ac), cos(ac)) * (ring_in + ring_w * 0.5)
let da = length(p - ap)
let live = step(0.002, a1 - a0) * on
let aw = max(ring_w * 0.22, 0.9)
let ga = exp(-max(da - aw, 0.0) * max(da - aw, 0.0) / (2.0 * 1.2 * 1.2)) * 0.6 * 0.750
ring = mix(ring, vec3(0.150, 0.470, 1.0), clamp(ga, 0.0, 1.0) * live)
ring = mix(ring, mix(vec3(0.300, 0.620, 1.0), vec3(0.88, 0.95, 1.0), 0.6), Finish.cover(da - aw, px) * live)
ring = mix(ring, vec3(0.243, 0.275, 0.322), Finish.band(dr, 0.0, px, px))
let ra = Finish.cover(dr, px)
res = Finish.over(res, vec4(ring * ra, ra))
// The cap: sixteen sides, turned with the value.
let nside = 16.0
let sector = 2.0 * PI / nside
let rel = ang - ve
let fa = rel - sector * floor(rel / sector + 0.5)
let apo = rc * cos(PI / nside)
let dc = rl * cos(fa) - apo
let fang = ang - fa
let fnr = vec2(0.0 - sin(fang), cos(fang))
// A shadow of the cap on the ring.
let cs = Finish.drop(p, c + vec2(0.3, 0.9), vec2(rc, rc), rc, vec2(0.0, 0.0), 1.0) * 0.5 * (1.0 - Finish.cover(dc, px))
res = Finish.over(res, vec4(0.0, 0.0, 0.0, cs))
let fw = clamp(rc * 0.26, 2.2, 10.0)
let facet = 1.0 - Finish.cover(dc + fw, px)
let l = mk_lit(fnr)
var col = mix(vec3(0.541, 0.569, 0.616), vec3(0.984, 0.988, 0.992), smoothstep(-0.95, 0.85, l))
// The top: lighter, lit toward the upper left, the lathe's rings.
let tf = clamp((q.y + rc) / max(rc * 2.0, 1.0), 0.0, 1.0)
var top = mix(vec3(0.984, 0.988, 0.992), vec3(0.804, 0.820, 0.847), smoothstep(0.1, 1.0, tf) * 0.8)
let rings = mk_noise(vec2(rl * 1.4, 0.37)) - 0.5
top = top * (1.0 + 0.05 * rings) + vec3(0.03, 0.03, 0.03) * hover
col = mix(top, col, facet)
// The edge between the top and the facets, and between facets.
col = mix(col, mix(vec3(0.541, 0.569, 0.616), vec3(0.984, 0.988, 0.992), 0.4), Finish.cover(abs(dc + fw) - px * 0.5, px) * 0.6)
let fe = Finish.cover(abs(fa) * rl - (sector * 0.5) * rl + px * 0.5, px)
col = mix(col, col * 0.85, (1.0 - fe) * facet * 0.6)
col = mix(col, vec3(0.243, 0.275, 0.322), Finish.band(dc, 0.0, px, px))
let kl = Finish.lum(col)
col = mix(col, vec3(kl, kl, kl) * 0.8, disabled)
let ka = Finish.cover(dc, px)
res = Finish.over(res, vec4(col * ka, ka))
// The index: a red line from near the middle toward the value, a
// darker edge, a white pin at its root.
let vd = vec2(0.0 - sin(ve), cos(ve))
let iw = clamp(rc * 0.055, 0.9, 2.2)
let ia = vd * (rc * 0.18)
let ib = vd * (apo - fw - 1.0)
let di = mk_seg(q, ia, ib) - iw
let red = mix(vec3(0.920, 0.120, 0.140), vec3(0.45, 0.40, 0.42), disabled)
var ix = mix(red, mix(red, vec3(1.0, 1.0, 1.0), 0.45), 1.0 - smoothstep(0.0, iw, abs(di + iw)))
res = Finish.over(res, vec4(0.0, 0.0, 0.0, 1.0) * Finish.cover(di - px * 0.8, px) * 0.25 * ka)
res = Finish.over(res, vec4(ix, 1.0) * Finish.cover(di, px))
let pin = Finish.cover(length(q) - max(iw * 1.15, 1.2), px) * step(8.0, rc)
res = Finish.over(res, vec4(vec3(0.984, 0.988, 0.992), 1.0) * pin)
// Focus: the blue ring just outside the ring.
let fr = Finish.ring_out(dr, 0.0, px * 1.5, px) * 0.9 + Finish.glow(dr - px * 1.5, 1.0) * 0.35 * (1.0 - ra)
return Finish.over(res, vec4(0.200, 0.520, 1.0, 1.0) * clamp(fr, 0.0, 1.0) * focus * on)
}
// The quad runs 10 points past the box, room for the knob's shadow.
mod.widgets.RotaryKnob.clip_x = false
mod.widgets.RotaryKnob.clip_y = false
mod.widgets.RotaryKnob.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RotaryKnob.draw_bg.pixel = fn() {
let m = 10.0
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let dia = min(self.rect_size.x, self.rect_size.y)
let c = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x, Finish.snap(self.rect_pos.y + self.rect_size.y - dia * 0.5, px) - self.rect_pos.y)
return mk_knob(p, c, dia * 0.5 + 1.0, px, self.slide_pos, self.origin_pos, self.arc_origin, self.gap, self.hover, self.drag, self.focus, self.disabled)
}
mod.widgets.RotaryFlat.clip_x = false
mod.widgets.RotaryFlat.clip_y = false
mod.widgets.RotaryFlat.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RotaryFlat.draw_bg.pixel = fn() {
let m = 10.0
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let dia = min(self.rect_size.x, self.rect_size.y - 22.0)
let c = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x, Finish.snap(self.rect_pos.y + 22.0 + dia * 0.5, px) - self.rect_pos.y)
return mk_knob(p, c, dia * 0.5 + 1.0, px, self.slide_pos, self.origin_pos, self.arc_origin, self.gap, self.hover, self.drag, self.focus, self.disabled)
}
// The legends over the knobs: bold capitals in the face's own weight.
mod.widgets.RotaryKnob.draw_text.text_style = mod.theme.font_bold{font_size: 8.0}
mod.widgets.RotaryFlat.draw_text.text_style = mod.theme.font_bold{font_size: 8.5}
// The spinner: a small steel knob in a dark ring whose blue arc is lit
// three quarters of the way round. A sheet may not read the pass clock and
// the spinner carries no other, so it stands still.
mod.widgets.SpinnerFlat.size = 22.0
mod.widgets.SpinnerFlat.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let o = self.rect_pos
let c = vec2(Finish.snap(o.x + self.rect_size.x * 0.5, px), Finish.snap(o.y + self.rect_size.y * 0.5, px)) - o
let size = min(self.rect_size.x, self.rect_size.y)
let res = mk_knob(p, c, size * 0.5, px, 0.75, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
return res * self.alpha
}
// ---------------------------------------------------------------- fields
// A text field: navy glass in a steel frame, 26 points tall, the frame's
// corners cut at 45 degrees. A text field clips what it holds, so the whole
// frame stands inside its own box. Hovered, the glass lifts a shade;
// focused, the frame's edge is lit blue and a thin blue line burns along
// the foot of the glass.
let mk_field = fn(p: vec2, size: vec2, o: vec2, px: float, hover: float, focus: float, disabled: float) -> vec4 {
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(size.x - px * 0.49, o.x, px), mk_snap(size.y - px * 0.49, o.y, px))
let ft = mix(vec3(0.137, 0.184, 0.251), vec3(0.227, 0.271, 0.333), disabled)
let fb = mix(vec3(0.184, 0.231, 0.306), vec3(0.263, 0.306, 0.373), disabled)
let fo = focus * (1.0 - disabled)
var res = mk_framed(p, lo, hi, 4.0, px, 2.0, ft, fb, 0.0, hover * (1.0 - disabled))
// Focus: the frame's outline lit blue, a blue line along the foot of
// the glass.
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = mk_oct(p, c, h, 4.0)
res = Finish.over(res, vec4(0.200, 0.520, 1.0, 1.0) * Finish.band(d, 0.0, px * 1.5, px) * fo)
let gy = hi.y - 2.0 - px * 2.0
let fl = Finish.cover(abs(p.y - gy + px * 0.5) - px * 0.5, px) * step(lo.x + 6.0, p.x) * step(p.x, hi.x - 6.0)
let fg = exp(-max(gy - p.y, 0.0) / 1.6) * step(p.y, gy) * step(lo.x + 6.0, p.x) * step(p.x, hi.x - 6.0) * Finish.cover(mk_oct(p, c, h - vec2(2.0 + px, 2.0 + px), mk_oct_k(4.0, 0.0 - (2.0 + px))), px)
res = Finish.over(res, vec4(0.150, 0.470, 1.0, 1.0) * fg * 0.45 * fo)
return Finish.over(res, vec4(0.75, 0.88, 1.0, 1.0) * fl * 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 = mk_px(self.draw_pass.dpi_factor)
return mk_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled)
}
// A well that holds an input and its parts (a form's field, a search
// with its icon) wears the same frame.
mod.widgets.FieldWell.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled)
}
mod.widgets.TextInputFlat.padding = mod.turtle.Inset{left: 10.0 right: 10.0 top: 4.0 bottom: 4.0}
mod.widgets.TextInputFlat.min_height = 26.0
mod.widgets.TextInputFlat.draw_cursor.color = #9cc6ff
mod.widgets.TextInputFlat.draw_text.color = #f0f3f8
mod.widgets.TextInputFlat.draw_text.color_hover = #f0f3f8
mod.widgets.TextInputFlat.draw_text.color_focus = #f0f3f8
mod.widgets.TextInputFlat.draw_text.color_down = #f0f3f8
mod.widgets.TextInputFlat.draw_text.color_empty = #a7b1bf
mod.widgets.TextInputFlat.draw_text.color_disabled = #808b9b
// The number field: the same frame; its stepper two small navy caps, one
// over the other, each with a white arrow.
mod.widgets.NumberField.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled)
}
mod.widgets.NumberField.spin_width = 17.0
mod.widgets.NumberSpin.width = 17.0
mod.widgets.NumberField.input.draw_text.color = #f0f3f8
mod.widgets.NumberField.input.draw_text.color_hover = #f0f3f8
mod.widgets.NumberField.input.draw_text.color_focus = #f0f3f8
// The widget's own filled triangles are hidden; the face draws its own.
mod.widgets.NumberSpin.draw_text.color = #00000000
mod.widgets.NumberSpin.draw_text.color_disabled = #00000000
mod.widgets.NumberSpin.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * self.rect_size
let x0 = mk_snap(0.0, o.x, px) + px
let xr = mk_snap(self.rect_size.x - px * 0.49, o.x, px) - 3.0
let yt = mk_snap(0.0, o.y, px) + 3.0
let yb = mk_snap(self.rect_size.y - px * 0.49, o.y, px) - 3.0
let mid = mk_snap(self.rect_size.y * 0.5, o.y, px)
let live = 1.0 - self.disabled
let hu = max(0.0, self.hot)
let hd = max(0.0, 0.0 - self.hot)
let du = max(0.0, self.down)
let dd = max(0.0, 0.0 - self.down)
var res = mk_cap(p, vec2(x0, yt), vec2(xr, mid - px * 0.5), 1.5, px, hu, du, 0.0, 0.0, vec3(0.271, 0.322, 0.416), vec3(0.169, 0.208, 0.275), vec3(0.553, 0.616, 0.714), vec3(0.071, 0.098, 0.145), vec3(0.071, 0.098, 0.145), vec3(0.071, 0.098, 0.145), 1.0, 0.0)
res = Finish.over(res, mk_cap(p, vec2(x0, mid + px * 0.5), vec2(xr, yb), 1.5, px, hd, dd, 0.0, 0.0, vec3(0.271, 0.322, 0.416), vec3(0.169, 0.208, 0.275), vec3(0.553, 0.616, 0.714), vec3(0.071, 0.098, 0.145), vec3(0.071, 0.098, 0.145), vec3(0.071, 0.098, 0.145), 1.0, 0.0))
// The arrows: small filled triangles, white.
let cx = (x0 + xr) * 0.5
let cu = (yt + mid) * 0.5 + px * 0.5 * du
let cd = (mid + yb) * 0.5 + px * 0.5 * dd
let qu = p - vec2(cx, cu)
let qd = p - vec2(cx, cd)
let tu = max(abs(qu.x) * 1.15 - qu.y * 1.0 - 1.4, qu.y - 1.5)
let td = max(abs(qd.x) * 1.15 + qd.y * 1.0 - 1.4, 0.0 - qd.y - 1.5)
let ink = mix(vec3(0.949, 0.961, 0.976), vec3(0.933, 0.945, 0.961), self.disabled)
res = Finish.over(res, vec4(ink, 1.0) * Finish.cover(min(tu, td), px) * (0.5 + 0.5 * live))
return res
}
// The drop-down: the field's navy glass in its steel frame, a chevron zone
// at its right behind a dark divider, a little lighter, holding a white
// triangle.
mod.widgets.DropDownFlat.padding = mod.turtle.Inset{left: 10.0 right: 30.0 top: 4.0 bottom: 4.0}
mod.widgets.DropDownFlat.clip_x = false
mod.widgets.DropDownFlat.clip_y = false
mod.widgets.DropDownFlat.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
let mk_chevron_zone = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, disabled: float, open: float) -> vec4 {
let bw = 2.0
let dx = hi.x - 20.0
let gt = lo.y + bw + px
let gb = hi.y - bw - px
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let inside = Finish.cover(mk_oct(p, c, h - vec2(bw + px, bw + px), mk_oct_k(4.0, 0.0 - (bw + px))), px)
let zone = step(dx, p.x) * inside
var res = Finish.over(dst, vec4(1.0, 1.0, 1.0, 1.0) * zone * 0.06)
let dark = step(dx - px, p.x) * step(p.x, dx) * step(gt, p.y) * step(p.y, gb)
res = Finish.over(res, vec4(0.0, 0.0, 0.0, 1.0) * dark * 0.7)
// The triangle, 8 x 5 points, pointing down (up while the list is
// open).
let cc = vec2(dx + 10.0, c.y)
let q = p - cc
let qy = mix(q.y, 0.0 - q.y, open)
let tri = max(abs(q.x) * 0.62 + qy * 1.0 - 1.5, 0.0 - qy - 2.4)
let ink = mix(vec3(0.96, 0.97, 0.99), vec3(0.45, 0.48, 0.53), disabled)
return Finish.over(res, vec4(ink, 1.0) * Finish.cover(tri, px))
}
mod.widgets.DropDownFlat.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let live = 1.0 - self.disabled
let ft = mix(vec3(0.137, 0.184, 0.251), vec3(0.227, 0.271, 0.333), self.disabled)
let fb = mix(vec3(0.184, 0.231, 0.306), vec3(0.263, 0.306, 0.373), self.disabled)
var res = mk_framed(p, lo, hi, 4.0, px, 2.0, ft, fb, self.focus * live, max(self.hover, self.down) * live)
// Open, a blue breath along the foot of the frame.
let d = mk_oct(p, (lo + hi) * 0.5, (hi - lo) * 0.5, 4.0)
res = Finish.over(res, vec4(0.150, 0.470, 1.0, 1.0) * Finish.glow(d, 1.6) * 0.5 * (1.0 - Finish.cover(d, px)) * step(hi.y - 2.0, p.y) * self.active * live)
return mk_chevron_zone(res, p, lo, hi, px, self.disabled, self.active)
}
mod.widgets.DropDownFlat.draw_text.color = #f0f3f8
mod.widgets.DropDownFlat.draw_text.color_hover = #f0f3f8
mod.widgets.DropDownFlat.draw_text.color_focus = #f0f3f8
mod.widgets.DropDownFlat.draw_text.color_down = #f0f3f8
mod.widgets.DropDownFlat.draw_text.color_disabled = #808b9b
// The combo box: the same frame and chevron zone.
mod.widgets.ComboBoxFlat.padding = mod.turtle.Inset{left: 10.0 right: 30.0 top: 4.0 bottom: 4.0}
mod.widgets.ComboBoxFlat.input.min_height = 0.0
mod.widgets.ComboBoxFlat.clip_x = false
mod.widgets.ComboBoxFlat.clip_y = false
mod.widgets.ComboBoxFlat.draw_bg.vertex = fn() {
let m = 7.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ComboBoxFlat.draw_bg.pixel = fn() {
let m = 7.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let live = 1.0 - self.disabled
let ft = mix(vec3(0.137, 0.184, 0.251), vec3(0.227, 0.271, 0.333), self.disabled)
let fb = mix(vec3(0.184, 0.231, 0.306), vec3(0.263, 0.306, 0.373), self.disabled)
let res = mk_framed(p, lo, hi, 4.0, px, 2.0, ft, fb, self.focus * live, self.hover * live)
return mk_chevron_zone(res, p, lo, hi, px, self.disabled, 0.0)
}
mod.widgets.ComboBoxFlat.input.draw_text.color = #f0f3f8
mod.widgets.ComboBoxFlat.input.draw_text.color_hover = #f0f3f8
mod.widgets.ComboBoxFlat.input.draw_text.color_focus = #f0f3f8
mod.widgets.ComboBoxFlat.input.draw_text.color_down = #f0f3f8
mod.widgets.ComboBoxFlat.input.draw_text.color_empty = #a7b1bf
// Open lists: a navy panel in a steel frame; rows lift under the hand and
// the chosen row is lit blue.
let mk_menu = fn(p: vec2, size: vec2, px: float) -> vec4 {
return mk_framed(p, vec2(0.0, 0.0), size - vec2(px, px), 4.0, px, 2.0, vec3(0.149, 0.196, 0.290), vec3(0.122, 0.165, 0.239), 0.0, 0.0)
}
let mk_row = fn(p: vec2, size: vec2, px: float, hover: float, active: float, disabled: float) -> vec4 {
// The row is an eight-sided bar a point in from its box, its corners
// cut as every plate's are.
let c = size * 0.5
let d = mk_oct(p, c, size * 0.5 - vec2(1.0, 0.5), 3.0)
let a = Finish.cover(d, px)
var col = vec4(1.0, 1.0, 1.0, 1.0) * 0.06 * hover * (1.0 - disabled) * a
let t = clamp(p.y / max(size.y, 1.0), 0.0, 1.0)
var sel = mix(vec3(0.200, 0.520, 1.0), vec3(0.040, 0.300, 0.850), t)
sel = mix(sel, vec3(0.62, 0.80, 1.0), Finish.band(d, 0.0, px, px) * 0.8)
return mix(col, vec4(sel * a, a), active)
}
mod.widgets.PopupMenuFlat.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_menu(self.pos * self.rect_size, self.rect_size, px)
}
mod.widgets.PopupMenuItem.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_row(self.pos * self.rect_size, self.rect_size, px, self.hover, self.active, self.disabled)
}
mod.widgets.PopupMenuItem.draw_text.color = #eef2f7
mod.widgets.PopupMenuItem.draw_text.color_hover = #eef2f7
mod.widgets.PopupMenuItem.draw_text.color_active = #ffffff
mod.widgets.ComboBoxFlat.draw_popup_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_menu(self.pos * self.rect_size, self.rect_size, px)
}
mod.widgets.ComboBoxFlat.draw_item.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
return mk_row(self.pos * self.rect_size, self.rect_size, px, self.hover, self.active, 0.0)
}
mod.widgets.ComboBoxFlat.draw_item_text.color = #eef2f7
mod.widgets.ComboBoxFlat.draw_item_text.color_active = #ffffff
// ---------------------------------------------------------------- tabs
// The segmented control: the strip one armour cap; the chosen answer is
// pushed in and lit blue from within, cut off from its neighbours by a dark
// seam either side, its word white.
mod.widgets.SegmentedControlFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SegmentedControlFlat.draw_bg.pixel = fn() {
let m = 12.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
return mk_cap(p, lo, hi, 5.0, px, 0.0, 0.0, 0.0, 1.0, vec3(0.906, 0.914, 0.925), vec3(0.812, 0.827, 0.855), vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), vec3(0.290, 0.322, 0.373), vec3(0.604, 0.631, 0.678), 2.0, 1.0)
}
// The strip lays the answers 2 points in from its edge; the quad grows so
// the chosen cap can reach the strip's outline and throw its glow.
mod.widgets.SegmentedControlFlat.draw_pill.vertex = fn() {
let m = 11.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
// On the column (its pill's `radius` set to -1 as the mark) the cap keeps
// to its own segment's height and reaches the strip's side outlines.
mod.widgets.SegmentedControlFlat.draw_pill.pixel = fn() {
let m = 11.0
let v = 1.0 - step(0.0, self.radius)
let hz = 1.0 - v
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(-2.0 * v, o.x, px), mk_snap(-2.0 * hz, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x + 2.0 * v, o.x, px), mk_snap(self.rect_size.y + 2.0 * hz, o.y, px))
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
// The seams that part it from its neighbours: the hull's dark under
// its sides.
let seam = Finish.cover(mk_oct(p, c, h + vec2(px * 1.5 * hz, px * 1.5 * v), mk_oct_k(4.0, px * 1.5)), px)
var res = vec4(vec3(0.290, 0.322, 0.373), 1.0) * seam
return Finish.over(res, mk_blue(p, lo, hi, 4.0, px, 2.0, 0.0, 1.0, 1.0))
}
mod.widgets.SegmentedControlFlat.draw_text.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.SegmentedControlFlat.draw_text_selected.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.SegmentedControlFlat.draw_text.color = #141b2b
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #ffffff
// The chosen word takes its ink from the control's role palette: white on
// the lit blue.
mod.widgets.SegmentedControlFlat.palette.on_neutral_container = #ffffff
mod.widgets.SegmentedControl.palette.on_neutral_container = #ffffff
mod.widgets.SegmentedControlVertical.draw_pill.radius = -1.0
// Tabs: each tab its own armour cap standing beside the next. The tab in
// use is pushed in, a blue light strip burning along the foot of its face.
mod.widgets.Tabs.height = 28.0
mod.widgets.Tabs.clip_x = false
mod.widgets.Tabs.clip_y = false
let mk_tab = fn(pos: vec2, size: vec2, o: vec2, dpi: float, hover: float, active: float) -> vec4 {
let m = 12.0
let px = mk_px(dpi)
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(size.x, o.x, px) - 3.0, mk_snap(size.y, o.y, px))
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
var res = mk_cap(p, lo, hi, 5.0, px, hover, active, 0.0, 1.0, vec3(0.906, 0.914, 0.925), vec3(0.812, 0.827, 0.855), vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), vec3(0.290, 0.322, 0.373), vec3(0.604, 0.631, 0.678), 2.0, 0.0)
let d = mk_oct(p, c, h, 5.0)
let clip = Finish.cover(d + 4.0, px)
let cy = mk_snap(hi.y - 4.5, o.y, px)
return mix(res, mk_strip(res, p, lo.x + 23.0, cy, 9.0, px, vec3(0.180, 0.520, 1.0), 1.0, clip), active)
}
mod.widgets.Tabs.draw_tab.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Tabs.draw_tab_hover.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Tabs.draw_tab_active.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Tabs.draw_bg.pixel = fn() {
return vec4(0.0, 0.0, 0.0, 0.0)
}
mod.widgets.Tabs.draw_tab.pixel = fn() {
return mk_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 0.0, 0.0)
}
mod.widgets.Tabs.draw_tab_hover.pixel = fn() {
return mk_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 1.0, 0.0)
}
mod.widgets.Tabs.draw_tab_active.pixel = fn() {
return mk_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 0.0, 1.0)
}
mod.widgets.Tabs.draw_sep.pixel = fn() {
return vec4(0.0, 0.0, 0.0, 0.0)
}
mod.widgets.Tabs.draw_mark.color = #141b2b
mod.widgets.Tabs.draw_text.color = #141b2b
mod.widgets.Tabs.draw_text_active.color = #141b2b
mod.widgets.Tabs.draw_text.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.Tabs.draw_text_active.text_style = mod.theme.font_bold{font_size: 8.5}
// ---------------------------------------------------------------- lamps
// LAMPS: an LED, as the hardware in the pictures has them. The emitter is
// the light: at full current its middle burns toward white and its rim is
// its most saturated colour; at lower current the middle is no longer
// white, the colour deeper, the glow smaller; out, it is dark tinted glass,
// its colour barely there. Round it a bezel: a raised ring of steel lit
// from the upper left, or a socket cut into the plate, its upper wall in
// shadow and a catch light on its lower rim. Over the lens one crisp
// highlight of a dome. Its light is added, never painted: a tight hot halo
// and a short bloom in its own hue, screened over whatever is there and
// gone in about two radii; on a pale ground, where light can barely
// brighten, its hue is also laid round it as a tint that keeps the
// ground's luminance, so it never darkens or greys its ground.
let mk_led_add = fn(res: vec4, light: vec3, ground: vec3) -> vec4 {
let est = res.rgb + ground * (1.0 - res.a)
let add = (vec3(1.0, 1.0, 1.0) - exp(-light)) * max(vec3(1.0, 1.0, 1.0) - est, vec3(0.0, 0.0, 0.0))
return vec4(res.rgb + add, res.a)
}
// An LED's colour at full strength: its brightest channel at one.
let mk_led_full = fn(ink: vec3) -> vec3 {
return ink / max(max(ink.x, max(ink.y, ink.z)), 0.01)
}
// The same colour at the emitter's own chroma: the hue kept and its
// saturation raised by `vivid`.
let mk_led_hue = fn(ink: vec3, vivid: float) -> vec3 {
let f = mk_led_full(ink)
let mn = min(f.x, min(f.y, f.z))
let ch = 1.0 - mn
let pure = (f - vec3(mn, mn, mn)) / max(ch, 0.001)
return mix(f, pure, clamp(vivid, 0.0, 1.0) * 0.72 * smoothstep(0.03, 0.12, ch))
}
// The lamp: its lens a rounded box `hb` either side of `c` (round when the
// two are equal), `lit` its current 0..1, `ink` its colour and `vivid` how
// far its chroma is raised, `ground` what shows through where nothing is
// drawn, `room` how far its light may reach, `bez` and `raised` its bezel
// (0: a socket cut in what is under it; 1: a raised ring of `bez`; 2: the
// same ring polished steel). Premultiplied over `dst`.
let mk_led_lamp = fn(dst: vec4, p: vec2, c: vec2, hb: vec2, lit: float, ink: vec3, vivid: float, px: float, ground: vec3, room: float, bez: vec3, raised: float) -> vec4 {
let rt = max(min(hb.x, hb.y), 0.4)
let d = Material.sd_box(p, c, hb, rt)
let bw = clamp(0.3 * rt, 0.7, 2.4)
let ds = d - bw
let L = clamp(lit, 0.0, 1.0)
let b = mk_led_full(ink)
let hue = mk_led_hue(ink, vivid)
let pw = L * sqrt(L)
let white = vec3(1.0, 1.0, 1.0)
let lum = vec3(0.2126, 0.7152, 0.0722)
let pale = smoothstep(0.35, 0.75, dot(ground, lum))
var res = dst
let x = max(ds, 0.0)
let reach = max(room, 1.0)
let win = 1.0 - smoothstep(0.55 * reach, reach, x)
let ex = max(hb.x - hb.y, 0.0)
let ey = max(hb.y - hb.x, 0.0)
let qx = p.x - c.x
let qy = p.y - c.y
let q = vec2(qx - clamp(qx, 0.0 - ex, ex), qy - clamp(qy, 0.0 - ey, ey)) / rt
let t = clamp(length(q), 0.0, 1.0)
let nrm = q / max(length(q), 0.001)
// What the bead and its bezel throw on the ground: a soft shadow, down.
let shd = Finish.drop(p, c, hb + vec2(bw, bw), rt + bw, vec2(0.0, 0.22 * rt + 0.3), 0.32 * rt + px) * mix(0.18, 0.34, min(raised, 1.0)) * (1.0 - 0.6 * pw)
res = Finish.over(res, vec4(ground * 0.4 * shd, shd))
if L > 0.001 && pale > 0.001 {
let h = clamp(0.42 * pw * pow(2.0, 0.0 - x / max(0.4 * reach, 0.5)) * win, 0.0, 0.45) * pale
let est = res.rgb + ground * (1.0 - res.a)
let w = clamp(dot(hue, lum) / max(dot(est, lum), 0.001), 0.0, 1.0)
res = Finish.over(res, vec4(hue * h * w, h * w))
}
let a_s = Finish.cover(ds, px)
let u = clamp(d / max(bw, 0.01), 0.0, 1.0)
let facing = dot(nrm * (u - 0.5) * 2.0, vec2(-0.45, -0.89))
let metal = clamp(raised - 1.0, 0.0, 1.0)
var rc = bez * (0.94 + mix(0.12, 0.55, metal) * facing)
rc = mix(rc, min(bez + vec3(0.30, 0.30, 0.30), white), smoothstep(mix(0.35, 0.55, metal), 0.9, facing) * mix(0.6, 0.85, metal))
rc = mix(rc, rc * 0.8, Finish.band(ds, 0.0, max(px, 0.45), px) * smoothstep(0.0, 0.8, nrm.y) * 0.8)
let under = res.rgb / max(res.a, 0.001) * step(0.001, res.a) + ground * (1.0 - step(0.001, res.a))
let gy = clamp(nrm.y * t, -1.0, 1.0)
var sc = under * mix(0.55, 0.92, smoothstep(-0.9, 0.9, gy))
sc = mix(sc, min(under * 1.06 + vec3(0.28, 0.28, 0.28), white), Finish.band(ds, 0.0, max(px, 0.45), px) * smoothstep(0.2, 0.95, nrm.y) * 0.85)
sc = min(mix(sc, rc, min(raised, 1.0)), white)
res = Finish.over(res, vec4(sc * a_s, a_s))
if L > 0.001 {
let xl = max(d, 0.0)
let halo = exp(0.0 - xl / max(0.30 * rt, 0.4)) * 1.5 + exp(0.0 - xl / max(0.65 * rt, 0.7)) * 0.32
let hwin = 1.0 - smoothstep(1.1 * rt, 2.0 * rt + 1.0, xl)
res = mk_led_add(res, mix(hue, white, 0.15 * pw) * halo * pw * hwin * win, ground)
}
// THE BEAD: a glossy dome of coloured glass.
let top = smoothstep(-1.0, 0.6, q.y)
let pool = exp(0.0 - (q.y - 0.55) * (q.y - 0.55) / 0.09 - q.x * q.x / 0.30)
var on = mix(hue * mix(0.55, 0.8, L), mix(b, hue, 0.5), top)
on = mix(on, min(mix(b, hue, 0.4) + vec3(0.35, 0.35, 0.35), white), pool * 0.5)
on = mix(on, vec3(1.0, 0.995, 0.985), exp(0.0 - t * t / 0.26) * smoothstep(0.5, 1.0, L) * 0.95)
on = mix(on, hue * (0.82 + 0.18 * L), smoothstep(0.62, 1.0, t))
on = on * (0.42 + 0.58 * L)
// Out: dark tinted glass, its colour barely there, the same dome, a
// catch light along its lower rim.
let offc = mix(vec3(0.085, 0.09, 0.115), b * 0.4, 0.14)
var off = mix(offc * 0.62, offc * 1.08, top)
off = mix(off, min(offc + vec3(0.18, 0.18, 0.18), white), pool * 0.35)
off = off * mix(1.0, 0.75, smoothstep(0.75, 1.0, t))
let catchl = smoothstep(0.62, 0.9, t) * (1.0 - smoothstep(0.9, 1.0, t)) * smoothstep(0.2, 0.9, nrm.y)
off = off + vec3(0.30, 0.30, 0.32) * catchl
var col = mix(off, min(on, white), smoothstep(0.0, 0.35, L))
let e = length(vec2(q.x / 0.62, (q.y + 0.46) / 0.30))
let spec = (1.0 - smoothstep(0.55, 1.0, e)) * mix(1.0, 0.3, clamp((q.y + 0.76) / 0.6, 0.0, 1.0))
col = mix(col, white, spec * mix(0.8, 0.55, L) * smoothstep(0.9, 2.2, rt))
col = mix(col, col * 0.7, Finish.band(d, 0.0, px * 0.6, px) * 0.6 * (1.0 - smoothstep(0.0, 0.25, L)))
col = min(col, white)
let a_l = Finish.cover(d, px)
return Finish.over(res, vec4(col * a_l, a_l))
}
// The Lamp widget and the bar lamp: the lamp above in a socket cut into
// the plate. The whole lamp -- socket, light and shadow -- stays inside its
// own box, a point clear of its edge, so a row of lamps or a lamp beside a
// switch never lays its light on a neighbour. A dot is a third of its box
// tall; a bar is a light pipe as thick as seven tenths of a dot and twice as
// long as it is thick, laid from the start of its box, so a bar in a box
// drawn wider than the row it stands in keeps to that row. The intents in
// the suit's code: accent blue, success green, warning yellow, error red,
// plain white.
mod.widgets.Lamp.halo = 0.3
mod.widgets.Lamp.halo_reach = 1.0
mod.widgets.Lamp.draw_bg.color_accent = uniform(#2a7dff)
mod.widgets.Lamp.draw_bg.color_success = uniform(#22d27a)
mod.widgets.Lamp.draw_bg.color_warning = uniform(#ffc21a)
mod.widgets.Lamp.draw_bg.color_error = uniform(#ff2a2a)
mod.widgets.Lamp.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.Lamp.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let reach = clamp(self.reach, 0.0, 1.0)
let sz = self.rect_size
let t = sz.y / (1.0 + 2.0 * reach)
let bar = step(0.5, self.bar)
// A dot in the middle of its box; a bar from the start of its box.
let th = mix(t * 0.5, t * 0.35, bar)
let hb = vec2(mix(th, th * 2.0, bar), th)
let bw = clamp(0.3 * th, 0.7, 2.4)
let c = vec2(mix(sz.x * 0.5, 2.6 + bw + hb.x, bar), sz.y * 0.5)
// The light's room: what is left of the box round the lens and its
// socket, less a point; a bar keeps two points.
let free = min(sz.x * 0.5, sz.y * 0.5) - th - bw - 1.0
let room = mix(max(free, 1.5), 2.0, bar)
let res = mk_led_lamp(vec4(0.0, 0.0, 0.0, 0.0), p, c, hb, self.lit, self.intent_color().rgb, 1.0 - step(3.5, self.intent), px, vec3(0.788, 0.804, 0.831), room, vec3(0.804, 0.820, 0.847), 0.0)
return res * self.opacity
}
// ---------------------------------------------------------------- feedback
// The colour of a cell of a ladder at `share` of its length: the suit's
// level code, blue at the foot through cyan, green and yellow to orange and
// red at the top.
let mk_ladder_col = fn(share: float) -> vec3 {
var c = vec3(0.090, 0.380, 1.0)
c = mix(c, vec3(0.060, 0.700, 0.900), step(0.14, share))
c = mix(c, vec3(0.120, 0.780, 0.420), step(0.30, share))
c = mix(c, vec3(0.560, 0.860, 0.180), step(0.48, share))
c = mix(c, vec3(1.0, 0.840, 0.120), step(0.62, share))
c = mix(c, vec3(1.0, 0.530, 0.080), step(0.76, share))
c = mix(c, vec3(0.950, 0.150, 0.150), step(0.88, share))
return c
}
// A cell of a ladder: a small block of coloured glass, lit (its top row
// pale, its foot deeper, a glow round it) or out (dark glass, its colour
// barely there). `lo`..`hi` its box. Premultiplied over `dst`.
let mk_cell = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, col: vec3, lit: float, glass: vec3) -> vec4 {
let c = (lo + hi) * 0.5
let h = max((hi - lo) * 0.5, vec2(0.2, 0.2))
let d = mk_oct(p, c, h, min(0.6, min(h.x, h.y)))
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 0.5), 0.0, 1.0)
var on = mix(mix(col, vec3(1.0, 1.0, 1.0), 0.45), col * 0.75, smoothstep(0.0, 1.0, t))
on = mix(on, col, 0.35)
let off = mix(glass, col, 0.16) * mix(1.15, 0.85, t)
var res = Finish.over(dst, vec4(col, 1.0) * Finish.glow(d, 1.0) * 0.30 * 0.750 * (1.0 - Finish.cover(d, px)) * lit)
return Finish.over(res, vec4(mix(off, on, lit), 1.0) * Finish.cover(d, px))
}
// The progress bar: a 9 point slot; the value a row of lit cells of the
// bar's colour, the rest dark glass.
mod.widgets.ProgressBarFlat.height = 9.0
mod.widgets.ProgressBarFlat.draw_bg.color_primary = uniform(#2a7dff)
mod.widgets.ProgressBarFlat.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * self.rect_size
let th = min(max(self.thickness, 7.0), self.rect_size.y)
let y0 = mk_snap((self.rect_size.y - th) * 0.5, o.y, px)
let x0 = mk_snap(self.rect_size.x * self.start, o.x, px)
let x1 = mk_snap(self.rect_size.x * self.end, o.x, px)
let lo = vec2(x0, y0)
let hi = vec2(x1, y0 + mk_snap(th, 0.0, px) - px)
var res = vec4(0.0, 0.0, 0.0, 0.0)
if self.track > 0.5 {
res = mk_slot8(p, lo, hi, 2.0, px, vec3(0.361, 0.392, 0.435), vec3(0.490, 0.522, 0.565))
}
let fill = self.fill_color()
let ins = 1.5
let span = hi.x - lo.x - ins * 2.0
let pitch = 9.0
let a = p.x - lo.x - ins
let k = floor(a / pitch)
let cl = lo.x + ins + k * pitch
let ch = cl + pitch - 1.5
var v0 = 0.0
var v1 = clamp((self.value - self.start) / max(self.end - self.start, 0.001), 0.0, 1.0)
if self.value < 0.0 {
// Still: the sweep would need the pass clock, and a window at rest
// reads no clock, so the unknown is a run of cells lit in the
// middle.
v0 = 0.3
v1 = 0.7
}
let cm = (cl + ch) * 0.5 - lo.x - ins
let lit = step(span * v0, cm) * step(cm, span * v1) * step(0.001, v1 - v0)
let inr = step(0.0, a) * step(cl + pitch * 0.5, hi.x - ins)
let cell = mk_cell(vec4(0.0, 0.0, 0.0, 0.0), p, vec2(cl, lo.y + ins), vec2(min(ch, hi.x - ins), hi.y - ins), px, fill.rgb, lit * fill.a, vec3(0.361, 0.392, 0.435))
res = Finish.over(res, cell * inr)
return res * self.opacity
}
// The level meter: a ladder of cells in a navy slot, coloured by the level
// code; the clip lamp an LED. 8 points thick lying, 14 standing.
mod.widgets.LevelMeter.height = 8.0
mod.widgets.LevelMeterColumn.width = 14.0
mod.widgets.LevelMeterColumn.draw_bg.segment = uniform(8.0)
mod.widgets.LevelMeter.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let sz = self.rect_size
let p = self.pos * sz
let v = self.vertical
let along = mix(sz.x, sz.y, v)
let across = mix(sz.y, sz.x, v)
let a = mix(p.x, sz.y - p.y, v)
let 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(mk_snap(wlo.x, o.x, px), mk_snap(wlo.y, o.y, px))
let hi = vec2(mk_snap(whi.x, o.x, px), mk_snap(whi.y, o.y, px) - px)
var res = mk_slot8(p, lo, hi, 1.5, px, vec3(0.118, 0.161, 0.227), vec3(0.118, 0.161, 0.227))
let pad = mix(1.5, 2.0, v)
let span = max(1.0, bar - pad * 2.0)
let pitch = max(self.segment, 3.0)
let gapw = mix(1.0, 2.0, v)
let k = floor((a - pad) / pitch)
let ca = pad + k * pitch
let share = clamp((ca + pitch * 0.5 - pad) / span, 0.0, 1.0)
let inr = step(0.0, k) * step(ca + pitch - gapw, bar - pad + 0.01)
let col = mk_ladder_col(share)
let on = step(share, self.level) * step(0.001, self.level)
let hold_at = pad + span * self.hold
let hc = step(abs(ca + pitch * 0.5 - hold_at), pitch * 0.5) * step(0.002, self.hold)
let clo = mix(vec2(ca, pad), vec2(pad, sz.y - ca - pitch + gapw), v)
let chi = mix(vec2(ca + pitch - gapw, across - pad - px), vec2(across - pad, sz.y - ca), v)
let cell = mk_cell(vec4(0.0, 0.0, 0.0, 0.0), p, clo, chi, px, col, max(on, hc), vec3(0.118, 0.161, 0.227))
res = Finish.over(res, cell * inr * self.opacity)
if self.lamp > 0.5 {
let lc = mix(vec2(bar + 3.0 + across * 0.5, sz.y * 0.5), vec2(sz.x * 0.5, across * 0.5), v)
let rl = clamp(across * 0.5 - 1.0, 1.2, 3.0)
res = mk_led_lamp(res, p, lc, vec2(rl, rl), step(0.5, self.over), vec3(0.950, 0.150, 0.150), 1.0, px, vec3(0.788, 0.804, 0.831), 3.0, vec3(0.804, 0.820, 0.847), 0.0)
}
return res * self.opacity
}
// ---------------------------------------------------------------- surfaces
// The scroll bar: a slot, the thumb a small steel cap.
mod.widgets.ScrollBar.bar_size = 9.0
mod.widgets.ScrollBar.bar_side_margin = 1.0
mod.widgets.ScrollBar.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let sz = self.rect_size
let p = self.pos * sz
let v = self.is_vertical
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(sz.x, o.x, px), mk_snap(sz.y, o.y, px))
var res = mk_slot8(p, lo, hi, 2.0, px, vec3(0.361, 0.392, 0.435), vec3(0.490, 0.522, 0.565))
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), vec2(1.0, t0), v)
let thi = mix(vec2(t1, sz.y - 1.0), vec2(sz.x - 1.0, t1), v)
let slo = vec2(mk_snap(tlo.x, o.x, px), mk_snap(tlo.y, o.y, px))
let shi = vec2(mk_snap(thi.x, o.x, px), mk_snap(thi.y, o.y, px))
let hv = max(self.hover, self.drag)
res = Finish.over(res, mk_cap(p, slo, shi, 2.0, px, hv, 0.0, 0.0, 0.0, mix(vec3(0.984, 0.988, 0.992), vec3(0.804, 0.820, 0.847), 0.4), vec3(0.804, 0.820, 0.847), vec3(0.984, 0.988, 0.992), vec3(0.541, 0.569, 0.616), vec3(0.243, 0.275, 0.322), vec3(0.541, 0.569, 0.616), 1.0, 0.0))
return res * self.opacity
}
// A RAISED PANEL: a big armour plate standing on the hull, its corners cut
// 10 points at 45 degrees. Its shadow stays within half the gap to its
// neighbours; a bevel three points wide lit from the upper left; the face
// painted, lighter at its top; a screw in each corner.
let mk_panel = fn(p: vec2, size: vec2, o: vec2, px: float, hover: float) -> vec4 {
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(size.x, o.x, px), mk_snap(size.y, o.y, px))
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let k = min(10.0, min(h.x, h.y) * 0.4)
let d = mk_oct(p, c, h, k)
let s1 = mk_oct_drop(p, c, h - vec2(1.0, 0.0), mk_oct_k(k, -0.5), vec2(0.6, 1.6), 1.4) * 0.7
let s2 = mk_oct_drop(p, c, h - vec2(4.0, 2.0), mk_oct_k(k, -3.0), vec2(1.2, 4.0), 3.6) * 0.5
let sha = (1.0 - (1.0 - s1) * (1.0 - s2)) * 0.420
var res = vec4(vec3(0.102, 0.133, 0.196) * sha, sha)
let cap = mk_cap(p, lo, hi, k, px, hover, 0.0, 0.0, 0.0, vec3(0.906, 0.914, 0.925), vec3(0.812, 0.827, 0.855), vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), vec3(0.290, 0.322, 0.373), vec3(0.604, 0.631, 0.678), 3.0, 0.0)
res = Finish.over(res, cap)
// The screws.
let inner = h - vec2(9.0, 9.0)
let sc = c + vec2(step(c.x, p.x) * 2.0 - 1.0, step(c.y, p.y) * 2.0 - 1.0) * inner
let screw = mk_screw(res, p, sc, 2.4, px)
res = mix(res, screw, step(30.0, h.y) * step(40.0, h.x))
// The suit's colour code by its top right corner, in the band above
// where its content begins: a red and a blue stripe slanted forward.
let u = p - vec2(hi.x - 46.0, lo.y + 4.0)
let sl = u.x + u.y * 0.7
let iny = smoothstep(-px, px, u.y) * smoothstep(-px, px, 6.0 - u.y)
let s1 = smoothstep(-px, px, sl) * smoothstep(-px, px, 6.0 - sl) * iny
let s2 = smoothstep(-px, px, sl - 8.0) * smoothstep(-px, px, 14.0 - sl) * iny
let room = step(80.0, hi.x - lo.x) * step(40.0, hi.y - lo.y)
res = Finish.over(res, vec4(0.850, 0.150, 0.170, 1.0) * s1 * room)
res = Finish.over(res, vec4(0.110, 0.420, 0.950, 1.0) * s2 * room)
return res
}
// A SUNKEN WELL: a box pressed into the hull. Its walls in shadow along
// the top and left and lit along the foot and right, a dark line where the
// floor begins, the floor darker than the hull with the surround's shade
// lying across its top.
let mk_well = fn(p: vec2, size: vec2, o: vec2, px: float) -> vec4 {
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(size.x, o.x, px), mk_snap(size.y, o.y, px) - px)
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let k = min(10.0, min(h.x, h.y) * 0.4)
let d = mk_oct(p, c, h, k)
let n = mk_cham_n(p, c, h, k)
let l = mk_lit(n)
let bw = 3.0
var wall = mix(vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), smoothstep(-0.8, 0.75, l))
let u = clamp((0.0 - d) / bw, 0.0, 1.0)
wall = mix(wall, mix(wall, vec3(0.745, 0.761, 0.788), 0.4), u)
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
var flr = mix(vec3(0.745, 0.761, 0.788), vec3(0.804, 0.820, 0.843), t) * 0.93
let fh = h - vec2(bw, bw)
flr = mix(flr, flr * 0.6, mk_oct_inset(p, c, fh, mk_oct_k(k, 0.0 - bw), vec2(1.0, 2.0), 2.0) * 0.8)
let tx = mk_paint(p + o, px, 0.010)
flr = flr + vec3(tx, tx, tx)
let fd = mk_oct(p, c, fh, mk_oct_k(k, 0.0 - bw))
flr = mix(flr, vec3(0.290, 0.322, 0.373), Finish.band(fd, 0.0, px, px) * 0.6)
var col = mix(wall, flr, Finish.cover(fd, px))
col = mix(col, vec3(0.290, 0.322, 0.373), Finish.band(d, 0.0, px, px) * 0.8)
let a = Finish.cover(d, px)
return vec4(col * a, a)
}
mod.widgets.PanelView.clip_x = false
mod.widgets.PanelView.clip_y = false
mod.widgets.PanelView.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 12.0 bottom: 12.0}
mod.widgets.PanelView.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.PanelView.draw_bg.pixel = fn() {
let m = 10.0
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
if self.sunken > 0.5 {
return mk_well(p, self.rect_size, self.rect_pos, px)
}
return mk_panel(p, self.rect_size, self.rect_pos, px, 0.0)
}
// The rounded view keeps the colour its caller gives it, its corners cut
// at 45 degrees, and when it is opaque a device pixel of outline.
mod.widgets.RoundedView.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let lo = vec2(self.border_inset.x, self.border_inset.y)
let hi = self.rect_size - vec2(self.border_inset.z, self.border_inset.w)
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.5, self.border_radius), min(h.x, h.y))
let d = mk_oct(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))
}
var ink = self.border_color
if self.border_size < 0.01 {
ink = vec4(vec3(0.290, 0.322, 0.373), fill.a * 0.6)
}
let a = Finish.cover(d, px)
let band = Finish.band(d, 0.0, max(self.border_size, px), px)
let col = mix(fill.rgb, ink.rgb, band * ink.a)
return vec4(col * a * fill.a, a * fill.a) + vec4(ink.rgb * ink.a, ink.a) * band * a * (1.0 - fill.a)
}
// The card: a raised armour plate of its own; 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: 14.0 right: 14.0 top: 12.0 bottom: 12.0}
mod.widgets.Card.draw_bg.vertex = fn() {
let m = 10.0
return self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Card.draw_bg.pixel = fn() {
let m = 10.0
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
return mk_panel(p, self.rect_size, self.rect_pos, px, self.hover)
}
// LIST ROWS: a list is a navy screen. Each row 22 points of navy glass, a
// hairline between rows; under the hand a row lifts. The chosen row is a
// bar of blue glass lit from within, its corners cut, a chevron at its
// right end.
mod.widgets.ListItem.height = 22.0
mod.widgets.ListItemOne.height = 22.0
mod.widgets.ListItem.padding = mod.turtle.Inset{left: 12.0 right: 8.0 top: 0.0 bottom: 0.0}
mod.widgets.ListItemOne.padding = mod.turtle.Inset{left: 12.0 right: 8.0 top: 0.0 bottom: 0.0}
mod.widgets.ListItem.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * self.rect_size
let live = 1.0 - self.disabled
let y0 = mk_snap(0.0, o.y, px)
let y1 = mk_snap(self.rect_size.y, o.y, px)
let x0 = mk_snap(0.0, o.x, px)
let x1 = mk_snap(self.rect_size.x, o.x, px)
let t = clamp((p.y - y0) / max(y1 - y0, 1.0), 0.0, 1.0)
var col = mix(vec3(0.149, 0.196, 0.290), vec3(0.122, 0.165, 0.239), t) + vec3(0.05, 0.06, 0.08) * (self.hover + 0.5 * self.down) * live
col = mix(col, vec3(0.231, 0.278, 0.357), step(y1 - px, p.y))
var res = vec4(col, 1.0) * step(y0, p.y) * step(p.y, y1)
// The chosen row.
let c = vec2((x0 + x1) * 0.5, (y0 + y1) * 0.5)
let h = vec2((x1 - x0) * 0.5 - 2.0, (y1 - y0) * 0.5 - 1.5)
let d = mk_oct(p, c, h, 3.0)
var sel = mix(vec3(0.200, 0.520, 1.0), vec3(0.040, 0.310, 0.880), t)
sel = sel + vec3(0.20, 0.25, 0.30) * exp(-max(p.y - (c.y - h.y), 0.0) / 2.0) * 0.6
sel = mix(sel, vec3(0.62, 0.80, 1.0), Finish.band(d, 0.0, px, px) * 0.8)
var s = Finish.over(vec4(0.150, 0.470, 1.0, 1.0) * Finish.glow(d, 1.2) * 0.45 * (1.0 - Finish.cover(d, px)), vec4(sel, 1.0) * Finish.cover(d, px))
// The chevron at its right end.
let cc = vec2(x1 - 12.0, c.y)
let ch = min(mk_seg(p, cc + vec2(-1.8, -3.2), cc + vec2(1.4, 0.0)), mk_seg(p, cc + vec2(1.4, 0.0), cc + vec2(-1.8, 3.2))) - 0.7
s = Finish.over(s, vec4(1.0, 1.0, 1.0, 1.0) * Finish.cover(ch, px))
res = mix(res, Finish.over(res, s), self.selected)
return res
}
mod.widgets.ListItem.draw_text.color = #eef2f7
// The rows of a tree stand on the hull, clear but for the faint tint of
// every other row; the open row is the list's lit blue bar.
mod.widgets.FileTreeNode.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let base = mix(self.color_1, self.color_2, self.is_even)
var res = vec4(base.rgb * base.a, base.a)
let c = self.rect_size * 0.5
let h = vec2(self.rect_size.x * 0.5 - 1.0, self.rect_size.y * 0.5 - 1.0)
let d = mk_oct(p, c, h, 3.0)
let t = clamp(p.y / max(self.rect_size.y, 1.0), 0.0, 1.0)
var sel = mix(vec3(0.200, 0.520, 1.0), vec3(0.040, 0.310, 0.880), t)
sel = sel + vec3(0.20, 0.25, 0.30) * exp(-max(p.y - 1.0, 0.0) / 2.0) * 0.6
sel = mix(sel, vec3(0.62, 0.80, 1.0), Finish.band(d, 0.0, px, px) * 0.8)
return mix(res, vec4(sel, 1.0) * Finish.cover(d, px), self.active)
}
mod.widgets.FileTreeNode.draw_text.color_active = #ffffff
mod.widgets.FileTreeNode.draw_icon.color_active = #ffffff
mod.widgets.ListItem.draw_text_2.color = #a7b1bf
// A chip is a small armour cap; its role colour only tints the face.
mod.widgets.ChipFlat.draw_text.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.ChipFlat.draw_text.color = #141b2b
mod.widgets.ChipFlat.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ChipFlat.draw_bg.pixel = fn() {
let m = 8.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px))
let l = Finish.lum(self.color.rgb)
let tint = (self.color.rgb - vec3(l, l, l)) * 0.25 * self.color.a
var res = mk_cap(p, lo, hi, 4.0, px, self.hover, self.down, 0.0, 0.8, vec3(0.906, 0.914, 0.925) + tint, vec3(0.812, 0.827, 0.855) + tint, vec3(1.000, 1.000, 1.000), vec3(0.498, 0.529, 0.580), vec3(0.290, 0.322, 0.373), vec3(0.604, 0.631, 0.678), 2.0, 0.0)
if self.keyed > 0.0 {
let d = mk_oct(p, (lo + hi) * 0.5, (hi - lo) * 0.5, 4.0)
res = Finish.over(res, vec4(0.200, 0.520, 1.0, 1.0) * Finish.band(d, 0.0, px * 1.5, px) * self.keyed)
}
return res
}
// A count is a small navy plate in a steel frame, its figures white.
mod.widgets.BadgePalette.neutral = #45526a
mod.widgets.BadgePalette.on_neutral = #f2f5f9
mod.widgets.BadgeFlat.shape = mod.widgets.BadgeShape.Rounded
mod.widgets.BadgeFlat.radius = 2.0
mod.widgets.BadgeFlat.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * self.rect_size
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x - px * 0.49, o.x, px), mk_snap(self.rect_size.y - px * 0.49, o.y, px))
let fill = self.color.rgb
let res = mk_framed(p, lo, hi, 3.0, px, 1.5, fill * 0.8, fill * 0.65, 0.0, 0.0)
return res * max(self.color.a, 0.0)
}
// ---------------------------------------------------------------- screens
// A SCREEN: navy glass in a steel frame, the readouts' and the meter's one
// housing -- the frame a chamfered plate lit from the upper left, a dark
// line where the glass begins, the glass shaded under its top edge. Its
// figures are the screen ink.
mod.widgets.ScreenView.clip_x = false
mod.widgets.ScreenView.clip_y = false
mod.widgets.ScreenView.padding = mod.turtle.Inset{top: 8.0 right: 10.0 bottom: 8.0 left: 10.0}
mod.widgets.ScreenView.draw_bg.vertex = fn() {
let m = 4.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ScreenView.draw_bg.pixel = fn() {
let m = 4.0
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px) - px)
let face = self.color.rgb
var res = mk_framed(p, lo, hi, 5.0, px, max(self.bezel, 2.5), face * 0.85, face * 1.1, 0.0, 0.0)
if self.scan > 0.0 {
let s = Finish.scan(p.y, px, 3.0 * px, self.scan)
res = vec4(res.rgb * mix(1.0, s, 0.6), res.a)
}
return res * self.color.a
}
// The glass: a faint diagonal band over the face.
mod.widgets.ScreenView.sheen = 0.03
mod.widgets.ScreenView.draw_glass.pixel = fn() {
let p = self.pos * self.rect_size
let px = mk_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) - px) - self.rect_pos
let c = (lo + hi) * 0.5
let ho = (hi - lo) * 0.5
let b = max(self.bezel, 2.5) + px
let hf = max(ho - vec2(b, b), vec2(0.5, 0.5))
let fd = mk_oct(p, c, hf, mk_oct_k(5.0, 0.0 - b))
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(0.85, 0.92, 1.0) * a, a)
}
// THE NEEDLE METER is a screen: the same housing, its scale printed on the
// glass in the screen ink, the red zone a red band along the arc, the
// needle red with a soft red light along it, its root under a steel pin.
mod.widgets.NeedleMeter.clip_x = false
mod.widgets.NeedleMeter.clip_y = false
mod.widgets.NeedleMeter.draw_bg.color_zone = uniform(#ff3a3a)
mod.widgets.NeedleMeter.draw_bg.color_needle = uniform(#ff4a3a)
mod.widgets.NeedleMeter.draw_bg.vertex = fn() {
let m = 4.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.NeedleMeter.draw_bg.pixel = fn() {
let m = 4.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let px = mk_px(self.draw_pass.dpi_factor)
let o = self.rect_pos
let lo = vec2(mk_snap(0.0, o.x, px), mk_snap(0.0, o.y, px))
let hi = vec2(mk_snap(self.rect_size.x, o.x, px), mk_snap(self.rect_size.y, o.y, px) - px)
let face = self.color.rgb
let res = mk_framed(p, lo, hi, 5.0, px, 2.5, face * 0.85, face * 1.1, 0.0, 0.0)
var col = res.rgb / max(res.a, 0.001)
let pv = self.pivot()
let rr = self.scale_radius()
let q = p - pv
let r = length(q)
let ang = atan2(q.x, 0.0 - q.y)
let sweep = self.sweep * 0.017453293
let t = ang / sweep + 0.5
let on_scale = step(-0.0001, t) * step(t, 1.0001)
let ink = self.color_scale.rgb
let arc = Finish.cover(abs(r - rr) - px * 0.5, px) * on_scale
let n = max(floor(self.majors + 0.5), 1.0) * max(floor(self.minors + 0.5), 1.0)
let k = floor(clamp(t, 0.0, 1.0) * n + 0.5)
let ak = (k / n - 0.5) * sweep
let 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.5, 1.0, major), px) * Finish.cover(radial, px) * step(0.0, 0.0 - q.y * cos(ak) + q.x * sin(ak))
col = mix(col, ink, max(arc * 0.5, tick * mix(0.6, 0.95, major)))
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 - 2.5), px)
col = mix(col, self.color_zone.rgb, zone_t * zone_r * 0.9)
}
let na = self.needle_angle(self.value)
let nd = vec2(sin(na), 0.0 - cos(na))
let tail = max(self.pivot_size - px, 0.0)
let along = dot(q, nd)
let across = abs(q.x * nd.y - q.y * nd.x)
let span = smoothstep(-1.0, 1.0, rr + 3.0 - along) * smoothstep(-1.0, 1.0, along + tail)
let halo = 0.35 / (1.0 + (across / 1.2) * (across / 1.2)) * span
col = col + (vec3(1.0, 1.0, 1.0) - exp(0.0 - vec3(0.95, 0.25, 0.20) * halo)) * max(vec3(1.0, 1.0, 1.0) - col, vec3(0.0, 0.0, 0.0))
let needle = Finish.cover(across - 0.6, px) * Finish.cover(along - (rr + 3.0), px) * Finish.cover(0.0 - along - tail, px)
col = mix(col, self.color_needle.rgb, needle)
let pr = max(self.pivot_size, 1.5)
let pq = p - pv
let pl = length(pq)
let pn = pq / max(pl, 0.001)
let pin = mix(vec3(0.541, 0.569, 0.616), vec3(0.984, 0.988, 0.992), smoothstep(-0.8, 0.8, mk_lit(pn) * smoothstep(0.0, pr, pl)))
col = mix(col, mix(pin, vec3(0.243, 0.275, 0.322), Finish.band(pl - pr, 0.0, px, px)), Finish.cover(pl - pr, px))
return vec4(col * res.a, res.a) * self.opacity
}
// ---------------------------------------------------------------- switches
// THE SWITCHES are the sheet's own toggle: the rocker and the slide switch
// take the toggle's housing, slot and steel knob, its size and its label
// gap. The rocker turns on a click; the slide switch follows a drag, as
// the toggle does.
mod.widgets.ToggleRocker.draw_bg.size = uniform(19.0)
mod.widgets.ToggleSlide.draw_bg.size = uniform(19.0)
mod.widgets.ToggleRocker.draw_bg.pill_aspect = uniform(1.9)
mod.widgets.ToggleSlide.draw_bg.pill_aspect = uniform(1.9)
mod.widgets.ToggleRocker.label_walk.margin.left = 36.0
mod.widgets.ToggleSlide.label_walk.margin.left = 36.0
mod.widgets.ToggleRocker.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
mod.widgets.ToggleSlide.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
// ---------------------------------------------------------------- field row
// A ROW OF FIELDS IS ONE HEIGHT. The box metrics are stated ONCE here and
// handed to each field below, so the search box, a number and a drop-down
// beside it stand at one height.
let field_height = 26.0
let field_pad_v = 4.0
let field_radius = 2.0
let field_border = 0.34
let field_room = fn(w) {
w.min_height = field_height
w.padding.top = field_pad_v
w.padding.bottom = field_pad_v
}
let field_face = fn(d) {
d.border_radius = field_radius
d.border_size = field_border
}
// A field that states a frame and lays its own parts out inside it: a number
// with a stepper, a date, a time. The height is all these take.
let field_frame = fn(w) {
w.min_height = field_height
}
field_room(mod.widgets.TextInput)
field_face(mod.widgets.TextInput.draw_bg)
field_room(mod.widgets.ComboBox)
field_face(mod.widgets.ComboBox.draw_bg)
field_room(mod.widgets.DropDown)
field_face(mod.widgets.DropDown.draw_bg)
field_room(mod.widgets.DropDown2)
field_face(mod.widgets.DropDown2.draw_bg)
field_room(mod.widgets.FieldWell)
field_face(mod.widgets.FieldWell.draw_bg)
field_room(mod.widgets.TagField)
field_face(mod.widgets.TagField.draw_bg)
field_frame(mod.widgets.NumberField)
mod.widgets.NumberField.draw_bg.border_radius = field_radius
field_frame(mod.widgets.ValueInput)
field_frame(mod.widgets.DateField)
field_frame(mod.widgets.TimeField)
field_frame(mod.widgets.DatePicker)
field_frame(mod.widgets.DateRangePicker)
// The chrome-less input a well holds must impose no height of its own.
mod.widgets.WellInput.min_height = 0.0
// ---------------------------------------------------------------- rules
// A rule is a panel line: one device pixel of the seam's dark over one of
// the plate's light, across the middle of its room.
mod.widgets.Hr.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let y0 = mk_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(vec3(0.490, 0.522, 0.573), 1.0) * dark * 0.8 + vec4(vec3(0.984, 0.988, 0.992), 1.0) * light * 0.9
}
mod.widgets.Vr.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
let x0 = mk_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(vec3(0.490, 0.522, 0.573), 1.0) * dark * 0.8 + vec4(vec3(0.984, 0.988, 0.992), 1.0) * light * 0.9
}
// ---------------------------------------------------------------- ground
// Plain views stop clipping so a shadow can fall past their edge; scroll
// views keep clipping their content.
mod.widgets.View.clip_x = false
mod.widgets.View.clip_y = false
mod.widgets.Window.pass.clear_color = #bec2c9
// The window's own ground: the armour frame round the window and the calm
// floor it holds (`mk_hull`).
mod.widgets.Window.show_bg = true
mod.widgets.Window.draw_bg.pixel = fn() {
let px = mk_px(self.draw_pass.dpi_factor)
let p = self.pos * self.rect_size
return vec4(mk_hull(p + self.rect_pos, px, self.rect_size), 1.0)
}
// ---------------------------------------------------------------- states
// Every held state is written as a plain value that cuts in at once (the
// library writes some as snap() values, which a state held as a default --
// the states page -- never applies). Live, the cut is the same as the snap.
mod.widgets.ButtonFlat.animator.hover.on.from = {all: Snap}
mod.widgets.ButtonFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
mod.widgets.ButtonFlat.animator.hover.down.from = {all: Snap}
mod.widgets.ButtonFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.CheckBoxFlat.animator.hover.down.from = {all: Snap}
mod.widgets.CheckBoxFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.RadioButtonFlat.animator.hover.down.from = {all: Snap}
mod.widgets.RadioButtonFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.TextInputFlat.animator.hover.on.from = {all: Snap}
mod.widgets.TextInputFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
mod.widgets.TextInputFlat.animator.hover.down.from = {all: Snap}
mod.widgets.TextInputFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.DropDownFlat.animator.hover.on.from = {all: Snap}
mod.widgets.DropDownFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
mod.widgets.DropDownFlat.animator.hover.down.from = {all: Snap}
mod.widgets.DropDownFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
// ---------------------------------------------------------------- ink
// Words are stamped into the armour: a device pixel under every glyph of a
// label lies a second copy of it, white under dark ink (the edge of the
// cut catching the light) and black under light ink (its shadow). Read
// from the glyph one device pixel above, so it costs one more sample and
// nothing else. On a cap the label also sinks with it: held, the glyphs are
// read one device pixel higher in the atlas, so they stand one pixel lower.
// Small labels come from the grayscale atlas, large ones from the
// multi-channel one; both paths carry the line and the nudge. A template
// made from another before this sheet ran keeps its own copy of the
// label's draw, so each cap template takes the literal.
mod.widgets.ButtonFlat.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.Button.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonPrimary.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonSecondary.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonTertiary.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonDanger.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonOutline.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonFlatter.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonXs.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonSm.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonLg.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
mod.widgets.ButtonXl.draw_text.pixel = fn() {
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
if self.texture_index < 0.5 {
let tex_size = self.grayscale_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return self.sample_text_pixel()
}
// The same line under every label that stands on the hull or a cap. A lit
// legend (the segment or tab in use) takes a halo of blue light instead.
mod.widgets.Label.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.CheckBoxFlat.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.RadioButtonFlat.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.SegmentedControlFlat.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.Tabs.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.Tabs.draw_text_active.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.SliderMinimal.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.RadioButtonTabFlat.draw_text.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
let c = self.get_color()
let dark = step(0.5, Finish.lum(c.rgb))
let sc = mix(vec3(1.000, 1.000, 1.000), vec3(0.0, 0.0, 0.0), dark)
let sa = mix(0.550, 0.5, dark) * c.a
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
return main + vec4(sc * s * sa, s * sa) * (1.0 - main.a)
}
return main
}
mod.widgets.SegmentedControlFlat.draw_text_selected.pixel = fn() {
let main = self.sample_text_pixel()
if self.texture_index > 1.5 && self.texture_index < 2.5 {
let dxt = length(dFdx(self.t))
let dyt = length(dFdy(self.t))
let tex_size = self.msdf_texture.size()
let scale = (dxt + dyt) * tex_size.x * 0.5
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
let 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.55 * c.a
return main + vec4(0.10 * a, 0.30 * a, 0.75 * a, a) * (1.0 - main.a)
}
return main
}
true