Skip to content

Fix initial Skiko popup positioning - #3321

Merged
Vendula Švastalová (svastven) merged 9 commits into
jb-mainfrom
svastven/CMP-10684
Sep 7, 2026
Merged

Fix initial Skiko popup positioning#3321
Vendula Švastalová (svastven) merged 9 commits into
jb-mainfrom
svastven/CMP-10684

Conversation

@svastven

@svastven Vendula Švastalová (svastven) commented Aug 20, 2026

Copy link
Copy Markdown

Fixes selection handles briefly shown at an offset.

Popup anchoring relied on the next snapshot apply (removed in #3272) during drawing to make parent bounds written during layout visible to the separate popup scene. As a result, the first popup layout used the initial IntRect.Zero anchor causing the issue.

We now compose the Popup content only after a real parent anchor is available which prevents drawing the first zero-anchor frame. Similar approach is used on Android

/** Track parent coordinates and content size; only show popup once we have both. */

Bug Fix
Bug.mov
Fix.mov

Fixes CMP-10684 Selection handles blink on the view top
Fixes CMP-10714 [iOS] Dismissing a Popup leaves stale content when another Popup layer remains

Testing

This should be tested by QA

Release Notes

Fixes - iOS

  • (prerelease fix) Fix an issue where text-selection handles could briefly appear in the wrong position when selecting text.

val canCalculatePosition by remember {
derivedStateOf { parentBoundsInWindow.value != null }
}
if (!canCalculatePosition) return@Content

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems it breaks "showing in the same frame" behavior. Test failures seems to be related

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, wrote a comment about that: #3321 (comment) we need to choose which is preferred.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Popup does not always depend on parent bounds and can have absolute positioning. In this case it seems we shouldn't delay showing it.
But I'm not sure if we can detect it here since it's just custom positioning logic from PoV of this code

Comment thread compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/window/Popup.skiko.kt Outdated
@svastven

Vendula Švastalová (svastven) commented Aug 20, 2026

Copy link
Copy Markdown
Author

testLayerContentOnFirstRender currently expects that a Popup that is created in a frame will also be rendered in the same frame, but the fix in this PR is not in line with this expectation. Now the popup scene is empty up until it has a valid parent anchor.

The core problem is that the main scene's placement phase and the popup layer layout phase are independent, the frame timing is shared but there is no guarantee in the dependency between layers so that we can expect main onPlaced -> popup layout -> popup draw.

now there are two options if we don't want to tackle the dependency issue ATM:

  1. Wrap the parent bounds write in Snapshot.withMutableSnapshot
Snapshot.withMutableSnapshot {
    parentBoundsInWindow.value = IntRect(layoutPosition, layoutSize)
}

This would restore the local snapshot apply during the main scene layout call (removed in #3272), and the popup scene can then consume the real anchor and render in the first frame.

  1. Keep the current state, wait with first popup frame until parent bounds are ready. This avoids applying snapshot changes during layout. But it would require losening the expecations of testLayerContentOnFirstRender. Android also waits for when parent bounds are ready but it makes the popup content transparent until then rather than leaving it empty.

@m-sasha

Copy link
Copy Markdown

The original code (and comments) assumed the popup and the parent are in the same scene. When that's not true, then indeed I don't think we can expect the popup to be shown in the same frame.

I wouldn't completely remove my comments or tests though, but instead update them to document the two cases (same scene, different scenes).

@svastven

Copy link
Copy Markdown
Author

I updated the comments and tests to document both cases based on the updated behavior. Alexander Maryanovsky (@m-sasha) can you please review?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR breaks a needed functionality: that popups are shown immediately.
This is needed for example, for initial sizing of a window that has a popup in it. Without this, it can't be sized to take into account the popup size.

I understand that this is not possible when the popup is in a separate native layer (maybe will be possible again after Ivan's planned shared recomposer change?), but can we keep it when the popup is in the same layer?

Comment thread compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeDialogTest.kt Outdated
@svastven
Vendula Švastalová (svastven) force-pushed the svastven/CMP-10684 branch 4 times, most recently from 286aa33 to 5bbfbb5 Compare August 26, 2026 12:56
@svastven

Copy link
Copy Markdown
Author

Alexander Maryanovsky (@m-sasha) Ivan Matkov (@MatkovIvan), can you please review?

Comment thread compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/window/Popup.skiko.kt Outdated
@svastven
Vendula Švastalová (svastven) merged commit e21de42 into jb-main Sep 7, 2026
18 checks passed
@svastven
Vendula Švastalová (svastven) deleted the svastven/CMP-10684 branch September 7, 2026 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants