Skip to content

Commit

Permalink
Refactor processor to support externally-created views
Browse files Browse the repository at this point in the history
  • Loading branch information
squarejesse committed Oct 6, 2023
1 parent 897da7d commit f31da99
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,6 @@ internal open class UIViewLazyList(
protected var onViewportChanged: ((firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) -> Unit)? = null

private val processor = object : LazyListUpdateProcessor<LazyListContainerCell, UIView>() {
override fun createView(
binding: Binding<LazyListContainerCell, UIView>,
index: Int,
): LazyListContainerCell {
val result = tableView.dequeueReusableCellWithIdentifier(
identifier = reuseIdentifier,
forIndexPath = NSIndexPath.indexPathForItem(index.convert(), 0.convert()),
) as LazyListContainerCell
require(result.binding == null)
result.binding = binding
return result
}

override fun insertRows(index: Int, count: Int) {
// TODO(jwilson): pass a range somehow when 'count' is large?
tableView.insertRowsAtIndexPaths(
Expand Down Expand Up @@ -120,7 +107,26 @@ internal open class UIViewLazyList(
override fun tableView(
tableView: UITableView,
cellForRowAtIndexPath: NSIndexPath,
) = processor.getView(cellForRowAtIndexPath.item.toInt())
): LazyListContainerCell {
val index = cellForRowAtIndexPath.item.toInt()
return processor.getOrCreateBoundView(index) { binding ->
createView(tableView, binding, index)
}
}

private fun createView(
tableView: UITableView,
binding: Binding<LazyListContainerCell, UIView>,
index: Int,
): LazyListContainerCell {
val result = tableView.dequeueReusableCellWithIdentifier(
identifier = reuseIdentifier,
forIndexPath = NSIndexPath.indexPathForItem(index.convert(), 0.convert()),
) as LazyListContainerCell
require(result.binding == null)
result.binding = binding
return result
}
}

private val tableViewDelegate: UITableViewDelegateProtocol =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,55 +270,51 @@ public abstract class LazyListUpdateProcessor<V : BoundView<W>, W : Any> {
return loaded
}

public fun getView(index: Int): V {
return when {
index < itemsBefore.size -> getOrCreatePlaceholder(itemsBefore, index, index)
index < itemsBefore.size + loadedItems.size -> getLoadedView(index)
else -> getOrCreatePlaceholder(itemsAfter, index - itemsBefore.size - loadedItems.size, index)
}
}

private fun getLoadedView(index: Int): V {
val binding = loadedItems[index - itemsBefore.size]
public fun getOrCreateBoundView(
index: Int,
createView: (binding: Binding<V, W>) -> V,
): V {
val binding = requireBindingAtIndex(index)

var view = binding.view
if (view == null) {
view = createView(binding, index)
view = createView(binding)
binding.bind(view)
}

return view
}

private fun getOrCreatePlaceholder(
placeholders: SparseList<Binding<V, W>?>,
placeholderIndex: Int,
cellIndex: Int,
): V {
var binding = placeholders[placeholderIndex]

// Return an existing placeholder.
if (binding != null) return binding.view!!
public fun bind(index: Int, view: V): Binding<V, W> {
return requireBindingAtIndex(index).apply {
bind(view)
}
}

// Create a new placeholder cell and bind it to the view.
binding = Binding(
processor = this,
/**
* Callers must immediately call [Binding.bind] on the result if it isn't already bound. Otherwise
* we could leak placeholder bindings.
*/
private fun requireBindingAtIndex(index: Int): Binding<V, W> {
fun createBinding() = Binding(
processor = this@LazyListUpdateProcessor,
isPlaceholder = true,
)
binding.content = takePlaceholder()
val view = createView(binding, cellIndex)
binding.bind(view)
placeholders.set(placeholderIndex, binding)
return view
).apply {
content = takePlaceholder()
}

return when {
index < itemsBefore.size -> itemsBefore.getOrCreate(index, ::createBinding)
index < itemsBefore.size + loadedItems.size -> loadedItems[index - itemsBefore.size]
else -> itemsAfter.getOrCreate(index - itemsBefore.size - loadedItems.size, ::createBinding)
}
}

private fun takePlaceholder(): Widget<W> {
return placeholdersQueue.removeFirstOrNull()
?: throw IllegalStateException("no more placeholders!")
}

protected abstract fun createView(binding: Binding<V, W>, index: Int): V

protected abstract fun insertRows(index: Int, count: Int)

protected abstract fun deleteRows(index: Int, count: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ internal class SparseList<T> : AbstractList<T?>() {
}
}

inline fun getOrCreate(index: Int, create: () -> T & Any): T & Any {
var result = get(index)

if (result != null) return result

result = create()
set(index, result)
return result
}

fun removeLast(): T? {
return removeAt(size - 1)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ class FakeProcessor : LazyListUpdateProcessor<FakeProcessor.StringCell, String>(
private var scrollWindowOffset = 0
private val scrollWindowCells = mutableListOf<StringCell>()

override fun createView(
binding: Binding<StringCell, String>,
index: Int,
) = StringCell(binding)
private fun getView(index: Int): StringCell {
return getOrCreateBoundView(index) { binding ->
StringCell(binding)
}
}

override fun insertRows(index: Int, count: Int) {
require(index >= 0 && count >= 0 && index <= dataSize + 1)
Expand Down

0 comments on commit f31da99

Please sign in to comment.