I want to determine the Popup
position.
I use androidx.compose.ui.window.Popup
As usual in Compose it's possible to do it, using Modifier
: onGloballyPositioned
But there is 2 issues:
onGloballyPositioned
in the child view, it returns zero offset, as I understood it considers Popup like a root layout.The popup is positioned relative to its parent, and I can use parent position, offset and alignment to calculate it, maybe there are some others (more simplest) approach to get the Popup global position?
This actually very good and tricky question which is something i have figured out recently while building a Tooltip.
Popup Position is calculated inside PopupPositionProvider you pass or it's implicitly set by overload function you pass Offset
or Alignment
This is the default AlignmentOffsetPositionProvider
for Popup
i copied from original source code.
internal 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
}
}
You can get popupPosition
from this by sending a class that holds value. However when your popUp has padding you need to take that into consideration because popupContentSize
is width or height of the content plus paddings.
And second thing to note is if your alignment is close any edge, let's say to start it returns negative values but when PopupProperties have clippingEnabled = true which is default value it will be reset to zero on screen while mathematical value is negative for instance. You should also take that into consideration.
Last but not least popup top position is always based on window. Even it's at (0, 0) in your Composable it will return (0, statusBarHeight) this is another tricky thing to consider.
And yes, Popup creates another window and because of that Modifier.globallyPositioned doesn't work if you use it.positionInRoot/Parent/Window
but you can still check top left with it.positionOnScreen
I have some examples with Popup which you can check and see logs for positions and other properties in link below and there are other samples as well.