@solidrt/core 0.0.54 → 0.0.56
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/AGENTS.md +100 -22
- package/agents/painting.md +36 -12
- package/agents/performance.md +36 -6
- package/docs/reference/detached.md +20 -10
- package/docs/reference/drawing.md +10 -3
- package/docs/reference/elements.md +3 -1
- package/docs/reference/gpu.md +12 -1
- package/docs/reference/layout.md +28 -0
- package/docs/reference/text.md +34 -0
- package/examples/README.md +5 -0
- package/examples/detached-positioning.tsx +3 -1
- package/examples/line-points.tsx +21 -0
- package/examples/stagger.tsx +45 -0
- package/examples/text-flow.tsx +90 -0
- package/examples/text-glyphs.tsx +73 -0
- package/examples/view-design-size.tsx +5 -0
- package/package.json +6 -5
- package/src/color.ts +17 -0
- package/src/core.ts +28 -7
- package/src/environment.ts +87 -69
- package/src/gamepad.ts +10 -8
- package/src/gpu.ts +130 -21
- package/src/index.ts +14 -4
- package/src/pan.ts +20 -5
- package/src/renderer.ts +16 -2
- package/src/runtime-modules.d.ts +7 -7
- package/src/svg.ts +3 -2
- package/src/text-input.ts +43 -8
- package/src/transform.ts +52 -23
- package/src/types.d.ts +158 -44
- package/src/window.ts +54 -38
package/src/window.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSignal, onCleanup, onSettled, flush } from "@solidjs/signals"
|
|
1
|
+
import { createSignal, getOwner, onCleanup, onSettled, runWithOwner, flush } from "@solidjs/signals"
|
|
2
2
|
import { requestFrame, setPointerLock } from "flux:rendertree"
|
|
3
3
|
import { renderFrame } from "srt:render"
|
|
4
4
|
import { on, once } from "srt:events"
|
|
@@ -7,8 +7,8 @@ import { getEventHandler, focusedNode, setFocus, activateTextInput, setInterestR
|
|
|
7
7
|
import { scanForOrphans, getNodePath } from "./renderer"
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Leaves the current app, unconditionally: back to the
|
|
11
|
-
* client, quitting when standalone or at the
|
|
10
|
+
* Leaves the current app, unconditionally: back to the player in a dev
|
|
11
|
+
* client, quitting when standalone or at the player itself (on Android the
|
|
12
12
|
* client backgrounds instead of dying). The default action of an unprevented
|
|
13
13
|
* `back` event; call it directly to exit programmatically, e.g. after
|
|
14
14
|
* intercepting back for an unsaved-changes dialog.
|
|
@@ -44,7 +44,10 @@ let refreshRate = 60
|
|
|
44
44
|
* control, but measure their delays against the wall clock, not this paced
|
|
45
45
|
* timeline. performance.now() is not on it either: it is real elapsed time
|
|
46
46
|
* for measuring work; Date.now() is calendar time.
|
|
47
|
-
* Returns a cleanup function; also auto-cleans within
|
|
47
|
+
* Returns a cleanup function; also auto-cleans within an owned scope. Outside
|
|
48
|
+
* one - an effect's apply phase, an event handler - the returned cleanup is
|
|
49
|
+
* the only handle, so return it from the apply (`return onFrame(...)`) to run
|
|
50
|
+
* a loop only while a condition holds.
|
|
48
51
|
*/
|
|
49
52
|
export function onFrame(fn: (tick: number, frame: number, rate: number) => void) {
|
|
50
53
|
let frameId: number = null!
|
|
@@ -77,7 +80,7 @@ export function onFrame(fn: (tick: number, frame: number, rate: number) => void)
|
|
|
77
80
|
cancelled = true
|
|
78
81
|
animationFrames.delete(frameId)
|
|
79
82
|
}
|
|
80
|
-
onCleanup(cleanup)
|
|
83
|
+
if (getOwner()) onCleanup(cleanup)
|
|
81
84
|
return cleanup
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -101,7 +104,7 @@ interface ResizeEvent {
|
|
|
101
104
|
|
|
102
105
|
export function onResize(fn: (data: ResizeEvent) => void) {
|
|
103
106
|
let unsubscribe = on("resize", fn)
|
|
104
|
-
onCleanup(unsubscribe)
|
|
107
|
+
if (getOwner()) onCleanup(unsubscribe)
|
|
105
108
|
return unsubscribe
|
|
106
109
|
}
|
|
107
110
|
|
|
@@ -110,9 +113,13 @@ export function onResize(fn: (data: ResizeEvent) => void) {
|
|
|
110
113
|
// Singleton accessors over the same events as onResize / onWindowFocus. There
|
|
111
114
|
// is one window, so these are bare accessors rather than a createX instance.
|
|
112
115
|
// Lazily subscribed on first read (resize is sticky, so the first read sees the
|
|
113
|
-
// current value); app-lifetime, so no onCleanup.
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
+
// current value); app-lifetime, so no onCleanup. Each is created under no
|
|
117
|
+
// owner (runWithOwner(null)): the first read is usually a component or a
|
|
118
|
+
// computation, and a sticky event replays its cached value synchronously
|
|
119
|
+
// inside on(), so creating the signal in the reader's scope would write it
|
|
120
|
+
// there too - a render-time write, which the guard rejects. Detached, the
|
|
121
|
+
// fact belongs to the app, not to whoever read it first (the reasoning
|
|
122
|
+
// in full is on the ensure* functions in environment.ts).
|
|
116
123
|
|
|
117
124
|
let sizeAccessor: (() => { width: number; height: number }) | undefined
|
|
118
125
|
let safeAreaAccessor: (() => SafeArea) | undefined
|
|
@@ -120,17 +127,19 @@ let displayScaleAccessor: (() => number) | undefined
|
|
|
120
127
|
|
|
121
128
|
function ensureResizeState() {
|
|
122
129
|
if (sizeAccessor) return
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
runWithOwner(null, () => {
|
|
131
|
+
let [size, setSize] = createSignal({ width: 0, height: 0 })
|
|
132
|
+
let [safe, setSafe] = createSignal<SafeArea>({ top: 0, left: 0, right: 0, bottom: 0 })
|
|
133
|
+
let [scale, setScale] = createSignal(1)
|
|
134
|
+
on("resize", (e: ResizeEvent) => {
|
|
135
|
+
setSize({ width: e.width, height: e.height })
|
|
136
|
+
setSafe(e.safeArea)
|
|
137
|
+
setScale(e.displayScale)
|
|
138
|
+
})
|
|
139
|
+
sizeAccessor = size
|
|
140
|
+
safeAreaAccessor = safe
|
|
141
|
+
displayScaleAccessor = scale
|
|
130
142
|
})
|
|
131
|
-
sizeAccessor = size
|
|
132
|
-
safeAreaAccessor = safe
|
|
133
|
-
displayScaleAccessor = scale
|
|
134
143
|
}
|
|
135
144
|
|
|
136
145
|
/** Current window size, as a reactive accessor. */
|
|
@@ -156,12 +165,14 @@ let focusedAccessor: (() => boolean) | undefined
|
|
|
156
165
|
/** Whether the window currently has focus, as a reactive accessor. */
|
|
157
166
|
export function windowFocused(): boolean {
|
|
158
167
|
if (!focusedAccessor) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
168
|
+
runWithOwner(null, () => {
|
|
169
|
+
let [focused, setFocused] = createSignal(true)
|
|
170
|
+
on("windowFocus", () => setFocused(true))
|
|
171
|
+
on("windowBlur", () => setFocused(false))
|
|
172
|
+
focusedAccessor = focused
|
|
173
|
+
})
|
|
163
174
|
}
|
|
164
|
-
return focusedAccessor()
|
|
175
|
+
return focusedAccessor!()
|
|
165
176
|
}
|
|
166
177
|
|
|
167
178
|
/**
|
|
@@ -183,12 +194,14 @@ let pointerLockedAccessor: (() => boolean) | undefined
|
|
|
183
194
|
/** Whether the pointer is currently locked (relative mouse mode), as a reactive accessor. */
|
|
184
195
|
export function pointerLocked(): boolean {
|
|
185
196
|
if (!pointerLockedAccessor) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
runWithOwner(null, () => {
|
|
198
|
+
let [locked, setLocked] = createSignal(false)
|
|
199
|
+
// Sticky event: a subscriber after the lock still observes the state.
|
|
200
|
+
on("pointerLock", ({ locked }: { locked: boolean }) => setLocked(locked))
|
|
201
|
+
pointerLockedAccessor = locked
|
|
202
|
+
})
|
|
190
203
|
}
|
|
191
|
-
return pointerLockedAccessor()
|
|
204
|
+
return pointerLockedAccessor!()
|
|
192
205
|
}
|
|
193
206
|
|
|
194
207
|
let keyboardHeightAccessor: (() => number) | undefined
|
|
@@ -201,11 +214,13 @@ let keyboardHeightAccessor: (() => number) | undefined
|
|
|
201
214
|
*/
|
|
202
215
|
export function keyboardHeight(): number {
|
|
203
216
|
if (!keyboardHeightAccessor) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
217
|
+
runWithOwner(null, () => {
|
|
218
|
+
let [height, setHeight] = createSignal(0)
|
|
219
|
+
on("keyboardVisibility", ({ height: h }: { height: number }) => setHeight(h ?? 0))
|
|
220
|
+
keyboardHeightAccessor = height
|
|
221
|
+
})
|
|
207
222
|
}
|
|
208
|
-
return keyboardHeightAccessor()
|
|
223
|
+
return keyboardHeightAccessor!()
|
|
209
224
|
}
|
|
210
225
|
|
|
211
226
|
// Post-layout handlers run in registration order from one bus subscription,
|
|
@@ -251,19 +266,19 @@ export function onLayout(fn: () => void) {
|
|
|
251
266
|
let i = layoutHandlers.indexOf(fn)
|
|
252
267
|
if (i >= 0) layoutHandlers.splice(i, 1)
|
|
253
268
|
}
|
|
254
|
-
onCleanup(unsubscribe)
|
|
269
|
+
if (getOwner()) onCleanup(unsubscribe)
|
|
255
270
|
return unsubscribe
|
|
256
271
|
}
|
|
257
272
|
|
|
258
273
|
export function onWindowFocus(fn: () => void) {
|
|
259
274
|
let unsubscribe = on("windowFocus", fn)
|
|
260
|
-
onCleanup(unsubscribe)
|
|
275
|
+
if (getOwner()) onCleanup(unsubscribe)
|
|
261
276
|
return unsubscribe
|
|
262
277
|
}
|
|
263
278
|
|
|
264
279
|
export function onWindowBlur(fn: () => void) {
|
|
265
280
|
let unsubscribe = on("windowBlur", fn)
|
|
266
|
-
onCleanup(unsubscribe)
|
|
281
|
+
if (getOwner()) onCleanup(unsubscribe)
|
|
267
282
|
return unsubscribe
|
|
268
283
|
}
|
|
269
284
|
|
|
@@ -295,7 +310,8 @@ let backHandlers: ((e: BackEvent) => void)[] = []
|
|
|
295
310
|
* handler that does not prevent must not act either - the event is still on its
|
|
296
311
|
* way to whoever will handle it.
|
|
297
312
|
*
|
|
298
|
-
* Returns a cleanup function; also auto-cleans within
|
|
313
|
+
* Returns a cleanup function; also auto-cleans within an owned scope (outside
|
|
314
|
+
* one the returned cleanup is the only handle).
|
|
299
315
|
*/
|
|
300
316
|
export function onBack(fn: (e: BackEvent) => void) {
|
|
301
317
|
backHandlers.push(fn)
|
|
@@ -303,7 +319,7 @@ export function onBack(fn: (e: BackEvent) => void) {
|
|
|
303
319
|
let i = backHandlers.lastIndexOf(fn)
|
|
304
320
|
if (i >= 0) backHandlers.splice(i, 1)
|
|
305
321
|
}
|
|
306
|
-
onCleanup(cleanup)
|
|
322
|
+
if (getOwner()) onCleanup(cleanup)
|
|
307
323
|
return cleanup
|
|
308
324
|
}
|
|
309
325
|
|