____ _ _______ __
/ __ \ | /| / / __/ |/ /
/ /_/ / |/ |/ / _// /
\____/|__/|__/___/_||_/
owenclave / app/src/main/java/io/nekohasekai/sagernet/ui/compose/components/CommonComponents.kt
package io.nekohasekai.sagernet.ui.compose.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.LoadingIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun OwenclaveTopAppBar(
title: String,
modifier: Modifier = Modifier,
navigationIcon: ImageVector? = null,
onNavigationClick: (() -> Unit)? = null,
actions: @Composable RowScope.() -> Unit = {},
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
// Fade the nav-icon container out as the bar collapses so it doesn't look
// "stuck" against the scrolled background.
val collapsedFraction = scrollBehavior?.state?.collapsedFraction ?: 0f
val navContainer = MaterialTheme.colorScheme.surfaceBright.copy(
alpha = 1f - collapsedFraction,
)
LargeFlexibleTopAppBar(
title = {
Text(
title,
style = MaterialTheme.typography.headlineSmall,
maxLines = 1,
softWrap = false,
modifier = Modifier.padding(vertical = 4.dp),
)
},
modifier = modifier,
navigationIcon = {
if (navigationIcon != null && onNavigationClick != null) {
IconButton(
onClick = onNavigationClick,
modifier = Modifier.size(48.dp),
colors = IconButtonDefaults.iconButtonColors(
containerColor = navContainer,
),
) {
Icon(
imageVector = navigationIcon,
contentDescription = "Navigate back",
modifier = Modifier.size(24.dp)
)
}
}
},
actions = actions,
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface,
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
titleContentColor = MaterialTheme.colorScheme.onSurface,
navigationIconContentColor = MaterialTheme.colorScheme.onSurface,
actionIconContentColor = MaterialTheme.colorScheme.onSurface,
),
scrollBehavior = scrollBehavior,
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun LoadingState(
modifier: Modifier = Modifier,
message: String? = null,
) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
if (message != null) {
androidx.compose.foundation.layout.Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
LoadingIndicator(
modifier = Modifier.size(48.dp),
color = MaterialTheme.colorScheme.primary,
)
Text(
text = message,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 16.dp),
)
}
} else {
LoadingIndicator(
modifier = Modifier.size(48.dp),
color = MaterialTheme.colorScheme.primary,
)
}
}
}
@Composable
fun EmptyState(
message: String,
modifier: Modifier = Modifier,
icon: ImageVector? = null,
) {
Box(
modifier = modifier.fillMaxSize().padding(32.dp),
contentAlignment = Alignment.Center,
) {
androidx.compose.foundation.layout.Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (icon != null) {
ShapedIconStatic(
icon = icon,
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
size = 96.dp,
)
}
Text(
text = message,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 20.dp),
)
}
}
}