react-x11 2.26.0 → 2.26.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cocoa/fonts.js +63 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-x11",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "react renderer with X11 as a target",
5
5
  "main": "./src/index.js",
6
6
  "files": [
@@ -150,6 +150,31 @@ function unpackLines(lineData = NO_GEOMETRY, runData = NO_GEOMETRY) {
150
150
  return lines;
151
151
  }
152
152
 
153
+ /**
154
+ * The one line of an empty paragraph, placed the way the native places a
155
+ * line with text in it: a `lineHeight` multiplies the face's natural line,
156
+ * and the leading is split either side of the glyphs (CSS half-leading,
157
+ * which ntk and `@windowkit/appkit` >= 0.15 both follow).
158
+ */
159
+ function blankLine(metrics, lineHeight, x) {
160
+ const { ascent, descent } = metrics;
161
+ const natural = metrics.lineHeight ?? ascent + descent;
162
+ const multiple = lineHeight >= 0 && lineHeight < Infinity ? lineHeight : 1;
163
+ const height = natural * multiple;
164
+ return {
165
+ x,
166
+ y: 0,
167
+ width: 0,
168
+ height,
169
+ baseline: (height - (ascent + descent)) / 2 + ascent,
170
+ ascent,
171
+ descent,
172
+ start: 0,
173
+ end: 0,
174
+ runs: [],
175
+ };
176
+ }
177
+
153
178
  /** Whether two spans read the same, field for field. */
154
179
  function sameSpan(a, b) {
155
180
  if (a === b) return true;
@@ -302,6 +327,19 @@ class CocoaTextLayout {
302
327
  ctx._drawLayout(this, x, y);
303
328
  }
304
329
 
330
+ /**
331
+ * An empty paragraph as ntk lays it out: one line with no runs, as tall as
332
+ * a line of the face it would have been set in. CoreText sets no line for
333
+ * no text, and a layout with no lines has no height and a caret of none —
334
+ * so an empty field's caret was its three-pixel mark padding either side
335
+ * of nothing, and grew to the line the moment the first letter arrived.
336
+ */
337
+ _setBlank(line) {
338
+ this.lines = [line];
339
+ this.height = line.height;
340
+ this._blank = line;
341
+ }
342
+
305
343
  /** Code point boundary nearest the point, in layout coordinates. */
306
344
  indexAt(x, y) {
307
345
  return this._cpOf(this._native.layoutIndexAt(this._handle, x, y));
@@ -309,6 +347,13 @@ class CocoaTextLayout {
309
347
 
310
348
  /** Caret rect for a code-point index: { x, y, height }. */
311
349
  caretPosition(cp) {
350
+ // the rect the native gives a caret on a line with text in it — the
351
+ // line's box — so typing the first letter moves the caret and nothing
352
+ // else
353
+ const blank = this._blank;
354
+ if (blank) {
355
+ return { x: blank.x, y: blank.y, height: blank.height, line: 0 };
356
+ }
312
357
  return this._native.layoutCaret(this._handle, this._cuOf(cp));
313
358
  }
314
359
 
@@ -1315,6 +1360,24 @@ export class CocoaFontManager {
1315
1360
  minContent ? laid.map((span) => span.text).join('') : text,
1316
1361
  );
1317
1362
  layout._contextInk = contextInk;
1363
+ if (!text) {
1364
+ const first = spans[0] ?? {};
1365
+ const size = first.size ?? base.size ?? 14;
1366
+ const face = first.font ?? base.font;
1367
+ const metrics = (
1368
+ typeof face?.metrics === 'function'
1369
+ ? face
1370
+ : this.match(first.family ?? base.family, {
1371
+ weight: first.weight ?? base.weight,
1372
+ style: first.style ?? base.style,
1373
+ })
1374
+ ).metrics(size);
1375
+ const container =
1376
+ Number.isFinite(maxWidth) && maxWidth > 0 ? maxWidth : 0;
1377
+ layout._setBlank(
1378
+ blankLine(metrics, lineHeight, flushFor(align, direction) * container),
1379
+ );
1380
+ }
1318
1381
  if (!minContent) {
1319
1382
  const runs = new Map();
1320
1383
  layout._hangRuns(sources, (i, rtl) => {