Skip to content

Commit

Permalink
BlockContainerArea becomes BoxArea, is always defined
Browse files Browse the repository at this point in the history
I was reading about absolute positioning and realized that inline
boxes generate a containing block as well, since they could have
children absolutely positioned against them. This means non-IFC
inlines get a new property, sadly (more memory).
  • Loading branch information
chearon committed Dec 29, 2023
1 parent 7f85bf5 commit ddd3d69
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 149 deletions.
8 changes: 4 additions & 4 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {generateBlockContainer, layoutBlockBox, BlockFormattingContext, BlockCon
import HtmlPaintBackend from './paint-html.js';
import CanvasPaintBackend, {Canvas, CanvasRenderingContext2D} from './paint-canvas.js';
import paintBlockContainer from './paint.js';
import {BlockContainerArea} from './flow.js';
import {BoxArea} from './box.js';
import {id} from './util.js';

export type {BlockContainer, DeclaredPlainStyle};
Expand Down Expand Up @@ -43,13 +43,13 @@ export function generate(rootElement: HTMLElement) {
}

// Re-use the root containing block
let initialContainingBlock: BlockContainerArea | undefined;
let initialContainingBlock: BoxArea | undefined;

export function layout(root: BlockContainer, width = 640, height = 480) {
if (!initialContainingBlock) {
initialContainingBlock = new BlockContainerArea(root, 0, 0, width, height);
initialContainingBlock = new BoxArea(root, 0, 0, width, height);
} else {
initialContainingBlock.blockContainer = root;
initialContainingBlock.box = root;
initialContainingBlock.inlineSize = width;
initialContainingBlock.blockSize = height;
}
Expand Down
112 changes: 111 additions & 1 deletion src/box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export abstract class RenderItem {
let extra = '';

if (options?.containingBlocks && this.isBlockContainer()) {
extra += ` (cb = ${this.containingBlock ? this.containingBlock.blockContainer.id : '(null)'})`;
extra += ` (cb = ${this.containingBlock ? this.containingBlock.box.id : '(null)'})`;
}

if (options?.css) {
Expand All @@ -82,6 +82,7 @@ export class Box extends RenderItem {
public id: string;
public children: RenderItem[];
public attrs: number;
public containingBlock: BoxArea;

static ATTRS: {
isAnonymous: number,
Expand All @@ -96,6 +97,7 @@ export class Box extends RenderItem {
this.id = id();
this.children = children;
this.attrs = attrs;
this.containingBlock = EmptyContainingBlock;
}

isBox(): this is Box {
Expand Down Expand Up @@ -140,3 +142,111 @@ Box.ATTRS = {
isFloat: 1 << 3,
enableLogging: 1 << 4
};

export class BoxArea {
parent: BoxArea | null;
box: Box;
blockStart: number;
blockSize: number;
lineLeft: number;
inlineSize: number;

constructor(box: Box, x?: number, y?: number, w?: number, h?: number) {
this.parent = null;
this.box = box;
this.blockStart = y || 0;
this.blockSize = h || 0;
this.lineLeft = x || 0;
this.inlineSize = w || 0;
}

clone() {
return new BoxArea(
this.box,
this.lineLeft,
this.blockStart,
this.inlineSize,
this.blockSize
);
}

get writingMode() {
return this.box.style.writingMode;
}

get direction() {
return this.box.style.direction;
}

get x() {
return this.lineLeft;
}

get y() {
return this.blockStart;
}

get width() {
return this.inlineSize;
}

get height() {
return this.blockSize;
}

setParent(p: BoxArea) {
this.parent = p;
}

inlineSizeForPotentiallyOrthogonal(box: BlockContainer) {
if (!this.box) return this.inlineSize; // root area
if (!this.box.isBlockContainer()) return this.inlineSize; // cannot be orthogonal
if (
(this.box.writingModeAsParticipant === 'horizontal-tb') !==
(box.writingModeAsParticipant === 'horizontal-tb')
) {
return this.blockSize;
} else {
return this.inlineSize;
}
}

absolutify() {
let x, y, width, height;

if (!this.parent) {
throw new Error(`Cannot absolutify area for ${this.box.id}, parent was never set`);
}

if (this.parent.writingMode === 'vertical-lr') {
x = this.blockStart;
y = this.lineLeft;
width = this.blockSize;
height = this.inlineSize;
} else if (this.parent.writingMode === 'vertical-rl') {
x = this.parent.width - this.blockStart - this.blockSize;
y = this.lineLeft;
width = this.blockSize;
height = this.inlineSize;
} else if (this.parent.writingMode === 'horizontal-tb') {
x = this.lineLeft;
y = this.blockStart;
width = this.inlineSize;
height = this.blockSize;
} else {
return;
}

this.lineLeft = this.parent.x + x;
this.blockStart = this.parent.y + y;
this.inlineSize = width;
this.blockSize = height;
}

repr(indent = 0) {
const {width: w, height: h, x, y} = this;
return ' '.repeat(indent) + `⚃ Area ${this.box.id}: ${w}${h} @${x},${y}`;
}
}

const EmptyContainingBlock = new BoxArea(null!);
2 changes: 0 additions & 2 deletions src/cascade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@ export type ComputedPlainStyle = {
type Used = Pick<ComputedPlainStyle, 'lineHeight' | 'textAlign'>;

function resolvePercent(box: BlockContainer | IfcInline, cssVal: number | {value: number, unit: '%'}) {
if (!box.containingBlock) throw new Error('Assertion failed');
if (typeof cssVal === 'object') {
if (box.containingBlock.width === undefined) throw new Error('Assertion failed');
const inlineSize = box.containingBlock[LogicalMaps[box.writingModeAsParticipant].inlineSize];
Expand Down Expand Up @@ -444,7 +443,6 @@ export class Style implements ComputedPlainStyle {

getBlockSize(box: BlockContainer | IfcInline) {
let cssVal = this[LogicalMaps[box.writingModeAsParticipant].blockSize];
if (!box.containingBlock) throw new Error('Assertion failed');
if (typeof cssVal === 'object') {
const parentBlockSize = box.containingBlock[LogicalMaps[box.writingModeAsParticipant].blockSize];
if (parentBlockSize === undefined) return 'auto' as const; // §CSS2 10.5
Expand Down
Loading

0 comments on commit ddd3d69

Please sign in to comment.