@solidrt/core 0.0.68 → 0.0.70
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 +3 -3
- package/src/core.ts +34 -2
- package/src/index.ts +3 -2
- package/src/scroll.ts +12 -16
- package/src/text-input.ts +3 -3
- package/src/types.d.ts +15 -0
- package/src/warn.ts +22 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.70",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"funding": "https://github.com/sponsors/wellawaretech",
|
|
6
6
|
"author": "Antoine van Wel",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"AGENTS.md"
|
|
34
34
|
],
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@solidrt/flux-types": "0.0.
|
|
37
|
-
"@solidrt/test": "0.0.
|
|
36
|
+
"@solidrt/flux-types": "0.0.70",
|
|
37
|
+
"@solidrt/test": "0.0.70"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@solidjs/signals": "2.0.0-rc.13",
|
package/src/core.ts
CHANGED
|
@@ -402,6 +402,30 @@ export function warmText(styles: tree.MeasureTextOptions[]): void {
|
|
|
402
402
|
tree.warmText(styles)
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
+
export type TextRenderingOptions = tree.TextRenderingOptions
|
|
406
|
+
export type TextRenderingPolicy = tree.TextRenderingPolicy
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Changes how every text is rendered: the coverage-to-color mode and its
|
|
410
|
+
* gamma or contrast, and the stem darkening and hinting of the glyph cells. Each option
|
|
411
|
+
* given replaces its part of the current policy, the rest stays; every text
|
|
412
|
+
* redraws at the next frame. The defaults are what an app that says nothing
|
|
413
|
+
* gets; this is the knob for judging a policy by eye on a running app.
|
|
414
|
+
*/
|
|
415
|
+
export function setTextRendering(options: TextRenderingOptions): void {
|
|
416
|
+
tree.setTextRendering(options)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* The text rendering policy as it stands, resolved for this display: the
|
|
421
|
+
* shape setTextRendering takes, with a darkening or hinting left at its
|
|
422
|
+
* default read as the value the display scale gives it. What a settings
|
|
423
|
+
* screen shows; setting it back changes nothing on this display.
|
|
424
|
+
*/
|
|
425
|
+
export function textRendering(): TextRenderingPolicy {
|
|
426
|
+
return tree.textRendering()
|
|
427
|
+
}
|
|
428
|
+
|
|
405
429
|
/** One laid-out line from layoutNextLine. */
|
|
406
430
|
export type TextLine = {
|
|
407
431
|
/** Unit range [from, to) into prepared.units. */
|
|
@@ -426,6 +450,13 @@ export type TextLine = {
|
|
|
426
450
|
* every glued piece that follows, plus the last piece's ink. What must fit on
|
|
427
451
|
* the line for the unit to go on it.
|
|
428
452
|
*/
|
|
453
|
+
// Slack a line's width may be exceeded by and still fit, logical px: the
|
|
454
|
+
// width a laid-out box hands back is a float ulp off the width its text
|
|
455
|
+
// measured (the rendertree's layout::FIT_SLACK, the same value), so an
|
|
456
|
+
// exact fit wraps a word inside its own box. 1/64 px is a browser layout
|
|
457
|
+
// unit: far above any ulp drift, far below a visible overhang.
|
|
458
|
+
export const FIT_SLACK = 1 / 64
|
|
459
|
+
|
|
429
460
|
export function unitInk(units: tree.TextUnit[], index: number): number {
|
|
430
461
|
let ink = units[index]!.width
|
|
431
462
|
let advance = 0
|
|
@@ -439,7 +470,8 @@ export function unitInk(units: tree.TextUnit[], index: number): number {
|
|
|
439
470
|
/**
|
|
440
471
|
* The next line of `prepared` from unit `cursor` that fits `width`, or null
|
|
441
472
|
* when the text is used up. Greedy: units go on the line while the pen plus
|
|
442
|
-
* the unit's ink stays within `width
|
|
473
|
+
* the unit's ink stays within `width` (a hair of slack for float drift
|
|
474
|
+
* included); a hard break ends the line; a unit
|
|
443
475
|
* wider than `width` on its own goes on the line whole and overflows. Draw a
|
|
444
476
|
* line as `<d-text x y w={line.width + 1}>{prepared.text.slice(line.start, line.end)}</d-text>`
|
|
445
477
|
* with the same font options; its words are already shaped, so that is
|
|
@@ -458,7 +490,7 @@ export function layoutNextLine(prepared: tree.PreparedText, cursor: number, widt
|
|
|
458
490
|
let unit = units[i]!
|
|
459
491
|
// Glued pieces stay with the unit they continue: the whole wrap unit
|
|
460
492
|
// (this piece through the last glued one) must fit for any of it to go on.
|
|
461
|
-
if (i > cursor && !unit.glue && pen + unitInk(units, i) > width) break
|
|
493
|
+
if (i > cursor && !unit.glue && pen + unitInk(units, i) > width + FIT_SLACK) break
|
|
462
494
|
pen += unit.advance
|
|
463
495
|
if (unit.ascent > ascent) ascent = unit.ascent
|
|
464
496
|
if (unit.descent > descent) descent = unit.descent
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./renderer"
|
|
2
|
-
export { setFocus, focusedNode, startTextInput, textInputActive, getFocusables, measureText, prepareText, warmText, layoutNextLine, unitInk, getBoundingBox, getBoundingBoxViewport, getLayoutBox, snapshotTexture, onPointerMove } from "./core"
|
|
3
|
-
export type { BoundingBox, GlobalPointerEvent, TextLine } from "./core"
|
|
2
|
+
export { setFocus, focusedNode, startTextInput, textInputActive, getFocusables, measureText, prepareText, warmText, setTextRendering, textRendering, layoutNextLine, unitInk, getBoundingBox, getBoundingBoxViewport, getLayoutBox, snapshotTexture, onPointerMove } from "./core"
|
|
3
|
+
export type { BoundingBox, GlobalPointerEvent, TextLine, TextRenderingOptions, TextRenderingPolicy } from "./core"
|
|
4
4
|
export { parseColor, mixColors, withAlpha, brightness, createLinearGradient, createRadialGradient } from "./color"
|
|
5
5
|
export type { Gradient, GradientStop } from "./color"
|
|
6
6
|
export { onFrame, frameTime, onBeforeRender, onLayout, onResize, onWindowFocus, onWindowBlur, onBack, onLink, onSuspend, onQuit, exit, background, registerProtocolHandler } from "./window"
|
|
@@ -25,6 +25,7 @@ export { Logo } from "./logo"
|
|
|
25
25
|
export type { LogoProps } from "./logo"
|
|
26
26
|
export type { SvgDocument, SvgDraw } from "./svg"
|
|
27
27
|
export { createScroll } from "./scroll"
|
|
28
|
+
export { warnOnce } from "./warn"
|
|
28
29
|
export type { Scroll, ScrollAxis, ScrollBehavior, ScrollOffset, ScrollOptions, ScrollToOptions } from "./scroll"
|
|
29
30
|
export { arena } from "./arena"
|
|
30
31
|
export type { ArenaOwner } from "./arena"
|
package/src/scroll.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { createSignal } from "@solidjs/signals"
|
|
8
8
|
import { getBoundingBox } from "./core"
|
|
9
9
|
import { onLayout } from "./window"
|
|
10
|
+
import { warnOnce } from "./warn"
|
|
10
11
|
|
|
11
12
|
export type ScrollAxis = "vertical" | "horizontal" | "both"
|
|
12
13
|
|
|
@@ -75,11 +76,9 @@ export function createScroll(
|
|
|
75
76
|
|
|
76
77
|
// A scroll viewport with no explicit main-axis size resolves to 0 in flex
|
|
77
78
|
// layout and its content silently vanishes - a classic trap (maxHeight alone
|
|
78
|
-
// does not size it either). Detect it at measure time and warn once,
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
let origin = new Error().stack ?? ""
|
|
82
|
-
let warnedCollapsed = false
|
|
79
|
+
// does not size it either). Detect it at measure time and warn once, from
|
|
80
|
+
// the component that built the scroller (warnOnce captures its stack here).
|
|
81
|
+
let warnCollapsed = warnOnce()
|
|
83
82
|
|
|
84
83
|
// Last measured overflow, refreshed each layout. scrollBy/scrollTo clamp
|
|
85
84
|
// against these between layouts; onLayout re-clamps once new sizes are known.
|
|
@@ -110,17 +109,14 @@ export function createScroll(
|
|
|
110
109
|
let vb = getBoundingBox(vp)
|
|
111
110
|
let cb = getBoundingBox(ct)
|
|
112
111
|
if (!vb || !cb) return
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
`Give it an explicit ${axisName} or flex; maxHeight/maxWidth alone does not size it.\n${origin}`,
|
|
122
|
-
)
|
|
123
|
-
}
|
|
112
|
+
let zeroY = canY && vb.height === 0 && cb.height > 0
|
|
113
|
+
let zeroX = canX && vb.width === 0 && cb.width > 0
|
|
114
|
+
if (zeroY || zeroX) {
|
|
115
|
+
let axisName = zeroY ? "height" : "width"
|
|
116
|
+
warnCollapsed(
|
|
117
|
+
`Scroll container resolved to ${axisName} 0, so its content is invisible. ` +
|
|
118
|
+
`Give it an explicit ${axisName} or flex; maxHeight/maxWidth alone does not size it.`,
|
|
119
|
+
)
|
|
124
120
|
}
|
|
125
121
|
maxX = Math.max(0, cb.width - vb.width)
|
|
126
122
|
maxY = Math.max(0, cb.height - vb.height)
|
package/src/text-input.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// are policy and belong to the component (the "skin") that composes these.
|
|
6
6
|
|
|
7
7
|
import { createMemo, createSignal, flush, untrack } from "@solidjs/signals"
|
|
8
|
-
import { getLayoutBox, layoutNextLine, measureText, prepareText, unitInk } from "./core"
|
|
8
|
+
import { FIT_SLACK, getLayoutBox, layoutNextLine, measureText, prepareText, unitInk } from "./core"
|
|
9
9
|
import type { MeasureTextOptions, PreparedText, TextRunRange, TextUnit } from "flux:rendertree"
|
|
10
10
|
import { onLayout } from "./window"
|
|
11
11
|
|
|
@@ -542,12 +542,12 @@ export function createTextEditorLayout(
|
|
|
542
542
|
// through as is.
|
|
543
543
|
function splitWide(prepared: PreparedText, width: number): PreparedText {
|
|
544
544
|
let all = prepared.units
|
|
545
|
-
if (!all.some((u, i) => !u.glue && unitInk(all, i) > width)) return prepared
|
|
545
|
+
if (!all.some((u, i) => !u.glue && unitInk(all, i) > width + FIT_SLACK)) return prepared
|
|
546
546
|
let wide = false
|
|
547
547
|
let units: TextUnit[] = []
|
|
548
548
|
for (let u = 0; u < all.length; u++) {
|
|
549
549
|
let unit = all[u]!
|
|
550
|
-
if (!unit.glue) wide = unitInk(all, u) > width
|
|
550
|
+
if (!unit.glue) wide = unitInk(all, u) > width + FIT_SLACK
|
|
551
551
|
let stops = unit.carets
|
|
552
552
|
if (!wide || !stops || stops.length <= 2) {
|
|
553
553
|
units.push(unit)
|
package/src/types.d.ts
CHANGED
|
@@ -103,6 +103,21 @@ export interface LayoutProps extends FlexboxProps, GridProps {
|
|
|
103
103
|
* resolves against whatever relative element is above all of them.
|
|
104
104
|
*/
|
|
105
105
|
position?: "relative" | "absolute"
|
|
106
|
+
/**
|
|
107
|
+
* CSS size containment: the element sizes as if it had no content. A
|
|
108
|
+
* texture or text measures as zero on every axis layout does not hand it,
|
|
109
|
+
* and a view's children add nothing to its box; they lay out inside
|
|
110
|
+
* whatever box it gets. The one CSS `contain` value with a meaning here.
|
|
111
|
+
* The element still needs a size from its own props or its parent (a
|
|
112
|
+
* `flexGrow` in a sized column), or it comes out empty.
|
|
113
|
+
*
|
|
114
|
+
* What takes the place of the web's `minHeight={0}` escape from the flex
|
|
115
|
+
* automatic minimum, declared on the element whose content would
|
|
116
|
+
* otherwise pin its ancestors: a fill-mode `<Scene>` or `<SpriteLayer>`
|
|
117
|
+
* leaf sets it, so the target that follows the leaf's box never feeds its
|
|
118
|
+
* pixel size back into layout.
|
|
119
|
+
*/
|
|
120
|
+
contain?: "size"
|
|
106
121
|
|
|
107
122
|
top?: Dimension
|
|
108
123
|
right?: Dimension
|
package/src/warn.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// A development warning for a misuse that shows later than the code that
|
|
2
|
+
// caused it: a layout that comes out empty, an event that never arrives. The
|
|
3
|
+
// site of the misuse is the component that built the thing, not the layout
|
|
4
|
+
// pass that notices, so the warning carries that component's stack.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A once-only dev warning bound to the place it is created: `let warn =
|
|
8
|
+
* warnOnce()` where a component builds something whose misuse surfaces
|
|
9
|
+
* later (a scroller, a fill-mode scene), `warn(message)` from the layout
|
|
10
|
+
* or event read that notices. The first call reports, with the stack of the
|
|
11
|
+
* creation site under the message (the dev server remaps the frames to
|
|
12
|
+
* .tsx); later calls are silent.
|
|
13
|
+
*/
|
|
14
|
+
export function warnOnce(): (message: string) => void {
|
|
15
|
+
let origin = new Error().stack ?? ""
|
|
16
|
+
let warned = false
|
|
17
|
+
return message => {
|
|
18
|
+
if (warned) return
|
|
19
|
+
warned = true
|
|
20
|
+
console.warn(`${message}\n${origin}`)
|
|
21
|
+
}
|
|
22
|
+
}
|