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

Use Widget as content type in LazyListUpdateProcessor #2446

Merged
merged 2 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ internal class HTMLLazyList(document: Document) : LazyList<HTMLElement> {
override fun deleteRows(index: Int, count: Int) {
}

override fun setContent(view: HTMLElement, content: HTMLElement?, modifier: Modifier) {
if (content != null) {
view.appendChild(content)
override fun setContent(view: HTMLElement, widget: Widget<HTMLElement>?) {
if (widget != null) {
view.appendChild(widget.value)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ internal class UIViewLazyList :
private var updateProcessor: LazyListUpdateProcessor<LazyListContainerCell, UIView>? = object : LazyListUpdateProcessor<LazyListContainerCell, UIView>() {
override fun createPlaceholder(original: UIView) = SizeOnlyPlaceholder(original)

override fun isSizeOnlyPlaceholder(placeholder: UIView) = placeholder is SizeOnlyPlaceholder

override fun insertRows(index: Int, count: Int) {
rowCount += count
val tableView = [email protected] ?: error("detached")
Expand Down Expand Up @@ -156,8 +154,8 @@ internal class UIViewLazyList :
tableView.endUpdates()
}

override fun setContent(view: LazyListContainerCell, content: UIView?, modifier: Modifier) {
view.setContent(content)
override fun setContent(view: LazyListContainerCell, widget: Widget<UIView>?) {
view.setContent(widget?.value)
}

override fun detach(view: LazyListContainerCell) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ internal class ViewLazyList private constructor(
override fun createPlaceholder(original: View) =
SizeOnlyPlaceholder(original, value.context)

override fun isSizeOnlyPlaceholder(placeholder: View) =
placeholder is SizeOnlyPlaceholder

override fun insertRows(index: Int, count: Int) {
adapter.notifyItemRangeInserted(index, count)
}
Expand All @@ -84,8 +81,8 @@ internal class ViewLazyList private constructor(
adapter.notifyItemRangeRemoved(index, count)
}

override fun setContent(view: ViewHolder, content: View?, modifier: Modifier) {
view.content = content
override fun setContent(view: ViewHolder, widget: Widget<View>?) {
view.content = widget?.value
}
}

Expand Down
3 changes: 1 addition & 2 deletions redwood-lazylayout-widget/api/redwood-lazylayout-widget.api
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,10 @@ public abstract class app/cash/redwood/lazylayout/widget/LazyListUpdateProcessor
public final fun getPlaceholder ()Lapp/cash/redwood/widget/Widget$Children;
public final fun getSize ()I
protected abstract fun insertRows (II)V
protected fun isSizeOnlyPlaceholder (Ljava/lang/Object;)Z
public final fun itemsAfter (I)V
public final fun itemsBefore (I)V
public final fun onEndChanges ()V
protected abstract fun setContent (Ljava/lang/Object;Ljava/lang/Object;Lapp/cash/redwood/Modifier;)V
protected abstract fun setContent (Ljava/lang/Object;Lapp/cash/redwood/widget/Widget;)V
}

public final class app/cash/redwood/lazylayout/widget/LazyListUpdateProcessor$Binding {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ abstract class <#A: kotlin/Any, #B: kotlin/Any> app.cash.redwood.lazylayout.widg

abstract fun deleteRows(kotlin/Int, kotlin/Int) // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.deleteRows|deleteRows(kotlin.Int;kotlin.Int){}[0]
abstract fun insertRows(kotlin/Int, kotlin/Int) // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.insertRows|insertRows(kotlin.Int;kotlin.Int){}[0]
abstract fun setContent(#A, #B?, app.cash.redwood/Modifier) // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.setContent|setContent(1:0;1:1?;app.cash.redwood.Modifier){}[0]
abstract fun setContent(#A, app.cash.redwood.widget/Widget<#B>?) // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.setContent|setContent(1:0;app.cash.redwood.widget.Widget<1:1>?){}[0]
final fun bind(kotlin/Int, #A): app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.Binding<#A, #B> // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.bind|bind(kotlin.Int;1:0){}[0]
final fun getOrCreateView(kotlin/Int, kotlin/Function1<app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.Binding<#A, #B>, #A>): #A // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.getOrCreateView|getOrCreateView(kotlin.Int;kotlin.Function1<app.cash.redwood.lazylayout.widget.LazyListUpdateProcessor.Binding<1:0,1:1>,1:0>){}[0]
final fun itemsAfter(kotlin/Int) // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.itemsAfter|itemsAfter(kotlin.Int){}[0]
Expand All @@ -71,7 +71,6 @@ abstract class <#A: kotlin/Any, #B: kotlin/Any> app.cash.redwood.lazylayout.widg
open fun createPlaceholder(#B): #B? // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.createPlaceholder|createPlaceholder(1:1){}[0]
open fun detach(#A) // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.detach|detach(1:0){}[0]
open fun detach() // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.detach|detach(){}[0]
open fun isSizeOnlyPlaceholder(#B): kotlin/Boolean // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.isSizeOnlyPlaceholder|isSizeOnlyPlaceholder(1:1){}[0]

final class <#A1: kotlin/Any, #B1: kotlin/Any> Binding { // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.Binding|null[0]
final val isBound // app.cash.redwood.lazylayout.widget/LazyListUpdateProcessor.Binding.isBound|{}isBound[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
get() = itemsBefore.nonNullElements + loadedItems + itemsAfter.nonNullElements

/** Pool of placeholder widgets. */
private val placeholdersQueue = ArrayDeque<W>()
private val placeholdersQueue = ArrayDeque<Widget<W>>()

/**
* The first placeholder ever returned. We use it to choose measured dimensions for created
* placeholders if the pool ever runs out.
*/
private var firstPlaceholder: W? = null
private var firstPlaceholder: Widget<W>? = null

/** Loaded items that may or may not have a view bound. */
private var loadedItems = mutableListOf<Binding<V, W>>()
Expand All @@ -68,8 +68,8 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {

override fun insert(index: Int, widget: Widget<W>) {
_widgets += widget
if (firstPlaceholder == null) firstPlaceholder = widget.value
placeholdersQueue += widget.value
if (firstPlaceholder == null) firstPlaceholder = widget
placeholdersQueue += widget
}

override fun move(fromIndex: Int, toIndex: Int, count: Int) {
Expand Down Expand Up @@ -244,7 +244,7 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
for (i in 0 until edit.widgets.size) {
val index = itemsBefore.size + edit.index + i
val binding = Binding(this).apply {
setContentAndModifier(edit.widgets[i])
setContent(edit.widgets[i])
}
loadedItems.add(edit.index + i, binding)

Expand Down Expand Up @@ -336,7 +336,7 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
// No binding for this index. Create one.
if (binding == null) {
return Binding(this).apply {
setContentAndModifier(loadedContent)
setContent(loadedContent)
}
}

Expand All @@ -346,7 +346,7 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
recyclePlaceholder(binding.content)
}
binding.isPlaceholder = false
binding.setContentAndModifier(loadedContent)
binding.setContent(loadedContent)
return binding
}

Expand All @@ -357,9 +357,9 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
if (!loaded.isBound) return null

// Show the placeholder in the bound view.
val placeholderContent = loaded.processor.takePlaceholder()
if (placeholderContent != null) {
loaded.setContentAndModifier(placeholderContent, Modifier)
val placeholder = loaded.processor.takePlaceholder()
if (placeholder != null) {
loaded.setContent(placeholder)
}
loaded.isPlaceholder = true
return loaded
Expand Down Expand Up @@ -396,7 +396,7 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
isPlaceholder = true,
).apply {
val placeholder = takePlaceholder() ?: return@apply // Detached.
setContentAndModifier(placeholder, Modifier)
setContent(placeholder)
}

return when {
Expand All @@ -406,17 +406,21 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
}
}

private fun takePlaceholder(): W? {
private fun takePlaceholder(): Widget<W>? {
val result = placeholdersQueue.removeFirstOrNull()
if (result != null) return result

val firstPlaceholder = this.firstPlaceholder ?: return null // Detached.
return createPlaceholder(firstPlaceholder)
val sizeOnlyPlaceholderValue = createPlaceholder(firstPlaceholder.value) ?: return null
return SizeOnlyPlaceholderWidget(
value = sizeOnlyPlaceholderValue,
modifier = firstPlaceholder.modifier,
)
}

private fun recyclePlaceholder(placeholder: W?) {
private fun recyclePlaceholder(placeholder: Widget<W>?) {
if (placeholder == null) return // Detached.
if (!isSizeOnlyPlaceholder(placeholder)) {
if (placeholder !is SizeOnlyPlaceholderWidget) {
placeholdersQueue += placeholder
}
}
Expand All @@ -429,13 +433,11 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
*/
protected open fun createPlaceholder(original: W): W? = null

protected open fun isSizeOnlyPlaceholder(placeholder: W): Boolean = false

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

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

protected abstract fun setContent(view: V, content: W?, modifier: Modifier)
protected abstract fun setContent(view: V, widget: Widget<W>?)

protected open fun detach(view: V) {
}
Expand Down Expand Up @@ -478,21 +480,14 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
* This may also be null if it should be a placeholder but the enclosing LazyList is detached.
* This could happen if the user scrolls the table after the view is detached.
*/
internal var content: W? = null
internal var content: Widget<W>? = null
private set

private var modifier: Modifier = Modifier

internal fun setContentAndModifier(widget: Widget<W>) {
setContentAndModifier(widget.value, widget.modifier)
}

internal fun setContentAndModifier(content: W, modifier: Modifier) {
internal fun setContent(content: Widget<W>) {
this.content = content
this.modifier = modifier

val view = this.view
if (view != null) processor.setContent(view, content, modifier)
if (view != null) processor.setContent(view, content)
}

public fun bind(view: V) {
Expand All @@ -503,14 +498,14 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
}

this.view = view
processor.setContent(view, content, modifier)
processor.setContent(view, content)
}

public fun unbind() {
val view = this.view ?: return

// Unbind the view.
processor.setContent(view, null, Modifier)
processor.setContent(view, null)
this.view = null

if (isPlaceholder) {
Expand Down Expand Up @@ -542,4 +537,9 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> {
class Move<W : Any>(val fromIndex: Int, val toIndex: Int, val count: Int) : Edit<W>()
class Remove<W : Any>(var index: Int, var count: Int) : Edit<W>()
}

private class SizeOnlyPlaceholderWidget<W : Any>(
override val value: W,
override var modifier: Modifier,
) : Widget<W>
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
*/
package app.cash.redwood.lazylayout.widget

import app.cash.redwood.Modifier
import app.cash.redwood.lazylayout.widget.FakeUpdateProcessor.StringCell
import app.cash.redwood.lazylayout.widget.FakeUpdateProcessor.StringContent
import app.cash.redwood.widget.Widget

/**
* This fake simulates a real scroll window, which is completely independent of the window of loaded
Expand Down Expand Up @@ -112,7 +112,9 @@ class FakeUpdateProcessor : LazyListUpdateProcessor<StringCell, StringContent>()
}
}

override fun setContent(view: StringCell, content: StringContent?, modifier: Modifier) {
override fun setContent(view: StringCell, widget: Widget<StringContent>?) {
val content = widget?.value

// It is an error for `content` to already have a parent cell.
val previous = view.content
require(content?.parentCell == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import app.cash.redwood.widget.ResizableWidget
import app.cash.redwood.widget.ResizableWidget.SizeListener
import kotlinx.cinterop.CValue
import kotlinx.cinterop.readValue
import platform.CoreGraphics.CGRectMake
import platform.CoreGraphics.CGRectZero
import platform.CoreGraphics.CGSize
import platform.CoreGraphics.CGSizeMake
Expand Down Expand Up @@ -84,35 +83,34 @@ class UIViewText :
}
}

class UIViewColor : Color<UIView> {
class UIViewColor :
Color<UIView>,
ResizableWidget<UIView> {
override var sizeListener: SizeListener? = null

override val value: UIView = object : UIView(CGRectZero.readValue()) {
override fun intrinsicContentSize(): CValue<CGSize> {
return CGSizeMake(width, height)
}
override fun intrinsicContentSize() = CGSizeMake(width, height)
override fun sizeThatFits(size: CValue<CGSize>) = CGSizeMake(width, height)
}

override var modifier: Modifier = Modifier

private var width = 0.0
private var height = 0.0

override fun width(width: Dp) {
this.width = with(Density.Default) { width.toPx() }
invalidate()
sizeListener?.invalidateSize()
}

override fun height(height: Dp) {
this.height = with(Density.Default) { height.toPx() }
invalidate()
sizeListener?.invalidateSize()
}

override fun color(color: Int) {
value.backgroundColor = color.toUIColor()
}

private fun invalidate() {
value.setFrame(CGRectMake(0.0, 0.0, width, height))
value.invalidateIntrinsicContentSize()
}
}

class UIViewSimpleColumn : SimpleColumn<UIView> {
Expand Down