Use lazy list for search results

This commit is contained in:
MM20 2022-08-22 20:55:49 +02:00
parent 74d02f9b4a
commit f3d8d95d78
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
7 changed files with 320 additions and 73 deletions

View File

@ -5,6 +5,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import de.mm20.launcher2.ui.locals.LocalCardStyle import de.mm20.launcher2.ui.locals.LocalCardStyle
@ -14,11 +15,12 @@ fun LauncherCard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
elevation: Dp = 2.dp, elevation: Dp = 2.dp,
backgroundOpacity: Float = LocalCardStyle.current.opacity, backgroundOpacity: Float = LocalCardStyle.current.opacity,
shape: Shape = MaterialTheme.shapes.medium,
content: @Composable () -> Unit = {} content: @Composable () -> Unit = {}
) { ) {
Surface( Surface(
modifier = modifier, modifier = modifier,
shape = MaterialTheme.shapes.medium, shape = shape,
border = LocalCardStyle.current.borderWidth.takeIf { it > 0 } border = LocalCardStyle.current.borderWidth.takeIf { it > 0 }
?.let { BorderStroke(it.dp, MaterialTheme.colorScheme.surface) }, ?.let { BorderStroke(it.dp, MaterialTheme.colorScheme.surface) },
content = content, content = content,

View File

@ -1,17 +1,17 @@
package de.mm20.launcher2.ui.launcher package de.mm20.launcher2.ui.launcher
import android.util.Log
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.slideIn import androidx.compose.animation.slideIn
import androidx.compose.animation.slideOut import androidx.compose.animation.slideOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FractionalThreshold import androidx.compose.material.FractionalThreshold
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Done import androidx.compose.material.icons.rounded.Done
@ -61,13 +61,26 @@ fun PagerScaffold(
val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false) val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false)
val widgetsScrollState = rememberScrollState() val widgetsScrollState = rememberScrollState()
val searchScrollState = rememberScrollState() val searchState = rememberLazyListState()
val swipeableState = rememberSwipeableState(if (isSearchOpen) Page.Search else Page.Widgets) val swipeableState = rememberSwipeableState(if (isSearchOpen) Page.Search else Page.Widgets)
val isSearchAtStart by remember {
derivedStateOf {
searchState.firstVisibleItemIndex == 0 && searchState.firstVisibleItemScrollOffset == 0
}
}
val isSearchAtEnd by remember {
derivedStateOf {
val lastItem = searchState.layoutInfo.visibleItemsInfo.last()
lastItem.offset + lastItem.size <= searchState.layoutInfo.viewportEndOffset - searchState.layoutInfo.afterContentPadding
}
}
val showStatusBarScrim by remember { val showStatusBarScrim by remember {
derivedStateOf { derivedStateOf {
if (isSearchOpen) { if (isSearchOpen) {
searchScrollState.value < searchScrollState.maxValue !isSearchAtEnd
} else { } else {
widgetsScrollState.value > 0 widgetsScrollState.value > 0
} }
@ -76,7 +89,7 @@ fun PagerScaffold(
val showNavBarScrim by remember { val showNavBarScrim by remember {
derivedStateOf { derivedStateOf {
if (isSearchOpen) { if (isSearchOpen) {
searchScrollState.value > 0 !isSearchAtStart
} else { } else {
widgetsScrollState.value > 0 && widgetsScrollState.value < widgetsScrollState.maxValue widgetsScrollState.value > 0 && widgetsScrollState.value < widgetsScrollState.maxValue
} }
@ -285,17 +298,21 @@ fun PagerScaffold(
val webSearchPadding by animateDpAsState( val webSearchPadding by animateDpAsState(
if (websearches.isEmpty()) 0.dp else 48.dp if (websearches.isEmpty()) 0.dp else 48.dp
) )
val windowInsets = WindowInsets.safeDrawing.asPaddingValues()
SearchColumn( SearchColumn(
modifier = Modifier modifier = Modifier
.requiredWidth(width) .requiredWidth(width)
.fillMaxHeight() .fillMaxHeight()
.verticalScroll(searchScrollState, reverseScrolling = true) .padding(
.imePadding() start = windowInsets.calculateStartPadding(LocalLayoutDirection.current),
.windowInsetsPadding(WindowInsets.safeDrawing) end = windowInsets.calculateStartPadding(LocalLayoutDirection.current),
.padding(horizontal = 8.dp) ),
.padding(top = 8.dp, bottom = 64.dp)
.padding(bottom = webSearchPadding),
reverse = true, reverse = true,
state = searchState,
paddingValues = PaddingValues(
top = 4.dp + windowInsets.calculateTopPadding(),
bottom = 60.dp + webSearchPadding + windowInsets.calculateBottomPadding()
)
) )
} }
} }
@ -306,6 +323,7 @@ fun PagerScaffold(
exit = slideOut { IntOffset(0, -it.height) } exit = slideOut { IntOffset(0, -it.height) }
) { ) {
CenterAlignedTopAppBar( CenterAlignedTopAppBar(
modifier = Modifier.systemBarsPadding(),
title = { title = {
Text(stringResource(R.string.menu_edit_widgets)) Text(stringResource(R.string.menu_edit_widgets))
}, },
@ -322,7 +340,7 @@ fun PagerScaffold(
when { when {
swipeableState.direction != 0f -> SearchBarLevel.Raised swipeableState.direction != 0f -> SearchBarLevel.Raised
!isSearchOpen && isWidgetsScrollZero -> SearchBarLevel.Resting !isSearchOpen && isWidgetsScrollZero -> SearchBarLevel.Resting
isSearchOpen && searchScrollState.value == 0 -> SearchBarLevel.Active isSearchOpen && isSearchAtStart -> SearchBarLevel.Active
else -> SearchBarLevel.Raised else -> SearchBarLevel.Raised
} }
} }

View File

@ -6,9 +6,9 @@ import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.slideIn import androidx.compose.animation.slideIn
import androidx.compose.animation.slideOut import androidx.compose.animation.slideOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -25,8 +25,8 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.Velocity
@ -41,7 +41,6 @@ import de.mm20.launcher2.ui.launcher.search.SearchBarLevel
import de.mm20.launcher2.ui.launcher.search.SearchColumn import de.mm20.launcher2.ui.launcher.search.SearchColumn
import de.mm20.launcher2.ui.launcher.search.SearchVM import de.mm20.launcher2.ui.launcher.search.SearchVM
import de.mm20.launcher2.ui.launcher.widgets.WidgetColumn import de.mm20.launcher2.ui.launcher.widgets.WidgetColumn
import de.mm20.launcher2.ui.modifier.verticalFadingEdges
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -60,20 +59,39 @@ fun PullDownScaffold(
val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false) val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false)
val widgetsScrollState = rememberScrollState() val widgetsScrollState = rememberScrollState()
val searchScrollState = rememberScrollState() val searchState = rememberLazyListState()
val isSearchAtStart by remember {
derivedStateOf {
searchState.firstVisibleItemIndex == 0 && searchState.firstVisibleItemScrollOffset == 0
}
}
val isSearchAtEnd by remember {
derivedStateOf {
val lastItem = searchState.layoutInfo.visibleItemsInfo.last()
lastItem.offset + lastItem.size <= searchState.layoutInfo.viewportEndOffset - searchState.layoutInfo.afterContentPadding
}
}
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
val isWidgetsScrollZero by remember { val isWidgetsAtStart by remember {
derivedStateOf { derivedStateOf {
widgetsScrollState.value == 0 widgetsScrollState.value == 0
} }
} }
val isWidgetsAtEnd by remember {
derivedStateOf {
widgetsScrollState.value >= widgetsScrollState.maxValue
}
}
val showStatusBarScrim by remember { val showStatusBarScrim by remember {
derivedStateOf { derivedStateOf {
if (isSearchOpen) { if (isSearchOpen) {
searchScrollState.value > 0 !isSearchAtStart
} else { } else {
widgetsScrollState.value > 0 widgetsScrollState.value > 0
} }
@ -82,7 +100,7 @@ fun PullDownScaffold(
val showNavBarScrim by remember { val showNavBarScrim by remember {
derivedStateOf { derivedStateOf {
if (isSearchOpen) { if (isSearchOpen) {
searchScrollState.value < searchScrollState.maxValue !isSearchAtEnd
} else { } else {
widgetsScrollState.value > 0 && widgetsScrollState.value < widgetsScrollState.maxValue widgetsScrollState.value > 0 && widgetsScrollState.value < widgetsScrollState.maxValue
} }
@ -146,7 +164,7 @@ fun PullDownScaffold(
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
LaunchedEffect(isSearchOpen) { LaunchedEffect(isSearchOpen) {
if (isSearchOpen) searchScrollState.scrollTo(0) if (isSearchOpen) searchState.scrollToItem(0)
if (!isSearchOpen) searchVM.search("") if (!isSearchOpen) searchVM.search("")
searchBarOffset.animateTo(0f) searchBarOffset.animateTo(0f)
} }
@ -176,22 +194,25 @@ fun PullDownScaffold(
object : NestedScrollConnection { object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (isWidgetEditMode) return Offset.Zero if (isWidgetEditMode) return Offset.Zero
val value = if (isSearchOpen) searchScrollState.value else widgetsScrollState.value val canPullDown = if (isSearchOpen) {
val newValue = value - available.y isSearchAtStart
} else {
isWidgetsAtStart
}
val canPullUp = isSearchOpen && isSearchAtEnd
val consumed = when { val consumed = when {
(offsetY.value > 0 || source == NestedScrollSource.Drag && newValue < 0) -> { canPullUp && available.y < 0 || offsetY.value < 0 -> {
val consumed = available.y - value val consumed = available.y
offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(0f, maxOffset)
consumed
}
isSearchOpen && (offsetY.value < 0 || source == NestedScrollSource.Drag && newValue > searchScrollState.maxValue) -> {
val consumed = available.y - (value - searchScrollState.maxValue)
offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(-maxOffset, 0f) offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(-maxOffset, 0f)
consumed consumed
} }
else -> { canPullDown && available.y > 0 || offsetY.value > 0 -> {
0f val consumed = available.y
offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(0f, maxOffset)
consumed
} }
else -> 0f
} }
searchBarOffset.value = searchBarOffset.value =
@ -246,11 +267,11 @@ fun PullDownScaffold(
) )
} }
) { ) {
val websearches by searchVM.websearchResults.observeAsState(emptyList()) val websearches by searchVM.websearchResults.observeAsState(emptyList())
val webSearchPadding by animateDpAsState( val webSearchPadding by animateDpAsState(
if (websearches.isEmpty()) 0.dp else 48.dp if (websearches.isEmpty()) 0.dp else 48.dp
) )
val windowInsets = WindowInsets.safeDrawing.asPaddingValues()
SearchColumn( SearchColumn(
modifier = Modifier modifier = Modifier
.graphicsLayer { .graphicsLayer {
@ -261,16 +282,20 @@ fun PullDownScaffold(
} }
.fillMaxWidth() .fillMaxWidth()
.requiredHeight(height) .requiredHeight(height)
.verticalScroll(searchScrollState) .padding(
.windowInsetsPadding(WindowInsets.safeDrawing) start = windowInsets.calculateStartPadding(LocalLayoutDirection.current),
.padding(8.dp) end = windowInsets.calculateStartPadding(LocalLayoutDirection.current),
.padding(top = 56.dp) ),
.padding(top = webSearchPadding) paddingValues = PaddingValues(
.imePadding() top = 60.dp + webSearchPadding + windowInsets.calculateTopPadding(),
bottom = 4.dp + windowInsets.calculateBottomPadding()
),
state = searchState,
) )
val editModePadding by animateDpAsState(if (isWidgetEditMode) 56.dp else 0.dp) val editModePadding by animateDpAsState(if (isWidgetEditMode) 56.dp else 0.dp)
val clockPadding by animateDpAsState( val clockPadding by animateDpAsState(
if (isWidgetsScrollZero) insets.calculateBottomPadding() else 0.dp if (isWidgetsAtStart) insets.calculateBottomPadding() else 0.dp
) )
val clockHeight by remember { val clockHeight by remember {
derivedStateOf { derivedStateOf {
@ -328,9 +353,9 @@ fun PullDownScaffold(
derivedStateOf { derivedStateOf {
when { when {
offsetY.value != 0f -> SearchBarLevel.Raised offsetY.value != 0f -> SearchBarLevel.Raised
isSearchOpen && searchScrollState.value == 0 -> SearchBarLevel.Active isSearchOpen && isSearchAtStart -> SearchBarLevel.Active
isSearchOpen && searchScrollState.value > 0 -> SearchBarLevel.Raised isSearchOpen && !isSearchAtStart -> SearchBarLevel.Raised
!isWidgetsScrollZero -> SearchBarLevel.Raised !isWidgetsAtStart -> SearchBarLevel.Raised
else -> SearchBarLevel.Resting else -> SearchBarLevel.Resting
} }
} }

View File

@ -1,41 +1,243 @@
package de.mm20.launcher2.ui.launcher.search package de.mm20.launcher2.ui.launcher.search
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import de.mm20.launcher2.ui.launcher.search.apps.AppResults import androidx.compose.ui.draw.clipToBounds
import de.mm20.launcher2.ui.launcher.search.appshortcuts.AppShortcutResults import androidx.compose.ui.graphics.RectangleShape
import de.mm20.launcher2.ui.launcher.search.calculator.CalculatorResults import androidx.compose.ui.unit.dp
import de.mm20.launcher2.ui.launcher.search.calendar.CalendarResults import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.launcher.search.contacts.ContactResults import de.mm20.launcher2.search.data.Searchable
import de.mm20.launcher2.ui.launcher.search.favorites.FavoritesResults import de.mm20.launcher2.ui.component.LauncherCard
import de.mm20.launcher2.ui.launcher.search.files.FileResults import de.mm20.launcher2.ui.launcher.search.calculator.CalculatorItem
import de.mm20.launcher2.ui.launcher.search.common.grid.GridItem
import de.mm20.launcher2.ui.launcher.search.common.list.ListItem
import de.mm20.launcher2.ui.launcher.search.hidden.HiddenResults import de.mm20.launcher2.ui.launcher.search.hidden.HiddenResults
import de.mm20.launcher2.ui.launcher.search.unitconverter.UnitConverterResults import de.mm20.launcher2.ui.launcher.search.unitconverter.UnitConverterItem
import de.mm20.launcher2.ui.launcher.search.website.WebsiteResults import de.mm20.launcher2.ui.launcher.search.website.WebsiteItem
import de.mm20.launcher2.ui.launcher.search.wikipedia.WikipediaResults import de.mm20.launcher2.ui.launcher.search.wikipedia.WikipediaItem
import de.mm20.launcher2.ui.layout.BottomReversed import de.mm20.launcher2.ui.locals.LocalGridColumns
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlin.math.ceil
@Composable @Composable
fun SearchColumn( fun SearchColumn(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
paddingValues: PaddingValues = PaddingValues(0.dp),
state: LazyListState = rememberLazyListState(),
reverse: Boolean = false, reverse: Boolean = false,
) { ) {
Column(
val columns = LocalGridColumns.current
val viewModel: SearchVM = viewModel()
val hideFavs by viewModel.hideFavorites.observeAsState(true)
val favorites by viewModel.favorites.observeAsState(emptyList())
val apps by viewModel.appResults.observeAsState(emptyList())
val appShortcuts by viewModel.appShortcutResults.observeAsState(emptyList())
val contacts by viewModel.contactResults.observeAsState(emptyList())
val files by viewModel.fileResults.observeAsState(emptyList())
val events by viewModel.calendarResults.observeAsState(emptyList())
val unitConverter by viewModel.unitConverterResult.observeAsState(null)
val calculator by viewModel.calculatorResult.observeAsState(null)
val wikipedia by viewModel.wikipediaResult.observeAsState(null)
val website by viewModel.websiteResult.observeAsState(null)
LazyColumn(
state = state,
modifier = modifier, modifier = modifier,
verticalArrangement = if (reverse) Arrangement.BottomReversed else Arrangement.Top contentPadding = paddingValues,
reverseLayout = reverse,
) { ) {
FavoritesResults(reverse) if (!hideFavs) {
AppResults(reverse) GridResults(favorites.toImmutableList(), columns, reverse)
AppShortcutResults(reverse) }
UnitConverterResults(reverse) GridResults(apps.toImmutableList(), columns, reverse)
CalculatorResults(reverse) ListResults(appShortcuts.toImmutableList(), reverse)
CalendarResults(reverse) val uc = unitConverter
ContactResults(reverse) if (uc != null) {
WikipediaResults(reverse) SingleResult {
WebsiteResults(reverse) UnitConverterItem(unitConverter = uc)
FileResults(reverse) }
HiddenResults() }
val calc = calculator
if (calc != null) {
SingleResult {
CalculatorItem(calculator = calc)
}
}
ListResults(events.toImmutableList(), reverse)
ListResults(contacts.toImmutableList(), reverse)
val wiki = wikipedia
if (wiki != null) {
SingleResult {
WikipediaItem(wikipedia = wiki)
}
}
val ws = website
if (ws != null) {
SingleResult {
WebsiteItem(website = ws)
}
}
ListResults(files.toImmutableList(), reverse)
item {
HiddenResults()
}
}
}
fun LazyListScope.GridResults(
items: ImmutableList<Searchable>,
columns: Int,
reverse: Boolean,
) {
if (items.isEmpty()) return
val rows = ceil(items.size / columns.toFloat()).toInt()
items(rows) {
GridRow(
items = items.subList(it * columns, (it * columns + columns).coerceAtMost(items.size)),
columns = columns,
isFirst = if (reverse) it == rows - 1 else it == 0,
isLast = if (reverse) it == 0 else it == rows - 1
)
}
}
@Composable
fun GridRow(
modifier: Modifier = Modifier,
items: ImmutableList<Searchable>,
columns: Int,
isFirst: Boolean,
isLast: Boolean,
) {
Box(
modifier = modifier
.clipToBounds()
) {
LauncherCard(
modifier = Modifier.padding(
start = 8.dp,
end = 8.dp,
top = if (isFirst) 4.dp else 0.dp,
bottom = if (isLast) 4.dp else 0.dp,
),
shape = when {
isFirst && isLast -> MaterialTheme.shapes.medium
isFirst -> MaterialTheme.shapes.medium.copy(
bottomEnd = CornerSize(0),
bottomStart = CornerSize(0),
)
isLast -> MaterialTheme.shapes.medium.copy(
topEnd = CornerSize(0),
topStart = CornerSize(0),
)
else -> RectangleShape
}
) {
Row {
for (item in items) {
GridItem(
modifier = Modifier
.weight(1f)
.padding(4.dp, 8.dp),
item = item,
showLabels = true
)
}
for (i in 0 until columns - items.size) {
Spacer(modifier = Modifier.weight(1f))
}
}
}
}
}
fun LazyListScope.ListResults(
items: ImmutableList<Searchable>,
reverse: Boolean,
) {
if (items.isEmpty()) return
items(items.size) {
ListRow(
item = items[it],
isFirst = if (reverse) it == items.size - 1 else it == 0,
isLast = if (reverse) it == 0 else it == items.size - 1
)
}
}
@Composable
fun ListRow(
modifier: Modifier = Modifier,
item: Searchable,
isFirst: Boolean,
isLast: Boolean,
) {
Box(
modifier = modifier
.clipToBounds()
) {
LauncherCard(
modifier = Modifier.padding(
start = 8.dp,
end = 8.dp,
top = if (isFirst) 4.dp else 0.dp,
bottom = if (isLast) 4.dp else 0.dp,
),
shape = when {
isFirst && isLast -> MaterialTheme.shapes.medium
isFirst -> MaterialTheme.shapes.medium.copy(
bottomEnd = CornerSize(0),
bottomStart = CornerSize(0),
)
isLast -> MaterialTheme.shapes.medium.copy(
topEnd = CornerSize(0),
topStart = CornerSize(0),
)
else -> RectangleShape
}
) {
Box(
modifier = Modifier.padding(
start = 8.dp,
end = 8.dp,
top = if (isFirst) 8.dp else 4.dp,
bottom = if (isLast) 8.dp else 4.dp,
)
) {
ListItem(
modifier = Modifier
.fillMaxWidth(),
item = item
)
}
}
}
}
fun LazyListScope.SingleResult(content: @Composable (() -> Unit)?) {
if (content == null) return
item {
LauncherCard(
modifier = Modifier.padding(
horizontal = 8.dp,
vertical = 4.dp,
)
) {
content()
}
} }
} }

View File

@ -14,7 +14,7 @@ import de.mm20.launcher2.ui.launcher.search.SearchVM
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
@Composable @Composable
fun ColumnScope.AppResults(reverse: Boolean = false) { fun AppResults(reverse: Boolean = false) {
val viewModel: SearchVM = viewModel() val viewModel: SearchVM = viewModel()
val apps by viewModel.appResults.observeAsState(emptyList()) val apps by viewModel.appResults.observeAsState(emptyList())

View File

@ -14,7 +14,7 @@ import de.mm20.launcher2.ui.launcher.search.SearchVM
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
@Composable @Composable
fun ColumnScope.FavoritesResults( fun FavoritesResults(
reverse: Boolean = false, reverse: Boolean = false,
) { ) {
val viewModel: SearchVM = viewModel() val viewModel: SearchVM = viewModel()

View File

@ -19,7 +19,7 @@ import de.mm20.launcher2.ui.launcher.modals.HiddenItemsSheet
import de.mm20.launcher2.ui.launcher.search.SearchVM import de.mm20.launcher2.ui.launcher.search.SearchVM
@Composable @Composable
fun ColumnScope.HiddenResults() { fun HiddenResults() {
val viewModel: SearchVM = viewModel() val viewModel: SearchVM = viewModel()
val hiddenResults by viewModel.hiddenResults.observeAsState( val hiddenResults by viewModel.hiddenResults.observeAsState(
emptyList() emptyList()
@ -27,7 +27,7 @@ fun ColumnScope.HiddenResults() {
var showHiddenItems by remember { mutableStateOf(false) } var showHiddenItems by remember { mutableStateOf(false) }
AnimatedVisibility(visible = hiddenResults.isNotEmpty()) { if(hiddenResults.isNotEmpty()) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),