Fix text layout without CanvasKit: estimate size instead of 100×100 default
Text nodes with textAutoResize=WIDTH_AND_HEIGHT kept their 100×100 default SceneNode size when MeasureFunc was unavailable (no CanvasKit), blowing up every HUG container. Add fallback estimator (~0.6 × fontSize per char) in layout.ts so headless layout produces sane sizes. DO NOT change textAutoResize defaults in renderer.ts without testing headless layout — see comments there and in layout.ts.
This commit is contained in:
parent
57eaf870af
commit
9caa89e6f8
|
|
@ -23,6 +23,8 @@ export type TextMeasurer = (
|
|||
|
||||
let globalTextMeasurer: TextMeasurer | null = null
|
||||
|
||||
const GLYPH_WIDTH_FACTOR = 0.6
|
||||
|
||||
export function setTextMeasurer(measurer: TextMeasurer | null): void {
|
||||
globalTextMeasurer = measurer
|
||||
}
|
||||
|
|
@ -343,6 +345,15 @@ function configureChildAsLeaf(yogaChild: YogaNode, child: SceneNode, parent: Sce
|
|||
|
||||
if (needsMeasureFunc) {
|
||||
configureTextLeaf(yogaChild, child, parent)
|
||||
} else if (isText && !globalTextMeasurer && child.textAutoResize !== 'NONE') {
|
||||
const est = estimateTextSize(child)
|
||||
if (child.textAutoResize === 'WIDTH_AND_HEIGHT') {
|
||||
yogaChild.setWidth(est.width)
|
||||
yogaChild.setHeight(est.height)
|
||||
} else if (child.textAutoResize === 'HEIGHT') {
|
||||
yogaChild.setWidth(child.width)
|
||||
yogaChild.setHeight(est.height)
|
||||
}
|
||||
} else {
|
||||
configureNonTextLeaf(yogaChild, child, isRow, stretchCross)
|
||||
}
|
||||
|
|
@ -353,6 +364,18 @@ function configureChildAsLeaf(yogaChild: YogaNode, child: SceneNode, parent: Sce
|
|||
applyMinMaxConstraints(yogaChild, child)
|
||||
}
|
||||
|
||||
// Fallback text size estimate when CanvasKit is unavailable (headless/tests).
|
||||
// Without this, text nodes keep their 100×100 default and blow up HUG containers.
|
||||
function estimateTextSize(node: SceneNode): { width: number; height: number } {
|
||||
const fontSize = node.fontSize || 14
|
||||
const lineHeight = fontSize * 1.2
|
||||
const charWidth = fontSize * GLYPH_WIDTH_FACTOR
|
||||
return {
|
||||
width: Math.ceil(node.text.length * charWidth),
|
||||
height: Math.ceil(lineHeight)
|
||||
}
|
||||
}
|
||||
|
||||
function configureTextLeaf(
|
||||
yogaChild: YogaNode,
|
||||
child: SceneNode,
|
||||
|
|
|
|||
|
|
@ -401,6 +401,7 @@ function applyTextOverrides(
|
|||
const fillsParent = w === 'fill' || (props.grow as number) > 0
|
||||
const isInsideAutoLayout = parentLayout !== 'NONE'
|
||||
|
||||
// Coupled with estimateTextSize() in layout.ts — test headless layout after changes.
|
||||
if (props.textAutoResize) {
|
||||
o.textAutoResize = TEXT_AUTO_RESIZE_MAP[props.textAutoResize as string] ?? 'NONE'
|
||||
} else if (hasExplicitWidth || (isInsideAutoLayout && fillsParent)) {
|
||||
|
|
|
|||
|
|
@ -1020,7 +1020,7 @@ describe('Auto Layout', () => {
|
|||
expect(updatedArrow2.x).toBe(190)
|
||||
})
|
||||
|
||||
test('without measurer, text keeps its existing width', () => {
|
||||
test('without measurer, text uses estimated size instead of 100x100 default', () => {
|
||||
const graph = new SceneGraph()
|
||||
const pid = pageId(graph)
|
||||
|
||||
|
|
@ -1045,7 +1045,11 @@ describe('Auto Layout', () => {
|
|||
computeAllLayouts(graph)
|
||||
|
||||
const updatedText = graph.getNode(text.id)!
|
||||
expect(updatedText.width).toBe(200)
|
||||
// Rough estimate: ~0.6 × fontSize × charCount, not the 100×100 default
|
||||
expect(updatedText.width).toBeLessThan(100)
|
||||
expect(updatedText.width).toBeGreaterThan(0)
|
||||
expect(updatedText.height).toBeLessThan(100)
|
||||
expect(updatedText.height).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('HEIGHT auto-resize text wraps via MeasureFunc when filling parent', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue