diff --git a/app/src/main/kotlin/org/fdroid/ui/categories/CategoryChip.kt b/app/src/main/kotlin/org/fdroid/ui/categories/CategoryChip.kt index e657f23de..f0fb82048 100644 --- a/app/src/main/kotlin/org/fdroid/ui/categories/CategoryChip.kt +++ b/app/src/main/kotlin/org/fdroid/ui/categories/CategoryChip.kt @@ -2,7 +2,6 @@ package org.fdroid.ui.categories import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -23,8 +22,6 @@ import androidx.compose.ui.unit.dp import org.fdroid.R import org.fdroid.ui.FDroidContent -private val chipHeight = 36.dp - @Composable fun CategoryChip( categoryItem: CategoryItem, @@ -53,7 +50,7 @@ fun CategoryChip( ) }, selected = selected, - modifier = modifier.padding(horizontal = 4.dp).height(chipHeight) + modifier = modifier.height(chipHeight) ) } @@ -80,7 +77,7 @@ fun CategoryChip( overflow = TextOverflow.Ellipsis, ) }, - modifier = modifier.padding(horizontal = 4.dp).height(chipHeight) + modifier = modifier.height(chipHeight) ) } @@ -88,7 +85,10 @@ fun CategoryChip( @Composable fun CategoryCardPreview() { FDroidContent { - Column { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(8.dp) + ) { CategoryChip( CategoryItem("VPN & Proxy", "VPN & Proxy"), selected = true, @@ -106,34 +106,3 @@ fun CategoryCardPreview() { } } } - -/** - * More similar to how multiple category chips are shown in the main category list. - * Used to show spacing between items, specifically with regards to how Android specifies - * a minimum height for interactive elements. This leads to the conclusion that if we make - * the chips too short, we get an artificially large vertical gap. Hence why we set - * the height explicitly on category chips to make them align with this minimum height. - */ -@Preview -@Composable -fun CategoryCardFlowRowPreview() { - val categories = listOf( - CategoryItem("Cloud Storage & File Sync", "Cloud Storage & File Sync"), - CategoryItem("Connectivity", "Connectivity"), - CategoryItem("Development", "Development"), - CategoryItem("doesn't exist", "Foo bar"), - ) - - FDroidContent { - FlowRow( - horizontalArrangement = Arrangement.Start, - verticalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier - .padding(24.dp, 8.dp, 4.dp, 20.dp) - ) { - categories.map { category -> - CategoryChip(category, onClick = {}) - } - } - } -} diff --git a/app/src/main/kotlin/org/fdroid/ui/categories/CategoryList.kt b/app/src/main/kotlin/org/fdroid/ui/categories/CategoryList.kt index 36e7c2512..a1ab719a4 100644 --- a/app/src/main/kotlin/org/fdroid/ui/categories/CategoryList.kt +++ b/app/src/main/kotlin/org/fdroid/ui/categories/CategoryList.kt @@ -1,8 +1,6 @@ package org.fdroid.ui.categories -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme @@ -48,11 +46,9 @@ fun CategoryList( .fillMaxWidth() .padding(16.dp, 2.dp), ) - FlowRow( - horizontalArrangement = Arrangement.Start, - verticalArrangement = Arrangement.spacedBy(8.dp), + ChipFlowRow( modifier = Modifier - .padding(24.dp, 8.dp, 4.dp, 20.dp) + .padding(start = 16.dp, bottom = 12.dp) ) { categories.forEach { category -> CategoryChip( diff --git a/app/src/main/kotlin/org/fdroid/ui/categories/ChipFlowRow.kt b/app/src/main/kotlin/org/fdroid/ui/categories/ChipFlowRow.kt new file mode 100644 index 000000000..7ea9363c6 --- /dev/null +++ b/app/src/main/kotlin/org/fdroid/ui/categories/ChipFlowRow.kt @@ -0,0 +1,78 @@ +package org.fdroid.ui.categories + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.FlowRowScope +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.fdroid.ui.FDroidContent + +val chipHeight = 36.dp + +/** + * When presenting a list of chips (e.g. categories, repositories, sort criteria), + * use this to ensure appropriate spacing between elements. Make sure to set the + * height of your chips to [chipHeight] so that all chips match. + */ +@Composable +fun ChipFlowRow( + modifier: Modifier = Modifier, + content: @Composable FlowRowScope.() -> Unit, +) { + FlowRow( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier.padding(8.dp), + content = content, + ) +} + +@Preview +@Composable +fun ChipFlowRowFewItemsPreview() { + val categories = listOf( + CategoryItem("News", "News"), + CategoryItem("Note", "Note"), + CategoryItem("doesn't exist", "Oops"), + ) + + FDroidContent { + ChipFlowRow { + categories.map { category -> + CategoryChip(category, {}) + } + } + } +} + +@Preview +@Composable +fun ChipFlowRowManyItemsPreview() { + val categories = listOf( + CategoryItem("Cloud Storage & File Sync", "Cloud Storage & File Sync"), + CategoryItem("Connectivity", "Connectivity"), + CategoryItem("Development", "Development"), + CategoryItem("doesn't exist", "Foo bar"), + CategoryItem("Online Media Player", "Online Media Player"), + CategoryItem("Pass Wallet", "Pass Wallet"), + CategoryItem("Password & 2FA", "Password & 2FA"), + CategoryItem("Phone & SMS", "Phone & SMS"), + CategoryItem("Podcast", "Podcast"), + CategoryItem("Public Transport", "Public Transport"), + CategoryItem("Reading", "Reading"), + CategoryItem("Recipe Manager", "Recipe Manager"), + CategoryItem("Religion", "Religion"), + CategoryItem("Science & Education", "Science & Education"), + ) + + FDroidContent { + ChipFlowRow { + categories.map { category -> + CategoryChip(category, {}) + } + } + } +} diff --git a/app/src/main/kotlin/org/fdroid/ui/details/AppDetails.kt b/app/src/main/kotlin/org/fdroid/ui/details/AppDetails.kt index e87c9c2e5..9479d2c60 100644 --- a/app/src/main/kotlin/org/fdroid/ui/details/AppDetails.kt +++ b/app/src/main/kotlin/org/fdroid/ui/details/AppDetails.kt @@ -3,7 +3,6 @@ package org.fdroid.ui.details import android.util.Log import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -69,6 +68,7 @@ import org.fdroid.R import org.fdroid.install.InstallState import org.fdroid.ui.FDroidContent import org.fdroid.ui.categories.CategoryChip +import org.fdroid.ui.categories.ChipFlowRow import org.fdroid.ui.icons.License import org.fdroid.ui.icons.Litecoin import org.fdroid.ui.lists.AppListType @@ -378,7 +378,7 @@ fun AppDetails( modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), initiallyExpanded = true, ) { - FlowRow(modifier = Modifier.padding(start = 16.dp)) { + ChipFlowRow(modifier = Modifier.padding(start = 8.dp)) { item.categories.forEach { item -> CategoryChip(item, onClick = { val categoryNav = AppListType.Category(item.name, item.id) diff --git a/app/src/main/kotlin/org/fdroid/ui/lists/AppsFilter.kt b/app/src/main/kotlin/org/fdroid/ui/lists/AppsFilter.kt index a3fbde1d8..55664e0ce 100644 --- a/app/src/main/kotlin/org/fdroid/ui/lists/AppsFilter.kt +++ b/app/src/main/kotlin/org/fdroid/ui/lists/AppsFilter.kt @@ -2,10 +2,10 @@ package org.fdroid.ui.lists import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -40,7 +40,9 @@ import org.fdroid.R import org.fdroid.database.AppListSortOrder import org.fdroid.ui.FDroidContent import org.fdroid.ui.categories.CategoryChip +import org.fdroid.ui.categories.ChipFlowRow import org.fdroid.ui.categories.CategoryItem +import org.fdroid.ui.categories.chipHeight import org.fdroid.ui.icons.PackageVariant import org.fdroid.ui.utils.AsyncShimmerImage import org.fdroid.ui.utils.getAppListInfo @@ -58,15 +60,13 @@ fun AppsFilter( icon = Icons.AutoMirrored.Default.Sort, text = stringResource(R.string.sort_title), ) - FlowRow( - horizontalArrangement = spacedBy(8.dp), - modifier = modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), + ChipFlowRow( + modifier = Modifier.padding(start = 16.dp) ) { val byNameSelected = info.model.sortBy == AppListSortOrder.NAME FilterChip( selected = byNameSelected, + modifier = Modifier.height(chipHeight), leadingIcon = { if (byNameSelected) { Icon( @@ -87,6 +87,7 @@ fun AppsFilter( val byLatestSelected = info.model.sortBy == AppListSortOrder.LAST_UPDATED FilterChip( selected = byLatestSelected, + modifier = Modifier.height(chipHeight), leadingIcon = { if (byLatestSelected) { Icon( @@ -106,6 +107,7 @@ fun AppsFilter( ) FilterChip( selected = info.model.filterIncompatible, + modifier = Modifier.height(chipHeight), leadingIcon = { if (info.model.filterIncompatible) { Icon( @@ -141,7 +143,7 @@ fun AppsFilter( icon = Icons.Default.Category, text = stringResource(R.string.main_menu__categories), ) - FlowRow( + ChipFlowRow( modifier = modifier .fillMaxWidth() .padding(horizontal = 16.dp), @@ -158,14 +160,14 @@ fun AppsFilter( } } } + if (info.model.repositories.isNotEmpty()) { HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp)) FilterHeader( icon = PackageVariant, text = stringResource(R.string.app_details_repositories), ) - FlowRow( - horizontalArrangement = spacedBy(8.dp), + ChipFlowRow( modifier = modifier .fillMaxWidth() .padding(horizontal = 16.dp), @@ -174,6 +176,7 @@ fun AppsFilter( val selected = repo.repoId in info.model.filteredRepositoryIds FilterChip( selected = selected, + modifier = Modifier.height(chipHeight), leadingIcon = { if (selected) { Icon( diff --git a/app/src/main/kotlin/org/fdroid/ui/search/SearchResults.kt b/app/src/main/kotlin/org/fdroid/ui/search/SearchResults.kt index a86433c27..550998f59 100644 --- a/app/src/main/kotlin/org/fdroid/ui/search/SearchResults.kt +++ b/app/src/main/kotlin/org/fdroid/ui/search/SearchResults.kt @@ -2,7 +2,6 @@ package org.fdroid.ui.search import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -24,6 +23,7 @@ import androidx.compose.ui.unit.dp import org.fdroid.R import org.fdroid.ui.categories.CategoryChip import org.fdroid.ui.categories.CategoryItem +import org.fdroid.ui.categories.ChipFlowRow import org.fdroid.ui.lists.AppListItem import org.fdroid.ui.lists.AppListRow import org.fdroid.ui.lists.AppListType @@ -135,7 +135,7 @@ private fun CategoriesFlowRow(categories: List, onNav: (Navigation style = MaterialTheme.typography.labelLarge, modifier = Modifier.padding(8.dp) ) - FlowRow { + ChipFlowRow { categories.forEach { item -> CategoryChip(categoryItem = item, onClick = { val type = AppListType.Category(item.name, item.id) diff --git a/app/src/main/kotlin/org/fdroid/ui/utils/PreviewUtils.kt b/app/src/main/kotlin/org/fdroid/ui/utils/PreviewUtils.kt index 0a376f2f9..3735f89ac 100644 --- a/app/src/main/kotlin/org/fdroid/ui/utils/PreviewUtils.kt +++ b/app/src/main/kotlin/org/fdroid/ui/utils/PreviewUtils.kt @@ -167,6 +167,9 @@ val testApp = AppDetailsItem( categories = listOf( CategoryItem("Multimedia", "Multimedia"), CategoryItem("Internet", "Internet"), + CategoryItem("Cloud Storage & File Sync", "Cloud Storage & File Sync"), + CategoryItem("Connectivity", "Connectivity"), + CategoryItem("Development", "Development"), ), antiFeatures = listOf( AntiFeature(