diff --git a/cascade-compose/src/main/java/me/saket/cascade/Cascade.kt b/cascade-compose/src/main/java/me/saket/cascade/Cascade.kt index 088d380..dce9ef8 100644 --- a/cascade-compose/src/main/java/me/saket/cascade/Cascade.kt +++ b/cascade-compose/src/main/java/me/saket/cascade/Cascade.kt @@ -167,7 +167,8 @@ fun CascadeDropdownMenu( clickableWithoutRipple(onClick = onDismissRequest) }, positionProvider = popupPositionProvider, - anchorBounds = anchorBounds + anchorBounds = anchorBounds, + properties = properties, ) { PopupContent( modifier = Modifier diff --git a/cascade-compose/src/main/java/me/saket/cascade/internal/PositionPopupContent.kt b/cascade-compose/src/main/java/me/saket/cascade/internal/PositionPopupContent.kt index 244dd83..1904529 100644 --- a/cascade-compose/src/main/java/me/saket/cascade/internal/PositionPopupContent.kt +++ b/cascade-compose/src/main/java/me/saket/cascade/internal/PositionPopupContent.kt @@ -1,8 +1,10 @@ package me.saket.cascade.internal +import android.os.Build import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.absoluteOffset import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -19,19 +21,33 @@ import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.round import androidx.compose.ui.unit.toOffset import androidx.compose.ui.window.PopupPositionProvider +import androidx.compose.ui.window.PopupProperties import kotlin.math.roundToInt +import android.graphics.Rect as AndroidRect @Composable +@Suppress("NAME_SHADOWING") internal fun PositionPopupContent( modifier: Modifier = Modifier, positionProvider: PopupPositionProvider, anchorBounds: ScreenRelativeBounds?, + properties: PopupProperties, content: @Composable () -> Unit ) { val popupView = LocalView.current val layoutDirection = LocalLayoutDirection.current var contentPosition: IntOffset? by remember { mutableStateOf(null) } + if (Build.VERSION.SDK_INT >= 29 && properties.excludeFromSystemGesture) { + contentPosition?.let { contentPosition -> + LaunchedEffect(contentPosition) { + popupView.systemGestureExclusionRects = mutableListOf( + AndroidRect(0, 0, contentPosition.x, contentPosition.y) + ) + } + } + } + Box(modifier) { Box( Modifier diff --git a/cascade-compose/src/main/java/me/saket/cascade/internal/ScreenRelativeBounds.kt b/cascade-compose/src/main/java/me/saket/cascade/internal/ScreenRelativeBounds.kt index 82408d5..ea9caef 100644 --- a/cascade-compose/src/main/java/me/saket/cascade/internal/ScreenRelativeBounds.kt +++ b/cascade-compose/src/main/java/me/saket/cascade/internal/ScreenRelativeBounds.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Immutable import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.boundsInRoot import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.findRootCoordinates import androidx.compose.ui.layout.positionInRoot @@ -37,6 +38,10 @@ internal fun ScreenRelativeBounds(coordinates: LayoutCoordinates, owner: View): size = coordinates.size.toSize() ), root = RootLayoutCoordinatesInfo( + // material3 uses View#getWindowVisibleDisplayFrame() for calculating window size, + // but that produces infinite-like values for windows that have FLAG_LAYOUT_NO_LIMITS + // set (source: WindowLayout.java). material3 ends up looking okay because WindowManager + // sanitizes bad values. layoutBoundsInWindow = coordinates.findRootCoordinates().boundsInWindow(), windowPositionOnScreen = run { owner.rootView.getLocationOnScreen(intArrayBuffer) @@ -46,7 +51,7 @@ internal fun ScreenRelativeBounds(coordinates: LayoutCoordinates, owner: View): ) } -// I do not expect this to be shared across threads to need any synchronization. +// I don't expect this to be shared across threads to need any synchronization. private val intArrayBuffer = IntArray(size = 2) /**