diff --git a/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyDsl.kt b/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyDsl.kt index 4d9396b0d7..656a0fcbbe 100644 --- a/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyDsl.kt +++ b/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyDsl.kt @@ -18,6 +18,7 @@ package app.cash.redwood.lazylayout.compose import androidx.compose.runtime.Composable import app.cash.redwood.LayoutScopeMarker import app.cash.redwood.Modifier +import app.cash.redwood.layout.api.Constraint @LayoutScopeMarker public interface LazyListScope { @@ -70,12 +71,16 @@ public annotation class ExperimentalRedwoodLazyLayoutApi @Composable public fun LazyRow( + width: Constraint = Constraint.Wrap, + height: Constraint = Constraint.Wrap, modifier: Modifier = Modifier, placeholder: @Composable () -> Unit, content: LazyListScope.() -> Unit, ) { LazyList( isVertical = false, + width = width, + height = height, modifier = modifier, placeholder = placeholder, content = content, @@ -87,6 +92,8 @@ public fun LazyRow( public fun LazyRow( refreshing: Boolean, onRefresh: (() -> Unit)?, + width: Constraint = Constraint.Wrap, + height: Constraint = Constraint.Wrap, modifier: Modifier = Modifier, placeholder: @Composable () -> Unit, content: LazyListScope.() -> Unit, @@ -95,6 +102,8 @@ public fun LazyRow( isVertical = false, refreshing = refreshing, onRefresh = onRefresh, + width = width, + height = height, modifier = modifier, placeholder = placeholder, content = content, @@ -103,12 +112,16 @@ public fun LazyRow( @Composable public fun LazyColumn( + width: Constraint = Constraint.Wrap, + height: Constraint = Constraint.Wrap, modifier: Modifier = Modifier, placeholder: @Composable () -> Unit, content: LazyListScope.() -> Unit, ) { LazyList( isVertical = true, + width = width, + height = height, modifier = modifier, placeholder = placeholder, content = content, @@ -120,6 +133,8 @@ public fun LazyColumn( public fun LazyColumn( refreshing: Boolean, onRefresh: (() -> Unit)?, + width: Constraint = Constraint.Wrap, + height: Constraint = Constraint.Wrap, modifier: Modifier = Modifier, placeholder: @Composable () -> Unit, content: LazyListScope.() -> Unit, @@ -128,6 +143,8 @@ public fun LazyColumn( isVertical = true, refreshing = refreshing, onRefresh = onRefresh, + width = width, + height = height, modifier = modifier, placeholder = placeholder, content = content, diff --git a/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyList.kt b/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyList.kt index 2fb402d2a9..50624f8000 100644 --- a/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyList.kt +++ b/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyList.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import app.cash.redwood.Modifier +import app.cash.redwood.layout.api.Constraint import kotlin.jvm.JvmName private const val OffscreenItemsBufferCount = 30 @@ -31,6 +32,8 @@ private const val OffscreenItemsBufferCount = 30 @Composable internal fun LazyList( isVertical: Boolean, + width: Constraint, + height: Constraint, modifier: Modifier = Modifier, placeholder: @Composable () -> Unit, content: LazyListScope.() -> Unit, @@ -48,6 +51,8 @@ internal fun LazyList( firstVisibleItemIndex = localFirstVisibleItemIndex lastVisibleItemIndex = localLastVisibleItemIndex }, + width = width, + height = height, modifier = modifier, placeholder = { repeat(75) { placeholder() } }, items = { @@ -65,6 +70,8 @@ internal fun RefreshableLazyList( isVertical: Boolean, refreshing: Boolean = false, onRefresh: (() -> Unit)? = null, + width: Constraint, + height: Constraint, modifier: Modifier = Modifier, placeholder: @Composable () -> Unit, content: LazyListScope.() -> Unit, @@ -84,6 +91,8 @@ internal fun RefreshableLazyList( }, refreshing = refreshing, onRefresh = onRefresh, + width = width, + height = height, modifier = modifier, placeholder = { repeat(75) { placeholder() } }, items = { diff --git a/redwood-lazylayout-composeui/src/commonMain/kotlin/app/cash/redwood/lazylayout/composeui/ComposeUiLazyList.kt b/redwood-lazylayout-composeui/src/commonMain/kotlin/app/cash/redwood/lazylayout/composeui/ComposeUiLazyList.kt index 6d03d2bfd0..1d4d3dcdf2 100644 --- a/redwood-lazylayout-composeui/src/commonMain/kotlin/app/cash/redwood/lazylayout/composeui/ComposeUiLazyList.kt +++ b/redwood-lazylayout-composeui/src/commonMain/kotlin/app/cash/redwood/lazylayout/composeui/ComposeUiLazyList.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import app.cash.redwood.Modifier as RedwoodModifier +import app.cash.redwood.layout.api.Constraint import app.cash.redwood.lazylayout.widget.LazyList import app.cash.redwood.lazylayout.widget.RefreshableLazyList import app.cash.redwood.widget.compose.ComposeWidgetChildren @@ -51,6 +52,8 @@ internal class ComposeUiLazyList : private var itemsAfter by mutableStateOf(0) private var isRefreshing by mutableStateOf(false) private var onRefresh: (() -> Unit)? by mutableStateOf(null) + private var width by mutableStateOf(Constraint.Wrap) + private var height by mutableStateOf(Constraint.Wrap) override var modifier: RedwoodModifier = RedwoodModifier @@ -82,6 +85,14 @@ internal class ComposeUiLazyList : this.onRefresh = onRefresh } + override fun width(width: Constraint) { + this.width = width + } + + override fun height(height: Constraint) { + this.height = height + } + override val value = @Composable { val content: LazyListScope.() -> Unit = { items(items.widgets) { item -> @@ -117,20 +128,20 @@ internal class ComposeUiLazyList : } } + val modifier = Modifier + .run { if (width == Constraint.Fill) fillMaxWidth() else this } + .run { if (height == Constraint.Fill) fillMaxHeight() else this } + .pullRefresh(state = refreshState, enabled = onRefresh != null) if (isVertical) { LazyColumn( - modifier = Modifier - .fillMaxWidth() - .pullRefresh(state = refreshState, enabled = onRefresh != null), + modifier = modifier, state = state, horizontalAlignment = Alignment.CenterHorizontally, content = content, ) } else { LazyRow( - modifier = Modifier - .fillMaxHeight() - .pullRefresh(state = refreshState, enabled = onRefresh != null), + modifier = modifier, state = state, verticalAlignment = Alignment.CenterVertically, content = content, diff --git a/redwood-lazylayout-schema/build.gradle b/redwood-lazylayout-schema/build.gradle index c9555f15eb..b358da9784 100644 --- a/redwood-lazylayout-schema/build.gradle +++ b/redwood-lazylayout-schema/build.gradle @@ -6,6 +6,10 @@ redwoodBuild { publishing() } +dependencies { + api projects.redwoodLayoutApi +} + redwoodSchema { type = 'app.cash.redwood.lazylayout.RedwoodLazyLayout' } diff --git a/redwood-lazylayout-schema/src/main/kotlin/app/cash/redwood/lazylayout/widgets.kt b/redwood-lazylayout-schema/src/main/kotlin/app/cash/redwood/lazylayout/widgets.kt index 1800b0a91b..77a31ebd8e 100644 --- a/redwood-lazylayout-schema/src/main/kotlin/app/cash/redwood/lazylayout/widgets.kt +++ b/redwood-lazylayout-schema/src/main/kotlin/app/cash/redwood/lazylayout/widgets.kt @@ -15,6 +15,7 @@ */ package app.cash.redwood.lazylayout +import app.cash.redwood.layout.api.Constraint import app.cash.redwood.schema.Children import app.cash.redwood.schema.Property import app.cash.redwood.schema.Widget @@ -25,6 +26,8 @@ public data class LazyList( @Property(2) val onViewportChanged: (firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) -> Unit, @Property(3) val itemsBefore: Int, @Property(4) val itemsAfter: Int, + @Property(5) val width: Constraint, + @Property(6) val height: Constraint, @Children(1) val placeholder: () -> Unit, @Children(2) val items: () -> Unit, ) @@ -37,6 +40,8 @@ public data class RefreshableLazyList( @Property(4) val itemsAfter: Int, @Property(5) val refreshing: Boolean, @Property(6) val onRefresh: (() -> Unit)?, + @Property(7) val width: Constraint, + @Property(8) val height: Constraint, @Children(1) val placeholder: () -> Unit, @Children(2) val items: () -> Unit, ) diff --git a/redwood-lazylayout-uiview/src/commonMain/kotlin/app/cash/redwood/lazylayout/uiview/UIViewLazyList.kt b/redwood-lazylayout-uiview/src/commonMain/kotlin/app/cash/redwood/lazylayout/uiview/UIViewLazyList.kt index e5d8ed2588..8cc0d39244 100644 --- a/redwood-lazylayout-uiview/src/commonMain/kotlin/app/cash/redwood/lazylayout/uiview/UIViewLazyList.kt +++ b/redwood-lazylayout-uiview/src/commonMain/kotlin/app/cash/redwood/lazylayout/uiview/UIViewLazyList.kt @@ -21,6 +21,7 @@ package app.cash.redwood.lazylayout.uiview import app.cash.redwood.Modifier +import app.cash.redwood.layout.api.Constraint import app.cash.redwood.lazylayout.widget.LazyList import app.cash.redwood.lazylayout.widget.RefreshableLazyList import app.cash.redwood.widget.MutableListChildren @@ -142,6 +143,13 @@ internal open class UIViewLazyListImpl() : LazyList { this.itemsAfter = itemsAfter } + // TODO Dynamically update width and height of UIViewLazyList when set + override fun width(width: Constraint) { + } + + override fun height(height: Constraint) { + } + override var modifier: Modifier = Modifier override val value: UIView get() = tableView diff --git a/redwood-lazylayout-view/src/main/kotlin/app/cash/redwood/lazylayout/view/ViewLazyList.kt b/redwood-lazylayout-view/src/main/kotlin/app/cash/redwood/lazylayout/view/ViewLazyList.kt index bc6b51ce09..1eb159cfb6 100644 --- a/redwood-lazylayout-view/src/main/kotlin/app/cash/redwood/lazylayout/view/ViewLazyList.kt +++ b/redwood-lazylayout-view/src/main/kotlin/app/cash/redwood/lazylayout/view/ViewLazyList.kt @@ -24,10 +24,12 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout import androidx.core.view.doOnDetach +import androidx.core.view.updateLayoutParams import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import app.cash.redwood.Modifier +import app.cash.redwood.layout.api.Constraint import app.cash.redwood.lazylayout.widget.LazyList import app.cash.redwood.lazylayout.widget.RefreshableLazyList import app.cash.redwood.widget.MutableListChildren @@ -123,11 +125,8 @@ internal open class ViewLazyListImpl( init { adapter.items = items recyclerView.apply { - setHasFixedSize(true) layoutManager = linearLayoutManager - - // TODO: sizing should be controlled by Modifiers - layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) + layoutParams = ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) addOnScrollListener( object : RecyclerView.OnScrollListener() { @@ -144,6 +143,18 @@ internal open class ViewLazyListImpl( recyclerView.adapter = adapter } + override fun width(width: Constraint) { + recyclerView.updateLayoutParams { + this.width = if (width == Constraint.Fill) MATCH_PARENT else WRAP_CONTENT + } + } + + override fun height(height: Constraint) { + recyclerView.updateLayoutParams { + this.height = if (height == Constraint.Fill) MATCH_PARENT else WRAP_CONTENT + } + } + override fun isVertical(isVertical: Boolean) { linearLayoutManager.orientation = if (isVertical) RecyclerView.VERTICAL else RecyclerView.HORIZONTAL } @@ -241,6 +252,7 @@ internal class RefreshableViewLazyListImpl( init { swipeRefreshLayout.apply { addView(recyclerView) + // TODO Dynamically update width and height of RefreshableViewLazyList when set layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) } } diff --git a/redwood-lazylayout-widget/build.gradle b/redwood-lazylayout-widget/build.gradle index c9249faaac..ca0d228fe7 100644 --- a/redwood-lazylayout-widget/build.gradle +++ b/redwood-lazylayout-widget/build.gradle @@ -10,6 +10,14 @@ redwoodBuild { kotlin { KmpTargets.addAllTargets(project) + + sourceSets { + commonMain { + dependencies { + api projects.redwoodLayoutModifiers + } + } + } } redwoodSchema { diff --git a/samples/emoji-search/browser/src/main/kotlin/com/example/redwood/emojisearch/browser/main.kt b/samples/emoji-search/browser/src/main/kotlin/com/example/redwood/emojisearch/browser/main.kt index 4a693d41c1..95a31557b2 100644 --- a/samples/emoji-search/browser/src/main/kotlin/com/example/redwood/emojisearch/browser/main.kt +++ b/samples/emoji-search/browser/src/main/kotlin/com/example/redwood/emojisearch/browser/main.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.Composable import app.cash.redwood.Modifier import app.cash.redwood.compose.RedwoodComposition import app.cash.redwood.compose.WindowAnimationFrameClock +import app.cash.redwood.layout.api.Constraint import app.cash.redwood.layout.compose.Column import app.cash.redwood.layout.dom.HTMLElementRedwoodLayoutWidgetFactory import app.cash.redwood.lazylayout.widget.RedwoodLazyLayoutWidgetFactory @@ -82,11 +83,17 @@ private object TruncatingColumnProvider : ColumnProvider { items: List, refreshing: Boolean, onRefresh: (() -> Unit)?, + width: Constraint, + height: Constraint, modifier: Modifier, placeholder: @Composable () -> Unit, itemContent: @Composable (item: T) -> Unit, ) { - Column(modifier = modifier) { + Column( + width = width, + height = height, + modifier = modifier, + ) { for (item in items.take(25)) { itemContent(item) } diff --git a/samples/emoji-search/presenter/src/commonMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearch.kt b/samples/emoji-search/presenter/src/commonMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearch.kt index 5b0e5b78dd..730c79eac5 100644 --- a/samples/emoji-search/presenter/src/commonMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearch.kt +++ b/samples/emoji-search/presenter/src/commonMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearch.kt @@ -58,6 +58,8 @@ interface ColumnProvider { items: List, refreshing: Boolean, onRefresh: (() -> Unit)?, + width: Constraint, + height: Constraint, modifier: Modifier, placeholder: @Composable () -> Unit, itemContent: @Composable (item: T) -> Unit, @@ -113,6 +115,8 @@ fun EmojiSearch( items = filteredEmojis, refreshing = refreshing, onRefresh = { refreshSignal++ }, + width = Constraint.Fill, + height = Constraint.Wrap, modifier = Modifier.grow(1.0), placeholder = { Item( diff --git a/samples/emoji-search/presenter/src/jsMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearchTreehouseUi.kt b/samples/emoji-search/presenter/src/jsMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearchTreehouseUi.kt index 833dd3ee32..fc32e00c8c 100644 --- a/samples/emoji-search/presenter/src/jsMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearchTreehouseUi.kt +++ b/samples/emoji-search/presenter/src/jsMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearchTreehouseUi.kt @@ -17,6 +17,7 @@ package com.example.redwood.emojisearch.presenter import androidx.compose.runtime.Composable import app.cash.redwood.Modifier +import app.cash.redwood.layout.api.Constraint import app.cash.redwood.lazylayout.compose.ExperimentalRedwoodLazyLayoutApi import app.cash.redwood.treehouse.TreehouseUi import app.cash.redwood.lazylayout.compose.LazyColumn @@ -40,6 +41,8 @@ private class LazyColumnProvider : ColumnProvider { items: List, refreshing: Boolean, onRefresh: (() -> Unit)?, + width: Constraint, + height: Constraint, modifier: Modifier, placeholder: @Composable () -> Unit, itemContent: @Composable (item: T) -> Unit, @@ -47,6 +50,8 @@ private class LazyColumnProvider : ColumnProvider { LazyColumn( refreshing = refreshing, onRefresh = onRefresh, + width = width, + height = height, modifier = modifier, placeholder = placeholder, ) { diff --git a/samples/repo-search/presenter/src/commonMain/kotlin/com/example/redwood/reposearch/presenter/RepoSearchTreehouseUi.kt b/samples/repo-search/presenter/src/commonMain/kotlin/com/example/redwood/reposearch/presenter/RepoSearchTreehouseUi.kt index bb6fe6345b..08d047e9a6 100644 --- a/samples/repo-search/presenter/src/commonMain/kotlin/com/example/redwood/reposearch/presenter/RepoSearchTreehouseUi.kt +++ b/samples/repo-search/presenter/src/commonMain/kotlin/com/example/redwood/reposearch/presenter/RepoSearchTreehouseUi.kt @@ -24,6 +24,7 @@ import app.cash.paging.PagingSourceLoadResult import app.cash.paging.PagingSourceLoadResultPage import app.cash.paging.PagingState import app.cash.paging.compose.collectAsLazyPagingItems +import app.cash.redwood.layout.api.Constraint import app.cash.redwood.lazylayout.compose.LazyColumn import app.cash.redwood.treehouse.TreehouseUi import kotlinx.serialization.json.Json @@ -46,7 +47,10 @@ class RepoSearchTreehouseUi( @Composable override fun Show() { val lazyPagingItems = pager.flow.collectAsLazyPagingItems() - LazyColumn(placeholder = { RepoSearch(Repository(fullName = "Placeholder…", 0)) }) { + LazyColumn( + width = Constraint.Fill, + placeholder = { RepoSearch(Repository(fullName = "Placeholder…", 0)) }, + ) { items(lazyPagingItems.itemCount) { index -> RepoSearch(lazyPagingItems[index]!!) }