diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt index c4831391a5..54ea403eeb 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt @@ -108,6 +108,7 @@ import org.dolphinemu.dolphinemu.ui.theme.MenuSpacer import org.dolphinemu.dolphinemu.ui.theme.OutlinedBox import org.dolphinemu.dolphinemu.ui.theme.PreviewTheme import org.dolphinemu.dolphinemu.ui.theme.ReadOnlyTextField +import org.dolphinemu.dolphinemu.ui.theme.bottomFadeOverlay import org.dolphinemu.dolphinemu.ui.theme.rememberSheetState import org.dolphinemu.dolphinemu.utils.CoilUtils import java.util.Locale @@ -164,7 +165,6 @@ fun NetplayScreen( val modifier = Modifier .fillMaxSize() .consumeWindowInsets(innerPadding) - .padding(innerPadding) // State which must live above the landscape/portrait split. var showChat by rememberSaveable { mutableStateOf(false) } @@ -202,6 +202,7 @@ fun NetplayScreen( joinAddresses = joinAddresses, selectedJoinInfoType = selectedJoinInfoType, onSelectedJoinInfoTypeChanged = { selectedJoinInfoType = it }, + contentPadding = innerPadding, modifier = modifier ) } else { @@ -227,6 +228,7 @@ fun NetplayScreen( joinAddresses = joinAddresses, selectedJoinInfoType = selectedJoinInfoType, onSelectedJoinInfoTypeChanged = { selectedJoinInfoType = it }, + contentPadding = innerPadding, modifier = modifier ) } @@ -344,11 +346,13 @@ private fun PortraitContent( joinAddresses: Map, selectedJoinInfoType: JoinInfoType, onSelectedJoinInfoTypeChanged: (JoinInfoType) -> Unit, + contentPadding: PaddingValues, modifier: Modifier = Modifier, ) { Column( modifier = modifier .verticalScroll(rememberScrollState()) + .padding(contentPadding) ) { Chat( messages = messages, @@ -414,11 +418,11 @@ private fun LandscapeContent( joinAddresses: Map, selectedJoinInfoType: JoinInfoType, onSelectedJoinInfoTypeChanged: (JoinInfoType) -> Unit, + contentPadding: PaddingValues, modifier: Modifier = Modifier, ) { Row( modifier = modifier - .padding(horizontal = DolphinTheme.scaffoldPadding) ) { Chat( messages = messages, @@ -426,16 +430,26 @@ private fun LandscapeContent( showBottomSheet = showChat, onShowBottomSheetChanged = onShowChatChanged, modifier = Modifier + .padding(contentPadding) + .padding( + start = DolphinTheme.scaffoldPadding, + end = DolphinTheme.scaffoldPadding / 2, + ) .weight(1f) .fillMaxHeight() ) - Spacer(modifier = Modifier.width(16.dp)) - + val scrollState = rememberScrollState() Column( modifier = Modifier .weight(1f) - .verticalScroll(rememberScrollState()) + .bottomFadeOverlay(scrollState, contentPadding.calculateBottomPadding()) + .verticalScroll(scrollState) + .padding(contentPadding) + .padding( + start = DolphinTheme.scaffoldPadding / 2, + end = DolphinTheme.scaffoldPadding + ) ) { PlayersAndSettings( game = game, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/theme/DolphinTheme.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/theme/DolphinTheme.kt index 37085b06d9..c142a44d73 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/theme/DolphinTheme.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/theme/DolphinTheme.kt @@ -5,11 +5,13 @@ package org.dolphinemu.dolphinemu.ui.theme import android.content.Context import androidx.annotation.AttrRes import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -28,10 +30,15 @@ import androidx.compose.material3.Text import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext @@ -286,3 +293,45 @@ fun rememberSheetState( ) } } + +@Composable +fun Modifier.bottomFadeOverlay( + scrollState: ScrollState, + fadeHeight: Dp, +): Modifier { + val surfaceColor = MaterialTheme.colorScheme.surface + val bottomPaddingPx = with(LocalDensity.current) { + fadeHeight.toPx() + } + val fadeDelayPx = with(LocalDensity.current) { 8.dp.toPx() } + val fadeDurationPx = with(LocalDensity.current) { 48.dp.toPx() } + val scrollProgress by remember { + derivedStateOf { + if (bottomPaddingPx <= 0f) 1f + else ((scrollState.value.coerceAtLeast(0) - fadeDelayPx) / fadeDurationPx) + .coerceIn(0f, 1f) + } + } + val surfaceStopFraction = (scrollProgress * 2f).coerceIn(0f, 1f) + val transparentStopFraction = ((scrollProgress * 2f) - 1f).coerceIn(0f, 1f) + + return drawWithContent { + drawContent() + if (transparentStopFraction < 1f) { + drawRect( + brush = Brush.verticalGradient( + colorStops = arrayOf( + 0f to Color.Transparent, + transparentStopFraction to Color.Transparent, + surfaceStopFraction to surfaceColor, + 1f to surfaceColor, + ), + startY = size.height - bottomPaddingPx, + endY = size.height, + ), + topLeft = Offset(0f, size.height - bottomPaddingPx), + size = Size(size.width, bottomPaddingPx), + ) + } + } +}