Added keyboard bindings to repository search
This commit is contained in:
parent
90b613bc59
commit
26545f4d9c
@ -8,7 +8,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
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.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
@ -21,9 +21,11 @@ import androidx.compose.ui.draw.alpha
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
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.Color
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import androidx.compose.ui.input.key.*
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
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.AppIcons
|
||||||
import com.jetpackduba.gitnuro.extensions.*
|
import com.jetpackduba.gitnuro.extensions.*
|
||||||
import com.jetpackduba.gitnuro.theme.AppTheme
|
import com.jetpackduba.gitnuro.theme.AppTheme
|
||||||
|
import com.jetpackduba.gitnuro.theme.backgroundSelected
|
||||||
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
||||||
import com.jetpackduba.gitnuro.theme.textButtonColors
|
import com.jetpackduba.gitnuro.theme.textButtonColors
|
||||||
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
|
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.ui.dialogs.AppInfoDialog
|
||||||
import com.jetpackduba.gitnuro.updates.Update
|
import com.jetpackduba.gitnuro.updates.Update
|
||||||
import com.jetpackduba.gitnuro.viewmodels.TabViewModel
|
import com.jetpackduba.gitnuro.viewmodels.TabViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -310,24 +314,73 @@ fun RecentRepositories(
|
|||||||
fun RecentRepositoriesList(
|
fun RecentRepositoriesList(
|
||||||
recentlyOpenedRepositories: List<String>,
|
recentlyOpenedRepositories: List<String>,
|
||||||
canRepositoriesBeRemoved: Boolean,
|
canRepositoriesBeRemoved: Boolean,
|
||||||
searchFieldFocusRequester: FocusRequester? = null,
|
searchFieldFocusRequester: FocusRequester = remember { FocusRequester() },
|
||||||
onRemoveRepositoryFromRecent: (String) -> Unit,
|
onRemoveRepositoryFromRecent: (String) -> Unit,
|
||||||
onOpenKnownRepository: (String) -> Unit,
|
onOpenKnownRepository: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
var filter by remember {
|
var filter by remember {
|
||||||
mutableStateOf("")
|
mutableStateOf("")
|
||||||
}
|
}
|
||||||
Column {
|
|
||||||
AdjustableOutlinedTextField(
|
var focusedItemIndex by remember { mutableStateOf(0) }
|
||||||
modifier = Modifier.run {
|
|
||||||
if (searchFieldFocusRequester != null) {
|
var isSearchFocused by remember { mutableStateOf(false) }
|
||||||
focusRequester(searchFieldFocusRequester)
|
|
||||||
|
val filteredRepositories = remember(filter, recentlyOpenedRepositories) {
|
||||||
|
if (filter.isBlank()) {
|
||||||
|
recentlyOpenedRepositories
|
||||||
} else {
|
} else {
|
||||||
this
|
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,
|
value = filter,
|
||||||
onValueChange = { filter = it },
|
onValueChange = { filter = it },
|
||||||
|
singleLine = true,
|
||||||
hint = "Search for recent repositories",
|
hint = "Search for recent repositories",
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
if (filter.isNotEmpty()) {
|
if (filter.isNotEmpty()) {
|
||||||
@ -347,35 +400,39 @@ fun RecentRepositoriesList(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
val filteredRepositories = remember(filter, recentlyOpenedRepositories) {
|
LaunchedEffect(filteredRepositories) {
|
||||||
if (filter.isBlank()) {
|
if (filter.isNotEmpty() && filteredRepositories.isNotEmpty()) {
|
||||||
recentlyOpenedRepositories
|
focusedItemIndex = 0
|
||||||
} else {
|
|
||||||
recentlyOpenedRepositories.filter { repository ->
|
|
||||||
repository.lowercaseContains(filter)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val listState = rememberLazyListState()
|
|
||||||
|
|
||||||
Box(modifier = Modifier.padding(top = 4.dp)) {
|
Box(modifier = Modifier.padding(top = 4.dp)) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
items(items = filteredRepositories) { repo ->
|
itemsIndexed(items = filteredRepositories) { index, repo ->
|
||||||
val repoDirName = repo.dirName
|
val repoDirName = repo.dirName
|
||||||
val repoDirPath = repo.dirPath
|
val repoDirPath = repo.dirPath
|
||||||
val hoverInteraction = remember { MutableInteractionSource() }
|
val hoverInteraction = remember { MutableInteractionSource() }
|
||||||
val isHovered by hoverInteraction.collectIsHoveredAsState()
|
val isHovered by hoverInteraction.collectIsHoveredAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(isHovered) {
|
||||||
|
if (isHovered) {
|
||||||
|
focusedItemIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(RoundedCornerShape(4.dp))
|
.clip(RoundedCornerShape(4.dp))
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.hoverable(hoverInteraction)
|
.hoverable(hoverInteraction)
|
||||||
.handMouseClickable { onOpenKnownRepository(repo) }
|
.handMouseClickable { onOpenKnownRepository(repo) }
|
||||||
|
.backgroundIf(
|
||||||
|
isSearchFocused && index == focusedItemIndex,
|
||||||
|
MaterialTheme.colors.backgroundSelected
|
||||||
|
)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
|
Loading…
Reference in New Issue
Block a user