react-x11 1.2.0 → 2.0.0
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 +330 -81
- package/package.json +132 -14
- package/src/ClickToComponent.js +9 -2
- package/src/DevToolsIntegration.js +440 -32
- package/src/Reconciler.js +463 -165
- package/src/a11y.js +1299 -0
- package/src/acceleratorhooks.js +86 -0
- package/src/accelerators.js +128 -0
- package/src/activate.js +168 -0
- package/src/anchor.js +360 -0
- package/src/appcontext.js +161 -0
- package/src/appearance.js +748 -0
- package/src/appearancehooks.js +96 -0
- package/src/apphooks.js +73 -0
- package/src/application.js +748 -0
- package/src/atspi.js +1953 -0
- package/src/bus.js +545 -0
- package/src/bushooks.js +136 -0
- package/src/clientmessage.js +140 -0
- package/src/clipboard.js +121 -0
- package/src/components/Button.js +148 -33
- package/src/components/Checkbox.js +66 -32
- package/src/components/Dialog.js +96 -29
- package/src/components/FileDialog.js +491 -0
- package/src/components/Icon.js +329 -0
- package/src/components/Menu.js +797 -133
- package/src/components/PasswordInput.js +434 -0
- package/src/components/ProgressBar.js +91 -12
- package/src/components/Radio.js +78 -35
- package/src/components/Select.js +255 -65
- package/src/components/Slider.js +102 -41
- package/src/components/SplitPane.js +206 -0
- package/src/components/Switch.js +100 -22
- package/src/components/Table.js +529 -0
- package/src/components/Tabs.js +211 -0
- package/src/components/Tooltip.js +528 -54
- package/src/components/anchor.js +154 -107
- package/src/components/change.js +34 -0
- package/src/components/dnd.js +103 -0
- package/src/components/index.js +16 -3
- package/src/components/keys.js +17 -20
- package/src/components/scribble.js +247 -0
- package/src/components/theme.js +350 -42
- package/src/components/typeahead.js +12 -2
- package/src/compose.js +868 -0
- package/src/compositing.js +224 -0
- package/src/dbusmenu.js +384 -0
- package/src/debug.d.ts +63 -0
- package/src/debug.js +642 -0
- package/src/decorations.js +486 -0
- package/src/desktopsettings.js +209 -0
- package/src/desktopsettingshooks.js +62 -0
- package/src/dnd.js +1725 -0
- package/src/editmenu.js +272 -0
- package/src/errors.js +98 -0
- package/src/events.js +1406 -162
- package/src/extensions.js +45 -0
- package/src/filedialog.js +375 -0
- package/src/filedialoghooks.js +132 -0
- package/src/fonthooks.js +64 -0
- package/src/fonts.js +301 -0
- package/src/foreignnodes.js +519 -0
- package/src/frame/child.js +33 -0
- package/src/frame/childmain.js +274 -0
- package/src/frame/env.js +140 -0
- package/src/frame/index.js +444 -0
- package/src/frame/lifecycle.js +67 -0
- package/src/frame/protocol.js +179 -0
- package/src/frames.js +73 -0
- package/src/glbackend.js +114 -0
- package/src/glnodes.js +125 -67
- package/src/globalmenu.js +683 -0
- package/src/host.d.ts +88 -0
- package/src/host.js +42 -0
- package/src/idle.js +490 -0
- package/src/idlehooks.js +100 -0
- package/src/imagesource.js +349 -0
- package/src/index.d.ts +475 -0
- package/src/index.js +83 -9
- package/src/inputtime.js +206 -0
- package/src/jsx-dev-runtime.d.ts +21 -0
- package/src/jsx-dev-runtime.js +2 -0
- package/src/jsx-runtime.d.ts +39 -0
- package/src/jsx-runtime.js +6 -0
- package/src/keyboard.js +256 -0
- package/src/keyboardstate.js +278 -0
- package/src/keyboardstatehooks.js +58 -0
- package/src/keysyms.d.ts +149 -0
- package/src/keysyms.js +270 -0
- package/src/locale.js +170 -0
- package/src/localehooks.js +47 -0
- package/src/menuitem.js +223 -0
- package/src/node.d.ts +597 -0
- package/src/node.js +44 -0
- package/src/nodes.js +9546 -690
- package/src/ntk.d.ts +44 -0
- package/src/ntk.js +25 -0
- package/src/paintcache.js +366 -0
- package/src/palette.js +380 -0
- package/src/pastestate.js +66 -0
- package/src/portal.js +461 -0
- package/src/priority.js +26 -0
- package/src/refresh/index.d.ts +40 -0
- package/src/refresh/index.js +122 -0
- package/src/refresh/loader.d.ts +37 -0
- package/src/refresh/loader.js +401 -0
- package/src/refresh/register.d.ts +5 -0
- package/src/refresh/register.js +13 -0
- package/src/registry.js +232 -0
- package/src/scale.js +626 -0
- package/src/scalehooks.js +27 -0
- package/src/screencolor.js +640 -0
- package/src/screencolorhooks.js +101 -0
- package/src/screens.js +754 -0
- package/src/screenshooks.js +137 -0
- package/src/startup.js +302 -0
- package/src/style.d.ts +126 -0
- package/src/style.js +33 -0
- package/src/styles.js +1482 -6
- package/src/svgnodes.js +306 -0
- package/src/testing/a11y.js +484 -0
- package/src/testing/components.js +414 -0
- package/src/testing/events.js +407 -0
- package/src/testing/harness.js +455 -0
- package/src/testing/index.d.ts +558 -0
- package/src/testing/index.js +85 -0
- package/src/testing/mock-app.js +463 -0
- package/src/testing/pixels.js +152 -0
- package/src/testing/queries.js +224 -0
- package/src/textrange.js +83 -0
- package/src/textselection.js +439 -0
- package/src/trace-registry.js +63 -0
- package/src/transfer.js +93 -0
- package/src/types/appearance.d.ts +84 -0
- package/src/types/application.d.ts +173 -0
- package/src/types/components.d.ts +820 -0
- package/src/types/dbus.d.ts +177 -0
- package/src/types/elements.d.ts +897 -0
- package/src/types/events.d.ts +549 -0
- package/src/types/filedialog.d.ts +260 -0
- package/src/types/fonts.d.ts +124 -0
- package/src/types/frame.d.ts +146 -0
- package/src/types/globalmenu.d.ts +42 -0
- package/src/types/nodes.d.ts +199 -0
- package/src/types/screencolor.d.ts +84 -0
- package/src/types/style.d.ts +434 -0
- package/src/types/system.d.ts +287 -0
- package/src/windowid.js +151 -0
- package/src/windowstate.js +393 -0
- package/src/xsettings.js +336 -0
- package/src/yoga.d.ts +55 -0
- package/src/yoga.js +135 -0
- package/src/components/Canvas3D.js +0 -28
- package/src/geometry3d.js +0 -223
- package/src/pointer3d.js +0 -158
- package/src/raycast3d.js +0 -146
- package/src/richnodes.js +0 -436
- package/src/scene3d.js +0 -683
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host elements. Only `<window>`, `<popup>`, `<glarea>` and `<foreign>`
|
|
3
|
+
* are real X11 windows; everything else is a retained node laid out by yoga
|
|
4
|
+
* and painted into the owning window. See docs/elements.md.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Ref, RefObject, ReactNode, Key } from 'react';
|
|
8
|
+
import type { Color, Cursor, StyleProp } from './style.js';
|
|
9
|
+
import type {
|
|
10
|
+
DrawnNode,
|
|
11
|
+
NtkWindow,
|
|
12
|
+
ScrollableNode,
|
|
13
|
+
TextInputNode,
|
|
14
|
+
} from './nodes.js';
|
|
15
|
+
import type { AnchorOptions } from './components.js';
|
|
16
|
+
import type {
|
|
17
|
+
ChangeEvent,
|
|
18
|
+
SelectionChangeEvent,
|
|
19
|
+
ClientMessageEvent,
|
|
20
|
+
EventHandlers,
|
|
21
|
+
MouseEvent,
|
|
22
|
+
ScrollEvent,
|
|
23
|
+
SubmitEvent,
|
|
24
|
+
SyntheticEvent,
|
|
25
|
+
ViewportEvent,
|
|
26
|
+
WindowResizeEvent,
|
|
27
|
+
} from './events.js';
|
|
28
|
+
|
|
29
|
+
/** Props every element takes. */
|
|
30
|
+
export interface CommonProps {
|
|
31
|
+
key?: Key;
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
style?: StyleProp;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Focus and interaction, on drawn elements and windows alike. */
|
|
37
|
+
export interface InteractionProps {
|
|
38
|
+
focusable?: boolean;
|
|
39
|
+
/** Sequential focus order; `-1` is focusable but not tabbable. */
|
|
40
|
+
tabIndex?: number;
|
|
41
|
+
autoFocus?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Own a focus scope: Tab and presses stay inside, and focus is restored
|
|
44
|
+
* when it unmounts. This is what makes a modal.
|
|
45
|
+
*/
|
|
46
|
+
trapFocus?: boolean;
|
|
47
|
+
/** Never focusable, and the trigger for a `:disabled` style block. Also
|
|
48
|
+
* clears the AT-SPI ENABLED/SENSITIVE states. */
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The ARIA role vocabulary the AT-SPI bridge maps (src/a11y.js). An
|
|
54
|
+
* unknown string falls back to the element default and warns in DEV; the
|
|
55
|
+
* open union keeps forward compatibility.
|
|
56
|
+
*/
|
|
57
|
+
export type A11yRole =
|
|
58
|
+
| 'alert'
|
|
59
|
+
| 'alertdialog'
|
|
60
|
+
| 'article'
|
|
61
|
+
| 'banner'
|
|
62
|
+
| 'blockquote'
|
|
63
|
+
| 'button'
|
|
64
|
+
| 'caption'
|
|
65
|
+
| 'cell'
|
|
66
|
+
| 'checkbox'
|
|
67
|
+
| 'columnheader'
|
|
68
|
+
| 'combobox'
|
|
69
|
+
| 'comment'
|
|
70
|
+
| 'complementary'
|
|
71
|
+
| 'contentinfo'
|
|
72
|
+
| 'dialog'
|
|
73
|
+
| 'document'
|
|
74
|
+
| 'form'
|
|
75
|
+
| 'grid'
|
|
76
|
+
| 'gridcell'
|
|
77
|
+
| 'group'
|
|
78
|
+
| 'heading'
|
|
79
|
+
| 'img'
|
|
80
|
+
| 'link'
|
|
81
|
+
| 'list'
|
|
82
|
+
| 'listbox'
|
|
83
|
+
| 'listitem'
|
|
84
|
+
| 'log'
|
|
85
|
+
| 'main'
|
|
86
|
+
| 'marquee'
|
|
87
|
+
| 'math'
|
|
88
|
+
| 'menu'
|
|
89
|
+
| 'menubar'
|
|
90
|
+
| 'menuitem'
|
|
91
|
+
| 'menuitemcheckbox'
|
|
92
|
+
| 'menuitemradio'
|
|
93
|
+
| 'meter'
|
|
94
|
+
| 'navigation'
|
|
95
|
+
| 'none'
|
|
96
|
+
| 'note'
|
|
97
|
+
| 'option'
|
|
98
|
+
| 'paragraph'
|
|
99
|
+
| 'presentation'
|
|
100
|
+
| 'progressbar'
|
|
101
|
+
| 'radio'
|
|
102
|
+
| 'radiogroup'
|
|
103
|
+
| 'region'
|
|
104
|
+
| 'row'
|
|
105
|
+
| 'rowheader'
|
|
106
|
+
| 'scrollbar'
|
|
107
|
+
| 'search'
|
|
108
|
+
| 'searchbox'
|
|
109
|
+
| 'separator'
|
|
110
|
+
| 'slider'
|
|
111
|
+
| 'spinbutton'
|
|
112
|
+
| 'status'
|
|
113
|
+
| 'switch'
|
|
114
|
+
| 'tab'
|
|
115
|
+
| 'table'
|
|
116
|
+
| 'tablist'
|
|
117
|
+
| 'tabpanel'
|
|
118
|
+
| 'term'
|
|
119
|
+
| 'textbox'
|
|
120
|
+
| 'timer'
|
|
121
|
+
| 'toolbar'
|
|
122
|
+
| 'tooltip'
|
|
123
|
+
| 'tree'
|
|
124
|
+
| 'treegrid'
|
|
125
|
+
| 'treeitem'
|
|
126
|
+
| 'window'
|
|
127
|
+
| (string & {});
|
|
128
|
+
|
|
129
|
+
/** An action assistive technology asked the app to perform. */
|
|
130
|
+
export interface A11yActionEvent {
|
|
131
|
+
action: 'setValue';
|
|
132
|
+
/** For `setValue`: the value the AT wants (AT-SPI `Value.CurrentValue`). */
|
|
133
|
+
value?: number;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Accessibility, in the web's vocabulary — the same `role` / `aria-*`
|
|
138
|
+
* names react-dom accepts. Read by the AT-SPI bridge (docs/accessibility.md);
|
|
139
|
+
* inert (and harmless) where there is no accessibility bus. Every host
|
|
140
|
+
* element takes these, which is the whole seam a component library needs.
|
|
141
|
+
*/
|
|
142
|
+
export interface A11yProps {
|
|
143
|
+
/** What this element *is* to a screen reader. */
|
|
144
|
+
role?: A11yRole;
|
|
145
|
+
/** The accessible name, above every other name source. */
|
|
146
|
+
'aria-label'?: string;
|
|
147
|
+
/** Supplementary description (AT-SPI `Description`). */
|
|
148
|
+
'aria-description'?: string;
|
|
149
|
+
/** Remove this subtree from the accessible tree entirely. */
|
|
150
|
+
'aria-hidden'?: boolean;
|
|
151
|
+
'aria-checked'?: boolean | 'mixed';
|
|
152
|
+
'aria-selected'?: boolean;
|
|
153
|
+
'aria-expanded'?: boolean;
|
|
154
|
+
'aria-pressed'?: boolean | 'mixed';
|
|
155
|
+
'aria-busy'?: boolean;
|
|
156
|
+
'aria-modal'?: boolean;
|
|
157
|
+
'aria-readonly'?: boolean;
|
|
158
|
+
'aria-required'?: boolean;
|
|
159
|
+
/** Truthy exposes HAS_POPUP; the string names what opens. */
|
|
160
|
+
'aria-haspopup'?: boolean | 'menu' | 'listbox' | 'dialog' | 'grid' | 'tree';
|
|
161
|
+
'aria-orientation'?: 'horizontal' | 'vertical';
|
|
162
|
+
/** Present ⇒ the element exposes the AT-SPI Value interface. */
|
|
163
|
+
'aria-valuenow'?: number;
|
|
164
|
+
'aria-valuemin'?: number;
|
|
165
|
+
'aria-valuemax'?: number;
|
|
166
|
+
'aria-valuetext'?: string;
|
|
167
|
+
/** Outline level, for `role="heading"` and tree items. */
|
|
168
|
+
'aria-level'?: number;
|
|
169
|
+
'aria-posinset'?: number;
|
|
170
|
+
'aria-setsize'?: number;
|
|
171
|
+
/** The shortcut that triggers this ("Ctrl+N"), announced with the item. */
|
|
172
|
+
'aria-keyshortcuts'?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Assistive technology drove the control — Orca setting a slider's value
|
|
175
|
+
* through the AT-SPI Value interface. Wire it to the same state setter
|
|
176
|
+
* as the pointer and the keyboard. Activation needs no handler here: the
|
|
177
|
+
* bridge dispatches a synthetic click through the normal event path.
|
|
178
|
+
*/
|
|
179
|
+
onAccessibilityAction?: (ev: A11yActionEvent) => void;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface DrawnProps<T = DrawnNode>
|
|
183
|
+
extends
|
|
184
|
+
CommonProps,
|
|
185
|
+
InteractionProps,
|
|
186
|
+
A11yProps,
|
|
187
|
+
SelectionProps<T>,
|
|
188
|
+
EventHandlers<T> {
|
|
189
|
+
ref?: Ref<T>;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Selecting read-only text. `selectable` on an element makes it the surface
|
|
194
|
+
* a drag inside it selects across — see
|
|
195
|
+
* [elements.md](elements.md#selecting-text).
|
|
196
|
+
*/
|
|
197
|
+
export interface SelectionProps<T = DrawnNode> {
|
|
198
|
+
/**
|
|
199
|
+
* `true` makes this element a selection surface: a drag across the text
|
|
200
|
+
* inside it selects, double and triple clicks take a word and a block,
|
|
201
|
+
* Ctrl+A and Ctrl+C work, and a release takes PRIMARY. It also makes the
|
|
202
|
+
* element a focus target, so the keys have somewhere to arrive —
|
|
203
|
+
* `tabIndex={-1}` keeps it out of the Tab cycle.
|
|
204
|
+
*
|
|
205
|
+
* `false` opts a subtree out of the surface above it, the way CSS's
|
|
206
|
+
* `user-select: none` does: a list's bullets, a table's chrome, a button
|
|
207
|
+
* inside a document.
|
|
208
|
+
*/
|
|
209
|
+
selectable?: boolean;
|
|
210
|
+
/** The highlight behind selected text. Defaults to a tint of the theme's
|
|
211
|
+
* accent, which keeps the ink's own contrast intact on any palette. */
|
|
212
|
+
selectionColor?: Color;
|
|
213
|
+
/** The selection in this surface changed. */
|
|
214
|
+
onSelectionChange?: (ev: SelectionChangeEvent<T>) => void;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// --- windows ---------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* ICCCM `WM_NORMAL_HINTS` — what the window manager will let the user do.
|
|
221
|
+
*
|
|
222
|
+
* The four bounds also take `'auto'`, which asks the content: on a floor
|
|
223
|
+
* that is the smallest size it can be drawn at, and on a cap the size it
|
|
224
|
+
* wanted. Both are re-measured as the content changes.
|
|
225
|
+
*/
|
|
226
|
+
export interface SizeHintProps {
|
|
227
|
+
minWidth?: number | 'auto';
|
|
228
|
+
minHeight?: number | 'auto';
|
|
229
|
+
maxWidth?: number | 'auto';
|
|
230
|
+
maxHeight?: number | 'auto';
|
|
231
|
+
widthInc?: number;
|
|
232
|
+
heightInc?: number;
|
|
233
|
+
baseWidth?: number;
|
|
234
|
+
baseHeight?: number;
|
|
235
|
+
minAspect?: [number, number];
|
|
236
|
+
maxAspect?: [number, number];
|
|
237
|
+
gravity?: number;
|
|
238
|
+
/** `false` pins min and max size to the current size. */
|
|
239
|
+
resizable?: boolean;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export type WindowType =
|
|
243
|
+
| 'normal'
|
|
244
|
+
| 'dialog'
|
|
245
|
+
| 'utility'
|
|
246
|
+
| 'toolbar'
|
|
247
|
+
| 'splash'
|
|
248
|
+
| 'menu'
|
|
249
|
+
| 'dropdown_menu'
|
|
250
|
+
| 'popup_menu'
|
|
251
|
+
| 'tooltip'
|
|
252
|
+
| 'notification'
|
|
253
|
+
| 'dock'
|
|
254
|
+
| 'desktop'
|
|
255
|
+
| (string & {});
|
|
256
|
+
|
|
257
|
+
export interface WindowProps
|
|
258
|
+
extends
|
|
259
|
+
CommonProps,
|
|
260
|
+
InteractionProps,
|
|
261
|
+
A11yProps,
|
|
262
|
+
EventHandlers<DrawnNode>,
|
|
263
|
+
SizeHintProps,
|
|
264
|
+
ScrollProps {
|
|
265
|
+
ref?: Ref<NtkWindow>;
|
|
266
|
+
/** Window title (UTF-8, via `WM_NAME` + `_NET_WM_NAME`). */
|
|
267
|
+
title?: string;
|
|
268
|
+
/**
|
|
269
|
+
* Created but never self-mapped: this window is waiting to be embedded
|
|
270
|
+
* (XEmbed / `<foreign>` on the other side), and from the reparent on,
|
|
271
|
+
* mapping is the embedder's decision. What a `<Frame>` pane's root window
|
|
272
|
+
* sets; without an embedder the window simply never appears.
|
|
273
|
+
*/
|
|
274
|
+
embeddable?: boolean;
|
|
275
|
+
/**
|
|
276
|
+
* Realized and laid out, but not on screen: the X window exists at its
|
|
277
|
+
* size (auto sizes included — the content is really measured), the tree
|
|
278
|
+
* behind it is live, and the window is simply never mapped while this is
|
|
279
|
+
* true. Clearing it maps the window where it stands; unlike conditional
|
|
280
|
+
* rendering, nothing unmounts in between, so state, subscriptions and the
|
|
281
|
+
* X window itself survive a hide.
|
|
282
|
+
*
|
|
283
|
+
* What `Tooltip` measures an element label in before placing it, and the
|
|
284
|
+
* declarative form of "minimize by unmapping". A subtree hidden by React
|
|
285
|
+
* (`<Activity mode="hidden">`, a suspended `<Suspense>`) composes with it:
|
|
286
|
+
* the window is on screen only when neither says hidden.
|
|
287
|
+
*/
|
|
288
|
+
hidden?: boolean;
|
|
289
|
+
/**
|
|
290
|
+
* Window geometry — window state, not yoga style: the user may resize.
|
|
291
|
+
*
|
|
292
|
+
* `'auto'`, which is also what leaving the prop out means, sizes the
|
|
293
|
+
* window from its content and caps it at the screen — CSS's `width: auto`
|
|
294
|
+
* for a box that shrinks to fit. The two axes are independent, so
|
|
295
|
+
* `width={600}` with no `height` gives a window whose height follows its
|
|
296
|
+
* content at that width.
|
|
297
|
+
*/
|
|
298
|
+
width?: number | 'auto';
|
|
299
|
+
height?: number | 'auto';
|
|
300
|
+
x?: number;
|
|
301
|
+
y?: number;
|
|
302
|
+
/** Palette that `$token` style values resolve against, for this subtree. */
|
|
303
|
+
theme?: Record<string, string | number>;
|
|
304
|
+
/** ICCCM `WM_CLASS`. */
|
|
305
|
+
wmClass?: string | [string, string] | { instance: string; class?: string };
|
|
306
|
+
/** EWMH `_NET_WM_WINDOW_TYPE`, or a list of fallbacks. */
|
|
307
|
+
windowType?: WindowType | WindowType[];
|
|
308
|
+
/**
|
|
309
|
+
* ICCCM `WM_TRANSIENT_FOR` — the window this one belongs to. It is what
|
|
310
|
+
* makes a second top-level window a *dialog* rather than an unrelated
|
|
311
|
+
* application window: the WM stacks it above its owner, keeps it out of
|
|
312
|
+
* the taskbar and pager, iconifies it alongside, places it relative to the
|
|
313
|
+
* owner and gives it a dialog's reduced frame.
|
|
314
|
+
*
|
|
315
|
+
* Takes a ref to a `<window>`/`<popup>`, a ref to any drawn node (resolved
|
|
316
|
+
* to the window that owns it), a raw XID, or `'root'` for "transient for
|
|
317
|
+
* this client's whole window group".
|
|
318
|
+
*
|
|
319
|
+
* Resolved in the commit phase. An owner that is not realized yet — a
|
|
320
|
+
* sibling `<window>` mounting in the same commit, whose ref attaches only
|
|
321
|
+
* in the layout phase — is retried rather than dropped.
|
|
322
|
+
*
|
|
323
|
+
* **Inert on an override-redirect window**, which is every `<popup>` by
|
|
324
|
+
* default: the WM does not manage those, so nothing reads the property.
|
|
325
|
+
* Pass `overrideRedirect={false}` to make the popup a managed window
|
|
326
|
+
* first. ICCCM 4.1.2.6 draws exactly that distinction.
|
|
327
|
+
*/
|
|
328
|
+
transientFor?: Ref<NtkWindow | DrawnNode> | number | 'root' | null;
|
|
329
|
+
/**
|
|
330
|
+
* EWMH `_NET_WM_STATE`. **Controlled**: this is what the window is asked
|
|
331
|
+
* to be, and {@link WindowProps.onStatesChange} is what it actually is —
|
|
332
|
+
* on X the window manager changes state behind the app's back, so the two
|
|
333
|
+
* diverge and react-x11 does not force reality back to the prop.
|
|
334
|
+
*
|
|
335
|
+
* Applied before the window is mapped, which is the only way to open
|
|
336
|
+
* already fullscreen rather than flashing at the normal size first.
|
|
337
|
+
*/
|
|
338
|
+
states?: WindowStateName[];
|
|
339
|
+
/** Sugar for `states={['fullscreen']}`; they union. */
|
|
340
|
+
fullscreen?: boolean;
|
|
341
|
+
/** Sugar for `states={['above']}`; they union. */
|
|
342
|
+
alwaysOnTop?: boolean;
|
|
343
|
+
/**
|
|
344
|
+
* `false` asks for no titlebar or border, via `_MOTIF_WM_HINTS`. Honoured
|
|
345
|
+
* by Mutter, KWin, Xfwm, Openbox and i3; a WM that ignores the hint
|
|
346
|
+
* simply decorates the window.
|
|
347
|
+
*/
|
|
348
|
+
decorations?: boolean;
|
|
349
|
+
/**
|
|
350
|
+
* Give the window a 32-bit ARGB visual, so what it does not paint stays
|
|
351
|
+
* transparent and a compositor shows the desktop through it. Together with
|
|
352
|
+
* `style={{ borderRadius }}` this is how a `<popup>` gets rounded,
|
|
353
|
+
* antialiased corners — no Shape extension, no 1-bit mask.
|
|
354
|
+
*
|
|
355
|
+
* The window's own `backgroundColor` may then be translucent
|
|
356
|
+
* (`rgba(20, 20, 24, 0.9)`), and leaving it unset makes the window empty
|
|
357
|
+
* except for what the tree paints.
|
|
358
|
+
*
|
|
359
|
+
* Transparency needs a 32-bit visual (XQuartz has none) **and** a running
|
|
360
|
+
* compositor to blend it. When either is missing, the window is filled
|
|
361
|
+
* edge to edge and `borderRadius` on it is ignored — a square opaque
|
|
362
|
+
* popup rather than a black-cornered one, which is what painting the
|
|
363
|
+
* corners away would give on a server with nothing compositing. Guard the
|
|
364
|
+
* enhanced design with a `'@supports transparency'` style block:
|
|
365
|
+
*
|
|
366
|
+
* ```jsx
|
|
367
|
+
* style={{
|
|
368
|
+
* backgroundColor: '#1c1c22',
|
|
369
|
+
* '@supports transparency': {
|
|
370
|
+
* backgroundColor: 'rgba(24, 24, 30, 0.86)',
|
|
371
|
+
* borderRadius: 14,
|
|
372
|
+
* },
|
|
373
|
+
* }}
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* The visual is still taken when nothing is compositing yet, so that a
|
|
377
|
+
* compositor starting mid-session turns the window transparent without a
|
|
378
|
+
* remount. Set at creation: a visual is a `CreateWindow` field, so
|
|
379
|
+
* toggling this prop on a mounted window does nothing until it remounts
|
|
380
|
+
* (change its `key`). Needs ntk >= 6.6.0.
|
|
381
|
+
*/
|
|
382
|
+
transparent?: boolean;
|
|
383
|
+
/**
|
|
384
|
+
* Read the pointer through XI2, which is what makes scrolling smooth: the
|
|
385
|
+
* device's own scroll valuators instead of the whole notches the server
|
|
386
|
+
* emulates as button 4/5 presses, so a touchpad reports the fractions of a
|
|
387
|
+
* notch it measured. Ignored where the server has no XI2 — the wheel
|
|
388
|
+
* buttons answer as they always did.
|
|
389
|
+
*
|
|
390
|
+
* - `'auto'` (the default) selects XI2 the first time the window is
|
|
391
|
+
* scrolled. An XI2 selection replaces the core one for the same event
|
|
392
|
+
* type, and an XIMotion is ~136 bytes against a core MotionNotify's 32,
|
|
393
|
+
* so an eager selection bills a window that never scrolls ~8 KB/s of
|
|
394
|
+
* pointer traffic for nothing. The opening event of the first gesture is
|
|
395
|
+
* a whole notch — which is all a mouse wheel ever reports anyway, and
|
|
396
|
+
* all an eager selection would have delivered too, since the first
|
|
397
|
+
* valuator event only seeds the accumulator.
|
|
398
|
+
* - `true` selects at creation, for an app whose whole interaction is a
|
|
399
|
+
* touchpad and which wants the very first flick smooth.
|
|
400
|
+
* - `false` refuses the selection for the window's whole life.
|
|
401
|
+
*
|
|
402
|
+
* A `<popup>` never upgrades under `'auto'`: it holds a pointer grab, a
|
|
403
|
+
* core grab delivers core events, and a window whose valuators are flowing
|
|
404
|
+
* has its emulated wheel buttons dropped, so a menu that had selected XI2
|
|
405
|
+
* could not be wheeled while it was grabbing. An explicit `true` still
|
|
406
|
+
* wins there. Needs ntk >= 7.5.0.
|
|
407
|
+
*/
|
|
408
|
+
xi2?: boolean | 'auto';
|
|
409
|
+
/**
|
|
410
|
+
* Mark this window as a drag preview: the drag router never treats it as
|
|
411
|
+
* the window under the pointer, so a `<popup dragPreview>` can follow the
|
|
412
|
+
* pointer without swallowing its own drag. See `useDragSource`.
|
|
413
|
+
*/
|
|
414
|
+
dragPreview?: boolean;
|
|
415
|
+
/**
|
|
416
|
+
* ConfigureNotify — the tree reflows on its own. Fires for moves and
|
|
417
|
+
* reparents too: check `ev.resized` / `ev.moved` before doing work of
|
|
418
|
+
* your own, or a window drag pays for it per pointer step.
|
|
419
|
+
*/
|
|
420
|
+
onResize?: (ev: WindowResizeEvent) => void;
|
|
421
|
+
onExpose?: (ev: SyntheticEvent<NtkWindow>) => void;
|
|
422
|
+
/**
|
|
423
|
+
* The states the window manager now has on the window. Subscribing is
|
|
424
|
+
* what makes react-x11 watch `_NET_WM_STATE`, so a window with no handler
|
|
425
|
+
* costs nothing.
|
|
426
|
+
*/
|
|
427
|
+
onStatesChange?: (states: WindowStateName[]) => void;
|
|
428
|
+
/**
|
|
429
|
+
* The window manager's close button, and anything else that asks a window
|
|
430
|
+
* to close (Alt+F4, a taskbar's "Close", `wmctrl -c`).
|
|
431
|
+
*
|
|
432
|
+
* Every WM-managed window speaks `WM_DELETE_WINDOW` whether or not this
|
|
433
|
+
* prop is passed — without it the window manager cannot ask at all and
|
|
434
|
+
* kills the connection instead. This prop replaces the default answer:
|
|
435
|
+
*
|
|
436
|
+
* - **without it** the app's primary window unmounts the tree and closes
|
|
437
|
+
* the connection, and any other window refuses (a dialog is opened by
|
|
438
|
+
* app state, so only app state can close it — dev warns).
|
|
439
|
+
* - **with it** nothing happens except this handler, which decides:
|
|
440
|
+
* `setOpen(false)` for a dialog, a "save your work?" prompt, or
|
|
441
|
+
* `root.unmount()` for a quit of your own.
|
|
442
|
+
*
|
|
443
|
+
* The primary window is the first top-level `<window>` that is not
|
|
444
|
+
* `transientFor` another and has no `windowType` of its own — a lone
|
|
445
|
+
* window always qualifies.
|
|
446
|
+
*/
|
|
447
|
+
onCloseRequest?: (ev: SyntheticEvent<NtkWindow>) => void;
|
|
448
|
+
/**
|
|
449
|
+
* Every ClientMessage addressed to this window — the carrier of EWMH,
|
|
450
|
+
* XEmbed, the system tray, and whatever two copies of one application
|
|
451
|
+
* agree between themselves.
|
|
452
|
+
*
|
|
453
|
+
* `ev.messageType` is the atom's **name**, so a handler is a `switch` over
|
|
454
|
+
* strings rather than a comparison against ids it had to intern first.
|
|
455
|
+
* Messages arrive in the order the server sent them, which the chunked
|
|
456
|
+
* protocols depend on.
|
|
457
|
+
*
|
|
458
|
+
* ```jsx
|
|
459
|
+
* <window onClientMessage={(ev) => {
|
|
460
|
+
* if (ev.messageType !== '_NET_SYSTEM_TRAY_OPCODE') return;
|
|
461
|
+
* if (ev.data[1] === SYSTEM_TRAY_REQUEST_DOCK) dock(ev.data[2]);
|
|
462
|
+
* }} />
|
|
463
|
+
* ```
|
|
464
|
+
*
|
|
465
|
+
* react-x11 answers some of these itself: `preventDefault()` stops it
|
|
466
|
+
* doing so for XDND. Nothing has to be armed — a ClientMessage reaches its
|
|
467
|
+
* window whatever event mask it selected — so a window without the prop
|
|
468
|
+
* costs nothing.
|
|
469
|
+
*/
|
|
470
|
+
onClientMessage?: (ev: ClientMessageEvent) => void;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* `_NET_WM_STATE` names, lower-cased without the atom prefix. `'maximized'`
|
|
475
|
+
* is the one that is not an atom: EWMH maximizes an axis at a time, and it
|
|
476
|
+
* expands to the vert/horz pair.
|
|
477
|
+
*
|
|
478
|
+
* These are what a `<window>` **asks for**. What the window manager actually
|
|
479
|
+
* did is `useWindowState()`, whose `states` is a list of these.
|
|
480
|
+
*/
|
|
481
|
+
export type WindowStateName =
|
|
482
|
+
| 'modal'
|
|
483
|
+
| 'sticky'
|
|
484
|
+
| 'maximized'
|
|
485
|
+
| 'maximized_vert'
|
|
486
|
+
| 'maximized_horz'
|
|
487
|
+
| 'shaded'
|
|
488
|
+
| 'skip_taskbar'
|
|
489
|
+
| 'skip_pager'
|
|
490
|
+
| 'hidden'
|
|
491
|
+
| 'fullscreen'
|
|
492
|
+
| 'above'
|
|
493
|
+
| 'below'
|
|
494
|
+
| 'demands_attention'
|
|
495
|
+
| 'focused'
|
|
496
|
+
| (string & {});
|
|
497
|
+
|
|
498
|
+
export interface PopupProps extends WindowProps {
|
|
499
|
+
/**
|
|
500
|
+
* Hold a pointer grab while the popup is up — what makes a menu
|
|
501
|
+
* dismissable, since without it a press on another window never reaches
|
|
502
|
+
* this client at all. Needs ntk >= 3.7.0.
|
|
503
|
+
*/
|
|
504
|
+
grab?: boolean;
|
|
505
|
+
/**
|
|
506
|
+
* `true` (the default) keeps the window manager out entirely, which is
|
|
507
|
+
* what makes a menu a menu — no frame, no repositioning, no taskbar entry.
|
|
508
|
+
*
|
|
509
|
+
* `false` makes the popup an ordinary managed window: decorated, movable,
|
|
510
|
+
* closable through the WM, and — with {@link WindowProps.transientFor} —
|
|
511
|
+
* stacked above its owner and iconified with it. That is the combination
|
|
512
|
+
* that turns a `<popup>` into a real dialog. Turn `grab` off with it: a
|
|
513
|
+
* client-side pointer grab over a window the WM is trying to let the user
|
|
514
|
+
* drag is a fight nobody wins.
|
|
515
|
+
*/
|
|
516
|
+
overrideRedirect?: boolean;
|
|
517
|
+
/** A press landed outside the popup: close it. */
|
|
518
|
+
onDismiss?: (ev: MouseEvent<DrawnNode>) => void;
|
|
519
|
+
/**
|
|
520
|
+
* Hang this popup off a node — `anchorRect`'s options plus the node to
|
|
521
|
+
* measure, and the popup works out its own position from them, ignoring
|
|
522
|
+
* `x`/`y`.
|
|
523
|
+
*
|
|
524
|
+
* What this does that computing a rect in the application cannot: a popup
|
|
525
|
+
* with an `'auto'` size only knows how big it is *inside* `realize()`,
|
|
526
|
+
* after the content is measured and before `CreateWindow` — and which side
|
|
527
|
+
* it flips to and how far it is pulled back from a screen edge are both
|
|
528
|
+
* functions of that size. So an anchored popup is born the right size in
|
|
529
|
+
* the right place, and keeps up afterwards with everything that can move
|
|
530
|
+
* either: the anchor's own layout, an ancestor scrolling, the owner window
|
|
531
|
+
* being dragged, its own content growing.
|
|
532
|
+
*
|
|
533
|
+
* `at` names a rect **inside** the node — a caret, a table cell — so a
|
|
534
|
+
* completion list follows the line being typed on rather than the editor.
|
|
535
|
+
* When the anchor scrolls out of view the popup unmaps until it comes
|
|
536
|
+
* back, since there is no position that points at something invisible;
|
|
537
|
+
* an app that would rather *close* it does the placement itself with
|
|
538
|
+
* {@link useAnchorTracking} and its `onOutOfView`.
|
|
539
|
+
*/
|
|
540
|
+
anchor?: PopupAnchor;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** A node to anchor to: a ref (the usual — refs attach after the commit
|
|
544
|
+
* that renders them) or the node itself. */
|
|
545
|
+
export type AnchorTarget = DrawnNode | RefObject<DrawnNode | null> | null;
|
|
546
|
+
|
|
547
|
+
export interface PopupAnchor extends Omit<AnchorOptions, 'alignTo'> {
|
|
548
|
+
/** The node this popup hangs off. */
|
|
549
|
+
to: AnchorTarget;
|
|
550
|
+
/** Takes the alignment axis from another node — see
|
|
551
|
+
* {@link AnchorOptions.alignTo}. */
|
|
552
|
+
alignTo?: AnchorTarget;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// --- drawn elements --------------------------------------------------------
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* The flex container — and, with `style={{overflow: 'scroll'}}`, the scroll
|
|
559
|
+
* container. The scrolling props below do nothing until the style says so.
|
|
560
|
+
*/
|
|
561
|
+
export interface BoxProps
|
|
562
|
+
extends Omit<DrawnProps<DrawnNode>, 'ref'>, ScrollProps {
|
|
563
|
+
/**
|
|
564
|
+
* Either node type, because a `<box>` is both: `DrawnNode` is what most
|
|
565
|
+
* refs want and what every existing one already is, and `ScrollableNode`
|
|
566
|
+
* is the same node with `scrollTo` / `scrollX` / `contentHeight` on it.
|
|
567
|
+
* Reach for the second only on a box you actually scroll —
|
|
568
|
+
* `useRef<ScrollableNode>(null)`.
|
|
569
|
+
*/
|
|
570
|
+
ref?: Ref<ScrollableNode> | Ref<DrawnNode>;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** What `overflow: 'scroll'` adds, on a `<box>` or a `<window>`. */
|
|
574
|
+
export interface ScrollProps {
|
|
575
|
+
onScroll?: (ev: ScrollEvent) => void;
|
|
576
|
+
/** Fired from layout, so it arrives for a list nobody has scrolled yet. */
|
|
577
|
+
onViewport?: (ev: ViewportEvent) => void;
|
|
578
|
+
/** Hide the drawn scrollbars; the content still scrolls. */
|
|
579
|
+
scrollbar?: boolean;
|
|
580
|
+
scrollbarColor?: Color;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export interface TextProps extends DrawnProps<DrawnNode> {
|
|
584
|
+
/** Strings and numbers are only legal inside `<text>`. */
|
|
585
|
+
children?: ReactNode;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export interface TextInputProps extends DrawnProps<TextInputNode> {
|
|
589
|
+
/** Controlled mode: the display follows the prop. */
|
|
590
|
+
value?: string;
|
|
591
|
+
/** Uncontrolled mode. */
|
|
592
|
+
defaultValue?: string;
|
|
593
|
+
/**
|
|
594
|
+
* Field name, echoed on the event as `ev.name` and `ev.target.name`.
|
|
595
|
+
* Nothing in the renderer reads it — it exists so form libraries, which
|
|
596
|
+
* key a field by its name, have somewhere to put one.
|
|
597
|
+
*/
|
|
598
|
+
name?: string;
|
|
599
|
+
/**
|
|
600
|
+
* The value changed. `ev.target.value` and `ev.value` are both the new
|
|
601
|
+
* text, so `(ev) => setText(ev.target.value)` and `(ev) => setText(ev.value)`
|
|
602
|
+
* both read naturally.
|
|
603
|
+
*/
|
|
604
|
+
onChange?: (ev: ChangeEvent<TextInputNode>) => void;
|
|
605
|
+
/** Enter — or Ctrl+Enter in a `<textarea>`. */
|
|
606
|
+
onSubmit?: (ev: SubmitEvent<TextInputNode>) => void;
|
|
607
|
+
placeholder?: string;
|
|
608
|
+
placeholderColor?: Color;
|
|
609
|
+
/** Code-point limit. */
|
|
610
|
+
maxLength?: number;
|
|
611
|
+
selectionColor?: Color;
|
|
612
|
+
caretColor?: Color;
|
|
613
|
+
/**
|
|
614
|
+
* `false` turns off the built-in right-click edit menu (Undo/Redo, Cut,
|
|
615
|
+
* Copy, Paste, Select All). `onContextMenu` still fires, so this is how
|
|
616
|
+
* you replace it with your own rather than suppressing it per-event.
|
|
617
|
+
*/
|
|
618
|
+
contextMenu?: false;
|
|
619
|
+
/**
|
|
620
|
+
* The text is a secret: **nothing here reaches a selection.** Ctrl+C and
|
|
621
|
+
* the copy half of Ctrl+X do nothing, the edit menu offers neither Cut nor
|
|
622
|
+
* Copy, and selecting text does not take PRIMARY — so a middle click in
|
|
623
|
+
* another window cannot spend it. Pasting *in*, editing, undo and the caret
|
|
624
|
+
* are untouched.
|
|
625
|
+
*
|
|
626
|
+
* What is on screen stops being on screen; what is on the clipboard does
|
|
627
|
+
* not. `PasswordInput` sets this on the input it shows while revealed.
|
|
628
|
+
*/
|
|
629
|
+
sensitive?: boolean;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export interface TextAreaProps extends TextInputProps {
|
|
633
|
+
/** Preferred height in text lines (default 3). */
|
|
634
|
+
rows?: number;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Raw, straight (non-premultiplied) RGBA pixels: the shape `getImageData`
|
|
639
|
+
* hands back, and what ntk's `new Image()` takes. `data` is
|
|
640
|
+
* `width * height * 4` bytes. Treated as immutable content — hand over a
|
|
641
|
+
* new object when the pixels change, or the renderer cannot tell.
|
|
642
|
+
*/
|
|
643
|
+
export interface RawImageSource {
|
|
644
|
+
width: number;
|
|
645
|
+
height: number;
|
|
646
|
+
data: Uint8Array | Uint8ClampedArray;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* A client-side picture source the 2d context composites as-is: an ntk
|
|
651
|
+
* `Image` or `Surface`, or anything with a size and a `picture(app)`.
|
|
652
|
+
* Passing one puts identity in your hands — the object's server upload is
|
|
653
|
+
* cached per connection, so many `<image>`s showing one `Image` upload once.
|
|
654
|
+
*/
|
|
655
|
+
export interface DirectImageSource {
|
|
656
|
+
readonly width: number;
|
|
657
|
+
readonly height: number;
|
|
658
|
+
picture(app: unknown): unknown;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* A WHATWG `URL`, matched structurally because these declarations use only
|
|
663
|
+
* ES lib types (no DOM, no node ambients). The useful case is a file URL
|
|
664
|
+
* from `new URL('./icon.png', import.meta.url)`, which finds an asset
|
|
665
|
+
* relative to the module however the app is launched.
|
|
666
|
+
*/
|
|
667
|
+
export interface FileUrl {
|
|
668
|
+
readonly href: string;
|
|
669
|
+
readonly protocol: string;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* What `src` accepts: a file path or file URL (PNG/JPEG, decoded in JS),
|
|
674
|
+
* encoded PNG/JPEG bytes, raw RGBA pixels, or an ntk `Image`/`Surface`.
|
|
675
|
+
*/
|
|
676
|
+
export type ImageSource =
|
|
677
|
+
string | FileUrl | Uint8Array | RawImageSource | DirectImageSource;
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* An existing server-side Picture, named by X id. The size is stated by the
|
|
681
|
+
* caller because asking the server for it would be a round trip — which
|
|
682
|
+
* this prop exists to avoid.
|
|
683
|
+
*/
|
|
684
|
+
export interface ImagePictureSource {
|
|
685
|
+
id: number;
|
|
686
|
+
width: number;
|
|
687
|
+
height: number;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* An existing server-side Pixmap or Window, named by X id. `depth` picks
|
|
692
|
+
* the picture format it is composited through: 24 (rgb, the default — what
|
|
693
|
+
* a window pixmap from Composite is), 32 (argb), or 8 (alpha only,
|
|
694
|
+
* composited as ink through its coverage).
|
|
695
|
+
*/
|
|
696
|
+
export interface ImageDrawableSource {
|
|
697
|
+
id: number;
|
|
698
|
+
width: number;
|
|
699
|
+
height: number;
|
|
700
|
+
depth?: 8 | 24 | 32;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
export interface ImageProps extends DrawnProps<DrawnNode> {
|
|
704
|
+
/**
|
|
705
|
+
* Client-side pixels: a file path or file URL (PNG/JPEG, decoded in JS),
|
|
706
|
+
* encoded PNG/JPEG bytes (no temp file), raw RGBA
|
|
707
|
+
* (`{ width, height, data }`), or an ntk `Image`/`Surface` used as-is.
|
|
708
|
+
*
|
|
709
|
+
* One source per element: `src`, `picture` and `drawable` are mutually
|
|
710
|
+
* exclusive, and passing two throws.
|
|
711
|
+
*/
|
|
712
|
+
src?: ImageSource;
|
|
713
|
+
/**
|
|
714
|
+
* Composite an existing server-side Picture into the element — one
|
|
715
|
+
* `RenderComposite`, no `PutImage`, no readback. The picture stays the
|
|
716
|
+
* caller's; drawing it scaled sets its transform/filter for the composite
|
|
717
|
+
* and resets them after.
|
|
718
|
+
*/
|
|
719
|
+
picture?: ImagePictureSource;
|
|
720
|
+
/**
|
|
721
|
+
* Composite an existing server-side Pixmap or Window, through a Picture
|
|
722
|
+
* the element creates over it (and frees when done). The drawable stays
|
|
723
|
+
* the caller's.
|
|
724
|
+
*/
|
|
725
|
+
drawable?: ImageDrawableSource;
|
|
726
|
+
/**
|
|
727
|
+
* The source's identity, when `src` is re-derived per render: an
|
|
728
|
+
* unchanged key means unchanged content, so a structurally new buffer is
|
|
729
|
+
* neither re-decoded nor re-uploaded — and two `<image>`s with one key
|
|
730
|
+
* share one decoded copy. The `<canvas cacheKey>` contract: the key must
|
|
731
|
+
* name the content. Not consulted for an ntk `Image` (the object is its
|
|
732
|
+
* own identity) and meaningless with `picture`/`drawable` (throws).
|
|
733
|
+
*/
|
|
734
|
+
cacheKey?: string | number;
|
|
735
|
+
/** The accessible name — what a screen reader says for this image. */
|
|
736
|
+
alt?: string;
|
|
737
|
+
}
|
|
738
|
+
// Size is `style={{ width, height }}`, like every other element: `width`
|
|
739
|
+
// and `height` are style names, so they are not declared here. With no
|
|
740
|
+
// style size, the source's own size (stated, for the server-side sources)
|
|
741
|
+
// is the natural one, kept to its aspect ratio.
|
|
742
|
+
|
|
743
|
+
/** What `onDraw` is told about the node it is painting. */
|
|
744
|
+
export interface DrawInfo {
|
|
745
|
+
/**
|
|
746
|
+
* The node's box in **device pixels** — the browser's own canvas
|
|
747
|
+
* contract: the backing store is the panel's grid, and `scale` says how
|
|
748
|
+
* many of its pixels one logical pixel is worth (docs/scale.md). A
|
|
749
|
+
* drawing that works in fractions of the box needs neither; one that
|
|
750
|
+
* draws N-logical-px strokes multiplies by `scale`.
|
|
751
|
+
*/
|
|
752
|
+
width: number;
|
|
753
|
+
height: number;
|
|
754
|
+
/**
|
|
755
|
+
* The node's origin in the drawable being drawn into — the translation
|
|
756
|
+
* already applied to the context, said out loud. Everything on the
|
|
757
|
+
* context works in node coordinates and never needs it; the raw-pixel
|
|
758
|
+
* calls are the exception, because `putImageData`/`getImageData` ignore
|
|
759
|
+
* the transform (the HTML canvas rule) and address the drawable
|
|
760
|
+
* directly: `ctx.putImageData(data, info.x + x, info.y + y)`. Zero when
|
|
761
|
+
* the drawing goes into a surface of its own — under a `cacheKey`, or a
|
|
762
|
+
* `<glarea>`'s window — so offsetting by it is correct everywhere.
|
|
763
|
+
*/
|
|
764
|
+
x: number;
|
|
765
|
+
/** See {@link DrawInfo.x}. */
|
|
766
|
+
y: number;
|
|
767
|
+
/** Device pixels per logical pixel — `useScale()`'s number. */
|
|
768
|
+
scale: number;
|
|
769
|
+
node: DrawnNode;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
export interface CanvasProps extends DrawnProps<DrawnNode> {
|
|
773
|
+
/**
|
|
774
|
+
* Paint the node. `ctx` is ntk's canvas-like 2d context, translated to
|
|
775
|
+
* the node's origin and clipped to its bounds. Runs on every repaint.
|
|
776
|
+
*
|
|
777
|
+
* One exception to "you are at the node's origin": `putImageData` (and
|
|
778
|
+
* `getImageData`) ignore the transform and the clip — the HTML canvas
|
|
779
|
+
* rule — and address the drawable itself. Offset by `info.x`/`info.y`,
|
|
780
|
+
* or better, draw raw pixels through an `Image` source and `drawImage`,
|
|
781
|
+
* which honours both and caches its upload server-side. In development,
|
|
782
|
+
* a write that lands outside the node warns, once. See
|
|
783
|
+
* docs/elements.md, "`<canvas>`".
|
|
784
|
+
*/
|
|
785
|
+
onDraw?: (ctx: any, info: DrawInfo) => void;
|
|
786
|
+
/**
|
|
787
|
+
* Opt this drawing into the paint cache: render it once and composite it
|
|
788
|
+
* on later repaints, instead of running `onDraw` again.
|
|
789
|
+
*
|
|
790
|
+
* Opt-in because only you know what `onDraw` reads — the key has to name
|
|
791
|
+
* every input the drawing depends on, and a key that leaves one out shows
|
|
792
|
+
* stale pixels. Include the things that change the picture:
|
|
793
|
+
*
|
|
794
|
+
* <canvas cacheKey={`spark:${series.id}:${w}x${h}`} onDraw={draw} />
|
|
795
|
+
*
|
|
796
|
+
* Develop with `REACT_X11_PAINT_CACHE=verify`, which turns a key that
|
|
797
|
+
* misses an input into a loud complaint rather than a wrong pixel. Leave
|
|
798
|
+
* unset for anything animated or driven by state outside the props.
|
|
799
|
+
*/
|
|
800
|
+
cacheKey?: string | number;
|
|
801
|
+
/**
|
|
802
|
+
* A promise about the drawing: everything it paints is one colour, and it
|
|
803
|
+
* is not the drawing's to choose. `onDraw` then names no colour at all —
|
|
804
|
+
* `fillStyle` and `strokeStyle` arrive preset from `style.color`.
|
|
805
|
+
*
|
|
806
|
+
* One drawing then serves every state a control puts it in — `:hover`,
|
|
807
|
+
* `:disabled`, a theme flip — since none of those is its business.
|
|
808
|
+
*
|
|
809
|
+
* A cached `mono` entry currently bakes its colour and keys on it: the
|
|
810
|
+
* coverage path that would apply the colour at composite time composites
|
|
811
|
+
* empty under nested non-rectangular clips. Sharing across instances is
|
|
812
|
+
* unaffected. See docs/elements.md#mono--one-colour-and-the-colour-out-of-the-key.
|
|
813
|
+
*
|
|
814
|
+
* Works without `cacheKey` — the ink is preset either way — but the two
|
|
815
|
+
* together are the point.
|
|
816
|
+
*/
|
|
817
|
+
mono?: boolean;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// --- vector drawings -------------------------------------------------------
|
|
821
|
+
|
|
822
|
+
export interface SvgProps extends DrawnProps<DrawnNode> {
|
|
823
|
+
source?: string;
|
|
824
|
+
viewBox?: string;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// --- GL --------------------------------------------------------------------
|
|
828
|
+
|
|
829
|
+
export type FrameLoop = 'demand' | 'always';
|
|
830
|
+
|
|
831
|
+
export interface GlAreaProps extends DrawnProps<DrawnNode> {
|
|
832
|
+
/** CSS colour, or `[r, g, b, a]` floats. Default black. */
|
|
833
|
+
clearColor?: Color | [number, number, number, number];
|
|
834
|
+
/** `'demand'` (default) redraws on change; `'always'` runs continuously. */
|
|
835
|
+
frameLoop?: FrameLoop;
|
|
836
|
+
/** Visual spec for ntk's `chooseGLXConfig`, e.g. `{ DEPTH_SIZE: 24 }`. */
|
|
837
|
+
glx?: Record<string, unknown>;
|
|
838
|
+
/** Runs once, with the context current: one-time state, uploads, lists. */
|
|
839
|
+
onCreated?: (gl: any, info: DrawInfo) => void;
|
|
840
|
+
/** Draw one frame. Viewport and clear happen before, SwapBuffers after. */
|
|
841
|
+
onDraw?: (gl: any, info: DrawInfo) => void;
|
|
842
|
+
/** No GL surface — no GLX, or no matching visual. */
|
|
843
|
+
onError?: (err: Error) => void;
|
|
844
|
+
/** A click inside the surface that hit no mesh. */
|
|
845
|
+
onPointerMissed?: (ev: MouseEvent<DrawnNode>) => void;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// --- embedding -------------------------------------------------------------
|
|
849
|
+
|
|
850
|
+
/** What arrived, and how. `xembed: false` is a client that set no
|
|
851
|
+
* `_XEMBED_INFO` and got plain reparenting — the common case. */
|
|
852
|
+
export interface EmbeddedInfo {
|
|
853
|
+
/** the client's X window id */
|
|
854
|
+
id: number;
|
|
855
|
+
/** whether the client speaks the XEmbed protocol */
|
|
856
|
+
xembed: boolean;
|
|
857
|
+
/** the protocol version in use, or 0 */
|
|
858
|
+
version: number;
|
|
859
|
+
node: DrawnNode;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
export interface ForeignProps extends DrawnProps<DrawnNode> {
|
|
863
|
+
/**
|
|
864
|
+
* The X window to embed. Changing it hands the old client back before the
|
|
865
|
+
* new one is taken. Omit it to instead **adopt** whatever is put inside
|
|
866
|
+
* this node, which is what `xterm -into WID` and `mpv --wid=WID` need —
|
|
867
|
+
* `onReady` is where that id comes from.
|
|
868
|
+
*/
|
|
869
|
+
windowId?: number;
|
|
870
|
+
/** The container window's id, offered as soon as it exists, so a program
|
|
871
|
+
* can be spawned into it. Fires before there is anything embedded. */
|
|
872
|
+
onReady?: (info: { windowId: number; node: DrawnNode }) => void;
|
|
873
|
+
/** A client is in. */
|
|
874
|
+
onEmbedded?: (info: EmbeddedInfo) => void;
|
|
875
|
+
/** Destroyed, or reparented away by someone else. */
|
|
876
|
+
onClientGone?: (info: { node: DrawnNode }) => void;
|
|
877
|
+
/** `XEMBED_REQUEST_FOCUS`: the client wants the focus. It is given through
|
|
878
|
+
* the focus manager unless a handler prevents it by focusing elsewhere. */
|
|
879
|
+
onRequestFocus?: (info: { node: DrawnNode }) => void;
|
|
880
|
+
/** The embed failed — no such window, or it went away mid-handshake.
|
|
881
|
+
* Without a handler the failure is a console warning. */
|
|
882
|
+
onError?: (err: Error) => void;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
export interface ReactX11Elements {
|
|
886
|
+
window: WindowProps;
|
|
887
|
+
popup: PopupProps;
|
|
888
|
+
box: BoxProps;
|
|
889
|
+
text: TextProps;
|
|
890
|
+
textinput: TextInputProps;
|
|
891
|
+
textarea: TextAreaProps;
|
|
892
|
+
image: ImageProps;
|
|
893
|
+
canvas: CanvasProps;
|
|
894
|
+
svg: SvgProps;
|
|
895
|
+
glarea: GlAreaProps;
|
|
896
|
+
foreign: ForeignProps;
|
|
897
|
+
}
|