Migrate reader shortcut menus to Compose
Contents' UIs should probably be improved, but that can happen separately.
This commit is contained in:
parent
400ca48456
commit
7308090288
10 changed files with 161 additions and 117 deletions
|
@ -0,0 +1,56 @@
|
|||
package eu.kanade.presentation.reader
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.domain.manga.model.orientationType
|
||||
import eu.kanade.presentation.components.AdaptiveSheet
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import tachiyomi.presentation.core.components.SettingsChipRow
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
|
||||
private val orientationTypeOptions = OrientationType.entries.map { it.stringRes to it }
|
||||
|
||||
@Composable
|
||||
fun OrientationModeSelectDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
screenModel: ReaderSettingsScreenModel,
|
||||
onChange: (Int) -> Unit,
|
||||
) {
|
||||
val manga by screenModel.mangaFlow.collectAsState()
|
||||
val orientationType = remember(manga) { OrientationType.fromPreference(manga?.orientationType?.toInt()) }
|
||||
|
||||
AdaptiveSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
) {
|
||||
SettingsChipRow(R.string.rotation_type) {
|
||||
orientationTypeOptions.map { (stringRes, it) ->
|
||||
FilterChip(
|
||||
selected = it == orientationType,
|
||||
onClick = {
|
||||
screenModel.onChangeOrientation(it)
|
||||
onChange(stringRes)
|
||||
},
|
||||
label = { Text(stringResource(stringRes)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package eu.kanade.tachiyomi.ui.reader
|
||||
package eu.kanade.presentation.reader
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
|
@ -0,0 +1,56 @@
|
|||
package eu.kanade.presentation.reader
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.domain.manga.model.readingModeType
|
||||
import eu.kanade.presentation.components.AdaptiveSheet
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
import tachiyomi.presentation.core.components.SettingsChipRow
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
|
||||
private val readingModeOptions = ReadingModeType.entries.map { it.stringRes to it }
|
||||
|
||||
@Composable
|
||||
fun ReadingModeSelectDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
screenModel: ReaderSettingsScreenModel,
|
||||
onChange: (Int) -> Unit,
|
||||
) {
|
||||
val manga by screenModel.mangaFlow.collectAsState()
|
||||
val readingMode = remember(manga) { ReadingModeType.fromPreference(manga?.readingModeType?.toInt()) }
|
||||
|
||||
AdaptiveSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
) {
|
||||
SettingsChipRow(R.string.pref_category_reading_mode) {
|
||||
readingModeOptions.map { (stringRes, it) ->
|
||||
FilterChip(
|
||||
selected = it == readingMode,
|
||||
onClick = {
|
||||
screenModel.onChangeReadingMode(it)
|
||||
onChange(stringRes)
|
||||
},
|
||||
label = { Text(stringResource(stringRes)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,11 +42,12 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
|
|||
pref = screenModel.preferences.fullscreen(),
|
||||
)
|
||||
|
||||
// TODO: hide if there's no cutout
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_cutout_short),
|
||||
pref = screenModel.preferences.cutoutShort(),
|
||||
)
|
||||
if (screenModel.hasDisplayCutout) {
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_cutout_short),
|
||||
pref = screenModel.preferences.cutoutShort(),
|
||||
)
|
||||
}
|
||||
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_keep_screen_on),
|
||||
|
|
|
@ -49,9 +49,11 @@ import com.google.android.material.shape.MaterialShapeDrawable
|
|||
import com.google.android.material.transition.platform.MaterialContainerTransform
|
||||
import dev.chrisbanes.insetter.applyInsetter
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.manga.model.orientationType
|
||||
import eu.kanade.presentation.reader.ChapterNavigator
|
||||
import eu.kanade.presentation.reader.OrientationModeSelectDialog
|
||||
import eu.kanade.presentation.reader.PageIndicatorText
|
||||
import eu.kanade.presentation.reader.ReaderPageActionsDialog
|
||||
import eu.kanade.presentation.reader.ReadingModeSelectDialog
|
||||
import eu.kanade.presentation.reader.settings.ReaderSettingsDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
|
@ -79,7 +81,6 @@ import eu.kanade.tachiyomi.util.system.isNightMode
|
|||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.copy
|
||||
import eu.kanade.tachiyomi.util.view.popupMenu
|
||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||
import eu.kanade.tachiyomi.util.view.setTooltip
|
||||
import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
|
||||
|
@ -124,7 +125,7 @@ class ReaderActivity : BaseActivity() {
|
|||
val viewModel by viewModels<ReaderViewModel>()
|
||||
private var assistUrl: String? = null
|
||||
|
||||
val hasCutout by lazy { hasDisplayCutout() }
|
||||
private val hasCutout by lazy { hasDisplayCutout() }
|
||||
|
||||
/**
|
||||
* Configuration at reader level, like background color or forced orientation.
|
||||
|
@ -393,6 +394,7 @@ class ReaderActivity : BaseActivity() {
|
|||
val settingsScreenModel = remember {
|
||||
ReaderSettingsScreenModel(
|
||||
readerState = viewModel.state,
|
||||
hasDisplayCutout = hasCutout,
|
||||
onChangeReadingMode = viewModel::setMangaReadingMode,
|
||||
onChangeOrientation = viewModel::setMangaOrientationType,
|
||||
)
|
||||
|
@ -423,6 +425,30 @@ class ReaderActivity : BaseActivity() {
|
|||
screenModel = settingsScreenModel,
|
||||
)
|
||||
}
|
||||
is ReaderViewModel.Dialog.ReadingModeSelect -> {
|
||||
ReadingModeSelectDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
screenModel = settingsScreenModel,
|
||||
onChange = { stringRes ->
|
||||
menuToggleToast?.cancel()
|
||||
if (!readerPreferences.showReadingMode().get()) {
|
||||
menuToggleToast = toast(stringRes)
|
||||
}
|
||||
|
||||
updateCropBordersShortcut()
|
||||
},
|
||||
)
|
||||
}
|
||||
is ReaderViewModel.Dialog.OrientationModeSelect -> {
|
||||
OrientationModeSelectDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
screenModel = settingsScreenModel,
|
||||
onChange = { stringRes ->
|
||||
menuToggleToast?.cancel()
|
||||
menuToggleToast = toast(stringRes)
|
||||
},
|
||||
)
|
||||
}
|
||||
is ReaderViewModel.Dialog.PageActions -> {
|
||||
ReaderPageActionsDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
|
@ -484,21 +510,7 @@ class ReaderActivity : BaseActivity() {
|
|||
setTooltip(R.string.viewer)
|
||||
|
||||
setOnClickListener {
|
||||
popupMenu(
|
||||
items = ReadingModeType.entries.map { it.flagValue to it.stringRes },
|
||||
selectedItemId = viewModel.getMangaReadingMode(resolveDefault = false),
|
||||
) {
|
||||
val newReadingMode = ReadingModeType.fromPreference(itemId)
|
||||
|
||||
viewModel.setMangaReadingMode(newReadingMode)
|
||||
|
||||
menuToggleToast?.cancel()
|
||||
if (!readerPreferences.showReadingMode().get()) {
|
||||
menuToggleToast = toast(newReadingMode.stringRes)
|
||||
}
|
||||
|
||||
updateCropBordersShortcut()
|
||||
}
|
||||
viewModel.openReadingModeSelectDialog()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -537,18 +549,7 @@ class ReaderActivity : BaseActivity() {
|
|||
setTooltip(R.string.rotation_type)
|
||||
|
||||
setOnClickListener {
|
||||
popupMenu(
|
||||
items = OrientationType.entries.map { it.flagValue to it.stringRes },
|
||||
selectedItemId = viewModel.manga?.orientationType?.toInt()
|
||||
?: readerPreferences.defaultOrientationType().get(),
|
||||
) {
|
||||
val newOrientation = OrientationType.fromPreference(itemId)
|
||||
|
||||
viewModel.setMangaOrientationType(newOrientation)
|
||||
|
||||
menuToggleToast?.cancel()
|
||||
menuToggleToast = toast(newOrientation.stringRes)
|
||||
}
|
||||
viewModel.openOrientationModeSelectDialog()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -683,6 +683,14 @@ class ReaderViewModel(
|
|||
mutableState.update { it.copy(dialog = Dialog.Loading) }
|
||||
}
|
||||
|
||||
fun openReadingModeSelectDialog() {
|
||||
mutableState.update { it.copy(dialog = Dialog.ReadingModeSelect) }
|
||||
}
|
||||
|
||||
fun openOrientationModeSelectDialog() {
|
||||
mutableState.update { it.copy(dialog = Dialog.OrientationModeSelect) }
|
||||
}
|
||||
|
||||
fun openPageDialog(page: ReaderPage) {
|
||||
mutableState.update { it.copy(dialog = Dialog.PageActions(page)) }
|
||||
}
|
||||
|
@ -863,6 +871,8 @@ class ReaderViewModel(
|
|||
sealed interface Dialog {
|
||||
data object Loading : Dialog
|
||||
data object Settings : Dialog
|
||||
data object ReadingModeSelect : Dialog
|
||||
data object OrientationModeSelect : Dialog
|
||||
data class PageActions(val page: ReaderPage) : Dialog
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import uy.kohesive.injekt.api.get
|
|||
|
||||
class ReaderSettingsScreenModel(
|
||||
readerState: StateFlow<ReaderViewModel.State>,
|
||||
val hasDisplayCutout: Boolean,
|
||||
val onChangeReadingMode: (ReadingModeType) -> Unit,
|
||||
val onChangeOrientation: (OrientationType) -> Unit,
|
||||
val preferences: ReaderPreferences = Injekt.get(),
|
||||
|
|
|
@ -7,20 +7,13 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.content.PermissionChecker
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.graphics.alpha
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
import androidx.core.net.toUri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
|
@ -35,7 +28,6 @@ import tachiyomi.core.util.system.logcat
|
|||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Copies a string to clipboard
|
||||
|
@ -69,25 +61,6 @@ fun Context.copyToClipboard(label: String, content: String) {
|
|||
*/
|
||||
fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
|
||||
|
||||
/**
|
||||
* Returns the color for the given attribute.
|
||||
*
|
||||
* @param resource the attribute.
|
||||
* @param alphaFactor the alpha number [0,1].
|
||||
*/
|
||||
@ColorInt fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
|
||||
val typedArray = obtainStyledAttributes(intArrayOf(resource))
|
||||
val color = typedArray.getColor(0, 0)
|
||||
typedArray.recycle()
|
||||
|
||||
if (alphaFactor < 1f) {
|
||||
val alpha = (color.alpha * alphaFactor).roundToInt()
|
||||
return Color.argb(alpha, color.red, color.green, color.blue)
|
||||
}
|
||||
|
||||
return color
|
||||
}
|
||||
|
||||
val Context.powerManager: PowerManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
package eu.kanade.tachiyomi.util.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Rect
|
||||
|
@ -15,8 +14,6 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.activity.compose.setContent
|
||||
import androidx.annotation.MenuRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.appcompat.view.menu.MenuBuilder
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
|
@ -27,11 +24,9 @@ import androidx.compose.runtime.CompositionContext
|
|||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.core.view.forEach
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
|
||||
inline fun ComponentActivity.setComposeContent(
|
||||
parent: CompositionContext? = null,
|
||||
|
@ -110,46 +105,6 @@ inline fun View.popupMenu(
|
|||
return popup
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a popup menu on top of this view.
|
||||
*
|
||||
* @param items menu item names to inflate the menu with. List of itemId to stringRes pairs.
|
||||
* @param selectedItemId optionally show a checkmark beside an item with this itemId.
|
||||
* @param onMenuItemClick function to execute when a menu item is clicked.
|
||||
*/
|
||||
@SuppressLint("RestrictedApi")
|
||||
inline fun View.popupMenu(
|
||||
items: List<Pair<Int, Int>>,
|
||||
selectedItemId: Int? = null,
|
||||
noinline onMenuItemClick: MenuItem.() -> Unit,
|
||||
): PopupMenu {
|
||||
val popup = PopupMenu(context, this, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0)
|
||||
items.forEach { (id, stringRes) ->
|
||||
popup.menu.add(0, id, 0, stringRes)
|
||||
}
|
||||
|
||||
if (selectedItemId != null) {
|
||||
(popup.menu as? MenuBuilder)?.setOptionalIconsVisible(true)
|
||||
val emptyIcon = AppCompatResources.getDrawable(context, R.drawable.ic_blank_24dp)
|
||||
popup.menu.forEach { item ->
|
||||
item.icon = when (item.itemId) {
|
||||
selectedItemId -> AppCompatResources.getDrawable(context, R.drawable.ic_check_24dp)?.mutate()?.apply {
|
||||
setTint(context.getResourceColor(android.R.attr.textColorPrimary))
|
||||
}
|
||||
else -> emptyIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
popup.setOnMenuItemClickListener {
|
||||
it.onMenuItemClick()
|
||||
true
|
||||
}
|
||||
|
||||
popup.show()
|
||||
return popup
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deep copy of the provided [Drawable]
|
||||
*/
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
|
||||
</vector>
|
Reference in a new issue