Switch to M3E ExtendedFloatingActionButton
Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com> (cherry picked from commit a39b5a56e853a9c11c0984b0385a2e60b0addc3a)
This commit is contained in:
parent
3ab83f1270
commit
f24949748f
7 changed files with 83 additions and 239 deletions
|
|
@ -3,12 +3,12 @@ package eu.kanade.presentation.browse.components
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.FilterList
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.SmallExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.i18n.sy.SYMR
|
||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
||||
@Composable
|
||||
|
|
@ -17,7 +17,7 @@ fun BrowseSourceFloatingActionButton(
|
|||
onFabClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
SmallExtendedFloatingActionButton(
|
||||
modifier = modifier,
|
||||
text = {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ import androidx.compose.foundation.lazy.LazyListState
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.SmallExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.shouldExpandFAB
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ fun CategoryFloatingActionButton(
|
|||
onCreate: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
SmallExtendedFloatingActionButton(
|
||||
text = { Text(text = stringResource(MR.strings.action_add)) },
|
||||
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) },
|
||||
onClick = onCreate,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
package eu.kanade.presentation.manga
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -32,9 +29,11 @@ import androidx.compose.material3.FabPosition
|
|||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SmallExtendedFloatingActionButton
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.animateFloatingActionButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableFloatState
|
||||
import androidx.compose.runtime.State
|
||||
|
|
@ -123,7 +122,6 @@ import tachiyomi.i18n.MR
|
|||
import tachiyomi.i18n.kmk.KMR
|
||||
import tachiyomi.presentation.core.components.TwoPanelBox
|
||||
import tachiyomi.presentation.core.components.VerticalFastScroller
|
||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||
import tachiyomi.presentation.core.components.material.PullRefresh
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
|
|
@ -537,12 +535,23 @@ private fun MangaScreenSmallImpl(
|
|||
val isFABVisible = remember(chapters) {
|
||||
chapters.fastAny { !it.chapter.read } && !isAnySelected
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = isFABVisible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
// KMK -->
|
||||
modifier = Modifier
|
||||
SmallExtendedFloatingActionButton(
|
||||
text = {
|
||||
val isReading = remember(state.chapters) {
|
||||
state.chapters.fastAny { it.chapter.read }
|
||||
}
|
||||
Text(
|
||||
text = stringResource(if (isReading) MR.strings.action_resume else MR.strings.action_start),
|
||||
)
|
||||
},
|
||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||
onClick = onContinueReading,
|
||||
expanded = chapterListState.shouldExpandFAB(),
|
||||
modifier = Modifier.animateFloatingActionButton(
|
||||
visible = isFABVisible,
|
||||
alignment = Alignment.BottomEnd,
|
||||
)
|
||||
// KMK -->
|
||||
.offset { IntOffset(offsetX.roundToInt(), 0) }
|
||||
.onGloballyPositioned { coordinates ->
|
||||
fabSize = coordinates.size
|
||||
|
|
@ -566,25 +575,9 @@ private fun MangaScreenSmallImpl(
|
|||
}
|
||||
}
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
// KMK <--
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
val isReading = remember(state.chapters) {
|
||||
state.chapters.fastAny { it.chapter.read }
|
||||
}
|
||||
Text(
|
||||
text = stringResource(if (isReading) MR.strings.action_resume else MR.strings.action_start),
|
||||
)
|
||||
},
|
||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||
onClick = onContinueReading,
|
||||
expanded = chapterListState.shouldExpandFAB(),
|
||||
// KMK -->
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
// KMK -->
|
||||
floatingActionButtonPosition = if (fabPosition == FabPosition.End.toString()) {
|
||||
|
|
@ -1002,12 +995,25 @@ private fun MangaScreenLargeImpl(
|
|||
val isFABVisible = remember(chapters) {
|
||||
chapters.fastAny { !it.chapter.read } && !isAnySelected
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = isFABVisible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
// KMK -->
|
||||
modifier = Modifier
|
||||
SmallExtendedFloatingActionButton(
|
||||
text = {
|
||||
val isReading = remember(state.chapters) {
|
||||
state.chapters.fastAny { it.chapter.read }
|
||||
}
|
||||
Text(
|
||||
text = stringResource(
|
||||
if (isReading) MR.strings.action_resume else MR.strings.action_start,
|
||||
),
|
||||
)
|
||||
},
|
||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||
onClick = onContinueReading,
|
||||
expanded = chapterListState.shouldExpandFAB(),
|
||||
modifier = Modifier.animateFloatingActionButton(
|
||||
visible = isFABVisible,
|
||||
alignment = Alignment.BottomEnd,
|
||||
)
|
||||
// KMK -->
|
||||
.offset { IntOffset(offsetX.roundToInt(), 0) }
|
||||
.onGloballyPositioned { coordinates ->
|
||||
fabSize = coordinates.size
|
||||
|
|
@ -1031,27 +1037,9 @@ private fun MangaScreenLargeImpl(
|
|||
}
|
||||
}
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
// KMK <--
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
val isReading = remember(state.chapters) {
|
||||
state.chapters.fastAny { it.chapter.read }
|
||||
}
|
||||
Text(
|
||||
text = stringResource(
|
||||
if (isReading) MR.strings.action_resume else MR.strings.action_start,
|
||||
),
|
||||
)
|
||||
},
|
||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||
onClick = onContinueReading,
|
||||
expanded = chapterListState.shouldExpandFAB(),
|
||||
// KMK -->
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
// KMK -->
|
||||
floatingActionButtonPosition = if (fabPosition == FabPosition.End.toString()) {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import androidx.compose.material.icons.outlined.SelectAll
|
|||
import androidx.compose.material.icons.outlined.ToggleOn
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SmallExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
|
|
@ -86,7 +87,6 @@ import kotlinx.coroutines.launch
|
|||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.i18n.kmk.KMR
|
||||
import tachiyomi.i18n.sy.SYMR
|
||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
|
@ -178,7 +178,7 @@ class PreMigrationScreen(val migration: MigrationType) : Screen() {
|
|||
},
|
||||
// KMK <--
|
||||
floatingActionButton = {
|
||||
ExtendedFloatingActionButton(
|
||||
SmallExtendedFloatingActionButton(
|
||||
text = { Text(text = stringResource(MR.strings.action_migrate)) },
|
||||
icon = {
|
||||
Icon(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.download
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -16,8 +13,10 @@ import androidx.compose.material.icons.outlined.Pause
|
|||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SmallExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.animateFloatingActionButton
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
|
|
@ -57,13 +56,13 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
import tachiyomi.core.common.util.lang.launchUI
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.Pill
|
||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object DownloadQueueScreen : Screen() {
|
||||
@Suppress("unused")
|
||||
private fun readResolve(): Any = DownloadQueueScreen
|
||||
|
||||
@Composable
|
||||
|
|
@ -209,39 +208,37 @@ object DownloadQueueScreen : Screen() {
|
|||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
AnimatedVisibility(
|
||||
visible = downloadList.isNotEmpty(),
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
val isRunning by screenModel.isDownloaderRunning.collectAsState()
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
val id = if (isRunning) {
|
||||
MR.strings.action_pause
|
||||
} else {
|
||||
MR.strings.action_resume
|
||||
}
|
||||
Text(text = stringResource(id))
|
||||
},
|
||||
icon = {
|
||||
val icon = if (isRunning) {
|
||||
Icons.Outlined.Pause
|
||||
} else {
|
||||
Icons.Filled.PlayArrow
|
||||
}
|
||||
Icon(imageVector = icon, contentDescription = null)
|
||||
},
|
||||
onClick = {
|
||||
if (isRunning) {
|
||||
screenModel.pauseDownloads()
|
||||
} else {
|
||||
screenModel.startDownloads()
|
||||
}
|
||||
},
|
||||
expanded = fabExpanded,
|
||||
)
|
||||
}
|
||||
val isRunning by screenModel.isDownloaderRunning.collectAsState()
|
||||
SmallExtendedFloatingActionButton(
|
||||
text = {
|
||||
val id = if (isRunning) {
|
||||
MR.strings.action_pause
|
||||
} else {
|
||||
MR.strings.action_resume
|
||||
}
|
||||
Text(text = stringResource(id))
|
||||
},
|
||||
icon = {
|
||||
val icon = if (isRunning) {
|
||||
Icons.Outlined.Pause
|
||||
} else {
|
||||
Icons.Filled.PlayArrow
|
||||
}
|
||||
Icon(imageVector = icon, contentDescription = null)
|
||||
},
|
||||
onClick = {
|
||||
if (isRunning) {
|
||||
screenModel.pauseDownloads()
|
||||
} else {
|
||||
screenModel.startDownloads()
|
||||
}
|
||||
},
|
||||
expanded = fabExpanded,
|
||||
modifier = Modifier.animateFloatingActionButton(
|
||||
visible = downloadList.isNotEmpty(),
|
||||
alignment = Alignment.BottomEnd,
|
||||
),
|
||||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
if (downloadList.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
package tachiyomi.presentation.core.components.material
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.CubicBezierEasing
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandHorizontally
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkHorizontally
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||
import androidx.compose.material3.FloatingActionButtonElevation
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* ExtendedFloatingActionButton with custom transition between collapsed/expanded state.
|
||||
*
|
||||
* @see androidx.compose.material3.ExtendedFloatingActionButton
|
||||
*/
|
||||
@Composable
|
||||
fun ExtendedFloatingActionButton(
|
||||
text: @Composable () -> Unit,
|
||||
icon: @Composable () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
expanded: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
shape: Shape = MaterialTheme.shapes.large,
|
||||
containerColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
||||
contentColor: Color = contentColorFor(containerColor),
|
||||
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||
) {
|
||||
FloatingActionButton(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
interactionSource = interactionSource,
|
||||
shape = shape,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
elevation = elevation,
|
||||
) {
|
||||
val minWidth by animateDpAsState(
|
||||
targetValue = if (expanded) ExtendedFabMinimumWidth else FabContainerWidth,
|
||||
animationSpec = tween(
|
||||
durationMillis = 500,
|
||||
easing = EasingEmphasizedCubicBezier,
|
||||
),
|
||||
label = "minWidth",
|
||||
)
|
||||
val startPadding by animateDpAsState(
|
||||
targetValue = if (expanded) ExtendedFabIconSize / 2 else 0.dp,
|
||||
animationSpec = tween(
|
||||
durationMillis = if (expanded) 300 else 900,
|
||||
easing = EasingEmphasizedCubicBezier,
|
||||
),
|
||||
label = "startPadding",
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.sizeIn(minWidth = minWidth)
|
||||
.padding(start = startPadding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
icon()
|
||||
AnimatedVisibility(
|
||||
visible = expanded,
|
||||
enter = ExtendedFabExpandAnimation,
|
||||
exit = ExtendedFabCollapseAnimation,
|
||||
) {
|
||||
Box(modifier = Modifier.padding(start = ExtendedFabIconPadding, end = ExtendedFabTextPadding)) {
|
||||
text()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val EasingLinearCubicBezier = CubicBezierEasing(0.0f, 0.0f, 1.0f, 1.0f)
|
||||
private val EasingEmphasizedCubicBezier = CubicBezierEasing(0.2f, 0.0f, 0.0f, 1.0f)
|
||||
|
||||
private val ExtendedFabMinimumWidth = 80.dp
|
||||
private val ExtendedFabIconSize = 24.0.dp
|
||||
private val ExtendedFabIconPadding = 12.dp
|
||||
private val ExtendedFabTextPadding = 20.dp
|
||||
|
||||
private val ExtendedFabCollapseAnimation = fadeOut(
|
||||
animationSpec = tween(
|
||||
durationMillis = 100,
|
||||
easing = EasingLinearCubicBezier,
|
||||
),
|
||||
) + shrinkHorizontally(
|
||||
animationSpec = tween(
|
||||
durationMillis = 500,
|
||||
easing = EasingEmphasizedCubicBezier,
|
||||
),
|
||||
shrinkTowards = Alignment.Start,
|
||||
)
|
||||
|
||||
private val ExtendedFabExpandAnimation = fadeIn(
|
||||
animationSpec = tween(
|
||||
durationMillis = 200,
|
||||
delayMillis = 100,
|
||||
easing = EasingLinearCubicBezier,
|
||||
),
|
||||
) + expandHorizontally(
|
||||
animationSpec = tween(
|
||||
durationMillis = 500,
|
||||
easing = EasingEmphasizedCubicBezier,
|
||||
),
|
||||
expandFrom = Alignment.Start,
|
||||
)
|
||||
|
||||
private val FabContainerWidth = 56.0.dp
|
||||
|
|
@ -12,17 +12,7 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.runtime.snapshotFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
||||
@Composable
|
||||
fun LazyListState.shouldExpandFAB(): Boolean {
|
||||
return remember {
|
||||
derivedStateOf {
|
||||
(firstVisibleItemIndex == 0 && firstVisibleItemScrollOffset == 0) ||
|
||||
lastScrolledBackward ||
|
||||
!canScrollForward
|
||||
}
|
||||
}
|
||||
.value
|
||||
}
|
||||
fun LazyListState.shouldExpandFAB(): Boolean = lastScrolledBackward || !canScrollForward || !canScrollBackward
|
||||
|
||||
@Composable
|
||||
fun LazyListState.isScrolledToStart(): Boolean {
|
||||
|
|
|
|||
Loading…
Reference in a new issue