-
Notifications
You must be signed in to change notification settings - Fork 95
Use onPlaced
instead of onGloballyPositioned
in PopupLayout
#2352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: jb-main
Are you sure you want to change the base?
Conversation
Added two reviewers, because you both have context, but only one is needed. |
@@ -463,16 +472,6 @@ private val PopupProperties.platformInsets: PlatformInsets | |||
} | |||
} | |||
|
|||
private fun Modifier.parentBoundsInWindow( | |||
onBoundsChanged: (IntRect) -> Unit | |||
) = this.onGloballyPositioned { childCoordinates -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why it's required to unroll this function? can we just replace the implementation inside?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I inlined it because
- It's important to use
onPlaced
specifically, and a separate function hides that - I wanted to add a comment explaining how it works without a recomposition (or even a relayout) and such a comment only makes sense when all the context is present in one place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I follow. How exactly wrapping modifier creation in function causes extra compositions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't. Not using onPlaced
does, and I wanted to highlight that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer keeping meaningfuly named functions over comments even if it's used once.
Probably not a blocker here
val layer = rememberComposeSceneLayer( | ||
focusable = properties.focusable | ||
) | ||
layer.setKeyEventListener(onPreviewKeyEvent, onKeyEvent) | ||
layer.setOutsidePointerEventListener(onOutsidePointerEvent) | ||
layer.Content { | ||
val platformInsets = properties.platformInsets | ||
val parentBoundsInWindow = layoutParentBoundsInWindow ?: return@Content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we keep this logic? Or add require/assert to make sure that it's initialized? I mean for possible future changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd need to make the type nullable (IntRect?
) again, which I prefer not to do.
But the unit test I added checks exactly this...
@@ -463,16 +472,6 @@ private val PopupProperties.platformInsets: PlatformInsets | |||
} | |||
} | |||
|
|||
private fun Modifier.parentBoundsInWindow( | |||
onBoundsChanged: (IntRect) -> Unit | |||
) = this.onGloballyPositioned { childCoordinates -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer keeping meaningfuly named functions over comments even if it's used once.
Probably not a blocker here
@@ -463,16 +472,6 @@ private val PopupProperties.platformInsets: PlatformInsets | |||
} | |||
} | |||
|
|||
private fun Modifier.parentBoundsInWindow( | |||
onBoundsChanged: (IntRect) -> Unit | |||
) = this.onGloballyPositioned { childCoordinates -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait. It HAS TO be onGloballyPositioned
.
Case: not full screen compose view opens Popup
. This view is moved by platform. Popup
must update the position.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After checking, there's a mechanism in Compose that makes sure to re-layout nodes that use LayoutCoordinates
in their measuring/layout when the coordinates relative to window/screen change, so this is not a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's quite unexpected behavior that onPlaced
is called in the same cases as onGloballyPositioned
. It seems dangerous to rely on this.
Let's make sure that
- such cases are covered by tests
- it's confirmed that it's expected by Googlers (or file a bug)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not that onPlaced
is called when onGloballyPositioned
is.
It's that nodes that read LayoutCoordinates.windowToLocal
get re-laid out when the window coordinates change.
Use
Modifier.onPlaced
instead ofModifier.onGloballyPositioned
to avoid calls when the window is moved.Also use a
MutableState
instead of a delegate to it, in order to avoid an extra recomposition.This also allows showing the popup without an extra recomposition because
onPlaced
is called in the layout phase following the initial composition when the popup is shown. It's also called before the popup measure policy is called, so there is not even a need to re-layout.Testing
Tested manually and added a unit test.
Release Notes
N/A