makepad/widgets/sheets/themes/neumorphic-dark/widgets.splash

1961 lines
106 KiB
Text

use mod.prelude.widgets_internal.*
use mod.sdf.Cap
// THE MATERIAL. One navy-charcoal ground, pushed up into raised parts or
// pressed into wells. Relief is light, not colour: a faint lift above a
// raised part, a soft fall and a tight contact shadow below it, a
// one-device-pixel near-black contact line round it, a lighter line just
// inside its top edge, and a face that is a shallow dish (darker under the
// top edge, a little lighter than the ground at the foot). State lives in
// small lights of one accent, set in a dark slot so they read as lights.
// Light comes from the top left. Distances are points; shadows are drawn
// OUTSIDE the layout box, in a quad grown in `vertex`, so they cost no
// padding.
let nd_ground = fn() -> vec3 {
return vec3(0.173, 0.184, 0.212)
}
let nd_accent = fn() -> vec3 {
return vec3(0.271, 0.922, 0.878)
}
let nd_line = fn() -> vec3 {
return vec3(0.043, 0.051, 0.067)
}
// Fine monochrome grain, one value per device pixel in window space, so a
// face and the ground round it read as one material. `amt` is the peak in
// levels of 255 (the standard deviation is 0.41 of it).
let nd_grain = fn(q: vec2, px: float, amt: float) -> float {
return Finish.grain(q / px, amt / 255.0)
}
// What a raised part throws round itself, premultiplied: a soft fall below
// (reaching about a quarter of the part's height), a tight contact shadow
// right under the edge, and a faint lift of the ground above and to the
// left. `s` is the part's own height, the rest their strengths.
let nd_cast = fn(p: vec2, c: vec2, h: vec2, r: float, s: float, fall: float, lift: float, con: float) -> vec4 {
let f = Finish.drop(p, c, h, r, vec2(0.02 * s, 0.075 * s), max(0.09 * s, 1.0)) * 0.7 * fall
let t = Finish.drop(p, c, h, r, vec2(0.0, 0.03 * s), max(0.03 * s, 0.5)) * 0.65 * con
let l = Finish.drop(p, c, h, r, vec2(-0.025 * s, -0.05 * s), max(0.06 * s, 0.7)) * 0.13 * lift
var acc = vec4(vec3(0.039, 0.043, 0.055) * f, f)
acc = Finish.over(acc, vec4(0.0, 0.0, 0.0, t))
acc = Finish.over(acc, vec4(l, l, l, l))
return acc
}
// A raised face: the dish, the light line one device pixel inside the top
// edge (fading down the corners), and a fainter one along the foot.
let nd_face = fn(p: vec2, c: vec2, h: vec2, r: float, d: float, px: float, raise: float, lift: float, lines: float) -> vec3 {
let v = clamp((p.y - (c.y - h.y)) / max(2.0 * h.y, 1.0), 0.0, 1.0)
let g = mix(-0.042, 0.03, smoothstep(0.02, 0.92, v)) * raise + lift
var col = nd_ground() + vec3(g, g, g)
let top = 1.0 - smoothstep(r * 0.3, r + 0.6, p.y - (c.y - h.y))
let bot = 1.0 - smoothstep(r * 0.3, r + 0.6, (c.y + h.y) - p.y)
let inner = Finish.band(d, px, 2.0 * px, px)
col = mix(col, vec3(1.0, 1.0, 1.0), inner * top * 0.11 * lines)
col = mix(col, vec3(1.0, 1.0, 1.0), inner * bot * 0.05 * lines)
return col
}
// A well: the fill given, the ground's shadow falling in along the top, and
// a dark line on the rim.
let nd_well = fn(fill: vec3, p: vec2, c: vec2, h: vec2, r: float, d: float, px: float, s: float, k: float) -> vec3 {
let sh = Finish.inset(p, c, h, r, vec2(0.0, 0.1 * s), max(0.14 * s, 0.8))
var o = mix(fill, vec3(0.0, 0.0, 0.0), sh * 0.6 * k)
o = mix(o, nd_line(), Finish.band(d, 0.0, px, px) * 0.55 * k)
return o
}
// The lit lip one device pixel outside a well's lower edge.
let nd_lip = fn(p: vec2, c: vec2, h: vec2, d: float, px: float, k: float) -> vec4 {
let low = smoothstep(c.y + h.y * 0.1, c.y + h.y, p.y)
let a = Finish.ring_out(d, 0.0, px, px) * low * 0.08 * k
return vec4(a, a, a, a)
}
// The focus mark: a one-pixel accent ring a point and a half outside the
// contact line, and a short teal halo either side of it.
let nd_focus = fn(d: float, px: float, f: float) -> vec4 {
let ring = Finish.ring_out(d, 1.5, 1.5 + px, px) * 0.55 * f
let soft = (1.0 - smoothstep(0.0, 2.5, abs(d - 1.5 - 0.5 * px))) * 0.25 * step(0.0, d) * f
var acc = vec4(vec3(0.118, 0.408, 0.412) * soft, soft)
return Finish.over(acc, vec4(nd_accent() * ring, ring))
}
// A light in a slot. `dc` is the distance to the lit core (negative
// inside): the core, one device pixel of dark gap round it, and beyond the
// gap a short halo that is gone three points out. Unlit, the core is a dark
// cell and there is no halo.
let nd_led_gap = fn(col: vec3, dc: float, px: float, gap: float, ink: vec3, lit: float, halo: float) -> vec3 {
let x = max(dc - gap, 0.0)
let fall = 1.0 - smoothstep(0.0, 2.3, x)
var o = col + ink * 0.2 * fall * fall * halo * lit * step(0.0, dc)
o = mix(o, nd_line(), Finish.ring_out(dc, 0.0, gap, px) * 0.85)
let core = mix(vec3(0.102, 0.110, 0.129), ink, lit)
return mix(o, core, Finish.cover(dc, px))
}
let nd_led = fn(col: vec3, dc: float, px: float, ink: vec3, lit: float, halo: float) -> vec3 {
return nd_led_gap(col, dc, px, px, ink, lit, halo)
}
// A round lamp: lit, the core with a hot middle; off, a dark cell below the
// ground.
let nd_lamp = fn(col: vec3, p: vec2, lc: vec2, lr: float, px: float, ink: vec3, lit: float, halo: float) -> vec3 {
let dd = length(p - lc) - lr
let hot = 1.0 - smoothstep(lr * 0.15, lr * 0.55, length(p - lc - vec2(0.0, -0.1 * lr)))
let lamp_ink = mix(ink, mix(ink, vec3(1.0, 1.0, 1.0), 0.4), hot * lit)
let off = vec3(0.086, 0.094, 0.102)
var o = nd_led(col, dd, px, lamp_ink, lit, halo)
return mix(o, mix(off, lamp_ink, lit), Finish.cover(dd, px))
}
// The distance to an arc of radius R about c, clockwise from a0 to a1
// measured from straight down, with round ends, w wide. Written here
// because the stock arc gives up on any sweep past half a turn.
let nd_arc = fn(p: vec2, c: vec2, R: float, a0: float, a1: float, w: float) -> float {
let q = p - c
var ap = atan2(-q.x, q.y)
if ap < 0.0 {
ap = ap + 2.0 * PI
}
let rel = ap - a0 - floor((ap - a0) / (2.0 * PI)) * 2.0 * PI
let on = step(rel, a1 - a0)
let band = abs(length(q) - R) - 0.5 * w
let e0 = length(q - vec2(-sin(a0), cos(a0)) * R) - 0.5 * w
let e1 = length(q - vec2(-sin(a1), cos(a1)) * R) - 0.5 * w
return mix(min(e0, e1), band, on)
}
// A distance to a segment, for the strokes the faces draw by hand.
let nd_seg = fn(p: vec2, a: vec2, b: vec2) -> float {
let pa = p - a
let ba = b - a
let t = clamp(dot(pa, ba) / max(dot(ba, ba), 0.0001), 0.0, 1.0)
return length(pa - ba * t)
}
// BUTTONS. `rest` 1 stands up off the page, 0 lies flat and rises two
// thirds under the pointer; `ghost` draws the contact line round a flat
// face; `hollow` shows nothing until the pointer asks, and then a faint
// hollow. `slot` puts an LED line in the foot of the face (`led.a` is how
// lit it is). Held, any of them is pressed in: flat, a step below the
// ground, the page's shadow falling in along the top.
let nd_button = fn(pq: vec2, size: vec2, org: vec2, px: float, hover: float, down: float, focus: float, disabled: float, loading: float, anim: float, ink: vec4, rest: float, ghost: float, hollow: float, slot: float, led: vec4) -> vec4 {
let lo = vec2(Finish.snap(org.x, px), Finish.snap(org.y, px)) - org
let hi = vec2(Finish.snap(org.x + size.x, px), Finish.snap(org.y + size.y, px)) - org
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let s = min(size.y, 32.0)
let r = min(0.27 * size.y, min(h.x, h.y))
let d = Material.sd_box(pq, c, h, r)
let en = 1.0 - disabled
let hv = hover * en
let press = down * en
let raised = step(0.5, rest)
let up = mix(mix(0.65 * hv, 1.0, raised), 0.0, hollow) * (1.0 - press)
var acc = nd_cast(pq, c, h, r, s, up * mix(1.0, 0.4, disabled), up * en * (1.0 + 0.4 * hv), max(up * mix(1.0, 0.5, disabled), 0.45 * press))
var col = nd_face(pq, c, h, r, d, px, up * en, 0.03 * hv, up * en * (1.0 + 0.6 * hv))
let sink = max(press, hollow * hv * 0.55)
let deep = Finish.inset(pq, c, h, r, vec2(0.0, 0.08 * s), 0.1 * s)
let pc = mix(nd_ground() - vec3(0.045, 0.045, 0.045), vec3(0.0, 0.0, 0.0), deep * 0.5)
col = mix(col, pc, sink)
// The LED line in the foot of the face: 0.4 of the width, two device
// pixels of light in a slot of dark, its centre on a pixel boundary so
// the core fills two whole rows.
let ly = Finish.snap(org.y + c.y + h.y - 0.12 * s, px) - org.y
let lw = h.x * 0.4
let dc = nd_seg(pq, vec2(c.x - lw, ly), vec2(c.x + lw, ly)) - px
col = mix(col, nd_led(col, dc, px, led.rgb, led.a * en, 1.0), slot)
col = col + vec3(nd_grain(org + pq, px, 1.9))
// The contact line: on anything that stands up, on a pressed face, and
// on the ghost's flat one.
let ol = max(max(up, sink), ghost) * mix(0.75, 0.3, disabled)
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * ol)
let a = Finish.cover(d, px)
acc = Finish.over(acc, vec4(col * a, a))
// A ghost face is cut into the page: the lit lip under its foot.
acc = Finish.over(acc, nd_lip(pq, c, h, d, px, max(ghost * (1.0 - up), sink) * en))
acc = Finish.over(acc, nd_focus(d, px, focus * en))
// The wait spinner is drawn by the face, so a face that replaces the
// stock one has to draw it again: a lit arc in a dark track.
let rr = max(min(h.x, h.y) - 5.0, 2.0)
let a0 = anim * 2.0 * PI
let sa = Finish.cover(nd_arc(pq, c, rr, a0, a0 + 1.5 * PI, 1.0), px) * ink.a * loading
return Finish.over(acc, vec4(ink.rgb * sa, sa))
}
// The room round every button for its shadows and its focus ring.
mod.widgets.ButtonFlat.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ButtonFlat.clip_x = false
mod.widgets.ButtonFlat.clip_y = false
// A view lets what it holds draw past its edge; scroll views still clip.
mod.widgets.View.clip_x = false
mod.widgets.View.clip_y = false
// The flat button: the page until the pointer raises it.
mod.widgets.ButtonFlat.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 0.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
// The standard, sized and compound buttons stand up off the page.
mod.widgets.Button.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
mod.widgets.ButtonXs.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
mod.widgets.ButtonSm.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
mod.widgets.ButtonLg.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
mod.widgets.ButtonXl.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
mod.widgets.ButtonCompound.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
// The primary face is the plain face with its LED line lit; the colour is
// that line and never a filled face.
mod.widgets.ButtonPrimary.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 1.0, vec4(0.271, 0.922, 0.878, 1.0)), m)
}
// The second voice is the plain raised face.
mod.widgets.ButtonSecondary.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
mod.widgets.ButtonTertiary.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
// The destructive button says so in its label only.
mod.widgets.ButtonDanger.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
// The outlined button is cut into the page: the contact line round a flat
// face and a lit lip under it; it rises under the pointer.
mod.widgets.ButtonOutline.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 0.0, 1.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
// The borderless button shows nothing until the pointer finds it.
mod.widgets.ButtonFlatter.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return self.host_over(nd_button(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.loading_color, 0.0, 0.0, 1.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0)), m)
}
// Rows of 26: 6 above and below the line, 12 either side.
mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 6.25 bottom: 6.25}
mod.widgets.ButtonFlat.margin = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.ButtonFlat.spacing = 6.0
mod.widgets.ButtonFlat.icon_walk.width = 13.0
mod.widgets.ButtonIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 6.0 bottom: 6.0}
mod.widgets.ButtonFlatIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 6.0 bottom: 6.0}
mod.widgets.ButtonFlatterIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 6.0 bottom: 6.0}
mod.widgets.ButtonFlat.draw_bg.loading_color = #45ebe0
mod.widgets.ButtonFlat.draw_icon.color = #b9bfca
mod.widgets.ButtonFlat.draw_icon_end.color = #b9bfca
// Hover eases in over the tenth of a second the animator already gives it
// rather than snapping, so a hover held still (the states page) shows.
mod.widgets.ButtonFlat.animator.hover.on.apply.draw_bg.hover = 1.0
mod.widgets.ButtonFlat.animator.hover.on.apply.draw_text.hover = 1.0
mod.widgets.TextInputFlat.animator.hover.on.apply.draw_bg.hover = 1.0
mod.widgets.TextInputFlat.animator.hover.on.apply.draw_text.hover = 1.0
// The press does the same, over three hundredths of a second, so a held
// press shows too.
mod.widgets.ButtonFlat.animator.hover.down.from.all.duration = 0.03
mod.widgets.ButtonFlat.animator.hover.down.apply.draw_bg.down = 1.0
mod.widgets.ButtonFlat.animator.hover.down.apply.draw_text.down = 1.0
mod.widgets.TextInputFlat.animator.hover.down.from.all.duration = 0.03
mod.widgets.TextInputFlat.animator.hover.down.apply.draw_bg.down = 1.0
mod.widgets.DropDownFlat.animator.hover.on.apply.draw_bg.hover = 1.0
mod.widgets.DropDownFlat.animator.hover.on.apply.draw_text.hover = 1.0
mod.widgets.DropDownFlat.animator.hover.down.from.all.duration = 0.03
mod.widgets.DropDownFlat.animator.hover.down.apply.draw_bg.down = 1.0
mod.widgets.CheckBoxFlat.animator.hover.down.from.all.duration = 0.03
mod.widgets.CheckBoxFlat.animator.hover.down.apply.draw_bg.down = 1.0
mod.widgets.ToggleFlat.animator.hover.down.from.all.duration = 0.03
mod.widgets.ToggleFlat.animator.hover.down.apply.draw_bg.down = 1.0
mod.widgets.RadioButtonFlat.animator.hover.down.from.all.duration = 0.03
mod.widgets.RadioButtonFlat.animator.hover.down.apply.draw_bg.down = 1.0
mod.widgets.RadioButtonTabFlat.animator.hover.down.from.all.duration = 0.03
mod.widgets.RadioButtonTabFlat.animator.hover.down.apply.draw_bg.down = 1.0
// Label inks: the working grey, a step brighter on the primary, the
// accent on the tertiary and the outline, coral on the destructive one.
let nd_medium = mod.theme.font_regular{font_family: mod.theme.font_regular.font_family{ibm_plex_text := FontMember{res: crate_resource("makepad_widgets:resources/Inter.ttf") weight: 500.0 asc: 0.0 desc: 0.0}}}
mod.widgets.ButtonFlat.draw_text.text_style = nd_medium
mod.widgets.ButtonPrimary.draw_text.color = #d7dde6
mod.widgets.ButtonPrimary.draw_text.color_hover = #eef2f7
mod.widgets.ButtonPrimary.draw_text.color_down = #eef2f7
mod.widgets.ButtonPrimary.draw_text.color_focus = #d7dde6
mod.widgets.ButtonPrimary.draw_text.color_disabled = #5a5e66
mod.widgets.ButtonPrimary.draw_icon.color = #d7dde6
mod.widgets.ButtonSecondary.draw_text.color = #b9bfca
mod.widgets.ButtonSecondary.draw_text.color_hover = #d7dde6
mod.widgets.ButtonSecondary.draw_text.color_down = #d7dde6
mod.widgets.ButtonSecondary.draw_text.color_focus = #b9bfca
mod.widgets.ButtonSecondary.draw_icon.color = #b9bfca
mod.widgets.ButtonTertiary.draw_text.color = #45ebe0
mod.widgets.ButtonTertiary.draw_text.color_hover = #8af3ec
mod.widgets.ButtonTertiary.draw_text.color_down = #8af3ec
mod.widgets.ButtonTertiary.draw_text.color_focus = #45ebe0
mod.widgets.ButtonTertiary.draw_icon.color = #45ebe0
mod.widgets.ButtonDanger.draw_text.color = #ec8f8a
mod.widgets.ButtonDanger.draw_text.color_hover = #f2a39e
mod.widgets.ButtonDanger.draw_text.color_down = #f2a39e
mod.widgets.ButtonDanger.draw_text.color_focus = #ec8f8a
mod.widgets.ButtonDanger.draw_icon.color = #ec8f8a
mod.widgets.ButtonOutline.draw_text.color = #b9bfca
mod.widgets.ButtonOutline.draw_text.color_hover = #d7dde6
mod.widgets.ButtonOutline.draw_text.color_down = #d7dde6
mod.widgets.ButtonOutline.draw_text.color_focus = #b9bfca
mod.widgets.ButtonOutline.draw_icon.color = #b9bfca
// CHECK BOX. A 14 point well with a 3 point corner, lit lip below; the mark
// is a 1.5 point accent stroke set in the dark, with a short halo. Under
// the pointer an unticked box shows the tick engraved.
let nd_check = fn(pq: vec2, size: vec2, org: vec2, px: float, box: float, hover: float, down: float, focus: float, active: float, disabled: float, mixed: float, error: float, round: float, at_end: float) -> vec4 {
let y0 = Finish.snap(org.y + size.y * 0.5 - box * 0.5, px) - org.y
let x0 = Finish.snap(org.x + (size.x - box) * at_end, px) - org.x
let c = vec2(x0 + box * 0.5, y0 + box * 0.5)
let h = vec2(box * 0.5, box * 0.5)
let r = mix(3.0, box * 0.5, round)
let d = Material.sd_box(pq, c, h, r)
let en = 1.0 - disabled
let fill = mix(vec3(0.055, 0.059, 0.075) + vec3(0.02, 0.02, 0.022) * hover * en - vec3(0.02, 0.02, 0.02) * down * en, vec3(0.133, 0.145, 0.165), disabled)
var col = nd_well(fill, pq, c, h, r, d, px, box, (1.0 + 0.3 * down) * mix(1.0, 0.4, disabled))
let ink = mix(mix(nd_accent(), vec3(0.878, 0.443, 0.424), error), vec3(0.29, 0.306, 0.337), disabled)
let k0 = vec2(c.x - box * 0.25, c.y + box * 0.0)
let k1 = vec2(c.x - box * 0.07, c.y + box * 0.18)
let k2 = vec2(c.x + box * 0.25, c.y - box * 0.17)
let tick = min(nd_seg(pq, k0, k1), nd_seg(pq, k1, k2)) - 0.75
let dash = nd_seg(pq, vec2(c.x - box * 0.22, c.y), vec2(c.x + box * 0.22, c.y)) - 0.75
let dm = mix(tick, dash, mixed)
let on = max(active, mixed)
// The short light of the mark on the well floor.
let x = max(dm, 0.0)
let fall = 1.0 - smoothstep(0.0, 2.5, x)
col = col + ink * 0.22 * fall * fall * on * en * step(0.0, dm)
// Off, under the pointer: the tick engraved in the floor.
let eng = Finish.cover(tick, px) * (1.0 - on) * hover * en
col = mix(col, vec3(0.2, 0.212, 0.239), eng * 0.8)
col = mix(col, ink, Finish.cover(dm, px) * on)
col = col + vec3(nd_grain(org + pq, px, 1.9))
// Under the error intent the well's rim takes the error ink.
col = mix(col, vec3(0.878, 0.443, 0.424), Finish.band(d, 0.0, px, px) * error * 0.85)
let a = Finish.cover(d, px)
var acc = vec4(col * a, a)
acc = Finish.over(acc, nd_lip(pq, c, h, d, px, en))
return Finish.over(acc, nd_focus(d, px, focus * en))
}
mod.widgets.CheckBoxFlat.draw_bg.vertex = fn() {
let m = 5.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.CheckBoxFlat.clip_x = false
mod.widgets.CheckBoxFlat.clip_y = false
mod.widgets.CheckBoxFlat.draw_bg.pixel = fn() {
let m = 5.0
let pq = 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)
return nd_check(pq, self.rect_size, self.rect_pos, px, 14.0, self.hover, self.down, self.focus, self.active, self.disabled, self.mixed, self.error, 0.0, self.mark_at_end)
}
mod.widgets.CheckBoxCircle.draw_bg.pixel = fn() {
let m = 5.0
let pq = 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)
return nd_check(pq, self.rect_size, self.rect_pos, px, 14.0, self.hover, self.down, self.focus, self.active, self.disabled, self.mixed, self.error, 1.0, self.mark_at_end)
}
mod.widgets.CheckBoxFlat.draw_bg.size = 14.0
mod.widgets.CheckBoxFlat.padding = mod.turtle.Inset{left: 0.0 right: 4.0 top: 6.5 bottom: 6.5}
mod.widgets.CheckBoxFlat.label_walk.margin.left = 21.0
// TOGGLE. A 34 by 18 pill well with a lit lip, and in it a 14 point raised
// thumb carrying a 5 point lamp: off a dark cell, on the accent with a hot
// middle.
let nd_toggle = fn(pq: vec2, size: vec2, org: vec2, px: float, hover: float, down: float, focus: float, active: float, disabled: float, error: float, t: float, at_end: float) -> vec4 {
let w = 34.0
let x0 = Finish.snap(org.x + (size.x - w) * at_end, px) - org.x
let cy = Finish.snap(org.y + size.y * 0.5, px) - org.y
let tc = vec2(x0 + w * 0.5, cy)
let th = vec2(w * 0.5, 9.0)
let td = Material.sd_box(pq, tc, th, 9.0)
let en = 1.0 - disabled
let dis = mix(1.0, 0.4, disabled)
var col = nd_well(mix(vec3(0.075, 0.082, 0.098), vec3(0.133, 0.145, 0.165), disabled), pq, tc, th, 9.0, td, px, 18.0, dis)
// A faint accent wash along the well where the lit thumb has been.
let wash = (1.0 - smoothstep(0.0, 18.0, abs(pq.x - (x0 + 9.0 + 16.0 * t)))) * active * en * 0.05
col = col + nd_accent() * wash
col = col + vec3(nd_grain(org + pq, px, 1.9))
let ta = Finish.cover(td, px)
var acc = vec4(col * ta, ta)
acc = Finish.over(acc, nd_lip(pq, tc, th, td, px, en))
// The thumb, and what it throws.
// Seven points, rounded to whole device pixels so the light line under
// the thumb's top edge lands on one pixel row.
let kr = Finish.snap(7.0, px)
let kc = vec2(x0 + 9.0 + 16.0 * t, cy)
let kh = vec2(kr, kr)
let kd = length(pq - kc) - kr
let press = down * en
let up = 1.0 - press
let hv = hover * en * (1.0 - press)
acc = Finish.over(acc, nd_cast(pq, kc, kh, kr, 14.0, up * dis, up * en * (1.0 + 0.4 * hv), dis))
// The face: a dish from the ground under its top edge to well above it
// at its foot, with the light line, so the thumb stands off the black
// track; held, it is pressed flat below the ground with the shadow
// falling in along its top.
var kcol = nd_face(pq, kc, kh, kr, kd, px, en, 0.042 * en + 0.03 * hv, en * (1.35 + 0.5 * hv))
let deep = Finish.inset(pq, kc, kh, kr, vec2(0.0, 1.6), 1.8)
let pc = mix(nd_ground() - vec3(0.05, 0.05, 0.05), vec3(0.0, 0.0, 0.0), deep * 0.6)
kcol = mix(kcol, pc, press)
kcol = mix(kcol, nd_line(), Finish.band(kd, 0.0, px, px) * 0.75 * dis)
let ink = mix(nd_accent(), vec3(0.878, 0.443, 0.424), error)
// The lamp cell, five points across with its dark ring.
kcol = nd_lamp(kcol, pq, kc, 2.5 - px, px, ink, active * en, 1.0)
kcol = kcol + vec3(nd_grain(org + pq, px, 1.9))
let ka = Finish.cover(kd, px)
acc = Finish.over(acc, vec4(kcol * ka, ka))
return Finish.over(acc, nd_focus(td, px, focus * en))
}
mod.widgets.ToggleFlat.draw_bg.vertex = fn() {
let m = 6.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ToggleFlat.clip_x = false
mod.widgets.ToggleFlat.clip_y = false
mod.widgets.ToggleFlat.draw_bg.pixel = fn() {
let m = 6.0
let pq = 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 t = mix(self.active, self.drag_pos, self.drag)
return nd_toggle(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.active, self.disabled, self.error, t, self.mark_at_end)
}
mod.widgets.ToggleFlat.draw_bg.size = 18.0
mod.widgets.ToggleFlat.draw_bg.pill_aspect = 1.889
mod.widgets.ToggleFlat.padding = mod.turtle.Inset{left: 0.0 right: 4.0 top: 4.0 bottom: 4.0}
mod.widgets.ToggleFlat.label_walk.margin.left = 42.0
// RADIO. A 14 point round well, darker than a box, with a 7 point lamp in
// it: off a dim grey cell, on the accent with a hot middle.
let nd_radio = fn(pq: vec2, size: vec2, org: vec2, px: float, hover: float, down: float, focus: float, active: float, disabled: float) -> vec4 {
let rr = 7.0
let cy = Finish.snap(org.y + size.y * 0.5, px) - org.y
let c = vec2(rr, cy)
let h = vec2(rr, rr)
let d = length(pq - c) - rr
let en = 1.0 - disabled
let dis = mix(1.0, 0.4, disabled)
let v = clamp((pq.y - (c.y - rr)) / (2.0 * rr), 0.0, 1.0)
var fill = mix(vec3(0.02, 0.024, 0.043), vec3(0.059, 0.071, 0.09), v)
fill = fill + vec3(0.04, 0.042, 0.046) * hover * en - vec3(0.015, 0.015, 0.015) * down * en
fill = mix(fill, vec3(0.133, 0.145, 0.165), disabled)
var col = nd_well(fill, pq, c, h, rr, d, px, 14.0, (1.0 + 0.3 * down) * dis)
let dr = 3.5
let dd = length(pq - c) - dr
let offc = mix(vec3(0.2, 0.212, 0.239) + vec3(0.08, 0.08, 0.09) * hover * en, vec3(0.16, 0.17, 0.19), disabled)
col = mix(col, offc, Finish.cover(dd, px) * (1.0 - active * en))
col = mix(col, nd_lamp(col, pq, c, dr, px, nd_accent(), 1.0, 1.0), active * en)
col = col + vec3(nd_grain(org + pq, px, 1.9))
let a = Finish.cover(d, px)
var acc = vec4(col * a, a)
acc = Finish.over(acc, nd_lip(pq, c, h, d, px, en))
return Finish.over(acc, nd_focus(d, px, focus * en))
}
mod.widgets.RadioButtonFlat.draw_bg.vertex = fn() {
let m = 5.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RadioButtonFlat.clip_x = false
mod.widgets.RadioButtonFlat.clip_y = false
mod.widgets.RadioButtonFlat.draw_bg.pixel = fn() {
let m = 5.0
let pq = 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)
return nd_radio(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.active, self.disabled)
}
mod.widgets.RadioButtonFlatter.draw_bg.pixel = fn() {
let m = 5.0
let pq = 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)
return nd_radio(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.focus, self.active, self.disabled)
}
mod.widgets.RadioButtonFlat.draw_bg.size = 14.0
mod.widgets.RadioButtonFlat.padding = mod.turtle.Inset{left: 0.0 right: 4.0 top: 6.5 bottom: 6.5}
mod.widgets.RadioButtonFlat.label_walk.margin.left = 21.0
// A 7 by 4 point chevron at a one-point stroke, pointing down.
let nd_chevron = fn(p: vec2, cx: float, cy: float, px: float) -> float {
let d = min(nd_seg(p, vec2(cx - 3.5, cy - 2.0), vec2(cx, cy + 2.0)), nd_seg(p, vec2(cx, cy + 2.0), vec2(cx + 3.5, cy - 2.0)))
return Finish.cover(d - 0.6, px)
}
// TEXT FIELDS. A raised pill plate whose face is a deeper dish than a
// button's: dark under the top edge, lighter at the foot, so the words sit
// in a hollow. Focused, an LED line lights in its foot, 0.6 of the width.
let nd_field = fn(pq: vec2, size: vec2, org: vec2, px: float, r: float, hover: float, down: float, focus: float, disabled: float) -> vec4 {
let lo = vec2(Finish.snap(org.x, px), Finish.snap(org.y, px)) - org
let hi = vec2(Finish.snap(org.x + size.x, px), Finish.snap(org.y + size.y, px)) - org
let c = (lo + hi) * 0.5
let h = (hi - lo) * 0.5
let rr = min(r, min(h.x, h.y))
let d = Material.sd_box(pq, c, h, rr)
let s = min(size.y, 30.0)
let en = 1.0 - disabled
let dis = mix(1.0, 0.4, disabled)
var acc = nd_cast(pq, c, h, rr, s, 0.9 * dis, 0.8 * en * (1.0 + 0.4 * hover), dis)
let v = clamp((pq.y - (c.y - h.y)) / max(2.0 * h.y, 1.0), 0.0, 1.0)
let g = mix(-0.06, 0.035, smoothstep(0.0, 0.95, v)) * en + 0.03 * hover * en
var col = nd_ground() + vec3(g, g, g)
// The hollow's own shadow under the top edge.
let sh = Finish.inset(pq, c, h, rr, vec2(0.0, 0.08 * s), 0.1 * s)
col = mix(col, vec3(0.0, 0.0, 0.0), sh * (0.25 + 0.35 * down) * en)
col = col - vec3(0.02, 0.02, 0.02) * down * en
let bot = 1.0 - smoothstep(rr * 0.3, rr + 0.6, (c.y + h.y) - pq.y)
col = mix(col, vec3(1.0, 1.0, 1.0), Finish.band(d, px, 2.0 * px, px) * bot * 0.06 * en)
let ly = c.y + h.y - 0.13 * s
let lw = h.x * 0.6 - rr * 0.5
let dc = nd_seg(pq, vec2(c.x - lw, ly), vec2(c.x + lw, ly)) - 0.5
col = mix(col, nd_led(col, dc, px, nd_accent(), 1.0, 1.0), focus * en)
col = col + vec3(nd_grain(org + pq, px, 1.9))
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * mix(0.75, 0.35, disabled))
let a = Finish.cover(d, px)
return Finish.over(acc, vec4(col * a, a))
}
// The inputs a slider, a combo box, a number or a well carries inside
// themselves draw nothing: the control round them is the box.
mod.widgets.TextInputFlat.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
// A field keeps its sideways clip (a long line scrolls inside it) and lets
// its shadows past its top and foot.
mod.widgets.TextInputFlat.clip_y = false
mod.widgets.TextInputFlat.draw_bg.pixel = fn() {
if self.color.w < 0.01 || self.border_radius < 0.5 {
return vec4(0.0, 0.0, 0.0, 0.0)
}
let m = 8.0
let pq = 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)
return nd_field(pq, self.rect_size, self.rect_pos, px, self.border_radius, self.hover, self.down, self.focus, self.disabled)
}
mod.widgets.TextInputFlat.padding.left = 12.0
mod.widgets.TextInputFlat.padding.right = 12.0
// The number field: a rectangular plate of the same kind, with its stepper
// cap standing at the right end.
mod.widgets.NumberField.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.NumberField.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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)
return nd_field(pq, self.rect_size, self.rect_pos, px, 7.0, self.hover, 0.0, self.focus, self.disabled)
}
mod.widgets.NumberField.clip_y = false
mod.widgets.NumberField.pad_left = 12.0
// The stepper: a raised cap split by a seam, one pixel dark over one pixel
// light; the half held sinks and its triangle lights.
mod.widgets.NumberSpin.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let w = self.rect_size.x
let h = self.rect_size.y
let mid = Finish.snap(self.rect_pos.y + h * 0.5, px) - self.rect_pos.y
let live = 1.0 - self.disabled
let upper = step(p.y, mid)
let hot = mix(max(0.0, -self.hot), max(0.0, self.hot), upper) * live
let dn = mix(max(0.0, -self.down), max(0.0, self.down), upper) * live
let c = vec2(w * 0.5, h * 0.5)
let hh = vec2(w * 0.5 - 2.0, h * 0.5 - 2.0)
let d = Material.sd_box(p, c, hh, 4.0)
var acc = nd_cast(p, c, hh, 4.0, 14.0, 0.8 * live, 0.8 * live, 1.0)
var col = nd_face(p, c, hh, 4.0, d, px, live, 0.018 * hot, live)
let hc = vec2(c.x, mix(mid + (h - 2.0 - mid) * 0.5, 2.0 + (mid - 2.0) * 0.5, upper))
let hb = vec2(hh.x, (mid - 2.0) * 0.5)
let deep = Finish.inset(p, hc, hb, 3.0, vec2(0.0, 1.0), 1.4)
col = mix(col, mix(nd_ground() - vec3(0.045, 0.045, 0.045), vec3(0.0, 0.0, 0.0), deep * 0.5), dn)
let inx = step(c.x - hh.x + 1.0, p.x) * step(p.x, c.x + hh.x - 1.0)
col = mix(col, nd_line(), step(mid - px, p.y) * step(p.y, mid) * inx * 0.8)
col = mix(col, vec3(1.0, 1.0, 1.0), step(mid, p.y) * step(p.y, mid + px) * inx * 0.06)
col = col + vec3(nd_grain(self.rect_pos + p, px, 1.9))
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * 0.75)
// Triangles five points wide, dim grey, lit while held.
let yu = mid * 0.5 + 1.0
let yd = mid + (h - mid) * 0.5 - 1.0
let tu = max((yu - 1.5 + 1.12 * abs(p.x - c.x) - p.y) * 0.67, p.y - yu - 1.3)
let td = max((p.y - yd - 1.5 + 1.12 * abs(p.x - c.x)) * 0.67, yd - 1.3 - p.y)
let mu = Finish.cover(tu, px)
let md = Finish.cover(td, px)
let dimc = mix(vec3(0.604, 0.624, 0.659), vec3(0.3, 0.31, 0.34), self.disabled)
col = mix(col, mix(dimc, nd_accent(), max(0.0, self.down) * live), mu)
col = mix(col, mix(dimc, nd_accent(), max(0.0, -self.down) * live), md)
let a = Finish.cover(d, px)
return Finish.over(acc, vec4(col * a, a))
}
// The printed arrow glyphs are hidden; the face draws the triangles.
mod.widgets.NumberSpin.draw_text.color = #9a9fa800
mod.widgets.NumberSpin.draw_text.color_disabled = #9a9fa800
// The drag-number: the field's plate, its track a faint accent wash.
mod.widgets.ValueInput.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ValueInput.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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)
var acc = nd_field(pq, self.rect_size, self.rect_pos, px, 7.0, max(self.hover, self.drag), 0.0, self.focus, 0.0)
let d = Material.sd_box(pq, self.rect_size * 0.5, self.rect_size * 0.5, 7.0)
let wash = step(0.0, self.fill) * step(pq.x, 1.0 + (self.rect_size.x - 2.0) * max(self.fill, 0.0)) * Finish.cover(d + 1.0, px) * 0.07
return Finish.over(acc, vec4(nd_accent() * wash, wash))
}
mod.widgets.ValueInput.draw_text.color = #b9bfca
mod.widgets.ValueInput.arrow_color = #9a9fa8
mod.widgets.ValueInput.text_input.draw_bg.color = #00000000
// DROP-DOWN. The button's body with a chevron at the right; open, the face
// is pressed in and the chevron lights.
let nd_dropdown = fn(pq: vec2, size: vec2, org: vec2, px: float, hover: float, down: float, open: float, focus: float, disabled: float) -> vec4 {
let face = nd_button(pq, size, org, px, hover, max(down, open), focus, disabled, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0))
let ch = nd_chevron(pq, size.x - 12.5, size.y * 0.5 + 0.5, px)
let ink = mix(mix(vec3(0.604, 0.624, 0.659), nd_accent(), open * (1.0 - disabled)), vec3(0.3, 0.31, 0.34), disabled)
return Finish.over(face, vec4(ink * ch, ch))
}
mod.widgets.DropDownFlat.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.DropDownFlat.clip_x = false
mod.widgets.DropDownFlat.clip_y = false
mod.widgets.DropDownFlat.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return nd_dropdown(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, self.active, self.focus, self.disabled)
}
mod.widgets.DropDownFlat.padding.left = 12.0
mod.widgets.DropDownFlat.padding.right = 26.0
// The select wears a button of its own, built before this sheet: it is
// given the drop-down's face and the grown quad by its own path.
mod.widgets.Select.face.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Select.face.clip_x = false
mod.widgets.Select.face.clip_y = false
mod.widgets.MultiSelect.face.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.MultiSelect.face.clip_x = false
mod.widgets.MultiSelect.face.clip_y = false
// The select round the face is a view of its own, and clips unless told.
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.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return nd_dropdown(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, 0.0, self.focus, self.disabled)
}
mod.widgets.Select.face.padding.right = 26.0
mod.widgets.MultiSelect.face.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return nd_dropdown(pq, self.rect_size, self.rect_pos, px, self.hover, self.down, 0.0, self.focus, self.disabled)
}
mod.widgets.MultiSelect.face.padding.right = 26.0
// COMBO BOX. A field one can type in: the field's plate with the chevron.
mod.widgets.ComboBoxFlat.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ComboBoxFlat.clip_y = false
mod.widgets.ComboBoxFlat.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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 face = nd_field(pq, self.rect_size, self.rect_pos, px, 7.0, self.hover, 0.0, self.focus, self.disabled)
let ch = nd_chevron(pq, self.rect_size.x - 12.5, self.rect_size.y * 0.5 + 0.5, px) * mix(1.0, 0.4, self.disabled)
return Finish.over(face, vec4(vec3(0.604, 0.624, 0.659) * ch, ch))
}
mod.widgets.ComboBoxFlat.padding.left = 12.0
mod.widgets.ComboBoxFlat.padding.right = 26.0
mod.widgets.ComboBoxFlat.input.min_height = 0.0
// SLIDERS. Every slider face draws its thumb and its shadows past its own
// box, so the family's root grows the quad and stops clipping.
mod.widgets.SliderMinimal.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SliderMinimal.clip_x = false
mod.widgets.SliderMinimal.clip_y = false
// A raised disc thumb of radius r with a lamp in its middle: a dark cell at
// rest, lit while it is dragged or keyed.
let nd_thumb = fn(acc: vec4, pq: vec2, org: vec2, hc: vec2, r: float, px: float, hover: float, drag: float, focus: float, disabled: float) -> vec4 {
let en = 1.0 - disabled
let dis = mix(1.0, 0.4, disabled)
let kh = vec2(r, r)
let hd = length(pq - hc) - r
var o = Finish.over(acc, nd_cast(pq, hc, kh, r, 2.0 * r, dis, en * (1.0 + 0.4 * hover), dis))
// The face a dish from just under the ground at its top to well over it
// at its foot, so the thumb reads as a raised handle on the black groove.
var col = nd_face(pq, hc, kh, r, hd, px, en, 0.03 * en + 0.035 * hover * en - 0.01 * drag * en, en * (1.0 + 0.5 * hover))
col = mix(col, nd_line(), Finish.band(hd, 0.0, px, px) * 0.75 * dis)
// The lamp cell: five points across (its dark ring included) on a 16
// point thumb, in proportion on the smaller ones.
col = nd_lamp(col, pq, hc, max(r * 0.3125 - px, 1.2), px, nd_accent(), max(drag, focus) * en, 1.0)
col = col + vec3(nd_grain(org + pq, px, 1.9))
let ha = Finish.cover(hd, px)
return Finish.over(o, vec4(col * ha, ha))
}
// The slot a value runs in: a pill well with a lit lip and a black groove
// down its middle. Returns the well colour; the lip is laid by the caller.
let nd_slot = fn(pq: vec2, org: vec2, c: vec2, h: vec2, groove: float, px: float, dis: float) -> vec3 {
let r = min(h.x, h.y)
let d = Material.sd_box(pq, c, h, r)
var col = nd_well(mix(vec3(0.075, 0.082, 0.098), vec3(0.133, 0.145, 0.165), 1.0 - dis), pq, c, h, r, d, px, h.y * 2.0 + 4.0, dis)
let gh = vec2(max(h.x - (h.y - groove * 0.5), 0.5), groove * 0.5)
let gd = Material.sd_box(pq, c, gh, groove * 0.5)
col = mix(col, vec3(0.0, 0.0, 0.0), Finish.cover(gd, px) * 0.9 * dis)
return col
}
// The plain slider: a 6 point slot with a 2 point black groove, the value a
// one-point LED line in the groove from the stop (or from the default) to
// the thumb, and a 16 point raised thumb. The label and the readout stand
// in the 20 points above.
mod.widgets.SliderFlat.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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 org = self.rect_pos
let en = 1.0 - self.disabled
let dis = mix(1.0, 0.4, self.disabled)
let top = 20.0
let cy = Finish.snap(org.y + top + (self.rect_size.y - top) * 0.5, px) - org.y
let hr = 8.0
let x0 = hr + 1.0
let x1 = self.rect_size.x - hr - 1.0
let gc = vec2(self.rect_size.x * 0.5, cy)
// Ten device pixels tall about a snapped centre, so both rims and the
// lit lip under the foot fall on whole pixel rows.
let gh = vec2(self.rect_size.x * 0.5 - 1.0, 5.0 * px)
let gd = Material.sd_box(pq, gc, gh, gh.y)
var col = nd_slot(pq, org, gc, gh, 4.0 * px, px, dis)
let lo = min(self.origin_pos, self.slide_pos)
let hi = max(self.origin_pos, self.slide_pos)
let from = mix(0.0, lo, self.arc_origin)
let to = mix(self.slide_pos, hi, self.arc_origin)
let fx0 = mix(x0, x1, from)
let fx1 = mix(x0, x1, to)
let fd = nd_seg(pq, vec2(fx0, cy), vec2(fx1, cy)) - px
let show = step(0.25, fx1 - fx0)
let lit = col + nd_accent() * 0.2 * (1.0 - smoothstep(0.0, 2.3, max(fd, 0.0))) * (1.0 - smoothstep(0.0, 2.3, max(fd, 0.0))) * en
col = mix(col, mix(lit, mix(nd_accent(), vec3(0.29, 0.306, 0.337), self.disabled), Finish.cover(fd, px)), show)
col = col + vec3(nd_grain(org + pq, px, 1.9))
let ga = Finish.cover(gd, px)
var acc = vec4(col * ga, ga)
acc = Finish.over(acc, nd_lip(pq, gc, gh, gd, px, 0.875 * en))
return nd_thumb(acc, pq, org, vec2(mix(x0, x1, self.slide_pos), cy), hr, px, self.hover, self.drag, self.focus, self.disabled)
}
// The minimal slider: an engraved hairline (one pixel dark over one pixel
// light), the value a one-point LED line on it, and a 10 point thumb.
mod.widgets.SliderMinimal.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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 org = self.rect_pos
let en = 1.0 - self.disabled
let top = self.offset_y
let cy = Finish.snap(org.y + top + (self.rect_size.y - top) * 0.5, px) - org.y
let hr = 5.0
let x0 = hr
let x1 = self.rect_size.x - hr
let inx = step(0.0, pq.x) * step(pq.x, self.rect_size.x)
let dk = step(cy - px, pq.y) * step(pq.y, cy + px) * inx * mix(0.85, 0.4, self.disabled)
let lt = step(cy + px, pq.y) * step(pq.y, cy + 2.0 * px) * inx * mix(0.07, 0.03, self.disabled)
var acc = vec4(nd_line() * dk, dk)
acc = Finish.over(acc, vec4(lt, lt, lt, lt))
let lo = min(self.origin_pos, self.slide_pos)
let hi = max(self.origin_pos, self.slide_pos)
let from = mix(0.0, lo, self.arc_origin)
let to = mix(self.slide_pos, hi, self.arc_origin)
let fx0 = mix(x0, x1, from)
let fx1 = mix(x0, x1, to)
let fd = nd_seg(pq, vec2(fx0, cy), vec2(fx1, cy)) - px
let fa = Finish.cover(fd, px) * step(0.25, fx1 - fx0)
let ink = mix(nd_accent(), vec3(0.29, 0.306, 0.337), self.disabled)
acc = Finish.over(acc, vec4(ink * fa, fa))
return nd_thumb(acc, pq, org, vec2(mix(x0, x1, self.slide_pos), cy), hr, px, self.hover, self.drag, self.focus, self.disabled)
}
// The round slider: the value runs in a pill well strip beside its label,
// an LED line in a black groove, and a small raised thumb rides in it.
mod.widgets.SliderRoundFlat.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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 org = self.rect_pos
let en = 1.0 - self.disabled
let dis = mix(1.0, 0.4, self.disabled)
let cy = Finish.snap(org.y + self.rect_size.y * 0.5, px) - org.y
let sx0 = self.label_size
let sh = min(self.rect_size.y * 0.5, 8.0)
let sc = vec2((sx0 + self.rect_size.x) * 0.5, cy)
let shh = vec2((self.rect_size.x - sx0) * 0.5, sh)
let sd = Material.sd_box(pq, sc, shh, sh)
var col = nd_well(mix(vec3(0.075, 0.082, 0.098), vec3(0.133, 0.145, 0.165), self.disabled), pq, sc, shh, sh, sd, px, 16.0, dis)
let hr = sh - 2.0
// The readout stands in the strip's right end, behind a seam.
let room = 34.0
let x0 = sx0 + sh
let x1 = self.rect_size.x - sh - room
let sx = Finish.snap(org.x + self.rect_size.x - room - 2.0, px) - org.x
let seam = step(sx - px, pq.x) * step(pq.x, sx) * step(abs(pq.y - cy), sh - 3.0)
col = mix(col, vec3(0.0, 0.0, 0.0), seam * 0.6)
col = mix(col, vec3(1.0, 1.0, 1.0), step(sx, pq.x) * step(pq.x, sx + px) * step(abs(pq.y - cy), sh - 3.0) * 0.05)
let gd = Material.sd_box(pq, vec2((x0 + x1) * 0.5, cy), vec2((x1 - x0) * 0.5 + 2.0 * px, 2.0 * px), 2.0 * px)
col = mix(col, vec3(0.0, 0.0, 0.0), Finish.cover(gd, px) * 0.9 * dis)
let hx = mix(x0, x1, self.slide_pos)
let fd = nd_seg(pq, vec2(x0, cy), vec2(hx, cy)) - px
col = col + nd_accent() * 0.2 * (1.0 - smoothstep(0.0, 2.3, max(fd, 0.0))) * (1.0 - smoothstep(0.0, 2.3, max(fd, 0.0))) * en * step(0.0, fd) * Finish.cover(sd, px)
col = mix(col, mix(nd_accent(), vec3(0.29, 0.306, 0.337), self.disabled), Finish.cover(fd, px))
col = col + vec3(nd_grain(org + pq, px, 1.9))
let sa = Finish.cover(sd, px)
var acc = vec4(col * sa, sa)
acc = Finish.over(acc, nd_lip(pq, sc, shh, sd, px, en))
return nd_thumb(acc, pq, org, vec2(hx, cy), hr, px, self.hover, self.drag, self.focus, self.disabled)
}
// The readout's field takes no height of its own, so its digits stand on
// the strip's middle line and not a field's padding below it.
mod.widgets.SliderRoundFlat.text_input.min_height = 0.0
mod.widgets.SliderRoundFlat.text_input.margin.top = 2.3
// The fader: the light sheet's white spheres puck in this sheet's dark
// palette. A round cap 16 points across and 8 tall in the raised handle's
// colour, its top cut by a sphere into a shallow dish inside a rounded rim,
// lit by one key light from the top left and throwing a soft shadow down
// and to the right. It rides a 2 point slot sunk in the page, with twenty
// ticks on the far side, every fifth long, and no value fill: the cap is
// the value. The shape, its curves and the material numbers are the bench
// preset's, as in the light sheet; the inks and the ground are this
// sheet's. The puck is as wide as it is long, so `cap_cross` is not read.
// Lying down or standing up.
mod.widgets.SliderFader.cap_size = 16.0
mod.widgets.SliderFader.track_inset = 8.0
mod.widgets.SliderFader.draw_bg.track_size = uniform(2.0)
mod.widgets.SliderFader.clip_x = false
mod.widgets.SliderFader.clip_y = false
// The material, live in the tweaker: the key light in screen space (x
// right, y down, z toward the viewer) and its intensity; the highlight, its
// roughness, the broad gloss sheen and the rim toward grazing; the
// occlusion at the foot and how far up it reaches; the plain sky's
// reflection and how close the eye sits over the cap (0 far above); metal,
// turned grain and self-light, all off here; the exposure in stops and the
// highlight roll-off; the cast shadow's strength and its softest edge (held
// to the cap's height), the contact shadow's strength and the inner shadow
// in the slot.
mod.widgets.SliderFader.draw_bg.cap_light = uniform(vec3(-0.36, -0.6, 0.72))
mod.widgets.SliderFader.draw_bg.cap_light_intensity = uniform(0.9)
mod.widgets.SliderFader.draw_bg.cap_specular = uniform(0.08)
mod.widgets.SliderFader.draw_bg.cap_roughness = uniform(0.38)
mod.widgets.SliderFader.draw_bg.cap_gloss = uniform(0.16)
mod.widgets.SliderFader.draw_bg.cap_rim = uniform(0.54)
mod.widgets.SliderFader.draw_bg.cap_ao = uniform(0.32)
mod.widgets.SliderFader.draw_bg.cap_ao_reach = uniform(1.4)
mod.widgets.SliderFader.draw_bg.cap_reflection = uniform(0.16)
mod.widgets.SliderFader.draw_bg.cap_reflection_perspective = uniform(0.35)
mod.widgets.SliderFader.draw_bg.cap_metal = uniform(0.0)
mod.widgets.SliderFader.draw_bg.cap_spun = uniform(0.0)
mod.widgets.SliderFader.draw_bg.cap_glow = uniform(0.0)
mod.widgets.SliderFader.draw_bg.cap_exposure = uniform(0.0)
mod.widgets.SliderFader.draw_bg.cap_rolloff = uniform(0.6)
mod.widgets.SliderFader.draw_bg.cap_shadow = uniform(0.45)
mod.widgets.SliderFader.draw_bg.cap_shadow_blur = uniform(18.0)
mod.widgets.SliderFader.draw_bg.cap_contact = uniform(0.25)
mod.widgets.SliderFader.draw_bg.cap_inner = uniform(0.6)
// The inks, this sheet's: the cap in the raised handle's colour, the light
// white and the shade black as the dark theme under this sheet sets them
// (the shade a multiplier: what a full shadow does to what it falls on),
// the slot in the well ink and the ticks in the dim ink the old fader
// printed them in. The ground is the page's own colour, so the fader, laid
// over the page premultiplied, leaves the page itself untouched.
mod.widgets.SliderFader.draw_bg.cap_ground = uniform(mod.theme.color_bg_app)
mod.widgets.SliderFader.draw_bg.cap_body = uniform(mod.theme.color_handle)
mod.widgets.SliderFader.draw_bg.cap_light_ink = uniform(mod.theme.color_material_light)
mod.widgets.SliderFader.draw_bg.cap_shadow_ink = uniform(mod.theme.color_material_shadow)
mod.widgets.SliderFader.draw_bg.cap_track_ink = uniform(mod.theme.color_inset)
mod.widgets.SliderFader.draw_bg.cap_tick_ink = uniform(#4a4e56)
// The puck's solid at `q`, a point in the cap's own frame (x along the
// track, y across it), `hs` its half size and `r` its corner: the height
// 0..1 in .x, its gradient per point in .yz, and in .w the footprint's
// distance, negative inside. The curves are the preset's, handles resolved
// by the bench: along, across and plan level at full (a round footprint
// under a flat top), and the edge rising from the outline and rounding level
// over the whole radius. Shape: corner and edge width the whole half size,
// blend 0.02, 8 points tall. No ribs, no mark. The cut is a sphere (2) at
// the middle (0), its radius 1.17 of the half length, its centre 1.38
// heights up. On the preset's 16 point cap the wall leaves the outline at 45
// degrees and the cut takes the top down to a fifth of the height at the
// middle, a dish inside a rim; the height and the cut's centre are in
// points, so a longer cap dishes deeper, as it does on the bench.
let nd_puck_solid = fn(q: vec2, hs: vec2, r: float) -> vec4 {
let plan = Cap.curve((q.x / hs.x + 1.0) * 0.5, 2.0, vec2(0.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0))
let fp = Cap.sd_cap(q, hs, r, plan)
let along = Cap.curve((q.x / hs.x + 1.0) * 0.5, 2.0, vec2(0.0, 1.0), vec2(0.1667, 0.0), vec2(-0.1667, 0.0), vec2(1.0, 1.0), vec2(0.1667, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0))
let across = Cap.curve(abs(q.y) / hs.y, 2.0, vec2(0.0, 1.0), vec2(0.1667, 0.0), vec2(-0.1667, 0.0), vec2(1.0, 1.0), vec2(0.1667, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0))
// The edge is read no further out than the outline: past it the curve
// has no slope, and the pixels the outline half covers would take the
// flat top's light, a dotted bright line along the shaded foot.
let edge = Cap.curve(Cap.edge_x(min(fp.x, 0.0), hs, 1.0), 2.0, vec2(0.0, 0.0), vec2(0.1667, 0.1667), vec2(-0.1667, 0.0), vec2(1.0, 1.0), vec2(0.1667, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0))
let f = Cap.field(q, hs, fp, along, across, edge, vec2(1.0, 0.0), vec4(1.0, 1.0, 0.02, 8.0), vec4(0.0, 0.0, 0.0, 1.0), vec4(0.0, 0.0, 0.0, 0.0), vec4(0.0, 0.5, 1.0, 0.0), vec4(2.0, 0.0, 1.17, 1.38))
return vec4(f.x, f.y, f.z, fp.x)
}
// A colour drawn on a page of colour `ground`, handed back premultiplied so
// the page itself is left as it is: the least cover that keeps every
// channel, and the light it adds over what shows through. Exact over that
// ground, so a shade keeps the hue of what it falls on; the engine's
// `on_ground` covers by the darkest channel alone and greys it.
let nd_on_page = fn(col: vec3, ground: vec3) -> vec4 {
let dark = max(ground - col, vec3(0.0, 0.0, 0.0)) / max(ground, vec3(0.01, 0.01, 0.01))
let a = clamp(max(dark.x, max(dark.y, dark.z)), 0.0, 1.0)
return vec4(max(col - ground * (1.0 - a), vec3(0.0, 0.0, 0.0)), a)
}
// The quad grows past the layout box by the cast shadow's reach (its throw,
// five and a half points from this light, and its softest edge, the cap's 8
// point height), the focus ring's four points, and whatever of the cap is
// wider than the control, so a cap at a stop never has its shadow or its
// ring cut at the quad's edge.
mod.widgets.SliderFader.draw_bg.vertex = fn() {
let across = mix(self.rect_size.y, self.rect_size.x, self.is_vertical)
let m = 14.0 + max(self.cap_px - across, 0.0) * 0.5
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SliderFader.draw_bg.pixel = fn() {
let v = self.is_vertical
let sz = self.rect_size
let along = mix(sz.x, sz.y, v)
let across = mix(sz.y, sz.x, v)
let m = 14.0 + max(self.cap_px - across, 0.0) * 0.5
let pq = self.pos * (sz + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let en = 1.0 - self.disabled
let dis = mix(1.0, 0.4, self.disabled)
// Track space: `ab.x` along the track from the value's zero (up the
// screen when the fader stands), `ab.y` across it.
let ab = vec2(mix(pq.x, sz.y - pq.y, v), mix(pq.y, pq.x, v))
let inset = self.inset_px
let travel = max(along - inset * 2.0 - self.cap_px, 1.0)
let foot = inset + self.cap_px * 0.5
let mid = across * 0.5
let ground = self.cap_ground.rgb
let body = self.cap_body.rgb
let light_ink = self.cap_light_ink.rgb
let shadow_ink = self.cap_shadow_ink.rgb
// The key light on screen for the cap's shading, and turned into track
// space for everything laid out along the track.
let l = normalize(self.cap_light)
let lt = Cap.light(self.cap_light, v)
// The ground: the slot the whole length of the track, its floor in the
// shade of the wall toward the light and its far lip lit, then the
// scale. Disabled, both fade toward the page as this sheet's grooves and
// ticks do.
let tw = min(self.track_size, across)
let tlen = max(along - inset * 2.0, 1.0)
let tbox = vec4(inset + tlen * 0.5, mid, tlen * 0.5, tw * 0.5)
let at = foot + self.slide_pos * travel
let hs = vec2(self.cap_px * 0.5, self.cap_px * 0.5)
let track_ink = self.cap_track_ink.rgb
var col = Cap.slot(ground, ab, tbox, tw * 0.5, 0.0, inset, inset, at, hs.x, lt, px, 0.0, 0.0, track_ink, track_ink, light_ink, shadow_ink, self.cap_inner, self.cap_ao)
col = Cap.ticks(col, ab, mid, foot, travel, 20.0, 1.0, 5.0, tw, 0.0, px, self.cap_tick_ink.rgb)
col = mix(ground, col, dis)
// The cap, worked out only where it, its shadow or its focus ring can
// reach. Under the hand its face lightens a step, as this sheet's raised
// faces do. Held, it sinks half a point, so its shadow shortens, and its
// face darkens as this sheet's held caps do. Disabled, its relief falls
// to four tenths as this sheet's does: its shadows and its own
// modelling, and its rim light goes, as this sheet's disabled faces lose
// their lit lines.
let q = ab - vec2(at, mid)
let r = min(hs.x, hs.y)
let height = 8.0
let rise = height - 0.5 * self.drag
let reach = rise * 0.7 * length(lt.xy) / max(lt.z, 0.25) + max(rise, 1.0) + 5.0
var capc = ground
var cov = 0.0
var shd = 0.0
var halo = 0.0
var cap_d = 100.0
if max(abs(q.x) - hs.x, abs(q.y) - hs.y) < reach {
let s = nd_puck_solid(q, hs, r)
let n = Cap.normal(s.yz, height, v)
let eye = Cap.view(q, hs, self.cap_reflection_perspective, v)
capc = Cap.shade(n, l, eye, s.x, body, 0.0, 0.0, 1.0, self.cap_specular, self.cap_roughness, self.cap_gloss, self.cap_rim * en, self.cap_ao, self.cap_ao_reach, self.cap_reflection, self.cap_metal, self.cap_light_intensity, body, light_ink, shadow_ink, ground)
if self.cap_spun > 0.001 {
let grain = Cap.spun(q, hs, lt.xy) * self.cap_spun
capc = Cap.in_shadow(Cap.in_light(capc, clamp(grain, 0.0, 1.0) * 0.55, light_ink), clamp(-grain, 0.0, 1.0) * 0.42, shadow_ink)
}
if self.cap_glow > 0.001 {
capc = Cap.emit(capc, s.x, body, light_ink, self.cap_glow)
}
if self.disabled > 0.001 {
// The cap as a flat disc would read, the relief laid back over
// it at four tenths.
let level = Cap.shade(vec3(0.0, 0.0, 1.0), l, vec3(0.0, 0.0, 1.0), 1.0, body, 0.0, 0.0, 1.0, self.cap_specular, self.cap_roughness, self.cap_gloss, 0.0, self.cap_ao, self.cap_ao_reach, self.cap_reflection, self.cap_metal, self.cap_light_intensity, body, light_ink, shadow_ink, ground)
capc = mix(level, capc, dis)
}
cov = Cap.cover(s.w, px)
// Under it: the cast shadow, thrown from the top outline the bench
// gives for this cap, and the contact shadow round its foot.
let cast_sh = Cap.shadow(q, hs, r, vec3(0.3529, 0.3529, 1.0), lt, rise, self.cap_shadow_blur, self.cap_shadow * dis, px)
let contact_sh = Cap.contact(s.w, max(rise * 0.3, 1.0), self.cap_contact * dis)
shd = clamp(cast_sh + contact_sh, 0.0, 1.0)
halo = Cap.glow(s.w, hs, self.cap_glow)
cap_d = s.w
}
col = mix(col, col * shadow_ink, shd * (1.0 - cov))
col = col + body * halo * 0.9 * (1.0 - cov)
col = mix(col, capc, cov)
// Toned, the page's colour toned alike. The face's lift under the hand
// and its darkening when held go on after the tone, as displayed, so
// the roll-off does not swallow them. Then the fader is laid over the
// page without painting it, and this sheet's focus ring goes round the
// cap.
let ev = pow(2.0, self.cap_exposure)
let lift = (0.016 * self.hover - 0.01 * self.drag) * en * cov
let shown = Cap.tone(col, ev, self.cap_rolloff) + vec3(lift, lift, lift)
let res = nd_on_page(shown, Cap.tone(ground, ev, self.cap_rolloff))
return Finish.over(res, nd_focus(cap_d, px, self.focus * en))
}
// KNOBS. A raised cap in a sunken collar, ringed by an LED arc in a dark
// slot: the unlit arc a dark track, the lit part the accent with a short
// halo. The pointer is a pale dot that lights while the knob is turned.
let nd_cap = fn(pq: vec2, org: vec2, c: vec2, rr: float, px: float, a0: float, a1: float, start: float, sweep: float, ang: float, hover: float, drag: float, focus: float, disabled: float) -> vec4 {
let en = 1.0 - disabled
let dis = mix(1.0, 0.4, disabled)
let q = pq - c
let dl = length(q)
// The collar: a sunken ring between the arc and the cap.
let cr = rr - clamp(0.17 * rr, 1.75, 3.5)
let r = cr - clamp(0.12 * rr, 1.25, 2.5)
let kd = dl - cr
var col = nd_well(vec3(0.082, 0.09, 0.106), pq, c, vec2(cr, cr), cr, kd, px, 2.0 * cr, dis)
// The arc: the whole sweep a dark track, the value lit. On a knob of 40
// points and more the ring is the knob's feature: a two-pixel core with
// a pixel and a half of dark either side; smaller knobs keep a hairline.
let big = step(15.5, rr)
let cw = mix(1.0, 2.0 * px, big)
let gap = mix(px, 1.5 * px, big)
let track = nd_arc(pq, c, rr, start, start + sweep, 1.0)
let va = nd_arc(pq, c, rr, a0, max(a1, a0 + 0.001), cw)
let lit = step(0.0005, a1 - a0) * en
var g = nd_ground() + vec3(nd_grain(org + pq, px, 3.7))
g = mix(g, nd_led_gap(g, track, px, gap, nd_accent(), 0.0, 0.0), mix(0.75, 1.0, big))
g = mix(g, nd_led_gap(g, va, px, gap, nd_accent(), 1.0, 1.0), lit)
let ga = 1.0 - Finish.cover(kd, px)
// The ground round the collar, with the arc on it, is drawn opaque only
// where the arc or its halo is; the rest is left to the page.
let arc_a = max(Finish.cover(track - gap - 0.1, px), (1.0 - smoothstep(0.0, 2.3, max(va - gap, 0.0))) * lit) * ga
var acc = vec4(g * arc_a, arc_a)
// The collar, its lip on the page below it.
col = col + vec3(nd_grain(org + pq, px, 1.9))
let wa = Finish.cover(kd, px)
acc = Finish.over(acc, vec4(col * wa, wa))
acc = Finish.over(acc, nd_lip(pq, c, vec2(cr, cr), kd, px, en))
// The cap and what it throws into the collar.
let kh = vec2(r, r)
let d = dl - r
acc = Finish.over(acc, nd_cast(pq, c, kh, r, 1.2 * r, dis, en * (1.0 + 0.4 * hover), dis))
let v = clamp(q.y / r * 0.5 + 0.5, 0.0, 1.0)
let gg = (mix(-0.05, 0.035, smoothstep(0.0, 0.85, v)) + 0.016 * hover - 0.01 * drag) * en
var kc = nd_ground() + vec3(gg, gg, gg)
let upper = smoothstep(0.15, -0.6, q.y / r)
let lower = smoothstep(0.3, 0.9, q.y / r)
let inner = Finish.band(d, px, 2.0 * px, px)
kc = mix(kc, vec3(1.0, 1.0, 1.0), inner * upper * 0.12 * en)
kc = mix(kc, vec3(1.0, 1.0, 1.0), inner * lower * 0.06 * en)
// The pointer: a small pale dot set in from the rim, lit in the accent
// while the knob is turned or keyed.
let dir = vec2(-sin(ang), cos(ang))
let pr = max(0.08 * r, 1.1)
let pd = length(q - dir * r * 0.7) - pr
let pink = mix(mix(vec3(0.82, 0.85, 0.89), nd_accent(), max(drag, focus) * en), vec3(0.3, 0.31, 0.34), disabled)
kc = kc + pink * 0.12 * (1.0 - smoothstep(0.0, 1.5, max(pd, 0.0))) * step(0.0, pd) * en
kc = mix(kc, pink, Finish.cover(pd, px))
kc = kc + vec3(nd_grain(org + pq, px, 1.9))
kc = mix(kc, nd_line(), Finish.band(d, 0.0, px, px) * 0.8 * dis)
let a = Finish.cover(d, px)
acc = Finish.over(acc, vec4(kc * a, a))
return Finish.over(acc, nd_focus(dl - rr - 0.5, px, focus * en))
}
// The panel knob: the arc in the biggest circle the box holds, pinned to
// the bottom under its legend.
mod.widgets.RotaryKnob.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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 dia = min(self.rect_size.x, self.rect_size.y)
let c = vec2(self.rect_size.x * 0.5, self.rect_size.y - dia * 0.5)
let rr = dia * 0.5 - 3.0
let one = PI / 180.0
let sweep = 2.0 * PI - self.gap * one
let start = self.gap * one * 0.5
let val = start + sweep * self.slide_pos
let org_a = start + sweep * self.origin_pos
let a0 = mix(start, min(org_a, val), self.arc_origin)
let a1 = mix(val, max(org_a, val), self.arc_origin)
return nd_cap(pq, self.rect_pos, c, rr, px, a0, a1, start, sweep, val, self.hover, self.drag, self.focus, self.disabled)
}
mod.widgets.RotaryKnob.width = 44.0
mod.widgets.RotaryKnob.height = 60.0
mod.widgets.RotaryFlat.text_input.draw_bg.color = #00000000
// The readout is plain digits like the slider values: the selection the
// slider keeps over its number shows only while the field is keyed.
mod.widgets.RotaryFlat.text_input.draw_selection.color = #0000
mod.widgets.RotaryFlat.text_input.draw_selection.color_hover = #0000
mod.widgets.RotaryFlat.text_input.draw_selection.color_down = #0000
mod.widgets.RotaryFlat.text_input.draw_selection.color_empty = #0000
mod.widgets.RotaryFlat.text_input.draw_selection.color_disabled = #0000
mod.widgets.RotaryFlat.text_input.draw_text.color = #b9bfca
mod.widgets.RotaryFlat.text_input.min_height = 0.0
mod.widgets.RotaryFlat.text_input.padding.top = 0.0
mod.widgets.RotaryFlat.text_input.padding.bottom = 0.0
mod.widgets.Rotary.text_input.min_height = 0.0
mod.widgets.Rotary.text_input.padding.top = 0.0
mod.widgets.Rotary.text_input.padding.bottom = 0.0
mod.widgets.RotaryFlat.label_walk.margin.top = 4.0
mod.widgets.Rotary.label_walk.margin.top = 4.0
// The legend over a knob is a caption: small capitals in the dim label
// grey, semibold.
mod.widgets.RotaryKnob.draw_text.color = #9a9fa8
mod.widgets.RotaryKnob.draw_text.text_style = mod.theme.font_bold{font_size: 7.0}
// The dial: the same cap, collar and arc, larger, under its legend and
// readout.
mod.widgets.RotaryFlat.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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 top = 24.0
let dia = min(self.rect_size.x, self.rect_size.y - top)
let c = vec2(self.rect_size.x * 0.5, top + dia * 0.5)
let rr = dia * 0.5 - 4.0
let one = PI / 180.0
let sweep = 2.0 * PI - self.gap * one
let start = self.gap * one * 0.5
let val = start + sweep * self.slide_pos
let org_a = start + sweep * self.origin_pos
let a0 = mix(start, min(org_a, val), self.arc_origin)
let a1 = mix(val, max(org_a, val), self.arc_origin)
return nd_cap(pq, self.rect_pos, c, rr, px, a0, a1, start, sweep, val, self.hover, self.drag, self.focus, self.disabled)
}
// PROGRESS. A 6 point slot with its lip; the fill a one-point LED line in
// its black groove, with a hot dot at the leading end. Without a value the
// bar holds one lit segment, 30 percent of the slot, in its middle: a
// shader may not read the clock, so the sweep stands still.
mod.widgets.ProgressBarFlat.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let org = self.rect_pos
let w = self.rect_size.x
let t = min(6.0, self.rect_size.y - 1.0)
let cy = Finish.snap(org.y + (self.rect_size.y - 1.0) * 0.5, px) - org.y
let x0 = w * self.start
let x1 = w * self.end
let gc = vec2((x0 + x1) * 0.5, cy)
let gh = vec2((x1 - x0) * 0.5, t * 0.5)
let gd = Material.sd_box(p, gc, gh, t * 0.5)
var col = nd_slot(p, org, gc, gh, 4.0 * px, px, 1.0)
let xv = w * clamp(self.value, self.start, self.end)
let det = step(0.0, self.value)
let fa0 = mix(x0 + w * 0.35, x0 + t * 0.5, det)
let fa1 = mix(x0 + w * 0.65, max(xv - t * 0.5, x0 + t * 0.5), det)
let ink = mix(nd_accent(), self.fill_color().rgb, step(0.5, self.intent))
let fd = nd_seg(p, vec2(fa0, cy), vec2(fa1, cy)) - px
let show = mix(1.0, step(x0 + 0.5, xv), det)
col = col + ink * 0.2 * (1.0 - smoothstep(0.0, 2.3, max(fd, 0.0))) * (1.0 - smoothstep(0.0, 2.3, max(fd, 0.0))) * step(0.0, fd) * show
col = mix(col, ink, Finish.cover(fd, px) * show)
// The hot end.
let hd = length(p - vec2(fa1, cy)) - 1.5
col = mix(col, mix(ink, vec3(1.0, 1.0, 1.0), 0.5), Finish.cover(hd, px) * show * det)
col = col + vec3(nd_grain(org + p, px, 1.9))
let ga = Finish.cover(gd, px) * step(0.5, self.track)
var acc = vec4(col * ga, ga)
acc = Finish.over(acc, nd_lip(p, gc, gh, gd, px, step(0.5, self.track)))
return acc * self.opacity
}
mod.widgets.ProgressBarFlat.height = 8.0
mod.widgets.ProgressBarFlat.draw_bg.thickness = 6.0
// LEVEL METER. A sunken slot holding a ladder of flat cells, 3 points
// along at a 5 point pitch: lit in the accent up to eight tenths, coral
// above; unlit cells are dark holes below the ground, never a dim accent.
// The peak holds as one lit cell; the lamp at the end is a round cell that
// turns coral when it latches. Nothing here glows.
mod.widgets.LevelMeter.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let org = self.rect_pos
let v = self.vertical
let along = mix(self.rect_size.x, self.rect_size.y, v)
let across = mix(self.rect_size.y, self.rect_size.x, v)
let q = vec2(mix(p.x, self.rect_size.y - p.y, v), mix(p.y, p.x, v))
let lamp_room = self.lamp * (across + self.lamp_gap)
let bar = max(4.0, along - lamp_room)
let r = min(2.5, across * 0.5)
let gc = vec2(bar * 0.5, across * 0.5)
let gh = vec2(bar * 0.5, across * 0.5)
let gd = Material.sd_box(q, gc, gh, r)
var col = nd_well(vec3(0.106, 0.114, 0.133), q, gc, gh, r, gd, px, 10.0, 1.0)
let pad = 1.5
let span = max(1.0, bar - pad * 2.0)
let pitch = 5.0
let n = floor((q.x - pad) / pitch)
let cx0 = pad + n * pitch
let cc = vec2(cx0 + 1.5, across * 0.5)
let cd = Material.sd_box(q, cc, vec2(1.5, across * 0.5 - pad), 0.75)
let share = clamp((cx0 + 1.5) / span, 0.0, 1.0)
let zone = mix(nd_accent(), vec3(0.878, 0.443, 0.424), step(0.8, share))
let inside = step(pad, q.x) * step(cx0 + 3.0, bar - pad + 0.01)
let lit = step(cx0 + 1.5, pad + span * self.level)
let hx = pad + span * self.hold
let held = step(0.002, self.hold) * step(cx0, hx) * step(hx, cx0 + pitch)
let cellc = mix(vec3(0.086, 0.094, 0.102), zone, max(lit, held))
col = mix(col, cellc, Finish.cover(cd, px) * inside)
col = col + vec3(nd_grain(org + p, px, 1.9))
let ga = Finish.cover(gd, px)
var acc = vec4(col * ga, ga)
acc = Finish.over(acc, nd_lip(q, gc, gh, gd, px, 1.0 - v))
// The lamp.
let lr = across * 0.5
let lc = vec2(along - lr, across * 0.5)
let ld = length(q - lc) - lr
var lcol = nd_well(vec3(0.075, 0.082, 0.098), q, lc, vec2(lr, lr), lr, ld, px, 8.0, 1.0)
lcol = mix(lcol, vec3(0.878, 0.443, 0.424), self.over * Finish.cover(ld + 1.0, px))
let la = Finish.cover(ld, px) * self.lamp
acc = Finish.over(acc, vec4(lcol * la, la))
return acc * self.opacity
}
mod.widgets.LevelMeter.height = 9.0
mod.widgets.LevelMeterColumn.width = 9.0
// SCROLL BAR. An 8 point slot at half depth with a 6 point raised pill
// thumb; dragged, a one-pixel accent line runs down the thumb.
mod.widgets.ScrollBar.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let v = self.is_vertical
let along = mix(self.rect_size.x, self.rect_size.y, v)
let across = mix(self.rect_size.y, self.rect_size.x, v)
let q = vec2(mix(p.x, p.y, v), mix(p.y, p.x, v))
let w = min(8.0, across)
let mb = across * 0.5
let tc = vec2(along * 0.5, mb)
let th = vec2(along * 0.5, w * 0.5)
let td = Material.sd_box(q, tc, th, w * 0.5)
let col = nd_well(vec3(0.106, 0.114, 0.133), q, tc, th, w * 0.5, td, px, 8.0, 0.6)
let ta = Finish.cover(td, px) * 0.8
var acc = vec4(col * ta, ta)
let s0 = along * self.norm_scroll + 1.0
let s1 = along * (self.norm_scroll + self.norm_handle) - 1.0
let hc = vec2((s0 + s1) * 0.5, mb)
let hh = vec2(max((s1 - s0) * 0.5, 3.0), 3.0)
let hd = Material.sd_box(q, hc, hh, 3.0)
let sh = Finish.drop(q, hc, hh, 3.0, vec2(0.0, 0.8), 1.2) * 0.45
acc = Finish.over(acc, vec4(0.0, 0.0, 0.0, sh))
let g = 0.012 + 0.015 * self.hover
var hcol = nd_ground() + vec3(g, g, g) + vec3(0.03, 0.03, 0.03) * clamp((q.y - (mb - 3.0)) / 6.0, 0.0, 1.0)
hcol = mix(hcol, nd_accent(), step(abs(q.y - mb), px * 0.5) * step(abs(q.x - hc.x), hh.x - 3.0) * self.drag * 0.6)
hcol = mix(hcol, nd_line(), Finish.band(hd, 0.0, px, px) * 0.6)
let ha = Finish.cover(hd, px)
acc = Finish.over(acc, vec4(hcol * ha, ha))
return acc * self.opacity
}
mod.widgets.ScrollBar.bar_size = 10.0
// SEGMENTED CONTROL. One raised body with a 7 point corner; the answer in
// use is a segment pressed into it, flat and a step darker, with its label
// in the accent.
mod.widgets.SegmentedControlFlat.segment_height = 22.0
mod.widgets.SegmentedControlFlat.segment_padding = 12.0
mod.widgets.SegmentedControlFlat.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.SegmentedControlFlat.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return nd_button(pq, self.rect_size, self.rect_pos, px, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0))
}
mod.widgets.SegmentedControlFlat.draw_pill.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let c = self.rect_size * 0.5
let h = self.rect_size * 0.5
let d = Material.sd_box(p, c, h, 5.0)
let deep = Finish.inset(p, c, h, 5.0, vec2(0.0, 2.0), 2.6)
var col = mix(nd_ground() - vec3(0.045, 0.045, 0.045), vec3(0.0, 0.0, 0.0), deep * 0.5)
col = col + vec3(nd_grain(self.rect_pos + p, px, 1.9))
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * 0.75)
let a = Finish.cover(d, px)
return vec4(col * a, a)
}
mod.widgets.SegmentedControlFlat.draw_text.color = #a3a9b4
// The selected label takes the ink of the control's intent family, not
// draw_text_selected's colour, so the family's container ink is set here.
mod.widgets.SegmentedControlFlat.intent = mod.widgets.BadgeIntent.Primary
mod.widgets.SegmentedControlFlat.palette.on_primary_container = #45ebe0
mod.widgets.SegmentedControl.palette.on_primary_container = #45ebe0
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #45ebe0
mod.widgets.SegmentedControlFlat.draw_text_selected.text_style = nd_medium
// TABS. The strip is one raised body; the tab in use is pressed into it,
// flat and a step darker; between the tabs runs a seam, one pixel dark
// with one pixel of light beside it.
mod.widgets.Tabs.height = 26.0
mod.widgets.Tabs.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Tabs.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return nd_button(pq, self.rect_size, self.rect_pos, px, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0), 1.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0))
}
mod.widgets.Tabs.draw_tab.pixel = fn() {
return vec4(0.0, 0.0, 0.0, 0.0)
}
mod.widgets.Tabs.draw_tab_hover.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let c = self.rect_size * 0.5
let h = self.rect_size * 0.5 - vec2(1.0, 1.0)
let d = Material.sd_box(p, c, h, 4.0)
let a = Finish.cover(d, px) * 0.035
return vec4(a, a, a, a)
}
mod.widgets.Tabs.draw_tab_active.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let c = self.rect_size * 0.5
let h = self.rect_size * 0.5 - vec2(2.0, 2.0)
let d = Material.sd_box(p, c, h, 5.0)
let deep = Finish.inset(p, c, h, 5.0, vec2(0.0, 2.0), 2.6)
var col = mix(nd_ground() - vec3(0.045, 0.045, 0.045), vec3(0.0, 0.0, 0.0), deep * 0.5)
col = col + vec3(nd_grain(self.rect_pos + p, px, 1.9))
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * 0.75)
let a = Finish.cover(d, px)
return vec4(col * a, a)
}
mod.widgets.Tabs.draw_sep.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(1.0, 0.0), self.rect_size + vec2(2.0, 0.0))
}
mod.widgets.Tabs.draw_sep.pixel = fn() {
let p = self.pos * (self.rect_size + vec2(2.0, 0.0)) - vec2(1.0, 0.0)
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let x = Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x
let iny = step(3.0, p.y) * step(p.y, self.rect_size.y - 3.0)
let dk = step(x - px, p.x) * step(p.x, x) * 0.8 * iny
let lt = step(x, p.x) * step(p.x, x + px) * 0.05 * iny
var acc = vec4(nd_line() * dk, dk)
return Finish.over(acc, vec4(lt, lt, lt, lt))
}
// Close and add: 7 point marks at a one-point stroke.
mod.widgets.Tabs.draw_mark.color = #9a9fa8
mod.widgets.Tabs.draw_mark.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let c = self.rect_size * 0.5
let e = 3.0
let a = mix(0.7853981634, 0.0, self.plus)
let r = e * mix(1.2, 1.0, self.plus)
let ax = cos(a) * r
let ay = sin(a) * r
let d = min(nd_seg(p, c - vec2(ax, ay), c + vec2(ax, ay)), nd_seg(p, c + vec2(ay, -ax), c + vec2(-ay, ax)))
let k = Finish.cover(d - 0.55, px) * self.color.w
return vec4(self.color.rgb * k, k)
}
mod.widgets.Tabs.draw_text.color = #a3a9b4
mod.widgets.Tabs.draw_text_active.color = #45ebe0
mod.widgets.Tabs.draw_text_active.text_style = nd_medium
// RADIO TABS. Flat on the page at rest, rising under the pointer; the one
// in use is pressed in, with its label in the accent.
mod.widgets.RadioButtonTabFlat.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RadioButtonTabFlat.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
return nd_button(pq, self.rect_size, self.rect_pos, px, self.hover * (1.0 - self.active), max(self.down, self.active), self.focus, self.disabled, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0), 0.0, 0.0, 0.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0))
}
mod.widgets.RadioButtonTabFlat.clip_x = false
mod.widgets.RadioButtonTabFlat.clip_y = false
mod.widgets.RadioButtonTabFlat.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 6.5 bottom: 6.5}
// The label stands in the middle: the stock walk keeps room at its left for
// the circle a tab does not draw.
mod.widgets.RadioButtonTabFlat.label_walk.margin = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
mod.widgets.RadioButtonTabFlat.draw_text.color = #a3a9b4
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #45ebe0
// CHIPS. A small raised pill, pressed in while held; a keyed chip wears the
// focus ring.
mod.widgets.ChipFlat.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.ChipFlat.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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 c = self.rect_size * 0.5
let h = self.rect_size * 0.5
let r = min(self.radius, h.y)
let d = Material.sd_box(pq, c, h, r)
let s = self.rect_size.y
let up = 1.0 - self.down
var acc = nd_cast(pq, c, h, r, s, up, up * (1.0 + 0.4 * self.hover), max(up, 0.45 * self.down))
var col = nd_face(pq, c, h, r, d, px, up, 0.014 * self.hover, up * (1.0 + 0.3 * self.hover))
let deep = Finish.inset(pq, c, h, r, vec2(0.0, 0.08 * s), 0.1 * s)
col = mix(col, mix(nd_ground() - vec3(0.045, 0.045, 0.045), vec3(0.0, 0.0, 0.0), deep * 0.5), self.down)
col = col + vec3(nd_grain(self.rect_pos + pq, px, 1.9))
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * 0.75)
let a = Finish.cover(d, px)
acc = Finish.over(acc, vec4(col * a, a))
return Finish.over(acc, nd_focus(d, px, self.keyed))
}
mod.widgets.ChipFlat.draw_text.color = #b9bfca
// SURFACES. A panel is the ground raised by a shallow step with a 10 point
// corner: a soft fall, the contact line, a faint light line on its top
// edge, and the ground's own grain. An inset panel is the same step pressed
// in: the contact line, the page's shadow falling in at the top, and the
// lit lip at the foot.
let nd_panel = fn(pq: vec2, size: vec2, org: vec2, px: float, face: vec3, r: float, raised: float, sunken: float, lift: float) -> vec4 {
let c = size * 0.5
let h = size * 0.5
let rr = min(r, min(h.x, h.y))
let d = Material.sd_box(pq, c, h, rr)
let fall = Finish.drop(pq, c, h, rr, vec2(0.5, 3.0), 4.0) * (0.4 + 0.08 * lift) * raised
let con = Finish.drop(pq, c, h, rr, vec2(0.0, 0.8), 0.8) * 0.45 * raised
let hl = Finish.drop(pq, c, h, rr, vec2(-0.6, -1.4), 1.8) * 0.05 * raised
var acc = vec4(vec3(0.039, 0.043, 0.055) * fall, fall)
acc = Finish.over(acc, vec4(0.0, 0.0, 0.0, con))
acc = Finish.over(acc, vec4(hl, hl, hl, hl))
let lf = 0.008 * raised + 0.006 * lift
var col = face + vec3(lf, lf, lf)
let top = 1.0 - smoothstep(rr * 0.3, rr + 0.6, pq.y)
col = mix(col, vec3(1.0, 1.0, 1.0), Finish.band(d, px, 2.0 * px, px) * top * 0.07 * raised)
let tray = face - vec3(0.02, 0.02, 0.02)
let dk = Finish.inset(pq, c, h, rr, vec2(0.0, 1.5), 2.2)
let tcol = mix(tray, vec3(0.0, 0.0, 0.0), dk * 0.4)
col = mix(col, tcol, sunken)
// A raised panel carries the ground's grain; the floor of a sunken one
// is knit, a two-pixel checker a couple of levels either side.
let kq = floor((org + pq) / (2.0 * px))
let knit = ((kq.x + kq.y) - 2.0 * floor((kq.x + kq.y) * 0.5) - 0.5) * 2.0 * 2.5 / 255.0
col = col + vec3(mix(nd_grain(org + pq, px, 3.7), knit, step(0.5, sunken)))
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * mix(0.6, 0.75, sunken) * max(raised, sunken))
let a = Finish.cover(d, px)
acc = Finish.over(acc, vec4(col * a, a))
return Finish.over(acc, nd_lip(pq, c, h, d, px, sunken * 0.8))
}
mod.widgets.RoundedView.clip_x = false
mod.widgets.RoundedView.clip_y = false
mod.widgets.PanelView.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.PanelView.draw_bg.pixel = fn() {
let m = 12.0
let pq = 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)
return nd_panel(pq, self.rect_size, self.rect_pos, px, nd_ground(), 10.0, 1.0 - self.sunken, self.sunken, 0.0)
}
// A card is a raised panel too; a pressable one lifts under the pointer.
mod.widgets.Card.radius = 10.0
mod.widgets.Card.clip_x = false
mod.widgets.Card.clip_y = false
mod.widgets.Card.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.Card.draw_bg.pixel = fn() {
let m = 12.0
let pq = 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)
// Elevated stands up; filled is a shallow tray; outlined lies flat
// inside the contact line. Under the pointer each rises a step.
let lift = self.hover * (1.0 - self.down)
let outlined = step(0.01, self.border_size)
let raised = max(clamp(self.shadow_radius / 4.0, 0.0, 1.0), 0.6 * lift) * (1.0 - self.down)
let sunk = max((1.0 - clamp(self.shadow_radius / 4.0, 0.0, 1.0)) * (1.0 - outlined) * (1.0 - lift), self.down)
var acc = nd_panel(pq, self.rect_size, self.rect_pos, px, nd_ground(), self.border_radius, raised, sunk, lift)
let c = self.rect_size * 0.5
let d = Material.sd_box(pq, c, c, min(self.border_radius, min(c.x, c.y)))
let eng = Finish.band(d, 0.0, px, px) * outlined * (1.0 - raised) * (1.0 - sunk) * 0.7
acc = Finish.over(acc, vec4(nd_line() * eng, eng))
return Finish.over(acc, nd_lip(pq, c, c, d, px, outlined * (1.0 - raised) * (1.0 - sunk)))
}
// The rounded view keeps whatever colour it is given; a view the colour of
// the page is a surface of the page, and stands up off it like a panel.
mod.widgets.RoundedView.draw_bg.vertex = fn() {
let m = 10.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RoundedView.draw_bg.pixel = fn() {
let m = 10.0
let pq = 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)
var fill = self.color
if self.color_2.x > -0.5 {
let dir = if self.gradient_fill_horizontal > 0.5 self.pos.x else self.pos.y
fill = mix(self.color, self.color_2, dir)
}
let lower = vec2(self.border_inset.x, self.border_inset.y)
let upper = self.rect_size - vec2(self.border_inset.z, self.border_inset.w)
let c = (lower + upper) * 0.5
let h = max((upper - lower) * 0.5, vec2(0.5, 0.5))
// The corner as the stock face draws it, so a view keeps its shape.
let r = min(2.0 * max(1.0, self.border_radius), min(h.x, h.y))
let d = Material.sd_box(pq, c, h, r)
let near = (1.0 - smoothstep(0.02, 0.06, abs(Finish.lum(fill.rgb) - 0.184))) * step(0.99, fill.w)
let fall = Finish.drop(pq, c, h, r, vec2(0.4, 2.2), 3.0) * 0.4 * near
let hl = Finish.drop(pq, c, h, r, vec2(-0.5, -1.2), 1.6) * 0.05 * near
var acc = vec4(vec3(0.039, 0.043, 0.055) * fall, fall)
acc = Finish.over(acc, vec4(hl, hl, hl, hl))
let a = Finish.cover(d, px)
var col = fill.rgb
let top = 1.0 - smoothstep(r * 0.3, r + 0.6, pq.y - lower.y)
col = mix(col, vec3(1.0, 1.0, 1.0), Finish.band(d, px, 2.0 * px, px) * top * 0.07 * near)
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * 0.6 * near)
acc = Finish.over(acc, vec4(col * a * fill.w, a * fill.w))
let bd = Finish.band(d, 0.0, max(self.border_size, 0.0), px) * step(0.001, self.border_size) * self.border_color.w
return Finish.over(acc, vec4(self.border_color.rgb * bd, bd))
}
// LIST ROWS. No shadow per row. Between rows a seam (one pixel dark over
// one pixel light) inset 12; under the pointer a faint lift; the selected
// row a sunken strip with a lit bar at its left end, half the row tall.
mod.widgets.ListItem.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let org = self.rect_pos
let w = self.rect_size.x
let h = self.rect_size.y
// The strip is set in from the row: 6 points at the left (the seams
// begin 12 in) and 10 at the right, clear of the scroll bar.
let c = vec2((6.0 + w - 10.0) * 0.5, h * 0.5)
let hh = vec2((w - 16.0) * 0.5, min(h * 0.5 - 4.0, 12.0))
let d = Material.sd_box(p, c, hh, 4.0)
let en = 1.0 - self.disabled
let sel = self.selected * en
let deep = Finish.inset(p, c, hh, 4.0, vec2(0.0, 1.8), 2.4)
var col = mix(nd_ground() - vec3(0.045, 0.045, 0.045), vec3(0.0, 0.0, 0.0), deep * 0.5)
col = mix(col, nd_line(), Finish.band(d, 0.0, px, px) * 0.6)
let bx = c.x - hh.x + 3.5
let bd = nd_seg(p, vec2(bx, c.y - hh.y * 0.5), vec2(bx, c.y + hh.y * 0.5)) - 0.5
col = nd_led(col, bd, px, nd_accent(), 1.0, 1.0)
col = col + vec3(nd_grain(org + p, px, 1.9))
let wa = Finish.cover(d, px) * max(sel, self.down * 0.6 * en)
var acc = vec4(col * wa, wa)
let hv = Finish.cover(d, px) * self.hover * en * (1.0 - sel) * 0.03
acc = Finish.over(acc, vec4(hv, hv, hv, hv))
let yb = Finish.snap(org.y + h, px) - org.y
let inx = step(12.0, p.x) * step(p.x, w - 12.0) * (1.0 - sel)
let dk = step(yb - 2.0 * px, p.y) * step(p.y, yb - px) * inx * 0.35
let lt = step(yb - px, p.y) * step(p.y, yb) * inx * 0.04
acc = Finish.over(acc, vec4(0.0, 0.0, 0.0, dk))
acc = Finish.over(acc, vec4(lt, lt, lt, lt))
return acc
}
mod.widgets.ListItem.draw_text.color = #b9bfca
// BADGE. A neutral badge is a small sunken cell with light digits; a
// coloured one (an error count, say) keeps its colour, flat.
let nd_badge = fn(pq: vec2, size: vec2, org: vec2, px: float, fill: vec4, radius: float, bw: float, bc: vec4) -> vec4 {
let c = size * 0.5
let h = size * 0.5
let r = min(radius, min(h.x, h.y))
let d = Material.sd_box(pq, c, h, r)
let neutral = 1.0 - step(0.12, abs(Finish.lum(fill.rgb) - 0.184) + length(fill.rgb - vec3(Finish.lum(fill.rgb), Finish.lum(fill.rgb), Finish.lum(fill.rgb))))
var col = mix(fill.rgb, nd_well(vec3(0.075, 0.082, 0.098), pq, c, h, r, d, px, size.y, 1.0), neutral)
col = col + vec3(nd_grain(org + pq, px, 1.9))
let a = Finish.cover(d, px) * max(fill.w, neutral)
var acc = vec4(col * a, a)
acc = Finish.over(acc, nd_lip(pq, c, h, d, px, neutral))
let bd = Finish.band(d, 0.0, max(bw, 0.0), px) * step(0.001, bw) * bc.w
return Finish.over(acc, vec4(bc.rgb * bd, bd))
}
mod.widgets.BadgeFlat.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.BadgeFlat.draw_bg.pixel = fn() {
let m = 2.0
let pq = 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)
return nd_badge(pq, self.rect_size, self.rect_pos, px, self.color, self.radius, self.border_size, self.border_color)
}
mod.widgets.Badge.draw_bg.pixel = fn() {
let m = 2.0
let pq = 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)
return nd_badge(pq, self.rect_size, self.rect_pos, px, self.color, self.radius, self.border_size, self.border_color)
}
mod.widgets.BadgeFlat.palette.neutral = #2c2f36
mod.widgets.BadgeFlat.palette.on_neutral = #d7dde6
mod.widgets.Badge.palette.neutral = #2c2f36
mod.widgets.Badge.palette.on_neutral = #d7dde6
mod.widgets.BadgeFlat.draw_text.text_style = mod.theme.font_regular{font_size: 7.5 font_family: mod.theme.font_regular.font_family{ibm_plex_text := FontMember{res: crate_resource("makepad_widgets:resources/Inter.ttf") weight: 500.0 asc: 0.0 desc: 0.0}}}
mod.widgets.Badge.draw_text.text_style = mod.theme.font_regular{font_size: 7.5 font_family: mod.theme.font_regular.font_family{ibm_plex_text := FontMember{res: crate_resource("makepad_widgets:resources/Inter.ttf") weight: 500.0 asc: 0.0 desc: 0.0}}}
// THE OPEN LIST. A raised panel with a 6 point corner; the row under the
// pointer lifts a little, the chosen row is a sunken strip with a lit bar
// at its left end and its label in the accent.
mod.widgets.PopupMenuFlat.draw_bg.vertex = fn() {
let m = 12.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.PopupMenuFlat.draw_bg.pixel = fn() {
let m = 12.0
let pq = 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)
return nd_panel(pq, self.rect_size, self.rect_pos, px, nd_ground() + vec3(0.012, 0.012, 0.014), 6.0, 1.0, 0.0, 0.0)
}
mod.widgets.PopupMenuFlat.clip_x = false
mod.widgets.PopupMenuFlat.clip_y = false
mod.widgets.PopupMenuItem.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let c = self.rect_size * 0.5
let h = self.rect_size * 0.5 - vec2(0.0, 1.0)
let d = Material.sd_box(p, c, h, 3.0)
let en = 1.0 - self.disabled
let deep = Finish.inset(p, c, h, 3.0, vec2(0.0, 1.6), 2.2)
var col = mix(nd_ground() - vec3(0.035, 0.035, 0.035), vec3(0.0, 0.0, 0.0), deep * 0.5)
let bx = 3.0
let bd = nd_seg(p, vec2(bx, c.y - h.y * 0.45), vec2(bx, c.y + h.y * 0.45)) - 0.5
col = mix(col, nd_led(col, bd, px, nd_accent(), 1.0, 1.0), self.active)
let wa = Finish.cover(d, px) * self.active * en
var acc = vec4(col * wa, wa)
let hv = Finish.cover(d, px) * self.hover * en * 0.04
return Finish.over(acc, vec4(hv, hv, hv, hv))
}
mod.widgets.PopupMenuItem.draw_text.color_active = #45ebe0
// 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.CheckBoxCustom.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)
}
mod.widgets.glass.RadioButton.draw_bg.vertex = fn() {
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
}
// THE PAGE. The window's own ground: the navy-charcoal, a long fall from
// the lit top left (a little bluer there) to the bottom right, and a fine
// free grain that doubles as the dither for the fall.
mod.widgets.Window.show_bg = true
mod.widgets.Window.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
let t = (self.pos.x + self.pos.y) * 0.5
let g = 0.02 - 0.055 * t
let tint = vec3(0.0, 2.0, 3.0) / 255.0 * (1.0 - t)
return vec4(nd_ground() + vec3(g, g, g) + tint + vec3(nd_grain(self.rect_pos + p, px, 3.7)), 1.0)
}
// A ROW OF FIELDS IS ONE HEIGHT. The box metrics are stated once here and
// handed to each field below, so the fields, the drop-down and the buttons
// stand in one 26 point row.
let field_height = 26.0
let field_pad_v = 6.0
// A pill: the plate's corner is half its height.
let field_radius = 13.0
// No borders: the relief draws the edge.
let field_border = 0.0
// 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 takes
// the height alone: 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.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 = 7.0
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 = #2c2f36
// ---------------------------------------------------------------- band
// THE BAND is the sheet's own slider with two thumbs: the same track, the
// same lit value and the same thumb as the Slider, the value lit between
// the two thumbs, its quad grown as the slider's is so the thumbs keep
// their shape. The widget's grab geometry is set to the thumbs' own, so
// what is drawn is what is dragged.
mod.widgets.RangeSliderFlat.clip_x = false
mod.widgets.RangeSliderFlat.clip_y = false
mod.widgets.RangeSliderFlat.track_inset = 1.0
mod.widgets.RangeSliderFlat.handle_size = 16.0
mod.widgets.RangeSliderFlat.draw_bg.vertex = fn() {
let m = 8.0
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
}
mod.widgets.RangeSliderFlat.draw_bg.pixel = fn() {
let m = 8.0
let pq = 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 org = self.rect_pos
let en = 1.0 - self.disabled
let dis = mix(1.0, 0.4, self.disabled)
let top = self.label_px
let cy = Finish.snap(org.y + top + (self.rect_size.y - top) * 0.5, px) - org.y
let hr = 8.0
let x0 = hr + 1.0
let x1 = self.rect_size.x - hr - 1.0
let gc = vec2(self.rect_size.x * 0.5, cy)
let gh = vec2(self.rect_size.x * 0.5 - 1.0, 5.0 * px)
let gd = Material.sd_box(pq, gc, gh, gh.y)
var col = nd_slot(pq, org, gc, gh, 4.0 * px, px, dis)
let fx0 = mix(x0, x1, self.start_pos)
let fx1 = mix(x0, x1, self.end_pos)
let fd = nd_seg(pq, vec2(fx0, cy), vec2(fx1, cy)) - px
let show = step(0.25, fx1 - fx0)
let lit = col + nd_accent() * 0.2 * (1.0 - smoothstep(0.0, 2.3, max(fd, 0.0))) * (1.0 - smoothstep(0.0, 2.3, max(fd, 0.0))) * en
col = mix(col, mix(lit, mix(nd_accent(), vec3(0.29, 0.306, 0.337), self.disabled), Finish.cover(fd, px)), show)
col = col + vec3(nd_grain(org + pq, px, 1.9))
let ga = Finish.cover(gd, px)
var acc = vec4(col * ga, ga)
acc = Finish.over(acc, nd_lip(pq, gc, gh, gd, px, 0.875 * en))
acc = nd_thumb(acc, pq, org, vec2(fx0, cy), hr, px, self.hot_start, self.drag * self.hot_start, self.focus, self.disabled)
return nd_thumb(acc, pq, org, vec2(fx1, cy), hr, px, self.hot_end, self.drag * self.hot_end, self.focus, self.disabled)
}
// ---------------------------------------------------------------- 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, its size and shape, its quad and its label
// gap (the library's own faces for them are flat boxes sized for
// another face). The rocker turns on a click; the slide switch follows a
// drag, as the toggle does.
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.clip_x = false
mod.widgets.ToggleSlide.clip_x = false
mod.widgets.ToggleRocker.clip_y = false
mod.widgets.ToggleSlide.clip_y = false
mod.widgets.ToggleRocker.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
mod.widgets.ToggleSlide.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
mod.widgets.ToggleRocker.draw_bg.size = 18.0
mod.widgets.ToggleSlide.draw_bg.size = 18.0
mod.widgets.ToggleRocker.draw_bg.pill_aspect = 1.889
mod.widgets.ToggleSlide.draw_bg.pill_aspect = 1.889
mod.widgets.ToggleRocker.padding = mod.turtle.Inset{left: 0.0 right: 4.0 top: 4.0 bottom: 4.0}
mod.widgets.ToggleSlide.padding = mod.turtle.Inset{left: 0.0 right: 4.0 top: 4.0 bottom: 4.0}
mod.widgets.ToggleRocker.label_walk.margin.left = 42.0
mod.widgets.ToggleSlide.label_walk.margin.left = 42.0
// THE METER sits in the same window as the readouts and the screen: the
// library's display housing drawn round its face -- the bezel ring, a shade
// of whatever ground it is set in with a device pixel of dark at its edge;
// the face with the surround's shadow falling across it from the top and a
// device pixel of black where the glass meets the bezel; the glass's quiet
// diagonal band and the light line inside its top edge; the lip of light on
// the ground under it -- with the same corner, so the meter and the
// readouts read as one family. The scale, the red zone, the needle and its
// pivot are the library's own.
mod.widgets.NeedleMeter.draw_bg.border_radius = uniform(theme.corner_radius * 0.5)
mod.widgets.NeedleMeter.draw_bg.color_bezel = uniform(theme.color_d_05)
mod.widgets.NeedleMeter.draw_bg.color_lip = uniform(theme.color_u_15)
mod.widgets.NeedleMeter.draw_bg.pixel = fn() {
let p = self.pos * self.rect_size
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 c = (lo + hi) * 0.5
let ho = (hi - lo) * 0.5
let r_out = min(self.border_radius, min(ho.x, ho.y))
let outer = Material.sd_box(p, c, ho, r_out)
let b = Finish.snap(2.0, px)
let hf = max(ho - vec2(b, b), vec2(0.5, 0.5))
let rf = max(r_out - b, 0.0)
let face_d = Material.sd_box(p, c, hf, rf)
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 = self.color.rgb
// The scale: a hairline arc and a tick at every division.
let arc = Finish.cover(abs(r - rr) - px * 0.5, px) * on_scale
let n = max(floor(self.majors + 0.5), 1.0) * max(floor(self.minors + 0.5), 1.0)
let k = floor(clamp(t, 0.0, 1.0) * n + 0.5)
let ak = (k / n - 0.5) * sweep
let perp = abs(q.x * cos(ak) + q.y * sin(ak))
let mn = max(floor(self.minors + 0.5), 1.0)
let major = 1.0 - step(0.5, k - mn * floor(k / mn + 0.0001))
let len = mix(self.tick_minor, self.tick_major, major)
let radial = max(rr - len - r, r - rr)
let tick = Finish.cover(perp - px * mix(0.5, 1.0, major), px) * Finish.cover(radial, px) * step(0.0, 0.0 - q.y * cos(ak) + q.x * sin(ak))
face = mix(face, self.color_scale.rgb, max(arc, tick))
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 cover 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.5, 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, mix(self.color_needle.rgb, self.color.rgb, 0.35), Finish.cover(pd, px))
face = mix(face, face * 0.55, Finish.band(pd, 0.0, px, px))
// The housing: the surround's shadow across the face from the top, the
// black device pixel at the glass's edge, and the glass over it all.
let insh = Finish.inset(p, c, hf, rf, vec2(0.0, 2.0), 1.8)
face = face * (1.0 - 0.55 * insh)
face = mix(face, face * 0.4, Finish.band(face_d, 0.0, px, px))
let u = (p - (c - hf)) / max(hf * 2.0, vec2(1.0, 1.0))
let diag = u.x + u.y * 0.6
let band = smoothstep(0.0, 0.12, diag) * (1.0 - smoothstep(0.28, 0.5, diag))
let top = Finish.cover(abs(p.y - (c.y - hf.y) - px * 1.5) - px * 0.5, px) * 0.6
let ga = clamp(0.03 * (band + top), 0.0, 1.0) * Finish.cover(face_d + px, px)
face = face * (1.0 - ga) + vec3(ga, ga, ga)
let bz = self.color_bezel
var col = vec4(bz.rgb * bz.a, bz.a)
col = Finish.over(col, vec4(0.0, 0.0, 0.0, 0.45 * Finish.band(outer, 0.0, px, px)))
col = mix(col, vec4(face, 1.0), Finish.cover(face_d, px))
col = col * Finish.cover(outer, px)
let lip = Finish.ring_out(outer, 0.0, px, px) * step(hi.y, p.y)
let la = self.color_lip.a * lip
return Finish.over(vec4(self.color_lip.rgb * la, la), col) * self.color.a * self.opacity
}
true