Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions lively.morphic/components/policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -969,14 +969,22 @@ export class StylePolicy {

asFullySynthesizedSpec () {
const extractBuildSpecs = (specOrPolicy, submorphs) => {
if (specOrPolicy.__alreadySynthesized__) return specOrPolicy;
if (specOrPolicy.COMMAND === 'add') {
specOrPolicy = specOrPolicy.props;
}
if (specOrPolicy.COMMAND === 'remove') return null; // target is already removed so just ignore the command
if (specOrPolicy.isPolicy) return specOrPolicy.asFullySynthesizedSpec();
if (!submorphs) submorphs = specOrPolicy.submorphs || [];
const modelClass = specOrPolicy.defaultViewModel || specOrPolicy.viewModelClass;
const modelParams = { ...specOrPolicy.viewModel } || {}; // accumulate the derivation chain for the viewModel
const synthesized = this.synthesizeSubSpec(specOrPolicy === this.spec ? null : specOrPolicy.name);
const virtualMorph = {
env: {},
width: specOrPolicy.width || specOrPolicy.extent?.x || 10,
height: specOrPolicy.height || specOrPolicy.extent?.y || 10
};
// if the owner of this morph has a layout, the extent or width may be different
const synthesized = this.synthesizeSubSpec(specOrPolicy === this.spec ? null : specOrPolicy.name, virtualMorph, virtualMorph);
if (specOrPolicy.__wasAddedToDerived__) synthesized.__wasAddedToDerived__ = true;
if (specOrPolicy.name) synthesized.name = specOrPolicy.name;
// remove the props that are equal to the default value
Expand All @@ -993,7 +1001,7 @@ export class StylePolicy {
return textOrAttr;
});
}
if (synthesized.layout) synthesized.layout = synthesized.layout.copy();

if (submorphs.length > 0) {
let transformedSubmorphs = submorphs.filter(spec => spec && !spec.__before__);
for (let spec of submorphs) {
Expand All @@ -1007,16 +1015,39 @@ export class StylePolicy {
}
synthesized.submorphs = transformedSubmorphs;
}

if (synthesized.layout) {
synthesized.layout = synthesized.layout.copy();
synthesized.layout.estimateSubmorphExtents(synthesized, extractBuildSpecs); // should be done before the submorph specs are synthesized
// however for submorphs which themselves are resized by layouts, the extent information is not yet determined, instead estimate the container extent below
}

if (modelClass) synthesized.viewModel = new modelClass(modelParams);
else delete synthesized.viewModel;

synthesized.__alreadySynthesized__ = true;

if (!synthesized.isPolicy) {
return sanitizeSpec(synthesized);
}

return synthesized;
};
const buildSpec = tree.mapTree(this.spec, extractBuildSpecs, node => node.props?.submorphs || node.submorphs);
const extractedBuildSpecs = new WeakMap();
const buildSpec = tree.mapTree(this.spec, (node, submorphs) => {
node = extractedBuildSpecs.get(node);
if (node.layout) {
node.layout.estimateContainerExtent(node, submorphs); // should be done after submorph specs are synthesized
}
if (submorphs.length > 0) node.submorphs = submorphs;
return node;
}, node => {
let submorphs = node.props?.submorphs || node.submorphs || [];
const synthesized = extractBuildSpecs(node, submorphs);
extractedBuildSpecs.set(node, synthesized);
if (node.isPolicy) return []; // no need to traverse further
return synthesized.submorphs;
});
const self = this;
buildSpec.onLoad = function () {
const policy = self;
Expand Down
110 changes: 110 additions & 0 deletions lively.morphic/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,14 @@ class Layout {
this.active = false;
}

estimateSubmorphExtents (containerSpec) {

}

estimateContainerExtent (containerSpec) {

}

resizesMorphVertically (aMorph) { // eslint-disable-line no-unused-vars
return false;
}
Expand Down Expand Up @@ -1375,6 +1383,108 @@ export class TilingLayout extends Layout {
* JS LAYOUT *
*************/

estimateTotalFixedExtent (containerSpec, extractBuildSpecs) {
const policies = this.config.resizePolicies;
const totalExtent = pt(
this.spacing * (containerSpec.submorphs.length - 1) + this.padding.left() + this.padding.right(),
this.spacing * (containerSpec.submorphs.length - 1) + this.padding.top() + this.padding.bottom()
);
totalExtent.horizontalFill = 0;
totalExtent.verticalFill = 0;
for (let i = 0; i < containerSpec.submorphs.length; i++) {
let match; let fixedTotalHeight; let fixedTotalWidth; let m = containerSpec.submorphs[i];
if (m.isPolicy) m = m.spec;
let ext = m.extent;
if (!ext && (m.width || m.height)) ext = pt(m.width || 10, m.height || 10);
if (m.isLayoutable === false) continue;
if (match = policies.find(([name, policy]) => name === m.name)) {
const [_, policy] = match;
if (policy.width === 'fixed') {
if (!ext) {
m = containerSpec.submorphs[i] = extractBuildSpecs(containerSpec.submorphs[i]);
ext = m.extent;
}
totalExtent.x += ext.x;
} else {
totalExtent.horizontalFill++;
}
if (policy.height === 'fixed') {
if (!ext) {
m = containerSpec.submorphs[i] = extractBuildSpecs(containerSpec.submorphs[i]);
ext = m.extent;
}
totalExtent.y += ext.y;
} else {
totalExtent.verticalFill++;
}
} else if (ext) {
totalExtent.x += ext.x;
totalExtent.y += ext.y;
}
}
return totalExtent;
}

estimateSubmorphExtents (containerSpec, extractBuildSpecs) {
if (!containerSpec.extent) return;
if (!containerSpec.submorphs?.length) return;
const policies = this.config.resizePolicies;
if (!policies) return;

for (let i = 0; i < containerSpec.submorphs.length; i++) {
let match; let fixedTotalExtent; let m = containerSpec.submorphs[i];
if (m.isPolicy) {
m = containerSpec.submorphs[i] = m.copy();
m = m.spec;
} else {
m = containerSpec.submorphs[i] = { ...m };
}
if (match = policies.find(([name, policy]) => name === m.name)) {
const [_, policy] = match;
if (policy.width === 'fill') {
if (this.axis === 'column') {
m.width = containerSpec.extent.x - this.padding.left() - this.padding.right();
}
if (this.axis === 'row') {
if (!fixedTotalExtent) fixedTotalExtent = this.estimateTotalFixedExtent(containerSpec, extractBuildSpecs);
m.width = (containerSpec.extent.x - fixedTotalExtent.x) / fixedTotalExtent.horizontalFill;
}
}

if (policy.height === 'fill') {
if (this.axis === 'row') {
m.height = containerSpec.extent.y - this.padding.top() - this.padding.bottom();
}
if (this.axis === 'column') {
if (!fixedTotalExtent) fixedTotalExtent = this.estimateTotalFixedExtent(containerSpec, extractBuildSpecs);
m.height = (containerSpec.extent.y - fixedTotalExtent.y) / fixedTotalExtent.verticalHeight;
}
}
}
}
}

estimateContainerExtent (containerSpec, submorphs) {
if (submorphs.length == 0) return;
if (!this.hugContentsVertically && !this.hugContentsHorizontally) return;

if (this.axis === 'column') {
const height = arr.sum(submorphs.map(m => m.extent.y)) + this.spacing * (submorphs.length - 1) + this.padding.top() + this.padding.bottom();
const width = arr.max(submorphs.map(m => m.extent.x)) + this.padding.left() + this.padding.right();
const ext = containerSpec.extent || pt(10, 10);
if (this.hugContentsVertically) containerSpec.extent = ext.withY(height);
if (this.hugContentsHorizontally) containerSpec.extent = ext.withX(width);
}

if (this.axis === 'row') {
const height = arr.max(submorphs.map(m => m.extent.y)) + this.padding.top() + this.padding.bottom();
const width = arr.sum(submorphs.map(m => m.extent.x)) + this.spacing * (submorphs.length - 1) + this.padding.left() + this.padding.right();
const ext = containerSpec.extent || pt(10, 10);
if (this.hugContentsVertically) containerSpec.extent = ext.withY(height);
if (this.hugContentsHorizontally) containerSpec.extent = ext.withX(width);
}
}

apply (animate = false) {
if (this.active || !this.container || this.renderViaCSS) return;

Expand Down
5 changes: 0 additions & 5 deletions lively.morphic/morph.js
Original file line number Diff line number Diff line change
Expand Up @@ -1664,11 +1664,6 @@ export class Morph {
submorph.withAllSubmorphsDo(ea => {
ea.onOwnerChanged(this);
});

if (this.world() && !this.isText) {
this.env.forceUpdate(this);
this.withAllSubmorphsDo(m => m.makeDirty());
}
});

return submorph;
Expand Down