diff --git a/lively.morphic/components/policy.js b/lively.morphic/components/policy.js index d4b71e68c6..d775eaf861 100644 --- a/lively.morphic/components/policy.js +++ b/lively.morphic/components/policy.js @@ -81,6 +81,10 @@ export function replace (replacedSiblingName, props) { function handleTextProps (props) { if (arr.intersect( ['text', 'label', Text, Label], withSuperclasses(props.type)).length === 0) { return props; } + if (props.textAndAttributes) { + delete props.textString; + delete props.value; + } if (props.textString) { props.textAndAttributes = [props.textString, null]; delete props.textString; @@ -450,7 +454,12 @@ export class StylePolicy { if (targetMorph._lastIndex && !obj.equals(targetMorph._lastIndex, currIndex)) { const limitExtent = bpStore.getLimitExtent(currIndex); const actualExtent = targetMorph.extent; - targetMorph.withMetaDo({ metaInteraction: true, reconcileChanges: false, doNotFit: true }, () => { + targetMorph.withMetaDo({ + metaInteraction: true, // do not record + reconcileChanges: false, + doNotFit: true, + doNotOverride: true + }, () => { const origLayoutableFlag = targetMorph.isLayoutable; targetMorph.isLayoutable = false; // avoid any resizing interference here targetMorph.extent = limitExtent; @@ -467,8 +476,17 @@ export class StylePolicy { })(); } + getLastMatchingBreakpoint (target) { + let curr = this; + let matchingBreakpoint; + while (curr = curr.getMatchingBreakpointMaster(target)) { + matchingBreakpoint = curr = curr.stylePolicy || curr; + } + return matchingBreakpoint || curr; + } + needsBreakpointUpdate (target) { - const matchingBreakpoint = this.getMatchingBreakpointMaster(target); + const matchingBreakpoint = this.getLastMatchingBreakpoint(target); if (typeof matchingBreakpoint === 'undefined') return false; if (matchingBreakpoint === (target._lastBreakpoint || null)) { return false; } target._lastBreakpoint = matchingBreakpoint; @@ -1273,7 +1291,7 @@ export class StylePolicy { */ isPositionedByLayout (aSubmorph) { const layout = aSubmorph.owner?.layout; - return layout?.name() !== 'Constraint' && layout?.layoutableSubmorphs?.includes(aSubmorph); + return layout && layout.name() !== 'Constraint' && aSubmorph.isLayoutable; } /** @@ -1282,7 +1300,7 @@ export class StylePolicy { * @returns { boolean | object } Wether or not size is controlled via layout and if so, the concrete policy. */ isResizedByLayout (aSubmorph) { - const layout = aSubmorph.owner && aSubmorph.owner.layout; + let layout = aSubmorph.owner && aSubmorph.owner.layout; let heightPolicy = 'fixed'; let widthPolicy = 'fixed'; if (aSubmorph.isText) { if (!aSubmorph.fixedHeight) heightPolicy = 'hug'; @@ -1293,6 +1311,19 @@ export class StylePolicy { if (widthPolicy !== 'hug') widthPolicy = layout.getResizeWidthPolicyFor(aSubmorph); if (heightPolicy === 'fill' || widthPolicy === 'fill') return { widthPolicy, heightPolicy }; } + + layout = aSubmorph.layout; + + if (layout?.hugContentsVertically || + layout?.hugContentsHorizontally || + widthPolicy === 'hug' || + heightPolicy === 'hug') { + return { + widthPolicy: layout?.hugContentsHorizontally ? 'hug' : widthPolicy, + heightPolicy: layout?.hugContentsVertically ? 'hug' : heightPolicy + }; + } + return false; } @@ -1391,11 +1422,21 @@ export class PolicyApplicator extends StylePolicy { } synthesizeSubSpec (submorphNameInPolicyContext, parentOfScope, previousTarget) { - const subSpec = super.synthesizeSubSpec(submorphNameInPolicyContext, parentOfScope, previousTarget); - if (subSpec.isPolicy && !subSpec.isPolicyApplicator) { - return new PolicyApplicator({}, subSpec); + let synthesized = super.synthesizeSubSpec(submorphNameInPolicyContext, parentOfScope, previousTarget); + if (synthesized.isPolicy && !synthesized.isPolicyApplicator) { + return new PolicyApplicator({}, synthesized); + } + synthesized = sanitizeSpec(synthesized); + if ('width' in synthesized && synthesized.extent?.isPoint) { + synthesized.extent = synthesized.extent.withX(synthesized.width); + delete synthesized.width; } - return subSpec; + + if ('height' in synthesized && synthesized.extent?.isPoint) { + synthesized.extent = synthesized.extent.withY(synthesized.height); + delete synthesized.height; + } + return synthesized; } applyIfNeeded (needsUpdate = false, animationConfig = false) { @@ -1472,11 +1513,20 @@ export class PolicyApplicator extends StylePolicy { continue; } - if (this.isPositionedByLayout(morphToBeStyled) && propName === 'position') continue; + if (propName === 'position' && this.isPositionedByLayout(morphToBeStyled)) continue; let resizePolicy; if (propName === 'extent' && (resizePolicy = this.isResizedByLayout(morphToBeStyled))) { - if (resizePolicy.widthPolicy === 'fixed' && morphToBeStyled.width !== propValue.x) morphToBeStyled.width = propValue.x; - if (resizePolicy.heightPolicy === 'fixed' && morphToBeStyled.height !== propValue.y) morphToBeStyled.height = propValue.y; + morphToBeStyled.withMetaDo({ deferLayoutApplication: true }, () => { + if (resizePolicy.widthPolicy === 'fixed' && morphToBeStyled.width !== propValue.x) { + morphToBeStyled.width = propValue.x; + } + if (resizePolicy.heightPolicy === 'fixed' && morphToBeStyled.height !== propValue.y) { + morphToBeStyled.height = propValue.y; + } + if (morphToBeStyled.isText && (resizePolicy.widthPolicy === 'hug' || resizePolicy.heightPolicy === 'hug')) { + morphToBeStyled.withMetaDo({ doNotFit: false }, () => morphToBeStyled.fit()); + } + }); continue; } @@ -1491,32 +1541,11 @@ export class PolicyApplicator extends StylePolicy { if (propName === 'position') continue; } - // FIXME: other special cases?? - if (morphToBeStyled.isText && propName === 'extent') { - if (!morphToBeStyled.fixedWidth && !morphToBeStyled.fixedHeight) continue; - if (!morphToBeStyled.fixedWidth) propValue = propValue.withX(morphToBeStyled.width); - if (!morphToBeStyled.fixedHeight) propValue = propValue.withY(morphToBeStyled.height); - } - - if (morphToBeStyled.isText && propName === 'width' && morphToBeStyled.lineWrapping !== 'no-wrap') { - if (!morphToBeStyled.fixedWidth) continue; - morphToBeStyled.width = propValue; - morphToBeStyled.withMetaDo({ doNotFit: false }, () => morphToBeStyled.fit()); - } - if (['border', 'borderTop', 'borderBottom', 'borderRight', 'borderLeft'].includes(propName)) continue; // handled by sub props; if (!obj.equals(morphToBeStyled[propName], propValue)) { morphToBeStyled[propName] = propValue; } - - // we may be late for the game when setting these props - // se we need to make sure, we restore the morphs "intended extent" - // for this purpose we enforce the masterSubmorph extent - if (['fixedHeight', 'fixedWidth'].includes(propName) && - morphToBeStyled._parametrizedProps?.extent) { - morphToBeStyled.extent = morphToBeStyled._parametrizedProps.extent; - } } } @@ -1550,6 +1579,7 @@ export class PolicyApplicator extends StylePolicy { */ onMorphChange (changedMorph, change) { if (change.meta?.metaInteraction || + change.meta?.doNotOverride || !this.targetMorph || this.isCurrentlyAnimated(changedMorph) ) return; diff --git a/lively.morphic/layout.js b/lively.morphic/layout.js index 7efb533377..86d619dcd6 100644 --- a/lively.morphic/layout.js +++ b/lively.morphic/layout.js @@ -419,6 +419,10 @@ export class TilingLayout extends Layout { } scheduleApply (submorph, animation, change = {}) { + if (change.meta?.deferLayoutApplication) { + return; + } + if (!change.meta?.isLayoutAction || !this.container?._yogaNode?.getParent()) { this._alreadyComputed = false; } @@ -830,10 +834,10 @@ export class TilingLayout extends Layout { aMorph._yogaNode = Yoga.Node.create(yogaConfig); if (aMorph.isText) { aMorph._yogaNode.setMeasureFunc((width, widthMode, height, heightMode) => { - if (aMorph.fixedWidth && widthMode !== 0) aMorph.width = width; - if (aMorph.fixedHeight && heightMode !== 0) aMorph.height = height; + if (aMorph.fixedWidth && widthMode !== 0) aMorph.withMetaDo({ doNotOverride: true }, () => aMorph.width = width); + if (aMorph.fixedHeight && heightMode !== 0) aMorph.withMetaDo({ doNotOverride: true }, () => aMorph.height = height); if (!aMorph.visible) return { width: aMorph.width, height: aMorph.height }; - if (!aMorph.fixedWidth || !aMorph.fixedHeight) aMorph.withMetaDo({ doNotFit: false }, () => aMorph.fit()); + if (!aMorph.fixedWidth || !aMorph.fixedHeight) aMorph.withMetaDo({ doNotFit: false, skipRerender: true }, () => aMorph.fitIfNeeded()); if (!aMorph.fixedWidth) width = aMorph.width; if (!aMorph.fixedHeight) height = aMorph.height; return { width, height }; @@ -930,10 +934,10 @@ export class TilingLayout extends Layout { if (this.container.submorphs.length > 0) { if (hugContentsVertically && container.height !== height) { - container.withMetaDo({ isLayoutAction: true, skipRender: true }, () => container.height = height); + container.withMetaDo({ isLayoutAction: true, skipRender: true, doNotOverride: true }, () => container.height = height); } if (hugContentsHorizontally && container.width !== width) { - container.withMetaDo({ isLayoutAction: true, skipRender: true }, () => container.width = width); + container.withMetaDo({ isLayoutAction: true, skipRender: true, doNotOverride: true }, () => container.width = width); } } } diff --git a/lively.morphic/text/morph.js b/lively.morphic/text/morph.js index 34288210c5..7b5c0dc3e2 100644 --- a/lively.morphic/text/morph.js +++ b/lively.morphic/text/morph.js @@ -2826,8 +2826,7 @@ export class Text extends Morph { } else if (this.env.renderer) { if (this.fixedHeight && this.fixedWidth) return; let textBoundsExtent = this.textBounds().extent(); - this.renderingState.needsFit = this.renderingState.needsRemeasure; - this.withMetaDo({ isLayoutAction: true, doNotFit: true }, () => { + this.withMetaDo({ isLayoutAction: true, doNotFit: true, doNotOverride: true }, () => { if (this.fixedWidth) textBoundsExtent = textBoundsExtent.withX(this.width); if (this.fixedHeight) textBoundsExtent = textBoundsExtent.withY(this.height); const newExt = textBoundsExtent.addXY( @@ -2836,6 +2835,7 @@ export class Text extends Morph { ); if (!this.extent.equals(newExt)) { this.extent = newExt; + this.renderingState.needsFit = this.renderingState.needsRemeasure; } }); } else {