From 8a29d46b383b0988fad21f25bd82f3b71fdbce42 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:56:23 +0800 Subject: [PATCH] Revert^3 "Update Compose snapshot and migrate to BTF2 (#1474)" This reverts commit 22b5a175f46aa34a857f57813eb43b8f2c0e3954. --- .../androidx/compose/material3/SearchBar.kt | 103 ++++++++++ .../compose/material3/SearchBarInputField.kt | 191 ------------------ .../hippo/ehviewer/ui/login/SignInScreen.kt | 38 ++-- .../hippo/ehviewer/ui/settings/EhScreen.kt | 8 +- .../ehviewer/ui/settings/FilterScreen.kt | 23 +-- .../com/hippo/ehviewer/ui/tools/Dialog.kt | 47 +++-- .../tachiyomi/ui/reader/ReaderPageSheet.kt | 6 +- settings.gradle.kts | 2 +- 8 files changed, 165 insertions(+), 253 deletions(-) create mode 100644 app/src/main/kotlin/androidx/compose/material3/SearchBar.kt delete mode 100644 app/src/main/kotlin/androidx/compose/material3/SearchBarInputField.kt diff --git a/app/src/main/kotlin/androidx/compose/material3/SearchBar.kt b/app/src/main/kotlin/androidx/compose/material3/SearchBar.kt new file mode 100644 index 000000000..1ead215d6 --- /dev/null +++ b/app/src/main/kotlin/androidx/compose/material3/SearchBar.kt @@ -0,0 +1,103 @@ +@file:Suppress("ktlint:standard:property-naming") + +package androidx.compose.material3 + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsFocusedAsState +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.sizeIn +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.input.TextFieldLineLimits +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.material3.SearchBarDefaults.InputFieldHeight +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +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.SolidColor +import androidx.compose.ui.graphics.takeOrElse +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.delay + +@Composable +fun SearchBarInputField( + state: TextFieldState, + onSearch: () -> Unit, + expanded: Boolean, + onExpandedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + label: @Composable (() -> Unit)? = null, + placeholder: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + colors: TextFieldColors = SearchBarDefaults.inputFieldColors(), + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, +) { + val focusRequester = remember { FocusRequester() } + val focusManager = LocalFocusManager.current + val focused by interactionSource.collectIsFocusedAsState() + val textColor = LocalTextStyle.current.color.takeOrElse { + if (focused) colors.focusedTextColor else colors.unfocusedTextColor + } + + BasicTextField( + state = state, + modifier = modifier.sizeIn( + minWidth = SearchBarMinWidth, + maxWidth = SearchBarMaxWidth, + minHeight = InputFieldHeight, + ).focusRequester(focusRequester).onFocusChanged { + if (it.isFocused) onExpandedChange(true) + }.fillMaxWidth(), + enabled = enabled, + lineLimits = TextFieldLineLimits.SingleLine, + textStyle = LocalTextStyle.current.merge(TextStyle(color = textColor)), + cursorBrush = SolidColor(colors.cursorColor), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), + onKeyboardAction = { onSearch() }, + interactionSource = interactionSource, + decorator = @Composable { innerTextField -> + TextFieldDefaults.DecorationBox( + value = state.text.toString(), + innerTextField = innerTextField, + enabled = enabled, + singleLine = true, + visualTransformation = VisualTransformation.None, + interactionSource = interactionSource, + placeholder = placeholder, + leadingIcon = leadingIcon?.let { leading -> { Box(Modifier.offset(x = SearchBarIconOffsetX)) { leading() } } }, + trailingIcon = trailingIcon?.let { trailing -> { Box(Modifier.offset(x = -SearchBarIconOffsetX)) { trailing() } } }, + shape = SearchBarDefaults.inputFieldShape, + colors = colors, + container = {}, + ) + }, + ) + + val shouldClearFocus = !expanded && focused + LaunchedEffect(expanded) { + if (shouldClearFocus) { + // Not strictly needed according to the motion spec, but since the animation + // already has a delay, this works around b/261632544. + delay(AnimationDelayMillis.toLong()) + focusManager.clearFocus() + } + } +} + +val SearchBarIconOffsetX = 4.dp +val SearchBarMinWidth = 360.dp +val SearchBarMaxWidth = 720.dp +const val AnimationDelayMillis = 100 diff --git a/app/src/main/kotlin/androidx/compose/material3/SearchBarInputField.kt b/app/src/main/kotlin/androidx/compose/material3/SearchBarInputField.kt deleted file mode 100644 index 1b9c5dd64..000000000 --- a/app/src/main/kotlin/androidx/compose/material3/SearchBarInputField.kt +++ /dev/null @@ -1,191 +0,0 @@ -package androidx.compose.material3 - -import androidx.compose.animation.animateColorAsState -import androidx.compose.foundation.ScrollState -import androidx.compose.foundation.interaction.Interaction -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.collectIsFocusedAsState -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.sizeIn -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.text.input.InputTransformation -import androidx.compose.foundation.text.input.OutputTransformation -import androidx.compose.foundation.text.input.TextFieldLineLimits -import androidx.compose.foundation.text.input.TextFieldState -import androidx.compose.material3.SearchBarDefaults.InputFieldHeight -import androidx.compose.material3.SearchBarDefaults.inputFieldColors -import androidx.compose.material3.SearchBarDefaults.inputFieldShape -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithCache -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.graphics.ColorProducer -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.drawOutline -import androidx.compose.ui.graphics.takeOrElse -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.unit.dp -import kotlinx.coroutines.delay - -/** - * A text field to input a query in a search bar. - * - * @param state [TextFieldState] that holds the internal editing state of the input field. - * @param onSearch the callback to be invoked when the input service triggers the - * [ImeAction.Search] action. The current query in the [state] comes as a parameter of the - * callback. - * @param expanded whether the search bar is expanded and showing search results. - * @param onExpandedChange the callback to be invoked when the search bar's expanded state is - * changed. - * @param modifier the [Modifier] to be applied to this input field. - * @param enabled the enabled state of this input field. When `false`, this component will not - * respond to user input, and it will appear visually disabled and disabled to accessibility - * services. - * @param readOnly controls the editable state of the input field. When `true`, the field cannot - * be modified. However, a user can focus it and copy text from it. - * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle]. - * @param placeholder the placeholder to be displayed when the input text is empty. - * @param leadingIcon the leading icon to be displayed at the start of the input field. - * @param trailingIcon the trailing icon to be displayed at the end of the input field. - * @param prefix the optional prefix to be displayed before the input text. - * @param suffix the optional suffix to be displayed after the input text. - * @param inputTransformation optional [InputTransformation] that will be used to transform - * changes to the [TextFieldState] made by the user. The transformation will be applied to - * changes made by hardware and software keyboard events, pasting or dropping text, - * accessibility services, and tests. The transformation will _not_ be applied when changing - * the [state] programmatically, or when the transformation is changed. If the transformation - * is changed on an existing text field, it will be applied to the next user edit. The - * transformation will not immediately affect the current [state]. - * @param outputTransformation optional [OutputTransformation] that transforms how the contents - * of the text field are presented. - * @param scrollState scroll state that manages the horizontal scroll of the input field. - * @param shape the shape of the input field. - * @param colors [TextFieldColors] that will be used to resolve the colors used for this input - * field in different states. See [SearchBarDefaults.inputFieldColors]. - * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and - * emitting [Interaction]s for this input field. You can use this to change the search bar's - * appearance or preview the search bar in different states. Note that if `null` is provided, - * interactions will still happen internally. - */ -@ExperimentalMaterial3Api -@Composable -fun SearchBarInputField( - state: TextFieldState, - onSearch: (String) -> Unit, - expanded: Boolean, - onExpandedChange: (Boolean) -> Unit, - modifier: Modifier = Modifier, - enabled: Boolean = true, - readOnly: Boolean = false, - textStyle: TextStyle = LocalTextStyle.current, - placeholder: @Composable (() -> Unit)? = null, - leadingIcon: @Composable (() -> Unit)? = null, - trailingIcon: @Composable (() -> Unit)? = null, - prefix: @Composable (() -> Unit)? = null, - suffix: @Composable (() -> Unit)? = null, - inputTransformation: InputTransformation? = null, - outputTransformation: OutputTransformation? = null, - scrollState: ScrollState = rememberScrollState(), - shape: Shape = inputFieldShape, - colors: TextFieldColors = inputFieldColors(), - interactionSource: MutableInteractionSource? = null, -) { - @Suppress("NAME_SHADOWING") - val interactionSource = interactionSource ?: remember { MutableInteractionSource() } - - val focused = interactionSource.collectIsFocusedAsState().value - val focusRequester = remember { FocusRequester() } - val focusManager = LocalFocusManager.current - - val textColor = textStyle.color.takeOrElse { - if (focused) colors.focusedTextColor else colors.unfocusedTextColor - } - val mergedTextStyle = textStyle.merge(TextStyle(color = textColor)) - - BasicTextField( - state = state, - modifier = modifier - .sizeIn( - minWidth = SearchBarMinWidth, - maxWidth = SearchBarMaxWidth, - minHeight = InputFieldHeight, - ) - .focusRequester(focusRequester) - .onFocusChanged { if (it.isFocused) onExpandedChange(true) } - .fillMaxWidth(), - enabled = enabled, - readOnly = readOnly, - lineLimits = TextFieldLineLimits.SingleLine, - textStyle = mergedTextStyle, - cursorBrush = SolidColor(colors.cursorColor), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), - onKeyboardAction = { onSearch(state.text.toString()) }, - interactionSource = interactionSource, - inputTransformation = inputTransformation, - outputTransformation = outputTransformation, - scrollState = scrollState, - decorator = TextFieldDefaults.decorator( - state = state, - enabled = enabled, - lineLimits = TextFieldLineLimits.SingleLine, - outputTransformation = outputTransformation, - interactionSource = interactionSource, - placeholder = placeholder, - leadingIcon = leadingIcon?.let { leading -> - { Box(Modifier.offset(x = SearchBarIconOffsetX)) { leading() } } - }, - trailingIcon = trailingIcon?.let { trailing -> - { Box(Modifier.offset(x = -SearchBarIconOffsetX)) { trailing() } } - }, - prefix = prefix, - suffix = suffix, - colors = colors, - contentPadding = TextFieldDefaults.contentPaddingWithoutLabel(), - container = { - val containerColor = - animateColorAsState( - targetValue = if (focused) colors.focusedContainerColor else colors.unfocusedContainerColor, - animationSpec = MaterialTheme.motionScheme.rememberFastEffectsSpec(), - label = "ContainerColor", - ) - Box(Modifier.textFieldBackground(containerColor::value, shape)) - }, - ), - ) - - val shouldClearFocus = !expanded && focused - LaunchedEffect(expanded) { - if (shouldClearFocus) { - // Not strictly needed according to the motion spec, but since the animation - // already has a delay, this works around b/261632544. - delay(AnimationDelayMillis.toLong()) - focusManager.clearFocus() - } - } -} - -private fun Modifier.textFieldBackground( - color: ColorProducer, - shape: Shape, -) = drawWithCache { - val outline = shape.createOutline(size, layoutDirection, this) - onDrawBehind { drawOutline(outline, color = color()) } -} - -private val SearchBarMinWidth = 360.dp -private val SearchBarMaxWidth = 720.dp -private val SearchBarIconOffsetX = 4.dp - -@Suppress("ktlint:standard:property-naming") -private const val AnimationDelayMillis = 100 diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SignInScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SignInScreen.kt index 02bd779fd..d4eed7f0d 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SignInScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SignInScreen.kt @@ -14,10 +14,8 @@ import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.text.input.TextFieldLineLimits -import androidx.compose.foundation.text.input.rememberTextFieldState -import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Info @@ -51,6 +49,8 @@ import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp @@ -85,8 +85,8 @@ fun SignInScreen(navigator: DestinationsNavigator) { var isProgressIndicatorVisible by rememberSaveable { mutableStateOf(false) } var showUsernameError by rememberSaveable { mutableStateOf(false) } var showPasswordError by rememberSaveable { mutableStateOf(false) } - val username = rememberTextFieldState() - val password = rememberTextFieldState() + var username by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } var passwordHidden by rememberSaveable { mutableStateOf(true) } val context = LocalContext.current var signInJob by remember { mutableStateOf(null) } @@ -94,13 +94,13 @@ fun SignInScreen(navigator: DestinationsNavigator) { fun signIn() { if (signInJob?.isActive == true) return - if (username.text.isEmpty()) { + if (username.isEmpty()) { showUsernameError = true return } else { showUsernameError = false } - if (password.text.isEmpty()) { + if (password.isEmpty()) { showPasswordError = true return } else { @@ -112,7 +112,7 @@ fun SignInScreen(navigator: DestinationsNavigator) { EhUtils.signOut() signInJob = coroutineScope.launchIO { runCatching { - EhEngine.signIn(username.text.toString(), password.text.toString()) + EhEngine.signIn(username, password) }.onFailure { withUIContext { focusManager.clearFocus() @@ -141,33 +141,31 @@ fun SignInScreen(navigator: DestinationsNavigator) { @Composable fun UsernameAndPasswordTextField() { OutlinedTextField( - state = username, + value = username, + onValueChange = { username = it }, modifier = Modifier.width(dimensionResource(id = R.dimen.single_max_width)).autofill( autofillTypes = listOf(AutofillType.Username), - onFill = { username.setTextAndPlaceCursorAtEnd(it) }, + onFill = { username = it }, ), label = { Text(stringResource(R.string.username)) }, supportingText = { if (showUsernameError) Text(stringResource(R.string.error_username_cannot_empty)) }, trailingIcon = { if (showUsernameError) Icon(imageVector = Icons.Filled.Info, contentDescription = null) }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), - lineLimits = TextFieldLineLimits.SingleLine, + singleLine = true, isError = showUsernameError, ) OutlinedTextField( - state = password, + value = password, + onValueChange = { password = it }, modifier = Modifier.width(dimensionResource(id = R.dimen.single_max_width)).autofill( autofillTypes = listOf(AutofillType.Password), - onFill = { password.setTextAndPlaceCursorAtEnd(it) }, + onFill = { password = it }, ), label = { Text(stringResource(R.string.password)) }, - outputTransformation = { - if (passwordHidden) { - replace(0, length, "\u2022".repeat(length)) - } - }, + visualTransformation = if (passwordHidden) PasswordVisualTransformation() else VisualTransformation.None, supportingText = { if (showPasswordError) Text(stringResource(R.string.error_password_cannot_empty)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - onKeyboardAction = { signIn() }, + keyboardActions = KeyboardActions(onDone = { signIn() }), trailingIcon = { if (showPasswordError) { Icon(imageVector = Icons.Filled.Info, contentDescription = null) @@ -178,7 +176,7 @@ fun SignInScreen(navigator: DestinationsNavigator) { } } }, - lineLimits = TextFieldLineLimits.SingleLine, + singleLine = true, isError = showPasswordError, ) } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt index de689155b..ffd73719f 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack @@ -126,15 +125,16 @@ fun EhScreen(navigator: DestinationsNavigator) { if (hasSignedIn) { Column { val warning = stringResource(id = R.string.settings_eh_identity_cookies_signed) - val state = rememberTextFieldState(cookies.joinToString("\n") { (k, v) -> "$k: $v" }) + val str = cookies.joinToString("\n") { (k, v) -> "$k: $v" } Text(text = AnnotatedString.fromHtml(warning)) Spacer(modifier = Modifier.size(dimensionResource(id = R.dimen.keyline_margin))) OutlinedTextField( - state = state, + value = str, + onValueChange = {}, readOnly = true, trailingIcon = { IconButton(onClick = { - copyTextToClipboard(state.text, true) + copyTextToClipboard(str, true) // Avoid double notify user since system have done that on Tiramisu above if (!isAtLeastT) launchSnackBar(copiedToClipboard) }) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt index d48d83736..e42252bdb 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt @@ -12,9 +12,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.text.input.TextFieldLineLimits -import androidx.compose.foundation.text.input.rememberTextFieldState -import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.Help @@ -87,17 +84,17 @@ fun FilterScreen(navigator: DestinationsNavigator) { scope.launch { dialogState.dialog { cont -> val types = stringArrayResource(id = R.array.filter_entries) - val type = rememberTextFieldState(types[0]) - val state = rememberTextFieldState() + var type by remember { mutableStateOf(types[0]) } + var value by remember { mutableStateOf("") } var error by remember { mutableStateOf(null) } fun invalidateAndSave() { - if (state.text.isBlank()) { + if (value.isBlank()) { error = textIsEmpty return } error = null - val mode = FilterMode.entries[types.indexOf(type.text)] - val filter = Filter(mode, state.text.toString()) + val mode = FilterMode.entries[types.indexOf(type)] + val filter = Filter(mode, value) filter.remember { if (it) { cont.resume(Unit) @@ -132,7 +129,8 @@ fun FilterScreen(navigator: DestinationsNavigator) { OutlinedTextField( modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable), readOnly = true, - state = type, + value = type, + onValueChange = {}, label = { Text(text = stringResource(id = R.string.filter_label)) }, @@ -148,7 +146,7 @@ fun FilterScreen(navigator: DestinationsNavigator) { text = { Text(text = it) }, onClick = { expanded = false - type.setTextAndPlaceCursorAtEnd(it) + type = it }, contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, ) @@ -158,7 +156,8 @@ fun FilterScreen(navigator: DestinationsNavigator) { Spacer(modifier = Modifier.size(16.dp)) val isError = error != null OutlinedTextField( - state = state, + value = value, + onValueChange = { value = it }, label = { Text(text = stringResource(id = R.string.filter_text)) }, supportingText = { error?.let { Text(text = it) } }, trailingIcon = { @@ -170,7 +169,7 @@ fun FilterScreen(navigator: DestinationsNavigator) { } }, isError = isError, - lineLimits = TextFieldLineLimits.SingleLine, + maxLines = 1, keyboardOptions = KeyboardOptions( imeAction = ImeAction.Done, ), diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt index 984c1a62a..faed4f61f 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt @@ -20,8 +20,6 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.text.input.clearText -import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check @@ -166,7 +164,7 @@ class DialogState { invalidator: (suspend Raise.(String) -> Unit)? = null, ) = dialog { cont -> val coroutineScope = rememberCoroutineScope() - val state = rememberTextFieldState(initial) + var state by remember(cont) { mutableStateOf(initial) } var error by remember(cont) { mutableStateOf(null) } AlertDialog( onDismissRequest = { @@ -175,13 +173,12 @@ class DialogState { }, confirmButton = { TextButton(onClick = { - val text = state.text.toString() if (invalidator == null) { - cont.resume(text) + cont.resume(state) } else { coroutineScope.launch { - error = either { invalidator(text) }.leftOrNull() - error ?: cont.resume(text) + error = either { invalidator(state) }.leftOrNull() + error ?: cont.resume(state) } } }) { @@ -191,9 +188,10 @@ class DialogState { title = title.ifNotNullThen { Text(text = title!!) }, text = { OutlinedTextField( - state = state, - label = hint?.let { - @Composable { Text(text = it) } + value = state, + onValueChange = { state = it }, + label = hint.ifNotNullThen { + Text(text = hint!!) }, trailingIcon = error.ifNotNullThen { Icon( @@ -221,20 +219,19 @@ class DialogState { invalidator: (suspend Raise.(String, Boolean) -> Unit)? = null, ): Pair = dialog { cont -> val coroutineScope = rememberCoroutineScope() - val state = rememberTextFieldState(initial) + var state by remember(cont) { mutableStateOf(initial) } var error by remember(cont) { mutableStateOf(null) } var checkedState by remember { mutableStateOf(checked) } AlertDialog( onDismissRequest = { cont.cancel() }, confirmButton = { TextButton(onClick = { - val text = state.text.toString() if (invalidator == null) { - cont.resume(text to checkedState) + cont.resume(state to checkedState) } else { coroutineScope.launch { - error = either { invalidator(text, checkedState) }.leftOrNull() - error ?: cont.resume(text to checkedState) + error = either { invalidator(state, checkedState) }.leftOrNull() + error ?: cont.resume(state to checkedState) } } }) { @@ -247,9 +244,10 @@ class DialogState { verticalArrangement = Arrangement.spacedBy(16.dp), ) { OutlinedTextField( - state = state, - label = hint?.let { - { Text(text = stringResource(id = it)) } + value = state, + onValueChange = { state = it }, + label = hint.ifNotNullThen { + Text(text = stringResource(id = hint!!)) }, trailingIcon = error.ifNotNullThen { Icon( @@ -551,21 +549,22 @@ class DialogState { modifier = Modifier.fillMaxWidth().aspectRatio(1F), placeFirstItemInCenter = true, ) { - val note = rememberTextFieldState(initialNote) + var note by remember { mutableStateOf(initialNote) } TextField( - state = note, + value = note, + onValueChange = { note = it }, modifier = Modifier.fillMaxWidth(0.45F).aspectRatio(1F), label = { Text(text = stringResource(id = hint)) }, trailingIcon = { - if (note.text.isNotEmpty()) { - IconButton(onClick = { note.clearText() }) { + if (note.isNotEmpty()) { + IconButton(onClick = { note = "" }) { Icon(imageVector = Icons.Default.Close, contentDescription = null) } } }, supportingText = { Text( - text = "${note.text.toString().toByteArray().size} / $maxChar", + text = "${note.toByteArray().size} / $maxChar", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.End, ) @@ -578,7 +577,7 @@ class DialogState { ) items.forEachIndexed { index, (icon, text) -> Column( - modifier = Modifier.clip(IconWithTextCorner).clickable { dismissWith(index to note.text.toString()) } + modifier = Modifier.clip(IconWithTextCorner).clickable { dismissWith(index to note) } .fillMaxWidth(0.2F), horizontalAlignment = Alignment.CenterHorizontally, ) { diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderPageSheet.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderPageSheet.kt index e0eef8515..c70a8e8b3 100644 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderPageSheet.kt +++ b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderPageSheet.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding @@ -47,7 +48,10 @@ fun ReaderPageSheetMeta( Spacer(modifier = Modifier.size(32.dp)) Text(text = stringResource(id = text)) } - Column(modifier = Modifier.verticalScroll(rememberScrollState()).navigationBarsPadding()) { + Column( + modifier = Modifier.fillMaxSize() // Workaround for https://issuetracker.google.com/341594885 + .verticalScroll(rememberScrollState()).navigationBarsPadding(), + ) { Item(icon = Icons.Default.Refresh, text = R.string.refresh, onClick = retry) Item(icon = Icons.Default.Refresh, text = R.string.refresh_original, onClick = retryOrigin) Item(icon = Icons.Default.Share, text = R.string.action_share, onClick = share) diff --git a/settings.gradle.kts b/settings.gradle.kts index 2ede52971..7974338f0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,7 +11,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() - maven("https://androidx.dev/snapshots/builds/12183479/artifacts/repository") + maven("https://androidx.dev/snapshots/builds/12043948/artifacts/repository") } }