Match setting group headings in settings search (#4044)

Closes #4044
Fixes #3098

Co-authored-by: Jammo <8018115+kaosjammo@users.noreply.github.com>
This commit is contained in:
AntsyLich
2026-10-10 17:36:21 +06:00
co-authored by Jammo
parent adc9ee74ec
commit e9234f205d
3 changed files with 36 additions and 30 deletions
@@ -54,7 +54,10 @@ fun PreferenceScreen(
item {
Column {
PreferenceGroupHeader(title = preference.title)
PreferenceGroupHeader(
title = preference.title,
highlighted = preference.title == highlightKey,
)
}
}
items(preference.preferenceItems) { item ->
@@ -86,7 +89,7 @@ private fun List<Preference>.findHighlightedIndex(highlightKey: String): Int {
return flatMap {
if (it is Preference.PreferenceGroup) {
buildList<String?> {
add(null) // Header
add(it.title) // Header
addAll(it.preferenceItems.map { groupItem -> groupItem.title })
add(null) // Spacer
}
@@ -168,45 +168,47 @@ private fun SearchResult(
val result by produceState<List<SearchResultItem>?>(initialValue = null, searchKey) {
value = index.asSequence()
.flatMap { settingsData ->
fun result(title: String, categoryTitle: String?) = SearchResultItem(
route = settingsData.route,
title = title,
breadcrumbs = getLocalizedBreadcrumb(
path = settingsData.title,
node = categoryTitle,
isLtr = isLtr,
),
highlightKey = title,
)
fun Preference.PreferenceItem<*, *>.matches(): Boolean {
// Don't show info preference
if (this is Preference.PreferenceItem.InfoPreference) return false
val inTitle = title.contains(searchKey, true)
val inSummary = subtitle?.contains(searchKey, true) ?: false
return inTitle || inSummary
}
settingsData.contents.asSequence()
// Only search from enabled prefs and one with valid title
.filter { it.visible && it.title.isNotBlank() }
// Flatten items contained inside *enabled* PreferenceGroup
.flatMap { p ->
when (p) {
is Preference.PreferenceGroup -> {
if (p.visible) {
p.preferenceItems.asSequence()
.filter { it.visible && it.title.isNotBlank() }
.map { p.title to it }
// A matching heading leads to the group itself, not to one of its items
val heading = if (p.title.contains(searchKey, true)) {
sequenceOf(result(p.title, null))
} else {
emptySequence()
}
val items = p.preferenceItems.asSequence()
.filter { it.visible && it.title.isNotBlank() && it.matches() }
.map { result(it.title, p.title) }
heading + items
}
is Preference.PreferenceItem<*, *> -> {
if (p.matches()) sequenceOf(result(p.title, null)) else emptySequence()
}
is Preference.PreferenceItem<*, *> -> sequenceOf(null to p)
}
}
// Don't show info preference
.filterNot { it.second is Preference.PreferenceItem.InfoPreference }
// Filter by search query
.filter { (_, p) ->
val inTitle = p.title.contains(searchKey, true)
val inSummary = p.subtitle?.contains(searchKey, true) ?: false
inTitle || inSummary
}
// Map result data
.map { (categoryTitle, p) ->
SearchResultItem(
route = settingsData.route,
title = p.title,
breadcrumbs = getLocalizedBreadcrumb(
path = settingsData.title,
node = categoryTitle,
isLtr = isLtr,
),
highlightKey = p.title,
)
}
}
.take(10) // Just take top 10 result for quicker result
.toList()
@@ -11,11 +11,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun PreferenceGroupHeader(title: String) {
fun PreferenceGroupHeader(title: String, highlighted: Boolean = false) {
Box(
contentAlignment = Alignment.CenterStart,
modifier = Modifier
.fillMaxWidth()
.highlightBackground(highlighted)
.padding(bottom = 8.dp, top = 14.dp),
) {
Text(