mirror of
https://github.com/rio-labs/rio.git
synced 2026-01-05 04:39:49 -06:00
149 lines
4.8 KiB
TypeScript
149 lines
4.8 KiB
TypeScript
import { pixelsPerRem } from './app';
|
|
import { getRootComponent } from './componentManagement';
|
|
import { ComponentBase } from './components/componentBase';
|
|
|
|
export class LayoutContext {
|
|
_immediateReLayoutCallbacks: (() => void)[] = [];
|
|
|
|
private updateRequestedWidthRecursive(component: ComponentBase): void {
|
|
if (!component.isLayoutDirty) return;
|
|
|
|
for (let child of component.children) {
|
|
this.updateRequestedWidthRecursive(child);
|
|
}
|
|
|
|
component.updateNaturalWidth(this);
|
|
component.requestedWidth = Math.max(
|
|
component.naturalWidth,
|
|
component.state._size_[0]
|
|
);
|
|
}
|
|
|
|
private updateAllocatedWidthRecursive(component: ComponentBase): void {
|
|
if (!component.isLayoutDirty) return;
|
|
|
|
let children = Array.from(component.children);
|
|
let childAllocatedWidths = children.map(
|
|
(child) => child.allocatedWidth
|
|
);
|
|
|
|
component.updateAllocatedWidth(this);
|
|
|
|
// The FundamentalRootComponent always has a width of 100vw, so we don't
|
|
// want to assign the width here. We'll only assign the width of this
|
|
// component's children.
|
|
|
|
for (let i = 0; i < children.length; i++) {
|
|
let child = children[i];
|
|
|
|
if (child.allocatedWidth !== childAllocatedWidths[i]) {
|
|
child.isLayoutDirty = true;
|
|
}
|
|
|
|
if (child.isLayoutDirty) {
|
|
this.updateAllocatedWidthRecursive(child);
|
|
}
|
|
|
|
let element = child.element;
|
|
element.style.width = `${child.allocatedWidth * pixelsPerRem}px`;
|
|
}
|
|
}
|
|
|
|
private updateRequestedHeightRecursive(component: ComponentBase): void {
|
|
if (!component.isLayoutDirty) return;
|
|
|
|
for (let child of component.children) {
|
|
this.updateRequestedHeightRecursive(child);
|
|
}
|
|
|
|
component.updateNaturalHeight(this);
|
|
component.requestedHeight = Math.max(
|
|
component.naturalHeight,
|
|
component.state._size_[1]
|
|
);
|
|
}
|
|
|
|
private updateAllocatedHeightRecursive(component: ComponentBase): void {
|
|
if (!component.isLayoutDirty) return;
|
|
|
|
let children = Array.from(component.children);
|
|
let childAllocatedHeights = children.map(
|
|
(child) => child.allocatedHeight
|
|
);
|
|
|
|
component.updateAllocatedHeight(this);
|
|
|
|
// The FundamentalRootComponent always has a height of 100vh, so we
|
|
// don't want to assign the width here. We'll only assign the height of
|
|
// this component's children.
|
|
|
|
for (let i = 0; i < children.length; i++) {
|
|
let child = children[i];
|
|
|
|
if (child.allocatedHeight !== childAllocatedHeights[i]) {
|
|
child.isLayoutDirty = true;
|
|
}
|
|
|
|
if (child.isLayoutDirty) {
|
|
this.updateAllocatedHeightRecursive(child);
|
|
}
|
|
|
|
child.isLayoutDirty = false;
|
|
|
|
let element = child.element;
|
|
element.style.height = `${child.allocatedHeight * pixelsPerRem}px`;
|
|
}
|
|
}
|
|
|
|
public updateLayout(): void {
|
|
let rootComponent = getRootComponent();
|
|
|
|
// Find out how large all components would like to be
|
|
this.updateRequestedWidthRecursive(rootComponent);
|
|
|
|
// Note: The FundamentalRootComponent is responsible for allocating the
|
|
// available window space. There is no need to take care of anything
|
|
// here.
|
|
|
|
// Distribute the just received width to all children
|
|
this.updateAllocatedWidthRecursive(rootComponent);
|
|
|
|
// Now that all components have their width set, find out their height.
|
|
// This is done later on, so that text can request height based on its
|
|
// width.
|
|
this.updateRequestedHeightRecursive(rootComponent);
|
|
|
|
// Distribute the just received height to all children
|
|
this.updateAllocatedHeightRecursive(rootComponent);
|
|
}
|
|
|
|
/// Signal to the layout engine that it should re-layout the component tree
|
|
/// immediately after the current layout cycle finishes. The given function
|
|
/// will be called before the re-layout happens, allowing the caller to
|
|
/// dirty components or do other things.
|
|
public requestImmediateReLayout(callback: () => void): void {
|
|
this._immediateReLayoutCallbacks.push(callback);
|
|
}
|
|
}
|
|
|
|
export function updateLayout(): void {
|
|
let context = new LayoutContext();
|
|
|
|
while (true) {
|
|
// Update the layout
|
|
context.updateLayout();
|
|
|
|
// Are any re-layouts requested?
|
|
if (context._immediateReLayoutCallbacks.length === 0) {
|
|
break;
|
|
}
|
|
|
|
// Call all hooks
|
|
for (let callback of context._immediateReLayoutCallbacks) {
|
|
callback();
|
|
}
|
|
|
|
context._immediateReLayoutCallbacks = [];
|
|
}
|
|
}
|