From 05303f947df38a3a61029d66823e18054228bf55 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 3 Nov 2020 20:31:07 +0000 Subject: [PATCH] Add scrollbar to Todo example (#41) --- .../example/todo/common/main/ui/TodoMainUi.kt | 75 ++++++++++++++----- .../todo/common/utils/compose/Scrollbars.kt | 26 +++++++ .../todo/common/utils/compose/Scrollbars.kt | 23 ++++++ .../todo/common/utils/compose/Scrollbars.kt | 36 +++++++++ .../kotlin/example/todo/desktop/Main.kt | 18 +++-- 5 files changed, 152 insertions(+), 26 deletions(-) create mode 100644 examples/todoapp/common/utils/src/androidMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt create mode 100644 examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt create mode 100644 examples/todoapp/common/utils/src/desktopMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt diff --git a/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt b/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt index ef4ec6c464..62d693e27d 100644 --- a/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt +++ b/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt @@ -1,18 +1,20 @@ package example.todo.common.main.ui -import androidx.compose.foundation.Icon import androidx.compose.foundation.Text import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumnFor +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.Button import androidx.compose.material.Checkbox import androidx.compose.material.Divider +import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.OutlinedTextField import androidx.compose.material.TopAppBar @@ -32,6 +34,9 @@ import example.todo.common.main.TodoMain.Output import example.todo.common.main.store.TodoItem import example.todo.common.main.store.TodoMainStore.Intent import example.todo.common.main.store.TodoMainStore.State +import example.todo.common.utils.compose.MARGIN_SCROLLBAR +import example.todo.common.utils.compose.VerticalScrollbar +import example.todo.common.utils.compose.rememberScrollbarAdapter import example.todo.common.utils.onKeyUp @Composable @@ -67,33 +72,63 @@ private fun TodoList( onDoneChanged: (id: Long, isDone: Boolean) -> Unit, onDeleteItemClicked: (id: Long) -> Unit ) { - LazyColumnFor(items = items) { item -> - Row(modifier = Modifier.clickable(onClick = { onItemClicked(item.id) })) { - Spacer(modifier = Modifier.width(8.dp)) - - Checkbox( - checked = item.isDone, - modifier = Modifier.align(Alignment.CenterVertically), - onCheckedChange = { onDoneChanged(item.id, it) } + Box { + val listState = rememberLazyListState() + + LazyColumnFor(items = items, state = listState) { + Item( + item = it, + onItemClicked = onItemClicked, + onDoneChanged = onDoneChanged, + onDeleteItemClicked = onDeleteItemClicked ) - Spacer(modifier = Modifier.width(8.dp)) + Divider() + } - Text( - text = AnnotatedString(item.text), - modifier = Modifier.weight(1F).align(Alignment.CenterVertically), - maxLines = 1, - overflow = TextOverflow.Ellipsis + VerticalScrollbar( + modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(), + adapter = rememberScrollbarAdapter( + scrollState = listState, + itemCount = items.size, + averageItemSize = 37.dp ) + ) + } +} + +@Composable +private fun Item( + item: TodoItem, + onItemClicked: (id: Long) -> Unit, + onDoneChanged: (id: Long, isDone: Boolean) -> Unit, + onDeleteItemClicked: (id: Long) -> Unit +) { + Row(modifier = Modifier.clickable(onClick = { onItemClicked(item.id) })) { + Spacer(modifier = Modifier.width(8.dp)) + + Checkbox( + checked = item.isDone, + modifier = Modifier.align(Alignment.CenterVertically), + onCheckedChange = { onDoneChanged(item.id, it) } + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text( + text = AnnotatedString(item.text), + modifier = Modifier.weight(1F).align(Alignment.CenterVertically), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) - Spacer(modifier = Modifier.width(8.dp)) + Spacer(modifier = Modifier.width(8.dp)) - IconButton(onClick = { onDeleteItemClicked(item.id) }) { - Icon(Icons.Default.Delete) - } + IconButton(onClick = { onDeleteItemClicked(item.id) }) { + Icon(Icons.Default.Delete) } - Divider() + Spacer(modifier = Modifier.width(MARGIN_SCROLLBAR)) } } diff --git a/examples/todoapp/common/utils/src/androidMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt b/examples/todoapp/common/utils/src/androidMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt new file mode 100644 index 0000000000..65b8ae38e8 --- /dev/null +++ b/examples/todoapp/common/utils/src/androidMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt @@ -0,0 +1,26 @@ +package example.todo.common.utils.compose + +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +actual val MARGIN_SCROLLBAR: Dp = 0.dp + +actual interface ScrollbarAdapter + +@Composable +actual fun rememberScrollbarAdapter( + scrollState: LazyListState, + itemCount: Int, + averageItemSize: Dp +): ScrollbarAdapter = + object : ScrollbarAdapter {} + +@Composable +actual fun VerticalScrollbar( + modifier: Modifier, + adapter: ScrollbarAdapter +) { +} diff --git a/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt b/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt new file mode 100644 index 0000000000..f29d9c5f75 --- /dev/null +++ b/examples/todoapp/common/utils/src/commonMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt @@ -0,0 +1,23 @@ +package example.todo.common.utils.compose + +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp + +expect val MARGIN_SCROLLBAR: Dp + +expect interface ScrollbarAdapter + +@Composable +expect fun rememberScrollbarAdapter( + scrollState: LazyListState, + itemCount: Int, + averageItemSize: Dp +): ScrollbarAdapter + +@Composable +expect fun VerticalScrollbar( + modifier: Modifier, + adapter: ScrollbarAdapter +) diff --git a/examples/todoapp/common/utils/src/desktopMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt b/examples/todoapp/common/utils/src/desktopMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt new file mode 100644 index 0000000000..69f3d8da57 --- /dev/null +++ b/examples/todoapp/common/utils/src/desktopMain/kotlin/example/todo/common/utils/compose/Scrollbars.kt @@ -0,0 +1,36 @@ +package example.todo.common.utils.compose + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +actual val MARGIN_SCROLLBAR: Dp = 8.dp + +actual typealias ScrollbarAdapter = androidx.compose.foundation.ScrollbarAdapter + +@OptIn(ExperimentalFoundationApi::class) +@Composable +actual fun rememberScrollbarAdapter( + scrollState: LazyListState, + itemCount: Int, + averageItemSize: Dp +): ScrollbarAdapter = + androidx.compose.foundation.rememberScrollbarAdapter( + scrollState = scrollState, + itemCount = itemCount, + averageItemSize = averageItemSize + ) + +@Composable +actual fun VerticalScrollbar( + modifier: Modifier, + adapter: ScrollbarAdapter +) { + androidx.compose.foundation.VerticalScrollbar( + modifier = modifier, + adapter = adapter + ) +} diff --git a/examples/todoapp/desktop/src/jvmMain/kotlin/example/todo/desktop/Main.kt b/examples/todoapp/desktop/src/jvmMain/kotlin/example/todo/desktop/Main.kt index bd15920be6..33b60be65d 100644 --- a/examples/todoapp/desktop/src/jvmMain/kotlin/example/todo/desktop/Main.kt +++ b/examples/todoapp/desktop/src/jvmMain/kotlin/example/todo/desktop/Main.kt @@ -1,7 +1,9 @@ package example.todo.desktop import androidx.compose.desktop.AppWindow +import androidx.compose.desktop.DesktopTheme import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.ui.Modifier import com.arkivanov.decompose.DefaultComponentContext @@ -23,13 +25,17 @@ fun main() { AppWindow("Todo").show { Surface(modifier = Modifier.fillMaxSize()) { - TodoRoot( - componentContext = DefaultComponentContext(lifecycle), - dependencies = object : TodoRoot.Dependencies { - override val storeFactory = DefaultStoreFactory - override val database = TodoDatabase(TodoDatabaseDriver()) + MaterialTheme { + DesktopTheme { + TodoRoot( + componentContext = DefaultComponentContext(lifecycle), + dependencies = object : TodoRoot.Dependencies { + override val storeFactory = DefaultStoreFactory + override val database = TodoDatabase(TodoDatabaseDriver()) + } + ).invoke() } - ).invoke() + } } } }