fix source search box's height

This commit is contained in:
Cuong-Tran 2024-10-18 23:16:32 +07:00
parent 949e326616
commit 3decc8b86f
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
5 changed files with 204 additions and 190 deletions

View file

@ -34,6 +34,7 @@ import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
import eu.kanade.presentation.browse.components.GlobalSearchResultItem import eu.kanade.presentation.browse.components.GlobalSearchResultItem
import eu.kanade.presentation.browse.components.SourceIcon import eu.kanade.presentation.browse.components.SourceIcon
import eu.kanade.presentation.components.SourcesSearchBox
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.getNameForMangaInfo import eu.kanade.tachiyomi.source.getNameForMangaInfo
import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenState import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenState
@ -331,7 +332,7 @@ fun RadioSelectorSearchable(
onSelectOption: (Int) -> Unit = {}, onSelectOption: (Int) -> Unit = {},
) { ) {
Column(Modifier.verticalScroll(rememberScrollState())) { Column(Modifier.verticalScroll(rememberScrollState())) {
SourcesSearch( SourcesSearchBox(
searchQuery = queryString, searchQuery = queryString,
onChangeSearchQuery = onChangeSearchQuery ?: {}, onChangeSearchQuery = onChangeSearchQuery ?: {},
modifier = Modifier modifier = Modifier

View file

@ -28,6 +28,7 @@ import androidx.compose.ui.text.style.TextOverflow
import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.browse.components.SourceIcon import eu.kanade.presentation.browse.components.SourceIcon
import eu.kanade.presentation.components.AnimatedFloatingSearchBox
import eu.kanade.presentation.util.animateItemFastScroll import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceScreenModel import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceScreenModel
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard

View file

@ -1,30 +1,17 @@
package eu.kanade.presentation.browse package eu.kanade.presentation.browse
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.PushPin import androidx.compose.material.icons.filled.PushPin
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.outlined.PushPin import androidx.compose.material.icons.outlined.PushPin
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -33,28 +20,15 @@ import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
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.SolidColor
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.source.model.installedExtension import eu.kanade.domain.source.model.installedExtension
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.AnimatedFloatingSearchBox
import eu.kanade.presentation.util.animateItemFastScroll import eu.kanade.presentation.util.animateItemFastScroll
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreenModel import eu.kanade.tachiyomi.ui.browse.source.SourcesScreenModel
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
@ -75,10 +49,6 @@ import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.theme.header import tachiyomi.presentation.core.theme.header
import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide
import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.presentation.core.util.runOnEnterKeyPressed
import tachiyomi.presentation.core.util.secondaryItemAlpha
import tachiyomi.source.local.LocalSource import tachiyomi.source.local.LocalSource
import tachiyomi.source.local.isLocal import tachiyomi.source.local.isLocal
@ -175,162 +145,6 @@ fun SourcesScreen(
} }
} }
// KMK -->
@Composable
fun AnimatedFloatingSearchBox(
listState: LazyListState,
searchQuery: String?,
onChangeSearchQuery: (String?) -> Unit,
modifier: Modifier = Modifier,
placeholderText: String? = null,
) {
AnimatedVisibility(
visible = listState.isScrollingUp(),
enter = expandVertically(),
exit = shrinkVertically(),
modifier = modifier,
) {
SourcesSearch(
searchQuery = searchQuery,
onChangeSearchQuery = onChangeSearchQuery,
placeholderText = placeholderText,
)
}
}
@Composable
fun SourcesSearch(
searchQuery: String?,
onChangeSearchQuery: (String?) -> Unit,
modifier: Modifier = Modifier,
placeholderText: String? = null,
) {
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val searchAndClearFocus: () -> Unit = f@{
if (searchQuery.isNullOrBlank()) return@f
focusManager.clearFocus()
keyboardController?.hide()
}
val onClickClearSearch: () -> Unit = {
onChangeSearchQuery(null)
focusRequester.requestFocus()
keyboardController?.show()
}
val onClickCloseSearch: () -> Unit = {
onClickClearSearch()
focusManager.clearFocus()
keyboardController?.hide()
}
var isFocused by remember { mutableStateOf(false) }
BasicTextField(
value = searchQuery ?: "",
onValueChange = onChangeSearchQuery,
modifier = modifier
.fillMaxWidth()
.height(40.dp)
.focusRequester(focusRequester)
.onFocusChanged { isFocused = it.isFocused }
.runOnEnterKeyPressed(action = searchAndClearFocus)
.clearFocusOnSoftKeyboardHide(),
enabled = true,
textStyle = MaterialTheme.typography.bodyMedium.copy(
color = MaterialTheme.colorScheme.onBackground,
),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(onSearch = { searchAndClearFocus() }),
singleLine = true,
cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground),
decorationBox = { innerTextField ->
TextFieldDefaults.DecorationBox(
value = searchQuery ?: "",
innerTextField = innerTextField,
enabled = true,
singleLine = true,
visualTransformation = VisualTransformation.None,
interactionSource = remember { MutableInteractionSource() },
placeholder = {
Text(
modifier = Modifier.secondaryItemAlpha(),
text = (placeholderText ?: stringResource(MR.strings.action_search_hint)),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium,
)
},
leadingIcon = {
SearchBoxLeadingIcon(
isFocused || !searchQuery.isNullOrBlank(),
modifier = Modifier,
onClickCloseSearch,
)
},
trailingIcon = {
SearchBoxTrailingIcon(
searchQuery.isNullOrEmpty(),
modifier = Modifier,
onClickClearSearch,
)
},
shape = RoundedCornerShape(24.dp),
colors = TextFieldDefaults.colors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent,
cursorColor = MaterialTheme.colorScheme.onBackground,
),
contentPadding = PaddingValues(12.dp),
)
},
)
}
@Composable
fun SearchBoxLeadingIcon(
isSearching: Boolean,
@Suppress("UNUSED_PARAMETER") modifier: Modifier = Modifier,
onClickCloseSearch: () -> Unit = {},
) {
if (isSearching) {
IconButton(
onClick = onClickCloseSearch,
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Close",
)
}
} else {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = "Search",
)
}
}
@Composable
fun SearchBoxTrailingIcon(
isEmpty: Boolean,
@Suppress("UNUSED_PARAMETER") modifier: Modifier = Modifier,
onClickClearSearch: () -> Unit = {},
) {
if (!isEmpty) {
IconButton(
onClick = onClickClearSearch,
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = "Clear",
)
}
}
}
// KMK <--
@Composable @Composable
private fun SourceHeader( private fun SourceHeader(
language: String, language: String,

View file

@ -0,0 +1,198 @@
package eu.kanade.presentation.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
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.SolidColor
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide
import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.presentation.core.util.runOnEnterKeyPressed
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
fun AnimatedFloatingSearchBox(
listState: LazyListState,
searchQuery: String?,
onChangeSearchQuery: (String?) -> Unit,
modifier: Modifier = Modifier,
placeholderText: String? = null,
) {
AnimatedVisibility(
visible = listState.isScrollingUp(),
enter = expandVertically(),
exit = shrinkVertically(),
modifier = modifier,
) {
SourcesSearchBox(
searchQuery = searchQuery,
onChangeSearchQuery = onChangeSearchQuery,
placeholderText = placeholderText,
)
}
}
@Composable
fun SourcesSearchBox(
searchQuery: String?,
onChangeSearchQuery: (String?) -> Unit,
modifier: Modifier = Modifier,
placeholderText: String? = null,
) {
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val searchAndClearFocus: () -> Unit = f@{
if (searchQuery.isNullOrBlank()) return@f
focusManager.clearFocus()
keyboardController?.hide()
}
val onClickClearSearch: () -> Unit = {
onChangeSearchQuery(null)
focusRequester.requestFocus()
keyboardController?.show()
}
val onClickCloseSearch: () -> Unit = {
onClickClearSearch()
focusManager.clearFocus()
keyboardController?.hide()
}
var isFocused by remember { mutableStateOf(false) }
BasicTextField(
value = searchQuery ?: "",
onValueChange = onChangeSearchQuery,
modifier = modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.onFocusChanged { isFocused = it.isFocused }
.runOnEnterKeyPressed(action = searchAndClearFocus)
.clearFocusOnSoftKeyboardHide(),
enabled = true,
textStyle = MaterialTheme.typography.bodyMedium.copy(
color = MaterialTheme.colorScheme.onBackground,
),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(onSearch = { searchAndClearFocus() }),
singleLine = true,
cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground),
decorationBox = { innerTextField ->
TextFieldDefaults.DecorationBox(
value = searchQuery ?: "",
innerTextField = innerTextField,
enabled = true,
singleLine = true,
visualTransformation = VisualTransformation.None,
interactionSource = remember { MutableInteractionSource() },
placeholder = {
Text(
modifier = Modifier.secondaryItemAlpha(),
text = (placeholderText ?: stringResource(MR.strings.action_search_hint)),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium,
)
},
leadingIcon = {
SearchBoxLeadingIcon(
isFocused || !searchQuery.isNullOrBlank(),
modifier = Modifier,
onClickCloseSearch,
)
},
trailingIcon = {
SearchBoxTrailingIcon(
searchQuery.isNullOrEmpty(),
modifier = Modifier,
onClickClearSearch,
)
},
shape = RoundedCornerShape(24.dp),
colors = TextFieldDefaults.colors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent,
cursorColor = MaterialTheme.colorScheme.onBackground,
),
contentPadding = PaddingValues(12.dp),
)
},
)
}
@Composable
fun SearchBoxLeadingIcon(
isSearching: Boolean,
@Suppress("UNUSED_PARAMETER") modifier: Modifier = Modifier,
onClickCloseSearch: () -> Unit = {},
) {
if (isSearching) {
IconButton(
onClick = onClickCloseSearch,
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Close",
)
}
} else {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = "Search",
)
}
}
@Composable
fun SearchBoxTrailingIcon(
isEmpty: Boolean,
@Suppress("UNUSED_PARAMETER") modifier: Modifier = Modifier,
onClickClearSearch: () -> Unit = {},
) {
if (!isEmpty) {
IconButton(
onClick = onClickClearSearch,
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = "Clear",
)
}
}
}

View file

@ -40,9 +40,9 @@ import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.SourcesSearch
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.SourcesSearchBox
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.databinding.PreMigrationListBinding import eu.kanade.tachiyomi.databinding.PreMigrationListBinding
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen
@ -207,7 +207,7 @@ class PreMigrationScreen(val migration: MigrationType) : Screen() {
) )
} }
// KMK --> // KMK -->
SourcesSearch( SourcesSearchBox(
modifier = Modifier modifier = Modifier
.onSizeChanged { searchBoxHeight = it.height } .onSizeChanged { searchBoxHeight = it.height }
.background(MaterialTheme.colorScheme.background) .background(MaterialTheme.colorScheme.background)