chat-layout 1.1.2 → 1.2.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -6
- package/example/chat.ts +6 -1
- package/index.d.mts +24 -6
- package/index.mjs +1553 -410
- package/index.mjs.map +1 -1
- package/package.json +2 -4
package/index.mjs
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { layoutNextLine, layoutWithLines, measureLineStats, measureNaturalWidth, prepareWithSegments } from "@chenglou/pretext";
|
|
2
|
-
import { layoutNextRichInlineLineRange, materializeRichInlineLineRange, measureRichInlineStats, prepareRichInline } from "@chenglou/pretext/rich-inline";
|
|
3
1
|
//#region src/internal/node-registry.ts
|
|
4
2
|
const registry = /* @__PURE__ */ new WeakMap();
|
|
5
3
|
const revisions = /* @__PURE__ */ new WeakMap();
|
|
@@ -816,9 +814,117 @@ var Place = class extends Wrapper {
|
|
|
816
814
|
});
|
|
817
815
|
}
|
|
818
816
|
};
|
|
819
|
-
|
|
817
|
+
//#endregion
|
|
818
|
+
//#region src/nodes/shrinkwrap.ts
|
|
819
|
+
const DEFAULT_TOLERANCE = .5;
|
|
820
|
+
const HEIGHT_EPSILON = 1e-6;
|
|
821
|
+
function withMaxWidth(constraints, maxWidth) {
|
|
822
|
+
return {
|
|
823
|
+
...constraints,
|
|
824
|
+
maxWidth
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function computeShrinkwrapWidth(measure, lowerBound, upperBound, referenceHeight, tolerance = DEFAULT_TOLERANCE) {
|
|
828
|
+
const minWidth = Math.min(lowerBound, upperBound);
|
|
829
|
+
const maxWidth = Math.max(lowerBound, upperBound);
|
|
830
|
+
const effectiveTolerance = Math.max(tolerance, HEIGHT_EPSILON);
|
|
831
|
+
const lowerBoundBox = measure(minWidth);
|
|
832
|
+
if (lowerBoundBox.height <= referenceHeight + HEIGHT_EPSILON) return {
|
|
833
|
+
maxWidth: minWidth,
|
|
834
|
+
box: lowerBoundBox
|
|
835
|
+
};
|
|
836
|
+
let lo = minWidth;
|
|
837
|
+
let hi = maxWidth;
|
|
838
|
+
let hiBox = measure(maxWidth);
|
|
839
|
+
while (hi - lo > effectiveTolerance) {
|
|
840
|
+
const probeWidth = (lo + hi) / 2;
|
|
841
|
+
const probeBox = measure(probeWidth);
|
|
842
|
+
if (probeBox.height <= referenceHeight + HEIGHT_EPSILON) {
|
|
843
|
+
hi = probeWidth;
|
|
844
|
+
hiBox = probeBox;
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
lo = probeWidth;
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
maxWidth: hi,
|
|
851
|
+
box: hiBox
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Shrinks a single child to the narrowest width that does not increase its reference height.
|
|
856
|
+
*/
|
|
857
|
+
var ShrinkWrap = class extends Wrapper {
|
|
858
|
+
constructor(inner, options = {}) {
|
|
859
|
+
super(inner);
|
|
860
|
+
this.options = options;
|
|
861
|
+
}
|
|
862
|
+
measure(ctx) {
|
|
863
|
+
const constraints = ctx.constraints;
|
|
864
|
+
const availableWidth = constraints?.maxWidth;
|
|
865
|
+
if (availableWidth == null) {
|
|
866
|
+
const childConstraints = constraints == null ? void 0 : { ...constraints };
|
|
867
|
+
const childBox = ctx.measureNode(this.inner, childConstraints);
|
|
868
|
+
this.#writeLayout(ctx, childBox, childConstraints);
|
|
869
|
+
return childBox;
|
|
870
|
+
}
|
|
871
|
+
const boundedConstraints = constraints == null ? { maxWidth: availableWidth } : constraints;
|
|
872
|
+
const referenceConstraints = { ...boundedConstraints };
|
|
873
|
+
const referenceBox = ctx.measureNode(this.inner, referenceConstraints);
|
|
874
|
+
let lowerBound = measureNodeMinContent(ctx, this.inner, boundedConstraints).width;
|
|
875
|
+
const preferredMinWidth = this.options.preferredMinWidth == null ? void 0 : Math.max(0, this.options.preferredMinWidth);
|
|
876
|
+
if (preferredMinWidth != null && preferredMinWidth <= availableWidth) lowerBound = Math.max(lowerBound, preferredMinWidth);
|
|
877
|
+
if (boundedConstraints.minWidth != null) lowerBound = Math.max(lowerBound, boundedConstraints.minWidth);
|
|
878
|
+
if (lowerBound >= availableWidth) {
|
|
879
|
+
this.#writeLayout(ctx, referenceBox, referenceConstraints);
|
|
880
|
+
return referenceBox;
|
|
881
|
+
}
|
|
882
|
+
const finalConstraints = withMaxWidth(boundedConstraints, computeShrinkwrapWidth((maxWidth) => ctx.measureNode(this.inner, withMaxWidth(boundedConstraints, maxWidth)), lowerBound, availableWidth, referenceBox.height, this.options.tolerance ?? DEFAULT_TOLERANCE).maxWidth);
|
|
883
|
+
const finalBox = ctx.measureNode(this.inner, finalConstraints);
|
|
884
|
+
this.#writeLayout(ctx, finalBox, finalConstraints);
|
|
885
|
+
return finalBox;
|
|
886
|
+
}
|
|
887
|
+
measureMinContent(ctx) {
|
|
888
|
+
return measureNodeMinContent(ctx, this.inner);
|
|
889
|
+
}
|
|
890
|
+
draw(ctx, x, y) {
|
|
891
|
+
const layoutResult = readLayoutResult(this, ctx);
|
|
892
|
+
if (!layoutResult) return this.inner.draw(ctx, x, y);
|
|
893
|
+
const childResult = getSingleChildLayout(layoutResult);
|
|
894
|
+
if (!childResult) return false;
|
|
895
|
+
return childResult.node.draw(withConstraints(ctx, childResult.constraints), x + childResult.rect.x, y + childResult.rect.y);
|
|
896
|
+
}
|
|
897
|
+
hittest(ctx, test) {
|
|
898
|
+
const layoutResult = readLayoutResult(this, ctx);
|
|
899
|
+
if (!layoutResult) return false;
|
|
900
|
+
const hit = findChildAtPoint(layoutResult.children, test.x, test.y, "rect");
|
|
901
|
+
if (!hit) return false;
|
|
902
|
+
return hit.child.node.hittest(withConstraints(ctx, hit.child.constraints), {
|
|
903
|
+
...test,
|
|
904
|
+
x: hit.localX,
|
|
905
|
+
y: hit.localY
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
#writeLayout(ctx, childBox, childConstraints) {
|
|
909
|
+
const childRect = createRect(0, 0, childBox.width, childBox.height);
|
|
910
|
+
writeLayoutResult(this, ctx, {
|
|
911
|
+
containerBox: childRect,
|
|
912
|
+
contentBox: childRect,
|
|
913
|
+
children: [{
|
|
914
|
+
node: this.inner,
|
|
915
|
+
rect: childRect,
|
|
916
|
+
contentBox: childRect,
|
|
917
|
+
constraints: childConstraints
|
|
918
|
+
}],
|
|
919
|
+
constraints: ctx.constraints
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
Number.POSITIVE_INFINITY;
|
|
820
924
|
const MIN_CONTENT_WIDTH_EPSILON = .001;
|
|
821
925
|
let sharedGraphemeSegmenter;
|
|
926
|
+
const fontShiftCache = /* @__PURE__ */ new Map();
|
|
927
|
+
const ellipsisWidthCache = /* @__PURE__ */ new Map();
|
|
822
928
|
function readLruValue$1(cache, key) {
|
|
823
929
|
const cached = cache.get(key);
|
|
824
930
|
if (cached == null) return;
|
|
@@ -836,11 +942,21 @@ function writeLruValue$1(cache, key, value, capacity) {
|
|
|
836
942
|
return value;
|
|
837
943
|
}
|
|
838
944
|
function measureFontShift(ctx) {
|
|
945
|
+
const font = ctx.graphics.font;
|
|
946
|
+
const cached = fontShiftCache.get(font);
|
|
947
|
+
if (cached != null) return cached;
|
|
839
948
|
const { fontBoundingBoxAscent: ascent = 0, fontBoundingBoxDescent: descent = 0 } = ctx.graphics.measureText("M");
|
|
840
|
-
|
|
949
|
+
const shift = ascent - descent;
|
|
950
|
+
fontShiftCache.set(font, shift);
|
|
951
|
+
return shift;
|
|
841
952
|
}
|
|
842
953
|
function measureEllipsisWidth(ctx) {
|
|
843
|
-
|
|
954
|
+
const font = ctx.graphics.font;
|
|
955
|
+
const cached = ellipsisWidthCache.get(font);
|
|
956
|
+
if (cached != null) return cached;
|
|
957
|
+
const width = ctx.graphics.measureText("…").width;
|
|
958
|
+
ellipsisWidthCache.set(font, width);
|
|
959
|
+
return width;
|
|
844
960
|
}
|
|
845
961
|
function getGraphemeSegmenter() {
|
|
846
962
|
if (sharedGraphemeSegmenter !== void 0) return sharedGraphemeSegmenter;
|
|
@@ -923,79 +1039,1025 @@ function selectEllipsisUnitCounts({ position, prefixWidths, suffixWidths, unitCo
|
|
|
923
1039
|
suffixCount
|
|
924
1040
|
};
|
|
925
1041
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
1042
|
+
function resolveEllipsisSelection({ widths, ellipsisWidth, maxWidth, position }) {
|
|
1043
|
+
if (ellipsisWidth > maxWidth) return;
|
|
1044
|
+
const prefixWidths = buildPrefixWidths(widths);
|
|
1045
|
+
const suffixWidths = buildSuffixWidths(widths);
|
|
1046
|
+
const { prefixCount, suffixCount } = selectEllipsisUnitCounts({
|
|
1047
|
+
position,
|
|
1048
|
+
prefixWidths,
|
|
1049
|
+
suffixWidths,
|
|
1050
|
+
unitCount: widths.length,
|
|
1051
|
+
availableWidth: Math.max(0, maxWidth - ellipsisWidth)
|
|
1052
|
+
});
|
|
1053
|
+
return {
|
|
1054
|
+
prefixCount,
|
|
1055
|
+
suffixCount,
|
|
1056
|
+
width: position === "start" ? ellipsisWidth + (suffixWidths[suffixCount] ?? 0) : position === "middle" ? (prefixWidths[prefixCount] ?? 0) + ellipsisWidth + (suffixWidths[suffixCount] ?? 0) : (prefixWidths[prefixCount] ?? 0) + ellipsisWidth
|
|
1057
|
+
};
|
|
933
1058
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1059
|
+
//#endregion
|
|
1060
|
+
//#region src/text/inline-engine.ts
|
|
1061
|
+
const LINE_FIT_EPSILON = .005;
|
|
1062
|
+
const PREPARED_INLINE_CACHE_CAPACITY = 512;
|
|
1063
|
+
const PREPARED_LINE_STATS_CACHE_CAPACITY = 16;
|
|
1064
|
+
const kinsokuStart = new Set([
|
|
1065
|
+
",",
|
|
1066
|
+
".",
|
|
1067
|
+
"!",
|
|
1068
|
+
":",
|
|
1069
|
+
";",
|
|
1070
|
+
"?",
|
|
1071
|
+
"、",
|
|
1072
|
+
"。",
|
|
1073
|
+
"・",
|
|
1074
|
+
")",
|
|
1075
|
+
"〕",
|
|
1076
|
+
"〉",
|
|
1077
|
+
"》",
|
|
1078
|
+
"」",
|
|
1079
|
+
"』",
|
|
1080
|
+
"】",
|
|
1081
|
+
"〗",
|
|
1082
|
+
"〙",
|
|
1083
|
+
"〛",
|
|
1084
|
+
"ー",
|
|
1085
|
+
"々",
|
|
1086
|
+
"〻",
|
|
1087
|
+
"ゝ",
|
|
1088
|
+
"ゞ",
|
|
1089
|
+
"ヽ",
|
|
1090
|
+
"ヾ"
|
|
1091
|
+
]);
|
|
1092
|
+
const kinsokuEnd = new Set([
|
|
1093
|
+
"\"",
|
|
1094
|
+
"(",
|
|
1095
|
+
"[",
|
|
1096
|
+
"{",
|
|
1097
|
+
"“",
|
|
1098
|
+
"‘",
|
|
1099
|
+
"«",
|
|
1100
|
+
"‹",
|
|
1101
|
+
"(",
|
|
1102
|
+
"〔",
|
|
1103
|
+
"〈",
|
|
1104
|
+
"《",
|
|
1105
|
+
"「",
|
|
1106
|
+
"『",
|
|
1107
|
+
"【",
|
|
1108
|
+
"〖",
|
|
1109
|
+
"〘",
|
|
1110
|
+
"〚"
|
|
1111
|
+
]);
|
|
1112
|
+
const leftStickyPunctuation = new Set([
|
|
1113
|
+
".",
|
|
1114
|
+
",",
|
|
1115
|
+
"!",
|
|
1116
|
+
"?",
|
|
1117
|
+
":",
|
|
1118
|
+
";",
|
|
1119
|
+
"،",
|
|
1120
|
+
"؛",
|
|
1121
|
+
"؟",
|
|
1122
|
+
"।",
|
|
1123
|
+
"॥",
|
|
1124
|
+
"၊",
|
|
1125
|
+
"။",
|
|
1126
|
+
"၌",
|
|
1127
|
+
"၍",
|
|
1128
|
+
"၏",
|
|
1129
|
+
")",
|
|
1130
|
+
"]",
|
|
1131
|
+
"}",
|
|
1132
|
+
"%",
|
|
1133
|
+
"\"",
|
|
1134
|
+
"”",
|
|
1135
|
+
"’",
|
|
1136
|
+
"»",
|
|
1137
|
+
"›",
|
|
1138
|
+
"…"
|
|
1139
|
+
]);
|
|
1140
|
+
const keepAllGlueChars = new Set([
|
|
1141
|
+
"\xA0",
|
|
1142
|
+
" ",
|
|
1143
|
+
"",
|
|
1144
|
+
""
|
|
1145
|
+
]);
|
|
1146
|
+
const closingQuoteChars = new Set([
|
|
1147
|
+
"”",
|
|
1148
|
+
"’",
|
|
1149
|
+
"»",
|
|
1150
|
+
"›",
|
|
1151
|
+
"」",
|
|
1152
|
+
"』",
|
|
1153
|
+
"】"
|
|
1154
|
+
]);
|
|
1155
|
+
const cjkCodePointRanges = [
|
|
1156
|
+
[19968, 40959],
|
|
1157
|
+
[13312, 19903],
|
|
1158
|
+
[131072, 173791],
|
|
1159
|
+
[173824, 177983],
|
|
1160
|
+
[177984, 178207],
|
|
1161
|
+
[178208, 183983],
|
|
1162
|
+
[183984, 191471],
|
|
1163
|
+
[191472, 192093],
|
|
1164
|
+
[194560, 195103],
|
|
1165
|
+
[196608, 201551],
|
|
1166
|
+
[201552, 205743],
|
|
1167
|
+
[205744, 210041],
|
|
1168
|
+
[63744, 64255],
|
|
1169
|
+
[12288, 12351],
|
|
1170
|
+
[12352, 12447],
|
|
1171
|
+
[12448, 12543],
|
|
1172
|
+
[44032, 55215],
|
|
1173
|
+
[65280, 65519]
|
|
1174
|
+
];
|
|
1175
|
+
let sharedMeasureContext = null;
|
|
1176
|
+
let sharedWordSegmenter;
|
|
1177
|
+
const textWidthCache = /* @__PURE__ */ new Map();
|
|
1178
|
+
const preparedInlineCache = /* @__PURE__ */ new Map();
|
|
1179
|
+
function getMeasureContext() {
|
|
1180
|
+
if (sharedMeasureContext != null) return sharedMeasureContext;
|
|
1181
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
1182
|
+
const ctx = new OffscreenCanvas(1, 1).getContext("2d");
|
|
1183
|
+
if (ctx != null) {
|
|
1184
|
+
sharedMeasureContext = ctx;
|
|
1185
|
+
return ctx;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
if (typeof document !== "undefined") {
|
|
1189
|
+
const ctx = document.createElement("canvas").getContext("2d");
|
|
1190
|
+
if (ctx != null) {
|
|
1191
|
+
sharedMeasureContext = ctx;
|
|
1192
|
+
return ctx;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
throw new Error("Text measurement requires OffscreenCanvas or a DOM canvas context.");
|
|
1196
|
+
}
|
|
1197
|
+
function getTextWidthCache(font) {
|
|
1198
|
+
let cache = textWidthCache.get(font);
|
|
1199
|
+
if (cache == null) {
|
|
1200
|
+
cache = /* @__PURE__ */ new Map();
|
|
1201
|
+
textWidthCache.set(font, cache);
|
|
1202
|
+
}
|
|
1203
|
+
return cache;
|
|
1204
|
+
}
|
|
1205
|
+
function measureTextWidth(font, text) {
|
|
1206
|
+
const cache = getTextWidthCache(font);
|
|
1207
|
+
const cached = cache.get(text);
|
|
937
1208
|
if (cached != null) return cached;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1209
|
+
const ctx = getMeasureContext();
|
|
1210
|
+
ctx.font = font;
|
|
1211
|
+
const width = ctx.measureText(text).width;
|
|
1212
|
+
cache.set(text, width);
|
|
1213
|
+
return width;
|
|
1214
|
+
}
|
|
1215
|
+
function isCollapsibleWhitespace(text) {
|
|
1216
|
+
return /^[ \t\n\f\r]+$/u.test(text);
|
|
1217
|
+
}
|
|
1218
|
+
function isPreservedWhitespaceGrapheme(text) {
|
|
1219
|
+
return text === " " || text === " ";
|
|
1220
|
+
}
|
|
1221
|
+
function normalizePreWrapText(text) {
|
|
1222
|
+
if (!/[\r\f]/.test(text)) return text.replace(/\r\n/g, "\n");
|
|
1223
|
+
return text.replace(/\r\n/g, "\n").replace(/[\r\f]/g, "\n");
|
|
1224
|
+
}
|
|
1225
|
+
function getLastCodePoint(text) {
|
|
1226
|
+
if (text.length === 0) return null;
|
|
1227
|
+
const codePoints = Array.from(text);
|
|
1228
|
+
return codePoints[codePoints.length - 1] ?? null;
|
|
1229
|
+
}
|
|
1230
|
+
function isCJKCodePoint(codePoint) {
|
|
1231
|
+
for (const [start, end] of cjkCodePointRanges) if (codePoint >= start && codePoint <= end) return true;
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
function isCJK(text) {
|
|
1235
|
+
for (const char of text) {
|
|
1236
|
+
const codePoint = char.codePointAt(0);
|
|
1237
|
+
if (codePoint != null && isCJKCodePoint(codePoint)) return true;
|
|
1238
|
+
}
|
|
1239
|
+
return false;
|
|
1240
|
+
}
|
|
1241
|
+
function endsWithClosingQuote(text) {
|
|
1242
|
+
const last = getLastCodePoint(text);
|
|
1243
|
+
return last != null && closingQuoteChars.has(last);
|
|
1244
|
+
}
|
|
1245
|
+
function endsWithKeepAllGlueText(text) {
|
|
1246
|
+
const last = getLastCodePoint(text);
|
|
1247
|
+
return last != null && keepAllGlueChars.has(last);
|
|
1248
|
+
}
|
|
1249
|
+
function endsWithLineStartProhibitedText(text) {
|
|
1250
|
+
const last = getLastCodePoint(text);
|
|
1251
|
+
return last != null && (kinsokuStart.has(last) || leftStickyPunctuation.has(last));
|
|
1252
|
+
}
|
|
1253
|
+
function canContinueKeepAllTextRun(text) {
|
|
1254
|
+
return !endsWithLineStartProhibitedText(text) && !endsWithKeepAllGlueText(text);
|
|
1255
|
+
}
|
|
1256
|
+
function getSharedWordSegmenter() {
|
|
1257
|
+
if (sharedWordSegmenter !== void 0) return sharedWordSegmenter;
|
|
1258
|
+
sharedWordSegmenter = typeof Intl.Segmenter === "function" ? new Intl.Segmenter(void 0, { granularity: "word" }) : null;
|
|
1259
|
+
return sharedWordSegmenter;
|
|
1260
|
+
}
|
|
1261
|
+
function joinAtomText(atoms, start = 0, end = atoms.length) {
|
|
1262
|
+
let text = "";
|
|
1263
|
+
for (let index = start; index < end; index += 1) {
|
|
1264
|
+
const atom = atoms[index];
|
|
1265
|
+
if (atom != null) text += atom.text;
|
|
1266
|
+
}
|
|
1267
|
+
return text;
|
|
1268
|
+
}
|
|
1269
|
+
function measureAtomSequenceWidth(atoms) {
|
|
1270
|
+
if (atoms.length === 0) return 0;
|
|
1271
|
+
let width = 0;
|
|
1272
|
+
let currentFont = atoms[0].font;
|
|
1273
|
+
let currentText = "";
|
|
1274
|
+
for (const atom of atoms) {
|
|
1275
|
+
if (atom.font !== currentFont && currentText.length > 0) {
|
|
1276
|
+
width += measureTextWidth(currentFont, currentText);
|
|
1277
|
+
currentFont = atom.font;
|
|
1278
|
+
currentText = "";
|
|
1279
|
+
}
|
|
1280
|
+
currentText += atom.text;
|
|
1281
|
+
width += atom.extraWidthAfter;
|
|
1282
|
+
}
|
|
1283
|
+
if (currentText.length > 0) width += measureTextWidth(currentFont, currentText);
|
|
1284
|
+
return width;
|
|
1285
|
+
}
|
|
1286
|
+
function pushTextPartAtoms(target, text, font, itemIndex, atomicGroupId, extraWidth) {
|
|
1287
|
+
const graphemes = splitGraphemes(text);
|
|
1288
|
+
for (let index = 0; index < graphemes.length; index += 1) {
|
|
1289
|
+
const grapheme = graphemes[index] ?? "";
|
|
1290
|
+
target.push({
|
|
1291
|
+
text: grapheme,
|
|
1292
|
+
font,
|
|
1293
|
+
itemIndex,
|
|
1294
|
+
width: measureTextWidth(font, grapheme),
|
|
1295
|
+
extraWidthAfter: index === graphemes.length - 1 ? extraWidth : 0,
|
|
1296
|
+
kind: "text",
|
|
1297
|
+
preservesLineEnd: false,
|
|
1298
|
+
atomicGroupId
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function buildCollapsedWhitespaceAtoms(items) {
|
|
1303
|
+
const chunks = [[]];
|
|
1304
|
+
const atoms = chunks[0];
|
|
1305
|
+
let pendingSpace = null;
|
|
1306
|
+
let lastVisible = null;
|
|
1307
|
+
for (const item of items) {
|
|
1308
|
+
const atomicGroupId = item.breakMode === "never" ? item.itemIndex + 1 : null;
|
|
1309
|
+
const parts = item.text.match(/[ \t\n\f\r]+|[^ \t\n\f\r]+/gu) ?? [];
|
|
1310
|
+
for (let partIndex = 0; partIndex < parts.length; partIndex += 1) {
|
|
1311
|
+
const part = parts[partIndex] ?? "";
|
|
1312
|
+
if (isCollapsibleWhitespace(part)) {
|
|
1313
|
+
if (lastVisible != null) pendingSpace = {
|
|
1314
|
+
font: lastVisible.font,
|
|
1315
|
+
itemIndex: lastVisible.itemIndex,
|
|
1316
|
+
atomicGroupId
|
|
1317
|
+
};
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
if (pendingSpace != null) {
|
|
1321
|
+
atoms.push({
|
|
1322
|
+
text: " ",
|
|
1323
|
+
font: pendingSpace.font,
|
|
1324
|
+
itemIndex: pendingSpace.itemIndex,
|
|
1325
|
+
width: measureTextWidth(pendingSpace.font, " "),
|
|
1326
|
+
extraWidthAfter: 0,
|
|
1327
|
+
kind: "space",
|
|
1328
|
+
preservesLineEnd: false,
|
|
1329
|
+
atomicGroupId: pendingSpace.atomicGroupId
|
|
1330
|
+
});
|
|
1331
|
+
pendingSpace = null;
|
|
1332
|
+
}
|
|
1333
|
+
pushTextPartAtoms(atoms, part, item.font, item.itemIndex, atomicGroupId, partIndex === parts.length - 1 ? item.extraWidth : 0);
|
|
1334
|
+
lastVisible = {
|
|
1335
|
+
font: item.font,
|
|
1336
|
+
itemIndex: item.itemIndex,
|
|
1337
|
+
atomicGroupId
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return chunks;
|
|
1342
|
+
}
|
|
1343
|
+
function buildPreWrapAtoms(items) {
|
|
1344
|
+
const chunks = [[]];
|
|
1345
|
+
let currentChunk = chunks[0];
|
|
1346
|
+
for (const item of items) {
|
|
1347
|
+
const atomicGroupId = item.breakMode === "never" ? item.itemIndex + 1 : null;
|
|
1348
|
+
const graphemes = splitGraphemes(normalizePreWrapText(item.text));
|
|
1349
|
+
for (let index = 0; index < graphemes.length; index += 1) {
|
|
1350
|
+
const grapheme = graphemes[index] ?? "";
|
|
1351
|
+
if (grapheme === "\n") {
|
|
1352
|
+
currentChunk = [];
|
|
1353
|
+
chunks.push(currentChunk);
|
|
1354
|
+
continue;
|
|
1355
|
+
}
|
|
1356
|
+
const isSpace = isPreservedWhitespaceGrapheme(grapheme);
|
|
1357
|
+
currentChunk.push({
|
|
1358
|
+
text: grapheme,
|
|
1359
|
+
font: item.font,
|
|
1360
|
+
itemIndex: item.itemIndex,
|
|
1361
|
+
width: measureTextWidth(item.font, grapheme),
|
|
1362
|
+
extraWidthAfter: index === graphemes.length - 1 ? item.extraWidth : 0,
|
|
1363
|
+
kind: isSpace ? "space" : "text",
|
|
1364
|
+
preservesLineEnd: isSpace,
|
|
1365
|
+
atomicGroupId
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return chunks;
|
|
1370
|
+
}
|
|
1371
|
+
function buildBaseCjkUnits(atoms) {
|
|
1372
|
+
const units = [];
|
|
1373
|
+
let current = [];
|
|
1374
|
+
let currentContainsCJK = false;
|
|
1375
|
+
let currentEndsWithClosingQuote = false;
|
|
1376
|
+
let currentIsSingleKinsokuEnd = false;
|
|
1377
|
+
const flushCurrent = () => {
|
|
1378
|
+
if (current.length === 0) return;
|
|
1379
|
+
units.push(current);
|
|
1380
|
+
current = [];
|
|
1381
|
+
currentContainsCJK = false;
|
|
1382
|
+
currentEndsWithClosingQuote = false;
|
|
1383
|
+
currentIsSingleKinsokuEnd = false;
|
|
1384
|
+
};
|
|
1385
|
+
for (const atom of atoms) {
|
|
1386
|
+
const atomContainsCJK = isCJK(atom.text);
|
|
1387
|
+
if (current.length === 0) {
|
|
1388
|
+
current = [atom];
|
|
1389
|
+
currentContainsCJK = atomContainsCJK;
|
|
1390
|
+
currentEndsWithClosingQuote = endsWithClosingQuote(atom.text);
|
|
1391
|
+
currentIsSingleKinsokuEnd = kinsokuEnd.has(atom.text);
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
if (currentIsSingleKinsokuEnd || kinsokuStart.has(atom.text) || leftStickyPunctuation.has(atom.text) || atomContainsCJK && currentEndsWithClosingQuote) {
|
|
1395
|
+
current.push(atom);
|
|
1396
|
+
currentContainsCJK = currentContainsCJK || atomContainsCJK;
|
|
1397
|
+
currentEndsWithClosingQuote = leftStickyPunctuation.has(atom.text) ? currentEndsWithClosingQuote || endsWithClosingQuote(atom.text) : endsWithClosingQuote(atom.text);
|
|
1398
|
+
currentIsSingleKinsokuEnd = false;
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
if (!currentContainsCJK && !atomContainsCJK) {
|
|
1402
|
+
current.push(atom);
|
|
1403
|
+
currentEndsWithClosingQuote = endsWithClosingQuote(atom.text);
|
|
1404
|
+
currentIsSingleKinsokuEnd = false;
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
flushCurrent();
|
|
1408
|
+
current = [atom];
|
|
1409
|
+
currentContainsCJK = atomContainsCJK;
|
|
1410
|
+
currentEndsWithClosingQuote = endsWithClosingQuote(atom.text);
|
|
1411
|
+
currentIsSingleKinsokuEnd = kinsokuEnd.has(atom.text);
|
|
1412
|
+
}
|
|
1413
|
+
flushCurrent();
|
|
1414
|
+
return units;
|
|
942
1415
|
}
|
|
943
|
-
function
|
|
944
|
-
|
|
945
|
-
|
|
1416
|
+
function mergeKeepAllUnits(units) {
|
|
1417
|
+
if (units.length <= 1) return units.slice();
|
|
1418
|
+
const merged = [];
|
|
1419
|
+
let current = units[0].slice();
|
|
1420
|
+
let currentText = joinAtomText(current);
|
|
1421
|
+
let currentContainsCJK = isCJK(currentText);
|
|
1422
|
+
let currentCanContinue = canContinueKeepAllTextRun(currentText);
|
|
1423
|
+
const flush = () => {
|
|
1424
|
+
merged.push(current);
|
|
1425
|
+
};
|
|
1426
|
+
for (let index = 1; index < units.length; index += 1) {
|
|
1427
|
+
const next = units[index];
|
|
1428
|
+
const nextText = joinAtomText(next);
|
|
1429
|
+
const nextContainsCJK = isCJK(nextText);
|
|
1430
|
+
const nextCanContinue = canContinueKeepAllTextRun(nextText);
|
|
1431
|
+
if (currentContainsCJK && currentCanContinue) {
|
|
1432
|
+
current = [...current, ...next];
|
|
1433
|
+
currentText += nextText;
|
|
1434
|
+
currentContainsCJK = currentContainsCJK || nextContainsCJK;
|
|
1435
|
+
currentCanContinue = nextCanContinue;
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
flush();
|
|
1439
|
+
current = next.slice();
|
|
1440
|
+
currentText = nextText;
|
|
1441
|
+
currentContainsCJK = nextContainsCJK;
|
|
1442
|
+
currentCanContinue = nextCanContinue;
|
|
1443
|
+
}
|
|
1444
|
+
flush();
|
|
1445
|
+
return merged;
|
|
1446
|
+
}
|
|
1447
|
+
function splitAtomsByWordSegments(atoms) {
|
|
1448
|
+
if (atoms.length <= 1) return atoms.length === 0 ? [] : [atoms.slice()];
|
|
1449
|
+
const segmenter = getSharedWordSegmenter();
|
|
1450
|
+
if (segmenter == null) return [atoms.slice()];
|
|
1451
|
+
const text = joinAtomText(atoms);
|
|
1452
|
+
const offsets = [0];
|
|
1453
|
+
for (let index = 0; index < atoms.length; index += 1) offsets.push((offsets[index] ?? 0) + (atoms[index]?.text.length ?? 0));
|
|
1454
|
+
const units = [];
|
|
1455
|
+
let atomStart = 0;
|
|
1456
|
+
let atomEnd = 0;
|
|
1457
|
+
for (const segment of segmenter.segment(text)) {
|
|
1458
|
+
const start = segment.index;
|
|
1459
|
+
const end = start + segment.segment.length;
|
|
1460
|
+
while ((offsets[atomStart] ?? 0) < start) atomStart += 1;
|
|
1461
|
+
atomEnd = atomStart;
|
|
1462
|
+
while ((offsets[atomEnd] ?? 0) < end) atomEnd += 1;
|
|
1463
|
+
if (atomStart < atomEnd) units.push(atoms.slice(atomStart, atomEnd));
|
|
1464
|
+
atomStart = atomEnd;
|
|
1465
|
+
}
|
|
1466
|
+
return units.length > 0 ? units : [atoms.slice()];
|
|
1467
|
+
}
|
|
1468
|
+
function makeTextUnit(atoms, atomic = false) {
|
|
1469
|
+
const width = measureAtomSequenceWidth(atoms);
|
|
946
1470
|
return {
|
|
947
|
-
|
|
948
|
-
|
|
1471
|
+
kind: "text",
|
|
1472
|
+
atoms,
|
|
1473
|
+
width,
|
|
1474
|
+
fitEndWidth: width,
|
|
1475
|
+
paintEndWidth: width,
|
|
1476
|
+
breakAfter: true,
|
|
1477
|
+
breakable: !atomic && atoms.length > 1,
|
|
1478
|
+
atomic
|
|
949
1479
|
};
|
|
950
1480
|
}
|
|
951
|
-
function
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1481
|
+
function makeSpaceUnit(atoms) {
|
|
1482
|
+
const width = measureAtomSequenceWidth(atoms);
|
|
1483
|
+
return {
|
|
1484
|
+
kind: "space",
|
|
1485
|
+
atoms,
|
|
1486
|
+
width,
|
|
1487
|
+
fitEndWidth: 0,
|
|
1488
|
+
paintEndWidth: atoms.every((atom) => atom.preservesLineEnd) ? width : 0,
|
|
1489
|
+
breakAfter: true,
|
|
1490
|
+
breakable: atoms.length > 1,
|
|
1491
|
+
atomic: false
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
function tokenizeChunkAtoms(chunkAtoms, wordBreak) {
|
|
1495
|
+
const units = [];
|
|
1496
|
+
let index = 0;
|
|
1497
|
+
while (index < chunkAtoms.length) {
|
|
1498
|
+
const atom = chunkAtoms[index];
|
|
1499
|
+
if (atom.atomicGroupId != null) {
|
|
1500
|
+
const start = index;
|
|
1501
|
+
const atomicGroupId = atom.atomicGroupId;
|
|
1502
|
+
while (index < chunkAtoms.length && chunkAtoms[index]?.atomicGroupId === atomicGroupId) index += 1;
|
|
1503
|
+
units.push(makeTextUnit(chunkAtoms.slice(start, index), true));
|
|
1504
|
+
continue;
|
|
962
1505
|
}
|
|
1506
|
+
if (atom.kind === "space") {
|
|
1507
|
+
const start = index;
|
|
1508
|
+
while (index < chunkAtoms.length && chunkAtoms[index]?.kind === "space" && chunkAtoms[index]?.atomicGroupId == null) index += 1;
|
|
1509
|
+
units.push(makeSpaceUnit(chunkAtoms.slice(start, index)));
|
|
1510
|
+
continue;
|
|
1511
|
+
}
|
|
1512
|
+
const start = index;
|
|
1513
|
+
while (index < chunkAtoms.length && chunkAtoms[index]?.kind === "text" && chunkAtoms[index]?.atomicGroupId == null) index += 1;
|
|
1514
|
+
const textRunAtoms = chunkAtoms.slice(start, index);
|
|
1515
|
+
const runText = joinAtomText(textRunAtoms);
|
|
1516
|
+
const rawUnits = isCJK(runText) ? buildBaseCjkUnits(textRunAtoms) : splitAtomsByWordSegments(textRunAtoms);
|
|
1517
|
+
const normalizedUnits = wordBreak === "keep-all" && isCJK(runText) ? mergeKeepAllUnits(rawUnits) : rawUnits;
|
|
1518
|
+
for (const unitAtoms of normalizedUnits) units.push(makeTextUnit(unitAtoms.slice(), false));
|
|
963
1519
|
}
|
|
964
|
-
return
|
|
1520
|
+
return units;
|
|
965
1521
|
}
|
|
966
|
-
function
|
|
1522
|
+
function buildPreparedInlineLayout(items, whiteSpace, wordBreak) {
|
|
1523
|
+
const atomChunks = whiteSpace === "pre-wrap" ? buildPreWrapAtoms(items) : buildCollapsedWhitespaceAtoms(items);
|
|
1524
|
+
const chunks = [];
|
|
967
1525
|
const units = [];
|
|
968
|
-
for (
|
|
969
|
-
const
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1526
|
+
for (const atomChunk of atomChunks) {
|
|
1527
|
+
const startUnit = units.length;
|
|
1528
|
+
const chunkUnits = tokenizeChunkAtoms(atomChunk, wordBreak);
|
|
1529
|
+
units.push(...chunkUnits);
|
|
1530
|
+
chunks.push({
|
|
1531
|
+
startUnit,
|
|
1532
|
+
endUnit: units.length
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
return {
|
|
1536
|
+
units,
|
|
1537
|
+
chunks,
|
|
1538
|
+
whiteSpace,
|
|
1539
|
+
wordBreak,
|
|
1540
|
+
lineStatsCache: /* @__PURE__ */ new Map(),
|
|
1541
|
+
minContentWidthCache: /* @__PURE__ */ new Map()
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
function readNumberLruValue(cache, key) {
|
|
1545
|
+
const cached = cache.get(key);
|
|
1546
|
+
if (cached == null) return;
|
|
1547
|
+
cache.delete(key);
|
|
1548
|
+
cache.set(key, cached);
|
|
1549
|
+
return cached;
|
|
1550
|
+
}
|
|
1551
|
+
function writeNumberLruValue(cache, key, value, capacity) {
|
|
1552
|
+
if (cache.has(key)) cache.delete(key);
|
|
1553
|
+
else if (cache.size >= capacity) {
|
|
1554
|
+
const firstKey = cache.keys().next().value;
|
|
1555
|
+
if (firstKey != null) cache.delete(firstKey);
|
|
1556
|
+
}
|
|
1557
|
+
cache.set(key, value);
|
|
1558
|
+
return value;
|
|
1559
|
+
}
|
|
1560
|
+
function cloneCursor(cursor) {
|
|
1561
|
+
return {
|
|
1562
|
+
chunkIndex: cursor.chunkIndex,
|
|
1563
|
+
unitIndex: cursor.unitIndex,
|
|
1564
|
+
atomIndex: cursor.atomIndex
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
function cursorEquals(a, b) {
|
|
1568
|
+
return a.chunkIndex === b.chunkIndex && a.unitIndex === b.unitIndex && a.atomIndex === b.atomIndex;
|
|
1569
|
+
}
|
|
1570
|
+
function fits(width, maxWidth) {
|
|
1571
|
+
return width <= maxWidth + LINE_FIT_EPSILON;
|
|
1572
|
+
}
|
|
1573
|
+
function getVisibleEndAfterBreak(chunkIndex, unitIndex, unit) {
|
|
1574
|
+
if (unit.kind === "space" && unit.paintEndWidth === 0) return {
|
|
1575
|
+
chunkIndex,
|
|
1576
|
+
unitIndex,
|
|
1577
|
+
atomIndex: 0
|
|
1578
|
+
};
|
|
1579
|
+
return {
|
|
1580
|
+
chunkIndex,
|
|
1581
|
+
unitIndex: unitIndex + 1,
|
|
1582
|
+
atomIndex: 0
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
function fitPartialUnit(unit, startAtomIndex, lineWidth, maxWidth) {
|
|
1586
|
+
let width = 0;
|
|
1587
|
+
let count = 0;
|
|
1588
|
+
for (let index = startAtomIndex; index < unit.atoms.length; index += 1) {
|
|
1589
|
+
const atom = unit.atoms[index];
|
|
1590
|
+
const atomWidth = atom.width + atom.extraWidthAfter;
|
|
1591
|
+
if (count > 0 && !fits(lineWidth + width + atomWidth, maxWidth)) break;
|
|
1592
|
+
if (count === 0 || fits(lineWidth + width + atomWidth, maxWidth)) {
|
|
1593
|
+
width += atomWidth;
|
|
1594
|
+
count += 1;
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
break;
|
|
1598
|
+
}
|
|
1599
|
+
return {
|
|
1600
|
+
count,
|
|
1601
|
+
width
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
function stepChunkLine(prepared, chunkIndex, startUnitIndex, startAtomIndex, maxWidth) {
|
|
1605
|
+
const chunk = prepared.chunks[chunkIndex];
|
|
1606
|
+
if (chunk == null || startUnitIndex >= chunk.endUnit) return null;
|
|
1607
|
+
const start = {
|
|
1608
|
+
chunkIndex,
|
|
1609
|
+
unitIndex: startUnitIndex,
|
|
1610
|
+
atomIndex: startAtomIndex
|
|
1611
|
+
};
|
|
1612
|
+
let unitIndex = startUnitIndex;
|
|
1613
|
+
let atomIndex = startAtomIndex;
|
|
1614
|
+
let lineWidth = 0;
|
|
1615
|
+
let hasContent = false;
|
|
1616
|
+
let pendingBreak = null;
|
|
1617
|
+
while (unitIndex < chunk.endUnit) {
|
|
1618
|
+
const unit = prepared.units[unitIndex];
|
|
1619
|
+
if (atomIndex > 0) {
|
|
1620
|
+
const fitted = fitPartialUnit(unit, atomIndex, lineWidth, maxWidth);
|
|
1621
|
+
if (fitted.count === 0) {
|
|
1622
|
+
if (hasContent) return {
|
|
1623
|
+
width: lineWidth,
|
|
1624
|
+
start,
|
|
1625
|
+
end: {
|
|
1626
|
+
chunkIndex,
|
|
1627
|
+
unitIndex,
|
|
1628
|
+
atomIndex
|
|
1629
|
+
},
|
|
1630
|
+
next: {
|
|
1631
|
+
chunkIndex,
|
|
1632
|
+
unitIndex,
|
|
1633
|
+
atomIndex
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
const atom = unit.atoms[atomIndex];
|
|
1637
|
+
return {
|
|
1638
|
+
width: atom.width + atom.extraWidthAfter,
|
|
1639
|
+
start,
|
|
1640
|
+
end: {
|
|
1641
|
+
chunkIndex,
|
|
1642
|
+
unitIndex,
|
|
1643
|
+
atomIndex: atomIndex + 1
|
|
1644
|
+
},
|
|
1645
|
+
next: {
|
|
1646
|
+
chunkIndex,
|
|
1647
|
+
unitIndex,
|
|
1648
|
+
atomIndex: atomIndex + 1
|
|
1649
|
+
}
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
if (atomIndex + fitted.count >= unit.atoms.length) {
|
|
1653
|
+
lineWidth += fitted.width;
|
|
1654
|
+
hasContent = true;
|
|
1655
|
+
atomIndex = 0;
|
|
1656
|
+
unitIndex += 1;
|
|
1657
|
+
pendingBreak = {
|
|
1658
|
+
end: {
|
|
1659
|
+
chunkIndex,
|
|
1660
|
+
unitIndex,
|
|
1661
|
+
atomIndex: 0
|
|
1662
|
+
},
|
|
1663
|
+
next: {
|
|
1664
|
+
chunkIndex,
|
|
1665
|
+
unitIndex,
|
|
1666
|
+
atomIndex: 0
|
|
1667
|
+
},
|
|
1668
|
+
width: lineWidth
|
|
1669
|
+
};
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
return {
|
|
1673
|
+
width: lineWidth + fitted.width,
|
|
1674
|
+
start,
|
|
1675
|
+
end: {
|
|
1676
|
+
chunkIndex,
|
|
1677
|
+
unitIndex,
|
|
1678
|
+
atomIndex: atomIndex + fitted.count
|
|
1679
|
+
},
|
|
1680
|
+
next: {
|
|
1681
|
+
chunkIndex,
|
|
1682
|
+
unitIndex,
|
|
1683
|
+
atomIndex: atomIndex + fitted.count
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
if (!hasContent) {
|
|
1688
|
+
if (fits(unit.width, maxWidth) || unit.atomic || !unit.breakable) {
|
|
1689
|
+
lineWidth = unit.width;
|
|
1690
|
+
hasContent = true;
|
|
1691
|
+
const next = {
|
|
1692
|
+
chunkIndex,
|
|
1693
|
+
unitIndex: unitIndex + 1,
|
|
1694
|
+
atomIndex: 0
|
|
1695
|
+
};
|
|
1696
|
+
pendingBreak = {
|
|
1697
|
+
end: getVisibleEndAfterBreak(chunkIndex, unitIndex, unit),
|
|
1698
|
+
next,
|
|
1699
|
+
width: unit.paintEndWidth
|
|
1700
|
+
};
|
|
1701
|
+
unitIndex += 1;
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
const fitted = fitPartialUnit(unit, 0, 0, maxWidth);
|
|
1705
|
+
if (fitted.count >= unit.atoms.length) {
|
|
1706
|
+
lineWidth = fitted.width;
|
|
1707
|
+
hasContent = true;
|
|
1708
|
+
const next = {
|
|
1709
|
+
chunkIndex,
|
|
1710
|
+
unitIndex: unitIndex + 1,
|
|
1711
|
+
atomIndex: 0
|
|
1712
|
+
};
|
|
1713
|
+
pendingBreak = {
|
|
1714
|
+
end: next,
|
|
1715
|
+
next,
|
|
1716
|
+
width: lineWidth
|
|
1717
|
+
};
|
|
1718
|
+
unitIndex += 1;
|
|
979
1719
|
continue;
|
|
980
1720
|
}
|
|
1721
|
+
return {
|
|
1722
|
+
width: fitted.width,
|
|
1723
|
+
start,
|
|
1724
|
+
end: {
|
|
1725
|
+
chunkIndex,
|
|
1726
|
+
unitIndex,
|
|
1727
|
+
atomIndex: fitted.count
|
|
1728
|
+
},
|
|
1729
|
+
next: {
|
|
1730
|
+
chunkIndex,
|
|
1731
|
+
unitIndex,
|
|
1732
|
+
atomIndex: fitted.count
|
|
1733
|
+
}
|
|
1734
|
+
};
|
|
981
1735
|
}
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1736
|
+
if (fits(lineWidth + unit.width, maxWidth)) {
|
|
1737
|
+
const nextWidth = lineWidth + unit.width;
|
|
1738
|
+
lineWidth = nextWidth;
|
|
1739
|
+
const next = {
|
|
1740
|
+
chunkIndex,
|
|
1741
|
+
unitIndex: unitIndex + 1,
|
|
1742
|
+
atomIndex: 0
|
|
1743
|
+
};
|
|
1744
|
+
pendingBreak = {
|
|
1745
|
+
end: getVisibleEndAfterBreak(chunkIndex, unitIndex, unit),
|
|
1746
|
+
next,
|
|
1747
|
+
width: nextWidth - unit.width + unit.paintEndWidth
|
|
1748
|
+
};
|
|
1749
|
+
unitIndex += 1;
|
|
1750
|
+
continue;
|
|
1751
|
+
}
|
|
1752
|
+
if (fits(lineWidth + unit.fitEndWidth, maxWidth)) {
|
|
1753
|
+
const next = {
|
|
1754
|
+
chunkIndex,
|
|
1755
|
+
unitIndex: unitIndex + 1,
|
|
1756
|
+
atomIndex: 0
|
|
1757
|
+
};
|
|
1758
|
+
return {
|
|
1759
|
+
width: lineWidth + unit.paintEndWidth,
|
|
1760
|
+
start,
|
|
1761
|
+
end: getVisibleEndAfterBreak(chunkIndex, unitIndex, unit),
|
|
1762
|
+
next
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
if (pendingBreak != null) return {
|
|
1766
|
+
width: pendingBreak.width,
|
|
1767
|
+
start,
|
|
1768
|
+
end: pendingBreak.end,
|
|
1769
|
+
next: pendingBreak.next
|
|
1770
|
+
};
|
|
1771
|
+
return {
|
|
1772
|
+
width: lineWidth,
|
|
1773
|
+
start,
|
|
1774
|
+
end: {
|
|
1775
|
+
chunkIndex,
|
|
1776
|
+
unitIndex,
|
|
1777
|
+
atomIndex: 0
|
|
1778
|
+
},
|
|
1779
|
+
next: {
|
|
1780
|
+
chunkIndex,
|
|
1781
|
+
unitIndex,
|
|
1782
|
+
atomIndex: 0
|
|
1783
|
+
}
|
|
1784
|
+
};
|
|
986
1785
|
}
|
|
987
|
-
return
|
|
1786
|
+
if (!hasContent) return null;
|
|
1787
|
+
if (pendingBreak != null && pendingBreak.next.unitIndex === chunk.endUnit && pendingBreak.next.atomIndex === 0) return {
|
|
1788
|
+
width: pendingBreak.width,
|
|
1789
|
+
start,
|
|
1790
|
+
end: pendingBreak.end,
|
|
1791
|
+
next: pendingBreak.next
|
|
1792
|
+
};
|
|
1793
|
+
return {
|
|
1794
|
+
width: lineWidth,
|
|
1795
|
+
start,
|
|
1796
|
+
end: {
|
|
1797
|
+
chunkIndex,
|
|
1798
|
+
unitIndex,
|
|
1799
|
+
atomIndex: 0
|
|
1800
|
+
},
|
|
1801
|
+
next: {
|
|
1802
|
+
chunkIndex,
|
|
1803
|
+
unitIndex,
|
|
1804
|
+
atomIndex: 0
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
988
1807
|
}
|
|
989
|
-
function
|
|
990
|
-
|
|
991
|
-
|
|
1808
|
+
function getPreparedLineStart(prepared) {
|
|
1809
|
+
for (let chunkIndex = 0; chunkIndex < prepared.chunks.length; chunkIndex += 1) {
|
|
1810
|
+
const chunk = prepared.chunks[chunkIndex];
|
|
1811
|
+
if (chunk.startUnit === chunk.endUnit) return {
|
|
1812
|
+
chunkIndex,
|
|
1813
|
+
unitIndex: chunk.startUnit,
|
|
1814
|
+
atomIndex: 0
|
|
1815
|
+
};
|
|
1816
|
+
return {
|
|
1817
|
+
chunkIndex,
|
|
1818
|
+
unitIndex: chunk.startUnit,
|
|
1819
|
+
atomIndex: 0
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
function layoutNextPreparedLine(prepared, start, maxWidth) {
|
|
1824
|
+
const chunk = prepared.chunks[start.chunkIndex];
|
|
1825
|
+
if (chunk == null) return null;
|
|
1826
|
+
if (chunk.startUnit === chunk.endUnit) return cursorEquals(start, {
|
|
1827
|
+
chunkIndex: start.chunkIndex,
|
|
1828
|
+
unitIndex: chunk.endUnit,
|
|
1829
|
+
atomIndex: 0
|
|
1830
|
+
}) ? null : {
|
|
1831
|
+
width: 0,
|
|
1832
|
+
start: cloneCursor(start),
|
|
1833
|
+
end: {
|
|
1834
|
+
chunkIndex: start.chunkIndex,
|
|
1835
|
+
unitIndex: chunk.endUnit,
|
|
1836
|
+
atomIndex: 0
|
|
1837
|
+
},
|
|
1838
|
+
next: {
|
|
1839
|
+
chunkIndex: start.chunkIndex,
|
|
1840
|
+
unitIndex: chunk.endUnit,
|
|
1841
|
+
atomIndex: 0
|
|
1842
|
+
}
|
|
1843
|
+
};
|
|
1844
|
+
return stepChunkLine(prepared, start.chunkIndex, start.unitIndex, start.atomIndex, maxWidth);
|
|
1845
|
+
}
|
|
1846
|
+
function walkPreparedLineRanges(prepared, maxWidth, onLine) {
|
|
1847
|
+
let lineCount = 0;
|
|
1848
|
+
for (let chunkIndex = 0; chunkIndex < prepared.chunks.length; chunkIndex += 1) {
|
|
1849
|
+
const chunk = prepared.chunks[chunkIndex];
|
|
1850
|
+
if (chunk.startUnit === chunk.endUnit) {
|
|
1851
|
+
const cursor = {
|
|
1852
|
+
chunkIndex,
|
|
1853
|
+
unitIndex: chunk.startUnit,
|
|
1854
|
+
atomIndex: 0
|
|
1855
|
+
};
|
|
1856
|
+
if (onLine({
|
|
1857
|
+
width: 0,
|
|
1858
|
+
start: cursor,
|
|
1859
|
+
end: cursor,
|
|
1860
|
+
next: cursor
|
|
1861
|
+
}) === false) {
|
|
1862
|
+
lineCount += 1;
|
|
1863
|
+
return lineCount;
|
|
1864
|
+
}
|
|
1865
|
+
lineCount += 1;
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
let cursor = {
|
|
1869
|
+
chunkIndex,
|
|
1870
|
+
unitIndex: chunk.startUnit,
|
|
1871
|
+
atomIndex: 0
|
|
1872
|
+
};
|
|
1873
|
+
while (true) {
|
|
1874
|
+
const line = layoutNextPreparedLine(prepared, cursor, maxWidth);
|
|
1875
|
+
if (line == null) break;
|
|
1876
|
+
if (onLine(line) === false) {
|
|
1877
|
+
lineCount += 1;
|
|
1878
|
+
return lineCount;
|
|
1879
|
+
}
|
|
1880
|
+
lineCount += 1;
|
|
1881
|
+
if (cursorEquals(line.next, cursor)) break;
|
|
1882
|
+
cursor = line.next;
|
|
1883
|
+
if (cursor.unitIndex >= chunk.endUnit && cursor.atomIndex === 0) break;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
return lineCount;
|
|
1887
|
+
}
|
|
1888
|
+
function forEachAtomFromCursorToEnd(prepared, start, cb) {
|
|
1889
|
+
for (let chunkIndex = start.chunkIndex; chunkIndex < prepared.chunks.length; chunkIndex += 1) {
|
|
1890
|
+
const chunk = prepared.chunks[chunkIndex];
|
|
1891
|
+
if (chunk == null) continue;
|
|
1892
|
+
forEachAtomInRange(prepared, chunkIndex === start.chunkIndex ? start : {
|
|
1893
|
+
chunkIndex,
|
|
1894
|
+
unitIndex: chunk.startUnit,
|
|
1895
|
+
atomIndex: 0
|
|
1896
|
+
}, {
|
|
1897
|
+
chunkIndex,
|
|
1898
|
+
unitIndex: chunk.endUnit,
|
|
1899
|
+
atomIndex: 0
|
|
1900
|
+
}, cb);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
function measurePreparedLineStats(prepared, maxWidth) {
|
|
1904
|
+
const cached = readNumberLruValue(prepared.lineStatsCache, maxWidth);
|
|
1905
|
+
if (cached != null) return cached;
|
|
1906
|
+
let lineCount = 0;
|
|
1907
|
+
let maxLineWidth = 0;
|
|
1908
|
+
walkPreparedLineRanges(prepared, maxWidth, (line) => {
|
|
1909
|
+
lineCount += 1;
|
|
1910
|
+
if (line.width > maxLineWidth) maxLineWidth = line.width;
|
|
1911
|
+
});
|
|
1912
|
+
return writeNumberLruValue(prepared.lineStatsCache, maxWidth, {
|
|
1913
|
+
lineCount,
|
|
1914
|
+
maxLineWidth
|
|
1915
|
+
}, PREPARED_LINE_STATS_CACHE_CAPACITY);
|
|
1916
|
+
}
|
|
1917
|
+
function forEachAtomInRange(prepared, start, end, cb) {
|
|
1918
|
+
if (start.chunkIndex !== end.chunkIndex) throw new Error("Atom range iteration only supports a single chunk.");
|
|
1919
|
+
for (let unitIndex = start.unitIndex; unitIndex < end.unitIndex; unitIndex += 1) {
|
|
1920
|
+
const unit = prepared.units[unitIndex];
|
|
1921
|
+
const atomStart = unitIndex === start.unitIndex ? start.atomIndex : 0;
|
|
1922
|
+
const atomEnd = unitIndex === end.unitIndex ? end.atomIndex : unit.atoms.length;
|
|
1923
|
+
for (let atomIndex = atomStart; atomIndex < atomEnd; atomIndex += 1) {
|
|
1924
|
+
const atom = unit.atoms[atomIndex];
|
|
1925
|
+
if (atom != null) cb(atom);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
if (end.unitIndex < prepared.units.length) {
|
|
1929
|
+
const unit = prepared.units[end.unitIndex];
|
|
1930
|
+
if (unit != null && start.unitIndex === end.unitIndex) for (let atomIndex = start.atomIndex; atomIndex < end.atomIndex; atomIndex += 1) {
|
|
1931
|
+
const atom = unit.atoms[atomIndex];
|
|
1932
|
+
if (atom != null) cb(atom);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
function collectAtomsInRange(prepared, start, end) {
|
|
1937
|
+
const atoms = [];
|
|
1938
|
+
if (start.unitIndex === end.unitIndex) {
|
|
1939
|
+
const unit = prepared.units[start.unitIndex];
|
|
1940
|
+
if (unit == null) return atoms;
|
|
1941
|
+
for (let atomIndex = start.atomIndex; atomIndex < end.atomIndex; atomIndex += 1) {
|
|
1942
|
+
const atom = unit.atoms[atomIndex];
|
|
1943
|
+
if (atom != null) atoms.push(atom);
|
|
1944
|
+
}
|
|
1945
|
+
return atoms;
|
|
1946
|
+
}
|
|
1947
|
+
if (prepared.chunks[start.chunkIndex] == null) return atoms;
|
|
1948
|
+
for (let unitIndex = start.unitIndex; unitIndex < end.unitIndex; unitIndex += 1) {
|
|
1949
|
+
const unit = prepared.units[unitIndex];
|
|
1950
|
+
const atomStart = unitIndex === start.unitIndex ? start.atomIndex : 0;
|
|
1951
|
+
for (let atomIndex = atomStart; atomIndex < unit.atoms.length; atomIndex += 1) {
|
|
1952
|
+
const atom = unit.atoms[atomIndex];
|
|
1953
|
+
if (atom != null) atoms.push(atom);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
const endUnit = prepared.units[end.unitIndex];
|
|
1957
|
+
if (endUnit != null) for (let atomIndex = 0; atomIndex < end.atomIndex; atomIndex += 1) {
|
|
1958
|
+
const atom = endUnit.atoms[atomIndex];
|
|
1959
|
+
if (atom != null) atoms.push(atom);
|
|
1960
|
+
}
|
|
1961
|
+
return atoms;
|
|
1962
|
+
}
|
|
1963
|
+
function collectLineAtoms(prepared, line) {
|
|
1964
|
+
return {
|
|
1965
|
+
atoms: collectAtomsInRange(prepared, line.start, line.end),
|
|
1966
|
+
width: line.width
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
function materializePreparedLineText(prepared, line) {
|
|
1970
|
+
return joinAtomText(collectLineAtoms(prepared, line).atoms);
|
|
1971
|
+
}
|
|
1972
|
+
function flattenPreparedLineAtoms(prepared, line) {
|
|
1973
|
+
return collectLineAtoms(prepared, line).atoms;
|
|
1974
|
+
}
|
|
1975
|
+
function measurePreparedMinContentWidth$1(prepared, overflowWrap = "break-word") {
|
|
1976
|
+
const cached = prepared.minContentWidthCache.get(overflowWrap);
|
|
1977
|
+
if (cached != null) return cached;
|
|
1978
|
+
let maxWidth = 0;
|
|
1979
|
+
let maxAnyWidth = 0;
|
|
1980
|
+
for (const unit of prepared.units) {
|
|
1981
|
+
if (unit.width > maxAnyWidth) maxAnyWidth = unit.width;
|
|
1982
|
+
if (unit.kind !== "text") continue;
|
|
1983
|
+
const candidateWidth = overflowWrap === "anywhere" && unit.breakable ? unit.atoms.reduce((widest, atom) => Math.max(widest, atom.width + atom.extraWidthAfter), 0) : unit.width;
|
|
1984
|
+
if (candidateWidth > maxWidth) maxWidth = candidateWidth;
|
|
1985
|
+
}
|
|
1986
|
+
const resolvedWidth = maxWidth > 0 ? maxWidth : maxAnyWidth;
|
|
1987
|
+
prepared.minContentWidthCache.set(overflowWrap, resolvedWidth);
|
|
1988
|
+
return resolvedWidth;
|
|
1989
|
+
}
|
|
1990
|
+
function measureAtomsWidth(atoms) {
|
|
1991
|
+
let total = 0;
|
|
1992
|
+
for (let index = 0; index < atoms.length; index += 1) {
|
|
1993
|
+
const atom = atoms[index];
|
|
1994
|
+
if (atom != null) total += atom.width + atom.extraWidthAfter;
|
|
1995
|
+
}
|
|
1996
|
+
return total;
|
|
1997
|
+
}
|
|
1998
|
+
function readPreparedInlineLayout(key, items, whiteSpace, wordBreak) {
|
|
1999
|
+
const cached = readLruValue$1(preparedInlineCache, key);
|
|
2000
|
+
if (cached != null) return cached;
|
|
2001
|
+
return writeLruValue$1(preparedInlineCache, key, buildPreparedInlineLayout(items, whiteSpace, wordBreak), PREPARED_INLINE_CACHE_CAPACITY);
|
|
2002
|
+
}
|
|
2003
|
+
function getPlainPreparedKey(text, font, whiteSpace, wordBreak) {
|
|
2004
|
+
return `plain\u0000${font}\u0000${whiteSpace}\u0000${wordBreak}\u0000${text}`;
|
|
2005
|
+
}
|
|
2006
|
+
function getRichPreparedKey(spans, defaultFont, whiteSpace, wordBreak) {
|
|
2007
|
+
let key = "";
|
|
2008
|
+
for (let index = 0; index < spans.length; index += 1) {
|
|
2009
|
+
const span = spans[index];
|
|
2010
|
+
if (index > 0) key += "";
|
|
2011
|
+
key += span.font ?? defaultFont;
|
|
2012
|
+
key += "\0";
|
|
2013
|
+
key += span.text;
|
|
2014
|
+
key += "\0";
|
|
2015
|
+
key += span.break ?? "";
|
|
2016
|
+
key += "\0";
|
|
2017
|
+
key += span.extraWidth ?? 0;
|
|
2018
|
+
}
|
|
2019
|
+
key += "";
|
|
2020
|
+
key += whiteSpace;
|
|
2021
|
+
key += "";
|
|
2022
|
+
key += wordBreak;
|
|
2023
|
+
return key;
|
|
2024
|
+
}
|
|
2025
|
+
function createPlainSourceItems(text, font) {
|
|
2026
|
+
return [{
|
|
2027
|
+
text,
|
|
2028
|
+
font,
|
|
2029
|
+
itemIndex: 0,
|
|
2030
|
+
breakMode: "normal",
|
|
2031
|
+
extraWidth: 0
|
|
2032
|
+
}];
|
|
2033
|
+
}
|
|
2034
|
+
function createRichSourceItems(spans, defaultFont) {
|
|
2035
|
+
return spans.map((span, index) => ({
|
|
2036
|
+
text: span.text,
|
|
2037
|
+
font: span.font ?? defaultFont,
|
|
2038
|
+
itemIndex: index,
|
|
2039
|
+
breakMode: span.break ?? "normal",
|
|
2040
|
+
extraWidth: span.extraWidth ?? 0
|
|
2041
|
+
}));
|
|
2042
|
+
}
|
|
2043
|
+
//#endregion
|
|
2044
|
+
//#region src/text/plain-core.ts
|
|
2045
|
+
function readPreparedText(text, font, whiteSpace, wordBreak) {
|
|
2046
|
+
return readPreparedInlineLayout(getPlainPreparedKey(text, font, whiteSpace, wordBreak), createPlainSourceItems(text, font), whiteSpace, wordBreak);
|
|
2047
|
+
}
|
|
2048
|
+
function measurePreparedMinContentWidth(prepared, overflowWrap = "break-word") {
|
|
2049
|
+
return measurePreparedMinContentWidth$1(prepared, overflowWrap);
|
|
992
2050
|
}
|
|
993
2051
|
//#endregion
|
|
994
2052
|
//#region src/text/plain.ts
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
2053
|
+
function measureTextLayoutWidth(lines) {
|
|
2054
|
+
let width = 0;
|
|
2055
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2056
|
+
const line = lines[index];
|
|
2057
|
+
if (line != null && line.width > width) width = line.width;
|
|
2058
|
+
}
|
|
2059
|
+
return width;
|
|
2060
|
+
}
|
|
999
2061
|
function clampMaxWidth(maxWidth) {
|
|
1000
2062
|
return Math.max(0, maxWidth);
|
|
1001
2063
|
}
|
|
@@ -1014,22 +2076,33 @@ function createEllipsisOnlyLayout(ctx, maxWidth, shift) {
|
|
|
1014
2076
|
overflowed: true
|
|
1015
2077
|
};
|
|
1016
2078
|
}
|
|
1017
|
-
function toTextBlockLayout(lines, shift) {
|
|
1018
|
-
const mappedLines =
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
2079
|
+
function toTextBlockLayout(lines, prepared, shift) {
|
|
2080
|
+
const mappedLines = [];
|
|
2081
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2082
|
+
const line = lines[index];
|
|
2083
|
+
mappedLines.push({
|
|
2084
|
+
width: line.width,
|
|
2085
|
+
text: materializePreparedLineText(prepared, line),
|
|
2086
|
+
shift
|
|
2087
|
+
});
|
|
2088
|
+
}
|
|
1023
2089
|
return {
|
|
1024
|
-
width: mappedLines
|
|
2090
|
+
width: measureTextLayoutWidth(mappedLines),
|
|
1025
2091
|
lines: mappedLines
|
|
1026
2092
|
};
|
|
1027
2093
|
}
|
|
1028
|
-
function
|
|
1029
|
-
const
|
|
2094
|
+
function collectEllipsisLayout(ctx, atoms, maxWidth, shift, position, forceEllipsis = false) {
|
|
2095
|
+
const widths = [];
|
|
2096
|
+
let intrinsicWidth = 0;
|
|
2097
|
+
for (let index = 0; index < atoms.length; index += 1) {
|
|
2098
|
+
const atom = atoms[index];
|
|
2099
|
+
const width = atom.width + atom.extraWidthAfter;
|
|
2100
|
+
widths.push(width);
|
|
2101
|
+
intrinsicWidth += width;
|
|
2102
|
+
}
|
|
1030
2103
|
if (!forceEllipsis && intrinsicWidth <= maxWidth) return {
|
|
1031
2104
|
width: intrinsicWidth,
|
|
1032
|
-
text,
|
|
2105
|
+
text: atoms.map((atom) => atom.text).join(""),
|
|
1033
2106
|
shift,
|
|
1034
2107
|
overflowed: false
|
|
1035
2108
|
};
|
|
@@ -1040,65 +2113,120 @@ function layoutPreparedEllipsis(ctx, prepared, text, maxWidth, shift, position,
|
|
|
1040
2113
|
shift,
|
|
1041
2114
|
overflowed: true
|
|
1042
2115
|
};
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
position,
|
|
1050
|
-
prefixWidths,
|
|
1051
|
-
suffixWidths,
|
|
1052
|
-
unitCount: units.length,
|
|
1053
|
-
availableWidth
|
|
2116
|
+
if (atoms.length === 0) return createEllipsisOnlyLayout(ctx, maxWidth, shift);
|
|
2117
|
+
const selection = resolveEllipsisSelection({
|
|
2118
|
+
widths,
|
|
2119
|
+
ellipsisWidth,
|
|
2120
|
+
maxWidth,
|
|
2121
|
+
position
|
|
1054
2122
|
});
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
2123
|
+
if (selection == null) return {
|
|
2124
|
+
width: 0,
|
|
2125
|
+
text: "",
|
|
2126
|
+
shift,
|
|
2127
|
+
overflowed: true
|
|
2128
|
+
};
|
|
2129
|
+
const { prefixCount, suffixCount, width } = selection;
|
|
2130
|
+
const prefixText = atoms.slice(0, prefixCount).map((atom) => atom.text).join("");
|
|
2131
|
+
const suffixText = atoms.slice(atoms.length - suffixCount).map((atom) => atom.text).join("");
|
|
1059
2132
|
return {
|
|
1060
|
-
width
|
|
1061
|
-
text: `${prefixText}…${suffixText}
|
|
2133
|
+
width,
|
|
2134
|
+
text: position === "start" ? `…${suffixText}` : position === "middle" ? `${prefixText}…${suffixText}` : `${prefixText}…`,
|
|
1062
2135
|
shift,
|
|
1063
2136
|
overflowed: true
|
|
1064
2137
|
};
|
|
1065
2138
|
}
|
|
1066
|
-
function
|
|
1067
|
-
|
|
1068
|
-
|
|
2139
|
+
function getFirstLineRange$1(prepared) {
|
|
2140
|
+
const start = getPreparedLineStart(prepared);
|
|
2141
|
+
if (start == null) return;
|
|
2142
|
+
return layoutNextPreparedLine(prepared, start, Number.POSITIVE_INFINITY) ?? void 0;
|
|
2143
|
+
}
|
|
2144
|
+
function walkLines$1(prepared, maxWidth) {
|
|
2145
|
+
const lines = [];
|
|
2146
|
+
walkPreparedLineRanges(prepared, maxWidth, (line) => {
|
|
2147
|
+
lines.push(line);
|
|
2148
|
+
});
|
|
2149
|
+
return lines;
|
|
2150
|
+
}
|
|
2151
|
+
function collectVisibleLines$1(prepared, maxWidth, maxLines) {
|
|
2152
|
+
const lines = [];
|
|
2153
|
+
let overflowed = false;
|
|
2154
|
+
walkPreparedLineRanges(prepared, maxWidth, (line) => {
|
|
2155
|
+
if (lines.length < maxLines) {
|
|
2156
|
+
lines.push(line);
|
|
2157
|
+
return true;
|
|
2158
|
+
}
|
|
2159
|
+
overflowed = true;
|
|
2160
|
+
return false;
|
|
2161
|
+
});
|
|
2162
|
+
return {
|
|
2163
|
+
lines,
|
|
2164
|
+
overflowed
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
function collectEndEllipsisLayoutFromCursor(ctx, prepared, start, maxWidth, shift) {
|
|
2168
|
+
const ellipsisWidth = measureEllipsisWidth(ctx);
|
|
2169
|
+
if (ellipsisWidth > maxWidth) return {
|
|
2170
|
+
width: 0,
|
|
2171
|
+
text: "",
|
|
2172
|
+
shift,
|
|
2173
|
+
overflowed: true
|
|
2174
|
+
};
|
|
2175
|
+
const widths = [];
|
|
2176
|
+
forEachAtomFromCursorToEnd(prepared, start, (atom) => {
|
|
2177
|
+
widths.push(atom.width + atom.extraWidthAfter);
|
|
2178
|
+
});
|
|
2179
|
+
if (widths.length === 0) return createEllipsisOnlyLayout(ctx, maxWidth, shift);
|
|
2180
|
+
const selection = resolveEllipsisSelection({
|
|
2181
|
+
widths,
|
|
2182
|
+
ellipsisWidth,
|
|
2183
|
+
maxWidth,
|
|
2184
|
+
position: "end"
|
|
2185
|
+
});
|
|
2186
|
+
if (selection == null) return {
|
|
2187
|
+
width: 0,
|
|
2188
|
+
text: "",
|
|
2189
|
+
shift,
|
|
2190
|
+
overflowed: true
|
|
2191
|
+
};
|
|
2192
|
+
const { prefixCount, width } = selection;
|
|
2193
|
+
let text = "";
|
|
2194
|
+
let atomIndex = 0;
|
|
2195
|
+
forEachAtomFromCursorToEnd(prepared, start, (atom) => {
|
|
2196
|
+
if (atomIndex < prefixCount) text += atom.text;
|
|
2197
|
+
atomIndex += 1;
|
|
2198
|
+
});
|
|
2199
|
+
return {
|
|
2200
|
+
width,
|
|
2201
|
+
text: `${text}…`,
|
|
2202
|
+
shift,
|
|
2203
|
+
overflowed: true
|
|
2204
|
+
};
|
|
1069
2205
|
}
|
|
1070
2206
|
function layoutFirstLineIntrinsic(ctx, text, whiteSpace = "normal", wordBreak = "normal") {
|
|
1071
|
-
const
|
|
1072
|
-
|
|
2207
|
+
const prepared = readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak);
|
|
2208
|
+
const line = getFirstLineRange$1(prepared);
|
|
2209
|
+
if (line == null) return {
|
|
1073
2210
|
width: 0,
|
|
1074
2211
|
text: "",
|
|
1075
2212
|
shift: 0
|
|
1076
2213
|
};
|
|
1077
|
-
const shift = measureFontShift(ctx);
|
|
1078
2214
|
return {
|
|
1079
|
-
width:
|
|
1080
|
-
text:
|
|
1081
|
-
shift
|
|
2215
|
+
width: line.width,
|
|
2216
|
+
text: materializePreparedLineText(prepared, line),
|
|
2217
|
+
shift: measureFontShift(ctx)
|
|
1082
2218
|
};
|
|
1083
2219
|
}
|
|
1084
2220
|
function measureTextIntrinsic(ctx, text, whiteSpace = "normal", wordBreak = "normal") {
|
|
1085
|
-
const { maxLineWidth: width, lineCount } =
|
|
1086
|
-
if (lineCount === 0) return {
|
|
1087
|
-
width: 0,
|
|
1088
|
-
lineCount: 0
|
|
1089
|
-
};
|
|
2221
|
+
const { maxLineWidth: width, lineCount } = measurePreparedLineStats(readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak), Number.POSITIVE_INFINITY);
|
|
1090
2222
|
return {
|
|
1091
2223
|
width,
|
|
1092
2224
|
lineCount
|
|
1093
2225
|
};
|
|
1094
2226
|
}
|
|
1095
2227
|
function layoutTextIntrinsic(ctx, text, whiteSpace = "normal", wordBreak = "normal") {
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1098
|
-
width: 0,
|
|
1099
|
-
lines: []
|
|
1100
|
-
};
|
|
1101
|
-
return toTextBlockLayout(intrinsic.lines, measureFontShift(ctx));
|
|
2228
|
+
const prepared = readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak);
|
|
2229
|
+
return toTextBlockLayout(walkLines$1(prepared, Number.POSITIVE_INFINITY), prepared, measureFontShift(ctx));
|
|
1102
2230
|
}
|
|
1103
2231
|
function layoutFirstLine(ctx, text, maxWidth, whiteSpace = "normal", wordBreak = "normal") {
|
|
1104
2232
|
const clampedMaxWidth = clampMaxWidth(maxWidth);
|
|
@@ -1108,7 +2236,14 @@ function layoutFirstLine(ctx, text, maxWidth, whiteSpace = "normal", wordBreak =
|
|
|
1108
2236
|
text: "",
|
|
1109
2237
|
shift
|
|
1110
2238
|
};
|
|
1111
|
-
const
|
|
2239
|
+
const prepared = readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak);
|
|
2240
|
+
const start = getPreparedLineStart(prepared);
|
|
2241
|
+
if (start == null) return {
|
|
2242
|
+
width: 0,
|
|
2243
|
+
text: "",
|
|
2244
|
+
shift
|
|
2245
|
+
};
|
|
2246
|
+
const line = layoutNextPreparedLine(prepared, start, clampedMaxWidth);
|
|
1112
2247
|
if (line == null) return {
|
|
1113
2248
|
width: 0,
|
|
1114
2249
|
text: "",
|
|
@@ -1116,19 +2251,12 @@ function layoutFirstLine(ctx, text, maxWidth, whiteSpace = "normal", wordBreak =
|
|
|
1116
2251
|
};
|
|
1117
2252
|
return {
|
|
1118
2253
|
width: line.width,
|
|
1119
|
-
text: line
|
|
2254
|
+
text: materializePreparedLineText(prepared, line),
|
|
1120
2255
|
shift
|
|
1121
2256
|
};
|
|
1122
2257
|
}
|
|
1123
2258
|
function layoutEllipsizedFirstLine(ctx, text, maxWidth, ellipsisPosition = "end", whiteSpace = "normal", wordBreak = "normal") {
|
|
1124
2259
|
const clampedMaxWidth = clampMaxWidth(maxWidth);
|
|
1125
|
-
const firstLine = readPreparedFirstLine(ctx, text, whiteSpace, wordBreak);
|
|
1126
|
-
if (firstLine == null) return {
|
|
1127
|
-
width: 0,
|
|
1128
|
-
text: "",
|
|
1129
|
-
shift: 0,
|
|
1130
|
-
overflowed: false
|
|
1131
|
-
};
|
|
1132
2260
|
const shift = measureFontShift(ctx);
|
|
1133
2261
|
if (clampedMaxWidth === 0) return {
|
|
1134
2262
|
width: 0,
|
|
@@ -1136,7 +2264,15 @@ function layoutEllipsizedFirstLine(ctx, text, maxWidth, ellipsisPosition = "end"
|
|
|
1136
2264
|
shift,
|
|
1137
2265
|
overflowed: true
|
|
1138
2266
|
};
|
|
1139
|
-
|
|
2267
|
+
const prepared = readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak);
|
|
2268
|
+
const intrinsicLine = getFirstLineRange$1(prepared);
|
|
2269
|
+
if (intrinsicLine == null) return {
|
|
2270
|
+
width: 0,
|
|
2271
|
+
text: "",
|
|
2272
|
+
shift: 0,
|
|
2273
|
+
overflowed: false
|
|
2274
|
+
};
|
|
2275
|
+
return collectEllipsisLayout(ctx, flattenPreparedLineAtoms(prepared, intrinsicLine), clampedMaxWidth, shift, ellipsisPosition);
|
|
1140
2276
|
}
|
|
1141
2277
|
function measureText(ctx, text, maxWidth, whiteSpace = "normal", wordBreak = "normal") {
|
|
1142
2278
|
const clampedMaxWidth = clampMaxWidth(maxWidth);
|
|
@@ -1144,7 +2280,7 @@ function measureText(ctx, text, maxWidth, whiteSpace = "normal", wordBreak = "no
|
|
|
1144
2280
|
width: 0,
|
|
1145
2281
|
lineCount: 0
|
|
1146
2282
|
};
|
|
1147
|
-
const { maxLineWidth: width, lineCount } =
|
|
2283
|
+
const { maxLineWidth: width, lineCount } = measurePreparedLineStats(readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak), clampedMaxWidth);
|
|
1148
2284
|
return {
|
|
1149
2285
|
width,
|
|
1150
2286
|
lineCount
|
|
@@ -1152,12 +2288,12 @@ function measureText(ctx, text, maxWidth, whiteSpace = "normal", wordBreak = "no
|
|
|
1152
2288
|
}
|
|
1153
2289
|
function measureTextMinContent(ctx, text, whiteSpace = "normal", wordBreak = "normal", overflowWrap = "break-word") {
|
|
1154
2290
|
const prepared = readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak);
|
|
1155
|
-
|
|
2291
|
+
const width = measurePreparedMinContentWidth(prepared, overflowWrap);
|
|
2292
|
+
if (width === 0) return {
|
|
1156
2293
|
width: 0,
|
|
1157
2294
|
lineCount: 0
|
|
1158
2295
|
};
|
|
1159
|
-
const
|
|
1160
|
-
const { lineCount } = measureLineStats(prepared, Math.max(width, MIN_CONTENT_WIDTH_EPSILON));
|
|
2296
|
+
const { lineCount } = measurePreparedLineStats(prepared, Math.max(width, MIN_CONTENT_WIDTH_EPSILON));
|
|
1161
2297
|
return {
|
|
1162
2298
|
width,
|
|
1163
2299
|
lineCount
|
|
@@ -1169,12 +2305,8 @@ function layoutText(ctx, text, maxWidth, whiteSpace = "normal", wordBreak = "nor
|
|
|
1169
2305
|
width: 0,
|
|
1170
2306
|
lines: []
|
|
1171
2307
|
};
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
width: 0,
|
|
1175
|
-
lines: []
|
|
1176
|
-
};
|
|
1177
|
-
return toTextBlockLayout(layout.lines, measureFontShift(ctx));
|
|
2308
|
+
const prepared = readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak);
|
|
2309
|
+
return toTextBlockLayout(walkLines$1(prepared, clampedMaxWidth), prepared, measureFontShift(ctx));
|
|
1178
2310
|
}
|
|
1179
2311
|
function layoutTextWithOverflow(ctx, text, maxWidth, options = {}) {
|
|
1180
2312
|
const clampedMaxWidth = clampMaxWidth(maxWidth);
|
|
@@ -1182,46 +2314,61 @@ function layoutTextWithOverflow(ctx, text, maxWidth, options = {}) {
|
|
|
1182
2314
|
const wordBreak = options.wordBreak ?? "normal";
|
|
1183
2315
|
const overflow = options.overflow ?? "clip";
|
|
1184
2316
|
const normalizedMaxLines = normalizeMaxLines(options.maxLines);
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
2317
|
+
const prepared = readPreparedText(text, ctx.graphics.font, whiteSpace, wordBreak);
|
|
2318
|
+
const shift = measureFontShift(ctx);
|
|
2319
|
+
if (normalizedMaxLines == null) {
|
|
2320
|
+
const layout = toTextBlockLayout(walkLines$1(prepared, clampedMaxWidth), prepared, shift);
|
|
2321
|
+
return {
|
|
2322
|
+
width: layout.width,
|
|
2323
|
+
lines: layout.lines.map((line) => ({
|
|
2324
|
+
...line,
|
|
2325
|
+
overflowed: false
|
|
2326
|
+
})),
|
|
2327
|
+
overflowed: false
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
const { lines: visibleRanges, overflowed } = collectVisibleLines$1(prepared, clampedMaxWidth, normalizedMaxLines);
|
|
2331
|
+
if (!overflowed) {
|
|
2332
|
+
const layout = toTextBlockLayout(visibleRanges, prepared, shift);
|
|
2333
|
+
return {
|
|
2334
|
+
width: layout.width,
|
|
2335
|
+
lines: layout.lines.map((line) => ({
|
|
2336
|
+
...line,
|
|
2337
|
+
overflowed: false
|
|
2338
|
+
})),
|
|
1190
2339
|
overflowed: false
|
|
1191
|
-
}
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
const visibleLines = visibleRanges.map((line) => ({
|
|
2343
|
+
width: line.width,
|
|
2344
|
+
text: materializePreparedLineText(prepared, line),
|
|
2345
|
+
shift,
|
|
1192
2346
|
overflowed: false
|
|
1193
|
-
};
|
|
1194
|
-
const visibleLines = layout.lines.slice(0, normalizedMaxLines);
|
|
2347
|
+
}));
|
|
1195
2348
|
if (overflow !== "ellipsis") return {
|
|
1196
|
-
width: visibleLines
|
|
1197
|
-
lines: visibleLines
|
|
1198
|
-
...line,
|
|
1199
|
-
overflowed: false
|
|
1200
|
-
})),
|
|
2349
|
+
width: measureTextLayoutWidth(visibleLines),
|
|
2350
|
+
lines: visibleLines,
|
|
1201
2351
|
overflowed: true
|
|
1202
2352
|
};
|
|
1203
|
-
const
|
|
1204
|
-
const
|
|
1205
|
-
const
|
|
1206
|
-
const lines = [...visibleLines.slice(0, -1).map((line) => ({
|
|
1207
|
-
...line,
|
|
1208
|
-
overflowed: false
|
|
1209
|
-
})), {
|
|
1210
|
-
...ellipsizedLastLine,
|
|
1211
|
-
shift
|
|
1212
|
-
}];
|
|
2353
|
+
const lastVisibleRange = visibleRanges[visibleRanges.length - 1];
|
|
2354
|
+
const ellipsizedLastLine = lastVisibleRange == null ? createEllipsisOnlyLayout(ctx, clampedMaxWidth, shift) : collectEndEllipsisLayoutFromCursor(ctx, prepared, lastVisibleRange.start, clampedMaxWidth, shift);
|
|
2355
|
+
const mergedLines = [...visibleLines.slice(0, -1), ellipsizedLastLine];
|
|
1213
2356
|
return {
|
|
1214
|
-
width:
|
|
1215
|
-
lines,
|
|
2357
|
+
width: measureTextLayoutWidth(mergedLines),
|
|
2358
|
+
lines: mergedLines,
|
|
1216
2359
|
overflowed: true
|
|
1217
2360
|
};
|
|
1218
2361
|
}
|
|
1219
2362
|
//#endregion
|
|
1220
2363
|
//#region src/text/rich.ts
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
const
|
|
2364
|
+
function measureRichBlockWidth(lines) {
|
|
2365
|
+
let width = 0;
|
|
2366
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2367
|
+
const line = lines[index];
|
|
2368
|
+
if (line != null && line.width > width) width = line.width;
|
|
2369
|
+
}
|
|
2370
|
+
return width;
|
|
2371
|
+
}
|
|
1225
2372
|
function withFont(ctx, font, cb) {
|
|
1226
2373
|
const previousFont = ctx.graphics.font;
|
|
1227
2374
|
ctx.graphics.font = font;
|
|
@@ -1231,177 +2378,108 @@ function withFont(ctx, font, cb) {
|
|
|
1231
2378
|
ctx.graphics.font = previousFont;
|
|
1232
2379
|
}
|
|
1233
2380
|
}
|
|
1234
|
-
function
|
|
1235
|
-
return
|
|
1236
|
-
}
|
|
1237
|
-
function readRichPrepared(spans, defaultFont) {
|
|
1238
|
-
const key = getRichPreparedCacheKey(spans, defaultFont);
|
|
1239
|
-
const cached = readLruValue$1(richPreparedCache, key);
|
|
1240
|
-
if (cached != null) return cached;
|
|
1241
|
-
const items = spans.map((span) => ({
|
|
1242
|
-
text: span.text,
|
|
1243
|
-
font: span.font ?? defaultFont,
|
|
1244
|
-
break: span.break,
|
|
1245
|
-
extraWidth: span.extraWidth
|
|
1246
|
-
}));
|
|
1247
|
-
const preparedItemIndexBySourceItemIndex = buildPreparedItemIndexBySourceItemIndex(spans);
|
|
1248
|
-
return writeLruValue$1(richPreparedCache, key, {
|
|
1249
|
-
prepared: prepareRichInline(items),
|
|
1250
|
-
preparedItemIndexBySourceItemIndex
|
|
1251
|
-
}, RICH_PREPARED_CACHE_CAPACITY);
|
|
1252
|
-
}
|
|
1253
|
-
function trimRichInlineBoundaryWhitespace(text) {
|
|
1254
|
-
return text.replace(LEADING_COLLAPSIBLE_BOUNDARY_RE, "").replace(TRAILING_COLLAPSIBLE_BOUNDARY_RE, "");
|
|
1255
|
-
}
|
|
1256
|
-
function buildPreparedItemIndexBySourceItemIndex(spans) {
|
|
1257
|
-
const preparedItemIndexBySourceItemIndex = Array.from({ length: spans.length });
|
|
1258
|
-
let preparedItemIndex = 0;
|
|
1259
|
-
for (let index = 0; index < spans.length; index += 1) {
|
|
1260
|
-
if (trimRichInlineBoundaryWhitespace(spans[index].text).length === 0) continue;
|
|
1261
|
-
preparedItemIndexBySourceItemIndex[index] = preparedItemIndex;
|
|
1262
|
-
preparedItemIndex += 1;
|
|
1263
|
-
}
|
|
1264
|
-
return preparedItemIndexBySourceItemIndex;
|
|
1265
|
-
}
|
|
1266
|
-
function getRichFragmentStartCursor(prepared, fragment) {
|
|
1267
|
-
const itemIndex = prepared.preparedItemIndexBySourceItemIndex[fragment.itemIndex];
|
|
1268
|
-
if (itemIndex == null) return null;
|
|
1269
|
-
return {
|
|
1270
|
-
itemIndex,
|
|
1271
|
-
segmentIndex: fragment.start.segmentIndex,
|
|
1272
|
-
graphemeIndex: fragment.start.graphemeIndex
|
|
1273
|
-
};
|
|
1274
|
-
}
|
|
1275
|
-
function splitOverflowingRichLineRange(prepared, lineRange, maxWidth) {
|
|
1276
|
-
if (lineRange.width <= maxWidth || lineRange.fragments.length <= 1) return lineRange;
|
|
1277
|
-
const trailingFragment = lineRange.fragments[lineRange.fragments.length - 1];
|
|
1278
|
-
const splitCursor = getRichFragmentStartCursor(prepared, trailingFragment);
|
|
1279
|
-
if (splitCursor == null) return lineRange;
|
|
1280
|
-
const fragments = lineRange.fragments.slice(0, -1);
|
|
1281
|
-
return {
|
|
1282
|
-
fragments,
|
|
1283
|
-
width: fragments.reduce((total, fragment) => total + fragment.gapBefore + fragment.occupiedWidth, 0),
|
|
1284
|
-
end: splitCursor
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
function layoutNextConstrainedRichInlineLineRange(prepared, maxWidth, start) {
|
|
1288
|
-
const lineRange = layoutNextRichInlineLineRange(prepared.prepared, maxWidth, start);
|
|
1289
|
-
if (lineRange == null) return null;
|
|
1290
|
-
return splitOverflowingRichLineRange(prepared, lineRange, maxWidth);
|
|
1291
|
-
}
|
|
1292
|
-
function walkConstrainedRichInlineLineRanges(prepared, maxWidth, onLine) {
|
|
1293
|
-
let lineCount = 0;
|
|
1294
|
-
let cursor;
|
|
1295
|
-
while (true) {
|
|
1296
|
-
const lineRange = layoutNextConstrainedRichInlineLineRange(prepared, maxWidth, cursor);
|
|
1297
|
-
if (lineRange == null) return lineCount;
|
|
1298
|
-
onLine(lineRange);
|
|
1299
|
-
lineCount += 1;
|
|
1300
|
-
cursor = lineRange.end;
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
function measureConstrainedRichInlineStats(prepared, maxWidth) {
|
|
1304
|
-
let lineCount = 0;
|
|
1305
|
-
let maxLineWidth = 0;
|
|
1306
|
-
walkConstrainedRichInlineLineRanges(prepared, maxWidth, (lineRange) => {
|
|
1307
|
-
lineCount += 1;
|
|
1308
|
-
if (lineRange.width > maxLineWidth) maxLineWidth = lineRange.width;
|
|
1309
|
-
});
|
|
1310
|
-
return {
|
|
1311
|
-
lineCount,
|
|
1312
|
-
maxLineWidth
|
|
1313
|
-
};
|
|
2381
|
+
function readRichPrepared(spans, defaultFont, whiteSpace, wordBreak) {
|
|
2382
|
+
return readPreparedInlineLayout(getRichPreparedKey(spans, defaultFont, whiteSpace, wordBreak), createRichSourceItems(spans, defaultFont), whiteSpace, wordBreak);
|
|
1314
2383
|
}
|
|
1315
2384
|
function measureRichFragmentShift(ctx, font) {
|
|
1316
2385
|
return withFont(ctx, font, () => measureFontShift(ctx));
|
|
1317
2386
|
}
|
|
1318
|
-
function
|
|
1319
|
-
const
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
const
|
|
2387
|
+
function materializeRichFragments(ctx, spans, defaultColor, atoms) {
|
|
2388
|
+
const fragments = [];
|
|
2389
|
+
let pendingGapBefore = 0;
|
|
2390
|
+
for (const atom of atoms) {
|
|
2391
|
+
const occupiedWidth = atom.width + atom.extraWidthAfter;
|
|
2392
|
+
if (atom.kind === "space" && !atom.preservesLineEnd && atom.atomicGroupId == null) {
|
|
2393
|
+
pendingGapBefore += occupiedWidth;
|
|
2394
|
+
continue;
|
|
2395
|
+
}
|
|
2396
|
+
const span = spans[atom.itemIndex];
|
|
2397
|
+
const font = span?.font ?? atom.font;
|
|
1323
2398
|
const color = span?.color ?? defaultColor;
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
text
|
|
2399
|
+
const previous = fragments[fragments.length - 1];
|
|
2400
|
+
if (previous != null && previous.itemIndex === atom.itemIndex && previous.font === font && pendingGapBefore === 0) {
|
|
2401
|
+
previous.text += atom.text;
|
|
2402
|
+
previous.occupiedWidth += occupiedWidth;
|
|
2403
|
+
continue;
|
|
2404
|
+
}
|
|
2405
|
+
fragments.push({
|
|
2406
|
+
itemIndex: atom.itemIndex,
|
|
2407
|
+
text: atom.text,
|
|
1327
2408
|
font,
|
|
1328
2409
|
color,
|
|
1329
|
-
gapBefore:
|
|
1330
|
-
occupiedWidth
|
|
2410
|
+
gapBefore: pendingGapBefore,
|
|
2411
|
+
occupiedWidth,
|
|
1331
2412
|
shift: measureRichFragmentShift(ctx, font)
|
|
1332
|
-
};
|
|
1333
|
-
|
|
1334
|
-
return {
|
|
1335
|
-
width: richLine.width,
|
|
1336
|
-
fragments,
|
|
1337
|
-
overflowed
|
|
1338
|
-
};
|
|
1339
|
-
}
|
|
1340
|
-
function flattenRichLineUnits(line) {
|
|
1341
|
-
const units = [];
|
|
1342
|
-
for (let fragmentIndex = 0; fragmentIndex < line.fragments.length; fragmentIndex += 1) {
|
|
1343
|
-
const fragment = line.fragments[fragmentIndex];
|
|
1344
|
-
const fragmentUnits = getPreparedUnits(readPreparedText(fragment.text, fragment.font, "normal", "normal"));
|
|
1345
|
-
if (fragmentUnits.length === 0) continue;
|
|
1346
|
-
const textWidth = fragmentUnits.reduce((total, unit) => total + unit.width, 0);
|
|
1347
|
-
const trailingExtraWidth = Math.max(0, fragment.occupiedWidth - textWidth);
|
|
1348
|
-
for (let unitIndex = 0; unitIndex < fragmentUnits.length; unitIndex += 1) {
|
|
1349
|
-
const unit = fragmentUnits[unitIndex];
|
|
1350
|
-
units.push({
|
|
1351
|
-
fragmentIndex,
|
|
1352
|
-
itemIndex: fragment.itemIndex,
|
|
1353
|
-
text: unit.text,
|
|
1354
|
-
width: unit.width + (unitIndex === fragmentUnits.length - 1 ? trailingExtraWidth : 0),
|
|
1355
|
-
font: fragment.font,
|
|
1356
|
-
color: fragment.color,
|
|
1357
|
-
leadingGap: unitIndex === 0 ? fragment.gapBefore : 0
|
|
1358
|
-
});
|
|
1359
|
-
}
|
|
2413
|
+
});
|
|
2414
|
+
pendingGapBefore = 0;
|
|
1360
2415
|
}
|
|
1361
|
-
return
|
|
1362
|
-
}
|
|
1363
|
-
function buildRichPrefixWidths(units) {
|
|
1364
|
-
return buildPrefixWidths(units.map((unit) => unit.leadingGap + unit.width));
|
|
2416
|
+
return fragments;
|
|
1365
2417
|
}
|
|
1366
|
-
function
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
2418
|
+
function appendRichFragment(ctx, spans, defaultColor, fragments, atom, pendingGapBefore) {
|
|
2419
|
+
const occupiedWidth = atom.width + atom.extraWidthAfter;
|
|
2420
|
+
if (atom.kind === "space" && !atom.preservesLineEnd && atom.atomicGroupId == null) return pendingGapBefore + occupiedWidth;
|
|
2421
|
+
const span = spans[atom.itemIndex];
|
|
2422
|
+
const font = span?.font ?? atom.font;
|
|
2423
|
+
const color = span?.color ?? defaultColor;
|
|
2424
|
+
const previous = fragments[fragments.length - 1];
|
|
2425
|
+
if (previous != null && previous.itemIndex === atom.itemIndex && previous.font === font && pendingGapBefore === 0) {
|
|
2426
|
+
previous.text += atom.text;
|
|
2427
|
+
previous.occupiedWidth += occupiedWidth;
|
|
2428
|
+
return 0;
|
|
1374
2429
|
}
|
|
1375
|
-
|
|
2430
|
+
fragments.push({
|
|
2431
|
+
itemIndex: atom.itemIndex,
|
|
2432
|
+
text: atom.text,
|
|
2433
|
+
font,
|
|
2434
|
+
color,
|
|
2435
|
+
gapBefore: pendingGapBefore,
|
|
2436
|
+
occupiedWidth,
|
|
2437
|
+
shift: measureRichFragmentShift(ctx, font)
|
|
2438
|
+
});
|
|
2439
|
+
return 0;
|
|
1376
2440
|
}
|
|
1377
|
-
function
|
|
2441
|
+
function materializeRichFragmentsInRange(ctx, spans, defaultColor, prepared, start, end) {
|
|
1378
2442
|
const fragments = [];
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
if (previous != null && previousUnit != null && previousUnit.fragmentIndex === unit.fragmentIndex) {
|
|
1384
|
-
previous.text += unit.text;
|
|
1385
|
-
previous.occupiedWidth += unit.width;
|
|
1386
|
-
continue;
|
|
1387
|
-
}
|
|
1388
|
-
fragments.push({
|
|
1389
|
-
itemIndex: unit.itemIndex,
|
|
1390
|
-
text: unit.text,
|
|
1391
|
-
font: unit.font,
|
|
1392
|
-
color: unit.color,
|
|
1393
|
-
gapBefore: fragments.length === 0 && suppressLeadingGap ? 0 : unit.leadingGap,
|
|
1394
|
-
occupiedWidth: unit.width,
|
|
1395
|
-
shift: 0
|
|
1396
|
-
});
|
|
1397
|
-
}
|
|
2443
|
+
let pendingGapBefore = 0;
|
|
2444
|
+
forEachAtomInRange(prepared, start, end, (atom) => {
|
|
2445
|
+
pendingGapBefore = appendRichFragment(ctx, spans, defaultColor, fragments, atom, pendingGapBefore);
|
|
2446
|
+
});
|
|
1398
2447
|
return fragments;
|
|
1399
2448
|
}
|
|
1400
|
-
function
|
|
1401
|
-
return
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
2449
|
+
function materializeRichLine(ctx, spans, defaultColor, prepared, line, overflowed) {
|
|
2450
|
+
return {
|
|
2451
|
+
width: line.width,
|
|
2452
|
+
fragments: materializeRichFragmentsInRange(ctx, spans, defaultColor, prepared, line.start, line.end),
|
|
2453
|
+
overflowed
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
function getFirstLineRange(prepared) {
|
|
2457
|
+
const start = getPreparedLineStart(prepared);
|
|
2458
|
+
if (start == null) return;
|
|
2459
|
+
return layoutNextPreparedLine(prepared, start, Number.POSITIVE_INFINITY) ?? void 0;
|
|
2460
|
+
}
|
|
2461
|
+
function walkLines(prepared, maxWidth) {
|
|
2462
|
+
const lines = [];
|
|
2463
|
+
walkPreparedLineRanges(prepared, maxWidth, (line) => {
|
|
2464
|
+
lines.push(line);
|
|
2465
|
+
});
|
|
2466
|
+
return lines;
|
|
2467
|
+
}
|
|
2468
|
+
function collectVisibleLines(prepared, maxWidth, maxLines) {
|
|
2469
|
+
const lines = [];
|
|
2470
|
+
let overflowed = false;
|
|
2471
|
+
walkPreparedLineRanges(prepared, maxWidth, (line) => {
|
|
2472
|
+
if (lines.length < maxLines) {
|
|
2473
|
+
lines.push(line);
|
|
2474
|
+
return true;
|
|
2475
|
+
}
|
|
2476
|
+
overflowed = true;
|
|
2477
|
+
return false;
|
|
2478
|
+
});
|
|
2479
|
+
return {
|
|
2480
|
+
lines,
|
|
2481
|
+
overflowed
|
|
2482
|
+
};
|
|
1405
2483
|
}
|
|
1406
2484
|
function createRichEllipsisFragment(ctx, font, color) {
|
|
1407
2485
|
return withFont(ctx, font, () => ({
|
|
@@ -1415,88 +2493,146 @@ function createRichEllipsisFragment(ctx, font, color) {
|
|
|
1415
2493
|
}));
|
|
1416
2494
|
}
|
|
1417
2495
|
function createRichEllipsisOnlyLayout(ctx, maxWidth, font, color) {
|
|
1418
|
-
const
|
|
1419
|
-
if (
|
|
2496
|
+
const fragment = createRichEllipsisFragment(ctx, font, color);
|
|
2497
|
+
if (fragment.occupiedWidth > maxWidth) return {
|
|
1420
2498
|
width: 0,
|
|
1421
2499
|
fragments: [],
|
|
1422
2500
|
overflowed: true
|
|
1423
2501
|
};
|
|
1424
2502
|
return {
|
|
1425
|
-
width:
|
|
1426
|
-
fragments: [
|
|
2503
|
+
width: fragment.occupiedWidth,
|
|
2504
|
+
fragments: [fragment],
|
|
1427
2505
|
overflowed: true
|
|
1428
2506
|
};
|
|
1429
2507
|
}
|
|
1430
|
-
function
|
|
1431
|
-
|
|
1432
|
-
|
|
2508
|
+
function layoutRichEllipsisFromAtoms(ctx, spans, defaultFont, defaultColor, atoms, maxWidth, position, forceEllipsis = false) {
|
|
2509
|
+
const intrinsicWidth = measureAtomsWidth(atoms);
|
|
2510
|
+
if (!forceEllipsis && intrinsicWidth <= maxWidth) return {
|
|
2511
|
+
width: intrinsicWidth,
|
|
2512
|
+
fragments: materializeRichFragments(ctx, spans, defaultColor, atoms),
|
|
1433
2513
|
overflowed: false
|
|
1434
2514
|
};
|
|
1435
|
-
const
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
unitCount: units.length,
|
|
1448
|
-
availableWidth,
|
|
1449
|
-
getMaxSuffixCount: position === "middle" ? (nextPrefixCount) => Math.max(0, units.length - nextPrefixCount - 1) : void 0
|
|
2515
|
+
const ellipsisWidth = measureEllipsisWidth(ctx);
|
|
2516
|
+
if (ellipsisWidth > maxWidth) return {
|
|
2517
|
+
width: 0,
|
|
2518
|
+
fragments: [],
|
|
2519
|
+
overflowed: true
|
|
2520
|
+
};
|
|
2521
|
+
if (atoms.length === 0) return createRichEllipsisOnlyLayout(ctx, maxWidth, defaultFont, defaultColor);
|
|
2522
|
+
const selection = resolveEllipsisSelection({
|
|
2523
|
+
widths: atoms.map((atom) => atom.width + atom.extraWidthAfter),
|
|
2524
|
+
ellipsisWidth,
|
|
2525
|
+
maxWidth,
|
|
2526
|
+
position
|
|
1450
2527
|
});
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
] : [
|
|
2528
|
+
if (selection == null) return {
|
|
2529
|
+
width: 0,
|
|
2530
|
+
fragments: [],
|
|
2531
|
+
overflowed: true
|
|
2532
|
+
};
|
|
2533
|
+
const { prefixCount, suffixCount, width } = selection;
|
|
2534
|
+
const prefixAtoms = atoms.slice(0, prefixCount);
|
|
2535
|
+
const suffixAtoms = atoms.slice(atoms.length - suffixCount);
|
|
2536
|
+
const ellipsisSource = position === "start" ? suffixAtoms[0] ?? atoms[0] : position === "middle" ? prefixAtoms[prefixAtoms.length - 1] ?? suffixAtoms[0] ?? atoms[atoms.length - 1] : prefixAtoms[prefixAtoms.length - 1] ?? atoms[atoms.length - 1];
|
|
2537
|
+
const ellipsisSpan = ellipsisSource == null ? void 0 : spans[ellipsisSource.itemIndex];
|
|
2538
|
+
const ellipsisFragment = createRichEllipsisFragment(ctx, ellipsisSpan?.font ?? ellipsisSource?.font ?? defaultFont, ellipsisSpan?.color ?? defaultColor);
|
|
2539
|
+
const prefixFragments = materializeRichFragments(ctx, spans, defaultColor, prefixAtoms);
|
|
2540
|
+
const suffixFragments = materializeRichFragments(ctx, spans, defaultColor, suffixAtoms);
|
|
1460
2541
|
return {
|
|
1461
|
-
width
|
|
1462
|
-
fragments,
|
|
2542
|
+
width,
|
|
2543
|
+
fragments: position === "start" ? [ellipsisFragment, ...suffixFragments] : position === "middle" ? [
|
|
2544
|
+
...prefixFragments,
|
|
2545
|
+
ellipsisFragment,
|
|
2546
|
+
...suffixFragments
|
|
2547
|
+
] : [...prefixFragments, ellipsisFragment],
|
|
1463
2548
|
overflowed: true
|
|
1464
2549
|
};
|
|
1465
2550
|
}
|
|
1466
|
-
function
|
|
1467
|
-
|
|
2551
|
+
function layoutRichEndEllipsisFromCursor(ctx, spans, defaultFont, defaultColor, prepared, start, maxWidth) {
|
|
2552
|
+
const ellipsisWidth = measureEllipsisWidth(ctx);
|
|
2553
|
+
if (ellipsisWidth > maxWidth) return {
|
|
1468
2554
|
width: 0,
|
|
1469
2555
|
fragments: [],
|
|
1470
|
-
overflowed:
|
|
2556
|
+
overflowed: true
|
|
2557
|
+
};
|
|
2558
|
+
const widths = [];
|
|
2559
|
+
forEachAtomFromCursorToEnd(prepared, start, (atom) => {
|
|
2560
|
+
widths.push(atom.width + atom.extraWidthAfter);
|
|
2561
|
+
});
|
|
2562
|
+
if (widths.length === 0) return createRichEllipsisOnlyLayout(ctx, maxWidth, defaultFont, defaultColor);
|
|
2563
|
+
const selection = resolveEllipsisSelection({
|
|
2564
|
+
widths,
|
|
2565
|
+
ellipsisWidth,
|
|
2566
|
+
maxWidth,
|
|
2567
|
+
position: "end"
|
|
2568
|
+
});
|
|
2569
|
+
if (selection == null) return {
|
|
2570
|
+
width: 0,
|
|
2571
|
+
fragments: [],
|
|
2572
|
+
overflowed: true
|
|
2573
|
+
};
|
|
2574
|
+
const { prefixCount, width } = selection;
|
|
2575
|
+
const fragments = [];
|
|
2576
|
+
let atomIndex = 0;
|
|
2577
|
+
let pendingGapBefore = 0;
|
|
2578
|
+
let lastVisibleAtom;
|
|
2579
|
+
let lastAtom;
|
|
2580
|
+
forEachAtomFromCursorToEnd(prepared, start, (atom) => {
|
|
2581
|
+
lastAtom = atom;
|
|
2582
|
+
if (atomIndex < prefixCount) {
|
|
2583
|
+
pendingGapBefore = appendRichFragment(ctx, spans, defaultColor, fragments, atom, pendingGapBefore);
|
|
2584
|
+
lastVisibleAtom = atom;
|
|
2585
|
+
}
|
|
2586
|
+
atomIndex += 1;
|
|
2587
|
+
});
|
|
2588
|
+
const ellipsisSource = lastVisibleAtom ?? lastAtom;
|
|
2589
|
+
const ellipsisSpan = ellipsisSource == null ? void 0 : spans[ellipsisSource.itemIndex];
|
|
2590
|
+
fragments.push(createRichEllipsisFragment(ctx, ellipsisSpan?.font ?? ellipsisSource?.font ?? defaultFont, ellipsisSpan?.color ?? defaultColor));
|
|
2591
|
+
return {
|
|
2592
|
+
width,
|
|
2593
|
+
fragments,
|
|
2594
|
+
overflowed: true
|
|
1471
2595
|
};
|
|
1472
|
-
|
|
1473
|
-
|
|
2596
|
+
}
|
|
2597
|
+
function layoutRichFirstLineIntrinsic(ctx, spans, defaultFont, defaultColor, whiteSpace = "normal", wordBreak = "normal") {
|
|
2598
|
+
const prepared = readRichPrepared(spans, defaultFont, whiteSpace, wordBreak);
|
|
2599
|
+
const line = getFirstLineRange(prepared);
|
|
2600
|
+
if (line == null) return {
|
|
1474
2601
|
width: 0,
|
|
1475
2602
|
fragments: [],
|
|
1476
2603
|
overflowed: false
|
|
1477
2604
|
};
|
|
1478
|
-
return materializeRichLine(ctx, spans,
|
|
2605
|
+
return materializeRichLine(ctx, spans, defaultColor, prepared, line, false);
|
|
1479
2606
|
}
|
|
1480
|
-
function layoutRichFirstLine(ctx, spans, maxWidth, defaultFont, defaultColor) {
|
|
2607
|
+
function layoutRichFirstLine(ctx, spans, maxWidth, defaultFont, defaultColor, whiteSpace = "normal", wordBreak = "normal") {
|
|
1481
2608
|
const clampedMaxWidth = Math.max(0, maxWidth);
|
|
1482
|
-
if (
|
|
2609
|
+
if (clampedMaxWidth === 0 || spans.length === 0) return {
|
|
2610
|
+
width: 0,
|
|
2611
|
+
fragments: [],
|
|
2612
|
+
overflowed: false
|
|
2613
|
+
};
|
|
2614
|
+
const prepared = readRichPrepared(spans, defaultFont, whiteSpace, wordBreak);
|
|
2615
|
+
const start = getPreparedLineStart(prepared);
|
|
2616
|
+
if (start == null) return {
|
|
1483
2617
|
width: 0,
|
|
1484
2618
|
fragments: [],
|
|
1485
2619
|
overflowed: false
|
|
1486
2620
|
};
|
|
1487
|
-
const
|
|
1488
|
-
if (
|
|
2621
|
+
const line = layoutNextPreparedLine(prepared, start, clampedMaxWidth);
|
|
2622
|
+
if (line == null) return {
|
|
1489
2623
|
width: 0,
|
|
1490
2624
|
fragments: [],
|
|
1491
2625
|
overflowed: false
|
|
1492
2626
|
};
|
|
1493
|
-
return materializeRichLine(ctx, spans,
|
|
2627
|
+
return materializeRichLine(ctx, spans, defaultColor, prepared, line, false);
|
|
1494
2628
|
}
|
|
1495
|
-
function layoutRichEllipsizedFirstLine(ctx, spans, maxWidth, defaultFont, defaultColor, ellipsisPosition = "end") {
|
|
2629
|
+
function layoutRichEllipsizedFirstLine(ctx, spans, maxWidth, defaultFont, defaultColor, ellipsisPosition = "end", whiteSpace = "normal", wordBreak = "normal") {
|
|
1496
2630
|
const clampedMaxWidth = Math.max(0, maxWidth);
|
|
1497
|
-
const
|
|
1498
|
-
|
|
1499
|
-
|
|
2631
|
+
const prepared = readRichPrepared(spans, defaultFont, whiteSpace, wordBreak);
|
|
2632
|
+
const intrinsicLine = getFirstLineRange(prepared);
|
|
2633
|
+
if (intrinsicLine == null) return {
|
|
2634
|
+
width: 0,
|
|
2635
|
+
fragments: [],
|
|
1500
2636
|
overflowed: false
|
|
1501
2637
|
};
|
|
1502
2638
|
if (clampedMaxWidth === 0) return {
|
|
@@ -1504,103 +2640,110 @@ function layoutRichEllipsizedFirstLine(ctx, spans, maxWidth, defaultFont, defaul
|
|
|
1504
2640
|
fragments: [],
|
|
1505
2641
|
overflowed: true
|
|
1506
2642
|
};
|
|
1507
|
-
return
|
|
2643
|
+
return layoutRichEllipsisFromAtoms(ctx, spans, defaultFont, defaultColor, collectAtomsInRange(prepared, intrinsicLine.start, intrinsicLine.end), clampedMaxWidth, ellipsisPosition);
|
|
1508
2644
|
}
|
|
1509
|
-
function measureRichText(_ctx, spans, maxWidth, defaultFont) {
|
|
1510
|
-
if (spans.length === 0) return {
|
|
2645
|
+
function measureRichText(_ctx, spans, maxWidth, defaultFont, whiteSpace = "normal", wordBreak = "normal") {
|
|
2646
|
+
if (spans.length === 0 || maxWidth <= 0) return {
|
|
1511
2647
|
width: 0,
|
|
1512
2648
|
lineCount: 0
|
|
1513
2649
|
};
|
|
1514
|
-
const { maxLineWidth: width, lineCount } =
|
|
2650
|
+
const { maxLineWidth: width, lineCount } = measurePreparedLineStats(readRichPrepared(spans, defaultFont, whiteSpace, wordBreak), maxWidth);
|
|
1515
2651
|
return {
|
|
1516
2652
|
width,
|
|
1517
2653
|
lineCount
|
|
1518
2654
|
};
|
|
1519
2655
|
}
|
|
1520
|
-
function measureRichTextIntrinsic(_ctx, spans, defaultFont) {
|
|
2656
|
+
function measureRichTextIntrinsic(_ctx, spans, defaultFont, whiteSpace = "normal", wordBreak = "normal") {
|
|
1521
2657
|
if (spans.length === 0) return {
|
|
1522
2658
|
width: 0,
|
|
1523
2659
|
lineCount: 0
|
|
1524
2660
|
};
|
|
1525
|
-
const { maxLineWidth: width, lineCount } =
|
|
2661
|
+
const { maxLineWidth: width, lineCount } = measurePreparedLineStats(readRichPrepared(spans, defaultFont, whiteSpace, wordBreak), Number.POSITIVE_INFINITY);
|
|
1526
2662
|
return {
|
|
1527
2663
|
width,
|
|
1528
2664
|
lineCount
|
|
1529
2665
|
};
|
|
1530
2666
|
}
|
|
1531
|
-
function measureRichTextMinContent(_ctx, spans, defaultFont, overflowWrap = "break-word") {
|
|
2667
|
+
function measureRichTextMinContent(_ctx, spans, defaultFont, overflowWrap = "break-word", whiteSpace = "normal", wordBreak = "normal") {
|
|
1532
2668
|
if (spans.length === 0) return {
|
|
1533
2669
|
width: 0,
|
|
1534
2670
|
lineCount: 0
|
|
1535
2671
|
};
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
const font = span.font ?? defaultFont;
|
|
1540
|
-
const spanMinWidth = measurePreparedMinContentWidth(readPreparedText(span.text, font, "normal", "normal"), overflowWrap) + (span.extraWidth ?? 0);
|
|
1541
|
-
if (spanMinWidth > maxWidth) maxWidth = spanMinWidth;
|
|
1542
|
-
}
|
|
1543
|
-
if (maxWidth === 0) return {
|
|
2672
|
+
const prepared = readRichPrepared(spans, defaultFont, whiteSpace, wordBreak);
|
|
2673
|
+
const width = measurePreparedMinContentWidth$1(prepared, overflowWrap);
|
|
2674
|
+
if (width === 0) return {
|
|
1544
2675
|
width: 0,
|
|
1545
2676
|
lineCount: 0
|
|
1546
2677
|
};
|
|
1547
|
-
const { lineCount } =
|
|
2678
|
+
const { lineCount } = measurePreparedLineStats(prepared, Math.max(width, MIN_CONTENT_WIDTH_EPSILON));
|
|
1548
2679
|
return {
|
|
1549
|
-
width
|
|
2680
|
+
width,
|
|
1550
2681
|
lineCount
|
|
1551
2682
|
};
|
|
1552
2683
|
}
|
|
1553
|
-
function layoutRichText(ctx, spans, maxWidth, defaultFont, defaultColor) {
|
|
1554
|
-
if (spans.length === 0) return {
|
|
2684
|
+
function layoutRichText(ctx, spans, maxWidth, defaultFont, defaultColor, whiteSpace = "normal", wordBreak = "normal") {
|
|
2685
|
+
if (spans.length === 0 || maxWidth <= 0) return {
|
|
1555
2686
|
width: 0,
|
|
1556
2687
|
lines: [],
|
|
1557
2688
|
overflowed: false
|
|
1558
2689
|
};
|
|
1559
|
-
const prepared = readRichPrepared(spans, defaultFont);
|
|
1560
|
-
const
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
lines: [],
|
|
2690
|
+
const prepared = readRichPrepared(spans, defaultFont, whiteSpace, wordBreak);
|
|
2691
|
+
const lines = walkLines(prepared, maxWidth).map((line) => materializeRichLine(ctx, spans, defaultColor, prepared, line, false));
|
|
2692
|
+
return {
|
|
2693
|
+
width: measureRichBlockWidth(lines),
|
|
2694
|
+
lines,
|
|
1565
2695
|
overflowed: false
|
|
1566
2696
|
};
|
|
1567
|
-
|
|
2697
|
+
}
|
|
2698
|
+
function layoutRichTextIntrinsic(ctx, spans, defaultFont, defaultColor, whiteSpace = "normal", wordBreak = "normal") {
|
|
2699
|
+
const prepared = readRichPrepared(spans, defaultFont, whiteSpace, wordBreak);
|
|
2700
|
+
const lines = walkLines(prepared, Number.POSITIVE_INFINITY).map((line) => materializeRichLine(ctx, spans, defaultColor, prepared, line, false));
|
|
1568
2701
|
return {
|
|
1569
|
-
width: lines
|
|
2702
|
+
width: measureRichBlockWidth(lines),
|
|
1570
2703
|
lines,
|
|
1571
2704
|
overflowed: false
|
|
1572
2705
|
};
|
|
1573
2706
|
}
|
|
1574
|
-
function
|
|
1575
|
-
|
|
1576
|
-
}
|
|
1577
|
-
function layoutRichTextWithOverflow(ctx, spans, maxWidth, defaultFont, defaultColor, maxLines, overflow = "clip") {
|
|
1578
|
-
if (spans.length === 0) return {
|
|
2707
|
+
function layoutRichTextWithOverflow(ctx, spans, maxWidth, defaultFont, defaultColor, maxLines, overflow = "clip", whiteSpace = "normal", wordBreak = "normal") {
|
|
2708
|
+
if (spans.length === 0 || maxWidth <= 0) return {
|
|
1579
2709
|
width: 0,
|
|
1580
2710
|
lines: [],
|
|
1581
2711
|
overflowed: false
|
|
1582
2712
|
};
|
|
1583
2713
|
const normalizedMaxLines = normalizeMaxLines(maxLines);
|
|
1584
|
-
const
|
|
1585
|
-
if (normalizedMaxLines == null
|
|
1586
|
-
|
|
2714
|
+
const prepared = readRichPrepared(spans, defaultFont, whiteSpace, wordBreak);
|
|
2715
|
+
if (normalizedMaxLines == null) {
|
|
2716
|
+
const lines = walkLines(prepared, maxWidth).map((line) => materializeRichLine(ctx, spans, defaultColor, prepared, line, false));
|
|
2717
|
+
return {
|
|
2718
|
+
width: measureRichBlockWidth(lines),
|
|
2719
|
+
lines,
|
|
2720
|
+
overflowed: false
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
2723
|
+
const { lines: visibleRanges, overflowed } = collectVisibleLines(prepared, maxWidth, normalizedMaxLines);
|
|
2724
|
+
if (!overflowed) {
|
|
2725
|
+
const lines = visibleRanges.map((line) => materializeRichLine(ctx, spans, defaultColor, prepared, line, false));
|
|
2726
|
+
return {
|
|
2727
|
+
width: measureRichBlockWidth(lines),
|
|
2728
|
+
lines,
|
|
2729
|
+
overflowed: false
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
const visibleLines = visibleRanges.map((line) => materializeRichLine(ctx, spans, defaultColor, prepared, line, false));
|
|
1587
2733
|
if (overflow !== "ellipsis") return {
|
|
1588
|
-
width: visibleLines
|
|
2734
|
+
width: measureRichBlockWidth(visibleLines),
|
|
1589
2735
|
lines: visibleLines,
|
|
1590
2736
|
overflowed: true
|
|
1591
2737
|
};
|
|
1592
|
-
const
|
|
1593
|
-
const ellipsizedLastLine =
|
|
2738
|
+
const lastVisibleRange = visibleRanges[visibleRanges.length - 1];
|
|
2739
|
+
const ellipsizedLastLine = lastVisibleRange == null ? {
|
|
1594
2740
|
width: 0,
|
|
1595
2741
|
fragments: [],
|
|
1596
2742
|
overflowed: true
|
|
1597
|
-
} :
|
|
1598
|
-
...lastVisibleLine,
|
|
1599
|
-
overflowed: true
|
|
1600
|
-
}, maxWidth, defaultFont, defaultColor, "end");
|
|
2743
|
+
} : layoutRichEndEllipsisFromCursor(ctx, spans, defaultFont, defaultColor, prepared, lastVisibleRange.start, maxWidth);
|
|
1601
2744
|
const lines = [...visibleLines.slice(0, -1), ellipsizedLastLine];
|
|
1602
2745
|
return {
|
|
1603
|
-
width: lines
|
|
2746
|
+
width: measureRichBlockWidth(lines),
|
|
1604
2747
|
lines,
|
|
1605
2748
|
overflowed: true
|
|
1606
2749
|
};
|
|
@@ -1679,7 +2822,7 @@ function getSingleLineLayout(node, ctx, text, options) {
|
|
|
1679
2822
|
}
|
|
1680
2823
|
function getRichSingleLineLayout(node, ctx, spans, options) {
|
|
1681
2824
|
const maxWidth = normalizeTextMaxWidth(ctx.constraints?.maxWidth);
|
|
1682
|
-
return readCachedTextLayout(node, ctx, getSingleLineLayoutKey(maxWidth), () => maxWidth == null ? layoutRichFirstLineIntrinsic(ctx, spans, options.font, options.color) : options.overflow === "ellipsis" ? layoutRichEllipsizedFirstLine(ctx, spans, maxWidth, options.font, options.color, options.ellipsisPosition ?? "end") : layoutRichFirstLine(ctx, spans, maxWidth, options.font, options.color));
|
|
2825
|
+
return readCachedTextLayout(node, ctx, getSingleLineLayoutKey(maxWidth), () => maxWidth == null ? layoutRichFirstLineIntrinsic(ctx, spans, options.font, options.color, options.whiteSpace, options.wordBreak) : options.overflow === "ellipsis" ? layoutRichEllipsizedFirstLine(ctx, spans, maxWidth, options.font, options.color, options.ellipsisPosition ?? "end", options.whiteSpace, options.wordBreak) : layoutRichFirstLine(ctx, spans, maxWidth, options.font, options.color, options.whiteSpace, options.wordBreak));
|
|
1683
2826
|
}
|
|
1684
2827
|
function getMultiLineOverflowLayout(node, ctx, text, options) {
|
|
1685
2828
|
const maxWidth = normalizeTextMaxWidth(ctx.constraints?.maxWidth);
|
|
@@ -1712,7 +2855,7 @@ function getSingleLineMinContentLayout(node, ctx, text, options) {
|
|
|
1712
2855
|
});
|
|
1713
2856
|
}
|
|
1714
2857
|
function getRichSingleLineMinContentWidth(node, ctx, spans, options) {
|
|
1715
|
-
return readCachedTextLayout(node, ctx, getSingleLineMinContentLayoutKey(), () => measureRichTextMinContent(ctx, spans, options.font, options.overflowWrap).width);
|
|
2858
|
+
return readCachedTextLayout(node, ctx, getSingleLineMinContentLayoutKey(), () => measureRichTextMinContent(ctx, spans, options.font, options.overflowWrap, options.whiteSpace, options.wordBreak).width);
|
|
1716
2859
|
}
|
|
1717
2860
|
function drawRichLine(ctx, line, fallbackColor, x, y, lineHeight) {
|
|
1718
2861
|
let cursorX = x;
|
|
@@ -1734,19 +2877,19 @@ function getMultiLineMinContentLayout(node, ctx, text, whiteSpace, wordBreak, ov
|
|
|
1734
2877
|
function getRichMultiLineMeasureLayout(node, ctx, spans, options) {
|
|
1735
2878
|
const maxWidth = normalizeTextMaxWidth(ctx.constraints?.maxWidth);
|
|
1736
2879
|
if (shouldReadConstrainedOverflowLayout(maxWidth, options)) return measureBlockLayout(getRichMultiLineOverflowLayout(node, ctx, spans, options));
|
|
1737
|
-
return readCachedTextLayout(node, ctx, getRichMultiLineMeasureLayoutKey(maxWidth), () => maxWidth == null ? measureRichTextIntrinsic(ctx, spans, options.font) : measureRichText(ctx, spans, maxWidth, options.font));
|
|
2880
|
+
return readCachedTextLayout(node, ctx, getRichMultiLineMeasureLayoutKey(maxWidth), () => maxWidth == null ? measureRichTextIntrinsic(ctx, spans, options.font, options.whiteSpace, options.wordBreak) : measureRichText(ctx, spans, maxWidth, options.font, options.whiteSpace, options.wordBreak));
|
|
1738
2881
|
}
|
|
1739
2882
|
function getRichMultiLineOverflowLayout(node, ctx, spans, options) {
|
|
1740
2883
|
const maxWidth = normalizeTextMaxWidth(ctx.constraints?.maxWidth);
|
|
1741
|
-
return readCachedTextLayout(node, ctx, getRichMultiLineOverflowLayoutKey(maxWidth), () => layoutRichTextWithOverflow(ctx, spans, maxWidth ?? 0, options.font, options.color, options.maxLines, options.overflow));
|
|
2884
|
+
return readCachedTextLayout(node, ctx, getRichMultiLineOverflowLayoutKey(maxWidth), () => layoutRichTextWithOverflow(ctx, spans, maxWidth ?? 0, options.font, options.color, options.maxLines, options.overflow, options.whiteSpace, options.wordBreak));
|
|
1742
2885
|
}
|
|
1743
2886
|
function getRichMultiLineDrawLayout(node, ctx, spans, options) {
|
|
1744
2887
|
const maxWidth = normalizeTextMaxWidth(ctx.constraints?.maxWidth);
|
|
1745
2888
|
if (shouldReadConstrainedOverflowLayout(maxWidth, options)) return getRichMultiLineOverflowLayout(node, ctx, spans, options);
|
|
1746
|
-
return readCachedTextLayout(node, ctx, getRichMultiLineDrawLayoutKey(maxWidth), () => maxWidth == null ? layoutRichTextIntrinsic(ctx, spans, options.font, options.color) : layoutRichText(ctx, spans, maxWidth, options.font, options.color));
|
|
2889
|
+
return readCachedTextLayout(node, ctx, getRichMultiLineDrawLayoutKey(maxWidth), () => maxWidth == null ? layoutRichTextIntrinsic(ctx, spans, options.font, options.color, options.whiteSpace, options.wordBreak) : layoutRichText(ctx, spans, maxWidth, options.font, options.color, options.whiteSpace, options.wordBreak));
|
|
1747
2890
|
}
|
|
1748
2891
|
function getRichMultiLineMinContentLayout(node, ctx, spans, options) {
|
|
1749
|
-
return readCachedTextLayout(node, ctx, getRichMultiLineMinContentLayoutKey(), () => measureRichTextMinContent(ctx, spans, options.font, options.overflowWrap));
|
|
2892
|
+
return readCachedTextLayout(node, ctx, getRichMultiLineMinContentLayoutKey(), () => measureRichTextMinContent(ctx, spans, options.font, options.overflowWrap, options.whiteSpace, options.wordBreak));
|
|
1750
2893
|
}
|
|
1751
2894
|
/**
|
|
1752
2895
|
* Draws wrapped text using the configured line height and alignment.
|
|
@@ -3029,6 +4172,6 @@ var TimelineRenderer = class extends VirtualizedRenderer {
|
|
|
3029
4172
|
}
|
|
3030
4173
|
};
|
|
3031
4174
|
//#endregion
|
|
3032
|
-
export { BaseRenderer, ChatRenderer, DebugRenderer, Fixed, Flex, FlexItem, Group, ListState, MultilineText, PaddingBox, Place, Text, TimelineRenderer, VirtualizedRenderer, Wrapper, memoRenderItem, memoRenderItemBy };
|
|
4175
|
+
export { BaseRenderer, ChatRenderer, DebugRenderer, Fixed, Flex, FlexItem, Group, ListState, MultilineText, PaddingBox, Place, ShrinkWrap, Text, TimelineRenderer, VirtualizedRenderer, Wrapper, memoRenderItem, memoRenderItemBy };
|
|
3033
4176
|
|
|
3034
4177
|
//# sourceMappingURL=index.mjs.map
|