Move advanced options below search bar
This commit is contained in:
1 parent
fc3b9d73ec
commit
828a3d09b6
11 files changed
+193
-210
No files matched your search
@@ -271,7 +271,7 @@ data class ListUrlBuilder(
|
||||
addQueryParameterIfNotBlank("next", mNext)
|
||||
addQueryParameterIfNotBlank("seek", mJumpTo)
|
||||
// Advance search
|
||||
if (advanceSearch != -1) {
|
||||
if (advanceSearch > 0 || minRating > 0 || pageFrom > 0 || pageTo > 0) {
|
||||
addQueryParameter("advsearch", "1")
|
||||
if (advanceSearch and AdvanceTable.SH != 0) {
|
||||
addQueryParameter("f_sh", "on")
|
||||
@@ -289,20 +289,20 @@ data class ListUrlBuilder(
|
||||
addQueryParameter("f_sft", "on")
|
||||
}
|
||||
// Set min star
|
||||
if (minRating != -1) {
|
||||
if (minRating > 0) {
|
||||
addQueryParameter("f_sr", "on")
|
||||
addQueryParameter("f_srdd", "$minRating")
|
||||
}
|
||||
// Pages
|
||||
if (pageFrom != -1 || pageTo != -1) {
|
||||
if (pageFrom > 0 || pageTo > 0) {
|
||||
addQueryParameter("f_sp", "on")
|
||||
addQueryParameterIfNotBlank(
|
||||
"f_spf",
|
||||
pageFrom.takeUnless { it == -1 }?.toString(),
|
||||
pageFrom.takeIf { it > 0 }?.toString(),
|
||||
)
|
||||
addQueryParameterIfNotBlank(
|
||||
"f_spt",
|
||||
pageTo.takeUnless { it == -1 }?.toString(),
|
||||
pageTo.takeIf { it > 0 }?.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ object AdvanceTable {
|
||||
@Parcelize
|
||||
data class AdvancedSearchOption(
|
||||
val advanceSearch: Int = 0,
|
||||
val minRating: Int = -1,
|
||||
val fromPage: Int = -1,
|
||||
val toPage: Int = -1,
|
||||
val minRating: Int = 0,
|
||||
val fromPage: Int = 0,
|
||||
val toPage: Int = 0,
|
||||
) : Parcelable
|
||||
@@ -1,136 +0,0 @@
|
||||
package com.hippo.ehviewer.ui.main
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.tools.LabeledCheckbox
|
||||
import com.hippo.ehviewer.util.toIntOrDefault
|
||||
|
||||
@Composable
|
||||
fun SearchAdvanced(
|
||||
modifier: Modifier = Modifier,
|
||||
state: AdvancedSearchOption,
|
||||
onStateChanged: (AdvancedSearchOption) -> Unit,
|
||||
) = FlowRow(modifier = modifier, horizontalArrangement = Arrangement.Center, maxItemsInEachRow = 2) {
|
||||
ProvideTextStyle(MaterialTheme.typography.bodyMedium) {
|
||||
val adv = state.advanceSearch
|
||||
fun checked(bit: Int) = adv and bit != 0
|
||||
fun AdvancedSearchOption.inv(bit: Int) = onStateChanged(copy(advanceSearch = advanceSearch xor bit))
|
||||
LabeledCheckbox(
|
||||
checked = checked(AdvanceTable.SH),
|
||||
onCheckedChange = { state.inv(AdvanceTable.SH) },
|
||||
label = stringResource(id = R.string.search_sh),
|
||||
)
|
||||
LabeledCheckbox(
|
||||
checked = checked(AdvanceTable.STO),
|
||||
onCheckedChange = { state.inv(AdvanceTable.STO) },
|
||||
label = stringResource(id = R.string.search_sto),
|
||||
)
|
||||
val minRatingItems = stringArrayResource(id = R.array.search_min_rating)
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = !expanded },
|
||||
modifier = Modifier.align(Alignment.CenterVertically).padding(12.dp),
|
||||
) {
|
||||
val softwareKeyboardController = LocalSoftwareKeyboardController.current
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.menuAnchor(),
|
||||
readOnly = true,
|
||||
value = minRatingItems[if (state.minRating in 2..5) state.minRating - 1 else 0],
|
||||
onValueChange = {},
|
||||
label = { Text(stringResource(id = R.string.search_sr)) },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
) {
|
||||
SideEffect {
|
||||
softwareKeyboardController?.hide()
|
||||
}
|
||||
minRatingItems.forEach { selectionOption ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(selectionOption) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
val index = minRatingItems.indexOf(selectionOption)
|
||||
onStateChanged(state.copy(minRating = if (index > 0) index + 1 else -1))
|
||||
},
|
||||
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
var enabled by rememberSaveable { mutableStateOf(false) }
|
||||
LabeledCheckbox(
|
||||
checked = enabled,
|
||||
onCheckedChange = { enabled = it },
|
||||
label = stringResource(id = R.string.search_sp),
|
||||
)
|
||||
OutlinedTextField(
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
|
||||
value = if (enabled && state.fromPage != -1) state.fromPage.toString() else "",
|
||||
onValueChange = { onStateChanged(state.copy(fromPage = it.toIntOrDefault(-1))) },
|
||||
modifier = Modifier.width(96.dp).padding(16.dp),
|
||||
singleLine = true,
|
||||
enabled = enabled,
|
||||
)
|
||||
Text(text = stringResource(id = R.string.search_sp_to))
|
||||
OutlinedTextField(
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
|
||||
value = if (enabled && state.toPage != -1) state.toPage.toString() else "",
|
||||
onValueChange = { onStateChanged(state.copy(toPage = it.toIntOrDefault(-1))) },
|
||||
modifier = Modifier.width(96.dp).padding(16.dp),
|
||||
singleLine = true,
|
||||
enabled = enabled,
|
||||
)
|
||||
Text(text = stringResource(id = R.string.search_sp_suffix))
|
||||
}
|
||||
Text(text = stringResource(id = R.string.search_sf), modifier = Modifier.align(Alignment.CenterVertically))
|
||||
FlowRow {
|
||||
LabeledCheckbox(
|
||||
checked = checked(AdvanceTable.SFL),
|
||||
onCheckedChange = { state.inv(AdvanceTable.SFL) },
|
||||
label = stringResource(id = R.string.search_sfl),
|
||||
)
|
||||
LabeledCheckbox(
|
||||
checked = checked(AdvanceTable.SFU),
|
||||
onCheckedChange = { state.inv(AdvanceTable.SFU) },
|
||||
label = stringResource(id = R.string.search_sfu),
|
||||
)
|
||||
LabeledCheckbox(
|
||||
checked = checked(AdvanceTable.SFT),
|
||||
onCheckedChange = { state.inv(AdvanceTable.SFT) },
|
||||
label = stringResource(id = R.string.search_sft),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.ui.tools.LabeledCheckbox
|
||||
|
||||
private val categoryTable = arrayOf(
|
||||
EhUtils.DOUJINSHI to R.string.doujinshi,
|
||||
@@ -55,8 +54,6 @@ fun NormalSearch(
|
||||
onCategoryChanged: (Int) -> Unit,
|
||||
searchMode: Int,
|
||||
onSearchModeChanged: (Int) -> Unit,
|
||||
isAdvanced: Boolean,
|
||||
onAdvancedChanged: (Boolean) -> Unit,
|
||||
showInfo: () -> Unit,
|
||||
maxItemsInEachRow: Int,
|
||||
) {
|
||||
@@ -101,11 +98,6 @@ fun NormalSearch(
|
||||
IconButton(onClick = showInfo) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Default.Help, contentDescription = null)
|
||||
}
|
||||
LabeledCheckbox(
|
||||
checked = isAdvanced,
|
||||
onCheckedChange = onAdvancedChanged,
|
||||
label = stringResource(id = R.string.search_enable_advance),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,6 @@ import com.hippo.ehviewer.ui.main.GalleryInfoListItem
|
||||
import com.hippo.ehviewer.ui.main.GalleryList
|
||||
import com.hippo.ehviewer.ui.main.ImageSearch
|
||||
import com.hippo.ehviewer.ui.main.NormalSearch
|
||||
import com.hippo.ehviewer.ui.main.SearchAdvanced
|
||||
import com.hippo.ehviewer.ui.showDatePicker
|
||||
import com.hippo.ehviewer.ui.tools.Deferred
|
||||
import com.hippo.ehviewer.ui.tools.DragHandle
|
||||
@@ -192,7 +191,6 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) {
|
||||
var showSearchLayout by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
var searchNormalMode by rememberSaveable { mutableStateOf(true) }
|
||||
var searchAdvancedMode by rememberSaveable { mutableStateOf(false) }
|
||||
var category by rememberSaveable { mutableIntStateOf(Settings.searchCategory) }
|
||||
var searchMethod by rememberSaveable { mutableIntStateOf(1) }
|
||||
var advancedSearchOption by rememberSaveable { mutableStateOf(AdvancedSearchOption()) }
|
||||
@@ -534,14 +532,13 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) {
|
||||
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
val searchErr1 = stringResource(R.string.search_sp_err1)
|
||||
val searchErr2 = stringResource(R.string.search_sp_err2)
|
||||
val selectImageFirst = stringResource(R.string.select_image_first)
|
||||
SearchBarScreen(
|
||||
title = suitableTitle,
|
||||
searchFieldState = searchFieldState,
|
||||
searchFieldHint = searchBarHint,
|
||||
showLanguageFilter = true,
|
||||
advancedOption = advancedSearchOption,
|
||||
onAdvancedOptionChanged = { advancedSearchOption = it },
|
||||
showSearchFab = showSearchLayout,
|
||||
onApplySearch = { query ->
|
||||
val builder = ListUrlBuilder()
|
||||
@@ -551,6 +548,10 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) {
|
||||
val newMode = if (oldMode == MODE_SUBSCRIPTION) MODE_SUBSCRIPTION else MODE_NORMAL
|
||||
builder.mode = newMode
|
||||
builder.keyword = query
|
||||
builder.advanceSearch = advancedSearchOption.advanceSearch
|
||||
builder.minRating = advancedSearchOption.minRating
|
||||
builder.pageFrom = advancedSearchOption.fromPage
|
||||
builder.pageTo = advancedSearchOption.toPage
|
||||
} else {
|
||||
if (searchNormalMode) {
|
||||
builder.mode = when (searchMethod) {
|
||||
@@ -561,21 +562,10 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) {
|
||||
}
|
||||
builder.keyword = query
|
||||
builder.category = category
|
||||
if (searchAdvancedMode) {
|
||||
builder.advanceSearch = advancedSearchOption.advanceSearch
|
||||
builder.minRating = advancedSearchOption.minRating
|
||||
val pageFrom = advancedSearchOption.fromPage
|
||||
val pageTo = advancedSearchOption.toPage
|
||||
if (pageTo != -1 && pageTo < 10) {
|
||||
activity.showTip(searchErr1)
|
||||
return@SearchBarScreen
|
||||
} else if (pageFrom != -1 && pageTo != -1 && pageTo - pageFrom < 20) {
|
||||
activity.showTip(searchErr2)
|
||||
return@SearchBarScreen
|
||||
}
|
||||
builder.pageFrom = pageFrom
|
||||
builder.pageTo = pageTo
|
||||
}
|
||||
builder.advanceSearch = advancedSearchOption.advanceSearch
|
||||
builder.minRating = advancedSearchOption.minRating
|
||||
builder.pageFrom = advancedSearchOption.fromPage
|
||||
builder.pageTo = advancedSearchOption.toPage
|
||||
} else {
|
||||
builder.mode = MODE_IMAGE_SEARCH
|
||||
if (imagePath.isBlank()) {
|
||||
@@ -672,8 +662,6 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) {
|
||||
},
|
||||
searchMode = searchMethod,
|
||||
onSearchModeChanged = { searchMethod = it },
|
||||
isAdvanced = searchAdvancedMode,
|
||||
onAdvancedChanged = { searchAdvancedMode = it },
|
||||
showInfo = { BaseDialogBuilder(context).setMessage(R.string.search_tip).show() },
|
||||
maxItemsInEachRow = when (windowSizeClass.widthSizeClass) {
|
||||
WindowWidthSizeClass.Compact -> 2
|
||||
@@ -684,28 +672,6 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(visible = searchNormalMode && searchAdvancedMode) {
|
||||
ElevatedCard(modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.search_layout_margin_v))) {
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = dimensionResource(id = R.dimen.search_category_padding_h),
|
||||
vertical = dimensionResource(id = R.dimen.search_category_padding_v),
|
||||
).fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.search_advance),
|
||||
modifier = Modifier.height(dimensionResource(id = R.dimen.search_category_title_height)),
|
||||
fontWeight = FontWeight.Bold,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
SearchAdvanced(
|
||||
state = advancedSearchOption,
|
||||
onStateChanged = { advancedSearchOption = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(visible = !searchNormalMode) {
|
||||
ElevatedCard(modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.search_layout_margin_v))) {
|
||||
Column(
|
||||
|
||||
@@ -3,7 +3,10 @@ package com.hippo.ehviewer.ui.screen
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -19,8 +22,11 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text2.input.TextFieldState
|
||||
import androidx.compose.foundation.text2.input.clearText
|
||||
import androidx.compose.foundation.text2.input.forEachTextValue
|
||||
@@ -30,6 +36,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.MenuBook
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.FilterAlt
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.outlined.FilterAlt
|
||||
@@ -39,6 +46,8 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SearchBar
|
||||
import androidx.compose.material3.SearchBarDefaults
|
||||
@@ -60,7 +69,9 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.layout
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.lerp
|
||||
@@ -74,9 +85,12 @@ import com.hippo.ehviewer.dao.Search
|
||||
import com.hippo.ehviewer.dao.SearchDao
|
||||
import com.hippo.ehviewer.ui.LocalNavDrawerState
|
||||
import com.hippo.ehviewer.ui.LockDrawer
|
||||
import com.hippo.ehviewer.ui.main.AdvanceTable
|
||||
import com.hippo.ehviewer.ui.main.AdvancedSearchOption
|
||||
import com.hippo.ehviewer.ui.main.FAB_ANIMATE_TIME
|
||||
import com.hippo.ehviewer.ui.tools.IconFix
|
||||
import com.hippo.ehviewer.ui.tools.LocalDialogState
|
||||
import com.hippo.ehviewer.util.toIntOrDefault
|
||||
import com.jamal.composeprefs3.ui.ifNotNullThen
|
||||
import com.jamal.composeprefs3.ui.ifTrueThen
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
@@ -107,7 +121,8 @@ fun SearchBarScreen(
|
||||
title: String? = null,
|
||||
searchFieldState: TextFieldState,
|
||||
searchFieldHint: String? = null,
|
||||
showLanguageFilter: Boolean = false,
|
||||
advancedOption: AdvancedSearchOption? = null,
|
||||
onAdvancedOptionChanged: (AdvancedSearchOption) -> Unit = {},
|
||||
showSearchFab: Boolean = false,
|
||||
onApplySearch: suspend (String) -> Unit,
|
||||
onSearchExpanded: () -> Unit,
|
||||
@@ -321,7 +336,7 @@ fun SearchBarScreen(
|
||||
}
|
||||
},
|
||||
) {
|
||||
if (showLanguageFilter) {
|
||||
if (advancedOption != null) {
|
||||
var languageFilter by Settings.languageFilter.asMutableState()
|
||||
val languages = remember {
|
||||
GalleryInfo.S_LANG_TAGS.map { tag ->
|
||||
@@ -334,7 +349,11 @@ fun SearchBarScreen(
|
||||
}
|
||||
}.toTypedArray()
|
||||
}
|
||||
Row(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.horizontalScroll(rememberScrollState())
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
val any = stringResource(id = R.string.any)
|
||||
val languageStr = stringResource(id = R.string.key_language)
|
||||
FilterChip(
|
||||
@@ -355,6 +374,132 @@ fun SearchBarScreen(
|
||||
)
|
||||
},
|
||||
)
|
||||
val pageErr1 = stringResource(R.string.search_sp_err1)
|
||||
val pageErr2 = stringResource(R.string.search_sp_err2)
|
||||
val pages = stringResource(id = R.string.key_pages)
|
||||
val pagesText = remember(advancedOption.fromPage, advancedOption.toPage) {
|
||||
advancedOption.run {
|
||||
val hasFrom = fromPage > 0
|
||||
val hasTo = toPage > 0
|
||||
if (hasFrom && hasTo) {
|
||||
"$fromPage - $toPage P"
|
||||
} else if (hasFrom) {
|
||||
"$fromPage+ P"
|
||||
} else if (hasTo) {
|
||||
"$toPage- P"
|
||||
} else {
|
||||
pages
|
||||
}
|
||||
}
|
||||
}
|
||||
FilterChip(
|
||||
selected = advancedOption.fromPage != 0 || advancedOption.toPage != 0,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
val (from, to) = dialogState.awaitResult(
|
||||
initial = advancedOption.fromPage to advancedOption.toPage,
|
||||
title = R.string.key_pages,
|
||||
invalidator = { (from, to) ->
|
||||
if (to != 0 && to < 10) {
|
||||
pageErr1
|
||||
} else if (from != 0 && to != 0 && to - from < 20) {
|
||||
pageErr2
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
) { error ->
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
OutlinedTextField(
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
|
||||
value = expectedValue.first.takeIf { it > 0 }?.toString().orEmpty(),
|
||||
onValueChange = {
|
||||
expectedValue =
|
||||
expectedValue.copy(first = it.toIntOrDefault(0).coerceAtLeast(0))
|
||||
},
|
||||
modifier = Modifier.width(112.dp).padding(16.dp),
|
||||
singleLine = true,
|
||||
isError = error != null,
|
||||
)
|
||||
Text(text = stringResource(id = R.string.search_sp_to))
|
||||
OutlinedTextField(
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
|
||||
value = expectedValue.second.takeIf { it > 0 }?.toString().orEmpty(),
|
||||
onValueChange = {
|
||||
expectedValue =
|
||||
expectedValue.copy(second = it.toIntOrDefault(0).coerceAtLeast(0))
|
||||
},
|
||||
modifier = Modifier.width(112.dp).padding(16.dp),
|
||||
singleLine = true,
|
||||
isError = error != null,
|
||||
)
|
||||
Text(text = stringResource(id = R.string.search_sp_suffix))
|
||||
}
|
||||
if (error != null) {
|
||||
ListItem(
|
||||
headlineContent = { Text(text = error) },
|
||||
leadingContent = {
|
||||
IconFix(imageVector = Icons.Default.Info, contentDescription = null)
|
||||
},
|
||||
colors = ListItemDefaults.colors(
|
||||
headlineColor = MaterialTheme.colorScheme.error,
|
||||
leadingIconColor = MaterialTheme.colorScheme.error,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
onAdvancedOptionChanged(advancedOption.copy(fromPage = from, toPage = to))
|
||||
}
|
||||
},
|
||||
label = { Text(text = pagesText) },
|
||||
)
|
||||
val minRatingItems = stringArrayResource(id = R.array.search_min_rating)
|
||||
val minRatingStr = stringResource(id = R.string.search_sr)
|
||||
val minRatingIndex = (advancedOption.minRating - 1).coerceAtLeast(0)
|
||||
FilterChip(
|
||||
selected = advancedOption.minRating != 0,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
val selected =
|
||||
dialogState.showSingleChoice(*minRatingItems, selected = minRatingIndex)
|
||||
onAdvancedOptionChanged(
|
||||
advancedOption.copy(minRating = if (selected == 0) 0 else selected + 1),
|
||||
)
|
||||
}
|
||||
},
|
||||
label = { Text(text = minRatingStr + minRatingItems[minRatingIndex]) },
|
||||
)
|
||||
fun checked(bit: Int) = advancedOption.advanceSearch and bit != 0
|
||||
fun AdvancedSearchOption.inv(bit: Int) =
|
||||
onAdvancedOptionChanged(copy(advanceSearch = advanceSearch xor bit))
|
||||
FilterChip(
|
||||
selected = checked(AdvanceTable.SH),
|
||||
onClick = { advancedOption.inv(AdvanceTable.SH) },
|
||||
label = { Text(text = stringResource(id = R.string.search_sh)) },
|
||||
)
|
||||
FilterChip(
|
||||
selected = checked(AdvanceTable.STO),
|
||||
onClick = { advancedOption.inv(AdvanceTable.STO) },
|
||||
label = { Text(text = stringResource(id = R.string.search_sto)) },
|
||||
)
|
||||
val disableFilter = stringResource(id = R.string.search_sf)
|
||||
FilterChip(
|
||||
selected = checked(AdvanceTable.SFL),
|
||||
onClick = { advancedOption.inv(AdvanceTable.SFL) },
|
||||
label = { Text(text = disableFilter + stringResource(id = R.string.search_sfl)) },
|
||||
)
|
||||
FilterChip(
|
||||
selected = checked(AdvanceTable.SFU),
|
||||
onClick = { advancedOption.inv(AdvanceTable.SFU) },
|
||||
label = { Text(text = disableFilter + stringResource(id = R.string.search_sfu)) },
|
||||
)
|
||||
FilterChip(
|
||||
selected = checked(AdvanceTable.SFT),
|
||||
onClick = { advancedOption.inv(AdvanceTable.SFT) },
|
||||
label = { Text(text = disableFilter + stringResource(id = R.string.search_sft)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
LazyColumn(
|
||||
|
||||
@@ -109,9 +109,16 @@ class DialogState {
|
||||
content = { block(realContinuation) }
|
||||
}
|
||||
|
||||
suspend fun <R> awaitResult(initial: R, @StringRes title: Int? = null, block: @Composable DialogScope<R>.() -> Unit): R {
|
||||
suspend fun <R> awaitResult(
|
||||
initial: R,
|
||||
@StringRes title: Int? = null,
|
||||
invalidator: (suspend (R) -> String?)? = null,
|
||||
block: @Composable DialogScope<R>.(String?) -> Unit,
|
||||
): R {
|
||||
return dialog { cont ->
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val state = remember(cont) { mutableStateOf(initial) }
|
||||
var error by remember(cont) { mutableStateOf<String?>(null) }
|
||||
val impl = remember(cont) {
|
||||
object : DialogScope<R> {
|
||||
override var expectedValue by state
|
||||
@@ -120,12 +127,21 @@ class DialogState {
|
||||
AlertDialog(
|
||||
onDismissRequest = { cont.cancel() },
|
||||
confirmButton = {
|
||||
TextButton(onClick = { cont.resume(state.value) }) {
|
||||
TextButton(onClick = {
|
||||
if (invalidator == null) {
|
||||
cont.resume(state.value)
|
||||
} else {
|
||||
coroutineScope.launch {
|
||||
error = invalidator.invoke(state.value)
|
||||
error ?: cont.resume(state.value)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text(text = stringResource(id = android.R.string.ok))
|
||||
}
|
||||
},
|
||||
title = title.ifNotNullThen { Text(text = stringResource(id = title!!)) },
|
||||
text = { block(impl) },
|
||||
text = { block(impl, error) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
<string name="search_specify_tag">指定标签</string>
|
||||
<string name="search_enable_advance">启用高级选项</string>
|
||||
<string name="search_tip">一般搜索:就是搜索。\n\n订阅搜素:在订阅中搜索。\n\n指定上传者:列出该上传者上传的画廊。其他选项会被忽略。\n\n指定标签:列出含有该标签的画廊。其他选项会被忽略。</string>
|
||||
<string name="search_sh">仅被删除的画廊</string>
|
||||
<string name="search_sto">仅有种子的画廊</string>
|
||||
<string name="search_sh">已删除</string>
|
||||
<string name="search_sto">有种子</string>
|
||||
<string name="search_sr">最低评分:</string>
|
||||
<string name="search_sp">页数:</string>
|
||||
<string name="search_sp_to">到</string>
|
||||
|
||||
@@ -101,8 +101,8 @@
|
||||
|
||||
<string name="search_tip">一般搜索:就是搜索。\n\n訂閲搜素:在訂閲中搜索。\n\n指定上傳者:列出該上傳者上傳的畫廊。其他選項會被忽略。\n\n指定標籤:列出含有該標籤的畫廊。其他選項會被忽略。</string>
|
||||
|
||||
<string name="search_sh">僅顯示被刪除的畫廊</string>
|
||||
<string name="search_sto">僅顯示有種子的畫廊</string>
|
||||
<string name="search_sh">已刪除</string>
|
||||
<string name="search_sto">有種子</string>
|
||||
<string name="search_sr">最低評分:</string>
|
||||
<string name="search_sp">頁數:</string>
|
||||
<string name="search_sp_to">到</string>
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
<string name="search_specify_tag">指定標籤</string>
|
||||
<string name="search_enable_advance">啟用進階選項</string>
|
||||
<string name="search_tip">一般搜尋:就是搜尋。\n\n訂閱搜尋:在訂閱中搜尋。\n\n指定上傳者:列出該上傳者上傳的圖庫。其他選項會被忽略。\n\n指定標籤:列出含有該標籤的圖庫。其他選項會被忽略。</string>
|
||||
<string name="search_sh">只顯示已被移除的圖庫</string>
|
||||
<string name="search_sto">只顯示有種子的圖庫</string>
|
||||
<string name="search_sh">已被移除</string>
|
||||
<string name="search_sto">有種子</string>
|
||||
<string name="search_sr">最低評分:</string>
|
||||
<string name="search_sp">頁數:</string>
|
||||
<string name="search_sp_to">到</string>
|
||||
|
||||
@@ -124,8 +124,8 @@
|
||||
<string name="search_specify_tag">Specify tag</string>
|
||||
<string name="search_enable_advance">Enable advance options</string>
|
||||
<string name="search_tip">Normal search: Simply search.\n\nSubscription search: Search in subscriptions.\n\nSpecify uploader: Search for galleries uploaded by the specified uploader. All other options will be ignored.\n\nSpecify tag: Search for galleries that contain the specified tag. Only one tag accepted. Other options will be ignored.</string>
|
||||
<string name="search_sh">Expunged Galleries only</string>
|
||||
<string name="search_sto">Require Gallery Torrent</string>
|
||||
<string name="search_sh">Expunged</string>
|
||||
<string name="search_sto">Has Torrent</string>
|
||||
<string name="search_sr">Minimum Rating:</string>
|
||||
<string name="search_sp">Pages:</string>
|
||||
<string name="search_sp_to">to</string>
|
||||
|
||||
Reference in new issue
Block a user