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