2437 lines
124 KiB
Text
2437 lines
124 KiB
Text
use mod.prelude.widgets_internal.*
|
|
|
|
// Kraft corrugated board, hand made, lit from straight above. A control is
|
|
// a PIECE of board cut out and laid back in the window it was cut from
|
|
// (button, segment, drop-down: a dark gap round it with the flutes of the
|
|
// cut wall showing, a pale line where each cut liner catches the light), a
|
|
// piece GLUED ON (knob, thumb, panel, card: its thin shadow and its cut edge
|
|
// along its foot, the fluted layer between the two liners seen side on), or
|
|
// a line of black felt MARKER drawn on the board (box, ring, track, scale,
|
|
// tick), its width wandering as a hand does and its ink bleeding a little
|
|
// into the fibres. What is chosen is black card or a marker fill; coloured
|
|
// paper (blue, red, yellow) and cream paper are glued on where a role needs
|
|
// a colour. Focus is a blue ballpoint line drawn round the control.
|
|
|
|
// A ROW OF FIELDS IS ONE HEIGHT. Left to themselves the library's fields each
|
|
// come out at a size of their own, so the box metrics are stated once here
|
|
// and handed to each field below. A field is a piece of card as a button
|
|
// is, its face the box and its cut edge under it, so the box is 32, the
|
|
// height of a button.
|
|
let field_height = 32.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.6
|
|
|
|
// The room a Fit field takes: where a field is Fit, the padding IS its
|
|
// height.
|
|
let field_room = fn(w) {
|
|
w.min_height = field_height
|
|
w.padding.top = field_pad_v
|
|
w.padding.bottom = field_pad_v
|
|
}
|
|
// The box a field draws. Given the draw object and not the widget, because
|
|
// not every field draws its own box.
|
|
let field_face = fn(d) {
|
|
d.border_radius = field_radius
|
|
d.border_size = field_border
|
|
}
|
|
// A field that states a frame and lays its own parts out inside it: the
|
|
// height is all these take; a padding would displace its line.
|
|
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)
|
|
// The chrome-less input a well holds must impose no height of its own.
|
|
mod.widgets.WellInput.min_height = 0.0
|
|
mod.widgets.Window.pass.clear_color = #b08659
|
|
|
|
// Plain views stop clipping, so a shadow, a cut edge or a marker's bleed 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. Every texture is laid in the piece's own space (a
|
|
// point from its corner, seeded by its size), so it rides with the piece and
|
|
// nothing changes when a page scrolls.
|
|
|
|
let g3 = fn(v: float) -> vec3 {
|
|
return vec3(v, v, v)
|
|
}
|
|
|
|
// A rounded box, true radius.
|
|
let cb_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 cb_rbox4 = 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))
|
|
let r = clamp(mix(rb, rt, step(q0.y, 0.0)), 0.0, min(h.x, h.y))
|
|
let q = abs(q0) - h + vec2(r, r)
|
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0, 0.0))) - r
|
|
}
|
|
|
|
// The corners a button group asks for: a corner written as 0 is a square
|
|
// join, any other the family's radius.
|
|
let cb_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 cb_seg = fn(p: vec2, a: vec2, b: vec2) -> float {
|
|
let pa = p - a
|
|
let ba = b - a
|
|
let k = clamp(dot(pa, ba) / max(dot(ba, ba), 0.0001), 0.0, 1.0)
|
|
return length(pa - ba * k)
|
|
}
|
|
|
|
let cb_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)
|
|
}
|
|
|
|
let cb_tri = fn(p: vec2, c: vec2, hw: float, hh: float, dir: float) -> float {
|
|
// a triangle hw wide either side, 2 hh tall, apex up (dir -1) or down (1)
|
|
let y = (p.y - c.y) * dir
|
|
let e = (abs(p.x - c.x) * 2.0 * hh + hw * (y - hh)) / sqrt(4.0 * hh * hh + hw * hw)
|
|
return max(-hh - y, e)
|
|
}
|
|
|
|
// THE PALETTE. The board's kraft #b08659; the marker #x18140e; shade is the
|
|
// board's own brown gone dark, never grey.
|
|
let cb_kraft_c = fn() -> vec3 {
|
|
return vec3(0.690, 0.525, 0.349)
|
|
}
|
|
let cb_ink_c = fn() -> vec3 {
|
|
return vec3(0.094, 0.078, 0.055)
|
|
}
|
|
let cb_dry_c = fn() -> vec3 {
|
|
return vec3(0.431, 0.353, 0.267)
|
|
}
|
|
let cb_shade_c = fn() -> vec3 {
|
|
return vec3(0.170, 0.105, 0.050)
|
|
}
|
|
let cb_liner_c = fn() -> vec3 {
|
|
return vec3(0.925, 0.835, 0.680)
|
|
}
|
|
let cb_cream_c = fn() -> vec3 {
|
|
return vec3(0.925, 0.878, 0.780)
|
|
}
|
|
let cb_black_c = fn() -> vec3 {
|
|
return vec3(0.165, 0.153, 0.141)
|
|
}
|
|
let cb_yellow_c = fn() -> vec3 {
|
|
return vec3(0.886, 0.722, 0.290)
|
|
}
|
|
let cb_blue_c = fn() -> vec3 {
|
|
return vec3(0.478, 0.608, 0.733)
|
|
}
|
|
let cb_red_c = fn() -> vec3 {
|
|
return vec3(0.808, 0.451, 0.380)
|
|
}
|
|
let cb_pen_c = fn() -> vec3 {
|
|
return vec3(0.184, 0.373, 0.620)
|
|
}
|
|
let cb_redink_c = fn() -> vec3 {
|
|
return vec3(0.690, 0.165, 0.125)
|
|
}
|
|
|
|
// THE FIBRES. One lattice of short straight fibres, at most one to a cell,
|
|
// each at its own angle and a length between `l0` and `l1` cells; a point
|
|
// looks at the four cells round it so a fibre is never cut at its cell's
|
|
// edge. `.x` the dark fibres, `.y` the pale ones (six in ten), as coverage.
|
|
let cb_fibres = fn(w: vec2, px: float, cell: float, seed: vec2, dens: float, thick: float, l0: float, l1: float) -> vec2 {
|
|
let q = w / cell
|
|
let g = floor(q)
|
|
let f = q - g
|
|
let o = vec2(step(0.5, f.x), step(0.5, f.y)) - vec2(1.0, 1.0)
|
|
var dk = 0.0
|
|
var lt = 0.0
|
|
var i = 0.0
|
|
for j in 0..4 {
|
|
let k = g + o + vec2(i - 2.0 * floor(i * 0.5), floor(i * 0.5))
|
|
let h0 = Finish.hash(k + seed)
|
|
let h1 = Finish.hash(k + seed + vec2(17.31, 41.73))
|
|
let h2 = Finish.hash(k + seed + vec2(-9.17, 23.41))
|
|
let h3 = Finish.hash(k + seed + vec2(5.53, -3.37))
|
|
let ctr = (k + vec2(h1, h2)) * cell
|
|
let an = h3 * 3.14159265
|
|
let hl = cell * (l0 + (l1 - l0) * fract(h0 * 7.13))
|
|
let dir = vec2(cos(an), sin(an)) * hl
|
|
let dd = cb_seg(w, ctr - dir, ctr + dir) - thick
|
|
let cov = Finish.cover(dd, px) * step(h0, dens)
|
|
let pale = step(0.4, fract(h1 * 13.7 + h2 * 3.1))
|
|
dk = max(dk, cov * (1.0 - pale))
|
|
lt = max(lt, cov * pale)
|
|
i = i + 1.0
|
|
}
|
|
return vec2(dk, lt)
|
|
}
|
|
|
|
// KRAFT at point `w` over `base`: a long drift of tone, a faint cloud
|
|
// (small, so it never reads as blotches), a mottle at two fine scales, a
|
|
// free grain per device pixel, and the felt of its fibres: short ones packed
|
|
// close (a point and a half to a cell) and longer ones among them, the pale
|
|
// ones more than the dark, both low in contrast, so the board reads as a
|
|
// fine crinkled speckle rather than as hairs on a smooth ground. `amt`
|
|
// scales the whole texture.
|
|
let cb_kraft = fn(w: vec2, px: float, base: vec3, amt: float) -> vec3 {
|
|
let broad = Finish.noise(w * 0.006 + vec2(3.1, 1.7)) - 0.5
|
|
let cloud = Finish.fbm(w * 0.07 + vec2(13.1, 7.7)) - 0.5
|
|
let m1 = Finish.noise(w * 0.8 + vec2(5.3, 2.9)) - 0.5
|
|
let m2 = Finish.noise(w * 0.3 + vec2(1.3, 8.9)) - 0.5
|
|
let tone = (broad * 0.06 + cloud * 0.04 + m1 * 0.06 + m2 * 0.07) * amt + Finish.grain(w / px, 0.016 * amt)
|
|
var col = base * (1.0 + tone)
|
|
let fa = cb_fibres(w, px, 1.8, vec2(0.0, 0.0), 0.7, 0.22, 0.3, 1.0)
|
|
let fb = cb_fibres(w, px, 4.0, vec2(71.0, 23.0), 0.6, 0.28, 0.3, 1.0)
|
|
let dk = max(fa.x, fb.x * 0.8)
|
|
let lt = max(fa.y, fb.y * 0.8)
|
|
col = col * (1.0 - 0.11 * dk * amt)
|
|
col = col + (vec3(1.0, 1.0, 1.0) - col) * (0.17 * lt * amt)
|
|
return col
|
|
}
|
|
|
|
// PAPER, cream or coloured: finer and calmer than the board.
|
|
let cb_paper = fn(w: vec2, px: float, base: vec3, amt: float) -> vec3 {
|
|
let cloud = Finish.fbm(w * 0.09 + vec2(2.3, 9.1)) - 0.5
|
|
let fine = Finish.noise(w * 0.9 + vec2(1.1, 4.4)) - 0.5
|
|
var col = base * (1.0 + (cloud * 0.05 + fine * 0.04) * amt + Finish.grain(w / px, 0.016 * amt))
|
|
let fa = cb_fibres(w, px, 3.0, vec2(13.0, 57.0), 0.5, 0.16, 0.3, 1.0)
|
|
col = mix(col, base * 0.88, fa.x * 0.40 * amt)
|
|
col = mix(col, Finish.lift(base, 0.35), fa.y * 0.45 * amt)
|
|
return col
|
|
}
|
|
|
|
// BLACK CARD: near black, a little cloud, and the pale scuffs of its fibres.
|
|
let cb_blackcard = fn(w: vec2, px: float, amt: float) -> vec3 {
|
|
let base = cb_black_c()
|
|
let cloud = Finish.fbm(w * 0.08 + vec2(7.0, 3.0)) - 0.5
|
|
var col = base * (1.0 + cloud * 0.30 * amt) + g3(Finish.grain(w / px, 0.010 * amt))
|
|
let fa = cb_fibres(w, px, 2.6, vec2(37.0, 11.0), 0.6, 0.18, 0.3, 1.0)
|
|
col = mix(col, vec3(0.40, 0.37, 0.34), fa.y * 0.40 * amt)
|
|
col = mix(col, vec3(0.07, 0.065, 0.06), fa.x * 0.40 * amt)
|
|
return col
|
|
}
|
|
|
|
// THE CUT EDGE of the board seen side on: `u` along the edge, `v` 0..1 from
|
|
// the face down to the foot of `th` points of board. The two liners as
|
|
// bands, the fluted layer between them a wave over dark hollows; the edge
|
|
// faces down, away from the light, so all of it is in shade.
|
|
let cb_flute = fn(u: float, v: float, th: float, px: float, base: vec3) -> vec3 {
|
|
let liner = 0.2
|
|
let pitch = max(th * 1.2, 2.4)
|
|
let amp = 0.24
|
|
let ph = u / pitch * 6.2831853
|
|
let wy = 0.5 + amp * sin(ph)
|
|
let sl = amp * th * 6.2831853 / pitch * cos(ph)
|
|
let dist = abs(v - wy) * th / sqrt(1.0 + sl * sl)
|
|
let medium = Finish.cover(dist - max(th * 0.08, px * 0.45), px)
|
|
var col = mix(base * 0.24, base * 0.68, medium)
|
|
let top = 1.0 - smoothstep(liner - 0.06, liner + 0.06, v)
|
|
let bot = smoothstep(1.0 - liner - 0.06, 1.0 - liner + 0.06, v)
|
|
col = mix(col, base * 0.80, top)
|
|
col = mix(col, base * 0.48, bot)
|
|
return col
|
|
}
|
|
|
|
// What a piece glued on the board shows beyond its face: the shadow it
|
|
// throws (a tight contact shade and a soft one, cast a little down, both
|
|
// gone within half a gap) and its cut edge along its foot, `th` points of
|
|
// board seen side on. `lift` 0..1 raises it (a softer, longer shadow).
|
|
// Premultiplied; the face goes over it.
|
|
let cb_under = fn(p: vec2, c: vec2, h: vec2, r: float, px: float, th: float, base: vec3, lift: float) -> vec4 {
|
|
let cs = c + vec2(0.0, th)
|
|
let s1 = Finish.drop(p, cs, h, r, vec2(0.0, 0.4 + 0.5 * lift), 0.45 + 0.4 * lift) * 0.50
|
|
let s2 = Finish.drop(p, cs, h, r, vec2(0.0, 0.8 + 1.0 * lift), 1.1 + 0.8 * lift) * 0.22
|
|
let sa = 1.0 - (1.0 - s1) * (1.0 - s2)
|
|
var out = vec4(cb_shade_c() * sa, sa)
|
|
if th > 0.01 {
|
|
let df = cb_rbox(p, c, h, r)
|
|
let ds = cb_rbox(p, c + vec2(0.0, th * 0.5), h + vec2(0.0, th * 0.5), r)
|
|
let v = clamp(df / th, 0.0, 1.0)
|
|
let side = cb_flute(p.x, v, th, px, base)
|
|
let a2 = Finish.cover(ds, px)
|
|
out = Finish.over(out, vec4(side * a2, a2))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// The face's own cut edge: a pale line where the top liner was cut,
|
|
// brightest along the top, and the kraft a shade darker just inside it where
|
|
// the knife pressed it down.
|
|
let cb_cut_edge = fn(col: vec3, d: float, yrel: float, hy: float, px: float, amt: float) -> vec3 {
|
|
let up = 1.0 - smoothstep(0.0 - hy, hy, yrel)
|
|
var c2 = mix(col, col * 0.84, Finish.band(d, 1.2 * px, 2.6 * px, px) * 0.55 * amt)
|
|
c2 = mix(c2, mix(Finish.lift(col, 0.40), cb_liner_c(), 0.5), Finish.band(d, 0.0, 1.2 * px, px) * (0.30 + 0.60 * up) * amt)
|
|
return c2
|
|
}
|
|
|
|
// The window a piece was cut from, round the piece: the gap between them `g`
|
|
// points wide, dark, the flutes of the window's wall showing in it as pale
|
|
// flecks and the gap deepest under the window's top wall; and outside it a
|
|
// pale line where the window's own liner was cut, brightest along its foot,
|
|
// which faces up. `d` is the piece's distance. Premultiplied.
|
|
let cb_gap = fn(p: vec2, d: float, yrel: float, hy: float, g: float, px: float, amt: float) -> vec4 {
|
|
let dw = d - g
|
|
let ring = Finish.cover(dw, px) * (1.0 - Finish.cover(d, px))
|
|
let up = 1.0 - smoothstep(0.0 - hy, hy, yrel)
|
|
let fl = smoothstep(0.52, 0.78, Finish.noise(p * 1.9 + vec2(3.0, 8.0)))
|
|
var col = mix(vec3(0.120, 0.078, 0.040), vec3(0.50, 0.37, 0.23), fl * 0.8)
|
|
col = col * (0.78 + 0.32 * (1.0 - up))
|
|
let ra = ring * amt
|
|
var out = vec4(col * ra, ra)
|
|
let lip = Finish.ring_out(dw, 0.0, 1.2 * px, px) * (0.30 + 0.55 * (1.0 - up)) * amt
|
|
out = Finish.over(out, vec4(cb_liner_c() * lip, lip))
|
|
return out
|
|
}
|
|
|
|
// A LINE OF FELT MARKER `hw` points either side of its middle; `dl` is the
|
|
// unsigned distance from the middle. Its width wanders as a hand's does,
|
|
// its ink is a shade thinner in streaks, and it bleeds a little way into
|
|
// the fibres beside it (gone within a point and a half). Premultiplied.
|
|
let cb_stroke = fn(dl: float, hw: float, p: vec2, px: float, ink: vec3, amt: float) -> vec4 {
|
|
let w = hw + clamp((Finish.noise(p * 0.22 + vec2(7.0, 3.0)) - 0.5) * 0.5 * hw, -0.2, 0.2)
|
|
let body = Finish.cover(dl - w, px)
|
|
let dens = 0.90 + 0.10 * Finish.noise(p * 1.3 + vec2(1.0, 9.0))
|
|
let x = max(dl - w, 0.0)
|
|
let bleed = exp(0.0 - x / 0.38) * (1.0 - smoothstep(1.0, 1.5, x)) * (1.0 - body) * 0.22 * (0.45 + Finish.noise(p * 3.1 + vec2(4.0, 4.0)))
|
|
let a = clamp(body * dens + bleed, 0.0, 1.0) * amt
|
|
return vec4(ink * a, a)
|
|
}
|
|
|
|
// A shape filled in with marker: `d` its signed distance.
|
|
let cb_fill = fn(d: float, p: vec2, px: float, ink: vec3, amt: float) -> vec4 {
|
|
return cb_stroke(max(d + 0.5, 0.0), 0.5, p, px, ink, amt)
|
|
}
|
|
|
|
// A hand's wobble on a line, at most `k` points either way, slow along it.
|
|
let cb_wob = fn(p: vec2, k: float, seed: vec2) -> float {
|
|
return (Finish.noise(p * 0.32 + seed) - 0.5) * 2.0 * k
|
|
}
|
|
|
|
// FOCUS: a blue ballpoint line drawn round the control, two and a half
|
|
// points out.
|
|
let cb_focus = fn(d: float, p: vec2, px: float, focus: float) -> vec4 {
|
|
let dd = d - 2.6 + cb_wob(p, 0.35, vec2(11.0, 3.0))
|
|
return cb_stroke(abs(dd), 0.42, p, px, cb_pen_c(), 0.9 * focus)
|
|
}
|
|
|
|
// The face of a piece: kind 0 the board, 1 black card, 2 blue paper, 4 red
|
|
// paper, 7 paper of `tint`, 8 cream paper, 9 yellow paper, 10 board of the
|
|
// colour `tint` (a paler slip).
|
|
let cb_face = fn(q: vec2, px: float, kind: float, tint: vec3) -> vec3 {
|
|
if kind > 0.5 && kind < 1.5 {
|
|
return cb_blackcard(q, px, 1.0)
|
|
}
|
|
if kind > 1.5 && kind < 2.5 {
|
|
return cb_paper(q, px, cb_blue_c(), 1.0)
|
|
}
|
|
if kind > 3.5 && kind < 4.5 {
|
|
return cb_paper(q, px, cb_red_c(), 1.0)
|
|
}
|
|
if kind > 6.5 && kind < 7.5 {
|
|
return cb_paper(q, px, tint, 1.0)
|
|
}
|
|
if kind > 7.5 && kind < 8.5 {
|
|
return cb_paper(q, px, cb_cream_c(), 1.0)
|
|
}
|
|
if kind > 8.5 && kind < 9.5 {
|
|
return cb_paper(q, px, cb_yellow_c(), 1.0)
|
|
}
|
|
if kind > 9.5 {
|
|
return cb_kraft(q, px, tint, 0.85)
|
|
}
|
|
return cb_kraft(q, px, Finish.lift(cb_kraft_c(), 0.03), 1.0)
|
|
}
|
|
|
|
// A disc of board riding on what is under it: its shadow, its cut edge
|
|
// along its foot (`th` points), a face a shade lighter at the top (the
|
|
// board's kraft, or paper of a colour glued on it: `kind` as for a piece),
|
|
// the pale cut line round its top and a line of shade round its foot.
|
|
// Pressed it sits down on its edge. Premultiplied.
|
|
let cb_disc_k = fn(p: vec2, c: vec2, r: float, px: float, th: float, hover: float, press: float, dis: float, kind: float) -> vec4 {
|
|
let live = 1.0 - dis
|
|
let ft = th * (1.0 - 0.45 * press)
|
|
let fc = c - vec2(0.0, ft * 0.5)
|
|
var out = cb_under(p, fc, vec2(r, r), r, px, ft, cb_kraft_c(), clamp(0.35 + 0.5 * hover * live - 0.35 * press, 0.0, 1.0))
|
|
out = out * mix(1.0, 0.55, dis)
|
|
let d = length(p - fc) - r
|
|
let q = (p - fc) / max(r, 0.5)
|
|
var face = cb_kraft(p - fc + vec2(31.0, 7.0), px, Finish.lift(cb_kraft_c(), 0.07), 1.0)
|
|
if kind > 0.5 {
|
|
face = cb_face(p - fc + vec2(31.0, 7.0), px, kind, vec3(0.0, 0.0, 0.0))
|
|
}
|
|
face = face * (1.05 - 0.12 * smoothstep(-1.0, 1.0, q.y))
|
|
face = Finish.lift(face, 0.16 * hover * live)
|
|
face = face * (1.0 - 0.07 * press)
|
|
face = mix(face, vec3(0.70, 0.64, 0.57), dis * 0.55)
|
|
face = cb_cut_edge(face, d, p.y - fc.y, r, px, 1.0 + 0.5 * hover * live)
|
|
face = mix(face, face * 0.55, Finish.band(d, 0.0, px, px) * smoothstep(-0.2, 0.8, q.y))
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(out, vec4(face * a, a))
|
|
}
|
|
let cb_disc = fn(p: vec2, c: vec2, r: float, px: float, th: float, hover: float, press: float, dis: float) -> vec4 {
|
|
return cb_disc_k(p, c, r, px, th, hover, press, dis, 0.0)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// A PIECE OF CARD GLUED ON THE BOARD, the way every working control is
|
|
// made: its face the box `c`, `h` (corners `r4`); under its foot the board's
|
|
// cut edge `th` points deep, seen side on, the fluted layer between its two
|
|
// liners; under it all a soft contact shadow, gone within four points of the
|
|
// box (half the gap to a neighbour); the pale line where the face's liner
|
|
// was cut; and a line of marker drawn round the face just inside its edge
|
|
// (`ol` its strength). Under the pointer the piece stands a little prouder,
|
|
// its shadow longer and its face a shade lighter; pressed it is pushed down
|
|
// onto the board, its face slipping down into the depth of its edge and its
|
|
// shadow drawn in close. Disabled it lies under a sheet of tracing paper.
|
|
// `kind` and `tint` pick the face as `cb_face` does. Premultiplied.
|
|
let cb_piece = fn(p: vec2, c: vec2, h: vec2, r4: vec4, px: float, th: float, hover: float, press: float, dis: float, kind: float, tint: vec3, ol: float, seed: vec2) -> vec4 {
|
|
let live = 1.0 - dis
|
|
let lift = hover * live * (1.0 - press)
|
|
let sink = th * 0.7 * press
|
|
let fc = c + vec2(0.0, sink)
|
|
let eth = th - sink
|
|
let wob = cb_wob(p, 0.16, seed * 0.01)
|
|
let d = cb_rbox4(p, fc, h, r4) + wob
|
|
let r = max(max(r4.x, r4.y), max(r4.z, r4.w))
|
|
// the shadow of the whole piece, face and edge together
|
|
let pc = c + vec2(0.0, th * 0.5)
|
|
let ph = h + vec2(0.0, th * 0.5)
|
|
let s1 = Finish.drop(p, pc, ph, r, vec2(0.3, 0.5 + 0.3 * lift - 0.3 * press), 0.6 + 0.25 * lift) * 0.62
|
|
let s2 = Finish.drop(p, pc, ph, r, vec2(0.6, 1.1 + 0.8 * lift - 0.7 * press), 1.3 + 0.5 * lift - 0.5 * press) * 0.42
|
|
let db = cb_rbox(p, c, h, r)
|
|
let fade = 1.0 - smoothstep(1.5, 4.0, db)
|
|
let sa = (1.0 - (1.0 - s1) * (1.0 - s2)) * fade * mix(1.0, 0.5, dis)
|
|
var out = vec4(cb_shade_c() * sa, sa)
|
|
// the cut edge, side on
|
|
if eth > 0.05 {
|
|
let de = cb_rbox4(p, fc + vec2(0.0, eth * 0.5), h + vec2(0.0, eth * 0.5), r4) + wob
|
|
var side = cb_flute(p.x + seed.x, clamp(d / eth, 0.0, 1.0), eth, px, cb_kraft_c())
|
|
side = mix(side, vec3(0.62, 0.56, 0.49), dis * 0.6)
|
|
let a2 = Finish.cover(de, px)
|
|
out = Finish.over(out, vec4(side * a2, a2))
|
|
}
|
|
var face = cb_face(p + seed, px, kind, tint)
|
|
face = face * (1.04 - 0.08 * smoothstep(0.0 - h.y, h.y, p.y - fc.y))
|
|
face = mix(face, mix(vec3(0.68, 0.62, 0.55), Finish.lift(face, 0.30), 0.25), dis * 0.78)
|
|
face = Finish.lift(face, 0.10 * lift)
|
|
face = face * (1.0 - 0.10 * press)
|
|
face = cb_cut_edge(face, d, p.y - fc.y, h.y, px, mix(1.0 + 0.4 * lift - 0.4 * press, 0.4, dis))
|
|
let a = Finish.cover(d, px)
|
|
out = Finish.over(out, vec4(face * a, a))
|
|
let ink = mix(cb_ink_c(), cb_dry_c(), dis)
|
|
let ow = 0.85 + 0.1 * clamp(h.y - 12.0, 0.0, 4.0) * 0.25
|
|
out = Finish.over(out, cb_stroke(abs(d + 1.2 + ow), ow, p, px, ink, ol * a * mix(1.0, 0.55, dis)))
|
|
return out
|
|
}
|
|
|
|
// BUTTONS. A piece of card glued on the board (`cb_piece`); `kind` picks the
|
|
// face: 0 the board, 1 black card (primary, no outline: the marker does not
|
|
// show on it), 2 blue paper (secondary), 4 red paper (danger), 7 paper of
|
|
// `tint`; 5 a frame drawn in marker straight on the board (outline), 6 bare
|
|
// until touched (flatter). The quad grows by `m` for the cut edge, the
|
|
// shadow and the focus line.
|
|
let cb_button = fn(pos: vec2, size: vec2, o: vec2, px: float, hover: float, down: float, focus: float, dis: float, loading: float, anim: float, r4: vec4, kind: float, m: float, tint: vec4, row: float) -> vec4 {
|
|
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let live = 1.0 - dis
|
|
let press = down * live
|
|
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
|
|
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let seed = size * vec2(1.37, 2.71)
|
|
let d = cb_rbox4(p, c, h, r4) + cb_wob(p, 0.16, seed * 0.01)
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
if kind < 4.5 || kind > 6.5 {
|
|
let th = clamp(h.y * 0.21, 2.0, 3.4)
|
|
let ol = 1.0 - step(0.5, kind) * step(kind, 1.5)
|
|
out = cb_piece(p, c, h, r4, px, th, hover, press, dis, kind, tint.rgb, ol, seed)
|
|
} else {
|
|
let bare = step(5.5, kind)
|
|
let show = clamp(hover * live + press, 0.0, 1.0)
|
|
let wash = Finish.cover(d, px) * (0.05 * hover * live + 0.12 * press)
|
|
out = vec4(cb_shade_c() * wash, wash)
|
|
let hw = 1.0 + 0.15 * hover * live
|
|
let pencil = mix(vec3(0.30, 0.24, 0.17), cb_ink_c(), press)
|
|
let ink = mix(mix(cb_ink_c(), pencil, bare), cb_dry_c(), dis)
|
|
let amt = mix(1.0, 0.55 * show, bare) * mix(1.0, 0.6, dis)
|
|
out = Finish.over(out, cb_stroke(abs(d + hw + 0.3), hw * mix(1.0, 0.55, bare), p, px, ink, amt))
|
|
}
|
|
out = Finish.over(out, cb_focus(d, p, px, focus * live))
|
|
// The wait spinner, which the stock face drew and this one must too.
|
|
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.4)
|
|
sdf.fill(vec4(0.094, 0.078, 0.055, loading))
|
|
out = Finish.over(out, sdf.result)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Every button template: the root and the sizes share its draw object, so
|
|
// they wear the plain piece; 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 = cb_grow(self.rect_pos, self.rect_size, 6.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(cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, cb_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 4.0), 0.0, 6.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0), 6.0)
|
|
}
|
|
mod.widgets.Button.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return self.host_over(cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, cb_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 4.0), 0.0, 6.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0), 6.0)
|
|
}
|
|
mod.widgets.ButtonPrimary.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return self.host_over(cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, cb_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 4.0), 1.0, 6.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0), 6.0)
|
|
}
|
|
mod.widgets.ButtonSecondary.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return self.host_over(cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, cb_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 4.0), 2.0, 6.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0), 6.0)
|
|
}
|
|
mod.widgets.ButtonTertiary.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return self.host_over(cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, cb_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 4.0), 0.0, 6.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0), 6.0)
|
|
}
|
|
mod.widgets.ButtonDanger.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return self.host_over(cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, cb_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 4.0), 4.0, 6.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0), 6.0)
|
|
}
|
|
mod.widgets.ButtonOutline.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return self.host_over(cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, cb_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 4.0), 5.0, 6.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0), 6.0)
|
|
}
|
|
mod.widgets.ButtonFlatter.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return self.host_over(cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, cb_corners(self.border_radius_tl, self.border_radius_tr, self.border_radius_br, self.border_radius_bl, 4.0), 6.0, 6.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0), 6.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: chunky pieces of card, one 32 point row, 15 points either side
|
|
// of the word; the small one 26, the large one 40.
|
|
mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 15.0 right: 15.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.Button.padding = mod.turtle.Inset{left: 15.0 right: 15.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonPrimary.padding = mod.turtle.Inset{left: 15.0 right: 15.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonSecondary.padding = mod.turtle.Inset{left: 15.0 right: 15.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonDanger.padding = mod.turtle.Inset{left: 15.0 right: 15.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonTertiary.padding = mod.turtle.Inset{left: 15.0 right: 15.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonOutline.padding = mod.turtle.Inset{left: 15.0 right: 15.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: 16.0 height: Fit}
|
|
mod.widgets.ButtonFlat.min_height = 32.0
|
|
mod.widgets.Button.min_height = 32.0
|
|
mod.widgets.ButtonFlat.draw_text.text_style.font_size = 13.0
|
|
mod.widgets.ButtonXs.min_height = 0.0
|
|
mod.widgets.ButtonXs.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ButtonXs.draw_text.text_style.font_size = 10.5
|
|
mod.widgets.ButtonSm.min_height = 0.0
|
|
mod.widgets.ButtonSm.height = 26.0
|
|
mod.widgets.ButtonSm.padding = mod.turtle.Inset{left: 10.0 right: 10.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ButtonSm.draw_text.text_style.font_size = 12.0
|
|
mod.widgets.ButtonLg.height = 40.0
|
|
mod.widgets.ButtonLg.padding = mod.turtle.Inset{left: 18.0 right: 18.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ButtonLg.draw_text.text_style.font_size = 15.0
|
|
|
|
// The words on a piece are hand printed in marker, heavier than running
|
|
// text; on black card they are written in a cream paint pen.
|
|
mod.widgets.ButtonFlat.draw_text.text_style.font_family = mod.theme.font_label.font_family
|
|
mod.widgets.ButtonPrimary.draw_text.color = #xf1e6cf
|
|
mod.widgets.ButtonPrimary.draw_text.color_hover = #xfbf3e2
|
|
mod.widgets.ButtonPrimary.draw_text.color_down = #xe6d8bc
|
|
mod.widgets.ButtonPrimary.draw_text.color_focus = #xf1e6cf
|
|
mod.widgets.ButtonPrimary.draw_text.color_disabled = #x6e5a44
|
|
mod.widgets.ButtonPrimary.draw_icon.color = #xf1e6cf
|
|
mod.widgets.ButtonPrimary.draw_text.text_style = mod.theme.font_label{font_size: 13.0}
|
|
mod.widgets.ButtonSecondary.draw_text.color = #x18140e
|
|
mod.widgets.ButtonSecondary.draw_text.color_hover = #100c08
|
|
mod.widgets.ButtonSecondary.draw_text.color_down = #100c08
|
|
mod.widgets.ButtonSecondary.draw_text.color_focus = #x18140e
|
|
mod.widgets.ButtonSecondary.draw_icon.color = #x18140e
|
|
mod.widgets.ButtonSecondary.draw_text.text_style = mod.theme.font_label{font_size: 13.0}
|
|
mod.widgets.ButtonTertiary.draw_text.color = #x18140e
|
|
mod.widgets.ButtonTertiary.draw_text.color_hover = #100c08
|
|
mod.widgets.ButtonTertiary.draw_text.color_down = #100c08
|
|
mod.widgets.ButtonTertiary.draw_text.color_focus = #x18140e
|
|
mod.widgets.ButtonTertiary.draw_icon.color = #x18140e
|
|
mod.widgets.ButtonTertiary.draw_text.text_style = mod.theme.font_label{font_size: 13.0}
|
|
mod.widgets.ButtonDanger.draw_text.color = #x18140e
|
|
mod.widgets.ButtonDanger.draw_text.color_hover = #100c08
|
|
mod.widgets.ButtonDanger.draw_text.color_down = #100c08
|
|
mod.widgets.ButtonDanger.draw_text.color_focus = #x18140e
|
|
mod.widgets.ButtonDanger.draw_icon.color = #x18140e
|
|
mod.widgets.ButtonDanger.draw_text.text_style = mod.theme.font_label{font_size: 13.0}
|
|
mod.widgets.ButtonOutline.draw_text.color = #x18140e
|
|
mod.widgets.ButtonOutline.draw_text.color_hover = #100c08
|
|
mod.widgets.ButtonOutline.draw_text.color_down = #100c08
|
|
mod.widgets.ButtonOutline.draw_text.color_focus = #x18140e
|
|
mod.widgets.ButtonOutline.draw_text.color_disabled = #x6e5a44
|
|
mod.widgets.ButtonOutline.draw_icon.color = #x18140e
|
|
mod.widgets.ButtonFlat.draw_bg.loading_color = #x18140e
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CHECK BOXES. A square drawn in marker; set, a tick drawn in one stroke
|
|
// that runs out past the box's top right corner, as a hand draws it; mixed,
|
|
// a dash. Under the pointer the line is a little heavier and a breath of
|
|
// pencil shades the inside; pressed, the inside is darker.
|
|
mod.widgets.CheckBoxFlat.clip_x = false
|
|
mod.widgets.CheckBoxFlat.clip_y = false
|
|
mod.widgets.CheckBoxFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.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: 24.0 right: 3.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.CheckBoxFlat.draw_bg.size = uniform(16.0)
|
|
mod.widgets.CheckBoxFlat.min_height = 26.0
|
|
mod.widgets.CheckBoxFlat.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 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 rnd = step(20.0, self.border_radius)
|
|
let r = mix(1.5, h.y, rnd)
|
|
let d = cb_rbox(p, c, h - vec2(0.9, 0.9), r) + cb_wob(p, 0.22, vec2(4.0, 1.0))
|
|
let ink = mix(mix(cb_ink_c(), cb_redink_c(), self.error), cb_dry_c(), self.disabled)
|
|
let fade = mix(1.0, 0.6, self.disabled)
|
|
let wash = Finish.cover(d, px) * (0.07 * self.hover + 0.14 * self.down) * live
|
|
var out = vec4(cb_shade_c() * wash, wash)
|
|
out = Finish.over(out, cb_stroke(abs(d), 0.88 + 0.12 * self.hover * live, p, px, ink, fade))
|
|
let k = sz / 14.0
|
|
let m0 = lo + vec2(2.8, 7.2) * k
|
|
let m1 = lo + vec2(6.0, 11.0) * k
|
|
let m2 = lo + vec2(15.2, -1.6) * k
|
|
let ck = min(cb_seg(p, m0, m1), cb_seg(p, m1, m2))
|
|
let dash = cb_seg(p, lo + vec2(3.6, 7.3) * k, lo + vec2(10.6, 6.8) * k)
|
|
let show_ck = self.active * (1.0 - self.mixed)
|
|
out = Finish.over(out, cb_stroke(ck, 1.05 * k, p, px, ink, show_ck * fade))
|
|
out = Finish.over(out, cb_stroke(dash, 1.05 * k, p, px, ink, self.mixed * fade))
|
|
return Finish.over(out, cb_focus(d, p, px, self.focus * live))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// TOGGLES. A slot cut through the board, 38 by 18 points: the gap round it,
|
|
// its floor the deeper board in the shade of its top wall; on, the floor is
|
|
// black card. A disc of board 15 points across rides in it.
|
|
mod.widgets.ToggleFlat.label_walk.margin = mod.turtle.Inset{left: 47.0 right: 3.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ToggleFlat.draw_bg.size = 18.0
|
|
mod.widgets.ToggleFlat.min_height = 26.0
|
|
mod.widgets.ToggleFlat.draw_bg.pill_aspect = 2.111
|
|
mod.widgets.ToggleFlat.draw_bg.knob_inset = 9.0
|
|
mod.widgets.ToggleFlat.clip_x = false
|
|
mod.widgets.ToggleFlat.clip_y = false
|
|
mod.widgets.ToggleFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
let cb_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, error: float, at_end: float, kind: float) -> vec4 {
|
|
let m = 5.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 = 18.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 h = vec2(tw * 0.5, th * 0.5)
|
|
let c = lo + h
|
|
let d = cb_rbox(p, c, h, h.y) + cb_wob(p, 0.14, vec2(2.0, 9.0))
|
|
let on = active
|
|
let g = 1.3
|
|
var out = cb_gap(p, d + g, p.y - c.y, h.y, g, px, mix(1.0, 0.5, dis))
|
|
let fd = d + g
|
|
let fa = Finish.cover(fd, px)
|
|
var flr = cb_kraft(p + vec2(17.0, 5.0), px, cb_kraft_c() * 0.84, 1.0)
|
|
// (the toggle's floor)
|
|
flr = mix(flr, cb_blackcard(p, px, 1.0), on)
|
|
flr = mix(flr, mix(cb_redink_c(), flr, 0.5), error * 0.5)
|
|
let ish = clamp(Finish.inset(p, c, h - vec2(g, g), h.y - g, vec2(0.0, 1.4), 1.1), 0.0, 1.0)
|
|
flr = mix(flr, cb_shade_c(), ish * 0.45)
|
|
flr = Finish.lift(flr, 0.06 * hover * live)
|
|
flr = mix(flr, vec3(0.62, 0.56, 0.49), dis * 0.6)
|
|
out = Finish.over(out, vec4(flr * fa, fa))
|
|
let kt = clamp(mix(on, drag_pos, drag), 0.0, 1.0)
|
|
let kr = 7.5
|
|
let kx = lo.x + h.y + kt * (tw - th)
|
|
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, c.y + 0.6)
|
|
out = Finish.over(out, cb_disc_k(p, kc, kr, px, 2.0, hover, down * live, dis, kind))
|
|
return Finish.over(out, cb_focus(d, p, px, focus * live))
|
|
}
|
|
mod.widgets.ToggleFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return cb_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 ring drawn in marker; chosen, a heavier ring and a dot of
|
|
// marker filled in at its middle.
|
|
let cb_radio = fn(p: vec2, c: vec2, rad: float, px: float, hover: float, down: float, focus: float, active: float, dis: float) -> vec4 {
|
|
let live = 1.0 - dis
|
|
let q = p - c
|
|
let d = length(q) - (rad - 0.8) + cb_wob(p, 0.2, vec2(2.0, 5.0))
|
|
let ink = mix(cb_ink_c(), cb_dry_c(), dis)
|
|
let fade = mix(1.0, 0.6, dis)
|
|
let wash = Finish.cover(d, px) * (0.07 * hover + 0.14 * down) * live
|
|
var out = vec4(cb_shade_c() * wash, wash)
|
|
out = Finish.over(out, cb_stroke(abs(d), 0.88 + 0.25 * active + 0.12 * hover * live, p, px, ink, fade))
|
|
let dd = length(q) - rad * 0.40 + cb_wob(p, 0.12, vec2(9.0, 1.0))
|
|
out = Finish.over(out, cb_fill(dd, p, px, ink, active * fade))
|
|
return Finish.over(out, cb_focus(d, p, px, focus * live))
|
|
}
|
|
|
|
mod.widgets.RadioButtonFlat.clip_x = false
|
|
mod.widgets.RadioButtonFlat.clip_y = false
|
|
mod.widgets.RadioButtonFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.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: 24.0 right: 3.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.RadioButtonFlat.draw_bg.size = uniform(16.0)
|
|
mod.widgets.RadioButtonFlat.min_height = 26.0
|
|
mod.widgets.RadioButtonFlat.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)
|
|
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 cb_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 is ringed round by
|
|
// hand in marker; under the pointer a faint pencil ring.
|
|
mod.widgets.RadioButtonFlatter.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)
|
|
let live = 1.0 - self.disabled
|
|
let c = self.rect_size * 0.5
|
|
let ab = self.rect_size * 0.5 + vec2(3.0, 1.0)
|
|
let q = (p - c) / ab
|
|
let d = (length(q) - 1.0) * min(ab.x, ab.y) + cb_wob(p, 0.3, vec2(6.0, 2.0))
|
|
let pencil = vec3(0.30, 0.24, 0.17)
|
|
var out = cb_stroke(abs(d), 0.45, p, px, pencil, 0.45 * self.hover * live * (1.0 - self.active))
|
|
out = Finish.over(out, cb_stroke(abs(d), 0.8, p, px, mix(cb_ink_c(), cb_dry_c(), self.disabled), self.active))
|
|
return Finish.over(out, cb_focus(d, p, px, self.focus * live))
|
|
}
|
|
|
|
// Radio tabs: each a piece in its window; the chosen one is black card,
|
|
// pressed in, its word in cream.
|
|
mod.widgets.RadioButtonTabFlat.padding = mod.turtle.Inset{left: 14.0 right: 14.0 top: 6.0 bottom: 4.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 = 30.0
|
|
mod.widgets.RadioButtonTabFlat.clip_x = false
|
|
mod.widgets.RadioButtonTabFlat.clip_y = false
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.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 held = clamp(max(self.active, self.down), 0.0, 1.0)
|
|
return cb_button(self.pos, self.rect_size, self.rect_pos, px, self.hover, held, self.focus, self.disabled, 0.0, 0.0, vec4(4.0, 4.0, 4.0, 4.0), step(0.5, self.active), 5.0, vec4(0.0, 0.0, 0.0, 0.0), 0.0)
|
|
}
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color = #x18140e
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #xf1e6cf
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #x6e5a44
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SLIDERS. The track is drawn in marker: a thick line the length of the
|
|
// travel, and over the part the value covers a broader stroke. The thumb is
|
|
// a disc of board 20 points across riding on it, glued up thick enough that
|
|
// its cut edge shows under it. Every slider's quad grows
|
|
// for the disc's shadow, its cut edge and the focus line.
|
|
mod.widgets.SliderMinimal.height = 32.0
|
|
mod.widgets.SliderMinimal.clip_x = false
|
|
mod.widgets.SliderMinimal.clip_y = false
|
|
mod.widgets.SliderMinimal.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
|
|
let cb_track = fn(p: vec2, x0: float, x1: float, cy: float, f0: float, f1: float, px: float, hw: float, thin: float, dis: float) -> vec4 {
|
|
let ink = mix(cb_ink_c(), cb_dry_c(), dis)
|
|
let wy = cy + (Finish.noise(vec2(p.x * 0.045, 3.0)) - 0.5) * 0.5
|
|
let dl = cb_seg(p, vec2(x0 + thin, wy), vec2(x1 - thin, wy))
|
|
var out = cb_stroke(dl, thin, p, px, ink, mix(0.85, 0.5, dis))
|
|
if f1 - f0 > 0.01 {
|
|
let a = min(f0 + hw, f1)
|
|
let b = max(f1 - hw, a)
|
|
let dt = cb_seg(p, vec2(a, wy), vec2(b, wy))
|
|
out = Finish.over(out, cb_stroke(dt, hw, p, px, ink, mix(1.0, 0.55, dis)))
|
|
}
|
|
return out
|
|
}
|
|
|
|
mod.widgets.SliderFlat.height = 40.0
|
|
mod.widgets.SliderFlat.clip_x = false
|
|
mod.widgets.SliderFlat.clip_y = false
|
|
mod.widgets.SliderFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.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 = 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 cy = Finish.snap(o.y + size.y - 12.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 kr = 10.0
|
|
let span = w - 2.0 * kr
|
|
let kx = x0 + kr + self.slide_pos * span
|
|
let ox = x0 + kr + self.origin_pos * span
|
|
let f0 = mix(x0, min(ox, kx), self.arc_origin)
|
|
let f1 = mix(kx, max(ox, kx), self.arc_origin)
|
|
var out = cb_track(p, x0, x1, cy, f0, f1, px, 2.7, 1.45, self.disabled)
|
|
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, cy + 1.2)
|
|
out = Finish.over(out, cb_disc(p, kc, kr, px, 3.0, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
|
|
return Finish.over(out, cb_focus(length(p - kc) - kr, p, px, self.focus * live))
|
|
}
|
|
mod.widgets.SliderFlat.text_input.draw_text.text_style = mod.theme.font_bold{font_size: 9.5}
|
|
|
|
// The range slider: the track's broad stroke between the two ends, and a
|
|
// disc on each end, sitting down while it is the one held.
|
|
mod.widgets.RangeSliderFlat.handle_size = 18.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 = cb_grow(self.rect_pos, self.rect_size, 5.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 = 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 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 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 = cb_track(p, x0, x1, cy, sx, ex, px, 2.7, 1.45, self.disabled)
|
|
let kr = self.handle_px * 0.5
|
|
let k0 = vec2(Finish.snap(o.x + sx, px) - o.x, cy + 1.0)
|
|
let k1 = vec2(Finish.snap(o.x + ex, px) - o.x, cy + 1.0)
|
|
out = Finish.over(out, cb_disc(p, k0, kr, px, 2.6, max(self.hot_start, self.focus * 0.5) * live, self.drag * self.hot_start * live, self.disabled))
|
|
out = Finish.over(out, cb_disc(p, k1, kr, px, 2.6, max(self.hot_end, self.focus * 0.5) * live, self.drag * self.hot_end * live, self.disabled))
|
|
return out
|
|
}
|
|
|
|
// The minimal slider: one thin marker line, a heavier one over the value,
|
|
// and a small disc of board.
|
|
mod.widgets.SliderMinimal.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 cy = Finish.snap(o.y + size.y - 8.0, px) - o.y
|
|
let kr = 6.5
|
|
let kx = kr + self.slide_pos * (size.x - 2.0 * kr)
|
|
var out = cb_track(p, 0.0, size.x, cy, 0.0, kx, px, 1.8, 1.0, self.disabled)
|
|
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, cy + 0.8)
|
|
out = Finish.over(out, cb_disc(p, kc, kr, px, 1.8, self.hover, self.drag * live, self.disabled))
|
|
return Finish.over(out, cb_focus(length(p - kc) - kr, p, px, self.focus * live))
|
|
}
|
|
mod.widgets.SliderMinimal.text_input.draw_text.text_style = mod.theme.font_bold{font_size: 9.5}
|
|
|
|
// The round slider: a slot cut through the board beside its label, its
|
|
// floor filled in black marker up to a small disc riding in it; the value
|
|
// reads beside the slot.
|
|
mod.widgets.SliderRoundFlat.height = 22.0
|
|
mod.widgets.SliderRoundFlat.clip_x = false
|
|
mod.widgets.SliderRoundFlat.clip_y = false
|
|
mod.widgets.SliderRoundFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.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 = 34.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_bold{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 = 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 x0 = Finish.snap(o.x + self.label_size, px) - o.x
|
|
let y0 = Finish.snap(o.y + 1.0, px) - o.y
|
|
let x1 = Finish.snap(o.x + size.x - 48.0, px) - o.x
|
|
let y1 = Finish.snap(o.y + size.y - 1.0, px) - o.y
|
|
let h = vec2((x1 - x0) * 0.5, (y1 - y0) * 0.5)
|
|
let c = vec2(x0, y0) + h
|
|
let d = cb_rbox(p, c, h, h.y) + cb_wob(p, 0.14, vec2(5.0, 5.0))
|
|
let g = 1.2
|
|
var out = cb_gap(p, d + g, p.y - c.y, h.y, g, px, mix(1.0, 0.5, dis))
|
|
let fd = d + g
|
|
let fa = Finish.cover(fd, px)
|
|
var flr = cb_kraft(p + vec2(3.0, 19.0), px, cb_kraft_c() * 0.84, 1.0)
|
|
let ish = clamp(Finish.inset(p, c, h - vec2(g, g), h.y - g, vec2(0.0, 1.2), 1.0), 0.0, 1.0)
|
|
flr = mix(flr, cb_shade_c(), ish * 0.45)
|
|
flr = Finish.lift(flr, 0.06 * self.hover * live)
|
|
flr = mix(flr, vec3(0.62, 0.56, 0.49), dis * 0.6)
|
|
out = Finish.over(out, vec4(flr * fa, fa))
|
|
let kr = h.y - g - 0.8
|
|
let kx = x0 + g + 0.8 + kr + self.slide_pos * (2.0 * h.x - 2.0 * (g + 0.8) - 2.0 * kr)
|
|
let fill = cb_rbox(p, vec2((x0 + g + kx) * 0.5, c.y), vec2((kx - x0 - g) * 0.5, h.y - g), h.y - g)
|
|
out = Finish.over(out, vec4(cb_blackcard(p, px, 1.0) * Finish.cover(max(fill, fd), px) * mix(1.0, 0.4, dis), Finish.cover(max(fill, fd), px) * mix(1.0, 0.4, dis)))
|
|
let kc = vec2(Finish.snap(o.x + kx, px) - o.x, c.y)
|
|
out = Finish.over(out, cb_disc(p, kc, kr, px, 1.6, self.hover, self.drag * live, dis))
|
|
return Finish.over(out, cb_focus(d, p, px, self.focus * live))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// FADERS. A slot cut through the board three points wide, its walls dark
|
|
// with the flutes in them, ticks drawn either side in marker, and a block
|
|
// of board glued up from three layers riding in it, 22 points across and
|
|
// 12 along the travel, yellow paper glued on its top and a marker line
|
|
// drawn across its middle.
|
|
mod.widgets.SliderFader.cap_size = 12.0
|
|
mod.widgets.SliderFader.clip_x = false
|
|
mod.widgets.SliderFader.clip_y = false
|
|
mod.widgets.SliderFader.draw_bg.vertex = fn() {
|
|
let g = cb_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 dis = 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 = 3.4
|
|
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)
|
|
let sd = cb_rbox(p, sc, sh, sw * 0.5)
|
|
var out = cb_gap(p, sd + sw * 0.5, p.y - sc.y, sh.y, sw * 0.5, px, mix(1.0, 0.5, dis))
|
|
let sa = Finish.cover(sd + sw * 0.5, px)
|
|
out = Finish.over(out, vec4(vec3(0.10, 0.065, 0.035) * sa, sa))
|
|
// ticks drawn by hand either side: a long one every quarter of the
|
|
// travel, three short ones between
|
|
let nt = 16.0
|
|
let f = (a - foot) / travel * nt
|
|
let fi = floor(f + 0.5)
|
|
let ta = 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 in_rng = step(-0.5, fi) * step(fi, nt + 0.5)
|
|
let off = abs(b - mid) - sw * 0.5 - 3.0
|
|
let tq = vec2(a, off)
|
|
let dt = cb_seg(tq, vec2(ta, 0.0), vec2(ta, tl))
|
|
let tk = in_rng * mix(1.0, 0.5, dis)
|
|
out = Finish.over(out, cb_stroke(dt, mix(0.42, 0.55, major), p, px, mix(cb_ink_c(), cb_dry_c(), dis), tk))
|
|
// the block, in screen space so its light stays on top
|
|
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 press = self.drag * live
|
|
let tk2 = 2.4 * (1.0 - 0.4 * press)
|
|
let cc = vec2(Finish.snap(o.x + cs.x, px) - o.x, Finish.snap(o.y + cs.y - tk2 * 0.5, px) - o.y)
|
|
out = Finish.over(out, cb_under(p, cc, ch, 1.5, px, tk2, cb_kraft_c(), clamp(0.4 + 0.5 * self.hover * live - 0.4 * press, 0.0, 1.0)) * mix(1.0, 0.55, dis))
|
|
let cd = cb_rbox(p, cc, ch, 1.5)
|
|
var face = cb_paper(p - cc + vec2(5.0, 61.0), px, cb_yellow_c(), 1.0)
|
|
face = face * (1.05 - 0.10 * smoothstep(0.0 - ch.y, ch.y, p.y - cc.y))
|
|
face = Finish.lift(face, 0.08 * max(self.hover, self.focus * 0.5) * live)
|
|
face = face * (1.0 - 0.07 * press)
|
|
face = mix(face, vec3(0.70, 0.64, 0.57), dis * 0.55)
|
|
face = cb_cut_edge(face, cd, p.y - cc.y, ch.y, px, 1.0)
|
|
let ca = Finish.cover(cd, px)
|
|
out = Finish.over(out, vec4(face * ca, ca))
|
|
let ld = mix(abs(p.y - cc.y), abs(p.x - cc.x), 1.0 - vert)
|
|
let lspan = mix(abs(p.x - cc.x) - (ch.x - 2.5), abs(p.y - cc.y) - (ch.y - 2.5), 1.0 - vert)
|
|
out = Finish.over(out, cb_stroke(max(ld, max(lspan, 0.0)), 0.6, p, px, mix(cb_ink_c(), cb_dry_c(), dis), ca))
|
|
return Finish.over(out, cb_focus(cd, p, px, self.focus * live))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// KNOBS. A disc of board glued up from a few layers, so its cut edge shows
|
|
// thick along its foot, a ring of marker drawn round its face, and a marker
|
|
// pointer from near its middle toward its rim, turned with the value. Round
|
|
// it, ticks drawn on the board in marker: the ones the value has passed in
|
|
// full ink, the rest in dry pencil.
|
|
let cb_knob = fn(p: vec2, c: vec2, rad: float, px: float, ang: float, hover: float, press: float, dis: float) -> vec4 {
|
|
let live = 1.0 - dis
|
|
let th = clamp(rad * 0.16, 1.6, 3.0) * (1.0 - 0.35 * press)
|
|
let fc = c - vec2(0.0, th * 0.5)
|
|
var out = cb_under(p, fc, vec2(rad, rad), rad, px, th, cb_kraft_c(), clamp(0.5 + 0.4 * hover * live - 0.4 * press, 0.0, 1.0))
|
|
out = out * mix(1.0, 0.55, dis)
|
|
let q = p - fc
|
|
let d = length(q) - rad
|
|
let t = q / max(rad, 0.5)
|
|
var face = cb_kraft(q + vec2(rad * 3.1, 17.0), px, Finish.lift(cb_kraft_c(), 0.07), 1.0)
|
|
face = face * (1.06 - 0.12 * smoothstep(-1.0, 1.0, t.y))
|
|
face = Finish.lift(face, 0.07 * hover * live)
|
|
face = face * (1.0 - 0.06 * press)
|
|
face = mix(face, vec3(0.70, 0.64, 0.57), dis * 0.55)
|
|
face = cb_cut_edge(face, d, q.y, rad, px, 1.0)
|
|
let a = Finish.cover(d, px)
|
|
out = Finish.over(out, vec4(face * a, a))
|
|
let ink = mix(cb_ink_c(), cb_dry_c(), dis)
|
|
let fade = mix(1.0, 0.6, dis)
|
|
let ring = abs(d + max(rad * 0.09, 1.1)) + cb_wob(p, 0.1, vec2(1.0, 3.0))
|
|
out = Finish.over(out, cb_stroke(max(ring, 0.0), max(rad * 0.055, 0.7), p, px, ink, fade * a))
|
|
let dir = vec2(0.0 - sin(ang), cos(ang))
|
|
let dp = cb_seg(p, fc + dir * rad * 0.22, fc + dir * rad * 0.74)
|
|
out = Finish.over(out, cb_stroke(dp, max(rad * 0.075, 0.9), p, px, ink, fade))
|
|
return out
|
|
}
|
|
|
|
// Ticks round a knob: `n` of them over the sweep from `r0` to `r1`, the
|
|
// ends and the middle longer; those between `v0` and `v1` in full ink.
|
|
let cb_ticks = fn(p: vec2, c: vec2, r0: float, r1: float, gap: float, v0: float, v1: float, n: float, px: 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 k = clamp(floor((ang - start) / sweep * (n - 1.0) + 0.5), 0.0, n - 1.0)
|
|
let f = k / (n - 1.0)
|
|
let ak = start + sweep * f
|
|
let dir = vec2(0.0 - sin(ak), cos(ak))
|
|
let major = max(1.0 - step(0.5, k), max(step(n - 1.5, k), 1.0 - step(0.5, abs(k - (n - 1.0) * 0.5))))
|
|
let ri = mix(r0 + (r1 - r0) * 0.35, r0, major)
|
|
let dl = cb_seg(p, c + dir * ri, c + dir * r1)
|
|
let on = step(v0 - 0.001, f) * step(f, v1 + 0.001)
|
|
let ink = mix(mix(vec3(0.33, 0.26, 0.18), cb_ink_c(), on), cb_dry_c(), dis)
|
|
let amt = mix(0.55, 1.0, on) * mix(1.0, 0.55, dis)
|
|
return cb_stroke(dl, 0.55, p, px, ink, amt)
|
|
}
|
|
|
|
// An arc drawn round a knob in marker over the value, in dry pencil over
|
|
// the rest; `ar` its middle radius. The gap is centred at the bottom and the
|
|
// value runs clockwise from its left end, or from the origin when bipolar.
|
|
let cb_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) + cb_wob(p, 0.2, vec2(8.0, 8.0)) - ar)
|
|
let in_arc = step(start, ang) * step(ang, start + sweep)
|
|
let e0 = c + vec2(0.0 - sin(start), cos(start)) * ar
|
|
let e1 = c + vec2(0.0 - sin(start + sweep), cos(start + sweep)) * ar
|
|
let track_d = min(mix(1000.0, rd, in_arc), min(length(p - e0), length(p - e1)))
|
|
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)))
|
|
var out = cb_stroke(track_d, 0.45, p, px, vec3(0.33, 0.26, 0.18), mix(0.6, 0.35, dis))
|
|
return Finish.over(out, cb_stroke(lit_d, aw * 0.5, p, px, mix(cb_ink_c(), cb_dry_c(), dis), mix(1.0, 0.55, dis)))
|
|
}
|
|
|
|
mod.widgets.RotaryKnob.clip_x = false
|
|
mod.widgets.RotaryKnob.clip_y = false
|
|
mod.widgets.RotaryKnob.draw_bg.vertex = fn() {
|
|
let g = cb_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 - 0.5
|
|
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 rad = floor(rr * 0.71 / 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 = cb_ticks(p, c, rad + 2.6, rr, gap, v0, v1, 11.0, px, self.disabled)
|
|
let ang = gap * 0.5 + (2.0 * PI - gap) * self.slide_pos
|
|
out = Finish.over(out, cb_knob(p, c, rad, px, ang, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
|
|
return Finish.over(out, cb_focus(length(p - c) - rr, p, px, self.focus * live))
|
|
}
|
|
// Knob captions are marker capitals.
|
|
mod.widgets.RotaryKnob.draw_text.color = #x18140e
|
|
mod.widgets.RotaryKnob.draw_text.text_style = mod.theme.font_bold{font_size: 9.5}
|
|
|
|
mod.widgets.RotaryFlat.clip_x = false
|
|
mod.widgets.RotaryFlat.clip_y = false
|
|
mod.widgets.RotaryFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.RotaryFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let m = 5.0
|
|
let size = self.rect_size
|
|
let p = self.pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let o = self.rect_pos
|
|
let live = 1.0 - self.disabled
|
|
let room = vec2(size.x, size.y - 24.0)
|
|
let dia = min(room.x, room.y)
|
|
let rr = dia * 0.5 - 3.5
|
|
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 aw = 2.2
|
|
let ar = floor((rr - aw * 0.5) / px) * px
|
|
let rad = floor(ar / 1.22 / 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 = cb_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, cb_knob(p, c, rad, px, ang, max(self.hover, self.focus * 0.5), self.drag * live, self.disabled))
|
|
return Finish.over(out, cb_focus(length(p - c) - ar - aw * 0.5, p, px, self.focus * live))
|
|
}
|
|
|
|
// THE INSTRUMENT WINDOW, one for the readouts, the screen, the meter and the
|
|
// dial's value: a frame of cream paper `b` points wide glued on the board
|
|
// (its thin shadow under it, its edges a little uneven where it was cut),
|
|
// and a face of black card set in it, a shade of the frame's shadow falling
|
|
// on its top edge. `face` is what the face shows. Premultiplied.
|
|
let cb_window = fn(p: vec2, lo: vec2, hi: vec2, r: float, b: float, px: float, face: vec3, recess: float) -> vec4 {
|
|
let c = (lo + hi) * 0.5
|
|
let ho = (hi - lo) * 0.5
|
|
let r_out = min(r, min(ho.x, ho.y))
|
|
let outer = cb_rbox(p, c, ho, r_out) + cb_wob(p, 0.22, vec2(3.0, 13.0))
|
|
let hf = max(ho - vec2(b, b), vec2(0.5, 0.5))
|
|
let face_d = cb_rbox(p, c, hf, max(r_out - b * 0.5, 0.5)) + cb_wob(p, 0.12, vec2(23.0, 1.0))
|
|
var out = cb_under(p, c, ho, r_out, px, 0.0, cb_kraft_c(), 0.3)
|
|
var frame = cb_paper(p + vec2(7.0, 3.0), px, cb_cream_c(), 1.0)
|
|
frame = frame * (1.03 - 0.06 * smoothstep(0.0 - ho.y, ho.y, p.y - c.y))
|
|
frame = mix(frame, frame * 0.80, Finish.band(outer, 0.0, px, px) * 0.6)
|
|
var f = face
|
|
if recess > 0.0 {
|
|
let insh = Finish.inset(p, c, hf, max(r_out - b * 0.5, 0.5), vec2(0.0, recess * 0.4), max(recess * 0.4, 0.3))
|
|
f = f * (1.0 - 0.45 * insh)
|
|
}
|
|
f = mix(f, Finish.lift(f, 0.25), Finish.band(face_d, 0.0, px, px) * 0.5)
|
|
var col = mix(frame, f, Finish.cover(face_d, px))
|
|
let a = Finish.cover(outer, px)
|
|
return Finish.over(out, vec4(col * a, a))
|
|
}
|
|
|
|
// The black card the instruments show on, with its own grain kept faint so
|
|
// the figures read.
|
|
let cb_screen_face = fn(p: vec2, px: float, base: vec3) -> vec3 {
|
|
let cloud = Finish.fbm(p * 0.07 + vec2(9.0, 2.0)) - 0.5
|
|
var col = base * (1.0 + cloud * 0.22) + g3(Finish.grain(p / px, 0.008))
|
|
let fa = cb_fibres(p, px, 3.0, vec2(5.0, 77.0), 0.45, 0.16, 0.3, 1.0)
|
|
col = mix(col, Finish.lift(base, 0.14), fa.y * 0.35)
|
|
return col
|
|
}
|
|
|
|
// The dial's value: the instrument window in small, yellow figures on it.
|
|
mod.widgets.RotaryFlat.text_input.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 2.0 bottom: 2.0}
|
|
mod.widgets.RotaryFlat.text_input.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.RotaryFlat.text_input.min_height = 22.0
|
|
mod.widgets.RotaryFlat.text_input.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, px)) - o + vec2(0.5, 0.5)
|
|
let hi = vec2(Finish.snap(o.x + self.rect_size.x, px), Finish.snap(o.y + self.rect_size.y, px)) - o - vec2(0.5, 1.0)
|
|
return cb_window(p, lo, hi, 2.0, 2.2, px, cb_screen_face(p, px, cb_black_c()), 2.0)
|
|
}
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color = #xf2c94e
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color_hover = #xf2c94e
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color_focus = #xf6d470
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color_empty = #a08a5a
|
|
mod.widgets.RotaryFlat.text_input.draw_text.text_style = mod.theme.font_bold{font_size: 9.5}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// TEXT FIELDS. A slip of paler card glued on the board as a piece
|
|
// (`cb_piece`): its cut edge under its foot, its shadow, and the marker
|
|
// frame round its face. The quad grows for the edge and the shadow; the text
|
|
// is clipped to the face as before. Focused, a blue ballpoint line runs
|
|
// inside the marker; disabled, the marker has gone dry and the slip is the
|
|
// board under tracing paper. `p` is the point in the grown quad.
|
|
let cb_field = fn(p: vec2, size: vec2, o: vec2, px: float, hover: float, focus: float, dis: float, r: 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 seed = size * vec2(0.61, 1.7)
|
|
let tint = Finish.lift(cb_kraft_c(), 0.15 + 0.05 * hover)
|
|
var out = cb_piece(p, c, h, vec4(r, r, r, r), px, 3.0, 0.0, 0.0, dis, 10.0, tint, 1.0 + 0.15 * focus, seed)
|
|
let fd = cb_rbox(p, c, h, r) + 3.9 + cb_wob(p, 0.2, vec2(1.0, 21.0))
|
|
return Finish.over(out, cb_stroke(abs(fd), 0.4, p, px, cb_pen_c(), 0.85 * focus))
|
|
}
|
|
|
|
mod.widgets.TextInputFlat.padding = mod.turtle.Inset{left: 10.0 right: 10.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.TextInputFlat.clip_x = false
|
|
mod.widgets.TextInputFlat.clip_y = false
|
|
mod.widgets.TextInputFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.TextInputFlat.draw_bg.pixel = fn() {
|
|
// The value field inside a slider or a well draws no box of its own.
|
|
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)
|
|
let m = 5.0
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
return cb_field(p, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled, 3.0)
|
|
}
|
|
mod.widgets.FieldWell.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.FieldWell.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 cb_field(p, self.rect_size, self.rect_pos, px, self.hover, self.focus, self.disabled, 3.0)
|
|
}
|
|
mod.widgets.TextInputFlat.draw_text.color_empty = #4a3a2a
|
|
mod.widgets.TextInputFlat.draw_text.color_disabled = #x6e5a44
|
|
mod.widgets.TextInputFlat.draw_cursor.color = #x18140e
|
|
|
|
// NUMBER FIELDS. The field, and on its right a stepper column set off by a
|
|
// marker line, its two halves each with a chevron drawn in marker; the half
|
|
// under the pointer takes a breath of pencil, the held half a darker one.
|
|
mod.widgets.NumberField.clip_x = false
|
|
mod.widgets.NumberField.clip_y = false
|
|
mod.widgets.NumberField.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.NumberField.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 cb_field(p, 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 = 18.0
|
|
mod.widgets.NumberSpin.draw_text.color = #00000000
|
|
mod.widgets.NumberSpin.draw_text.color_disabled = #00000000
|
|
mod.widgets.NumberSpin.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let o = self.rect_pos
|
|
let live = 1.0 - self.disabled
|
|
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y + 3.2, px)) - o
|
|
let hi = vec2(Finish.snap(o.x + self.rect_size.x - 3.2, px), Finish.snap(o.y + self.rect_size.y - 3.2, px)) - o
|
|
let mid = (lo.y + hi.y) * 0.5
|
|
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 inside = step(lo.x, p.x) * step(p.x, hi.x) * step(lo.y, p.y) * step(p.y, hi.y)
|
|
let wash = inside * (0.07 * hot + 0.14 * dn)
|
|
var out = vec4(cb_shade_c() * wash, wash)
|
|
let ink = mix(cb_ink_c(), cb_dry_c(), self.disabled)
|
|
let fade = mix(1.0, 0.6, self.disabled)
|
|
let dv = abs(p.x - lo.x - 0.6) + cb_wob(p, 0.15, vec2(3.0, 3.0))
|
|
let lv = max(dv, max(lo.y + 0.5 - p.y, p.y - hi.y + 0.5))
|
|
out = Finish.over(out, cb_stroke(max(lv, 0.0), 0.75, p, px, ink, fade))
|
|
let cx = (lo.x + hi.x) * 0.5 + 0.6
|
|
let yu = (lo.y + mid) * 0.5 + 0.5
|
|
let yd = (mid + hi.y) * 0.5 - 0.5
|
|
let cu = min(cb_seg(p, vec2(cx - 3.4, yu + 1.8), vec2(cx, yu - 1.6)), cb_seg(p, vec2(cx, yu - 1.6), vec2(cx + 3.4, yu + 1.8)))
|
|
let cdn = min(cb_seg(p, vec2(cx - 3.4, yd - 1.8), vec2(cx, yd + 1.6)), cb_seg(p, vec2(cx, yd + 1.6), vec2(cx + 3.4, yd - 1.8)))
|
|
return Finish.over(out, cb_stroke(min(cu, cdn), 0.75, p, px, ink, fade))
|
|
}
|
|
|
|
// The drag-number: the field, its track a wash of pencil behind the digits.
|
|
mod.widgets.ValueInput.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.ValueInput.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)
|
|
var out = cb_field(p, self.rect_size, self.rect_pos, px, max(self.hover, self.drag), self.focus, 0.0, 3.0)
|
|
let d = cb_rbox(p, self.rect_size * 0.5, self.rect_size * 0.5 - vec2(3.5, 3.5), 2.0)
|
|
let wx = 3.5 + (self.rect_size.x - 7.0) * max(self.fill, 0.0)
|
|
let wash = step(0.0, self.fill) * step(p.x, wx) * Finish.cover(d, px) * 0.12
|
|
return Finish.over(out, vec4(cb_shade_c() * wash, wash))
|
|
}
|
|
mod.widgets.ValueInput.draw_text.color = #x18140e
|
|
mod.widgets.ValueInput.draw_text.text_style = mod.theme.font_label{font_size: 12.5 line_spacing: 1.0}
|
|
mod.widgets.ValueInput.arrow_color = #x18140e
|
|
mod.widgets.ValueInput.text_input.draw_bg.color = #00000000
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DROP-DOWNS. The button's piece of card, with a triangle filled in marker
|
|
// on its right. Open, the piece is held pressed down. A drop-down in a row
|
|
// of fields stands as tall as they do: both are the row's height, the cut
|
|
// edge under them.
|
|
let cb_dropdown = fn(pos: vec2, size: vec2, o: vec2, px: float, hover: float, held: float, focus: float, dis: float, ins: float) -> vec4 {
|
|
let m = 5.0
|
|
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
var out = cb_button(pos, size, o, px, hover, held, focus, dis, 0.0, 0.0, vec4(4.0, 4.0, 4.0, 4.0), 0.0, m, vec4(0.0, 0.0, 0.0, 0.0), 0.0)
|
|
let th = clamp(size.y * 0.105, 2.0, 3.4)
|
|
let dn = held * (1.0 - dis) * th * 0.7
|
|
let cc = vec2(size.x - 13.0, size.y * 0.5 + dn + 0.5)
|
|
let tri = cb_tri(p, cc, 4.6, 2.8, 1.0) + cb_wob(p, 0.12, vec2(2.0, 2.0))
|
|
return Finish.over(out, cb_fill(tri, p, px, mix(cb_ink_c(), cb_dry_c(), dis), mix(1.0, 0.6, dis)))
|
|
}
|
|
|
|
mod.widgets.DropDownFlat.padding = mod.turtle.Inset{left: 12.0 right: 30.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.DropDown.padding = mod.turtle.Inset{left: 12.0 right: 30.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.DropDownFlat.clip_x = false
|
|
mod.widgets.DropDownFlat.clip_y = false
|
|
mod.widgets.DropDownFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.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 cb_dropdown(self.pos, self.rect_size, self.rect_pos, px, self.hover, max(self.down, self.active), self.focus, self.disabled, 0.0)
|
|
}
|
|
|
|
// The select wears a button of its own, built before this sheet: it is given
|
|
// the drop-down's face, its grown quad, and no clip, here by name.
|
|
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 = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.MultiSelect.face.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.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 cb_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 cb_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 = 30.0
|
|
mod.widgets.MultiSelect.face.padding.right = 30.0
|
|
|
|
// The combo box: the field one types into, its triangle filled in marker at
|
|
// the right end; the triangle sits down a device pixel while held.
|
|
mod.widgets.ComboBox.padding = mod.turtle.Inset{left: 10.0 right: 30.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ComboBox.input.min_height = 0.0
|
|
mod.widgets.ComboBox.clip_x = false
|
|
mod.widgets.ComboBox.clip_y = false
|
|
mod.widgets.ComboBox.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 5.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.ComboBox.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 live = 1.0 - self.disabled
|
|
var out = cb_field(p, size, self.rect_pos, px, self.hover, self.focus, self.disabled, 3.0)
|
|
let held = self.down * self.hover * live
|
|
let cc = vec2(size.x - 14.0, size.y * 0.5 + held * px + 0.5)
|
|
let tri = cb_tri(p, cc, 4.6, 2.8, 1.0) + cb_wob(p, 0.12, vec2(2.0, 2.0))
|
|
return Finish.over(out, cb_fill(tri, p, px, mix(cb_ink_c(), cb_dry_c(), self.disabled), mix(1.0, 0.6, self.disabled)))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SEGMENTS AND TABS. The strip is one piece of card glued on the board,
|
|
// framed in marker; the chosen segment is a piece of black card set into it
|
|
// with a gap of its own, pressed in, its word in cream.
|
|
let cb_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 h = (hi - lo) * 0.5
|
|
let c = lo + h
|
|
return cb_piece(p, c, h, vec4(4.0, 4.0, 4.0, 4.0), px, clamp(h.y * 0.19, 1.8, 3.0), 0.0, 0.0, 0.0, 0.0, vec3(0.0, 0.0, 0.0), 1.0, size * vec2(1.1, 0.7))
|
|
}
|
|
// The chosen cell: black card, its gap round it, pressed in. `lo` and `hi`
|
|
// are the cell's corners in the quad's own points.
|
|
let cb_cell = fn(p: vec2, lo: vec2, hi: vec2, px: float) -> vec4 {
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let d = cb_rbox(p, c, h, 3.0) + cb_wob(p, 0.14, vec2(6.0, 6.0))
|
|
var out = cb_gap(p, d, p.y - c.y, h.y, 1.1, px, 1.0)
|
|
var face = cb_blackcard(p + hi * 1.7, px, 1.0)
|
|
let insh = clamp(Finish.inset(p, c, h, 3.0, vec2(0.0, 1.2), 1.0), 0.0, 1.0)
|
|
face = mix(face, vec3(0.03, 0.025, 0.02), insh * 0.5)
|
|
face = cb_cut_edge(face, d, p.y - c.y, h.y, px, 0.6)
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(out, vec4(face * a, a))
|
|
}
|
|
|
|
mod.widgets.SegmentedControlFlat.segment_height = 26.0
|
|
mod.widgets.SegmentedControlFlat.segment_padding = 10.0
|
|
mod.widgets.SegmentedControlFlat.draw_bg.vertex = fn() {
|
|
let g = cb_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 cb_strip(p, self.rect_size, self.rect_pos, px)
|
|
}
|
|
// The pill is the segment's own box, two points in from the strip; the card
|
|
// stands a point further in, inside the strip's marker frame. Its quad grows
|
|
// two points round it for the gap and its lip.
|
|
mod.widgets.SegmentedControlFlat.draw_pill.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(2.0, 2.0), self.rect_size + vec2(4.0, 4.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(4.0, 4.0)) - vec2(2.0, 2.0)
|
|
let lo = vec2(Finish.snap(o.x + 1.5, px), Finish.snap(o.y + 1.5, px)) - o
|
|
let hi = vec2(Finish.snap(o.x + size.x - 1.5, px), Finish.snap(o.y + size.y - 1.5, px)) - o
|
|
return cb_cell(p, lo, hi, px)
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_text.color = #x18140e
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #xf1e6cf
|
|
mod.widgets.SegmentedControlFlat.palette.on_neutral_container = #xf1e6cf
|
|
mod.widgets.SegmentedControlFlat.draw_text.text_style = mod.theme.font_label{}
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.text_style = mod.theme.font_label{}
|
|
|
|
// The tab strip: every tab a piece of card glued on the board side by side,
|
|
// framed in marker; the tab in use is yellow paper, a hovered one a shade
|
|
// lighter. The close mark is a marker cross; the add mark a small piece of
|
|
// card of its own with a marker plus.
|
|
let cb_tab = fn(pos: vec2, size: vec2, o: vec2, px: float, kind: float, hover: float) -> vec4 {
|
|
let m = 5.0
|
|
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = vec2(Finish.snap(o.x + 2.0, px), Finish.snap(o.y, px)) - o
|
|
let hi = vec2(Finish.snap(o.x + size.x - 2.0, px), Finish.snap(o.y + size.y, px)) - o
|
|
let h = (hi - lo) * 0.5
|
|
let c = lo + h
|
|
return cb_piece(p, c, h, vec4(4.0, 4.0, 4.0, 4.0), px, clamp(h.y * 0.19, 1.8, 3.0), hover, 0.0, 0.0, kind, vec3(0.0, 0.0, 0.0), 1.0, size * vec2(0.9, 1.3))
|
|
}
|
|
mod.widgets.Tabs.height = 30.0
|
|
mod.widgets.Tabs.clip_x = false
|
|
mod.widgets.Tabs.clip_y = false
|
|
mod.widgets.Tabs.draw_bg.pixel = fn() {
|
|
return vec4(0.0, 0.0, 0.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab.vertex = fn() {
|
|
let g = cb_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_tab.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return cb_tab(self.pos, self.rect_size, self.rect_pos, px, 0.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_hover.vertex = fn() {
|
|
let g = cb_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_tab_hover.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return cb_tab(self.pos, self.rect_size, self.rect_pos, px, 0.0, 1.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_active.vertex = fn() {
|
|
let g = cb_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_tab_active.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return cb_tab(self.pos, self.rect_size, self.rect_pos, px, 9.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_sep.pixel = fn() {
|
|
return vec4(0.0, 0.0, 0.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_mark.color = #x18140e
|
|
mod.widgets.Tabs.draw_mark.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 6.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.Tabs.draw_mark.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let m = 6.0
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let c = self.rect_size * 0.5
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
if self.plus > 0.5 {
|
|
let bh = self.rect_size * 0.5 + vec2(2.0, 2.0)
|
|
out = cb_piece(p, c, bh, vec4(3.0, 3.0, 3.0, 3.0), px, 2.0, 0.0, 0.0, 0.0, 0.0, vec3(0.0, 0.0, 0.0), 1.0, vec2(23.0, 7.0))
|
|
}
|
|
let a = mix(0.7853981634, 0.0, self.plus)
|
|
let r = mix(3.2, 4.4, self.plus)
|
|
let u = vec2(cos(a), sin(a)) * r
|
|
let v = vec2(0.0 - sin(a), cos(a)) * r
|
|
let dd = min(cb_seg(p, c - u, c + u), cb_seg(p, c - v, c + v))
|
|
return Finish.over(out, cb_stroke(dd, mix(0.65, 0.85, self.plus), p, px, self.color.rgb, self.color.a))
|
|
}
|
|
mod.widgets.Tabs.draw_text.color = #x18140e
|
|
mod.widgets.Tabs.draw_text_active.color = #100c08
|
|
mod.widgets.Tabs.draw_text.text_style = mod.theme.font_label{font_size: 11.5}
|
|
mod.widgets.Tabs.draw_text_active.text_style = mod.theme.font_label{font_size: 11.5}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// PROGRESS. A thin marker line the length of the bar, and over the value a
|
|
// broad stroke of marker (a coloured marker for an intent). The unknown
|
|
// value is a row of short slanted strokes along the whole line: a face may
|
|
// not run a clock.
|
|
mod.widgets.ProgressBarFlat.height = 8.0
|
|
mod.widgets.ProgressBarFlat.draw_bg.thickness = uniform(7.0)
|
|
mod.widgets.ProgressBarFlat.clip_x = false
|
|
mod.widgets.ProgressBarFlat.clip_y = false
|
|
mod.widgets.ProgressBarFlat.draw_bg.vertex = fn() {
|
|
let g = cb_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 cy = size.y * 0.5
|
|
let x0 = size.x * self.start
|
|
let x1 = size.x * self.end
|
|
let dis = step(4.5, self.intent)
|
|
var ink = cb_ink_c()
|
|
if self.intent > 0.5 {
|
|
ink = mix(self.fill_color().rgb * 0.85, cb_ink_c(), 0.25)
|
|
}
|
|
ink = mix(ink, cb_dry_c(), dis)
|
|
let wy = cy + (Finish.noise(vec2(p.x * 0.05, 7.0)) - 0.5) * 0.5
|
|
let hw = th * 0.5 - 0.3
|
|
var out = cb_stroke(cb_seg(p, vec2(x0 + 0.7, wy), vec2(x1 - 0.7, wy)), 0.7, p, px, cb_ink_c(), 0.75 * self.track)
|
|
if self.value < 0.0 {
|
|
let u = (p.x - x0) / 6.0
|
|
let k = floor(u)
|
|
let sx = x0 + (k + 0.5) * 6.0
|
|
let ds = cb_seg(p, vec2(sx - 1.6, wy + hw), vec2(sx + 1.6, wy - hw))
|
|
let inr = step(x0 + 1.0, sx) * step(sx, x1 - 1.0)
|
|
out = Finish.over(out, cb_stroke(ds, 0.8, p, px, ink, inr))
|
|
} else {
|
|
let v1 = x0 + (x1 - x0) * clamp(self.value, 0.0, 1.0)
|
|
if v1 - x0 > 0.5 {
|
|
let a = x0 + hw
|
|
let b = max(v1 - hw, a)
|
|
out = Finish.over(out, cb_stroke(cb_seg(p, vec2(a, wy), vec2(b, wy)), hw, p, px, ink, 1.0))
|
|
}
|
|
}
|
|
return out * self.opacity
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LAMPS: an LED pushed through a hole punched in the card. The emitter is
|
|
// the light: at full current its middle burns toward white and its rim is
|
|
// its most saturated colour; at lower current the middle is no longer
|
|
// white, the colour deeper, the glow smaller; out, it is dark tinted glass,
|
|
// its colour barely there. Round it the punched hole: a dark socket, its
|
|
// upper wall in shade and a catch light on its lower rim, and round that a
|
|
// ring of torn card fibres the punch pushed down. Over the lens one crisp
|
|
// highlight of a dome. Its light is added, never painted: a tight hot halo
|
|
// and a short bloom in its own hue, screened over the card and gone in about
|
|
// two radii.
|
|
let cb_led_add = fn(res: vec4, light: vec3, ground: vec3) -> vec4 {
|
|
let est = res.rgb + ground * (1.0 - res.a)
|
|
let add = (vec3(1.0, 1.0, 1.0) - exp(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 cb_led = fn(p: vec2, c: vec2, hb: vec2, lit: float, ink: vec3, vivid: float, px: float, 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.42 * rt, 1.0, 2.4)
|
|
let ds = d - bw
|
|
let L = clamp(lit, 0.0, 1.0)
|
|
let b = ink / max(max(ink.x, max(ink.y, ink.z)), 0.01)
|
|
let mn = min(b.x, min(b.y, b.z))
|
|
let pure = (b - vec3(mn, mn, mn)) / max(1.0 - mn, 0.001)
|
|
let hue = mix(b, pure, clamp(vivid, 0.0, 1.0) * 0.72 * smoothstep(0.03, 0.12, 1.0 - mn))
|
|
let pw = L * sqrt(L)
|
|
let white = vec3(1.0, 1.0, 1.0)
|
|
let ground = cb_kraft_c()
|
|
var res = vec4(0.0, 0.0, 0.0, 0.0)
|
|
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 punched edge: torn fibres pushed down round the hole
|
|
let ang = atan2(q.y, q.x)
|
|
let tear = (Finish.noise(vec2(ang * 3.0, rt)) - 0.5) * 0.9 + (Finish.hash(vec2(floor(ang * 9.0), rt)) - 0.5) * 0.5
|
|
let dr = ds - 1.2 - tear * 0.7
|
|
let rag = Finish.cover(dr, px) * (1.0 - Finish.cover(ds, px))
|
|
let ragc = mix(cb_liner_c() * 0.95, cb_kraft_c() * 0.58, smoothstep(-0.7, 0.7, 0.0 - nrm.y))
|
|
res = Finish.over(res, vec4(ragc * rag * 0.9, rag * 0.9))
|
|
// the socket: the hole's wall, shade above and a catch light below
|
|
let a_s = Finish.cover(ds, px)
|
|
let gy = clamp(nrm.y * t, -1.0, 1.0)
|
|
var sc = vec3(0.14, 0.09, 0.05) * mix(0.55, 1.2, smoothstep(-0.9, 0.9, gy))
|
|
sc = mix(sc, cb_liner_c() * 0.8, Finish.band(ds, 0.0, max(px, 0.45), px) * smoothstep(0.2, 0.95, nrm.y) * 0.7)
|
|
res = Finish.over(res, vec4(sc * a_s, a_s))
|
|
// the light: a tight hot halo and a short bloom in its hue
|
|
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(0.55 * room, room, xl)
|
|
res = cb_led_add(res, mix(hue, white, 0.15 * pw) * halo * pw * hwin, ground)
|
|
}
|
|
// the bead: a glossy dome of coloured glass
|
|
let top = smoothstep(-1.0, 0.6, q.y)
|
|
let pool = exp(0.0 - (q.y - 0.55) * (q.y - 0.55) / 0.09 - q.x * q.x / 0.30)
|
|
var on = mix(hue * mix(0.55, 0.8, L), mix(b, hue, 0.5), top)
|
|
on = mix(on, min(mix(b, hue, 0.4) + vec3(0.35, 0.35, 0.35), white), pool * 0.5)
|
|
on = mix(on, vec3(1.0, 0.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)
|
|
let offc = mix(vec3(0.085, 0.075, 0.07), b * 0.45, 0.24)
|
|
var off = mix(offc * 0.62, offc * 1.08, top)
|
|
off = mix(off, min(offc + vec3(0.18, 0.18, 0.18), white), pool * 0.35)
|
|
off = off * mix(1.0, 0.75, smoothstep(0.75, 1.0, t))
|
|
let catchl = smoothstep(0.62, 0.9, t) * (1.0 - smoothstep(0.9, 1.0, t)) * smoothstep(0.2, 0.9, nrm.y)
|
|
off = off + vec3(0.30, 0.30, 0.32) * catchl
|
|
var col = mix(off, min(on, white), smoothstep(0.0, 0.35, L))
|
|
let e = length(vec2(q.x / 0.62, (q.y + 0.46) / 0.30))
|
|
let spec = (1.0 - smoothstep(0.55, 1.0, e)) * mix(1.0, 0.3, clamp((q.y + 0.76) / 0.6, 0.0, 1.0))
|
|
col = mix(col, white, spec * mix(0.8, 0.55, L) * smoothstep(0.9, 2.2, rt))
|
|
col = mix(col, col * 0.7, Finish.band(d, 0.0, px * 0.6, px) * 0.6 * (1.0 - smoothstep(0.0, 0.25, L)))
|
|
col = min(col, white)
|
|
let a_l = Finish.cover(d, px)
|
|
return Finish.over(res, vec4(col * a_l, a_l))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LEVEL METERS. A row of short marker dashes, the lit ones full ink and the
|
|
// last tenth in red marker, the unlit ones only outlined in dry pencil; the
|
|
// hold is a dash drawn across; the over lamp an LED in a punched hole. On a
|
|
// display (a meter with no lamp lying along a screen) the dashes are the
|
|
// display's yellow paint, the unlit ones a ghost of it.
|
|
mod.widgets.LevelMeter.height = 9.0
|
|
mod.widgets.LevelMeterColumn.width = 9.0
|
|
mod.widgets.LevelMeter.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 3.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
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 + vec2(6.0, 6.0)) - vec2(3.0, 3.0)
|
|
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 screen = (1.0 - step(0.5, self.lamp)) * (1.0 - step(0.5, vert))
|
|
let th = min(7.0, across - 1.0)
|
|
let lamp_room = self.lamp * (th + 6.0)
|
|
let bar = max(4.0, along - lamp_room)
|
|
let pitch = mix(5.0, 3.5, screen)
|
|
let cw = mix(2.6, 2.0, screen)
|
|
let n = max(floor((bar - 1.0) / pitch), 1.0)
|
|
let pad = (bar - n * pitch) * 0.5
|
|
let k = clamp(floor((a - pad) / pitch), 0.0, n - 1.0)
|
|
let ca = pad + (k + 0.5) * pitch
|
|
let share = (k + 0.5) / n
|
|
let lit = step(share, self.level + 0.5 / n) * step(0.001, self.level)
|
|
let q = vec2(a, b)
|
|
let hh = th * 0.5 - mix(0.5, 1.0, screen)
|
|
let dd = cb_rbox(q, vec2(ca, across * 0.5), vec2(cw * 0.5, hh), cw * 0.5) + cb_wob(p, 0.12, vec2(7.0, 2.0))
|
|
let hot = step(0.9, share)
|
|
var ink = mix(cb_ink_c(), cb_redink_c(), hot)
|
|
ink = mix(ink, vec3(0.949, 0.788, 0.306), screen)
|
|
var out = cb_fill(dd, p, px, ink, lit * mix(1.0, 0.95, screen))
|
|
let pencil = mix(vec3(0.30, 0.24, 0.17), vec3(0.949, 0.788, 0.306), screen)
|
|
out = Finish.over(out, cb_stroke(abs(dd), 0.38, p, px, pencil, (1.0 - lit) * mix(0.45, 0.0, screen)))
|
|
out = Finish.over(out, cb_fill(dd, p, px, pencil, (1.0 - lit) * screen * 0.16))
|
|
if self.hold > 0.002 {
|
|
let at = pad + n * pitch * self.hold
|
|
let hd = cb_seg(q, vec2(at, across * 0.5 - hh - 1.0), vec2(at, across * 0.5 + hh + 1.0))
|
|
out = Finish.over(out, cb_stroke(hd, 0.7, p, px, ink, 1.0))
|
|
}
|
|
if self.lamp > 0.5 {
|
|
let la = bar + 3.0 + th * 0.5
|
|
let lc = vec2(mix(la, across * 0.5, vert), mix(across * 0.5, self.rect_size.y - la, vert))
|
|
out = Finish.over(out, cb_led(p, lc, vec2(2.4, 2.4), step(0.5, self.over), vec3(0.95, 0.20, 0.12), 1.0, px, 2.2))
|
|
}
|
|
return out * self.opacity
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SCROLL BARS. A narrow slot cut in the board, and riding in it a strip of
|
|
// board glued on, its thin shadow under 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 along = mix(self.rect_size.x, self.rect_size.y, vert)
|
|
let across = mix(self.rect_size.y, self.rect_size.x, vert)
|
|
let sc = self.rect_size * 0.5
|
|
let sh = mix(vec2(along * 0.5 - 1.0, 1.4), vec2(1.4, along * 0.5 - 1.0), vert)
|
|
let sd = cb_rbox(p, sc, sh, 1.4)
|
|
let sa = Finish.cover(sd, px) * 0.55
|
|
var out = vec4(vec3(0.10, 0.065, 0.035) * sa, sa)
|
|
let t0 = along * self.norm_scroll
|
|
let tl = along * self.norm_handle
|
|
let tc1 = t0 + tl * 0.5
|
|
let tc = mix(vec2(tc1, sc.y - 0.6), vec2(sc.x, tc1 - 0.6), vert)
|
|
let th = mix(vec2(max(tl * 0.5 - 1.0, 4.0), 3.2), vec2(3.2, max(tl * 0.5 - 1.0, 4.0)), vert)
|
|
out = Finish.over(out, cb_under(p, tc, th, 3.2, px, 1.1, cb_kraft_c(), 0.2))
|
|
let td = cb_rbox(p, tc, th, 3.2)
|
|
var face = cb_kraft(p + vec2(13.0, 3.0), px, Finish.lift(cb_kraft_c(), 0.08), 1.0)
|
|
face = Finish.lift(face, 0.10 * self.hover - 0.06 * self.drag)
|
|
face = cb_cut_edge(face, td, p.y - tc.y, th.y, px, 1.0)
|
|
let ta = Finish.cover(td, px)
|
|
out = Finish.over(out, vec4(face * ta, ta))
|
|
return out * self.opacity
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// VIEWS AND PANELS. A raised panel is a piece of board glued on the board:
|
|
// its shadow, its cut edge along its foot with the flutes in it, the pale
|
|
// cut line round its face. The sunken panel is a window cut through the
|
|
// board: the cut wall along its top seen side on, the floor the deeper
|
|
// board beyond it in the wall's shade, a pale lip where the window's liner
|
|
// was cut along its foot.
|
|
let cb_plate = fn(p: vec2, size: vec2, o: vec2, px: float, r: float, sunken: float) -> vec4 {
|
|
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
|
|
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o
|
|
let h = (hi - lo) * 0.5
|
|
let c = lo + h
|
|
let seed = size * vec2(0.73, 1.19)
|
|
let d = cb_rbox(p, c, h, r) + cb_wob(p, 0.22, seed * 0.01)
|
|
if sunken > 0.5 {
|
|
let th = 3.0
|
|
let g = cb_gap(p, d + 0.01, p.y - c.y, h.y, 0.01, px, 1.0)
|
|
let yin = p.y - lo.y
|
|
let wall = cb_flute(p.x + seed.x, clamp(yin / th, 0.0, 1.0), th, px, cb_kraft_c())
|
|
var flr = cb_kraft(p + seed, px, cb_kraft_c() * 0.86, 1.0)
|
|
let ish = clamp(Finish.inset(p, c + vec2(0.0, th), h, r, vec2(0.0, 1.6), 2.0), 0.0, 1.0)
|
|
flr = mix(flr, cb_shade_c(), ish * 0.40)
|
|
let side = clamp(Finish.inset(p, c, h, r, vec2(0.0, 0.0), 0.6), 0.0, 1.0)
|
|
flr = mix(flr, cb_shade_c(), side * 0.35)
|
|
var col = mix(flr, wall, (1.0 - step(th, yin)) * step(0.0, yin))
|
|
let a = Finish.cover(d, px)
|
|
let lip = Finish.ring_out(d, 0.0, 1.2 * px, px) * smoothstep(0.0, h.y, p.y - c.y) * 0.8
|
|
var out = vec4(cb_liner_c() * lip, lip)
|
|
out = Finish.over(out, vec4(col * a, a))
|
|
return Finish.over(g, out)
|
|
}
|
|
var out = cb_under(p, c, h, r, px, 2.6, cb_kraft_c(), 0.6)
|
|
var face = cb_kraft(p + seed, px, Finish.lift(cb_kraft_c(), 0.05), 1.0)
|
|
// the flutes under the liner show through it as the faintest ribs
|
|
face = face * (1.0 + 0.016 * sin((p.y + seed.y) / 4.4 * 6.2831853) * (0.6 + 0.4 * Finish.noise(p * 0.05)))
|
|
face = cb_cut_edge(face, d, p.y - c.y, h.y, px, 1.0)
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(out, vec4(face * a, a))
|
|
}
|
|
|
|
mod.widgets.PanelView.clip_x = false
|
|
mod.widgets.PanelView.clip_y = false
|
|
mod.widgets.PanelView.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 8.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 = 8.0
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
return cb_plate(p, self.rect_size, self.rect_pos, px, 5.0, self.sunken)
|
|
}
|
|
|
|
// A STRIP OF MASKING TAPE: crepe paper, cream and a little translucent, its
|
|
// fine crinkles running across it, its two ends torn ragged. `c` its
|
|
// middle, `ang` its turn, `hl` and `hw` half its length and width.
|
|
let cb_tape = fn(p: vec2, c: vec2, ang: float, hl: float, hw: float, px: float, seed: float) -> vec4 {
|
|
let cs = cos(ang)
|
|
let sn = sin(ang)
|
|
let q = vec2((p.x - c.x) * cs + (p.y - c.y) * sn, (p.y - c.y) * cs - (p.x - c.x) * sn)
|
|
let tear = (Finish.noise(vec2(q.y * 1.7, seed + step(0.0, q.x) * 9.0)) - 0.5) * 1.8 + (Finish.hash(vec2(floor(q.y * 2.5), seed)) - 0.5) * 0.6
|
|
let de = abs(q.x) - hl - tear
|
|
let ds = abs(q.y) - hw - (Finish.noise(vec2(q.x * 0.6, seed + 3.0)) - 0.5) * 0.3
|
|
let d = max(de, ds)
|
|
let crepe = 1.0 + (Finish.noise(vec2(q.x * 2.4, q.y * 0.25 + seed)) - 0.5) * 0.08 + Finish.grain(p / px, 0.012)
|
|
let col = vec3(0.925, 0.870, 0.725) * crepe
|
|
let a = Finish.cover(d, px) * 0.84
|
|
let edge = Finish.ring_out(d, 0.0, 0.8, px) * 0.12
|
|
var out = vec4(cb_shade_c() * edge, edge)
|
|
return Finish.over(out, vec4(col * a, a))
|
|
}
|
|
|
|
// The card is a sheet of cream paper glued on, held down at its top right
|
|
// corner by a strip of masking tape laid across it; it lifts a little under
|
|
// the pointer.
|
|
mod.widgets.Card.radius = 3.0
|
|
mod.widgets.Card.clip_x = false
|
|
mod.widgets.Card.clip_y = false
|
|
mod.widgets.Card.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 8.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 = 8.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 d = cb_rbox(p, c, h, 2.0) + cb_wob(p, 0.2, vec2(5.0, 1.0))
|
|
let lift = 0.4 + 0.4 * self.hover - 0.3 * self.down
|
|
var out = cb_under(p, c, h, 2.0, px, 0.0, cb_kraft_c(), lift)
|
|
var face = cb_paper(p + size * 0.37, px, cb_cream_c(), 1.0)
|
|
face = face * (1.02 - 0.05 * smoothstep(0.0 - h.y, h.y, p.y - c.y))
|
|
face = Finish.lift(face, 0.04 * self.hover)
|
|
face = mix(face, face * 0.86, Finish.band(d, 0.0, px, px) * 0.6)
|
|
let a = Finish.cover(d, px)
|
|
out = Finish.over(out, vec4(face * a, a))
|
|
return Finish.over(out, cb_tape(p, vec2(hi.x - 12.0, lo.y + 6.0), 0.62, 13.0, 5.0, px, 3.0))
|
|
}
|
|
|
|
// The plain rounded view keeps the colour its caller gives it, laid as a
|
|
// sheet of paper of that colour; the edge a snapped line at the 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 = cb_rbox(p, c, h, max(self.border_radius, 1.0))
|
|
var col = cb_paper(p + self.rect_size * 0.29, px, fill.rgb, step(0.9, fill.a))
|
|
if self.border_size > 0.0 {
|
|
col = mix(col, self.border_color.rgb, Finish.band(d, 0.0, max(self.border_size, px), px) * self.border_color.a)
|
|
}
|
|
let a = Finish.cover(d, px) * fill.a
|
|
return vec4(col * a, a)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LISTS. Rows of 22 points of words on the board; a hovered row is
|
|
// underlined in faint pencil; the chosen row is a strip of yellow paper
|
|
// glued on, its thin shadow under it.
|
|
mod.widgets.ListItemOne.height = 22.0
|
|
mod.widgets.ListItem.padding = mod.turtle.Inset{left: 10.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
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
if self.selected > 0.5 {
|
|
let h = vec2((hi.x - lo.x) * 0.5 - 6.0, (hi.y - lo.y) * 0.5 - 2.0)
|
|
let c = vec2(lo.x + h.x + 2.0, (lo.y + hi.y) * 0.5 - 0.6)
|
|
let d = cb_rbox(p, c, h, 1.0) + cb_wob(p, 0.2, vec2(8.0, 3.0))
|
|
out = cb_under(p, c, h, 1.0, px, 0.0, cb_kraft_c(), 0.1)
|
|
var face = cb_paper(p + vec2(3.0, 41.0), px, cb_yellow_c(), 1.0)
|
|
face = mix(face, face * 0.84, Finish.band(d, 0.0, px, px) * 0.6)
|
|
let a = Finish.cover(d, px)
|
|
out = Finish.over(out, vec4(face * a, a))
|
|
}
|
|
let uy = hi.y - 2.5 + (Finish.noise(vec2(p.x * 0.07, 5.0)) - 0.5) * 0.6
|
|
let du = cb_seg(p, vec2(lo.x + 8.0, uy), vec2(hi.x - 14.0, uy))
|
|
out = Finish.over(out, cb_stroke(du, 0.4, p, px, vec3(0.30, 0.24, 0.17), (0.45 * self.hover + 0.25 * self.down) * live * (1.0 - self.selected)))
|
|
return out
|
|
}
|
|
mod.widgets.ListItem.draw_text.color = #x18140e
|
|
|
|
// The open list of a drop-down: a sheet of cream paper with its shadow;
|
|
// the row under the pointer is swept with yellow highlighter, the chosen
|
|
// row carries a marker tick.
|
|
mod.widgets.PopupMenuFlat.clip_x = false
|
|
mod.widgets.PopupMenuFlat.clip_y = false
|
|
mod.widgets.PopupMenuFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 6.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 = 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 lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
|
|
let hi = vec2(Finish.snap(o.x + size.x, px), Finish.snap(o.y + size.y, px)) - o
|
|
let h = (hi - lo) * 0.5
|
|
let c = lo + h
|
|
let d = cb_rbox(p, c, h, 2.0) + cb_wob(p, 0.2, vec2(1.0, 1.0))
|
|
var out = cb_under(p, c, h, 2.0, px, 0.0, cb_kraft_c(), 1.0)
|
|
var face = cb_paper(p + vec2(17.0, 9.0), px, cb_cream_c(), 1.0)
|
|
face = mix(face, face * 0.82, Finish.band(d, 0.0, px, px) * 0.7)
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(out, vec4(face * a, a))
|
|
}
|
|
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 live = 1.0 - self.disabled
|
|
let hy = c.y + (Finish.noise(vec2(p.x * 0.06, 2.0)) - 0.5) * 0.8
|
|
let h = vec2(c.x - 3.0, c.y - 2.0)
|
|
let d = cb_rbox(p, vec2(c.x, hy), h, h.y)
|
|
let wa = Finish.cover(d, px) * self.hover * live * 0.55
|
|
var out = vec4(cb_yellow_c() * wa, wa)
|
|
let m0 = vec2(6.0, c.y)
|
|
let m1 = vec2(8.6, c.y + 2.8)
|
|
let m2 = vec2(13.0, c.y - 3.6)
|
|
let mk = min(cb_seg(p, m0, m1), cb_seg(p, m1, m2))
|
|
return Finish.over(out, cb_stroke(mk, 0.8, p, px, cb_ink_c(), self.active))
|
|
}
|
|
|
|
// The dock's tabs: the tab in use is a piece of board glued on, rounded at
|
|
// the top and open at the foot where it joins the panel below; the others
|
|
// take a breath of pencil under the pointer.
|
|
mod.widgets.Tab.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 3.0 bottom: 3.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, 6.0)
|
|
let h = (hi - lo) * 0.5
|
|
let c = lo + h
|
|
let d = cb_rbox(p, c, h, 4.0)
|
|
var face = cb_kraft(p + vec2(7.0, 7.0), px, Finish.lift(cb_kraft_c(), 0.05), 1.0)
|
|
face = cb_cut_edge(face, d, p.y - c.y, h.y, px, 1.0)
|
|
let a = Finish.cover(d, px) * self.active
|
|
let wash = Finish.cover(d, px) * 0.08 * self.hover * (1.0 - self.active)
|
|
return Finish.over(vec4(cb_shade_c() * wash, wash), vec4(face * a, a))
|
|
}
|
|
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 = self.rect_size.y - 1.0 + (Finish.noise(vec2(p.x * 0.05, 1.0)) - 0.5) * 0.4
|
|
return cb_stroke(abs(p.y - yb), 0.5, p, px, cb_ink_c(), 0.7)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CHIPS AND BADGES. A chip is a small piece in its window (paper of its
|
|
// colour when it has one, framed in marker when outlined, bare when a
|
|
// ghost); a badge is a slip of paper framed in marker with its count.
|
|
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 = cb_grow(self.rect_pos, self.rect_size, 5.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 = 5.0
|
|
let ch = min(24.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)
|
|
let mx = max(self.color.r, max(self.color.g, self.color.b))
|
|
let mn = min(self.color.r, min(self.color.g, self.color.b))
|
|
let neutral = 1.0 - step(0.25, (mx - mn) / max(mx, 0.01))
|
|
let light_ink = step(0.6, Finish.lum(self.ink.rgb))
|
|
let kind = mix(mix(mix(6.0, 5.0, step(0.01, self.border_size)), mix(7.0, 0.0, neutral), has_fill), 1.0, light_ink)
|
|
let tint = vec4(self.color.rgb, has_fill)
|
|
// light words ask for a dark ground: they are written on black card
|
|
return cb_button(pp, sz, self.rect_pos + vec2(0.0, dy), px, self.hover, self.down, self.keyed, 0.0, 0.0, 0.0, vec4(4.0, 4.0, 4.0, 4.0), kind, m, tint, 0.0)
|
|
}
|
|
mod.widgets.BadgePalette.neutral = #xe6d8bc
|
|
mod.widgets.BadgePalette.on_neutral = #x18140e
|
|
mod.widgets.BadgeFlat.draw_text.text_style = mod.theme.font_label{font_size: 10.0}
|
|
mod.widgets.Badge.draw_text.text_style = mod.theme.font_label{font_size: 10.0}
|
|
mod.widgets.BadgeFlat.clip_x = false
|
|
mod.widgets.BadgeFlat.clip_y = false
|
|
mod.widgets.BadgeFlat.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 4.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.BadgeFlat.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 bh = min(18.0, self.rect_size.y - 1.0)
|
|
let y0 = (self.rect_size.y - bh) * 0.5
|
|
let lo = vec2(1.0, y0)
|
|
let hi = vec2(self.rect_size.x - 1.0, y0 + bh)
|
|
let h = (hi - lo) * 0.5
|
|
let c = lo + h
|
|
let r = mix(2.5, h.y, step(self.rect_size.x, self.rect_size.y + 0.5) * step(self.rect_size.y, 12.5))
|
|
let d = cb_rbox(p, c, h, r) + cb_wob(p, 0.18, vec2(2.0, 8.0))
|
|
var out = cb_under(p, c, h, r, px, 0.0, cb_kraft_c(), 0.0)
|
|
let face = cb_paper(p + vec2(9.0, 4.0), px, self.color.rgb, 1.0)
|
|
let a = Finish.cover(d, px)
|
|
out = Finish.over(out, vec4(face * a, a))
|
|
out = Finish.over(out, cb_stroke(abs(d + 0.7), 0.65, p, px, cb_ink_c(), 1.0))
|
|
return out * self.color.a
|
|
}
|
|
|
|
// The spinner keeps its stock turning face (it runs on the pass clock, which
|
|
// a face may not read), in marker with a dry pencil track ring.
|
|
mod.widgets.SpinnerFlat.draw_bg.color = #x18140e
|
|
mod.widgets.SpinnerFlat.draw_bg.stroke_width = 3.0
|
|
mod.widgets.SpinnerFlat.draw_bg.track_alpha = 0.22
|
|
|
|
// Rules between groups: a line of pencil drawn by hand.
|
|
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 = self.rect_size.y * 0.5 + (Finish.noise(vec2(p.x * 0.04, 3.0)) - 0.5) * 0.8
|
|
return cb_stroke(abs(p.y - y), 0.45, p, px, vec3(0.30, 0.24, 0.17), 0.6)
|
|
}
|
|
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 = self.rect_size.x * 0.5 + (Finish.noise(vec2(3.0, p.y * 0.04)) - 0.5) * 0.8
|
|
return cb_stroke(abs(p.x - x), 0.45, p, px, vec3(0.30, 0.24, 0.17), 0.6)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// WORDS IN MARKER. Every word is drawn in felt marker, and the felt bleeds a
|
|
// little into the board's fibres: the glyph's edge carries a soft ink that
|
|
// is the glyph read again a device pixel away on each diagonal. Pale ink
|
|
// (the cream paint pen on black card, the figures on a display) keeps its
|
|
// edge, so nothing pale grows a halo. Written once, handed to every label.
|
|
let cb_words = fn() {
|
|
if self.texture_index > 0.5 && self.texture_index < 1.5 {
|
|
return self.sample_text_pixel()
|
|
}
|
|
if self.texture_index > 2.5 {
|
|
return self.sample_text_pixel()
|
|
}
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let e1 = dFdx(self.t.xy) + dFdy(self.t.xy)
|
|
let e2 = dFdx(self.t.xy) - dFdy(self.t.xy)
|
|
var a0 = 0.0
|
|
var a1 = 0.0
|
|
var a2 = 0.0
|
|
var a3 = 0.0
|
|
var a4 = 0.0
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let ht = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
a0 = self.sdf(scale, clamp(self.t.xy, self.t_min + ht, self.t_max - ht), c)
|
|
a1 = self.sdf(scale, clamp(self.t.xy + e1, self.t_min + ht, self.t_max - ht), c)
|
|
a2 = self.sdf(scale, clamp(self.t.xy - e1, self.t_min + ht, self.t_max - ht), c)
|
|
a3 = self.sdf(scale, clamp(self.t.xy + e2, self.t_min + ht, self.t_max - ht), c)
|
|
a4 = self.sdf(scale, clamp(self.t.xy - e2, self.t_min + ht, self.t_max - ht), c)
|
|
} else {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let ht = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
a0 = self.msdf(scale, clamp(self.t.xy, self.t_min + ht, self.t_max - ht), c)
|
|
a1 = self.msdf(scale, clamp(self.t.xy + e1, self.t_min + ht, self.t_max - ht), c)
|
|
a2 = self.msdf(scale, clamp(self.t.xy - e1, self.t_min + ht, self.t_max - ht), c)
|
|
a3 = self.msdf(scale, clamp(self.t.xy + e2, self.t_min + ht, self.t_max - ht), c)
|
|
a4 = self.msdf(scale, clamp(self.t.xy - e2, self.t_min + ht, self.t_max - ht), c)
|
|
}
|
|
let pale = step(0.45, dot(c.rgb, vec3(0.299, 0.587, 0.114)))
|
|
let bleed = (a1 + a2 + a3 + a4) * 0.25 * 0.30 * (1.0 - pale)
|
|
let a = clamp(a0 + bleed * (1.0 - a0), 0.0, 1.0) * c.a
|
|
return vec4(c.rgb * a, a)
|
|
}
|
|
mod.widgets.Label.draw_text.pixel = cb_words
|
|
mod.widgets.H1.draw_text.pixel = cb_words
|
|
mod.widgets.H2.draw_text.pixel = cb_words
|
|
mod.widgets.H3.draw_text.pixel = cb_words
|
|
mod.widgets.H4.draw_text.pixel = cb_words
|
|
mod.widgets.H5.draw_text.pixel = cb_words
|
|
mod.widgets.H6.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonFlat.draw_text.pixel = cb_words
|
|
mod.widgets.Button.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonFlatter.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonPrimary.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonSecondary.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonTertiary.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonDanger.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonOutline.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonXs.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonSm.draw_text.pixel = cb_words
|
|
mod.widgets.ButtonLg.draw_text.pixel = cb_words
|
|
mod.widgets.CheckBoxFlat.draw_text.pixel = cb_words
|
|
mod.widgets.CheckBox.draw_text.pixel = cb_words
|
|
mod.widgets.ToggleFlat.draw_text.pixel = cb_words
|
|
mod.widgets.Toggle.draw_text.pixel = cb_words
|
|
mod.widgets.RadioButtonFlat.draw_text.pixel = cb_words
|
|
mod.widgets.RadioButton.draw_text.pixel = cb_words
|
|
mod.widgets.RadioButtonTabFlat.draw_text.pixel = cb_words
|
|
mod.widgets.RadioButtonTab.draw_text.pixel = cb_words
|
|
mod.widgets.SliderFlat.draw_text.pixel = cb_words
|
|
mod.widgets.Slider.draw_text.pixel = cb_words
|
|
mod.widgets.SliderMinimal.draw_text.pixel = cb_words
|
|
mod.widgets.SliderRoundFlat.draw_text.pixel = cb_words
|
|
mod.widgets.SliderRound.draw_text.pixel = cb_words
|
|
mod.widgets.RotaryFlat.draw_text.pixel = cb_words
|
|
mod.widgets.Rotary.draw_text.pixel = cb_words
|
|
mod.widgets.RotaryKnob.draw_text.pixel = cb_words
|
|
mod.widgets.DropDownFlat.draw_text.pixel = cb_words
|
|
mod.widgets.DropDown.draw_text.pixel = cb_words
|
|
mod.widgets.SegmentedControlFlat.draw_text.pixel = cb_words
|
|
mod.widgets.SegmentedControl.draw_text.pixel = cb_words
|
|
mod.widgets.Tabs.draw_text.pixel = cb_words
|
|
mod.widgets.ListItem.draw_text.pixel = cb_words
|
|
mod.widgets.ListItemOne.draw_text.pixel = cb_words
|
|
mod.widgets.ChipFlat.draw_text.pixel = cb_words
|
|
mod.widgets.Chip.draw_text.pixel = cb_words
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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 BOARD under everything: the window's own ground, kraft over its whole
|
|
// face and calm under the content. The heavy detail is at the frame: the
|
|
// board's edges a little rubbed and darker, its cut edge showing along the
|
|
// window's foot with the flutes in it, and a strip of brown packing tape
|
|
// run along the top where the caption is, glossy, with the fine streaks of
|
|
// its stretched film and its long edges a little uneven.
|
|
let cb_packing_tape = fn(p: vec2, sz: vec2, px: float) -> vec4 {
|
|
let top = 1.5 + (Finish.noise(vec2(p.x * 0.03, 1.0)) - 0.5) * 0.8
|
|
let bot = 27.0 + (Finish.noise(vec2(p.x * 0.025, 7.0)) - 0.5) * 1.2
|
|
let d = max(top - p.y, p.y - bot)
|
|
let a = Finish.cover(d, px)
|
|
let v = clamp((p.y - top) / max(bot - top, 1.0), 0.0, 1.0)
|
|
let streak = (Finish.noise(vec2(p.x * 0.012, p.y * 2.2)) - 0.5) * 0.10 + (Finish.noise(vec2(p.x * 0.004, p.y * 0.7 + 4.0)) - 0.5) * 0.08
|
|
var col = vec3(0.560, 0.365, 0.170) * (1.0 + streak)
|
|
let sheen = smoothstep(0.08, 0.3, v) * (1.0 - smoothstep(0.32, 0.55, v)) * (0.6 + 0.4 * Finish.noise(vec2(p.x * 0.01, 3.0)))
|
|
col = mix(col, vec3(0.98, 0.90, 0.76), sheen * 0.22)
|
|
col = mix(col, col * 0.75, Finish.band(d, 0.0, 1.5 * px, px))
|
|
let ta = a * 0.62
|
|
var out = vec4(col * ta, ta)
|
|
let sh = Finish.ring_out(d, 0.0, 1.0, px) * step(bot - 2.0, p.y) * 0.18
|
|
return Finish.over(vec4(cb_shade_c() * sh, sh), out)
|
|
}
|
|
mod.widgets.Window.show_bg = true
|
|
mod.widgets.Window.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let sz = self.rect_size
|
|
var col = cb_kraft(p, px, cb_kraft_c(), 1.0)
|
|
let e = min(min(p.x, sz.x - p.x), sz.y - p.y)
|
|
let rub = (1.0 - smoothstep(0.0, 22.0, e)) * (0.6 + 0.4 * Finish.noise(p * 0.05))
|
|
col = col * (1.0 - 0.13 * rub)
|
|
let th = 4.0
|
|
let fb = sz.y - p.y
|
|
col = mix(col, cb_flute(p.x, clamp(1.0 - fb / th, 0.0, 1.0), th, px, cb_kraft_c()), 1.0 - step(th, fb))
|
|
let fs = min(p.x, sz.x - p.x)
|
|
col = mix(col, col * 0.62, (1.0 - smoothstep(0.0, 1.6, fs)) * 0.5)
|
|
let tape = cb_packing_tape(p, sz, px)
|
|
col = col * (1.0 - tape.a) + tape.rgb
|
|
let mt = cb_tape(p, vec2(sz.x - 20.0, sz.y - 22.0), -0.80, 30.0, 8.5, px, 7.0)
|
|
col = col * (1.0 - mt.a) + mt.rgb
|
|
return vec4(col, 1.0)
|
|
}
|
|
|
|
// A splitter is a seam between two panes: the board shows through its
|
|
// strip, and down its middle a faint line of pencil; under the hand, held or
|
|
// focused, the line is gone over in marker along the grip's length.
|
|
mod.widgets.Splitter.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let v = self.is_vertical
|
|
let across = mix(p.y, p.x, v)
|
|
let along = mix(p.x, p.y, v)
|
|
let thick = mix(self.rect_size.y, self.rect_size.x, v)
|
|
let span = mix(self.rect_size.x, self.rect_size.y, v)
|
|
let m = thick * 0.5 + (Finish.noise(vec2(along * 0.03, 2.0)) - 0.5) * 0.6
|
|
var out = cb_stroke(abs(across - m), 0.35, p, px, vec3(0.30, 0.24, 0.17), 0.35)
|
|
let act = max(max(self.hover, self.drag), self.focus)
|
|
let inbar = 1.0 - smoothstep(self.bar_size * 0.5 - 2.0, self.bar_size * 0.5, abs(along - span * 0.5))
|
|
return Finish.over(out, cb_stroke(abs(across - m), 0.7, p, px, cb_ink_c(), act * inbar))
|
|
}
|
|
|
|
// ---------------------------------------------------------------- switches and meter
|
|
|
|
// THE SWITCHES are the sheet's own toggle, in proportion with the labels
|
|
// and the controls beside them: the rocker and the slide switch take the
|
|
// toggle's face (the disc in its slot, black card under it when on), its
|
|
// size and shape, its quad and its label gap; the rocker's disc is faced
|
|
// with yellow paper and the slide's with blue, so the two kinds tell apart.
|
|
mod.widgets.ToggleRocker.label_walk.margin = mod.turtle.Inset{left: 47.0 right: 3.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ToggleSlide.label_walk.margin = mod.turtle.Inset{left: 47.0 right: 3.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ToggleRocker.draw_bg.size = 18.0
|
|
mod.widgets.ToggleSlide.draw_bg.size = 18.0
|
|
mod.widgets.ToggleRocker.min_height = 26.0
|
|
mod.widgets.ToggleSlide.min_height = 26.0
|
|
mod.widgets.ToggleRocker.draw_bg.pill_aspect = 2.111
|
|
mod.widgets.ToggleSlide.draw_bg.pill_aspect = 2.111
|
|
mod.widgets.ToggleRocker.draw_bg.knob_inset = 9.0
|
|
mod.widgets.ToggleSlide.draw_bg.knob_inset = 9.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 cb_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, 9.0)
|
|
}
|
|
mod.widgets.ToggleSlide.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return cb_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, 2.0)
|
|
}
|
|
|
|
// The readouts and the screen: the instrument window round black card.
|
|
mod.widgets.ScreenView.clip_x = false
|
|
mod.widgets.ScreenView.clip_y = false
|
|
mod.widgets.ScreenView.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 4.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.ScreenView.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 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 b = 3.0
|
|
var face = cb_screen_face(p, px, self.color.rgb)
|
|
if self.scan > 0.0 {
|
|
face = face * Finish.scan(p.y - lo.y, px, 3.0 * px, self.scan)
|
|
}
|
|
return cb_window(p, lo, hi, 2.0, b, px, face, max(self.recess, 2.5))
|
|
}
|
|
mod.widgets.ScreenView.bezel = 3.0
|
|
mod.widgets.ScreenView.sheen = 0.0
|
|
|
|
// THE METER sits in the same window as the readouts and the screen, on the
|
|
// same black card: the scale painted in the display's yellow, the red zone
|
|
// in red paint, the needle a cream paint line and its pivot a yellow dot.
|
|
mod.widgets.NeedleMeter.clip_x = false
|
|
mod.widgets.NeedleMeter.clip_y = false
|
|
mod.widgets.NeedleMeter.draw_bg.border_radius = uniform(2.0)
|
|
mod.widgets.NeedleMeter.draw_bg.color_needle = uniform(#xf1e6cf)
|
|
mod.widgets.NeedleMeter.draw_bg.color_zone = uniform(#xe0533e)
|
|
mod.widgets.NeedleMeter.draw_bg.color_scale = uniform(#xf2c94e)
|
|
mod.widgets.NeedleMeter.draw_bg.vertex = fn() {
|
|
let g = cb_grow(self.rect_pos, self.rect_size, 4.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.NeedleMeter.draw_bg.pixel = fn() {
|
|
let m = 4.0
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let px = 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 b = 3.0
|
|
let pv = self.pivot()
|
|
let rr = self.scale_radius()
|
|
let q = p - pv
|
|
let r = length(q)
|
|
let ang = atan2(q.x, 0.0 - q.y)
|
|
let sweep = self.sweep * 0.017453293
|
|
let t = ang / sweep + 0.5
|
|
let on_scale = step(-0.0001, t) * step(t, 1.0001)
|
|
var face = cb_screen_face(p, px, cb_black_c())
|
|
// The scale: a painted arc and a tick at every division.
|
|
let arc = Finish.cover(abs(r - rr) - px * 0.75, px) * on_scale
|
|
let n = max(floor(self.majors + 0.5), 1.0) * max(floor(self.minors + 0.5), 1.0)
|
|
let k = floor(clamp(t, 0.0, 1.0) * n + 0.5)
|
|
let ak = (k / n - 0.5) * sweep
|
|
let perp = abs(q.x * cos(ak) + q.y * sin(ak))
|
|
let mn = max(floor(self.minors + 0.5), 1.0)
|
|
let major = 1.0 - step(0.5, k - mn * floor(k / mn + 0.0001))
|
|
let len = mix(self.tick_minor, self.tick_major, major)
|
|
let radial = max(rr - len - r, r - rr)
|
|
let tick = Finish.cover(perp - px * mix(0.6, 1.1, major), px) * Finish.cover(radial, px) * step(0.0, 0.0 - q.y * cos(ak) + q.x * sin(ak))
|
|
face = mix(face, self.color_scale.rgb, max(arc, tick) * 0.92)
|
|
if self.zone < 0.999 {
|
|
let zone_t = Finish.cover((self.zone - t) * sweep * r, px) * Finish.cover((t - 1.0) * sweep * r, px)
|
|
let zone_r = Finish.cover(max(rr + px - r, r - rr - 2.5), px)
|
|
face = mix(face, self.color_zone.rgb, zone_t * zone_r)
|
|
}
|
|
// The needle, 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 along = dot(q, nd)
|
|
let across = abs(q.x * nd.y - q.y * nd.x)
|
|
let needle = Finish.cover(across - px * 0.9, px) * Finish.cover(along - (rr + 3.0), px) * Finish.cover(0.0 - along - max(self.pivot_size - px, 0.0), px)
|
|
face = mix(face, self.color_needle.rgb, needle)
|
|
let pd = r - self.pivot_size
|
|
face = mix(face, self.color_scale.rgb, Finish.cover(pd, px))
|
|
face = mix(face, face * 0.5, Finish.band(pd, 0.0, px, px))
|
|
return cb_window(p, lo, hi, self.border_radius, b, px, face, 2.5) * self.color.a * self.opacity
|
|
}
|
|
mod.widgets.NeedleMeter.draw_label.color = #xf2c94e
|
|
|
|
// ---------------------------------------------------------------- lamps
|
|
// The Lamp widget and the bar lamp. A round lamp's halo has its room in the
|
|
// lamp's own box (the library reserves a lens thickness either side once a
|
|
// halo is asked for), so the light is never cut at its edge and stays in
|
|
// its cell. A bar is a short light pipe: six points thick and a little over
|
|
// twice that long, set at the left of its cell three points in, its glow
|
|
// held to two and a half points round its hole -- so 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.halo_reach = 1.0
|
|
mod.widgets.Lamp.draw_bg.color_accent = uniform(#xff9a2a)
|
|
mod.widgets.Lamp.draw_bg.vertex = fn() {
|
|
let m = 2.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.Lamp.draw_bg.pixel = fn() {
|
|
let m = 2.0
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let reach = clamp(self.reach, 0.0, 1.0)
|
|
let t = self.rect_size.y / (1.0 + 2.0 * reach)
|
|
let room = t * reach
|
|
let tb = min(t, 6.0)
|
|
let lb = tb * 2.1
|
|
let c = mix(self.rect_size * 0.5, vec2(3.5 + 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 - 0.5, 2.0), 2.5, self.bar)
|
|
let res = cb_led(p, c, hb, self.lit, self.intent_color().rgb, 1.0 - step(3.5, self.intent), px, glow)
|
|
return res * self.opacity
|
|
}
|
|
|
|
// ---------------------------------------------------------------- tree rows
|
|
// The rows of a tree are strips of cream paper glued on the board one under
|
|
// another, every other one a shade warmer; the chosen row a strip of yellow
|
|
// paper.
|
|
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 h = vec2(self.rect_size.x * 0.5 - 1.0, self.rect_size.y * 0.5)
|
|
let c = self.rect_size * 0.5
|
|
let d = cb_rbox(p, c, h, 0.5) + cb_wob(p, 0.2, vec2(self.is_even * 7.0, 3.0))
|
|
let base = mix(mix(self.color_1.rgb, self.color_2.rgb, self.is_even), cb_yellow_c(), self.active)
|
|
var face = cb_paper(p + vec2(self.is_even * 37.0, 11.0), px, base, 1.0)
|
|
let ry = self.rect_size.y - 0.8 + (Finish.noise(vec2(p.x * 0.05, self.is_even * 3.0)) - 0.5) * 0.5
|
|
face = mix(face, face * 0.78, Finish.cover(abs(p.y - ry) - 0.3, px) * 0.7)
|
|
let a = Finish.cover(d, px)
|
|
return vec4(face * a, a)
|
|
}
|
|
true
|