Fix font loading not registering in core cache, add layout text measurement tests
Tauri's loadFont registered fonts with the CanvasKit provider but not in core's loadedFamilies cache, so isFontLoaded returned false and measureTextNode returned null during layout recomputation. Add markFontLoaded() to core and call it from Tauri font loader. Add tests verifying text measurement integrates with auto-layout centering.
This commit is contained in:
parent
bf83118f96
commit
9c82cba672
|
|
@ -133,6 +133,12 @@ export async function ensureNodeFont(family: string, weight: number): Promise<vo
|
|||
await loadFont(family, style)
|
||||
}
|
||||
|
||||
export function markFontLoaded(family: string, style: string, data: ArrayBuffer): void {
|
||||
const cacheKey = `${family}|${style}`
|
||||
loadedFamilies.set(cacheKey, data)
|
||||
registerFontInCanvasKit(family, data)
|
||||
}
|
||||
|
||||
export function isFontLoaded(family: string): boolean {
|
||||
return [...loadedFamilies.keys()].some((k) => k.startsWith(`${family}|`))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export {
|
|||
initFontService,
|
||||
getFontProvider,
|
||||
isFontLoaded,
|
||||
markFontLoaded,
|
||||
ensureNodeFont,
|
||||
styleToWeight,
|
||||
weightToStyle
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export { initFontService, getFontProvider, ensureNodeFont } from '@open-pencil/core'
|
||||
|
||||
import { loadFont as loadFontCore, getFontProvider, styleToWeight } from '@open-pencil/core'
|
||||
import { loadFont as loadFontCore, markFontLoaded, styleToWeight } from '@open-pencil/core'
|
||||
|
||||
interface TauriFontFamily {
|
||||
family: string
|
||||
|
|
@ -60,8 +60,7 @@ export async function loadFont(family: string, style = 'Regular'): Promise<Array
|
|||
const data = await invoke<number[]>('load_system_font', { family, style })
|
||||
const buffer = new Uint8Array(data).buffer
|
||||
|
||||
const provider = getFontProvider()
|
||||
if (provider) provider.registerFont(buffer, family)
|
||||
markFontLoaded(family, style, buffer)
|
||||
|
||||
const weight = styleToWeight(style)
|
||||
const italic = style.toLowerCase().includes('italic') ? 'italic' : 'normal'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, test, expect } from 'bun:test'
|
||||
|
||||
import { SceneGraph, type SceneNode } from '../../src/engine/scene-graph'
|
||||
import { computeLayout, computeAllLayouts } from '../../src/engine/layout'
|
||||
import { computeLayout, computeAllLayouts, setTextMeasurer } from '../../src/engine/layout'
|
||||
|
||||
function pageId(graph: SceneGraph) {
|
||||
return graph.getPages()[0].id
|
||||
|
|
@ -885,4 +885,84 @@ describe('Auto Layout', () => {
|
|||
expect(children[3].y).toBe(90)
|
||||
})
|
||||
})
|
||||
|
||||
describe('text measurement', () => {
|
||||
test('WIDTH_AND_HEIGHT text uses measured width in centered layout', () => {
|
||||
const graph = new SceneGraph()
|
||||
const pid = pageId(graph)
|
||||
|
||||
const frame = autoFrame(graph, pid, {
|
||||
width: 300,
|
||||
height: 40,
|
||||
layoutMode: 'HORIZONTAL',
|
||||
primaryAxisSizing: 'FIXED',
|
||||
counterAxisSizing: 'FIXED',
|
||||
primaryAxisAlign: 'CENTER',
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
itemSpacing: 10,
|
||||
})
|
||||
|
||||
const arrow1 = graph.createNode('FRAME', frame.id, { width: 20, height: 20 })
|
||||
const text = graph.createNode('TEXT', frame.id, {
|
||||
width: 200,
|
||||
height: 20,
|
||||
text: 'Test',
|
||||
fontSize: 14,
|
||||
textAutoResize: 'WIDTH_AND_HEIGHT' as const,
|
||||
})
|
||||
const arrow2 = graph.createNode('FRAME', frame.id, { width: 20, height: 20 })
|
||||
|
||||
setTextMeasurer((node) => {
|
||||
if (node.type === 'TEXT' && node.textAutoResize === 'WIDTH_AND_HEIGHT') {
|
||||
return { width: 60, height: 20 }
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
computeAllLayouts(graph)
|
||||
|
||||
setTextMeasurer(null)
|
||||
|
||||
const updatedText = graph.getNode(text.id)!
|
||||
const updatedArrow1 = graph.getNode(arrow1.id)!
|
||||
const updatedArrow2 = graph.getNode(arrow2.id)!
|
||||
|
||||
expect(updatedText.width).toBe(60)
|
||||
|
||||
// Total content: 10 + 20 + 10 + 60 + 10 + 20 + 10 = 140
|
||||
// Free space: 300 - 140 = 160, centered offset = 80
|
||||
expect(updatedArrow1.x).toBe(90)
|
||||
expect(updatedText.x).toBe(120)
|
||||
expect(updatedArrow2.x).toBe(190)
|
||||
})
|
||||
|
||||
test('without measurer, text keeps its existing width', () => {
|
||||
const graph = new SceneGraph()
|
||||
const pid = pageId(graph)
|
||||
|
||||
const frame = autoFrame(graph, pid, {
|
||||
width: 300,
|
||||
height: 40,
|
||||
layoutMode: 'HORIZONTAL',
|
||||
primaryAxisSizing: 'FIXED',
|
||||
counterAxisSizing: 'FIXED',
|
||||
primaryAxisAlign: 'CENTER',
|
||||
})
|
||||
|
||||
const text = graph.createNode('TEXT', frame.id, {
|
||||
width: 200,
|
||||
height: 20,
|
||||
text: 'Test',
|
||||
fontSize: 14,
|
||||
textAutoResize: 'WIDTH_AND_HEIGHT' as const,
|
||||
})
|
||||
|
||||
setTextMeasurer(null)
|
||||
computeAllLayouts(graph)
|
||||
|
||||
const updatedText = graph.getNode(text.id)!
|
||||
expect(updatedText.width).toBe(200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue