react-x11 2.6.0 → 2.7.0
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 +5 -3
- package/package.json +10 -3
- package/src/activate.js +12 -0
- package/src/anchor.js +6 -0
- package/src/appearance.js +351 -28
- package/src/appearancehooks.js +5 -2
- package/src/application.js +41 -0
- package/src/cocoa/app.js +358 -20
- package/src/cocoa/bezels.js +51 -1
- package/src/cocoa/context2d.js +271 -25
- package/src/cocoa/dnd.js +347 -0
- package/src/cocoa/dock.js +39 -0
- package/src/cocoa/filepanels.js +155 -0
- package/src/cocoa/fonts.js +93 -2
- package/src/cocoa/globalmenu.js +41 -33
- package/src/cocoa/notifications.js +244 -0
- package/src/cocoa/permissions.js +74 -0
- package/src/cocoa/presenter.js +190 -2
- package/src/cocoa/statusitem.js +112 -0
- package/src/cocoa/window.js +85 -4
- package/src/components/Button.js +20 -1
- package/src/components/Checkbox.js +17 -2
- package/src/components/Menu.js +108 -38
- package/src/components/Radio.js +17 -2
- package/src/components/Select.js +159 -27
- package/src/components/Switch.js +8 -1
- package/src/components/native.js +99 -0
- package/src/components/theme.js +37 -20
- package/src/desktopsettings.js +34 -2
- package/src/dnd.js +92 -3
- package/src/errors.js +6 -3
- package/src/filedialog.js +81 -16
- package/src/index.d.ts +17 -1
- package/src/index.js +17 -0
- package/src/launcher.js +170 -0
- package/src/launcherhooks.js +81 -0
- package/src/nodes.js +553 -35
- package/src/notificationhooks.js +56 -0
- package/src/notifications.js +558 -0
- package/src/palette.js +144 -8
- package/src/permissionhooks.js +89 -0
- package/src/permissions.js +196 -0
- package/src/screens.js +39 -4
- package/src/style.d.ts +10 -4
- package/src/style.js +1 -0
- package/src/styles.js +161 -15
- package/src/textselection.js +1 -4
- package/src/trayhooks.js +90 -0
- package/src/types/appearance.d.ts +24 -0
- package/src/types/components.d.ts +10 -0
- package/src/types/elements.d.ts +14 -0
- package/src/types/events.d.ts +14 -0
- package/src/types/filedialog.d.ts +18 -7
- package/src/types/launcher.d.ts +43 -0
- package/src/types/notifications.d.ts +113 -0
- package/src/types/permissions.d.ts +100 -0
- package/src/types/style.d.ts +30 -2
- package/src/types/system.d.ts +5 -3
- package/src/types/tray.d.ts +54 -0
- package/src/windowid.js +23 -0
package/src/cocoa/context2d.js
CHANGED
|
@@ -92,6 +92,61 @@ const clamp01 = (v) => Math.min(1, Math.max(0, Number(v) || 0));
|
|
|
92
92
|
const PICT_OP = Object.freeze({ Src: 1, Over: 3 });
|
|
93
93
|
const RENDER = Object.freeze({ PictOp: PICT_OP });
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The path, recorded alongside the native one, so `stroke` can re-issue it
|
|
97
|
+
* in pieces — `CGContextStrokePath` is QUADRATIC in the number of subpaths
|
|
98
|
+
* in the path it is given (issue #456). Measured here, 13-vertex closed
|
|
99
|
+
* rings scattered over a 1024x1024 surface at a 2px line, one stroke call:
|
|
100
|
+
*
|
|
101
|
+
* 500 rings 20ms | 1000 rings 59ms | 2000 rings 208ms | 4000 rings 986ms
|
|
102
|
+
*
|
|
103
|
+
* Splitting the same geometry into strokes of a few hundred subpaths is
|
|
104
|
+
* linear in it: 14ms, 29ms, 56ms, 113ms. The driver is the subpath count,
|
|
105
|
+
* not the vertex count — one 26,000-vertex subpath strokes in 4ms where
|
|
106
|
+
* two thousand 13-vertex ones take 208. The full table, the two shapes
|
|
107
|
+
* left whole and why, are in docs/macos.md
|
|
108
|
+
* §"Stroking a path with many subpaths".
|
|
109
|
+
*
|
|
110
|
+
* Note the X11 context wants the opposite — there a stroke is an a8
|
|
111
|
+
* coverage mask over the path's bounding box uploaded with one PutImage,
|
|
112
|
+
* so a bigger path is fewer uploads over the same pixels. That is why this
|
|
113
|
+
* lives in the backend: a caller that batches for one backend pessimizes
|
|
114
|
+
* the other, and it cannot know which it is drawing on.
|
|
115
|
+
*
|
|
116
|
+
* The commands are a flat number array — `[op, ...args, op, ...args]` —
|
|
117
|
+
* reused across paths, so a path build is three pushes into a packed
|
|
118
|
+
* double array per point next to the napi call it already makes.
|
|
119
|
+
*/
|
|
120
|
+
const P_MOVE = 0;
|
|
121
|
+
const P_LINE = 1;
|
|
122
|
+
const P_CURVE = 2;
|
|
123
|
+
const P_QUAD = 3;
|
|
124
|
+
const P_CLOSE = 4;
|
|
125
|
+
const P_RECT = 5;
|
|
126
|
+
const P_ROUND = 6;
|
|
127
|
+
const P_ARC = 7;
|
|
128
|
+
const P_ELLIPSE = 8;
|
|
129
|
+
/** how many numbers each op carries, and what it costs a chunk's budget */
|
|
130
|
+
const P_ARGS = [2, 2, 6, 4, 0, 4, 8, 6, 4];
|
|
131
|
+
const P_POINTS = [1, 1, 3, 2, 0, 4, 8, 8, 4];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A chunk closes at the first subpath boundary past either budget. Swept
|
|
135
|
+
* over the shapes above: 512 points is within 5% of the best chunk for
|
|
136
|
+
* every one of them, and the subpath cap catches the degenerate shape the
|
|
137
|
+
* point budget misses — thousands of 3- and 4-point subpaths, where 512
|
|
138
|
+
* points is already 128 strokes' worth of setup.
|
|
139
|
+
*/
|
|
140
|
+
const STROKE_CHUNK_POINTS = 512;
|
|
141
|
+
const STROKE_CHUNK_SUBPATHS = 128;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The off switch, for a process that cannot reach the context — the same
|
|
145
|
+
* line `ctx.strokeChunking = false` draws, and what a bench comparing the
|
|
146
|
+
* two sets. Read once.
|
|
147
|
+
*/
|
|
148
|
+
const NO_STROKE_CHUNKING = process.env.REACT_X11_NO_STROKE_CHUNKING === '1';
|
|
149
|
+
|
|
95
150
|
/**
|
|
96
151
|
* A solid ink for `drawGlyphs` — ntk's `createSolidPicture` answers an
|
|
97
152
|
* XRender picture; here it is the colour itself. Premultiplied 0..1 in, as
|
|
@@ -136,6 +191,11 @@ export class CocoaContext2D {
|
|
|
136
191
|
ctm: [1, 0, 0, 1, 0, 0],
|
|
137
192
|
};
|
|
138
193
|
this._onDirty = null;
|
|
194
|
+
// the recorded path, and whether the native one still matches it (a
|
|
195
|
+
// chunked stroke leaves only its last chunk behind)
|
|
196
|
+
this._cmds = [];
|
|
197
|
+
this._pathStale = false;
|
|
198
|
+
this._strokeChunking = !NO_STROKE_CHUNKING;
|
|
139
199
|
}
|
|
140
200
|
|
|
141
201
|
_s() {
|
|
@@ -152,6 +212,10 @@ export class CocoaContext2D {
|
|
|
152
212
|
n.ctxSetGlobalAlpha(surface, st.globalAlpha);
|
|
153
213
|
n.ctxSetLineDash(surface, st.dash, st.dashOffset);
|
|
154
214
|
this._stack.length = 0;
|
|
215
|
+
// the path went with the surface it was built on; nothing may
|
|
216
|
+
// replay it onto the new one
|
|
217
|
+
this._cmds.length = 0;
|
|
218
|
+
this._pathStale = false;
|
|
155
219
|
}
|
|
156
220
|
return surface;
|
|
157
221
|
}
|
|
@@ -218,6 +282,26 @@ export class CocoaContext2D {
|
|
|
218
282
|
}
|
|
219
283
|
}
|
|
220
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Whether a stroke of a path with many subpaths may go out as several
|
|
287
|
+
* `CGContextStrokePath` calls — on by default, because the alternative
|
|
288
|
+
* is quadratic (see P_MOVE above) and every path small enough for the
|
|
289
|
+
* difference to be invisible is below the threshold anyway.
|
|
290
|
+
*
|
|
291
|
+
* Set it false for a path whose subpaths OVERLAP and whose seams have to
|
|
292
|
+
* composite exactly: chunked, a pixel the strokes of two subpaths each
|
|
293
|
+
* half cover is inked twice at half coverage rather than once at full,
|
|
294
|
+
* and reads a little lighter. `REACT_X11_NO_STROKE_CHUNKING=1` is the
|
|
295
|
+
* same switch for a whole process.
|
|
296
|
+
*/
|
|
297
|
+
get strokeChunking() {
|
|
298
|
+
return this._strokeChunking;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
set strokeChunking(value) {
|
|
302
|
+
this._strokeChunking = !!value;
|
|
303
|
+
}
|
|
304
|
+
|
|
221
305
|
get font() {
|
|
222
306
|
return this._state.font;
|
|
223
307
|
}
|
|
@@ -389,20 +473,96 @@ export class CocoaContext2D {
|
|
|
389
473
|
|
|
390
474
|
// --- paths ---------------------------------------------------------------
|
|
391
475
|
|
|
476
|
+
/**
|
|
477
|
+
* The surface to build or paint the current path on, with the native
|
|
478
|
+
* path restored first if a chunked stroke consumed it. Lazy on purpose:
|
|
479
|
+
* a caller that strokes and then starts a new path — which is every
|
|
480
|
+
* caller in a paint loop — never pays for the rebuild.
|
|
481
|
+
*/
|
|
482
|
+
_path() {
|
|
483
|
+
const surface = this._s();
|
|
484
|
+
if (this._pathStale) {
|
|
485
|
+
this._pathStale = false;
|
|
486
|
+
this._native.ctxBeginPath(surface);
|
|
487
|
+
this._emit(surface, 0, this._cmds.length);
|
|
488
|
+
}
|
|
489
|
+
return surface;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/** replay recorded commands `[from, to)` into the native path */
|
|
493
|
+
_emit(surface, from, to) {
|
|
494
|
+
const c = this._cmds;
|
|
495
|
+
const n = this._native;
|
|
496
|
+
for (let i = from; i < to;) {
|
|
497
|
+
const op = c[i];
|
|
498
|
+
const a = i + 1;
|
|
499
|
+
if (op === P_MOVE) n.ctxMoveTo(surface, c[a], c[a + 1]);
|
|
500
|
+
else if (op === P_LINE) n.ctxLineTo(surface, c[a], c[a + 1]);
|
|
501
|
+
else if (op === P_CURVE)
|
|
502
|
+
n.ctxCurveTo(
|
|
503
|
+
surface,
|
|
504
|
+
c[a],
|
|
505
|
+
c[a + 1],
|
|
506
|
+
c[a + 2],
|
|
507
|
+
c[a + 3],
|
|
508
|
+
c[a + 4],
|
|
509
|
+
c[a + 5],
|
|
510
|
+
);
|
|
511
|
+
else if (op === P_QUAD)
|
|
512
|
+
n.ctxQuadTo(surface, c[a], c[a + 1], c[a + 2], c[a + 3]);
|
|
513
|
+
else if (op === P_CLOSE) n.ctxClosePath(surface);
|
|
514
|
+
else if (op === P_RECT)
|
|
515
|
+
n.ctxRect(surface, c[a], c[a + 1], c[a + 2], c[a + 3]);
|
|
516
|
+
else if (op === P_ROUND)
|
|
517
|
+
n.ctxRoundRect(
|
|
518
|
+
surface,
|
|
519
|
+
c[a],
|
|
520
|
+
c[a + 1],
|
|
521
|
+
c[a + 2],
|
|
522
|
+
c[a + 3],
|
|
523
|
+
c[a + 4],
|
|
524
|
+
c[a + 5],
|
|
525
|
+
c[a + 6],
|
|
526
|
+
c[a + 7],
|
|
527
|
+
);
|
|
528
|
+
else if (op === P_ARC)
|
|
529
|
+
n.ctxArc(
|
|
530
|
+
surface,
|
|
531
|
+
c[a],
|
|
532
|
+
c[a + 1],
|
|
533
|
+
c[a + 2],
|
|
534
|
+
c[a + 3],
|
|
535
|
+
c[a + 4],
|
|
536
|
+
!!c[a + 5],
|
|
537
|
+
);
|
|
538
|
+
else if (op === P_ELLIPSE)
|
|
539
|
+
n.ctxEllipse(surface, c[a], c[a + 1], c[a + 2], c[a + 3]);
|
|
540
|
+
i += 1 + P_ARGS[op];
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
392
544
|
beginPath() {
|
|
545
|
+
this._cmds.length = 0;
|
|
546
|
+
this._pathStale = false;
|
|
393
547
|
this._native.ctxBeginPath(this._s());
|
|
394
548
|
}
|
|
395
549
|
|
|
396
550
|
moveTo(x, y) {
|
|
397
|
-
this.
|
|
551
|
+
const surface = this._path();
|
|
552
|
+
this._cmds.push(P_MOVE, x, y);
|
|
553
|
+
this._native.ctxMoveTo(surface, x, y);
|
|
398
554
|
}
|
|
399
555
|
|
|
400
556
|
lineTo(x, y) {
|
|
401
|
-
this.
|
|
557
|
+
const surface = this._path();
|
|
558
|
+
this._cmds.push(P_LINE, x, y);
|
|
559
|
+
this._native.ctxLineTo(surface, x, y);
|
|
402
560
|
}
|
|
403
561
|
|
|
404
562
|
rect(x, y, w, h) {
|
|
405
|
-
this.
|
|
563
|
+
const surface = this._path();
|
|
564
|
+
this._cmds.push(P_RECT, x, y, w, h);
|
|
565
|
+
this._native.ctxRect(surface, x, y, w, h);
|
|
406
566
|
}
|
|
407
567
|
|
|
408
568
|
roundRect(x, y, w, h, radii) {
|
|
@@ -413,37 +573,45 @@ export class CocoaContext2D {
|
|
|
413
573
|
else if (r.length === 3) r = [r[0], r[1], r[2], r[1]];
|
|
414
574
|
const cap = Math.min(Math.abs(w) / 2, Math.abs(h) / 2);
|
|
415
575
|
const clamp = (v) => Math.max(0, Math.min(Number(v) || 0, cap));
|
|
416
|
-
this.
|
|
417
|
-
|
|
418
|
-
x,
|
|
419
|
-
y,
|
|
420
|
-
w,
|
|
421
|
-
h,
|
|
576
|
+
const surface = this._path();
|
|
577
|
+
const [r0, r1, r2, r3] = [
|
|
422
578
|
clamp(r[0]),
|
|
423
579
|
clamp(r[1]),
|
|
424
580
|
clamp(r[2]),
|
|
425
581
|
clamp(r[3]),
|
|
426
|
-
|
|
582
|
+
];
|
|
583
|
+
this._cmds.push(P_ROUND, x, y, w, h, r0, r1, r2, r3);
|
|
584
|
+
this._native.ctxRoundRect(surface, x, y, w, h, r0, r1, r2, r3);
|
|
427
585
|
}
|
|
428
586
|
|
|
429
587
|
arc(x, y, radius, start, end, anticlockwise = false) {
|
|
430
|
-
this.
|
|
588
|
+
const surface = this._path();
|
|
589
|
+
this._cmds.push(P_ARC, x, y, radius, start, end, anticlockwise ? 1 : 0);
|
|
590
|
+
this._native.ctxArc(surface, x, y, radius, start, end, anticlockwise);
|
|
431
591
|
}
|
|
432
592
|
|
|
433
593
|
ellipse(x, y, rx, ry) {
|
|
434
|
-
this.
|
|
594
|
+
const surface = this._path();
|
|
595
|
+
this._cmds.push(P_ELLIPSE, x, y, rx, ry);
|
|
596
|
+
this._native.ctxEllipse(surface, x, y, rx, ry);
|
|
435
597
|
}
|
|
436
598
|
|
|
437
599
|
bezierCurveTo(c1x, c1y, c2x, c2y, x, y) {
|
|
438
|
-
this.
|
|
600
|
+
const surface = this._path();
|
|
601
|
+
this._cmds.push(P_CURVE, c1x, c1y, c2x, c2y, x, y);
|
|
602
|
+
this._native.ctxCurveTo(surface, c1x, c1y, c2x, c2y, x, y);
|
|
439
603
|
}
|
|
440
604
|
|
|
441
605
|
quadraticCurveTo(cx, cy, x, y) {
|
|
442
|
-
this.
|
|
606
|
+
const surface = this._path();
|
|
607
|
+
this._cmds.push(P_QUAD, cx, cy, x, y);
|
|
608
|
+
this._native.ctxQuadTo(surface, cx, cy, x, y);
|
|
443
609
|
}
|
|
444
610
|
|
|
445
611
|
closePath() {
|
|
446
|
-
this.
|
|
612
|
+
const surface = this._path();
|
|
613
|
+
this._cmds.push(P_CLOSE);
|
|
614
|
+
this._native.ctxClosePath(surface);
|
|
447
615
|
}
|
|
448
616
|
|
|
449
617
|
// --- painting ------------------------------------------------------------
|
|
@@ -477,24 +645,95 @@ export class CocoaContext2D {
|
|
|
477
645
|
_replayPath(path) {
|
|
478
646
|
const cmds = path?._cmds;
|
|
479
647
|
if (!Array.isArray(cmds)) return false;
|
|
480
|
-
|
|
481
|
-
const s = this._s();
|
|
482
|
-
n.ctxBeginPath(s);
|
|
648
|
+
this.beginPath();
|
|
483
649
|
for (const c of cmds) {
|
|
484
|
-
if (c.type === 'M')
|
|
485
|
-
else if (c.type === 'L')
|
|
650
|
+
if (c.type === 'M') this.moveTo(c.x, c.y);
|
|
651
|
+
else if (c.type === 'L') this.lineTo(c.x, c.y);
|
|
486
652
|
else if (c.type === 'C')
|
|
487
|
-
|
|
488
|
-
else if (c.type === 'Q')
|
|
489
|
-
else if (c.type === 'Z')
|
|
653
|
+
this.bezierCurveTo(c.x1, c.y1, c.x2, c.y2, c.x, c.y);
|
|
654
|
+
else if (c.type === 'Q') this.quadraticCurveTo(c.x1, c.y1, c.x, c.y);
|
|
655
|
+
else if (c.type === 'Z') this.closePath();
|
|
490
656
|
}
|
|
491
657
|
return true;
|
|
492
658
|
}
|
|
493
659
|
|
|
660
|
+
/**
|
|
661
|
+
* Whether a stroke of the current path may be split into several
|
|
662
|
+
* `CGContextStrokePath` calls. Two things say no:
|
|
663
|
+
*
|
|
664
|
+
* - A hairline. At or below a device-space width of 1 CoreGraphics
|
|
665
|
+
* strokes through a path that is already linear in the subpath count
|
|
666
|
+
* — 2,000 rings cost 7ms whole — and splitting it is a 2x LOSS, the
|
|
667
|
+
* per-call setup with nothing to win back.
|
|
668
|
+
* - Anything that composites. Each chunk paints separately, so where two
|
|
669
|
+
* subpaths' strokes overlap, a translucent ink, a globalAlpha or a
|
|
670
|
+
* shadow blends twice and reads darker where one call blends the union
|
|
671
|
+
* once. An opaque ink is exact everywhere the coverage is full, which
|
|
672
|
+
* is what the geometry that gets big looks like; what is left is the
|
|
673
|
+
* antialiased fringe at those same overlaps, half-covered twice
|
|
674
|
+
* instead of covered once, which reads a little lighter — the one
|
|
675
|
+
* difference `strokeChunking` exists to turn off.
|
|
676
|
+
*/
|
|
677
|
+
_chunkableStroke() {
|
|
678
|
+
if (!this._strokeChunking) return false;
|
|
679
|
+
const st = this._state;
|
|
680
|
+
const [a, b, c, d] = st.ctm;
|
|
681
|
+
const scale = Math.sqrt(Math.abs(a * d - b * c));
|
|
682
|
+
if (!(st.lineWidth * scale > 1)) return false;
|
|
683
|
+
if (st.globalAlpha < 1) return false;
|
|
684
|
+
if (parseColor(st.strokeStyle)[3] < 1) return false;
|
|
685
|
+
if (st.shadowBlur > 0 && parseColor(st.shadowColor)[3] > 0) return false;
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Stroke the recorded path as a series of chunks, cut at subpath
|
|
691
|
+
* boundaries so every chunk carries the `moveTo` its segments start
|
|
692
|
+
* from. Answers false when there was nothing to split, leaving the
|
|
693
|
+
* native path untouched for the caller's single stroke.
|
|
694
|
+
*/
|
|
695
|
+
_strokeChunks(surface) {
|
|
696
|
+
if (!this._chunkableStroke()) return false;
|
|
697
|
+
const cmds = this._cmds;
|
|
698
|
+
const n = this._native;
|
|
699
|
+
let start = 0;
|
|
700
|
+
let points = 0;
|
|
701
|
+
let subpaths = 0;
|
|
702
|
+
let split = false;
|
|
703
|
+
for (let i = 0; i < cmds.length;) {
|
|
704
|
+
const op = cmds[i];
|
|
705
|
+
if (
|
|
706
|
+
op === P_MOVE &&
|
|
707
|
+
i > start &&
|
|
708
|
+
(points >= STROKE_CHUNK_POINTS || subpaths >= STROKE_CHUNK_SUBPATHS)
|
|
709
|
+
) {
|
|
710
|
+
n.ctxBeginPath(surface);
|
|
711
|
+
this._emit(surface, start, i);
|
|
712
|
+
n.ctxStroke(surface);
|
|
713
|
+
split = true;
|
|
714
|
+
start = i;
|
|
715
|
+
points = 0;
|
|
716
|
+
subpaths = 0;
|
|
717
|
+
}
|
|
718
|
+
if (op === P_MOVE) subpaths++;
|
|
719
|
+
points += P_POINTS[op];
|
|
720
|
+
i += 1 + P_ARGS[op];
|
|
721
|
+
}
|
|
722
|
+
if (!split) return false;
|
|
723
|
+
n.ctxBeginPath(surface);
|
|
724
|
+
this._emit(surface, start, cmds.length);
|
|
725
|
+
n.ctxStroke(surface);
|
|
726
|
+
// the native path is the last chunk now; _path() puts the whole one
|
|
727
|
+
// back if anything asks for it
|
|
728
|
+
this._pathStale = true;
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
|
|
494
732
|
fill(pathOrRule, maybeRule) {
|
|
495
733
|
const hasPath = pathOrRule != null && typeof pathOrRule === 'object';
|
|
496
734
|
const rule = hasPath ? maybeRule : pathOrRule;
|
|
497
735
|
if (hasPath && !this._replayPath(pathOrRule)) return;
|
|
736
|
+
this._path();
|
|
498
737
|
const style = this._state.fillStyle;
|
|
499
738
|
if (style instanceof LinearGradient) {
|
|
500
739
|
const { coords, flat } = style._normalized();
|
|
@@ -518,7 +757,14 @@ export class CocoaContext2D {
|
|
|
518
757
|
return;
|
|
519
758
|
}
|
|
520
759
|
this._applyStroke();
|
|
521
|
-
|
|
760
|
+
// one call per chunk where the path has enough subpaths to be worth it
|
|
761
|
+
// — see P_MOVE and _chunkableStroke above — and one for everything
|
|
762
|
+
// else. The chunks are issued from the record, so a stroke that splits
|
|
763
|
+
// never pays for the restore a previous split owed: `_path()` is asked
|
|
764
|
+
// for the surface only on the whole-path route.
|
|
765
|
+
if (!this._strokeChunks(this._s())) {
|
|
766
|
+
this._native.ctxStroke(this._path());
|
|
767
|
+
}
|
|
522
768
|
this._dirty();
|
|
523
769
|
}
|
|
524
770
|
|
|
@@ -530,7 +776,7 @@ export class CocoaContext2D {
|
|
|
530
776
|
) {
|
|
531
777
|
return;
|
|
532
778
|
}
|
|
533
|
-
this._native.ctxClip(this.
|
|
779
|
+
this._native.ctxClip(this._path());
|
|
534
780
|
}
|
|
535
781
|
|
|
536
782
|
fillRect(x, y, w, h) {
|