Added keyboard bindings to repository search

This commit is contained in:
Abdelilah El Aissaoui 2024-07-29 01:22:04 +02:00
parent 90b613bc59
commit 26545f4d9c
No known key found for this signature in database
GPG Key ID: 7587FC860F594869

View File

@ -8,7 +8,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
@ -21,9 +21,11 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.input.key.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@ -32,6 +34,7 @@ import com.jetpackduba.gitnuro.AppConstants
import com.jetpackduba.gitnuro.AppIcons
import com.jetpackduba.gitnuro.extensions.*
import com.jetpackduba.gitnuro.theme.AppTheme
import com.jetpackduba.gitnuro.theme.backgroundSelected
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
import com.jetpackduba.gitnuro.theme.textButtonColors
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
@ -39,6 +42,7 @@ import com.jetpackduba.gitnuro.ui.components.SecondaryButton
import com.jetpackduba.gitnuro.ui.dialogs.AppInfoDialog
import com.jetpackduba.gitnuro.updates.Update
import com.jetpackduba.gitnuro.viewmodels.TabViewModel
import kotlinx.coroutines.launch
@Composable
@ -310,24 +314,73 @@ fun RecentRepositories(
fun RecentRepositoriesList(
recentlyOpenedRepositories: List<String>,
canRepositoriesBeRemoved: Boolean,
searchFieldFocusRequester: FocusRequester? = null,
searchFieldFocusRequester: FocusRequester = remember { FocusRequester() },
onRemoveRepositoryFromRecent: (String) -> Unit,
onOpenKnownRepository: (String) -> Unit,
) {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
var filter by remember {
mutableStateOf("")
}
Column {
AdjustableOutlinedTextField(
modifier = Modifier.run {
if (searchFieldFocusRequester != null) {
focusRequester(searchFieldFocusRequester)
} else {
this
var focusedItemIndex by remember { mutableStateOf(0) }
var isSearchFocused by remember { mutableStateOf(false) }
val filteredRepositories = remember(filter, recentlyOpenedRepositories) {
if (filter.isBlank()) {
recentlyOpenedRepositories
} else {
recentlyOpenedRepositories.filter { repository ->
repository.lowercaseContains(filter)
}
}
}
Column(
modifier = Modifier.onPreviewKeyEvent {
if (it.type != KeyEventType.KeyDown) {
return@onPreviewKeyEvent false
}
when (it.key) {
Key.DirectionDown -> {
if (focusedItemIndex < filteredRepositories.lastIndex) {
focusedItemIndex += 1
scope.launch { listState.animateScrollToItem(focusedItemIndex) }
}
true
}
},
Key.DirectionUp -> {
if (focusedItemIndex > 0) {
focusedItemIndex -= 1
scope.launch { listState.animateScrollToItem(focusedItemIndex) }
}
true
}
Key.Enter -> {
val repo = filteredRepositories.getOrNull(focusedItemIndex)
if (repo != null && isSearchFocused) {
onOpenKnownRepository(repo)
}
true
}
else -> {
false
}
}
}
) {
AdjustableOutlinedTextField(
modifier = Modifier
.focusRequester(searchFieldFocusRequester)
.onFocusChanged { isSearchFocused = it.isFocused },
value = filter,
onValueChange = { filter = it },
singleLine = true,
hint = "Search for recent repositories",
trailingIcon = {
if (filter.isNotEmpty()) {
@ -347,35 +400,39 @@ fun RecentRepositoriesList(
}
)
val filteredRepositories = remember(filter, recentlyOpenedRepositories) {
if (filter.isBlank()) {
recentlyOpenedRepositories
} else {
recentlyOpenedRepositories.filter { repository ->
repository.lowercaseContains(filter)
}
LaunchedEffect(filteredRepositories) {
if (filter.isNotEmpty() && filteredRepositories.isNotEmpty()) {
focusedItemIndex = 0
}
}
val listState = rememberLazyListState()
Box(modifier = Modifier.padding(top = 4.dp)) {
LazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
) {
items(items = filteredRepositories) { repo ->
itemsIndexed(items = filteredRepositories) { index, repo ->
val repoDirName = repo.dirName
val repoDirPath = repo.dirPath
val hoverInteraction = remember { MutableInteractionSource() }
val isHovered by hoverInteraction.collectIsHoveredAsState()
LaunchedEffect(isHovered) {
if (isHovered) {
focusedItemIndex = index
}
}
Row(
modifier = Modifier
.clip(RoundedCornerShape(4.dp))
.fillMaxWidth()
.hoverable(hoverInteraction)
.handMouseClickable { onOpenKnownRepository(repo) }
.backgroundIf(
isSearchFocused && index == focusedItemIndex,
MaterialTheme.colors.backgroundSelected
)
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {