makepad/widgets/sheets/themes/art-nouveau/widgets.splash

2670 lines
135 KiB
Text

use mod.prelude.widgets_internal.*
// Art nouveau. The ground is aged cream parchment, its fibre and a faint warm
// mottle drawn at the device pixel; the window is held in a frame of deep
// teal enamel bands edged with bronze-gold wire, the panes set in it with
// rounded corners, and the only ornament (lilies, leaves, whiplash stems and
// tendrils, all drawn as curves) grows in the frame's corners and the
// corners of the big surfaces, never behind words or controls.
// A control is an ENAMEL face set in a bronze CLOISON: a raised wire of
// bronze-gold round a flat matte enamel (teal for the working colour, ochre
// for the primary action, terracotta for the one that deletes, ivory or
// parchment for the quiet ones), or a WELL of ivory sunk into the parchment
// behind the same wire. Knobs and thumbs are bronze, lamps are cabochons of
// coloured glass in bronze settings, screens deep teal glass with amber
// figures. The light falls from the upper left.
// A ROW OF FIELDS IS ONE HEIGHT. The box metrics are stated once here and
// handed to each field below, so the search box, the number beside it and
// the drop-down after it stand at one height.
let field_height = 28.0
let field_pad_v = mod.theme.mspace_1.top
let field_radius = 8.0
// A hairline, for the few stock fields the faces below do not redraw.
let field_border = 0.5
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 lays its own parts out inside a frame: only the height.
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.TextInputFlat)
field_room(mod.widgets.ComboBox)
field_face(mod.widgets.ComboBox.draw_bg)
field_room(mod.widgets.DropDown)
field_face(mod.widgets.DropDown.draw_bg)
field_room(mod.widgets.DropDown2)
field_face(mod.widgets.DropDown2.draw_bg)
field_room(mod.widgets.FieldWell)
field_face(mod.widgets.FieldWell.draw_bg)
field_room(mod.widgets.TagField)
field_face(mod.widgets.TagField.draw_bg)
field_frame(mod.widgets.NumberField)
mod.widgets.NumberField.draw_bg.border_radius = field_radius
field_frame(mod.widgets.ValueInput)
field_frame(mod.widgets.DateField)
field_frame(mod.widgets.TimeField)
field_frame(mod.widgets.DatePicker)
field_frame(mod.widgets.DateRangePicker)
mod.widgets.WellInput.min_height = 0.0
mod.widgets.Window.pass.clear_color = #d8c29c
// Plain views stop clipping, so a shadow or a lip may fall into the gap
// beside its control. Scroll views keep their own clip.
mod.widgets.View.clip_x = false
mod.widgets.View.clip_y = false
// ---------------------------------------------------------------------------
// SHARED PARTS. Distances in points, negative inside, y down; `px` is one
// device pixel in points.
let g3 = fn(v: float) -> vec3 {
return vec3(v, v, v)
}
// The palette.
let an_ink = fn() -> vec3 {
return vec3(0.165, 0.129, 0.086)
}
let an_ivory = fn() -> vec3 {
return vec3(0.953, 0.910, 0.812)
}
let an_parch_c = fn() -> vec3 {
return vec3(0.847, 0.761, 0.612)
}
let an_well_c = fn() -> vec3 {
return vec3(0.922, 0.875, 0.769)
}
let an_teal = fn() -> vec3 {
return vec3(0.275, 0.388, 0.353)
}
let an_teal_dk = fn() -> vec3 {
return vec3(0.184, 0.282, 0.259)
}
let an_teal_lt = fn() -> vec3 {
return vec3(0.420, 0.545, 0.490)
}
let an_ochre = fn() -> vec3 {
return vec3(0.851, 0.659, 0.341)
}
let an_terra = fn() -> vec3 {
return vec3(0.627, 0.290, 0.212)
}
let an_sage = fn() -> vec3 {
return vec3(0.776, 0.784, 0.678)
}
let an_bronze_c = fn() -> vec3 {
return vec3(0.776, 0.608, 0.318)
}
let an_bronze_dk = fn() -> vec3 {
return vec3(0.380, 0.270, 0.110)
}
let an_gold_hi = fn() -> vec3 {
return vec3(0.988, 0.894, 0.643)
}
let an_line_c = fn() -> vec3 {
return vec3(0.690, 0.510, 0.220)
}
let an_shade = fn() -> vec3 {
return vec3(0.180, 0.120, 0.050)
}
let an_glass_c = fn() -> vec3 {
return vec3(0.129, 0.208, 0.188)
}
// A rounded box, true radius.
let md_rbox = fn(p: vec2, c: vec2, h: vec2, r: float) -> float {
let rr = clamp(r, 0.0, min(h.x, h.y))
let q = abs(p - c) - h + vec2(rr, rr)
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0, 0.0))) - rr
}
// A rounded box with a radius per corner (tl, tr, br, bl).
let md_r4 = fn(p: vec2, c: vec2, h: vec2, r4: vec4) -> float {
let q0 = p - c
let rt = mix(r4.y, r4.x, step(q0.x, 0.0))
let rb = mix(r4.z, r4.w, step(q0.x, 0.0))
return clamp(mix(rb, rt, step(q0.y, 0.0)), 0.0, min(h.x, h.y))
}
let md_rbox4 = fn(p: vec2, c: vec2, h: vec2, r4: vec4) -> float {
return md_rbox(p, c, h, md_r4(p, c, h, r4))
}
// The outward normal of a rounded box at `p`.
let an_nrm = fn(p: vec2, c: vec2, h: vec2, r: float) -> vec2 {
let q0 = p - c
let sx = mix(-1.0, 1.0, step(0.0, q0.x))
let sy = mix(-1.0, 1.0, step(0.0, q0.y))
let rr = clamp(r, 0.0, min(h.x, h.y))
let q = abs(q0) - h + vec2(rr, rr)
var n = vec2(0.0, 1.0)
if q.x > 0.0 && q.y > 0.0 {
n = q / max(length(q), 0.0001)
} else {
if q.x > q.y {
n = vec2(1.0, 0.0)
}
}
return vec2(n.x * sx, n.y * sy)
}
// The corners a button group asks for: a corner written as 0 is a square
// join, any other the family's radius.
let md_corners = fn(tl: float, tr: float, br: float, bl: float, r: float) -> vec4 {
return vec4(mix(r, step(0.5, tl) * r, step(0.0, tl)), mix(r, step(0.5, tr) * r, step(0.0, tr)), mix(r, step(0.5, br) * r, step(0.0, br)), mix(r, step(0.5, bl) * r, step(0.0, bl)))
}
let md_seg = fn(p: vec2, a: vec2, b: vec2) -> float {
let pa = p - a
let ba = b - a
let k = clamp(dot(pa, ba) / max(dot(ba, ba), 0.0001), 0.0, 1.0)
return length(pa - ba * k)
}
let md_grow = fn(rp: vec2, rs: vec2, m: float) -> vec4 {
return vec4(rp.x - m, rp.y - m, rs.x + 2.0 * m, rs.y + 2.0 * m)
}
// THE PARCHMENT at a point `w` (points) in the surface's own space: a faint
// warm mottle in two slow octaves (a few per cent, wide and soft, so no
// stain reads as a blotch), sparse short fibres -- a darker and a paler set,
// each the contour of a stretched noise, so they wander -- and a grain of
// one device pixel.
let an_parch = fn(w: vec2, px: float, base: vec3) -> vec3 {
let m = Finish.noise(w * 0.0065) * 0.6 + Finish.noise(w * 0.019 + vec2(3.1, 7.7)) * 0.4 - 0.5
var col = base * (1.0 + 0.055 * m) + vec3(0.010, 0.002, -0.012) * m
let n1 = Finish.noise(vec2(w.x * 0.055 + w.y * 0.012, w.y * 0.42 - w.x * 0.05))
let k1 = Finish.noise(w * 0.035 + vec2(9.0, 2.0))
let f1 = (1.0 - smoothstep(0.0, 0.016, abs(n1 - 0.5))) * smoothstep(0.6, 0.8, k1)
let n2 = Finish.noise(vec2(w.y * 0.06 - w.x * 0.02, w.x * 0.38 + w.y * 0.07) + vec2(17.0, 5.0))
let k2 = Finish.noise(w * 0.03 + vec2(1.0, 13.0))
let f2 = (1.0 - smoothstep(0.0, 0.014, abs(n2 - 0.5))) * smoothstep(0.64, 0.82, k2)
col = mix(col, col * 0.88, f1 * 0.32)
col = mix(col, min(col * 1.03 + vec3(0.01, 0.01, 0.008), vec3(1.0, 1.0, 1.0)), f2 * 0.22)
// the tooth of the skin: a fine streaky noise across the grain
let tooth = Finish.noise(vec2(w.x * 0.9 + w.y * 0.3, w.y * 0.22 - w.x * 0.08)) - 0.5
col = col * (1.0 + tooth * 0.022)
return col + g3(Finish.grain(w / px, 0.010))
}
// A RAISED BRONZE WIRE `w` points wide lying just inside an edge: `d` the
// edge's distance (negative inside), `n` its outward normal. Lit from the
// upper left: the half of the wire that faces the light catches a warm
// highlight, the other half falls toward dark bronze; a dark hairline at
// both of its edges keeps it crisp on any ground. Premultiplied.
let an_wire = fn(d: float, n: vec2, w: float, px: float, glint: float) -> vec4 {
let u = clamp(-d / max(w, 0.01), 0.0, 1.0)
let k = 1.0 - 2.0 * u
let bulge = sqrt(max(1.0 - k * k, 0.0))
let facing = dot(n * k, vec2(-0.42, -0.91))
var col = mix(an_bronze_dk(), an_bronze_c(), 0.35 + 0.65 * bulge)
col = col * (0.80 + 0.32 * facing)
col = mix(col, an_gold_hi(), smoothstep(0.2, 0.85, facing * 0.8 + 0.3 * bulge) * 0.6 * glint)
let e = Finish.band(d, 0.0, px, px) + Finish.band(d, w - px, w, px)
col = mix(col, an_bronze_dk() * 0.75, clamp(e, 0.0, 1.0) * 0.5)
let a = Finish.band(d, 0.0, w, px)
return vec4(col * a, a)
}
// A fine gold line `lw` wide at `d` = -`at` (inside an edge): the inner line
// of a frame within a frame.
let an_fine = fn(d: float, at: float, lw: float, px: float, amt: float) -> vec4 {
let a = Finish.band(d, at, at + lw, px) * amt
let col = mix(an_line_c(), an_gold_hi(), 0.35)
return vec4(col * a, a)
}
// The soft shadow a raised part throws on the parchment, straight down and
// a little to the right, in warm sepia. Premultiplied.
let an_drop = fn(p: vec2, c: vec2, h: vec2, r: float, amt: float) -> vec4 {
let t = Finish.drop(p, c, h, r, vec2(0.3, 0.9), 0.6) * 0.30 * amt
let s = Finish.drop(p, c, h, r, vec2(0.5, 1.7), 1.5) * 0.14 * amt
let a = t + s * (1.0 - t)
return vec4(an_shade() * a, a)
}
// The tight contact shadow of a part with no room round it for the soft
// one: gone a point and a half under its edge.
let an_contact = fn(p: vec2, c: vec2, h: vec2, r: float, amt: float) -> vec4 {
let a = Finish.drop(p, c, h, r, vec2(0.0, 0.45), 0.3) * 0.28 * amt
return vec4(an_shade() * a, a)
}
// A FLAT MATTE ENAMEL face inside its cloison (`c`, `h`, `r` the enamel's
// own box): a broad soft light across its upper part, a faint unevenness of
// the paint, and the shade the wire throws into it under the top edge.
// Lifted a shade under the pointer; pressed, it sinks: darker, with the
// wire's shade deeper.
let an_enamel = fn(p: vec2, c: vec2, h: vec2, r: float, base: vec3, hover: float, press: float) -> vec3 {
let t = clamp((p.y - (c.y - h.y)) / max(2.0 * h.y, 1.0), 0.0, 1.0)
var col = base * (1.05 - 0.10 * t)
col = mix(col, Finish.lift(col, 0.10), (1.0 - smoothstep(0.05, 0.6, t)) * 0.55 * (1.0 - press))
let mott = (Finish.noise(p * 0.7 + vec2(5.0, 1.0)) - 0.5) * 0.022 + (Finish.noise(p * 0.09 + vec2(2.0, 8.0)) - 0.5) * 0.012
col = col * (1.0 + mott)
col = Finish.lift(col, 0.15 * hover * (1.0 - press))
col = mix(col, col * 0.82, press)
let sh = clamp(Finish.inset(p, c, h, r, vec2(0.0, 0.8 + 1.2 * press), 0.8 + 0.6 * press), 0.0, 1.0)
col = mix(col, an_shade(), sh * (0.24 + 0.26 * press))
return col
}
// Focus: a teal ring of a device pixel and a half, two points outside.
let md_focus = fn(d: float, px: float, focus: float) -> vec4 {
let f = Finish.ring_out(d, 1.5, 1.5 + 1.5 * px, px) * 0.9 * focus
let c = vec3(0.184, 0.435, 0.384)
return vec4(c * f, f)
}
// A SUNK IVORY WELL behind its wire: the fill, the shade falling in under
// the top edge, the bronze wire round it and a light lip on the parchment
// under its foot. `fill` its colour; `wire` the wire's width.
let an_well = fn(p: vec2, c: vec2, h: vec2, r: float, px: float, fill: vec3, shade: float, wire: float, dis: float) -> vec4 {
let d = md_rbox(p, c, h, r)
let n = an_nrm(p, c, h, r)
let hi = h - vec2(wire, wire)
var col = fill
let sh = clamp(Finish.inset(p, c, hi, max(r - wire, 0.0), vec2(0.0, 1.4), 1.2), 0.0, 1.0)
col = mix(col, an_shade(), sh * shade)
let a = Finish.cover(d, px)
var out = vec4(0.0, 0.0, 0.0, 0.0)
let lip = Finish.ring_out(d, 0.0, px, px) * smoothstep(0.0, h.y * 0.8, p.y - c.y) * 0.45
out = Finish.over(out, vec4(vec3(0.98, 0.95, 0.88) * lip, lip))
out = Finish.over(out, vec4(col * a, a))
let wv = an_wire(d, n, wire, px, 1.0 - dis)
return Finish.over(out, wv * mix(1.0, 0.55, dis))
}
// An indicator jewel set in a cap: used where a stock face carries a lamp.
let md_led = fn(p: vec2, lc: vec2, rad: float, px: float, ink: vec3, lit: float, glow: float) -> vec4 {
let q = p - lc
let d = length(q) - rad
let k = clamp(length(q) / max(rad, 0.001), 0.0, 1.0)
let on = mix(Finish.lift(ink, 0.25), ink * 0.85, smoothstep(0.2, 1.0, k))
let off = vec3(0.12, 0.13, 0.11)
var col = mix(off, on, lit)
col = mix(col, an_bronze_c(), Finish.band(d, 0.0, max(px, 0.6), px))
let a = Finish.cover(d, px)
let ha = glow * lit * exp(-max(d, 0.0) / 0.7) * step(0.0, d) * (1.0 - a)
var out = vec4(ink * ha, ha)
return Finish.over(out, vec4(col * a, a))
}
// ---------------------------------------------------------------------------
// THE BOTANICAL ORNAMENT. Every part is a curve, drawn at the device pixel.
// A leaf or a petal: a lens `len` long from its base `b` along the unit
// direction `dir`, `wid` wide either side of its midline at its widest, the
// midline bowed by `bend` points. Returns the signed distance, the side
// across it (-1..1) and the way along it (0..1).
let an_leaf = fn(p: vec2, b: vec2, dir: vec2, len: float, wid: float, bend: float) -> vec4 {
let q = p - b
let u = dot(q, dir)
let t = clamp(u / max(len, 0.01), 0.0, 1.0)
let v = dot(q, vec2(0.0 - dir.y, dir.x)) - bend * 4.0 * t * (1.0 - t)
let hw = wid * 4.0 * t * (1.0 - t) * (1.15 - 0.3 * t)
let slope = wid * 4.0 * (1.0 - 2.0 * t) / max(len, 0.01)
let dv = (abs(v) - hw) / sqrt(1.0 + slope * slope)
let du = max(0.0 - u, u - len)
return vec4(max(dv, du), v / max(hw, 0.3), t, 0.0)
}
// The distance to a quadratic curve from `a` through the pull of `b` to `c`
// (.x), and how far along it the nearest point lies (.y, 0..1).
let an_bez = fn(pos: vec2, A: vec2, B: vec2, C: vec2) -> vec2 {
let a = B - A
let b = A - 2.0 * B + C
let c = a * 2.0
let d = A - pos
let kk = 1.0 / max(dot(b, b), 0.0001)
let kx = kk * dot(a, b)
let ky = kk * (2.0 * dot(a, a) + dot(d, b)) / 3.0
let kz = kk * dot(d, a)
let p = ky - kx * kx
let p3 = p * p * p
let q = kx * (2.0 * kx * kx - 3.0 * ky) + kz
var h = q * q + 4.0 * p3
var res = 0.0
var tt = 0.0
if h >= 0.0 {
h = sqrt(h)
let x0 = (h - q) * 0.5
let x1 = (0.0 - h - q) * 0.5
let u0 = sign(x0) * pow(abs(x0), 1.0 / 3.0)
let u1 = sign(x1) * pow(abs(x1), 1.0 / 3.0)
tt = clamp(u0 + u1 - kx, 0.0, 1.0)
let e = d + (c + b * tt) * tt
res = dot(e, e)
} else {
let z = sqrt(0.0 - p)
let v = acos(clamp(q / (p * z * 2.0), -1.0, 1.0)) / 3.0
let m = cos(v)
let n = sin(v) * 1.732050808
let t0 = clamp((m + m) * z - kx, 0.0, 1.0)
let t1 = clamp((0.0 - n - m) * z - kx, 0.0, 1.0)
let e0 = d + (c + b * t0) * t0
let e1 = d + (c + b * t1) * t1
let r0 = dot(e0, e0)
let r1 = dot(e1, e1)
res = min(r0, r1)
tt = mix(t1, t0, step(r0, r1))
}
return vec2(sqrt(res), tt)
}
// An open spiral, a tendril's curl: `pitch` points between its turns,
// `turns` of them out from its eye `c`, its outer end at the angle `rot`.
let an_spiral = fn(p: vec2, c: vec2, pitch: float, turns: float, rot: float) -> float {
let q = p - c
let r = length(q)
var th = atan2(q.y, q.x) - rot
th = (th - floor(th / (2.0 * PI)) * 2.0 * PI) / (2.0 * PI)
let k = clamp(floor(r / pitch - th + 0.5), 0.0, turns - 1.0)
return abs(r - pitch * (th + k))
}
// Lays a filled shape of distance `d` over `dst`: its fill, and a
// bronze-gold outline `lw` points wide inside its edge.
let an_lay = fn(dst: vec4, d: float, fill: vec3, lw: float, px: float) -> vec4 {
let a = Finish.cover(d, px)
let l = Finish.band(d, 0.0, lw, px)
let col = mix(fill, an_line_c(), clamp(l / max(a, 0.001), 0.0, 1.0) * 0.9)
return Finish.over(dst, vec4(col * a, a))
}
// A leaf laid with its two halves in two greens (the half toward the light
// paler), a gold midrib, a gold outline.
let an_lay_leaf = fn(dst: vec4, l: vec4, dark: vec3, light: vec3, lw: float, px: float) -> vec4 {
var fill = mix(dark, light, smoothstep(-0.15, 0.15, l.y))
fill = mix(fill, fill * 0.85, smoothstep(0.6, 1.0, l.z) * 0.4)
let rib = (1.0 - smoothstep(0.05, 0.16, abs(l.y))) * step(l.z, 0.88) * 0.6
fill = mix(fill, an_line_c(), rib)
return an_lay(dst, l.x, fill, lw, px)
}
// A petal: from a dusty rose throat to a cream tip.
let an_lay_petal = fn(dst: vec4, l: vec4, root: vec3, tip: vec3, lw: float, px: float) -> vec4 {
var fill = mix(root, tip, smoothstep(0.1, 0.75, l.z))
fill = mix(fill, fill * 0.88, smoothstep(0.2, 1.0, abs(l.y)) * 0.35)
return an_lay(dst, l.x, fill, lw, px)
}
// A stem: a tapering stroke along a curve, green with gold edges.
let an_lay_stem = fn(dst: vec4, b: vec2, w0: float, w1: float, fill: vec3, px: float) -> vec4 {
let d = b.x - mix(w0, w1, b.y)
return an_lay(dst, d, fill, min(0.6, mix(w0, w1, b.y) * 0.5), px)
}
let an_dir = fn(a: float) -> vec2 {
return vec2(cos(a), sin(a))
}
// A LILY seen from the front: three long petals behind, three shorter ones
// before them, curling back at their tips, a throat of `root` and three
// gold stamens. `s` its size; `rose` 0 for cream and ochre petals, 1 for
// dusty rose ones.
let an_lily = fn(dst: vec4, p: vec2, c: vec2, s: float, rot: float, px: float, rose: float) -> vec4 {
var out = dst
let root = mix(vec3(0.788, 0.553, 0.478), vec3(0.62, 0.40, 0.44), rose)
let tip = mix(vec3(0.965, 0.886, 0.690), vec3(0.88, 0.70, 0.68), rose)
let fr = mix(vec3(0.925, 0.733, 0.420), vec3(0.80, 0.55, 0.54), rose)
let lw = max(0.45, s * 0.045)
out = an_lay_petal(out, an_leaf(p, c, an_dir(rot), s, s * 0.26, s * 0.10), root, tip, lw, px)
out = an_lay_petal(out, an_leaf(p, c, an_dir(rot + 2.094), s, s * 0.26, s * 0.10), root, tip, lw, px)
out = an_lay_petal(out, an_leaf(p, c, an_dir(rot + 4.189), s, s * 0.26, s * 0.10), root, tip, lw, px)
out = an_lay_petal(out, an_leaf(p, c, an_dir(rot + 1.047), s * 0.82, s * 0.24, 0.0 - s * 0.08), root, fr, lw, px)
out = an_lay_petal(out, an_leaf(p, c, an_dir(rot + 3.142), s * 0.82, s * 0.24, 0.0 - s * 0.08), root, fr, lw, px)
out = an_lay_petal(out, an_leaf(p, c, an_dir(rot + 5.236), s * 0.82, s * 0.24, 0.0 - s * 0.08), root, fr, lw, px)
// the stamens
var st = 1000.0
st = min(st, md_seg(p, c, c + an_dir(rot + 0.5) * s * 0.58))
st = min(st, md_seg(p, c, c + an_dir(rot + 2.6) * s * 0.58))
st = min(st, md_seg(p, c, c + an_dir(rot + 4.7) * s * 0.58))
let sa = Finish.cover(st - max(0.3, s * 0.02), px)
out = Finish.over(out, vec4(an_line_c() * sa, sa))
var dots = 1000.0
dots = min(dots, length(p - (c + an_dir(rot + 0.5) * s * 0.58)))
dots = min(dots, length(p - (c + an_dir(rot + 2.6) * s * 0.58)))
dots = min(dots, length(p - (c + an_dir(rot + 4.7) * s * 0.58)))
out = an_lay(out, dots - max(0.6, s * 0.06), vec3(0.50, 0.27, 0.15), 0.0, px)
out = an_lay(out, length(p - c) - s * 0.11, vec3(0.70, 0.50, 0.22), lw, px)
return out
}
// A BUD: a closed petal in its two green sepals, on the end of a stem.
let an_bud = fn(dst: vec4, p: vec2, b: vec2, dir: vec2, s: float, px: float) -> vec4 {
var out = dst
let lw = max(0.45, s * 0.06)
out = an_lay_petal(out, an_leaf(p, b, dir, s, s * 0.30, 0.0), vec3(0.80, 0.55, 0.45), vec3(0.95, 0.80, 0.55), lw, px)
let side = vec2(0.0 - dir.y, dir.x)
out = an_lay_leaf(out, an_leaf(p, b, normalize(dir + side * 0.35), s * 0.55, s * 0.12, 0.0), vec3(0.24, 0.38, 0.32), vec3(0.42, 0.58, 0.48), lw, px)
out = an_lay_leaf(out, an_leaf(p, b, normalize(dir - side * 0.35), s * 0.55, s * 0.12, 0.0), vec3(0.24, 0.38, 0.32), vec3(0.42, 0.58, 0.48), lw, px)
return out
}
// A SPRIG for a surface's corner, in the corner's own frame: `p` in points
// from the corner along its two edges, both into the surface. It is drawn
// the same about the corner's diagonal, so it sits in any of the four: a
// whiplash stem bowed round the corner like a bracket, curling at both of
// its ends, a leaf on each arm, a larger pair at the corner and an ochre bud
// between them. `s` scales it (1 is about 38 points along each edge).
let an_sprig = fn(dst: vec4, p: vec2, s: float, px: float) -> vec4 {
var out = dst
let q = p / s
let pxs = px / s
let dk = vec3(0.220, 0.357, 0.306)
let lt = vec3(0.427, 0.580, 0.494)
let stem = vec3(0.290, 0.420, 0.357)
// fold onto one side of the diagonal for the mirrored arms
let m = vec2(min(q.x, q.y), max(q.x, q.y))
out = an_lay(out, an_spiral(m, vec2(4.6, 33.0), 1.5, 2.0, 0.0 - 1.4) - 0.42, stem, 0.25, pxs)
out = an_lay_stem(out, an_bez(m, vec2(3.0, 4.0), vec2(1.5, 18.0), vec2(5.1, 30.0)), 1.0, 0.45, stem, pxs)
out = an_lay_leaf(out, an_leaf(m, vec2(2.6, 15.0), normalize(vec2(1.0, 0.55)), 11.0, 2.4, 1.2), dk, lt, 0.38, pxs)
out = an_lay_leaf(out, an_leaf(q, vec2(3.0, 3.0), normalize(vec2(1.0, 0.45)), 16.0, 3.2, 2.2), dk, lt, 0.4, pxs)
out = an_lay_leaf(out, an_leaf(q, vec2(3.0, 3.0), normalize(vec2(0.45, 1.0)), 16.0, 3.2, -2.2), dk, lt, 0.4, pxs)
out = an_bud(out, q, vec2(3.5, 3.5), normalize(vec2(1.0, 1.0)), 11.0, pxs)
return out
}
// The sprig set into a box's corner `k` (0 top left, 1 top right, 2 bottom
// right, 3 bottom left), `ins` points in from its edges.
let an_corner = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, k: float, ins: float, s: float, px: float) -> vec4 {
let right = step(0.5, k) * step(k, 2.5)
let bottom = step(1.5, k)
let x = mix(p.x - lo.x, hi.x - p.x, right) - ins
let y = mix(p.y - lo.y, hi.y - p.y, bottom) - ins
if x > -2.0 && y > -2.0 && x < 42.0 * s && y < 42.0 * s {
return an_sprig(dst, vec2(x, y), s, px)
}
return dst
}
// ---------------------------------------------------------------------------
// BUTTONS. One face for the family: an enamel pill in its bronze wire, a
// fine gold line inside the wire on the filled ones, a soft sepia shadow
// under it. `kind`: 0 teal enamel, 1 ochre (primary), 2 a peacock teal
// (secondary), 3 pale sage (tertiary), 4 terracotta (danger), 5 the outline
// (the parchment shows, a bronze wire and a teal line inside it), 6 bare
// until touched (flatter), 7 an ivory pill (the plain button). Pressed, the
// enamel sinks; the quad is grown by `m` for the shadow and the focus ring.
let an_kind_c = fn(kind: float) -> vec3 {
var c = an_teal()
c = mix(c, an_ochre(), step(0.5, kind) * step(kind, 1.5))
c = mix(c, vec3(0.224, 0.400, 0.420), step(1.5, kind) * step(kind, 2.5))
c = mix(c, an_sage(), step(2.5, kind) * step(kind, 3.5))
c = mix(c, an_terra(), step(3.5, kind) * step(kind, 4.5))
c = mix(c, vec3(0.906, 0.851, 0.737), step(4.5, kind))
return c
}
let an_button = fn(pos: vec2, size: vec2, o: vec2, px: float, hover: float, down: float, focus: float, dis: float, loading: float, anim: float, r4: vec4, kind: float, m: float) -> vec4 {
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let live = 1.0 - dis
let press = down * live
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let r = md_r4(p, c, h, r4)
let d = md_rbox(p, c, h, r)
let n = an_nrm(p, c, h, r)
let wire = 1.6
let outline = step(4.5, kind) * step(kind, 5.5)
let bare = step(5.5, kind) * step(kind, 6.5)
let show = mix(1.0, clamp(hover * live * 0.8 + press, 0.0, 1.0), bare)
var out = an_drop(p, c, h, r, (1.0 - 0.65 * press) * mix(1.0, 0.45, dis) * (1.0 - outline) * show)
var base = an_kind_c(kind)
base = mix(base, mix(base, vec3(0.70, 0.69, 0.60), 0.62) * 1.08, dis)
let hf = h - vec2(wire, wire)
var face = an_enamel(p, c, hf, max(r - wire, 0.0), base, hover * live, press)
let fa = Finish.cover(d + wire * 0.5, px) * (1.0 - outline * (1.0 - max(press * 0.5, hover * live * 0.35)))
out = Finish.over(out, vec4(face * fa, fa) * show)
// the fine line inside the wire, on the filled enamels
let filled = 1.0 - step(4.5, kind)
out = Finish.over(out, an_fine(d, wire + 1.2, max(px, 0.55), px, 0.55 * filled * mix(1.0, 0.4, dis)))
// the outline's teal line inside the wire
let tl = Finish.band(d, wire + 0.8, wire + 0.8 + max(px, 0.6), px) * outline * mix(0.85, 0.4, dis)
out = Finish.over(out, vec4(an_teal_dk() * tl, tl))
let wv = an_wire(d, n, wire, px, mix(1.0, 0.3, dis) * (1.0 + 0.4 * hover * live))
out = Finish.over(out, wv * mix(1.0, 0.55, dis) * show)
out = Finish.over(out, md_focus(d, px, focus * live))
if loading > 0.0 {
let sdf = Sdf2d.viewport(p)
let rr = max(min(h.x, h.y) - 5.0, 2.0)
let a0 = anim * 2.0 * PI
sdf.arc_round_caps(c.x, c.y, rr, a0, a0 + 1.5 * PI, 1.2)
sdf.fill(vec4(0.165, 0.129, 0.086, loading))
out = Finish.over(out, sdf.result)
}
return out
}
// Every button template: the plain root (and the sizes, which share its draw
// object) wears the ivory pill; the roles are specialised below.
mod.widgets.ButtonFlat.clip_x = false
mod.widgets.ButtonFlat.clip_y = false
mod.widgets.ButtonFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 8.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.ButtonFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 7.0, 8.0), 8.0)
}
mod.widgets.Button.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 0.0, 8.0), 8.0)
}
mod.widgets.ButtonPrimary.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 1.0, 8.0), 8.0)
}
mod.widgets.ButtonSecondary.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 2.0, 8.0), 8.0)
}
mod.widgets.ButtonTertiary.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 3.0, 8.0), 8.0)
}
mod.widgets.ButtonDanger.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 4.0, 8.0), 8.0)
}
mod.widgets.ButtonOutline.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 5.0, 8.0), 8.0)
}
mod.widgets.ButtonFlatter.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 6.0, 8.0), 8.0)
}
// The large button is the teal one, as in the pictures.
mod.widgets.ButtonLg.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, md_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 99.0), 0.0, 8.0), 8.0)
}
// Templates that draw a face of their own on top of these roots keep the
// plain quad: the grown one is only for the faces written here.
mod.widgets.CloseButton.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.CopyButton.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.BurgerButton.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.ButtonFloating.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.ButtonFloatingSm.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.glass.CutButton.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
// Geometry: one 26 point row, 14 points either side of the word.
mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 4.0 bottom: 4.0}
mod.widgets.Button.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonPrimary.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonSecondary.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonDanger.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonTertiary.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonOutline.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 4.0 bottom: 4.0}
mod.widgets.ButtonIcon.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
mod.widgets.Button.icon_walk = mod.turtle.Walk{width: 14.0 height: Fit}
mod.widgets.ButtonFlat.min_height = 26.0
mod.widgets.Button.min_height = 26.0
mod.widgets.ButtonXs.min_height = 0.0
mod.widgets.ButtonXs.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 0.0 bottom: 0.0}
mod.widgets.ButtonXs.draw_text.text_style.font_size = 7.5
mod.widgets.ButtonSm.min_height = 0.0
// Words on the buttons: Cormorant semibold; ivory on the dark enamels, the
// sepia ink on the pale ones.
mod.widgets.ButtonFlat.draw_text.text_style.font_family = mod.theme.font_label.font_family
mod.widgets.ButtonFlat.draw_text.text_style.font_size = 10.5
mod.widgets.ButtonFlat.draw_text.color = #2a2116
mod.widgets.ButtonFlat.draw_text.color_hover = #1f180f
mod.widgets.ButtonFlat.draw_text.color_down = #1f180f
mod.widgets.ButtonFlat.draw_text.color_focus = #2a2116
mod.widgets.ButtonFlat.draw_text.color_disabled = #8c7a5c
mod.widgets.Button.draw_text.color = #f3e8cf
mod.widgets.Button.draw_text.color_hover = #fbf3e0
mod.widgets.Button.draw_text.color_down = #e6dac0
mod.widgets.Button.draw_text.color_focus = #f3e8cf
mod.widgets.Button.draw_text.color_disabled = #e8e2d0
// The icon on a teal button is ivory, whatever colour its caller asked for
// (the catalogue asks for the working ink, which a teal face would swallow).
mod.widgets.Button.draw_icon.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.953, 0.910, 0.812)
return vec4(k * base.a, base.a) * self.opacity
}
mod.widgets.ButtonLg.draw_text.color = #f3e8cf
mod.widgets.ButtonLg.draw_text.color_hover = #fbf3e0
mod.widgets.ButtonLg.draw_text.color_down = #e6dac0
mod.widgets.ButtonLg.draw_text.color_focus = #f3e8cf
mod.widgets.ButtonLg.draw_text.text_style.font_size = 12.0
mod.widgets.ButtonPrimary.draw_text.color = #2a2116
mod.widgets.ButtonPrimary.draw_text.color_hover = #1f180f
mod.widgets.ButtonPrimary.draw_text.color_down = #1f180f
mod.widgets.ButtonPrimary.draw_text.color_focus = #2a2116
mod.widgets.ButtonPrimary.draw_text.color_disabled = #8c7a5c
mod.widgets.ButtonPrimary.draw_icon.color = #2a2116
mod.widgets.ButtonPrimary.draw_text.text_style = mod.theme.font_bold{font_size: 10.5}
mod.widgets.ButtonSecondary.draw_text.color = #f3e8cf
mod.widgets.ButtonSecondary.draw_text.color_hover = #fbf3e0
mod.widgets.ButtonSecondary.draw_text.color_down = #e6dac0
mod.widgets.ButtonSecondary.draw_text.color_focus = #f3e8cf
mod.widgets.ButtonSecondary.draw_icon.color = #f3e8cf
mod.widgets.ButtonSecondary.draw_text.text_style = mod.theme.font_label{font_size: 10.5}
mod.widgets.ButtonTertiary.draw_text.color = #24342f
mod.widgets.ButtonTertiary.draw_text.color_hover = #1a2622
mod.widgets.ButtonTertiary.draw_text.color_down = #1a2622
mod.widgets.ButtonTertiary.draw_text.color_focus = #24342f
mod.widgets.ButtonTertiary.draw_icon.color = #24342f
mod.widgets.ButtonTertiary.draw_text.text_style = mod.theme.font_label{font_size: 10.5}
mod.widgets.ButtonDanger.draw_text.color = #f3e8cf
mod.widgets.ButtonDanger.draw_text.color_hover = #fbf3e0
mod.widgets.ButtonDanger.draw_text.color_down = #e6dac0
mod.widgets.ButtonDanger.draw_text.color_focus = #f3e8cf
mod.widgets.ButtonDanger.draw_icon.color = #f3e8cf
mod.widgets.ButtonDanger.draw_text.text_style = mod.theme.font_bold{font_size: 10.5}
mod.widgets.ButtonOutline.draw_text.color = #24342f
mod.widgets.ButtonOutline.draw_text.color_hover = #1a2622
mod.widgets.ButtonOutline.draw_text.color_down = #1a2622
mod.widgets.ButtonOutline.draw_text.color_focus = #24342f
mod.widgets.ButtonOutline.draw_text.color_disabled = #8c7a5c
mod.widgets.ButtonOutline.draw_icon.color = #24342f
mod.widgets.ButtonFlat.draw_bg.loading_color = #2a2116
// The window's own buttons stand on the teal band: ivory strokes.
mod.widgets.DesktopButton.draw_bg.color = uniform(#f3e8cf)
mod.widgets.DesktopButton.draw_bg.color_hover = uniform(#fffaf0)
mod.widgets.DesktopButton.draw_bg.color_down = uniform(#e8dcc0)
mod.widgets.DesktopButton.draw_bg.bg_color_hover = uniform(#f3e8cf26)
mod.widgets.DesktopButton.draw_bg.bg_color_down = uniform(#f3e8cf40)
// ---------------------------------------------------------------------------
// CHECK BOXES. A 14 point box with rounded corners in its bronze wire: clear,
// a sunk ivory well; set, teal enamel with an ivory check; mixed, teal with
// an ivory dash.
mod.widgets.CheckBoxFlat.clip_x = false
mod.widgets.CheckBoxFlat.clip_y = false
mod.widgets.CheckBoxFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 4.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.CheckBoxCustom.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.CheckBoxFlat.padding = mod.turtle.Inset{left: 0.0 right: 3.0 top: 3.0 bottom: 3.0}
mod.widgets.CheckBoxFlat.label_walk.margin = mod.turtle.Inset{left: 22.0 right: 3.0 top: 0.0 bottom: 0.0}
mod.widgets.CheckBoxFlat.draw_bg.size = uniform(14.0)
mod.widgets.CheckBoxFlat.min_height = 24.0
mod.widgets.CheckBoxFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 4.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let sz = self.size
let live = 1.0 - self.disabled
let x0 = mix(1.0, size.x - sz - 1.0, self.mark_at_end)
let lo = vec2(Finish.snap(o.x + x0, px), Finish.snap(o.y + (size.y - sz) * 0.5, px)) - o
let h = vec2(sz * 0.5, sz * 0.5)
let c = lo + h
let r = mix(3.5, h.y, step(20.0, self.border_radius))
let d = md_rbox(p, c, h, r)
let on = max(self.active, self.mixed)
let press = self.down * live
var fill = Finish.lift(an_well_c(), 0.6 * self.hover * live)
fill = mix(fill, mix(an_well_c(), an_parch_c(), 0.6), self.disabled)
var out = an_well(p, c, h, r, px, fill, 0.30 - 0.12 * self.hover * live, 1.3, self.disabled)
// the teal enamel when set
let hf = h - vec2(1.3, 1.3)
var tcol = an_enamel(p, c, hf, max(r - 1.3, 0.0), mix(an_teal(), vec3(0.62, 0.63, 0.55), self.disabled), self.hover * live, press)
let ta = Finish.cover(md_rbox(p, c, hf, max(r - 1.3, 0.0)), px) * on
out = Finish.over(out, vec4(tcol * ta, ta))
let k = sz / 14.0
let m0 = lo + vec2(3.6, 7.3) * k
let m1 = lo + vec2(6.0, 9.8) * k
let m2 = lo + vec2(10.6, 4.3) * k
let ck = min(md_seg(p, m0, m1), md_seg(p, m1, m2)) - 0.85 * k
let d0 = lo + vec2(4.0, 7.0) * k
let d1 = lo + vec2(10.0, 7.0) * k
let dash = md_seg(p, d0, d1) - 0.85 * k
let show_ck = self.active * (1.0 - self.mixed)
let mk = mix(Finish.cover(ck, px) * show_ck, Finish.cover(dash, px), self.mixed)
let ink = mix(an_ivory(), vec3(0.85, 0.82, 0.74), self.disabled)
out = Finish.over(out, vec4(ink * mk, mk))
let err = Finish.band(d, 0.0, 2.0 * px, px) * self.error
out = Finish.over(out, vec4(vec3(0.60, 0.20, 0.15) * err, err))
return Finish.over(out, md_focus(d, px, self.focus * live))
}
// ---------------------------------------------------------------------------
// TOGGLES. A 38 by 16 point pill sunk in its wire: parchment-sage while off,
// teal enamel while on; riding in it a 12 point ivory disc in a bronze ring,
// a teal jewel at its middle, with its own small shadow.
let an_toggle = fn(pos: vec2, size: vec2, o: vec2, px: float, active: float, drag_pos: float, drag: float, hover: float, down: float, focus: float, dis: float, err: float, at_end: float, metal: float) -> vec4 {
let m = 4.0
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let live = 1.0 - dis
let tw = 38.0
let th = 16.0
let x0 = mix(1.0, size.x - tw - 1.0, at_end)
let lo = vec2(Finish.snap(o.x + x0, px), Finish.snap(o.y + (size.y - th) * 0.5, px)) - o
let c = lo + vec2(tw * 0.5, th * 0.5)
let h = vec2(tw * 0.5, th * 0.5)
let r = h.y
let d = md_rbox(p, c, h, r)
let on = active
var fill = mix(vec3(0.788, 0.769, 0.639), an_teal(), on)
fill = Finish.lift(fill, 0.10 * hover * live)
fill = mix(fill, mix(fill, vec3(0.80, 0.78, 0.70), 0.6), dis)
var out = an_well(p, c, h, r, px, fill, mix(0.38, 0.45, on), 1.3, dis)
let ea = Finish.band(d, 0.0, 2.0 * px, px) * err
out = Finish.over(out, vec4(vec3(0.60, 0.20, 0.15) * ea, ea))
// the disc
let kt = clamp(mix(on, drag_pos, drag), 0.0, 1.0)
let kr = 6.0
let kx = Finish.snap(o.x + lo.x + 2.0 + kr + kt * (tw - 4.0 - 2.0 * kr), px) - o.x
let kc = vec2(kx, c.y)
let press = down * live
out = Finish.over(out, an_drop(p, kc, vec2(kr, kr), kr, (0.9 - 0.5 * press) * mix(1.0, 0.4, dis)))
let q = p - kc
let kd = length(q) - kr
let kn = q / max(length(q), 0.001)
// the ivory (or, on the rocker, bronze) face, domed toward the light
let lit = clamp(0.5 - 0.5 * dot(q / kr, vec2(0.45, 0.85)), 0.0, 1.0)
var face = mix(mix(vec3(0.80, 0.74, 0.60), an_ivory(), lit), mix(an_bronze_dk(), an_gold_hi(), lit * 0.9), metal)
face = Finish.lift(face, 0.12 * hover * live * (1.0 - press))
face = mix(face, face * 0.9, press)
face = mix(face, mix(face, vec3(0.80, 0.78, 0.72), 0.6), dis)
let ka = Finish.cover(kd, px)
out = Finish.over(out, vec4(face * ka, ka))
out = Finish.over(out, an_wire(kd, kn, 1.3, px, live) * mix(1.0, 0.5, dis))
// the jewel: teal when on, a dark bead when off
let jd = length(q) - 2.0
var jc = mix(vec3(0.20, 0.22, 0.18), vec3(0.30, 0.62, 0.53), on * live)
jc = mix(jc, an_ivory(), (1.0 - smoothstep(0.0, 1.2, length(q - vec2(-0.6, -0.7)))) * 0.6)
out = an_lay(out, jd, jc, 0.4, px)
return Finish.over(out, md_focus(d, px, focus * live))
}
mod.widgets.ToggleFlat.label_walk.margin = mod.turtle.Inset{left: 46.0 right: 3.0 top: 0.0 bottom: 0.0}
mod.widgets.ToggleFlat.draw_bg.size = 14.0
mod.widgets.ToggleFlat.min_height = 24.0
mod.widgets.ToggleFlat.draw_bg.pill_aspect = 2.7143
mod.widgets.ToggleFlat.draw_bg.knob_inset = 7.0
mod.widgets.ToggleFlat.clip_x = false
mod.widgets.ToggleFlat.clip_y = false
mod.widgets.ToggleFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 4.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.ToggleFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return an_toggle(self.pos, self.rect_size, self.rect_pos, px, self.active, self.drag_pos, self.drag, self.hover, self.down, self.focus, self.disabled, self.error, self.mark_at_end, 0.0)
}
// ---------------------------------------------------------------------------
// RADIOS. A round ivory well in its wire; chosen, a teal enamel disc fills
// it with an ivory pip at its middle.
let md_radio = fn(p: vec2, c: vec2, rad: float, px: float, hover: float, down: float, focus: float, active: float, dis: float) -> vec4 {
let live = 1.0 - dis
let q = p - c
let d = length(q) - rad
var fill = Finish.lift(an_well_c(), 0.6 * hover * live)
fill = mix(fill, mix(an_well_c(), an_parch_c(), 0.6), dis)
var out = an_well(p, c, vec2(rad, rad), rad, px, fill, 0.30 - 0.12 * hover * live, 1.3, dis)
let ir = rad - 2.6
let tcol = an_enamel(p, c, vec2(ir, ir), ir, mix(an_teal(), vec3(0.62, 0.63, 0.55), dis), hover * live, down * live)
let ta = Finish.cover(length(q) - ir, px) * active
out = Finish.over(out, vec4(tcol * ta, ta))
let pa = Finish.cover(length(q) - rad * 0.26, px) * active
out = Finish.over(out, vec4(mix(an_ivory(), vec3(0.85, 0.82, 0.74), dis) * pa, pa))
return Finish.over(out, md_focus(d, px, focus * live))
}
mod.widgets.RadioButtonFlat.clip_x = false
mod.widgets.RadioButtonFlat.clip_y = false
mod.widgets.RadioButtonFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 4.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.glass.RadioButton.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
mod.widgets.RadioButtonFlat.padding = mod.turtle.Inset{left: 0.0 right: 3.0 top: 3.0 bottom: 3.0}
mod.widgets.RadioButtonFlat.label_walk.margin = mod.turtle.Inset{left: 22.0 right: 3.0 top: 0.0 bottom: 0.0}
mod.widgets.RadioButtonFlat.draw_bg.size = uniform(14.0)
mod.widgets.RadioButtonFlat.min_height = 24.0
mod.widgets.RadioButtonFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 4.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let sz = self.size
let lo = vec2(Finish.snap(o.x + 1.0, px), Finish.snap(o.y + (self.rect_size.y - sz) * 0.5, px)) - o
let c = lo + vec2(sz * 0.5, sz * 0.5)
return md_radio(p, c, sz * 0.5, px, self.hover, self.down, self.focus, self.active, self.disabled)
}
// The flatter radio is its word alone: chosen, the word sits on a small
// ochre enamel pill; under the pointer a faint wash.
mod.widgets.RadioButtonFlatter.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 4.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let live = 1.0 - self.disabled
let lo = vec2(Finish.snap(o.x - 3.0, px), Finish.snap(o.y + 1.0, px)) - o
let hi = vec2(Finish.snap(o.x + self.rect_size.x + 3.0, px), Finish.snap(o.y + self.rect_size.y - 1.0, px)) - o
let h = (hi - lo) * 0.5
let c = lo + h
let d = md_rbox(p, c, h, h.y)
let wash = Finish.cover(d, px) * 0.15 * self.hover * live * (1.0 - self.active)
var out = vec4(an_ochre() * wash, wash)
let face = an_enamel(p, c, h - vec2(1.2, 1.2), h.y, an_ochre(), 0.0, 0.0)
let a = Finish.cover(d, px) * self.active
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, an_wire(d, an_nrm(p, c, h, h.y), 1.2, px, 1.0) * self.active)
return Finish.over(out, md_focus(d, px, self.focus * live))
}
// Radio tabs (the catalogue's Docs, Controls, Actions, Theme): ivory pills
// in their wire; the chosen one teal enamel, pressed in, its word ivory.
mod.widgets.RadioButtonTabFlat.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 6.0 bottom: 2.0}
mod.widgets.RadioButtonTabFlat.label_walk.margin = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.RadioButtonTabFlat.min_height = 26.0
mod.widgets.RadioButtonTabFlat.clip_x = false
mod.widgets.RadioButtonTabFlat.clip_y = false
mod.widgets.RadioButtonTabFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.RadioButtonTabFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let ivory = an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, 0.0, 0.0, vec4(99.0, 99.0, 99.0, 99.0), 7.0, 6.0)
let teal = an_button(self.pos, self.rect_size, self.rect_pos, px, self.hover * 0.5, 0.55, self.focus, self.disabled, 0.0, 0.0, vec4(99.0, 99.0, 99.0, 99.0), 0.0, 6.0)
return mix(ivory, teal, self.active)
}
mod.widgets.RadioButtonTabFlat.draw_text.color = #2a2116
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #f3e8cf
mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #8c7a5c
mod.widgets.RadioButtonTabFlat.draw_text.text_style = mod.theme.font_label{font_size: 10.5}
// ---------------------------------------------------------------------------
// SLIDERS. A fine bronze channel: a 4 point groove behind its wire, dark
// parchment where it is empty and teal enamel where the value fills it. The
// thumb is a bronze coin with a teal leaf on its face, riding with its own
// shadow. Every slider's quad grows six points for the coin, its shadow and
// the focus ring.
let an_coin = fn(p: vec2, c: vec2, rad: float, px: float, hover: float, press: float, dis: float) -> vec4 {
let live = 1.0 - dis
var out = an_drop(p, c, vec2(rad, rad), rad, (1.0 - 0.5 * press) * mix(1.0, 0.4, dis))
let q = p - c
let d = length(q) - rad
let n = q / max(length(q), 0.001)
let rr = clamp(length(q) / rad, 0.0, 1.0)
// the coin's face: a polished dome, light from the upper left
let lit = clamp(0.5 - 0.6 * dot(q / rad, vec2(0.45, 0.85)), 0.0, 1.0)
var face = mix(an_bronze_dk(), an_bronze_c(), 0.45 + 0.55 * lit)
face = mix(face, an_gold_hi(), smoothstep(0.55, 1.0, lit) * 0.55)
face = Finish.lift(face, 0.14 * hover * live * (1.0 - press))
face = mix(face, face * 0.88, press)
face = mix(face, mix(face, vec3(0.75, 0.72, 0.64), 0.55), dis)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
// an engraved ring a little inside the rim
let ring = Finish.band(d, rad * 0.24, rad * 0.24 + max(px, 0.5), px)
out = Finish.over(out, vec4(an_bronze_dk() * ring * 0.8, ring * 0.8))
let ringl = Finish.band(d, rad * 0.24 + max(px, 0.5), rad * 0.24 + 2.0 * max(px, 0.5), px) * 0.6
out = Finish.over(out, vec4(an_gold_hi() * ringl, ringl))
// the leaf on its face
let lf = an_leaf(p, c + vec2(-0.42, 0.42) * rad * 0.62, normalize(vec2(1.0, -1.0)), rad * 1.24, rad * 0.30, rad * 0.10)
out = an_lay_leaf(out, lf, mix(vec3(0.16, 0.30, 0.25), vec3(0.5, 0.5, 0.45), dis), mix(vec3(0.33, 0.52, 0.44), vec3(0.6, 0.6, 0.55), dis), 0.35, px)
out = Finish.over(out, an_wire(d, n, max(1.0, rad * 0.16), px, live))
return out
}
let an_groove = fn(p: vec2, c: vec2, h: vec2, px: float, f0: float, f1: float, dis: float, hover: float) -> vec4 {
let r = h.y
let d = md_rbox(p, c, h, r)
var empty = mix(vec3(0.765, 0.682, 0.541), vec3(0.80, 0.74, 0.62), dis)
empty = Finish.lift(empty, 0.08 * hover)
var out = an_well(p, c, h, r, px, empty, 0.4, 1.0, dis)
let fh = vec2(max((f1 - f0) * 0.5, 0.0), h.y - 1.0)
let fc = vec2((f0 + f1) * 0.5, c.y)
let fd = md_rbox(p, fc, fh, fh.y)
let fcol = an_enamel(p, fc, fh, fh.y, mix(an_teal_lt(), vec3(0.66, 0.67, 0.60), dis), hover, 0.0)
let fa = Finish.cover(fd, px) * step(0.01, f1 - f0) * Finish.cover(d + 1.0, px)
return Finish.over(out, vec4(fcol * fa, fa))
}
mod.widgets.SliderFlat.height = 32.0
mod.widgets.SliderFlat.clip_x = false
mod.widgets.SliderFlat.clip_y = false
mod.widgets.SliderFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.SliderFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 6.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let live = 1.0 - self.disabled
let cy = Finish.snap(o.y + size.y - 8.0, px) - o.y
let x0 = Finish.snap(o.x + 1.0, px) - o.x
let x1 = Finish.snap(o.x + size.x - 1.0, px) - o.x
let w = x1 - x0
let span = w - 18.0
let kx = x0 + 9.0 + self.slide_pos * span
let ox = x0 + 9.0 + self.origin_pos * span
let f0 = mix(x0 + 2.0, min(ox, kx), self.arc_origin)
let f1 = mix(kx, max(ox, kx), self.arc_origin)
var out = an_groove(p, vec2((x0 + x1) * 0.5, cy), vec2(w * 0.5, 2.5), px, f0, f1, self.disabled, max(self.hover, self.drag) * live)
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, cy)
out = Finish.over(out, an_coin(p, kc, 7.5, px, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
return Finish.over(out, md_focus(length(p - kc) - 7.5, px, self.focus * live))
}
mod.widgets.SliderFlat.text_input.draw_text.text_style = mod.theme.font_regular{font_size: 9.5}
// The range slider: the same channel, teal between the two ends, a round
// coin on each end, pressed while it is the one held.
mod.widgets.RangeSliderFlat.handle_size = 16.0
mod.widgets.RangeSliderFlat.track_inset = 1.0
mod.widgets.RangeSliderFlat.clip_x = false
mod.widgets.RangeSliderFlat.clip_y = false
mod.widgets.RangeSliderFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.RangeSliderFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 6.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let live = 1.0 - self.disabled
let top = self.label_px
let cy = Finish.snap(o.y + top + (size.y - top) * 0.5, px) - o.y
let x0 = Finish.snap(o.x + 1.0, px) - o.x
let x1 = Finish.snap(o.x + size.x - 1.0, px) - o.x
let w = x1 - x0
let span = max(size.x - self.inset_px * 2.0 - self.handle_px, 1.0)
let sx = self.inset_px + self.handle_px * 0.5 + self.start_pos * span
let ex = self.inset_px + self.handle_px * 0.5 + self.end_pos * span
var out = an_groove(p, vec2((x0 + x1) * 0.5, cy), vec2(w * 0.5, 2.5), px, sx, ex, self.disabled, max(self.hover, self.drag) * live)
let k0 = vec2(Finish.snap(o.x + sx, px) - o.x, cy)
let k1 = vec2(Finish.snap(o.x + ex, px) - o.x, cy)
out = Finish.over(out, an_coin(p, k0, 7.0, px, max(self.hot_start, self.focus * 0.5) * live, self.drag * self.hot_start * live, self.disabled))
out = Finish.over(out, an_coin(p, k1, 7.0, px, max(self.hot_end, self.focus * 0.5) * live, self.drag * self.hot_end * live, self.disabled))
return out
}
// The minimal slider: a bronze hairline (a dark device pixel over a gold
// one), a teal thread along it to the thumb, and a bronze drop riding on
// it, its point upward, a teal jewel in its round.
mod.widgets.SliderMinimal.clip_x = false
mod.widgets.SliderMinimal.clip_y = false
mod.widgets.SliderMinimal.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.SliderMinimal.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 6.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let live = 1.0 - self.disabled
let cy = Finish.snap(o.y + size.y - 6.0, px) - o.y
let kr = 5.0
let kx = kr + self.slide_pos * (size.x - 2.0 * kr)
let inx = step(0.0, p.x) * step(p.x, size.x)
let dark = step(cy - px, p.y) * step(p.y, cy) * inx
let light = step(cy, p.y) * step(p.y, cy + px) * inx
let thread = step(cy - 2.0 * px, p.y) * step(p.y, cy + px) * step(p.x, kx) * inx
var out = vec4(0.0, 0.0, 0.0, 0.0)
let la = light * 0.8
out = Finish.over(out, vec4(an_gold_hi() * la, la))
let da = dark * 0.85 * mix(1.0, 0.5, self.disabled)
out = Finish.over(out, vec4(an_bronze_dk() * da, da))
let tcol = mix(an_teal(), vec3(0.66, 0.67, 0.60), self.disabled)
out = Finish.over(out, vec4(tcol * thread, thread))
// the drop: a round body on the line, a point rising from it
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, cy)
let q = p - kc
let body = length(q) - kr
let ty = q.y + kr * 1.9
let cone = max((abs(q.x) - ty * 0.618) * 0.851, 0.0 - ty)
let dd = min(body, max(cone, q.y))
let press = self.drag * live
out = Finish.over(out, an_drop(p, kc, vec2(kr, kr), kr, (1.0 - 0.5 * press) * mix(1.0, 0.4, self.disabled)))
let lit = clamp(0.5 - 0.6 * dot(q / (kr * 1.6), vec2(0.45, 0.85)), 0.0, 1.0)
var face = mix(an_bronze_dk(), an_bronze_c(), 0.45 + 0.55 * lit)
face = mix(face, an_gold_hi(), smoothstep(0.55, 1.0, lit) * 0.55)
face = Finish.lift(face, 0.14 * self.hover * live * (1.0 - press))
face = mix(face, mix(face, vec3(0.75, 0.72, 0.64), 0.55), self.disabled)
let a = Finish.cover(dd, px)
out = Finish.over(out, vec4(face * a, a))
let e = Finish.band(dd, 0.0, max(px, 0.55), px)
out = Finish.over(out, vec4(an_bronze_dk() * e * 0.85, e * 0.85))
let jd = length(q - vec2(0.0, 0.3)) - kr * 0.45
out = an_lay(out, jd, mix(vec3(0.26, 0.52, 0.45), vec3(0.6, 0.6, 0.55), self.disabled), 0.35, px)
return Finish.over(out, md_focus(body, px, self.focus * live))
}
mod.widgets.SliderMinimal.text_input.draw_text.text_style = mod.theme.font_regular{font_size: 9.5}
// The round slider: a teal-lined channel beside its label, a teal capsule in
// it and a bronze coin on the capsule's end; the value reads past it.
mod.widgets.SliderRoundFlat.height = 18.0
mod.widgets.SliderRoundFlat.clip_x = false
mod.widgets.SliderRoundFlat.clip_y = false
mod.widgets.SliderRoundFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.SliderRoundFlat.text_input.margin = mod.turtle.Inset{left: 0.0 right: 8.0 top: 0.0 bottom: 0.0}
mod.widgets.SliderRoundFlat.text_input.width = 30.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.SliderRoundFlat.text_input.draw_text.text_style = mod.theme.font_regular{font_size: 9.5}
mod.widgets.SliderRoundFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 6.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let live = 1.0 - self.disabled
let x0 = Finish.snap(o.x + self.label_size, px) - o.x
let y0 = Finish.snap(o.y + 1.0, px) - o.y
let x1 = Finish.snap(o.x + size.x - 44.0, px) - o.x
let y1 = Finish.snap(o.y + size.y - 1.0, px) - o.y
let h = vec2((x1 - x0) * 0.5, (y1 - y0) * 0.5)
let c = vec2(x0, y0) + h
let ins = 2.0
let kr = h.y - ins + 0.5
let kx = x0 + ins + kr + self.slide_pos * (2.0 * h.x - 2.0 * ins - 2.0 * kr)
var out = an_groove(p, c, h, px, x0 + ins, kx, self.disabled, self.hover * live)
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, c.y)
out = Finish.over(out, an_coin(p, kc, kr, px, self.hover, self.drag * live, self.disabled))
return Finish.over(out, md_focus(md_rbox(p, c, h, h.y), px, self.focus * live))
}
// ---------------------------------------------------------------------------
// FADERS. A deep teal slot four points wide in its bronze wire, bronze ticks
// either side, a gold thread in the slot for the value, and a cap: a teal
// enamel plaque 22 points across in a bronze wire, a gold line across its
// middle and a small bronze lozenge on it.
mod.widgets.SliderFader.cap_size = 13.0
mod.widgets.SliderFader.clip_x = false
mod.widgets.SliderFader.clip_y = false
mod.widgets.SliderFader.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.SliderFader.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 5.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let live = 1.0 - self.disabled
let vert = self.is_vertical
let a = mix(p.x, size.y - p.y, vert)
let b = mix(p.y, p.x, vert)
let along = mix(size.x, size.y, vert)
let across = mix(size.y, size.x, vert)
let travel = max(along - self.inset_px * 2.0 - self.cap_px, 1.0)
let foot = self.inset_px + self.cap_px * 0.5
let mid = floor(across * 0.5 / px) * px
// the slot, in screen space
let sw = 5.0
let sc = mix(vec2(along * 0.5, mid), vec2(mid, size.y - along * 0.5), vert)
let sh = mix(vec2(along * 0.5 - self.inset_px, sw * 0.5), vec2(sw * 0.5, along * 0.5 - self.inset_px), vert)
var out = an_well(p, sc, sh, sw * 0.5, px, mix(an_teal_dk() * 0.8, vec3(0.6, 0.6, 0.55), self.disabled), 0.5, 1.0, self.disabled)
// the value: a gold thread in the slot, from the origin (or the foot) to
// the cap
let va0 = foot + self.fill_lo * travel
let va1 = foot + self.fill_hi * travel
let vl = step(va0, a) * step(a, va1) * (1.0 - smoothstep(0.55, 0.55 + px, abs(b - mid))) * live
out = Finish.over(out, vec4(an_gold_hi() * 0.9 * vl, vl * 0.9))
// ticks: a long one every quarter of the travel, short ones between
let nt = 16.0
let f = (a - foot) / travel * nt
let fi = floor(f + 0.5)
let da = abs(a - (foot + fi / nt * travel))
let major = 1.0 - step(0.1, abs(fi * 0.25 - floor(fi * 0.25 + 0.5)))
let tl = mix(2.5, 4.5, major)
let off = abs(b - mid) - sw * 0.5 - 2.5
let in_rng = step(-0.5, fi) * step(fi, nt + 0.5)
let tk = (1.0 - smoothstep(px * 0.6, px * 0.6 + 0.01 + px * 0.4, da)) * step(0.0, off) * step(off, tl) * in_rng * 0.8 * mix(1.0, 0.5, self.disabled)
out = Finish.over(out, vec4(an_line_c() * 0.8 * tk, tk))
// the cap
let at = foot + self.slide_pos * travel
let cw = min(22.0, across)
let cs = mix(vec2(at, mid), vec2(mid, size.y - at), vert)
let ch = mix(vec2(self.cap_px * 0.5, cw * 0.5), vec2(cw * 0.5, self.cap_px * 0.5), vert)
let cc = vec2(Finish.snap(o.x + cs.x, px) - o.x, Finish.snap(o.y + cs.y, px) - o.y)
let cr = 4.0
let cd = md_rbox(p, cc, ch, cr)
let press = self.drag * live
out = Finish.over(out, an_drop(p, cc, ch, cr, (1.0 - press * 0.5) * mix(1.0, 0.4, self.disabled)))
let hf = ch - vec2(1.4, 1.4)
let face = an_enamel(p, cc, hf, cr - 1.4, mix(an_teal(), vec3(0.62, 0.63, 0.55), self.disabled), max(self.hover, self.focus * 0.5) * live, press)
let fa = Finish.cover(cd, px)
out = Finish.over(out, vec4(face * fa, fa))
// the gold line across the cap
let ln = mix(abs(p.y - cc.y), abs(p.x - cc.x), 1.0 - vert)
let lin = (1.0 - smoothstep(0.4, 0.4 + px, ln)) * Finish.cover(cd + 2.0, px)
out = Finish.over(out, vec4(an_gold_hi() * 0.9 * lin, lin * 0.9))
// the lozenge
let lq = abs(p - cc)
let lz = (lq.x + lq.y) - 3.2
out = an_lay(out, lz, mix(an_bronze_c(), an_gold_hi(), 0.3), 0.5, px)
out = Finish.over(out, an_wire(cd, an_nrm(p, cc, ch, cr), 1.4, px, live) * mix(1.0, 0.5, self.disabled))
return Finish.over(out, md_focus(cd, px, self.focus * live))
}
// ---------------------------------------------------------------------------
// KNOBS. A polished bronze disc, lit from the upper left, two rings engraved
// in it (a dark cut with a bright edge under it); in its middle a medallion
// of teal enamel with a spray of three leaves rising from its foot; the
// pointer a gold spear from a bronze boss out to the rim, turning with the
// value. Round it a fine bronze track and the value in gold.
let an_engrave = fn(d: float, at: float, px: float, amt: float) -> vec4 {
let w = max(px, 0.5)
let dk = Finish.band(d, at, at + w, px) * 0.75 * amt
let lt = Finish.band(d, at + w, at + 2.0 * w, px) * 0.6 * amt
var out = vec4(an_bronze_dk() * 0.7 * dk, dk)
return Finish.over(out, vec4(an_gold_hi() * lt, lt))
}
let an_knob = fn(p: vec2, c: vec2, rad: float, px: float, ang: float, hover: float, press: float, dis: float) -> vec4 {
let live = 1.0 - dis
var out = an_drop(p, c, vec2(rad, rad), rad, (1.0 - 0.4 * press) * mix(1.0, 0.4, dis))
let q = p - c
let d = length(q) - rad
let n = q / max(length(q), 0.001)
// the bronze disc: a dome, its light pooled on the upper left, a broad
// soft sheen and a darker foot
let lit = clamp(0.5 - 0.62 * dot(q / rad, vec2(0.45, 0.85)), 0.0, 1.0)
var face = mix(an_bronze_dk(), an_bronze_c(), 0.30 + 0.70 * lit)
face = mix(face, an_gold_hi(), smoothstep(0.62, 1.0, lit) * 0.55)
face = Finish.lift(face, 0.12 * hover * live * (1.0 - press))
face = mix(face, face * 0.9, press)
face = mix(face, mix(face, vec3(0.75, 0.72, 0.64), 0.55), dis)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, an_engrave(d, rad * 0.10, px, live * 0.9 + 0.1))
// the medallion
let mr = rad * 0.70
let md = length(q) - mr
let mcol = an_enamel(p, c, vec2(mr, mr), mr, mix(an_teal(), vec3(0.62, 0.63, 0.55), dis), hover * live, press * 0.4) * (0.9 + 0.2 * lit)
let ma = Finish.cover(md, px)
out = Finish.over(out, vec4(mcol * ma, ma))
// three leaves rising from the medallion's foot, fixed (the medallion
// is the knob's face, the pointer turns over it)
let ldk = mix(vec3(0.235, 0.369, 0.322), vec3(0.56, 0.57, 0.51), dis)
let llt = mix(vec3(0.459, 0.604, 0.525), vec3(0.67, 0.67, 0.61), dis)
let lw = max(0.3, rad * 0.02)
let lb = c + vec2(0.0, mr * 0.80)
if md < 1.0 {
out = an_lay_leaf(out, an_leaf(p, lb, normalize(vec2(-0.62, -1.0)), mr * 1.25, mr * 0.20, mr * 0.14), ldk, llt, lw, px)
out = an_lay_leaf(out, an_leaf(p, lb, normalize(vec2(0.62, -1.0)), mr * 1.25, mr * 0.20, mr * -0.14), ldk, llt, lw, px)
out = an_lay_leaf(out, an_leaf(p, lb, vec2(0.0, -1.0), mr * 1.45, mr * 0.17, 0.0), ldk, llt, lw, px)
}
// the medallion's wire
out = Finish.over(out, an_wire(md, n, max(0.9, rad * 0.07), px, live) * mix(1.0, 0.55, dis))
// the spear: a long lozenge from the boss to the rim
let dir = vec2(0.0 - sin(ang), cos(ang))
let sl = an_leaf(p, c - dir * rad * 0.10, dir, rad * 1.0, max(0.9, rad * 0.085), 0.0)
let sfill = mix(mix(an_bronze_c(), an_gold_hi(), smoothstep(-0.3, 0.3, sl.y) * 0.85), vec3(0.8, 0.78, 0.7), dis)
out = an_lay(out, sl.x, sfill, max(0.35, rad * 0.025), px)
// the boss
let br = max(1.6, rad * 0.15)
let bd = length(q) - br
let bl = clamp(0.5 - 0.6 * dot(q / br, vec2(0.45, 0.85)), 0.0, 1.0)
out = an_lay(out, bd, mix(an_bronze_dk(), an_gold_hi(), bl), max(0.35, rad * 0.02), px)
// the rim
out = Finish.over(out, an_wire(d, n, max(1.2, rad * 0.08), px, live) * mix(1.0, 0.55, dis))
return out
}
// The value arc round a knob: `ar` its middle radius, `aw` its width; the
// gap centred at the bottom, the value clockwise from its left end, or from
// the origin when the knob is bipolar. The track a fine bronze line, the
// value gold with a dark edge.
let md_arc = fn(p: vec2, c: vec2, ar: float, aw: float, px: float, v0: float, v1: float, gap: float, dis: float) -> vec4 {
let start = gap * 0.5
let sweep = 2.0 * PI - gap
let q = p - c
var ang = atan2(0.0 - q.x, q.y)
if ang < 0.0 {
ang = ang + 2.0 * PI
}
let rd = abs(length(q) - ar) - aw * 0.5
let rt = abs(length(q) - ar) - max(px, 0.45)
let in_arc = step(start, ang) * step(ang, start + sweep)
let e0 = c + vec2(0.0 - sin(start), cos(start)) * ar
let e1 = c + vec2(0.0 - sin(start + sweep), cos(start + sweep)) * ar
let track_d = min(mix(1000.0, rt, in_arc), min(length(p - e0), length(p - e1)) - aw * 0.6)
let a0 = start + sweep * v0
let a1 = start + sweep * v1
let l0 = c + vec2(0.0 - sin(a0), cos(a0)) * ar
let l1 = c + vec2(0.0 - sin(a1), cos(a1)) * ar
let lit_in = step(a0, ang) * step(ang, a1)
let lit_d = min(mix(1000.0, rd, lit_in), min(length(p - l0), length(p - l1)) - aw * 0.5)
let ta = Finish.cover(track_d, px) * 0.7
var out = vec4(an_line_c() * ta, ta)
let la = Finish.cover(lit_d, px)
let ink = mix(mix(an_bronze_c(), an_gold_hi(), 0.55), vec3(0.75, 0.72, 0.64), dis)
out = Finish.over(out, vec4(ink * la, la))
let le = Finish.band(lit_d, 0.0, max(px, 0.4), px) * 0.6
return Finish.over(out, vec4(an_bronze_dk() * le, le))
}
mod.widgets.RotaryKnob.clip_x = false
mod.widgets.RotaryKnob.clip_y = false
mod.widgets.RotaryKnob.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.RotaryKnob.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 5.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let live = 1.0 - self.disabled
let dia = min(size.x, size.y)
let rr = dia * 0.5 - 1.0
let c = vec2(Finish.snap(o.x + size.x * 0.5, px) - o.x, Finish.snap(o.y + size.y - dia * 0.5, px) - o.y)
let gap = self.gap * PI / 180.0
let aw = 1.8
let ar = floor((rr - aw * 0.5) / px) * px
let rad = floor((ar - aw * 0.5 - 2.4) / px) * px
let lo = min(self.origin_pos, self.slide_pos)
let hi = max(self.origin_pos, self.slide_pos)
let v0 = mix(0.0, lo, self.arc_origin)
let v1 = mix(self.slide_pos, hi, self.arc_origin)
var out = md_arc(p, c, ar, aw, px, v0, v1, gap, self.disabled)
let ang = gap * 0.5 + (2.0 * PI - gap) * self.slide_pos
out = Finish.over(out, an_knob(p, c, rad, px, ang, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
return Finish.over(out, md_focus(length(p - c) - ar - aw * 0.5, px, self.focus * live))
}
// Knob captions are small engraved words in the sepia ink.
mod.widgets.RotaryKnob.draw_text.color = #3a2e1f
mod.widgets.RotaryKnob.draw_text.text_style = mod.theme.font_bold{font_size: 9.0}
// The dial: the knob above set in a deep teal bezel ring between two bronze
// wires, gold pips on the bezel every thirtieth of a turn, the value in gold
// round the bezel.
mod.widgets.RotaryFlat.clip_x = false
mod.widgets.RotaryFlat.clip_y = false
mod.widgets.RotaryFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.RotaryFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 5.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let live = 1.0 - self.disabled
let room = vec2(size.x, size.y - 24.0)
let dia = min(room.x, room.y)
let rr = dia * 0.5 - 1.0
let c = vec2(Finish.snap(o.x + size.x * 0.5, px) - o.x, Finish.snap(o.y + 24.0 + room.y * 0.5, px) - o.y)
let gap = self.gap * PI / 180.0
let lo = min(self.origin_pos, self.slide_pos)
let hi = max(self.origin_pos, self.slide_pos)
let v0 = mix(0.0, lo, self.arc_origin)
let v1 = mix(self.slide_pos, hi, self.arc_origin)
// the bezel: a teal ring between two wires
let q = p - c
let n = q / max(length(q), 0.001)
let bw = max(5.0, rr * 0.20)
let d_out = length(q) - rr
let d_in = length(q) - (rr - bw)
var out = an_drop(p, c, vec2(rr, rr), rr, mix(1.0, 0.4, self.disabled))
let ba = Finish.cover(d_out, px)
let bcol = an_enamel(p, c, vec2(rr, rr), rr, mix(an_teal_dk(), vec3(0.6, 0.6, 0.55), self.disabled), 0.0, 0.0)
out = Finish.over(out, vec4(bcol * ba, ba))
// the value round the bezel
let ar = rr - bw * 0.5
out = Finish.over(out, md_arc(p, c, ar, bw * 0.38, px, v0, v1, gap, self.disabled))
// the pips
var ang = atan2(0.0 - q.x, q.y)
if ang < 0.0 {
ang = ang + 2.0 * PI
}
let st = 2.0 * PI / 24.0
let ai = floor(ang / st + 0.5) * st
let pc = c + vec2(0.0 - sin(ai), cos(ai)) * ar
let on_track = step(gap * 0.5 - 0.01, ai) * step(ai, 2.0 * PI - gap * 0.5 + 0.01)
let pd = length(p - pc) - max(0.7, bw * 0.12)
let pa = Finish.cover(pd, px) * on_track * 0.85
out = Finish.over(out, vec4(an_gold_hi() * pa, pa))
out = Finish.over(out, an_wire(d_out, n, 1.6, px, live) * mix(1.0, 0.55, self.disabled))
out = Finish.over(out, an_wire(0.0 - d_in - 1.4, n * -1.0, 1.4, px, live) * mix(1.0, 0.55, self.disabled) * Finish.cover(d_out, px))
let rad = rr - bw - 1.5
let kang = gap * 0.5 + (2.0 * PI - gap) * self.slide_pos
out = Finish.over(out, an_knob(p, c, rad, px, kang, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
return Finish.over(out, md_focus(d_out, px, self.focus * live))
}
// The dial's readout: a small pill of the screens' deep teal glass in its
// bronze wire, amber figures.
let md_lcd = fn(p: vec2, size: vec2, o: vec2, px: float) -> vec4 {
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o + vec2(0.0, px)
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o - vec2(0.0, 2.0 * px)
let h = (hi - lo) * 0.5
let c = lo + h
let r = h.y
let d = md_rbox(p, c, h, r)
let t = clamp((p.y - lo.y) / max(2.0 * h.y, 1.0), 0.0, 1.0)
var col = an_glass_c() * (1.12 - 0.2 * t)
col = mix(col, an_shade(), clamp(Finish.inset(p, c, h, r, vec2(0.0, 1.2), 1.0), 0.0, 1.0) * 0.5)
let a = Finish.cover(d, px)
var out = an_drop(p, c, h, r, 0.6)
out = Finish.over(out, vec4(col * a, a))
return Finish.over(out, an_wire(d, an_nrm(p, c, h, r), 1.3, px, 1.0))
}
mod.widgets.RotaryFlat.text_input.padding = mod.turtle.Inset{left: 7.0 right: 7.0 top: 2.0 bottom: 2.0}
mod.widgets.RotaryFlat.text_input.min_height = 20.0
mod.widgets.RotaryFlat.text_input.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return md_lcd(self.pos * self.rect_size, self.rect_size, self.rect_pos, px)
}
mod.widgets.RotaryFlat.text_input.draw_text.color = #ecc77c
mod.widgets.RotaryFlat.text_input.draw_text.color_hover = #f4d692
mod.widgets.RotaryFlat.text_input.draw_text.color_focus = #f4d692
mod.widgets.RotaryFlat.text_input.draw_text.color_empty = #a89060
mod.widgets.RotaryFlat.text_input.draw_text.text_style = mod.theme.font_label{font_size: 10.0}
// ---------------------------------------------------------------------------
// TEXT FIELDS. A sunk ivory well in its bronze wire, its corners rounded
// half way to a pill; focused, a teal line inside the wire and the ivory
// takes a breath of sage. Read only, the well is a deeper parchment.
let md_field = fn(p: vec2, size: vec2, o: vec2, px: float, hover: float, focus: float, dis: float, r: float) -> vec4 {
let ins = 1.5
let lo = vec2(Finish.snap(o.x + ins, px), Finish.snap(o.y + ins, px)) - o
let hi = vec2(Finish.snap(o.x + size.x - ins, px), Finish.snap(o.y + size.y - ins, px)) - o - vec2(0.0, px)
let h = (hi - lo) * 0.5
let c = lo + h
let rr = min(r, h.y)
let d = md_rbox(p, c, h, rr)
var fill = Finish.lift(an_well_c(), 0.30 * hover * (1.0 - dis))
fill = mix(fill, vec3(0.855, 0.867, 0.773), focus * 0.6)
fill = mix(fill, vec3(0.827, 0.749, 0.600), dis)
var out = an_well(p, c, h, rr, px, fill, mix(0.32, 0.18, dis), 1.3, dis)
let il = Finish.band(d, 2.0, 2.0 + max(px, 0.5), px) * mix(0.55, 0.25, dis) * (1.0 - focus)
out = Finish.over(out, vec4(an_teal_dk() * il, il))
let fl = Finish.band(d, 1.3, 1.3 + 1.2 * max(px, 0.75), px) * focus * (1.0 - dis)
return Finish.over(out, vec4(vec3(0.184, 0.435, 0.384) * fl, fl))
}
mod.widgets.TextInputFlat.padding = mod.turtle.Inset{left: 10.0 right: 10.0 top: 4.0 bottom: 4.0}
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 = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return md_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled, 14.0)
}
mod.widgets.TextInputFlat.draw_text.color_empty = #6a5940
mod.widgets.TextInputFlat.draw_text.color_disabled = #7a6a50
mod.widgets.TextInputFlat.draw_cursor.color = #2a2116
// NUMBER FIELDS. The ivory well, and on its right a column split by a fine
// bronze line, a small chevron in each half; the half under the pointer
// lifts, the held half darkens.
mod.widgets.NumberField.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return md_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled, 14.0)
}
mod.widgets.NumberField.pad_left = 10.0
mod.widgets.NumberSpin.width = 16.0
mod.widgets.NumberSpin.draw_text.color = #00000000
mod.widgets.NumberSpin.draw_text.color_disabled = #00000000
let an_chev = fn(p: vec2, c: vec2, hw: float, hh: float, dir: float) -> float {
// a chevron hw either side, hh tall, pointing up (dir -1) or down (1)
let a = c + vec2(0.0 - hw, 0.0 - hh * dir * 0.5)
let b = c + vec2(0.0, hh * dir * 0.5)
let e = c + vec2(hw, 0.0 - hh * dir * 0.5)
return min(md_seg(p, a, b), md_seg(p, b, e))
}
mod.widgets.NumberSpin.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let o = self.rect_pos
let live = 1.0 - self.disabled
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y + 3.0, px)) - o
let hi = vec2(Finish.snap(o.x + self.rect_size.x - 3.0, px), Finish.snap(o.y + self.rect_size.y - 3.0, px)) - o
let mid = Finish.snap(o.y + (lo.y + hi.y) * 0.5, px) - o.y
let upper = step(p.y, mid)
let hot = mix(max(0.0, 0.0 - self.hot), max(0.0, self.hot), upper) * live
let dn = mix(max(0.0, 0.0 - self.down), max(0.0, self.down), upper) * live
let inx = step(lo.x, p.x) * step(p.x, hi.x) * step(lo.y, p.y) * step(p.y, hi.y)
let wash = inx * (0.10 * hot + 0.12 * dn)
var out = vec4(mix(vec3(1.0, 0.97, 0.9), an_shade(), step(0.01, dn)) * wash, wash)
// the fine bronze line down its left and across its middle
let vl = step(lo.x, p.x) * step(p.x, lo.x + max(px, 0.6)) * step(lo.y, p.y) * step(p.y, hi.y) * 0.8
let hl = step(abs(p.y - mid), max(px, 0.6) * 0.5) * step(lo.x, p.x) * step(p.x, hi.x) * 0.6
let la = max(vl, hl)
out = Finish.over(out, vec4(an_line_c() * la, la))
let cx = (lo.x + hi.x) * 0.5 + 0.5
let cu = an_chev(p, vec2(cx, (lo.y + mid) * 0.5), 2.8, 2.6, -1.0) - 0.55
let cd = an_chev(p, vec2(cx, (mid + hi.y) * 0.5), 2.8, 2.6, 1.0) - 0.55
let ti = Finish.cover(min(cu, cd), px)
let ink = mix(an_ink(), vec3(0.6, 0.53, 0.42), self.disabled)
return Finish.over(out, vec4(ink * ti, ti))
}
// The drag-number: the ivory well, its track a pale teal wash behind the
// ink.
mod.widgets.ValueInput.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
var out = md_field(p, self.rect_size, self.rect_pos, px, max(self.hover, self.drag), self.focus, 0.0, 14.0)
let d = md_rbox(p, self.rect_size * 0.5, self.rect_size * 0.5 - vec2(3.0, 3.0), 6.0)
let wx = 3.0 + (self.rect_size.x - 6.0) * max(self.fill, 0.0)
let wash = step(0.0, self.fill) * step(p.x, wx) * Finish.cover(d, px) * 0.22
return Finish.over(out, vec4(an_teal_lt() * wash, wash))
}
mod.widgets.ValueInput.draw_text.color = #2a2116
mod.widgets.ValueInput.draw_text.text_style = mod.theme.font_label{font_size: 10.0 line_spacing: 1.0}
mod.widgets.ValueInput.arrow_color = #5a4a34
mod.widgets.ValueInput.text_input.draw_bg.color = #00000000
// ---------------------------------------------------------------------------
// DROP-DOWNS. The ivory pill with a chevron on the right, set off by a fine
// bronze line. Open, the pill is held pressed in.
let md_dropdown = fn(pos0: vec2, size0: vec2, o0: vec2, px: float, hover: float, held: float, focus: float, dis: float, ins: float) -> vec4 {
let m = 6.0
let p = pos0 * (size0 + vec2(2.0 * m, 2.0 * m)) - vec2(m, m) - vec2(0.0, ins)
let size = size0 - vec2(0.0, 2.0 * ins)
let o = o0 + vec2(0.0, ins)
let pos = (p + vec2(m, m)) / (size + vec2(2.0 * m, 2.0 * m))
var out = an_button(pos, size, o, px, hover, held, focus, dis, 0.0, 0.0, vec4(99.0, 99.0, 99.0, 99.0), 7.0, m)
let live = 1.0 - dis
let dx = Finish.snap(o.x + size.x - 24.0, px) - o.x
let in_y = step(size.y * 0.22, p.y) * step(p.y, size.y * 0.78)
let dl = step(dx - max(px, 0.6) * 0.5, p.x) * step(p.x, dx + max(px, 0.6) * 0.5) * in_y * mix(0.8, 0.4, dis)
out = Finish.over(out, vec4(an_line_c() * dl, dl))
let cc = vec2(Finish.snap(o.x + size.x - 12.5, px) - o.x, size.y * 0.5 + held * live * px)
let ch = an_chev(p, cc, 3.4, 3.2, 1.0) - 0.65
let tri = Finish.cover(ch, px)
let ink = mix(an_ink(), vec3(0.6, 0.53, 0.42), dis)
return Finish.over(out, vec4(ink * tri, tri))
}
mod.widgets.DropDownFlat.padding = mod.turtle.Inset{left: 12.0 right: 32.0 top: 4.0 bottom: 4.0}
mod.widgets.DropDown.padding = mod.turtle.Inset{left: 12.0 right: 32.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 g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.DropDownFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return md_dropdown(self.pos, self.rect_size, self.rect_pos, px, self.hover, max(self.down, self.active), self.focus, self.disabled, 1.0)
}
mod.widgets.DropDownFlat.draw_text.text_style = mod.theme.font_label{font_size: 10.5}
mod.widgets.Select.clip_x = false
mod.widgets.Select.clip_y = false
mod.widgets.MultiSelect.clip_x = false
mod.widgets.MultiSelect.clip_y = false
mod.widgets.Select.face.clip_x = false
mod.widgets.Select.face.clip_y = false
mod.widgets.MultiSelect.face.clip_x = false
mod.widgets.MultiSelect.face.clip_y = false
mod.widgets.Select.face.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.MultiSelect.face.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.Select.face.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return md_dropdown(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, 0.0)
}
mod.widgets.MultiSelect.face.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return md_dropdown(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, 0.0)
}
mod.widgets.Select.face.padding.right = 32.0
mod.widgets.MultiSelect.face.padding.right = 32.0
// The combo box: the ivory well one types into, its chevron behind a fine
// bronze line at the right end.
mod.widgets.ComboBox.padding = mod.turtle.Inset{left: 10.0 right: 32.0 top: 4.0 bottom: 4.0}
mod.widgets.ComboBox.input.min_height = 0.0
mod.widgets.ComboBox.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let size = self.rect_size
let o = self.rect_pos
let live = 1.0 - self.disabled
var out = md_field(p, size, o, px, self.hover, self.focus, self.disabled, 14.0)
let dx = Finish.snap(o.x + size.x - 24.0, px) - o.x
let in_y = step(size.y * 0.25, p.y) * step(p.y, size.y * 0.75)
let dl = step(dx - max(px, 0.6) * 0.5, p.x) * step(p.x, dx + max(px, 0.6) * 0.5) * in_y * mix(0.8, 0.4, self.disabled)
out = Finish.over(out, vec4(an_line_c() * dl, dl))
let held = self.down * self.hover * live
let cc = vec2(Finish.snap(o.x + size.x - 12.5, px) - o.x, size.y * 0.5 + held * px)
let tri = Finish.cover(an_chev(p, cc, 3.4, 3.2, 1.0) - 0.65, px)
let ink = mix(an_ink(), vec3(0.6, 0.53, 0.42), self.disabled)
return Finish.over(out, vec4(ink * tri, tri))
}
// ---------------------------------------------------------------------------
// SEGMENTS AND TABS. One ivory pill across the strip in its wire; the chosen
// segment is a teal enamel pill pressed into it, its word ivory.
let md_strip = fn(p: vec2, size: vec2, o: vec2, px: float) -> vec4 {
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = md_rbox(p, c, h, h.y)
var out = an_drop(p, c, h, h.y, 0.7)
let hf = h - vec2(1.5, 1.5)
let face = an_enamel(p, c, hf, hf.y, vec3(0.906, 0.851, 0.737), 0.0, 0.0)
let fa = Finish.cover(d + 0.75, px)
out = Finish.over(out, vec4(face * fa, fa))
return Finish.over(out, an_wire(d, an_nrm(p, c, h, h.y), 1.5, px, 1.0))
}
// The chosen cell of a strip: an enamel pill of `base` inside the cell,
// pressed in, in a wire of its own.
let md_cell = fn(p: vec2, lo: vec2, hi: vec2, px: float, base: vec3) -> vec4 {
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = md_rbox(p, c, h, h.y)
let hf = h - vec2(1.2, 1.2)
let face = an_enamel(p, c, hf, hf.y, base, 0.0, 0.6)
let a = Finish.cover(d + 0.6, px)
var out = vec4(face * a, a)
return Finish.over(out, an_wire(d, an_nrm(p, c, h, h.y), 1.2, px, 1.0))
}
mod.widgets.SegmentedControlFlat.segment_height = 22.0
mod.widgets.SegmentedControlFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.SegmentedControlFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 5.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
return md_strip(p, self.rect_size, self.rect_pos, px)
}
// The pill is the segment's own box: its quad grows a point each way so the
// chosen pill stands just inside the strip's wire.
mod.widgets.SegmentedControlFlat.draw_pill.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(1.0, 1.0), self.rect_size + vec2(2.0, 2.0))
}
mod.widgets.SegmentedControlFlat.draw_pill.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let o = self.rect_pos
let size = self.rect_size
let p = self.pos * (size + vec2(2.0, 2.0)) - vec2(1.0, 1.0)
let lo = vec2(Finish.snap(o.x - 0.5, px), Finish.snap(o.y - 0.5, px)) - o
let hi = vec2(Finish.snap(o.x + size.x + 0.5, px), Finish.snap(o.y + size.y + 0.5, px)) - o
return md_cell(p, lo, hi, px, an_teal())
}
mod.widgets.SegmentedControlFlat.draw_text.color = #3a2e1f
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #f6eedb
mod.widgets.SegmentedControlFlat.palette.on_neutral_container = #f6eedb
mod.widgets.SegmentedControlFlat.draw_text.text_style = mod.theme.font_label{font_size: 10.5}
mod.widgets.SegmentedControlFlat.draw_text_selected.text_style = mod.theme.font_label{font_size: 10.5}
// The tab strip: the same pill; the tab in use an ochre enamel pill pressed
// into it, a hovered tab lifts a little, the quiet ones parted by a fine
// bronze line with a gold dot at its middle.
mod.widgets.Tabs.height = 26.0
mod.widgets.Tabs.tab_min_width = 46.0
mod.widgets.Tabs.clip_x = false
mod.widgets.Tabs.clip_y = false
mod.widgets.Tabs.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.Tabs.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 5.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
return md_strip(p, self.rect_size, self.rect_pos, px)
}
mod.widgets.Tabs.draw_tab.pixel = fn() {
return vec4(0.0, 0.0, 0.0, 0.0)
}
mod.widgets.Tabs.draw_tab_hover.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let h = self.rect_size * 0.5 - vec2(2.0, 2.5)
let d = md_rbox(p, self.rect_size * 0.5, h, h.y)
let a = Finish.cover(d, px) * 0.35
return vec4(vec3(1.0, 0.98, 0.92) * a, a)
}
mod.widgets.Tabs.draw_tab_active.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let o = self.rect_pos
let size = self.rect_size
let lo = vec2(Finish.snap(o.x + 1.0, px), Finish.snap(o.y + 1.5, px)) - o
let hi = vec2(Finish.snap(o.x + size.x - 1.0, px), Finish.snap(o.y + size.y - 1.5, px)) - o
return md_cell(self.pos * size, lo, hi, px, an_ochre())
}
mod.widgets.Tabs.draw_sep.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let x = self.rect_size.x * 0.5
let y = self.rect_size.y * 0.5
let ln = step(abs(p.x - x), max(px, 0.6) * 0.5) * step(abs(p.y - y), self.rect_size.y * 0.28) * 0.7
let dot = Finish.cover(length(p - vec2(x, y)) - 1.2, px)
let a = max(ln, dot)
return vec4(mix(an_line_c(), an_gold_hi(), dot * 0.4) * a, a)
}
mod.widgets.Tabs.draw_mark.color = #3a2e1f
mod.widgets.Tabs.draw_mark.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
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
// the add mark a plus in the middle of its room; the close mark a small
// cross tucked into the top right of its room, clear of the word
let a = mix(0.7853981634, 0.0, self.plus)
let r = mix(2.0, 3.6, self.plus)
let cm = mix(vec2(self.rect_size.x - 3.0, 4.5), c, self.plus)
let u = vec2(cos(a), sin(a)) * r
let v = vec2(0.0 - sin(a), cos(a)) * r
let dd = min(md_seg(p, cm - u, cm + u), md_seg(p, cm - v, cm + v)) - mix(0.5, 0.6, self.plus)
let k = Finish.cover(dd, px) * self.color.a
return vec4(self.color.rgb * k, k)
}
mod.widgets.Tabs.draw_text.color = #3a2e1f
mod.widgets.Tabs.draw_text_active.color = #1f180f
mod.widgets.Tabs.draw_text.text_style = mod.theme.font_label{font_size: 9.0 top_drop: 0.22}
mod.widgets.Tabs.draw_text_active.text_style = mod.theme.font_label{font_size: 9.0 top_drop: 0.22}
// ---------------------------------------------------------------------------
// PROGRESS. A channel of deep teal glass in its bronze wire; the value a
// sage-teal enamel combed with the fine veins of a long feather-like leaf,
// a gold midrib down it. The unknown value is a still run of leaves across
// the whole channel: a face may not run a clock.
mod.widgets.ProgressBarFlat.height = 12.0
mod.widgets.ProgressBarFlat.draw_bg.thickness = uniform(12.0)
mod.widgets.ProgressBarFlat.clip_x = false
mod.widgets.ProgressBarFlat.clip_y = false
mod.widgets.ProgressBarFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 3.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.ProgressBarFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 3.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let th = min(self.thickness, size.y)
let top = Finish.snap(o.y + (size.y - th) * 0.5, px) - o.y
let bot = Finish.snap(o.y + (size.y + th) * 0.5, px) - o.y
let x0 = Finish.snap(o.x + size.x * self.start, px) - o.x
let x1 = Finish.snap(o.x + size.x * self.end, px) - o.x
let h = vec2((x1 - x0) * 0.5, (bot - top) * 0.5)
let c = vec2(x0, top) + h
let r = h.y
let d = md_rbox(p, c, h, r)
let dis = step(4.5, self.intent)
var out = an_well(p, c, h, r, px, mix(vec3(0.150, 0.235, 0.212), vec3(0.62, 0.62, 0.56), dis), 0.55, 1.3, dis)
var v1 = x0 + (x1 - x0) * clamp(self.value, 0.0, 1.0)
var unknown = 0.0
if self.value < 0.0 {
v1 = x1
unknown = 1.0
}
let fh = vec2(max((v1 - x0) * 0.5 - 1.3, 0.0), h.y - 1.3)
let fc = vec2((x0 + v1) * 0.5, c.y)
let fd = md_rbox(p, fc, fh, min(fh.y, fh.x + fh.y))
var base = vec3(0.447, 0.616, 0.537)
if self.intent > 0.5 {
base = self.fill_color().rgb
}
base = mix(base, vec3(0.70, 0.69, 0.60), dis)
var fcol = an_enamel(p, fc, fh, fh.y, base, 0.0, 0.0)
// the veins: fine pale strokes slanting back from the leading edge,
// wavering a little so no two run alike
let vy = (p.y - c.y) / max(h.y, 1.0)
let vx = p.x - abs(vy) * 6.0 + (Finish.noise(vec2(p.x * 0.15, 3.0)) - 0.5) * 2.0
let fv = abs(fract(vx / 3.6) - 0.5) * 3.6
let vln = (1.0 - smoothstep(0.3, 0.3 + px * 1.5, fv)) * 0.30
fcol = mix(fcol, Finish.lift(fcol, 0.45), vln)
fcol = mix(fcol, fcol * 0.8, smoothstep(0.35, 1.0, abs(vy)) * 0.5)
// the midrib along the value
let rib = (1.0 - smoothstep(0.3, 0.3 + px, abs(p.y - c.y))) * 0.35
fcol = mix(fcol, an_gold_hi(), rib)
// the unknown value: leaves laid end to end
if unknown > 0.5 {
let cell = 12.0
let lx = p.x - floor(p.x / cell) * cell
let lf = an_leaf(vec2(lx, p.y), vec2(1.0, c.y + 1.5), normalize(vec2(1.0, -0.25)), cell - 2.0, h.y * 0.55, 0.0)
let la = Finish.cover(lf.x, px)
fcol = mix(vec3(0.150, 0.235, 0.212), mix(vec3(0.30, 0.46, 0.40), vec3(0.50, 0.66, 0.57), smoothstep(-0.2, 0.2, lf.y)), la)
fcol = mix(fcol, an_line_c(), Finish.band(lf.x, 0.0, max(px, 0.45), px))
}
let fa = Finish.cover(fd, px) * step(0.01, v1 - x0)
out = Finish.over(out, vec4(fcol * fa, fa))
return out * self.track * self.opacity
}
// ---------------------------------------------------------------------------
// LEVEL METERS. A dark teal well of cells four points long with a point
// between: unlit cells the deep teal glass, lit ones a sage green, ochre from
// 70 % and terracotta from 90 %; the hold an ivory line; the over lamp a
// small jewel.
mod.widgets.LevelMeter.height = 8.0
mod.widgets.LevelMeterColumn.width = 8.0
mod.widgets.LevelMeter.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let vert = self.vertical
let along = mix(self.rect_size.x, self.rect_size.y, vert)
let across = mix(self.rect_size.y, self.rect_size.x, vert)
let a = mix(p.x, self.rect_size.y - p.y, vert)
let b = mix(p.y, p.x, vert)
let q = vec2(a, b)
let th = min(7.0, across)
let lamp_room = self.lamp * (th + 4.0)
let bar = max(4.0, along - lamp_room)
let h = vec2(bar * 0.5, th * 0.5)
let c = vec2(h.x, across * 0.5)
let r = 2.0
let d = md_rbox(q, c, h, r)
let pad = 1.5
let span = max(1.0, bar - pad * 2.0)
let share = clamp((a - pad) / span, 0.0, 1.0)
let pitch = 5.0
let cell = fract((a - pad) / pitch)
let in_cell = step(cell, 4.0 / pitch)
let lit = step(a, pad + span * self.level) * step(pad, a) * in_cell
let ramp = mix(mix(mix(vec3(0.45, 0.66, 0.52), vec3(0.70, 0.76, 0.44), smoothstep(0.0, 0.7, share)), vec3(0.92, 0.72, 0.34), step(0.7, share)), vec3(0.86, 0.40, 0.27), step(0.9, share))
let well = vec3(0.12, 0.19, 0.17)
var fill = mix(well, ramp, lit)
let cell_row = in_cell * step(pad, a) * step(a, pad + span)
fill = mix(fill, mix(well, vec3(0.2, 0.3, 0.27), 0.5), (1.0 - lit) * cell_row * step(1.2, abs(b - c.y) + 1.2))
let bt = b - (c.y - h.y)
fill = mix(fill, vec3(1.0, 0.97, 0.88), lit * step(1.0, bt) * step(bt, 1.0 + px) * 0.3)
if self.hold > 0.002 {
let at = pad + span * self.hold
let hm = step(abs(a - at), px) * step(abs(b - c.y), h.y)
fill = mix(fill, an_ivory(), hm)
}
let ca = Finish.cover(d, px)
var out = vec4(fill * ca, ca)
let e = Finish.band(d, 0.0, max(px, 0.7), px)
out = Finish.over(out, vec4(an_line_c() * e, e))
if self.lamp > 0.5 {
let la = bar + 2.0 + th * 0.5 + 1.0
let lc = vec2(mix(la, across * 0.5, vert), mix(across * 0.5, self.rect_size.y - la, vert))
out = Finish.over(out, md_led(p, lc, 2.6, px, vec3(0.90, 0.30, 0.20), step(0.5, self.over), 0.3))
}
return out * self.opacity
}
// ---------------------------------------------------------------------------
// SCROLL BARS. A bronze hairline down the length of the view and a slim teal
// enamel pill riding on it in its own wire.
mod.widgets.ScrollBar.bar_size = 10.0
mod.widgets.ScrollBar.bar_side_margin = 0.0
mod.widgets.ScrollBar.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let vert = self.is_vertical
let a = mix(p.x, p.y, vert)
let b = mix(p.y, p.x, vert)
let along = mix(self.rect_size.x, self.rect_size.y, vert)
let across = mix(self.rect_size.y, self.rect_size.x, vert)
let q = vec2(a, b)
let mid = across * 0.5
let rail = (1.0 - smoothstep(0.35, 0.35 + px, abs(b - mid))) * 0.55
var out = vec4(an_line_c() * rail, rail)
let t0 = along * self.norm_scroll
let tl = along * self.norm_handle
let th = vec2(max(tl * 0.5 - 1.0, 4.0), 3.5)
let tc = vec2(t0 + tl * 0.5, mid)
let td = md_rbox(q, tc, th, th.y)
let hf = th - vec2(1.0, 1.0)
let face = an_enamel(q, tc, hf, hf.y, Finish.lift(an_teal(), 0.12 * self.hover), 0.0, 0.3 * self.drag)
let ta = Finish.cover(td, px)
out = Finish.over(out, vec4(face * ta, ta))
let e = Finish.band(td, 0.0, 1.0, px)
out = Finish.over(out, vec4(mix(an_bronze_c(), an_gold_hi(), 0.3) * e, e))
return out * self.opacity
}
// ---------------------------------------------------------------------------
// VIEWS AND PANELS. A raised panel is sage enamel in a bronze wire with a
// fine gold line inside it, a sprig of leaves and a bud in its top right and
// bottom right corners; the sunken one a well of deeper parchment under the
// same wire, sprigs at its bottom corners. The words on both keep the sepia
// ink, at better than 6:1.
let md_plate = fn(p: vec2, size: vec2, o: vec2, px: float, r: float, sunken: float) -> vec4 {
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o
let h = (hi - lo) * 0.5
let c = lo + h
let d = md_rbox(p, c, h, r)
let n = an_nrm(p, c, h, r)
var out = vec4(0.0, 0.0, 0.0, 0.0)
let s = clamp(min(size.x, size.y) / 80.0, 0.55, 1.1)
if sunken > 0.5 {
var face = an_parch(p, px, vec3(0.545, 0.616, 0.561))
let sh = clamp(Finish.inset(p, c, h - vec2(1.6, 1.6), r - 1.6, vec2(0.0, 2.0), 2.4), 0.0, 1.0)
face = mix(face, an_shade(), sh * 0.38)
let a = Finish.cover(d, px)
let lip = Finish.ring_out(d, 0.0, px, px) * smoothstep(0.0, h.y, p.y - c.y) * 0.5
out = Finish.over(out, vec4(vec3(0.98, 0.95, 0.88) * lip, lip))
out = Finish.over(out, vec4(face * a, a))
out = an_corner(out, p, lo, hi, 1.0, 3.5, s * 0.8, px)
out = an_corner(out, p, lo, hi, 2.0, 3.5, s, px)
out = Finish.over(out, an_fine(d, 3.4, max(px, 0.6), px, 0.6))
return Finish.over(out, an_wire(d, n, 1.6, px, 1.0))
}
out = an_drop(p, c, h, r, 1.0)
var face = an_enamel(p, c, h - vec2(1.6, 1.6), r - 1.6, vec3(0.580, 0.663, 0.596), 0.0, 0.0)
face = face + g3(Finish.grain(p / px, 0.008))
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = an_corner(out, p, lo, hi, 1.0, 3.5, s * 0.8, px)
out = an_corner(out, p, lo, hi, 2.0, 3.5, s, px)
out = Finish.over(out, an_fine(d, 3.4, max(px, 0.6), px, 0.7))
return Finish.over(out, an_wire(d, n, 1.6, px, 1.0))
}
mod.widgets.PanelView.clip_x = false
mod.widgets.PanelView.clip_y = false
mod.widgets.PanelView.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.PanelView.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 5.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
return md_plate(p, self.rect_size, self.rect_pos, px, 10.0, self.sunken)
}
// The card is an ivory leaf of parchment in a bronze wire and a fine gold
// line, a sprig in its bottom right corner and a smaller one top right. It
// lifts a little under the pointer.
mod.widgets.Card.radius = 10.0
mod.widgets.Card.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
return self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.Card.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 5.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o
let h = (hi - lo) * 0.5
let c = lo + h
let r = 10.0
let d = md_rbox(p, c, h, r)
var out = an_drop(p, c, h, r, 1.0 + 0.4 * self.hover)
var face = an_parch(p, px, vec3(0.925, 0.878, 0.776))
face = Finish.lift(face, 0.05 * self.hover - 0.0 * self.down)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
let s = clamp(min(size.x, size.y) / 80.0, 0.55, 1.1)
out = an_corner(out, p, lo, hi, 1.0, 3.5, s * 0.8, px)
out = an_corner(out, p, lo, hi, 2.0, 3.5, s, px)
out = Finish.over(out, an_fine(d, 3.4, max(px, 0.6), px, 0.75))
return Finish.over(out, an_wire(d, an_nrm(p, c, h, r), 1.6, px, 1.0))
}
// The plain rounded view keeps the colours its caller gives it; the edge is
// a snapped hairline at the true radius.
mod.widgets.RoundedView.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
var fill = self.color
if self.color_2.x > -0.5 {
fill = mix(self.color, self.color_2, mix(self.pos.y, self.pos.x, self.gradient_fill_horizontal))
}
let lo = vec2(self.border_inset.x, self.border_inset.y)
let hi = self.rect_size - vec2(self.border_inset.z, self.border_inset.w)
let h = (hi - lo) * 0.5
let c = lo + h
let d = md_rbox(p, c, h, max(self.border_radius * 2.0, 1.0))
var col = fill
if self.border_size > 0.0 {
col = mix(col, self.border_color, Finish.band(d, 0.0, max(self.border_size, px), px) * self.border_color.a)
}
let a = Finish.cover(d, px) * col.a
return vec4(col.rgb * a, a)
}
// ---------------------------------------------------------------------------
// LISTS. Rows of 22 points on the parchment, parted by a faint bronze
// hairline; a hovered row takes a breath of ochre; the chosen row is an
// ochre enamel pill in its bronze wire, ending short of the scroll bar.
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.ListItem.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let o = self.rect_pos
let live = 1.0 - self.disabled
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
let hi = vec2(Finish.snap(o.x + self.rect_size.x, px), Finish.snap(o.y + self.rect_size.y, px)) - o
let lay = (self.hover * 0.16 + self.down * 0.10) * live * (1.0 - self.selected)
let hd = md_rbox(p, (lo + hi) * 0.5 - vec2(3.0, 0.0), (hi - lo) * 0.5 - vec2(5.0, 1.5), 8.0)
let la = Finish.cover(hd, px) * lay
var out = vec4(an_ochre() * la, la)
if self.selected > 0.5 {
let h = vec2((hi.x - lo.x) * 0.5 - 6.0, (hi.y - lo.y) * 0.5 - 1.5)
let c = vec2(lo.x + h.x + 2.0, (lo.y + hi.y) * 0.5)
let r = h.y
let d = md_rbox(p, c, h, r)
out = Finish.over(out, an_contact(p, c, h, r, 1.0))
let face = an_enamel(p, c, h - vec2(1.3, 1.3), r - 1.3, an_ochre(), 0.0, 0.0)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, an_wire(d, an_nrm(p, c, h, r), 1.3, px, 1.0))
}
let yb = hi.y
let dk = step(yb - max(px, 0.6), p.y) * step(p.y, yb) * step(lo.x + 4.0, p.x) * step(p.x, hi.x - 8.0) * 0.30 * (1.0 - self.selected)
out = Finish.over(out, vec4(an_line_c() * dk, dk))
return out
}
mod.widgets.ListItem.draw_text.color = #2a2116
// The open list of a drop-down: an ivory leaf in a bronze wire and a fine
// gold line, with a soft shadow; the row under the pointer an ochre wash,
// the chosen row a teal check.
mod.widgets.PopupMenuFlat.clip_x = false
mod.widgets.PopupMenuFlat.clip_y = false
mod.widgets.PopupMenuFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 10.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.PopupMenuFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 10.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let o = self.rect_pos
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o
let h = (hi - lo) * 0.5
let c = lo + h
let r = 8.0
let d = md_rbox(p, c, h, r)
let sh = Finish.drop(p, c, h, r, vec2(0.5, 2.5), 2.2) * 0.22
var out = vec4(an_shade() * sh, sh)
let face = an_parch(p, px, vec3(0.925, 0.878, 0.776))
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, an_fine(d, 3.0, max(px, 0.6), px, 0.7))
return Finish.over(out, an_wire(d, an_nrm(p, c, h, r), 1.5, px, 1.0))
}
mod.widgets.PopupMenuItem.padding.left = 18.0
mod.widgets.PopupMenuItem.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let c = self.rect_size * 0.5
let h = c - vec2(3.0, 0.5)
let d = md_rbox(p, c, h, h.y)
let live = 1.0 - self.disabled
let wa = Finish.cover(d, px) * self.hover * live * 0.45
var out = vec4(an_ochre() * wa, wa)
let m0 = vec2(5.0, c.y + 0.2)
let m1 = vec2(7.2, c.y + 2.4)
let m2 = vec2(10.6, c.y - 2.6)
let mk = Finish.cover(min(md_seg(p, m0, m1), md_seg(p, m1, m2)) - 0.75, px) * self.active
return Finish.over(out, vec4(an_teal_dk() * mk, mk))
}
// The dock's tabs: the bar is the parchment with a bronze line at its foot;
// the tab in use an ivory leaf rounded at the top in its wire, open at the
// foot where it joins the panel below; the others lift under the pointer.
mod.widgets.Tab.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 4.0 bottom: 4.0}
mod.widgets.Tab.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let o = self.rect_pos
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y + 2.0, px)) - o
let hi = vec2(Finish.snap(o.x + self.rect_size.x, px), Finish.snap(o.y + self.rect_size.y, px)) - o + vec2(0.0, 8.0)
let h = (hi - lo) * 0.5
let c = lo + h
let r = 7.0
let d = md_rbox(p, c, h, r)
let face = an_parch(p, px, vec3(0.925, 0.878, 0.776))
let a = Finish.cover(d, px) * self.active
var out = vec4(face * a, a)
out = Finish.over(out, an_wire(d, an_nrm(p, c, h, r), 1.3, px, 1.0) * self.active)
let lift = Finish.cover(d, px) * 0.18 * self.hover * (1.0 - self.active)
return Finish.over(vec4(vec3(1.0, 0.97, 0.9) * lift, lift), out)
}
mod.widgets.TabBar.draw_fill.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let yb = Finish.snap(self.rect_pos.y + self.rect_size.y, px) - self.rect_pos.y
let dk = step(yb - 1.3, p.y) * step(p.y, yb) * 0.9
return vec4(an_line_c() * dk, dk)
}
// ---------------------------------------------------------------------------
// CHIPS AND BADGES. A chip is a small ivory pill (teal enamel while chosen);
// a badge a small teal enamel pill with ivory figures.
mod.widgets.ChipFlat.size = mod.widgets.ChipSize.Small
mod.widgets.Chip.size = mod.widgets.ChipSize.Small
mod.widgets.ChipFlat.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 6.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.ChipFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 6.0
let ch = min(22.0, self.rect_size.y)
let dy = (self.rect_size.y - ch) * 0.5
let sz = vec2(self.rect_size.x, ch)
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let pp = (p - vec2(0.0, dy) + vec2(m, m)) / (sz + vec2(2.0 * m, 2.0 * m))
let has_fill = step(0.05, self.color.a) * step(0.6, Finish.lum(self.ink.rgb))
let kind = mix(mix(6.0, 5.0, step(0.01, self.border_size)), 0.0, has_fill)
let kk = mix(7.0, kind, step(0.5, has_fill + step(0.01, self.border_size)))
return an_button(pp, sz, self.rect_pos + vec2(0.0, dy), px, self.hover, self.down, self.keyed, 0.0, 0.0, 0.0, vec4(99.0, 99.0, 99.0, 99.0), kk, m)
}
mod.widgets.ChipFlat.draw_text.text_style = mod.theme.font_label{font_size: 10.0}
mod.widgets.BadgePalette.neutral = #46635a
mod.widgets.BadgePalette.on_neutral = #f3e8cf
mod.widgets.BadgeFlat.draw_text.text_style = mod.theme.font_bold{font_size: 9.0}
mod.widgets.Badge.draw_text.text_style = mod.theme.font_bold{font_size: 9.0}
mod.widgets.BadgeFlat.clip_x = false
mod.widgets.BadgeFlat.clip_y = false
mod.widgets.BadgeFlat.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let o = self.rect_pos
let bh = min(18.0, self.rect_size.y - 2.0)
let y0 = Finish.snap(o.y + (self.rect_size.y - bh) * 0.5, px) - o.y
let lo = vec2(Finish.snap(o.x + 1.0, px) - o.x, y0)
let hi = vec2(Finish.snap(o.x + self.rect_size.x - 1.0, px) - o.x, Finish.snap(o.y + y0 + bh, px) - o.y)
let h = (hi - lo) * 0.5
let c = lo + h
let r = h.y
let d = md_rbox(p, c, h, r)
var out = an_contact(p, c, h, r, 1.0)
let face = an_enamel(p, c, h - vec2(1.2, 1.2), r - 1.2, self.color.rgb, 0.0, 0.0)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, an_wire(d, an_nrm(p, c, h, r), 1.2, px, 1.0))
return out * self.color.a
}
// The spinner keeps its stock turning face (it runs on the pass clock, which
// a face may not read), in teal on a faint track.
mod.widgets.SpinnerFlat.draw_bg.color = #46635a
mod.widgets.SpinnerFlat.draw_bg.stroke_width = 2.5
mod.widgets.SpinnerFlat.draw_bg.track_alpha = 0.25
// Rules between groups: a bronze-gold hairline with a small lozenge at its
// middle.
mod.widgets.Hr.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let y = Finish.snap(self.rect_pos.y + self.rect_size.y * 0.5, px) - self.rect_pos.y
let ln = (1.0 - smoothstep(0.35, 0.35 + px, abs(p.y - y))) * 0.8
let lq = abs(p - vec2(self.rect_size.x * 0.5, y))
let lz = Finish.cover(lq.x * 0.6 + lq.y - 2.0, px)
let a = max(ln, lz)
return vec4(an_line_c() * a, a)
}
mod.widgets.Vr.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let x = Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x
let a = (1.0 - smoothstep(0.35, 0.35 + px, abs(p.x - x))) * 0.8
return vec4(an_line_c() * a, a)
}
// ---------------------------------------------------------------------------
// THE TREE. Its rows stand on the parchment, clear; the open row is an
// ochre enamel pill in its bronze wire, as the chosen row of a list.
mod.widgets.FileTreeNode.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let lo = vec2(2.0, 1.5)
let hi = self.rect_size - vec2(4.0, 1.5)
let h = (hi - lo) * 0.5
let c = lo + h
let r = h.y
let d = md_rbox(p, c, h, r)
let face = an_enamel(p, c, h - vec2(1.2, 1.2), r - 1.2, an_ochre(), 0.0, 0.0)
let a = Finish.cover(d, px)
var out = vec4(face * a, a)
out = Finish.over(out, an_wire(d, an_nrm(p, c, h, r), 1.2, px, 1.0))
return out * self.active
}
// ---------------------------------------------------------------------------
// 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.on.from = {all: Snap}
mod.widgets.CheckBoxFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.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.ToggleFlat.animator.hover.on.from = {all: Snap}
mod.widgets.ToggleFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
mod.widgets.ToggleFlat.animator.hover.down.from = {all: Snap}
mod.widgets.ToggleFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
mod.widgets.RadioButtonFlat.animator.hover.on.from = {all: Snap}
mod.widgets.RadioButtonFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.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}}
mod.widgets.SliderFlat.animator.hover.on.from = {all: Snap}
mod.widgets.SliderFlat.animator.hover.on.apply = {draw_bg: {hover: 1.0} draw_text: {hover: 1.0}}
mod.widgets.SliderFlat.animator.drag.on.from = {all: Snap}
mod.widgets.SliderFlat.animator.drag.on.apply = {draw_bg: {drag: 1.0} draw_text: {drag: 1.0}}
// ---------------------------------------------------------------------------
// THE WINDOW'S GROUND. Parchment, held in a frame of teal enamel bands edged
// with bronze-gold wire: a band along the top where the window's caption
// stands, with a parchment cartouche under the title and lily sprays in its
// two ends; a header of parchment under it where the catalogue's toolbar
// stands; a band under the header; the panes' parchment; and a band round
// the window's other three edges. Between the panes the splitters draw the
// same band. The heavy detail is all on the bands and in the corners; the
// panes are calm, only the faint mottle and the fibre.
// A lily spray along the top band, in the band's own frame: x along it from
// its anchor, y down from the window's top edge, about 250 points long. A
// whiplash stem in two arcs ending in a tendril's curl; a second stem with a
// bud on it; a short one carrying a small dusty rose lily; leaves along
// them, turned up and down by turns; a large lily at its root.
let an_band_spray = fn(dst: vec4, q: vec2, px: float) -> vec4 {
var out = dst
if q.x > 262.0 {
return out
}
let dk = vec3(0.420, 0.580, 0.486)
let lt = vec3(0.616, 0.737, 0.627)
let stem = vec3(0.500, 0.650, 0.560)
// the curl and the two arcs of the whiplash
out = an_lay(out, an_spiral(q, vec2(246.0, 12.0), 2.2, 2.0, PI) - 0.6, stem, 0.35, px)
out = an_lay_stem(out, an_bez(q, vec2(2.0, 29.0), vec2(46.0, 0.0), vec2(118.0, 15.0)), 1.7, 1.1, stem, px)
out = an_lay_stem(out, an_bez(q, vec2(118.0, 15.0), vec2(190.0, 30.0), vec2(241.6, 12.0)), 1.1, 0.6, stem, px)
// the second stem and its bud; the short stem with the rose lily
out = an_lay_stem(out, an_bez(q, vec2(58.0, 11.0), vec2(90.0, 31.0), vec2(150.0, 26.0)), 1.0, 0.6, stem, px)
out = an_lay_stem(out, an_bez(q, vec2(146.0, 21.5), vec2(156.0, 9.0), vec2(174.0, 12.0)), 0.9, 0.6, stem, px)
out = an_lay_leaf(out, an_leaf(q, vec2(28.0, 15.0), normalize(vec2(1.0, -0.42)), 21.0, 4.0, -1.8), dk, lt, 0.5, px)
out = an_lay_leaf(out, an_leaf(q, vec2(66.0, 8.5), normalize(vec2(1.0, 0.45)), 19.0, 3.6, 1.8), dk, lt, 0.5, px)
out = an_lay_leaf(out, an_leaf(q, vec2(96.0, 13.0), normalize(vec2(1.0, -0.55)), 16.0, 3.0, -1.4), dk, lt, 0.5, px)
out = an_lay_leaf(out, an_leaf(q, vec2(104.0, 25.5), normalize(vec2(1.0, 0.15)), 15.0, 2.8, 1.2), dk, lt, 0.5, px)
out = an_lay_leaf(out, an_leaf(q, vec2(136.0, 20.5), normalize(vec2(1.0, 0.42)), 17.0, 3.2, 1.6), dk, lt, 0.5, px)
out = an_lay_leaf(out, an_leaf(q, vec2(196.0, 23.5), normalize(vec2(1.0, -0.62)), 16.0, 3.0, -1.4), dk, lt, 0.5, px)
out = an_lay_leaf(out, an_leaf(q, vec2(214.0, 20.0), normalize(vec2(1.0, 0.5)), 12.0, 2.4, 1.0), dk, lt, 0.45, px)
out = an_lay_leaf(out, an_leaf(q, vec2(8.0, 23.0), normalize(vec2(0.6, 1.0)), 9.0, 2.6, 1.0), dk, lt, 0.5, px)
out = an_bud(out, q, vec2(150.0, 26.0), normalize(vec2(1.0, -0.1)), 9.0, px)
out = an_lily(out, q, vec2(175.0, 12.5), 8.5, 0.7, px, 1.0)
out = an_lily(out, q, vec2(16.0, 15.5), 13.0, 0.4, px, 0.0)
return out
}
// The small ornament in a bottom corner of the window, `q` in points from
// the corner along its two edges: a tendril's curl in the spandrel the
// panes' rounded corner leaves, a leaf running out along each edge from it
// and a gold bud on the diagonal. Nothing of it reaches the pane's hole.
let an_foot_corner = fn(dst: vec4, q: vec2, px: float) -> vec4 {
var out = dst
let dk = vec3(0.420, 0.580, 0.486)
let lt = vec3(0.616, 0.737, 0.627)
let stem = vec3(0.500, 0.650, 0.560)
out = an_lay_leaf(out, an_leaf(q, vec2(9.0, 3.0), vec2(1.0, 0.0), 30.0, 1.6, -0.5), dk, lt, 0.4, px)
out = an_lay_leaf(out, an_leaf(q, vec2(3.0, 9.0), vec2(0.0, 1.0), 30.0, 1.6, 0.5), dk, lt, 0.4, px)
out = an_lay(out, an_spiral(q, vec2(9.6, 9.6), 1.35, 2.0, PI * 1.25) - 0.45, stem, 0.3, px)
out = an_lay(out, length(q - vec2(4.6, 4.6)) - 1.5, mix(an_bronze_c(), an_gold_hi(), 0.5), 0.35, px)
return out
}
mod.widgets.Window.show_bg = true
mod.widgets.Window.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let p = self.pos * self.rect_size
let W = self.rect_size.x
let H = self.rect_size.y
var col = an_parch(p, px, an_parch_c())
// the two holes in the frame: the header and the panes
let hc = vec2(W * 0.5, 60.0)
let hh = vec2(W * 0.5 - 6.0, 27.0)
let pc = vec2(W * 0.5, (95.0 + H - 6.0) * 0.5)
let ph = vec2(W * 0.5 - 6.0, (H - 6.0 - 95.0) * 0.5)
let dh = md_rbox(p, hc, hh, 14.0)
// the panes' outer foot corners are wider arches: in the spandrels
// they leave the frame carries its foot ornaments
let pr = md_r4(p, pc, ph, vec4(12.0, 12.0, 26.0, 26.0))
let dp = md_rbox(p, pc, ph, pr)
var dhole = dp
var nh = an_nrm(p, pc, ph, pr)
if dh < dp {
dhole = dh
nh = an_nrm(p, hc, hh, 14.0)
}
// the header is a lighter leaf of parchment
col = mix(col, Finish.lift(col, 0.12), Finish.cover(dh, px))
// the parchment is aged toward the frame: a warm browning that fades
// out some thirty points in, never a stain in the middle
let age = 1.0 - smoothstep(0.0, 34.0, 0.0 - dhole)
col = mix(col, col * vec3(0.84, 0.77, 0.66), age * age * 0.55)
// the frame's shade on the parchment along each hole's upper and left
// edges, three points deep
let sh = (1.0 - smoothstep(0.0, 3.5, 0.0 - dhole)) * Finish.cover(dhole, px) * (0.5 + 0.5 * clamp(0.0 - nh.y - nh.x * 0.5, 0.0, 1.0))
col = mix(col, col * 0.80, sh * 0.6)
// the teal bands
let band = Finish.cover(0.0 - dhole, px)
var teal = an_teal() * (1.0 + (Finish.noise(p * 0.11) - 0.5) * 0.05) + g3(Finish.grain(p / px, 0.008))
teal = mix(teal, teal * 1.12, (1.0 - smoothstep(0.0, 33.0, p.y)) * 0.4)
col = mix(col, teal, band)
// the window's outer edge: a darker teal lip and a bronze wire
let dw = md_rbox(p, vec2(W, H) * 0.5, vec2(W, H) * 0.5, 0.0)
let nw = an_nrm(p, vec2(W, H) * 0.5, vec2(W, H) * 0.5, 0.0)
var out = vec4(col, 1.0)
// THE CARTOUCHE under the title, and its whiplash scrolls
let cc = vec2(W * 0.5, 16.0)
let ch = vec2(84.0, 12.5)
let cd = md_rbox(p, cc, ch, 12.5)
if abs(p.x - cc.x) < 140.0 && p.y < 35.0 {
let cw = an_parch(p, px, vec3(0.925, 0.878, 0.776))
out = an_lay(out, cd, cw, 0.0, px)
out = Finish.over(out, an_fine(cd, 2.6, max(px, 0.6), px, 0.7))
out = Finish.over(out, an_wire(cd, an_nrm(p, cc, ch, 12.5), 1.5, px, 1.0))
let sx = abs(p.x - cc.x)
let sq = vec2(sx, p.y)
out = an_lay(out, an_spiral(sq, vec2(ch.x + 22.0, 13.0), 2.0, 2.0, 2.6) - 0.7, mix(an_bronze_c(), an_gold_hi(), 0.3), 0.0, px)
out = an_lay_stem(out, an_bez(sq, vec2(ch.x - 2.0, 25.0), vec2(ch.x + 14.0, 30.0), vec2(ch.x + 18.6, 15.1)), 1.0, 0.6, mix(an_bronze_c(), an_gold_hi(), 0.3), px)
}
// THE SPRAYS in the top band's two ends, kept clear of the cartouche
if p.y < 35.0 && p.x < min(270.0, W * 0.5 - 122.0) {
out = an_band_spray(out, vec2(p.x - 6.0, p.y), px)
}
if p.y < 35.0 && p.x > max(W - 410.0, W * 0.5 + 122.0) && p.x < W - 138.0 {
out = an_band_spray(out, vec2(W - 138.0 - p.x, p.y), px)
}
// the foot corners
if p.y > H - 40.0 && (p.x < 40.0 || p.x > W - 40.0) {
out = an_foot_corner(out, vec2(min(p.x, W - p.x), H - p.y), px)
}
// the bronze wire along every hole's edge, on the band's side
out = Finish.over(out, an_wire(0.0 - dhole, nh * -1.0, 1.5, px, 1.0))
out = Finish.over(out, an_fine(0.0 - dhole, 2.6, max(px, 0.5), px, 0.35 * step(39.0, p.y)))
out = Finish.over(out, an_wire(dw, nw, 1.6, px, 1.0))
return vec4(out.rgb, 1.0)
}
// A splitter is the frame's teal band run down between two panes, ten
// points wide, its bronze wires turning into the header's band above and
// the foot band below with rounded corners, as the panes' corners are.
mod.widgets.Splitter.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 14.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.Splitter.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let m = 14.0
let size = self.rect_size
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let v = self.is_vertical
// work in the bar's own frame: `a` along it, `b` across it
let a = mix(p.x, p.y, v)
let b = mix(p.y, p.x, v)
let along = mix(size.x, size.y, v)
let thick = mix(size.y, size.x, v)
let mid = thick * 0.5
let hw = 5.0
let r = 12.0
let a0 = 4.0
let a1 = along - 6.0
// the panes either side, as rounded boxes
let q = vec2(b, a)
let lc = vec2(mid - hw - 200.0, (a0 + a1) * 0.5)
let rc = vec2(mid + hw + 200.0, (a0 + a1) * 0.5)
let ph = vec2(200.0, (a1 - a0) * 0.5)
let dl = md_rbox(q, lc, ph, r)
let dr = md_rbox(q, rc, ph, r)
var dh = dl
var nh = an_nrm(q, lc, ph, r)
if dr < dl {
dh = dr
nh = an_nrm(q, rc, ph, r)
}
// the header above: everything before -4 along the bar
let dt = a + 4.0
if dt < dh {
dh = dt
nh = vec2(0.0, 1.0)
}
let inside = step(-8.0, a) * step(a, along - 1.6) * step(abs(b - mid), hw + r + 0.5)
let band = Finish.cover(0.0 - dh, px) * inside
var teal = an_teal() * (1.0 + (Finish.noise((p + self.rect_pos) * 0.11) - 0.5) * 0.05)
let act = max(max(self.hover, self.drag), self.focus)
teal = Finish.lift(teal, 0.08 * act)
var out = vec4(teal * band, band)
let nn = vec2(mix(nh.y, nh.x, v), mix(nh.x, nh.y, v))
out = Finish.over(out, an_wire(0.0 - dh, nn * -1.0, 1.5, px, 1.0) * inside)
// under the hand, a gold line down the middle of the grip
let inbar = 1.0 - smoothstep(self.bar_size * 0.5 - 2.0, self.bar_size * 0.5, abs(a - along * 0.5))
let gl = (1.0 - smoothstep(0.5, 0.5 + px, abs(b - mid))) * inbar * act
out = Finish.over(out, vec4(an_gold_hi() * gl, gl))
return out
}
// ---------------------------------------------------------------- switches and meter
// THE SWITCHES are the sheet's own toggle: the rocker carries a bronze disc
// (the metal of a lever), the slide switch the ivory one, both riding in the
// sunk pill, teal when on.
mod.widgets.ToggleRocker.label_walk.margin = mod.turtle.Inset{left: 46.0 right: 3.0 top: 0.0 bottom: 0.0}
mod.widgets.ToggleSlide.label_walk.margin = mod.turtle.Inset{left: 46.0 right: 3.0 top: 0.0 bottom: 0.0}
mod.widgets.ToggleRocker.draw_bg.size = 14.0
mod.widgets.ToggleSlide.draw_bg.size = 14.0
mod.widgets.ToggleRocker.min_height = 24.0
mod.widgets.ToggleSlide.min_height = 24.0
mod.widgets.ToggleRocker.draw_bg.pill_aspect = 2.7143
mod.widgets.ToggleSlide.draw_bg.pill_aspect = 2.7143
mod.widgets.ToggleRocker.draw_bg.knob_inset = 7.0
mod.widgets.ToggleSlide.draw_bg.knob_inset = 7.0
mod.widgets.ToggleRocker.clip_x = false
mod.widgets.ToggleRocker.clip_y = false
mod.widgets.ToggleSlide.clip_x = false
mod.widgets.ToggleSlide.clip_y = false
mod.widgets.ToggleRocker.draw_bg.vertex = mod.widgets.ToggleFlat.draw_bg.vertex
mod.widgets.ToggleSlide.draw_bg.vertex = mod.widgets.ToggleFlat.draw_bg.vertex
mod.widgets.ToggleRocker.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return an_toggle(self.pos, self.rect_size, self.rect_pos, px, self.active, self.drag_pos, self.drag, self.hover, self.down, self.focus, self.disabled, self.error, self.mark_at_end, 1.0)
}
mod.widgets.ToggleSlide.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return an_toggle(self.pos, self.rect_size, self.rect_pos, px, self.active, self.drag_pos, self.drag, self.hover, self.down, self.focus, self.disabled, self.error, self.mark_at_end, 0.0)
}
// THE GLASS of the readouts, the screen and the meter: deep teal glass with
// the faint unevenness of cathedral glass and a soft sheen across its upper
// left, set behind a bronze wire, a dark gap and a fine gold line, its
// corners rounded; the surround's shade falls in under its top edge.
let an_glass = fn(p: vec2, lo: vec2, hi: vec2, r4: vec4, px: float, face: vec3) -> vec4 {
let h = (hi - lo) * 0.5
let c = lo + h
let r = md_r4(p, c, h, r4)
let d = md_rbox(p, c, h, r)
var out = an_drop(p, c, h, r, 0.8)
let hf = h - vec2(1.8, 1.8)
var g = face * (1.0 + (Finish.noise(p * 0.21 + vec2(4.0, 9.0)) - 0.5) * 0.035 + (Finish.noise(p * 0.6) - 0.5) * 0.025)
let u = (p - lo) / max(hi - lo, vec2(1.0, 1.0))
let diag = u.x * 0.8 + u.y
let sheen = smoothstep(0.0, 0.25, diag) * (1.0 - smoothstep(0.35, 0.75, diag)) * 0.06
g = g + vec3(0.95, 0.92, 0.80) * sheen
g = mix(g, vec3(0.03, 0.05, 0.04), clamp(Finish.inset(p, c, hf, max(r - 1.8, 0.0), vec2(0.0, 2.2), 2.0), 0.0, 1.0) * 0.55)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(g * a, a))
out = Finish.over(out, an_fine(d, 3.0, max(px, 0.55), px, 0.5))
return Finish.over(out, an_wire(d, an_nrm(p, c, h, r), 1.8, px, 1.0))
}
mod.widgets.ScreenView.clip_x = false
mod.widgets.ScreenView.clip_y = false
mod.widgets.ScreenView.padding = mod.turtle.Inset{top: 8.0 right: 11.0 bottom: 8.0 left: 11.0}
mod.widgets.ScreenView.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.ScreenView.draw_bg.pixel = fn() {
let m = 5.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let lo = vec2(Finish.snap(self.rect_pos.x, px), Finish.snap(self.rect_pos.y, px)) - self.rect_pos
let hi = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x, px), Finish.snap(self.rect_pos.y + self.rect_size.y, px) - px) - self.rect_pos
return an_glass(p, lo, hi, vec4(9.0, 9.0, 9.0, 9.0), px, self.color.rgb)
}
mod.widgets.ScreenView.bezel = 3.0
mod.widgets.ScreenView.sheen = 0.02
// THE METER sits in the same glass: its window arched at the top, the scale
// a fine gold arc with gold ticks, the colour band under it running sage to
// ochre to terracotta, the needle an ivory spear and its pivot a bronze boss.
mod.widgets.NeedleMeter.clip_x = false
mod.widgets.NeedleMeter.clip_y = false
mod.widgets.NeedleMeter.draw_bg.border_radius = uniform(9.0)
mod.widgets.NeedleMeter.draw_bg.color = uniform(#213530)
mod.widgets.NeedleMeter.draw_bg.color_needle = uniform(#xf3e8cf)
mod.widgets.NeedleMeter.draw_bg.color_zone = uniform(#xd0603f)
mod.widgets.NeedleMeter.draw_bg.color_scale = uniform(#xe6c47e)
mod.widgets.NeedleMeter.draw_bg.vertex = fn() {
let g = md_grow(self.rect_pos, self.rect_size, 5.0)
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
}
mod.widgets.NeedleMeter.draw_bg.pixel = fn() {
let m = 5.0
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let lo = vec2(Finish.snap(self.rect_pos.x, px), Finish.snap(self.rect_pos.y, px)) - self.rect_pos
let hi = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x, px), Finish.snap(self.rect_pos.y + self.rect_size.y, px) - px) - self.rect_pos
let r = min(self.border_radius, min(hi.x - lo.x, hi.y - lo.y) * 0.5)
let rt = min(34.0, min(hi.x - lo.x, hi.y - lo.y) * 0.45)
var out = an_glass(p, lo, hi, vec4(rt, rt, r, r), px, an_glass_c())
let pv = self.pivot()
let rr = self.scale_radius()
let q = p - pv
let rq = 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 inside = Finish.cover(md_rbox4(p, (lo + hi) * 0.5, (hi - lo) * 0.5 - vec2(2.0, 2.0), vec4(rt - 2.0, rt - 2.0, r - 2.0, r - 2.0)), px)
// the colour band under the scale
let bandr = Finish.cover(abs(rq - (rr - 2.6)) - 1.6, px) * on_scale
let ramp = mix(mix(vec3(0.47, 0.66, 0.53), vec3(0.88, 0.68, 0.33), smoothstep(0.35, 0.7, t)), self.color_zone.rgb, step(self.zone, t))
let ba = bandr * 0.85 * inside
out = Finish.over(out, vec4(ramp * ba, ba))
// the scale: a hairline arc and a tick at every division
let arc = Finish.cover(abs(rq - rr) - px * 0.6, px) * on_scale
let n = max(floor(self.majors + 0.5), 1.0) * max(floor(self.minors + 0.5), 1.0)
let k = floor(clamp(t, 0.0, 1.0) * n + 0.5)
let ak = (k / n - 0.5) * sweep
let perp = abs(q.x * cos(ak) + q.y * sin(ak))
let mn = max(floor(self.minors + 0.5), 1.0)
let major = 1.0 - step(0.5, k - mn * floor(k / mn + 0.0001))
let len = mix(self.tick_minor, self.tick_major, major)
let radial = max(rr - len - rq, rq - rr)
let tick = Finish.cover(perp - px * mix(0.55, 0.9, major), px) * Finish.cover(radial, px) * step(0.0, 0.0 - q.y * cos(ak) + q.x * sin(ak))
let sa = max(arc, tick) * inside
out = Finish.over(out, vec4(self.color_scale.rgb * sa, sa))
// the needle: a slim spear from under the pivot to just past the scale
let na = self.needle_angle(self.value)
let nd = vec2(sin(na), 0.0 - cos(na))
let sp = an_leaf(p, pv - nd * 3.0, nd, rr + 6.0, 1.4, 0.0)
let nf = mix(self.color_needle.rgb, an_gold_hi(), smoothstep(0.3, 1.0, sp.z) * 0.3)
out = an_lay(out, sp.x, nf * inside + self.color_needle.rgb * (1.0 - inside), 0.4, px)
let pd = rq - self.pivot_size - 0.8
let bl = clamp(0.5 - 0.6 * dot(q / max(self.pivot_size, 1.0), vec2(0.45, 0.85)), 0.0, 1.0)
out = an_lay(out, pd, mix(an_bronze_dk(), an_gold_hi(), bl), 0.5, px)
return out * self.color.a * self.opacity
}
mod.widgets.NeedleMeter.draw_label.color = #xe6c47e
// ---------------------------------------------------------------- lamps
// LAMPS: jewels of coloured glass, cabochons in polished bronze settings, as
// the stained glass of the period. 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. Its light
// is added, never painted: a tight hot halo and a short bloom in its own
// hue, gone in about two radii; on the pale parchment its hue is also laid
// round it as a tint that keeps the ground's luminance. A bar is the same
// light drawn out into a short light pipe.
let md_led_lamp_add = fn(res: vec4, light: vec3, ground: vec3) -> vec4 {
let est = res.rgb + ground * (1.0 - res.a)
let add = (vec3(1.0, 1.0, 1.0) - exp(0.0 - light)) * max(vec3(1.0, 1.0, 1.0) - est, vec3(0.0, 0.0, 0.0))
return vec4(res.rgb + add, res.a)
}
let md_led_lamp_full = fn(ink: vec3) -> vec3 {
return ink / max(max(ink.x, max(ink.y, ink.z)), 0.01)
}
let md_led_lamp_hue = fn(ink: vec3, vivid: float) -> vec3 {
let f = md_led_lamp_full(ink)
let mn = min(f.x, min(f.y, f.z))
let ch = 1.0 - mn
let pure = (f - vec3(mn, mn, mn)) / max(ch, 0.001)
return mix(f, pure, clamp(vivid, 0.0, 1.0) * 0.72 * smoothstep(0.03, 0.12, ch))
}
let md_led_lamp = fn(dst: vec4, p: vec2, c: vec2, hb: vec2, lit: float, ink: vec3, vivid: float, px: float, ground: vec3, room: 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.32 * rt, 0.8, 2.4)
let ds = d - bw
let L = clamp(lit, 0.0, 1.0)
let b = md_led_lamp_full(ink)
let hue = md_led_lamp_hue(ink, vivid)
let pw = L * sqrt(L)
let white = vec3(1.0, 1.0, 1.0)
let lum = vec3(0.2126, 0.7152, 0.0722)
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)
// the setting's shadow on the parchment
let shd = Finish.drop(p, c, hb + vec2(bw, bw), rt + bw, vec2(0.15, 0.12 * rt + 0.3), 0.10 * rt + 0.5) * 0.32 * (1.0 - 0.6 * pw)
res = Finish.over(res, vec4(an_shade() * shd, shd))
if L > 0.001 {
// the tint on the pale ground: its hue laid over, never darker
let h = clamp(0.42 * pw * pow(2.0, 0.0 - x / max(0.4 * reach, 0.5)) * win, 0.0, 0.45)
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))
}
// the bronze setting: a polished ring rounded over, lit from the upper
// left, a dark seat line where it holds the stone
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))
var rc = an_bronze_c() * (0.92 + 0.45 * facing)
rc = mix(rc, an_gold_hi(), smoothstep(0.5, 0.95, facing) * 0.75)
rc = mix(rc, an_bronze_dk(), Finish.band(ds, 0.0, max(px, 0.45), px) * 0.7)
rc = mix(rc, an_bronze_dk() * 0.6, Finish.ring_out(d, 0.0, max(px, 0.45), px) * 0.8)
res = Finish.over(res, vec4(min(rc, white) * 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 = md_led_lamp_add(res, mix(hue, white, 0.15 * pw) * halo * pw * hwin * win, ground)
}
// THE STONE: a glossy cabochon of coloured glass, deeper at the top,
// light pooling low inside; lit, its middle burns toward white at full
// current and its rim is its most saturated colour.
let top = smoothstep(-1.0, 0.6, q.y)
let pool = exp(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.99, 0.975), exp(0.0 - t * t / 0.20) * smoothstep(0.5, 1.0, L) * 0.85)
on = mix(on, hue * (0.82 + 0.18 * L), smoothstep(0.62, 1.0, t))
on = on * (0.42 + 0.58 * L)
// out: dark tinted glass, its colour barely there, a catch light along
// its lower rim
let offc = mix(vec3(0.075, 0.08, 0.07), b * 0.42, 0.24)
var off = mix(offc * 0.62, offc * 1.15, 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.28, 0.24) * 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 = min(col, white)
let a_l = Finish.cover(d, px)
return Finish.over(res, vec4(col * a_l, a_l))
}
// The Lamp widget and the bar lamp. A round jewel takes the middle of its
// cell; its halo's room is held in the cell (the library reserves a lens
// thickness either side once a halo is asked for), so the light is never cut
// at its edge and fades within half the gap. A bar is a short light pipe set
// at the left of its cell, six points thick and twice that long, its glow
// held to two and a half points round its setting -- the whole lamp takes
// the first twenty points of its cell and leaves the rest free.
mod.widgets.Lamp.halo = 0.3
mod.widgets.Lamp.size = 14.0
mod.widgets.Lamp.halo_reach = 0.3
mod.widgets.LampBar.size = 14.0
mod.widgets.LampBar.halo_reach = 0.3
mod.widgets.Lamp.draw_bg.color_accent = uniform(#xffa83a)
mod.widgets.Lamp.draw_bg.color_success = uniform(#x34c79c)
mod.widgets.Lamp.draw_bg.color_warning = uniform(#xf2c443)
mod.widgets.Lamp.draw_bg.color_error = uniform(#xe5483a)
mod.widgets.Lamp.draw_bg.color_plain = uniform(#xfff3d8)
mod.widgets.Lamp.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.Lamp.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 = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let reach = clamp(self.reach, 0.0, 1.0)
let t = min(self.rect_size.y / (1.0 + 2.0 * reach), 14.0)
let room = (self.rect_size.y - t) * 0.5
let tb = min(t, 6.0)
let lb = 14.0
let c = mix(self.rect_size * 0.5, vec2(1.6 + lb * 0.5, self.rect_size.y * 0.5), self.bar)
let hb = mix(vec2(t * 0.5, t * 0.5), vec2(lb * 0.5, tb * 0.5), self.bar)
let glow = mix(max(room - 2.5, 2.0), 2.2, self.bar)
let res = md_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, an_parch_c(), glow)
return res * self.opacity
}
true