From 4086590631d3711cee8edead48c2e69bec5e65e7 Mon Sep 17 00:00:00 2001 From: Karsten Schmidt Date: Tue, 6 Aug 2019 12:37:12 +0100 Subject: [PATCH] feat(imgui): add/update layout types, handling, add more ctrl key consts --- packages/imgui/src/api.ts | 16 +++++++ packages/imgui/src/components/button.ts | 11 +++-- packages/imgui/src/components/dropdown.ts | 8 ++-- packages/imgui/src/components/radio.ts | 7 +-- packages/imgui/src/components/sliderh.ts | 11 +++-- packages/imgui/src/components/textfield.ts | 5 +- packages/imgui/src/components/toggle.ts | 11 +++-- packages/imgui/src/gui.ts | 7 ++- packages/imgui/src/layout.ts | 56 +++++++++++----------- 9 files changed, 81 insertions(+), 51 deletions(-) diff --git a/packages/imgui/src/api.ts b/packages/imgui/src/api.ts index 2c09bb4f50..95a19bfcbc 100644 --- a/packages/imgui/src/api.ts +++ b/packages/imgui/src/api.ts @@ -59,6 +59,12 @@ export interface LayoutBox { gap: number; } +export interface ILayout { + next(opts?: O): T; +} + +export interface IGridLayout extends ILayout<[number, number], LayoutBox> {} + export const enum MouseButton { LEFT = 1, RIGHT = 2, @@ -76,16 +82,20 @@ export const enum Key { ALT = "Alt", BACKSPACE = "Backspace", CAPSLOCK = "CapsLock", + CONTEXT_MENU = "ContextMenu", CONTROL = "Control", DELETE = "Delete", DOWN = "ArrowDown", END = "End", ENTER = "Enter", ESC = "Escape", + HELP = "Help", HOME = "Home", LEFT = "ArrowLeft", META = "Meta", NUM_LOCK = "NumLock", + PAGE_DOWN = "PageDown", + PAGE_UP = "PageUp", RIGHT = "ArrowRight", SHIFT = "Shift", SPACE = " ", @@ -97,22 +107,28 @@ export const CONTROL_KEYS = new Set([ Key.ALT, Key.BACKSPACE, Key.CAPSLOCK, + Key.CONTEXT_MENU, Key.CONTROL, Key.DELETE, Key.DOWN, Key.END, Key.ENTER, Key.ESC, + Key.HELP, Key.HOME, Key.LEFT, Key.META, Key.NUM_LOCK, + Key.PAGE_DOWN, + Key.PAGE_UP, Key.RIGHT, Key.SHIFT, Key.TAB, Key.UP ]); +export const NONE = "__NONE__"; + export const DEFAULT_THEME: GUITheme = { globalBg: "#333", font: "10px Menlo, monospace", diff --git a/packages/imgui/src/components/button.ts b/packages/imgui/src/components/button.ts index 07eb117580..faccaa1a64 100644 --- a/packages/imgui/src/components/button.ts +++ b/packages/imgui/src/components/button.ts @@ -1,13 +1,18 @@ import { pointInside, rect } from "@thi.ng/geom"; -import { Key, LayoutBox, MouseButton } from "../api"; +import { + IGridLayout, + Key, + LayoutBox, + MouseButton +} from "../api"; import { IMGUI } from "../gui"; -import { GridLayout, isLayout } from "../layout"; +import { isLayout } from "../layout"; import { textLabelRaw } from "./textlabel"; import { tooltipRaw } from "./tooltip"; export const button = ( gui: IMGUI, - layout: GridLayout | LayoutBox, + layout: IGridLayout | LayoutBox, id: string, label?: string, info?: string diff --git a/packages/imgui/src/components/dropdown.ts b/packages/imgui/src/components/dropdown.ts index e141fe509c..0f9a3c6139 100644 --- a/packages/imgui/src/components/dropdown.ts +++ b/packages/imgui/src/components/dropdown.ts @@ -1,19 +1,19 @@ import { polygon } from "@thi.ng/geom"; -import { Key, LayoutBox } from "../api"; +import { IGridLayout, Key, LayoutBox } from "../api"; import { IMGUI } from "../gui"; -import { GridLayout, isLayout } from "../layout"; +import { isLayout } from "../layout"; import { buttonRaw } from "./button"; export const dropdown = ( gui: IMGUI, - layout: GridLayout | LayoutBox, + layout: IGridLayout | LayoutBox, id: string, state: [number, boolean], items: string[], info?: string ) => { const { x, y, w, ch, gap } = isLayout(layout) - ? layout.next(1, state[1] ? items.length : 1) + ? layout.next([1, state[1] ? items.length : 1]) : layout; // prettier-ignore return dropdownRaw(gui, id, x, y, w, ch, gap, state, items, info); diff --git a/packages/imgui/src/components/radio.ts b/packages/imgui/src/components/radio.ts index 673ed433b9..913d77f8a8 100644 --- a/packages/imgui/src/components/radio.ts +++ b/packages/imgui/src/components/radio.ts @@ -1,10 +1,11 @@ +import { IGridLayout } from "../api"; import { IMGUI } from "../gui"; -import { GridLayout, isLayout } from "../layout"; +import { isLayout } from "../layout"; import { toggleRaw } from "./toggle"; export const radioH = ( gui: IMGUI, - layout: GridLayout, + layout: IGridLayout, id: string, val: number[], idx: number, @@ -12,7 +13,7 @@ export const radioH = ( info: string[] = [] ) => { const { x, y, cw, ch, gap } = isLayout(layout) - ? layout.next(labels.length) + ? layout.next([labels.length, 1]) : layout; // prettier-ignore return radioRaw(gui, id, x, y, ch, ch, ch, cw + gap, 0, val, idx, labels, info); diff --git a/packages/imgui/src/components/sliderh.ts b/packages/imgui/src/components/sliderh.ts index 36d9e74efb..1000cf822f 100644 --- a/packages/imgui/src/components/sliderh.ts +++ b/packages/imgui/src/components/sliderh.ts @@ -7,13 +7,14 @@ import { roundTo } from "@thi.ng/math"; import { + IGridLayout, Key, KeyModifier, LayoutBox, MouseButton } from "../api"; import { IMGUI } from "../gui"; -import { GridLayout, isLayout } from "../layout"; +import { isLayout } from "../layout"; import { textLabelRaw } from "./textlabel"; import { tooltipRaw } from "./tooltip"; @@ -22,7 +23,7 @@ const $ = (x: number, prec: number, min: number, max: number) => export const sliderH = ( gui: IMGUI, - layout: GridLayout | LayoutBox, + layout: IGridLayout | LayoutBox, id: string, min: number, max: number, @@ -118,7 +119,7 @@ export const sliderHRaw = ( export const sliderHGroup = ( gui: IMGUI, - layout: GridLayout | LayoutBox, + layout: IGridLayout | LayoutBox, id: string, horizontal: boolean, min: number, @@ -131,8 +132,8 @@ export const sliderHGroup = ( ) => { const { x, y, cw, ch, gap } = isLayout(layout) ? horizontal - ? layout.next(vals.length, 1) - : layout.next(1, vals.length) + ? layout.next([vals.length, 1]) + : layout.next([1, vals.length]) : layout; const [offX, offY] = horizontal ? [cw + gap, 0] : [0, ch + gap]; // prettier-ignore diff --git a/packages/imgui/src/components/textfield.ts b/packages/imgui/src/components/textfield.ts index 65beff57c3..cc5b51d885 100644 --- a/packages/imgui/src/components/textfield.ts +++ b/packages/imgui/src/components/textfield.ts @@ -3,18 +3,19 @@ import { pointInside, rect } from "@thi.ng/geom"; import { fitClamped } from "@thi.ng/math"; import { CONTROL_KEYS, + IGridLayout, Key, LayoutBox, MouseButton } from "../api"; import { IMGUI } from "../gui"; -import { GridLayout, isLayout } from "../layout"; +import { isLayout } from "../layout"; import { textLabelRaw } from "./textlabel"; import { tooltipRaw } from "./tooltip"; export const textField = ( gui: IMGUI, - layout: GridLayout | LayoutBox, + layout: IGridLayout | LayoutBox, id: string, label: [string, number?, number?], filter: Predicate = () => true, diff --git a/packages/imgui/src/components/toggle.ts b/packages/imgui/src/components/toggle.ts index b465bd684b..22baf8cf78 100644 --- a/packages/imgui/src/components/toggle.ts +++ b/packages/imgui/src/components/toggle.ts @@ -1,13 +1,18 @@ import { pointInside, rect } from "@thi.ng/geom"; -import { Key, LayoutBox, MouseButton } from "../api"; +import { + IGridLayout, + Key, + LayoutBox, + MouseButton +} from "../api"; import { IMGUI } from "../gui"; -import { GridLayout, isLayout } from "../layout"; +import { isLayout } from "../layout"; import { textLabelRaw } from "./textlabel"; import { tooltipRaw } from "./tooltip"; export const toggle = ( gui: IMGUI, - layout: GridLayout | LayoutBox, + layout: IGridLayout | LayoutBox, id: string, lx: number, val: boolean[], diff --git a/packages/imgui/src/gui.ts b/packages/imgui/src/gui.ts index 7dfb9b72b7..a3356bfcf8 100644 --- a/packages/imgui/src/gui.ts +++ b/packages/imgui/src/gui.ts @@ -5,11 +5,10 @@ import { GUITheme, IMGUIOpts, KeyModifier, - MouseButton + MouseButton, + NONE } from "./api"; -const NONE = "__NONE__"; - export class IMGUI implements IToHiccup { width: number; height: number; @@ -165,7 +164,7 @@ export class IMGUI implements IToHiccup { } focusColor(id: string) { - return this.focusID === id ? this.theme.focus : "none"; + return this.focusID === id ? this.theme.focus : undefined; } add(...els: any[]) { diff --git a/packages/imgui/src/layout.ts b/packages/imgui/src/layout.ts index ba8e08409c..e392352122 100644 --- a/packages/imgui/src/layout.ts +++ b/packages/imgui/src/layout.ts @@ -1,23 +1,23 @@ -import { LayoutBox } from "./api"; +import { implementsFunction } from "@thi.ng/checks"; +import { IGridLayout, ILayout, LayoutBox } from "./api"; -export class GridLayout { +const DEFAULT_SPANS: [number, number] = [1, 1]; + +export class GridLayout implements IGridLayout { readonly parent: GridLayout | null; readonly cols: number; readonly width: number; readonly x: number; readonly y: number; - readonly colW: number; - readonly rowH: number; + readonly cellW: number; + readonly cellH: number; readonly gap: number; currCol: number; currRow: number; rows: number; - id: string; - constructor( - id: string, parent: GridLayout | null, cols: number, x: number, @@ -26,21 +26,22 @@ export class GridLayout { rowH: number, gap: number ) { - this.id = id; this.parent = parent; this.cols = cols; this.x = x; this.y = y; this.width = width; - this.rowH = rowH; + this.cellW = (width - (cols - 1) * gap) / cols; + this.cellH = rowH; this.gap = gap; - this.colW = (width - (cols - 1) * gap) / cols; this.currCol = 0; this.currRow = 0; this.rows = 0; } - next(cspan = 1, rspan = 1) { + next(spans = DEFAULT_SPANS) { + const cspan = spans[0] || 1; + const rspan = spans[1] || 1; if (this.currCol > 0) { if (this.currCol + cspan > this.cols) { this.currCol = 0; @@ -50,32 +51,33 @@ export class GridLayout { this.currRow = this.rows; } const gap = this.gap; - const h = rspan * this.rowH + (rspan - 1) * gap; + const h = (rspan * this.cellH + (rspan - 1) * gap) | 0; const cell = { - x: this.x + this.currCol * (this.colW + gap), - y: this.y + this.currRow * (this.rowH + gap), - w: cspan * this.colW + (cspan - 1) * gap, + x: (this.x + this.currCol * (this.cellW + gap)) | 0, + y: (this.y + this.currRow * (this.cellH + gap)) | 0, + w: (cspan * this.cellW + (cspan - 1) * gap) | 0, h, - cw: this.colW, - ch: this.rowH, + cw: this.cellW, + ch: this.cellH, gap }; - this.updateMaxRows(rspan); + this.propagateSize(rspan); this.currCol = Math.min(this.currCol + cspan, this.cols) % this.cols; return cell; } - nest(id: string, cols: number, cspan = 1, rspan = 1) { - const { x, y, w } = this.next(cspan, rspan); - return new GridLayout(id, this, cols, x, y, w, this.rowH, this.gap); + nest(cols: number, spans?: [number, number]) { + const { x, y, w } = this.next(spans); + return new GridLayout(this, cols, x, y, w, this.cellH, this.gap); } - protected updateMaxRows(rspan: number) { - this.rows = Math.max(this.rows, this.currRow + rspan); - if (this.parent) { - this.parent.updateMaxRows(this.rows); - } + protected propagateSize(rspan: number) { + let rows = this.rows; + rows = this.rows = Math.max(rows, this.currRow + rspan); + const parent = this.parent; + parent && parent.propagateSize(rows); } } -export const isLayout = (x: any): x is GridLayout => x instanceof GridLayout; +export const isLayout = (x: any): x is ILayout => + implementsFunction(x, "next");