Estimate text width for WIDTH_AND_HEIGHT auto-resize in layout
Text nodes with textAutoResize=WIDTH_AND_HEIGHT now use an estimated content width instead of the node's current width when computing auto-layout. Fixes month headers (and similar centered text in auto-layout) appearing left-aligned after paste.
This commit is contained in:
parent
9eaab86733
commit
d7e2015967
|
|
@ -48,7 +48,8 @@ export { FigmaAPI, FigmaNodeProxy, type FigmaFontName } from './figma-api'
|
|||
export { ALL_TOOLS, defineTool, toolsToAI } from './tools'
|
||||
export type { ToolDef, ParamDef, ParamType } from './tools'
|
||||
export { SkiaRenderer, type RenderOverlays } from './renderer'
|
||||
export { computeLayout, computeAllLayouts } from './layout'
|
||||
export { computeLayout, computeAllLayouts, setTextMeasurer } from './layout'
|
||||
export type { TextMeasurer } from './layout'
|
||||
export { getCanvasKit, getGpuBackend, type CanvasKitOptions, type GpuBackend } from './canvaskit'
|
||||
export {
|
||||
loadFont,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,14 @@ import Yoga, {
|
|||
|
||||
import type { SceneGraph, SceneNode } from './scene-graph'
|
||||
|
||||
export type TextMeasurer = (node: SceneNode) => { width: number; height: number } | null
|
||||
|
||||
let globalTextMeasurer: TextMeasurer | null = null
|
||||
|
||||
export function setTextMeasurer(measurer: TextMeasurer | null): void {
|
||||
globalTextMeasurer = measurer
|
||||
}
|
||||
|
||||
export function computeLayout(graph: SceneGraph, frameId: string): void {
|
||||
const frame = graph.getNode(frameId)
|
||||
if (!frame || frame.layoutMode === 'NONE') return
|
||||
|
|
@ -155,19 +163,25 @@ function configureChildAsLeaf(yogaChild: YogaNode, child: SceneNode, parent: Sce
|
|||
const isRow = parent.layoutMode === 'HORIZONTAL'
|
||||
const stretchCross = child.layoutAlignSelf === 'STRETCH' || parent.counterAxisAlign === 'STRETCH'
|
||||
|
||||
const measured = child.type === 'TEXT' && child.textAutoResize === 'WIDTH_AND_HEIGHT'
|
||||
? measureTextSize(child)
|
||||
: null
|
||||
const w = measured ? measured.width : child.width
|
||||
const h = child.height
|
||||
|
||||
if (child.layoutGrow > 0) {
|
||||
yogaChild.setFlexGrow(child.layoutGrow)
|
||||
if (!stretchCross) {
|
||||
if (isRow) yogaChild.setHeight(child.height)
|
||||
else yogaChild.setWidth(child.width)
|
||||
if (isRow) yogaChild.setHeight(h)
|
||||
else yogaChild.setWidth(w)
|
||||
}
|
||||
} else {
|
||||
if (isRow) {
|
||||
yogaChild.setWidth(child.width)
|
||||
if (!stretchCross) yogaChild.setHeight(child.height)
|
||||
yogaChild.setWidth(w)
|
||||
if (!stretchCross) yogaChild.setHeight(h)
|
||||
} else {
|
||||
yogaChild.setHeight(child.height)
|
||||
if (!stretchCross) yogaChild.setWidth(child.width)
|
||||
yogaChild.setHeight(h)
|
||||
if (!stretchCross) yogaChild.setWidth(w)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,6 +190,11 @@ function configureChildAsLeaf(yogaChild: YogaNode, child: SceneNode, parent: Sce
|
|||
}
|
||||
}
|
||||
|
||||
function measureTextSize(node: SceneNode): { width: number; height: number } | null {
|
||||
if (!globalTextMeasurer) return null
|
||||
return globalTextMeasurer(node)
|
||||
}
|
||||
|
||||
function setSizing(
|
||||
yogaNode: YogaNode,
|
||||
axis: 'width' | 'height',
|
||||
|
|
|
|||
|
|
@ -1963,6 +1963,18 @@ export class SkiaRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
measureTextNode(node: SceneNode): { width: number; height: number } | null {
|
||||
if (!this.fontsLoaded || !this.fontProvider || !this.isNodeFontLoaded(node)) return null
|
||||
if (node.type !== 'TEXT' || !node.text) return null
|
||||
|
||||
const paragraph = this.buildParagraph(node)
|
||||
paragraph.layout(node.textAutoResize === 'WIDTH_AND_HEIGHT' ? 1e6 : node.width || 1e6)
|
||||
const width = paragraph.getLongestLine()
|
||||
const height = paragraph.getHeight()
|
||||
paragraph.delete()
|
||||
return { width: Math.ceil(width), height: Math.ceil(height) }
|
||||
}
|
||||
|
||||
isNodeFontLoaded(node: SceneNode): boolean {
|
||||
const families = new Set<string>()
|
||||
families.add(node.fontFamily || DEFAULT_FONT_FAMILY)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
export { computeLayout, computeAllLayouts } from '@open-pencil/core'
|
||||
export { computeLayout, computeAllLayouts, setTextMeasurer } from '@open-pencil/core'
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import {
|
|||
prefetchFigmaSchema
|
||||
} from '@/engine/clipboard'
|
||||
import { exportFigFile } from '@/engine/fig-export'
|
||||
import { computeLayout, computeAllLayouts } from '@/engine/layout'
|
||||
import { computeLayout, computeAllLayouts, setTextMeasurer } from '@/engine/layout'
|
||||
import { renderNodesToImage } from '@/engine/render-image'
|
||||
import { SceneGraph } from '@/engine/scene-graph'
|
||||
import { TextEditor } from '@/engine/text-editor'
|
||||
|
|
@ -603,6 +603,7 @@ export function createEditorStore() {
|
|||
_ck = ck
|
||||
_renderer = renderer
|
||||
_textEditor = new TextEditor(ck)
|
||||
setTextMeasurer((node) => renderer.measureTextNode(node))
|
||||
}
|
||||
|
||||
function buildFigFile() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue