Skip to content

Commit

Permalink
added search fab
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Mar 16, 2024
1 parent e9a15d3 commit cc4a75e
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package dev.datlag.burningseries.shared.ui.custom

import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import dev.icerock.moko.resources.compose.stringResource
import dev.datlag.burningseries.shared.SharedRes
import dev.datlag.burningseries.shared.common.withIOContext
import kotlinx.coroutines.delay

@Composable
fun FloatingSearchButton(
icon: ImageVector,
contentDescription: String?,
clearIcon: ImageVector,
closeIcon: ImageVector,
modifier: Modifier = Modifier,
onTextChange: (String) -> Unit
) {
val focusRequester = remember { FocusRequester() }
var opened by remember { mutableStateOf(false) }

Surface(
color = MaterialTheme.colorScheme.primaryContainer,
modifier = modifier,
shape = FloatingActionButtonDefaults.shape,
shadowElevation = 6.dp,
onClick = {
if (!opened) {
opened = true
} else {
focusRequester.requestFocus()
}
}
) {
AnimatedContent(targetState = opened) { expand ->
if (expand) {
SearchBar(
close = {
opened = false
},
clearIcon = clearIcon,
closeIcon = closeIcon,
focusRequester = focusRequester,
onTextChange = onTextChange
)

LaunchedEffect(focusRequester) {
withIOContext {
delay(500) // wait till transition is done
}
focusRequester.requestFocus()
}
} else {
Box(
modifier = Modifier.size(56.dp),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = icon,
contentDescription = contentDescription
)
}
}
}
}
}

@Composable
private fun SearchBar(
focusRequester: FocusRequester,
close: () -> Unit,
clearIcon: ImageVector,
closeIcon: ImageVector,
onTextChange: (String) -> Unit
) {
var text by remember { mutableStateOf("") }

TextField(
value = text,
onValueChange = {
text = it
onTextChange(text)
},
modifier = Modifier.focusRequester(focusRequester),
placeholder = {
Text(
text = stringResource(SharedRes.strings.search),
style = MaterialTheme.typography.labelLarge
)
},
singleLine = true,
colors = searchTextFieldColors(),
keyboardOptions = KeyboardOptions(autoCorrect = false, imeAction = ImeAction.Search),
leadingIcon = {
IconButton(
onClick = close
) {
Icon(
imageVector = closeIcon,
contentDescription = stringResource(SharedRes.strings.close)
)
}
},
trailingIcon = {
IconButton(
onClick = {
text = ""
onTextChange(text)
}
) {
Icon(
imageVector = clearIcon,
contentDescription = stringResource(SharedRes.strings.clear)
)
}
}
)
}

@Composable
private fun searchTextFieldColors(
contentColor: Color = LocalContentColor.current
): TextFieldColors {
return TextFieldDefaults.colors(
unfocusedTextColor = contentColor,
focusedTextColor = contentColor,
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
cursorColor = contentColor,
selectionColors = TextSelectionColors(
handleColor = contentColor,
backgroundColor = contentColor.copy(alpha = 0.3f)
),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
focusedLeadingIconColor = contentColor,
unfocusedLeadingIconColor = contentColor,
focusedTrailingIconColor = contentColor,
unfocusedTrailingIconColor = contentColor,
unfocusedPlaceholderColor = contentColor.copy(alpha = 0.5F),
focusedPlaceholderColor = contentColor.copy(alpha = 0.5F),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package dev.datlag.burningseries.shared.ui.custom.toolbar
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
Expand All @@ -26,6 +29,7 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import dev.datlag.burningseries.model.common.safeCast
import kotlin.math.max
import kotlin.math.roundToInt

@Stable
class CollapsingToolbarScaffoldState(
Expand Down Expand Up @@ -71,13 +75,15 @@ interface CollapsingToolbarScaffoldScope {
fun Modifier.align(alignment: Alignment): Modifier
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CollapsingToolbarScaffold(
modifier: Modifier,
state: CollapsingToolbarScaffoldState,
scrollStrategy: ScrollStrategy,
enabled: Boolean = true,
toolbarModifier: Modifier = Modifier,
toolbarPadding: Int = TopAppBarDefaults.windowInsets.asPaddingValues().calculateTopPadding().value.roundToInt(),
toolbarClipToBounds: Boolean = true,
toolbarScrollable: Boolean = false,
toolbar: @Composable CollapsingToolbarScope.() -> Unit,
Expand Down Expand Up @@ -131,13 +137,14 @@ fun CollapsingToolbarScaffold(
)

val toolbarPlaceable = measurables[0].measure(toolbarConstraints)
val toolbarHeight = toolbarPlaceable.height + toolbarPadding

val bodyConstraints = constraints.copy(
minWidth = 0,
minHeight = 0,
maxHeight = when (scrollStrategy) {
ScrollStrategy.ExitUntilCollapsed ->
(constraints.maxHeight - toolbarState.minHeight).coerceAtLeast(0)
(constraints.maxHeight - toolbarHeight).coerceAtLeast(0)

ScrollStrategy.EnterAlways, ScrollStrategy.EnterAlwaysCollapsed ->
constraints.maxHeight
Expand All @@ -152,8 +159,6 @@ fun CollapsingToolbarScaffold(
it.measure(bodyConstraints)
}

val toolbarHeight = toolbarPlaceable.height

val width = max(
toolbarPlaceable.width,
bodyPlaceables.maxOfOrNull { it.width } ?: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ private fun CompactScreen(
Box(modifier = Modifier.fillMaxSize()) {
val homeScrollEnabled by component.homeScrollEnabled.collectAsStateWithLifecycle()
val favoriteScrollEnabled by component.favoriteScrollEnabled.collectAsStateWithLifecycle()
val searchScrollEnabled by component.searchScrollEnabled.collectAsStateWithLifecycle()

Pages(
pages = component.pages,
Expand All @@ -112,8 +111,7 @@ private fun CompactScreen(
val scrollEnabled = when (state.currentPage) {
0 -> homeScrollEnabled
1 -> favoriteScrollEnabled
2 -> searchScrollEnabled
else -> homeScrollEnabled && favoriteScrollEnabled && searchScrollEnabled
else -> homeScrollEnabled && favoriteScrollEnabled
}
HorizontalPager(
modifier = modifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,6 @@ class InitialScreenComponent(
label = SharedRes.strings.favorites,
unselectedIcon = Icons.Outlined.FavoriteBorder,
selectedIcon = Icons.Filled.Favorite
),
InitialComponent.PagerItem(
label = SharedRes.strings.search,
unselectedIcon = Icons.Outlined.Search,
selectedIcon = Icons.Filled.Search
)
)

Expand All @@ -62,8 +57,7 @@ class InitialScreenComponent(
Pages(
items = listOf(
View.Home(shortcutIntent),
View.Favorite,
View.Search
View.Favorite
),
selectedIndex = when (shortcutIntent) {
is Shortcut.Intent.SEARCH -> 2
Expand Down Expand Up @@ -114,14 +108,6 @@ class InitialScreenComponent(
},
scrollEnabled = { favoriteScrollEnabled.value = it }
)
is View.Search -> SearchScreenComponent(
componentContext = componentContext,
di = di,
watchVideo = { schemeKey, series, episode, stream ->
watchVideo(schemeKey, series, episode, stream)
},
scrollEnabled = { searchScrollEnabled.value = it }
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,4 @@ sealed class View {

@Serializable
data object Favorite : View()

@Serializable
data object Search : View()
}
Loading

0 comments on commit cc4a75e

Please sign in to comment.