1847 lines
92 KiB
Text
1847 lines
92 KiB
Text
use mod.prelude.widgets_internal.*
|
|
|
|
// A text interface: one face, one ink, drawn in character cells.
|
|
// Box lines one device pixel wide, snapped to the pixel grid; square
|
|
// corners; state as inverse video. The only light is the halo of a lit
|
|
// element, drawn outside its box and gone within a third of a row.
|
|
mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 2.5 bottom: 2.5}
|
|
mod.widgets.ButtonFlat.min_height = 24.0
|
|
mod.widgets.ButtonFlat.spacing = 6.0
|
|
mod.widgets.Button.draw_bg.border_radius = 0.0
|
|
mod.widgets.Button.draw_bg.border_size = 0.34
|
|
// A ROW OF FIELDS IS ONE HEIGHT: the box metrics are stated once here and
|
|
// handed to each field below. Buttons stand at the same 24.
|
|
let field_height = 24.0
|
|
let field_pad_v = 2.5
|
|
let field_radius = 0.0
|
|
let field_border = 0.34
|
|
|
|
// 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 = field_radius
|
|
field_frame(mod.widgets.ValueInput)
|
|
field_frame(mod.widgets.DateField)
|
|
field_frame(mod.widgets.TimeField)
|
|
field_frame(mod.widgets.DatePicker)
|
|
field_frame(mod.widgets.DateRangePicker)
|
|
// The chrome-less input a well holds must impose no height of its own.
|
|
mod.widgets.WellInput.min_height = 0.0
|
|
mod.widgets.Window.pass.clear_color = #040603
|
|
|
|
// Plain views stop clipping, so a halo or a focus keyline may fall into the
|
|
// gap beside a control. Scroll views keep their own clip.
|
|
mod.widgets.View.clip_x = false
|
|
mod.widgets.View.clip_y = false
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SHARED PARTS. Distances in points, negative inside, y down; `px` is one
|
|
// device pixel in points.
|
|
|
|
// A square box.
|
|
let tm_box = fn(p: vec2, c: vec2, h: vec2) -> float {
|
|
let q = abs(p - c) - h
|
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0, 0.0)))
|
|
}
|
|
|
|
// The halo of a lit element: a gaussian of sigma 1.7 points outside its
|
|
// edge, at `amp` of the core at the edge, cut to nothing at 7 points.
|
|
// `amp` is held at 0.10 or under: 9 % one device pixel out, 2 % at 3 points.
|
|
let tm_halo = fn(d: float, amp: float) -> float {
|
|
let o = max(d, 0.0)
|
|
return amp * exp(-o * o / 5.78) * (1.0 - smoothstep(5.0, 7.0, o)) * step(0.0, d)
|
|
}
|
|
|
|
// Scanlines: every other device row of the screen a shade darker, counted
|
|
// in window space so they run on unbroken from one surface to the next.
|
|
let tm_scan = fn(y: float, dpi: float, depth: float) -> float {
|
|
return 1.0 - depth * step(0.5, fract(floor(y * dpi) * 0.5))
|
|
}
|
|
|
|
// The screen's grain, one value per device pixel, zero mean.
|
|
let tm_grain = fn(q: vec2, px: float, amount: float) -> float {
|
|
return Finish.grain(q / px, amount)
|
|
}
|
|
|
|
// Premultiplied source-over.
|
|
let tm_over = fn(dst: vec4, src: vec4) -> vec4 {
|
|
return src + dst * (1.0 - src.a)
|
|
}
|
|
|
|
// A box snapped to whole device pixels in window space, in the quad's
|
|
// local points: .xy the low corner, .zw the high one.
|
|
let tm_snapbox = fn(o: vec2, lo: vec2, hi: vec2, px: float) -> vec4 {
|
|
let a = vec2(Finish.snap(o.x + lo.x, px), Finish.snap(o.y + lo.y, px)) - o
|
|
let b = vec2(Finish.snap(o.x + hi.x, px), Finish.snap(o.y + hi.y, px)) - o
|
|
return vec4(a.x, a.y, b.x, b.y)
|
|
}
|
|
|
|
// Dotted line pattern: one device pixel on, two off, along `t` points.
|
|
let tm_dots = fn(t: float, px: float) -> float {
|
|
return step(fract(floor(t / px) / 3.0), 0.2)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// BUTTONS. One face for the family; the template's own colours pick the
|
|
// dressing. `frame`: 0 none, 1 single line, 2 double line, 3 dotted line.
|
|
// The quad is grown by 7 points on every side for the halo and the focus
|
|
// keyline, which cost no layout.
|
|
let tm_button = fn(pos: vec2, size: vec2, o: vec2, px: float, fill0: vec4, stroke0: vec4, frame: float, hover: float, down: float, focus: float, disabled: float, loading: float, anim: float, glow_ink: vec4, glow_rest: float, m: float) -> vec4 {
|
|
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let live = 1.0 - disabled
|
|
// A waiting button is not a held one: it goes back to the outline form,
|
|
// a line-green box round a clear face, whatever face it wore.
|
|
let fill = vec4(fill0.rgb, fill0.a * (1.0 - loading))
|
|
let stroke = mix(stroke0, vec4(0.376, 0.784, 0.243, 1.0), loading)
|
|
let sb = tm_snapbox(o, vec2(0.0, 0.0), size, px)
|
|
let c = (sb.xy + sb.zw) * 0.5
|
|
let h = (sb.zw - sb.xy) * 0.5
|
|
let d = tm_box(p, c, h)
|
|
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
// The halo: at rest only where the template asks, under the pointer a
|
|
// little, while held the most (0.08 at the edge, the family's ceiling
|
|
// being 0.10 one pixel out). Never on a disabled face.
|
|
let lit = max(fill.a, stroke.a)
|
|
let amp = max(glow_rest, max(0.05 * hover, 0.08 * down)) * live * lit
|
|
let ha = tm_halo(d, amp)
|
|
out = vec4(glow_ink.rgb * ha, ha)
|
|
|
|
// The face, flat, with the hotter top device row a lit block carries.
|
|
let inside = Finish.cover(d, px)
|
|
let top = Finish.band(p.y - sb.y, -px, 0.0, px) * step(0.5, fill.a) * 0.08
|
|
let face = vec4(mix(fill.rgb, vec3(1.0, 1.0, 1.0), top) , fill.a)
|
|
out = tm_over(out, vec4(face.rgb * face.a * inside, face.a * inside))
|
|
|
|
// The box line.
|
|
var line = Finish.band(d, 0.0, px, px)
|
|
if frame > 1.5 {
|
|
// double: one pixel, two clear, one pixel
|
|
line = max(line, Finish.band(d, 3.0 * px, 4.0 * px, px))
|
|
}
|
|
if frame > 2.5 {
|
|
// dotted along x on the top and bottom lines, along y on the sides
|
|
let side = step(abs(p.y - c.y) - h.y, abs(p.x - c.x) - h.x)
|
|
let t = mix(p.x, p.y, side) + mix(o.x, o.y, side)
|
|
line = Finish.band(d, 0.0, px, px) * tm_dots(t + 0.01 * px, px)
|
|
}
|
|
let la = line * stroke.a * max(step(0.5, frame), loading)
|
|
out = tm_over(out, vec4(stroke.rgb * la, la))
|
|
|
|
// Focus: a keyline one pixel wide, two pixels clear of the face.
|
|
let kf = Finish.ring_out(d, 2.0 * px, 3.0 * px, px) * focus * live
|
|
out = tm_over(out, vec4(glow_ink.rgb * kf, kf))
|
|
|
|
// The wait spinner, which the stock face drew and this one must too: a
|
|
// bar turning in four steps, the way a console spins | / - \, in the
|
|
// first cell, in front of the dimmed label.
|
|
if loading > 0.0 {
|
|
let step4 = floor(anim * 4.0) * 0.25 * PI
|
|
let dir = vec2(sin(step4), -cos(step4))
|
|
let q = p - vec2(sb.x + 4.5, c.y)
|
|
let along = clamp(dot(q, dir), -3.0, 3.0)
|
|
let dl = length(q - dir * along) - 0.5 * px
|
|
let sa = Finish.cover(dl, px) * loading
|
|
out = tm_over(out, vec4(vec3(0.596, 1.0, 0.125) * sa, sa))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// The label of a waiting button stays, in the disabled green, instead of
|
|
// fading out under the spinner.
|
|
mod.widgets.ButtonFlat.draw_text.get_color = fn() {
|
|
let ink = self.color
|
|
.mix(self.color_focus, self.focus)
|
|
.mix(self.color_hover, self.hover)
|
|
.mix(self.color_down, self.down)
|
|
.mix(self.color_disabled, self.disabled)
|
|
return vec4(mix(ink.rgb, vec3(0.114, 0.275, 0.067), self.loading), ink.a)
|
|
}
|
|
|
|
let tm_grow = fn(rp: vec2, rs: vec2, m: float) -> vec4 {
|
|
return vec4(rp.x - m, rp.y - m, rs.x + 2.0 * m, rs.y + 2.0 * m)
|
|
}
|
|
|
|
// The frame kind and the halo at rest are the template's own.
|
|
mod.widgets.ButtonFlat.draw_bg.frame = uniform(1.0)
|
|
mod.widgets.ButtonFlat.draw_bg.glow_rest = uniform(0.0)
|
|
mod.widgets.ButtonFlat.draw_bg.glow_ink = uniform(#98ff20)
|
|
mod.widgets.ButtonFlat.draw_bg.vertex = fn() {
|
|
let g = tm_grow(self.rect_pos, self.rect_size, 7.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.ButtonFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return tm_button(self.pos, self.rect_size, self.rect_pos, px, self.face_fill(), self.face_stroke(), self.frame, self.hover, self.down, self.focus, self.disabled, self.loading, self.anim_time, self.glow_ink, self.glow_rest, 7.0)
|
|
}
|
|
mod.widgets.ButtonFlat.clip_x = false
|
|
mod.widgets.ButtonFlat.clip_y = false
|
|
|
|
// The root, the flat button: bare letters until the pointer comes, then a
|
|
// faint tint under them; held, a lit block with ground letters.
|
|
mod.widgets.ButtonFlat.draw_bg.color = #00000000
|
|
mod.widgets.ButtonFlat.draw_bg.color_hover = #98ff201a
|
|
mod.widgets.ButtonFlat.draw_bg.color_down = #98ff20
|
|
mod.widgets.ButtonFlat.draw_bg.color_focus = #00000000
|
|
mod.widgets.ButtonFlat.draw_bg.color_disabled = #00000000
|
|
mod.widgets.ButtonFlat.draw_bg.border_color = #00000000
|
|
mod.widgets.ButtonFlat.draw_bg.border_color_hover = #00000000
|
|
mod.widgets.ButtonFlat.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.ButtonFlat.draw_bg.border_color_focus = #00000000
|
|
mod.widgets.ButtonFlat.draw_bg.border_color_disabled = #00000000
|
|
mod.widgets.ButtonFlat.draw_bg.border_color_2 = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
mod.widgets.ButtonFlat.draw_bg.color_2 = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
mod.widgets.ButtonFlat.draw_bg.layer_color = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
mod.widgets.ButtonFlat.draw_bg.loading_color = #98ff20
|
|
mod.widgets.ButtonFlat.draw_text.color = #6ee747
|
|
mod.widgets.ButtonFlat.draw_text.color_hover = #98ff20
|
|
mod.widgets.ButtonFlat.draw_text.color_down = #040603
|
|
mod.widgets.ButtonFlat.draw_text.color_focus = #98ff20
|
|
mod.widgets.ButtonFlat.draw_text.color_disabled = #1d4611
|
|
|
|
// The standard button: the outline form. A line in the line green round a
|
|
// transparent face; the pointer lights line and letters; held, inverse.
|
|
mod.widgets.Button.draw_bg.color = #00000000
|
|
mod.widgets.Button.draw_bg.color_hover = #00000000
|
|
mod.widgets.Button.draw_bg.color_down = #98ff20
|
|
mod.widgets.Button.draw_bg.color_focus = #00000000
|
|
mod.widgets.Button.draw_bg.color_disabled = #00000000
|
|
mod.widgets.Button.draw_bg.color_2 = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
mod.widgets.Button.draw_bg.border_color = #60c83e
|
|
mod.widgets.Button.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.Button.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.Button.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.Button.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.Button.draw_bg.border_color_2 = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
|
|
// The primary: the one filled face, lit green with ground letters.
|
|
mod.widgets.ButtonPrimary.draw_bg.color = #74fa4b
|
|
mod.widgets.ButtonPrimary.draw_bg.color_hover = #98ff20
|
|
mod.widgets.ButtonPrimary.draw_bg.color_down = #b7f588
|
|
mod.widgets.ButtonPrimary.draw_bg.color_focus = #74fa4b
|
|
mod.widgets.ButtonPrimary.draw_bg.color_disabled = #00000000
|
|
mod.widgets.ButtonPrimary.draw_bg.border_color = #74fa4b
|
|
mod.widgets.ButtonPrimary.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.ButtonPrimary.draw_bg.border_color_down = #b7f588
|
|
mod.widgets.ButtonPrimary.draw_bg.border_color_focus = #74fa4b
|
|
mod.widgets.ButtonPrimary.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.ButtonPrimary.draw_bg.layer_color = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
mod.widgets.ButtonPrimary.draw_bg.loading_color = #040603
|
|
mod.widgets.ButtonPrimary.draw_text.color = #040603
|
|
mod.widgets.ButtonPrimary.draw_text.color_hover = #040603
|
|
mod.widgets.ButtonPrimary.draw_text.color_down = #040603
|
|
mod.widgets.ButtonPrimary.draw_text.color_focus = #040603
|
|
mod.widgets.ButtonPrimary.draw_text.color_disabled = #1d4611
|
|
mod.widgets.ButtonPrimary.draw_icon.color = #040603
|
|
|
|
// The second action: a dim block with the hot core ink.
|
|
mod.widgets.ButtonSecondary.draw_bg.color = #2e5d1d
|
|
mod.widgets.ButtonSecondary.draw_bg.color_hover = #3a7426
|
|
mod.widgets.ButtonSecondary.draw_bg.color_down = #98ff20
|
|
mod.widgets.ButtonSecondary.draw_bg.color_focus = #2e5d1d
|
|
mod.widgets.ButtonSecondary.draw_bg.border_color = #2e5d1d
|
|
mod.widgets.ButtonSecondary.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.ButtonSecondary.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.ButtonSecondary.draw_bg.border_color_focus = #2e5d1d
|
|
mod.widgets.ButtonSecondary.draw_text.color = #b7f588
|
|
mod.widgets.ButtonSecondary.draw_text.color_hover = #d6fab6
|
|
mod.widgets.ButtonSecondary.draw_text.color_down = #040603
|
|
mod.widgets.ButtonSecondary.draw_text.color_focus = #b7f588
|
|
mod.widgets.ButtonSecondary.draw_icon.color = #b7f588
|
|
|
|
// The tonal action: a well, a frame line and accent letters.
|
|
mod.widgets.ButtonTertiary.draw_bg.color = #0f2403
|
|
mod.widgets.ButtonTertiary.draw_bg.color_hover = #163507
|
|
mod.widgets.ButtonTertiary.draw_bg.color_down = #98ff20
|
|
mod.widgets.ButtonTertiary.draw_bg.color_focus = #0f2403
|
|
mod.widgets.ButtonTertiary.draw_bg.border_color = #43862b
|
|
mod.widgets.ButtonTertiary.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.ButtonTertiary.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.ButtonTertiary.draw_bg.border_color_focus = #43862b
|
|
mod.widgets.ButtonTertiary.draw_text.color = #98ff20
|
|
mod.widgets.ButtonTertiary.draw_text.color_hover = #b7f588
|
|
mod.widgets.ButtonTertiary.draw_text.color_down = #040603
|
|
mod.widgets.ButtonTertiary.draw_text.color_focus = #98ff20
|
|
mod.widgets.ButtonTertiary.draw_icon.color = #98ff20
|
|
|
|
// The destructive action keeps the one hue and says it with the double
|
|
// box line instead: no red outside a meter.
|
|
mod.widgets.ButtonDanger.draw_bg.frame = 2.0
|
|
mod.widgets.ButtonDanger.draw_bg.color = #00000000
|
|
mod.widgets.ButtonDanger.draw_bg.color_hover = #00000000
|
|
mod.widgets.ButtonDanger.draw_bg.color_down = #98ff20
|
|
mod.widgets.ButtonDanger.draw_bg.color_focus = #00000000
|
|
mod.widgets.ButtonDanger.draw_bg.border_color = #60c83e
|
|
mod.widgets.ButtonDanger.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.ButtonDanger.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.ButtonDanger.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.ButtonDanger.draw_text.color = #98ff20
|
|
mod.widgets.ButtonDanger.draw_text.color_hover = #b7f588
|
|
mod.widgets.ButtonDanger.draw_text.color_down = #040603
|
|
mod.widgets.ButtonDanger.draw_text.color_focus = #98ff20
|
|
mod.widgets.ButtonDanger.draw_icon.color = #98ff20
|
|
|
|
// The outline: a quieter line than the standard button, in the frame green.
|
|
mod.widgets.ButtonOutline.draw_bg.color = #00000000
|
|
mod.widgets.ButtonOutline.draw_bg.color_hover = #00000000
|
|
mod.widgets.ButtonOutline.draw_bg.color_down = #98ff20
|
|
mod.widgets.ButtonOutline.draw_bg.color_focus = #00000000
|
|
mod.widgets.ButtonOutline.draw_bg.color_disabled = #00000000
|
|
mod.widgets.ButtonOutline.draw_bg.border_color = #43862b
|
|
mod.widgets.ButtonOutline.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.ButtonOutline.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.ButtonOutline.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.ButtonOutline.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.ButtonOutline.draw_bg.layer_color = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
mod.widgets.ButtonOutline.draw_text.color = #6ee747
|
|
mod.widgets.ButtonOutline.draw_text.color_hover = #98ff20
|
|
mod.widgets.ButtonOutline.draw_text.color_down = #040603
|
|
mod.widgets.ButtonOutline.draw_text.color_focus = #98ff20
|
|
mod.widgets.ButtonOutline.draw_icon.color = #6ee747
|
|
// Dashed is the dotted box line: one pixel on, two off.
|
|
mod.widgets.ButtonDashed.draw_bg.frame = 3.0
|
|
mod.widgets.ButtonDashed.draw_bg.border_dash = 0.0
|
|
|
|
// The flatter faces: nothing but letters until the pointer comes.
|
|
mod.widgets.ButtonFlatter.draw_bg.color_disabled = #00000000
|
|
mod.widgets.ButtonFlatter.draw_bg.color_hover = #98ff201a
|
|
mod.widgets.ButtonFlatter.draw_bg.color_down = #98ff20
|
|
mod.widgets.ButtonFlatter.draw_bg.border_color_down = #98ff20
|
|
|
|
// The size rungs keep the cell padding.
|
|
mod.widgets.ButtonSm.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ButtonSm.min_height = 20.0
|
|
mod.widgets.ButtonXs.min_height = 17.0
|
|
mod.widgets.ButtonLg.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ButtonXl.padding = mod.turtle.Inset{left: 16.0 right: 16.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ButtonIcon.padding = mod.turtle.Inset{left: 5.0 right: 5.0 top: 3.0 bottom: 3.0}
|
|
mod.widgets.ButtonFlat.icon_walk.width = 14.0
|
|
|
|
// HELD STATES. The stock tracks write the pointer's hover and the press as
|
|
// snapped values, and a snapped value never arrives when a state is held
|
|
// from the start (a still page, a restored screen): a held button showed
|
|
// rest, a held press only the hover. The same states with plain values and
|
|
// a snapped entry look and move the same under a live pointer, and hold.
|
|
let tm_held_states = fn(w) {
|
|
w.animator.hover.on.from = {all: Snap}
|
|
w.animator.hover.on.apply = {draw_bg: {down: 0.0, hover: 1.0} draw_text: {down: 0.0, hover: 1.0}}
|
|
w.animator.hover.down.from = {all: Snap}
|
|
w.animator.hover.down.apply = {draw_bg: {down: 1.0, hover: 1.0} draw_text: {down: 1.0, hover: 1.0}}
|
|
}
|
|
tm_held_states(mod.widgets.ButtonFlat)
|
|
tm_held_states(mod.widgets.CheckBoxFlat)
|
|
tm_held_states(mod.widgets.ToggleFlat)
|
|
tm_held_states(mod.widgets.RadioButtonFlat)
|
|
tm_held_states(mod.widgets.RadioButtonTabFlat)
|
|
tm_held_states(mod.widgets.TextInputFlat)
|
|
tm_held_states(mod.widgets.DropDownFlat)
|
|
|
|
// The size rungs are standard buttons: the line round them. They share
|
|
// the flat button's draw object, so they are handed one of their own.
|
|
let tm_lined_bg = mod.widgets.ButtonFlat.draw_bg{
|
|
border_color: #60c83e
|
|
border_color_hover: #98ff20
|
|
border_color_down: #98ff20
|
|
border_color_focus: #98ff20
|
|
border_color_disabled: #1d4611
|
|
}
|
|
mod.widgets.ButtonXs.draw_bg = tm_lined_bg
|
|
mod.widgets.ButtonSm.draw_bg = tm_lined_bg
|
|
mod.widgets.ButtonLg.draw_bg = tm_lined_bg
|
|
mod.widgets.ButtonXl.draw_bg = tm_lined_bg
|
|
mod.widgets.ButtonCompound.draw_bg = tm_lined_bg
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CHECK BOXES. A 12 point box, one pixel of line green; on, a 6 point lit
|
|
// block three points in; mixed, a bar; an error doubles the line and hatches
|
|
// the box. Held, the box fills (inverse). The label stands one cell off.
|
|
mod.widgets.CheckBoxFlat.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 5.0 bottom: 5.0}
|
|
mod.widgets.CheckBoxFlat.label_walk.margin = mod.turtle.Inset{left: 20.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.CheckBoxFlat.draw_bg.size = uniform(12.0)
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_size = uniform(0.34)
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_radius = uniform(0.0)
|
|
mod.widgets.CheckBoxFlat.draw_bg.color = #00000000
|
|
mod.widgets.CheckBoxFlat.draw_bg.color_hover = #98ff201a
|
|
mod.widgets.CheckBoxFlat.draw_bg.color_down = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_bg.color_active = #00000000
|
|
mod.widgets.CheckBoxFlat.draw_bg.color_focus = #00000000
|
|
mod.widgets.CheckBoxFlat.draw_bg.color_disabled = #00000000
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_color = #60c83e
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_color_active = #60c83e
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_color_error = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color = #00000000
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color_hover = #00000000
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color_down = #00000000
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color_active = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color_active_hover = #b7f588
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color_focus = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color_disabled = #1d4611
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color_mixed = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_bg.mark_color_error = #98ff20
|
|
mod.widgets.CheckBoxFlat.draw_text.color_error = #98ff20
|
|
mod.widgets.CheckBox.draw_bg.border_color = #60c83e
|
|
mod.widgets.CheckBox.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.CheckBox.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.CheckBox.draw_bg.border_color_active = #60c83e
|
|
mod.widgets.CheckBox.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.CheckBox.draw_bg.border_color_disabled = #1d4611
|
|
|
|
// The mark box of a check box, radio or toggle: where it stands in the quad.
|
|
// `w` its width, `hgt` its height; `at_end` puts it after the label.
|
|
let tm_markbox = fn(size: vec2, o: vec2, w: float, hgt: float, at_end: float, px: float) -> vec4 {
|
|
let x = (size.x - w) * at_end
|
|
let y = size.y * 0.5 - hgt * 0.5
|
|
return tm_snapbox(o, vec2(x, y), vec2(x + w, y + hgt), px)
|
|
}
|
|
|
|
mod.widgets.CheckBoxFlat.draw_bg.vertex = fn() {
|
|
let g = tm_grow(self.rect_pos, self.rect_size, 6.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.CheckBoxFlat.draw_bg.pixel = fn() {
|
|
let m = 6.0
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let live = 1.0 - self.disabled
|
|
let sb = tm_markbox(self.rect_size, self.rect_pos, self.size, self.size, self.mark_at_end, px)
|
|
let c = (sb.xy + sb.zw) * 0.5
|
|
let h = (sb.zw - sb.xy) * 0.5
|
|
let d = tm_box(p, c, h)
|
|
let fill = self.color
|
|
.mix(self.color_focus, self.focus)
|
|
.mix(self.color_active, self.active)
|
|
.mix(self.color_hover, self.hover)
|
|
.mix(self.color_down, self.down)
|
|
.mix(self.color_disabled, self.disabled)
|
|
let stroke = self.border_color
|
|
.mix(self.border_color_focus, self.focus)
|
|
.mix(self.border_color_active, self.active)
|
|
.mix(self.border_color_hover, self.hover)
|
|
.mix(self.border_color_down, self.down)
|
|
.mix(self.border_color_error, self.error)
|
|
.mix(self.border_color_disabled, self.disabled)
|
|
let mark0 = self.mark_color
|
|
.mix(self.mark_color_hover, self.hover)
|
|
.mix(self.mark_color_active, self.active)
|
|
.mix(self.mark_color_error, self.error * self.active)
|
|
let mark = vec4(mix(mark0.rgb, self.mark_color_disabled.rgb, self.disabled), mark0.a)
|
|
let ink = vec3(0.596, 1.0, 0.125)
|
|
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
// The halo of the line under the pointer, and of the held box.
|
|
let ha = tm_halo(d, max(0.05 * self.hover, 0.08 * self.down) * live)
|
|
out = vec4(ink * ha, ha)
|
|
let inside = Finish.cover(d, px)
|
|
out = tm_over(out, vec4(fill.rgb * fill.a * inside, fill.a * inside))
|
|
// An error hatches the box: one pixel on the diagonal every three.
|
|
let hatch = Finish.stripes(p + self.rect_pos, 0.785398, 3.0 * px, 0.34, px) * inside * self.error * 0.3
|
|
out = tm_over(out, vec4(stroke.rgb * hatch, hatch))
|
|
var line = Finish.band(d, 0.0, px, px)
|
|
line = max(line, Finish.ring_out(d, 2.0 * px, 3.0 * px, px) * self.error)
|
|
let la = line * stroke.a
|
|
out = tm_over(out, vec4(stroke.rgb * la, la))
|
|
// On: a lit block. Mixed: a bar. Both inverse while held.
|
|
let hold = self.down * live
|
|
let blk = Finish.cover(tm_box(p, c, h - vec2(3.0, 3.0)), px) * mark.a * (1.0 - self.mixed)
|
|
let bar = Finish.cover(tm_box(p, c, vec2(h.x - 3.0, 1.0)), px) * self.mixed * self.mark_color_mixed.a
|
|
let mk = max(blk, bar)
|
|
let mink = mix(mix(mark.rgb, self.mark_color_mixed.rgb, self.mixed), self.mark_color_disabled.rgb, self.disabled)
|
|
let mcol = mix(mink, vec3(0.016, 0.024, 0.012), hold)
|
|
out = tm_over(out, vec4(mcol * mk, mk))
|
|
// Focus: the keyline two pixels out.
|
|
let kf = Finish.ring_out(d, 2.0 * px, 3.0 * px, px) * self.focus * live * (1.0 - self.error)
|
|
out = tm_over(out, vec4(ink * kf, kf))
|
|
return out
|
|
}
|
|
mod.widgets.CheckBoxFlat.clip_x = false
|
|
mod.widgets.CheckBoxFlat.clip_y = false
|
|
|
|
// TOGGLES. A 28 by 12 track, one pixel of line green round a well; the knob
|
|
// a 10 by 8 block two points in, dim at the left when off, lit at the right
|
|
// when on; the side it has left is a fifty per cent checker of the dim ink.
|
|
mod.widgets.ToggleFlat.label_walk.margin = mod.turtle.Inset{left: 36.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ToggleFlat.draw_bg.pill_aspect = uniform(2.3333)
|
|
mod.widgets.ToggleFlat.draw_bg.knob_inset = uniform(2.0)
|
|
mod.widgets.ToggleFlat.draw_bg.color = #0f2403
|
|
mod.widgets.ToggleFlat.draw_bg.color_hover = #0f2403
|
|
mod.widgets.ToggleFlat.draw_bg.color_down = #0f2403
|
|
mod.widgets.ToggleFlat.draw_bg.color_active = #0f2403
|
|
mod.widgets.ToggleFlat.draw_bg.color_focus = #0f2403
|
|
mod.widgets.ToggleFlat.draw_bg.color_disabled = #040603
|
|
mod.widgets.ToggleFlat.draw_bg.mark_color = #2e5d1d
|
|
mod.widgets.ToggleFlat.draw_bg.mark_color_hover = #43862b
|
|
mod.widgets.ToggleFlat.draw_bg.mark_color_down = #98ff20
|
|
mod.widgets.ToggleFlat.draw_bg.mark_color_active = #98ff20
|
|
mod.widgets.ToggleFlat.draw_bg.mark_color_active_hover = #b7f588
|
|
mod.widgets.ToggleFlat.draw_bg.mark_color_disabled = #1d4611
|
|
mod.widgets.Toggle.draw_bg.border_color = #60c83e
|
|
mod.widgets.Toggle.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.Toggle.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.Toggle.draw_bg.border_color_active = #60c83e
|
|
mod.widgets.Toggle.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.Toggle.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.ToggleFlat.draw_bg.pixel = fn() {
|
|
let m = 6.0
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let live = 1.0 - self.disabled
|
|
let tw = self.size * self.pill_aspect
|
|
let sb = tm_markbox(self.rect_size, self.rect_pos, tw, self.size, self.mark_at_end, px)
|
|
let c = (sb.xy + sb.zw) * 0.5
|
|
let h = (sb.zw - sb.xy) * 0.5
|
|
let d = tm_box(p, c, h)
|
|
let fill = self.color
|
|
.mix(self.color_active, self.active)
|
|
.mix(self.color_hover, self.hover)
|
|
.mix(self.color_disabled, self.disabled)
|
|
let stroke = self.border_color
|
|
.mix(self.border_color_focus, self.focus)
|
|
.mix(self.border_color_active, self.active)
|
|
.mix(self.border_color_hover, self.hover)
|
|
.mix(self.border_color_down, self.down)
|
|
.mix(self.border_color_error, self.error)
|
|
.mix(self.border_color_disabled, self.disabled)
|
|
let knob_col = self.mark_color
|
|
.mix(self.mark_color_hover, self.hover)
|
|
.mix(self.mark_color_active, self.active)
|
|
.mix(self.mark_color_down, self.down)
|
|
.mix(self.mark_color_disabled, self.disabled)
|
|
let ink = vec3(0.596, 1.0, 0.125)
|
|
// The knob: 10 by 8, travelling 14 points; it follows the pointer while
|
|
// dragged.
|
|
let t = mix(self.active, self.drag_pos, self.drag)
|
|
let ki = self.knob_inset
|
|
let kw = 10.0
|
|
let kh = h.y * 2.0 - ki * 2.0
|
|
let kx = Finish.snap(sb.x + self.rect_pos.x + ki + t * (h.x * 2.0 - ki * 2.0 - kw), px) - self.rect_pos.x
|
|
let kc = vec2(kx + kw * 0.5, c.y)
|
|
let kd = tm_box(p, kc, vec2(kw * 0.5, kh * 0.5))
|
|
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
// A lit knob throws the family's halo; nothing else here does.
|
|
let ha = tm_halo(kd, max(0.05 * self.active * self.hover, 0.08 * self.down) * live) * (1.0 - Finish.cover(d, px))
|
|
out = vec4(ink * ha, ha)
|
|
let inside = Finish.cover(d, px)
|
|
out = tm_over(out, vec4(fill.rgb * fill.a * inside, fill.a * inside))
|
|
// The side the knob has left: a one pixel checker, dim.
|
|
let cell = floor((p + self.rect_pos) / px)
|
|
let chk = step(0.5, fract((cell.x + cell.y) * 0.5))
|
|
let slot = Finish.cover(tm_box(p, c, h - vec2(ki, ki)), px) * (1.0 - Finish.cover(kd + ki * 0.5, px))
|
|
let ca = chk * slot * mix(0.55, 0.3, self.disabled)
|
|
out = tm_over(out, vec4(vec3(0.18, 0.365, 0.114) * ca, ca))
|
|
let la = Finish.band(d, 0.0, px, px) * stroke.a
|
|
out = tm_over(out, vec4(stroke.rgb * la, la))
|
|
let ka = Finish.cover(kd, px) * knob_col.a
|
|
// the lit block carries a hotter top pixel row
|
|
let top = Finish.band(p.y - (kc.y - kh * 0.5), -px, 0.0, px) * self.active * 0.08
|
|
out = tm_over(out, vec4(mix(knob_col.rgb, vec3(1.0, 1.0, 1.0), top) * ka, ka))
|
|
let kf = Finish.ring_out(d, 2.0 * px, 3.0 * px, px) * self.focus * live
|
|
out = tm_over(out, vec4(ink * kf, kf))
|
|
return out
|
|
}
|
|
|
|
// RADIOS. A 12 point diamond drawn in one pixel of line green; on, a lit
|
|
// 6 point diamond inside it.
|
|
mod.widgets.RadioButtonFlat.padding = mod.turtle.Inset{left: 0.0 right: 8.0 top: 5.0 bottom: 5.0}
|
|
mod.widgets.RadioButtonFlat.label_walk.margin = mod.turtle.Inset{left: 20.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.RadioButtonFlat.draw_bg.size = uniform(12.0)
|
|
mod.widgets.RadioButtonFlat.draw_bg.color = #00000000
|
|
mod.widgets.RadioButtonFlat.draw_bg.color_hover = #98ff201a
|
|
mod.widgets.RadioButtonFlat.draw_bg.color_down = #98ff20
|
|
mod.widgets.RadioButtonFlat.draw_bg.color_active = #00000000
|
|
mod.widgets.RadioButtonFlat.draw_bg.color_focus = #00000000
|
|
mod.widgets.RadioButtonFlat.draw_bg.color_disabled = #00000000
|
|
mod.widgets.RadioButtonFlat.draw_bg.mark_color = #00000000
|
|
mod.widgets.RadioButtonFlat.draw_bg.mark_color_active = #98ff20
|
|
mod.widgets.RadioButtonFlat.draw_bg.mark_color_disabled = #1d4611
|
|
mod.widgets.RadioButton.draw_bg.border_color = #60c83e
|
|
mod.widgets.RadioButton.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.RadioButton.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.RadioButton.draw_bg.border_color_active = #60c83e
|
|
mod.widgets.RadioButton.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.RadioButton.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.RadioButtonFlat.draw_bg.vertex = fn() {
|
|
let g = tm_grow(self.rect_pos, self.rect_size, 6.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.RadioButtonFlat.draw_bg.pixel = fn() {
|
|
let m = 6.0
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let live = 1.0 - self.disabled
|
|
let sb = tm_markbox(self.rect_size, self.rect_pos, self.size, self.size, 0.0, px)
|
|
let c = (sb.xy + sb.zw) * 0.5
|
|
let r = (sb.z - sb.x) * 0.5
|
|
let q = abs(p - c)
|
|
// true distance to a diamond of half-diagonal r
|
|
let d = (q.x + q.y - r) * 0.70711
|
|
let fill = self.color
|
|
.mix(self.color_focus, self.focus)
|
|
.mix(self.color_active, self.active)
|
|
.mix(self.color_hover, self.hover)
|
|
.mix(self.color_down, self.down)
|
|
.mix(self.color_disabled, self.disabled)
|
|
let stroke = self.border_color
|
|
.mix(self.border_color_focus, self.focus)
|
|
.mix(self.border_color_active, self.active)
|
|
.mix(self.border_color_hover, self.hover)
|
|
.mix(self.border_color_down, self.down)
|
|
.mix(self.border_color_disabled, self.disabled)
|
|
let mark0 = self.mark_color.mix(self.mark_color_active, self.active)
|
|
let mark = vec4(mix(mark0.rgb, self.mark_color_disabled.rgb, self.disabled), mark0.a)
|
|
let ink = vec3(0.596, 1.0, 0.125)
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
let ha = tm_halo(d, max(0.05 * self.hover, 0.08 * self.down) * live)
|
|
out = vec4(ink * ha, ha)
|
|
let inside = Finish.cover(d, px)
|
|
out = tm_over(out, vec4(fill.rgb * fill.a * inside, fill.a * inside))
|
|
let la = Finish.band(d, 0.0, px, px) * stroke.a
|
|
out = tm_over(out, vec4(stroke.rgb * la, la))
|
|
let md = (q.x + q.y - r * 0.5) * 0.70711
|
|
let ma = Finish.cover(md, px) * mark.a
|
|
let mcol = mix(mark.rgb, vec3(0.016, 0.024, 0.012), self.down * live)
|
|
out = tm_over(out, vec4(mcol * ma, ma))
|
|
let kf = Finish.ring_out(d, 2.0 * px, 3.0 * px, px) * self.focus * live
|
|
out = tm_over(out, vec4(ink * kf, kf))
|
|
return out
|
|
}
|
|
mod.widgets.RadioButtonFlat.clip_x = false
|
|
mod.widgets.RadioButtonFlat.clip_y = false
|
|
|
|
// RADIO TABS. One outlined cell a row tall; the chosen one inverse, and the
|
|
// one lit thing of its row.
|
|
mod.widgets.RadioButtonTabFlat.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 5.0 bottom: 5.0}
|
|
mod.widgets.RadioButtonTabFlat.label_walk.margin = mod.turtle.Inset{left: 8.0 right: 8.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.color = #00000000
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.color_hover = #98ff201a
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.color_active = #74fa4b
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.color_disabled = #00000000
|
|
mod.widgets.RadioButtonTab.draw_bg.border_color = #43862b
|
|
mod.widgets.RadioButtonTab.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.RadioButtonTab.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.RadioButtonTab.draw_bg.border_color_active = #74fa4b
|
|
mod.widgets.RadioButtonTab.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.RadioButtonTab.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color = #6ee747
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_hover = #98ff20
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_down = #040603
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_focus = #98ff20
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #040603
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #1d4611
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.vertex = fn() {
|
|
let g = tm_grow(self.rect_pos, self.rect_size, 7.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let fill = self.color
|
|
.mix(self.color_hover, self.hover * (1.0 - self.active))
|
|
.mix(self.color_active, max(self.active, self.down))
|
|
.mix(self.color_disabled, self.disabled)
|
|
let stroke = self.border_color
|
|
.mix(self.border_color_focus, self.focus)
|
|
.mix(self.border_color_active, self.active)
|
|
.mix(self.border_color_hover, self.hover)
|
|
.mix(self.border_color_down, self.down)
|
|
.mix(self.border_color_disabled, self.disabled)
|
|
return tm_button(self.pos, self.rect_size, self.rect_pos, px, fill, stroke, 1.0, self.hover, self.down, self.focus, self.disabled, 0.0, 0.0, vec4(0.596, 1.0, 0.125, 1.0), 0.05 * self.active, 7.0)
|
|
}
|
|
mod.widgets.RadioButtonTabFlat.clip_x = false
|
|
mod.widgets.RadioButtonTabFlat.clip_y = false
|
|
mod.widgets.RadioButtonTab.clip_x = false
|
|
mod.widgets.RadioButtonTab.clip_y = false
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SLIDERS. The line form: a one pixel track across the middle of the track
|
|
// row, solid accent on the filled side and a dotted dim line on the rest,
|
|
// and a solid 8 by 14 block for the handle. The pointer draws the keyline
|
|
// two pixels round the block; a drag lights it.
|
|
let tm_slider_line = fn(p: vec2, o: vec2, size: vec2, top: float, px: float, slide: float, lo: float, hi: float, hw: float, hh: float, val: vec4, handle: vec4, hover: float, focus: float, drag: float, disabled: float) -> vec4 {
|
|
let live = 1.0 - disabled
|
|
let ink = vec3(0.596, 1.0, 0.125)
|
|
let yc = Finish.snap(o.y + top + (size.y - top) * 0.5, px) - o.y
|
|
let x0 = hw * 0.5
|
|
let span = max(size.x - hw, 1.0)
|
|
let hx = Finish.snap(o.x + x0 + slide * span, px) - o.x
|
|
let fx0 = x0 + lo * span
|
|
let fx1 = x0 + hi * span
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
// the track row: one device pixel under the centre line
|
|
let row = Finish.band(p.y - yc, -px, 0.0, px) * step(0.0, p.x) * step(p.x, size.x)
|
|
let filled = step(fx0, p.x) * step(p.x, fx1)
|
|
let dot = tm_dots(p.x + o.x, px) * (1.0 - filled)
|
|
let ta = row * filled * val.a
|
|
out = tm_over(out, vec4(val.rgb * ta, ta))
|
|
let da = row * dot * mix(1.0, 0.6, disabled)
|
|
let dim = vec3(0.18, 0.365, 0.114)
|
|
out = tm_over(out, vec4(dim * da, da))
|
|
// the handle block
|
|
let hc = vec2(hx, yc - px * 0.5)
|
|
let hd = tm_box(p, hc, vec2(hw * 0.5, hh * 0.5))
|
|
let ha = tm_halo(hd, 0.08 * drag * live)
|
|
out = tm_over(out, vec4(ink * ha, ha))
|
|
let ba = Finish.cover(hd, px) * handle.a
|
|
let topr = Finish.band(p.y - (hc.y - hh * 0.5), -px, 0.0, px) * 0.08 * live
|
|
out = tm_over(out, vec4(mix(handle.rgb, vec3(1.0, 1.0, 1.0), topr) * ba, ba))
|
|
let kf = Finish.ring_out(hd, 2.0 * px, 3.0 * px, px) * max(hover, focus) * live
|
|
out = tm_over(out, vec4(ink * kf, kf))
|
|
return out
|
|
}
|
|
|
|
mod.widgets.SliderFlat.draw_bg.handle_size = 8.0
|
|
mod.widgets.SliderFlat.draw_bg.vertex = fn() {
|
|
let g = tm_grow(self.rect_pos, self.rect_size, 6.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.SliderFlat.draw_bg.pixel = fn() {
|
|
let m = 6.0
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = min(self.origin_pos, self.slide_pos) * self.arc_origin
|
|
let hi = mix(self.slide_pos, max(self.origin_pos, self.slide_pos), self.arc_origin)
|
|
let val = self.val_color
|
|
.mix(self.val_color_focus, self.focus)
|
|
.mix(self.val_color_hover.mix(self.val_color_drag, self.drag), self.hover)
|
|
.mix(self.val_color_disabled, self.disabled)
|
|
let handle = self.handle_color
|
|
.mix(self.handle_color_focus, self.focus)
|
|
.mix(self.handle_color_hover.mix(self.handle_color_drag, self.drag), self.hover)
|
|
.mix(self.handle_color_disabled, self.disabled)
|
|
return tm_slider_line(p, self.rect_pos, self.rect_size, 20.0, px, self.slide_pos, lo, hi, self.handle_size, 14.0, val, handle, self.hover, self.focus, self.drag, self.disabled)
|
|
}
|
|
mod.widgets.SliderFlat.clip_x = false
|
|
mod.widgets.SliderFlat.clip_y = false
|
|
let tm_slider_inks = fn(d) {
|
|
d.val_color = #98ff20
|
|
d.val_color_hover = #98ff20
|
|
d.val_color_focus = #98ff20
|
|
d.val_color_drag = #b7f588
|
|
d.val_color_disabled = #1d4611
|
|
d.handle_color = #74fa4b
|
|
d.handle_color_hover = #98ff20
|
|
d.handle_color_focus = #98ff20
|
|
d.handle_color_drag = #b7f588
|
|
d.handle_color_disabled = #1d4611
|
|
}
|
|
tm_slider_inks(mod.widgets.SliderMinimal.draw_bg)
|
|
tm_slider_inks(mod.widgets.SliderFlat.draw_bg)
|
|
tm_slider_inks(mod.widgets.Slider.draw_bg)
|
|
tm_slider_inks(mod.widgets.SliderRoundFlat.draw_bg)
|
|
tm_slider_inks(mod.widgets.SliderRound.draw_bg)
|
|
tm_slider_inks(mod.widgets.SliderFader.draw_bg)
|
|
// the fader cap is the lit block itself
|
|
mod.widgets.SliderFader.draw_bg.handle_color = #98ff20
|
|
mod.widgets.SliderFader.draw_bg.handle_color_focus = #98ff20
|
|
|
|
// The minimal slider: the same line, and a one-cell tick for a handle that
|
|
// grows into the block under the pointer.
|
|
mod.widgets.SliderMinimal.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let lo = min(self.origin_pos, self.slide_pos) * self.arc_origin
|
|
let hi = mix(self.slide_pos, max(self.origin_pos, self.slide_pos), self.arc_origin)
|
|
let val = self.val_color
|
|
.mix(self.val_color_hover.mix(self.val_color_drag, self.drag), self.hover)
|
|
.mix(self.val_color_disabled, self.disabled)
|
|
let handle = self.handle_color
|
|
.mix(self.handle_color_hover.mix(self.handle_color_drag, self.drag), self.hover)
|
|
.mix(self.handle_color_disabled, self.disabled)
|
|
let hw = mix(2.0, 6.0, max(self.hover, self.drag))
|
|
return tm_slider_line(p, self.rect_pos, self.rect_size, self.offset_y, px, self.slide_pos, lo, hi, hw, 5.0, val, handle, 0.0, self.focus, self.drag, self.disabled)
|
|
}
|
|
|
|
// The bar form (the round slider): a framed bar, one pixel of line, one
|
|
// pixel clear, the fill a solid accent block from the left, the empty part
|
|
// the well. The number reads inside the bar at its right end.
|
|
mod.widgets.SliderRoundFlat.height = 20.0
|
|
mod.widgets.SliderRoundFlat.draw_bg.color = #0f2403
|
|
mod.widgets.SliderRoundFlat.draw_bg.border_color = #60c83e
|
|
mod.widgets.SliderRoundFlat.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.SliderRoundFlat.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.SliderRoundFlat.draw_bg.border_color_drag = #98ff20
|
|
mod.widgets.SliderRoundFlat.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.SliderRound.draw_bg.border_color = #60c83e
|
|
mod.widgets.SliderRound.draw_bg.border_color_hover = #98ff20
|
|
mod.widgets.SliderRound.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.SliderRound.draw_bg.border_color_drag = #98ff20
|
|
mod.widgets.SliderRound.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.SliderRoundFlat.draw_bg.vertex = fn() {
|
|
let g = tm_grow(self.rect_pos, self.rect_size, 6.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.SliderRoundFlat.draw_bg.pixel = fn() {
|
|
let m = 6.0
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let live = 1.0 - self.disabled
|
|
let ink = vec3(0.596, 1.0, 0.125)
|
|
let sb = tm_snapbox(self.rect_pos, vec2(self.label_size, 0.0), self.rect_size, px)
|
|
let c = (sb.xy + sb.zw) * 0.5
|
|
let h = (sb.zw - sb.xy) * 0.5
|
|
let d = tm_box(p, c, h)
|
|
let stroke = self.border_color
|
|
.mix(self.border_color_focus.mix(self.border_color_hover.mix(self.border_color_drag, self.drag), self.hover), self.focus)
|
|
.mix(self.border_color_hover, self.hover)
|
|
.mix(self.border_color_disabled, self.disabled)
|
|
let val = self.val_color
|
|
.mix(self.val_color_hover.mix(self.val_color_drag, self.drag), self.hover)
|
|
.mix(self.val_color_disabled, self.disabled)
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
let inside = Finish.cover(d, px)
|
|
let well = self.color.mix(vec4(0.016, 0.024, 0.012, 1.0), self.disabled)
|
|
out = tm_over(out, vec4(well.rgb * inside, inside))
|
|
// the fill: inside the one pixel gap
|
|
let ih = h - vec2(2.0 * px, 2.0 * px)
|
|
let lo = min(self.origin_pos, self.slide_pos) * self.arc_origin
|
|
let hi = mix(self.slide_pos, max(self.origin_pos, self.slide_pos), self.arc_origin)
|
|
let fx0 = Finish.snap(self.rect_pos.x + c.x - ih.x + lo * ih.x * 2.0, px) - self.rect_pos.x
|
|
let fx1 = Finish.snap(self.rect_pos.x + c.x - ih.x + hi * ih.x * 2.0, px) - self.rect_pos.x
|
|
let fd = tm_box(p, vec2((fx0 + fx1) * 0.5, c.y), vec2(max((fx1 - fx0) * 0.5, 0.0), ih.y))
|
|
let fa = Finish.cover(fd, px) * val.a
|
|
let topr = Finish.band(p.y - (c.y - ih.y), -px, 0.0, px) * 0.08 * live
|
|
out = tm_over(out, vec4(mix(val.rgb, vec3(1.0, 1.0, 1.0), topr) * fa, fa))
|
|
let la = Finish.band(d, 0.0, px, px) * stroke.a
|
|
out = tm_over(out, vec4(stroke.rgb * la, la))
|
|
let kf = Finish.ring_out(d, 2.0 * px, 3.0 * px, px) * self.focus * live
|
|
out = tm_over(out, vec4(ink * kf, kf))
|
|
return out
|
|
}
|
|
mod.widgets.SliderRoundFlat.clip_x = false
|
|
mod.widgets.SliderRoundFlat.clip_y = false
|
|
mod.widgets.SliderRoundFlat.text_input.draw_text.color = #b7f588
|
|
mod.widgets.RotaryFlat.text_input.padding = mod.turtle.Inset{left: 4.0 right: 4.0 top: 1.0 bottom: 1.0}
|
|
mod.widgets.RotaryFlat.text_input.min_height = 0.0
|
|
|
|
// FADERS. A one pixel rail in the frame green, lit accent below the cap; a
|
|
// ladder of one pixel dots every 4 points either side, a longer tick every
|
|
// fourth; the cap a 14 by 6 lit block cut across by one ground-coloured
|
|
// pixel line, the line the value is read off.
|
|
mod.widgets.SliderFader.cap_size = 6.0
|
|
mod.widgets.SliderFader.track_inset = 6.0
|
|
mod.widgets.SliderFaderY.width = 24.0
|
|
mod.widgets.SliderFader.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let live = 1.0 - self.disabled
|
|
let ink = vec3(0.596, 1.0, 0.125)
|
|
let along = mix(self.rect_size.x, self.rect_size.y, self.is_vertical)
|
|
let across = mix(self.rect_size.y, self.rect_size.x, self.is_vertical)
|
|
// track space: `a` along from the value's zero, `b` across
|
|
let a = mix(p.x, self.rect_size.y - p.y, self.is_vertical)
|
|
let b = mix(p.y, p.x, self.is_vertical)
|
|
let oa = mix(self.rect_pos.x, self.rect_pos.y, self.is_vertical)
|
|
let ob = mix(self.rect_pos.y, self.rect_pos.x, self.is_vertical)
|
|
let travel = max(along - self.inset_px * 2.0 - self.cap_px, 1.0)
|
|
let foot = self.inset_px + self.cap_px * 0.5
|
|
let mid = Finish.snap(ob + across * 0.5, px) - ob
|
|
let val = self.val_color
|
|
.mix(self.val_color_hover.mix(self.val_color_drag, self.drag), self.hover)
|
|
.mix(self.val_color_disabled, self.disabled)
|
|
let cap = self.handle_color
|
|
.mix(self.handle_color_focus, self.focus)
|
|
.mix(self.handle_color_hover.mix(self.handle_color_drag, self.drag), self.hover)
|
|
.mix(self.handle_color_disabled, self.disabled)
|
|
let dim = mix(vec3(0.18, 0.365, 0.114), vec3(0.114, 0.275, 0.067), self.disabled)
|
|
let frame = mix(vec3(0.263, 0.525, 0.169), vec3(0.114, 0.275, 0.067), self.disabled)
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
let on_track = step(self.inset_px, a) * step(a, along - self.inset_px)
|
|
// the ladder
|
|
let da = a - foot
|
|
let k = floor(da / 4.0 + 0.5)
|
|
let near = abs(da - k * 4.0)
|
|
let dotl = Finish.band(near - px * 0.5, 0.0, px, px) * step(0.0, da + 0.5) * step(da, travel + 0.5)
|
|
let off = abs(b - mid)
|
|
let major = step(fract(k / 4.0 + 0.01), 0.1)
|
|
let ldot = Finish.band(off - 3.0, -px, 0.0, px)
|
|
let ltick = step(2.5, off) * step(off, 5.5) * major
|
|
let lad = dotl * max(ldot, ltick)
|
|
out = tm_over(out, vec4(dim * lad, lad))
|
|
// the rail, lit where the value has filled it
|
|
let rail = Finish.cover(abs(b - mid - px * 0.5) - px * 0.5, px) * on_track
|
|
let lo = foot + self.fill_lo * travel
|
|
let hi = foot + self.fill_hi * travel
|
|
let lit = step(lo, a) * step(a, hi)
|
|
let rcol = mix(frame, val.rgb, lit)
|
|
out = tm_over(out, vec4(rcol * rail, rail))
|
|
// the cap
|
|
let cap_w = 14.0
|
|
// The cap's centre on a device pixel edge in window space, whichever
|
|
// way the fader lies (a vertical one counts `a` up from its foot).
|
|
let at = foot + self.slide_pos * travel
|
|
let ca_h = Finish.snap(oa + at, px) - oa
|
|
let ca_v = along - (Finish.snap(oa + along - at, px) - oa)
|
|
let ca = mix(ca_h, ca_v, self.is_vertical)
|
|
let cd = tm_box(vec2(a, b), vec2(ca, mid + px * 0.5), vec2(self.cap_px * 0.5, cap_w * 0.5))
|
|
let ha = tm_halo(cd, 0.08 * self.drag * live)
|
|
out = tm_over(out, vec4(ink * ha, ha))
|
|
// A solid lit block of whole device pixels: nine along, one of them the
|
|
// ground-coloured centre line, and 21 across, centred on the rail.
|
|
let half = floor(self.cap_px * 0.5 / px)
|
|
let blk = step(ca - half * px, a) * step(a, ca + (half + 1.0) * px) * step(mid - 10.0 * px, b) * step(b, mid + 11.0 * px)
|
|
let cov = blk * cap.a
|
|
let cut = step(ca, a) * step(a, ca + px)
|
|
let ccol = mix(cap.rgb, vec3(0.016, 0.024, 0.012), cut)
|
|
out = tm_over(out, vec4(ccol * cov, cov))
|
|
let kf = Finish.ring_out(cd, 2.0 * px, 3.0 * px, px) * max(self.hover, self.focus) * live
|
|
out = tm_over(out, vec4(ink * kf, kf))
|
|
return out
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// KNOBS. The one permitted curve: a thin drawn ring. Round it a 270 degree
|
|
// arc of block segments, one pixel apart, lit accent up to the value and
|
|
// the disabled green beyond; a straight pointer in the hot core ink. No
|
|
// cap, no bevel, no shading.
|
|
let tm_knob = fn(p: vec2, c: vec2, rad: float, px: float, slide: float, origin: float, arc_origin: float, gap: float, hover: float, focus: float, drag: float, disabled: float) -> vec4 {
|
|
let live = 1.0 - disabled
|
|
let ink = vec3(0.596, 1.0, 0.125)
|
|
let v = p - c
|
|
let r = length(v)
|
|
// angle from straight down, clockwise on screen, 0..2 pi
|
|
var th = atan2(-v.x, v.y)
|
|
if th < 0.0 {
|
|
th = th + 2.0 * PI
|
|
}
|
|
let start = gap * 0.5 * PI / 180.0
|
|
let sweep = 2.0 * PI - gap * PI / 180.0
|
|
let t = (th - start) / sweep
|
|
let n = 24.0
|
|
let rin = rad * 0.89
|
|
let rout = rad * 0.98
|
|
let rmid = (rin + rout) * 0.5
|
|
let seg = floor(t * n)
|
|
let seglen = sweep * rmid / n
|
|
let f = fract(t * n) * seglen
|
|
let in_seg = step(f, seglen - px) * step(0.0, t) * step(t, 1.0)
|
|
let band = Finish.cover(max(rin - r, r - rout), px) * in_seg
|
|
let lo = min(origin, slide) * arc_origin
|
|
let hi = mix(slide, max(origin, slide), arc_origin)
|
|
let sc = (seg + 0.5) / n
|
|
let lit = step(lo, sc) * step(sc, hi + 0.5 / n - 0.0001) * live
|
|
let unlit = vec3(0.114, 0.275, 0.067)
|
|
let bright = mix(ink, vec3(0.718, 0.961, 0.533), drag)
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
// halo of the lit arc while it is turned
|
|
let bd = max(rin - r, r - rout)
|
|
let tv = clamp((th - start) / sweep, 0.0, 1.0)
|
|
let ha = tm_halo(bd, 0.08 * drag * live) * step(lo, tv) * step(tv, hi) * step(0.0, t) * step(t, 1.0)
|
|
out = vec4(ink * ha, ha)
|
|
let col = mix(unlit, bright, lit)
|
|
let ba = band
|
|
out = tm_over(out, vec4(col * ba, ba))
|
|
// the drawn ring
|
|
let frame = mix(vec3(0.263, 0.525, 0.169), vec3(0.596, 1.0, 0.125), hover * live)
|
|
// one device pixel: the strip within half a pixel of the ring radius
|
|
let ring = Finish.band(abs(r - rad * 0.6) - px * 0.5, 0.0, px, px) * mix(1.0, 0.5, disabled)
|
|
out = tm_over(out, vec4(frame * ring, ring))
|
|
// the pointer
|
|
let av = start + sweep * slide
|
|
let dir = vec2(-sin(av), cos(av))
|
|
let along = clamp(dot(v, dir), rad * 0.12, rad * 0.54)
|
|
let pd = length(v - dir * along) - px * 0.75
|
|
let pa = Finish.cover(pd, px)
|
|
let pcol = mix(vec3(0.718, 0.961, 0.533), unlit, disabled)
|
|
out = tm_over(out, vec4(pcol * pa, pa))
|
|
// focus: the keyline ring two pixels out
|
|
let kf = Finish.band(abs(r - rad - 2.5 * px), -px * 0.5, px * 0.5, px) * focus * live
|
|
out = tm_over(out, vec4(ink * kf, kf))
|
|
return out
|
|
}
|
|
|
|
mod.widgets.RotaryKnob.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let dia = min(self.rect_size.x, self.rect_size.y)
|
|
let rad = dia * 0.5 - 2.0
|
|
let c = vec2(self.rect_size.x * 0.5, self.rect_size.y - dia * 0.5)
|
|
return tm_knob(p, c, rad, px, self.slide_pos, self.origin_pos, self.arc_origin, self.gap, self.hover, self.focus, self.drag, self.disabled)
|
|
}
|
|
mod.widgets.RotaryFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let dia = min(self.rect_size.x, self.rect_size.y - 24.0)
|
|
let rad = dia * 0.5 - 2.0
|
|
let c = vec2(self.rect_size.x * 0.5, 24.0 + dia * 0.5)
|
|
return tm_knob(p, c, rad, px, self.slide_pos, self.origin_pos, self.arc_origin, self.gap, self.hover, self.focus, self.drag, self.disabled)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// FIELDS. A flat well in one pixel of frame green; the pointer brings the
|
|
// line green, focus the accent; no inner shadow. A field that a host has
|
|
// made invisible (the value field inside a slider) stays invisible: the face
|
|
// draws only what its colours carry.
|
|
let tm_field = fn(p: vec2, o: vec2, size: vec2, px: float, fill: vec4, stroke: vec4, lined: float, arrow: float, acol: vec4) -> vec4 {
|
|
let sb = tm_snapbox(o, vec2(0.0, 0.0), size, px)
|
|
let c = (sb.xy + sb.zw) * 0.5
|
|
let h = (sb.zw - sb.xy) * 0.5
|
|
let d = tm_box(p, c, h)
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
let inside = Finish.cover(d, px)
|
|
out = vec4(fill.rgb * fill.a * inside, fill.a * inside)
|
|
let la = Finish.band(d, 0.0, px, px) * stroke.a * step(0.001, lined)
|
|
out = tm_over(out, vec4(stroke.rgb * la, la))
|
|
if arrow > 0.5 {
|
|
// a solid down triangle in the last cell, no divider
|
|
let ac = vec2(Finish.snap(o.x + size.x - 11.0, px) - o.x, Finish.snap(o.y + size.y * 0.5 + 2.0, px) - o.y)
|
|
let q = p - ac
|
|
let td = max(-2.0 - q.y, (abs(q.x) - (2.0 - q.y) * 0.9) * 0.74)
|
|
let ta = Finish.cover(td, px) * acol.a
|
|
out = tm_over(out, vec4(acol.rgb * ta, ta))
|
|
}
|
|
return out
|
|
}
|
|
|
|
mod.widgets.TextInputFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let fill = self.color
|
|
.mix(self.color_empty, self.empty)
|
|
.mix(self.color_focus, self.focus)
|
|
.mix(self.color_hover.mix(self.color_down, self.down), self.hover)
|
|
.mix(self.color_disabled, self.disabled)
|
|
let stroke = self.border_color
|
|
.mix(self.border_color_empty, self.empty)
|
|
.mix(self.border_color_hover.mix(self.border_color_down, self.down), self.hover)
|
|
.mix(self.border_color_focus, self.focus)
|
|
.mix(self.border_color_disabled, self.disabled)
|
|
return tm_field(self.pos * self.rect_size, self.rect_pos, self.rect_size, px, fill, stroke, self.border_size, 0.0, vec4(0.0, 0.0, 0.0, 0.0))
|
|
}
|
|
let tm_field_inks = fn(d) {
|
|
d.color = #0f2403
|
|
d.color_hover = #0f2403
|
|
d.color_focus = #0f2403
|
|
d.color_down = #0f2403
|
|
d.color_empty = #0f2403
|
|
d.color_disabled = #040603
|
|
d.border_color = #43862b
|
|
d.border_color_hover = #60c83e
|
|
d.border_color_focus = #98ff20
|
|
d.border_color_down = #98ff20
|
|
d.border_color_empty = #43862b
|
|
d.border_color_disabled = #1d4611
|
|
}
|
|
tm_field_inks(mod.widgets.TextInputFlat.draw_bg)
|
|
tm_field_inks(mod.widgets.TextInput.draw_bg)
|
|
mod.widgets.TextInputFlat.draw_text.color_empty = #43862b
|
|
mod.widgets.TextInputFlat.draw_text.color_empty_hover = #4f9a33
|
|
mod.widgets.TextInputFlat.draw_text.color_empty_focus = #43862b
|
|
// The caret is the underline block of a console: one cell wide, three
|
|
// device pixels tall at the foot of the cell, lit. (A full block is drawn
|
|
// after the letters here and cannot turn the letter under it to the
|
|
// ground, so it would hide it.)
|
|
mod.widgets.TextInputFlat.draw_cursor.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(vec2(self.rect_pos.x + 1.0, self.rect_pos.y + self.rect_size.y - 2.0), vec2(7.5, 2.0))
|
|
}
|
|
mod.widgets.TextInputFlat.draw_cursor.pixel = fn() {
|
|
let a = (1.0 - self.blink) * self.focus * (1.0 - self.disabled)
|
|
return vec4(self.color.rgb * a, a)
|
|
}
|
|
mod.widgets.TextInputFlat.draw_selection.border_radius = 0.0
|
|
|
|
// The number field draws its own box the same way; its step column is two
|
|
// dim cells split by one pixel of frame.
|
|
mod.widgets.NumberField.draw_bg.color = #0f2403
|
|
mod.widgets.NumberField.draw_bg.color_hover = #0f2403
|
|
mod.widgets.NumberField.draw_bg.color_focus = #0f2403
|
|
mod.widgets.NumberField.draw_bg.color_disabled = #040603
|
|
mod.widgets.NumberField.draw_bg.border_color = #43862b
|
|
mod.widgets.NumberField.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.NumberField.draw_bg.border_color_disabled = #1d4611
|
|
mod.widgets.NumberField.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let fill = self.color
|
|
.mix(self.color_hover, self.hover)
|
|
.mix(self.color_focus, self.focus)
|
|
.mix(self.color_disabled, self.disabled)
|
|
let stroke = self.border_color
|
|
.mix(vec4(0.376, 0.784, 0.243, 1.0), self.hover)
|
|
.mix(self.border_color_focus, self.focus)
|
|
.mix(self.border_color_disabled, self.disabled)
|
|
return tm_field(self.pos * self.rect_size, self.rect_pos, self.rect_size, px, fill, stroke, 1.0, 0.0, vec4(0.0, 0.0, 0.0, 0.0))
|
|
}
|
|
mod.widgets.NumberSpin.draw_bg.color = #00000000
|
|
mod.widgets.NumberSpin.draw_bg.color_hover = #98ff201a
|
|
mod.widgets.NumberSpin.draw_bg.color_down = #98ff20
|
|
mod.widgets.NumberSpin.draw_bg.color_disabled = #00000000
|
|
mod.widgets.NumberSpin.draw_bg.rule_color = #43862b
|
|
mod.widgets.NumberSpin.draw_bg.rule_color_disabled = #1d4611
|
|
mod.widgets.NumberSpin.draw_bg.rule = 0.67
|
|
mod.widgets.NumberSpin.draw_text.color = #6ee747
|
|
mod.widgets.NumberSpin.draw_text.color_disabled = #1d4611
|
|
|
|
// DROP-DOWNS. The field, with a solid down triangle in its last cell.
|
|
mod.widgets.DropDownFlat.padding = mod.turtle.Inset{left: 8.0 right: 22.0 top: 2.5 bottom: 2.5}
|
|
mod.widgets.DropDownFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let fill = self.color
|
|
.mix(self.color_focus, self.focus)
|
|
.mix(self.color_hover, self.hover)
|
|
.mix(self.color_down, self.down * self.hover)
|
|
.mix(self.color_disabled, self.disabled)
|
|
let stroke = self.border_color
|
|
.mix(self.border_color_focus, self.focus)
|
|
.mix(self.border_color_hover, self.hover)
|
|
.mix(self.border_color_down, max(self.down * self.hover, self.active))
|
|
.mix(self.border_color_disabled, self.disabled)
|
|
let acol = self.arrow_color
|
|
.mix(self.arrow_color_focus, self.focus)
|
|
.mix(self.arrow_color_hover.mix(self.arrow_color_down, self.down), self.hover)
|
|
.mix(self.arrow_color_disabled, self.disabled)
|
|
return tm_field(self.pos * self.rect_size, self.rect_pos, self.rect_size, px, fill, stroke, 1.0, 1.0, acol)
|
|
}
|
|
let tm_dropdown_inks = fn(d) {
|
|
d.color = #0f2403
|
|
d.color_hover = #163507
|
|
d.color_focus = #0f2403
|
|
d.color_down = #163507
|
|
d.color_disabled = #040603
|
|
d.color_2 = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
d.border_color = #43862b
|
|
d.border_color_hover = #60c83e
|
|
d.border_color_focus = #98ff20
|
|
d.border_color_down = #98ff20
|
|
d.border_color_disabled = #1d4611
|
|
d.border_color_2 = vec4(-1.0, -1.0, -1.0, -1.0)
|
|
d.arrow_color = #6ee747
|
|
d.arrow_color_hover = #98ff20
|
|
d.arrow_color_focus = #98ff20
|
|
d.arrow_color_down = #98ff20
|
|
d.arrow_color_disabled = #1d4611
|
|
}
|
|
tm_dropdown_inks(mod.widgets.DropDownFlat.draw_bg)
|
|
tm_dropdown_inks(mod.widgets.DropDown.draw_bg)
|
|
mod.widgets.DropDownFlat.draw_text.color = #6ee747
|
|
mod.widgets.DropDownFlat.draw_text.color_hover = #98ff20
|
|
mod.widgets.DropDownFlat.draw_text.color_focus = #98ff20
|
|
mod.widgets.DropDownFlat.draw_text.color_down = #98ff20
|
|
mod.widgets.DropDownFlat.draw_text.color_disabled = #1d4611
|
|
|
|
// The combo box: the same field and triangle; its open list lights the
|
|
// chosen row inverse.
|
|
mod.widgets.ComboBoxFlat.draw_bg.color = #0f2403
|
|
mod.widgets.ComboBoxFlat.draw_bg.color_hover = #0f2403
|
|
mod.widgets.ComboBoxFlat.draw_bg.color_focus = #0f2403
|
|
mod.widgets.ComboBoxFlat.draw_bg.color_down = #0f2403
|
|
mod.widgets.ComboBoxFlat.draw_bg.color_disabled = #040603
|
|
mod.widgets.ComboBoxFlat.draw_bg.border_color = #43862b
|
|
mod.widgets.ComboBoxFlat.draw_bg.border_color_hover = #60c83e
|
|
mod.widgets.ComboBoxFlat.draw_bg.border_color_focus = #98ff20
|
|
mod.widgets.ComboBoxFlat.draw_bg.border_color_down = #98ff20
|
|
mod.widgets.ComboBoxFlat.draw_bg.arrow_color = #6ee747
|
|
mod.widgets.ComboBoxFlat.draw_bg.arrow_color_hover = #98ff20
|
|
mod.widgets.ComboBoxFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let fill = self.color
|
|
.mix(self.color_focus, self.focus)
|
|
.mix(self.color_hover, self.hover)
|
|
.mix(self.color_down, self.down * self.hover)
|
|
.mix(self.color_disabled, self.disabled)
|
|
let stroke = self.border_color
|
|
.mix(self.border_color_focus, self.focus)
|
|
.mix(self.border_color_hover, self.hover)
|
|
.mix(self.border_color_down, self.down * self.hover)
|
|
let acol = self.arrow_color.mix(self.arrow_color_hover, self.hover)
|
|
return tm_field(self.pos * self.rect_size, self.rect_pos, self.rect_size, px, fill, stroke, 1.0, 1.0, acol)
|
|
}
|
|
mod.widgets.ComboBoxFlat.draw_popup_bg.color = #0e1e09
|
|
mod.widgets.ComboBoxFlat.draw_popup_bg.border_color = #43862b
|
|
mod.widgets.ComboBoxFlat.draw_popup_bg.border_radius = 0.0
|
|
mod.widgets.ComboBoxFlat.draw_item.color_hover = #98ff201f
|
|
mod.widgets.ComboBoxFlat.draw_item.color_active = #74fa4b
|
|
mod.widgets.ComboBoxFlat.draw_item_text.color_active = #040603
|
|
mod.widgets.ComboBoxFlat.item_height = 24.0
|
|
// the input inside the box takes no row of its own
|
|
mod.widgets.ComboBoxFlat.input.min_height = 0.0
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// TABS. Outlined cells a row tall sharing their one pixel lines; the tab in
|
|
// use is filled (inverse) and is the one lit thing of the strip. A rule in
|
|
// the frame green runs under the strip.
|
|
mod.widgets.Tabs.height = 24.0
|
|
mod.widgets.Tabs.draw_bg.color = #00000000
|
|
mod.widgets.Tabs.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let yb = Finish.snap(self.rect_pos.y + self.rect_size.y, px) - self.rect_pos.y
|
|
let la = Finish.band(p.y - yb, 0.0, px, px)
|
|
return vec4(vec3(0.263, 0.525, 0.169) * la, la)
|
|
}
|
|
let tm_tab_face = fn(pos: vec2, size: vec2, o: vec2, px: float, fill: vec4, stroke: vec3, glow: float) -> vec4 {
|
|
let m = 7.0
|
|
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
// the box runs one pixel past the right edge, so neighbours share it
|
|
let sb = tm_snapbox(o, vec2(0.0, 0.0), size + vec2(px, 0.0), px)
|
|
let c = (sb.xy + sb.zw) * 0.5
|
|
let h = (sb.zw - sb.xy) * 0.5
|
|
let d = tm_box(p, c, h)
|
|
let ink = vec3(0.596, 1.0, 0.125)
|
|
let ha = tm_halo(d, glow)
|
|
var out = vec4(ink * ha, ha)
|
|
let inside = Finish.cover(d, px)
|
|
let top = Finish.band(p.y - sb.y, -px, 0.0, px) * step(0.5, fill.a) * 0.08
|
|
out = tm_over(out, vec4(mix(fill.rgb, vec3(1.0, 1.0, 1.0), top) * fill.a * inside, fill.a * inside))
|
|
let la = Finish.band(d, 0.0, px, px)
|
|
out = tm_over(out, vec4(stroke * la, la))
|
|
return out
|
|
}
|
|
let tm_tab_vertex = fn(rp: vec2, rs: vec2) -> vec4 {
|
|
return tm_grow(rp, rs, 7.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab.vertex = fn() {
|
|
let g = tm_tab_vertex(self.rect_pos, self.rect_size)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.Tabs.draw_tab.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return tm_tab_face(self.pos, self.rect_size, self.rect_pos, px, vec4(0.0, 0.0, 0.0, 0.0), vec3(0.263, 0.525, 0.169), 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_hover.vertex = fn() {
|
|
let g = tm_tab_vertex(self.rect_pos, self.rect_size)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_hover.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return tm_tab_face(self.pos, self.rect_size, self.rect_pos, px, vec4(0.596, 1.0, 0.125, 0.1), vec3(0.596, 1.0, 0.125), 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_active.vertex = fn() {
|
|
let g = tm_tab_vertex(self.rect_pos, self.rect_size)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_active.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return tm_tab_face(self.pos, self.rect_size, self.rect_pos, px, vec4(0.455, 0.98, 0.294, 1.0), vec3(0.455, 0.98, 0.294), 0.05)
|
|
}
|
|
mod.widgets.Tabs.draw_sep.color = #00000000
|
|
mod.widgets.Tabs.draw_mark.color = #43862b
|
|
// The close mark is an x one cell wide at the right of its box, so it
|
|
// stands a cell clear of the label and a cell in from the tab's line; the
|
|
// add mark a one pixel plus in the middle of its own box.
|
|
mod.widgets.Tabs.draw_mark.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let o = self.rect_pos
|
|
let is_plus = step(0.5, self.plus)
|
|
let cx0 = mix(self.rect_size.x - 5.5, self.rect_size.x * 0.5, is_plus)
|
|
let c = vec2(Finish.snap(o.x + cx0, px), Finish.snap(o.y + self.rect_size.y * 0.5, px)) - o + vec2(px * 0.5, px * 0.5)
|
|
let q = p - c
|
|
let r = mix(2.5, 3.5, is_plus)
|
|
// the cross: two diagonals; the plus: two straight arms
|
|
let x1 = abs(q.x - q.y) * 0.70711
|
|
let x2 = abs(q.x + q.y) * 0.70711
|
|
let inbox = step(max(abs(q.x), abs(q.y)), r + px * 0.5)
|
|
let cross = max(Finish.cover(x1 - px * 0.5, px), Finish.cover(x2 - px * 0.5, px)) * inbox
|
|
let plus = max(step(abs(q.x), px * 0.5) , step(abs(q.y), px * 0.5)) * inbox
|
|
let a = mix(cross, plus, is_plus) * self.color.a
|
|
return vec4(self.color.rgb * a, a)
|
|
}
|
|
mod.widgets.Tabs.draw_text.color = #6ee747
|
|
mod.widgets.Tabs.draw_text_active.color = #040603
|
|
mod.widgets.Tabs.draw_text.ink_centered = true
|
|
mod.widgets.Tabs.draw_text_active.ink_centered = true
|
|
// The strip shares its width among the tabs and sets every label 10
|
|
// points in from a fixed box, so the label type is a step under the body
|
|
// type: the longest label then has a cell of room on either side.
|
|
let tm_tab_type = mod.theme.font_regular{font_size: 8.5 line_spacing: 1.0}
|
|
mod.widgets.Tabs.draw_text.text_style = tm_tab_type
|
|
mod.widgets.Tabs.draw_text_active.text_style = tm_tab_type
|
|
mod.widgets.Tabs.clip_x = false
|
|
mod.widgets.Tabs.clip_y = false
|
|
|
|
// The segmented group: one frame round the row, the chosen cell inverse.
|
|
mod.widgets.SegmentedControlFlat.segment_height = 24.0
|
|
mod.widgets.SegmentedControlFlat.segment_padding = 8.0
|
|
mod.widgets.SegmentedControlFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let sb = tm_snapbox(self.rect_pos, vec2(0.0, 0.0), self.rect_size, px)
|
|
let d = tm_box(p, (sb.xy + sb.zw) * 0.5, (sb.zw - sb.xy) * 0.5)
|
|
let la = Finish.band(d, 0.0, px, px)
|
|
return vec4(vec3(0.263, 0.525, 0.169) * la, la)
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_pill.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let sb = tm_snapbox(self.rect_pos, vec2(0.0, 0.0), self.rect_size, px)
|
|
let d = tm_box(p, (sb.xy + sb.zw) * 0.5, (sb.zw - sb.xy) * 0.5)
|
|
let a = Finish.cover(d, px)
|
|
let top = Finish.band(p.y - sb.y, -px, 0.0, px) * 0.08
|
|
return vec4(mix(vec3(0.455, 0.98, 0.294), vec3(1.0, 1.0, 1.0), top) * a, a)
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_text.color = #6ee747
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #040603
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.text_style = mod.theme.font_regular{}
|
|
// the chosen cell's letters are the palette's ink on the container
|
|
mod.widgets.SegmentedControlFlat.palette.on_neutral_container = #040603
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// PROGRESS. An 8 point bar: a one pixel frame, two pixels clear, and a fill
|
|
// of lit cells four device pixels apart with a one pixel darker gap;
|
|
// the rest of the bar is ground. The indeterminate bar cannot move without
|
|
// a clock, so it stands still as a light-shade dither over the whole bar.
|
|
mod.widgets.ProgressBarFlat.height = 8.0
|
|
mod.widgets.ProgressBarFlat.draw_bg.thickness = uniform(8.0)
|
|
mod.widgets.ProgressBarFlat.draw_bg.border_radius = uniform(0.0)
|
|
mod.widgets.ProgressBarFlat.draw_bg.color_primary = #98ff20
|
|
mod.widgets.ProgressBarFlat.draw_bg.color_success = #98ff20
|
|
mod.widgets.ProgressBarFlat.draw_bg.color_info = #98ff20
|
|
mod.widgets.ProgressBarFlat.draw_bg.color_warning = #98ff20
|
|
mod.widgets.ProgressBarFlat.draw_bg.color_error = #98ff20
|
|
mod.widgets.ProgressBarFlat.draw_bg.color_disabled = #1d4611
|
|
mod.widgets.ProgressBarFlat.draw_bg.track_color = #00000000
|
|
mod.widgets.ProgressBarFlat.draw_bg.border_color = #60c83e
|
|
mod.widgets.ProgressBar.draw_bg.border_color = #60c83e
|
|
mod.widgets.ProgressBarFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let o = self.rect_pos
|
|
let hgt = min(self.thickness, self.rect_size.y)
|
|
let y0 = (self.rect_size.y - hgt) * 0.5
|
|
let x0 = self.rect_size.x * self.start
|
|
let x1 = self.rect_size.x * self.end
|
|
let sb = tm_snapbox(o, vec2(x0, y0), vec2(x1, y0 + hgt), px)
|
|
let c = (sb.xy + sb.zw) * 0.5
|
|
let h = (sb.zw - sb.xy) * 0.5
|
|
let d = tm_box(p, c, h)
|
|
let fill = self.fill_color()
|
|
var out = vec4(0.0, 0.0, 0.0, 0.0)
|
|
if self.track > 0.5 {
|
|
let la = Finish.band(d, 0.0, px, px) * self.border_color.a
|
|
out = vec4(self.border_color.rgb * la, la)
|
|
let ta = Finish.cover(d, px) * self.track_color.a * (1.0 - Finish.band(d, 0.0, px, px))
|
|
out = tm_over(vec4(self.track_color.rgb * ta, ta), out)
|
|
}
|
|
// the inner field, three pixels in (one of line, two clear)
|
|
let ih = h - vec2(3.0 * px, 3.0 * px)
|
|
let inner = Finish.cover(tm_box(p, c, ih), px)
|
|
let fx0 = c.x - ih.x
|
|
let span = ih.x * 2.0
|
|
if self.value >= 0.0 {
|
|
let v = clamp((self.value - self.start) / max(self.end - self.start, 0.0001), 0.0, 1.0)
|
|
let fx1 = Finish.snap(o.x + fx0 + v * span, px) - o.x
|
|
let on = step(p.x, fx1) * inner
|
|
// cells four device pixels apart (three lit, one gap in the darker
|
|
// green), counted in whole window pixels so the pitch never beats
|
|
let k = floor((p.x + o.x) / px + 0.001)
|
|
let gap = step(0.7, fract(k * 0.25))
|
|
let col = mix(fill.rgb, vec3(0.153, 0.549, 0.0), gap)
|
|
let fa = on * fill.a
|
|
out = tm_over(out, vec4(col * fa, fa))
|
|
} else {
|
|
let q = floor((p + o) / px)
|
|
let dot = step(fract(q.x / 3.0), 0.2) * step(fract((q.y + floor(q.x / 3.0) * 1.5) / 3.0), 0.2)
|
|
let da = dot * inner
|
|
out = tm_over(out, vec4(fill.rgb * da, da))
|
|
}
|
|
return out * self.opacity
|
|
}
|
|
|
|
// LEVEL METERS. Cells 8 points long with 2 clear, 6 points across; the
|
|
// quiet run in the accent, amber from 70 %, red over the last 10 % -- the
|
|
// only other hues this palette allows, and only here. Unlit cells stand at
|
|
// a quarter of their ink; the held peak is one pixel across in the core ink.
|
|
mod.widgets.LevelMeter.height = 8.0
|
|
mod.widgets.LevelMeterColumn.width = 8.0
|
|
mod.widgets.LevelMeter.draw_bg.color_level = #98ff20
|
|
mod.widgets.LevelMeter.draw_bg.color_hot = #dd8b1b
|
|
mod.widgets.LevelMeter.draw_bg.color_mark = #b7f588
|
|
mod.widgets.LevelMeter.draw_bg.color_lamp = #ff2116
|
|
mod.widgets.LevelMeter.draw_bg.color_lamp_off = #1d4611
|
|
mod.widgets.LevelMeter.draw_bg.lamp_size = uniform(6.0)
|
|
mod.widgets.LevelMeter.draw_bg.lamp_gap = uniform(4.0)
|
|
mod.widgets.LevelMeter.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let along = mix(self.rect_size.x, self.rect_size.y, self.vertical)
|
|
let across = mix(self.rect_size.y, self.rect_size.x, self.vertical)
|
|
let s = mix(self.pos.x * self.rect_size.x, (1.0 - self.pos.y) * self.rect_size.y, self.vertical)
|
|
let q = mix(self.pos.y * self.rect_size.y, self.pos.x * self.rect_size.x, self.vertical)
|
|
let lamp_room = self.lamp * (self.lamp_size + self.lamp_gap)
|
|
let bar = max(4.0, along - lamp_room)
|
|
let pitch = 10.0
|
|
let n = floor((bar + 2.0) / pitch)
|
|
let k = floor(s / pitch)
|
|
let within = s - k * pitch
|
|
let in_cell = step(within, pitch - 2.0) * step(k, n - 1.0) * step(0.0, s)
|
|
let cw = min(6.0, across)
|
|
let qa = (across - cw) * 0.5
|
|
let in_q = step(qa, q) * step(q, qa + cw)
|
|
// the scale share at the middle of this cell
|
|
let share = (k + 0.5) / max(n, 1.0)
|
|
let zone_amber = step(0.7, share)
|
|
let zone_red = step(0.9, share)
|
|
var ink = mix(self.color_level.rgb, self.color_hot.rgb, zone_amber)
|
|
ink = mix(ink, vec3(1.0, 0.129, 0.086), zone_red)
|
|
let lit = step(share, self.level + 0.5 / max(n, 1.0) - 0.0001) * step(0.0001, self.level)
|
|
let col = mix(ink * 0.25, ink, lit)
|
|
let a = in_cell * in_q
|
|
var out = vec4(col * a, a)
|
|
// the held peak: one pixel across the whole meter, in the core ink
|
|
if self.hold > 0.002 {
|
|
let at = floor(self.hold * bar)
|
|
let pk = Finish.band(abs(s - at) - px * 0.5, 0.0, px, px) * in_q
|
|
out = tm_over(out, vec4(self.color_mark.rgb * pk, pk))
|
|
}
|
|
if self.lamp > 0.5 {
|
|
let lc = vec2(bar + self.lamp_gap + self.lamp_size * 0.5, across * 0.5)
|
|
let ld = tm_box(vec2(s, q), lc, vec2(self.lamp_size * 0.5, 3.0))
|
|
let on = step(0.5, self.over)
|
|
let lcol = mix(self.color_lamp_off.rgb, self.color_lamp.rgb, on)
|
|
// a lit lamp is whiter at its centre, and carries the halo
|
|
let core = (1.0 - smoothstep(0.0, 2.5, length(vec2(s, q) - lc))) * 0.15 * on
|
|
let ha = tm_halo(ld, 0.08 * on)
|
|
out = tm_over(out, vec4(lcol * ha, ha))
|
|
let la = Finish.cover(ld, px)
|
|
out = tm_over(out, vec4(mix(lcol, vec3(1.0, 1.0, 1.0), core) * la, la))
|
|
}
|
|
return out * self.opacity
|
|
}
|
|
|
|
// SCROLL BARS. Half a cell wide: a light-shade dither of the disabled green
|
|
// for the track, a solid block for the thumb, lit while dragged.
|
|
mod.widgets.ScrollBar.bar_size = 8.0
|
|
mod.widgets.ScrollBar.bar_side_margin = 0.0
|
|
mod.widgets.ScrollBar.draw_bg.size = 8.0
|
|
mod.widgets.ScrollBar.draw_bg.color = #43862b
|
|
mod.widgets.ScrollBar.draw_bg.color_hover = #60c83e
|
|
mod.widgets.ScrollBar.draw_bg.color_drag = #98ff20
|
|
mod.widgets.ScrollBar.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let len = mix(self.rect_size.x, self.rect_size.y, self.is_vertical)
|
|
let t = mix(p.x, p.y, self.is_vertical)
|
|
let q = floor((p + self.rect_pos) / px)
|
|
let dot = step(fract(q.x / 3.0), 0.2) * step(fract((q.y + floor(q.x / 3.0) * 1.5) / 3.0), 0.2)
|
|
let da = dot * 0.9
|
|
var out = vec4(vec3(0.114, 0.275, 0.067) * da, da)
|
|
let t0 = len * self.norm_scroll
|
|
let t1 = t0 + len * self.norm_handle
|
|
let on = Finish.cover(max(t0 - t, t - t1), px)
|
|
let col = self.color.mix(self.color_hover, self.hover).mix(self.color_drag, self.drag)
|
|
out = tm_over(out, vec4(col.rgb * on, on))
|
|
return out * self.opacity
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SURFACES. Flat rectangles, never slabs. The rounded view is square here.
|
|
mod.widgets.RoundedView.draw_bg.border_radius = 0.0
|
|
mod.widgets.RoundedView.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let lo = vec2(self.border_inset.x, self.border_inset.y)
|
|
let hi = self.rect_size - vec2(self.border_inset.z, self.border_inset.w)
|
|
let sb = tm_snapbox(self.rect_pos, lo, hi, px)
|
|
let d = tm_box(p, (sb.xy + sb.zw) * 0.5, (sb.zw - sb.xy) * 0.5)
|
|
var fill = self.color
|
|
if self.color_2.x > -0.5 {
|
|
fill = mix(self.color, self.color_2, mix(self.pos.y, self.pos.x, self.gradient_fill_horizontal))
|
|
}
|
|
let inside = Finish.cover(d, px)
|
|
var out = vec4(fill.rgb * fill.a * inside, fill.a * inside)
|
|
if self.border_size > 0.0 {
|
|
let la = Finish.band(d, 0.0, px, px) * self.border_color.a
|
|
out = tm_over(out, vec4(self.border_color.rgb * la, la))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// The panel ground: the panel green with scanlines (every other device row
|
|
// at 0.92) and a grain of about 0.004, both in window space.
|
|
let tm_ground = fn(base: vec3, p: vec2, o: vec2, px: float, dpi: float) -> vec3 {
|
|
let w = p + o
|
|
let s = tm_scan(w.y, dpi, 0.08)
|
|
return base * s + vec3(1.0, 1.0, 1.0) * tm_grain(w, px, 0.01)
|
|
}
|
|
|
|
// Panels: a single box line of the frame green half a cell in from the edge.
|
|
// Sunken, the panel is an embedded screen: darker, a double line (the outer
|
|
// one at half strength), and three points of darkening under its top edge.
|
|
mod.widgets.PanelView.draw_bg.color = #0e1e09
|
|
mod.widgets.PanelView.draw_bg.border_radius = 0.0
|
|
mod.widgets.PanelView.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let dpi = max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let o = self.rect_pos
|
|
let sunk = step(0.5, self.sunken)
|
|
let base = mix(self.color.rgb, vec3(0.008, 0.016, 0.004), sunk)
|
|
var col = tm_ground(base, p, o, px, dpi)
|
|
let top = (1.0 - clamp(p.y / 3.0, 0.0, 1.0)) * 0.2 * sunk
|
|
col = col * (1.0 - top)
|
|
let sb = tm_snapbox(o, vec2(4.0, 4.0), self.rect_size - vec2(4.0, 4.0), px)
|
|
let d = tm_box(p, (sb.xy + sb.zw) * 0.5, (sb.zw - sb.xy) * 0.5)
|
|
let frame = vec3(0.263, 0.525, 0.169)
|
|
let l1 = Finish.band(d, 0.0, px, px)
|
|
let l2 = Finish.band(d, 3.0 * px, 4.0 * px, px) * sunk
|
|
col = mix(col, frame, max(l1 * mix(1.0, 0.5, sunk), l2))
|
|
return vec4(col * self.color.a, self.color.a)
|
|
}
|
|
|
|
// Cards: the panel ground and one box line at the card's own edge.
|
|
mod.widgets.Card.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let dpi = max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size3
|
|
let o = self.rect_pos2
|
|
let lo = self.sdf_rect_pos - vec2(self.border_size, self.border_size)
|
|
let hi = lo + self.sdf_rect_size + vec2(2.0 * self.border_size, 2.0 * self.border_size)
|
|
let sb = tm_snapbox(o, lo, hi, px)
|
|
let d = tm_box(p, (sb.xy + sb.zw) * 0.5, (sb.zw - sb.xy) * 0.5)
|
|
var col = tm_ground(vec3(0.055, 0.118, 0.035), p, o, px, dpi)
|
|
let layer = self.hover * 0.06 + self.down * 0.1
|
|
col = mix(col, vec3(0.596, 1.0, 0.125), layer)
|
|
col = mix(col, vec3(0.263, 0.525, 0.169), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
return vec4(col * a, a)
|
|
}
|
|
|
|
// List rows: a row a line tall, no rules. The chosen row carries the dim
|
|
// bar and a lit block in its first half cell; the pointer lays a 12 % tint.
|
|
mod.widgets.ListItemOne.height = 24.0
|
|
mod.widgets.ListItem.padding = mod.turtle.Inset{left: 12.0 right: 8.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ListItem.draw_text.color = #6ee747
|
|
mod.widgets.ListItem.draw_text_2.color = #4f9a33
|
|
mod.widgets.ListItem.draw_bg.color_selected = #1d4611
|
|
mod.widgets.ListItem.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
var ground = mix(self.color, self.color_selected, self.selected)
|
|
ground = vec4(ground.rgb, ground.a * mix(1.0, 0.4, self.disabled))
|
|
let layer = (self.hover * 0.12 + self.down * 0.2) * (1.0 - self.disabled)
|
|
var out = vec4(ground.rgb * ground.a, ground.a)
|
|
out = tm_over(out, vec4(vec3(0.596, 1.0, 0.125) * layer, layer))
|
|
let mark = step(p.x, Finish.snap(self.rect_pos.x + 4.0, px) - self.rect_pos.x) * self.selected
|
|
out = tm_over(out, vec4(vec3(0.596, 1.0, 0.125) * mark, mark))
|
|
return out
|
|
}
|
|
|
|
// The open list of a drop-down: the panel ground, the pointer's row a
|
|
// faint tint, the chosen row inverse.
|
|
mod.widgets.PopupMenuItem.draw_bg.color_hover = #98ff201f
|
|
mod.widgets.PopupMenuItem.draw_bg.color_active = #74fa4b
|
|
mod.widgets.PopupMenuItem.draw_text.color_active = #040603
|
|
|
|
// THE SPINNER. One cell of a console spinner, the slash of | / - \, one
|
|
// device pixel wide in the accent, no circle. A sheet may not read the
|
|
// pass clock and the spinner carries no other, so the slash stands still.
|
|
mod.widgets.SpinnerFlat.size = 16.0
|
|
mod.widgets.SpinnerFlat.draw_bg.color = #98ff20
|
|
mod.widgets.SpinnerFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let o = self.rect_pos
|
|
let c = vec2(Finish.snap(o.x + self.rect_size.x * 0.5, px), Finish.snap(o.y + self.rect_size.y * 0.5, px)) - o
|
|
let q = p - c
|
|
// a cell 8 wide and 16 tall: the slash runs corner to corner of its
|
|
// inner 6 by 10
|
|
let dir = normalize(vec2(3.0, -5.0))
|
|
let along = clamp(dot(q, dir), -5.83, 5.83)
|
|
let dl = length(q - dir * along) - px * 0.5
|
|
let a = Finish.cover(dl, px) * self.color.a * self.alpha
|
|
return vec4(self.color.rgb * a, a)
|
|
}
|
|
|
|
// Chips and badges are square cells too, in the one hue: a neutral mark is
|
|
// a dim block with the core ink, a tonal chip a well with the text ink.
|
|
mod.widgets.ChipFlat.radius = 0.0
|
|
// A chip is a cell row 24 points tall inside the 28 its size rung takes,
|
|
// boxed by one snapped device pixel of the frame green (its own stroke
|
|
// colour when it has one); the pointer lays the accent tint over it.
|
|
mod.widgets.ChipFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let inset = max((self.rect_size.y - 24.0) * 0.5, 0.0)
|
|
let sb = tm_snapbox(self.rect_pos, vec2(0.0, inset), self.rect_size - vec2(0.0, inset), px)
|
|
let d = tm_box(p, (sb.xy + sb.zw) * 0.5, (sb.zw - sb.xy) * 0.5)
|
|
let inside = Finish.cover(d, px)
|
|
let layer = (self.hover * 0.1 + self.down * 0.2) * self.ink.a
|
|
let fill = vec4(mix(self.color.rgb, vec3(0.596, 1.0, 0.125), layer), max(self.color.a, layer))
|
|
var out = vec4(fill.rgb * fill.a * inside, fill.a * inside)
|
|
let lined = step(0.001, self.border_size)
|
|
let lc = mix(vec4(0.263, 0.525, 0.169, 1.0), self.border_color, lined)
|
|
let la = Finish.band(d, 0.0, px, px) * lc.a
|
|
out = tm_over(out, vec4(lc.rgb * la, la))
|
|
let kf = Finish.band(d, -3.0 * px, -2.0 * px, px) * self.keyed
|
|
out = tm_over(out, vec4(vec3(0.596, 1.0, 0.125) * kf, kf))
|
|
return out
|
|
}
|
|
mod.widgets.BadgeFlat.shape = mod.widgets.BadgeShape.Square
|
|
mod.widgets.BadgePalette.neutral = #2e5d1d
|
|
mod.widgets.BadgePalette.on_neutral = #b7f588
|
|
mod.widgets.BadgePalette.neutral_container = #0f2403
|
|
mod.widgets.BadgePalette.on_neutral_container = #6ee747
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------- 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. 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 = 0.0
|
|
mod.widgets.RangeSliderFlat.handle_size = 8.0
|
|
mod.widgets.RangeSliderFlat.draw_bg.vertex = fn() {
|
|
let g = tm_grow(self.rect_pos, self.rect_size, 6.0)
|
|
self.vertex_pos = self.clip_and_transform_vertex(g.xy, g.zw)
|
|
}
|
|
mod.widgets.RangeSliderFlat.draw_bg.pixel = fn() {
|
|
let m = 6.0
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let val = self.val_color
|
|
.mix(self.val_color_focus, self.focus)
|
|
.mix(self.val_color_hover.mix(self.val_color_drag, self.drag), self.hover)
|
|
.mix(self.val_color_disabled, self.disabled)
|
|
let handle = self.handle_color
|
|
.mix(self.handle_color_focus, self.focus)
|
|
.mix(self.handle_color_hover.mix(self.handle_color_drag, self.drag), self.hover)
|
|
.mix(self.handle_color_disabled, self.disabled)
|
|
let far = tm_slider_line(p, self.rect_pos, self.rect_size, self.label_px, px, self.end_pos, self.start_pos, self.end_pos, 8.0, 14.0, val, handle, self.hot_end, self.focus, self.drag * self.hot_end, self.disabled)
|
|
let near = tm_slider_line(p, self.rect_pos, self.rect_size, self.label_px, px, self.start_pos, self.start_pos, self.end_pos, 8.0, 14.0, val, handle, self.hot_start, self.focus, self.drag * self.hot_start, self.disabled)
|
|
let mid = 4.0 + (self.start_pos + self.end_pos) * 0.5 * (self.rect_size.x - 8.0)
|
|
return mix(near, far, step(mid, p.x))
|
|
}
|
|
|
|
// ---------------------------------------------------------------- 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.label_walk.margin = mod.turtle.Inset{left: 36.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ToggleSlide.label_walk.margin = mod.turtle.Inset{left: 36.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ToggleRocker.draw_bg.pill_aspect = uniform(2.3333)
|
|
mod.widgets.ToggleSlide.draw_bg.pill_aspect = uniform(2.3333)
|
|
mod.widgets.ToggleRocker.draw_bg.knob_inset = uniform(2.0)
|
|
mod.widgets.ToggleSlide.draw_bg.knob_inset = uniform(2.0)
|
|
mod.widgets.ToggleRocker.draw_bg.color = #0f2403
|
|
mod.widgets.ToggleSlide.draw_bg.color = #0f2403
|
|
mod.widgets.ToggleRocker.draw_bg.color_hover = #0f2403
|
|
mod.widgets.ToggleSlide.draw_bg.color_hover = #0f2403
|
|
mod.widgets.ToggleRocker.draw_bg.color_down = #0f2403
|
|
mod.widgets.ToggleSlide.draw_bg.color_down = #0f2403
|
|
mod.widgets.ToggleRocker.draw_bg.color_active = #0f2403
|
|
mod.widgets.ToggleSlide.draw_bg.color_active = #0f2403
|
|
mod.widgets.ToggleRocker.draw_bg.color_focus = #0f2403
|
|
mod.widgets.ToggleSlide.draw_bg.color_focus = #0f2403
|
|
mod.widgets.ToggleRocker.draw_bg.color_disabled = #040603
|
|
mod.widgets.ToggleSlide.draw_bg.color_disabled = #040603
|
|
mod.widgets.ToggleRocker.draw_bg.mark_color = #2e5d1d
|
|
mod.widgets.ToggleSlide.draw_bg.mark_color = #2e5d1d
|
|
mod.widgets.ToggleRocker.draw_bg.mark_color_hover = #43862b
|
|
mod.widgets.ToggleSlide.draw_bg.mark_color_hover = #43862b
|
|
mod.widgets.ToggleRocker.draw_bg.mark_color_down = #98ff20
|
|
mod.widgets.ToggleSlide.draw_bg.mark_color_down = #98ff20
|
|
mod.widgets.ToggleRocker.draw_bg.mark_color_active = #98ff20
|
|
mod.widgets.ToggleSlide.draw_bg.mark_color_active = #98ff20
|
|
mod.widgets.ToggleRocker.draw_bg.mark_color_active_hover = #b7f588
|
|
mod.widgets.ToggleSlide.draw_bg.mark_color_active_hover = #b7f588
|
|
mod.widgets.ToggleRocker.draw_bg.mark_color_disabled = #1d4611
|
|
mod.widgets.ToggleSlide.draw_bg.mark_color_disabled = #1d4611
|
|
mod.widgets.ToggleRocker.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
|
|
mod.widgets.ToggleSlide.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
|
|
|
|
// 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
|
|
}
|
|
|
|
// ---------------------------------------------------------------- lamps
|
|
// LAMPS: an LED drawn in light. Its light is added, never painted, screened
|
|
// over whatever is there.
|
|
let tm_led_lamp_add = fn(res: vec4, light: vec3, ground: vec3) -> vec4 {
|
|
let est = res.rgb + ground * (1.0 - res.a)
|
|
let add = (vec3(1.0, 1.0, 1.0) - exp(-light)) * max(vec3(1.0, 1.0, 1.0) - est, vec3(0.0, 0.0, 0.0))
|
|
return vec4(res.rgb + add, res.a)
|
|
}
|
|
// An LED's colour at full strength: its brightest channel at one.
|
|
let tm_led_lamp_full = fn(ink: vec3) -> vec3 {
|
|
return ink / max(max(ink.x, max(ink.y, ink.z)), 0.01)
|
|
}
|
|
// The same colour at the emitter's own chroma: the hue kept and its
|
|
// saturation raised by `vivid` (0 leaves a pale colour pale; a colour with
|
|
// next to no hue, a white or a plain lamp, stays neutral either way).
|
|
let tm_led_lamp_hue = fn(ink: vec3, vivid: float) -> vec3 {
|
|
let f = tm_led_lamp_full(ink)
|
|
let mn = min(f.x, min(f.y, f.z))
|
|
let ch = 1.0 - mn
|
|
let pure = (f - vec3(mn, mn, mn)) / max(ch, 0.001)
|
|
return mix(f, pure, clamp(vivid, 0.0, 1.0) * 0.72 * smoothstep(0.03, 0.12, ch))
|
|
}
|
|
// The same LED where the sheet draws with light alone (a vector display, a
|
|
// phosphor screen, a neon tube): no glass and no bezel, only the emitter --
|
|
// a white-hot core at full current, its rim the most saturated colour, a
|
|
// tight halo in its hue gone in about two radii -- and, out, a dark lens
|
|
// rimmed by a faint line of the colour it would light. Premultiplied over
|
|
// `dst`.
|
|
let tm_led_lamp_light = fn(dst: vec4, p: vec2, c: vec2, hb: vec2, lit: float, ink: vec3, vivid: float, px: float, ground: vec3, room: float) -> vec4 {
|
|
let rt = max(min(hb.x, hb.y), 0.4)
|
|
let d = Material.sd_box(p, c, hb, rt)
|
|
let L = clamp(lit, 0.0, 1.0)
|
|
let b = tm_led_lamp_full(ink)
|
|
let hue = tm_led_lamp_hue(ink, vivid)
|
|
let pw = L * sqrt(L)
|
|
let white = vec3(1.0, 1.0, 1.0)
|
|
var res = dst
|
|
let x = max(d, 0.0)
|
|
let reach = max(room, 1.0)
|
|
let win = 1.0 - smoothstep(0.55 * reach, reach, x)
|
|
if L > 0.001 {
|
|
let halo = exp(-x / max(0.32 * rt, 0.45)) * 1.6 + exp(-x / max(0.7 * rt, 0.8)) * 0.35
|
|
let hwin = 1.0 - smoothstep(1.1 * rt, 2.0 * rt + 1.0, x)
|
|
res = tm_led_lamp_add(res, mix(hue, white, 0.12 * pw) * halo * pw * hwin * win, ground)
|
|
}
|
|
let ex = max(hb.x - hb.y, 0.0)
|
|
let ey = max(hb.y - hb.x, 0.0)
|
|
let qx = p.x - c.x
|
|
let qy = p.y - c.y
|
|
let q = vec2(qx - clamp(qx, 0.0 - ex, ex), qy - clamp(qy, 0.0 - ey, ey)) / rt
|
|
let t = clamp(length(q), 0.0, 1.0)
|
|
var on = mix(b, hue, 0.5) * (0.35 + 0.65 * L)
|
|
on = mix(on, vec3(1.0, 0.99, 0.98), exp(-t * t / 0.22) * smoothstep(0.5, 1.0, L) * 0.9)
|
|
on = mix(on, hue * (0.4 + 0.6 * L), smoothstep(0.6, 1.0, t))
|
|
var off = mix(vec3(0.02, 0.02, 0.03), b * 0.2, 0.15)
|
|
off = mix(off, hue * 0.45, Finish.band(d, 0.0, max(px * 1.2, 0.5), px))
|
|
let col = mix(off, min(on, white), smoothstep(0.0, 0.3, L))
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(res, vec4(col * a, a))
|
|
}
|
|
|
|
// The Lamp widget and the bar lamp: the light above, phosphor: no glass and no bezel, the light alone.
|
|
// The halo's room is held in the lamp's own box (the library reserves a
|
|
// lens thickness either side once a halo is asked for), so the light is
|
|
// never cut at its edge.
|
|
mod.widgets.Lamp.halo = 0.3
|
|
mod.widgets.Lamp.halo_reach = 1.0
|
|
mod.widgets.Lamp.draw_bg.vertex = fn() {
|
|
let m = 2.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.Lamp.draw_bg.pixel = fn() {
|
|
let m = 2.0
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let reach = clamp(self.reach, 0.0, 1.0)
|
|
let t = self.rect_size.y / (1.0 + 2.0 * reach)
|
|
let room = t * reach
|
|
let c = self.rect_size * 0.5
|
|
let half_len = max((self.rect_size.x - 2.0 * room) * 0.5, t * 0.5)
|
|
// The lens is the lamp's size; its socket stands round it in the room.
|
|
let hb = vec2(mix(t * 0.5, half_len, self.bar), t * 0.5)
|
|
let res = tm_led_lamp_light(vec4(0.0, 0.0, 0.0, 0.0), p, c, hb, self.lit, self.intent_color().rgb, 1.0 - step(3.5, self.intent), px, vec3(0.016, 0.024, 0.012), max(room - 0.5, 2.0))
|
|
return res * self.opacity
|
|
}
|
|
true
|