Slightly optimized bindChildrenTo for the frequent case where all list cell elements have been replaced.

This commit is contained in:
Daan Vanden Bosch 2021-12-03 22:08:04 +01:00
parent e492b28aac
commit 4792dc1172
7 changed files with 86 additions and 23 deletions

View File

@ -65,9 +65,12 @@ abstract class AbstractDependentListCell<E> :
observer( observer(
ListChangeEvent( ListChangeEvent(
value, value,
listOf( listOf(ListChange.Structural(
ListChange.Structural(index = 0, removed = emptyList(), inserted = value), index = 0,
), prevSize = 0,
removed = emptyList(),
inserted = value,
)),
) )
) )
} }
@ -81,7 +84,15 @@ abstract class AbstractDependentListCell<E> :
computeElements() computeElements()
emitDependencyChanged( emitDependencyChanged(
ListChangeEvent(elements, listOf(ListChange.Structural(0, oldElements, elements))) ListChangeEvent(
elements,
listOf(ListChange.Structural(
index = 0,
prevSize = oldElements.size,
removed = oldElements,
inserted = elements,
)),
)
) )
} }

View File

@ -49,9 +49,12 @@ abstract class AbstractListCell<E> : AbstractCell<List<E>>(), ListCell<E> {
observer( observer(
ListChangeEvent( ListChangeEvent(
value, value,
listOf( listOf(ListChange.Structural(
ListChange.Structural(index = 0, removed = emptyList(), inserted = value), index = 0,
), prevSize = 0,
removed = emptyList(),
inserted = value
)),
) )
) )
} }

View File

@ -48,6 +48,7 @@ class FilteredListCell<E>(
override fun dependencyChanged(dependency: Dependency, event: ChangeEvent<*>?) { override fun dependencyChanged(dependency: Dependency, event: ChangeEvent<*>?) {
if (event is ListChangeEvent<*>) { if (event is ListChangeEvent<*>) {
val prevSize = elements.size
val filteredChanges = mutableListOf<ListChange<E>>() val filteredChanges = mutableListOf<ListChange<E>>()
for (change in event.changes) { for (change in event.changes) {
@ -95,6 +96,7 @@ class FilteredListCell<E>(
filteredChanges.add( filteredChanges.add(
ListChange.Structural( ListChange.Structural(
eventIndex, eventIndex,
prevSize,
removed, removed,
inserted inserted
) )
@ -140,6 +142,7 @@ class FilteredListCell<E>(
filteredChanges.add( filteredChanges.add(
ListChange.Structural( ListChange.Structural(
insertIndex, insertIndex,
prevSize,
removed = emptyList(), removed = emptyList(),
inserted = listOf(change.updated), inserted = listOf(change.updated),
) )
@ -167,6 +170,7 @@ class FilteredListCell<E>(
filteredChanges.add( filteredChanges.add(
ListChange.Structural( ListChange.Structural(
index, index,
prevSize,
removed = listOf(change.updated), removed = listOf(change.updated),
inserted = emptyList(), inserted = emptyList(),
) )

View File

@ -32,7 +32,15 @@ class ImmutableListCell<E>(private val elements: List<E>) : AbstractDependency()
override fun observeList(callNow: Boolean, observer: ListObserver<E>): Disposable { override fun observeList(callNow: Boolean, observer: ListObserver<E>): Disposable {
if (callNow) { if (callNow) {
observer(ListChangeEvent(value, listOf(ListChange.Structural(0, emptyList(), value)))) observer(ListChangeEvent(
value,
listOf(ListChange.Structural(
index = 0,
prevSize = 0,
removed = emptyList(),
inserted = value,
)),
))
} }
return nopDisposable() return nopDisposable()

View File

@ -13,6 +13,7 @@ sealed class ListChange<out E> {
*/ */
class Structural<out E>( class Structural<out E>(
val index: Int, val index: Int,
val prevSize: Int,
/** /**
* The elements that were removed from the list at [index]. * The elements that were removed from the list at [index].
* *
@ -27,7 +28,9 @@ sealed class ListChange<out E> {
* be mutated when the originating [ListCell] is mutated. * be mutated when the originating [ListCell] is mutated.
*/ */
val inserted: List<E>, val inserted: List<E>,
) : ListChange<E>() ) : ListChange<E>() {
val allRemoved: Boolean get() = removed.size == prevSize
}
/** /**
* Represents a change to an element in a list cell. Will only be emitted if the list is * Represents a change to an element in a list cell. Will only be emitted if the list is

View File

@ -50,7 +50,12 @@ class SimpleListCell<E>(
elementDependents[index] = ElementDependent(index, element) elementDependents[index] = ElementDependent(index, element)
} }
changes.add(ListChange.Structural(index, listOf(removed), listOf(element))) changes.add(ListChange.Structural(
index,
prevSize = elements.size,
removed = listOf(removed),
inserted = listOf(element),
))
ChangeManager.changed(this) ChangeManager.changed(this)
return removed return removed
@ -63,17 +68,23 @@ class SimpleListCell<E>(
copyAndResetWrapper() copyAndResetWrapper()
elements.add(element) elements.add(element)
finalizeStructuralChange(index, emptyList(), listOf(element)) finalizeStructuralChange(
index,
prevSize = index,
removed = emptyList(),
inserted = listOf(element),
)
} }
override fun add(index: Int, element: E) { override fun add(index: Int, element: E) {
checkIndex(index, elements.size) val prevSize = elements.size
checkIndex(index, prevSize)
emitMightChange() emitMightChange()
copyAndResetWrapper() copyAndResetWrapper()
elements.add(index, element) elements.add(index, element)
finalizeStructuralChange(index, emptyList(), listOf(element)) finalizeStructuralChange(index, prevSize, removed = emptyList(), inserted = listOf(element))
} }
override fun remove(element: E): Boolean { override fun remove(element: E): Boolean {
@ -91,34 +102,41 @@ class SimpleListCell<E>(
checkIndex(index, elements.lastIndex) checkIndex(index, elements.lastIndex)
emitMightChange() emitMightChange()
val prevSize = elements.size
copyAndResetWrapper() copyAndResetWrapper()
val removed = elements.removeAt(index) val removed = elements.removeAt(index)
finalizeStructuralChange(index, listOf(removed), emptyList()) finalizeStructuralChange(index, prevSize, removed = listOf(removed), inserted = emptyList())
return removed return removed
} }
override fun replaceAll(elements: Iterable<E>) { override fun replaceAll(elements: Iterable<E>) {
emitMightChange() emitMightChange()
val prevSize = this.elements.size
val removed = elementsWrapper val removed = elementsWrapper
copyAndResetWrapper() copyAndResetWrapper()
this.elements.replaceAll(elements) this.elements.replaceAll(elements)
finalizeStructuralChange(0, removed, elementsWrapper) finalizeStructuralChange(index = 0, prevSize, removed, inserted = elementsWrapper)
} }
override fun replaceAll(elements: Sequence<E>) { override fun replaceAll(elements: Sequence<E>) {
emitMightChange() emitMightChange()
val prevSize = this.elements.size
val removed = elementsWrapper val removed = elementsWrapper
copyAndResetWrapper() copyAndResetWrapper()
this.elements.replaceAll(elements) this.elements.replaceAll(elements)
finalizeStructuralChange(0, removed, elementsWrapper) finalizeStructuralChange(index = 0, prevSize, removed, inserted = elementsWrapper)
} }
override fun splice(fromIndex: Int, removeCount: Int, newElement: E) { override fun splice(fromIndex: Int, removeCount: Int, newElement: E) {
val prevSize = elements.size
val removed = ArrayList<E>(removeCount) val removed = ArrayList<E>(removeCount)
for (i in fromIndex until (fromIndex + removeCount)) { for (i in fromIndex until (fromIndex + removeCount)) {
@ -131,17 +149,19 @@ class SimpleListCell<E>(
repeat(removeCount) { elements.removeAt(fromIndex) } repeat(removeCount) { elements.removeAt(fromIndex) }
elements.add(fromIndex, newElement) elements.add(fromIndex, newElement)
finalizeStructuralChange(fromIndex, removed, listOf(newElement)) finalizeStructuralChange(fromIndex, prevSize, removed, inserted = listOf(newElement))
} }
override fun clear() { override fun clear() {
emitMightChange() emitMightChange()
val prevSize = elements.size
val removed = elementsWrapper val removed = elementsWrapper
copyAndResetWrapper() copyAndResetWrapper()
elements.clear() elements.clear()
finalizeStructuralChange(0, removed, emptyList()) finalizeStructuralChange(index = 0, prevSize, removed, inserted = emptyList())
} }
override fun sortWith(comparator: Comparator<E>) { override fun sortWith(comparator: Comparator<E>) {
@ -157,7 +177,12 @@ class SimpleListCell<E>(
throwable = e throwable = e
} }
finalizeStructuralChange(0, removed, elementsWrapper) finalizeStructuralChange(
index = 0,
prevSize = elements.size,
removed,
inserted = elementsWrapper,
)
if (throwable != null) { if (throwable != null) {
throw throwable throw throwable
@ -200,7 +225,12 @@ class SimpleListCell<E>(
} }
} }
private fun finalizeStructuralChange(index: Int, removed: List<E>, inserted: List<E>) { private fun finalizeStructuralChange(
index: Int,
prevSize: Int,
removed: List<E>,
inserted: List<E>,
) {
if (dependents.isNotEmpty() && extractDependencies != null) { if (dependents.isNotEmpty() && extractDependencies != null) {
repeat(removed.size) { repeat(removed.size) {
elementDependents.removeAt(index).dispose() elementDependents.removeAt(index).dispose()
@ -218,7 +248,7 @@ class SimpleListCell<E>(
} }
} }
changes.add(ListChange.Structural(index, removed, inserted)) changes.add(ListChange.Structural(index, prevSize, removed, inserted))
ChangeManager.changed(this) ChangeManager.changed(this)
} }

View File

@ -273,8 +273,12 @@ private fun <T> bindChildrenTo(
list.observeList(callNow = true) { event: ListChangeEvent<T> -> list.observeList(callNow = true) { event: ListChangeEvent<T> ->
for (change in event.changes) { for (change in event.changes) {
if (change is ListChange.Structural) { if (change is ListChange.Structural) {
repeat(change.removed.size) { if (change.allRemoved) {
parent.removeChild(parent.childNodes[change.index].unsafeCast<Node>()) parent.innerHTML = ""
} else {
repeat(change.removed.size) {
parent.removeChild(parent.childNodes[change.index].unsafeCast<Node>())
}
} }
childrenRemoved(change.index, change.removed.size) childrenRemoved(change.index, change.removed.size)