@react-x11/components 0.14.0 → 0.14.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/dist/html/css/cascade.d.ts +42 -1
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +310 -12
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/shapes.d.ts +44 -0
- package/dist/html/css/shapes.d.ts.map +1 -0
- package/dist/html/css/shapes.js +194 -0
- package/dist/html/css/shapes.js.map +1 -0
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +1 -15
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/layout/block.d.ts +16 -5
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +26 -5
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +13 -0
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +18 -0
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +58 -10
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.js +19 -6
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +38 -16
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +1 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +57 -12
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +4 -1
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +269 -84
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +27 -10
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +259 -46
- package/dist/html/svg.js.map +1 -1
- package/package.json +1 -1
- package/src/html/css/cascade.ts +343 -8
- package/src/html/css/shapes.ts +239 -0
- package/src/html/css/style.ts +1 -13
- package/src/html/layout/block.ts +27 -5
- package/src/html/layout/boxes.ts +30 -0
- package/src/html/layout/css-grid.ts +77 -13
- package/src/html/layout/flex.ts +22 -9
- package/src/html/layout/table.ts +45 -18
- package/src/html/node.ts +54 -11
- package/src/html/paint.ts +379 -90
- package/src/html/svg.ts +318 -39
package/dist/html/paint.js
CHANGED
|
@@ -494,6 +494,7 @@ export function paintDocument(ctx, tree, options) {
|
|
|
494
494
|
canvasSource: canvas?.source,
|
|
495
495
|
negative: tree.negative,
|
|
496
496
|
selectionStyler: options.selection ? tree.selectionStyler : null,
|
|
497
|
+
shapeStyler: tree.shapeStyler,
|
|
497
498
|
});
|
|
498
499
|
ctx.restore();
|
|
499
500
|
}
|
|
@@ -1175,8 +1176,9 @@ function paintContent(ctx, box, options) {
|
|
|
1175
1176
|
paintStacked(ctx, child, options);
|
|
1176
1177
|
}
|
|
1177
1178
|
if (visible) {
|
|
1178
|
-
if (box.marker)
|
|
1179
|
-
paintMarker(ctx, box.marker, options);
|
|
1179
|
+
if (box.marker) {
|
|
1180
|
+
paintMarker(ctx, box.marker, options, box.style.colorScheme);
|
|
1181
|
+
}
|
|
1180
1182
|
if (box.replaced === 'image')
|
|
1181
1183
|
paintImage(ctx, box, options);
|
|
1182
1184
|
else if (box.replaced === 'svg')
|
|
@@ -1742,7 +1744,7 @@ function paintFlowLines(ctx, box, options, outlines) {
|
|
|
1742
1744
|
if (!intersects(child, options))
|
|
1743
1745
|
continue;
|
|
1744
1746
|
if (child.marker && child.style.visibility === 'visible') {
|
|
1745
|
-
paintMarker(ctx, child.marker, options);
|
|
1747
|
+
paintMarker(ctx, child.marker, options, child.style.colorScheme);
|
|
1746
1748
|
}
|
|
1747
1749
|
if (child.lines)
|
|
1748
1750
|
paintLines(ctx, child, options);
|
|
@@ -3426,12 +3428,13 @@ function paintBackgroundImage(ctx, style, area, at, options, corners = null) {
|
|
|
3426
3428
|
// a drawing is drawn a tile at a time, at the size it was given
|
|
3427
3429
|
if (tiles <= MAX_TILES) {
|
|
3428
3430
|
for (let y = fromY; y < toY; y += stepY) {
|
|
3429
|
-
for (let x = fromX; x < toX; x += stepX)
|
|
3430
|
-
svg.
|
|
3431
|
+
for (let x = fromX; x < toX; x += stepX) {
|
|
3432
|
+
svg.drawImage(ctx, Math.round(x), Math.round(y), iw, ih, scale, style.colorScheme);
|
|
3433
|
+
}
|
|
3431
3434
|
}
|
|
3432
3435
|
}
|
|
3433
3436
|
else {
|
|
3434
|
-
svg.
|
|
3437
|
+
svg.drawImage(ctx, x0, y0, iw, ih, scale, style.colorScheme);
|
|
3435
3438
|
}
|
|
3436
3439
|
}
|
|
3437
3440
|
else if (tiles > 1 &&
|
|
@@ -3647,7 +3650,8 @@ function paintOutline(ctx, box, options) {
|
|
|
3647
3650
|
* width: the CSS triangle a dropdown's caret and a tooltip's arrow are
|
|
3648
3651
|
* drawn with, which came out as the rectangle around it. Only a solid side
|
|
3649
3652
|
* is cut, and only against a side that is solid or paints nothing; dots,
|
|
3650
|
-
* dashes and a double border's two lines keep their rectangles
|
|
3653
|
+
* dashes and a double border's two lines keep their rectangles, the lines
|
|
3654
|
+
* joined where two double sides of one colour meet (`fillSide`).
|
|
3651
3655
|
*/
|
|
3652
3656
|
function paintBorders(ctx, box, options) {
|
|
3653
3657
|
// most boxes have none, and are asked on every paint
|
|
@@ -3687,14 +3691,12 @@ function paintBorders(ctx, box, options) {
|
|
|
3687
3691
|
const tr = paths && mitred(t, r, topInk, rightInk, topStyle, rightStyle);
|
|
3688
3692
|
const br = paths && mitred(b, r, bottomInk, rightInk, bottomStyle, rightStyle);
|
|
3689
3693
|
const bl = paths && mitred(b, l, bottomInk, leftInk, bottomStyle, leftStyle);
|
|
3690
|
-
const
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
+
const widths = [t, r, b, l];
|
|
3695
|
+
const joins = doubleJoins(box);
|
|
3696
|
+
/** A side with square corners, the top first and clockwise. */
|
|
3697
|
+
const edge = (at, style, color) => {
|
|
3694
3698
|
ctx.fillStyle = color;
|
|
3695
|
-
|
|
3696
|
-
// not crawl as the viewport moves along a long edge.
|
|
3697
|
-
fillEdge(ctx, rect, horizontal ? ex : ey, style, horizontal);
|
|
3699
|
+
fillSide(ctx, options, at, x, y, w, h, widths, style, joins);
|
|
3698
3700
|
};
|
|
3699
3701
|
/** A side with a cut corner: its outer edge and then its inner one. */
|
|
3700
3702
|
const side = (points, color) => {
|
|
@@ -3724,7 +3726,7 @@ function paintBorders(ctx, box, options) {
|
|
|
3724
3726
|
side([x, y, x1, y, cutRight ? px1 : x1, py0, cutLeft ? px0 : x, py0], topInk);
|
|
3725
3727
|
}
|
|
3726
3728
|
else
|
|
3727
|
-
edge(
|
|
3729
|
+
edge(0, topStyle, topInk);
|
|
3728
3730
|
}
|
|
3729
3731
|
if (bottomInk !== null) {
|
|
3730
3732
|
const cutLeft = bl && !over(leftInk);
|
|
@@ -3733,21 +3735,21 @@ function paintBorders(ctx, box, options) {
|
|
|
3733
3735
|
side([x1, y1, x, y1, cutLeft ? px0 : x, py1, cutRight ? px1 : x1, py1], bottomInk);
|
|
3734
3736
|
}
|
|
3735
3737
|
else
|
|
3736
|
-
edge(
|
|
3738
|
+
edge(2, bottomStyle, bottomInk);
|
|
3737
3739
|
}
|
|
3738
3740
|
if (leftInk !== null) {
|
|
3739
3741
|
if (tl || bl) {
|
|
3740
3742
|
side([x, bl ? y1 : py1, x, tl ? y : py0, px0, py0, px0, py1], leftInk);
|
|
3741
3743
|
}
|
|
3742
3744
|
else
|
|
3743
|
-
edge(
|
|
3745
|
+
edge(3, leftStyle, leftInk);
|
|
3744
3746
|
}
|
|
3745
3747
|
if (rightInk !== null) {
|
|
3746
3748
|
if (tr || br) {
|
|
3747
3749
|
side([x1, tr ? y : py0, x1, br ? y1 : py1, px1, py1, px1, py0], rightInk);
|
|
3748
3750
|
}
|
|
3749
3751
|
else
|
|
3750
|
-
edge(
|
|
3752
|
+
edge(1, rightStyle, rightInk);
|
|
3751
3753
|
}
|
|
3752
3754
|
}
|
|
3753
3755
|
/**
|
|
@@ -3858,9 +3860,10 @@ function paintBorderImage(ctx, box, options) {
|
|
|
3858
3860
|
const svg = image instanceof SvgDrawing ? image : null;
|
|
3859
3861
|
const w = Math.max(1, Math.round(cw));
|
|
3860
3862
|
const h = Math.max(1, Math.round(ch));
|
|
3861
|
-
const
|
|
3863
|
+
const scheme = box.style.colorScheme;
|
|
3864
|
+
const drawn = options.cached?.(`border-image|${key}|${w}x${h}|${scheme}`, w, h, (sctx) => {
|
|
3862
3865
|
if (svg)
|
|
3863
|
-
svg.
|
|
3866
|
+
svg.drawImage(sctx, 0, 0, w, h, scale, scheme);
|
|
3864
3867
|
else if (typeof source !== 'string') {
|
|
3865
3868
|
const paint = gradientFill(sctx, source, 0, 0, w, h, box.style.color);
|
|
3866
3869
|
if (paint)
|
|
@@ -4062,22 +4065,107 @@ function roundedRing(ctx, box, x, y, w, h, options) {
|
|
|
4062
4065
|
return true;
|
|
4063
4066
|
if (isTransparent(color))
|
|
4064
4067
|
return true;
|
|
4065
|
-
const
|
|
4066
|
-
if (!
|
|
4068
|
+
const ring = cutRing(options, x, y, w, h, corners, box.borderTop, box.borderRight, box.borderBottom, box.borderLeft);
|
|
4069
|
+
if (!ring)
|
|
4067
4070
|
return true;
|
|
4068
|
-
const
|
|
4069
|
-
const right = box.borderRight;
|
|
4070
|
-
const bottom = box.borderBottom;
|
|
4071
|
-
const left = box.borderLeft;
|
|
4071
|
+
const { outer, inner } = ring;
|
|
4072
4072
|
ctx.fillStyle = inkColor(color, s.color);
|
|
4073
|
-
fillRing(ctx, { x:
|
|
4074
|
-
x: rect.x + left,
|
|
4075
|
-
y: rect.y + top,
|
|
4076
|
-
width: Math.max(0, rect.w - left - right),
|
|
4077
|
-
height: Math.max(0, rect.h - top - bottom),
|
|
4078
|
-
}, insetCorners(corners, top, right, bottom, left));
|
|
4073
|
+
fillRing(ctx, outer.rect, outer.corners, inner ? inner.rect : { x: 0, y: 0, width: 0, height: 0 }, inner ? inner.corners : outer.corners);
|
|
4079
4074
|
return true;
|
|
4080
4075
|
}
|
|
4076
|
+
/**
|
|
4077
|
+
* A rounded box's ring — its border edge and its padding edge, the box
|
|
4078
|
+
* inside its borders — cut to the neighbourhood of what is being painted,
|
|
4079
|
+
* as `clampRect` cuts a rectangle. Both edges are the box's own, cut to
|
|
4080
|
+
* one window, so what is left is the part of the true ring in it. Cutting
|
|
4081
|
+
* the box first and insetting the cut by the borders put the hole a
|
|
4082
|
+
* border's width in from the cut rather than from the box's edge, and left
|
|
4083
|
+
* none where a border was wider than `CLAMP_PAD`: the ring filled whatever
|
|
4084
|
+
* of the box a paint reached. A cut is straight, so a side of the window
|
|
4085
|
+
* is moved out past any corner's curve it would cross, and a corner on a
|
|
4086
|
+
* side the window cut is square, its curve being outside. Null where the
|
|
4087
|
+
* ring has nothing in the window.
|
|
4088
|
+
*/
|
|
4089
|
+
function cutRing(options, x, y, w, h, corners, top, right, bottom, left) {
|
|
4090
|
+
const { x: cx, y: cy } = corners;
|
|
4091
|
+
// where each corner's curve runs, across and down — the padding edge's
|
|
4092
|
+
// run inside the border edge's
|
|
4093
|
+
const across = [
|
|
4094
|
+
[x, x + cx[0]],
|
|
4095
|
+
[x + w - cx[1], x + w],
|
|
4096
|
+
[x + w - cx[2], x + w],
|
|
4097
|
+
[x, x + cx[3]],
|
|
4098
|
+
];
|
|
4099
|
+
const down = [
|
|
4100
|
+
[y, y + cy[0]],
|
|
4101
|
+
[y, y + cy[1]],
|
|
4102
|
+
[y + h - cy[2], y + h],
|
|
4103
|
+
[y + h - cy[3], y + h],
|
|
4104
|
+
];
|
|
4105
|
+
const d = options.damage;
|
|
4106
|
+
const side = (at, spans, towards) => Math.max(-COORD_LIMIT, Math.min(COORD_LIMIT, outOfSpans(at, spans, towards)));
|
|
4107
|
+
const x0 = side(d ? d.x - CLAMP_PAD : -COORD_LIMIT, across, -1);
|
|
4108
|
+
const y0 = side(d ? d.y - CLAMP_PAD : -COORD_LIMIT, down, -1);
|
|
4109
|
+
const x1 = side(d ? d.x + d.width + CLAMP_PAD : COORD_LIMIT, across, 1);
|
|
4110
|
+
const y1 = side(d ? d.y + d.height + CLAMP_PAD : COORD_LIMIT, down, 1);
|
|
4111
|
+
const cut = (rx, ry, rw, rh, c) => {
|
|
4112
|
+
const l = Math.max(rx, x0);
|
|
4113
|
+
const t = Math.max(ry, y0);
|
|
4114
|
+
const r = Math.min(rx + rw, x1);
|
|
4115
|
+
const b = Math.min(ry + rh, y1);
|
|
4116
|
+
if (r <= l || b <= t)
|
|
4117
|
+
return null;
|
|
4118
|
+
// the corners of the sides the window left where they were
|
|
4119
|
+
const kept = [
|
|
4120
|
+
t === ry && l === rx,
|
|
4121
|
+
t === ry && r === rx + rw,
|
|
4122
|
+
b === ry + rh && r === rx + rw,
|
|
4123
|
+
b === ry + rh && l === rx,
|
|
4124
|
+
];
|
|
4125
|
+
return {
|
|
4126
|
+
rect: { x: l, y: t, width: r - l, height: b - t },
|
|
4127
|
+
corners: {
|
|
4128
|
+
x: c.x.map((v, i) => (kept[i] ? v : 0)),
|
|
4129
|
+
y: c.y.map((v, i) => (kept[i] ? v : 0)),
|
|
4130
|
+
},
|
|
4131
|
+
};
|
|
4132
|
+
};
|
|
4133
|
+
const outer = cut(x, y, w, h, corners);
|
|
4134
|
+
if (!outer)
|
|
4135
|
+
return null;
|
|
4136
|
+
const iw = w - left - right;
|
|
4137
|
+
const ih = h - top - bottom;
|
|
4138
|
+
const inner = iw > 0 && ih > 0
|
|
4139
|
+
? cut(x + left, y + top, iw, ih, insetCorners(corners, top, right, bottom, left))
|
|
4140
|
+
: null;
|
|
4141
|
+
// a window inside the padding edge, where the ring has nothing
|
|
4142
|
+
if (inner && sameRounded(outer, inner))
|
|
4143
|
+
return null;
|
|
4144
|
+
return { outer, inner };
|
|
4145
|
+
}
|
|
4146
|
+
/** A position moved out of any of `spans` it is inside, towards their
|
|
4147
|
+
* starts where `towards` is negative and their ends where it is not. */
|
|
4148
|
+
function outOfSpans(at, spans, towards) {
|
|
4149
|
+
for (let moved = true; moved;) {
|
|
4150
|
+
moved = false;
|
|
4151
|
+
for (const [from, to] of spans) {
|
|
4152
|
+
if (at > from && at < to) {
|
|
4153
|
+
at = towards < 0 ? from : to;
|
|
4154
|
+
moved = true;
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
return at;
|
|
4159
|
+
}
|
|
4160
|
+
/** Whether two rounded rectangles are one shape. */
|
|
4161
|
+
function sameRounded(a, b) {
|
|
4162
|
+
return (a.rect.x === b.rect.x &&
|
|
4163
|
+
a.rect.y === b.rect.y &&
|
|
4164
|
+
a.rect.width === b.rect.width &&
|
|
4165
|
+
a.rect.height === b.rect.height &&
|
|
4166
|
+
a.corners.x.every((v, i) => v === b.corners.x[i]) &&
|
|
4167
|
+
a.corners.y.every((v, i) => v === b.corners.y[i]));
|
|
4168
|
+
}
|
|
4081
4169
|
/**
|
|
4082
4170
|
* Clip to what a box's border paints, for a layer of `background-clip:
|
|
4083
4171
|
* border-area` (CSS Backgrounds 4, 2.1): each side's width and style, and
|
|
@@ -4085,12 +4173,17 @@ function roundedRing(ctx, box, x, y, w, h, options) {
|
|
|
4085
4173
|
* the value is for. The shapes are the border painter's own, so that the
|
|
4086
4174
|
* background shows exactly where that border would: the ring `roundedRing`
|
|
4087
4175
|
* fills where a rounded border is solid, the ring the trapezoids of a 3D
|
|
4088
|
-
* style make, and otherwise the rectangles `
|
|
4089
|
-
* time — the dots, the dashes, the two lines of a double border
|
|
4176
|
+
* style make, and otherwise the rectangles `fillSide` fills a side at a
|
|
4177
|
+
* time — the dots, the dashes, the two lines of a double border joined as
|
|
4178
|
+
* they are painted, by the sides' colours though neither is drawn. All of
|
|
4090
4179
|
* them run clockwise and a ring's inside the other way, so the clip is the
|
|
4091
|
-
* union by the non-zero rule, the one every context's `clip` takes.
|
|
4092
|
-
*
|
|
4093
|
-
*
|
|
4180
|
+
* union by the non-zero rule, the one every context's `clip` takes. Each
|
|
4181
|
+
* is placed from the box's edges and then cut to the painted area: placed
|
|
4182
|
+
* from the cut, a border wider than `CLAMP_PAD` put its bands and its
|
|
4183
|
+
* ring's hole a border's width in from wherever the paint was cut, and
|
|
4184
|
+
* the background was painted over the content of a box repainted in part.
|
|
4185
|
+
* False where the border paints nothing in the painted area or the context
|
|
4186
|
+
* cannot clip, and nothing is pushed.
|
|
4094
4187
|
*/
|
|
4095
4188
|
function pushBorderArea(ctx, box, options) {
|
|
4096
4189
|
const t = box.borderTop;
|
|
@@ -4109,44 +4202,53 @@ function pushBorderArea(ctx, box, options) {
|
|
|
4109
4202
|
const h = Math.round(top + frameHeight(box)) - y;
|
|
4110
4203
|
if (w <= 0 || h <= 0)
|
|
4111
4204
|
return false;
|
|
4112
|
-
|
|
4113
|
-
if (!area)
|
|
4205
|
+
if (!clampRect(options, x, y, w, h))
|
|
4114
4206
|
return false;
|
|
4115
4207
|
const corners = cornersOf(s, w, h);
|
|
4116
4208
|
ctx.save();
|
|
4117
4209
|
ctx.beginPath();
|
|
4210
|
+
// how many shapes the path has: none, and the border paints nothing here
|
|
4211
|
+
let shapes = 0;
|
|
4118
4212
|
if (corners && canCurve(ctx) && solidBorder(box)) {
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4213
|
+
const ring = cutRing(options, x, y, w, h, corners, t, r, b, l);
|
|
4214
|
+
if (ring) {
|
|
4215
|
+
const add = ({ rect, corners: c }, hole) => roundedRect(ctx, rect.x, rect.y, rect.width, rect.height, c, true, hole);
|
|
4216
|
+
add(ring.outer, false);
|
|
4217
|
+
// the padding edge run the other way, which cuts it out
|
|
4218
|
+
if (ring.inner)
|
|
4219
|
+
add(ring.inner, true);
|
|
4220
|
+
shapes = 1;
|
|
4125
4221
|
}
|
|
4126
4222
|
}
|
|
4127
|
-
else if (sculpted(s)) {
|
|
4128
|
-
// a band a side, which the trapezoids cover between them
|
|
4129
|
-
ctx.rect(area.x, area.y, area.w, t);
|
|
4130
|
-
ctx.rect(area.x, area.y + area.h - b, area.w, b);
|
|
4131
|
-
ctx.rect(area.x, area.y + t, l, area.h - t - b);
|
|
4132
|
-
ctx.rect(area.x + area.w - r, area.y + t, r, area.h - t - b);
|
|
4133
|
-
}
|
|
4134
4223
|
else {
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4224
|
+
// A side at a time, from the box's edges and cut to the painted area
|
|
4225
|
+
// after: a 3D side as a band, which the trapezoids cover between them,
|
|
4226
|
+
// and any other as `fillSide` fills it.
|
|
4227
|
+
const path = {
|
|
4228
|
+
fillRect(ex, ey, ew, eh) {
|
|
4229
|
+
ctx.rect(ex, ey, ew, eh);
|
|
4230
|
+
shapes += 1;
|
|
4231
|
+
},
|
|
4140
4232
|
};
|
|
4141
|
-
const
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4233
|
+
const widths = [t, r, b, l];
|
|
4234
|
+
const joins = doubleJoins(box);
|
|
4235
|
+
const styles = sculpted(s)
|
|
4236
|
+
? ['solid', 'solid', 'solid', 'solid']
|
|
4237
|
+
: [
|
|
4238
|
+
s.borderTopStyle,
|
|
4239
|
+
s.borderRightStyle,
|
|
4240
|
+
s.borderBottomStyle,
|
|
4241
|
+
s.borderLeftStyle,
|
|
4242
|
+
];
|
|
4243
|
+
for (let at = 0; at < 4; at += 1) {
|
|
4244
|
+
if (widths[at] > 0) {
|
|
4245
|
+
fillSide(path, options, at, x, y, w, h, widths, styles[at], joins);
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
if (!shapes) {
|
|
4250
|
+
ctx.restore();
|
|
4251
|
+
return false;
|
|
4150
4252
|
}
|
|
4151
4253
|
ctx.clip();
|
|
4152
4254
|
return true;
|
|
@@ -4175,10 +4277,12 @@ function sculpted(s) {
|
|
|
4175
4277
|
* colour's shadow and `outset` its bottom and right; `groove` is `inset`
|
|
4176
4278
|
* outside `outset`, a band each, and `ridge` the other way round
|
|
4177
4279
|
* (`borderShades`). A side of any other style takes its trapezoid in its
|
|
4178
|
-
* colour. The trapezoids are
|
|
4179
|
-
*
|
|
4180
|
-
*
|
|
4181
|
-
*
|
|
4280
|
+
* colour. The trapezoids are cut to the painted area (`fillPolygon`), which
|
|
4281
|
+
* keeps a long box's far corners out of X's coordinates and a join on its
|
|
4282
|
+
* diagonal: its corners clamped in, a border wider than `CLAMP_PAD` turned
|
|
4283
|
+
* the join to cross the damage at 45° wherever it was, and a scrolled
|
|
4284
|
+
* strip smeared the corners of a thick one. False where the context cannot
|
|
4285
|
+
* draw a path, and the sides are drawn straight.
|
|
4182
4286
|
*/
|
|
4183
4287
|
function paintSculpted(ctx, box, x, y, w, h, options) {
|
|
4184
4288
|
if (!ctx.beginPath || !ctx.moveTo || !ctx.lineTo || !ctx.fill)
|
|
@@ -4191,8 +4295,6 @@ function paintSculpted(ctx, box, x, y, w, h, options) {
|
|
|
4191
4295
|
const r = box.borderRight;
|
|
4192
4296
|
const b = box.borderBottom;
|
|
4193
4297
|
const l = box.borderLeft;
|
|
4194
|
-
const cx = (v) => Math.max(area.x - 1, Math.min(v, area.x + area.w + 1));
|
|
4195
|
-
const cy = (v) => Math.max(area.y - 1, Math.min(v, area.y + area.h + 1));
|
|
4196
4298
|
/** The band between the edge `outer` of the way in and `inner`, for one
|
|
4197
4299
|
* side: 0 is the border box, 1 the padding box. */
|
|
4198
4300
|
const band = (side, outer, inner, color) => {
|
|
@@ -4212,12 +4314,7 @@ function paintSculpted(ctx, box, x, y, w, h, options) {
|
|
|
4212
4314
|
? [o.x1, o.y1, o.x0, o.y1, i.x0, i.y1, i.x1, i.y1]
|
|
4213
4315
|
: [o.x0, o.y1, o.x0, o.y0, i.x0, i.y0, i.x0, i.y1];
|
|
4214
4316
|
ctx.fillStyle = color;
|
|
4215
|
-
ctx
|
|
4216
|
-
ctx.moveTo(cx(points[0]), cy(points[1]));
|
|
4217
|
-
for (let k = 2; k < 8; k += 2)
|
|
4218
|
-
ctx.lineTo(cx(points[k]), cy(points[k + 1]));
|
|
4219
|
-
ctx.closePath?.();
|
|
4220
|
-
ctx.fill();
|
|
4317
|
+
fillPolygon(ctx, points, area);
|
|
4221
4318
|
};
|
|
4222
4319
|
const sides = [
|
|
4223
4320
|
[t, s.borderTopStyle, s.borderTopColor],
|
|
@@ -4329,6 +4426,91 @@ function paintCollapsedBorders(ctx, table, options) {
|
|
|
4329
4426
|
fillEdge(ctx, rect, s.horizontal ? s.x : s.y, s.border.style, s.horizontal);
|
|
4330
4427
|
}
|
|
4331
4428
|
}
|
|
4429
|
+
/**
|
|
4430
|
+
* One side of a border box at `x, y`, `w` by `h` — `at` counts the top
|
|
4431
|
+
* first and clockwise — as rectangles cut to what the paint reaches: the
|
|
4432
|
+
* top and the bottom the width of the box and the left and the right
|
|
4433
|
+
* between them, so a corner is a square of one side or the other.
|
|
4434
|
+
*
|
|
4435
|
+
* A double side is two lines, a band each along its outer and its inner
|
|
4436
|
+
* edge. Where it meets another double side of its colour (`joins`, the
|
|
4437
|
+
* top left corner first) the lines of the two meet as two frames do, one
|
|
4438
|
+
* inside the other (CSS Backgrounds 3, 4.2 and 4.3): the outer line of the
|
|
4439
|
+
* left or the right runs on to the outer line of the top or the bottom,
|
|
4440
|
+
* and the inner line of the top or the bottom stops at the inner line of
|
|
4441
|
+
* the side. With square corners the outer frame was open at each of them
|
|
4442
|
+
* and the inner line of the top ran on past the side's to the border edge.
|
|
4443
|
+
* Each line is cut to the paint on its own, so a border wider than the
|
|
4444
|
+
* reach around the paint keeps its lines at its edges, and not at the
|
|
4445
|
+
* edges of the part of it cut out.
|
|
4446
|
+
*/
|
|
4447
|
+
function fillSide(ctx, options, at, x, y, w, h, widths, style, joins) {
|
|
4448
|
+
const [t, r, b, l] = widths;
|
|
4449
|
+
const width = widths[at];
|
|
4450
|
+
const horizontal = (at & 1) === 0;
|
|
4451
|
+
if (style === 'double' && width >= 3) {
|
|
4452
|
+
const band = doubleBand(width);
|
|
4453
|
+
const line = (lx, ly, lw, lh) => {
|
|
4454
|
+
const rect = clampRect(options, lx, ly, lw, lh);
|
|
4455
|
+
if (rect)
|
|
4456
|
+
ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
|
|
4457
|
+
};
|
|
4458
|
+
if (horizontal) {
|
|
4459
|
+
// the outer line the width of the box, and the inner one from the
|
|
4460
|
+
// inner line of a side it joins, or from the edge of the box
|
|
4461
|
+
const from = joins[at === 0 ? 0 : 3] ? x + l - doubleBand(l) : x;
|
|
4462
|
+
const to = joins[at === 0 ? 1 : 2] ? x + w - r + doubleBand(r) : x + w;
|
|
4463
|
+
line(x, at === 0 ? y : y + h - band, w, band);
|
|
4464
|
+
line(from, at === 0 ? y + t - band : y + h - b, to - from, band);
|
|
4465
|
+
}
|
|
4466
|
+
else {
|
|
4467
|
+
// the outer line from the outer line of a side it joins, or from
|
|
4468
|
+
// that side's inner edge, and the inner one between the two sides
|
|
4469
|
+
const from = joins[at === 3 ? 0 : 1] ? y + doubleBand(t) : y + t;
|
|
4470
|
+
const to = joins[at === 3 ? 3 : 2] ? y + h - doubleBand(b) : y + h - b;
|
|
4471
|
+
line(at === 3 ? x : x + w - band, from, band, to - from);
|
|
4472
|
+
line(at === 3 ? x + l - band : x + w - r, y + t, band, h - t - b);
|
|
4473
|
+
}
|
|
4474
|
+
return;
|
|
4475
|
+
}
|
|
4476
|
+
const ex = at === 1 ? x + w - r : x;
|
|
4477
|
+
const ey = at === 0 ? y : at === 2 ? y + h - b : y + t;
|
|
4478
|
+
const rect = horizontal
|
|
4479
|
+
? clampRect(options, ex, ey, w, width)
|
|
4480
|
+
: clampRect(options, ex, ey, width, h - t - b);
|
|
4481
|
+
// The un-clamped start is the dash phase's origin, so the pattern does
|
|
4482
|
+
// not crawl as the viewport moves along a long edge.
|
|
4483
|
+
if (rect)
|
|
4484
|
+
fillEdge(ctx, rect, horizontal ? ex : ey, style, horizontal);
|
|
4485
|
+
}
|
|
4486
|
+
/**
|
|
4487
|
+
* The corners of a box where two double sides of one colour meet, the top
|
|
4488
|
+
* left first and clockwise, and whose lines `fillSide` joins. By the
|
|
4489
|
+
* colours as written, a transparent one included, which is how a clip to
|
|
4490
|
+
* the border's area draws the border it does not paint.
|
|
4491
|
+
*/
|
|
4492
|
+
function doubleJoins(box) {
|
|
4493
|
+
const s = box.style;
|
|
4494
|
+
const color = (width, style, value) => style === 'double' && width >= 3 ? inkColor(value, s.color) : null;
|
|
4495
|
+
const top = color(box.borderTop, s.borderTopStyle, s.borderTopColor);
|
|
4496
|
+
const bottom = color(box.borderBottom, s.borderBottomStyle, s.borderBottomColor);
|
|
4497
|
+
if (top === null && bottom === null)
|
|
4498
|
+
return UNJOINED;
|
|
4499
|
+
const right = color(box.borderRight, s.borderRightStyle, s.borderRightColor);
|
|
4500
|
+
const left = color(box.borderLeft, s.borderLeftStyle, s.borderLeftColor);
|
|
4501
|
+
return [
|
|
4502
|
+
top !== null && top === left,
|
|
4503
|
+
top !== null && top === right,
|
|
4504
|
+
bottom !== null && bottom === right,
|
|
4505
|
+
bottom !== null && bottom === left,
|
|
4506
|
+
];
|
|
4507
|
+
}
|
|
4508
|
+
const UNJOINED = [false, false, false, false];
|
|
4509
|
+
/** How wide each of a double border's two lines is, the gap between them
|
|
4510
|
+
* what is left of the side. */
|
|
4511
|
+
function doubleBand(thickness) {
|
|
4512
|
+
return Math.max(1, Math.floor(thickness / 3));
|
|
4513
|
+
}
|
|
4332
4514
|
function fillEdge(ctx, rect, phaseOrigin, style, horizontal) {
|
|
4333
4515
|
const { x, y, w, h } = rect;
|
|
4334
4516
|
const length = horizontal ? w : h;
|
|
@@ -4355,7 +4537,7 @@ function fillEdge(ctx, rect, phaseOrigin, style, horizontal) {
|
|
|
4355
4537
|
return;
|
|
4356
4538
|
}
|
|
4357
4539
|
if (style === 'double' && thickness >= 3) {
|
|
4358
|
-
const band =
|
|
4540
|
+
const band = doubleBand(thickness);
|
|
4359
4541
|
if (horizontal) {
|
|
4360
4542
|
ctx.fillRect(x, y, length, band);
|
|
4361
4543
|
ctx.fillRect(x, y + thickness - band, length, band);
|
|
@@ -4369,7 +4551,9 @@ function fillEdge(ctx, rect, phaseOrigin, style, horizontal) {
|
|
|
4369
4551
|
ctx.fillRect(x, y, w, h);
|
|
4370
4552
|
}
|
|
4371
4553
|
/** A list item's bullet or number, in the margin. */
|
|
4372
|
-
function paintMarker(ctx, marker, options
|
|
4554
|
+
function paintMarker(ctx, marker, options,
|
|
4555
|
+
/** The item's colour scheme, which an SVG image in it is drawn in. */
|
|
4556
|
+
scheme) {
|
|
4373
4557
|
const x = marker.x + options.originX;
|
|
4374
4558
|
const y = marker.y + options.originY;
|
|
4375
4559
|
if (marker.image) {
|
|
@@ -4379,7 +4563,7 @@ function paintMarker(ctx, marker, options) {
|
|
|
4379
4563
|
if (!loaded || !(width > 0 && height > 0))
|
|
4380
4564
|
return;
|
|
4381
4565
|
if (loaded.image instanceof SvgDrawing) {
|
|
4382
|
-
loaded.image.
|
|
4566
|
+
loaded.image.drawImage(ctx, x, y, width, height, options.scale ?? 1, scheme);
|
|
4383
4567
|
}
|
|
4384
4568
|
else
|
|
4385
4569
|
ctx.drawImage?.(loaded.image, x, y, width, height);
|
|
@@ -4411,7 +4595,7 @@ function paintImage(ctx, box, options) {
|
|
|
4411
4595
|
const past = at.x < x || at.y < y || at.x + at.w > x + w || at.y + at.h > y + h;
|
|
4412
4596
|
const clipped = (!!corners || past) && pushClip(ctx, { x, y, w, h }, corners);
|
|
4413
4597
|
if (image instanceof SvgDrawing) {
|
|
4414
|
-
image.
|
|
4598
|
+
image.drawImage(ctx, at.x, at.y, at.w, at.h, options.scale ?? 1, box.style.colorScheme);
|
|
4415
4599
|
}
|
|
4416
4600
|
else
|
|
4417
4601
|
ctx.drawImage(image, at.x, at.y, at.w, at.h);
|
|
@@ -4499,7 +4683,8 @@ function fitted(box, x, y, w, h) {
|
|
|
4499
4683
|
/** An inline `<svg>`, drawn in its content box. Its `currentColor` is the
|
|
4500
4684
|
* box's `color`, as an icon's is the text's around it, and what it is
|
|
4501
4685
|
* painted with the box's `fill` and `stroke`, where the document's style
|
|
4502
|
-
* sheets set them: an icon set's `.icon { fill: currentColor }`.
|
|
4686
|
+
* sheets set them: an icon set's `.icon { fill: currentColor }`. What
|
|
4687
|
+
* they give a shape inside it is asked for here (`BoxTree.shapeStyler`). */
|
|
4503
4688
|
function paintSvg(ctx, box, options) {
|
|
4504
4689
|
if (!box.el)
|
|
4505
4690
|
return;
|
|
@@ -4511,7 +4696,7 @@ function paintSvg(ctx, box, options) {
|
|
|
4511
4696
|
const paint = (value) => value === null || value === 'currentColor' || value === 'none'
|
|
4512
4697
|
? value
|
|
4513
4698
|
: inkColor(value, style.color);
|
|
4514
|
-
inlineDrawing(box.el).draw(ctx, x, y, w, h, options.scale ?? 1, style.color, paint(style.fill), paint(style.stroke));
|
|
4699
|
+
inlineDrawing(box.el).draw(ctx, x, y, w, h, options.scale ?? 1, style.color, paint(style.fill), paint(style.stroke), options.shapeStyler?.(box) ?? null);
|
|
4515
4700
|
}
|
|
4516
4701
|
/**
|
|
4517
4702
|
* The X protocol carries glyph positions as Int16, so anything drawn past
|