Skip to content

Commit 3991737

Browse files
committed
[css-layout-api] Add fragmenting example.
1 parent 1ba2909 commit 3991737

File tree

2 files changed

+168
-47
lines changed

2 files changed

+168
-47
lines changed

css-layout-api/EXPLAINER.md

+29-6
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ We pass the `BreakToken` to add back into the `layout()` call in order to produc
340340
registerLayout('basic-inline', class extends Layout {
341341
static inputProperties = super.inputProperties;
342342

343-
*layout(constraintSpace, children, styleMap) {
343+
*layout(constraintSpace, children, styleMap, breakToken) {
344344
// Resolve our inline size.
345345
const inlineSize = resolveInlineSize(constraintSpace, styleMap);
346346

@@ -381,8 +381,15 @@ registerLayout('basic-inline', class extends Layout {
381381
maxLineBlockSize = 0;
382382
}
383383

384+
let childBreakToken = null;
385+
if (breakToken) {
386+
childBreakToken = breakToken.childBreakTokens[0];
387+
388+
// Remove all the children we have already produced fragments for.
389+
children.splice(0, children.indexOf(childBreakToken.child));
390+
}
391+
384392
let child = children.shift();
385-
let breakToken = null;
386393
while (child) {
387394
// Make sure we actually have space on the current line.
388395
if (usedInlineSize > availableInlineSize) {
@@ -395,15 +402,22 @@ registerLayout('basic-inline', class extends Layout {
395402
const constraintSpace = new ConstraintSpace({
396403
inlineSize: availableInlineSize - usedInlineSize,
397404
blockSize: availableBlockSize,
398-
percentageInlineSize: availableInlineSize
405+
percentageInlineSize: availableInlineSize,
406+
inlineShrinkToFit: true,
399407
});
400408

401-
const fragment = yield child.doLayout(constraintSpace, breakToken);
409+
const fragment = yield child.doLayout(constraintSpace, childBreakToken);
402410
childFragments.push(fragment);
403411

404412
// Check if there is still space on the current line.
405413
if (fragment.inlineSize > remainingInlineSize) {
406414
nextLine();
415+
416+
// Check if we have gone over the block fragmentation limit.
417+
if (constraintSpace.blockFragmentationType != 'none' &&
418+
lineOffset > constraintSpace.blockSize) {
419+
break;
420+
}
407421
}
408422

409423
// Insert fragment on the current line.
@@ -428,11 +442,12 @@ registerLayout('basic-inline', class extends Layout {
428442
}
429443

430444
if (fragment.breakToken) {
431-
breakToken = fragment.breakToken;
445+
childBreakToken = fragment.breakToken;
432446
} else {
433447
// If a fragment doesn't have a break token, we move onto the next
434448
// child.
435449
child = children.shift();
450+
childBreakToken = null;
436451
}
437452
}
438453

@@ -444,13 +459,21 @@ registerLayout('basic-inline', class extends Layout {
444459
const blockSize = resolveBlockSize(constraintSpace, styleMap, blockOverflowSize);
445460

446461
// Return our fragment.
447-
return {
462+
const result = {
448463
inlineSize: inlineSize,
449464
blockSize: blockSize,
450465
inlineOverflowSize: maxInlineSize,
451466
blockOverflowSize: blockOverflowSize,
452467
childFragments: childFragments,
453468
}
469+
470+
if (childBreakToken) {
471+
result.breakToken = {
472+
childBreakTokens: [childBreakToken],
473+
};
474+
}
475+
476+
return result;
454477
}
455478
});
456479
```

css-layout-api/Overview.bs

+139-41
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ Breaking and Fragmentation {#breaking-and-fragmentation}
493493
<pre class="idl">
494494
interface ChildBreakToken {
495495
readonly attribute BreakType breakType;
496-
readonly attribute Box box;
496+
readonly attribute LayoutChild child;
497497
};
498498

499499
interface BreakToken {
@@ -519,62 +519,160 @@ A subsequent {{Fragment}} is produced by using the previous {{Fragment}}'s {{Fra
519519
This tells the <a>child layout</a> to produce a {{Fragment}} starting at the point encoded in the
520520
{{ChildBreakToken}}.
521521

522-
When returning a list of {{Fragment}}s from the <a>current layout</a> the list of {{Fragment}}s must
523-
have a contiguous set of {{Fragment/breakToken}}s.
524-
525522
<div class="example">
526-
TODO: add example showing non-contiguous set of fragments.
527-
</div>
523+
This example shows a simple inline layout which places child fragments in the inline direction. It
524+
places each of its on a line, aligning their dominant baselines.
528525

529-
<div class="example">
530-
This example shows how to use a previous {{Fragment}}'s {{Fragment/breakToken}} to produce the next
531-
{{Fragment}} in the sequence.
526+
This example also demonstrates using the previous {{Fragment/breakToken}} of a {{Fragment}} to
527+
produce the next fragment for the {{LayoutChild}}.
528+
529+
It also demonstrates using the {{BreakToken}} to respect the {{ConstraintSpace}}'s
530+
{{ConstraintSpace/blockFragmentationType}}, it resumes it layout from the previous {{BreakToken}}.
531+
It returns a {{FragmentResultOptions}} with a {{FragmentResultOptions/breakToken}} which is used to
532+
resume the layout.
532533

533534
<pre class="lang-javascript">
534-
registerLayout('fragmenting', class {
535-
*layout(space, children, styleMap, breakToken) {
535+
registerLayout('basic-inline', class extends Layout {
536+
static inputProperties = super.inputProperties;
536537

537-
// ... snip ...
538+
*layout(constraintSpace, children, styleMap, breakToken) {
539+
// Resolve our inline size.
540+
const inlineSize = resolveInlineSize(constraintSpace, styleMap);
538541

539-
const fragments = [];
540-
let breakToken = null;
541-
for (let child of children) {
542-
// This do-while loop will keep producing fragments for a child box
543-
// until it cannot produce any more.
544-
do {
545-
// ... snip ... (setting up child constraint space).
542+
// Determine our (inner) available size.
543+
const bordersAndPadding =
544+
resolveBordersAndPadding(constraintSpace, styleMap);
545+
const scrollbarSize = resolveScrollbarSize(constraintSpace, styleMap);
546+
const availableInlineSize = inlineSize -
547+
bordersAndPadding.inlineStart -
548+
bordersAndPadding.inlineEnd -
549+
scrollbarSize.inline;
546550

547-
let fragment = yield child.doLayout(childSpace, breakToken);
548-
breakToken = fragment.breakToken;
551+
const availableBlockSize = resolveBlockSize(constraintSpace, styleMap) -
552+
bordersAndPadding.blockStart -
553+
bordersAndPadding.blockEnd -
554+
scrollbarSize.block;
549555

550-
fragments.push(fragment);
551-
} while (breakToken);
552-
}
556+
const childFragments = [];
557+
let maxInlineSize = 0;
553558

554-
// ... snip ...
559+
let currentLine = [];
560+
let usedInlineSize = 0;
561+
let maxBaseline = 0;
555562

556-
}
557-
});
558-
</pre>
559-
</div>
563+
let lineOffset = 0;
564+
let maxLineBlockSize = 0;
560565

561-
<div class="example">
562-
This example shows how to use resume a layout given a breakToken.
566+
// Just a small little function which will update the above variables.
567+
const nextLine = function() {
568+
if (usedInlineSize > maxInlineSize) {
569+
maxInlineSize = usedInlineSize;
570+
}
563571

564-
<pre class="lang-javascript">
565-
registerLayout('fragmenting', class {
566-
*layout(space, children, styleMap, breakToken) {
567-
const fragments = [];
572+
currentLine = [];
573+
usedInlineSize = 0;
574+
maxBaseline = 0;
568575

569-
// Produce the next fragment in the sequence if we have a breakToken.
576+
lineOffset += maxLineBlockSize;
577+
maxLineBlockSize = 0;
578+
}
579+
580+
let childBreakToken = null;
570581
if (breakToken) {
571-
for (let childBreakToken of breakToken.childBreakTokens) {
572-
let fragment = yield childBreakToken.box.doLayout(
573-
childSpace, childBreakToken);
582+
childBreakToken = breakToken.childBreakTokens[0];
583+
584+
// Remove all the children we have already produced fragments for.
585+
children.splice(0, children.indexOf(childBreakToken.child));
586+
}
587+
588+
let child = children.shift();
589+
while (child) {
590+
// Make sure we actually have space on the current line.
591+
if (usedInlineSize > availableInlineSize) {
592+
nextLine();
593+
}
594+
595+
// The constraint space here will have the inline size of the
596+
// remaining space on the line.
597+
const remainingInlineSize = availableInlineSize - usedInlineSize;
598+
const constraintSpace = new ConstraintSpace({
599+
inlineSize: availableInlineSize - usedInlineSize,
600+
blockSize: availableBlockSize,
601+
percentageInlineSize: availableInlineSize,
602+
inlineShrinkToFit: true,
603+
});
604+
605+
const fragment = yield child.doLayout(constraintSpace,
606+
childBreakToken);
607+
childFragments.push(fragment);
608+
609+
// Check if there is still space on the current line.
610+
if (fragment.inlineSize > remainingInlineSize) {
611+
nextLine();
612+
613+
// Check if we have gone over the block fragmentation limit.
614+
if (constraintSpace.blockFragmentationType != 'none' &&
615+
lineOffset > constraintSpace.blockSize) {
616+
break;
617+
}
618+
}
619+
620+
// Insert fragment on the current line.
621+
currentLine.push(fragment);
622+
fragment.inlineOffset = usedInlineSize;
623+
624+
if (fragment.dominantBaseline > maxBaseline) {
625+
maxBaseline = fragment.dominantBaseline;
626+
}
627+
628+
// Go through each of the fragments on the line and update their
629+
// block offsets.
630+
for (let fragmentOnLine of currentLine) {
631+
fragmentOnLine.blockOffset = lineOffset +
632+
maxBaseline - fragmentOnLine.dominantBaseline;
633+
634+
const lineBlockSize =
635+
fragmentOnLine.blockOffset + fragmentOnLine.blockSize;
636+
if (maxLineBlockSize < lineBlockSize) {
637+
maxLineBlockSize = lineBlockSize;
638+
}
639+
}
574640

575-
fragments.push(fragment);
641+
if (fragment.breakToken) {
642+
childBreakToken = fragment.breakToken;
643+
} else {
644+
// If a fragment doesn't have a break token, we move onto the
645+
// next child.
646+
child = children.shift();
647+
childBreakToken = null;
576648
}
577649
}
650+
651+
// Determine our block size.
652+
nextLine();
653+
const blockOverflowSize = lineOffset +
654+
bordersAndPadding.blockStart +
655+
bordersAndPadding.blockEnd;
656+
const blockSize = resolveBlockSize(constraintSpace,
657+
styleMap,
658+
blockOverflowSize);
659+
660+
// Return our fragment.
661+
const result = {
662+
inlineSize: inlineSize,
663+
blockSize: blockSize,
664+
inlineOverflowSize: maxInlineSize,
665+
blockOverflowSize: blockOverflowSize,
666+
childFragments: childFragments,
667+
}
668+
669+
if (childBreakToken) {
670+
result.breakToken = {
671+
childBreakTokens: [childBreakToken],
672+
};
673+
}
674+
675+
return result;
578676
}
579677
});
580678
</pre>
@@ -969,7 +1067,7 @@ context</a> for a given |box|, |constraintSpace|, |children| and an optional |br
9691067
- The <a>border box</a> <a>inline size</a> is set to |fragmentResult|'s
9701068
{{FragmentResultOptions/inlineSize}}.
9711069

972-
- The <a>border box</a> <a>block size</a> is set to |fragmentRequest|'s
1070+
- The <a>border box</a> <a>block size</a> is set to |fragmentResult|'s
9731071
{{FragmentResultOptions/blockSize}}.
9741072

9751073
- The <a>inline overflow size</a> is set to |fragmentResult|'s

0 commit comments

Comments
 (0)