@solidrt/core 0.0.6 → 0.0.8
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/package.json +2 -2
- package/src/types.d.ts +10 -1
- package/src/window.ts +39 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Antoine van Wel",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"colord": "^2.9.3"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@solidrt/flux-types": "0.0.
|
|
23
|
+
"@solidrt/flux-types": "0.0.8"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"@solidjs/signals": "2.0.0-beta.14",
|
package/src/types.d.ts
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
import type { JSX as SolidJSX } from "@solidjs/signals"
|
|
4
4
|
|
|
5
5
|
declare global {
|
|
6
|
+
function requestAnimationFrame(callback: (time: number) => void): number
|
|
7
|
+
function cancelAnimationFrame(id: number): void
|
|
8
|
+
|
|
9
|
+
// UI event bus (lattice). on/once return an unsubscribe function.
|
|
10
|
+
let srt: {
|
|
11
|
+
on(event: string, callback: (data: any) => void): () => void
|
|
12
|
+
once(event: string, callback: (data: any) => void): () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
let ffi: {
|
|
7
16
|
createRoot(id: number): void
|
|
8
17
|
createNode(id: number, kind: string): void
|
|
@@ -207,7 +216,7 @@ export interface PathProps extends Position, PaintProps, PointerProps {
|
|
|
207
216
|
fillRule?: "nonZero" | "evenOdd"
|
|
208
217
|
}
|
|
209
218
|
|
|
210
|
-
export interface TextProps extends PaintProps {
|
|
219
|
+
export interface TextProps extends PaintProps, PointerProps {
|
|
211
220
|
children?: Children
|
|
212
221
|
fontFamily?: "sans" | "mono" | (string & {})
|
|
213
222
|
fontSize?: number
|
package/src/window.ts
CHANGED
|
@@ -6,16 +6,24 @@ import { getEventHandler, getFocusedNodeId, setFocus } from "./core"
|
|
|
6
6
|
let nextFrameId = 1
|
|
7
7
|
let animationFrames = new Map<number, Function>()
|
|
8
8
|
|
|
9
|
+
// Latest display refresh rate (Hz) reported by the runtime, passed to onFrame
|
|
10
|
+
// callbacks. Defaults to 60 until the first displayRefreshRate event arrives.
|
|
11
|
+
let refreshRate = 60
|
|
12
|
+
|
|
9
13
|
/**
|
|
10
|
-
* Calls `fn` before every frame is painted
|
|
11
|
-
*
|
|
12
|
-
* (
|
|
14
|
+
* Calls `fn` before every frame is painted: `tick` is the time in ms, `frame` is
|
|
15
|
+
* the present count, and `rate` is the current refresh rate in Hz. `tick` is paced
|
|
16
|
+
* by the runtime (one refresh period per present, slow-corrected toward the wall
|
|
17
|
+
* clock) so animations driven off it stay smooth even when swap-return times
|
|
18
|
+
* jitter. For raw, continuous wall-clock time (e.g. measuring code), use
|
|
19
|
+
* performance.now() instead.
|
|
20
|
+
* Returns a cleanup function; also auto-cleans within a reactive scope.
|
|
13
21
|
*/
|
|
14
|
-
export function onFrame(fn: (tick: number, frame: number) => void) {
|
|
22
|
+
export function onFrame(fn: (tick: number, frame: number, rate: number) => void) {
|
|
15
23
|
let frameId: number = null!
|
|
16
24
|
|
|
17
|
-
let extendedFn = (tick: number, frame: number) => {
|
|
18
|
-
fn(tick, frame)
|
|
25
|
+
let extendedFn = (tick: number, frame: number, rate: number) => {
|
|
26
|
+
fn(tick, frame, rate)
|
|
19
27
|
frameId = nextFrameId++
|
|
20
28
|
animationFrames.set(frameId, extendedFn)
|
|
21
29
|
}
|
|
@@ -45,7 +53,7 @@ interface ResizeEvent {
|
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
export function onResize(fn: (data: ResizeEvent) => void) {
|
|
48
|
-
let unsubscribe =
|
|
56
|
+
let unsubscribe = srt.on("resize", fn)
|
|
49
57
|
onCleanup(unsubscribe)
|
|
50
58
|
return unsubscribe
|
|
51
59
|
}
|
|
@@ -55,19 +63,19 @@ export function onResize(fn: (data: ResizeEvent) => void) {
|
|
|
55
63
|
// by a re-layout pass before painting (one extra pass; cascades beyond that
|
|
56
64
|
// paint stale).
|
|
57
65
|
export function onLayout(fn: () => void) {
|
|
58
|
-
let unsubscribe =
|
|
66
|
+
let unsubscribe = srt.on("postLayout", fn)
|
|
59
67
|
onCleanup(unsubscribe)
|
|
60
68
|
return unsubscribe
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
export function onWindowFocus(fn: () => void) {
|
|
64
|
-
let unsubscribe =
|
|
72
|
+
let unsubscribe = srt.on("windowFocus", fn)
|
|
65
73
|
onCleanup(unsubscribe)
|
|
66
74
|
return unsubscribe
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
export function onWindowBlur(fn: () => void) {
|
|
70
|
-
let unsubscribe =
|
|
78
|
+
let unsubscribe = srt.on("windowBlur", fn)
|
|
71
79
|
onCleanup(unsubscribe)
|
|
72
80
|
return unsubscribe
|
|
73
81
|
}
|
|
@@ -86,24 +94,30 @@ export function attachWindow(_nodeId: number) {
|
|
|
86
94
|
let unsubKeyUp: () => void = null!
|
|
87
95
|
let unsubTextInput: () => void = null!
|
|
88
96
|
let unsubKeyboardVisibility: () => void = null!
|
|
97
|
+
let unsubRefreshRate: () => void = null!
|
|
89
98
|
let unsubFirstResize: (() => void) | null = null
|
|
90
99
|
|
|
91
100
|
function runFrame(t: number, frame: number) {
|
|
92
101
|
if (animationFrames.size > 0) {
|
|
93
102
|
let frames = animationFrames
|
|
94
103
|
animationFrames = new Map()
|
|
95
|
-
for (let fn of frames.values()) fn(t, frame)
|
|
104
|
+
for (let fn of frames.values()) fn(t, frame, refreshRate)
|
|
96
105
|
}
|
|
97
106
|
flush()
|
|
98
107
|
draw()
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
onSettled(() => {
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
// Sticky event: a late subscriber still receives the current rate.
|
|
112
|
+
unsubRefreshRate = srt.on("displayRefreshRate", ({ hz }: { hz: number }) => {
|
|
113
|
+
if (hz > 0) refreshRate = hz
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
unsubscribe = srt.on("render", ({ time, frame }: { time: number; frame: number }) => {
|
|
117
|
+
runFrame(time * 1000, frame)
|
|
104
118
|
})
|
|
105
119
|
|
|
106
|
-
unsubDown =
|
|
120
|
+
unsubDown = srt.on(
|
|
107
121
|
"pointerDown",
|
|
108
122
|
({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
109
123
|
for (let nodeId of targets) {
|
|
@@ -118,13 +132,13 @@ export function attachWindow(_nodeId: number) {
|
|
|
118
132
|
},
|
|
119
133
|
)
|
|
120
134
|
|
|
121
|
-
unsubUp =
|
|
135
|
+
unsubUp = srt.on("pointerUp", ({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
122
136
|
for (let nodeId of targets) {
|
|
123
137
|
getEventHandler(nodeId, "onPointerUp")?.(e)
|
|
124
138
|
}
|
|
125
139
|
})
|
|
126
140
|
|
|
127
|
-
unsubMove =
|
|
141
|
+
unsubMove = srt.on(
|
|
128
142
|
"pointerMove",
|
|
129
143
|
({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
130
144
|
for (let nodeId of targets) {
|
|
@@ -133,7 +147,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
133
147
|
},
|
|
134
148
|
)
|
|
135
149
|
|
|
136
|
-
unsubEnter =
|
|
150
|
+
unsubEnter = srt.on(
|
|
137
151
|
"pointerEnter",
|
|
138
152
|
({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
139
153
|
for (let nodeId of targets) {
|
|
@@ -142,7 +156,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
142
156
|
},
|
|
143
157
|
)
|
|
144
158
|
|
|
145
|
-
unsubLeave =
|
|
159
|
+
unsubLeave = srt.on(
|
|
146
160
|
"pointerLeave",
|
|
147
161
|
({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
148
162
|
for (let nodeId of targets) {
|
|
@@ -151,27 +165,27 @@ export function attachWindow(_nodeId: number) {
|
|
|
151
165
|
},
|
|
152
166
|
)
|
|
153
167
|
|
|
154
|
-
unsubWheel =
|
|
168
|
+
unsubWheel = srt.on("wheel", ({ targets, ...e }: { targets: number[]; [k: string]: any }) => {
|
|
155
169
|
for (let nodeId of targets) {
|
|
156
170
|
getEventHandler(nodeId, "onWheel")?.(e)
|
|
157
171
|
}
|
|
158
172
|
})
|
|
159
173
|
|
|
160
|
-
unsubKeyDown =
|
|
174
|
+
unsubKeyDown = srt.on("keydown", (e: any) => {
|
|
161
175
|
let id = getFocusedNodeId()
|
|
162
176
|
if (id != null) {
|
|
163
177
|
getEventHandler(id, "onKeyDown")?.(e)
|
|
164
178
|
}
|
|
165
179
|
})
|
|
166
180
|
|
|
167
|
-
unsubKeyUp =
|
|
181
|
+
unsubKeyUp = srt.on("keyup", (e: any) => {
|
|
168
182
|
let id = getFocusedNodeId()
|
|
169
183
|
if (id != null) {
|
|
170
184
|
getEventHandler(id, "onKeyUp")?.(e)
|
|
171
185
|
}
|
|
172
186
|
})
|
|
173
187
|
|
|
174
|
-
unsubTextInput =
|
|
188
|
+
unsubTextInput = srt.on("textInput", (e: any) => {
|
|
175
189
|
let id = getFocusedNodeId()
|
|
176
190
|
if (id != null) {
|
|
177
191
|
getEventHandler(id, "onTextInput")?.(e)
|
|
@@ -180,7 +194,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
180
194
|
|
|
181
195
|
// When the user dismisses the on-screen keyboard (swipe down, "Done",
|
|
182
196
|
// back button), blur the focused node so the app's UI state catches up.
|
|
183
|
-
unsubKeyboardVisibility =
|
|
197
|
+
unsubKeyboardVisibility = srt.on("keyboardVisibility", ({ shown }: { shown: boolean }) => {
|
|
184
198
|
if (!shown) setFocus(null)
|
|
185
199
|
})
|
|
186
200
|
|
|
@@ -191,7 +205,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
191
205
|
// synchronously here, while we are still inside this onSettled callback
|
|
192
206
|
// where flush() is illegal (not reentrant). Defer runFrame to a microtask
|
|
193
207
|
// so the first frame always runs after this callback returns.
|
|
194
|
-
unsubFirstResize =
|
|
208
|
+
unsubFirstResize = srt.once("resize", () => {
|
|
195
209
|
queueMicrotask(() => runFrame(0, 0))
|
|
196
210
|
})
|
|
197
211
|
})
|
|
@@ -208,6 +222,7 @@ export function attachWindow(_nodeId: number) {
|
|
|
208
222
|
if (unsubKeyUp) unsubKeyUp()
|
|
209
223
|
if (unsubTextInput) unsubTextInput()
|
|
210
224
|
if (unsubKeyboardVisibility) unsubKeyboardVisibility()
|
|
225
|
+
if (unsubRefreshRate) unsubRefreshRate()
|
|
211
226
|
if (unsubFirstResize) unsubFirstResize()
|
|
212
227
|
})
|
|
213
228
|
}
|