mirror of
https://github.com/mihonapp/mihon.git
synced 2024-12-26 02:48:24 +01:00
EmptyScreen: Compose-ify and apply content padding (#8177)
* Apply content padding to empty screen except the empty screens in browse * compose-ify EmptyScreen * center face when action show * fix padding * apply content padding to browse tabs * fix duplicate bottom insets
This commit is contained in:
parent
23bfa1f18f
commit
8500add09f
@ -13,6 +13,9 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.HelpOutline
|
||||
import androidx.compose.material.icons.filled.Public
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.outlined.Favorite
|
||||
import androidx.compose.material.icons.outlined.FilterList
|
||||
import androidx.compose.material.icons.outlined.NewReleases
|
||||
@ -49,6 +52,7 @@ import eu.kanade.presentation.browse.components.BrowseSourceToolbar
|
||||
import eu.kanade.presentation.components.AppStateBanners
|
||||
import eu.kanade.presentation.components.Divider
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.EmptyScreenAction
|
||||
import eu.kanade.presentation.components.ExtendedFloatingActionButton
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
@ -56,7 +60,6 @@ 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
|
||||
import eu.kanade.tachiyomi.widget.EmptyView
|
||||
|
||||
@Composable
|
||||
fun BrowseSourceScreen(
|
||||
@ -248,13 +251,29 @@ fun BrowseSourceContent(
|
||||
message = getErrorMessage(errorState),
|
||||
actions = if (state.source is LocalSource) {
|
||||
listOf(
|
||||
EmptyView.Action(R.string.local_source_help_guide, R.drawable.ic_help_24dp) { onLocalSourceHelpClick() },
|
||||
EmptyScreenAction(
|
||||
stringResId = R.string.local_source_help_guide,
|
||||
icon = Icons.Default.HelpOutline,
|
||||
onClick = onLocalSourceHelpClick,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
EmptyView.Action(R.string.action_retry, R.drawable.ic_refresh_24dp) { mangaList.refresh() },
|
||||
EmptyView.Action(R.string.action_open_in_web_view, R.drawable.ic_public_24dp) { onWebViewClick() },
|
||||
EmptyView.Action(R.string.label_help, R.drawable.ic_help_24dp) { onHelpClick() },
|
||||
EmptyScreenAction(
|
||||
stringResId = R.string.action_retry,
|
||||
icon = Icons.Default.Refresh,
|
||||
onClick = mangaList::refresh,
|
||||
),
|
||||
EmptyScreenAction(
|
||||
stringResId = R.string.action_open_in_web_view,
|
||||
icon = Icons.Default.Public,
|
||||
onClick = onWebViewClick,
|
||||
),
|
||||
EmptyScreenAction(
|
||||
stringResId = R.string.label_help,
|
||||
icon = Icons.Default.HelpOutline,
|
||||
onClick = onHelpClick,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
@ -133,7 +133,10 @@ private fun ExtensionDetails(
|
||||
) {
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.extension == null -> EmptyScreen(textResource = R.string.empty_screen)
|
||||
presenter.extension == null -> EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
val context = LocalContext.current
|
||||
val extension = presenter.extension
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -37,7 +38,10 @@ fun ExtensionFilterScreen(
|
||||
) { contentPadding ->
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.empty_screen)
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
SourceFilterContent(
|
||||
contentPadding = contentPadding,
|
||||
|
@ -5,11 +5,9 @@ import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
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.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.items
|
||||
@ -55,11 +53,11 @@ import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsPresenter
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding
|
||||
|
||||
@Composable
|
||||
fun ExtensionScreen(
|
||||
presenter: ExtensionsPresenter,
|
||||
contentPadding: PaddingValues,
|
||||
onLongClickItem: (Extension) -> Unit,
|
||||
onClickItemCancel: (Extension) -> Unit,
|
||||
onInstallExtension: (Extension.Available) -> Unit,
|
||||
@ -77,10 +75,14 @@ fun ExtensionScreen(
|
||||
) {
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(R.string.empty_screen)
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
ExtensionContent(
|
||||
state = presenter,
|
||||
contentPadding = contentPadding,
|
||||
onLongClickItem = onLongClickItem,
|
||||
onClickItemCancel = onClickItemCancel,
|
||||
onInstallExtension = onInstallExtension,
|
||||
@ -98,6 +100,7 @@ fun ExtensionScreen(
|
||||
@Composable
|
||||
private fun ExtensionContent(
|
||||
state: ExtensionsState,
|
||||
contentPadding: PaddingValues,
|
||||
onLongClickItem: (Extension) -> Unit,
|
||||
onClickItemCancel: (Extension) -> Unit,
|
||||
onInstallExtension: (Extension.Available) -> Unit,
|
||||
@ -110,7 +113,7 @@ private fun ExtensionContent(
|
||||
var trustState by remember { mutableStateOf<Extension.Untrusted?>(null) }
|
||||
|
||||
FastScrollLazyColumn(
|
||||
contentPadding = bottomNavPadding + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
contentPadding = contentPadding + topPaddingValues,
|
||||
) {
|
||||
items(
|
||||
items = state.items,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@ -39,7 +40,10 @@ fun MigrateMangaScreen(
|
||||
) { contentPadding ->
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.empty_screen)
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
MigrateMangaContent(
|
||||
contentPadding = contentPadding,
|
||||
|
@ -3,10 +3,8 @@ package eu.kanade.presentation.browse
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
@ -42,20 +40,24 @@ import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesPresenter
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding
|
||||
|
||||
@Composable
|
||||
fun MigrateSourceScreen(
|
||||
presenter: MigrationSourcesPresenter,
|
||||
contentPadding: PaddingValues,
|
||||
onClickItem: (Source) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_library)
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.information_empty_library,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else ->
|
||||
MigrateSourceList(
|
||||
list = presenter.items,
|
||||
contentPadding = contentPadding,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { source ->
|
||||
val sourceId = source.id.toString()
|
||||
@ -72,6 +74,7 @@ fun MigrateSourceScreen(
|
||||
@Composable
|
||||
private fun MigrateSourceList(
|
||||
list: List<Pair<Source, Long>>,
|
||||
contentPadding: PaddingValues,
|
||||
onClickItem: (Source) -> Unit,
|
||||
onLongClickItem: (Source) -> Unit,
|
||||
sortingMode: SetMigrateSorting.Mode,
|
||||
@ -80,7 +83,7 @@ private fun MigrateSourceList(
|
||||
onToggleSortingDirection: () -> Unit,
|
||||
) {
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = bottomNavPadding + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
contentPadding = contentPadding + topPaddingValues,
|
||||
) {
|
||||
stickyHeader(key = "header") {
|
||||
Row(
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Switch
|
||||
@ -43,7 +44,10 @@ fun SourcesFilterScreen(
|
||||
) { contentPadding ->
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.source_filter_empty_screen)
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.source_filter_empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
SourcesFilterContent(
|
||||
contentPadding = contentPadding,
|
||||
|
@ -2,10 +2,8 @@ package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
@ -40,12 +38,12 @@ import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesPresenter
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun SourcesScreen(
|
||||
presenter: SourcesPresenter,
|
||||
contentPadding: PaddingValues,
|
||||
onClickItem: (Source, String) -> Unit,
|
||||
onClickDisable: (Source) -> Unit,
|
||||
onClickPin: (Source) -> Unit,
|
||||
@ -53,10 +51,14 @@ fun SourcesScreen(
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(R.string.source_empty_screen)
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.source_empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
SourceList(
|
||||
state = presenter,
|
||||
contentPadding = contentPadding,
|
||||
onClickItem = onClickItem,
|
||||
onClickDisable = onClickDisable,
|
||||
onClickPin = onClickPin,
|
||||
@ -77,12 +79,13 @@ fun SourcesScreen(
|
||||
@Composable
|
||||
private fun SourceList(
|
||||
state: SourcesState,
|
||||
contentPadding: PaddingValues,
|
||||
onClickItem: (Source, String) -> Unit,
|
||||
onClickDisable: (Source) -> Unit,
|
||||
onClickPin: (Source) -> Unit,
|
||||
) {
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = bottomNavPadding + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
contentPadding = contentPadding + topPaddingValues,
|
||||
) {
|
||||
items(
|
||||
items = state.items,
|
||||
|
@ -1,9 +1,11 @@
|
||||
package eu.kanade.presentation.category
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.presentation.category.components.CategoryContent
|
||||
@ -48,7 +50,10 @@ fun CategoryScreen(
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_category)
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.information_empty_category,
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
)
|
||||
else -> {
|
||||
CategoryContent(
|
||||
state = presenter,
|
||||
|
@ -1,23 +1,49 @@
|
||||
package eu.kanade.presentation.components
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.paddingFromBaseline
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.HelpOutline
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import eu.kanade.tachiyomi.widget.EmptyView
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||
import eu.kanade.tachiyomi.R
|
||||
import kotlin.random.Random
|
||||
|
||||
@Composable
|
||||
fun EmptyScreen(
|
||||
@StringRes textResource: Int,
|
||||
actions: List<EmptyView.Action>? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
actions: List<EmptyScreenAction>? = null,
|
||||
) {
|
||||
EmptyScreen(
|
||||
message = stringResource(textResource),
|
||||
modifier = modifier,
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
@ -25,24 +51,174 @@ fun EmptyScreen(
|
||||
@Composable
|
||||
fun EmptyScreen(
|
||||
message: String,
|
||||
actions: List<EmptyView.Action>? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
actions: List<EmptyScreenAction>? = null,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
AndroidView(
|
||||
factory = { context ->
|
||||
EmptyView(context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
)
|
||||
show(message, actions)
|
||||
val face = remember { getRandomErrorFace() }
|
||||
Layout(
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.layoutId("face")
|
||||
.padding(horizontal = 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = face,
|
||||
modifier = Modifier.secondaryItemAlpha(),
|
||||
fontFamily = FontFamily.Monospace,
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = message,
|
||||
modifier = Modifier.paddingFromBaseline(top = 24.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
if (!actions.isNullOrEmpty()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.layoutId("actions")
|
||||
.padding(
|
||||
top = 24.dp,
|
||||
start = horizontalPadding,
|
||||
end = horizontalPadding,
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||
) {
|
||||
actions.forEach {
|
||||
ActionButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
title = stringResource(id = it.stringResId),
|
||||
icon = it.icon,
|
||||
onClick = it.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) { measurables, constraints ->
|
||||
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
|
||||
val facePlaceable = measurables.first { it.layoutId == "face" }
|
||||
.measure(looseConstraints)
|
||||
val actionsPlaceable = measurables.firstOrNull { it.layoutId == "actions" }
|
||||
?.measure(looseConstraints)
|
||||
|
||||
layout(constraints.maxWidth, constraints.maxHeight) {
|
||||
val faceY = (constraints.maxHeight - facePlaceable.height) / 2
|
||||
facePlaceable.placeRelative(
|
||||
x = (constraints.maxWidth - facePlaceable.width) / 2,
|
||||
y = faceY,
|
||||
)
|
||||
|
||||
actionsPlaceable?.placeRelative(
|
||||
x = (constraints.maxWidth - actionsPlaceable.width) / 2,
|
||||
y = faceY + facePlaceable.height,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActionButton(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
icon: ImageVector,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TextButton(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = title,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(
|
||||
name = "Light",
|
||||
widthDp = 400,
|
||||
heightDp = 400,
|
||||
)
|
||||
@Preview(
|
||||
name = "Dark",
|
||||
widthDp = 400,
|
||||
heightDp = 400,
|
||||
uiMode = UI_MODE_NIGHT_YES,
|
||||
)
|
||||
@Composable
|
||||
private fun NoActionPreview() {
|
||||
TachiyomiTheme {
|
||||
Surface {
|
||||
EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(
|
||||
name = "Light",
|
||||
widthDp = 400,
|
||||
heightDp = 400,
|
||||
)
|
||||
@Preview(
|
||||
name = "Dark",
|
||||
widthDp = 400,
|
||||
heightDp = 400,
|
||||
uiMode = UI_MODE_NIGHT_YES,
|
||||
)
|
||||
@Composable
|
||||
private fun WithActionPreview() {
|
||||
TachiyomiTheme {
|
||||
Surface {
|
||||
EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
actions = listOf(
|
||||
EmptyScreenAction(
|
||||
stringResId = R.string.action_retry,
|
||||
icon = Icons.Default.Refresh,
|
||||
onClick = {},
|
||||
),
|
||||
EmptyScreenAction(
|
||||
stringResId = R.string.getting_started_guide,
|
||||
icon = Icons.Default.HelpOutline,
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class EmptyScreenAction(
|
||||
@StringRes val stringResId: Int,
|
||||
val icon: ImageVector,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
private val horizontalPadding = 24.dp
|
||||
|
||||
private val ERROR_FACES = listOf(
|
||||
"(・o・;)",
|
||||
"Σ(ಠ_ಠ)",
|
||||
"ಥ_ಥ",
|
||||
"(˘・_・˘)",
|
||||
"(; ̄Д ̄)",
|
||||
"(・Д・。",
|
||||
)
|
||||
|
||||
private fun getRandomErrorFace(): String {
|
||||
return ERROR_FACES[Random.nextInt(ERROR_FACES.size)]
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package eu.kanade.presentation.components
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@ -17,6 +18,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.google.accompanist.pager.HorizontalPager
|
||||
import com.google.accompanist.pager.rememberPagerState
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
@ -95,7 +97,11 @@ fun TabbedScreen(
|
||||
state = state,
|
||||
verticalAlignment = Alignment.Top,
|
||||
) { page ->
|
||||
tabs[page].content()
|
||||
tabs[page].content(
|
||||
TachiyomiBottomNavigationView.withBottomNavPadding(
|
||||
PaddingValues(bottom = contentPadding.calculateBottomPadding()),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,5 +111,5 @@ data class TabContent(
|
||||
@StringRes val titleRes: Int,
|
||||
val badgeNumber: Int? = null,
|
||||
val actions: List<AppBar.Action> = emptyList(),
|
||||
val content: @Composable () -> Unit,
|
||||
val content: @Composable (contentPadding: PaddingValues) -> Unit,
|
||||
)
|
||||
|
@ -1,9 +1,11 @@
|
||||
package eu.kanade.presentation.history
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import eu.kanade.domain.history.model.HistoryWithRelations
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
@ -19,6 +21,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter
|
||||
import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter.Dialog
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import java.util.Date
|
||||
|
||||
@ -41,15 +44,19 @@ fun HistoryScreen(
|
||||
},
|
||||
) { contentPadding ->
|
||||
val items by presenter.getHistory().collectAsState(initial = null)
|
||||
val contentPaddingWithNavBar = TachiyomiBottomNavigationView.withBottomNavPadding(contentPadding)
|
||||
items.let {
|
||||
if (it == null) {
|
||||
LoadingScreen()
|
||||
} else if (it.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.information_no_recent_manga)
|
||||
EmptyScreen(
|
||||
textResource = R.string.information_no_recent_manga,
|
||||
modifier = Modifier.padding(contentPaddingWithNavBar),
|
||||
)
|
||||
} else {
|
||||
HistoryContent(
|
||||
history = it,
|
||||
contentPadding = contentPadding,
|
||||
contentPadding = contentPaddingWithNavBar,
|
||||
onClickCover = onClickCover,
|
||||
onClickResume = onClickResume,
|
||||
onClickDelete = { item -> presenter.dialog = Dialog.Delete(item) },
|
||||
|
@ -11,8 +11,6 @@ import eu.kanade.presentation.components.RelativeDateHeader
|
||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||
import eu.kanade.presentation.history.HistoryUiModel
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.text.DateFormat
|
||||
@ -30,7 +28,7 @@ fun HistoryContent(
|
||||
val dateFormat: DateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
|
||||
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = contentPadding + bottomNavPadding + topPaddingValues,
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
items(
|
||||
items = history,
|
||||
|
@ -1,17 +1,26 @@
|
||||
package eu.kanade.presentation.library
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.HelpOutline
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.domain.library.model.display
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.EmptyScreenAction
|
||||
import eu.kanade.presentation.components.LibraryBottomActionMenu
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.library.components.LibraryContent
|
||||
import eu.kanade.presentation.library.components.LibraryToolbar
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryPresenter
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView
|
||||
|
||||
@Composable
|
||||
fun LibraryScreen(
|
||||
@ -60,9 +69,26 @@ fun LibraryScreen(
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
val contentPadding = TachiyomiBottomNavigationView.withBottomNavPadding(paddingValues)
|
||||
if (presenter.searchQuery.isNullOrEmpty() && presenter.isLibraryEmpty) {
|
||||
val handler = LocalUriHandler.current
|
||||
EmptyScreen(
|
||||
textResource = R.string.information_empty_library,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
actions = listOf(
|
||||
EmptyScreenAction(
|
||||
stringResId = R.string.getting_started_guide,
|
||||
icon = Icons.Default.HelpOutline,
|
||||
onClick = { handler.openUri("https://tachiyomi.org/help/guides/getting-started") },
|
||||
),
|
||||
),
|
||||
)
|
||||
return@Scaffold
|
||||
}
|
||||
|
||||
LibraryContent(
|
||||
state = presenter,
|
||||
contentPadding = paddingValues,
|
||||
contentPadding = contentPadding,
|
||||
currentPage = { presenter.activeCategory },
|
||||
isLibraryEmpty = presenter.isLibraryEmpty,
|
||||
showPageTabs = presenter.tabVisibility,
|
||||
|
@ -15,7 +15,6 @@ import androidx.compose.ui.zIndex
|
||||
import eu.kanade.presentation.components.FastScrollLazyVerticalGrid
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding
|
||||
|
||||
@Composable
|
||||
fun LazyLibraryGrid(
|
||||
@ -27,7 +26,7 @@ fun LazyLibraryGrid(
|
||||
FastScrollLazyVerticalGrid(
|
||||
columns = if (columns == 0) GridCells.Adaptive(128.dp) else GridCells.Fixed(columns),
|
||||
modifier = modifier,
|
||||
contentPadding = contentPadding + bottomNavPadding + PaddingValues(12.dp),
|
||||
contentPadding = contentPadding + PaddingValues(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
content = content,
|
||||
|
@ -15,18 +15,15 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import com.google.accompanist.pager.rememberPagerState
|
||||
import eu.kanade.core.prefs.PreferenceMutableState
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.domain.library.model.LibraryDisplayMode
|
||||
import eu.kanade.domain.library.model.LibraryManga
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.SwipeRefresh
|
||||
import eu.kanade.presentation.library.LibraryState
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||
import eu.kanade.tachiyomi.widget.EmptyView
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -100,19 +97,6 @@ fun LibraryContent(
|
||||
},
|
||||
enabled = state.selectionMode.not(),
|
||||
) {
|
||||
if (state.searchQuery.isNullOrEmpty() && isLibraryEmpty) {
|
||||
val handler = LocalUriHandler.current
|
||||
EmptyScreen(
|
||||
R.string.information_empty_library,
|
||||
listOf(
|
||||
EmptyView.Action(R.string.getting_started_guide, R.drawable.ic_help_24dp) {
|
||||
handler.openUri("https://tachiyomi.org/help/guides/getting-started")
|
||||
},
|
||||
),
|
||||
)
|
||||
return@SwipeRefresh
|
||||
}
|
||||
|
||||
LibraryPager(
|
||||
state = pagerState,
|
||||
contentPadding = PaddingValues(bottom = contentPadding.calculateBottomPadding()),
|
||||
|
@ -31,7 +31,6 @@ import eu.kanade.presentation.util.selectedBackground
|
||||
import eu.kanade.presentation.util.verticalPadding
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding
|
||||
|
||||
@Composable
|
||||
fun LibraryList(
|
||||
@ -45,7 +44,7 @@ fun LibraryList(
|
||||
) {
|
||||
FastScrollLazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = bottomNavPadding + contentPadding,
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
item {
|
||||
if (searchQuery.isNullOrEmpty().not()) {
|
||||
|
@ -27,7 +27,7 @@ import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.more.DownloadQueueState
|
||||
import eu.kanade.tachiyomi.ui.more.MoreController
|
||||
import eu.kanade.tachiyomi.ui.more.MorePresenter
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView
|
||||
|
||||
@Composable
|
||||
fun MoreScreen(
|
||||
@ -43,7 +43,7 @@ fun MoreScreen(
|
||||
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
contentPadding = bottomNavPadding,
|
||||
contentPadding = TachiyomiBottomNavigationView.withBottomNavPadding(),
|
||||
) {
|
||||
item {
|
||||
LogoHeader()
|
||||
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.FlipToBack
|
||||
@ -33,7 +34,6 @@ import eu.kanade.presentation.components.MangaBottomActionMenu
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.components.SwipeRefresh
|
||||
import eu.kanade.presentation.components.VerticalFastScroller
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
@ -43,7 +43,7 @@ import eu.kanade.tachiyomi.ui.recent.updates.UpdatesPresenter
|
||||
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesPresenter.Dialog
|
||||
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesPresenter.Event
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView.Companion.bottomNavPadding
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
@ -96,13 +96,17 @@ fun UpdateScreen(
|
||||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
val contentPaddingWithNavBar = TachiyomiBottomNavigationView.withBottomNavPadding(contentPadding)
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.uiModels.isEmpty() -> EmptyScreen(textResource = R.string.information_no_recent)
|
||||
presenter.uiModels.isEmpty() -> EmptyScreen(
|
||||
textResource = R.string.information_no_recent,
|
||||
modifier = Modifier.padding(contentPaddingWithNavBar),
|
||||
)
|
||||
else -> {
|
||||
UpdateScreenContent(
|
||||
presenter = presenter,
|
||||
contentPadding = contentPadding,
|
||||
contentPadding = contentPaddingWithNavBar,
|
||||
onUpdateLibrary = onUpdateLibrary,
|
||||
onClickCover = onClickCover,
|
||||
)
|
||||
@ -120,10 +124,6 @@ private fun UpdateScreenContent(
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val updatesListState = rememberLazyListState()
|
||||
|
||||
// During selection mode bottom nav is not visible
|
||||
val contentPaddingWithNavBar = contentPadding + bottomNavPadding
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
|
||||
@ -140,39 +140,35 @@ private fun UpdateScreenContent(
|
||||
}
|
||||
},
|
||||
enabled = presenter.selectionMode.not(),
|
||||
indicatorPadding = contentPaddingWithNavBar,
|
||||
indicatorPadding = contentPadding,
|
||||
) {
|
||||
if (presenter.uiModels.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.information_no_recent)
|
||||
} else {
|
||||
VerticalFastScroller(
|
||||
listState = updatesListState,
|
||||
topContentPadding = contentPaddingWithNavBar.calculateTopPadding(),
|
||||
endContentPadding = contentPaddingWithNavBar.calculateEndPadding(LocalLayoutDirection.current),
|
||||
VerticalFastScroller(
|
||||
listState = updatesListState,
|
||||
topContentPadding = contentPadding.calculateTopPadding(),
|
||||
endContentPadding = contentPadding.calculateEndPadding(LocalLayoutDirection.current),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
state = updatesListState,
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
state = updatesListState,
|
||||
contentPadding = contentPaddingWithNavBar,
|
||||
) {
|
||||
if (presenter.lastUpdated > 0L) {
|
||||
updatesLastUpdatedItem(presenter.lastUpdated)
|
||||
}
|
||||
|
||||
updatesUiItems(
|
||||
uiModels = presenter.uiModels,
|
||||
selectionMode = presenter.selectionMode,
|
||||
onUpdateSelected = presenter::toggleSelection,
|
||||
onClickCover = onClickCover,
|
||||
onClickUpdate = {
|
||||
val intent = ReaderActivity.newIntent(context, it.update.mangaId, it.update.chapterId)
|
||||
context.startActivity(intent)
|
||||
},
|
||||
onDownloadChapter = presenter::downloadChapters,
|
||||
relativeTime = presenter.relativeTime,
|
||||
dateFormat = presenter.dateFormat,
|
||||
)
|
||||
if (presenter.lastUpdated > 0L) {
|
||||
updatesLastUpdatedItem(presenter.lastUpdated)
|
||||
}
|
||||
|
||||
updatesUiItems(
|
||||
uiModels = presenter.uiModels,
|
||||
selectionMode = presenter.selectionMode,
|
||||
onUpdateSelected = presenter::toggleSelection,
|
||||
onClickCover = onClickCover,
|
||||
onClickUpdate = {
|
||||
val intent = ReaderActivity.newIntent(context, it.update.mangaId, it.update.chapterId)
|
||||
context.startActivity(intent)
|
||||
},
|
||||
onDownloadChapter = presenter::downloadChapters,
|
||||
relativeTime = presenter.relativeTime,
|
||||
dateFormat = presenter.dateFormat,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,10 @@ fun extensionsTab(
|
||||
onClick = { router?.pushController(ExtensionFilterController()) },
|
||||
),
|
||||
),
|
||||
content = {
|
||||
content = { contentPadding ->
|
||||
ExtensionScreen(
|
||||
presenter = presenter,
|
||||
contentPadding = contentPadding,
|
||||
onLongClickItem = { extension ->
|
||||
when (extension) {
|
||||
is Extension.Available -> presenter.installExtension(extension)
|
||||
|
@ -31,9 +31,10 @@ fun migrateSourcesTab(
|
||||
},
|
||||
),
|
||||
),
|
||||
content = {
|
||||
content = { contentPadding ->
|
||||
MigrateSourceScreen(
|
||||
presenter = presenter,
|
||||
contentPadding = contentPadding,
|
||||
onClickItem = { source ->
|
||||
router?.pushController(
|
||||
MigrationMangaController(
|
||||
|
@ -32,9 +32,10 @@ fun sourcesTab(
|
||||
onClick = { router?.pushController(SourceFilterController()) },
|
||||
),
|
||||
),
|
||||
content = {
|
||||
content = { contentPadding ->
|
||||
SourcesScreen(
|
||||
presenter = presenter,
|
||||
contentPadding = contentPadding,
|
||||
onClickItem = { source, query ->
|
||||
presenter.onOpenSource(source)
|
||||
router?.pushController(BrowseSourceController(source, query))
|
||||
|
@ -256,7 +256,10 @@ class DownloadController :
|
||||
},
|
||||
) { contentPadding ->
|
||||
if (downloadList.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.information_no_downloads)
|
||||
EmptyScreen(
|
||||
textResource = R.string.information_no_downloads,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
return@Scaffold
|
||||
}
|
||||
val density = LocalDensity.current
|
||||
|
@ -1,26 +1,24 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.AbstractComposeView
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.CommonViewEmptyBinding
|
||||
import eu.kanade.tachiyomi.util.system.getThemeColor
|
||||
import kotlin.random.Random
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
|
||||
class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
RelativeLayout(context, attrs) {
|
||||
AbstractComposeView(context, attrs) {
|
||||
|
||||
private val binding: CommonViewEmptyBinding =
|
||||
CommonViewEmptyBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
var message by mutableStateOf("")
|
||||
|
||||
/**
|
||||
* Hide the information view
|
||||
@ -33,62 +31,17 @@ class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
||||
* Show the information view
|
||||
* @param textResource text of information view
|
||||
*/
|
||||
fun show(@StringRes textResource: Int, actions: List<Action>? = null) {
|
||||
show(context.getString(textResource), actions)
|
||||
}
|
||||
|
||||
fun show(message: String, actions: List<Action>? = null) {
|
||||
binding.textFace.text = getRandomErrorFace()
|
||||
binding.textLabel.text = message
|
||||
|
||||
binding.actionsContainer.removeAllViews()
|
||||
val buttonContext = ContextThemeWrapper(context, R.style.Widget_Tachiyomi_Button_ActionButton)
|
||||
val buttonColor = ColorStateList.valueOf(context.getThemeColor(R.attr.colorOnBackground))
|
||||
actions?.forEach {
|
||||
val button = MaterialButton(
|
||||
buttonContext,
|
||||
null,
|
||||
R.attr.borderlessButtonStyle,
|
||||
).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
0,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
1f / actions.size,
|
||||
)
|
||||
|
||||
setTextColor(buttonColor)
|
||||
iconTint = buttonColor
|
||||
|
||||
setIconResource(it.iconResId)
|
||||
setText(it.stringResId)
|
||||
|
||||
setOnClickListener(it.listener)
|
||||
}
|
||||
|
||||
binding.actionsContainer.addView(button)
|
||||
}
|
||||
|
||||
fun show(@StringRes textResource: Int) {
|
||||
message = context.getString(textResource)
|
||||
this.isVisible = true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val ERROR_FACES = listOf(
|
||||
"(・o・;)",
|
||||
"Σ(ಠ_ಠ)",
|
||||
"ಥ_ಥ",
|
||||
"(˘・_・˘)",
|
||||
"(; ̄Д ̄)",
|
||||
"(・Д・。",
|
||||
)
|
||||
|
||||
fun getRandomErrorFace(): String {
|
||||
return ERROR_FACES[Random.nextInt(ERROR_FACES.size)]
|
||||
@Composable
|
||||
override fun Content() {
|
||||
TachiyomiTheme {
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
|
||||
EmptyScreen(message = message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Action(
|
||||
@StringRes val stringResId: Int,
|
||||
@DrawableRes val iconResId: Int,
|
||||
val listener: OnClickListener,
|
||||
)
|
||||
}
|
||||
|
@ -9,10 +9,16 @@ import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewPropertyAnimator
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.max
|
||||
import androidx.customview.view.AbsSavedState
|
||||
import androidx.interpolator.view.animation.FastOutLinearInInterpolator
|
||||
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
|
||||
@ -58,7 +64,7 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor(
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
bottomNavPadding = PaddingValues(bottom = h.pxToDp.dp)
|
||||
bottomNavPadding = h.pxToDp.dp
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,6 +80,7 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor(
|
||||
SLIDE_UP_ANIMATION_DURATION,
|
||||
LinearOutSlowInInterpolator(),
|
||||
)
|
||||
bottomNavPadding = height.pxToDp.dp
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,6 +96,7 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor(
|
||||
SLIDE_DOWN_ANIMATION_DURATION,
|
||||
FastOutLinearInInterpolator(),
|
||||
)
|
||||
bottomNavPadding = 0.dp
|
||||
}
|
||||
|
||||
private fun animateTranslation(targetY: Float, duration: Long, interpolator: TimeInterpolator) {
|
||||
@ -149,7 +157,21 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor(
|
||||
private const val SLIDE_UP_ANIMATION_DURATION = 225L
|
||||
private const val SLIDE_DOWN_ANIMATION_DURATION = 175L
|
||||
|
||||
var bottomNavPadding by mutableStateOf(PaddingValues())
|
||||
private set
|
||||
private var bottomNavPadding by mutableStateOf(0.dp)
|
||||
|
||||
/**
|
||||
* Merges [bottomNavPadding] to the origin's [PaddingValues] bottom side.
|
||||
*/
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
fun withBottomNavPadding(origin: PaddingValues = PaddingValues()): PaddingValues {
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
return PaddingValues(
|
||||
start = origin.calculateStartPadding(layoutDirection),
|
||||
top = origin.calculateTopPadding(),
|
||||
end = origin.calculateEndPadding(layoutDirection),
|
||||
bottom = max(origin.calculateBottomPadding(), bottomNavPadding),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_face"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="48sp"
|
||||
tools:text="-_-" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
tools:text="Label" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/actions_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</LinearLayout>
|
@ -81,16 +81,6 @@
|
||||
</style>
|
||||
|
||||
|
||||
<!--==============-->
|
||||
<!--Widgets.Button-->
|
||||
<!--==============-->
|
||||
<style name="Widget.Tachiyomi.Button.ActionButton" parent="Widget.Material3.Button.TextButton.Icon">
|
||||
<item name="iconGravity">top</item>
|
||||
<item name="iconTint">@color/button_action_selector</item>
|
||||
<item name="iconPadding">4dp</item>
|
||||
<item name="android:textColor">@color/button_action_selector</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
</style>
|
||||
<!--=======================-->
|
||||
<!--Widgets.MaterialDivider-->
|
||||
<!--=======================-->
|
||||
|
Loading…
Reference in New Issue
Block a user