@react-x11/components 0.3.0 → 0.5.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 +131 -4
- package/dist/charts/components.d.ts +3 -1
- package/dist/charts/components.d.ts.map +1 -1
- package/dist/charts/components.js +8 -4
- package/dist/charts/components.js.map +1 -1
- package/dist/charts/node.d.ts +13 -6
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +75 -24
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +4 -0
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +11 -6
- package/dist/charts/render.js.map +1 -1
- package/dist/code-editor/node.d.ts +47 -16
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +121 -51
- package/dist/code-editor/node.js.map +1 -1
- package/dist/flow/draw.d.ts +19 -1
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +64 -35
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +24 -8
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/model.d.ts +4 -2
- package/dist/flow/model.d.ts.map +1 -1
- package/dist/flow/model.js +4 -2
- package/dist/flow/model.js.map +1 -1
- package/dist/flow/node.d.ts +40 -9
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +142 -53
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/types.d.ts +46 -21
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/formula/index.d.ts +4 -2
- package/dist/formula/index.d.ts.map +1 -1
- package/dist/formula/index.js.map +1 -1
- package/dist/formula/node.d.ts +5 -1
- package/dist/formula/node.d.ts.map +1 -1
- package/dist/formula/node.js +19 -3
- package/dist/formula/node.js.map +1 -1
- package/dist/html/controls.d.ts +4 -1
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +11 -5
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +18 -8
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/parse.d.ts +15 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +41 -16
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +3 -0
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3 -1
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/values.d.ts +14 -1
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +11 -8
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +1 -0
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +12 -5
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +7 -4
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +22 -1
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/node.d.ts +22 -8
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +97 -15
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +3 -0
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +2 -2
- package/dist/html/paint.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/dist/maps/clip.d.ts +43 -0
- package/dist/maps/clip.d.ts.map +1 -0
- package/dist/maps/clip.js +171 -0
- package/dist/maps/clip.js.map +1 -0
- package/dist/maps/gzip.d.ts +15 -0
- package/dist/maps/gzip.d.ts.map +1 -0
- package/dist/maps/gzip.js +56 -0
- package/dist/maps/gzip.js.map +1 -0
- package/dist/maps/index.d.ts +55 -0
- package/dist/maps/index.d.ts.map +1 -0
- package/dist/maps/index.js +141 -0
- package/dist/maps/index.js.map +1 -0
- package/dist/maps/labels.d.ts +123 -0
- package/dist/maps/labels.d.ts.map +1 -0
- package/dist/maps/labels.js +323 -0
- package/dist/maps/labels.js.map +1 -0
- package/dist/maps/mvt.d.ts +244 -0
- package/dist/maps/mvt.d.ts.map +1 -0
- package/dist/maps/mvt.js +820 -0
- package/dist/maps/mvt.js.map +1 -0
- package/dist/maps/node.d.ts +278 -0
- package/dist/maps/node.d.ts.map +1 -0
- package/dist/maps/node.js +1459 -0
- package/dist/maps/node.js.map +1 -0
- package/dist/maps/overlay.d.ts +157 -0
- package/dist/maps/overlay.d.ts.map +1 -0
- package/dist/maps/overlay.js +472 -0
- package/dist/maps/overlay.js.map +1 -0
- package/dist/maps/paint.d.ts +226 -0
- package/dist/maps/paint.d.ts.map +1 -0
- package/dist/maps/paint.js +705 -0
- package/dist/maps/paint.js.map +1 -0
- package/dist/maps/proj.d.ts +281 -0
- package/dist/maps/proj.d.ts.map +1 -0
- package/dist/maps/proj.js +451 -0
- package/dist/maps/proj.js.map +1 -0
- package/dist/maps/sources.d.ts +256 -0
- package/dist/maps/sources.d.ts.map +1 -0
- package/dist/maps/sources.js +210 -0
- package/dist/maps/sources.js.map +1 -0
- package/dist/maps/style.d.ts +141 -0
- package/dist/maps/style.d.ts.map +1 -0
- package/dist/maps/style.js +147 -0
- package/dist/maps/style.js.map +1 -0
- package/dist/maps/styles.d.ts +82 -0
- package/dist/maps/styles.d.ts.map +1 -0
- package/dist/maps/styles.js +898 -0
- package/dist/maps/styles.js.map +1 -0
- package/dist/maps/tiles.d.ts +283 -0
- package/dist/maps/tiles.d.ts.map +1 -0
- package/dist/maps/tiles.js +469 -0
- package/dist/maps/tiles.js.map +1 -0
- package/dist/maps/types.d.ts +244 -0
- package/dist/maps/types.d.ts.map +1 -0
- package/dist/maps/types.js +2 -0
- package/dist/maps/types.js.map +1 -0
- package/dist/qml/containers.d.ts +3 -0
- package/dist/qml/containers.d.ts.map +1 -0
- package/dist/qml/containers.js +331 -0
- package/dist/qml/containers.js.map +1 -0
- package/dist/qml/controls.d.ts +12 -0
- package/dist/qml/controls.d.ts.map +1 -0
- package/dist/qml/controls.js +21 -0
- package/dist/qml/controls.js.map +1 -0
- package/dist/qml/globals.d.ts +6 -0
- package/dist/qml/globals.d.ts.map +1 -0
- package/dist/qml/globals.js +25 -0
- package/dist/qml/globals.js.map +1 -0
- package/dist/qml/index.d.ts +15 -0
- package/dist/qml/index.d.ts.map +1 -0
- package/dist/qml/index.js +25 -0
- package/dist/qml/index.js.map +1 -0
- package/dist/qml/interaction.d.ts +12 -0
- package/dist/qml/interaction.d.ts.map +1 -0
- package/dist/qml/interaction.js +246 -0
- package/dist/qml/interaction.js.map +1 -0
- package/dist/qml/ir.d.ts +106 -0
- package/dist/qml/ir.d.ts.map +1 -0
- package/dist/qml/ir.js +7 -0
- package/dist/qml/ir.js.map +1 -0
- package/dist/qml/layouts.d.ts +3 -0
- package/dist/qml/layouts.d.ts.map +1 -0
- package/dist/qml/layouts.js +122 -0
- package/dist/qml/layouts.js.map +1 -0
- package/dist/qml/models.d.ts +14 -0
- package/dist/qml/models.d.ts.map +1 -0
- package/dist/qml/models.js +80 -0
- package/dist/qml/models.js.map +1 -0
- package/dist/qml/objects.d.ts +228 -0
- package/dist/qml/objects.d.ts.map +1 -0
- package/dist/qml/objects.js +1103 -0
- package/dist/qml/objects.js.map +1 -0
- package/dist/qml/parse.d.ts +10 -0
- package/dist/qml/parse.d.ts.map +1 -0
- package/dist/qml/parse.js +607 -0
- package/dist/qml/parse.js.map +1 -0
- package/dist/qml/qtquick.d.ts +7 -0
- package/dist/qml/qtquick.d.ts.map +1 -0
- package/dist/qml/qtquick.js +347 -0
- package/dist/qml/qtquick.js.map +1 -0
- package/dist/qml/react.d.ts +83 -0
- package/dist/qml/react.d.ts.map +1 -0
- package/dist/qml/react.js +323 -0
- package/dist/qml/react.js.map +1 -0
- package/dist/qml/resolver.d.ts +38 -0
- package/dist/qml/resolver.d.ts.map +1 -0
- package/dist/qml/resolver.js +64 -0
- package/dist/qml/resolver.js.map +1 -0
- package/dist/qml/slots.d.ts +65 -0
- package/dist/qml/slots.d.ts.map +1 -0
- package/dist/qml/slots.js +230 -0
- package/dist/qml/slots.js.map +1 -0
- package/dist/qml/states.d.ts +5 -0
- package/dist/qml/states.d.ts.map +1 -0
- package/dist/qml/states.js +276 -0
- package/dist/qml/states.js.map +1 -0
- package/dist/qml/view-utils.d.ts +42 -0
- package/dist/qml/view-utils.d.ts.map +1 -0
- package/dist/qml/view-utils.js +121 -0
- package/dist/qml/view-utils.js.map +1 -0
- package/dist/reorder/hx.d.ts +18 -0
- package/dist/reorder/hx.d.ts.map +1 -0
- package/dist/reorder/hx.js +10 -0
- package/dist/reorder/hx.js.map +1 -0
- package/dist/reorder/index.d.ts +314 -0
- package/dist/reorder/index.d.ts.map +1 -0
- package/dist/reorder/index.js +1539 -0
- package/dist/reorder/index.js.map +1 -0
- package/dist/reorder/model.d.ts +107 -0
- package/dist/reorder/model.d.ts.map +1 -0
- package/dist/reorder/model.js +162 -0
- package/dist/reorder/model.js.map +1 -0
- package/dist/richtext/node.d.ts +15 -5
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +40 -11
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +35 -13
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +83 -27
- package/dist/richtext/runs.js.map +1 -1
- package/dist/tabs/hx.d.ts +18 -0
- package/dist/tabs/hx.d.ts.map +1 -0
- package/dist/tabs/hx.js +10 -0
- package/dist/tabs/hx.js.map +1 -0
- package/dist/tabs/index.d.ts +160 -0
- package/dist/tabs/index.d.ts.map +1 -0
- package/dist/tabs/index.js +1265 -0
- package/dist/tabs/index.js.map +1 -0
- package/dist/terminal/vt/fonts.d.ts +45 -15
- package/dist/terminal/vt/fonts.d.ts.map +1 -1
- package/dist/terminal/vt/fonts.js +89 -19
- package/dist/terminal/vt/fonts.js.map +1 -1
- package/dist/terminal/vt/index.d.ts +1 -1
- package/dist/terminal/vt/index.d.ts.map +1 -1
- package/dist/terminal/vt/index.js +3 -3
- package/dist/terminal/vt/index.js.map +1 -1
- package/dist/terminal/vt/node.d.ts +42 -0
- package/dist/terminal/vt/node.d.ts.map +1 -1
- package/dist/terminal/vt/node.js +95 -11
- package/dist/terminal/vt/node.js.map +1 -1
- package/dist/terminal/vt/pty.d.ts +16 -0
- package/dist/terminal/vt/pty.d.ts.map +1 -1
- package/dist/terminal/vt/pty.js +186 -0
- package/dist/terminal/vt/pty.js.map +1 -1
- package/dist/terminal/vt/renderer.d.ts.map +1 -1
- package/dist/terminal/vt/renderer.js +13 -3
- package/dist/terminal/vt/renderer.js.map +1 -1
- package/package.json +23 -3
- package/src/charts/components.ts +14 -7
- package/src/charts/node.ts +81 -26
- package/src/charts/render.ts +16 -6
- package/src/code-editor/node.ts +147 -53
- package/src/flow/draw.ts +104 -35
- package/src/flow/index.ts +33 -13
- package/src/flow/model.ts +4 -2
- package/src/flow/node.ts +158 -59
- package/src/flow/types.ts +46 -21
- package/src/formula/index.ts +4 -2
- package/src/formula/node.ts +24 -5
- package/src/html/controls.ts +4 -1
- package/src/html/css/cascade.ts +18 -7
- package/src/html/css/parse.ts +46 -21
- package/src/html/css/style.ts +6 -1
- package/src/html/css/values.ts +25 -9
- package/src/html/index.ts +1 -0
- package/src/html/layout/boxes.ts +19 -9
- package/src/html/layout/inline.ts +23 -1
- package/src/html/node.ts +110 -17
- package/src/html/paint.ts +17 -2
- package/src/index.ts +142 -0
- package/src/maps/clip.ts +184 -0
- package/src/maps/gzip.ts +66 -0
- package/src/maps/index.ts +316 -0
- package/src/maps/labels.ts +442 -0
- package/src/maps/mvt.ts +857 -0
- package/src/maps/node.ts +1819 -0
- package/src/maps/overlay.ts +639 -0
- package/src/maps/paint.ts +978 -0
- package/src/maps/proj.ts +641 -0
- package/src/maps/sources.ts +425 -0
- package/src/maps/style.ts +309 -0
- package/src/maps/styles.ts +1040 -0
- package/src/maps/tiles.ts +676 -0
- package/src/maps/types.ts +260 -0
- package/src/qml/containers.tsx +380 -0
- package/src/qml/controls.tsx +47 -0
- package/src/qml/globals.ts +33 -0
- package/src/qml/index.ts +67 -0
- package/src/qml/interaction.tsx +317 -0
- package/src/qml/ir.ts +80 -0
- package/src/qml/layouts.tsx +141 -0
- package/src/qml/models.ts +101 -0
- package/src/qml/objects.ts +1431 -0
- package/src/qml/parse.ts +624 -0
- package/src/qml/qtquick.tsx +413 -0
- package/src/qml/react.tsx +454 -0
- package/src/qml/resolver.ts +99 -0
- package/src/qml/slots.ts +249 -0
- package/src/qml/states.ts +318 -0
- package/src/qml/view-utils.ts +140 -0
- package/src/reorder/hx.ts +31 -0
- package/src/reorder/index.ts +2223 -0
- package/src/reorder/model.ts +232 -0
- package/src/richtext/node.ts +49 -14
- package/src/richtext/runs.ts +115 -30
- package/src/tabs/hx.ts +31 -0
- package/src/tabs/index.ts +1686 -0
- package/src/terminal/vt/fonts.ts +128 -36
- package/src/terminal/vt/index.ts +4 -2
- package/src/terminal/vt/node.ts +105 -12
- package/src/terminal/vt/pty.ts +243 -0
- package/src/terminal/vt/renderer.ts +13 -3
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// The public vocabulary: what `<Map>` takes, what it hands back, and what
|
|
2
|
+
// its events carry.
|
|
3
|
+
//
|
|
4
|
+
// Separate from `index.ts` for the reason `src/flow/types.ts` is: the
|
|
5
|
+
// element, the component and the tests all name these, and a types module
|
|
6
|
+
// with no runtime in it can be imported by any of them without dragging the
|
|
7
|
+
// others in.
|
|
8
|
+
import type { Style } from 'react-x11/style';
|
|
9
|
+
import type { ReactNode, Ref } from 'react';
|
|
10
|
+
|
|
11
|
+
import type { LngLat, LngLatBounds, MapCamera, TileId } from './proj.js';
|
|
12
|
+
import type { MapSource } from './sources.js';
|
|
13
|
+
import type { MapStyle } from './style.js';
|
|
14
|
+
import type { MapMarker, MapOverlay } from './overlay.js';
|
|
15
|
+
|
|
16
|
+
export type { LngLat, LngLatBounds, MapCamera };
|
|
17
|
+
export type { MapSource, TileData, TileRequest } from './sources.js';
|
|
18
|
+
export type { MapMarker, MapOverlay, OverlayPalette } from './overlay.js';
|
|
19
|
+
export type {
|
|
20
|
+
MapStyle,
|
|
21
|
+
MapStyleLayer,
|
|
22
|
+
MapFilter,
|
|
23
|
+
FillLayer,
|
|
24
|
+
LineLayer,
|
|
25
|
+
CircleLayer,
|
|
26
|
+
SymbolLayer,
|
|
27
|
+
Zoomed,
|
|
28
|
+
} from './style.js';
|
|
29
|
+
|
|
30
|
+
/** Where a pointer event happened, in every space that could be wanted. */
|
|
31
|
+
export interface MapPointerEvent {
|
|
32
|
+
/** Geographic. */
|
|
33
|
+
lngLat: LngLat;
|
|
34
|
+
/** Pane-local logical pixels. */
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
/** The marker under the pointer, when there was one. */
|
|
38
|
+
marker: MapMarker | null;
|
|
39
|
+
/** Modifier state, as the synthetic event reported it. */
|
|
40
|
+
shiftKey: boolean;
|
|
41
|
+
ctrlKey: boolean;
|
|
42
|
+
altKey: boolean;
|
|
43
|
+
metaKey: boolean;
|
|
44
|
+
/** `1` left, `2` middle, `3` right. */
|
|
45
|
+
button: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** What one frame cost — for a performance HUD, and what
|
|
49
|
+
* `scripts/bench/maps.ts` reads. */
|
|
50
|
+
export interface MapFrameStats {
|
|
51
|
+
/** Milliseconds spent rasterizing tiles into their surfaces this frame.
|
|
52
|
+
* Bounded by `rasterBudgetMs` except for the one run that crossed it. */
|
|
53
|
+
rasterMs: number;
|
|
54
|
+
/** …and compositing them, drawing the labels, overlays and markers. */
|
|
55
|
+
drawMs: number;
|
|
56
|
+
/** Tiles the cover asked for. */
|
|
57
|
+
tiles: number;
|
|
58
|
+
/** …of which were composited from their own finished surface. Under
|
|
59
|
+
* `progressive` this also counts the half-drawn ones. */
|
|
60
|
+
ready: number;
|
|
61
|
+
/** …and how many were drawn from a coarser ancestor instead, scaled up.
|
|
62
|
+
* What covers a hole while zooming *in*. */
|
|
63
|
+
fromAncestor: number;
|
|
64
|
+
/** …and how many from their finer descendants, scaled down. What covers
|
|
65
|
+
* a hole while zooming *out*, where there is no ancestor to borrow. */
|
|
66
|
+
fromDescendant: number;
|
|
67
|
+
/** Tiles still to rasterize — non-zero means the map is still sharpening
|
|
68
|
+
* and another frame is already scheduled. */
|
|
69
|
+
pending: number;
|
|
70
|
+
/** Labels placed, and labels drawn. */
|
|
71
|
+
labels: number;
|
|
72
|
+
/**
|
|
73
|
+
* Tiles in the cover whose load failed and are waiting on a retry.
|
|
74
|
+
*
|
|
75
|
+
* Here because a map whose tiles all fail looks exactly like a map that
|
|
76
|
+
* is still loading — an empty background and nothing else — and the
|
|
77
|
+
* difference is not something a user can see.
|
|
78
|
+
*/
|
|
79
|
+
errors: number;
|
|
80
|
+
/** Bytes of rendered surfaces the cache is holding. */
|
|
81
|
+
surfaceBytes: number;
|
|
82
|
+
/**
|
|
83
|
+
* What this pass repainted, in device pixels, or `null` for a full one.
|
|
84
|
+
*
|
|
85
|
+
* The number to watch when a map looks busy: a frame that is only
|
|
86
|
+
* continuing a rasterization should claim almost nothing, because the
|
|
87
|
+
* tile being drawn is a second surface nobody is looking at. A run of
|
|
88
|
+
* full-pane damage while tiles are still landing means something is
|
|
89
|
+
* asking for repaints it does not need — which on a backend that paints
|
|
90
|
+
* many frames a second reads as flashing.
|
|
91
|
+
*/
|
|
92
|
+
damage: { x: number; y: number; width: number; height: number } | null;
|
|
93
|
+
/** What the rasterizer did, summed over the tiles drawn this frame. */
|
|
94
|
+
draw: {
|
|
95
|
+
features: number;
|
|
96
|
+
vertices: number;
|
|
97
|
+
decimated: number;
|
|
98
|
+
culled: number;
|
|
99
|
+
batches: number;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Options for framing a box. */
|
|
104
|
+
export interface FitBoundsOptions {
|
|
105
|
+
/** Logical pixels kept clear on every side. 24 by default. */
|
|
106
|
+
padding?: number;
|
|
107
|
+
maxZoom?: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The imperative surface, through `ref`. */
|
|
111
|
+
export interface MapHandle {
|
|
112
|
+
getCamera(): MapCamera;
|
|
113
|
+
setCamera(camera: Partial<MapCamera>): void;
|
|
114
|
+
panBy(dx: number, dy: number): void;
|
|
115
|
+
zoomIn(step?: number): void;
|
|
116
|
+
zoomOut(step?: number): void;
|
|
117
|
+
zoomTo(zoom: number): void;
|
|
118
|
+
/** Frame a box. */
|
|
119
|
+
fitBounds(bounds: LngLatBounds, options?: FitBoundsOptions): void;
|
|
120
|
+
/** Frame every marker, or the ones named. */
|
|
121
|
+
fitMarkers(ids?: readonly string[], options?: FitBoundsOptions): void;
|
|
122
|
+
/** What the pane can see. `west > east` across the antimeridian. */
|
|
123
|
+
getBounds(): LngLatBounds;
|
|
124
|
+
/** Geography to pane-local logical pixels, and back. */
|
|
125
|
+
project(position: LngLat): { x: number; y: number };
|
|
126
|
+
unproject(x: number, y: number): LngLat;
|
|
127
|
+
/** The marker at a pane-local point, or null. */
|
|
128
|
+
markerAt(x: number, y: number): MapMarker | null;
|
|
129
|
+
/** Throw away every rendered tile — after a style edit an application
|
|
130
|
+
* made in place, which the map cannot see. */
|
|
131
|
+
refresh(): void;
|
|
132
|
+
/** What the last frame cost. */
|
|
133
|
+
stats(): MapFrameStats | null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** What the `<mapview>` element takes. `<Map>` adds the uncontrolled
|
|
137
|
+
* camera and the box around it. */
|
|
138
|
+
export interface MapViewProps {
|
|
139
|
+
/**
|
|
140
|
+
* Where tiles come from, drawn in order — a basemap, then an overlay
|
|
141
|
+
* pyramid over it. Empty draws the style's background and nothing else,
|
|
142
|
+
* which is what a map with only markers on it wants.
|
|
143
|
+
*/
|
|
144
|
+
sources?: readonly MapSource[];
|
|
145
|
+
/** How to draw them. Defaults to {@link shortbreadStyle} in the theme's
|
|
146
|
+
* light or dark palette. */
|
|
147
|
+
mapStyle?: MapStyle;
|
|
148
|
+
/**
|
|
149
|
+
* The camera, controlled. Leave it out and the **element** owns it —
|
|
150
|
+
* which is not just a convenience: an element-owned camera means a pan is
|
|
151
|
+
* a blit and a claim, with no React render in the loop at all.
|
|
152
|
+
*/
|
|
153
|
+
camera?: MapCamera;
|
|
154
|
+
/** Where an element-owned camera starts. Read once, at construction. */
|
|
155
|
+
defaultCamera?: MapCamera;
|
|
156
|
+
onCameraChange?: (camera: MapCamera) => void;
|
|
157
|
+
/** Fired when a gesture ends and the camera has settled — the moment to
|
|
158
|
+
* fetch what is now on screen. */
|
|
159
|
+
onMoveEnd?: (camera: MapCamera) => void;
|
|
160
|
+
minZoom?: number;
|
|
161
|
+
maxZoom?: number;
|
|
162
|
+
markers?: readonly MapMarker[];
|
|
163
|
+
overlays?: readonly MapOverlay[];
|
|
164
|
+
onMapClick?: (event: MapPointerEvent) => void;
|
|
165
|
+
onMarkerClick?: (marker: MapMarker, event: MapPointerEvent) => void;
|
|
166
|
+
/**
|
|
167
|
+
* The marker under the pointer, or null when it left every marker.
|
|
168
|
+
*
|
|
169
|
+
* `event` is null for the leave that comes from the pointer leaving the
|
|
170
|
+
* map altogether: there is no position on the map to report, and
|
|
171
|
+
* inventing one would be worse than saying so.
|
|
172
|
+
*/
|
|
173
|
+
onMarkerHover?: (
|
|
174
|
+
marker: MapMarker | null,
|
|
175
|
+
event: MapPointerEvent | null,
|
|
176
|
+
) => void;
|
|
177
|
+
/** `false` freezes the camera: no drag, no wheel, no keys. The map still
|
|
178
|
+
* draws and still reports clicks. */
|
|
179
|
+
interactive?: boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Show a tile as it is drawn rather than when it is finished.
|
|
182
|
+
*
|
|
183
|
+
* `false` by default, which is what every other map client does: a tile
|
|
184
|
+
* appears whole, and until it does the coarser one already in the cache
|
|
185
|
+
* is scaled up in its place. `true` composites a tile's surface as soon
|
|
186
|
+
* as it exists — so a dense tile arrives as water, then landuse, then
|
|
187
|
+
* road casings, then roads, across a dozen frames — which is honest about
|
|
188
|
+
* what the renderer is doing and does not look like a map.
|
|
189
|
+
*
|
|
190
|
+
* The one thing the default costs is a transition that invalidates every
|
|
191
|
+
* surface at once: a `mapStyle` change, `refresh()`, or a display-scale
|
|
192
|
+
* change leaves no finished tile *and* no finished ancestor, so the map
|
|
193
|
+
* drops to its background colour until the new tiles land. `true` shows
|
|
194
|
+
* it repainting instead.
|
|
195
|
+
*/
|
|
196
|
+
progressive?: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Milliseconds a frame may spend rasterizing tiles. 8 by default, which
|
|
199
|
+
* leaves the rest of a 60 Hz frame for everything else; a tile that takes
|
|
200
|
+
* longer than the budget is finished over the frames after it. `0`
|
|
201
|
+
* suspends rasterization, which is what a gesture does on its own.
|
|
202
|
+
*/
|
|
203
|
+
rasterBudgetMs?: number;
|
|
204
|
+
/**
|
|
205
|
+
* Device pixels per logical pixel for the tile surfaces, if not the
|
|
206
|
+
* display's.
|
|
207
|
+
*
|
|
208
|
+
* Lowering it to 1 on a retina panel makes rasterization about 1.6×
|
|
209
|
+
* quicker and the basemap correspondingly softer; labels, markers and
|
|
210
|
+
* overlays are unaffected, since none of them goes through a tile
|
|
211
|
+
* surface. Worth having on a slow machine, and worth knowing about before
|
|
212
|
+
* reaching for it.
|
|
213
|
+
*/
|
|
214
|
+
rasterScale?: number;
|
|
215
|
+
/** Bytes of rendered tile surfaces to keep. 128 MB by default. */
|
|
216
|
+
surfaceBudget?: number;
|
|
217
|
+
/**
|
|
218
|
+
* Vertices after which the rasterizer flushes its path. 12,000 by
|
|
219
|
+
* default.
|
|
220
|
+
*
|
|
221
|
+
* A real trade on X11, where a fill becomes one coverage-mask upload and
|
|
222
|
+
* a bigger path is fewer of them over the same pixels. It used to be a
|
|
223
|
+
* per-backend trade — `CGContextStrokePath` was quadratic in the number
|
|
224
|
+
* of subpaths, so the Cocoa backend wanted the opposite value and this
|
|
225
|
+
* element probed for it — and react-x11 2.6.1 chunks that stroke inside
|
|
226
|
+
* the backend instead (react-x11#457). Set it only with a profile in
|
|
227
|
+
* hand.
|
|
228
|
+
*/
|
|
229
|
+
batchVertices?: number;
|
|
230
|
+
/** What the licence requires, drawn in the corner. Taken from the
|
|
231
|
+
* sources when they carry one; `''` removes it, which is the
|
|
232
|
+
* application saying it has put the attribution somewhere else. */
|
|
233
|
+
attribution?: string;
|
|
234
|
+
/** Called once per painted frame with what it cost. */
|
|
235
|
+
onFrame?: (stats: MapFrameStats) => void;
|
|
236
|
+
/**
|
|
237
|
+
* Called once per failed tile load, with whatever the source threw.
|
|
238
|
+
*
|
|
239
|
+
* Nothing is drawn for a failed tile, so without this a source that is
|
|
240
|
+
* misconfigured, rate-limited or down is indistinguishable from one that
|
|
241
|
+
* is slow. The tile is retried on a backoff (0.5 s doubling to 30 s), so
|
|
242
|
+
* this fires again for each retry rather than once and forever.
|
|
243
|
+
*/
|
|
244
|
+
onTileError?: (error: unknown, tile: TileId & { sourceId: string }) => void;
|
|
245
|
+
style?: Style;
|
|
246
|
+
role?: string;
|
|
247
|
+
'aria-label'?: string;
|
|
248
|
+
'data-testname'?: string;
|
|
249
|
+
ref?: Ref<unknown>;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** What `<Map>` takes. */
|
|
253
|
+
export interface MapProps extends Omit<MapViewProps, 'ref'> {
|
|
254
|
+
ref?: Ref<MapHandle>;
|
|
255
|
+
/** Anything absolutely positioned over the map — a legend, a control
|
|
256
|
+
* panel. Laid out as siblings of the drawn pane rather than inside it,
|
|
257
|
+
* because a registered element paints its children *before* its own
|
|
258
|
+
* drawing and anything inside would be painted over. */
|
|
259
|
+
children?: ReactNode;
|
|
260
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
// Containers: the positioners (bindings the Row installs on its children),
|
|
2
|
+
// Repeater, Loader, and the scroll pair — Flickable over core's
|
|
3
|
+
// `overflow: 'scroll'` box, ListView over Flickable with windowed
|
|
4
|
+
// delegates: only the visible range (plus a buffer) is instantiated, and
|
|
5
|
+
// scrolling moves the window.
|
|
6
|
+
|
|
7
|
+
import { useEffect } from 'react';
|
|
8
|
+
import type { ReactElement } from 'react';
|
|
9
|
+
import type { ScrollableNode } from 'react-x11';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
QmlInstance,
|
|
13
|
+
instantiateComponentDoc,
|
|
14
|
+
instantiateTemplate,
|
|
15
|
+
resolveComponentByPath,
|
|
16
|
+
type QmlTypeDef,
|
|
17
|
+
} from './objects.js';
|
|
18
|
+
import { renderQmlChildren, geometryStyle } from './react.js';
|
|
19
|
+
import {
|
|
20
|
+
applyMotion,
|
|
21
|
+
captureNode,
|
|
22
|
+
hostNode,
|
|
23
|
+
initItem,
|
|
24
|
+
num,
|
|
25
|
+
} from './view-utils.js';
|
|
26
|
+
import { delegateExtras, resolveModel } from './models.js';
|
|
27
|
+
import { warn } from './globals.js';
|
|
28
|
+
|
|
29
|
+
// --- positioners -----------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
function positionerLayout(inst: QmlInstance): void {
|
|
32
|
+
const horizontal = inst.typeInfo.name === 'Row';
|
|
33
|
+
const main = horizontal ? 'x' : 'y';
|
|
34
|
+
const mainSize = horizontal ? 'width' : 'height';
|
|
35
|
+
const crossSize = horizontal ? 'height' : 'width';
|
|
36
|
+
for (const kid of inst.visualChildren()) {
|
|
37
|
+
kid.slot(main).setBinding(() => {
|
|
38
|
+
let pos = 0;
|
|
39
|
+
const spacing = num(inst.slot('spacing').get());
|
|
40
|
+
for (const prev of inst.visualChildren()) {
|
|
41
|
+
if (prev === kid) break;
|
|
42
|
+
if (prev.slot('visible').get() === false) continue;
|
|
43
|
+
pos += num(prev.slot(mainSize).get()) + spacing;
|
|
44
|
+
}
|
|
45
|
+
return pos;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const extent = (): number => {
|
|
49
|
+
let sum = 0;
|
|
50
|
+
let count = 0;
|
|
51
|
+
const spacing = num(inst.slot('spacing').get());
|
|
52
|
+
for (const kid of inst.visualChildren()) {
|
|
53
|
+
if (kid.slot('visible').get() === false) continue;
|
|
54
|
+
sum += num(kid.slot(mainSize).get());
|
|
55
|
+
count++;
|
|
56
|
+
}
|
|
57
|
+
return sum + spacing * Math.max(0, count - 1);
|
|
58
|
+
};
|
|
59
|
+
const crossExtent = (): number => {
|
|
60
|
+
let max = 0;
|
|
61
|
+
for (const kid of inst.visualChildren()) {
|
|
62
|
+
if (kid.slot('visible').get() === false) continue;
|
|
63
|
+
max = Math.max(max, num(kid.slot(crossSize).get()));
|
|
64
|
+
}
|
|
65
|
+
return max;
|
|
66
|
+
};
|
|
67
|
+
inst.slot(horizontal ? 'implicitWidth' : 'implicitHeight').setBinding(extent);
|
|
68
|
+
inst
|
|
69
|
+
.slot(horizontal ? 'implicitHeight' : 'implicitWidth')
|
|
70
|
+
.setBinding(crossExtent);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function positionerInit(inst: QmlInstance): void {
|
|
74
|
+
initItem(inst);
|
|
75
|
+
positionerLayout(inst);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// --- Repeater --------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
interface RepeaterState {
|
|
81
|
+
items?: QmlInstance[];
|
|
82
|
+
revOff?: (() => void) | null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function repeaterRebuild(inst: QmlInstance): void {
|
|
86
|
+
const parent = inst.parentInst;
|
|
87
|
+
const tpl = inst.templates.get('delegate');
|
|
88
|
+
if (!parent || !tpl) return;
|
|
89
|
+
const state = inst.state as RepeaterState;
|
|
90
|
+
for (const item of state.items ?? []) {
|
|
91
|
+
const at = parent.children.indexOf(item);
|
|
92
|
+
if (at >= 0) parent.children.splice(at, 1);
|
|
93
|
+
item.destroy();
|
|
94
|
+
}
|
|
95
|
+
const { rows } = resolveModel(inst.slot('model').peek());
|
|
96
|
+
const items = rows.map(
|
|
97
|
+
(_, index) =>
|
|
98
|
+
instantiateTemplate(tpl, parent, delegateExtras(rows, index)).inst,
|
|
99
|
+
);
|
|
100
|
+
const at = parent.children.indexOf(inst);
|
|
101
|
+
parent.children.splice(at + 1, 0, ...items);
|
|
102
|
+
state.items = items;
|
|
103
|
+
inst.slot('count').assign(items.length);
|
|
104
|
+
parent._structChanged();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function repeaterModelChanged(inst: QmlInstance): void {
|
|
108
|
+
const state = inst.state as RepeaterState;
|
|
109
|
+
state.revOff?.();
|
|
110
|
+
state.revOff = null;
|
|
111
|
+
const { revSlot } = resolveModel(inst.slot('model').peek());
|
|
112
|
+
if (revSlot) state.revOff = revSlot.watch(() => repeaterRebuild(inst));
|
|
113
|
+
repeaterRebuild(inst);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// --- Flickable -------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
function FlickableView({ inst }: { inst: QmlInstance }): ReactElement {
|
|
119
|
+
const style = geometryStyle(inst);
|
|
120
|
+
style.overflow = 'scroll';
|
|
121
|
+
applyMotion(inst, style);
|
|
122
|
+
const cw = num(inst.slot('contentWidth').peek());
|
|
123
|
+
const ch = num(inst.slot('contentHeight').peek());
|
|
124
|
+
const cx = num(inst.slot('contentX').peek());
|
|
125
|
+
const cy = num(inst.slot('contentY').peek());
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
const node = hostNode(inst) as ScrollableNode | null;
|
|
128
|
+
if (!node || typeof node.scrollTo !== 'function') return;
|
|
129
|
+
if (node.scrollX !== cx || node.scrollY !== cy)
|
|
130
|
+
node.scrollTo({ x: cx, y: cy });
|
|
131
|
+
}, [inst, cx, cy]);
|
|
132
|
+
return (
|
|
133
|
+
<box
|
|
134
|
+
ref={captureNode(inst)}
|
|
135
|
+
style={style}
|
|
136
|
+
onScroll={(ev: { scrollX: number; scrollY: number }) => {
|
|
137
|
+
inst.slot('contentX').assign(ev.scrollX);
|
|
138
|
+
inst.slot('contentY').assign(ev.scrollY);
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
<box
|
|
142
|
+
style={{
|
|
143
|
+
position: 'relative',
|
|
144
|
+
width: cw || undefined,
|
|
145
|
+
height: ch || undefined,
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
{renderQmlChildren(inst)}
|
|
149
|
+
</box>
|
|
150
|
+
</box>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// --- ListView --------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
interface ListState {
|
|
157
|
+
items?: Map<number, QmlInstance>;
|
|
158
|
+
revOff?: (() => void) | null;
|
|
159
|
+
measuredWatch?: boolean;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function listViewInit(lv: QmlInstance): void {
|
|
163
|
+
initItem(lv);
|
|
164
|
+
const state = lv.state as ListState;
|
|
165
|
+
const items = new Map<number, QmlInstance>();
|
|
166
|
+
state.items = items;
|
|
167
|
+
|
|
168
|
+
const destroyItem = (i: number): void => {
|
|
169
|
+
const it = items.get(i);
|
|
170
|
+
if (!it) return;
|
|
171
|
+
const at = lv.children.indexOf(it);
|
|
172
|
+
if (at >= 0) lv.children.splice(at, 1);
|
|
173
|
+
it.destroy();
|
|
174
|
+
items.delete(i);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const update = (): void => {
|
|
178
|
+
if (lv.destroyed) return;
|
|
179
|
+
const tpl = lv.templates.get('delegate');
|
|
180
|
+
if (!tpl) return;
|
|
181
|
+
const { rows } = resolveModel(lv.slot('model').peek());
|
|
182
|
+
const spacing = num(lv.slot('spacing').peek());
|
|
183
|
+
const count = rows.length;
|
|
184
|
+
for (const i of [...items.keys()]) if (i >= count) destroyItem(i);
|
|
185
|
+
if (count === 0) {
|
|
186
|
+
lv.slot('contentHeight').assign(0);
|
|
187
|
+
lv._structChanged();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const ensure = (i: number): QmlInstance => {
|
|
191
|
+
let it = items.get(i);
|
|
192
|
+
if (!it) {
|
|
193
|
+
it = instantiateTemplate(tpl, lv, delegateExtras(rows, i)).inst;
|
|
194
|
+
items.set(i, it);
|
|
195
|
+
lv.children.push(it);
|
|
196
|
+
}
|
|
197
|
+
return it;
|
|
198
|
+
};
|
|
199
|
+
const first = ensure(0);
|
|
200
|
+
if (!state.measuredWatch) {
|
|
201
|
+
state.measuredWatch = true;
|
|
202
|
+
first.slot('height').watch(update); // implicit sizes settle late
|
|
203
|
+
}
|
|
204
|
+
const h = num(first.slot('height').peek());
|
|
205
|
+
if (!h) {
|
|
206
|
+
// Nothing measurable yet: stack whatever heights the delegates say.
|
|
207
|
+
let y = 0;
|
|
208
|
+
for (let i = 0; i < count; i++) {
|
|
209
|
+
const it = ensure(i);
|
|
210
|
+
it.slot('y').assign(y);
|
|
211
|
+
y += num(it.slot('height').peek()) + spacing;
|
|
212
|
+
}
|
|
213
|
+
lv.slot('contentHeight').assign(Math.max(0, y - spacing));
|
|
214
|
+
} else {
|
|
215
|
+
// Uniform rows (the first row's height): window the range.
|
|
216
|
+
const stride = h + spacing;
|
|
217
|
+
const viewH = num(lv.slot('height').peek());
|
|
218
|
+
const cy = num(lv.slot('contentY').peek());
|
|
219
|
+
const buffer = Math.max(2, Math.ceil((viewH || stride) / stride));
|
|
220
|
+
const start = Math.max(0, Math.floor(cy / stride) - buffer);
|
|
221
|
+
const end = Math.min(
|
|
222
|
+
count - 1,
|
|
223
|
+
Math.ceil((cy + (viewH || stride)) / stride) + buffer,
|
|
224
|
+
);
|
|
225
|
+
for (const i of [...items.keys()]) {
|
|
226
|
+
if (i !== 0 && (i < start || i > end)) destroyItem(i);
|
|
227
|
+
}
|
|
228
|
+
for (let i = start; i <= end; i++)
|
|
229
|
+
ensure(i)
|
|
230
|
+
.slot('y')
|
|
231
|
+
.assign(i * stride);
|
|
232
|
+
first.slot('y').assign(0);
|
|
233
|
+
lv.slot('contentHeight').assign(count * stride - spacing);
|
|
234
|
+
}
|
|
235
|
+
lv._structChanged();
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const fullRebuild = (): void => {
|
|
239
|
+
for (const i of [...items.keys()]) destroyItem(i);
|
|
240
|
+
state.measuredWatch = false;
|
|
241
|
+
update();
|
|
242
|
+
};
|
|
243
|
+
const modelChanged = (): void => {
|
|
244
|
+
state.revOff?.();
|
|
245
|
+
state.revOff = null;
|
|
246
|
+
const { revSlot } = resolveModel(lv.slot('model').peek());
|
|
247
|
+
if (revSlot) state.revOff = revSlot.watch(fullRebuild);
|
|
248
|
+
fullRebuild();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
lv.slot('model').watch(modelChanged);
|
|
252
|
+
lv.slot('contentY').watch(update);
|
|
253
|
+
lv.slot('height').watch(update);
|
|
254
|
+
lv.slot('spacing').watch(fullRebuild);
|
|
255
|
+
modelChanged();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// --- Loader ----------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
function loaderInit(ld: QmlInstance): void {
|
|
261
|
+
initItem(ld);
|
|
262
|
+
let child: QmlInstance | null = null;
|
|
263
|
+
const sync = (): void => {
|
|
264
|
+
if (child) {
|
|
265
|
+
const at = ld.children.indexOf(child);
|
|
266
|
+
if (at >= 0) ld.children.splice(at, 1);
|
|
267
|
+
child.destroy();
|
|
268
|
+
child = null;
|
|
269
|
+
ld.slot('item').assign(null);
|
|
270
|
+
ld.slot('implicitWidth').assign(0);
|
|
271
|
+
ld.slot('implicitHeight').assign(0);
|
|
272
|
+
}
|
|
273
|
+
const tpl = ld.templates.get('sourceComponent');
|
|
274
|
+
const sourcePath = String(ld.slot('source').peek() ?? '');
|
|
275
|
+
const active = ld.slot('active').peek() !== false;
|
|
276
|
+
let loaded: QmlInstance | null = null;
|
|
277
|
+
if (!ld.destroyed && active && tpl) {
|
|
278
|
+
loaded = instantiateTemplate(tpl, ld).inst;
|
|
279
|
+
} else if (!ld.destroyed && active && sourcePath) {
|
|
280
|
+
// `source: "widgets/Meter.qml"` — a component document through the
|
|
281
|
+
// resolver seam, relative to the document that names it.
|
|
282
|
+
const compDoc = resolveComponentByPath(ld.doc, sourcePath);
|
|
283
|
+
if (compDoc) {
|
|
284
|
+
loaded = instantiateComponentDoc(compDoc, ld.context, ld).inst;
|
|
285
|
+
} else {
|
|
286
|
+
warn(
|
|
287
|
+
`QML: Loader.source '${sourcePath}' did not resolve — ` +
|
|
288
|
+
(ld.doc.load
|
|
289
|
+
? 'no such .qml file relative to this document.'
|
|
290
|
+
: 'pass a resolver (createFileResolver) to QmlView.'),
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (loaded) {
|
|
295
|
+
child = loaded;
|
|
296
|
+
const item = loaded;
|
|
297
|
+
ld.children.push(item);
|
|
298
|
+
ld.slot('item').assign(item.facade);
|
|
299
|
+
ld.slot('implicitWidth').setBinding(() => num(item.slot('width').get()));
|
|
300
|
+
ld.slot('implicitHeight').setBinding(() =>
|
|
301
|
+
num(item.slot('height').get()),
|
|
302
|
+
);
|
|
303
|
+
ld.emit('loaded');
|
|
304
|
+
}
|
|
305
|
+
ld._structChanged();
|
|
306
|
+
};
|
|
307
|
+
ld.slot('active').watch(sync);
|
|
308
|
+
ld.slot('source').watch(sync);
|
|
309
|
+
sync();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// --- the type definitions --------------------------------------------------
|
|
313
|
+
|
|
314
|
+
export const containerTypes: Record<string, QmlTypeDef> = {
|
|
315
|
+
Row: {
|
|
316
|
+
extends: 'Item',
|
|
317
|
+
properties: { spacing: { default: 0 } },
|
|
318
|
+
init: positionerInit,
|
|
319
|
+
onStructure: positionerLayout,
|
|
320
|
+
},
|
|
321
|
+
Column: {
|
|
322
|
+
extends: 'Item',
|
|
323
|
+
properties: { spacing: { default: 0 } },
|
|
324
|
+
init: positionerInit,
|
|
325
|
+
onStructure: positionerLayout,
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
Repeater: {
|
|
329
|
+
nonVisual: true,
|
|
330
|
+
capture: 'delegate',
|
|
331
|
+
properties: { model: { default: 0 }, count: { default: 0 } },
|
|
332
|
+
init(inst) {
|
|
333
|
+
inst.slot('model').watch(() => repeaterModelChanged(inst));
|
|
334
|
+
repeaterModelChanged(inst);
|
|
335
|
+
},
|
|
336
|
+
dispose(inst) {
|
|
337
|
+
const state = inst.state as RepeaterState;
|
|
338
|
+
state.revOff?.();
|
|
339
|
+
for (const item of state.items ?? []) item.destroy();
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
Flickable: {
|
|
344
|
+
extends: 'Item',
|
|
345
|
+
view: FlickableView,
|
|
346
|
+
properties: {
|
|
347
|
+
contentWidth: { default: 0 },
|
|
348
|
+
contentHeight: { default: 0 },
|
|
349
|
+
contentX: { default: 0 },
|
|
350
|
+
contentY: { default: 0 },
|
|
351
|
+
},
|
|
352
|
+
init: initItem,
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
ListView: {
|
|
356
|
+
extends: 'Flickable',
|
|
357
|
+
capture: 'delegate',
|
|
358
|
+
properties: {
|
|
359
|
+
model: { default: 0 },
|
|
360
|
+
spacing: { default: 0 },
|
|
361
|
+
},
|
|
362
|
+
init: listViewInit,
|
|
363
|
+
dispose(lv) {
|
|
364
|
+
const state = lv.state as ListState;
|
|
365
|
+
state.revOff?.();
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
Loader: {
|
|
370
|
+
extends: 'Item',
|
|
371
|
+
capture: 'sourceComponent',
|
|
372
|
+
properties: {
|
|
373
|
+
active: { default: true },
|
|
374
|
+
item: { default: null },
|
|
375
|
+
source: { default: '' },
|
|
376
|
+
},
|
|
377
|
+
signals: { loaded: [] },
|
|
378
|
+
init: loaderInit,
|
|
379
|
+
},
|
|
380
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// `import QtQuick.Controls` — the beginnings of the Controls story:
|
|
2
|
+
// widgets react-x11 already ships, registered as QML types. The widgets
|
|
3
|
+
// are passed in rather than imported here, so the family costs nothing
|
|
4
|
+
// when Controls are unused and the app decides which widget set answers
|
|
5
|
+
// (core's `Button`, or its own).
|
|
6
|
+
|
|
7
|
+
import type { ComponentType } from 'react';
|
|
8
|
+
import { registerQmlModule } from './objects.js';
|
|
9
|
+
import { geometryStyle } from './react.js';
|
|
10
|
+
import { str } from './view-utils.js';
|
|
11
|
+
|
|
12
|
+
export interface ControlsWidgets {
|
|
13
|
+
/** A pressable taking `primary`, `disabled`, `onPress` and children —
|
|
14
|
+
* core's `Button` as it stands. Deliberately `ComponentType<any>`:
|
|
15
|
+
* React's `propTypes`/`defaultProps` members make component types
|
|
16
|
+
* near-invariant in their props, so a props-typed seam rejects any
|
|
17
|
+
* richer button; `any` is the honest edge (eslint does not see
|
|
18
|
+
* TypeScript here — AGENTS.md, "Linting"). */
|
|
19
|
+
Button: ComponentType<any>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function registerControls({ Button }: ControlsWidgets): void {
|
|
23
|
+
registerQmlModule('QtQuick.Controls', {
|
|
24
|
+
version: '2.0',
|
|
25
|
+
types: {
|
|
26
|
+
Button: {
|
|
27
|
+
extends: 'Item',
|
|
28
|
+
properties: {
|
|
29
|
+
text: { default: '' },
|
|
30
|
+
primary: { default: false },
|
|
31
|
+
},
|
|
32
|
+
signals: { clicked: [] },
|
|
33
|
+
view: ({ inst }) => (
|
|
34
|
+
<box style={geometryStyle(inst)}>
|
|
35
|
+
<Button
|
|
36
|
+
primary={inst.slot('primary').peek() === true}
|
|
37
|
+
disabled={inst.slot('enabled').peek() === false}
|
|
38
|
+
onPress={() => inst.emit('clicked')}
|
|
39
|
+
>
|
|
40
|
+
{str(inst.slot('text').peek())}
|
|
41
|
+
</Button>
|
|
42
|
+
</box>
|
|
43
|
+
),
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|