Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/some sliver usage cases #922

Merged
merged 10 commits into from Nov 30, 2021
13 changes: 13 additions & 0 deletions integration_tests/specs/css/css-display/sliver.ts
Expand Up @@ -199,4 +199,17 @@ describe('display sliver', () => {

await simulateClick(50, 20); // Will trigger done.
});

it('sliver child with none-static position not throw errors', async () => {
var container = createSliverBasicCase();
var firstChild = container.firstChild; // should be element.
firstChild.style.position = 'relative';

var innerChild = document.createElement('div');
innerChild.appendChild(document.createTextNode('helloworld'));
innerChild.style.position = 'relative';
innerChild.style.top = innerChild.style.left = '15px';
firstChild?.appendChild(innerChild);
await snapshot();
});
});
3 changes: 2 additions & 1 deletion integration_tests/specs/window/global.ts
@@ -1,4 +1,4 @@
describe('windowisglobal', () => {
describe('window', () => {
it('window equal to globalThis', () => {
expect(window).toBe(globalThis as any);
});
Expand All @@ -7,6 +7,7 @@ describe('windowisglobal', () => {
// @ts-ignore
expect(typeof window.kraken).toBe('object');
});

it('equal to this', () => {
function f() {
// @ts-ignore
Expand Down
2 changes: 1 addition & 1 deletion kraken/lib/src/css/display.dart
Expand Up @@ -14,7 +14,7 @@ enum CSSDisplay {
flex,
inlineFlex,

sliver, // @TODO temp name.
sliver,

none
}
Expand Down
5 changes: 4 additions & 1 deletion kraken/lib/src/css/overflow.dart
Expand Up @@ -217,7 +217,10 @@ mixin ElementOverflowMixin on ElementBase {
}

void scrollingContentBoxStyleListener(String property, String? original, String present) {
RenderLayoutBox scrollingContentBox = (renderBoxModel as RenderLayoutBox).renderScrollingContent!;
RenderLayoutBox? scrollingContentBox = (renderBoxModel as RenderLayoutBox).renderScrollingContent;
// Sliver content has no multi scroll content box.
if (scrollingContentBox == null) return;

RenderStyle scrollingContentRenderStyle = scrollingContentBox.renderStyle;

switch (property) {
Expand Down
13 changes: 8 additions & 5 deletions kraken/lib/src/css/positioned.dart
Expand Up @@ -31,13 +31,16 @@ BoxSizeType? _getChildHeightSizeType(RenderBox child) {

// RenderPositionHolder may be affected by overflow: scroller offset.
// We need to reset these offset to keep positioned elements render at their original position.
// @NOTE: Attention that renderObjects in tree may not all subtype of RenderBoxModel, use `is` to identify.
Offset? _getRenderPositionHolderScrollOffset(RenderPositionPlaceholder holder, RenderObject root) {
RenderBoxModel? parent = holder.parent as RenderBoxModel?;
while (parent != null && parent != root) {
if (parent.clipX || parent.clipY) {
return Offset(parent.scrollLeft, parent.scrollTop);
AbstractNode? current = holder.parent;
while (current != null && current != root) {
if (current is RenderBoxModel) {
if (current.clipX || current.clipY) {
return Offset(current.scrollLeft, current.scrollTop);
}
}
parent = parent.parent as RenderBoxModel?;
current = current.parent;
}
return null;
}
Expand Down
12 changes: 9 additions & 3 deletions kraken/lib/src/css/style_declaration.dart
Expand Up @@ -367,18 +367,24 @@ class CSSStyleDeclaration {
}

void flushPendingProperties() {
if (target?.parentNode?.renderer == null) return;
Element? _target = target;
// If style target element not exists, no need to do flush operation.
if (_target == null) return;

// Display change from none to other value that the renderBoxModel is null.
if (_pendingProperties.containsKey(DISPLAY) && target!.isConnected) {
if (_pendingProperties.containsKey(DISPLAY) && _target.isConnected &&
_target.parentElement?.renderStyle.display != CSSDisplay.sliver) {
String? prevValue = _properties[DISPLAY];
String currentValue = _pendingProperties[DISPLAY]!;
_properties[DISPLAY] = currentValue;
_pendingProperties.remove(DISPLAY);
_emitPropertyChanged(DISPLAY, prevValue, currentValue);
}

RenderBoxModel? renderBoxModel = target!.renderBoxModel;
// If target has no renderer attached, no need to flush.
if (!_target.isRendererAttached) return;

RenderBoxModel? renderBoxModel = _target.renderBoxModel;
if (_pendingProperties.isEmpty || renderBoxModel == null) {
return;
}
Expand Down
41 changes: 23 additions & 18 deletions kraken/lib/src/dom/element.dart
Expand Up @@ -188,15 +188,19 @@ class Element extends Node

RenderBox? previousRenderBoxModel = renderBoxModel;
if (nextRenderBoxModel != previousRenderBoxModel) {
RenderBox? parentRenderBox;
RenderObject? parentRenderObject;
RenderBox? after;
if (previousRenderBoxModel != null) {
parentRenderBox = previousRenderBoxModel.parent as RenderBox?;
after = (previousRenderBoxModel.parentData as ContainerParentDataMixin<RenderBox>?)?.previousSibling;
parentRenderObject = previousRenderBoxModel.parent as RenderObject?;

if (previousRenderBoxModel.parentData is ContainerParentDataMixin<RenderBox>) {
after = (previousRenderBoxModel.parentData as ContainerParentDataMixin<RenderBox>).previousSibling;
}

_detachRenderBoxModel(previousRenderBoxModel);

if (parentRenderBox != null) {
_attachRenderBoxModel(parentRenderBox, nextRenderBoxModel, after: after);
if (parentRenderObject != null) {
_attachRenderBoxModel(parentRenderObject, nextRenderBoxModel, after: after);
}
}
renderBoxModel = nextRenderBoxModel;
Expand Down Expand Up @@ -501,7 +505,6 @@ class Element extends Node

// Detach renderBoxModel from original parent.
_detachRenderBoxModel(_renderBoxModel);

_updateRenderBoxModel();
_addToContainingBlock(after: previousSibling);

Expand Down Expand Up @@ -576,7 +579,10 @@ class Element extends Node
void attachTo(Node parent, {RenderBox? after}) {
_applyStyle(style);

willAttachRenderer();
// @NOTE: Sliver should not create renderer here.
if (parentElement?.renderStyle.display != CSSDisplay.sliver) {
willAttachRenderer();
}

if (renderer != null) {
// HTML element override attachTo method to attach renderObject to viewportBox.
Expand Down Expand Up @@ -799,28 +805,27 @@ class Element extends Node
// Update renderBoxModel.
_updateRenderBoxModel();
// Attach renderBoxModel to parent if change from `display: none` to other values.
if (renderBoxModel!.parent == null) {
if (!isRendererAttached && parentElement != null && parentElement!.isRendererAttached) {
_addToContainingBlock(after: previousSibling?.renderer);
ensureChildAttached();
}
}

void _attachRenderBoxModel(RenderBox parentRenderBox, RenderBox renderBox, {RenderObject? after, bool isLast = false}) {
void _attachRenderBoxModel(RenderObject parentRenderObject, RenderBox renderBox, {RenderObject? after, bool isLast = false}) {
if (isLast) {
assert(after == null);
}
if (parentRenderBox is RenderObjectWithChildMixin) { // RenderViewportBox
(parentRenderBox as RenderObjectWithChildMixin).child = renderBox;
} else if (parentRenderBox is ContainerRenderObjectMixin) { // RenderLayoutBox or RenderSliverList
if (parentRenderObject is RenderObjectWithChildMixin) { // RenderViewportBox
parentRenderObject.child = renderBox;
} else if (parentRenderObject is ContainerRenderObjectMixin) { // RenderLayoutBox or RenderSliverList
// Should attach to renderScrollingContent if it is scrollable.
if (parentRenderBox is RenderLayoutBox) {
parentRenderBox = parentRenderBox.renderScrollingContent ?? parentRenderBox;
if (parentRenderObject is RenderLayoutBox) {
parentRenderObject = parentRenderObject.renderScrollingContent ?? parentRenderObject;
}
if (isLast) {
after = (parentRenderBox as ContainerRenderObjectMixin).lastChild;
after = parentRenderObject.lastChild;
}
(parentRenderBox as ContainerRenderObjectMixin).insert(renderBox, after: after);

parentRenderObject.insert(renderBox, after: after);
}
}

Expand Down Expand Up @@ -1705,7 +1710,7 @@ bool _hasIntersectionObserverEvent(Map eventHandlers) {
eventHandlers.containsKey('intersectionchange');
}

void _detachRenderBoxModel(RenderBox renderBox) {
void _detachRenderBoxModel(RenderObject renderBox) {
if (renderBox.parent == null) return;

// Remove reference from parent
Expand Down
1 change: 1 addition & 0 deletions kraken/lib/src/dom/elements/head.dart
Expand Up @@ -131,6 +131,7 @@ class StyleElement extends Element {
type = value.toString().toLowerCase().trim();
}
}

@override
void connectedCallback() {
if (type == _CSS_MIME) {
Expand Down
5 changes: 5 additions & 0 deletions kraken/lib/src/dom/elements/html.dart
Expand Up @@ -41,6 +41,8 @@ class HTMLElement extends Element {
super.attachTo(parent);
if (renderBoxModel != null) {
elementManager.viewport.child = renderBoxModel!;
// Flush pending style immediately.
style.flushPendingProperties();
}
}

Expand All @@ -62,4 +64,7 @@ class HTMLElement extends Element {

@override
String get tagName => HTML;

@override
bool get isRendererAttached => true;
}
10 changes: 4 additions & 6 deletions kraken/lib/src/dom/sliver_manager.dart
Expand Up @@ -49,11 +49,6 @@ class RenderSliverElementChildManager implements RenderSliverBoxChildManager {
childNode.willAttachRenderer();

RenderBox? child;

if (childNode is Element) {
childNode.style.flushPendingProperties();
}

if (childNode is Node) {
child = childNode.renderer;
} else {
Expand All @@ -68,8 +63,11 @@ class RenderSliverElementChildManager implements RenderSliverBoxChildManager {
_sliverListLayout.insertSliverChild(child, after: after);
}

if (childNode is Element) {
childNode.style.flushPendingProperties();
}

childNode.didAttachRenderer();
childNode.ensureChildAttached();
}

RenderBox _createEmptyRenderObject() {
Expand Down
61 changes: 31 additions & 30 deletions kraken/lib/src/rendering/flex.dart
Expand Up @@ -667,7 +667,7 @@ class RenderFlexLayout extends RenderLayoutBox {
// Layout placeholder of positioned element(absolute/fixed) in new layer
if (child is RenderBoxModel && childParentData.isPositioned) {
CSSPositionedLayout.layoutPositionedChild(this, child);
} else if (child is RenderPositionPlaceholder && isPlaceholderPositioned(child)) {
} else if (child is RenderPositionPlaceholder && _isPlaceholderPositioned(child)) {
_layoutChildren(child);
}

Expand Down Expand Up @@ -716,19 +716,6 @@ class RenderFlexLayout extends RenderLayoutBox {
didLayout();
}


bool isPlaceholderPositioned(RenderObject child) {
if (child is RenderPositionPlaceholder) {
RenderBoxModel realDisplayedBox = child.positioned!;
RenderLayoutParentData parentData =
realDisplayedBox.parentData as RenderLayoutParentData;
if (parentData.isPositioned) {
return true;
}
}
return false;
}

/// There are 4 stages when layouting children
/// 1. Layout children in flow order to calculate flex lines according to its constaints and flex-wrap property
/// 2. Relayout children according to flex-grow and flex-shrink factor
Expand Down Expand Up @@ -906,7 +893,7 @@ class RenderFlexLayout extends RenderLayoutBox {
// Exclude positioned placeholder renderObject when layout non placeholder object
// and positioned renderObject
if (placeholderChild == null &&
(isPlaceholderPositioned(child) || childParentData!.isPositioned)) {
(_isPlaceholderPositioned(child) || childParentData!.isPositioned)) {
child = childParentData!.nextSibling;
continue;
}
Expand All @@ -920,19 +907,22 @@ class RenderFlexLayout extends RenderLayoutBox {
childNodeId = child.hashCode;
}

if (child is RenderPositionPlaceholder && isPlaceholderPositioned(child)) {
RenderBoxModel realDisplayedBox = child.positioned!;
// Flutter only allow access size of direct children, so cannot use realDisplayedBox.size
Size realDisplayedBoxSize =
realDisplayedBox.getBoxSize(realDisplayedBox.contentSize);
double realDisplayedBoxWidth = realDisplayedBoxSize.width;
double realDisplayedBoxHeight = realDisplayedBoxSize.height;
childConstraints = BoxConstraints(
minWidth: realDisplayedBoxWidth,
maxWidth: realDisplayedBoxWidth,
minHeight: realDisplayedBoxHeight,
maxHeight: realDisplayedBoxHeight,
);
if (_isPlaceholderPositioned(child)) {
RenderBoxModel positionedBox = (child as RenderPositionPlaceholder).positioned!;
if (positionedBox.hasSize) {
// Flutter only allow access size of direct children, so cannot use realDisplayedBox.size
Size realDisplayedBoxSize = positionedBox.getBoxSize(positionedBox.contentSize);
double realDisplayedBoxWidth = realDisplayedBoxSize.width;
double realDisplayedBoxHeight = realDisplayedBoxSize.height;
childConstraints = BoxConstraints(
minWidth: realDisplayedBoxWidth,
maxWidth: realDisplayedBoxWidth,
minHeight: realDisplayedBoxHeight,
maxHeight: realDisplayedBoxHeight,
);
} else {
childConstraints = BoxConstraints();
}
} else if (child is RenderBoxModel) {
childConstraints = child.getConstraints();
} else if (child is RenderTextBox) {
Expand Down Expand Up @@ -1345,7 +1335,7 @@ class RenderFlexLayout extends RenderLayoutBox {

// Whether child is positioned placeholder or positioned renderObject
bool isChildPositioned = placeholderChild == null &&
(isPlaceholderPositioned(child) || childParentData!.isPositioned);
(_isPlaceholderPositioned(child) || childParentData!.isPositioned);
// Whether child cross size should be changed based on cross axis alignment change
bool isCrossSizeChanged = false;

Expand Down Expand Up @@ -2008,7 +1998,7 @@ class RenderFlexLayout extends RenderLayoutBox {
// Exclude positioned placeholder renderObject when layout non placeholder object
// and positioned renderObject
if (placeholderChild == null &&
(isPlaceholderPositioned(child) || childParentData!.isPositioned)) {
(_isPlaceholderPositioned(child) || childParentData!.isPositioned)) {
child = childParentData!.nextSibling;
continue;
}
Expand Down Expand Up @@ -2496,6 +2486,17 @@ class RenderFlexLayout extends RenderLayoutBox {
properties
.add(DiagnosticsProperty<FlexWrap>('flexWrap', renderStyle.flexWrap));
}

static bool _isPlaceholderPositioned(RenderObject child) {
if (child is RenderPositionPlaceholder) {
RenderBoxModel realDisplayedBox = child.positioned!;
RenderLayoutParentData parentData = realDisplayedBox.parentData as RenderLayoutParentData;
if (parentData.isPositioned) {
return true;
}
}
return false;
}
}

// Render flex layout with self repaint boundary.
Expand Down