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

2952 lines
147 KiB
Text

use mod.prelude.widgets_internal.*
// Art deco, black and gold. The window is a cabinet of black lacquer: a
// polished gold rim runs round its edge and round every pane, a thin gold
// pinstripe beside each rim makes the double line of the period, and the
// panes are stepped at their corners like a ziggurat. The panes are dark
// macassar ebony, a fine straight figure under one broad lacquer sheen, calm
// under the words. The caption band carries the only large ornament: a
// stepped crest with mother-of-pearl inlays round the title and a gold
// sunburst fanning out to either side; the big surfaces carry small
// sunburst fans, studs and pearl wedges in their corners and on their
// frames, never under their words.
// A control is a PLATE of satin metal with rounded corners (antique brass,
// polished gold, champagne, ivory-cream or copper, dark ink on all of them)
// or of black lacquer in a polished gold rim (cream ink); or a WELL of black
// lacquer sunk behind the same rim. Knobs are ivory with fluted skirts under
// spun gold caps, slider thumbs gold streamline wings, toggles gold balls in
// black pills, the rocker a gold bat-handle lever, the chosen radio a lit
// lamp among gold beads, lamps LEDs in gold bezels, screens black glass in
// gold frames with warm amber figures. The light falls from the upper left;
// polished gold is a mirror: a hot white-gold highlight where it faces the
// light, a warm darker band where it turns away.
// 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 = 3.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 = #1f1810
// 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 ad_cream = fn() -> vec3 {
return vec3(0.929, 0.886, 0.776)
}
let ad_black = fn() -> vec3 {
return vec3(0.034, 0.031, 0.028)
}
let ad_ebony_c = fn() -> vec3 {
return vec3(0.120, 0.095, 0.060)
}
let ad_gold_dk = fn() -> vec3 {
return vec3(0.270, 0.190, 0.085)
}
let ad_gold_md = fn() -> vec3 {
return vec3(0.640, 0.520, 0.310)
}
let ad_gold_lt = fn() -> vec3 {
return vec3(0.910, 0.830, 0.640)
}
let ad_gold_hi = fn() -> vec3 {
return vec3(1.000, 0.970, 0.880)
}
let ad_line_c = fn() -> vec3 {
return vec3(0.780, 0.670, 0.450)
}
let ad_gold_base = fn() -> vec3 {
return vec3(0.790, 0.660, 0.420)
}
let ad_champ = fn() -> vec3 {
return vec3(0.850, 0.785, 0.640)
}
let ad_ivory = fn() -> vec3 {
return vec3(0.925, 0.890, 0.790)
}
let ad_ivory_dk = fn() -> vec3 {
return vec3(0.640, 0.580, 0.455)
}
let ad_brass = fn() -> vec3 {
return vec3(0.660, 0.555, 0.370)
}
let ad_copper = fn() -> vec3 {
return vec3(0.700, 0.480, 0.330)
}
let ad_bronze = fn() -> vec3 {
return vec3(0.215, 0.140, 0.062)
}
// 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 ad_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)
}
// A plate's outline: a box with a radius per corner (tl, tr, br, bl; 0
// leaves it square), and its outward normal.
let ad_cut = fn(p: vec2, c: vec2, h: vec2, k4: vec4) -> float {
return md_rbox4(p, c, h, k4)
}
let ad_cut_n = fn(p: vec2, c: vec2, h: vec2, k4: vec4) -> vec2 {
let e = 0.3
let gx = md_rbox4(p + vec2(e, 0.0), c, h, k4) - md_rbox4(p - vec2(e, 0.0), c, h, k4)
let gy = md_rbox4(p + vec2(0.0, e), c, h, k4) - md_rbox4(p - vec2(0.0, e), c, h, k4)
let g = vec2(gx, gy)
let l = length(g)
if l < 0.0001 {
return vec2(0.0, 1.0)
}
return g / l
}
// A box STEPPED at its corners like a ziggurat: two steps of `s` points.
let ad_step = fn(p: vec2, c: vec2, h: vec2, s: float) -> float {
let s1 = min(s, min(h.x, h.y) * 0.25)
let a = md_rbox(p, c, h - vec2(0.0, 2.0 * s1), 0.0)
let b = md_rbox(p, c, h - vec2(s1, s1), 0.0)
let e = md_rbox(p, c, h - vec2(2.0 * s1, 0.0), 0.0)
return min(a, min(b, e))
}
let ad_step_n = fn(p: vec2, c: vec2, h: vec2, s: float) -> vec2 {
let e = 0.3
let gx = ad_step(p + vec2(e, 0.0), c, h, s) - ad_step(p - vec2(e, 0.0), c, h, s)
let gy = ad_step(p + vec2(0.0, e), c, h, s) - ad_step(p - vec2(0.0, e), c, h, s)
let g = vec2(gx, gy)
let l = length(g)
if l < 0.0001 {
return vec2(0.0, 1.0)
}
return g / l
}
// The corners a button group asks for: a corner written as 0 is a square
// join, any other the family's cut.
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)
}
// A stroke tapering from `r0` at `a` to `r1` at `b`: .x its distance, .y the
// way along it.
let ad_taper = fn(p: vec2, a: vec2, b: vec2, r0: float, r1: float) -> vec2 {
let pa = p - a
let ba = b - a
let t = clamp(dot(pa, ba) / max(dot(ba, ba), 0.0001), 0.0, 1.0)
return vec2(length(pa - ba * t) - mix(r0, r1, t), t)
}
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 EBONY at a point `w` (points): macassar, its figure running straight
// down the panel in streaks of a few points to a few tens, wandering a
// little sideways as a real board does; fine dark pores a device pixel or
// two wide; a grain of one device pixel. Low in contrast, so a word reads
// the same on any streak.
let ad_ebony = fn(w: vec2, px: float) -> vec3 {
let wx = w.x + (Finish.noise(vec2(w.x * 0.0035, w.y * 0.0021)) - 0.5) * 32.0 + (Finish.noise(vec2(w.x * 0.02, w.y * 0.008) + vec2(3.0, 1.0)) - 0.5) * 5.0
let s1 = Finish.noise(vec2(wx * 0.055, w.y * 0.0018 + 7.0))
let s2 = Finish.noise(vec2(wx * 0.21, w.y * 0.006 + 2.0))
let fig = s1 * 0.65 + s2 * 0.35
var col = mix(vec3(0.086, 0.068, 0.042), vec3(0.168, 0.122, 0.078), smoothstep(0.38, 0.86, fig))
let pore = Finish.noise(vec2(wx * 1.1, w.y * 0.03 + 11.0))
col = col * (1.0 - 0.22 * smoothstep(0.72, 0.9, pore))
let lift = Finish.noise(vec2(wx * 0.75 + 4.0, w.y * 0.02))
col = col * (1.0 + 0.10 * smoothstep(0.75, 0.92, lift))
return col + g3(Finish.grain(w / px, 0.004))
}
// MOTHER-OF-PEARL: a cool white with soft swirling bands of rose and of
// green-blue, as the nacre turns.
let ad_pearl = fn(p: vec2) -> vec3 {
let n1 = Finish.noise(p * 0.16 + vec2(3.0, 7.0))
let n2 = Finish.noise(p * 0.45 + vec2(11.0, 2.0))
let sw = sin((p.x * 0.55 + p.y * 0.9) * 0.8 + n1 * 6.0)
var col = vec3(0.860, 0.850, 0.820)
col = mix(col, vec3(0.950, 0.860, 0.880), smoothstep(0.2, 0.9, sw) * 0.5)
col = mix(col, vec3(0.790, 0.900, 0.880), smoothstep(0.3, 0.9, 0.0 - sw) * 0.4)
return col * (0.86 + 0.18 * n2)
}
// A POLISHED GOLD RIM `w` points wide just inside an edge: `d` the edge's
// distance (negative inside), `n` its outward normal. Rounded over and lit
// from the upper left as a mirror: the half that faces the light carries a
// hot white-gold highlight, the turn away a warm darker band, the far side
// the deep gold of the floor's reflection; a dark hairline at both of its
// edges keeps it crisp on black. Premultiplied.
let ad_rim = fn(d: float, n: vec2, w: float, px: float, glint: float) -> vec4 {
let u = clamp(0.0 - 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.45, -0.89))
let t = clamp(0.5 + 0.55 * facing + 0.15 * bulge, 0.0, 1.0)
var col = mix(ad_gold_dk(), ad_gold_md(), smoothstep(0.0, 0.45, t))
col = mix(col, ad_gold_lt(), smoothstep(0.45, 0.78, t))
col = mix(col, ad_gold_hi(), smoothstep(0.80, 1.0, t) * clamp(glint, 0.0, 1.5))
col = col * (1.0 - 0.30 * exp(0.0 - (facing + 0.1) * (facing + 0.1) / 0.03) * bulge)
let e = Finish.band(d, 0.0, px, px) + Finish.band(d, w - px, w, px)
col = mix(col, ad_gold_dk() * 0.5, clamp(e, 0.0, 1.0) * 0.5)
let a = Finish.band(d, 0.0, w, px)
return vec4(col * a, a)
}
// A thin gold pinstripe `lw` wide at `at` points inside an edge.
let ad_pin = fn(d: float, at: float, lw: float, px: float, amt: float) -> vec4 {
let a = Finish.band(d, at, at + lw, px) * amt
return vec4(ad_line_c() * a, a)
}
// The shadow a raised part throws on the ground, straight down: tight, gone
// within half the gap to a neighbour. Premultiplied black.
let ad_drop = fn(p: vec2, c: vec2, h: vec2, r: float, amt: float) -> vec4 {
let t = Finish.drop(p, c, h, r, vec2(0.0, 0.8), 0.6) * 0.55 * amt
let s = Finish.drop(p, c, h, r, vec2(0.0, 1.6), 1.2) * 0.40 * amt
let a = t + s * (1.0 - t)
return vec4(0.0, 0.0, 0.0, a)
}
// Focus: a warm white-gold ring of a device pixel and a half, a point and a
// half outside.
let ad_focus = fn(d: float, px: float, focus: float) -> vec4 {
let f = Finish.ring_out(d, 1.5, 1.5 + 1.5 * px, px) * 0.95 * focus
let c = vec3(1.0, 0.86, 0.52)
return vec4(c * f, f)
}
// BLACK LACQUER over the box `lo`..`hi`: a mirror. A soft band of the room's
// light across its upper part, the face darkening toward its foot, and a
// warm glow of its own gold rim reflected along the foot. `k` scales the
// sheen (a tall surface takes less, so no band crosses its words).
let ad_lacq = fn(p: vec2, lo: vec2, hi: vec2, base: vec3, k: float) -> vec3 {
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
var col = base * (1.30 - 0.50 * t)
let band = exp(0.0 - (t - 0.20) * (t - 0.20) / 0.010)
col = col + vec3(0.075, 0.068, 0.058) * band * 0.65 * k
col = col + vec3(0.090, 0.060, 0.020) * smoothstep(0.78, 1.0, t) * 0.30
return col
}
// POLISHED METAL over the box `lo`..`hi`: a mirror gradient, bright along
// the top where it faces the light, a warm darker band below the middle, a
// little light again at the foot from the floor; satin brushing across it.
// `contrast` 1 is polished gold, less is champagne or ivory. Pressed, the
// face is dished: the gradient turns over.
let ad_metal = fn(p: vec2, lo: vec2, hi: vec2, base: vec3, contrast: float, hover: float, press: float) -> vec3 {
let t0 = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
let t = mix(t0, 1.0 - t0, press * 0.8)
let up = 1.0 - smoothstep(0.0, 0.45, t)
let dip = exp(0.0 - (t - 0.68) * (t - 0.68) / 0.03)
let foot = smoothstep(0.85, 1.0, t)
var col = base * (1.0 + contrast * (0.24 * up - 0.24 * dip + 0.10 * foot))
col = mix(col, vec3(1.0, 0.97, 0.88), exp(0.0 - t * t / 0.006) * 0.35 * contrast * (1.0 - press))
let br = Finish.brushed(p, vec2(1.0, 0.0), 0.4) - 0.5
col = col * (1.0 + br * 0.06)
col = Finish.lift(col, 0.20 * hover * (1.0 - press))
col = col * (1.0 - 0.14 * press)
return min(col, vec3(1.0, 1.0, 1.0))
}
// A shaped piece of polished gold (a thumb, a cap, a stud): `d` its
// distance, `n` its outward normal, `depth` how far in its rounded edge
// runs, `t` 0..1 down it for the mirror gradient.
let ad_gold_shape = fn(d: float, n: vec2, depth: float, t: float, hover: float, dis: float) -> vec3 {
let ins = clamp(0.0 - d / max(depth, 0.01), 0.0, 1.0)
let facing = dot(n, vec2(-0.45, -0.89)) * (1.0 - ins)
var col = ad_gold_base() * (1.06 + 0.22 * (1.0 - smoothstep(0.0, 0.5, t)) - 0.26 * exp(0.0 - (t - 0.72) * (t - 0.72) / 0.03))
col = mix(col, ad_gold_hi(), smoothstep(0.30, 0.95, facing) * 0.85)
col = mix(col, ad_gold_dk(), smoothstep(0.15, 0.95, 0.0 - facing) * 0.75)
col = Finish.lift(col, 0.12 * hover)
col = mix(col, mix(col, g3(Finish.lum(col)), 0.7) * 0.6, dis)
return min(col, vec3(1.0, 1.0, 1.0))
}
// A polished gold dome (a stud, a bead): lit on its upper left.
let ad_dome = fn(dst: vec4, p: vec2, c: vec2, r: float, px: float, base: vec3) -> vec4 {
let q = (p - c) / max(r, 0.01)
let lit = clamp(0.5 - 0.6 * dot(q, vec2(0.45, 0.85)), 0.0, 1.0)
var col = mix(ad_gold_dk(), base, 0.25 + 0.75 * lit)
col = mix(col, ad_gold_hi(), exp(0.0 - dot(q - vec2(-0.35, -0.4), q - vec2(-0.35, -0.4)) / 0.06) * 0.85)
let d = length(p - c) - r
col = mix(col, ad_gold_dk() * 0.5, Finish.band(d, 0.0, max(px, 0.4), px) * 0.6)
let a = Finish.cover(d, px)
return Finish.over(dst, vec4(col * a, a))
}
// A SUNK WELL behind a polished gold rim: the fill, the shade falling in
// under the top edge, the rim, and a faint catch of gold light on the ground
// under its foot. `d`, `n` the well's edge; `c`, `h`, `r` its box for the
// inner shadow.
let ad_well = fn(p: vec2, c: vec2, h: vec2, r: float, d: float, n: vec2, px: float, fill: vec3, shade: float, w: float, dis: float) -> vec4 {
var col = fill
let sh = clamp(Finish.inset(p, c, h - vec2(w, w), max(r - w, 0.0), vec2(0.0, 1.4), 1.2), 0.0, 1.0)
col = mix(col, vec3(0.0, 0.0, 0.0), sh * shade)
let a = Finish.cover(d, px)
let lip = Finish.ring_out(d, 0.0, px, px) * clamp(n.y, 0.0, 1.0) * 0.30
var out = vec4(ad_gold_md() * lip, lip)
out = Finish.over(out, vec4(col * a, a))
return Finish.over(out, ad_rim(d, n, w, px, 1.0 - 0.7 * dis) * mix(1.0, 0.5, dis))
}
let ad_well_r = fn(p: vec2, c: vec2, h: vec2, r: float, px: float, fill: vec3, shade: float, w: float, dis: float) -> vec4 {
let d = md_rbox(p, c, h, r)
let n = ad_nrm(p, c, h, r)
return ad_well(p, c, h, r, d, n, px, fill, shade, w, dis)
}
// A dark plaque's indicator jewel, 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.35), ink * 0.9, smoothstep(0.2, 1.0, k))
let off = vec3(0.10, 0.08, 0.06)
var col = mix(off, on, lit)
col = mix(col, ad_gold_md(), Finish.band(d, 0.0, max(px, 0.6), px))
let a = Finish.cover(d, px)
let ha = glow * lit * exp(0.0 - max(d, 0.0) / 0.8) * step(0.0, d) * (1.0 - a)
var out = vec4(ink * ha, ha)
return Finish.over(out, vec4(col * a, a))
}
// A QUARTER SUNBURST in a corner, in the corner's own frame: `q` in points
// from the corner along its two edges, both into the surface. A small gold
// quarter disc on the corner, nine rays fanning out from it, long and short
// by turns, and a fine arc closing the long ones. `s` its reach in points.
let ad_fan = fn(dst: vec4, q: vec2, s: float, px: float, amt: float) -> vec4 {
var out = dst
let r = length(q)
if r > s + 2.0 || q.x < -1.0 || q.y < -1.0 {
return out
}
let phi = atan2(q.y, q.x)
let st = 1.5707963 / 8.0
let k = phi / st
let kf = floor(k + 0.5)
let dperp = abs(k - kf) * st * r
let long = 1.0 - step(0.25, abs(fract(kf * 0.5)))
let len = mix(s * 0.68, s, long)
let ray = Finish.cover(dperp - max(px * 0.5, 0.32), px) * Finish.cover(r - len, px) * step(r, len + 1.0) * smoothstep(3.5, 5.0, r)
let a = ray * amt * 0.9
out = Finish.over(out, vec4(ad_line_c() * a, a))
let arc = Finish.cover(abs(r - s * 0.68) - max(px * 0.5, 0.3), px) * amt * 0.7
out = Finish.over(out, vec4(ad_line_c() * arc, arc))
out = ad_dome(out, q, vec2(0.0, 0.0), 4.0, px, ad_gold_base())
return out
}
// The fan 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 ad_corner_fan = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, k: float, ins: float, s: float, px: float, amt: 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
return ad_fan(dst, vec2(x, y), s, px, amt)
}
// ---------------------------------------------------------------------------
// BUTTONS. One face for the family: a plate of satin metal with rounded
// corners in its own polished rim, its edge bevelled toward the light, a
// tight shadow under it, the legend in dark ink. `kind`: 0 antique brass
// (the plain button), 1 polished gold (primary), 2 champagne (secondary), 3
// ivory-cream (tertiary), 4 copper (the one that deletes, its legend in
// oxblood), 5 the outline (the ground shows inside a double gold line), 6
// bare until touched (flatter), 7 champagne (the flat root and its sizes),
// 8 black lacquer (the quiet plates of a strip: a tab, a closed drop-down).
// Pressed, a plate sinks and its mirror turns over; disabled, it goes flat
// and dull and a small unlit lamp shows at its left end. The quad is grown
// by `m` for the shadow and the focus ring.
let ad_face = fn(p: vec2, lo: vec2, hi: vec2, kind: float, hover: float, press: float) -> vec3 {
if kind > 7.5 {
return Finish.lift(ad_lacq(p, lo, hi, ad_black(), 1.0 + 1.6 * hover), 0.04 * hover)
}
if kind < 0.5 {
return ad_metal(p, lo, hi, ad_brass(), 0.8, hover, press)
}
if kind < 1.5 {
return ad_metal(p, lo, hi, ad_gold_base(), 1.0, hover, press)
}
if kind < 2.5 {
return ad_metal(p, lo, hi, ad_champ(), 0.7, hover, press)
}
if kind < 3.5 {
return ad_metal(p, lo, hi, ad_ivory(), 0.35, hover, press)
}
if kind < 4.5 {
return ad_metal(p, lo, hi, ad_copper(), 0.85, hover, press)
}
return ad_metal(p, lo, hi, ad_champ(), 0.7, hover, press)
}
let ad_button = fn(pos: vec2, size: vec2, o: vec2, px: float, hover: float, down: float, focus: float, dis: float, loading: float, anim: float, k4: 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 hov = hover * 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 d = ad_cut(p, c, h, k4)
let n = ad_cut_n(p, c, h, k4)
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(hov * 0.85 + press, 0.0, 1.0), bare)
let solid = 1.0 - max(outline, bare)
var out = ad_drop(p, c, h, 2.0, (1.0 - 0.7 * press) * mix(1.0, 0.5, dis) * solid)
let w = 1.5
let lacq = step(7.5, kind)
var face = ad_face(p, lo + vec2(w, w), hi - vec2(w, w), kind, hov, press)
// disabled: the metal goes flat and dull, the lacquer greyed
let tt = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
let dull = mix(vec3(0.440, 0.410, 0.350) * (1.06 - 0.12 * tt), mix(face, g3(Finish.lum(face)), 0.65) * 0.55 + vec3(0.025, 0.020, 0.015), lacq)
face = mix(face, dull, dis)
// the plate's bevelled shoulder inside the rim, lit from the upper
// left: light along its top and left, shade along its foot and right
let bev = Finish.bevel(d + w, n, 1.8, vec2(-0.45, -0.89)) * Finish.cover(d + w, px) * (1.0 - press)
face = face * (1.0 + 0.22 * max(bev, 0.0) * (1.0 - lacq) + 0.22 * min(bev, 0.0))
face = face + vec3(0.05, 0.045, 0.04) * max(bev, 0.0) * lacq
// pressed: the surround's shade falls in under the top edge
let sh = clamp(Finish.inset(p, c, h - vec2(w, w), 1.5, vec2(0.0, 1.7), 1.2), 0.0, 1.0) * press
face = mix(face, face * 0.3, sh * 0.85)
let fa = Finish.cover(d + w * 0.5, px)
out = Finish.over(out, vec4(face * fa, fa) * solid)
// the outline and the bare kinds: a faint gold wash under the pointer
let wash = max(outline, bare) * (0.10 * hov + 0.18 * press) * fa
out = Finish.over(out, vec4(ad_gold_md() * wash, wash))
// the pinstripe inside the rim, on the lacquer and the outline
let pin = max(lacq, outline) * mix(0.85, 0.35, dis)
out = Finish.over(out, ad_pin(d, w + 1.4, max(px, 0.6), px, pin))
// disabled, a small lamp at the plate's left end is out: a slot of
// milky glass in a dark seat
let slot = dis * solid * (1.0 - lacq) * step(50.0, size.x)
if slot > 0.5 {
let sc = vec2(lo.x + w + 4.4, c.y)
let sd = md_rbox(p, sc, vec2(1.3, 4.6), 1.3)
let sa = Finish.cover(sd - 0.7, px)
out = Finish.over(out, vec4(vec3(0.16, 0.13, 0.09) * sa, sa))
let lt = clamp((p.y - (sc.y - 4.6)) / 9.2, 0.0, 1.0)
let lc = mix(vec3(0.80, 0.76, 0.66), vec3(0.60, 0.56, 0.47), lt)
let la = Finish.cover(sd, px)
out = Finish.over(out, vec4(lc * la, la))
}
// the rim: pressed, its light turns over, so the plaque reads sunk in it
out = Finish.over(out, ad_rim(d, n * (1.0 - 2.0 * press), w, px, mix(1.0, 0.3, dis) * (1.0 + 0.5 * hov)) * mix(1.0, 0.5, dis) * show)
out = Finish.over(out, ad_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.93, 0.79, 0.48, loading))
out = Finish.over(out, sdf.result)
}
return out
}
// Every button template: the flat root (and the sizes, which share its draw
// object) wears champagne gold; 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(ad_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, 3.5), 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(ad_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, 3.5), 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(ad_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, 3.5), 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(ad_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, 3.5), 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(ad_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, 3.5), 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(ad_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, 3.5), 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(ad_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, 3.5), 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(ad_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, 3.5), 6.0, 8.0), 8.0)
}
// The large button is the black lacquer one, as the plain button.
mod.widgets.ButtonLg.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return self.host_over(ad_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, 4.5), 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: 12.0 right: 12.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 = mod.theme.font_label{font_size: 5.5}
mod.widgets.ButtonXl.draw_text.text_style = mod.theme.font_label{font_size: 9.5}
mod.widgets.ButtonSm.min_height = 0.0
// Words on the plates: the wide geometric legend face in dark ink; gold on
// the outline and on a bare button, which stand on the ground.
mod.widgets.ButtonFlat.draw_text.text_style = mod.theme.font_label{font_size: 6.5}
mod.widgets.ButtonFlat.draw_text.color = #22170a
mod.widgets.ButtonFlat.draw_text.color_hover = #160e05
mod.widgets.ButtonFlat.draw_text.color_down = #160e05
mod.widgets.ButtonFlat.draw_text.color_focus = #22170a
mod.widgets.ButtonFlat.draw_text.color_disabled = #3d3529
mod.widgets.Button.draw_text.text_style = mod.theme.font_label{font_size: 7.0}
mod.widgets.Button.draw_text.color = #1e1408
mod.widgets.Button.draw_text.color_hover = #120b03
mod.widgets.Button.draw_text.color_down = #120b03
mod.widgets.Button.draw_text.color_focus = #1e1408
mod.widgets.Button.draw_text.color_disabled = #3d3529
// An icon on a plate is in the plate's dark ink, whatever colour its
// caller asked for (the catalogue asks for the working cream, which a
// brass plate would swallow); on the outline and on a bare button, which
// stand on the dark ground, it is gold.
mod.widgets.ButtonFlat.draw_icon.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.110, 0.075, 0.035)
return vec4(k * base.a, base.a) * self.opacity
}
mod.widgets.Button.draw_icon.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.110, 0.075, 0.035)
return vec4(k * base.a, base.a) * self.opacity
}
mod.widgets.ButtonOutline.draw_icon.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.880, 0.780, 0.560)
return vec4(k * base.a, base.a) * self.opacity
}
mod.widgets.ButtonFlat.draw_icon_end.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.110, 0.075, 0.035)
return vec4(k * base.a, base.a) * self.opacity
}
mod.widgets.Button.draw_icon_end.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.110, 0.075, 0.035)
return vec4(k * base.a, base.a) * self.opacity
}
mod.widgets.ButtonOutline.draw_icon_end.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.880, 0.780, 0.560)
return vec4(k * base.a, base.a) * self.opacity
}
mod.widgets.ButtonFlatter.draw_icon_end.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.880, 0.780, 0.560)
return vec4(k * base.a, base.a) * self.opacity
}
// A compound button's second line, on the plate: a quieter brown.
mod.widgets.ButtonFlat.draw_description.color = #4a3a22
mod.widgets.ButtonFlat.draw_description.text_style = mod.theme.font_regular{font_size: 8.0}
mod.widgets.ButtonFlatter.draw_icon.get_color = fn() {
let base = self.eval_gradient()
let k = vec3(0.880, 0.780, 0.560)
return vec4(k * base.a, base.a) * self.opacity
}
mod.widgets.ButtonLg.draw_text.text_style = mod.theme.font_label{font_size: 8.5}
mod.widgets.ButtonLg.draw_text.color = #1e1408
mod.widgets.ButtonLg.draw_text.color_hover = #120b03
mod.widgets.ButtonLg.draw_text.color_down = #120b03
mod.widgets.ButtonLg.draw_text.color_focus = #1e1408
mod.widgets.ButtonLg.draw_text.color_disabled = #3d3529
mod.widgets.ButtonSm.draw_text.text_style = mod.theme.font_label{font_size: 6.5}
mod.widgets.ButtonPrimary.draw_text.text_style = mod.theme.font_label{font_size: 7.0}
mod.widgets.ButtonPrimary.draw_text.color = #1c1206
mod.widgets.ButtonPrimary.draw_text.color_hover = #120b03
mod.widgets.ButtonPrimary.draw_text.color_down = #120b03
mod.widgets.ButtonPrimary.draw_text.color_focus = #1c1206
mod.widgets.ButtonPrimary.draw_text.color_disabled = #3d3529
mod.widgets.ButtonPrimary.draw_icon.color = #1c1206
mod.widgets.ButtonSecondary.draw_text.text_style = mod.theme.font_label{font_size: 7.0}
mod.widgets.ButtonSecondary.draw_text.color = #22170a
mod.widgets.ButtonSecondary.draw_text.color_hover = #160e05
mod.widgets.ButtonSecondary.draw_text.color_down = #160e05
mod.widgets.ButtonSecondary.draw_text.color_focus = #22170a
mod.widgets.ButtonSecondary.draw_icon.color = #22170a
mod.widgets.ButtonTertiary.draw_text.text_style = mod.theme.font_label{font_size: 7.0}
mod.widgets.ButtonTertiary.draw_text.color = #2a1f10
mod.widgets.ButtonTertiary.draw_text.color_hover = #1c1408
mod.widgets.ButtonTertiary.draw_text.color_down = #1c1408
mod.widgets.ButtonTertiary.draw_text.color_focus = #2a1f10
mod.widgets.ButtonTertiary.draw_icon.color = #2a1f10
mod.widgets.ButtonDanger.draw_text.text_style = mod.theme.font_label{font_size: 7.0}
mod.widgets.ButtonDanger.draw_text.color = #3a0c06
mod.widgets.ButtonDanger.draw_text.color_hover = #2a0703
mod.widgets.ButtonDanger.draw_text.color_down = #2a0703
mod.widgets.ButtonDanger.draw_text.color_focus = #3a0c06
mod.widgets.ButtonDanger.draw_icon.color = #3a0c06
mod.widgets.ButtonOutline.draw_text.text_style = mod.theme.font_label{font_size: 7.0}
mod.widgets.ButtonOutline.draw_text.color = #e0c891
mod.widgets.ButtonOutline.draw_text.color_hover = #f0dcaa
mod.widgets.ButtonOutline.draw_text.color_down = #f0dcaa
mod.widgets.ButtonOutline.draw_text.color_focus = #e0c891
mod.widgets.ButtonOutline.draw_text.color_disabled = #6a5c44
mod.widgets.ButtonOutline.draw_icon.color = #e0c891
mod.widgets.ButtonFlatter.draw_text.color = #e0c891
mod.widgets.ButtonFlatter.draw_text.color_hover = #f0dcaa
mod.widgets.ButtonFlatter.draw_text.color_down = #f0dcaa
mod.widgets.ButtonFlatter.draw_text.color_focus = #e0c891
mod.widgets.ButtonFlat.draw_bg.loading_color = #ecd08e
// The window's own buttons stand on the black lacquer: cream strokes.
mod.widgets.DesktopButton.draw_bg.color = uniform(#ede2c6)
mod.widgets.DesktopButton.draw_bg.color_hover = uniform(#fff6df)
mod.widgets.DesktopButton.draw_bg.color_down = uniform(#e0d4b6)
mod.widgets.DesktopButton.draw_bg.bg_color_hover = uniform(#ede2c620)
mod.widgets.DesktopButton.draw_bg.bg_color_down = uniform(#ede2c638)
// ---------------------------------------------------------------------------
// CHECK BOXES. A 14 point well of black lacquer, its corners rounded, in
// its gold rim; set, a plaque of polished gold fills it with a black check;
// mixed, the gold with a black 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 k = mix(2.5, h.y, step(20.0, self.border_radius))
let k4 = vec4(k, k, k, k)
let d = ad_cut(p, c, h, k4)
let n = ad_cut_n(p, c, h, k4)
let on = max(self.active, self.mixed)
let press = self.down * live
var fill = mix(vec3(0.016, 0.014, 0.013), vec3(0.045, 0.038, 0.030), self.hover * live)
fill = mix(fill, vec3(0.06, 0.05, 0.04), self.disabled)
var out = ad_well(p, c, h, k, d, n, px, fill, 0.6, 1.2, self.disabled)
out = Finish.over(out, ad_pin(d, 1.8, max(px, 0.6), px, 0.55 * self.hover * live * (1.0 - on)))
// the gold when set
let hf = h - vec2(1.2, 1.2)
var gcol = ad_metal(p, c - hf, c + hf, ad_gold_base(), 1.0, self.hover * live, press)
gcol = mix(gcol, vec3(0.30, 0.26, 0.20), self.disabled)
let ga = Finish.cover(ad_cut(p, c, hf, k4 - vec4(0.5, 0.5, 0.5, 0.5)), px) * on
out = Finish.over(out, vec4(gcol * ga, ga))
let kk = sz / 14.0
let m0 = lo + vec2(3.6, 7.3) * kk
let m1 = lo + vec2(6.0, 9.8) * kk
let m2 = lo + vec2(10.6, 4.3) * kk
let ck = min(md_seg(p, m0, m1), md_seg(p, m1, m2)) - 0.9 * kk
let d0 = lo + vec2(4.0, 7.0) * kk
let d1 = lo + vec2(10.0, 7.0) * kk
let dash = md_seg(p, d0, d1) - 0.9 * kk
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(vec3(0.07, 0.045, 0.02), vec3(0.15, 0.13, 0.10), 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.75, 0.20, 0.15) * err, err))
return Finish.over(out, ad_focus(d, px, self.focus * live))
}
// ---------------------------------------------------------------------------
// TOGGLES. A 38 by 16 point pill of black lacquer sunk in its gold rim, a
// 12 point ball of polished gold riding in it with its own small shadow;
// while on, the check at its left end burns warm white; while off, a small
// ring is engraved at its right end. The slide switch (below) fills its
// track with gold while on and rides an ivory dome.
let ad_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, slide: 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.016, 0.014, 0.013), vec3(0.040, 0.034, 0.028), hover * live)
fill = mix(fill, vec3(0.06, 0.05, 0.04), dis)
var out = ad_well_r(p, c, h, r, px, fill, 0.6, 1.3, dis)
out = Finish.over(out, ad_pin(d, 1.9, max(px, 0.6), px, 0.55 * hover * live * (1.0 - on)))
let inner = Finish.cover(d + 1.3, px)
let cq = p - (lo + vec2(8.0, 8.0))
let ck = min(md_seg(cq, vec2(-3.0, 0.2), vec2(-1.0, 2.4)), md_seg(cq, vec2(-1.0, 2.4), vec2(3.2, -2.4))) - 0.75
if slide > 0.5 {
// the slide switch: the gold of the lit track, a check cut in it
let hf = h - vec2(1.3, 1.3)
var gcol = ad_metal(p, c - hf, c + hf, ad_gold_base(), 1.0, hover * live, 0.0)
gcol = mix(gcol, vec3(0.30, 0.26, 0.20), dis)
let ga = Finish.cover(md_rbox(p, c, hf, hf.y), px) * on
out = Finish.over(out, vec4(gcol * ga, ga))
let cka = Finish.cover(ck, px) * on * 0.85
out = Finish.over(out, vec4(vec3(0.10, 0.065, 0.025) * cka, cka))
} else {
// the toggle: the track stays black; while on, the check at its
// left end burns warm white in a short amber glow; while off, a
// small ring is engraved at its right end
let lit = on * live
let cg = exp(0.0 - max(ck, 0.0) / 1.2) * lit * 0.6 * inner
out = Finish.over(out, vec4(vec3(1.0, 0.68, 0.30) * cg, cg))
let cka = Finish.cover(ck, px) * on
let ckc = mix(vec3(0.40, 0.36, 0.30), vec3(1.0, 0.94, 0.78), live)
out = Finish.over(out, vec4(ckc * cka, cka))
let rq = p - (lo + vec2(tw - 8.0, 8.0))
let ring = abs(length(rq) - 2.2) - 0.45
let ra = Finish.cover(ring, px) * (1.0 - on) * mix(0.75, 0.4, dis)
out = Finish.over(out, vec4(ad_gold_md() * ra, ra))
}
let ea = Finish.band(d, 0.0, 2.0 * px, px) * err
out = Finish.over(out, vec4(vec3(0.75, 0.20, 0.15) * ea, ea))
// the ball: polished gold on the toggle, an ivory dome on the slide
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, ad_drop(p, kc, vec2(kr, kr), kr, (0.9 - 0.5 * press) * mix(1.0, 0.5, dis)))
let q = p - kc
let kd = length(q) - kr
let kn = q / max(length(q), 0.001)
let lit2 = clamp(0.5 - 0.55 * dot(q / kr, vec2(0.45, 0.85)), 0.0, 1.0)
let iv = mix(ad_ivory_dk(), ad_ivory(), 0.3 + 0.7 * lit2)
let gd = mix(ad_gold_dk(), ad_gold_base(), 0.3 + 0.7 * lit2)
var face = mix(gd, iv, slide)
face = mix(face, vec3(1.0, 0.99, 0.95), exp(0.0 - dot(q / kr - vec2(-0.35, -0.42), q / kr - vec2(-0.35, -0.42)) / 0.05) * 0.65)
face = Finish.lift(face, 0.12 * hover * live * (1.0 - press))
face = mix(face, face * 0.88, press)
face = mix(face, mix(face, g3(Finish.lum(face)), 0.7) * 0.5, dis)
let ka = Finish.cover(kd, px)
out = Finish.over(out, vec4(face * ka, ka))
out = Finish.over(out, ad_rim(kd, kn, 1.1, px, live) * mix(1.0, 0.5, dis))
return Finish.over(out, ad_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 ad_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 seat of black lacquer in its gold rim holding a polished
// gold bead; the chosen one is a lamp instead, burning white at its middle
// and warm at its rim, its glow held in the seat.
let ad_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 lr = length(q)
let d = lr - rad
var fill = mix(vec3(0.016, 0.014, 0.013), vec3(0.045, 0.038, 0.030), hover * live)
fill = mix(fill, vec3(0.06, 0.05, 0.04), dis)
var out = ad_well_r(p, c, vec2(rad, rad), rad, px, fill, 0.6, 1.2, dis)
out = Finish.over(out, ad_pin(d, 1.8, max(px, 0.6), px, 0.45 * hover * live * (1.0 - active)))
let br = rad * 0.58
// the glow of the lamp, held inside its seat
let on = active * live
let glow = exp(0.0 - max(lr - br, 0.0) / 1.3) * on * Finish.cover(d + 1.2, px) * 0.75
out = Finish.over(out, vec4(vec3(1.0, 0.70, 0.34) * glow, glow))
// not chosen: a polished gold bead, a little lower while pressed
let bead = ad_dome(vec4(0.0, 0.0, 0.0, 0.0), p, c, br, px, mix(ad_gold_lt(), vec3(0.36, 0.31, 0.24), dis))
out = Finish.over(out, bead * (1.0 - active) * (1.0 - 0.3 * down * live))
// chosen: the lamp itself, its middle burning white, warm at its rim
let t = clamp(lr / br, 0.0, 1.0)
var lc = mix(vec3(1.0, 0.99, 0.95), vec3(1.0, 0.78, 0.46), smoothstep(0.25, 1.0, t))
lc = mix(lc, vec3(0.46, 0.42, 0.35), dis)
let la = Finish.cover(lr - br, px) * active
out = Finish.over(out, vec4(lc * la, la))
let gq = q / br - vec2(-0.32, -0.38)
let gl = exp(0.0 - dot(gq, gq) / 0.04) * la * 0.55
out = Finish.over(out, vec4(vec3(1.0, 1.0, 1.0) * gl, gl))
return Finish.over(out, ad_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 ad_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
// lacquer plaque in a gold rim; under the pointer a faint gold 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 k4 = vec4(2.5, 2.5, 2.5, 2.5)
let d = ad_cut(p, c, h, k4)
let wash = Finish.cover(d, px) * 0.15 * self.hover * live * (1.0 - self.active)
var out = vec4(ad_gold_md() * wash, wash)
let face = ad_lacq(p, lo, hi, ad_black(), 1.0)
let a = Finish.cover(d, px) * self.active
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, ad_rim(d, ad_cut_n(p, c, h, k4), 1.2, px, 1.0) * self.active)
return Finish.over(out, ad_focus(d, px, self.focus * live))
}
// Radio tabs (the catalogue's Docs, Controls, Actions, Theme): lacquer
// plaques in their rims; the chosen one champagne gold pressed in, its word
// dark.
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 k4 = vec4(3.0, 3.0, 3.0, 3.0)
let dark = ad_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, 0.0, 0.0, k4, 8.0, 6.0)
let gold = ad_button(self.pos, self.rect_size, self.rect_pos, px, self.hover * 0.5, 0.6, self.focus, self.disabled, 0.0, 0.0, k4, 2.0, 6.0)
return mix(dark, gold, self.active)
}
mod.widgets.RadioButtonTabFlat.draw_text.color = #ede2c6
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #1c1206
mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #7a6e5a
mod.widgets.RadioButtonTabFlat.draw_text.text_style = mod.theme.font_label{font_size: 7.0}
// ---------------------------------------------------------------------------
// SLIDERS. A gold tube: a capsule three and a half points high in its
// polished rim, a black slot inside, polished gold filling the slot up to
// the value; fine gold ticks above it. The thumb is a gold streamline wing:
// an upright capsule on the value with three feathers swept back from its
// shoulder, each shorter than the one above it, with its own shadow. Every
// slider's quad grows for the wing, its shadow and the focus ring.
let ad_tube = fn(p: vec2, c: vec2, h: vec2, px: float, f0: float, f1: float, dis: float, hover: float) -> vec4 {
let r = h.y
var out = ad_well_r(p, c, h, r, px, vec3(0.012, 0.011, 0.010), 0.5, 1.4, dis)
let w = 1.4
let fh = vec2(max((f1 - f0) * 0.5, 0.0), max(h.y - w - 0.5, 0.6))
let fc = vec2((f0 + f1) * 0.5, c.y)
let fd = md_rbox(p, fc, fh, fh.y)
var fcol = ad_metal(p, fc - fh - vec2(0.0, 0.8), fc + fh + vec2(0.0, 0.8), ad_gold_base(), 1.0, hover, 0.0)
fcol = mix(fcol, vec3(0.30, 0.26, 0.20), dis)
let fa = Finish.cover(fd, px) * step(0.01, f1 - f0) * Finish.cover(md_rbox(p, c, h, r) + w, px)
return Finish.over(out, vec4(fcol * fa, fa))
}
// 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 ad_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)
}
// A feather of the wing: a stroke along the curve `A`, `B`, `C`, tapering
// from `r0` at its root to a rounded tip of `r1`, rounded across, lit along
// its top, with a dark groove along its edge where it lies over the next.
let ad_feather = fn(dst: vec4, p: vec2, A: vec2, B: vec2, C: vec2, r0: float, r1: float, t: float, px: float, hover: float, dis: float) -> vec4 {
let bz = ad_bez(p, A, B, C)
let u = 1.0 - bz.y
let q = A * u * u + B * 2.0 * u * bz.y + C * bz.y * bz.y
let axis = p - q
let rr = mix(r0, r1, bz.y)
let d = bz.x - rr
let n = axis / max(length(axis), 0.0001)
var col = ad_gold_shape(d, n, rr * 1.2, t, hover, dis)
col = mix(col, ad_gold_dk() * 0.35, Finish.band(d, 0.0, max(px, 0.55), px) * 0.8)
let fa = Finish.cover(d, px)
return Finish.over(dst, vec4(col * fa, fa))
}
let ad_wing = fn(p: vec2, kc: vec2, s: float, px: float, hover: float, press: float, dis: float) -> vec4 {
let live = 1.0 - dis
let bh = vec2(5.0, 8.5) * s
let bc = kc + vec2(0.0, 0.5 * s)
var out = ad_drop(p, bc, bh, bh.x, (1.0 - 0.5 * press) * mix(1.0, 0.5, dis))
out = Finish.over(out, ad_drop(p, kc + vec2(-9.5, -5.0) * s, vec2(9.0, 4.5) * s, 3.0 * s, 0.5 * (1.0 - 0.5 * press)))
let t = clamp((p.y - (kc.y - 12.0 * s)) / (21.0 * s), 0.0, 1.0)
let hv = hover * live * (1.0 - press)
if p.x < kc.x + 3.0 * s && p.y < kc.y + 2.5 * s && p.y > kc.y - 14.5 * s && p.x > kc.x - 22.0 * s {
out = ad_feather(out, p, kc + vec2(0.0, -1.0) * s, kc + vec2(-6.5, -1.3) * s, kc + vec2(-10.8, -3.4) * s, 2.2 * s, 1.0 * s, t, px, hv, dis)
out = ad_feather(out, p, kc + vec2(0.0, -4.1) * s, kc + vec2(-8.5, -4.6) * s, kc + vec2(-14.0, -7.0) * s, 2.2 * s, 1.0 * s, t, px, hv, dis)
out = ad_feather(out, p, kc + vec2(0.0, -7.2) * s, kc + vec2(-10.5, -7.9) * s, kc + vec2(-17.2, -10.3) * s, 2.3 * s, 1.05 * s, t, px, hv, dis)
}
// the body, over the roots of the feathers
let db = md_rbox(p, bc, bh, bh.x)
let bn = ad_nrm(p, bc, bh, bh.x)
var bcol = ad_gold_shape(db, bn, 2.4 * s, t, hv, dis)
bcol = mix(bcol, bcol * 0.88, press)
bcol = mix(bcol, ad_gold_dk() * 0.4, Finish.band(db, 0.0, max(px, 0.5), px) * 0.75)
let ba = Finish.cover(db, px)
return Finish.over(out, vec4(bcol * ba, ba))
}
// Fine gold ticks above a track: one every tenth of the travel, the ends
// and the middle longer.
let ad_ticks = fn(p: vec2, x0: float, span: float, ytop: float, px: float, dis: float) -> vec4 {
let f = (p.x - x0) / max(span, 1.0) * 10.0
let fi = floor(f + 0.5)
let dx = abs(p.x - (x0 + fi / 10.0 * span))
let major = 1.0 - step(0.1, abs(fi * 0.2 - floor(fi * 0.2 + 0.5)))
let len = mix(2.0, 3.4, major)
let inr = step(-0.5, fi) * step(fi, 10.5)
let tk = Finish.cover(dx - max(px * 0.5, 0.3), px) * step(ytop - len, p.y) * step(p.y, ytop) * inr * mix(0.85, 0.4, dis)
return vec4(ad_line_c() * tk, tk)
}
mod.widgets.SliderFlat.height = 36.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, 18.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 = 18.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 - 9.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 = ad_ticks(p, x0 + 9.0, span, cy - 6.0, px, self.disabled)
out = Finish.over(out, ad_tube(p, vec2((x0 + x1) * 0.5, cy), vec2(w * 0.5, 4.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, ad_wing(p, kc, 1.0, px, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
return Finish.over(out, ad_focus(md_rbox(p, kc + vec2(0.0, 0.5), vec2(5.0, 8.5), 5.0), 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 tube, gold between the two ends, a round gold
// coin on each end with a ring engraved in it, pressed while it is the one
// held.
let ad_coin = fn(p: vec2, c: vec2, rad: float, px: float, hover: float, press: float, dis: float) -> vec4 {
var out = ad_drop(p, c, vec2(rad, rad), rad, (1.0 - 0.5 * press) * mix(1.0, 0.5, dis))
let q = p - c
let d = length(q) - rad
let n = q / max(length(q), 0.001)
let t = clamp((p.y - (c.y - rad)) / (2.0 * rad), 0.0, 1.0)
var col = ad_gold_shape(d, n, rad * 0.45, t, hover * (1.0 - press), dis)
col = mix(col, col * 0.88, press)
col = mix(col, ad_gold_dk() * 0.5, Finish.band(d, 0.0, max(px, 0.45), px) * 0.6)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(col * a, a))
let ring = Finish.band(d, rad * 0.42, rad * 0.42 + max(px, 0.5), px) * 0.7
out = Finish.over(out, vec4(ad_gold_dk() * 0.6 * ring, ring))
let ringl = Finish.band(d, rad * 0.42 + max(px, 0.5), rad * 0.42 + 2.0 * max(px, 0.5), px) * 0.5
return Finish.over(out, vec4(ad_gold_hi() * ringl, ringl))
}
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 = ad_tube(p, vec2((x0 + x1) * 0.5, cy), vec2(w * 0.5, 4.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, ad_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, ad_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 gold hairline (a dark device pixel over a bright
// one), a brighter gold thread along it to the thumb, and the wing a size
// smaller riding on it.
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, 16.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 = 16.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.75 * mix(1.0, 0.5, self.disabled)
out = Finish.over(out, vec4(ad_gold_md() * la, la))
let da = dark * 0.9
out = Finish.over(out, vec4(vec3(0.0, 0.0, 0.0) * da, da))
let tcol = mix(ad_gold_lt(), vec3(0.36, 0.31, 0.24), self.disabled)
out = Finish.over(out, vec4(tcol * thread, thread))
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, cy - 1.0)
out = Finish.over(out, ad_wing(p, kc, 0.8, px, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
return Finish.over(out, ad_focus(md_rbox(p, kc + vec2(0.0, 0.4), vec2(4.0, 6.8), 4.0), 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 short gold tube beside its label, gold to the value,
// a gold ball on the end of the gold; 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 + 2.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 - 2.0, px) - o.y
let h = vec2((x1 - x0) * 0.5, (y1 - y0) * 0.5)
let c = vec2(x0, y0) + h
let ins = 1.4
let kr = h.y - ins + 0.2
let kx = x0 + ins + kr + self.slide_pos * (2.0 * h.x - 2.0 * ins - 2.0 * kr)
var out = ad_tube(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, ad_coin(p, kc, kr + 1.0, px, self.hover, self.drag * live, self.disabled))
return Finish.over(out, ad_focus(md_rbox(p, c, h, h.y), px, self.focus * live))
}
// ---------------------------------------------------------------------------
// FADERS. A black slot five points wide in its gold rim, fine gold ticks
// either side, a gold thread in the slot for the value; the cap a polished
// gold block 22 points across with its corners rounded, two grip grooves cut
// across it and a black line with a cream thread through its middle.
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 = ad_well_r(p, sc, sh, sw * 0.5, px, vec3(0.010, 0.009, 0.008), 0.5, 1.0, self.disabled)
// the value: a gold thread in the slot, from the origin (or the foot)
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.6, 0.6 + px, abs(b - mid))) * live
out = Finish.over(out, vec4(ad_gold_lt() * 0.95 * vl, vl * 0.95))
// 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.85 * mix(1.0, 0.5, self.disabled)
out = Finish.over(out, vec4(ad_line_c() * 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 k4 = vec4(2.5, 2.5, 2.5, 2.5)
let cd = ad_cut(p, cc, ch, k4)
let press = self.drag * live
out = Finish.over(out, ad_drop(p, cc, ch, 2.0, (1.0 - press * 0.5) * mix(1.0, 0.5, self.disabled)))
var face = ad_metal(p, cc - ch, cc + ch, ad_gold_base(), 1.0, max(self.hover, self.focus * 0.5) * live, press)
face = mix(face, vec3(0.30, 0.26, 0.20), self.disabled)
let fa = Finish.cover(cd, px)
out = Finish.over(out, vec4(face * fa, fa))
// the grooves and the middle line, across the cap
let ln = mix(abs(p.y - cc.y), abs(p.x - cc.x), 1.0 - vert)
let inside = Finish.cover(cd + 2.0, px)
let gr = (1.0 - smoothstep(0.35, 0.35 + px, abs(ln - 3.6))) * inside * 0.55
out = Finish.over(out, vec4(vec3(0.20, 0.13, 0.05) * gr, gr))
let bl = (1.0 - smoothstep(0.9, 0.9 + px, ln)) * inside
out = Finish.over(out, vec4(vec3(0.02, 0.018, 0.016) * bl, bl))
let cl = (1.0 - smoothstep(0.25, 0.25 + px, ln)) * inside * 0.9
out = Finish.over(out, vec4(ad_cream() * cl, cl))
out = Finish.over(out, ad_rim(cd, ad_cut_n(p, cc, ch, k4), 1.2, px, live) * mix(1.0, 0.5, self.disabled))
return Finish.over(out, ad_focus(cd, px, self.focus * live))
}
// ---------------------------------------------------------------------------
// KNOBS. An ivory knob: a fluted skirt, its flutes running round its side
// and turning with the light from the upper left, under a cap of spun gold
// in a polished collar; the pointer a lit cream slit in the cap. Round it a
// fine gold track and the value in bright gold.
let ad_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 = ad_drop(p, c, vec2(rad, rad), rad, (1.0 - 0.4 * press) * mix(1.0, 0.5, dis))
let q = p - c
let l = length(q)
let d = l - rad
let n = q / max(l, 0.001)
// the fluted skirt
let th = atan2(q.y, q.x)
let nf = max(floor(rad * 1.1), 14.0)
let fl = cos(th * nf)
let lit = clamp(0.5 - 0.6 * dot(n, vec2(0.45, 0.85)), 0.0, 1.0)
var sk = mix(ad_ivory_dk(), ad_ivory(), 0.2 + 0.8 * lit)
sk = sk * (0.86 + 0.14 * fl)
sk = mix(sk, sk * 0.72, smoothstep(0.82, 1.0, l / rad) * 0.6)
sk = Finish.lift(sk, 0.08 * hover * live)
sk = mix(sk, mix(sk, g3(Finish.lum(sk)), 0.7) * 0.55, dis)
sk = mix(sk, vec3(0.30, 0.25, 0.18), Finish.band(d, 0.0, max(px, 0.5), px) * 0.6)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(sk * a, a))
// the spun gold cap in its collar
let cr = rad * 0.64
let dc = l - cr
var cap = ad_gold_base() * Finish.spun(p, c, vec2(-0.45, -0.89))
cap = mix(cap, ad_gold_hi(), smoothstep(0.55, 1.0, lit) * 0.25)
cap = Finish.lift(cap, 0.10 * hover * live * (1.0 - press))
cap = mix(cap, cap * 0.88, press)
cap = mix(cap, mix(cap, g3(Finish.lum(cap)), 0.7) * 0.55, dis)
let ca = Finish.cover(dc, px)
out = Finish.over(out, vec4(min(cap, vec3(1.0, 1.0, 1.0)) * ca, ca))
out = Finish.over(out, ad_rim(dc, n, max(0.9, rad * 0.07), px, live) * mix(1.0, 0.5, dis))
// the pointer: a lit cream slit cut in the cap from its middle to its
// edge, a dark hairline round it
let dir = vec2(0.0 - sin(ang), cos(ang))
let lw = max(0.5, rad * 0.04)
let ld = md_seg(p, c + dir * cr * 0.10, c + dir * (cr - max(1.0, rad * 0.07)))
let ea = Finish.cover(ld - lw - max(px, 0.45), px) * 0.7
out = Finish.over(out, vec4(vec3(0.06, 0.04, 0.02) * ea, ea))
let la = Finish.cover(ld - lw, px)
let lcol = mix(vec3(1.0, 0.97, 0.88), vec3(0.60, 0.56, 0.48), dis)
return Finish.over(out, vec4(lcol * la, la))
}
// 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 gold line with a
// tick at either end, the value bright gold.
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.6, 0.4)
let in_arc = step(start, ang) * step(ang, start + sweep)
let e0 = vec2(0.0 - sin(start), cos(start))
let e1 = vec2(0.0 - sin(start + sweep), cos(start + sweep))
let t0 = md_seg(p, c + e0 * (ar - aw), c + e0 * (ar + aw * 1.2)) - max(px * 0.6, 0.4)
let t1 = md_seg(p, c + e1 * (ar - aw), c + e1 * (ar + aw * 1.2)) - max(px * 0.6, 0.4)
let track_d = min(mix(1000.0, rt, in_arc), min(t0, t1))
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.75
var out = vec4(ad_line_c() * ta, ta)
let la = Finish.cover(lit_d, px)
let ink = mix(ad_gold_lt(), vec3(0.36, 0.31, 0.24), dis)
return Finish.over(out, vec4(ink * la, la))
}
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.6
let ar = floor((rr - aw * 0.5) / px) * px
let rad = floor((ar - aw * 0.5 - 2.6) / 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, ad_knob(p, c, rad, px, ang, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
return Finish.over(out, ad_focus(length(p - c) - ar - aw * 0.5, px, self.focus * live))
}
// Knob legends are small wide capitals in gold.
mod.widgets.RotaryKnob.draw_text.color = #d8c08c
mod.widgets.RotaryKnob.draw_text.text_style = mod.theme.font_label{font_size: 6.5}
// The dial: the knob above set in a sunburst. A polished gold band round
// the outside steps down to a ring of fan blades, ivory and gold by turns,
// each ivory blade carrying an LED slot that lights warm white up to the
// value; a gold step inside the fan, a black moat, and in it the value as a
// glowing ring round the knob. Where the sweep leaves its gap the fan is
// black lacquer.
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 dis = 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 start = gap * 0.5
let sweep = 2.0 * PI - gap
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)
let q = p - c
let l = length(q)
let n = q / max(l, 0.001)
var ang = atan2(0.0 - q.x, q.y)
if ang < 0.0 {
ang = ang + 2.0 * PI
}
let d_out = l - rr
var out = ad_drop(p, c, vec2(rr, rr), rr, mix(1.0, 0.5, dis))
// the black lacquer disc
let lac = ad_lacq(p, c - vec2(rr, rr), c + vec2(rr, rr), ad_black(), 0.6)
let ba = Finish.cover(d_out, px)
out = Finish.over(out, vec4(lac * ba, ba))
// the ring of fan blades over the sweep
let bw = max(2.0, rr * 0.075)
let fo = rr - bw
let fw = max(5.0, rr * 0.22)
let fi = fo - fw
let nb = max(floor(sweep / (2.0 * PI / 24.0) + 0.5), 4.0)
let seg = sweep / nb
let kf = floor((ang - start) / seg)
let ivory = 1.0 - step(0.25, abs(fract(kf * 0.5)))
let in_sweep = step(start, ang) * step(ang, start + sweep)
let ring = Finish.cover(l - fo, px) * Finish.cover(fi - l, px) * in_sweep
let u = clamp((l - fi) / max(fw, 0.01), 0.0, 1.0)
let side = fract((ang - start) / seg)
var blade = mix(ad_gold_base() * (0.85 + 0.35 * (1.0 - side)), ad_ivory() * (0.80 + 0.22 * (1.0 - side)), ivory)
blade = blade * (0.88 + 0.16 * clamp(0.5 - 0.6 * dot(n, vec2(0.45, 0.85)), 0.0, 1.0))
blade = mix(blade, mix(blade, g3(Finish.lum(blade)), 0.7) * 0.55, dis)
// a dark hairline between the blades
let bdist = min(side, 1.0 - side) * seg * l
blade = mix(blade, vec3(0.06, 0.04, 0.02), Finish.cover(bdist - max(px * 0.5, 0.3), px) * 0.8)
out = Finish.over(out, vec4(blade * ring, ring))
// the LED slot in each ivory blade, lit up to the value
let bmid = start + (kf + 0.5) * seg
let bv = (kf + 0.5) / nb
let led_on = step(v0 - 0.001, bv) * step(bv, v1 + 0.001) * live
let dir = vec2(0.0 - sin(bmid), cos(bmid))
let sd = md_seg(p, c + dir * (fi + fw * 0.30), c + dir * (fi + fw * 0.70)) - max(0.75, rr * 0.022)
let sa = Finish.cover(sd, px) * ivory * in_sweep
let sglow = exp(0.0 - max(sd, 0.0) / 1.2) * ivory * in_sweep * led_on * step(fi, l) * step(l, fo)
out = Finish.over(out, vec4(vec3(1.0, 0.70, 0.30) * sglow * 0.55, sglow * 0.55))
let scol = mix(vec3(0.16, 0.12, 0.08), vec3(1.0, 0.97, 0.88), led_on)
out = Finish.over(out, vec4(scol * sa, sa))
// the polished band outside, and the gold step inside the fan
out = Finish.over(out, ad_rim(d_out, n, bw, px, live) * mix(1.0, 0.5, dis))
let sw = max(1.4, rr * 0.045)
let d_step = l - fi
out = Finish.over(out, ad_rim(d_step, n, sw, px, live) * mix(1.0, 0.5, dis))
// the moat and the glowing value ring round the knob
let kr = fi - sw - max(3.0, rr * 0.09)
let gr = (fi - sw + kr) * 0.5
let la0 = start + sweep * v0
let la1 = start + sweep * v1
let lit_in = step(la0, ang) * step(ang, la1)
let gd = abs(l - gr) - 0.6
let core = Finish.cover(gd, px) * lit_in * live
let halo = exp(0.0 - max(gd, 0.0) / 1.1) * lit_in * live * step(kr, l) * step(l, fi - sw)
out = Finish.over(out, vec4(vec3(1.0, 0.72, 0.32) * halo * 0.6, halo * 0.6))
out = Finish.over(out, vec4(vec3(1.0, 0.97, 0.88) * core, core))
let kang = start + sweep * self.slide_pos
out = Finish.over(out, ad_knob(p, c, kr, px, kang, max(self.hover, self.focus * 0.5), self.drag * live, dis))
return Finish.over(out, ad_focus(d_out, px, self.focus * live))
}
// The dial's readout: a small plaque of the screens' black glass in its
// gold rim, 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 k4 = vec4(2.5, 2.5, 2.5, 2.5)
let d = ad_cut(p, c, h, k4)
let t = clamp((p.y - lo.y) / max(2.0 * h.y, 1.0), 0.0, 1.0)
var col = vec3(0.020, 0.018, 0.016) * (1.6 - 0.8 * t)
col = mix(col, vec3(0.0, 0.0, 0.0), clamp(Finish.inset(p, c, h, 2.0, vec2(0.0, 1.2), 1.0), 0.0, 1.0) * 0.5)
let a = Finish.cover(d, px)
var out = ad_drop(p, c, h, 2.0, 0.6)
out = Finish.over(out, vec4(col * a, a))
return Finish.over(out, ad_rim(d, ad_cut_n(p, c, h, k4), 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 = #ffb648
mod.widgets.RotaryFlat.text_input.draw_text.color_hover = #ffc768
mod.widgets.RotaryFlat.text_input.draw_text.color_focus = #ffc768
mod.widgets.RotaryFlat.text_input.draw_text.color_empty = #a07a3a
mod.widgets.RotaryFlat.text_input.draw_text.text_style = mod.theme.font_regular{font_size: 9.5}
// ---------------------------------------------------------------------------
// TEXT FIELDS. A well of black lacquer sunk behind its gold rim, its corners
// cut; focused, a fine bright gold line lights inside the rim. Read only,
// the well is a dull bronze-black and the rim is dimmed.
let md_field = fn(p: vec2, size: vec2, o: vec2, px: float, hover: float, focus: float, dis: float, k: 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 k4 = vec4(k, k, k, k)
let d = ad_cut(p, c, h, k4)
let n = ad_cut_n(p, c, h, k4)
var fill = mix(vec3(0.016, 0.014, 0.013), vec3(0.032, 0.028, 0.024), hover * (1.0 - dis))
fill = mix(fill, vec3(0.055, 0.045, 0.035), dis)
var out = ad_well(p, c, h, k, d, n, px, fill, mix(0.7, 0.3, dis), 1.3, dis)
out = Finish.over(out, ad_pin(d, 2.3, max(px, 0.6), px, 0.45 * hover * (1.0 - focus) * (1.0 - dis)))
let fl = Finish.band(d, 2.3, 2.3 + max(px, 0.7), px) * focus * (1.0 - dis)
return Finish.over(out, vec4(vec3(1.0, 0.88, 0.60) * 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, 3.0)
}
mod.widgets.TextInputFlat.draw_text.color_empty = #a08f70
mod.widgets.TextInputFlat.draw_text.color_disabled = #8a7c64
mod.widgets.TextInputFlat.draw_cursor.color = #f2d07c
// The well a form's field stands in is the same lacquer well; a caller who
// asks for no box (a clear colour) gets none.
mod.widgets.FieldWell.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, 3.0)
}
// NUMBER FIELDS. The lacquer well, and on its right a block of champagne
// gold split by a fine cut, a small black chevron in each half; the half
// under the pointer lifts, the held half sinks.
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, 3.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 ad_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 + 2.8, px)) - o
let hi = vec2(Finish.snap(o.x + self.rect_size.x - 2.8, px), Finish.snap(o.y + self.rect_size.y - 2.8, 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 c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let k4 = vec4(0.0, 1.5, 1.5, 0.0)
let d = ad_cut(p, c, h, k4)
var face = ad_metal(p, lo, hi, ad_champ(), 0.7, hot, dn)
face = mix(face, vec3(0.30, 0.26, 0.20), self.disabled)
let a = Finish.cover(d, px)
var out = vec4(face * a, a)
// the cut down its left and across its middle
let vl = step(lo.x, p.x) * step(p.x, lo.x + max(px, 0.7)) * step(lo.y, p.y) * step(p.y, hi.y) * 0.9
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(vec3(0.16, 0.10, 0.04) * la, la))
let cx = (lo.x + hi.x) * 0.5 + 0.5
let cu = ad_chev(p, vec2(cx, (lo.y + mid) * 0.5), 2.6, 2.4, -1.0) - 0.55
let cd = ad_chev(p, vec2(cx, (mid + hi.y) * 0.5), 2.6, 2.4, 1.0) - 0.55
let ti = Finish.cover(min(cu, cd), px)
return Finish.over(out, vec4(vec3(0.08, 0.05, 0.02) * ti, ti))
}
// The drag-number: the lacquer well, its track a faint gold 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, 3.0)
let d = md_rbox(p, self.rect_size * 0.5, self.rect_size * 0.5 - vec2(3.0, 3.0), 1.5)
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(ad_gold_md() * wash, wash))
}
mod.widgets.ValueInput.draw_text.color = #ede2c6
mod.widgets.ValueInput.draw_text.text_style = mod.theme.font_regular{font_size: 9.5 line_spacing: 1.0}
mod.widgets.ValueInput.arrow_color = #d8c08c
mod.widgets.ValueInput.text_input.draw_bg.color = #00000000
// ---------------------------------------------------------------------------
// DROP-DOWNS. A black lacquer plaque in its gold rim, and at its right end a
// block of champagne gold carrying a black chevron. Open, the plaque 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))
let live = 1.0 - dis
var out = ad_button(pos, size, o, px, hover, held, focus, dis, 0.0, 0.0, vec4(3.0, 3.0, 3.0, 3.0), 8.0, m)
let lo = vec2(Finish.snap(o.x + size.x - 22.0, px) - o.x, 1.5)
let hi = vec2(Finish.snap(o.x + size.x - 1.5, px) - o.x, size.y - 1.5)
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let k4 = vec4(0.0, 2.0, 2.0, 0.0)
let d = ad_cut(p, c, h, k4)
var face = ad_metal(p, lo, hi, ad_champ(), 0.7, hover * live, held * live)
face = mix(face, vec3(0.30, 0.26, 0.20), dis)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
let sl = step(lo.x, p.x) * step(p.x, lo.x + max(px, 0.7)) * step(lo.y, p.y) * step(p.y, hi.y)
out = Finish.over(out, vec4(vec3(0.10, 0.065, 0.025) * sl, sl))
let cc = vec2(c.x + 0.5, size.y * 0.5 + held * live * px)
let tri = Finish.cover(ad_chev(p, cc, 3.2, 3.0, 1.0) - 0.65, px)
return Finish.over(out, vec4(vec3(0.08, 0.05, 0.02) * 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_regular{font_size: 9.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 lacquer well one types into, and the champagne gold
// block with its chevron 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, 3.0)
let held = self.down * self.hover * live
let lo = vec2(Finish.snap(o.x + size.x - 23.0, px) - o.x, Finish.snap(o.y + 2.8, px) - o.y)
let hi = vec2(Finish.snap(o.x + size.x - 2.8, px) - o.x, Finish.snap(o.y + size.y - 2.8, px) - o.y - px)
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let d = ad_cut(p, c, h, vec4(0.0, 1.6, 1.6, 0.0))
var face = ad_metal(p, lo, hi, ad_champ(), 0.7, self.hover * live, held)
face = mix(face, vec3(0.30, 0.26, 0.20), self.disabled)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
let sl = step(lo.x, p.x) * step(p.x, lo.x + max(px, 0.7)) * step(lo.y, p.y) * step(p.y, hi.y)
out = Finish.over(out, vec4(vec3(0.10, 0.065, 0.025) * sl, sl))
let cc = vec2(c.x + 0.5, c.y + held * px)
let tri = Finish.cover(ad_chev(p, cc, 3.2, 3.0, 1.0) - 0.65, px)
return Finish.over(out, vec4(vec3(0.08, 0.05, 0.02) * tri, tri))
}
// ---------------------------------------------------------------------------
// SEGMENTS AND TABS. One black lacquer plaque across the strip in its gold
// rim; the chosen segment a champagne gold plaque pressed into it, its word
// dark.
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 k4 = vec4(3.0, 3.0, 3.0, 3.0)
let d = ad_cut(p, c, h, k4)
var out = ad_drop(p, c, h, 2.0, 0.8)
let face = ad_lacq(p, lo, hi, ad_black(), 1.0)
let fa = Finish.cover(d + 0.75, px)
out = Finish.over(out, vec4(face * fa, fa))
out = Finish.over(out, ad_pin(d, 2.9, max(px, 0.6), px, 0.6))
return Finish.over(out, ad_rim(d, ad_cut_n(p, c, h, k4), 1.5, px, 1.0))
}
// The chosen cell of a strip: a champagne plaque inside the cell, pressed
// in, in a rim 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 k4 = vec4(2.0, 2.0, 2.0, 2.0)
let d = ad_cut(p, c, h, k4)
var face = ad_metal(p, lo, hi, base, 0.7, 0.0, 0.6)
let sh = clamp(Finish.inset(p, c, h - vec2(1.2, 1.2), 1.0, vec2(0.0, 1.0), 0.9), 0.0, 1.0)
face = mix(face, face * 0.45, sh * 0.6)
let a = Finish.cover(d + 0.6, px)
var out = vec4(face * a, a)
return Finish.over(out, ad_rim(d, ad_cut_n(p, c, h, k4), 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 plaque stands just inside the strip's rim.
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, ad_champ())
}
mod.widgets.SegmentedControlFlat.draw_text.color = #ede2c6
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #1c1206
mod.widgets.SegmentedControlFlat.palette.on_neutral_container = #1c1206
mod.widgets.SegmentedControlFlat.draw_text.text_style = mod.theme.font_label{font_size: 7.0}
mod.widgets.SegmentedControlFlat.draw_text_selected.text_style = mod.theme.font_label{font_size: 7.0}
// The tab strip: the same plaque; the tab in use a champagne plaque pressed
// into it, a hovered tab takes a faint gold wash, the quiet ones parted by a
// fine gold line with a small gold lozenge 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.5, 3.0)
let d = md_rbox(p, self.rect_size * 0.5, h, 1.5)
let a = Finish.cover(d, px) * 0.14
return vec4(ad_gold_lt() * 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, ad_champ())
}
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.30) * 0.75
let lq = abs(p - vec2(x, y))
let lz = Finish.cover(lq.x + lq.y * 0.6 - 1.6, px)
let a = max(ln, lz)
return vec4(mix(ad_line_c(), ad_gold_lt(), lz * 0.5) * a, a)
}
mod.widgets.Tabs.draw_mark.color = #ede2c6
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 = #ede2c6
mod.widgets.Tabs.draw_text_active.color = #1c1206
mod.widgets.Tabs.draw_text.text_style = mod.theme.font_regular{font_size: 9.0 top_drop: 0.22}
mod.widgets.Tabs.draw_text_active.text_style = mod.theme.font_regular{font_size: 9.0 top_drop: 0.22}
// ---------------------------------------------------------------------------
// PROGRESS. A gold tube with a black slot; the value a string of small LED
// beads down the slot, each burning warm white with a tight glow, a faint
// gold light under the lit run; the beads past the value are dark glass.
// The unknown value is a still scatter of lit beads along the whole slot: a
// face may not run a clock.
mod.widgets.ProgressBarFlat.height = 11.0
mod.widgets.ProgressBarFlat.draw_bg.thickness = uniform(11.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 = ad_well_r(p, c, h, r, px, vec3(0.012, 0.011, 0.010), 0.5, 1.4, 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
}
var hue = vec3(1.0, 0.94, 0.80)
if self.intent > 0.5 {
hue = mix(self.fill_color().rgb, vec3(1.0, 1.0, 1.0), 0.35)
}
hue = mix(hue, vec3(0.40, 0.36, 0.30), dis)
let inner = Finish.cover(d + 1.6, px)
let pitch = 4.0
let bx0 = x0 + r
let k = floor((p.x - bx0) / pitch + 0.5)
let bc = vec2(bx0 + k * pitch, c.y)
let inb = step(bx0 - 0.5, bc.x) * step(bc.x, x1 - r + 0.5)
var lit = step(bc.x, v1 - 1.0)
if unknown > 0.5 {
lit = step(0.62, Finish.hash(vec2(k * 1.37, 4.1)))
}
lit = lit * inb * (1.0 - dis * 0.6)
let bd = length(p - bc) - 1.05
// the faint gold light under the lit run
let under = step(x0 + 1.0, p.x) * step(p.x, v1) * inner * 0.10 * (1.0 - unknown)
out = Finish.over(out, vec4(vec3(1.0, 0.70, 0.30) * under, under))
let glow = exp(0.0 - max(bd, 0.0) / 0.9) * lit * inner * 0.55
out = Finish.over(out, vec4(mix(hue, vec3(1.0, 0.75, 0.40), 0.4) * glow, glow))
let core = Finish.cover(bd, px) * inb * inner
let bcol = mix(vec3(0.10, 0.085, 0.07), hue, lit)
out = Finish.over(out, vec4(bcol * core, core))
return out * self.track * self.opacity
}
// ---------------------------------------------------------------------------
// LEVEL METERS. A black well of cells four points long with a point
// between: unlit cells dark glass, lit ones warm gold, amber from 70 % and
// a red ember from 90 %; the hold a cream line; the over lamp a small LED.
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 = 1.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.93, 0.76, 0.38), vec3(1.0, 0.80, 0.42), smoothstep(0.0, 0.7, share)), vec3(1.0, 0.58, 0.20), step(0.7, share)), vec3(0.95, 0.27, 0.15), step(0.9, share))
let well = vec3(0.012, 0.011, 0.010)
var fill = mix(well, ramp, lit)
let cell_row = in_cell * step(pad, a) * step(a, pad + span)
fill = mix(fill, vec3(0.075, 0.060, 0.045), (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.35)
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, ad_cream(), 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(ad_gold_md() * 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(1.0, 0.32, 0.18), step(0.5, self.over), 0.35))
}
return out * self.opacity
}
// ---------------------------------------------------------------------------
// SCROLL BARS. A gold hairline down the length of the view and a slim
// capsule of polished gold riding on it.
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(ad_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.0)
let tc = vec2(t0 + tl * 0.5, mid)
let td = md_rbox(q, tc, th, th.y)
let tn = ad_nrm(q, tc, th, th.y)
let tn2 = mix(tn, vec2(tn.y, tn.x), vert)
let t = clamp((b - (mid - th.y)) / (2.0 * th.y), 0.0, 1.0)
var col = ad_gold_shape(td, tn2, 1.6, t, 0.12 * self.hover, 0.0)
col = mix(col, col * 0.85, self.drag)
col = mix(col, ad_gold_dk() * 0.5, Finish.band(td, 0.0, max(px, 0.45), px) * 0.6)
let ta = Finish.cover(td, px)
out = Finish.over(out, vec4(col * ta, ta))
return out * self.opacity
}
// ---------------------------------------------------------------------------
// VIEWS AND PANELS. A raised panel is black lacquer stepped at its corners,
// in a polished gold rim with a pinstripe inside it, a small sunburst fan in
// its top right and bottom right corners and gold studs in the other two;
// the sunken one a deeper black well inside a double gold frame, a stepped
// wedge of mother-of-pearl set into the frame at the middle of its top and
// of its foot. The words on both keep the cream ink.
let ad_wedge = fn(dst: vec4, p: vec2, cx: float, y0: float, dir: float, px: float) -> vec4 {
// a stepped wedge on the edge at `y0`, growing into the surface (`dir`
// 1 down, -1 up): a broad low step and a narrow tall one, pearl inside
// a gold line
let yy = (p.y - y0) * dir
let qa = vec2(abs(p.x - cx), yy)
let s1 = md_rbox(qa, vec2(0.0, 2.2), vec2(13.0, 2.2), 0.0)
let s2 = md_rbox(qa, vec2(0.0, 3.6), vec2(7.0, 3.6), 0.0)
let d = min(s1, s2)
let a = Finish.cover(d, px)
var out = Finish.over(dst, vec4(ad_pearl(p * 1.3) * a, a))
let e = Finish.band(d, 0.0, max(px, 0.7), px)
return Finish.over(out, vec4(ad_gold_md() * e, e))
}
let md_plate = fn(p: vec2, size: vec2, o: vec2, px: 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 s = 4.0
let d = ad_step(p, c, h, s)
let n = ad_step_n(p, c, h, s)
var out = vec4(0.0, 0.0, 0.0, 0.0)
let k = clamp(30.0 / max(size.y, 1.0), 0.15, 1.0)
if sunken > 0.5 {
var face = ad_lacq(p, lo, hi, vec3(0.016, 0.014, 0.013), k * 0.5)
let sh = clamp(Finish.inset(p, c, h - vec2(1.6, 1.6), 2.0, vec2(0.0, 2.2), 2.4), 0.0, 1.0)
face = mix(face, vec3(0.0, 0.0, 0.0), sh * 0.6)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, ad_pin(d, 2.7, max(px, 0.6), px, 0.75))
out = Finish.over(out, ad_rim(d + 4.0, n, 1.2, px, 0.8))
out = Finish.over(out, ad_rim(d, n, 1.6, px, 1.0))
out = ad_wedge(out, p, c.x, lo.y + 0.5, 1.0, px)
out = ad_wedge(out, p, c.x, hi.y - 0.5, -1.0, px)
return out
}
out = ad_drop(p, c, h, 2.0, 1.0)
var face = ad_lacq(p, lo, hi, ad_black(), k)
face = face + g3(Finish.grain(p / px, 0.004))
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
let fs = clamp(min(size.x, size.y) * 0.32, 14.0, 26.0)
out = ad_corner_fan(out, p, lo, hi, 1.0, 5.0, fs, px, 1.0)
out = ad_corner_fan(out, p, lo, hi, 2.0, 5.0, fs, px, 1.0)
out = ad_dome(out, p, lo + vec2(8.0, 8.0), 2.1, px, ad_gold_base())
out = ad_dome(out, p, vec2(lo.x + 8.0, hi.y - 8.0), 2.1, px, ad_gold_base())
out = Finish.over(out, ad_pin(d, 3.4, max(px, 0.6), px, 0.75))
return Finish.over(out, ad_rim(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, self.sunken)
}
// The card is a leaf of macassar ebony laid crosswise, warmer than the
// ground, in a gold rim and pinstripe, a polished stud in each of its four
// corners. It lifts a little under the pointer.
mod.widgets.Card.radius = 2.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 k4 = vec4(4.0, 4.0, 4.0, 4.0)
let d = ad_cut(p, c, h, k4)
var out = ad_drop(p, c, h, 2.0, 1.0 + 0.4 * self.hover)
var face = ad_ebony(vec2(p.y * 1.0 + 300.0, p.x * 0.6 + 40.0), px)
face = mix(face, ad_ebony_c(), 0.35) * vec3(1.25, 1.08, 0.95) + vec3(0.018, 0.010, 0.004)
face = Finish.lift(face, 0.03 * self.hover)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = ad_dome(out, p, lo + vec2(7.0, 7.0), 2.0, px, ad_gold_base())
out = ad_dome(out, p, vec2(hi.x - 7.0, lo.y + 7.0), 2.0, px, ad_gold_base())
out = ad_dome(out, p, hi - vec2(7.0, 7.0), 2.0, px, ad_gold_base())
out = ad_dome(out, p, vec2(lo.x + 7.0, hi.y - 7.0), 2.0, px, ad_gold_base())
out = Finish.over(out, ad_pin(d, 3.2, max(px, 0.6), px, 0.75))
return Finish.over(out, ad_rim(d, ad_cut_n(p, c, h, k4), 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 ebony, parted by a faint gold hairline; a
// hovered row takes a breath of gold; the chosen row is a plaque of dark old
// gold in a polished rim (dark enough that the cream ink keeps its 5:1),
// ending short of the scroll bar.
let ad_row_plate = fn(p: vec2, c: vec2, h: vec2, px: float) -> vec4 {
let k4 = vec4(2.5, 2.5, 2.5, 2.5)
let d = ad_cut(p, c, h, k4)
let t = clamp((p.y - (c.y - h.y)) / max(2.0 * h.y, 1.0), 0.0, 1.0)
var face = ad_bronze() * (1.25 - 0.45 * t)
face = face + vec3(0.10, 0.07, 0.03) * exp(0.0 - (t - 0.18) * (t - 0.18) / 0.012) * 0.6
let br = Finish.brushed(p, vec2(1.0, 0.0), 0.4) - 0.5
face = face * (1.0 + br * 0.08)
let a = Finish.cover(d, px)
var out = vec4(face * a, a)
return Finish.over(out, ad_rim(d, ad_cut_n(p, c, h, k4), 1.2, px, 1.0))
}
mod.widgets.ListItemOne.height = 22.0
mod.widgets.ListItem.padding = mod.turtle.Inset{left: 12.0 right: 8.0 top: 0.0 bottom: 0.0}
mod.widgets.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.12 + self.down * 0.08) * 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), 1.5)
let la = Finish.cover(hd, px) * lay
var out = vec4(ad_gold_md() * 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.0)
let c = vec2(lo.x + h.x + 2.0, (lo.y + hi.y) * 0.5)
out = Finish.over(out, ad_row_plate(p, c, h, px))
}
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.25 * (1.0 - self.selected)
out = Finish.over(out, vec4(ad_line_c() * dk, dk))
return out
}
mod.widgets.ListItem.draw_text.color = #ede2c6
// The open list of a drop-down: a black lacquer leaf stepped at its corners
// in a gold rim and pinstripe, with a soft shadow; the row under the
// pointer a gold wash, the chosen row a gold 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 s = 3.0
let d = ad_step(p, c, h, s)
let sh = Finish.drop(p, c, h, 2.0, vec2(0.5, 2.5), 2.2) * 0.55
var out = vec4(0.0, 0.0, 0.0, sh)
let face = ad_lacq(p, lo, hi, ad_black(), clamp(30.0 / max(size.y, 1.0), 0.2, 1.0))
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, ad_pin(d, 3.0, max(px, 0.6), px, 0.7))
return Finish.over(out, ad_rim(d, ad_step_n(p, c, h, s), 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, 1.5)
let live = 1.0 - self.disabled
let wa = Finish.cover(d, px) * self.hover * live * 0.22
var out = vec4(ad_gold_md() * 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(ad_gold_lt() * mk, mk))
}
// The dock's tabs: the bar is the ground with a gold line at its foot; the
// tab in use a black lacquer leaf rounded at its top corners in its rim, 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 k4 = vec4(3.0, 3.0, 0.0, 0.0)
let d = ad_cut(p, c, h, k4)
let face = ad_lacq(p, lo, hi, ad_black(), 1.0)
let a = Finish.cover(d, px) * self.active
var out = vec4(face * a, a)
out = Finish.over(out, ad_rim(d, ad_cut_n(p, c, h, k4), 1.3, px, 1.0) * self.active)
let lift = Finish.cover(d, px) * 0.12 * self.hover * (1.0 - self.active)
return Finish.over(vec4(ad_gold_md() * 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(ad_gold_md() * dk, dk)
}
// ---------------------------------------------------------------------------
// CHIPS AND BADGES. A chip is a small lacquer plaque in its rim (the
// outline when it asks for a border); a badge a small lacquer plaque in a
// gold rim with gold 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(5.0, 8.0, step(0.5, has_fill + 1.0 - step(0.01, self.border_size)))
let lift = mix(0.0, 0.6, has_fill)
return ad_button(pp, sz, self.rect_pos + vec2(0.0, dy), px, max(self.hover, lift), self.down, self.keyed, 0.0, 0.0, 0.0, vec4(2.5, 2.5, 2.5, 2.5), kind, m)
}
mod.widgets.ChipFlat.draw_text.text_style = mod.theme.font_label{font_size: 6.5}
mod.widgets.BadgePalette.neutral = #1a1510
mod.widgets.BadgePalette.on_neutral = #e8d4a2
mod.widgets.BadgeFlat.draw_text.text_style = mod.theme.font_label{font_size: 6.5}
mod.widgets.Badge.draw_text.text_style = mod.theme.font_label{font_size: 6.5}
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 k4 = vec4(2.0, 2.0, 2.0, 2.0)
let d = ad_cut(p, c, h, k4)
let face = ad_lacq(p, lo, hi, self.color.rgb, 1.0)
let a = Finish.cover(d, px)
var out = vec4(face * a, a)
out = Finish.over(out, ad_rim(d, ad_cut_n(p, c, h, k4), 1.2, px, 1.0))
return out * self.color.a
}
// Bold in running text is the reading serif's own bold: the wide legend
// face is for captions and plaques, and set inside a sentence it would
// shout.
mod.widgets.Markdown.text_style_bold = mod.theme.font_regular{font_size: 9.5 font_family: mod.theme.font_regular.font_family{ibm_plex_text := FontMember{res: crate_resource("self:resources/PlayfairDisplay-Variable.ttf") weight: 700.0 asc: 0.0 desc: 0.0}}}
mod.widgets.Html.text_style_bold = mod.theme.font_regular{font_size: 9.5 font_family: mod.theme.font_regular.font_family{ibm_plex_text := FontMember{res: crate_resource("self:resources/PlayfairDisplay-Variable.ttf") weight: 700.0 asc: 0.0 desc: 0.0}}}
mod.widgets.TextFlow.text_style_bold = mod.theme.font_regular{font_size: 9.5 font_family: mod.theme.font_regular.font_family{ibm_plex_text := FontMember{res: crate_resource("self:resources/PlayfairDisplay-Variable.ttf") weight: 700.0 asc: 0.0 desc: 0.0}}}
// The spinner keeps its stock turning face (it runs on the pass clock, which
// a face may not read), in gold on a faint track.
mod.widgets.SpinnerFlat.draw_bg.color = #cdb07a
mod.widgets.SpinnerFlat.draw_bg.stroke_width = 2.5
mod.widgets.SpinnerFlat.draw_bg.track_alpha = 0.25
// Rules between groups: a gold hairline with a small stepped 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.5 + lq.y - 2.0, px)
let a = max(ln, lz)
return vec4(ad_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(ad_line_c() * a, a)
}
// ---------------------------------------------------------------------------
// THE TREE. Its rows stand on the ebony, clear; the open row is the dark old
// gold plaque of the list's chosen row.
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.0)
let hi = self.rect_size - vec2(4.0, 1.0)
let h = (hi - lo) * 0.5
let c = lo + h
return ad_row_plate(p, c, h, px) * 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. Black lacquer, with two holes cut in it: the header
// where the catalogue's toolbar stands and the panes below it, both stepped
// at their corners, both showing the ebony. A polished gold rim runs round
// each hole on the lacquer's side and a pinstripe beside it, and a rim runs
// round the window's own edge, so every band of lacquer between them is
// drawn with the double gold line. The caption band above the header holds
// the crest round the title and the sunburst; nothing else of the frame is
// ornamented, and the ebony is calm under the words.
let ad_holes = fn(p: vec2, W: float, H: float) -> float {
let dh = ad_step(p, vec2(W * 0.5, 60.0), vec2(W * 0.5 - 6.0, 27.0), 4.0)
let dp = ad_step(p, vec2(W * 0.5, (95.0 + H - 6.0) * 0.5), vec2(W * 0.5 - 6.0, (H - 6.0 - 95.0) * 0.5), 5.0)
return min(dh, dp)
}
// The crest round the title, in the window's middle: `q` is (distance from
// the middle, y), so it is drawn the same on both sides. A plaque of black
// lacquer stepped at its corners in a gold rim and pinstripe, and three
// steps falling away from it either side, the nearest inlaid with
// mother-of-pearl, the two beyond with upright gold pinstripes.
let ad_crest = fn(dst: vec4, q: vec2, px: float) -> vec4 {
var out = dst
if q.x > 140.0 {
return out
}
// the steps either side
let s0 = md_rbox(q, vec2(93.0, 15.5), vec2(8.0, 10.5), 0.0)
let s1 = md_rbox(q, vec2(110.0, 18.0), vec2(8.0, 8.0), 0.0)
let s2 = md_rbox(q, vec2(127.0, 20.5), vec2(8.0, 5.5), 0.0)
let ds = min(s0, min(s1, s2))
let sa = Finish.cover(ds, px)
let sl = ad_lacq(q, vec2(0.0, 5.0), vec2(0.0, 26.0), ad_black(), 1.0)
out = Finish.over(out, vec4(sl * sa, sa))
// the pearl in the nearest step
let pd = md_rbox(q, vec2(93.0, 15.5), vec2(5.6, 8.1), 0.0)
let pa = Finish.cover(pd, px)
out = Finish.over(out, vec4(ad_pearl(q * 1.2 + vec2(3.0, 0.0)) * pa, pa))
// upright pinstripes in the two beyond
let u1 = (1.0 - smoothstep(0.35, 0.35 + px, abs(abs(q.x - 110.0) - 2.5))) * Finish.cover(md_rbox(q, vec2(110.0, 18.0), vec2(6.0, 6.0), 0.0), px)
let u2 = (1.0 - smoothstep(0.35, 0.35 + px, abs(q.x - 127.0))) * Finish.cover(md_rbox(q, vec2(127.0, 20.5), vec2(6.0, 3.5), 0.0), px)
let ua = max(u1, u2) * 0.85
out = Finish.over(out, vec4(ad_line_c() * ua, ua))
// each step's gold outline
let e0 = Finish.band(s0, 0.0, 1.0, px)
let e1 = Finish.band(s1, 0.0, 1.0, px)
let e2 = Finish.band(s2, 0.0, 1.0, px)
let ea = clamp(e0 + e1 + e2, 0.0, 1.0)
out = Finish.over(out, vec4(ad_gold_md() * ea, ea))
// the plaque
let pc = vec2(0.0, 15.5)
let ph = vec2(83.0, 11.5)
let d = ad_step(q, pc, ph, 3.0)
let n = ad_step_n(q, pc, ph, 3.0)
let a = Finish.cover(d, px)
let face = ad_lacq(q, pc - ph, pc + ph, ad_black(), 1.0)
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, ad_pin(d, 2.8, max(px, 0.6), px, 0.8))
return Finish.over(out, ad_rim(d, n, 1.5, px, 1.0))
}
// The sunburst: gold rays fanning from a point far below the window's
// middle, thick and thin by turns, across the caption band clear of the
// crest; they fade out short of the window's buttons and of its left end.
let ad_burst = fn(dst: vec4, p: vec2, W: float, px: float) -> vec4 {
let cx = W * 0.5
let x = abs(p.x - cx)
if x < 136.0 {
return dst
}
let q = vec2(p.x - cx, 160.0 - p.y)
let r = length(q)
let phi = atan2(q.x, q.y)
let st = 0.03
let k = phi / st
let kf = floor(k + 0.5)
let dperp = abs(k - kf) * st * r
let thick = mix(0.26, 0.6, step(0.25, abs(fract(kf * 0.5))))
let ray = Finish.cover(dperp - thick, px)
let clear = smoothstep(136.0, 150.0, x) * smoothstep(2.0, 5.0, p.y) * (1.0 - smoothstep(26.0, 28.0, p.y))
let fade_r = 1.0 - smoothstep(W - 300.0, W - 165.0, p.x)
let fade_l = smoothstep(6.0, 70.0, p.x)
let ra = ray * clear * fade_r * fade_l * 0.75
return Finish.over(dst, vec4(mix(ad_line_c(), ad_gold_lt(), 0.25) * ra, ra))
}
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
let dhole = ad_holes(p, W, H)
let e = 0.35
let gx = ad_holes(p + vec2(e, 0.0), W, H) - ad_holes(p - vec2(e, 0.0), W, H)
let gy = ad_holes(p + vec2(0.0, e), W, H) - ad_holes(p - vec2(0.0, e), W, H)
var nh = vec2(0.0, 1.0)
if length(vec2(gx, gy)) > 0.0001 {
nh = normalize(vec2(gx, gy))
}
// the lacquer of the frame: a band of the room's light high in the
// caption band, a little lighter toward the top
var col = vec3(0.026, 0.024, 0.022)
let lb = exp(0.0 - (p.y - 9.0) * (p.y - 9.0) / 45.0) * 0.032
col = col + vec3(lb, lb * 0.95, lb * 0.88)
// the ebony in the holes under one broad diagonal lacquer sheen
var wood = ad_ebony(p, px)
let u = (p.x * 0.45 + p.y) / max(H, 1.0)
let sheen = exp(0.0 - (u - 0.30) * (u - 0.30) / 0.02) * 0.016
wood = wood + vec3(sheen, sheen * 0.92, sheen * 0.8)
let inh = Finish.cover(dhole, px)
col = mix(col, wood, inh)
// the frame's shade on the wood along each hole's upper and left edges
let sh = (1.0 - smoothstep(0.0, 5.0, 0.0 - dhole)) * inh * (0.4 + 0.6 * clamp(0.0 - nh.y - nh.x * 0.5, 0.0, 1.0))
col = mix(col, col * 0.45, sh * 0.7)
var out = vec4(col, 1.0)
// the caption band's ornament
if p.y < 31.0 {
out = ad_burst(out, p, W, px)
out = ad_crest(out, vec2(abs(p.x - W * 0.5), p.y), px)
}
// the gold rim along every hole on the lacquer's side, the pinstripe
// beyond it, and the rim round the window's edge
out = Finish.over(out, ad_rim(0.0 - dhole, nh * -1.0, 2.0, px, 1.0))
out = Finish.over(out, ad_pin(0.0 - dhole, 3.0, max(px, 0.7), px, 0.85))
let dw = md_rbox(p, vec2(W, H) * 0.5, vec2(W, H) * 0.5, 0.0)
let nw = ad_nrm(p, vec2(W, H) * 0.5, vec2(W, H) * 0.5, 0.0)
out = Finish.over(out, ad_rim(dw, nw, 1.6, px, 1.0))
return vec4(out.rgb, 1.0)
}
// A splitter is the frame's lacquer band run down between two panes, ten
// points wide, its gold rims stepping into the header's band above and the
// foot band below as the panes' corners step.
let ad_split = fn(q: vec2, mid: float, hw: float, a0: float, a1: float) -> float {
let ph = vec2(200.0, (a1 - a0) * 0.5)
let dl = ad_step(q, vec2(mid - hw - 200.0, (a0 + a1) * 0.5), ph, 5.0)
let dr = ad_step(q, vec2(mid + hw + 200.0, (a0 + a1) * 0.5), ph, 5.0)
return min(min(dl, dr), q.y + 4.0)
}
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 a0 = 4.0
let a1 = along - 6.0
let q = vec2(b, a)
let dh = ad_split(q, mid, hw, a0, a1)
let e = 0.35
let gx = ad_split(q + vec2(e, 0.0), mid, hw, a0, a1) - ad_split(q - vec2(e, 0.0), mid, hw, a0, a1)
let gy = ad_split(q + vec2(0.0, e), mid, hw, a0, a1) - ad_split(q - vec2(0.0, e), mid, hw, a0, a1)
var nh = vec2(0.0, 1.0)
if length(vec2(gx, gy)) > 0.0001 {
nh = normalize(vec2(gx, gy))
}
let inside = step(-8.0, a) * step(a, along - 1.6) * step(abs(b - mid), hw + 12.0)
let band = Finish.cover(0.0 - dh, px) * inside
let act = max(max(self.hover, self.drag), self.focus)
var lac = vec3(0.026, 0.024, 0.022)
lac = Finish.lift(lac, 0.04 * act)
var out = vec4(lac * band, band)
let nn = vec2(mix(nh.y, nh.x, v), mix(nh.x, nh.y, v))
out = Finish.over(out, ad_rim(0.0 - dh, nn * -1.0, 2.0, px, 1.0) * inside)
out = Finish.over(out, ad_pin(0.0 - dh, 3.0, max(px, 0.7), px, 0.85) * inside)
// under the hand, a bright 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(ad_gold_lt() * gl, gl))
return out
}
// ---------------------------------------------------------------- switches and meter
// THE SWITCHES. The slide switch is a black pill in its gold rim that fills
// with gold while on, an ivory dome riding in it. The rocker is a lever
// switch: a black lacquer plate in its gold rim, a polished gold collar at
// its middle with a small lamp set in it, and a gold bat-handle lever
// thrown to the left while off and to the right while on, when the lamp in
// the collar burns amber.
let ad_lever_d = fn(p: vec2, cc: vec2, tip: vec2) -> vec4 {
// the lever's distance (.x) and its outward normal (.yz): a tapered
// shaft from the collar to the tip, and a ball on the tip
let ba = tip - cc
let k = clamp(dot(p - cc, ba) / max(dot(ba, ba), 0.0001), 0.0, 1.0)
let ax = p - (cc + ba * k)
let dl = length(ax) - mix(2.4, 1.6, k)
let bq = p - tip
let db = length(bq) - 3.1
if db < dl {
let nb = bq / max(length(bq), 0.0001)
return vec4(db, nb.x, nb.y, 0.0)
}
let nl = ax / max(length(ax), 0.0001)
return vec4(dl, nl.x, nl.y, 0.0)
}
let ad_rocker = fn(pos: vec2, size: vec2, o: vec2, px: float, active: float, hover: float, down: float, focus: float, dis: float, at_end: 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 hi = lo + vec2(tw, th)
let c = (lo + hi) * 0.5
let h = vec2(tw * 0.5, th * 0.5)
let d = md_rbox(p, c, h, 3.0)
let n = ad_nrm(p, c, h, 3.0)
var out = ad_drop(p, c, h, 3.0, mix(1.0, 0.5, dis))
let face = mix(ad_lacq(p, lo, hi, ad_black(), 1.0), vec3(0.06, 0.05, 0.04), dis)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(face * a, a))
out = Finish.over(out, ad_pin(d, 2.6, max(px, 0.6), px, mix(0.6, 0.3, dis)))
out = Finish.over(out, ad_rim(d, n, 1.3, px, 1.0 - 0.7 * dis) * mix(1.0, 0.5, dis))
let on = clamp(active, 0.0, 1.0)
let cc = vec2(Finish.snap(o.x + c.x, px) - o.x, c.y)
let tip = cc + vec2(mix(-11.0, 11.0, on), 0.3)
let press = down * live
// the lever's shadow on the plate, down and to the right
let sh = ad_lever_d(p - vec2(0.6, 1.5), cc, tip)
let sa = (1.0 - smoothstep(-0.6, 1.8, sh.x)) * 0.6 * a
out = Finish.over(out, vec4(0.0, 0.0, 0.0, sa))
// the lever, polished gold
let lv = ad_lever_d(p, cc, tip)
let t = clamp((p.y - (cc.y - 3.0)) / 6.0, 0.0, 1.0)
var lc = ad_gold_shape(lv.x, vec2(lv.y, lv.z), 1.8, t, hover * live * (1.0 - press), dis)
lc = mix(lc, lc * 0.85, press)
lc = mix(lc, ad_gold_dk() * 0.4, Finish.band(lv.x, 0.0, max(px, 0.5), px) * 0.7)
let lfa = Finish.cover(lv.x, px)
out = Finish.over(out, vec4(lc * lfa, lfa))
// the collar over the lever's root, and the lamp in it: amber and
// burning while on, dark glass while off
out = Finish.over(out, ad_drop(p, cc, vec2(5.2, 5.2), 5.2, 0.6))
out = ad_dome(out, p, cc, 5.2, px, mix(ad_gold_base(), vec3(0.36, 0.31, 0.24), dis))
let jq = p - cc
let jl = length(jq)
let seat = Finish.cover(jl - 2.9, px)
out = Finish.over(out, vec4(vec3(0.05, 0.035, 0.02) * seat, seat))
let lit = on * live
let jt = clamp(jl / 2.3, 0.0, 1.0)
let jon = mix(vec3(1.0, 0.95, 0.80), vec3(1.0, 0.56, 0.16), smoothstep(0.2, 1.0, jt))
let joff = mix(vec3(0.16, 0.09, 0.04), vec3(0.08, 0.045, 0.02), jt)
var jc = mix(joff, jon, lit)
let gq = jq / 2.3 - vec2(-0.3, -0.4)
jc = mix(jc, vec3(1.0, 1.0, 1.0), exp(0.0 - dot(gq, gq) / 0.05) * mix(0.35, 0.5, lit))
let ja = Finish.cover(jl - 2.3, px)
out = Finish.over(out, vec4(jc * ja, ja))
let jg = exp(0.0 - max(jl - 2.3, 0.0) / 1.0) * lit * (1.0 - ja) * Finish.cover(jl - 2.9, px) * 0.8
out = Finish.over(out, vec4(vec3(1.0, 0.62, 0.22) * jg, jg))
return Finish.over(out, ad_focus(d, px, focus * live))
}
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 ad_rocker(self.pos, self.rect_size, self.rect_pos, px, self.active, self.hover, self.down, self.focus, self.disabled, self.mark_at_end)
}
mod.widgets.ToggleSlide.draw_bg.pixel = fn() {
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
return ad_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)
}
// THE GLASS of the readouts, the screen and the meter: black glass, a faint
// warm sheen across its upper left and the surround's shade falling in under
// its top edge, set in a polished gold rim with a pinstripe inside it.
let ad_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 = ad_drop(p, c, h, r, 0.8)
let hf = h - vec2(1.8, 1.8)
var g = face
let uv = (p - lo) / max(hi - lo, vec2(1.0, 1.0))
let diag = uv.x * 0.7 + uv.y
let sheen = smoothstep(0.0, 0.25, diag) * (1.0 - smoothstep(0.30, 0.70, diag)) * 0.045
g = g + vec3(0.95, 0.88, 0.72) * sheen
g = mix(g, vec3(0.0, 0.0, 0.0), clamp(Finish.inset(p, c, hf, max(r - 1.8, 0.0), vec2(0.0, 2.2), 2.0), 0.0, 1.0) * 0.6)
let a = Finish.cover(d, px)
out = Finish.over(out, vec4(g * a, a))
out = Finish.over(out, ad_pin(d, 3.0, max(px, 0.55), px, 0.55))
return Finish.over(out, ad_rim(d, ad_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 ad_glass(p, lo, hi, vec4(3.0, 3.0, 3.0, 3.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, faint gold
// rays fanning up from the pivot behind the scale, the scale a fine gold arc
// with gold ticks, the colour band under it running gold to amber to an
// ember red, the needle a cream spear and its pivot a polished gold boss.
mod.widgets.NeedleMeter.clip_x = false
mod.widgets.NeedleMeter.clip_y = false
mod.widgets.NeedleMeter.draw_bg.border_radius = uniform(3.0)
mod.widgets.NeedleMeter.draw_bg.color = uniform(#0b0907)
mod.widgets.NeedleMeter.draw_bg.color_needle = uniform(#xf3e8cf)
mod.widgets.NeedleMeter.draw_bg.color_zone = uniform(#xe0482a)
mod.widgets.NeedleMeter.draw_bg.color_scale = uniform(#xe2cc96)
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 = ad_glass(p, lo, hi, vec4(rt, rt, r, r), px, vec3(0.043, 0.035, 0.027))
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.5, 2.5), vec4(rt - 2.5, rt - 2.5, r, r)), px)
// the rays behind the scale
let st = sweep / 16.0
let kr = ang / st
let kd = abs(kr - floor(kr + 0.5)) * st * rq
let rays = Finish.cover(kd - max(px * 0.5, 0.3), px) * smoothstep(6.0, 10.0, rq) * (1.0 - smoothstep(rr - 7.0, rr - 5.0, rq)) * on_scale * inside * 0.22
out = Finish.over(out, vec4(ad_line_c() * rays, rays))
// 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.86, 0.70, 0.38), vec3(1.0, 0.62, 0.24), 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 = ad_taper(p, pv - nd * 3.0, pv + nd * (rr + 4.0), 1.3, 0.35)
let nf = mix(self.color_needle.rgb, ad_gold_lt(), smoothstep(0.3, 1.0, sp.y) * 0.25)
let nk = Finish.cover(sp.x, px)
out = Finish.over(out, vec4(nf * nk, nk))
out = ad_dome(out, p, pv, self.pivot_size + 0.8, px, ad_gold_base())
return out * self.color.a * self.opacity
}
mod.widgets.NeedleMeter.draw_label.color = #xe2cc96
// ---------------------------------------------------------------- lamps
// LAMPS: LEDs in polished gold bezels. 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,
// with a catch light along its lower rim. Its light is added, never
// painted: a tight hot halo and a short bloom in its own hue, gone in about
// two radii. 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)
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 bezel's shadow on the ground
let shd = Finish.drop(p, c, hb + vec2(bw, bw), rt + bw, vec2(0.0, 0.12 * rt + 0.3), 0.10 * rt + 0.5) * 0.5 * (1.0 - 0.6 * pw)
res = Finish.over(res, vec4(0.0, 0.0, 0.0, shd))
// the polished gold bezel: a ring rounded over, lit from the upper left,
// a dark seat line where it holds the lens
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 = ad_gold_base() * (0.85 + 0.45 * facing)
rc = mix(rc, ad_gold_hi(), smoothstep(0.5, 0.95, facing) * 0.8)
rc = mix(rc, ad_gold_dk() * 0.6, Finish.band(ds, 0.0, max(px, 0.45), px) * 0.6)
rc = mix(rc, vec3(0.02, 0.015, 0.01), Finish.ring_out(d, 0.0, max(px, 0.45), px) * 0.85)
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 LENS: 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, a catch light along its lower rim
let offc = mix(vec3(0.06, 0.055, 0.05), b * 0.42, 0.26)
var off = mix(offc * 0.62, offc * 1.15, top)
off = mix(off, min(offc + vec3(0.16, 0.16, 0.16), 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.26, 0.20) * 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 LED takes the middle of its
// cell; its halo's room is held in the cell, 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 bezel -- 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(#x3ad98a)
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, ad_ebony_c(), glow)
return res * self.opacity
}
true