From 5a37f2398a45949bb327a61210ab46d5207d09cb Mon Sep 17 00:00:00 2001 From: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> Date: Tue, 27 Sep 2022 19:05:10 -0300 Subject: [PATCH] Improve search toolbar UX a little bit (#8102) * Improve search toolbar UX a little. * Fix wrong stringResource import. * Revert `FocusRequester` change in favour of #8093. --- .../presentation/browse/SourceSearchScreen.kt | 3 ++ .../browse/components/BrowseSourceToolbar.kt | 10 +++++ .../kanade/presentation/components/AppBar.kt | 44 ++++++++++++++++++- .../presentation/components/TabbedScreen.kt | 2 + .../history/components/HistoryToolbar.kt | 18 ++++++++ .../library/components/LibraryToolbar.kt | 38 ++++++++++++---- .../more/settings/SettingsSearchScreen.kt | 23 ++++++++-- .../tachiyomi/ui/browse/BrowseController.kt | 1 + i18n/src/main/res/values/strings.xml | 1 + 9 files changed, 126 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourceSearchScreen.kt index 756925151a..73f5bc6a59 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourceSearchScreen.kt @@ -6,10 +6,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.stringResource import androidx.paging.compose.collectAsLazyPagingItems import eu.kanade.domain.manga.model.Manga import eu.kanade.presentation.browse.components.BrowseSourceSearchToolbar import eu.kanade.presentation.components.Scaffold +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter import eu.kanade.tachiyomi.ui.more.MoreController @@ -39,6 +41,7 @@ fun SourceSearchScreen( BrowseSourceSearchToolbar( searchQuery = presenter.searchQuery ?: "", onSearchQueryChanged = { presenter.searchQuery = it }, + placeholderText = stringResource(R.string.action_search_hint), navigateUp = navigateUp, onResetClick = { presenter.searchQuery = "" }, onSearchClick = { presenter.search() }, diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt index c51873cc98..69653a8a26 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt @@ -17,6 +17,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import eu.kanade.domain.library.model.LibraryDisplayMode @@ -57,6 +59,7 @@ fun BrowseSourceToolbar( BrowseSourceSearchToolbar( searchQuery = state.searchQuery!!, onSearchQueryChanged = { state.searchQuery = it }, + placeholderText = stringResource(R.string.action_search_hint), navigateUp = { state.searchQuery = null }, onResetClick = { state.searchQuery = "" }, onSearchClick = onSearch, @@ -159,18 +162,25 @@ fun BrowseSourceRegularToolbar( fun BrowseSourceSearchToolbar( searchQuery: String, onSearchQueryChanged: (String) -> Unit, + placeholderText: String?, navigateUp: () -> Unit, onResetClick: () -> Unit, onSearchClick: () -> Unit, scrollBehavior: TopAppBarScrollBehavior?, ) { + val keyboardController = LocalSoftwareKeyboardController.current + val focusManager = LocalFocusManager.current + SearchToolbar( searchQuery = searchQuery, onChangeSearchQuery = onSearchQueryChanged, + placeholderText = placeholderText, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), keyboardActions = KeyboardActions( onSearch = { onSearchClick() + focusManager.clearFocus() + keyboardController?.hide() }, ), onClickCloseSearch = navigateUp, diff --git a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt index 9bda4d05f9..f1c104f340 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.components import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets @@ -20,6 +21,7 @@ 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.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior @@ -38,8 +40,10 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +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.sp import eu.kanade.tachiyomi.R @Composable @@ -63,7 +67,10 @@ fun AppBar( scrollBehavior: TopAppBarScrollBehavior? = null, ) { - val isActionMode by derivedStateOf { actionModeCounter > 0 } + val isActionMode by remember(actionModeCounter) { + derivedStateOf { actionModeCounter > 0 } + } + AppBar( modifier = modifier, titleContent = { @@ -216,6 +223,7 @@ fun AppBarActions( fun SearchToolbar( searchQuery: String, onChangeSearchQuery: (String) -> Unit, + placeholderText: String? = null, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, keyboardActions: KeyboardActions = KeyboardActions.Default, onClickCloseSearch: () -> Unit, @@ -223,8 +231,11 @@ fun SearchToolbar( incognitoMode: Boolean = false, downloadedOnlyMode: Boolean = false, scrollBehavior: TopAppBarScrollBehavior? = null, + visualTransformation: VisualTransformation = VisualTransformation.None, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { val focusRequester = remember { FocusRequester() } + AppBar( titleContent = { BasicTextField( @@ -233,11 +244,40 @@ fun SearchToolbar( modifier = Modifier .fillMaxWidth() .focusRequester(focusRequester), - textStyle = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onBackground), + textStyle = MaterialTheme.typography.titleMedium.copy( + color = MaterialTheme.colorScheme.onBackground, + fontWeight = FontWeight.Normal, + fontSize = 18.sp, + ), keyboardOptions = keyboardOptions, keyboardActions = keyboardActions, singleLine = true, cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground), + visualTransformation = visualTransformation, + interactionSource = interactionSource, + decorationBox = { innerTextField -> + TextFieldDefaults.TextFieldDecorationBox( + value = searchQuery, + innerTextField = innerTextField, + enabled = true, + singleLine = true, + visualTransformation = visualTransformation, + interactionSource = interactionSource, + placeholder = { + if (!placeholderText.isNullOrEmpty()) { + Text( + text = placeholderText, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleMedium.copy( + fontSize = 18.sp, + fontWeight = FontWeight.Normal, + ), + ) + } + }, + ) + }, ) }, navigationIcon = Icons.Outlined.ArrowBack, diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt index 9284af56df..3452c1ef9f 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt @@ -22,6 +22,7 @@ fun TabbedScreen( tabs: List, startIndex: Int? = null, searchQuery: String? = null, + @StringRes placeholderRes: Int? = null, onChangeSearchQuery: (String?) -> Unit = {}, incognitoMode: Boolean, downloadedOnlyMode: Boolean, @@ -47,6 +48,7 @@ fun TabbedScreen( } else { SearchToolbar( searchQuery = searchQuery, + placeholderText = placeholderRes?.let { stringResource(it) }, onChangeSearchQuery = { onChangeSearchQuery(it) }, diff --git a/app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt b/app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt index c2ac4b126d..e68d725796 100644 --- a/app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt @@ -1,5 +1,7 @@ package eu.kanade.presentation.history.components +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.DeleteSweep import androidx.compose.material.icons.outlined.Search @@ -7,7 +9,10 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.SearchToolbar import eu.kanade.tachiyomi.R @@ -21,6 +26,9 @@ fun HistoryToolbar( incognitoMode: Boolean, downloadedOnlyMode: Boolean, ) { + val keyboardController = LocalSoftwareKeyboardController.current + val focusManager = LocalFocusManager.current + if (state.searchQuery == null) { HistoryRegularToolbar( onClickSearch = { state.searchQuery = "" }, @@ -33,10 +41,20 @@ fun HistoryToolbar( SearchToolbar( searchQuery = state.searchQuery!!, onChangeSearchQuery = { state.searchQuery = it }, + placeholderText = stringResource(R.string.action_search_hint), onClickCloseSearch = { state.searchQuery = null }, onClickResetSearch = { state.searchQuery = "" }, incognitoMode = incognitoMode, downloadedOnlyMode = downloadedOnlyMode, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Search, + ), + keyboardActions = KeyboardActions( + onSearch = { + focusManager.clearFocus() + keyboardController?.hide() + }, + ), ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryToolbar.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryToolbar.kt index 7b6858e5be..e097de7fae 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryToolbar.kt @@ -2,6 +2,8 @@ package eu.kanade.presentation.library.components import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.FilterList import androidx.compose.material.icons.outlined.FlipToBack @@ -17,7 +19,10 @@ import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.sp import eu.kanade.presentation.components.AppBar @@ -48,15 +53,30 @@ fun LibraryToolbar( onClickSelectAll = onClickSelectAll, onClickInvertSelection = onClickInvertSelection, ) - state.searchQuery != null -> SearchToolbar( - searchQuery = state.searchQuery!!, - onChangeSearchQuery = { state.searchQuery = it }, - onClickCloseSearch = { state.searchQuery = null }, - onClickResetSearch = { state.searchQuery = "" }, - scrollBehavior = scrollBehavior, - incognitoMode = incognitoMode, - downloadedOnlyMode = downloadedOnlyMode, - ) + state.searchQuery != null -> { + val keyboardController = LocalSoftwareKeyboardController.current + val focusManager = LocalFocusManager.current + + SearchToolbar( + searchQuery = state.searchQuery!!, + onChangeSearchQuery = { state.searchQuery = it }, + onClickCloseSearch = { state.searchQuery = null }, + onClickResetSearch = { state.searchQuery = "" }, + scrollBehavior = scrollBehavior, + incognitoMode = incognitoMode, + downloadedOnlyMode = downloadedOnlyMode, + placeholderText = stringResource(R.string.action_search_hint), + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Search, + ), + keyboardActions = KeyboardActions( + onSearch = { + focusManager.clearFocus() + keyboardController?.hide() + }, + ), + ) + } else -> LibraryRegularToolbar( title = title, hasFilters = state.hasActiveFilters, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/SettingsSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/SettingsSearchScreen.kt index 0a860d4397..bb66166b2c 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/SettingsSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/SettingsSearchScreen.kt @@ -5,6 +5,8 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -14,11 +16,16 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.components.ScrollbarLazyColumn import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.util.horizontalPadding +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.setting.SettingsController import eu.kanade.tachiyomi.ui.setting.search.SettingsSearchHelper import eu.kanade.tachiyomi.ui.setting.search.SettingsSearchPresenter @@ -33,6 +40,9 @@ fun SettingsSearchScreen( val results by presenter.state.collectAsState() var query by remember { mutableStateOf("") } + val keyboardController = LocalSoftwareKeyboardController.current + val focusManager = LocalFocusManager.current + Scaffold( topBar = { scrollBehavior -> SearchToolbar( @@ -41,13 +51,20 @@ fun SettingsSearchScreen( query = it presenter.searchSettings(it) }, + placeholderText = stringResource(R.string.action_search_settings), onClickCloseSearch = navigateUp, onClickResetSearch = { query = "" }, scrollBehavior = scrollBehavior, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Search, + ), + keyboardActions = KeyboardActions( + onSearch = { + focusManager.clearFocus() + keyboardController?.hide() + }, + ), ) - - // TODO: search placeholder - // Text(stringResource(R.string.action_search_settings)) }, ) { contentPadding -> ScrollbarLazyColumn( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseController.kt index ecf2de02dc..002ea96251 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseController.kt @@ -45,6 +45,7 @@ class BrowseController : FullComposeController, RootController startIndex = 1.takeIf { toExtensions }, searchQuery = query, onChangeSearchQuery = { presenter.extensionsPresenter.search(it) }, + placeholderRes = R.string.action_search_hint, incognitoMode = presenter.isIncognitoMode, downloadedOnlyMode = presenter.isDownloadOnly, ) diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 518d9078a7..3e60f66292 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -50,6 +50,7 @@ Chapter fetch date Date added Search + Search… Search settings Global search Select all