[go: nahoru, domu]

blob: 511360655302383842057836ac5e9ada87027799 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.ui.rendering.obj
import androidx.ui.engine.geometry.Offset
import androidx.ui.engine.text.TextBaseline
import androidx.ui.gestures.hit_test.HitTestResult
import androidx.ui.rendering.box.RenderBox
import androidx.ui.rendering.flex.ContainerBoxParentData
/**
* Generic mixin for render objects with a list of children.
*
* Provides a child model for a render object subclass that has a doubly-linked
* list of children.
*/
// TODO(migration/Mihai): complete this class, has many commented out parts
// TODO(migration/Mihai): inheriting from RenderBox to workaround mixins for flex, but this a hack
abstract class ContainerRenderObjectMixin<
ChildType : RenderObject,
ParentDataType : ContainerParentDataMixin<ChildType>
> : RenderBox() {
// This class is intended to be used as a mixin, and should not be
// extended directly.
// factory ContainerRenderObjectMixin._() => null;
// bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType equals }) {
// ParentDataType childParentData = child.parentData;
// while (childParentData.previousSibling != null) {
// assert(childParentData.previousSibling != child);
// child = childParentData.previousSibling;
// childParentData = child.parentData;
// }
// return child == equals;
// }
// bool _debugUltimateNextSiblingOf(ChildType child, { ChildType equals }) {
// ParentDataType childParentData = child.parentData;
// while (childParentData.nextSibling != null) {
// assert(childParentData.nextSibling != child);
// child = childParentData.nextSibling;
// childParentData = child.parentData;
// }
// return child == equals;
// }
/** The number of children. */
var childCount = 0
private set
/**
* Checks whether the given render object has the correct [runtimeType] to be
* a child of this render object.
*
* Does nothing if assertions are disabled.
*
* Always returns true.
*/
// bool debugValidateChild(RenderObject child) {
// assert(() {
// if (child is! ChildType) {
// throw new FlutterError(
// 'A $runtimeType expected a child of type $ChildType but received a '
// 'child of type ${child.runtimeType}.\n'
// 'RenderObjects expect specific types of children because they '
// 'coordinate with their children during layout and paint. For '
// 'example, a RenderSliver cannot be the child of a RenderBox because '
// 'a RenderSliver does not understand the RenderBox layout protocol.\n'
// '\n'
// 'The $runtimeType that expected a $ChildType child was created by:\n'
// ' $debugCreator\n'
// '\n'
// 'The ${child.runtimeType} that did not match the expected child type '
// 'was created by:\n'
// ' ${child.debugCreator}\n'
// );
// }
// return true;
// }());
// return true;
// }
/** The first child in the child list. */
var firstChild: ChildType? = null
private set
/** The last child in the child list. */
var lastChild: ChildType? = null
private set
private fun insertIntoChildList(child: ChildType, after: ChildType? = null) {
val childParentData = child.parentData as ParentDataType
assert(childParentData.nextSibling == null)
assert(childParentData.previousSibling == null)
childCount += 1
assert(childCount > 0)
if (after == null) {
// insert at the start (firstChild)
childParentData.nextSibling = firstChild
if (firstChild != null) {
val firstChildParentData = firstChild!!.parentData as ParentDataType
firstChildParentData.previousSibling = child
}
firstChild = child
if (lastChild == null) {
lastChild = child
}
} else {
assert(firstChild != null)
assert(lastChild != null)
// assert(_debugUltimatePreviousSiblingOf(after, equals: firstChild));
// assert(_debugUltimateNextSiblingOf(after, equals: lastChild));
val afterParentData = after.parentData as ParentDataType
if (afterParentData.nextSibling == null) {
// insert at the end (lastChild); we'll end up with two or more children
assert(after == lastChild)
childParentData.previousSibling = after
afterParentData.nextSibling = child
lastChild = child
} else {
// insert in the middle; we'll end up with three or more children
// set up links from child to siblings
childParentData.nextSibling = afterParentData.nextSibling
childParentData.previousSibling = after
// set up links from siblings to child
val childPreviousSiblingParentData =
childParentData.previousSibling!!.parentData as ParentDataType
val childNextSiblingParentData =
childParentData.nextSibling!!.parentData as ParentDataType
childPreviousSiblingParentData.nextSibling = child
childNextSiblingParentData.previousSibling = child
assert(afterParentData.nextSibling == child)
}
}
}
/**
* Insert child into this render object's child list after the given child.
*
* If `after` is null, then this inserts the child at the start of the list,
* and the child becomes the new [firstChild].
*/
fun insert(child: ChildType, after: ChildType? = null) {
assert(child != this, { "A RenderObject cannot be inserted into itself." })
assert(after != this, { "A RenderObject cannot simultaneously be both the parent" +
"and the sibling of another RenderObject." })
assert(child != after, { "A RenderObject cannot be inserted after itself." })
assert(child != firstChild)
assert(child != lastChild)
adoptChild(child)
insertIntoChildList(child, after = after)
}
/** Append child to the end of this render object's child list. */
fun add(child: ChildType) {
insert(child, after = lastChild)
}
/** Add all the children to the end of this render object's child list. */
fun addAll(children: List<ChildType>?) {
children?.forEach {
add(it)
}
}
// void _removeFromChildList(ChildType child) {
// final ParentDataType childParentData = child.parentData;
// assert(_debugUltimatePreviousSiblingOf(child, equals: firstChild));
// assert(_debugUltimateNextSiblingOf(child, equals: lastChild));
// assert(_childCount >= 0);
// if (childParentData.previousSibling == null) {
// assert(firstChild == child);
// firstChild = childParentData.nextSibling;
// } else {
// final ParentDataType childPreviousSiblingParentData = childParentData.previousSibling.parentData;
// childPreviousSiblingParentData.nextSibling = childParentData.nextSibling;
// }
// if (childParentData.nextSibling == null) {
// assert(lastChild == child);
// lastChild = childParentData.previousSibling;
// } else {
// final ParentDataType childNextSiblingParentData = childParentData.nextSibling.parentData;
// childNextSiblingParentData.previousSibling = childParentData.previousSibling;
// }
// childParentData.previousSibling = null;
// childParentData.nextSibling = null;
// _childCount -= 1;
// }
/**
* Remove this child from the child list.
*
* Requires the child to be present in the child list.
*/
// void remove(ChildType child) {
// _removeFromChildList(child);
// dropChild(child);
// }
/**
* Remove all their children from this render object's child list.
*
* More efficient than removing them individually.
*/
// void removeAll() {
// ChildType child = firstChild;
// while (child != null) {
// final ParentDataType childParentData = child.parentData;
// final ChildType next = childParentData.nextSibling;
// childParentData.previousSibling = null;
// childParentData.nextSibling = null;
// dropChild(child);
// child = next;
// }
// firstChild = null;
// lastChild = null;
// _childCount = 0;
// }
/**
* Move this child in the child list to be before the given child.
*
* More efficient than removing and re-adding the child. Requires the child
* to already be in the child list at some position. Pass null for before to
* move the child to the end of the child list.
*/
// void move(ChildType child, { ChildType after }) {
// assert(child != this);
// assert(after != this);
// assert(child != after);
// assert(child.parent == this);
// final ParentDataType childParentData = child.parentData;
// if (childParentData.previousSibling == after)
// return;
// _removeFromChildList(child);
// _insertIntoChildList(child, after: after);
// markNeedsLayout();
// }
override fun attach(owner: Any /* PipelineOwner */) {
super.attach(owner)
var child = firstChild
while (child != null) {
child.attach(owner)
val childParentData = child.parentData as ParentDataType
child = childParentData.nextSibling
}
}
override fun detach() {
super.detach()
var child = firstChild
while (child != null) {
child.detach()
val childParentData = child.parentData as ParentDataType
child = childParentData.nextSibling
}
}
override fun redepthChildren() {
var child = firstChild
while (child != null) {
redepthChild(child)
val childParentData = child.parentData as ParentDataType
child = childParentData.nextSibling
}
}
override fun visitChildren(visitor: RenderObjectVisitor) {
var child = firstChild
while (child != null) {
visitor(child)
val childParentData = child.parentData as ParentDataType
child = childParentData.nextSibling
}
}
/** The previous child before the given child in the child list. */
fun childBefore(child: ChildType): ChildType? {
assert(child.parent == this)
val childParentData = child.parentData as ParentDataType
return childParentData.previousSibling
}
/** The next child after the given child in the child list. */
fun childAfter(child: ChildType): ChildType? {
assert(child.parent == this)
val childParentData = child.parentData as ParentDataType
return childParentData.nextSibling
}
// @override
// List<DiagnosticsNode> debugDescribeChildren() {
// final List<DiagnosticsNode> children = <DiagnosticsNode>[];
// if (firstChild != null) {
// ChildType child = firstChild;
// int count = 1;
// while (true) {
// children.add(child.toDiagnosticsNode(name: 'child $count'));
// if (child == lastChild)
// break;
// count += 1;
// final ParentDataType childParentData = child.parentData;
// child = childParentData.nextSibling;
// }
// }
// return children;
// }
// TODO(Migration/Mihai): these are the contents of RenderBoxContainerDefaultsMixin
// Putting them here as a work around for mixins..
/**
* Returns the baseline of the first child with a baseline.
*
* Useful when the children are displayed vertically in the same order they
* appear in the child list.
*/
fun defaultComputeDistanceToFirstActualBaseline(baseline: TextBaseline): Float {
TODO("Migration/Mihai: baselines")
// assert(!debugNeedsLayout);
// ChildType child = firstChild;
// while (child != null) {
// final ParentDataType childParentData = child.parentData;
// final double result = child.getDistanceToActualBaseline(baseline);
// if (result != null)
// return result + childParentData.offset.dy;
// child = childParentData.nextSibling;
// }
// return null;
}
/**
* Returns the minimum baseline value among every child.
*
* Useful when the vertical position of the children isn't determined by the
* order in the child list.
*/
fun defaultComputeDistanceToHighestActualBaseline(baseline: TextBaseline): Float {
TODO("Migration/Mihai: baselines")
// assert(!debugNeedsLayout);
// double result;
// ChildType child = firstChild;
// while (child != null) {
// final ParentDataType childParentData = child.parentData;
// double candidate = child.getDistanceToActualBaseline(baseline);
// if (candidate != null) {
// candidate += childParentData.offset.dy;
// if (result != null)
// result = math.min(result, candidate);
// else
// result = candidate;
// }
// child = childParentData.nextSibling;
// }
// return result;
}
/**
* Performs a hit test on each child by walking the child list backwards.
*
* Stops walking once after the first child reports that it contains the
* given point. Returns whether any children contain the given point.
*
* See also:
*
* * [defaultPaint], which paints the children appropriate for this
* hit-testing strategy.
*/
fun defaultHitTestChildren(result: HitTestResult, position: Offset? = null) {
TODO("Migration/Mihai: flex hit test")
// // the x, y parameters have the top left of the node's box as the origin
// ChildType child = lastChild;
// while (child != null) {
// final ParentDataType childParentData = child.parentData;
// if (child.hitTest(result, position: position - childParentData.offset))
// return true;
// child = childParentData.previousSibling;
// }
// return false;
}
/**
* Paints each child by walking the child list forwards.
*
* See also:
*
* * [defaultHitTestChildren], which implements hit-testing of the children
* in a manner appropriate for this painting strategy.
*/
fun defaultPaint(context: PaintingContext, offset: Offset) {
var child: RenderBox? = firstChild as RenderBox
while (child != null) {
val childParentData = child.parentData as ContainerBoxParentData<RenderBox>
context.paintChild(child, childParentData.offset + offset)
child = childParentData.nextSibling
}
}
/**
* Returns a list containing the children of this render object.
*
* This function is useful when you need random-access to the children of
* this render object. If you're accessing the children in order, consider
* walking the child list directly.
*/
fun getChildrenAsList(): List<ChildType> {
val result = mutableListOf<ChildType>()
var child: RenderBox? = firstChild as RenderBox
while (child != null) {
val childParentData = child.parentData as ContainerBoxParentData<RenderBox>
result.add(child as ChildType)
child = childParentData.nextSibling
}
return result
}
}