Fix NetplayScreen edge to edge

NetplayScreen was not drawing under the bottom nav bar like other screens. In landscape mode half of the screen scrolls and half is fixed, to make this look natural with edge to edge, provide a fade on the bottom of the scrolling side so things look natural when the screens loads and is resting at 0 scroll offset.
This commit is contained in:
Tom Pratt
2026-05-19 09:46:22 +02:00
committed by Tom Pratt
parent fde035dc0c
commit 5b49060a9d
2 changed files with 68 additions and 5 deletions
@@ -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<JoinInfoType, JoinAddress>,
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<JoinInfoType, JoinAddress>,
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,
@@ -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),
)
}
}
}