Skip to content

Commit

Permalink
Fix AWT index calculation during re-placement
Browse files Browse the repository at this point in the history
  • Loading branch information
MatkovIvan committed May 27, 2024
1 parent acdcd96 commit 1c19447
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,11 @@ import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.node.TrackInteropContainer
import androidx.compose.ui.node.TrackInteropModifierElement
import androidx.compose.ui.node.TrackInteropModifierNode
import androidx.compose.ui.node.countInteropComponentsBefore
import androidx.compose.ui.node.countInteropComponentsBelow
import androidx.compose.ui.scene.ComposeSceneMediator
import androidx.compose.ui.unit.IntRect
import java.awt.Component
import java.awt.Container
import java.awt.event.ContainerAdapter
import java.awt.event.ContainerEvent
import java.awt.event.ContainerListener
import javax.swing.SwingUtilities
import org.jetbrains.skiko.ClipRectangle

/**
Expand Down Expand Up @@ -67,19 +63,45 @@ internal class SwingInteropContainer(
override val interopViews: Set<InteropComponent>
get() = interopComponents.values.toSet()

/**
* Index of last interop component in [container].
*
* [ComposeSceneMediator] might keep extra components in the same container.
* So based on [placeInteropAbove] they should go below or under all interop views.
*
* @see ComposeSceneMediator.contentComponent
* @see ComposeSceneMediator.invisibleComponent
*/
private val lastInteropIndex: Int
get() {
var lastInteropIndex = interopComponents.size - 1
if (!placeInteropAbove) {
val nonInteropComponents = container.componentCount - interopComponents.size
lastInteropIndex += nonInteropComponents
}
return lastInteropIndex
}

override fun placeInteropView(nativeView: InteropComponent) {
val component = nativeView.container
val nonInteropComponents = container.componentCount - interopComponents.size
// AWT uses the reverse order for drawing and events, so index = size - count
var index = interopComponents.size - countInteropComponentsBefore(nativeView)
if (!placeInteropAbove) {
index += nonInteropComponents

// Add this component to [interopComponents] to track count and clip rects
val alreadyAdded = component in interopComponents
if (!alreadyAdded) {
interopComponents[component] = nativeView
}
if (component in interopComponents) {
container.setComponentZOrder(component, index)

// Iterate through a Compose layout tree in draw order and count interop view below this one
val countBelow = countInteropComponentsBelow(nativeView)

// AWT/Swing uses the **REVERSE ORDER** for drawing and events
val awtIndex = lastInteropIndex - countBelow

// Update AWT/Swing hierarchy
if (alreadyAdded) {
container.setComponentZOrder(component, awtIndex)
} else {
interopComponents[component] = nativeView
container.add(component, index)
container.add(component, awtIndex)
}

// Sometimes Swing displays the rest of interop views in incorrect order after adding,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ internal interface InteropContainer<T> {
* @param nativeView The native view to count interop components before.
* @return The number of interop components before the given native view.
*/
internal fun <T> InteropContainer<T>.countInteropComponentsBefore(nativeView: T): Int {
internal fun <T> InteropContainer<T>.countInteropComponentsBelow(nativeView: T): Int {
var componentsBefore = 0
rootModifier?.traverseDescendantsInDrawOrder {
if (it.nativeView != nativeView) {
// It might be inside Compose tree before adding in InteropContainer in case
// It might be inside a Compose tree before adding in InteropContainer in case
// if it was initiated out of scroll visible bounds for example.
if (it.nativeView in interopViews) {
componentsBefore++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import androidx.compose.ui.node.InteropContainer
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.node.TrackInteropModifierElement
import androidx.compose.ui.node.TrackInteropModifierNode
import androidx.compose.ui.node.countInteropComponentsBefore
import androidx.compose.ui.node.countInteropComponentsBelow
import kotlinx.cinterop.CValue
import kotlinx.cinterop.readValue
import platform.CoreGraphics.CGPoint
Expand Down Expand Up @@ -50,7 +50,7 @@ internal class UIKitInteropContainer(
private set

override fun placeInteropView(nativeView: UIView) = interopContext.deferAction {
val index = countInteropComponentsBefore(nativeView)
val index = countInteropComponentsBelow(nativeView)
if (nativeView in interopViews) {
// Place might be called multiple times
nativeView.removeFromSuperview()
Expand Down

0 comments on commit 1c19447

Please sign in to comment.