Skip to content

Commit

Permalink
feat(imgui): add cursor & LayoutBox support, add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Aug 15, 2019
1 parent 5a75f3d commit b8d0892
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 65 deletions.
69 changes: 69 additions & 0 deletions packages/imgui/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Predicate } from "@thi.ng/api";
import { ReadonlyVec } from "@thi.ng/vectors";

export type Color = string | number | number[];

Expand All @@ -7,6 +8,7 @@ export type Hash = number | string;
export interface GUITheme {
globalBg?: Color;
font?: string;
fontSize: number;
charWidth: number;
baseLine: number;
pad: number;
Expand Down Expand Up @@ -73,8 +75,74 @@ export interface IGridLayout extends ILayout<[number, number], LayoutBox> {
readonly cellH: number;
readonly gap: number;

/**
* Returns the number of columns for given width.
*
* @param w
*/
colsForWidth(w: number): number;

/**
* Returns the number of rows for given height.
*
* @param w
*/
rowsForHeight(h: number): number;

/**
* Calculates the required number of columns & rows for the given
* size.
*
* @param size
*/
spansForSize(size: ReadonlyVec): [number, number];
spansForSize(w: number, h: number): [number, number];

/**
* Returns a squared `LayoutBox` based on this layout's column
* width. This box will consume `ceil(columnWidth / rowHeight)`
* rows, but the returned box height might be less to satisfy the
* square constraint.
*/
nextSquare(): LayoutBox;

/**
* Requests a `spans` sized cell from this layout (via `.next()`)
* and creates and returns a new child `GridLayout` for the returned
* box / grid cell. This child layout is configured to use `cols`
* columns and shares same `gap` as this (parent) layout. The
* configured row span only acts as initial minimum vertical space
* reseervation, but is allowed to grow and if needed will propagate
* the new space requirements to parent layouts.
*
* Note: this size child-parent size propagation ONLY works until
* the next cell is requested from any parent. IOW, child layouts
* MUST be completed/populated first before continuing with
* siblings/ancestors of this current layout.
*
* ```
* // single column layout (default config)
* const outer = gridLayout(null, 0, 0, 200, 1, 16, 4);
*
* // add button (full 1st row)
* button(gui, outer, "foo",...);
*
* // 2-column nested layout (2nd row)
* const inner = outer.nest(2)
* // these buttons are on same row
* button(gui, inner, "bar",...);
* button(gui, inner, "baz",...);
*
* // continue with outer, create empty row
* outer.next();
*
* // continue with outer (4th row)
* button(gui, outer, "bye",...);
* ```
*
* @param cols columns in nested layout
* @param spans default [1, 1] (i.e. size of single cell)
*/
nest(cols: number, spans?: [number, number]): IGridLayout;
}

Expand Down Expand Up @@ -144,6 +212,7 @@ export const NONE = "__NONE__";

export const DEFAULT_THEME: GUITheme = {
font: "10px Menlo, monospace",
fontSize: 10,
charWidth: 6,
baseLine: 4,
pad: 8,
Expand Down
5 changes: 4 additions & 1 deletion packages/imgui/src/behaviors/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { IMGUI } from "../gui";
export const isHoverButton = (gui: IMGUI, id: string, shape: IShape) => {
const aid = gui.activeID;
const hover = (aid === "" || aid === id) && pointInside(shape, gui.mouse);
hover && (gui.hotID = id);
if (hover) {
gui.setCursor("pointer");
gui.hotID = id;
}
return hover;
};

Expand Down
12 changes: 10 additions & 2 deletions packages/imgui/src/behaviors/slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ import {
import { Key } from "../api";
import { IMGUI } from "../gui";

export const isHoverSlider = (gui: IMGUI, id: string, shape: IShape) => {
export const isHoverSlider = (
gui: IMGUI,
id: string,
shape: IShape,
cursor = "ew-resize"
) => {
const aid = gui.activeID;
const hover = aid === id || (aid === "" && pointInside(shape, gui.mouse));
hover && (gui.hotID = id);
if (hover) {
gui.setCursor(cursor);
gui.hotID = id;
}
return hover;
};

Expand Down
8 changes: 4 additions & 4 deletions packages/imgui/src/components/dial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import {
TAU
} from "@thi.ng/math";
import { cartesian2, hash } from "@thi.ng/vectors";
import { LayoutBox } from "../api";
import { IGridLayout, LayoutBox } from "../api";
import { dialVal } from "../behaviors/dial";
import { handleSlider1Keys, isHoverSlider } from "../behaviors/slider";
import { IMGUI } from "../gui";
import { GridLayout, isLayout } from "../layout";
import { isLayout } from "../layout";
import { textLabelRaw } from "./textlabel";
import { tooltipRaw } from "./tooltip";

export const dial = (
gui: IMGUI,
layout: GridLayout | LayoutBox,
layout: IGridLayout | LayoutBox,
id: string,
min: number,
max: number,
Expand Down Expand Up @@ -71,7 +71,7 @@ export const dialRaw = (
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);
const hover = isHoverSlider(gui, id, bgShape, "pointer");
let v: number | undefined = val;
let res: number | undefined;
if (hover) {
Expand Down
9 changes: 6 additions & 3 deletions packages/imgui/src/components/dropdown.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { polygon } from "@thi.ng/geom";
import { hash } from "@thi.ng/vectors";
import { IGridLayout, Key } from "../api";
import { IGridLayout, Key, LayoutBox } from "../api";
import { IMGUI } from "../gui";
import { gridLayout, isLayout } from "../layout";
import { buttonH } from "./button";

/**
Expand All @@ -16,15 +17,17 @@ import { buttonH } from "./button";
*/
export const dropdown = (
gui: IMGUI,
layout: IGridLayout,
layout: IGridLayout | LayoutBox,
id: string,
sel: number,
items: string[],
title: string,
info?: string
) => {
const open = gui.state<boolean>(id, () => false);
const nested = layout.nest(1, [1, open ? items.length : 1]);
const nested = isLayout(layout)
? layout.nest(1, [1, open ? items.length : 1])
: gridLayout(layout.x, layout.y, layout.w, 1, layout.ch, layout.gap);
let res: number | undefined;
const box = nested.next();
const { x, y, w, h } = box;
Expand Down
6 changes: 3 additions & 3 deletions packages/imgui/src/components/icon-button.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { rect } from "@thi.ng/geom";
import { hash } from "@thi.ng/vectors";
import { LayoutBox } from "../api";
import { IGridLayout, LayoutBox } from "../api";
import { IMGUI } from "../gui";
import { GridLayout, isLayout } from "../layout";
import { isLayout } from "../layout";
import { buttonRaw } from "./button";
import { textLabelRaw } from "./textlabel";

export const iconButton = (
gui: IMGUI,
layout: GridLayout | LayoutBox,
layout: IGridLayout | LayoutBox,
id: string,
icon: any,
iconW: number,
Expand Down
13 changes: 10 additions & 3 deletions packages/imgui/src/components/radio.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { IGridLayout } from "../api";
import { IGridLayout, LayoutBox } from "../api";
import { IMGUI } from "../gui";
import { gridLayout, isLayout } from "../layout";
import { toggle } from "./toggle";

export const radio = (
gui: IMGUI,
layout: IGridLayout,
layout: IGridLayout | LayoutBox,
id: string,
horizontal: boolean,
sel: number,
Expand All @@ -13,7 +14,13 @@ export const radio = (
info: string[] = []
) => {
const n = labels.length;
const nested = horizontal ? layout.nest(n, [n, 1]) : layout.nest(1, [1, n]);
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);
let res: number | undefined;
for (let i = 0; i < n; i++) {
toggle(
Expand Down
17 changes: 13 additions & 4 deletions packages/imgui/src/components/ring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import {
} from "@thi.ng/math";
import { map, normRange } from "@thi.ng/transducers";
import { cartesian2, hash, Vec } from "@thi.ng/vectors";
import { IGridLayout, LayoutBox } from "../api";
import { dialVal } from "../behaviors/dial";
import { handleSlider1Keys } from "../behaviors/slider";
import { IMGUI } from "../gui";
import { GridLayout } from "../layout";
import { isLayout } from "../layout";
import { textLabelRaw } from "./textlabel";
import { tooltipRaw } from "./tooltip";

Expand All @@ -36,7 +37,7 @@ const arcVerts = (

export const ring = (
gui: IMGUI,
layout: GridLayout,
layout: IGridLayout | LayoutBox,
id: string,
min: number,
max: number,
Expand All @@ -48,8 +49,15 @@ export const ring = (
fmt?: Fn<number, string>,
info?: string
) => {
const h = (layout.cellW / 2) * (1 + Math.sin(HALF_PI + thetaGap / 2));
const box = layout.next([1, layout.rowsForHeight(h) + 1]);
let h: number;
let box: LayoutBox;
if (isLayout(layout)) {
h = (layout.cellW / 2) * (1 + Math.sin(HALF_PI + thetaGap / 2));
box = layout.next([1, layout.rowsForHeight(h) + 1]);
} else {
h = (layout.cw / 2) * (1 + Math.sin(HALF_PI + thetaGap / 2));
box = layout;
}
return ringRaw(
gui,
id,
Expand Down Expand Up @@ -102,6 +110,7 @@ export const ringRaw = (
let v: number | undefined = val;
let res: number | undefined;
if (hover) {
gui.setCursor("pointer");
gui.hotID = id;
if (gui.isMouseDown()) {
gui.activeID = id;
Expand Down
2 changes: 1 addition & 1 deletion packages/imgui/src/components/sliderv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const sliderVRaw = (
gui.registerID(id, key);
const box = gui.resource(id, key, () => rect([x, y], [w, h], {}));
const ymax = y + h;
const hover = isHoverSlider(gui, id, box);
const hover = isHoverSlider(gui, id, box, "ns-resize");
let v: number | undefined = val;
let res: number | undefined;
if (hover) {
Expand Down
2 changes: 1 addition & 1 deletion packages/imgui/src/components/textfield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const textFieldRaw = (
const key = hash([x, y, w, h]);
gui.registerID(id, key);
const box = gui.resource(id, key, () => rect([x, y], [w, h], {}));
const hover = isHoverSlider(gui, id, box);
const hover = isHoverSlider(gui, id, box, "text");
if (hover) {
if (gui.isMouseDown()) {
gui.activeID = id;
Expand Down
2 changes: 1 addition & 1 deletion packages/imgui/src/components/xypad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const xyPadRaw = (
gui.registerID(id, key);
const box = gui.resource(id, key, () => rect([x, y], [w, h]));
const col = gui.textColor(false);
const hover = isHoverSlider(gui, id, box);
const hover = isHoverSlider(gui, id, box, "move");
let v: Vec | undefined = val;
let res: Vec | undefined;
if (hover) {
Expand Down
Loading

0 comments on commit b8d0892

Please sign in to comment.