@thi.ng/imgui 2.2.12 → 2.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/api.js +94 -84
- package/behaviors/button.js +26 -22
- package/behaviors/dial.js +12 -4
- package/behaviors/slider.js +46 -43
- package/behaviors/text.js +75 -70
- package/components/button.js +64 -41
- package/components/dial.js +102 -52
- package/components/dropdown.js +62 -64
- package/components/icon-button.js +40 -31
- package/components/radial-menu.js +43 -37
- package/components/radio.js +19 -14
- package/components/ring.js +123 -79
- package/components/sliderh.js +96 -46
- package/components/sliderv.js +100 -53
- package/components/textfield.js +83 -44
- package/components/textlabel.js +28 -14
- package/components/toggle.js +45 -38
- package/components/tooltip.js +16 -6
- package/components/xypad.js +89 -73
- package/events.js +36 -47
- package/gui.js +400 -415
- package/hash.js +17 -45
- package/layout.js +4 -1
- package/package.json +16 -14
package/components/dial.js
CHANGED
|
@@ -9,60 +9,110 @@ import { dialVal } from "../behaviors/dial.js";
|
|
|
9
9
|
import { handleSlider1Keys, isHoverSlider } from "../behaviors/slider.js";
|
|
10
10
|
import { dialValueLabel } from "./textlabel.js";
|
|
11
11
|
import { tooltipRaw } from "./tooltip.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const dial = (gui, layout, id, min, max, prec, val, label, fmt, info) => {
|
|
13
|
+
const { x, y, w, h, ch } = isLayout(layout) ? layout.nextSquare() : layout;
|
|
14
|
+
return dialRaw(
|
|
15
|
+
gui,
|
|
16
|
+
id,
|
|
17
|
+
x,
|
|
18
|
+
y,
|
|
19
|
+
w,
|
|
20
|
+
h,
|
|
21
|
+
min,
|
|
22
|
+
max,
|
|
23
|
+
prec,
|
|
24
|
+
val,
|
|
25
|
+
gui.theme.pad,
|
|
26
|
+
h + ch / 2 + gui.theme.baseLine,
|
|
27
|
+
label,
|
|
28
|
+
fmt,
|
|
29
|
+
info
|
|
30
|
+
);
|
|
15
31
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
const dialGroup = (gui, layout, id, min, max, prec, horizontal, vals, label, fmt, info = []) => {
|
|
33
|
+
const n = vals.length;
|
|
34
|
+
const nested = horizontal ? layout.nest(n, [n, 1]) : layout.nest(1, [1, (layout.rowsForHeight(layout.cellW) + 1) * n]);
|
|
35
|
+
let res;
|
|
36
|
+
let idx = -1;
|
|
37
|
+
for (let i = 0; i < n; i++) {
|
|
38
|
+
const v = dial(
|
|
39
|
+
gui,
|
|
40
|
+
nested,
|
|
41
|
+
`${id}-${i}`,
|
|
42
|
+
min,
|
|
43
|
+
max,
|
|
44
|
+
prec,
|
|
45
|
+
vals[i],
|
|
46
|
+
label[i],
|
|
47
|
+
fmt,
|
|
48
|
+
info[i]
|
|
49
|
+
);
|
|
50
|
+
if (v !== void 0) {
|
|
51
|
+
res = v;
|
|
52
|
+
idx = i;
|
|
29
53
|
}
|
|
30
|
-
|
|
54
|
+
}
|
|
55
|
+
return res !== void 0 ? [idx, res] : void 0;
|
|
31
56
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
info && draw && tooltipRaw(gui, info);
|
|
57
|
+
const dialRaw = (gui, id, x, y, w, h, min, max, prec, val, lx, ly, label, fmt, info) => {
|
|
58
|
+
const r = Math.min(w, h) / 2;
|
|
59
|
+
const pos = [x + w / 2, y + h / 2];
|
|
60
|
+
const thetaGap = PI / 2;
|
|
61
|
+
const startTheta = HALF_PI + thetaGap / 2;
|
|
62
|
+
const key = hash([x, y, r]);
|
|
63
|
+
gui.registerID(id, key);
|
|
64
|
+
const bgShape = gui.resource(id, key, () => circle(pos, r, {}));
|
|
65
|
+
const hover = isHoverSlider(gui, id, bgShape, "pointer");
|
|
66
|
+
const draw = gui.draw;
|
|
67
|
+
let v = val;
|
|
68
|
+
let res;
|
|
69
|
+
if (hover) {
|
|
70
|
+
gui.hotID = id;
|
|
71
|
+
if (gui.isMouseDown()) {
|
|
72
|
+
gui.activeID = id;
|
|
73
|
+
res = dialVal(gui.mouse, pos, startTheta, thetaGap, min, max, prec);
|
|
51
74
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
info && draw && tooltipRaw(gui, info);
|
|
76
|
+
}
|
|
77
|
+
const focused = gui.requestFocus(id);
|
|
78
|
+
if (draw) {
|
|
79
|
+
const valShape = gui.resource(
|
|
80
|
+
id,
|
|
81
|
+
v,
|
|
82
|
+
() => line(
|
|
83
|
+
cartesian2(
|
|
84
|
+
null,
|
|
85
|
+
[r, startTheta + (TAU - thetaGap) * norm(v, min, max)],
|
|
86
|
+
pos
|
|
87
|
+
),
|
|
88
|
+
pos,
|
|
89
|
+
{}
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
const valLabel = dialValueLabel(
|
|
93
|
+
gui,
|
|
94
|
+
id,
|
|
95
|
+
key,
|
|
96
|
+
v,
|
|
97
|
+
x + lx,
|
|
98
|
+
y + ly,
|
|
99
|
+
label,
|
|
100
|
+
fmt
|
|
101
|
+
);
|
|
102
|
+
bgShape.attribs.fill = gui.bgColor(hover || focused);
|
|
103
|
+
bgShape.attribs.stroke = gui.focusColor(id);
|
|
104
|
+
valShape.attribs.stroke = gui.fgColor(hover);
|
|
105
|
+
valShape.attribs.weight = 2;
|
|
106
|
+
gui.add(bgShape, valShape, valLabel);
|
|
107
|
+
}
|
|
108
|
+
if (focused && (v = handleSlider1Keys(gui, min, max, prec, v)) !== void 0) {
|
|
109
|
+
return v;
|
|
110
|
+
}
|
|
111
|
+
gui.lastID = id;
|
|
112
|
+
return res;
|
|
113
|
+
};
|
|
114
|
+
export {
|
|
115
|
+
dial,
|
|
116
|
+
dialGroup,
|
|
117
|
+
dialRaw
|
|
68
118
|
};
|
package/components/dropdown.js
CHANGED
|
@@ -5,76 +5,74 @@ import { clamp0 } from "@thi.ng/math/interval";
|
|
|
5
5
|
import { hash } from "@thi.ng/vectors/hash";
|
|
6
6
|
import { Key } from "../api.js";
|
|
7
7
|
import { buttonH } from "./button.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (open) {
|
|
31
|
-
const bt = buttonH(gui, box, `${id}-title`, title);
|
|
32
|
-
draw &&
|
|
33
|
-
gui.add(gui.resource(id, key + 1, () => triangle(gui, tx, ty, true)));
|
|
34
|
-
if (bt) {
|
|
35
|
-
gui.setState(id, false);
|
|
8
|
+
const dropdown = (gui, layout, id, sel, items, title, info) => {
|
|
9
|
+
const open = gui.state(id, () => false);
|
|
10
|
+
const nested = isLayout(layout) ? layout.nest(1, [1, open ? items.length : 1]) : gridLayout(layout.x, layout.y, layout.w, 1, layout.ch, layout.gap);
|
|
11
|
+
let res;
|
|
12
|
+
const box = nested.next();
|
|
13
|
+
const { x, y, w, h } = box;
|
|
14
|
+
const key = hash([x, y, w, h, ~~gui.disabled]);
|
|
15
|
+
const tx = x + w - gui.theme.pad - 4;
|
|
16
|
+
const ty = y + h / 2;
|
|
17
|
+
const draw = gui.draw;
|
|
18
|
+
if (open) {
|
|
19
|
+
const bt = buttonH(gui, box, `${id}-title`, title);
|
|
20
|
+
draw && gui.add(
|
|
21
|
+
gui.resource(id, key + 1, () => triangle(gui, tx, ty, true))
|
|
22
|
+
);
|
|
23
|
+
if (bt) {
|
|
24
|
+
gui.setState(id, false);
|
|
25
|
+
} else {
|
|
26
|
+
for (let i = 0, n = items.length; i < n; i++) {
|
|
27
|
+
if (buttonH(gui, nested, `${id}-${i}`, items[i])) {
|
|
28
|
+
i !== sel && (res = i);
|
|
29
|
+
gui.setState(id, false);
|
|
36
30
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return update(gui, id, Math.min(items.length - 1, sel + 1));
|
|
53
|
-
default:
|
|
54
|
-
}
|
|
55
|
-
}
|
|
31
|
+
}
|
|
32
|
+
if (gui.focusID.startsWith(`${id}-`)) {
|
|
33
|
+
switch (gui.key) {
|
|
34
|
+
case Key.ESC:
|
|
35
|
+
gui.setState(id, false);
|
|
36
|
+
break;
|
|
37
|
+
case Key.UP:
|
|
38
|
+
return update(gui, id, clamp0(sel - 1));
|
|
39
|
+
case Key.DOWN:
|
|
40
|
+
return update(
|
|
41
|
+
gui,
|
|
42
|
+
id,
|
|
43
|
+
Math.min(items.length - 1, sel + 1)
|
|
44
|
+
);
|
|
45
|
+
default:
|
|
56
46
|
}
|
|
47
|
+
}
|
|
57
48
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
draw &&
|
|
63
|
-
gui.add(gui.resource(id, key + 2, () => triangle(gui, tx, ty, false)));
|
|
49
|
+
} else {
|
|
50
|
+
if (buttonH(gui, box, `${id}-${sel}`, items[sel], title, info)) {
|
|
51
|
+
gui.setState(id, true);
|
|
64
52
|
}
|
|
65
|
-
|
|
53
|
+
draw && gui.add(
|
|
54
|
+
gui.resource(id, key + 2, () => triangle(gui, tx, ty, false))
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return res;
|
|
66
58
|
};
|
|
67
59
|
const update = (gui, id, next) => {
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
gui.focusID = `${id}-${next}`;
|
|
61
|
+
return next;
|
|
70
62
|
};
|
|
71
63
|
const triangle = (gui, x, y, open) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
const s = open ? 2 : -2;
|
|
65
|
+
return polygon(
|
|
66
|
+
[
|
|
67
|
+
[x - 4, y + s],
|
|
68
|
+
[x + 4, y + s],
|
|
69
|
+
[x, y - s]
|
|
70
|
+
],
|
|
71
|
+
{
|
|
72
|
+
fill: gui.textColor(false)
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
export {
|
|
77
|
+
dropdown
|
|
80
78
|
};
|
|
@@ -4,35 +4,44 @@ import { hash } from "@thi.ng/vectors/hash";
|
|
|
4
4
|
import { mixHash } from "../hash.js";
|
|
5
5
|
import { buttonRaw } from "./button.js";
|
|
6
6
|
import { textLabelRaw } from "./textlabel.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
7
|
+
const iconButton = (gui, layout, id, icon, iconW, iconH, label, info) => {
|
|
8
|
+
const theme = gui.theme;
|
|
9
|
+
const pad = theme.pad;
|
|
10
|
+
const bodyW = label ? iconW + 3 * pad + gui.textWidth(label) : iconW + 2 * pad;
|
|
11
|
+
const bodyH = iconH + pad;
|
|
12
|
+
const { x, y, w, h } = isLayout(layout) ? layout.next(layout.spanForSize(bodyW, bodyH)) : layout;
|
|
13
|
+
const key = hash([x, y, w, h, ~~gui.disabled]);
|
|
14
|
+
const mkIcon = (hover) => {
|
|
15
|
+
const col = gui.textColor(hover);
|
|
16
|
+
const pos = [x + pad, y + (h - iconH) / 2];
|
|
17
|
+
return [
|
|
18
|
+
"g",
|
|
19
|
+
{
|
|
20
|
+
translate: pos,
|
|
21
|
+
fill: col,
|
|
22
|
+
stroke: col
|
|
23
|
+
},
|
|
24
|
+
icon,
|
|
25
|
+
label ? textLabelRaw(
|
|
26
|
+
[
|
|
27
|
+
iconW + pad,
|
|
28
|
+
-(h - iconH) / 2 + h / 2 + theme.baseLine
|
|
29
|
+
],
|
|
30
|
+
{ fill: col, stroke: "none" },
|
|
31
|
+
label
|
|
32
|
+
) : void 0
|
|
33
|
+
];
|
|
34
|
+
};
|
|
35
|
+
return buttonRaw(
|
|
36
|
+
gui,
|
|
37
|
+
id,
|
|
38
|
+
gui.resource(id, key, () => rect([x, y], [w, h])),
|
|
39
|
+
key,
|
|
40
|
+
gui.resource(id, mixHash(key, `l${label}`), () => mkIcon(false)),
|
|
41
|
+
gui.resource(id, mixHash(key, `lh${label}`), () => mkIcon(true)),
|
|
42
|
+
info
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
export {
|
|
46
|
+
iconButton
|
|
38
47
|
};
|
|
@@ -10,42 +10,48 @@ import { hash } from "@thi.ng/vectors/hash";
|
|
|
10
10
|
import { Key } from "../api.js";
|
|
11
11
|
import { buttonRaw } from "./button.js";
|
|
12
12
|
import { textLabelRaw } from "./textlabel.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
13
|
+
const radialMenu = (gui, id, x, y, r, items, info) => {
|
|
14
|
+
const n = items.length;
|
|
15
|
+
const key = hash([x, y, r, n, ~~gui.disabled]);
|
|
16
|
+
gui.registerID(id, key);
|
|
17
|
+
const cells = gui.resource(id, key, () => [
|
|
18
|
+
...mapIndexed((i, pts) => {
|
|
19
|
+
const cell = polygon(pts);
|
|
20
|
+
const p = add2(
|
|
21
|
+
null,
|
|
22
|
+
[-gui.textWidth(items[i]) >> 1, gui.theme.baseLine],
|
|
23
|
+
centroid(cell)
|
|
24
|
+
);
|
|
25
|
+
return [
|
|
26
|
+
cell,
|
|
27
|
+
hash(p),
|
|
28
|
+
textLabelRaw(p, gui.textColor(false), items[i]),
|
|
29
|
+
textLabelRaw(p, gui.textColor(true), items[i])
|
|
30
|
+
];
|
|
31
|
+
}, triFan(vertices(circle([x, y], r), n)))
|
|
32
|
+
]);
|
|
33
|
+
let res;
|
|
34
|
+
let sel = -1;
|
|
35
|
+
for (let i = 0; i < n; i++) {
|
|
36
|
+
const cell = cells[i];
|
|
37
|
+
const _id = id + i;
|
|
38
|
+
buttonRaw(gui, _id, cell[0], cell[1], cell[2], cell[3], info[i]) && (res = i);
|
|
39
|
+
gui.focusID === _id && (sel = i);
|
|
40
|
+
}
|
|
41
|
+
if (sel !== -1) {
|
|
42
|
+
switch (gui.key) {
|
|
43
|
+
case Key.UP:
|
|
44
|
+
case Key.RIGHT:
|
|
45
|
+
gui.focusID = id + mod(sel + 1, n);
|
|
46
|
+
break;
|
|
47
|
+
case Key.DOWN:
|
|
48
|
+
case Key.LEFT:
|
|
49
|
+
gui.focusID = id + mod(sel - 1, n);
|
|
50
|
+
default:
|
|
37
51
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
break;
|
|
44
|
-
case Key.DOWN:
|
|
45
|
-
case Key.LEFT:
|
|
46
|
-
gui.focusID = id + mod(sel - 1, n);
|
|
47
|
-
default:
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return res;
|
|
52
|
+
}
|
|
53
|
+
return res;
|
|
54
|
+
};
|
|
55
|
+
export {
|
|
56
|
+
radialMenu
|
|
51
57
|
};
|
package/components/radio.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { isLayout } from "@thi.ng/layout/checks";
|
|
2
2
|
import { gridLayout } from "@thi.ng/layout/grid-layout";
|
|
3
3
|
import { toggle } from "./toggle.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
const radio = (gui, layout, id, horizontal, sel, square, labels, info = []) => {
|
|
5
|
+
const n = labels.length;
|
|
6
|
+
const nested = isLayout(layout) ? horizontal ? layout.nest(n, [n, 1]) : layout.nest(1, [1, n]) : horizontal ? gridLayout(layout.x, layout.y, layout.w, n, layout.ch, layout.gap) : gridLayout(layout.x, layout.y, layout.w, 1, layout.ch, layout.gap);
|
|
7
|
+
let res;
|
|
8
|
+
for (let i = 0; i < n; i++) {
|
|
9
|
+
toggle(
|
|
10
|
+
gui,
|
|
11
|
+
nested,
|
|
12
|
+
`${id}-${i}`,
|
|
13
|
+
sel === i,
|
|
14
|
+
square,
|
|
15
|
+
labels[i],
|
|
16
|
+
info[i]
|
|
17
|
+
) !== void 0 && (res = i);
|
|
18
|
+
}
|
|
19
|
+
return res;
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
radio
|
|
18
23
|
};
|