import type {Rect} from '../../view-base';
import {rectEqualToRect} from '../../view-base';
import {COLORS, FONT_SIZE, TEXT_PADDING} from '../constants';
const cachedTextWidths = new Map<string, number>();
export function getTextWidth(
context: CanvasRenderingContext2D,
text: string,
): number {
let measuredWidth = cachedTextWidths.get(text);
if (measuredWidth == null) {
measuredWidth = context.measureText(text).width;
cachedTextWidths.set(text, measuredWidth);
}
return ((measuredWidth: any): number);
}
export function trimText(
context: CanvasRenderingContext2D,
text: string,
width: number,
): string | null {
const maxIndex = text.length - 1;
let startIndex = 0;
let stopIndex = maxIndex;
let longestValidIndex = 0;
let longestValidText = null;
while (startIndex <= stopIndex) {
const currentIndex = Math.floor((startIndex + stopIndex) / 2);
const trimmedText =
currentIndex === maxIndex ? text : text.slice(0, currentIndex) + '…';
if (getTextWidth(context, trimmedText) <= width) {
if (longestValidIndex < currentIndex) {
longestValidIndex = currentIndex;
longestValidText = trimmedText;
}
startIndex = currentIndex + 1;
} else {
stopIndex = currentIndex - 1;
}
}
return longestValidText;
}
type TextConfig = {
fillStyle?: string,
fontSize?: number,
textAlign?: 'left' | 'center',
};
export function drawText(
text: string,
context: CanvasRenderingContext2D,
fullRect: Rect,
drawableRect: Rect,
config?: TextConfig,
): void {
const {
fillStyle = COLORS.TEXT_COLOR,
fontSize = FONT_SIZE,
textAlign = 'left',
} = config || {};
if (fullRect.size.width > TEXT_PADDING * 2) {
context.textAlign = textAlign;
context.textBaseline = 'middle';
context.font = `${fontSize}px sans-serif`;
const {x, y} = fullRect.origin;
const trimmedName = trimText(
context,
text,
fullRect.size.width - TEXT_PADDING * 2 + (x < 0 ? x : 0),
);
if (trimmedName !== null) {
context.fillStyle = fillStyle;
const textOverflowsViewableArea = !rectEqualToRect(
drawableRect,
fullRect,
);
if (textOverflowsViewableArea) {
context.save();
context.beginPath();
context.rect(
drawableRect.origin.x,
drawableRect.origin.y,
drawableRect.size.width,
drawableRect.size.height,
);
context.closePath();
context.clip();
}
let textX;
if (textAlign === 'center') {
textX = x + fullRect.size.width / 2 + TEXT_PADDING - (x < 0 ? x : 0);
} else {
textX = x + TEXT_PADDING - (x < 0 ? x : 0);
}
const textY = y + fullRect.size.height / 2;
context.fillText(trimmedName, textX, textY);
if (textOverflowsViewableArea) {
context.restore();
}
}
}
}