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
26 changes: 16 additions & 10 deletions webf/lib/src/css/render_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3378,19 +3378,25 @@ class CSSRenderStyle extends RenderStyle
maxConstraintWidth = childWrapper?.effectiveChildConstraints.maxWidth;
} catch (_) {}

if (ancestorRenderStyle.isSelfRenderWidget() &&
final RenderBoxModel? currentLayoutBoxForAncestor =
renderBoxModelInLayoutStack.isNotEmpty ? renderBoxModelInLayoutStack.last : null;
final bool ancestorIsAncestorInCurrentTree = currentLayoutBoxForAncestor == null
? true
: isRenderSubtreeAncestor(
ancestorRenderStyle.attachedRenderBoxModel,
currentLayoutBoxForAncestor,
);

if (renderStyle.isSelfRenderWidget() &&
!ancestorIsAncestorInCurrentTree &&
childWrapper != null &&
maxConstraintWidth != null &&
maxConstraintWidth.isFinite) {
logicalWidth = maxConstraintWidth;
} else if (ancestorRenderStyle.isSelfRenderWidget() &&
childWrapper != null &&
maxConstraintWidth != null &&
maxConstraintWidth.isFinite) {
final RenderBoxModel? currentLayoutBoxForAncestor =
renderBoxModelInLayoutStack.isNotEmpty ? renderBoxModelInLayoutStack.last : null;
final bool ancestorIsAncestorInCurrentTree = currentLayoutBoxForAncestor == null
? true
: isRenderSubtreeAncestor(
ancestorRenderStyle.attachedRenderBoxModel,
currentLayoutBoxForAncestor,
);

if (ancestorIsAncestorInCurrentTree) {
final double? ancestorContentLogicalWidth = ancestorRenderStyle.contentBoxLogicalWidth;
if (ancestorContentLogicalWidth != null && ancestorContentLogicalWidth.isFinite) {
Expand Down
49 changes: 27 additions & 22 deletions webf/lib/src/rendering/box_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ bool debugRenderObjectNeedsLayout(RenderObject renderObject) {
return result;
}

bool canReuseStableProxyChildLayout(RenderBox? child, BoxConstraints constraints) {
bool canReuseStableProxyChildLayout(
RenderBox? child, BoxConstraints constraints) {
if (child == null || !child.hasSize || child.constraints != constraints) {
return false;
}
Expand Down Expand Up @@ -434,11 +435,13 @@ abstract class RenderBoxModel extends RenderBox

// Whether it needs relayout due to percentage calculation.
bool needsRelayout = false;
bool _hasPendingLayoutUpdate = true;
bool _hasPendingIntrinsicMeasurementInvalidation = true;
bool _hasPendingSubtreeIntrinsicMeasurementInvalidation = true;
String? _debugIntrinsicMeasurementDirtyReason = 'initial';
int _clearIntrinsicMeasurementInvalidationAfterLayoutPass = 1;

bool get hasPendingLayoutUpdate => _hasPendingLayoutUpdate;
bool get hasPendingIntrinsicMeasurementInvalidation =>
_hasPendingIntrinsicMeasurementInvalidation;
bool get hasPendingSubtreeIntrinsicMeasurementInvalidation =>
Expand All @@ -462,6 +465,11 @@ abstract class RenderBoxModel extends RenderBox
needsRelayout = true;
}

@protected
void clearPendingLayoutUpdateForCurrentLayoutPass() {
_hasPendingLayoutUpdate = false;
}

void markNeedsIntrinsicMeasurementUpdate([String reason = 'unspecified']) {
_hasPendingIntrinsicMeasurementInvalidation = true;
_debugIntrinsicMeasurementDirtyReason = reason;
Expand Down Expand Up @@ -525,8 +533,10 @@ abstract class RenderBoxModel extends RenderBox

@override
void layout(Constraints constraints, {bool parentUsesSize = false}) {
_lastLaidOutAsRelayoutBoundary =
!parentUsesSize || sizedByParent || constraints.isTight || parent == null;
_lastLaidOutAsRelayoutBoundary = !parentUsesSize ||
sizedByParent ||
constraints.isTight ||
parent == null;
if (renderBoxModelInLayoutStack.isEmpty) {
renderBoxModelLayoutPassId++;
}
Expand Down Expand Up @@ -649,9 +659,8 @@ abstract class RenderBoxModel extends RenderBox
// width of 0 (e.g., auto-width flex item whose only child is out-of-flow).
// In such cases, the containing block still exists, but the abspos box should size
// from its own content rather than the flex item's zero content width.
final bool isAbsOrFixed =
style.position == CSSPositionType.absolute ||
style.position == CSSPositionType.fixed;
final bool isAbsOrFixed = style.position == CSSPositionType.absolute ||
style.position == CSSPositionType.fixed;
if (isAbsOrFixed &&
style.width.isAuto &&
style.isParentRenderFlexLayout() &&
Expand Down Expand Up @@ -681,8 +690,7 @@ abstract class RenderBoxModel extends RenderBox
}
} else {
// Not in a flex context, inherit parent's content width constraint normally
double parentContentWidth =
parentFlowStyle.contentMaxConstraintsWidth;
double parentContentWidth = parentFlowStyle.contentMaxConstraintsWidth;
if (parentContentWidth != double.infinity) {
parentBoxContentConstraintsWidth = parentContentWidth;
}
Expand Down Expand Up @@ -710,7 +718,7 @@ abstract class RenderBoxModel extends RenderBox
!style.isSelfRenderReplaced() &&
style.borderBoxLogicalWidth == null &&
parentBoxContentConstraintsWidth != null) {
parentBoxContentConstraintsWidth = null;
parentBoxContentConstraintsWidth = null;
}

double? containingBlockPaddingBoxWidth;
Expand All @@ -731,12 +739,10 @@ abstract class RenderBoxModel extends RenderBox
style.minWidth.isAuto ? null : style.minWidth.computedValue;
double? maxWidth =
style.maxWidth.isNone ? null : style.maxWidth.computedValue;
double? minHeight = style.minHeight.isAuto
? null
: style.minHeight.computedValue;
double? maxHeight = style.maxHeight.isNone
? null
: style.maxHeight.computedValue;
double? minHeight =
style.minHeight.isAuto ? null : style.minHeight.computedValue;
double? maxHeight =
style.maxHeight.isNone ? null : style.maxHeight.computedValue;

double maxConstraintWidth = style.borderBoxLogicalWidth ??
parentBoxContentConstraintsWidth ??
Expand All @@ -747,9 +753,8 @@ abstract class RenderBoxModel extends RenderBox
// This handles cases where style-tree logical widths are unavailable (e.g., parent inline-block
// with auto width) but the containing block has been measured. See:
// https://www.w3.org/TR/css-position-3/#abs-non-replaced-width
final bool isAbsOrFixed =
style.position == CSSPositionType.absolute ||
style.position == CSSPositionType.fixed;
final bool isAbsOrFixed = style.position == CSSPositionType.absolute ||
style.position == CSSPositionType.fixed;
if (maxConstraintWidth == double.infinity &&
isAbsOrFixed &&
!style.isSelfRenderReplaced() &&
Expand Down Expand Up @@ -832,8 +837,7 @@ abstract class RenderBoxModel extends RenderBox
}
// Height should be not smaller than border and padding in vertical direction
// when box-sizing is border-box which is only supported.
double minConstraintHeight =
styleBorder.vertical + stylePadding.vertical;
double minConstraintHeight = styleBorder.vertical + stylePadding.vertical;
double maxConstraintHeight =
style.borderBoxLogicalHeight ?? double.infinity;

Expand Down Expand Up @@ -876,8 +880,7 @@ abstract class RenderBoxModel extends RenderBox
maxConstraintWidth =
maxConstraintWidth > maxWidth ? maxWidth : maxConstraintWidth;
// Only reduce minConstraintWidth if maxWidth is larger than border+padding requirements
double borderPadding =
styleBorder.horizontal + stylePadding.horizontal;
double borderPadding = styleBorder.horizontal + stylePadding.horizontal;
if (maxWidth >= borderPadding) {
minConstraintWidth =
minConstraintWidth > maxWidth ? maxWidth : minConstraintWidth;
Expand Down Expand Up @@ -968,6 +971,7 @@ abstract class RenderBoxModel extends RenderBox

@override
void markNeedsLayout() {
_hasPendingLayoutUpdate = true;
final RenderObject? relayoutParent = _relayoutParentOnSizeChange;
// Some wrapper parents mirror child.boxSize while laying the child out
// with parentUsesSize: false to keep a local relayout boundary.
Expand Down Expand Up @@ -1111,6 +1115,7 @@ abstract class RenderBoxModel extends RenderBox
this.contentConstraints = contentConstraints;
clearOverflowLayout();
isSelfSizeChanged = false;
clearPendingLayoutUpdateForCurrentLayoutPass();
updateIntrinsicMeasurementInvalidationForCurrentLayoutPass();

// Reset cached CSS baselines before a new layout pass. They will be
Expand Down
100 changes: 67 additions & 33 deletions webf/lib/src/rendering/box_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class RenderLayoutBoxWrapper extends RenderBoxModel

@override
void performLayout() {
clearPendingLayoutUpdateForCurrentLayoutPass();
renderStyle.computeContentBoxLogicalWidth();
renderStyle.computeContentBoxLogicalHeight();

Expand All @@ -106,7 +107,8 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
final RenderBox? c = child;
if (c == null) {
size = constraints.constrain(Size.zero);
initOverflowLayout(Rect.fromLTRB(0, 0, size.width, size.height), Rect.fromLTRB(0, 0, size.width, size.height));
initOverflowLayout(Rect.fromLTRB(0, 0, size.width, size.height),
Rect.fromLTRB(0, 0, size.width, size.height));
_lastWrapperConstraints = constraints;
_lastResolvedChildConstraints = null;
return;
Expand All @@ -127,9 +129,13 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
// CSS logic already accounts for its padding/border.
} else if (c is RenderTextBox) {
// Text nodes inside wrappers should measure themselves with a sensible bound.
final double maxW = constraints.hasBoundedWidth ? constraints.maxWidth : double.infinity;
final double maxH = constraints.hasBoundedHeight ? constraints.maxHeight : double.infinity;
childConstraints = BoxConstraints(minWidth: 0, maxWidth: maxW, minHeight: 0, maxHeight: maxH);
final double maxW =
constraints.hasBoundedWidth ? constraints.maxWidth : double.infinity;
final double maxH = constraints.hasBoundedHeight
? constraints.maxHeight
: double.infinity;
childConstraints = BoxConstraints(
minWidth: 0, maxWidth: maxW, minHeight: 0, maxHeight: maxH);
} else {
// Fallback: provide loose, unbounded constraints so inner WebF render boxes
// can compute their own CSS-based constraints without being forced to expand.
Expand Down Expand Up @@ -158,7 +164,8 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
}
if (minW > maxW) minW = maxW;
if (minH > maxH) minH = maxH;
return BoxConstraints(minWidth: minW, maxWidth: maxW, minHeight: minH, maxHeight: maxH);
return BoxConstraints(
minWidth: minW, maxWidth: maxW, minHeight: minH, maxHeight: maxH);
}

childConstraints = intersect(childConstraints, constraints);
Expand All @@ -176,7 +183,8 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
// Use sibling-oriented collapsed margins so the inter-item spacing equals
// the CSS collapsed result between previous bottom and current top.
final double childMarginTop = renderStyle.collapsedMarginTopForSibling;
final double childMarginBottom = renderStyle.collapsedMarginBottomForSibling;
final double childMarginBottom =
renderStyle.collapsedMarginBottomForSibling;
final double childMarginLeft = renderStyle.marginLeft.computedValue;
final double childMarginRight = renderStyle.marginRight.computedValue;

Expand All @@ -186,34 +194,47 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
// elements where content can extend beyond the box; for clipping containers
// the intermediate RenderLayoutBoxWrapper (created for the Scrollable chain)
// never propagates scrollableSize, leaving it at Size.zero.
final bool isClippingContainer = c.renderStyle.effectiveOverflowX != CSSOverflowType.visible ||
c.renderStyle.effectiveOverflowY != CSSOverflowType.visible;
final Size contentScrollable = isClippingContainer ? c.size : c.scrollableSize;
final bool isClippingContainer =
c.renderStyle.effectiveOverflowX != CSSOverflowType.visible ||
c.renderStyle.effectiveOverflowY != CSSOverflowType.visible;
final Size contentScrollable =
isClippingContainer ? c.size : c.scrollableSize;

// Decide sizing based on list axis. In a horizontal list (unbounded width),
// widen by left+right margins so gaps appear between items. In a vertical
// list (unbounded height), increase height by top+bottom margins.
final bool isHorizontalList = constraints.hasBoundedHeight && !constraints.hasBoundedWidth;
final bool isVerticalList = constraints.hasBoundedWidth && !constraints.hasBoundedHeight;
final bool isHorizontalList =
constraints.hasBoundedHeight && !constraints.hasBoundedWidth;
final bool isVerticalList =
constraints.hasBoundedWidth && !constraints.hasBoundedHeight;

double wrapperWidth;
double wrapperHeight;
if (isHorizontalList) {
wrapperWidth = contentScrollable.width + childMarginLeft + childMarginRight;
wrapperWidth =
contentScrollable.width + childMarginLeft + childMarginRight;
// Height is tight from the viewport; still offset child by vertical margins below.
wrapperHeight = constraints.hasBoundedHeight ? constraints.maxHeight : (contentScrollable.height + childMarginTop + childMarginBottom);
wrapperHeight = constraints.hasBoundedHeight
? constraints.maxHeight
: (contentScrollable.height + childMarginTop + childMarginBottom);
} else if (isVerticalList) {
wrapperWidth = constraints.hasBoundedWidth ? constraints.maxWidth : (contentScrollable.width + childMarginLeft + childMarginRight);
wrapperHeight = contentScrollable.height + childMarginTop + childMarginBottom;
wrapperWidth = constraints.hasBoundedWidth
? constraints.maxWidth
: (contentScrollable.width + childMarginLeft + childMarginRight);
wrapperHeight =
contentScrollable.height + childMarginTop + childMarginBottom;
} else {
// Fallback (no tightness info): include both margins conservatively.
wrapperWidth = contentScrollable.width + childMarginLeft + childMarginRight;
wrapperHeight = contentScrollable.height + childMarginTop + childMarginBottom;
wrapperWidth =
contentScrollable.width + childMarginLeft + childMarginRight;
wrapperHeight =
contentScrollable.height + childMarginTop + childMarginBottom;
}

size = constraints.constrain(Size(wrapperWidth, wrapperHeight));

if (renderStyle.isSelfPositioned() || renderStyle.isSelfStickyPosition()) {
if (renderStyle.isSelfPositioned() ||
renderStyle.isSelfStickyPosition()) {
CSSPositionedLayout.applyPositionedChildOffset(this, c);
} else {
// Offset the child within the wrapper by its margins
Expand All @@ -226,7 +247,8 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
}

calculateBaseline();
initOverflowLayout(Rect.fromLTRB(0, 0, size.width, size.height), Rect.fromLTRB(0, 0, size.width, size.height));
initOverflowLayout(Rect.fromLTRB(0, 0, size.width, size.height),
Rect.fromLTRB(0, 0, size.width, size.height));
}

@override
Expand All @@ -241,17 +263,22 @@ class RenderLayoutBoxWrapper extends RenderBoxModel

@override
bool hitTest(BoxHitTestResult result, {required Offset position}) {
if (!hasSize || !contentVisibilityHitTest(result, position: position) || renderStyle.isVisibilityHidden) {
if (!hasSize ||
!contentVisibilityHitTest(result, position: position) ||
renderStyle.isVisibilityHidden) {
return false;
}

assert(() {
if (!hasSize) {
if (debugNeedsLayout) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Cannot hit test a render box that has never been laid out.'),
describeForError('The hitTest() method was called on this RenderBox'),
ErrorDescription("Unfortunately, this object's geometry is not known at this time, "
ErrorSummary(
'Cannot hit test a render box that has never been laid out.'),
describeForError(
'The hitTest() method was called on this RenderBox'),
ErrorDescription(
"Unfortunately, this object's geometry is not known at this time, "
'probably because it has never been laid out. '
'This means it cannot be accurately hit-tested.'),
ErrorHint('If you are trying '
Expand All @@ -263,7 +290,8 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Cannot hit test a render box with no size.'),
describeForError('The hitTest() method was called on this RenderBox'),
ErrorDescription('Although this node is not marked as needing layout, '
ErrorDescription(
'Although this node is not marked as needing layout, '
'its size is not set.'),
ErrorHint('A RenderBox object must have an '
'explicit size before it can be hit-tested. Make sure '
Expand All @@ -283,14 +311,18 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
}

final BoxParentData childParentData = child!.parentData as BoxParentData;
bool isHit = result.addWithPaintOffset(offset: childParentData.offset, position: position, hitTest: (result, position) {
// addWithPaintOffset is to add an offset to the child node, the calculation itself does not need to bring an offset.
if (hasSize && hitTestChildren(result, position: position) || hitTestSelf(position)) {
result.add(BoxHitTestEntry(this, position));
return true;
}
return false;
});
bool isHit = result.addWithPaintOffset(
offset: childParentData.offset,
position: position,
hitTest: (result, position) {
// addWithPaintOffset is to add an offset to the child node, the calculation itself does not need to bring an offset.
if (hasSize && hitTestChildren(result, position: position) ||
hitTestSelf(position)) {
result.add(BoxHitTestEntry(this, position));
return true;
}
return false;
});
return isHit;
}

Expand All @@ -304,7 +336,9 @@ class RenderLayoutBoxWrapper extends RenderBoxModel
class LayoutBoxWrapper extends SingleChildRenderObjectWidget {
final dom.Element ownerElement;

const LayoutBoxWrapper({super.key, required Widget child, required this.ownerElement}) : super(child: child);
const LayoutBoxWrapper(
{super.key, required Widget child, required this.ownerElement})
: super(child: child);

@override
RenderObject createRenderObject(BuildContext context) {
Expand Down
1 change: 1 addition & 0 deletions webf/lib/src/rendering/event_listener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class RenderEventListener extends RenderBoxModel

@override
void performLayout() {
clearPendingLayoutUpdateForCurrentLayoutPass();
updateIntrinsicMeasurementInvalidationForCurrentLayoutPass();
final RenderBox? currentChild = child;
if (hasSize &&
Expand Down
Loading
Loading