feat(search): line up the first result with the article title bar
With the search bar at the bottom, offset the results list so the first result sits on the article title row and the back button lands under the finger, in single- and two-pane layouts alike.
This commit is contained in:
1 parent
41bc116c56
commit
c9d1a3439c
4 files changed
+25
-2
No files matched your search
@@ -107,6 +107,7 @@ fun AdaptiveMainScreen(
|
|||||||
)
|
)
|
||||||
.padding(start = Spacing.contentStart, end = listEndPadding),
|
.padding(start = Spacing.contentStart, end = listEndPadding),
|
||||||
listModifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars),
|
listModifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars),
|
||||||
|
alignFirstResultWithTitleBar = isSearchBarAtBottom,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
detailPane = {
|
detailPane = {
|
||||||
@@ -124,7 +125,7 @@ fun AdaptiveMainScreen(
|
|||||||
articleState = articleState,
|
articleState = articleState,
|
||||||
showBackButton = isShowingDetail && articleState != null,
|
showBackButton = isShowingDetail && articleState != null,
|
||||||
onNavigateBack = onNavigateBackFromArticle,
|
onNavigateBack = onNavigateBackFromArticle,
|
||||||
compact = true,
|
compact = !isSearchBarAtBottom,
|
||||||
selectionReset = titleSelectionReset.intValue,
|
selectionReset = titleSelectionReset.intValue,
|
||||||
modifier = Modifier.windowInsetsPadding(
|
modifier = Modifier.windowInsetsPadding(
|
||||||
WindowInsets.safeDrawing.only(WindowInsetsSides.Top)
|
WindowInsets.safeDrawing.only(WindowInsetsSides.Top)
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ internal fun MainScreen(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.windowInsetsPadding(AppWindowInsets.horizontalSafeDrawing)
|
.windowInsetsPadding(AppWindowInsets.horizontalSafeDrawing)
|
||||||
.padding(start = Spacing.contentStart, end = 16.dp),
|
.padding(start = Spacing.contentStart, end = 16.dp),
|
||||||
|
alignFirstResultWithTitleBar = true,
|
||||||
)
|
)
|
||||||
searchBar()
|
searchBar()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,12 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
|
import com.example.research.common.ui.theme.dictionaryTitleLarge
|
||||||
|
import com.example.research.ui.theme.Spacing
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import com.example.research.core.domain.model.IndexEntry
|
import com.example.research.core.domain.model.IndexEntry
|
||||||
@@ -43,6 +48,7 @@ internal fun SearchResultsPane(
|
|||||||
onArticleClick: (IndexEntry) -> Unit,
|
onArticleClick: (IndexEntry) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
listModifier: Modifier = Modifier,
|
listModifier: Modifier = Modifier,
|
||||||
|
alignFirstResultWithTitleBar: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val listState = rememberLazyListState(
|
val listState = rememberLazyListState(
|
||||||
initialFirstVisibleItemIndex = scrollPosition.first,
|
initialFirstVisibleItemIndex = scrollPosition.first,
|
||||||
@@ -55,6 +61,7 @@ internal fun SearchResultsPane(
|
|||||||
val imeBottomPadding = with(LocalDensity.current) {
|
val imeBottomPadding = with(LocalDensity.current) {
|
||||||
WindowInsets.ime.getBottom(this).toDp()
|
WindowInsets.ime.getBottom(this).toDp()
|
||||||
}
|
}
|
||||||
|
val topPadding = if (alignFirstResultWithTitleBar) titleBarAlignedTopPadding() else 0.dp
|
||||||
|
|
||||||
LaunchedEffect(listState) {
|
LaunchedEffect(listState) {
|
||||||
snapshotFlow {
|
snapshotFlow {
|
||||||
@@ -139,8 +146,21 @@ internal fun SearchResultsPane(
|
|||||||
modifier = listModifier.weight(1f),
|
modifier = listModifier.weight(1f),
|
||||||
listState = listState,
|
listState = listState,
|
||||||
showDictionaryName = showDictionaryName,
|
showDictionaryName = showDictionaryName,
|
||||||
contentPadding = PaddingValues(bottom = imeBottomPadding),
|
contentPadding = PaddingValues(top = topPadding, bottom = imeBottomPadding),
|
||||||
staleEntries = shownStale,
|
staleEntries = shownStale,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun titleBarAlignedTopPadding(): Dp {
|
||||||
|
val textMeasurer = rememberTextMeasurer(cacheSize = 0)
|
||||||
|
val style = MaterialTheme.typography.dictionaryTitleLarge
|
||||||
|
val density = LocalDensity.current
|
||||||
|
return remember(textMeasurer, style, density) {
|
||||||
|
val titleHeight = with(density) {
|
||||||
|
textMeasurer.measure(text = "A", style = style, maxLines = 1).size.height.toDp()
|
||||||
|
}
|
||||||
|
(Spacing.topBarCenter - Spacing.listItemVertical - titleHeight / 2).coerceAtLeast(0.dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ object Spacing {
|
|||||||
// Semantic spacing constants for specific UI elements
|
// Semantic spacing constants for specific UI elements
|
||||||
val topBarPadding = 8.dp
|
val topBarPadding = 8.dp
|
||||||
val topBarHeight = 56.dp
|
val topBarHeight = 56.dp
|
||||||
|
val topBarCenter = topBarPadding + topBarHeight / 2
|
||||||
val listItemVertical = 8.dp
|
val listItemVertical = 8.dp
|
||||||
|
|
||||||
val contentStart = 32.dp
|
val contentStart = 32.dp
|
||||||
|
|||||||
Reference in new issue
Block a user