@niivue/uikit 1.0.0-rc.2 → 1.0.0-rc.21
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/README.md +702 -8
- package/dist/button.d.ts +97 -0
- package/dist/buttonOverlay.d.ts +121 -0
- package/dist/colorControl.d.ts +130 -0
- package/dist/colorControlOverlay.d.ts +72 -0
- package/dist/controls.d.ts +151 -0
- package/dist/dialog.d.ts +138 -0
- package/dist/dialogOverlay.d.ts +115 -0
- package/dist/filePicker.d.ts +95 -0
- package/dist/filePickerOverlay.d.ts +75 -0
- package/dist/host.d.ts +87 -0
- package/dist/index.d.ts +61 -0
- package/dist/menu.d.ts +227 -0
- package/dist/menuOverlay.d.ts +149 -0
- package/dist/numberInput.d.ts +172 -0
- package/dist/numberInputOverlay.d.ts +131 -0
- package/dist/pointLabel.d.ts +68 -0
- package/dist/pointLabelOverlay.d.ts +39 -0
- package/dist/rect.d.ts +27 -0
- package/dist/rectOverlay.d.ts +13 -0
- package/dist/render/glRectRenderer.d.ts +13 -0
- package/dist/render/shaders.d.ts +3 -0
- package/dist/render/wgpuRectRenderer.d.ts +18 -0
- package/dist/scroll.d.ts +70 -0
- package/dist/segmented.d.ts +127 -0
- package/dist/segmentedOverlay.d.ts +87 -0
- package/dist/select.d.ts +66 -0
- package/dist/selectOverlay.d.ts +102 -0
- package/dist/slider.d.ts +152 -0
- package/dist/sliderOverlay.d.ts +92 -0
- package/dist/text/layout.d.ts +8 -0
- package/dist/textArea.d.ts +216 -0
- package/dist/textAreaOverlay.d.ts +145 -0
- package/dist/textEdit.d.ts +72 -0
- package/dist/textField.d.ts +33 -0
- package/dist/textInput.d.ts +123 -0
- package/dist/textInputOverlay.d.ts +139 -0
- package/dist/toggle.d.ts +115 -0
- package/dist/toggleOverlay.d.ts +80 -0
- package/dist/uikit.js +6269 -221
- package/package.json +3 -3
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { UIKitKeyEvent } from './controls';
|
|
2
|
+
import { ScrollWindow } from './scroll';
|
|
3
|
+
import { UIKitFontMetrics } from './text/font';
|
|
4
|
+
export interface TextEditState {
|
|
5
|
+
text: string;
|
|
6
|
+
/** Caret position, 0 to `text.length`. */
|
|
7
|
+
caret: number;
|
|
8
|
+
/** The other end of the selection; equals `caret` when nothing is selected. */
|
|
9
|
+
anchor: number;
|
|
10
|
+
}
|
|
11
|
+
/** A state with the caret at `caret` (default: the end) and no selection. */
|
|
12
|
+
export declare function textEditState(text: string, caret?: number, anchor?: number): TextEditState;
|
|
13
|
+
/** The selected span as [start, end) (empty when start equals end). */
|
|
14
|
+
export declare function selectionOf(s: TextEditState): [number, number];
|
|
15
|
+
export declare function hasSelection(s: TextEditState): boolean;
|
|
16
|
+
export declare function selectAll(s: TextEditState): TextEditState;
|
|
17
|
+
/** Move the caret to `index`; with `extend`, the anchor stays and the selection grows. */
|
|
18
|
+
export declare function setCaret(s: TextEditState, index: number, extend?: boolean): TextEditState;
|
|
19
|
+
/** Replace the selection (or insert at the caret) with `str`. */
|
|
20
|
+
export declare function insertText(s: TextEditState, str: string): TextEditState;
|
|
21
|
+
/** Delete the selection, or the character before the caret. */
|
|
22
|
+
export declare function deleteBackward(s: TextEditState): TextEditState;
|
|
23
|
+
/** Delete the selection, or the character after the caret. */
|
|
24
|
+
export declare function deleteForward(s: TextEditState): TextEditState;
|
|
25
|
+
/**
|
|
26
|
+
* The state after an editing key: caret moves (arrows, Home, End, with Shift
|
|
27
|
+
* extending the selection and Meta or Ctrl jumping to the ends), Backspace,
|
|
28
|
+
* Delete, select-all (Meta or Ctrl plus A) and typed characters. A typed
|
|
29
|
+
* character that `accept` rejects leaves the state as it is but still counts
|
|
30
|
+
* as handled, so a number field swallows letters. Returns null for keys that
|
|
31
|
+
* are not edits (Enter, Escape, Tab, ArrowUp, ...), which the widget decides.
|
|
32
|
+
*/
|
|
33
|
+
export declare function editKey(s: TextEditState, e: UIKitKeyEvent, accept?: (ch: string) => boolean): TextEditState | null;
|
|
34
|
+
/** The selected text, empty when nothing is selected. */
|
|
35
|
+
export declare function selectedText(s: TextEditState): string;
|
|
36
|
+
/** The clipboard action a key asks for: Meta or Ctrl plus C, X or V. */
|
|
37
|
+
export declare function clipboardKey(e: UIKitKeyEvent): 'copy' | 'cut' | 'paste' | null;
|
|
38
|
+
/**
|
|
39
|
+
* A usable `maxLength`: a non-negative finite count, else undefined (no
|
|
40
|
+
* limit), so a NaN or negative option neither empties the field nor blocks
|
|
41
|
+
* every key.
|
|
42
|
+
*/
|
|
43
|
+
export declare function maxLengthOf(maxLength: number | undefined): number | undefined;
|
|
44
|
+
/** `text` cut to `maxLength` (see `maxLengthOf`); the same string when it fits. */
|
|
45
|
+
export declare function truncateText(text: string, maxLength: number | undefined): string;
|
|
46
|
+
/**
|
|
47
|
+
* An edit carried across a `maxLength` change: the text cut to the limit with
|
|
48
|
+
* the caret and anchor still inside it. The same state when nothing changes.
|
|
49
|
+
*/
|
|
50
|
+
export declare function truncateEdit(s: TextEditState, maxLength: number | undefined): TextEditState;
|
|
51
|
+
/**
|
|
52
|
+
* Replace the selection with the characters of `str` that `accept` allows,
|
|
53
|
+
* as many as keep the text within `maxLength` (pasting into a full field
|
|
54
|
+
* inserts nothing).
|
|
55
|
+
*/
|
|
56
|
+
export declare function pasteText(s: TextEditState, str: string, accept?: (ch: string) => boolean, maxLength?: number): TextEditState;
|
|
57
|
+
/** The advance of each code unit of `text` at `sizePx` (0 for a glyph the font lacks). */
|
|
58
|
+
export declare function glyphAdvances(metrics: UIKitFontMetrics, text: string, sizePx: number): number[];
|
|
59
|
+
/** Width of the glyphs from index `from` up to (not including) `to`. */
|
|
60
|
+
export declare function advanceBetween(advances: readonly number[], from: number, to: number): number;
|
|
61
|
+
/**
|
|
62
|
+
* The caret index nearest to `px`, measured from the left edge of the glyph
|
|
63
|
+
* at `first`: a point past the middle of a glyph lands after it.
|
|
64
|
+
*/
|
|
65
|
+
export declare function caretIndexAt(advances: readonly number[], first: number, px: number): number;
|
|
66
|
+
/**
|
|
67
|
+
* The run of whole glyphs a field of `viewport` width shows, starting at
|
|
68
|
+
* `first` but scrolled the least distance that keeps the caret in view: the
|
|
69
|
+
* caret's glyph becomes the first shown when the caret lies before the run,
|
|
70
|
+
* and the glyph before the caret becomes the last shown when it lies after.
|
|
71
|
+
*/
|
|
72
|
+
export declare function textWindow(advances: readonly number[], viewport: number, first: number, caret: number): ScrollWindow;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { RectData } from './rect';
|
|
2
|
+
import { ScrollWindow, UIKitBox } from './scroll';
|
|
3
|
+
import { UIKitFontMetrics } from './text/font';
|
|
4
|
+
import { RGBA } from './text/layout';
|
|
5
|
+
import { TextEditState } from './textEdit';
|
|
6
|
+
import { UIKitTextItem } from './textOverlay';
|
|
7
|
+
/** The style knobs the text of a field needs. */
|
|
8
|
+
export interface TextFieldStyle {
|
|
9
|
+
textSizePx: number;
|
|
10
|
+
caretWidth: number;
|
|
11
|
+
caretColor: RGBA;
|
|
12
|
+
/** Color behind selected text. */
|
|
13
|
+
selectionFill: RGBA;
|
|
14
|
+
}
|
|
15
|
+
/** The width a text area has for glyphs, keeping room for the caret. */
|
|
16
|
+
export declare function textFieldViewport(area: UIKitBox, caretWidth: number): number;
|
|
17
|
+
/**
|
|
18
|
+
* The run of glyphs a text area shows, starting at `firstGlyph` but scrolled
|
|
19
|
+
* the least distance that keeps `caret` in view.
|
|
20
|
+
*/
|
|
21
|
+
export declare function textFieldWindow(area: UIKitBox, style: TextFieldStyle, metrics: UIKitFontMetrics, text: string, firstGlyph: number, caret: number): ScrollWindow;
|
|
22
|
+
/** The caret index for a canvas x over text shown from `firstGlyph`. */
|
|
23
|
+
export declare function textFieldCaretAt(area: UIKitBox, style: TextFieldStyle, metrics: UIKitFontMetrics, text: string, firstGlyph: number, px: number): number;
|
|
24
|
+
/**
|
|
25
|
+
* The draw data for a field's text: the selection (behind), the glyphs that
|
|
26
|
+
* fit and, while focused with nothing selected, the caret. `edit` is the
|
|
27
|
+
* edit in progress or null when the field is not focused.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildTextFieldContent(area: UIKitBox, baseline: number, style: TextFieldStyle, metrics: UIKitFontMetrics, text: string, edit: TextEditState | null, firstGlyph: number, color: RGBA, enabled: boolean): {
|
|
30
|
+
rects: RectData[];
|
|
31
|
+
text: UIKitTextItem[];
|
|
32
|
+
shown: ScrollWindow;
|
|
33
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { RectData } from './rect';
|
|
2
|
+
import { ScrollWindow, UIKitBox } from './scroll';
|
|
3
|
+
import { UIKitFontMetrics } from './text/font';
|
|
4
|
+
import { RGBA } from './text/layout';
|
|
5
|
+
import { TextEditState } from './textEdit';
|
|
6
|
+
import { UIKitTextItem } from './textOverlay';
|
|
7
|
+
/** Every visual knob on a text input. All lengths are in canvas pixels. */
|
|
8
|
+
export interface TextInputStyle {
|
|
9
|
+
/** Field color at rest. */
|
|
10
|
+
fill: RGBA;
|
|
11
|
+
/** Field color while the pointer hovers it. */
|
|
12
|
+
hoverFill: RGBA;
|
|
13
|
+
/** Field color when disabled. */
|
|
14
|
+
disabledFill: RGBA;
|
|
15
|
+
border: RGBA;
|
|
16
|
+
/** Border color while the field has keyboard focus. */
|
|
17
|
+
focusBorder: RGBA;
|
|
18
|
+
borderWidth: number;
|
|
19
|
+
radius: number;
|
|
20
|
+
textColor: RGBA;
|
|
21
|
+
disabledTextColor: RGBA;
|
|
22
|
+
/** Color of the placeholder shown while the field is empty. */
|
|
23
|
+
placeholderColor: RGBA;
|
|
24
|
+
/** Label and field text em size. */
|
|
25
|
+
textSizePx: number;
|
|
26
|
+
/** Halo outline width around the label (it sits on the scene); 0 draws none. */
|
|
27
|
+
textOutlineWidth: number;
|
|
28
|
+
/** Halo color; null picks black or white for contrast with `textColor`. */
|
|
29
|
+
textOutlineColor: RGBA | null;
|
|
30
|
+
/** Space between the field's left border and the text. */
|
|
31
|
+
paddingX: number;
|
|
32
|
+
/** Space between the cap-height box and the field's top and bottom edges. */
|
|
33
|
+
paddingY: number;
|
|
34
|
+
/** Space between a label and the field. */
|
|
35
|
+
labelGap: number;
|
|
36
|
+
/** Field width when the spec gives none. */
|
|
37
|
+
fieldWidth: number;
|
|
38
|
+
caretColor: RGBA;
|
|
39
|
+
caretWidth: number;
|
|
40
|
+
/** Color behind selected text. */
|
|
41
|
+
selectionFill: RGBA;
|
|
42
|
+
}
|
|
43
|
+
export declare const DEFAULT_TEXT_INPUT_STYLE: TextInputStyle;
|
|
44
|
+
export interface TextInputSpec {
|
|
45
|
+
/** Stable key: the handle for updates, pointer tracking and the callbacks. */
|
|
46
|
+
id: string;
|
|
47
|
+
/** Optional label, drawn to the left of the field. */
|
|
48
|
+
label?: string;
|
|
49
|
+
/** Top-left corner of the whole control (label included), in canvas pixels. */
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
/** Field width (the label adds to it). Default: the style's `fieldWidth`. */
|
|
53
|
+
width?: number;
|
|
54
|
+
/** Initial text. Default empty. */
|
|
55
|
+
value?: string;
|
|
56
|
+
/** Dimmed text shown while the field is empty. */
|
|
57
|
+
placeholder?: string;
|
|
58
|
+
/** Typing stops at this many characters. */
|
|
59
|
+
maxLength?: number;
|
|
60
|
+
/** Characters the field refuses from the keyboard. Default: accept all. */
|
|
61
|
+
accept?: (ch: string) => boolean;
|
|
62
|
+
/** A disabled input draws dimmed and ignores input. Default true. */
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
/** Per-input overrides of the overlay's default style. */
|
|
65
|
+
style?: Partial<TextInputStyle>;
|
|
66
|
+
/** Fired on every keystroke that changes the text. */
|
|
67
|
+
onInput?: (text: string, id: string) => void;
|
|
68
|
+
/** Fired once when changed text is committed: on Enter and on losing focus. */
|
|
69
|
+
onChange?: (text: string, id: string) => void;
|
|
70
|
+
/** Fired on Enter, changed or not, after `onChange`. */
|
|
71
|
+
onSubmit?: (text: string, id: string) => void;
|
|
72
|
+
}
|
|
73
|
+
/** The control's boxes in canvas pixels. */
|
|
74
|
+
export interface TextInputLayout {
|
|
75
|
+
/** The whole control: label and field. */
|
|
76
|
+
x: number;
|
|
77
|
+
y: number;
|
|
78
|
+
width: number;
|
|
79
|
+
height: number;
|
|
80
|
+
/** The bordered field. */
|
|
81
|
+
field: UIKitBox;
|
|
82
|
+
/** Where the text, caret and selection draw. */
|
|
83
|
+
textArea: UIKitBox;
|
|
84
|
+
/** Baseline of the label and the field text. */
|
|
85
|
+
baseline: number;
|
|
86
|
+
}
|
|
87
|
+
/** The transient state that changes how a text input looks. */
|
|
88
|
+
export interface TextInputVisual {
|
|
89
|
+
/** The text shown: the committed text, or the edit in progress. */
|
|
90
|
+
text: string;
|
|
91
|
+
/** The edit in progress while the input has keyboard focus, else null. */
|
|
92
|
+
edit: TextEditState | null;
|
|
93
|
+
/** Index of the first glyph the text area shows (the overlay keeps it between frames). */
|
|
94
|
+
firstGlyph: number;
|
|
95
|
+
hover: boolean;
|
|
96
|
+
enabled: boolean;
|
|
97
|
+
}
|
|
98
|
+
export declare function resolveTextInputStyle(base: TextInputStyle, override?: Partial<TextInputStyle>): TextInputStyle;
|
|
99
|
+
/** The control's boxes for a spec. */
|
|
100
|
+
export declare function layoutTextInput(spec: TextInputSpec, style: TextInputStyle, metrics: UIKitFontMetrics): TextInputLayout;
|
|
101
|
+
/** Scale every length in a spec and style by `k` (CSS pixels to canvas pixels). */
|
|
102
|
+
export declare function scaleTextInput(spec: TextInputSpec, style: TextInputStyle, k: number): {
|
|
103
|
+
spec: TextInputSpec;
|
|
104
|
+
style: TextInputStyle;
|
|
105
|
+
};
|
|
106
|
+
/** True when canvas point (px, py) lies inside the field (the label is not interactive). */
|
|
107
|
+
export declare function textInputContains(layout: TextInputLayout, px: number, py: number): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* The run of glyphs the text area shows, starting at `firstGlyph` but
|
|
110
|
+
* scrolled the least distance that keeps `caret` in view.
|
|
111
|
+
*/
|
|
112
|
+
export declare function textInputTextWindow(layout: TextInputLayout, style: TextInputStyle, metrics: UIKitFontMetrics, text: string, firstGlyph: number, caret: number): ScrollWindow;
|
|
113
|
+
/** The caret index for a canvas x over text shown from `firstGlyph`. */
|
|
114
|
+
export declare function textInputCaretAt(layout: TextInputLayout, style: TextInputStyle, metrics: UIKitFontMetrics, text: string, firstGlyph: number, px: number): number;
|
|
115
|
+
/**
|
|
116
|
+
* The draw data for one text input in one visual state: the field, the
|
|
117
|
+
* placeholder or the selection, the visible run of text and the caret, and
|
|
118
|
+
* the label.
|
|
119
|
+
*/
|
|
120
|
+
export declare function buildTextInput(spec: TextInputSpec, style: TextInputStyle, layout: TextInputLayout, metrics: UIKitFontMetrics, visual: TextInputVisual): {
|
|
121
|
+
rects: RectData[];
|
|
122
|
+
text: UIKitTextItem[];
|
|
123
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { UIKitOverlayFrame, UIKitOverlayRenderer } from '@niivue/niivue';
|
|
2
|
+
import { UIKitInteractive, UIKitKeyEvent, UIKitRedrawSource } from './controls';
|
|
3
|
+
import { ClipboardBridge } from './host';
|
|
4
|
+
import { UIKitFont } from './text/font';
|
|
5
|
+
import { TextInputLayout, TextInputSpec, TextInputStyle } from './textInput';
|
|
6
|
+
export interface UIKitTextInputOverlayOptions {
|
|
7
|
+
/** Called whenever the overlay needs another frame. A control layer fills it in. */
|
|
8
|
+
requestRedraw?: () => void;
|
|
9
|
+
/** Default style for every input; a spec's `style` overrides per key. */
|
|
10
|
+
style?: Partial<TextInputStyle>;
|
|
11
|
+
/**
|
|
12
|
+
* Interpret spec positions and style lengths as CSS pixels and scale them by
|
|
13
|
+
* the frame's device pixel ratio, instead of canvas pixels.
|
|
14
|
+
*/
|
|
15
|
+
cssUnits?: boolean;
|
|
16
|
+
/** Two presses on a focused field within this many ms select all. Default 400. */
|
|
17
|
+
doubleClickMs?: number;
|
|
18
|
+
/** Clock for the double-click interval. Default `performance.now`. */
|
|
19
|
+
now?: () => number;
|
|
20
|
+
/** Where copy, cut and paste go. Default: the browser's async clipboard. */
|
|
21
|
+
clipboard?: ClipboardBridge;
|
|
22
|
+
}
|
|
23
|
+
export declare class UIKitTextInputOverlay implements UIKitOverlayRenderer, UIKitInteractive {
|
|
24
|
+
private readonly font;
|
|
25
|
+
private readonly rects;
|
|
26
|
+
private readonly labels;
|
|
27
|
+
private readonly entries;
|
|
28
|
+
private baseStyle;
|
|
29
|
+
private readonly cssUnits;
|
|
30
|
+
private readonly doubleClickMs;
|
|
31
|
+
private readonly now;
|
|
32
|
+
private requestRedraw;
|
|
33
|
+
private layer;
|
|
34
|
+
private clipboard;
|
|
35
|
+
private scale;
|
|
36
|
+
private geometryDirty;
|
|
37
|
+
private hoverId;
|
|
38
|
+
private focusedId;
|
|
39
|
+
/** The held press, which drags the selection. */
|
|
40
|
+
private pressId;
|
|
41
|
+
private lastDownId;
|
|
42
|
+
private lastDownAt;
|
|
43
|
+
constructor(font: UIKitFont, options?: UIKitTextInputOverlayOptions);
|
|
44
|
+
/** CSS cursor while a field is hovered: a text beam. */
|
|
45
|
+
get hoverCursor(): string;
|
|
46
|
+
/** Replace the default style for every input (per-spec overrides still win). */
|
|
47
|
+
setDefaultStyle(style: Partial<TextInputStyle>): void;
|
|
48
|
+
/**
|
|
49
|
+
* Add an input, or replace the one with the same id. A replacement takes
|
|
50
|
+
* the new spec's text and ends any edit in progress.
|
|
51
|
+
*/
|
|
52
|
+
addTextInput(spec: TextInputSpec): void;
|
|
53
|
+
/** Replace the whole input set. */
|
|
54
|
+
setTextInputs(specs: readonly TextInputSpec[]): void;
|
|
55
|
+
removeTextInput(id: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Patch one input's spec (label, position, placeholder, enabled, ...). The
|
|
58
|
+
* committed text is kept unless the patch sets `value`.
|
|
59
|
+
*/
|
|
60
|
+
updateTextInput(id: string, patch: Partial<Omit<TextInputSpec, 'id'>>): void;
|
|
61
|
+
setEnabled(id: string, enabled: boolean): void;
|
|
62
|
+
/** Set an input's text from code. Fires no callback; a focused field shows it selected. */
|
|
63
|
+
setValue(id: string, value: string): void;
|
|
64
|
+
/** The committed text. */
|
|
65
|
+
getValue(id: string): string | undefined;
|
|
66
|
+
/** The text the field shows: the edit in progress, or the committed text. */
|
|
67
|
+
getText(id: string): string | undefined;
|
|
68
|
+
/** The ids of all inputs, in draw order. */
|
|
69
|
+
get ids(): string[];
|
|
70
|
+
/** An input's boxes in spec units (canvas pixels, or CSS pixels with `cssUnits`). */
|
|
71
|
+
getLayout(id: string): TextInputLayout | null;
|
|
72
|
+
/** The input with keyboard focus (being edited), if any. */
|
|
73
|
+
get focusedInput(): string | null;
|
|
74
|
+
/**
|
|
75
|
+
* Give an input keyboard focus with its whole text selected, or null to
|
|
76
|
+
* clear. Leaving an input commits its edit. Disabled inputs refuse.
|
|
77
|
+
*/
|
|
78
|
+
focus(id: string | null): void;
|
|
79
|
+
hitTest(x: number, y: number): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Focuses the field and places the caret at the pointer, starting a drag
|
|
82
|
+
* selection; a second press within the double-click interval selects all.
|
|
83
|
+
*/
|
|
84
|
+
pointerDown(x: number, y: number): boolean;
|
|
85
|
+
pointerMove(x: number, y: number): boolean;
|
|
86
|
+
pointerUp(x: number, y: number): boolean;
|
|
87
|
+
pointerCancel(): void;
|
|
88
|
+
/**
|
|
89
|
+
* Editing keys change the text; Enter commits and submits; Escape reverts;
|
|
90
|
+
* Meta or Ctrl plus C, X and V go through the clipboard.
|
|
91
|
+
*/
|
|
92
|
+
/** Enter commits the field and, in a dialog, submits it. */
|
|
93
|
+
submitsForm(e: UIKitKeyEvent): boolean;
|
|
94
|
+
keyDown(e: UIKitKeyEvent): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Paste text into the focused input from code, as a host that catches the
|
|
97
|
+
* DOM paste event itself would. Line breaks are dropped, the spec's
|
|
98
|
+
* `accept` and `maxLength` apply.
|
|
99
|
+
*/
|
|
100
|
+
paste(text: string): boolean;
|
|
101
|
+
/** Commits the edit in progress and drops focus. */
|
|
102
|
+
blur(): void;
|
|
103
|
+
bindLayer(layer: UIKitRedrawSource): void;
|
|
104
|
+
drawOverlay(frame: UIKitOverlayFrame): void;
|
|
105
|
+
/** Release GPU resources on both backends. */
|
|
106
|
+
destroy(): void;
|
|
107
|
+
private invalidate;
|
|
108
|
+
/** Take a new edit state, redrawing and firing `onInput` when the text changed. */
|
|
109
|
+
private applyEdit;
|
|
110
|
+
/**
|
|
111
|
+
* Copy or cut the selection to the clipboard, or start a paste: the read
|
|
112
|
+
* is asynchronous, and its text lands only if the input is still being
|
|
113
|
+
* edited when it arrives.
|
|
114
|
+
*/
|
|
115
|
+
private clipboardAction;
|
|
116
|
+
/** The edit state for a freshly focused (or reverted) input: all selected. */
|
|
117
|
+
private freshEdit;
|
|
118
|
+
/**
|
|
119
|
+
* Commit the edit: changed text becomes the value and fires `onChange`.
|
|
120
|
+
* When the input stays focused the text is selected again.
|
|
121
|
+
*/
|
|
122
|
+
private commit;
|
|
123
|
+
/** The caret for a drag: a pointer left of the shown text pulls one hidden glyph into view. */
|
|
124
|
+
private dragCaretAt;
|
|
125
|
+
private caretAt;
|
|
126
|
+
/**
|
|
127
|
+
* Settle the scroll position for the next frame: the stored first glyph,
|
|
128
|
+
* moved the least distance that keeps the caret in view. Every caret move
|
|
129
|
+
* (key edit, press, drag) settles, so `firstGlyph` is always the window
|
|
130
|
+
* the next frame shows and pointer math can read it as is; `rebuild` settles
|
|
131
|
+
* again for caret moves that bypass this (focus, commit).
|
|
132
|
+
*/
|
|
133
|
+
private firstShown;
|
|
134
|
+
private scaled;
|
|
135
|
+
private layoutOf;
|
|
136
|
+
/** The topmost (last added) enabled input whose field is under a canvas point. */
|
|
137
|
+
private hitEntry;
|
|
138
|
+
private rebuild;
|
|
139
|
+
}
|
package/dist/toggle.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { LineData } from './line';
|
|
2
|
+
import { RectData } from './rect';
|
|
3
|
+
import { UIKitFontMetrics } from './text/font';
|
|
4
|
+
import { RGBA } from './text/layout';
|
|
5
|
+
import { UIKitTextItem } from './textOverlay';
|
|
6
|
+
/** Every visual knob on a toggle. All lengths are in canvas pixels. */
|
|
7
|
+
export interface ToggleStyle {
|
|
8
|
+
/** Side of the square check box. */
|
|
9
|
+
boxSize: number;
|
|
10
|
+
/** Corner radius of the box. */
|
|
11
|
+
radius: number;
|
|
12
|
+
/** Space between the box and the label. */
|
|
13
|
+
gap: number;
|
|
14
|
+
/** Box color when unchecked. */
|
|
15
|
+
fill: RGBA;
|
|
16
|
+
/** Box color when checked. */
|
|
17
|
+
checkedFill: RGBA;
|
|
18
|
+
/** Box color when unchecked and hovered. */
|
|
19
|
+
hoverFill: RGBA;
|
|
20
|
+
/** Box color when checked and hovered. */
|
|
21
|
+
checkedHoverFill: RGBA;
|
|
22
|
+
/** Box color when disabled (checked or not; the mark still shows). */
|
|
23
|
+
disabledFill: RGBA;
|
|
24
|
+
/** Box border color. */
|
|
25
|
+
border: RGBA;
|
|
26
|
+
borderWidth: number;
|
|
27
|
+
/** Check-mark color and stroke width. */
|
|
28
|
+
checkColor: RGBA;
|
|
29
|
+
checkWidth: number;
|
|
30
|
+
textColor: RGBA;
|
|
31
|
+
disabledTextColor: RGBA;
|
|
32
|
+
/** Label em size. */
|
|
33
|
+
textSizePx: number;
|
|
34
|
+
/**
|
|
35
|
+
* Halo outline width around the label, so it reads over any scene; 0 draws
|
|
36
|
+
* none. 1 to 2 reads best with the bundled font.
|
|
37
|
+
*/
|
|
38
|
+
textOutlineWidth: number;
|
|
39
|
+
/** Halo color; null picks black or white for contrast with `textColor`. */
|
|
40
|
+
textOutlineColor: RGBA | null;
|
|
41
|
+
/** Scale of the box while the pointer holds it down. */
|
|
42
|
+
pressScale: number;
|
|
43
|
+
/** Focus ring color (drawn around the box while the toggle has keyboard focus). */
|
|
44
|
+
focusRing: RGBA;
|
|
45
|
+
focusRingWidth: number;
|
|
46
|
+
}
|
|
47
|
+
export declare const DEFAULT_TOGGLE_STYLE: ToggleStyle;
|
|
48
|
+
export interface ToggleSpec {
|
|
49
|
+
/** Stable key: the handle for updates, pointer tracking and the callback. */
|
|
50
|
+
id: string;
|
|
51
|
+
label: string;
|
|
52
|
+
/** Top-left corner of the whole control (box plus label), in canvas pixels. */
|
|
53
|
+
x: number;
|
|
54
|
+
y: number;
|
|
55
|
+
/** Initial state. Default false. */
|
|
56
|
+
checked?: boolean;
|
|
57
|
+
/** Per-toggle overrides of the overlay's default style. */
|
|
58
|
+
style?: Partial<ToggleStyle>;
|
|
59
|
+
/** A disabled toggle draws dimmed and ignores input. Default true. */
|
|
60
|
+
enabled?: boolean;
|
|
61
|
+
/** Fired when the user changes the state (pointer or keyboard), not by `setChecked`. */
|
|
62
|
+
onChange?: (checked: boolean, id: string) => void;
|
|
63
|
+
}
|
|
64
|
+
/** The control's box in canvas pixels, plus the check box inside it. */
|
|
65
|
+
export interface ToggleLayout {
|
|
66
|
+
x: number;
|
|
67
|
+
y: number;
|
|
68
|
+
width: number;
|
|
69
|
+
height: number;
|
|
70
|
+
box: {
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
size: number;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** The transient state that changes how a toggle looks. */
|
|
77
|
+
export interface ToggleVisual {
|
|
78
|
+
checked: boolean;
|
|
79
|
+
hover: boolean;
|
|
80
|
+
pressed: boolean;
|
|
81
|
+
focused: boolean;
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
}
|
|
84
|
+
export declare function resolveToggleStyle(base: ToggleStyle, override?: Partial<ToggleStyle>): ToggleStyle;
|
|
85
|
+
/** The control box for a spec: the check box plus the label beside it. */
|
|
86
|
+
export declare function layoutToggle(spec: ToggleSpec, style: ToggleStyle, metrics: UIKitFontMetrics): ToggleLayout;
|
|
87
|
+
/** Scale every length in a spec and style by `k` (CSS pixels to canvas pixels). */
|
|
88
|
+
export declare function scaleToggle(spec: ToggleSpec, style: ToggleStyle, k: number): {
|
|
89
|
+
spec: ToggleSpec;
|
|
90
|
+
style: ToggleStyle;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Check-mark strokes inside a square box, as line segments. Exported so a
|
|
94
|
+
* checkable menu item can draw the same mark.
|
|
95
|
+
*/
|
|
96
|
+
export declare function buildCheckMark(x: number, y: number, size: number, width: number, color: RGBA): LineData[];
|
|
97
|
+
/** A transparent rect whose border is the focus ring, drawn `inset` outside a box. */
|
|
98
|
+
export declare function buildFocusRing(box: {
|
|
99
|
+
x: number;
|
|
100
|
+
y: number;
|
|
101
|
+
width: number;
|
|
102
|
+
height: number;
|
|
103
|
+
}, radius: number, color: RGBA, width: number, inset?: number): RectData;
|
|
104
|
+
/**
|
|
105
|
+
* The draw data for one toggle in one visual state: the box (shrunk about its
|
|
106
|
+
* centre while pressed), the check mark when checked, a focus ring when
|
|
107
|
+
* focused, and the label vertically centred on the box.
|
|
108
|
+
*/
|
|
109
|
+
export declare function buildToggle(spec: ToggleSpec, style: ToggleStyle, metrics: UIKitFontMetrics, layout: ToggleLayout, visual: ToggleVisual): {
|
|
110
|
+
rects: RectData[];
|
|
111
|
+
lines: LineData[];
|
|
112
|
+
text: UIKitTextItem;
|
|
113
|
+
};
|
|
114
|
+
/** True when canvas point (px, py) lies inside the control box (box plus label). */
|
|
115
|
+
export declare function toggleContains(layout: ToggleLayout, px: number, py: number): boolean;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { UIKitOverlayFrame, UIKitOverlayRenderer } from '@niivue/niivue';
|
|
2
|
+
import { UIKitInteractive, UIKitKeyEvent, UIKitRedrawSource } from './controls';
|
|
3
|
+
import { UIKitFont } from './text/font';
|
|
4
|
+
import { ToggleLayout, ToggleSpec, ToggleStyle } from './toggle';
|
|
5
|
+
export interface UIKitToggleOverlayOptions {
|
|
6
|
+
/** Called whenever the overlay needs another frame. A control layer fills it in. */
|
|
7
|
+
requestRedraw?: () => void;
|
|
8
|
+
/** Default style for every toggle; a spec's `style` overrides per key. */
|
|
9
|
+
style?: Partial<ToggleStyle>;
|
|
10
|
+
/**
|
|
11
|
+
* Interpret spec positions and style lengths as CSS pixels and scale them by
|
|
12
|
+
* the frame's device pixel ratio, instead of canvas pixels.
|
|
13
|
+
*/
|
|
14
|
+
cssUnits?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare class UIKitToggleOverlay implements UIKitOverlayRenderer, UIKitInteractive {
|
|
17
|
+
readonly hoverCursor = "pointer";
|
|
18
|
+
private readonly font;
|
|
19
|
+
private readonly rects;
|
|
20
|
+
private readonly lines;
|
|
21
|
+
private readonly labels;
|
|
22
|
+
private readonly entries;
|
|
23
|
+
private baseStyle;
|
|
24
|
+
private readonly cssUnits;
|
|
25
|
+
private requestRedraw;
|
|
26
|
+
private scale;
|
|
27
|
+
private geometryDirty;
|
|
28
|
+
private pressedId;
|
|
29
|
+
/** While pressed: whether the pointer is currently over the pressed toggle. */
|
|
30
|
+
private pressInside;
|
|
31
|
+
private hoverId;
|
|
32
|
+
private focusedId;
|
|
33
|
+
private layer;
|
|
34
|
+
constructor(font: UIKitFont, options?: UIKitToggleOverlayOptions);
|
|
35
|
+
/** Replace the default style for every toggle (per-spec overrides still win). */
|
|
36
|
+
setDefaultStyle(style: Partial<ToggleStyle>): void;
|
|
37
|
+
/**
|
|
38
|
+
* Add a toggle, or replace the one with the same id. A replacement keeps the
|
|
39
|
+
* current checked state unless the new spec sets `checked`.
|
|
40
|
+
*/
|
|
41
|
+
addToggle(spec: ToggleSpec): void;
|
|
42
|
+
/** Replace the whole toggle set. */
|
|
43
|
+
setToggles(specs: readonly ToggleSpec[]): void;
|
|
44
|
+
removeToggle(id: string): void;
|
|
45
|
+
/** Patch one toggle's spec (label, position, style, enabled, checked, callback). */
|
|
46
|
+
updateToggle(id: string, patch: Partial<Omit<ToggleSpec, 'id'>>): void;
|
|
47
|
+
setEnabled(id: string, enabled: boolean): void;
|
|
48
|
+
/** Set a toggle's state from code. Does not fire `onChange`. */
|
|
49
|
+
setChecked(id: string, checked: boolean): void;
|
|
50
|
+
isChecked(id: string): boolean;
|
|
51
|
+
/** Flip a toggle as the user would (a keyboard shortcut, say): fires `onChange`. */
|
|
52
|
+
toggle(id: string): void;
|
|
53
|
+
/** The ids of all toggles, in draw order. */
|
|
54
|
+
get ids(): string[];
|
|
55
|
+
/** A toggle's box in spec units (canvas pixels, or CSS pixels with `cssUnits`). */
|
|
56
|
+
getLayout(id: string): ToggleLayout | null;
|
|
57
|
+
/** The toggle with keyboard focus, if any. */
|
|
58
|
+
get focusedToggle(): string | null;
|
|
59
|
+
/** Give a toggle keyboard focus (Space/Enter flips it), or null to clear. */
|
|
60
|
+
focus(id: string | null): void;
|
|
61
|
+
hitTest(x: number, y: number): boolean;
|
|
62
|
+
pointerDown(x: number, y: number): boolean;
|
|
63
|
+
pointerMove(x: number, y: number): boolean;
|
|
64
|
+
/** Flips the toggle when released inside the one that was pressed. */
|
|
65
|
+
pointerUp(x: number, y: number): boolean;
|
|
66
|
+
pointerCancel(): void;
|
|
67
|
+
/** Space or Enter flips the focused toggle. */
|
|
68
|
+
keyDown(e: UIKitKeyEvent): boolean;
|
|
69
|
+
blur(): void;
|
|
70
|
+
bindLayer(layer: UIKitRedrawSource): void;
|
|
71
|
+
drawOverlay(frame: UIKitOverlayFrame): void;
|
|
72
|
+
/** Release GPU resources on both backends. */
|
|
73
|
+
destroy(): void;
|
|
74
|
+
private invalidate;
|
|
75
|
+
private scaled;
|
|
76
|
+
private layoutOf;
|
|
77
|
+
/** The topmost (last added) enabled toggle under a canvas point. */
|
|
78
|
+
private hitEntry;
|
|
79
|
+
private rebuild;
|
|
80
|
+
}
|