UNPKG

@thi.ng/imgui

Version:

Immediate mode GUI with flexible state handling & data only shape output

128 lines (127 loc) 3.11 kB
import { circle } from "@thi.ng/geom/circle"; import { line } from "@thi.ng/geom/line"; import { isLayout } from "@thi.ng/layout/checks"; import { HALF_PI, PI, TAU } from "@thi.ng/math/api"; import { norm } from "@thi.ng/math/fit"; import { clamp } from "@thi.ng/math/interval"; import { cartesian2 } from "@thi.ng/vectors/cartesian"; import { hash } from "@thi.ng/vectors/hash"; import { dialVal } from "../behaviors/dial.js"; import { handleSlider1Keys, isHoverSlider } from "../behaviors/slider.js"; import { dialValueLabel } from "./textlabel.js"; import { tooltipRaw } from "./tooltip.js"; const dial = ({ gui, layout, id, min, max, step, value, label, fmt, info }) => { const { x, y, w, h, ch } = isLayout(layout) ? layout.nextSquare() : layout; return dialRaw( gui, id, x, y, w, h, min, max, step, value, gui.theme.pad, h + ch / 2 + gui.theme.baseLine, label, fmt, info ); }; const dialGroup = (opts) => { const { layout, id, value, label, info } = opts; const n = opts.value.length; const nested = opts.horizontal !== false ? layout.nest(n, [n, 1]) : layout.nest(1, [1, (layout.rowsForHeight(layout.cellW) + 1) * n]); let res; let idx = -1; for (let i = 0; i < n; i++) { const v = dial({ ...opts, layout: nested, id: `${id}-${i}`, value: value[i], label: label[i], info: info?.[i] }); if (v !== void 0) { res = v; idx = i; } } return res !== void 0 ? [idx, res] : void 0; }; const dialRaw = (gui, id, x, y, w, h, min, max, step, value, labelX, labelY, label, fmt, info) => { const r = Math.min(w, h) / 2; const pos = [x + w / 2, y + h / 2]; const thetaGap = PI / 2; const startTheta = HALF_PI + thetaGap / 2; const key = hash([x, y, r]); gui.registerID(id, key); const bgShape = gui.resource(id, key, () => circle(pos, r, {})); const hover = isHoverSlider(gui, id, bgShape, "pointer"); const draw = gui.draw; let v = clamp(value, min, max); let res; if (hover) { gui.hotID = id; if (gui.isMouseDown()) { gui.activeID = id; res = dialVal(gui.mouse, pos, startTheta, thetaGap, min, max, step); } info && draw && tooltipRaw(gui, info); } const focused = gui.requestFocus(id); if (draw) { const valShape = gui.resource( id, v, () => line( cartesian2( null, [r, startTheta + (TAU - thetaGap) * norm(v, min, max)], pos ), pos, {} ) ); const valLabel = dialValueLabel( gui, id, key, v, x + labelX, y + labelY, label, fmt ); bgShape.attribs.fill = gui.bgColor(hover || focused); bgShape.attribs.stroke = gui.focusColor(id); valShape.attribs.stroke = gui.fgColor(hover); valShape.attribs.weight = 2; gui.add(bgShape, valShape, valLabel); } if (focused && (v = handleSlider1Keys(gui, min, max, step, v)) !== void 0) { return v; } gui.lastID = id; return res; }; export { dial, dialGroup, dialRaw };