Skip to content

Commit

Permalink
Merge pull request #662 from openkraken/fix/recycler-list-view
Browse files Browse the repository at this point in the history
🎨 fix: recycler view with overflow error
  • Loading branch information
wssgcg1213 committed Sep 15, 2021
2 parents eb817b0 + b861c0a commit eb3f37d
Show file tree
Hide file tree
Showing 19 changed files with 495 additions and 394 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions integration_tests/specs/css/css-display/sliver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,33 @@ describe('display sliver', () => {
await snapshot();
});

it('scrollTop', async () => {
const container = createSliverBasicCase();

container.scrollBy(0, 200);
expect(container.scrollTop).toEqual(200);

container.scrollBy(0, -150);
expect(container.scrollTop).toEqual(50);
});

it('scrollHeight', async () => {
const container = createSliverBasicCase();

container.scrollBy(0, 200);
expect(container.scrollHeight).toEqual(100 * 99);
});

it('child contains Comment and Text', async () => {
const container = createSliverBasicCase();
const comment = document.createComment('foo');
container.appendChild(comment);
container.appendChild(document.createTextNode('hello'));

// No error occurred, pass.
await snapshot();
});

it('continuous scroll works', async () => {
const container = createSliverBasicCase();

Expand All @@ -51,6 +78,27 @@ describe('display sliver', () => {
await snapshot();
});

it('insertBefore with right order', async () => {
var d = document.createElement('div');

for (var i = 0; i < 100; i ++) {
var e = document.createElement('div');
e.style.background = 'red';
e.style.width = e.style.height = '99px';
e.appendChild(document.createTextNode(i + ''));
d.insertBefore(e, d.firstChild);
}

d.style.display = 'sliver';
d.style.width = '100px';
d.style.height = '150px';

document.body.appendChild(d);

// Order from 99 -> 0
await snapshot();
});

it('should works with positioned element of no top and left', async () => {
let div;
div = createElement(
Expand Down
2 changes: 1 addition & 1 deletion kraken/lib/src/bridge/to_native.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ nativeDynamicLibrary.lookup<NativeFunction<NativeEvaluateScripts>>('evaluateScri
final DartParseHTML _parseHTML =
nativeDynamicLibrary.lookup<NativeFunction<NativeParseHTML>>('parseHTML').asFunction();

void evaluateScripts(int contextId, String code, String url, int line) {
void evaluateScripts(int contextId, String code, String url, [int line = 0]) {
if(KrakenController.getControllerOfJSContextId(contextId) == null) {
return;
}
Expand Down
179 changes: 101 additions & 78 deletions kraken/lib/src/css/overflow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ CSSOverflowType _getOverflowType(String definition) {
}
}

typedef ScrollListener = void Function(double scrollTop, AxisDirection axisDirection);

mixin CSSOverflowStyleMixin on RenderStyleBase {
CSSOverflowType _overflowX = CSSOverflowType.visible;
CSSOverflowType get overflowX {
Expand Down Expand Up @@ -87,82 +85,92 @@ mixin CSSOverflowMixin on ElementBase {
// House content which can be scrolled.
RenderLayoutBox? scrollingContentLayoutBox;

void updateRenderOverflow(Element element, ScrollListener scrollListener) {
void updateRenderOverflow(Element element) {
ScrollListener scrollListener = element.elementDelegate.handleScroll;
CSSStyleDeclaration style = element.style;
RenderBoxModel renderBoxModel = element.renderBoxModel!;
RenderStyle renderStyle = renderBoxModel.renderStyle;

renderStyle.updateOverflow(style);
CSSOverflowType overflowX = renderStyle.overflowX;
CSSOverflowType overflowY = renderStyle.overflowY;
bool shouldRepaintSelf = false;

switch(overflowX) {
case CSSOverflowType.hidden:
_scrollableX = null;
renderBoxModel.clipX = true;
// overflow hidden can be scrolled programmatically
renderBoxModel.enableScrollX = true;
break;
case CSSOverflowType.clip:
_scrollableX = null;
renderBoxModel.clipX = true;
// overflow clip can't scrolled programmatically
renderBoxModel.enableScrollX = false;
break;
case CSSOverflowType.auto:
case CSSOverflowType.scroll:
_scrollableX = KrakenScrollable(axisDirection: AxisDirection.right, scrollListener: scrollListener);
shouldRepaintSelf = true;
renderBoxModel.clipX = true;
renderBoxModel.enableScrollX = true;
renderBoxModel.scrollOffsetX = _scrollableX!.position;
break;
case CSSOverflowType.visible:
default:
_scrollableX = null;
renderBoxModel.clipX = false;
renderBoxModel.enableScrollX = false;
break;
}

switch(overflowY) {
case CSSOverflowType.hidden:
_scrollableY = null;
renderBoxModel.clipY = true;
// overflow hidden can be scrolled programmatically
renderBoxModel.enableScrollY = true;
break;
case CSSOverflowType.clip:
_scrollableY = null;
renderBoxModel.clipY = true;
// overflow clip can't scrolled programmatically
renderBoxModel.enableScrollY = false;
break;
case CSSOverflowType.auto:
case CSSOverflowType.scroll:
_scrollableY = KrakenScrollable(axisDirection: AxisDirection.down, scrollListener: scrollListener);
shouldRepaintSelf = true;
renderBoxModel.clipY = true;
renderBoxModel.enableScrollY = true;
renderBoxModel.scrollOffsetY = _scrollableY!.position;
break;
case CSSOverflowType.visible:
default:
_scrollableY = null;
renderBoxModel.clipY = false;
renderBoxModel.enableScrollY = false;
break;
}
if (renderBoxModel is RenderRecyclerLayout) {
// Recycler layout not need repaintBoundary and scroll/pointer listeners,
// ignoring overflowX or overflowY sets, which handle it self.
renderBoxModel.clipX = renderBoxModel.clipY = false;
renderBoxModel.scrollOffsetX = renderBoxModel.axis == Axis.horizontal
? renderBoxModel.scrollable.position : null;
renderBoxModel.scrollOffsetY = renderBoxModel.axis == Axis.vertical
? renderBoxModel.scrollable.position : null;
} else {
CSSOverflowType overflowX = renderStyle.overflowX;
CSSOverflowType overflowY = renderStyle.overflowY;
bool shouldRepaintSelf = false;

switch(overflowX) {
case CSSOverflowType.hidden:
_scrollableX = null;
renderBoxModel.clipX = true;
// Overflow hidden can be scrolled programmatically.
renderBoxModel.enableScrollX = true;
break;
case CSSOverflowType.clip:
_scrollableX = null;
renderBoxModel.clipX = true;
// Overflow clip can't scrolled programmatically.
renderBoxModel.enableScrollX = false;
break;
case CSSOverflowType.auto:
case CSSOverflowType.scroll:
_scrollableX = KrakenScrollable(axisDirection: AxisDirection.right, scrollListener: scrollListener);
shouldRepaintSelf = true;
renderBoxModel.clipX = true;
renderBoxModel.enableScrollX = true;
renderBoxModel.scrollOffsetX = _scrollableX!.position;
break;
case CSSOverflowType.visible:
default:
_scrollableX = null;
renderBoxModel.clipX = false;
renderBoxModel.enableScrollX = false;
break;
}

switch(overflowY) {
case CSSOverflowType.hidden:
_scrollableY = null;
renderBoxModel.clipY = true;
renderBoxModel.enableScrollY = true;
break;
case CSSOverflowType.clip:
_scrollableY = null;
renderBoxModel.clipY = true;
renderBoxModel.enableScrollY = false;
break;
case CSSOverflowType.auto:
case CSSOverflowType.scroll:
_scrollableY = KrakenScrollable(axisDirection: AxisDirection.down, scrollListener: scrollListener);
shouldRepaintSelf = true;
renderBoxModel.clipY = true;
renderBoxModel.enableScrollY = true;
renderBoxModel.scrollOffsetY = _scrollableY!.position;
break;
case CSSOverflowType.visible:
default:
_scrollableY = null;
renderBoxModel.clipY = false;
renderBoxModel.enableScrollY = false;
break;
}

renderBoxModel.scrollListener = scrollListener;
renderBoxModel.pointerListener = _pointerListener;
renderBoxModel.scrollListener = scrollListener;
renderBoxModel.pointerListener = _pointerListener;

if (renderBoxModel is RenderLayoutBox) {
if (shouldRepaintSelf) {
_upgradeToSelfRepaint(element);
} else {
_downgradeToParentRepaint(element);
if (renderBoxModel is RenderLayoutBox) {
if (shouldRepaintSelf) {
_upgradeToSelfRepaint(element);
} else {
_downgradeToParentRepaint(element);
}
}
}
}
Expand Down Expand Up @@ -267,7 +275,7 @@ mixin CSSOverflowMixin on ElementBase {
if (parent is RenderObjectWithChildMixin<RenderBox>) {
parent.child = null;
} else if (parent is ContainerRenderObjectMixin) {
ContainerBoxParentData parentData = renderObject!.parentData as ContainerBoxParentData<RenderObject>;
ContainerParentDataMixin parentData = renderObject!.parentData as ContainerParentDataMixin<RenderObject>;
RenderObject? previousSibling = parentData.previousSibling;
parent.remove(renderObject);
return previousSibling;
Expand Down Expand Up @@ -298,31 +306,45 @@ mixin CSSOverflowMixin on ElementBase {
}

double get scrollTop {
if (_scrollableY != null) {
return _scrollableY!.position?.pixels ?? 0;
KrakenScrollable? scrollableY = _getScrollable(Axis.vertical);
if (scrollableY != null) {
return scrollableY.position?.pixels ?? 0;
}
return 0.0;
}

set scrollTop(double value) {
scrollTo(y: value);
}

double get scrollLeft {
if (_scrollableX != null) {
return _scrollableX!.position?.pixels ?? 0;
KrakenScrollable? scrollableX = _getScrollable(Axis.horizontal);
if (scrollableX != null) {
return scrollableX.position?.pixels ?? 0;
}
return 0.0;
}

set scrollLeft(double value) {
scrollTo(x: value);
}

get scrollHeight {
double get scrollHeight {
KrakenScrollable? scrollable = _getScrollable(Axis.vertical);
if (scrollable?.position?.maxScrollExtent != null) {
// Viewport height + maxScrollExtent
return renderBoxModel!.clientHeight + scrollable!.position!.maxScrollExtent!;
}

Size scrollContainerSize = renderBoxModel!.scrollableSize;
return scrollContainerSize.height;
}

get scrollWidth {
double get scrollWidth {
KrakenScrollable? scrollable = _getScrollable(Axis.horizontal);
if (scrollable?.position?.maxScrollExtent != null) {
return renderBoxModel!.clientWidth + scrollable!.position!.maxScrollExtent!;
}
Size scrollContainerSize = renderBoxModel!.scrollableSize;
return scrollContainerSize.width;
}
Expand All @@ -349,7 +371,8 @@ mixin CSSOverflowMixin on ElementBase {
KrakenScrollable? _getScrollable(Axis direction) {
KrakenScrollable? scrollable;
if (renderer is RenderRecyclerLayout) {
scrollable = (renderer as RenderRecyclerLayout).scrollable;
RenderRecyclerLayout recyclerLayout = renderer as RenderRecyclerLayout;
scrollable = direction == recyclerLayout.axis ? recyclerLayout.scrollable : null;
} else {
if (direction == Axis.horizontal) {
scrollable = _scrollableX;
Expand Down
10 changes: 5 additions & 5 deletions kraken/lib/src/css/sliver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ mixin CSSSliverMixin on RenderStyleBase {

if (renderBoxModel is RenderRecyclerLayout) {
RenderRecyclerLayout recyclerLayout = renderBoxModel as RenderRecyclerLayout;

AxisDirection axisDirection = RenderRecyclerLayout.getAxisDirection(value);

recyclerLayout.scrollable = KrakenScrollable(axisDirection: axisDirection);
RenderViewport renderViewport = recyclerLayout.renderViewport!;
renderViewport.axisDirection = axisDirection;
renderViewport.crossAxisDirection = RenderRecyclerLayout.getCrossAxisDirection(value);
renderViewport.offset = recyclerLayout.scrollable!.position!;
recyclerLayout.viewport
..axisDirection = axisDirection
..crossAxisDirection = RenderRecyclerLayout.getCrossAxisDirection(value)
..offset = recyclerLayout.scrollable.position!;

recyclerLayout.markNeedsLayout();
}
Expand Down

0 comments on commit eb3f37d

Please sign in to comment.