@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
package/dist/button.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { RectData } from './rect';
|
|
2
|
+
import { UIKitFontMetrics } from './text/font';
|
|
3
|
+
import { RGBA } from './text/layout';
|
|
4
|
+
import { UIKitTextItem } from './textOverlay';
|
|
5
|
+
/** Every visual knob on a button. All lengths are in canvas pixels. */
|
|
6
|
+
export interface ButtonStyle {
|
|
7
|
+
/** Face color at rest. */
|
|
8
|
+
fill: RGBA;
|
|
9
|
+
/** Face color while the pointer hovers (not pressed). */
|
|
10
|
+
hoverFill: RGBA;
|
|
11
|
+
/** Face color at full press; blended in by the press amount. */
|
|
12
|
+
pressedFill: RGBA;
|
|
13
|
+
/** Face color when the button is disabled. */
|
|
14
|
+
disabledFill: RGBA;
|
|
15
|
+
/** Border ring color. */
|
|
16
|
+
border: RGBA;
|
|
17
|
+
/** Border ring width; 0 draws no border. */
|
|
18
|
+
borderWidth: number;
|
|
19
|
+
/** Corner radius. */
|
|
20
|
+
radius: number;
|
|
21
|
+
textColor: RGBA;
|
|
22
|
+
disabledTextColor: RGBA;
|
|
23
|
+
/** Label em size. */
|
|
24
|
+
textSizePx: number;
|
|
25
|
+
/** Space between the label and the left/right edges when auto-sized. */
|
|
26
|
+
paddingX: number;
|
|
27
|
+
/** Space between the cap-height box and the top/bottom edges when auto-sized. */
|
|
28
|
+
paddingY: number;
|
|
29
|
+
/** Scale of the whole button at full press (1 disables the shrink). */
|
|
30
|
+
pressScale: number;
|
|
31
|
+
/** Time for the press amount to go from 0 to 1 on pointer down. */
|
|
32
|
+
pressMs: number;
|
|
33
|
+
/** Time for the press amount to go from 1 back to 0 on release. */
|
|
34
|
+
releaseMs: number;
|
|
35
|
+
}
|
|
36
|
+
export declare const DEFAULT_BUTTON_STYLE: ButtonStyle;
|
|
37
|
+
export interface ButtonSpec {
|
|
38
|
+
/** Stable key: the handle for updates, pointer tracking and the click callback. */
|
|
39
|
+
id: string;
|
|
40
|
+
label: string;
|
|
41
|
+
/** Top-left corner of the resting box, in canvas pixels. */
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
/** Fixed box size. Omit either to size that axis to the label plus padding. */
|
|
45
|
+
width?: number;
|
|
46
|
+
height?: number;
|
|
47
|
+
/** Per-button overrides of the overlay's default style. */
|
|
48
|
+
style?: Partial<ButtonStyle>;
|
|
49
|
+
/** A disabled button draws dimmed and ignores the pointer. Default true. */
|
|
50
|
+
enabled?: boolean;
|
|
51
|
+
/** Fired on a completed click (pointer up inside the button it went down on). */
|
|
52
|
+
onClick?: (id: string) => void;
|
|
53
|
+
}
|
|
54
|
+
/** The button's resting box (before any press shrink), in canvas pixels. */
|
|
55
|
+
export interface ButtonLayout {
|
|
56
|
+
x: number;
|
|
57
|
+
y: number;
|
|
58
|
+
width: number;
|
|
59
|
+
height: number;
|
|
60
|
+
}
|
|
61
|
+
/** The transient state that changes how a button looks. */
|
|
62
|
+
export interface ButtonVisual {
|
|
63
|
+
hover: boolean;
|
|
64
|
+
/** Press amount, 0 (at rest) to 1 (fully pressed). */
|
|
65
|
+
press: number;
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
}
|
|
68
|
+
export declare function resolveButtonStyle(base: ButtonStyle, override?: Partial<ButtonStyle>): ButtonStyle;
|
|
69
|
+
/** Linear blend of two colors, t in [0, 1]. */
|
|
70
|
+
export declare function mixColor(a: RGBA, b: RGBA, t: number): RGBA;
|
|
71
|
+
/** Resting box for a spec: fixed where given, otherwise fitted to the label. */
|
|
72
|
+
export declare function layoutButton(spec: ButtonSpec, style: ButtonStyle, metrics: UIKitFontMetrics): ButtonLayout;
|
|
73
|
+
/**
|
|
74
|
+
* Scale every length in a spec and style by `k` (used to lay out CSS-pixel
|
|
75
|
+
* specs on a high-DPR canvas). Returns new objects; the inputs are untouched.
|
|
76
|
+
*/
|
|
77
|
+
export declare function scaleButton(spec: ButtonSpec, style: ButtonStyle, k: number): {
|
|
78
|
+
spec: ButtonSpec;
|
|
79
|
+
style: ButtonStyle;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* The draw data for one button in one visual state: the face rect and the
|
|
83
|
+
* centred label. A press shrinks both about the box centre by
|
|
84
|
+
* `1 - (1 - pressScale) * press` and blends the face toward `pressedFill`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function buildButton(spec: ButtonSpec, style: ButtonStyle, metrics: UIKitFontMetrics, layout: ButtonLayout, visual: ButtonVisual): {
|
|
87
|
+
rect: RectData;
|
|
88
|
+
text: UIKitTextItem;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Move a press amount toward its target at a constant rate so the full travel
|
|
92
|
+
* takes `durationMs`. A non-positive or non-finite duration (or a non-finite
|
|
93
|
+
* clock) snaps to the target, so the animation always ends.
|
|
94
|
+
*/
|
|
95
|
+
export declare function advancePress(current: number, target: number, dtMs: number, durationMs: number): number;
|
|
96
|
+
/** True when canvas point (px, py) lies inside the resting box. */
|
|
97
|
+
export declare function buttonContains(layout: ButtonLayout, px: number, py: number): boolean;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { UIKitOverlayFrame, UIKitOverlayRenderer } from '@niivue/niivue';
|
|
2
|
+
import { ButtonLayout, ButtonSpec, ButtonStyle } from './button';
|
|
3
|
+
import { UIKitHost, UIKitInteractive, UIKitKeyEvent, UIKitRedrawSource } from './controls';
|
|
4
|
+
import { UIKitFont } from './text/font';
|
|
5
|
+
/** What `attach` needs from a host: NiiVue satisfies it as-is. */
|
|
6
|
+
export type UIKitButtonHost = UIKitHost;
|
|
7
|
+
export interface UIKitButtonOverlayOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Called whenever the overlay needs another frame (a hover/press change, or
|
|
10
|
+
* each step of a running press animation). `attach` fills this in with the
|
|
11
|
+
* host's `drawScene` when it is not set.
|
|
12
|
+
*/
|
|
13
|
+
requestRedraw?: () => void;
|
|
14
|
+
/** Default style for every button; a spec's `style` overrides per key. */
|
|
15
|
+
style?: Partial<ButtonStyle>;
|
|
16
|
+
/**
|
|
17
|
+
* Interpret spec positions, sizes and style lengths as CSS pixels and scale
|
|
18
|
+
* them by the frame's device pixel ratio, instead of canvas pixels. Pointer
|
|
19
|
+
* coordinates always arrive in canvas pixels (the hook's convention).
|
|
20
|
+
*/
|
|
21
|
+
cssUnits?: boolean;
|
|
22
|
+
/** Clock for the press animation (ms). Default `performance.now`. */
|
|
23
|
+
now?: () => number;
|
|
24
|
+
}
|
|
25
|
+
export declare class UIKitButtonOverlay implements UIKitOverlayRenderer, UIKitInteractive {
|
|
26
|
+
readonly hoverCursor = "pointer";
|
|
27
|
+
private readonly font;
|
|
28
|
+
private readonly rects;
|
|
29
|
+
private readonly labels;
|
|
30
|
+
private readonly entries;
|
|
31
|
+
private baseStyle;
|
|
32
|
+
private readonly cssUnits;
|
|
33
|
+
private readonly now;
|
|
34
|
+
private requestRedraw;
|
|
35
|
+
/** Scale from spec units to canvas pixels (the frame dpr when cssUnits). */
|
|
36
|
+
private scale;
|
|
37
|
+
private geometryDirty;
|
|
38
|
+
private lastTick;
|
|
39
|
+
private pressedId;
|
|
40
|
+
private hoverId;
|
|
41
|
+
/** The button that takes Enter/Space, set by the last press on it. */
|
|
42
|
+
private focusedId;
|
|
43
|
+
constructor(font: UIKitFont, options?: UIKitButtonOverlayOptions);
|
|
44
|
+
/** Replace the default style for every button (per-spec overrides still win). */
|
|
45
|
+
setDefaultStyle(style: Partial<ButtonStyle>): void;
|
|
46
|
+
/** Add a button, or replace the one with the same id (keeping its press state). */
|
|
47
|
+
addButton(spec: ButtonSpec): void;
|
|
48
|
+
/** Replace the whole button set. */
|
|
49
|
+
setButtons(specs: readonly ButtonSpec[]): void;
|
|
50
|
+
removeButton(id: string): void;
|
|
51
|
+
/** Patch one button's spec (label, position, style, enabled, callback). */
|
|
52
|
+
updateButton(id: string, patch: Partial<Omit<ButtonSpec, 'id'>>): void;
|
|
53
|
+
setEnabled(id: string, enabled: boolean): void;
|
|
54
|
+
/** The ids of all buttons, in draw order. */
|
|
55
|
+
get ids(): string[];
|
|
56
|
+
/**
|
|
57
|
+
* The resting box of a button in spec units (canvas pixels, or CSS pixels
|
|
58
|
+
* with `cssUnits`), or null for an unknown id. Handy for placing the next
|
|
59
|
+
* button after an auto-sized one.
|
|
60
|
+
*/
|
|
61
|
+
getLayout(id: string): ButtonLayout | null;
|
|
62
|
+
/** True while the pointer is over an enabled button. */
|
|
63
|
+
get hovering(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Click a button from code (a keyboard shortcut, say): plays the press and
|
|
66
|
+
* release animation and fires its callback as a pointer click would.
|
|
67
|
+
*/
|
|
68
|
+
click(id: string): void;
|
|
69
|
+
/** True when canvas point (x, y) is over an enabled button. */
|
|
70
|
+
hitTest(x: number, y: number): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Pointer down at canvas pixel (x, y). Returns true when a button took it
|
|
73
|
+
* (the host should then not treat it as a scene interaction). The button
|
|
74
|
+
* also takes keyboard focus: Enter or Space clicks it until focus moves.
|
|
75
|
+
*/
|
|
76
|
+
pointerDown(x: number, y: number): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Pointer move to canvas pixel (x, y); pass coordinates outside the canvas
|
|
79
|
+
* (or a negative pair) when the pointer leaves. Updates hover, and while a
|
|
80
|
+
* press is held, releases the face when the pointer strays off the button and
|
|
81
|
+
* re-presses when it comes back. Returns true while a press is held.
|
|
82
|
+
*/
|
|
83
|
+
pointerMove(x: number, y: number): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Pointer up at canvas pixel (x, y). Fires the click when it lands inside the
|
|
86
|
+
* button the pointer went down on. Returns true when a press was being held.
|
|
87
|
+
*/
|
|
88
|
+
pointerUp(x: number, y: number): boolean;
|
|
89
|
+
/** Abandon a held press (pointer cancelled or captured away) without a click. */
|
|
90
|
+
pointerCancel(): void;
|
|
91
|
+
/** Enter or Space clicks the last-pressed button. */
|
|
92
|
+
keyDown(e: UIKitKeyEvent): boolean;
|
|
93
|
+
blur(): void;
|
|
94
|
+
bindLayer(layer: UIKitRedrawSource): void;
|
|
95
|
+
/**
|
|
96
|
+
* Wire the host canvas's pointer events to this overlay alone and return a
|
|
97
|
+
* function that unwires them: a one-widget `UIKitControls` layer. To combine
|
|
98
|
+
* buttons with other widgets, add this overlay to a shared layer instead.
|
|
99
|
+
*/
|
|
100
|
+
attach(host: UIKitButtonHost): () => void;
|
|
101
|
+
/** Current press amount of a button (0 at rest, 1 fully pressed). */
|
|
102
|
+
pressOf(id: string): number;
|
|
103
|
+
/**
|
|
104
|
+
* Step the press animation as a frame would, without drawing. Exposed for
|
|
105
|
+
* headless tests of the animation; a host never needs it.
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
108
|
+
tickForTest(): boolean;
|
|
109
|
+
/** Advance every running press animation by the wall clock. */
|
|
110
|
+
private tick;
|
|
111
|
+
drawOverlay(frame: UIKitOverlayFrame): void;
|
|
112
|
+
/** Release GPU resources on both backends. */
|
|
113
|
+
destroy(): void;
|
|
114
|
+
private invalidate;
|
|
115
|
+
private startAnimation;
|
|
116
|
+
private scaled;
|
|
117
|
+
private layoutOf;
|
|
118
|
+
/** The topmost (last added) enabled button under a canvas point. */
|
|
119
|
+
private hitEntry;
|
|
120
|
+
private rebuild;
|
|
121
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { RectData } from './rect';
|
|
2
|
+
import { UIKitBox } from './scroll';
|
|
3
|
+
import { SliderStyle } from './slider';
|
|
4
|
+
import { UIKitFontMetrics } from './text/font';
|
|
5
|
+
import { RGBA } from './text/layout';
|
|
6
|
+
import { UIKitTextItem } from './textOverlay';
|
|
7
|
+
/** Every visual knob on a color control. All lengths are in canvas pixels. */
|
|
8
|
+
export interface ColorControlStyle {
|
|
9
|
+
textColor: RGBA;
|
|
10
|
+
disabledTextColor: RGBA;
|
|
11
|
+
/** Label and channel letter em size. */
|
|
12
|
+
textSizePx: number;
|
|
13
|
+
/** Halo outline width around the label and letters (they sit on the scene); 0 draws none. */
|
|
14
|
+
textOutlineWidth: number;
|
|
15
|
+
/** Halo color; null picks black or white against `textColor`. */
|
|
16
|
+
textOutlineColor: RGBA | null;
|
|
17
|
+
/** Space between the label row and the block below it. */
|
|
18
|
+
labelGap: number;
|
|
19
|
+
/** Side of the square preview swatch. */
|
|
20
|
+
previewSize: number;
|
|
21
|
+
previewRadius: number;
|
|
22
|
+
previewBorder: RGBA;
|
|
23
|
+
previewBorderWidth: number;
|
|
24
|
+
/** Drawn under the preview so a translucent color shows its alpha. */
|
|
25
|
+
previewBackdrop: RGBA;
|
|
26
|
+
/** Space between the preview and the sliders, and between the letters and tracks. */
|
|
27
|
+
gap: number;
|
|
28
|
+
/** Vertical space between channel sliders. */
|
|
29
|
+
channelGap: number;
|
|
30
|
+
/** Style overrides for the channel sliders. */
|
|
31
|
+
slider: Partial<SliderStyle>;
|
|
32
|
+
/** Side of each palette swatch. */
|
|
33
|
+
swatchSize: number;
|
|
34
|
+
swatchGap: number;
|
|
35
|
+
swatchRadius: number;
|
|
36
|
+
swatchBorder: RGBA;
|
|
37
|
+
swatchBorderWidth: number;
|
|
38
|
+
/** Ring around the swatch matching the current color. */
|
|
39
|
+
selectedRing: RGBA;
|
|
40
|
+
selectedRingWidth: number;
|
|
41
|
+
/** Ring around the hovered swatch. */
|
|
42
|
+
hoverRing: RGBA;
|
|
43
|
+
/** Space between the sliders block and the palette row. */
|
|
44
|
+
paletteGap: number;
|
|
45
|
+
}
|
|
46
|
+
export declare const DEFAULT_COLOR_CONTROL_STYLE: ColorControlStyle;
|
|
47
|
+
/** A named palette choice. */
|
|
48
|
+
export interface ColorSwatch {
|
|
49
|
+
name: string;
|
|
50
|
+
color: RGBA;
|
|
51
|
+
}
|
|
52
|
+
export interface ColorControlSpec {
|
|
53
|
+
/** Stable key: the handle for updates, pointer tracking and the callbacks. */
|
|
54
|
+
id: string;
|
|
55
|
+
/** Optional label, drawn above the control at the left. */
|
|
56
|
+
label?: string;
|
|
57
|
+
/** Top-left corner of the whole control, in canvas pixels. */
|
|
58
|
+
x: number;
|
|
59
|
+
y: number;
|
|
60
|
+
/** Width of the whole control. Default 220. */
|
|
61
|
+
width?: number;
|
|
62
|
+
/** Initial color, channels 0 to 1. */
|
|
63
|
+
value: RGBA;
|
|
64
|
+
/** Show an alpha slider. Default false (alpha stays as given). */
|
|
65
|
+
alpha?: boolean;
|
|
66
|
+
/** Named colors drawn as a row of swatches under the sliders. */
|
|
67
|
+
palette?: readonly ColorSwatch[];
|
|
68
|
+
/** A disabled control draws dimmed and ignores input. Default true. */
|
|
69
|
+
enabled?: boolean;
|
|
70
|
+
/** Per-control overrides of the overlay's default style. */
|
|
71
|
+
style?: Partial<ColorControlStyle>;
|
|
72
|
+
/** Fired for every change: each slider move or key press, each swatch press. */
|
|
73
|
+
onInput?: (color: RGBA, id: string) => void;
|
|
74
|
+
/** Fired once per committed change: slider release, key press, swatch press. */
|
|
75
|
+
onChange?: (color: RGBA, id: string) => void;
|
|
76
|
+
}
|
|
77
|
+
export type ColorChannel = 'r' | 'g' | 'b' | 'a';
|
|
78
|
+
export declare const COLOR_CHANNELS: readonly ColorChannel[];
|
|
79
|
+
/** The control's boxes in canvas pixels. */
|
|
80
|
+
export interface ColorControlLayout {
|
|
81
|
+
x: number;
|
|
82
|
+
y: number;
|
|
83
|
+
width: number;
|
|
84
|
+
height: number;
|
|
85
|
+
/** Baseline of the label, or null without one. */
|
|
86
|
+
labelBaseline: number | null;
|
|
87
|
+
preview: UIKitBox;
|
|
88
|
+
/** One row per channel: the letter's position and the slider's box. */
|
|
89
|
+
channels: {
|
|
90
|
+
channel: ColorChannel;
|
|
91
|
+
letterX: number;
|
|
92
|
+
baseline: number;
|
|
93
|
+
slider: UIKitBox;
|
|
94
|
+
}[];
|
|
95
|
+
/** Palette swatches in spec order. */
|
|
96
|
+
swatches: UIKitBox[];
|
|
97
|
+
}
|
|
98
|
+
/** The transient state that changes how a color control looks. */
|
|
99
|
+
export interface ColorControlVisual {
|
|
100
|
+
value: RGBA;
|
|
101
|
+
/** Index of the hovered swatch, or -1. */
|
|
102
|
+
hoverSwatch: number;
|
|
103
|
+
enabled: boolean;
|
|
104
|
+
}
|
|
105
|
+
export declare function resolveColorControlStyle(base: ColorControlStyle, override?: Partial<ColorControlStyle>): ColorControlStyle;
|
|
106
|
+
/** True when two colors agree in every channel within a rounding tolerance. */
|
|
107
|
+
export declare function colorsEqual(a: RGBA, b: RGBA, tolerance?: number): boolean;
|
|
108
|
+
/** A copy of `color` with one channel replaced (clamped to 0..1). */
|
|
109
|
+
export declare function withChannel(color: RGBA, channel: ColorChannel, v: number): RGBA;
|
|
110
|
+
/** The palette index whose color equals `color`, or -1. */
|
|
111
|
+
export declare function selectedSwatch(palette: readonly ColorSwatch[] | undefined, color: RGBA): number;
|
|
112
|
+
export declare function layoutColorControl(spec: ColorControlSpec, style: ColorControlStyle, metrics: UIKitFontMetrics): ColorControlLayout;
|
|
113
|
+
/** Scale every length in a spec and style by `k` (CSS pixels to canvas pixels). */
|
|
114
|
+
export declare function scaleColorControl(spec: ColorControlSpec, style: ColorControlStyle, k: number): {
|
|
115
|
+
spec: ColorControlSpec;
|
|
116
|
+
style: ColorControlStyle;
|
|
117
|
+
};
|
|
118
|
+
/** The index of the swatch under canvas point (px, py), or -1. */
|
|
119
|
+
export declare function colorSwatchAt(layout: ColorControlLayout, px: number, py: number): number;
|
|
120
|
+
/** True when canvas point (px, py) lies inside the whole control. */
|
|
121
|
+
export declare function colorControlContains(layout: ColorControlLayout, px: number, py: number): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* The draw data for a color control: the label, the preview over its
|
|
124
|
+
* backdrop, the channel letters and the palette swatches with the selected
|
|
125
|
+
* and hovered rings. The sliders are a slider overlay's.
|
|
126
|
+
*/
|
|
127
|
+
export declare function buildColorControl(spec: ColorControlSpec, style: ColorControlStyle, layout: ColorControlLayout, visual: ColorControlVisual): {
|
|
128
|
+
rects: RectData[];
|
|
129
|
+
text: UIKitTextItem[];
|
|
130
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { UIKitOverlayFrame, UIKitOverlayRenderer } from '@niivue/niivue';
|
|
2
|
+
import { ColorControlLayout, ColorControlSpec, ColorControlStyle } from './colorControl';
|
|
3
|
+
import { UIKitInteractive, UIKitKeyEvent, UIKitRedrawSource } from './controls';
|
|
4
|
+
import { UIKitFont } from './text/font';
|
|
5
|
+
import { RGBA } from './text/layout';
|
|
6
|
+
export interface UIKitColorControlOverlayOptions {
|
|
7
|
+
/** Called whenever the overlay needs another frame. A control layer fills it in. */
|
|
8
|
+
requestRedraw?: () => void;
|
|
9
|
+
/** Default style for every control; a spec's `style` overrides per key. */
|
|
10
|
+
style?: Partial<ColorControlStyle>;
|
|
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
|
+
}
|
|
17
|
+
export declare class UIKitColorControlOverlay implements UIKitOverlayRenderer, UIKitInteractive {
|
|
18
|
+
readonly hoverCursor = "pointer";
|
|
19
|
+
private readonly font;
|
|
20
|
+
private readonly rects;
|
|
21
|
+
private readonly labels;
|
|
22
|
+
/** The channel sliders of every control, placed in spec units. */
|
|
23
|
+
private readonly sliders;
|
|
24
|
+
private readonly entries;
|
|
25
|
+
private baseStyle;
|
|
26
|
+
private readonly cssUnits;
|
|
27
|
+
private requestRedraw;
|
|
28
|
+
private scale;
|
|
29
|
+
private geometryDirty;
|
|
30
|
+
/** The control whose slider holds the pointer. */
|
|
31
|
+
private draggingId;
|
|
32
|
+
private focusedId;
|
|
33
|
+
constructor(font: UIKitFont, options?: UIKitColorControlOverlayOptions);
|
|
34
|
+
/** Replace the default style for every control (per-spec overrides still win). */
|
|
35
|
+
setDefaultStyle(style: Partial<ColorControlStyle>): void;
|
|
36
|
+
/** Add a control, or replace the one with the same id (keeping its color). */
|
|
37
|
+
addColorControl(spec: ColorControlSpec): void;
|
|
38
|
+
setColorControls(specs: readonly ColorControlSpec[]): void;
|
|
39
|
+
removeColorControl(id: string): void;
|
|
40
|
+
/** Patch one control's spec (label, position, palette, callbacks, ...). */
|
|
41
|
+
updateColorControl(id: string, patch: Partial<Omit<ColorControlSpec, 'id'>>): void;
|
|
42
|
+
setEnabled(id: string, enabled: boolean): void;
|
|
43
|
+
/** Set a control's color without firing its callbacks. */
|
|
44
|
+
setValue(id: string, color: RGBA): void;
|
|
45
|
+
getValue(id: string): RGBA | undefined;
|
|
46
|
+
get ids(): string[];
|
|
47
|
+
/** A control's boxes in spec units, or null for an unknown id. */
|
|
48
|
+
getLayout(id: string): ColorControlLayout | null;
|
|
49
|
+
/** The control whose slider has keyboard focus, if any. */
|
|
50
|
+
get focusedControl(): string | null;
|
|
51
|
+
hitTest(x: number, y: number): boolean;
|
|
52
|
+
pointerDown(x: number, y: number): boolean;
|
|
53
|
+
pointerMove(x: number, y: number): boolean;
|
|
54
|
+
pointerUp(x: number, y: number): boolean;
|
|
55
|
+
pointerCancel(): void;
|
|
56
|
+
/** Arrow keys, Home, End and Page keys move the focused channel slider. */
|
|
57
|
+
keyDown(e: UIKitKeyEvent): boolean;
|
|
58
|
+
blur(): void;
|
|
59
|
+
bindLayer(layer: UIKitRedrawSource): void;
|
|
60
|
+
drawOverlay(frame: UIKitOverlayFrame): void;
|
|
61
|
+
/** Release GPU resources on both backends. */
|
|
62
|
+
destroy(): void;
|
|
63
|
+
private invalidate;
|
|
64
|
+
private rebuild;
|
|
65
|
+
private getValueOf;
|
|
66
|
+
private layoutOf;
|
|
67
|
+
private swatchHit;
|
|
68
|
+
/** Create or move the channel sliders of a control, in spec units. */
|
|
69
|
+
private placeSliders;
|
|
70
|
+
/** Push a control's color into its sliders without firing callbacks. */
|
|
71
|
+
private syncSliders;
|
|
72
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { UIKitOverlayFrame, UIKitOverlayRenderer } from '@niivue/niivue';
|
|
2
|
+
/** What `attach` needs from a host: a NiiVue controller satisfies it as-is. */
|
|
3
|
+
export interface UIKitHost {
|
|
4
|
+
canvas: HTMLCanvasElement | null;
|
|
5
|
+
/** Client (CSS) coordinates to canvas backing-store pixels, or null if outside. */
|
|
6
|
+
clientToCanvas(clientX: number, clientY: number): [number, number] | null;
|
|
7
|
+
/** Schedule a redraw of the scene (and so of the overlays). */
|
|
8
|
+
drawScene(): void;
|
|
9
|
+
}
|
|
10
|
+
/** The parts of a keyboard event a widget reacts to. */
|
|
11
|
+
export interface UIKitKeyEvent {
|
|
12
|
+
/** The DOM `KeyboardEvent.key` value ('ArrowLeft', 'Enter', ' ', 'a', ...). */
|
|
13
|
+
key: string;
|
|
14
|
+
shiftKey: boolean;
|
|
15
|
+
ctrlKey: boolean;
|
|
16
|
+
altKey: boolean;
|
|
17
|
+
metaKey: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Something the control layer can bind a widget to: redraw requests, and a
|
|
21
|
+
* way to take the keyboard back when the widget opens or focuses from code.
|
|
22
|
+
*/
|
|
23
|
+
export interface UIKitRedrawSource {
|
|
24
|
+
requestRedraw(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Make `child` the keyboard focus and let the layer route keys again, as a
|
|
27
|
+
* canvas press would. Widgets call it from their own `open` and `focus`
|
|
28
|
+
* so a dialog opened from a page button gets its keys at once.
|
|
29
|
+
*/
|
|
30
|
+
focus?(child: UIKitInteractive): void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* An interactive overlay: drawn through the overlay hook and fed pointer and
|
|
34
|
+
* keyboard input in canvas pixels. Every pointer method returns whether the
|
|
35
|
+
* widget consumed the event (the host must then not treat it as a scene
|
|
36
|
+
* interaction).
|
|
37
|
+
*/
|
|
38
|
+
export interface UIKitInteractive extends UIKitOverlayRenderer {
|
|
39
|
+
/** True when (x, y) is over an interactive part (hover routing, cursor). */
|
|
40
|
+
hitTest(x: number, y: number): boolean;
|
|
41
|
+
pointerDown(x: number, y: number): boolean;
|
|
42
|
+
/** Pass a point off the canvas (e.g. -1, -1) when the pointer leaves. */
|
|
43
|
+
pointerMove(x: number, y: number): boolean;
|
|
44
|
+
pointerUp(x: number, y: number): boolean;
|
|
45
|
+
/** Abandon a held interaction without completing it. */
|
|
46
|
+
pointerCancel(): void;
|
|
47
|
+
/** Handle a key while focused or modal. Return true to consume it. */
|
|
48
|
+
keyDown?(e: UIKitKeyEvent): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Whether a key this widget took should also submit the dialog it sits
|
|
51
|
+
* in, as Enter in a form's text field submits the form. A text area's
|
|
52
|
+
* Enter (a new line) and a select's (opening its list) do not.
|
|
53
|
+
*/
|
|
54
|
+
submitsForm?(e: UIKitKeyEvent): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Handle a wheel turn over (x, y), deltas in pixels (positive is down and
|
|
57
|
+
* right). Sent to the modal widget first, else the widget under the point.
|
|
58
|
+
* Return true to consume it (the page then does not scroll).
|
|
59
|
+
*/
|
|
60
|
+
wheel?(x: number, y: number, deltaX: number, deltaY: number): boolean;
|
|
61
|
+
/** Drop keyboard focus (another widget took it, or the scene was clicked). */
|
|
62
|
+
blur?(): void;
|
|
63
|
+
/** True while this widget must see every event first (an open popup). */
|
|
64
|
+
isModal?(): boolean;
|
|
65
|
+
/** Close any popup without choosing (the pointer or focus went elsewhere in the page). */
|
|
66
|
+
dismiss?(): void;
|
|
67
|
+
/** CSS cursor to show while this widget is hovered or captured. */
|
|
68
|
+
readonly hoverCursor?: string;
|
|
69
|
+
/** Draw anything that must sit above every other widget (a popup). */
|
|
70
|
+
drawPopup?(frame: UIKitOverlayFrame): void;
|
|
71
|
+
/**
|
|
72
|
+
* Called when added to a control layer: a widget without its own redraw
|
|
73
|
+
* requester should take the layer's, and may stop drawing its popup from
|
|
74
|
+
* `drawOverlay` because the layer calls `drawPopup` after all widgets.
|
|
75
|
+
*/
|
|
76
|
+
bindLayer?(layer: UIKitRedrawSource): void;
|
|
77
|
+
destroy?(): void;
|
|
78
|
+
}
|
|
79
|
+
export interface UIKitControlsOptions {
|
|
80
|
+
/** Called whenever a widget needs a frame. `attach` defaults it to `drawScene`. */
|
|
81
|
+
requestRedraw?: () => void;
|
|
82
|
+
}
|
|
83
|
+
export declare class UIKitControls implements UIKitOverlayRenderer, UIKitRedrawSource {
|
|
84
|
+
private readonly children;
|
|
85
|
+
private redraw;
|
|
86
|
+
private captured;
|
|
87
|
+
private focused;
|
|
88
|
+
private hovered;
|
|
89
|
+
private active;
|
|
90
|
+
constructor(options?: UIKitControlsOptions);
|
|
91
|
+
/** Add a widget on top of the existing ones. Returns the layer for chaining. */
|
|
92
|
+
add(child: UIKitInteractive): this;
|
|
93
|
+
remove(child: UIKitInteractive): void;
|
|
94
|
+
/** The widgets in draw order (bottom first). */
|
|
95
|
+
get widgets(): readonly UIKitInteractive[];
|
|
96
|
+
/** The widget holding keyboard focus, if any. */
|
|
97
|
+
get focusedWidget(): UIKitInteractive | null;
|
|
98
|
+
/** The widget that currently owns the pointer (a held press or drag). */
|
|
99
|
+
get capturedWidget(): UIKitInteractive | null;
|
|
100
|
+
/** The widget that must see every event first, if any. */
|
|
101
|
+
get modalWidget(): UIKitInteractive | null;
|
|
102
|
+
/** CSS cursor for the current hover or capture, or null for the default. */
|
|
103
|
+
get hoverCursor(): string | null;
|
|
104
|
+
requestRedraw(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Give keyboard focus to a widget (blurring the previous one). With a
|
|
107
|
+
* widget this also ends a `deactivate`: the layer routes keys again.
|
|
108
|
+
*/
|
|
109
|
+
focus(child: UIKitInteractive | null): void;
|
|
110
|
+
blur(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Drop every live interaction: cancel a held press, dismiss open popups and
|
|
113
|
+
* blur the focus. `attach` calls this when the user presses outside the
|
|
114
|
+
* canvas, moves focus to another element or leaves the window, so the
|
|
115
|
+
* layer stops consuming keys the page should get.
|
|
116
|
+
*/
|
|
117
|
+
deactivate(): void;
|
|
118
|
+
/** Pointer down at canvas pixel (x, y): true when a widget took it. */
|
|
119
|
+
pointerDown(x: number, y: number): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Pointer move to canvas pixel (x, y), or off-canvas coordinates when the
|
|
122
|
+
* pointer leaves. True while a widget owns the pointer or a modal is up.
|
|
123
|
+
*/
|
|
124
|
+
pointerMove(x: number, y: number): boolean;
|
|
125
|
+
/** Pointer up at canvas pixel (x, y): true when a widget owned the pointer. */
|
|
126
|
+
pointerUp(x: number, y: number): boolean;
|
|
127
|
+
pointerCancel(): void;
|
|
128
|
+
/**
|
|
129
|
+
* Route a key to the modal widget, else the focused one. True if consumed.
|
|
130
|
+
* The layer owns keys until `deactivate`, and again from the next canvas
|
|
131
|
+
* press or `focus` call; an open dialog does not keep them once the user
|
|
132
|
+
* has moved to the rest of the page.
|
|
133
|
+
*/
|
|
134
|
+
keyDown(e: UIKitKeyEvent): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Route a wheel turn to the modal widget, else to the topmost widget under
|
|
137
|
+
* (x, y) that handles wheels. True if consumed.
|
|
138
|
+
*/
|
|
139
|
+
wheel(x: number, y: number, deltaX: number, deltaY: number): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Wire the host canvas's pointer and wheel events (and the window's keydown)
|
|
142
|
+
* to this layer and return a function that unwires them. Events a widget consumes are
|
|
143
|
+
* stopped before the host's own handlers run; the cursor follows the widgets.
|
|
144
|
+
*/
|
|
145
|
+
attach(host: UIKitHost): () => void;
|
|
146
|
+
/** Draw every widget in order, then every popup on top. */
|
|
147
|
+
drawOverlay(frame: UIKitOverlayFrame): void;
|
|
148
|
+
/** Release every widget's GPU resources. */
|
|
149
|
+
destroy(): void;
|
|
150
|
+
private topDown;
|
|
151
|
+
}
|