Skip to content

Instantly share code, notes, and snippets.

@habibg1232191
Last active June 27, 2024 15:09
Show Gist options
  • Save habibg1232191/2ca21b6f113a875e05f4494f32f87d4e to your computer and use it in GitHub Desktop.
Save habibg1232191/2ca21b6f113a875e05f4494f32f87d4e to your computer and use it in GitHub Desktop.
import androidx.compose.animation.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.window.Popup
@Composable
fun AniJinPopup(
expanded: Boolean,
popupPositionProvider: PopupPositionProvider,
onDismissRequest: (() -> Unit)? = null,
onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false },
onKeyEvent: ((KeyEvent) -> Boolean) = { false },
focusable: Boolean = false,
enter: EnterTransition = fadeIn(),
exit: ExitTransition = fadeOut(),
modifier: Modifier = Modifier,
content: @Composable AnimatedVisibilityScope.() -> Unit
) {
val expandedState = remember { MutableTransitionState(false) }
expandedState.targetState = expanded
if(expandedState.currentState || expandedState.targetState || !expandedState.isIdle) {
Popup(
focusable = focusable,
popupPositionProvider = popupPositionProvider,
onDismissRequest = onDismissRequest,
onPreviewKeyEvent = onPreviewKeyEvent,
onKeyEvent = onKeyEvent
) {
AnimatedVisibility(
visibleState = expandedState,
enter = enter,
exit = exit,
modifier = modifier,
content = content
)
}
}
}
@Composable
fun AniJinPopup(
expanded: Boolean,
alignment: Alignment = Alignment.TopStart,
offset: IntOffset = IntOffset(0, 0),
onDismissRequest: (() -> Unit)? = null,
onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false },
onKeyEvent: ((KeyEvent) -> Boolean) = { false },
focusable: Boolean = false,
enter: EnterTransition = fadeIn(),
exit: ExitTransition = fadeOut(),
modifier: Modifier = Modifier,
content: @Composable AnimatedVisibilityScope.() -> Unit
) {
val popupPositionProvider = remember(alignment, offset) {
AlignmentOffsetPositionProvider(
alignment,
offset
)
}
AniJinPopup(
expanded = expanded,
popupPositionProvider = popupPositionProvider,
onDismissRequest = onDismissRequest,
onPreviewKeyEvent = onPreviewKeyEvent,
onKeyEvent = onKeyEvent,
focusable = focusable,
enter = enter,
exit = exit,
modifier = modifier,
content = content
)
}
class AlignmentOffsetPositionProvider(
val alignment: Alignment,
val offset: IntOffset
) : PopupPositionProvider {
override fun calculatePosition(
anchorBounds: IntRect,
windowSize: IntSize,
layoutDirection: LayoutDirection,
popupContentSize: IntSize
): IntOffset {
// TODO: Decide which is the best way to round to result without reimplementing Alignment.align
var popupPosition = IntOffset(0, 0)
// Get the aligned point inside the parent
val parentAlignmentPoint = alignment.align(
IntSize.Zero,
IntSize(anchorBounds.width, anchorBounds.height),
layoutDirection
)
// Get the aligned point inside the child
val relativePopupPos = alignment.align(
IntSize.Zero,
IntSize(popupContentSize.width, popupContentSize.height),
layoutDirection
)
// Add the position of the parent
popupPosition += IntOffset(anchorBounds.left, anchorBounds.top)
// Add the distance between the parent's top left corner and the alignment point
popupPosition += parentAlignmentPoint
// Subtract the distance between the children's top left corner and the alignment point
popupPosition -= IntOffset(relativePopupPos.x, relativePopupPos.y)
// Add the user offset
val resolvedOffset = IntOffset(
offset.x * (if (layoutDirection == LayoutDirection.Ltr) 1 else -1),
offset.y
)
popupPosition += resolvedOffset
return popupPosition
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment