diff --git a/packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx b/packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx index f95604491..3c70e078d 100644 --- a/packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx @@ -92,7 +92,6 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { const cx = w / 2 const cy = h / 2 - const strokeWidth = STROKE_SIZES[shape.props.size] * shape.props.scale const isFilled = shape.props.fill !== 'none' // || shape.props.text.trim().length > 0 let body: Geometry2d @@ -318,20 +317,28 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { } } - const labelSize = getLabelSize(this.editor, shape) - const minWidth = Math.min(100, w / 2) - const minHeight = Math.min( - LABEL_FONT_SIZES[shape.props.size] * shape.props.scale * TEXT_PROPS.lineHeight + - LABEL_PADDING * 2, - h / 2 + const unscaledlabelSize = getUnscaledLabelSize(this.editor, shape) + // unscaled w and h + const unscaledW = w / shape.props.scale + const unscaledH = h / shape.props.scale + const unscaledminWidth = Math.min(100, unscaledW / 2) + const unscaledMinHeight = Math.min( + LABEL_FONT_SIZES[shape.props.size] * TEXT_PROPS.lineHeight + LABEL_PADDING * 2, + unscaledH / 2 ) - const labelWidth = Math.min(w, Math.max(labelSize.w, Math.min(minWidth, Math.max(1, w - 8)))) - const labelHeight = Math.min(h, Math.max(labelSize.h, Math.min(minHeight, Math.max(1, w - 8)))) + const unscaledLabelWidth = Math.min( + unscaledW, + Math.max(unscaledlabelSize.w, Math.min(unscaledminWidth, Math.max(1, unscaledW - 8))) + ) + const unscaledLabelHeight = Math.min( + unscaledH, + Math.max(unscaledlabelSize.h, Math.min(unscaledMinHeight, Math.max(1, unscaledH - 8))) + ) // not sure if bug - const lines = getLines(shape.props, strokeWidth) + const lines = getLines(shape.props, STROKE_SIZES[shape.props.size] * shape.props.scale) const edges = lines ? lines.map((line) => new Polyline2d({ points: line })) : [] // todo: use centroid for label position @@ -344,16 +351,16 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { shape.props.align === 'start' ? 0 : shape.props.align === 'end' - ? w - labelWidth - : (w - labelWidth) / 2, + ? (unscaledW - unscaledLabelWidth) * shape.props.scale + : ((unscaledW - unscaledLabelWidth) / 2) * shape.props.scale, y: shape.props.verticalAlign === 'start' ? 0 : shape.props.verticalAlign === 'end' - ? h - labelHeight - : (h - labelHeight) / 2, - width: labelWidth, - height: labelHeight, + ? (unscaledH - unscaledLabelHeight) * shape.props.scale + : ((unscaledH - unscaledLabelHeight) / 2) * shape.props.scale, + width: unscaledLabelWidth * shape.props.scale, + height: unscaledLabelHeight * shape.props.scale, isFilled: true, isLabel: true, }), @@ -443,7 +450,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { font={font} fontSize={LABEL_FONT_SIZES[size] * shape.props.scale} lineHeight={TEXT_PROPS.lineHeight} - padding={16 * shape.props.scale} + padding={LABEL_PADDING * shape.props.scale} fill={fill} align={align} verticalAlign={verticalAlign} @@ -569,49 +576,52 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { shape, { handle, newPoint, scaleX, scaleY, initialShape } ) => { + const unscaledInitialW = initialShape.props.w / initialShape.props.scale + const unscaledInitialH = initialShape.props.h / initialShape.props.scale + const unscaledGrowY = initialShape.props.growY / initialShape.props.scale // use the w/h from props here instead of the initialBounds here, // since cloud shapes calculated bounds can differ from the props w/h. - let w = initialShape.props.w * scaleX - let h = (initialShape.props.h + initialShape.props.growY) * scaleY + let unscaledW = unscaledInitialW * scaleX + let unscaledH = (unscaledInitialH + unscaledGrowY) * scaleY let overShrinkX = 0 let overShrinkY = 0 + const min = MIN_SIZE_WITH_LABEL + if (shape.props.text.trim()) { - let newW = Math.max(Math.abs(w), MIN_SIZE_WITH_LABEL) - let newH = Math.max(Math.abs(h), MIN_SIZE_WITH_LABEL) + let newW = Math.max(Math.abs(unscaledW), min) + let newH = Math.max(Math.abs(unscaledH), min) - if (newW < MIN_SIZE_WITH_LABEL && newH === MIN_SIZE_WITH_LABEL) { - newW = MIN_SIZE_WITH_LABEL - } + if (newW < min && newH === min) newW = min + if (newW === min && newH < min) newH = min - if (newW === MIN_SIZE_WITH_LABEL && newH < MIN_SIZE_WITH_LABEL) { - newH = MIN_SIZE_WITH_LABEL - } - - const labelSize = getLabelSize(this.editor, { + const unscaledLabelSize = getUnscaledLabelSize(this.editor, { ...shape, props: { ...shape.props, - w: newW, - h: newH, + w: newW * shape.props.scale, + h: newH * shape.props.scale, }, }) - const nextW = Math.max(Math.abs(w), labelSize.w) * Math.sign(w) - const nextH = Math.max(Math.abs(h), labelSize.h) * Math.sign(h) - overShrinkX = Math.abs(nextW) - Math.abs(w) - overShrinkY = Math.abs(nextH) - Math.abs(h) + const nextW = Math.max(Math.abs(unscaledW), unscaledLabelSize.w) * Math.sign(unscaledW) + const nextH = Math.max(Math.abs(unscaledH), unscaledLabelSize.h) * Math.sign(unscaledH) + overShrinkX = Math.abs(nextW) - Math.abs(unscaledW) + overShrinkY = Math.abs(nextH) - Math.abs(unscaledH) - w = nextW - h = nextH + unscaledW = nextW + unscaledH = nextH } + const scaledW = unscaledW * shape.props.scale + const scaledH = unscaledH * shape.props.scale + const offset = new Vec(0, 0) // x offsets if (scaleX < 0) { - offset.x += w + offset.x += scaledW } if (handle === 'left' || handle === 'top_left' || handle === 'bottom_left') { @@ -621,7 +631,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { // y offsets if (scaleY < 0) { - offset.y += h + offset.y += scaledH } if (handle === 'top' || handle === 'top_left' || handle === 'top_right') { @@ -634,8 +644,8 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { x, y, props: { - w: Math.max(Math.abs(w), 1), - h: Math.max(Math.abs(h), 1), + w: Math.max(Math.abs(scaledW), 1), + h: Math.max(Math.abs(scaledH), 1), growY: 0, }, } @@ -658,13 +668,13 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { } } - const prevHeight = shape.props.h - const nextHeight = getLabelSize(this.editor, shape).h + const unscaledPrevHeight = shape.props.h / shape.props.scale + const unscaledNextHeight = getUnscaledLabelSize(this.editor, shape).h let growY: number | null = null - if (nextHeight > prevHeight) { - growY = nextHeight - prevHeight + if (unscaledNextHeight > unscaledPrevHeight) { + growY = unscaledNextHeight - unscaledPrevHeight } else { if (shape.props.growY) { growY = 0 @@ -676,7 +686,8 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { ...shape, props: { ...shape.props, - growY, + // scale the growY + growY: growY * shape.props.scale, }, } } @@ -686,6 +697,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { const prevText = prev.props.text const nextText = next.props.text + // No change to text, font, or size, no need to update update if ( prevText === nextText && prev.props.font === next.props.font && @@ -694,6 +706,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { return } + // If we got rid of the text, cancel out any growY from the prev text if (prevText && !nextText) { return { ...next, @@ -704,23 +717,27 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { } } - const prevWidth = prev.props.w - const prevHeight = prev.props.h - const nextSize = getLabelSize(this.editor, next) - const nextWidth = nextSize.w - const nextHeight = nextSize.h + // Get the prev width and height in unscaled values + const unscaledPrevWidth = prev.props.w / prev.props.scale + const unscaledPrevHeight = prev.props.h / prev.props.scale + const unscaledPrevGrowY = prev.props.growY / prev.props.scale + + // Get the next width and height in unscaled values + const unscaledNextLabelSize = getUnscaledLabelSize(this.editor, next) // When entering the first character in a label (not pasting in multiple characters...) if (!prevText && nextText && nextText.length === 1) { - let w = Math.max(prevWidth, nextWidth) - let h = Math.max(prevHeight, nextHeight) + let unscaledW = Math.max(unscaledPrevWidth, unscaledNextLabelSize.w) + let unscaledH = Math.max(unscaledPrevHeight, unscaledNextLabelSize.h) + + const min = MIN_SIZE_WITH_LABEL // If both the width and height were less than the minimum size, make the shape square - if (prev.props.w < MIN_SIZE_WITH_LABEL && prev.props.h < MIN_SIZE_WITH_LABEL) { - w = Math.max(w, MIN_SIZE_WITH_LABEL) - h = Math.max(h, MIN_SIZE_WITH_LABEL) - w = Math.max(w, h) - h = Math.max(w, h) + if (unscaledPrevWidth < min && unscaledPrevHeight < min) { + unscaledW = Math.max(unscaledW, min) + unscaledH = Math.max(unscaledH, min) + unscaledW = Math.max(unscaledW, unscaledH) + unscaledH = Math.max(unscaledW, unscaledH) } // Don't set a growY—at least, not until we've implemented a growX property @@ -728,8 +745,9 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { ...next, props: { ...next.props, - w, - h, + // Scale the results + w: unscaledW * next.props.scale, + h: unscaledH * next.props.scale, growY: 0, }, } @@ -737,34 +755,39 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { let growY: number | null = null - if (nextHeight > prevHeight) { - growY = nextHeight - prevHeight + if (unscaledNextLabelSize.h > unscaledPrevHeight) { + growY = unscaledNextLabelSize.h - unscaledPrevHeight } else { - if (prev.props.growY) { + if (unscaledPrevGrowY) { growY = 0 } } if (growY !== null) { + const unscaledNextWidth = next.props.w / next.props.scale return { ...next, props: { ...next.props, - growY, - w: Math.max(next.props.w, nextWidth), + // Scale the results + growY: growY * next.props.scale, + w: Math.max(unscaledNextWidth, unscaledNextLabelSize.w) * next.props.scale, }, } } - if (nextWidth > prev.props.w) { + if (unscaledNextLabelSize.w > unscaledPrevWidth) { return { ...next, props: { ...next.props, - w: nextWidth, + // Scale the results + w: unscaledNextLabelSize.w * next.props.scale, }, } } + + // otherwise, no update needed } override onDoubleClick = (shape: TLGeoShape) => { @@ -795,8 +818,8 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { } } -function getLabelSize(editor: Editor, shape: TLGeoShape) { - const text = shape.props.text +function getUnscaledLabelSize(editor: Editor, shape: TLGeoShape) { + const { text, font, size, w } = shape.props if (!text) { return { w: 0, h: 0 } @@ -804,8 +827,8 @@ function getLabelSize(editor: Editor, shape: TLGeoShape) { const minSize = editor.textMeasure.measureText('w', { ...TEXT_PROPS, - fontFamily: FONT_FAMILIES[shape.props.font], - fontSize: LABEL_FONT_SIZES[shape.props.size] * shape.props.scale, + fontFamily: FONT_FAMILIES[font], + fontSize: LABEL_FONT_SIZES[size], maxWidth: 100, // ? }) @@ -817,23 +840,23 @@ function getLabelSize(editor: Editor, shape: TLGeoShape) { xl: 10, } - const size = editor.textMeasure.measureText(text, { + const textSize = editor.textMeasure.measureText(text, { ...TEXT_PROPS, - fontFamily: FONT_FAMILIES[shape.props.font], - fontSize: LABEL_FONT_SIZES[shape.props.size] * shape.props.scale, + fontFamily: FONT_FAMILIES[font], + fontSize: LABEL_FONT_SIZES[size], minWidth: minSize.w, maxWidth: Math.max( // Guard because a DOM nodes can't be less 0 0, // A 'w' width that we're setting as the min-width - Math.ceil(minSize.w + sizes[shape.props.size]), + Math.ceil(minSize.w + sizes[size]), // The actual text size - Math.ceil(shape.props.w - LABEL_PADDING * 2) + Math.ceil(w / shape.props.scale - LABEL_PADDING * 2) ), }) return { - w: size.w + LABEL_PADDING * 2, - h: size.h + LABEL_PADDING * 2, + w: textSize.w + LABEL_PADDING * 2, + h: textSize.h + LABEL_PADDING * 2, } }