@react-x11/components 0.12.0 → 0.13.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/dist/html/controls.d.ts +12 -3
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +15 -55
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +134 -20
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +519 -122
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/parse.d.ts +29 -2
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +277 -9
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +14 -0
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +2 -3
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts +11 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +59 -7
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +3 -1
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +25 -1
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +99 -6
- package/dist/html/fonts.js.map +1 -1
- package/dist/html/form.d.ts +200 -0
- package/dist/html/form.d.ts.map +1 -0
- package/dist/html/form.js +801 -0
- package/dist/html/form.js.map +1 -0
- package/dist/html/index.d.ts +12 -0
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +30 -301
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +5 -1
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +31 -1
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +52 -8
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/flex.js +4 -1
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +10 -0
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/node.d.ts +87 -15
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +510 -152
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +22 -1
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +290 -20
- package/dist/html/paint.js.map +1 -1
- package/dist/html/widgets.d.ts +29 -0
- package/dist/html/widgets.d.ts.map +1 -0
- package/dist/html/widgets.js +620 -0
- package/dist/html/widgets.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/html/controls.ts +29 -49
- package/src/html/css/cascade.ts +591 -140
- package/src/html/css/parse.ts +324 -9
- package/src/html/css/style.ts +24 -3
- package/src/html/css/ua.ts +62 -8
- package/src/html/dom.ts +3 -1
- package/src/html/fonts.ts +116 -6
- package/src/html/form.ts +962 -0
- package/src/html/index.ts +49 -338
- package/src/html/layout/block.ts +3 -0
- package/src/html/layout/boxes.ts +78 -11
- package/src/html/layout/flex.ts +4 -1
- package/src/html/layout/inline.ts +10 -0
- package/src/html/node.ts +498 -135
- package/src/html/paint.ts +330 -29
- package/src/html/widgets.ts +735 -0
- package/src/index.ts +1 -0
package/dist/html/paint.js
CHANGED
|
@@ -43,6 +43,15 @@ import { SvgDrawing, inlineDrawing } from './svg.js';
|
|
|
43
43
|
* (`layoutDocument`).
|
|
44
44
|
*/
|
|
45
45
|
export const OUT_OF_FLOW_REACH = new WeakMap();
|
|
46
|
+
/**
|
|
47
|
+
* How far above and below a block's lines its inline content draws, at
|
|
48
|
+
* most, by the array its lines are: glyphs taller than a `line-height` under
|
|
49
|
+
* their face's height, and an atomic's ink past the line it is on. The
|
|
50
|
+
* slack a paint's cull of the lines takes on top of their own rows
|
|
51
|
+
* (`paintLines`); none for the lines of almost every block, whose content
|
|
52
|
+
* is inside them.
|
|
53
|
+
*/
|
|
54
|
+
const LINE_INK = new WeakMap();
|
|
46
55
|
export function computePaintBounds(box, moved = false) {
|
|
47
56
|
// A box with no rectangle of its own gives only what it holds: nothing,
|
|
48
57
|
// until a child with bounds is met.
|
|
@@ -170,8 +179,21 @@ export function computePaintBounds(box, moved = false) {
|
|
|
170
179
|
// above has its bounds already: walked from its line as well, an
|
|
171
180
|
// inline-block in an inline-block was walked twice a level, and twenty of
|
|
172
181
|
// them took seventy milliseconds a layout.
|
|
182
|
+
//
|
|
183
|
+
// And a line's text is drawn from its face's ascent above the baseline to
|
|
184
|
+
// its descent below, its content area (CSS 2.1 10.6.1), which is past the
|
|
185
|
+
// line box wherever `line-height` is under the face's height: a line box
|
|
186
|
+
// is its line-height, whatever its glyphs are. Zen Garden 215's 91px title
|
|
187
|
+
// sits on the 20px lines its body's `line-height: 1.25em` gave it, as a
|
|
188
|
+
// length, and hangs 40px above them and 40px below — ink that a repaint
|
|
189
|
+
// of those rows has to reach, where the lines alone do not.
|
|
190
|
+
let above = 0;
|
|
191
|
+
let below = 0;
|
|
173
192
|
if (lines) {
|
|
193
|
+
const movedOff = MOVED_OFF_LINES.get(lines);
|
|
174
194
|
for (const line of lines) {
|
|
195
|
+
let top = line.y;
|
|
196
|
+
let bottom = line.y + line.height;
|
|
175
197
|
for (const placed of line.atomics) {
|
|
176
198
|
const atomic = placed.box;
|
|
177
199
|
if (atomic.boundsY === Infinity)
|
|
@@ -180,9 +202,35 @@ export function computePaintBounds(box, moved = false) {
|
|
|
180
202
|
y1 = Math.min(y1, atomic.boundsY);
|
|
181
203
|
x2 = Math.max(x2, atomic.boundsX + atomic.boundsWidth);
|
|
182
204
|
y2 = Math.max(y2, atomic.boundsY + atomic.boundsHeight);
|
|
205
|
+
top = Math.min(top, atomic.boundsY);
|
|
206
|
+
bottom = Math.max(bottom, atomic.boundsY + atomic.boundsHeight);
|
|
207
|
+
}
|
|
208
|
+
// what `position: relative` moved off a line the cull finds on its
|
|
209
|
+
// own (`reachedOff`), wherever it went
|
|
210
|
+
if (movedOff?.has(line))
|
|
211
|
+
continue;
|
|
212
|
+
for (const text of line.texts) {
|
|
213
|
+
const natural = text.layout.lines[text.layoutLine];
|
|
214
|
+
if (!natural)
|
|
215
|
+
continue;
|
|
216
|
+
const baseline = text.drawY + natural.baseline;
|
|
217
|
+
top = Math.min(top, baseline - (natural.ascent ?? 0));
|
|
218
|
+
bottom = Math.max(bottom, baseline + (natural.descent ?? 0));
|
|
219
|
+
}
|
|
220
|
+
if (top < line.y) {
|
|
221
|
+
y1 = Math.min(y1, top);
|
|
222
|
+
above = Math.max(above, line.y - top);
|
|
223
|
+
}
|
|
224
|
+
if (bottom > line.y + line.height) {
|
|
225
|
+
y2 = Math.max(y2, bottom);
|
|
226
|
+
below = Math.max(below, bottom - line.y - line.height);
|
|
183
227
|
}
|
|
184
228
|
}
|
|
185
229
|
}
|
|
230
|
+
if (lines && (above > 0 || below > 0))
|
|
231
|
+
LINE_INK.set(lines, { above, below });
|
|
232
|
+
else if (lines)
|
|
233
|
+
LINE_INK.delete(lines);
|
|
186
234
|
if (x1 === Infinity) {
|
|
187
235
|
// nothing to draw: no damage meets it, and no parent takes it in
|
|
188
236
|
box.boundsX = Infinity;
|
|
@@ -369,6 +417,7 @@ export function paintDocument(ctx, tree, options) {
|
|
|
369
417
|
...options,
|
|
370
418
|
canvasSource: canvas?.source,
|
|
371
419
|
negative: tree.negative,
|
|
420
|
+
selectionStyler: options.selection ? tree.selectionStyler : null,
|
|
372
421
|
});
|
|
373
422
|
ctx.restore();
|
|
374
423
|
}
|
|
@@ -3502,7 +3551,12 @@ function paintLines(ctx, box, options) {
|
|
|
3502
3551
|
const bottom = top + damage.height;
|
|
3503
3552
|
let lo = 0;
|
|
3504
3553
|
let hi = lines.length;
|
|
3505
|
-
|
|
3554
|
+
// and a line whose glyphs, or an atomic on it, reach past it
|
|
3555
|
+
// (`LINE_INK`) is drawn where they reach
|
|
3556
|
+
const ink = LINE_INK.get(lines);
|
|
3557
|
+
const above = ink?.above ?? 0;
|
|
3558
|
+
const below = ink?.below ?? 0;
|
|
3559
|
+
const slack = top - box.maxLineHeight - below;
|
|
3506
3560
|
while (lo < hi) {
|
|
3507
3561
|
const mid = (lo + hi) >> 1;
|
|
3508
3562
|
if (lines[mid].y > slack)
|
|
@@ -3512,10 +3566,13 @@ function paintLines(ctx, box, options) {
|
|
|
3512
3566
|
}
|
|
3513
3567
|
for (let i = lo; i < lines.length; i += 1) {
|
|
3514
3568
|
const line = lines[i];
|
|
3515
|
-
if (line.y >= bottom)
|
|
3569
|
+
if (line.y - above >= bottom)
|
|
3516
3570
|
break;
|
|
3517
|
-
if (line.y + line.height > top)
|
|
3571
|
+
if (line.y < bottom && line.y + line.height > top)
|
|
3572
|
+
visible.push(line);
|
|
3573
|
+
else if (ink && inkInto(line, top, bottom)) {
|
|
3518
3574
|
visible.push(line);
|
|
3575
|
+
}
|
|
3519
3576
|
}
|
|
3520
3577
|
// and a line the damage misses whose text or inline-block `position:
|
|
3521
3578
|
// relative` moved into it: drawn by its line, it went undrawn with it
|
|
@@ -3523,7 +3580,7 @@ function paintLines(ctx, box, options) {
|
|
|
3523
3580
|
if (moved && reachedOff(visible, moved, top, bottom)) {
|
|
3524
3581
|
const drawn = new Set(visible);
|
|
3525
3582
|
for (const line of moved) {
|
|
3526
|
-
if (
|
|
3583
|
+
if (inkInto(line, top, bottom))
|
|
3527
3584
|
drawn.add(line);
|
|
3528
3585
|
}
|
|
3529
3586
|
visible.length = 0;
|
|
@@ -3560,6 +3617,10 @@ function paintLines(ctx, box, options) {
|
|
|
3560
3617
|
// drawing it once per line would be one X request per line for the same
|
|
3561
3618
|
// batch.
|
|
3562
3619
|
const drawn = new Set();
|
|
3620
|
+
// text a `::selection` colours is drawn in that colour where selected
|
|
3621
|
+
const recolored = options.selectionStyler
|
|
3622
|
+
? recoloredBands(visible, options)
|
|
3623
|
+
: null;
|
|
3563
3624
|
for (const line of visible) {
|
|
3564
3625
|
for (const text of line.texts) {
|
|
3565
3626
|
if (drawn.has(text.layout))
|
|
@@ -3574,7 +3635,11 @@ function paintLines(ctx, box, options) {
|
|
|
3574
3635
|
// thrown from the protocol encoder.
|
|
3575
3636
|
continue;
|
|
3576
3637
|
}
|
|
3577
|
-
text.layout
|
|
3638
|
+
const bands = recolored?.get(text.layout);
|
|
3639
|
+
if (bands)
|
|
3640
|
+
drawRecolored(ctx, text.layout, text.drawX + dx, top, bands);
|
|
3641
|
+
else
|
|
3642
|
+
text.layout.draw(ctx, text.drawX + dx, top);
|
|
3578
3643
|
}
|
|
3579
3644
|
}
|
|
3580
3645
|
paintRules(ctx, visible, dx, dy, options.scale ?? 1, 'over');
|
|
@@ -3596,15 +3661,16 @@ function paintLines(ctx, box, options) {
|
|
|
3596
3661
|
* does not already draw, has it within the damage's rows. */
|
|
3597
3662
|
function reachedOff(visible, moved, top, bottom) {
|
|
3598
3663
|
for (const line of moved) {
|
|
3599
|
-
if (!visible.includes(line) &&
|
|
3664
|
+
if (!visible.includes(line) && inkInto(line, top, bottom))
|
|
3600
3665
|
return true;
|
|
3601
3666
|
}
|
|
3602
3667
|
return false;
|
|
3603
3668
|
}
|
|
3604
|
-
/** Whether what
|
|
3605
|
-
*
|
|
3606
|
-
* between `top` and `bottom
|
|
3607
|
-
|
|
3669
|
+
/** Whether what a line draws — its inline-blocks and images, where their
|
|
3670
|
+
* ink is, and its texts, where they are drawn, their glyphs from ascent to
|
|
3671
|
+
* descent — reaches the rows between `top` and `bottom`: what was moved off
|
|
3672
|
+
* it, and what hangs past it. */
|
|
3673
|
+
function inkInto(line, top, bottom) {
|
|
3608
3674
|
for (const placed of line.atomics) {
|
|
3609
3675
|
const box = placed.box;
|
|
3610
3676
|
if (box.boundsY < bottom && box.boundsY + box.boundsHeight > top) {
|
|
@@ -3615,8 +3681,10 @@ function movedInto(line, top, bottom) {
|
|
|
3615
3681
|
const natural = text.layout.lines[text.layoutLine];
|
|
3616
3682
|
if (!natural)
|
|
3617
3683
|
continue;
|
|
3618
|
-
const
|
|
3619
|
-
|
|
3684
|
+
const baseline = text.drawY + natural.baseline;
|
|
3685
|
+
const y1 = Math.min(text.drawY + natural.y, baseline - (natural.ascent ?? 0));
|
|
3686
|
+
const y2 = Math.max(text.drawY + natural.y + natural.height, baseline + (natural.descent ?? 0));
|
|
3687
|
+
if (y1 < bottom && y2 > top)
|
|
3620
3688
|
return true;
|
|
3621
3689
|
}
|
|
3622
3690
|
return false;
|
|
@@ -4361,15 +4429,34 @@ export function forgetDecoratedAncestors(box) {
|
|
|
4361
4429
|
*
|
|
4362
4430
|
* Translucent under the glyphs rather than inverted over them, so the ink
|
|
4363
4431
|
* keeps its contrast on either palette — the same call `<textarea>` and
|
|
4364
|
-
* `<richtext>` both make.
|
|
4432
|
+
* `<richtext>` both make. From the top of the line to its bottom, and past
|
|
4433
|
+
* them over the glyphs where they are taller than the line (`selectionRows`).
|
|
4365
4434
|
*/
|
|
4366
4435
|
function paintSelection(ctx, line, options) {
|
|
4436
|
+
for (const band of selectedBands(line, options)) {
|
|
4437
|
+
const fill = band.style ? band.style.background : options.selectionColor;
|
|
4438
|
+
if (!fill || isTransparent(fill))
|
|
4439
|
+
continue;
|
|
4440
|
+
ctx.fillStyle = fill;
|
|
4441
|
+
ctx.fillRect(band.x, band.y, band.width, band.height);
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
const NO_BANDS = [];
|
|
4445
|
+
/**
|
|
4446
|
+
* Where the document selection covers a line's text, a band for each
|
|
4447
|
+
* stretch of it one `::selection` styles, in window pixels as the band is
|
|
4448
|
+
* filled — rounded, so a band's text is drawn to its edges and no further
|
|
4449
|
+
* (`drawRecolored`).
|
|
4450
|
+
*/
|
|
4451
|
+
function selectedBands(line, options) {
|
|
4367
4452
|
const range = options.selection;
|
|
4368
|
-
if (!range || range.end <= range.start
|
|
4369
|
-
return;
|
|
4370
|
-
if (line.textEnd <= range.start || line.textStart >= range.end)
|
|
4371
|
-
return;
|
|
4372
|
-
|
|
4453
|
+
if (!range || range.end <= range.start)
|
|
4454
|
+
return NO_BANDS;
|
|
4455
|
+
if (line.textEnd <= range.start || line.textStart >= range.end) {
|
|
4456
|
+
return NO_BANDS;
|
|
4457
|
+
}
|
|
4458
|
+
const styler = options.selectionStyler;
|
|
4459
|
+
const out = [];
|
|
4373
4460
|
for (const text of line.texts) {
|
|
4374
4461
|
const natural = text.layout.lines[text.layoutLine];
|
|
4375
4462
|
if (!natural)
|
|
@@ -4381,9 +4468,192 @@ function paintSelection(ctx, line, options) {
|
|
|
4381
4468
|
const offsets = layoutOffsets(text.layout);
|
|
4382
4469
|
const layoutFrom = layoutOffsetOf(text, from);
|
|
4383
4470
|
const layoutTo = layoutOffsetOf(text, to, true);
|
|
4384
|
-
|
|
4385
|
-
|
|
4471
|
+
const rows = selectionRows(line, text, natural);
|
|
4472
|
+
const y = Math.round(rows.y + options.originY);
|
|
4473
|
+
const height = Math.ceil(rows.height);
|
|
4474
|
+
const pieces = styler
|
|
4475
|
+
? stylePieces(text, natural, layoutFrom, layoutTo, styler)
|
|
4476
|
+
: [{ from: layoutFrom, to: layoutTo, style: null }];
|
|
4477
|
+
for (const piece of pieces) {
|
|
4478
|
+
for (const band of lineBands(text.layout, natural, offsets, piece.from, piece.to)) {
|
|
4479
|
+
out.push({
|
|
4480
|
+
x: Math.round(band.x + text.drawX + options.originX),
|
|
4481
|
+
y,
|
|
4482
|
+
width: Math.ceil(band.width),
|
|
4483
|
+
height,
|
|
4484
|
+
style: piece.style,
|
|
4485
|
+
layout: text.layout,
|
|
4486
|
+
});
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
return out;
|
|
4491
|
+
}
|
|
4492
|
+
/**
|
|
4493
|
+
* A line's selected text, `from` to `to` in the layout's offsets, cut where
|
|
4494
|
+
* the `::selection` over it changes: a run's is its element's, and a run of
|
|
4495
|
+
* no element's — an inline box's edge, laid out as a spacer — the one
|
|
4496
|
+
* beside it, so a styled band has no palette-coloured gap at a `<span>`.
|
|
4497
|
+
*/
|
|
4498
|
+
function stylePieces(text, natural, from, to, styler) {
|
|
4499
|
+
const runs = natural.runs
|
|
4500
|
+
.filter((run) => run.end > from && run.start < to)
|
|
4501
|
+
.sort((a, b) => a.start - b.start);
|
|
4502
|
+
const boxAt = text.spans.boxAt;
|
|
4503
|
+
const styles = runs.map((run) => {
|
|
4504
|
+
const box = boxAt?.call(text.spans, run.start) ?? null;
|
|
4505
|
+
return box ? styler(box) : undefined;
|
|
4506
|
+
});
|
|
4507
|
+
for (let i = 1; i < styles.length; i += 1) {
|
|
4508
|
+
if (styles[i] === undefined)
|
|
4509
|
+
styles[i] = styles[i - 1];
|
|
4510
|
+
}
|
|
4511
|
+
for (let i = styles.length - 2; i >= 0; i -= 1) {
|
|
4512
|
+
if (styles[i] === undefined)
|
|
4513
|
+
styles[i] = styles[i + 1];
|
|
4514
|
+
}
|
|
4515
|
+
const out = [];
|
|
4516
|
+
runs.forEach((run, i) => {
|
|
4517
|
+
const style = styles[i] ?? null;
|
|
4518
|
+
const last = out[out.length - 1];
|
|
4519
|
+
if (last && last.style === style)
|
|
4520
|
+
last.to = Math.min(to, run.end);
|
|
4521
|
+
else {
|
|
4522
|
+
out.push({
|
|
4523
|
+
from: Math.max(from, run.start),
|
|
4524
|
+
to: Math.min(to, run.end),
|
|
4525
|
+
style,
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
4528
|
+
});
|
|
4529
|
+
return out;
|
|
4530
|
+
}
|
|
4531
|
+
/**
|
|
4532
|
+
* The layouts whose selected text a `::selection` gives a colour of its
|
|
4533
|
+
* own, each with the bands it is drawn in that colour (`drawRecolored`).
|
|
4534
|
+
* Null where none does, which is every document with no such rule.
|
|
4535
|
+
*/
|
|
4536
|
+
function recoloredBands(lines, options) {
|
|
4537
|
+
let out = null;
|
|
4538
|
+
for (const line of lines) {
|
|
4539
|
+
for (const band of selectedBands(line, options)) {
|
|
4540
|
+
if (!band.style?.color || !(band.width > 0))
|
|
4541
|
+
continue;
|
|
4542
|
+
out ??= new Map();
|
|
4543
|
+
const list = out.get(band.layout);
|
|
4544
|
+
if (list)
|
|
4545
|
+
list.push(band);
|
|
4546
|
+
else
|
|
4547
|
+
out.set(band.layout, [band]);
|
|
4548
|
+
}
|
|
4549
|
+
}
|
|
4550
|
+
return out;
|
|
4551
|
+
}
|
|
4552
|
+
/**
|
|
4553
|
+
* A layout with some of its text selected in a `::selection`'s colour
|
|
4554
|
+
* (CSS Pseudo 4, 3.2): its own colours everywhere but those bands, and in
|
|
4555
|
+
* each band the glyphs in that band's colour — cast there as a hard shadow
|
|
4556
|
+
* from the layout drawn clear of the window, the way `castShadows` draws a
|
|
4557
|
+
* text shadow, since a layout draws in its runs' own colours. Drawn over
|
|
4558
|
+
* rather than in place of the text, the old glyphs' edges showed round the
|
|
4559
|
+
* new ones. Where the context cannot clip or cast, the text keeps its own
|
|
4560
|
+
* colours.
|
|
4561
|
+
*/
|
|
4562
|
+
function drawRecolored(ctx, layout, left, top, bands) {
|
|
4563
|
+
if (!ctx.save ||
|
|
4564
|
+
!ctx.restore ||
|
|
4565
|
+
!ctx.beginPath ||
|
|
4566
|
+
!ctx.rect ||
|
|
4567
|
+
!ctx.clip ||
|
|
4568
|
+
!('shadowBlur' in ctx)) {
|
|
4569
|
+
layout.draw(ctx, left, top);
|
|
4570
|
+
return;
|
|
4571
|
+
}
|
|
4572
|
+
ctx.save();
|
|
4573
|
+
ctx.beginPath();
|
|
4574
|
+
for (const r of outsideOf(bands))
|
|
4575
|
+
ctx.rect(r.x, r.y, r.width, r.height);
|
|
4576
|
+
ctx.clip();
|
|
4577
|
+
layout.draw(ctx, left, top);
|
|
4578
|
+
ctx.restore();
|
|
4579
|
+
let right = layout.width;
|
|
4580
|
+
for (const natural of layout.lines) {
|
|
4581
|
+
right = Math.max(right, natural.x + natural.width);
|
|
4582
|
+
}
|
|
4583
|
+
const shift = Math.ceil(left + right) + 1;
|
|
4584
|
+
const colors = new Map();
|
|
4585
|
+
for (const band of bands) {
|
|
4586
|
+
const color = band.style.color;
|
|
4587
|
+
const group = colors.get(color);
|
|
4588
|
+
if (group)
|
|
4589
|
+
group.push(band);
|
|
4590
|
+
else
|
|
4591
|
+
colors.set(color, [band]);
|
|
4592
|
+
}
|
|
4593
|
+
for (const [color, group] of colors) {
|
|
4594
|
+
ctx.save();
|
|
4595
|
+
ctx.beginPath();
|
|
4596
|
+
for (const r of group)
|
|
4597
|
+
ctx.rect(r.x, r.y, r.width, r.height);
|
|
4598
|
+
ctx.clip();
|
|
4599
|
+
ctx.shadowColor = color;
|
|
4600
|
+
ctx.shadowBlur = 0.01;
|
|
4601
|
+
ctx.shadowOffsetX = shift;
|
|
4602
|
+
ctx.shadowOffsetY = 0;
|
|
4603
|
+
layout.draw(ctx, left - shift, top);
|
|
4604
|
+
ctx.restore();
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
/** Everything but some rectangles, as rectangles: a strip between each two
|
|
4608
|
+
* of their edges, less what of it they cover. */
|
|
4609
|
+
function outsideOf(rects) {
|
|
4610
|
+
const far = COORD_LIMIT;
|
|
4611
|
+
const edges = new Set();
|
|
4612
|
+
for (const r of rects) {
|
|
4613
|
+
edges.add(r.y);
|
|
4614
|
+
edges.add(r.y + r.height);
|
|
4615
|
+
}
|
|
4616
|
+
const ys = [...edges].sort((a, b) => a - b);
|
|
4617
|
+
const out = [
|
|
4618
|
+
{ x: -far, y: -far, width: 2 * far, height: ys[0] + far },
|
|
4619
|
+
{
|
|
4620
|
+
x: -far,
|
|
4621
|
+
y: ys[ys.length - 1],
|
|
4622
|
+
width: 2 * far,
|
|
4623
|
+
height: far - ys[ys.length - 1],
|
|
4624
|
+
},
|
|
4625
|
+
];
|
|
4626
|
+
for (let i = 0; i + 1 < ys.length; i += 1) {
|
|
4627
|
+
const y0 = ys[i];
|
|
4628
|
+
const y1 = ys[i + 1];
|
|
4629
|
+
const spans = rects
|
|
4630
|
+
.filter((r) => r.y <= y0 && r.y + r.height >= y1)
|
|
4631
|
+
.sort((a, b) => a.x - b.x);
|
|
4632
|
+
let x = -far;
|
|
4633
|
+
for (const r of spans) {
|
|
4634
|
+
if (r.x > x)
|
|
4635
|
+
out.push({ x, y: y0, width: r.x - x, height: y1 - y0 });
|
|
4636
|
+
x = Math.max(x, r.x + r.width);
|
|
4386
4637
|
}
|
|
4638
|
+
if (x < far)
|
|
4639
|
+
out.push({ x, y: y0, width: far - x, height: y1 - y0 });
|
|
4387
4640
|
}
|
|
4641
|
+
return out;
|
|
4642
|
+
}
|
|
4643
|
+
/**
|
|
4644
|
+
* The rows a highlight over a line's text covers: the line box's, united
|
|
4645
|
+
* with the text's content area — its face's ascent above the baseline to
|
|
4646
|
+
* its descent below (CSS 2.1 10.6.1) — which is taller wherever
|
|
4647
|
+
* `line-height` is under the face's height. Blink unites the two in the
|
|
4648
|
+
* block direction (`ExpandSelectionRectToLineHeight`), so a selection over
|
|
4649
|
+
* a tall line fills it and one over tall glyphs on a short line covers
|
|
4650
|
+
* them: Zen Garden 215's 91px title on 20px lines had a band a fifth of
|
|
4651
|
+
* its letters' height through their middle.
|
|
4652
|
+
*/
|
|
4653
|
+
export function selectionRows(line, text, natural) {
|
|
4654
|
+
const baseline = text.drawY + natural.baseline;
|
|
4655
|
+
const top = Math.min(line.y, baseline - (natural.ascent ?? 0));
|
|
4656
|
+
const bottom = Math.max(line.y + line.height, baseline + (natural.descent ?? 0));
|
|
4657
|
+
return { y: top, height: bottom - top };
|
|
4388
4658
|
}
|
|
4389
4659
|
//# sourceMappingURL=paint.js.map
|